@pluv/io 0.3.9 → 0.4.1
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 +7 -7
- package/CHANGELOG.md +28 -0
- package/dist/index.d.ts +33 -30
- package/dist/index.js +196 -189
- package/dist/index.mjs +196 -189
- package/package.json +9 -9
- package/src/IORoom.ts +242 -212
- package/src/PluvIO.ts +191 -61
- package/src/types.ts +38 -7
package/src/IORoom.ts
CHANGED
|
@@ -15,16 +15,20 @@ import type {
|
|
|
15
15
|
JsonObject,
|
|
16
16
|
Maybe,
|
|
17
17
|
MaybePromise,
|
|
18
|
+
Spread,
|
|
18
19
|
} from "@pluv/types";
|
|
19
20
|
import colors from "kleur";
|
|
20
21
|
import type { AbstractPlatform, InferWebSocketType } from "./AbstractPlatform";
|
|
21
22
|
import { authorize } from "./authorize";
|
|
22
23
|
import type {
|
|
24
|
+
EventResolver,
|
|
23
25
|
EventResolverContext,
|
|
26
|
+
EventResolverObject,
|
|
24
27
|
InferEventConfig,
|
|
25
28
|
SendMessageOptions,
|
|
26
29
|
WebSocketSession,
|
|
27
30
|
} from "./types";
|
|
31
|
+
import { AbstractMessageEvent } from "./AbstractWebSocket";
|
|
28
32
|
|
|
29
33
|
const PING_TIMEOUT_MS = 30_000;
|
|
30
34
|
|
|
@@ -46,13 +50,20 @@ export type IORoomConfig<
|
|
|
46
50
|
TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize,
|
|
47
51
|
TContext extends JsonObject = {},
|
|
48
52
|
TInput extends EventRecord<string, any> = {},
|
|
49
|
-
|
|
53
|
+
TOutputBroadcast extends EventRecord<string, any> = {},
|
|
54
|
+
TOutputSelf extends EventRecord<string, any> = {},
|
|
55
|
+
TOutputSync extends EventRecord<string, any> = {}
|
|
50
56
|
> = Partial<IORoomListeners> & {
|
|
51
57
|
authorize?: TAuthorize;
|
|
52
58
|
context: TContext;
|
|
53
59
|
debug: boolean;
|
|
54
|
-
events: InferEventConfig<
|
|
55
|
-
|
|
60
|
+
events: InferEventConfig<
|
|
61
|
+
TContext,
|
|
62
|
+
TInput,
|
|
63
|
+
TOutputBroadcast,
|
|
64
|
+
TOutputSelf,
|
|
65
|
+
TOutputSync
|
|
66
|
+
>;
|
|
56
67
|
platform: TPlatform;
|
|
57
68
|
};
|
|
58
69
|
|
|
@@ -62,7 +73,7 @@ interface SendMessageSender {
|
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
export interface WebsocketRegisterOptions {
|
|
65
|
-
token?: string;
|
|
76
|
+
token?: string | null;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
export class IORoom<
|
|
@@ -70,37 +81,46 @@ export class IORoom<
|
|
|
70
81
|
TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize,
|
|
71
82
|
TContext extends JsonObject = {},
|
|
72
83
|
TInput extends EventRecord<string, any> = {},
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
TOutputBroadcast extends EventRecord<string, any> = {},
|
|
85
|
+
TOutputSelf extends EventRecord<string, any> = {},
|
|
86
|
+
TOutputSync extends EventRecord<string, any> = {}
|
|
87
|
+
> implements
|
|
88
|
+
IOLike<TAuthorize, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>
|
|
75
89
|
{
|
|
76
90
|
private readonly _context: TContext;
|
|
77
91
|
private readonly _debug: boolean;
|
|
78
92
|
private readonly _platform: TPlatform;
|
|
79
93
|
|
|
80
94
|
private _doc: YjsDoc<any> = doc();
|
|
81
|
-
private _initialStorage: (() => MaybePromise<Maybe<string>>) | null = null;
|
|
82
95
|
private _listeners: IORoomListeners;
|
|
83
96
|
private _sessions = new Map<string, WebSocketSession>();
|
|
84
97
|
private _uninitialize: (() => Promise<void>) | null = null;
|
|
85
98
|
|
|
86
99
|
readonly _authorize: TAuthorize | null = null;
|
|
87
|
-
readonly _events: InferEventConfig<
|
|
100
|
+
readonly _events: InferEventConfig<
|
|
101
|
+
TContext,
|
|
102
|
+
TInput,
|
|
103
|
+
TOutputBroadcast,
|
|
104
|
+
TOutputSelf,
|
|
105
|
+
TOutputSync
|
|
106
|
+
>;
|
|
88
107
|
|
|
89
108
|
readonly id: string;
|
|
90
109
|
|
|
91
110
|
constructor(
|
|
92
111
|
id: string,
|
|
93
|
-
config: IORoomConfig<
|
|
112
|
+
config: IORoomConfig<
|
|
113
|
+
TPlatform,
|
|
114
|
+
TAuthorize,
|
|
115
|
+
TContext,
|
|
116
|
+
TInput,
|
|
117
|
+
TOutputBroadcast,
|
|
118
|
+
TOutputSelf,
|
|
119
|
+
TOutputSync
|
|
120
|
+
>
|
|
94
121
|
) {
|
|
95
|
-
const {
|
|
96
|
-
|
|
97
|
-
context,
|
|
98
|
-
debug,
|
|
99
|
-
events,
|
|
100
|
-
initialStorage,
|
|
101
|
-
onDestroy,
|
|
102
|
-
platform,
|
|
103
|
-
} = config;
|
|
122
|
+
const { authorize, context, debug, events, onDestroy, platform } =
|
|
123
|
+
config;
|
|
104
124
|
|
|
105
125
|
this._context = context;
|
|
106
126
|
this._debug = debug;
|
|
@@ -116,7 +136,6 @@ export class IORoom<
|
|
|
116
136
|
};
|
|
117
137
|
|
|
118
138
|
if (authorize) this._authorize = authorize;
|
|
119
|
-
if (initialStorage) this._initialStorage = initialStorage;
|
|
120
139
|
}
|
|
121
140
|
|
|
122
141
|
public broadcast<TEvent extends keyof InferIOInput<this>>(
|
|
@@ -210,120 +229,14 @@ export class IORoom<
|
|
|
210
229
|
user ?? {}
|
|
211
230
|
);
|
|
212
231
|
|
|
213
|
-
this.
|
|
214
|
-
this.
|
|
215
|
-
this._emitUserJoined(session);
|
|
216
|
-
|
|
217
|
-
const context: EventResolverContext<TContext> = {
|
|
218
|
-
context: this._context,
|
|
219
|
-
doc: this._doc,
|
|
220
|
-
room: this.id,
|
|
221
|
-
session,
|
|
222
|
-
sessions: this._sessions,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
pluvWs.addEventListener("message", (event) => {
|
|
226
|
-
if (!this._uninitialize) return;
|
|
227
|
-
|
|
228
|
-
if (session.quit) {
|
|
229
|
-
session.webSocket.close(1011, "WebSocket broken.");
|
|
230
|
-
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const message = this._parseMessage(event);
|
|
235
|
-
|
|
236
|
-
if (!message) return;
|
|
237
|
-
|
|
238
|
-
const eventConfig = this._getEventConfig(message);
|
|
239
|
-
|
|
240
|
-
if (!eventConfig) return;
|
|
241
|
-
|
|
242
|
-
this._resolveMessage(message, context).then((output) => {
|
|
243
|
-
if (!output) return;
|
|
244
|
-
|
|
245
|
-
const eventType = eventConfig.options?.type ?? "broadcast";
|
|
246
|
-
|
|
247
|
-
switch (eventType) {
|
|
248
|
-
case "self": {
|
|
249
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
250
|
-
this._sendSelfMessage({ data, type }, session);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
case "sync": {
|
|
256
|
-
this._platform.pubSub.publish(this.id, {
|
|
257
|
-
connectionId: session.id,
|
|
258
|
-
options: eventConfig.options,
|
|
259
|
-
room: this.id,
|
|
260
|
-
user: session.user,
|
|
261
|
-
...message,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
Object.entries(output).forEach(([type, data]) => {
|
|
265
|
-
this._sendSelfMessage({ data, type }, session);
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
case "broadcast":
|
|
271
|
-
default: {
|
|
272
|
-
Object.entries(output).forEach(([type, data]: any) => {
|
|
273
|
-
this._broadcast({
|
|
274
|
-
message: { data, type },
|
|
275
|
-
senderId: session.id,
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const closeHandler = () => {
|
|
284
|
-
if (!this._uninitialize) return;
|
|
285
|
-
|
|
286
|
-
session.quit = true;
|
|
287
|
-
|
|
288
|
-
this._logDebug(
|
|
289
|
-
`${colors.blue(
|
|
290
|
-
`(Unregistering connection for room ${this.id}:`
|
|
291
|
-
)} ${sessionId}`
|
|
292
|
-
);
|
|
293
|
-
this._sessions.delete(session.id);
|
|
294
|
-
|
|
295
|
-
this._platform.persistance
|
|
296
|
-
.deleteUser(this.id, session.id)
|
|
297
|
-
.finally(() => {
|
|
298
|
-
this._broadcast({
|
|
299
|
-
message: {
|
|
300
|
-
type: "$EXIT",
|
|
301
|
-
data: { sessionId: session.id },
|
|
302
|
-
},
|
|
303
|
-
senderId: session.id,
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
uninitializeWs();
|
|
232
|
+
const onClose = this._onClose(session, uninitializeWs).bind(this);
|
|
233
|
+
const onMessage = this._onMessage(session).bind(this);
|
|
307
234
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
`${colors.blue(
|
|
312
|
-
`Unregistered connection for room ${this.id}:`
|
|
313
|
-
)} ${sessionId}`
|
|
314
|
-
);
|
|
315
|
-
this._logDebug(
|
|
316
|
-
`${colors.blue(`Room ${this.id} size:`)} ${size}`
|
|
317
|
-
);
|
|
235
|
+
pluvWs.addEventListener("close", onClose);
|
|
236
|
+
pluvWs.addEventListener("error", onClose);
|
|
237
|
+
pluvWs.addEventListener("message", onMessage);
|
|
318
238
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
this._uninitialize?.();
|
|
322
|
-
});
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
pluvWs.addEventListener("close", closeHandler);
|
|
326
|
-
pluvWs.addEventListener("error", closeHandler);
|
|
239
|
+
this._emitRegistered(session);
|
|
327
240
|
|
|
328
241
|
this._logDebug(
|
|
329
242
|
`${colors.blue(
|
|
@@ -384,44 +297,8 @@ export class IORoom<
|
|
|
384
297
|
);
|
|
385
298
|
}
|
|
386
299
|
|
|
387
|
-
private _emitStorageReceived(session: WebSocketSession): void {
|
|
388
|
-
this._platform.persistance
|
|
389
|
-
.getStorageState(this.id)
|
|
390
|
-
.then(async (_state) => {
|
|
391
|
-
const state =
|
|
392
|
-
_state ??
|
|
393
|
-
(await this._initialStorage?.()) ??
|
|
394
|
-
this._doc.encodeStateAsUpdate();
|
|
395
|
-
|
|
396
|
-
this._sendSelfMessage(
|
|
397
|
-
{
|
|
398
|
-
type: "$STORAGE_RECEIVED",
|
|
399
|
-
data: { state },
|
|
400
|
-
},
|
|
401
|
-
session
|
|
402
|
-
);
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
private _emitUserJoined(session: WebSocketSession): void {
|
|
407
|
-
const user = session.user as any;
|
|
408
|
-
|
|
409
|
-
if (!user) return;
|
|
410
|
-
|
|
411
|
-
this._broadcast({
|
|
412
|
-
message: {
|
|
413
|
-
type: "$USER_JOINED",
|
|
414
|
-
data: {
|
|
415
|
-
connectionId: session.id,
|
|
416
|
-
user,
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
senderId: session.id,
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
300
|
private async _getAuthorizedUser(
|
|
424
|
-
token: string
|
|
301
|
+
token: Maybe<string>
|
|
425
302
|
): Promise<InferIOAuthorizeUser<InferIOAuthorize<this>> | null> {
|
|
426
303
|
if (!this._authorize) return null;
|
|
427
304
|
if (!token) return null;
|
|
@@ -461,10 +338,66 @@ export class IORoom<
|
|
|
461
338
|
|
|
462
339
|
private _getEventConfig(
|
|
463
340
|
message: EventMessage<string, any>
|
|
464
|
-
):
|
|
341
|
+
):
|
|
342
|
+
| InferEventConfig<
|
|
343
|
+
TContext,
|
|
344
|
+
TInput,
|
|
345
|
+
TOutputBroadcast,
|
|
346
|
+
TOutputSelf,
|
|
347
|
+
TOutputSync
|
|
348
|
+
>[keyof TInput]
|
|
349
|
+
| null {
|
|
465
350
|
return this._events[message.type as keyof TInput] ?? null;
|
|
466
351
|
}
|
|
467
352
|
|
|
353
|
+
private _getEventInputs(
|
|
354
|
+
message: EventMessage<string, any>
|
|
355
|
+
): TInput[string] {
|
|
356
|
+
const eventConfig = this._getEventConfig(message);
|
|
357
|
+
|
|
358
|
+
if (!eventConfig) return message.data;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* !HACK
|
|
362
|
+
* @description If the config doesn't specify validation, we
|
|
363
|
+
* assume no input is to be expected, but pass through all values
|
|
364
|
+
* regardless. Leave this up to the implementer of the event.
|
|
365
|
+
* @author leedavidcs
|
|
366
|
+
* @date September 25, 2022
|
|
367
|
+
*/
|
|
368
|
+
return eventConfig.input
|
|
369
|
+
? (eventConfig.input.parse(message.data) as TInput[string])
|
|
370
|
+
: message.data;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private _getEventResolverObject(
|
|
374
|
+
message: EventMessage<string, any>
|
|
375
|
+
): EventResolverObject<
|
|
376
|
+
TContext,
|
|
377
|
+
TInput[string],
|
|
378
|
+
TOutputBroadcast,
|
|
379
|
+
TOutputSelf,
|
|
380
|
+
TOutputSync
|
|
381
|
+
> {
|
|
382
|
+
const eventConfig = this._getEventConfig(message);
|
|
383
|
+
|
|
384
|
+
if (!eventConfig) return {};
|
|
385
|
+
|
|
386
|
+
const resolver:
|
|
387
|
+
| EventResolver<TContext, TInput[string], TOutputBroadcast>
|
|
388
|
+
| EventResolverObject<
|
|
389
|
+
TContext,
|
|
390
|
+
TInput[string],
|
|
391
|
+
TOutputBroadcast,
|
|
392
|
+
TOutputSelf,
|
|
393
|
+
TOutputSync
|
|
394
|
+
> = eventConfig.resolver;
|
|
395
|
+
|
|
396
|
+
return typeof resolver === "function"
|
|
397
|
+
? { broadcast: resolver }
|
|
398
|
+
: { ...resolver };
|
|
399
|
+
}
|
|
400
|
+
|
|
468
401
|
private async _initialize(): Promise<void> {
|
|
469
402
|
this._logDebug(`${colors.blue(`Initializing room ${this.id}:`)}`);
|
|
470
403
|
|
|
@@ -504,6 +437,130 @@ export class IORoom<
|
|
|
504
437
|
this._debug && console.log(...data);
|
|
505
438
|
}
|
|
506
439
|
|
|
440
|
+
private _onClose(
|
|
441
|
+
session: WebSocketSession,
|
|
442
|
+
callback?: () => void
|
|
443
|
+
): () => void {
|
|
444
|
+
return (): void => {
|
|
445
|
+
if (!this._uninitialize) return;
|
|
446
|
+
|
|
447
|
+
session.quit = true;
|
|
448
|
+
|
|
449
|
+
this._logDebug(
|
|
450
|
+
`${colors.blue(
|
|
451
|
+
`(Unregistering connection for room ${this.id}:`
|
|
452
|
+
)} ${session.id}`
|
|
453
|
+
);
|
|
454
|
+
this._sessions.delete(session.id);
|
|
455
|
+
|
|
456
|
+
this._platform.persistance
|
|
457
|
+
.deleteUser(this.id, session.id)
|
|
458
|
+
.finally(() => {
|
|
459
|
+
this._broadcast({
|
|
460
|
+
message: {
|
|
461
|
+
type: "$EXIT",
|
|
462
|
+
data: { sessionId: session.id },
|
|
463
|
+
},
|
|
464
|
+
senderId: session.id,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
callback?.();
|
|
468
|
+
|
|
469
|
+
const size = this.getSize();
|
|
470
|
+
|
|
471
|
+
this._logDebug(
|
|
472
|
+
`${colors.blue(
|
|
473
|
+
`Unregistered connection for room ${this.id}:`
|
|
474
|
+
)} ${session.id}`
|
|
475
|
+
);
|
|
476
|
+
this._logDebug(
|
|
477
|
+
`${colors.blue(`Room ${this.id} size:`)} ${size}`
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (size) return;
|
|
481
|
+
|
|
482
|
+
this._uninitialize?.();
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private _onMessage(
|
|
488
|
+
session: WebSocketSession
|
|
489
|
+
): (event: AbstractMessageEvent) => void {
|
|
490
|
+
return (event: AbstractMessageEvent): void => {
|
|
491
|
+
const context: EventResolverContext<TContext> = {
|
|
492
|
+
context: this._context,
|
|
493
|
+
doc: this._doc,
|
|
494
|
+
room: this.id,
|
|
495
|
+
session,
|
|
496
|
+
sessions: this._sessions,
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
if (!this._uninitialize) return;
|
|
500
|
+
|
|
501
|
+
if (session.quit) {
|
|
502
|
+
session.webSocket.close(1011, "WebSocket broken.");
|
|
503
|
+
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const message = this._parseMessage(event);
|
|
508
|
+
|
|
509
|
+
if (!message) return;
|
|
510
|
+
|
|
511
|
+
const resolver = this._getEventResolverObject(message);
|
|
512
|
+
|
|
513
|
+
let inputs: TInput[string];
|
|
514
|
+
|
|
515
|
+
try {
|
|
516
|
+
inputs = this._getEventInputs(message);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
session?.webSocket.handleError({
|
|
519
|
+
error,
|
|
520
|
+
message: "Invalid input",
|
|
521
|
+
session,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
Promise.all([
|
|
528
|
+
resolver.broadcast?.(inputs, context),
|
|
529
|
+
resolver.self?.(inputs, context),
|
|
530
|
+
resolver.sync?.(inputs, context),
|
|
531
|
+
]).then(([broadcast, self, sync]) => {
|
|
532
|
+
if (broadcast) {
|
|
533
|
+
Object.entries(broadcast).forEach(([type, data]: any) => {
|
|
534
|
+
this._broadcast({
|
|
535
|
+
message: { data, type },
|
|
536
|
+
senderId: session.id,
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (self) {
|
|
542
|
+
Object.entries(self).forEach(([type, data]) => {
|
|
543
|
+
this._sendSelfMessage({ data, type }, session);
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (sync) {
|
|
548
|
+
this._platform.pubSub.publish(this.id, {
|
|
549
|
+
connectionId: session.id,
|
|
550
|
+
options: { type: "sync" },
|
|
551
|
+
room: this.id,
|
|
552
|
+
user: session.user,
|
|
553
|
+
...message,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
Object.entries(sync).forEach(([type, data]) => {
|
|
557
|
+
this._sendSelfMessage({ data, type }, session);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
507
564
|
private _parseMessage(message: {
|
|
508
565
|
data: string | ArrayBuffer;
|
|
509
566
|
}): EventMessage<string, any> | null {
|
|
@@ -520,47 +577,6 @@ export class IORoom<
|
|
|
520
577
|
}
|
|
521
578
|
}
|
|
522
579
|
|
|
523
|
-
private async _resolveMessage(
|
|
524
|
-
message: EventMessage<string, any>,
|
|
525
|
-
context: EventResolverContext<TContext>
|
|
526
|
-
): Promise<TOutput | null> {
|
|
527
|
-
const { session } = context;
|
|
528
|
-
|
|
529
|
-
const eventConfig = this._getEventConfig(message);
|
|
530
|
-
|
|
531
|
-
if (!eventConfig) return null;
|
|
532
|
-
|
|
533
|
-
let input: TInput[string];
|
|
534
|
-
|
|
535
|
-
/**
|
|
536
|
-
* !HACK
|
|
537
|
-
* @description If the config doesn't specify validation, we
|
|
538
|
-
* assume no input is to be expected, but pass through all values
|
|
539
|
-
* regardless. Leave this up to the implementer of the event.
|
|
540
|
-
* @author leedavidcs
|
|
541
|
-
* @date September 25, 2022
|
|
542
|
-
*/
|
|
543
|
-
try {
|
|
544
|
-
input = eventConfig.input
|
|
545
|
-
? (eventConfig.input.parse(message.data) as TInput[string])
|
|
546
|
-
: message.data;
|
|
547
|
-
} catch (error) {
|
|
548
|
-
session?.webSocket.handleError({
|
|
549
|
-
error,
|
|
550
|
-
message: "Invalid input",
|
|
551
|
-
session,
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
return null;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const output = await eventConfig.resolver(input, context);
|
|
558
|
-
|
|
559
|
-
if (!output) return null;
|
|
560
|
-
|
|
561
|
-
return output;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
580
|
private _sendMessage(
|
|
565
581
|
message: EventMessage<string, any>,
|
|
566
582
|
sender: SendMessageSender | null,
|
|
@@ -638,13 +654,27 @@ export class IORoom<
|
|
|
638
654
|
|
|
639
655
|
if (!senderId) return;
|
|
640
656
|
|
|
641
|
-
|
|
657
|
+
const context: EventResolverContext<TContext> = {
|
|
642
658
|
context: this._context,
|
|
643
659
|
doc: this._doc,
|
|
644
660
|
room: this.id,
|
|
645
661
|
session: null,
|
|
646
662
|
sessions: this._sessions,
|
|
647
|
-
}
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
const resolver = this._getEventResolverObject(message).sync;
|
|
666
|
+
|
|
667
|
+
if (!resolver) return;
|
|
668
|
+
|
|
669
|
+
let inputs: TInput[string];
|
|
670
|
+
|
|
671
|
+
try {
|
|
672
|
+
inputs = this._getEventInputs(message);
|
|
673
|
+
} catch {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
Promise.resolve(resolver(inputs, context)).then((output) => {
|
|
648
678
|
if (!output) return;
|
|
649
679
|
|
|
650
680
|
Object.keys(output).forEach((type: string) => {
|