Skip to content

Commit b1d9a2e

Browse files
committed
refactor(BREAKING): remove listenStream and add listen that add event listners for socket.io
1 parent 4f0727a commit b1d9a2e

16 files changed

+294
-316
lines changed

browser/websocket/applyCommit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CommitNotification } from "./websocket-types.ts";
1+
import type { CommitNotification } from "./listen-events.ts";
22
import type { BaseLine } from "@cosense/types/rest";
33
import { getUnixTimeFromId } from "./id.ts";
44

browser/websocket/change.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
export type Change =
2+
| InsertChange
3+
| UpdateChange
4+
| DeleteChange
5+
| LinksChange
6+
| ProjectLinksChange
7+
| IconsChange
8+
| DescriptionsChange
9+
| ImageChange
10+
| FilesChange
11+
| HelpFeelsChange
12+
| infoboxDefinitionChange
13+
| TitleChange;
14+
export interface InsertChange {
15+
_insert: string;
16+
lines: {
17+
id: string;
18+
text: string;
19+
};
20+
}
21+
export interface UpdateChange {
22+
_update: string;
23+
lines: {
24+
text: string;
25+
};
26+
noTimestampUpdate?: unknown;
27+
}
28+
export interface DeleteChange {
29+
_delete: string;
30+
lines: -1;
31+
}
32+
export interface LinksChange {
33+
links: string[];
34+
}
35+
export interface ProjectLinksChange {
36+
projectLinks: string[];
37+
}
38+
export interface IconsChange {
39+
icons: string[];
40+
}
41+
export interface DescriptionsChange {
42+
descriptions: string[];
43+
}
44+
export interface ImageChange {
45+
image: string | null;
46+
}
47+
export interface TitleChange {
48+
title: string;
49+
}
50+
export interface FilesChange {
51+
/** file id */
52+
files: string[];
53+
}
54+
export interface HelpFeelsChange {
55+
/** Helpfeel記法の先頭の`? `をとったもの */
56+
helpfeels: string[];
57+
}
58+
export interface infoboxDefinitionChange {
59+
/** `table:infobox`または`table:cosense`の各行をtrimしたもの */
60+
infoboxDefinition: string[];
61+
}
62+
export interface PinChange {
63+
pin: number;
64+
}
65+
export interface DeletePageChange {
66+
deleted: true;
67+
merged?: true;
68+
}

browser/websocket/diffToChanges.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { diff, toExtendedChanges } from "../../deps/onp.ts";
22
import type { Line } from "@cosense/types/userscript";
3-
import type {
4-
DeleteChange,
5-
InsertChange,
6-
UpdateChange,
7-
} from "./websocket-types.ts";
3+
import type { DeleteChange, InsertChange, UpdateChange } from "./change.ts";
84
import { createNewLineId } from "./id.ts";
95

106
type Options = {

browser/websocket/emit-events.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { Change, DeletePageChange, PinChange } from "./change.ts";
2+
3+
export interface EmitEvents {
4+
"socket.io-request": (
5+
req: { method: "commit"; data: PageCommit } | {
6+
method: "room:join";
7+
data: JoinRoomRequest;
8+
},
9+
callback: (
10+
res:
11+
| { data: PageCommitResponse | JoinRoomResponse }
12+
| { error: { name: string; message?: string } },
13+
) => void,
14+
) => void;
15+
cursor: (req: Omit<MoveCursorData, "socketId">) => void;
16+
}
17+
18+
export interface PageCommit {
19+
kind: "page";
20+
parentId: string;
21+
projectId: string;
22+
pageId: string;
23+
userId: string;
24+
changes: Change[] | [PinChange] | [DeletePageChange];
25+
cursor?: null;
26+
freeze: true;
27+
}
28+
29+
export interface PageCommitResponse {
30+
commitId: string;
31+
}
32+
33+
export type JoinRoomRequest =
34+
| JoinPageRoomRequest
35+
| JoinProjectRoomRequest
36+
| JoinStreamRoomRequest;
37+
38+
export interface JoinProjectRoomRequest {
39+
pageId: null;
40+
projectId: string;
41+
projectUpdatesStream: false;
42+
}
43+
44+
export interface JoinPageRoomRequest {
45+
pageId: string;
46+
projectId: string;
47+
projectUpdatesStream: false;
48+
}
49+
50+
export interface JoinStreamRoomRequest {
51+
pageId: null;
52+
projectId: string;
53+
projectUpdatesStream: true;
54+
}
55+
56+
export interface JoinRoomResponse {
57+
success: true;
58+
pageId: string | null;
59+
projectId: string;
60+
}
61+
62+
export interface MoveCursorData {
63+
user: {
64+
id: string;
65+
name: string;
66+
displayName: string;
67+
};
68+
pageId: string;
69+
position: {
70+
line: number;
71+
char: number;
72+
};
73+
visible: boolean;
74+
socketId: string;
75+
}

browser/websocket/emit.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
import { createErr, createOk, type Result } from "option-t/plain_result";
22
import type { Socket } from "socket.io-client";
33
import type {
4-
EmitEvents,
54
JoinRoomRequest,
6-
ListenEvents,
75
MoveCursorData,
86
PageCommit,
97
PageCommitResponse,
10-
} from "./websocket-types.ts";
11-
export * from "./websocket-types.ts";
8+
} from "./emit-events.ts";
129
import {
1310
isPageCommitError,
1411
type PageCommitError,
1512
type SocketIOServerDisconnectError,
1613
type TimeoutError,
1714
type UnexpectedRequestError,
1815
} from "./error.ts";
19-
import type { JoinRoomResponse } from "./websocket-types.ts";
16+
import type { JoinRoomResponse } from "./emit-events.ts";
17+
import type { ScrapboxSocket } from "./socket.ts";
2018

