@pluv/io 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +196 -0
- package/dist/index.d.mts +101 -101
- package/dist/index.d.ts +101 -101
- package/dist/index.js +217 -202
- package/dist/index.mjs +217 -202
- package/package.json +5 -5
- package/src/AbstractPlatform.ts +5 -5
- package/src/IORoom.ts +182 -115
- package/src/PluvIO.ts +16 -185
- package/src/PluvRouter.ts +1 -3
- package/src/PluvServer.ts +194 -76
- package/src/createIO.ts +3 -7
- package/src/index.ts +1 -1
- package/src/types.ts +31 -6
- package/src/version.generated.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"jose": "^5.6.3",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
23
|
"wonka": "^6.3.4",
|
|
24
|
-
"@pluv/crdt": "^0.
|
|
25
|
-
"@pluv/types": "^0.
|
|
24
|
+
"@pluv/crdt": "^0.24.0",
|
|
25
|
+
"@pluv/types": "^0.24.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"eslint": "^8.57.0",
|
|
29
29
|
"genversion": "^3.2.0",
|
|
30
30
|
"tsup": "^8.2.4",
|
|
31
31
|
"typescript": "^5.5.4",
|
|
32
|
-
"@pluv/tsconfig": "^0.
|
|
33
|
-
"eslint-config-pluv": "^0.
|
|
32
|
+
"@pluv/tsconfig": "^0.24.0",
|
|
33
|
+
"eslint-config-pluv": "^0.24.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
|
package/src/AbstractPlatform.ts
CHANGED
|
@@ -39,16 +39,16 @@ export abstract class AbstractPlatform<
|
|
|
39
39
|
TPlatformContext extends Record<string, any> = {},
|
|
40
40
|
TRoomContext extends Record<string, any> = {},
|
|
41
41
|
> {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
readonly _ioContext: TPlatformContext | undefined;
|
|
45
|
-
readonly _roomContext: TRoomContext | undefined;
|
|
42
|
+
protected readonly _ioContext: TPlatformContext | undefined;
|
|
43
|
+
protected readonly _roomContext: TRoomContext | undefined;
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
private _initialized: boolean = false;
|
|
48
46
|
|
|
49
47
|
public persistance: AbstractPersistance;
|
|
50
48
|
public pubSub: AbstractPubSub;
|
|
51
49
|
|
|
50
|
+
public abstract readonly _registrationMode: WebSocketRegistrationMode;
|
|
51
|
+
|
|
52
52
|
constructor(config: AbstractPlatformConfig<TPlatformContext, TRoomContext> = {}) {
|
|
53
53
|
const { context, persistance, pubSub } = config;
|
|
54
54
|
|
package/src/IORoom.ts
CHANGED
|
@@ -5,9 +5,11 @@ import type {
|
|
|
5
5
|
BaseIOEventRecord,
|
|
6
6
|
EventMessage,
|
|
7
7
|
IOAuthorize,
|
|
8
|
+
IOEventMessage,
|
|
8
9
|
IOLike,
|
|
9
10
|
Id,
|
|
10
11
|
InferEventMessage,
|
|
12
|
+
InferEventsOutput,
|
|
11
13
|
InferIOAuthorize,
|
|
12
14
|
InferIOAuthorizeUser,
|
|
13
15
|
InferIOInput,
|
|
@@ -16,12 +18,7 @@ import type {
|
|
|
16
18
|
Maybe,
|
|
17
19
|
} from "@pluv/types";
|
|
18
20
|
import colors from "kleur";
|
|
19
|
-
import type {
|
|
20
|
-
AbstractPlatform,
|
|
21
|
-
InferPlatformWebSocketSource,
|
|
22
|
-
InferPlatformWebSocketType,
|
|
23
|
-
InferRoomContextType,
|
|
24
|
-
} from "./AbstractPlatform";
|
|
21
|
+
import type { AbstractPlatform, InferPlatformWebSocketSource, InferRoomContextType } from "./AbstractPlatform";
|
|
25
22
|
import { AbstractCloseEvent, AbstractErrorEvent, AbstractMessageEvent, AbstractWebSocket } from "./AbstractWebSocket";
|
|
26
23
|
import type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
|
|
27
24
|
import { authorize } from "./authorize";
|
|
@@ -29,7 +26,7 @@ import { PING_TIMEOUT_MS } from "./constants";
|
|
|
29
26
|
import type {
|
|
30
27
|
EventResolverContext,
|
|
31
28
|
IORoomListenerEvent,
|
|
32
|
-
|
|
29
|
+
IORoomMessageEvent,
|
|
33
30
|
WebSocketSession,
|
|
34
31
|
WebSocketType,
|
|
35
32
|
} from "./types";
|
|
@@ -43,8 +40,14 @@ interface BroadcastParams<TIO extends IORoom<any, any, any, any>> {
|
|
|
43
40
|
senderId?: string;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
interface IORoomListeners<
|
|
47
|
-
|
|
43
|
+
export interface IORoomListeners<
|
|
44
|
+
TPlatform extends AbstractPlatform<any>,
|
|
45
|
+
TAuthorize extends IOAuthorize<any, any, InferRoomContextType<TPlatform>>,
|
|
46
|
+
TContext extends JsonObject,
|
|
47
|
+
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
|
|
48
|
+
> {
|
|
49
|
+
onDestroy: (event: IORoomListenerEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;
|
|
50
|
+
onMessage: (event: IORoomMessageEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
export type BroadcastProxy<TIO extends IORoom<any, any, any, any>> = (<TEvent extends keyof InferIOInput<TIO>>(
|
|
@@ -59,7 +62,7 @@ export type IORoomConfig<
|
|
|
59
62
|
TAuthorize extends IOAuthorize<any, any, InferRoomContextType<TPlatform>> = BaseIOAuthorize,
|
|
60
63
|
TContext extends JsonObject = {},
|
|
61
64
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
62
|
-
> = Partial<IORoomListeners<TPlatform>> & {
|
|
65
|
+
> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
|
|
63
66
|
authorize?: TAuthorize;
|
|
64
67
|
context: TContext & InferRoomContextType<TPlatform>;
|
|
65
68
|
crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
@@ -84,20 +87,19 @@ export class IORoom<
|
|
|
84
87
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
85
88
|
> implements IOLike<TAuthorize, TEvents>
|
|
86
89
|
{
|
|
90
|
+
public readonly id: string;
|
|
91
|
+
public readonly _authorize: TAuthorize | null = null;
|
|
92
|
+
|
|
93
|
+
private _doc: Promise<AbstractCrdtDoc<any>>;
|
|
94
|
+
private _uninitialize: Promise<() => Promise<void>> | null = null;
|
|
95
|
+
|
|
87
96
|
private readonly _context: TContext & InferRoomContextType<TPlatform>;
|
|
88
97
|
private readonly _debug: boolean;
|
|
89
98
|
private readonly _docFactory: AbstractCrdtDocFactory<any>;
|
|
99
|
+
private readonly _listeners: IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>;
|
|
90
100
|
private readonly _platform: TPlatform;
|
|
91
|
-
|
|
92
|
-
private
|
|
93
|
-
private _listeners: IORoomListeners<TPlatform>;
|
|
94
|
-
private _sessions = new Map<string, AbstractWebSocket>();
|
|
95
|
-
private _uninitialize: (() => Promise<void>) | null = null;
|
|
96
|
-
|
|
97
|
-
readonly _authorize: TAuthorize | null = null;
|
|
98
|
-
readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
99
|
-
|
|
100
|
-
readonly id: string;
|
|
101
|
+
private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
102
|
+
private readonly _sessions = new Map<string, AbstractWebSocket>();
|
|
101
103
|
|
|
102
104
|
public get _events() {
|
|
103
105
|
return this._router._events;
|
|
@@ -120,28 +122,30 @@ export class IORoom<
|
|
|
120
122
|
}) as BroadcastProxy<this>;
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
private get _initialized(): Promise<boolean> {
|
|
126
|
+
return Promise.resolve(!!this._uninitialize?.then((result) => !!result));
|
|
127
|
+
}
|
|
128
|
+
|
|
123
129
|
constructor(id: string, config: IORoomConfig<TPlatform, TAuthorize, TContext, TEvents>) {
|
|
124
|
-
const { authorize, context, crdt = noop, debug, onDestroy, platform, router } = config;
|
|
130
|
+
const { authorize, context, crdt = noop, debug, onDestroy, onMessage, platform, router } = config;
|
|
131
|
+
|
|
132
|
+
this.id = id;
|
|
125
133
|
|
|
126
134
|
this._context = context;
|
|
127
135
|
this._debug = debug;
|
|
128
136
|
this._docFactory = crdt.doc(() => ({}));
|
|
129
|
-
this._doc = this._docFactory.getEmpty();
|
|
130
137
|
this._router = router;
|
|
131
138
|
this._platform = platform.initialize({ context });
|
|
132
139
|
|
|
133
|
-
this.id = id;
|
|
134
|
-
|
|
135
140
|
this._listeners = {
|
|
136
141
|
onDestroy: (event) => onDestroy?.(event),
|
|
142
|
+
onMessage: (event) => onMessage?.(event),
|
|
137
143
|
};
|
|
138
144
|
|
|
139
145
|
if (authorize) this._authorize = authorize;
|
|
140
146
|
|
|
141
147
|
const webSockets = this._platform.getWebSockets() as readonly InferPlatformWebSocketSource<TPlatform>[];
|
|
142
148
|
|
|
143
|
-
if (!webSockets.length) return;
|
|
144
|
-
|
|
145
149
|
webSockets.forEach((webSocket) => {
|
|
146
150
|
const deserialized = this._platform.getSerializedState(webSocket);
|
|
147
151
|
const sessionId = this._platform.getSessionId(webSocket);
|
|
@@ -149,8 +153,15 @@ export class IORoom<
|
|
|
149
153
|
if (!deserialized) return;
|
|
150
154
|
if (typeof sessionId !== "string") return;
|
|
151
155
|
|
|
152
|
-
|
|
156
|
+
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
157
|
+
|
|
158
|
+
this._sessions.set(sessionId, pluvWs);
|
|
153
159
|
});
|
|
160
|
+
|
|
161
|
+
const { doc, uninitialize } = this._initialize();
|
|
162
|
+
|
|
163
|
+
this._doc = doc;
|
|
164
|
+
this._uninitialize = uninitialize;
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
public getSize(): number {
|
|
@@ -171,32 +182,32 @@ export class IORoom<
|
|
|
171
182
|
}, 0);
|
|
172
183
|
}
|
|
173
184
|
|
|
174
|
-
public onClose(webSocket: WebSocketType<TPlatform>): (event: AbstractCloseEvent) => void {
|
|
185
|
+
public onClose(webSocket: WebSocketType<TPlatform>): (event: AbstractCloseEvent) => Promise<void> {
|
|
175
186
|
this._ensureDetached();
|
|
176
187
|
|
|
177
188
|
const wsSession = this._getAbstractWs(webSocket);
|
|
178
189
|
|
|
179
|
-
if (!wsSession) return ()
|
|
190
|
+
if (!wsSession) return async () => undefined;
|
|
180
191
|
|
|
181
192
|
return this._onClose(wsSession);
|
|
182
193
|
}
|
|
183
194
|
|
|
184
|
-
public onError(webSocket: WebSocketType<TPlatform>): (event: AbstractErrorEvent) => void {
|
|
195
|
+
public onError(webSocket: WebSocketType<TPlatform>): (event: AbstractErrorEvent) => Promise<void> {
|
|
185
196
|
this._ensureDetached();
|
|
186
197
|
|
|
187
198
|
const wsSession = this._getAbstractWs(webSocket);
|
|
188
199
|
|
|
189
|
-
if (!wsSession) return ()
|
|
200
|
+
if (!wsSession) return async () => undefined;
|
|
190
201
|
|
|
191
202
|
return this._onClose(wsSession);
|
|
192
203
|
}
|
|
193
204
|
|
|
194
|
-
public onMessage(webSocket: WebSocketType<TPlatform>): (event: AbstractMessageEvent) => void {
|
|
205
|
+
public onMessage(webSocket: WebSocketType<TPlatform>): (event: AbstractMessageEvent) => Promise<void> {
|
|
195
206
|
this._ensureDetached();
|
|
196
207
|
|
|
197
208
|
const wsSession = this._getAbstractWs(webSocket);
|
|
198
209
|
|
|
199
|
-
if (!wsSession) return ()
|
|
210
|
+
if (!wsSession) return async () => undefined;
|
|
200
211
|
|
|
201
212
|
return this._onMessage(wsSession);
|
|
202
213
|
}
|
|
@@ -207,7 +218,15 @@ export class IORoom<
|
|
|
207
218
|
): Promise<void> {
|
|
208
219
|
const { token } = options;
|
|
209
220
|
|
|
210
|
-
|
|
221
|
+
const sessionId = this._platform.getSessionId(webSocket);
|
|
222
|
+
const sessionExists = typeof sessionId === "string" && this._sessions.has(sessionId);
|
|
223
|
+
|
|
224
|
+
if (sessionExists) return;
|
|
225
|
+
if (!(await this._initialized)) {
|
|
226
|
+
this._initialize();
|
|
227
|
+
|
|
228
|
+
await this._initialized;
|
|
229
|
+
}
|
|
211
230
|
|
|
212
231
|
const user = await this._getAuthorizedUser(token);
|
|
213
232
|
const ioAuthorize = this._getIOAuthorize();
|
|
@@ -218,7 +237,7 @@ export class IORoom<
|
|
|
218
237
|
!!user &&
|
|
219
238
|
(await this._getSessions().then((sessions) => sessions.some((session) => session.user?.id === user.id)));
|
|
220
239
|
|
|
221
|
-
const pluvWs =
|
|
240
|
+
const pluvWs = this._platform.convertWebSocket(webSocket, { room: this.id });
|
|
222
241
|
|
|
223
242
|
this._logDebug(`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`);
|
|
224
243
|
|
|
@@ -323,14 +342,6 @@ export class IORoom<
|
|
|
323
342
|
return sessions.find((pluvWs) => pluvWs.webSocket === webSocket) ?? null;
|
|
324
343
|
}
|
|
325
344
|
|
|
326
|
-
private async _getSession(webSocket: WebSocketType<TPlatform>): Promise<WebSocketSession<TAuthorize>> {
|
|
327
|
-
const pluvWs = this._getAbstractWs(webSocket);
|
|
328
|
-
|
|
329
|
-
if (!pluvWs) throw new Error("Session could not be found");
|
|
330
|
-
|
|
331
|
-
return await pluvWs.getSession<TAuthorize>();
|
|
332
|
-
}
|
|
333
|
-
|
|
334
345
|
private async _getAuthorizedUser(token: Maybe<string>): Promise<InferIOAuthorizeUser<InferIOAuthorize<this>>> {
|
|
335
346
|
const ioAuthorize = this._getIOAuthorize();
|
|
336
347
|
|
|
@@ -366,6 +377,15 @@ export class IORoom<
|
|
|
366
377
|
}
|
|
367
378
|
}
|
|
368
379
|
|
|
380
|
+
private async _getInitialDoc(): Promise<AbstractCrdtDoc<any>> {
|
|
381
|
+
const doc = this._docFactory.getEmpty();
|
|
382
|
+
const encodedState = await this._platform.persistance.getStorageState(this.id);
|
|
383
|
+
|
|
384
|
+
if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
|
|
385
|
+
|
|
386
|
+
return doc;
|
|
387
|
+
}
|
|
388
|
+
|
|
369
389
|
private _getIOAuthorize() {
|
|
370
390
|
if (typeof this._authorize === "function") {
|
|
371
391
|
return this._authorize(this._context);
|
|
@@ -392,58 +412,97 @@ export class IORoom<
|
|
|
392
412
|
return procedure.config.input ? procedure.config.input.parse(message.data) : message.data;
|
|
393
413
|
}
|
|
394
414
|
|
|
415
|
+
private async _getSession(webSocket: WebSocketType<TPlatform>): Promise<WebSocketSession<TAuthorize>> {
|
|
416
|
+
const pluvWs = this._getAbstractWs(webSocket);
|
|
417
|
+
|
|
418
|
+
if (!pluvWs) throw new Error("Session could not be found");
|
|
419
|
+
|
|
420
|
+
return await pluvWs.getSession<TAuthorize>();
|
|
421
|
+
}
|
|
422
|
+
|
|
395
423
|
private async _getSessions() {
|
|
396
424
|
const promises = Array.from(this._sessions.values()).map(async (pluvWs) => await pluvWs.getSession());
|
|
397
425
|
|
|
398
426
|
return await Promise.all(promises);
|
|
399
427
|
}
|
|
400
428
|
|
|
401
|
-
private
|
|
402
|
-
|
|
429
|
+
private _initialize() {
|
|
430
|
+
const promise = (async () => {
|
|
431
|
+
this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
|
|
403
432
|
|
|
404
|
-
|
|
405
|
-
|
|
433
|
+
if (!!this._uninitialize) {
|
|
434
|
+
const uninitialize = (await this._uninitialize).bind(this);
|
|
435
|
+
await uninitialize();
|
|
406
436
|
|
|
407
|
-
|
|
408
|
-
|
|
437
|
+
this._uninitialize = null;
|
|
438
|
+
}
|
|
409
439
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
440
|
+
const pubSubId = await this._platform.pubSub.subscribe(
|
|
441
|
+
this.id,
|
|
442
|
+
async ({ options = {}, ...message }): Promise<void> => {
|
|
443
|
+
const sender: SendMessageSender = {
|
|
444
|
+
sessionId: message.connectionId,
|
|
445
|
+
user: message.user,
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
switch (options.type) {
|
|
449
|
+
case "self": {
|
|
450
|
+
await this._sendSelfMessage(message, sender);
|
|
451
|
+
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
case "sync": {
|
|
455
|
+
await this._sendSyncMessage(message, sender);
|
|
456
|
+
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
case "broadcast":
|
|
460
|
+
default: {
|
|
461
|
+
const sessionIds = options.sessionIds;
|
|
462
|
+
|
|
463
|
+
await this._sendBroadcastMessage(message, sender, sessionIds);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
);
|
|
415
468
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
469
|
+
const doc = await this._getInitialDoc();
|
|
470
|
+
const uninitialize = async () => {
|
|
471
|
+
this._platform.pubSub.unsubscribe(pubSubId);
|
|
419
472
|
|
|
420
|
-
|
|
421
|
-
|
|
473
|
+
const doc = await this._doc;
|
|
474
|
+
const encodedState = doc.getEncodedState();
|
|
422
475
|
|
|
423
|
-
|
|
476
|
+
doc.destroy();
|
|
477
|
+
this._doc = Promise.resolve(this._docFactory.getEmpty());
|
|
424
478
|
|
|
425
|
-
|
|
426
|
-
|
|
479
|
+
this._listeners.onDestroy({
|
|
480
|
+
context: this._context,
|
|
481
|
+
encodedState,
|
|
482
|
+
room: this.id,
|
|
483
|
+
});
|
|
427
484
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
encodedState,
|
|
431
|
-
room: this.id,
|
|
432
|
-
});
|
|
485
|
+
this._uninitialize = null;
|
|
486
|
+
};
|
|
433
487
|
|
|
434
|
-
|
|
435
|
-
};
|
|
488
|
+
return { doc, uninitialize };
|
|
489
|
+
})();
|
|
490
|
+
|
|
491
|
+
this._doc = promise.then((result) => result.doc);
|
|
492
|
+
this._uninitialize = promise.then((result) => result.uninitialize);
|
|
493
|
+
|
|
494
|
+
return { doc: this._doc, uninitialize: this._uninitialize };
|
|
436
495
|
}
|
|
437
496
|
|
|
438
497
|
private _logDebug(...data: any[]): void {
|
|
439
498
|
this._debug && console.log(...data);
|
|
440
499
|
}
|
|
441
500
|
|
|
442
|
-
private _onClose(webSocket: AbstractWebSocket): () => void {
|
|
443
|
-
return (): void => {
|
|
444
|
-
if (!this.
|
|
501
|
+
private _onClose(webSocket: AbstractWebSocket): () => Promise<void> {
|
|
502
|
+
return async (): Promise<void> => {
|
|
503
|
+
if (!(await this._initialized)) return;
|
|
445
504
|
|
|
446
|
-
webSocket.state.quit
|
|
505
|
+
webSocket.state = { ...webSocket.state, quit: true };
|
|
447
506
|
|
|
448
507
|
this._logDebug(`${colors.blue(`Unregistering connection for room ${this.id}:`)} ${webSocket.sessionId}`);
|
|
449
508
|
this._sessions.delete(webSocket.sessionId);
|
|
@@ -464,13 +523,18 @@ export class IORoom<
|
|
|
464
523
|
|
|
465
524
|
if (size) return;
|
|
466
525
|
|
|
467
|
-
this._uninitialize
|
|
526
|
+
if (this._uninitialize) {
|
|
527
|
+
const uninitialize = (await this._uninitialize).bind(this);
|
|
528
|
+
await uninitialize();
|
|
529
|
+
}
|
|
468
530
|
});
|
|
469
531
|
};
|
|
470
532
|
}
|
|
471
533
|
|
|
472
|
-
private _onMessage(webSocket: AbstractWebSocket): (event: AbstractMessageEvent) => void {
|
|
534
|
+
private _onMessage(webSocket: AbstractWebSocket): (event: AbstractMessageEvent) => Promise<void> {
|
|
473
535
|
return async (event: AbstractMessageEvent): Promise<void> => {
|
|
536
|
+
if (!(await this._initialized)) return;
|
|
537
|
+
|
|
474
538
|
const pluvWs = this._getAbstractWs(webSocket as WebSocketType<TPlatform>);
|
|
475
539
|
|
|
476
540
|
if (!pluvWs) throw new Error("Could not get session");
|
|
@@ -480,16 +544,15 @@ export class IORoom<
|
|
|
480
544
|
this._getSessions(),
|
|
481
545
|
]);
|
|
482
546
|
|
|
547
|
+
const doc = await this._doc;
|
|
483
548
|
const baseContext: EventResolverContext<TPlatform, TAuthorize, TContext> = {
|
|
484
549
|
context: this._context,
|
|
485
|
-
doc
|
|
550
|
+
doc,
|
|
486
551
|
room: this.id,
|
|
487
552
|
session,
|
|
488
553
|
sessions,
|
|
489
554
|
};
|
|
490
555
|
|
|
491
|
-
if (!this._uninitialize) return;
|
|
492
|
-
|
|
493
556
|
if (pluvWs.state.quit) {
|
|
494
557
|
pluvWs.close(1011, "WebSocket broken.");
|
|
495
558
|
|
|
@@ -525,7 +588,7 @@ export class IORoom<
|
|
|
525
588
|
TContext & InferRoomContextType<TPlatform>
|
|
526
589
|
> = { ...baseContext, context: { ...this._context } };
|
|
527
590
|
|
|
528
|
-
Promise.all([
|
|
591
|
+
await Promise.all([
|
|
529
592
|
procedure.config.broadcast?.(inputs, extendedContext),
|
|
530
593
|
procedure.config.self?.(inputs, extendedContext),
|
|
531
594
|
procedure.config.sync?.(inputs, baseContext),
|
|
@@ -533,25 +596,33 @@ export class IORoom<
|
|
|
533
596
|
const sessionId = session.id;
|
|
534
597
|
const user = session.user;
|
|
535
598
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
599
|
+
const handleBroadcast = async () => {
|
|
600
|
+
if (!broadcast) return;
|
|
601
|
+
|
|
602
|
+
await Promise.all(
|
|
603
|
+
Object.entries(broadcast).map(async ([type, data]: any) => {
|
|
604
|
+
await this._broadcast({
|
|
605
|
+
message: { data, type },
|
|
606
|
+
senderId: sessionId,
|
|
607
|
+
});
|
|
608
|
+
}),
|
|
609
|
+
);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
const handleSelf = async () => {
|
|
613
|
+
if (!self) return;
|
|
544
614
|
|
|
545
|
-
if (self) {
|
|
546
615
|
await Promise.all(
|
|
547
616
|
Object.entries(self).map(async ([type, data]) => {
|
|
548
617
|
await this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
549
618
|
}),
|
|
550
619
|
);
|
|
551
|
-
}
|
|
620
|
+
};
|
|
552
621
|
|
|
553
|
-
|
|
554
|
-
|
|
622
|
+
const handleSync = async () => {
|
|
623
|
+
if (!sync) return;
|
|
624
|
+
|
|
625
|
+
await this._platform.pubSub.publish(this.id, {
|
|
555
626
|
connectionId: session.id,
|
|
556
627
|
options: { type: "sync" },
|
|
557
628
|
room: this.id,
|
|
@@ -564,7 +635,9 @@ export class IORoom<
|
|
|
564
635
|
await this._sendSelfMessage({ data, type }, { sessionId, user });
|
|
565
636
|
}),
|
|
566
637
|
);
|
|
567
|
-
}
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
await Promise.all([handleBroadcast(), handleSelf(), handleSync()]);
|
|
568
641
|
});
|
|
569
642
|
};
|
|
570
643
|
}
|
|
@@ -583,29 +656,22 @@ export class IORoom<
|
|
|
583
656
|
}
|
|
584
657
|
}
|
|
585
658
|
|
|
586
|
-
private async _sendMessage(
|
|
587
|
-
|
|
588
|
-
sender: SendMessageSender | null,
|
|
589
|
-
options: SendMessageOptions = {},
|
|
590
|
-
): Promise<void> {
|
|
591
|
-
switch (options.type) {
|
|
592
|
-
case "self": {
|
|
593
|
-
await this._sendSelfMessage(message, sender);
|
|
659
|
+
private async _sendMessage(pluvWs: AbstractWebSocket<any>, message: IOEventMessage<any>): Promise<void> {
|
|
660
|
+
const { data, type } = message;
|
|
594
661
|
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
case "sync": {
|
|
598
|
-
await this._sendSyncMessage(message, sender);
|
|
662
|
+
if (!(await this._initialized)) return;
|
|
599
663
|
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
case "broadcast":
|
|
603
|
-
default: {
|
|
604
|
-
const sessionIds = options.sessionIds;
|
|
664
|
+
const doc = await this._doc;
|
|
605
665
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
666
|
+
this._listeners.onMessage({
|
|
667
|
+
context: this._context,
|
|
668
|
+
encodedState: doc.getEncodedState(),
|
|
669
|
+
message: { data, type } as InferEventMessage<InferEventsOutput<TEvents>, keyof InferEventsOutput<TEvents>>,
|
|
670
|
+
room: this.id,
|
|
671
|
+
user: message.user,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
await Promise.resolve(pluvWs.sendMessage(message));
|
|
609
675
|
}
|
|
610
676
|
|
|
611
677
|
private async _sendBroadcastMessage(
|
|
@@ -628,7 +694,7 @@ export class IORoom<
|
|
|
628
694
|
const session = await pluvWs.getSession();
|
|
629
695
|
const user = session.user ?? null;
|
|
630
696
|
|
|
631
|
-
|
|
697
|
+
this._sendMessage(pluvWs, { connectionId, data, room, type, user });
|
|
632
698
|
});
|
|
633
699
|
|
|
634
700
|
await Promise.all(promises);
|
|
@@ -650,7 +716,7 @@ export class IORoom<
|
|
|
650
716
|
const session = await pluvWs.getSession();
|
|
651
717
|
const user = session.user ?? null;
|
|
652
718
|
|
|
653
|
-
|
|
719
|
+
this._sendMessage(pluvWs, {
|
|
654
720
|
connectionId: senderId,
|
|
655
721
|
room: this.id,
|
|
656
722
|
user,
|
|
@@ -664,9 +730,10 @@ export class IORoom<
|
|
|
664
730
|
): Promise<void> {
|
|
665
731
|
if (!sender) return;
|
|
666
732
|
|
|
733
|
+
const doc = await this._doc;
|
|
667
734
|
const context: EventResolverContext<TPlatform, TAuthorize, TContext> = {
|
|
668
735
|
context: this._context,
|
|
669
|
-
doc
|
|
736
|
+
doc,
|
|
670
737
|
room: this.id,
|
|
671
738
|
session: null,
|
|
672
739
|
sessions: await this._getSessions(),
|