2119
export interface WrapperdEmitEvents {
2220
commit: { req: PageCommit; res: PageCommitResponse; err: PageCommitError };
@@ -47,7 +45,7 @@ export interface EmitOptions {
4745
* @returns A promise that resolves with the result of the emit operation.
4846
*/
4947
export const emit = <EventName extends keyof WrapperdEmitEvents>(
50-
socket: Socket<ListenEvents, EmitEvents>,
48+
socket: ScrapboxSocket,
5149
event: EventName,
5250
data: WrapperdEmitEvents[EventName]["req"],
5351
options?: EmitOptions,

browser/websocket/listen-events.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import type { MoveCursorData, PageCommit } from "./emit-events.ts";
2+
import type {
3+
DeleteChange,
4+
DeletePageChange,
5+
DescriptionsChange,
6+
IconsChange,
7+
ImageChange,
8+
InsertChange,
9+
LinksChange,
10+
TitleChange,
11+
UpdateChange,
12+
} from "./change.ts";
13+
14+
export interface ListenEvents {
15+
"projectUpdatesStream:commit": (event: ProjectUpdatesStreamCommit) => void;
16+
"projectUpdatesStream:event": (event: ProjectUpdatesStreamEvent) => void;
17+
commit: (event: CommitNotification) => void;
18+
cursor: (event: MoveCursorData) => void;
19+
"quick-search:commit": (event: QuickSearchCommit) => void;
20+
"quick-search:replace-link": QuickSearchReplaceLink;
21+
"infobox:updating": boolean;
22+
"infobox:reload": void;
23+
"literal-database:reload": void;
24+
}
25+
26+
export interface ProjectUpdatesStreamCommit {
27+
kind: "page";
28+
id: string;
29+
parentId: string;
30+
projectId: string;
31+
pageId: string;
32+
userId: string;
33+
changes:
34+
| (
35+
| InsertChange
36+
| UpdateChange
37+
| DeleteChange
38+
| TitleChange
39+
| LinksChange
40+
| IconsChange
41+
)[]
42+
| [DeletePageChange];
43+
cursor: null;
44+
freeze: true;
45+
}
46+
47+
export type ProjectUpdatesStreamEvent =
48+
| PageDeleteEvent
49+
| MemberJoinEvent
50+
| MemberAddEvent
51+
| AdminAddEvent
52+
| AdminDeleteEvent
53+
| OwnerSetEvent
54+
| InvitationResetEvent;
55+
56+
export interface ProjectEvent {
57+
id: string;
58+
pageId: string;
59+
userId: string;
60+
projectId: string;
61+
created: number;
62+
updated: number;
63+
}
64+
65+
export interface PageDeleteEvent extends ProjectEvent {
66+
type: "page.delete";
67+
data: {
68+
titleLc: string;
69+
};
70+
}
71+
export interface MemberJoinEvent extends ProjectEvent {
72+
type: "member.join";
73+
}
74+
export interface MemberAddEvent extends ProjectEvent {
75+
type: "member.add";
76+
}
77+
export interface InvitationResetEvent extends ProjectEvent {
78+
type: "invitation.reset";
79+
}
80+
export interface AdminAddEvent extends ProjectEvent {
81+
type: "admin.add";
82+
targetUserId: string;
83+
}
84+
export interface AdminDeleteEvent extends ProjectEvent {
85+
type: "admin.delete";
86+
targetUserId: string;
87+
}
88+
export interface OwnerSetEvent extends ProjectEvent {
89+
type: "owner.set";
90+
targetUserId: string;
91+
}
92+
93+
export interface CommitNotification extends PageCommit {
94+
id: string;
95+
}
96+
97+
export interface QuickSearchCommit extends Omit<CommitNotification, "changes"> {
98+
changes:
99+
| (TitleChange | LinksChange | DescriptionsChange | ImageChange)[]
100+
| [DeletePageChange];
101+
}
102+
103+
export interface QuickSearchReplaceLink {
104+
from: string;
105+
to: string;
106+
}

browser/websocket/listen.ts

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
1-
import { createOk, isErr, type Result, unwrapOk } from "option-t/plain_result";
21
import type {
32
NotFoundError,
43
NotLoggedInError,
54
NotMemberError,
65
} from "@cosense/types/rest";
7-
import type {
8-
ProjectUpdatesStreamCommit,
9-
ProjectUpdatesStreamEvent,
10-
} from "./emit.ts";
116
import type { HTTPError } from "../../rest/responseIntoResult.ts";
127
import type { AbortError, NetworkError } from "../../rest/robustFetch.ts";
13-
import { getProjectId } from "./pull.ts";
14-
import { connect, disconnect } from "./socket.ts";
15-
import type { Socket } from "socket.io-client";
8+
import type { ScrapboxSocket } from "./socket.ts";
9+
import type { ListenEvents } from "./listen-events.ts";
1610

1711
export type {
1812
ProjectUpdatesStreamCommit,
1913
ProjectUpdatesStreamEvent,
20-
} from "./websocket-types.ts";
14+
} from "./listen-events.ts";
2115

2216
export interface ListenStreamOptions {
23-
socket?: Socket;
17+
signal?: AbortSignal;
18+
once?: boolean;
2419
}
2520

2621
export type ListenStreamError =
@@ -37,46 +32,21 @@ export type ListenStreamError =
3732
* @param events 購読したいevent。配列で指定する
3833
* @param options 使用したいSocketがあれば指定する
3934
*/
40-
export async function* listenStream(
41-
project: string,
42-
events: ["commit" | "event", ...("commit" | "event")[]],
35+
export const listen = <EventName extends keyof ListenEvents>(
36+
socket: ScrapboxSocket,
37+
event: EventName,
38+
listener: ListenEvents[EventName],
4339
options?: ListenStreamOptions,
44-
): AsyncGenerator<
45-
Result<
46-
ProjectUpdatesStreamEvent | ProjectUpdatesStreamCommit,
47-
ListenStreamError
48-
>,
49-
void,
50-
unknown
51-
> {
52-
const result = await getProjectId(project);
53-
if (isErr(result)) {
54-
yield result;
55-
return;
56-
}
57-
const projectId = unwrapOk(result);
40+
): void => {
41+
if (options?.signal?.aborted) return;
5842

59-
const injectedSocket = options?.socket;
60-
const result2 = await connect(injectedSocket);
61-
if (isErr(result2)) throw new Error("Failed to connect to websocket");
62-
const socket = unwrapOk(result2);
63-
const { request, response } = wrap(socket);
43+
// deno-lint-ignore no-explicit-any
44+
(options?.once ? socket.once : socket.on)(event, listener as any);
6445

65-
try {
66-
// 部屋に入って購読し始める
67-
await request("socket.io-request", {
68-
method: "room:join",
69-
data: { projectId, pageId: null, projectUpdatesStream: true },
70-
});
71-
72-
for await (
73-
const streamEvent of response(
74-
...events.map((event) => `projectUpdatesStream:${event}` as const),
75-
)
76-
) {
77-
yield createOk(streamEvent);
78-
}
79-
} finally {
80-
if (!injectedSocket) await disconnect(socket);
81-
}
82-
}
46+
options?.signal?.addEventListener?.(
47+
"abort",
48+
// deno-lint-ignore no-explicit-any
49+
() => socket.off(event, listener as any),
50+
{ once: true },
51+
);
52+
};

0 commit comments

Comments
 (0)