@pluv/io 0.44.2 → 1.0.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 +9 -9
- package/CHANGELOG.md +24 -0
- package/README.md +137 -67
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +65 -31
- package/dist/index.mjs +65 -31
- package/package.json +5 -5
- package/src/AbstractPersistence.ts +8 -2
- package/src/AbstractPlatform.ts +6 -2
- package/src/AbstractWebSocket.ts +11 -3
- package/src/IORoom.ts +113 -37
- package/src/PluvIO.ts +38 -11
- package/src/PluvProcedure.ts +57 -10
- package/src/PluvRouter.ts +14 -3
- package/src/PluvServer.ts +98 -36
- package/src/PubSub.ts +4 -1
- package/src/authorize.ts +4 -1
- package/src/createIO.ts +10 -2
- package/src/types.ts +25 -11
- package/src/utils/pickBy.ts +4 -1
- package/src/version.generated.ts +1 -1
package/src/IORoom.ts
CHANGED
|
@@ -22,7 +22,11 @@ import type {
|
|
|
22
22
|
InferPlatformWebSocketSource,
|
|
23
23
|
InferRoomContextType,
|
|
24
24
|
} from "./AbstractPlatform";
|
|
25
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
AbstractCloseEvent,
|
|
27
|
+
AbstractErrorEvent,
|
|
28
|
+
AbstractMessageEvent,
|
|
29
|
+
} from "./AbstractWebSocket";
|
|
26
30
|
import { AbstractWebSocket } from "./AbstractWebSocket";
|
|
27
31
|
import type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
|
|
28
32
|
import { authorize } from "./authorize";
|
|
@@ -61,7 +65,9 @@ export interface IORoomListeners<
|
|
|
61
65
|
onUserDisconnected: (event: IOUserDisconnectedEvent<TPlatform, TAuthorize, TContext>) => void;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
export type BroadcastProxy<TIO extends IORoom<any, any, any, any>> = (<
|
|
68
|
+
export type BroadcastProxy<TIO extends IORoom<any, any, any, any>> = (<
|
|
69
|
+
TEvent extends keyof InferIOInput<TIO>,
|
|
70
|
+
>(
|
|
65
71
|
event: TEvent,
|
|
66
72
|
data: Id<InferIOInput<TIO>[TEvent]>,
|
|
67
73
|
) => Promise<void>) & {
|
|
@@ -70,7 +76,11 @@ export type BroadcastProxy<TIO extends IORoom<any, any, any, any>> = (<TEvent ex
|
|
|
70
76
|
|
|
71
77
|
export type IORoomConfig<
|
|
72
78
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
73
|
-
TAuthorize extends PluvIOAuthorize<
|
|
79
|
+
TAuthorize extends PluvIOAuthorize<
|
|
80
|
+
TPlatform,
|
|
81
|
+
any,
|
|
82
|
+
InferInitContextType<TPlatform>
|
|
83
|
+
> | null = any,
|
|
74
84
|
TContext extends Record<string, any> = {},
|
|
75
85
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
76
86
|
> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
|
|
@@ -94,13 +104,19 @@ interface PatchPresenceParams {
|
|
|
94
104
|
timer?: number | null;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
export type WebSocketRegisterConfig<
|
|
107
|
+
export type WebSocketRegisterConfig<
|
|
108
|
+
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
109
|
+
> = {
|
|
98
110
|
token?: string | null;
|
|
99
111
|
} & InferInitContextType<TPlatform>;
|
|
100
112
|
|
|
101
113
|
export class IORoom<
|
|
102
114
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
103
|
-
TAuthorize extends PluvIOAuthorize<
|
|
115
|
+
TAuthorize extends PluvIOAuthorize<
|
|
116
|
+
TPlatform,
|
|
117
|
+
any,
|
|
118
|
+
InferInitContextType<TPlatform>
|
|
119
|
+
> | null = null,
|
|
104
120
|
TContext extends Record<string, any> = {},
|
|
105
121
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
106
122
|
> implements IOLike<TAuthorize, TEvents>
|
|
@@ -118,7 +134,10 @@ export class IORoom<
|
|
|
118
134
|
private readonly _listeners: IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>;
|
|
119
135
|
private readonly _platform: TPlatform;
|
|
120
136
|
private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
121
|
-
private readonly _sessions = new Map<
|
|
137
|
+
private readonly _sessions = new Map<
|
|
138
|
+
[sessionId: string][0],
|
|
139
|
+
AbstractWebSocket<any, TAuthorize>
|
|
140
|
+
>();
|
|
122
141
|
private readonly _userSessionss = new Map<[userId: string][0], Set<[sessionId: string][0]>>();
|
|
123
142
|
|
|
124
143
|
/**
|
|
@@ -199,7 +218,8 @@ export class IORoom<
|
|
|
199
218
|
* @see https://developers.cloudflare.com/durable-objects/best-practices/websockets/#websocket-hibernation-api
|
|
200
219
|
* @date April 17, 2025
|
|
201
220
|
*/
|
|
202
|
-
const webSockets =
|
|
221
|
+
const webSockets =
|
|
222
|
+
this._platform.getWebSockets() as readonly InferPlatformWebSocketSource<TPlatform>[];
|
|
203
223
|
|
|
204
224
|
webSockets.forEach((webSocket) => {
|
|
205
225
|
const deserialized = this._platform.getSerializedState(webSocket);
|
|
@@ -276,7 +296,9 @@ export class IORoom<
|
|
|
276
296
|
}, 0);
|
|
277
297
|
}
|
|
278
298
|
|
|
279
|
-
public onClose(
|
|
299
|
+
public onClose(
|
|
300
|
+
webSocket: WebSocketType<TPlatform>,
|
|
301
|
+
): (event: AbstractCloseEvent) => Promise<void> {
|
|
280
302
|
this._ensureDetached();
|
|
281
303
|
|
|
282
304
|
const wsSession = this._getAbstractWs(webSocket);
|
|
@@ -286,7 +308,9 @@ export class IORoom<
|
|
|
286
308
|
return this._onClose(wsSession);
|
|
287
309
|
}
|
|
288
310
|
|
|
289
|
-
public onError(
|
|
311
|
+
public onError(
|
|
312
|
+
webSocket: WebSocketType<TPlatform>,
|
|
313
|
+
): (event: AbstractErrorEvent) => Promise<void> {
|
|
290
314
|
this._ensureDetached();
|
|
291
315
|
|
|
292
316
|
const wsSession = this._getAbstractWs(webSocket);
|
|
@@ -296,7 +320,9 @@ export class IORoom<
|
|
|
296
320
|
return this._onClose(wsSession);
|
|
297
321
|
}
|
|
298
322
|
|
|
299
|
-
public onMessage(
|
|
323
|
+
public onMessage(
|
|
324
|
+
webSocket: WebSocketType<TPlatform>,
|
|
325
|
+
): (event: AbstractMessageEvent) => Promise<void> {
|
|
300
326
|
this._ensureDetached();
|
|
301
327
|
|
|
302
328
|
const wsSession = this._getAbstractWs(webSocket);
|
|
@@ -358,7 +384,9 @@ export class IORoom<
|
|
|
358
384
|
this._addUserSession(user.id, pluvWs.sessionId);
|
|
359
385
|
}
|
|
360
386
|
|
|
361
|
-
this._logDebug(
|
|
387
|
+
this._logDebug(
|
|
388
|
+
`${colors.blue(`Registering connection for room ${this.id}:`)} ${pluvWs.sessionId}`,
|
|
389
|
+
);
|
|
362
390
|
|
|
363
391
|
await this._platform.acceptWebSocket(pluvWs);
|
|
364
392
|
this._sessions.set(pluvWs.sessionId, pluvWs);
|
|
@@ -377,7 +405,9 @@ export class IORoom<
|
|
|
377
405
|
|
|
378
406
|
const size = this.getSize();
|
|
379
407
|
|
|
380
|
-
this._logDebug(
|
|
408
|
+
this._logDebug(
|
|
409
|
+
`${colors.blue(`Registered connection for room ${this.id}:`)} ${pluvWs.sessionId}`,
|
|
410
|
+
);
|
|
381
411
|
this._logDebug(`${colors.blue(`Room ${this.id} size:`)} ${size}`);
|
|
382
412
|
}
|
|
383
413
|
|
|
@@ -407,13 +437,19 @@ export class IORoom<
|
|
|
407
437
|
});
|
|
408
438
|
}
|
|
409
439
|
|
|
410
|
-
private async _closeWebSockets(
|
|
411
|
-
|
|
440
|
+
private async _closeWebSockets(
|
|
441
|
+
webSockets: readonly AbstractWebSocket<any, TAuthorize>[],
|
|
442
|
+
): Promise<void> {
|
|
443
|
+
const closeWebSocket = async (
|
|
444
|
+
webSocket: AbstractWebSocket<any, TAuthorize>,
|
|
445
|
+
): Promise<void> => {
|
|
412
446
|
webSocket.state = { ...webSocket.state, quit: true };
|
|
413
447
|
|
|
414
448
|
const sessionId = webSocket.sessionId;
|
|
415
449
|
|
|
416
|
-
this._logDebug(
|
|
450
|
+
this._logDebug(
|
|
451
|
+
`${colors.blue(`Unregistering connection for room ${this.id}:`)} ${sessionId}`,
|
|
452
|
+
);
|
|
417
453
|
this._sessions.delete(sessionId);
|
|
418
454
|
|
|
419
455
|
await this._platform.persistence.deleteUser(this.id, sessionId).catch(() => null);
|
|
@@ -443,7 +479,9 @@ export class IORoom<
|
|
|
443
479
|
console.error(error);
|
|
444
480
|
}
|
|
445
481
|
|
|
446
|
-
this._logDebug(
|
|
482
|
+
this._logDebug(
|
|
483
|
+
`${colors.blue(`Unregistered connection for room ${this.id}:`)} ${webSocket.sessionId}`,
|
|
484
|
+
);
|
|
447
485
|
};
|
|
448
486
|
|
|
449
487
|
const promises = webSockets.map(async (webSocket) => {
|
|
@@ -465,9 +503,9 @@ export class IORoom<
|
|
|
465
503
|
|
|
466
504
|
private async _emitQuitters(): Promise<void> {
|
|
467
505
|
const currentTime = new Date().getTime();
|
|
468
|
-
const quitters = Array.from(this._sessions.values()).filter(
|
|
469
|
-
|
|
470
|
-
);
|
|
506
|
+
const quitters = Array.from(this._sessions.values()).filter((pluvWs) => {
|
|
507
|
+
return pluvWs.state.quit || currentTime - pluvWs.state.timers.ping > PING_TIMEOUT_MS;
|
|
508
|
+
});
|
|
471
509
|
|
|
472
510
|
this._closeWebSockets(quitters);
|
|
473
511
|
}
|
|
@@ -478,12 +516,16 @@ export class IORoom<
|
|
|
478
516
|
const presence = session.presence;
|
|
479
517
|
const user = session.user;
|
|
480
518
|
|
|
519
|
+
const doc = await this._doc;
|
|
520
|
+
const encodedState = doc.getEncodedState();
|
|
521
|
+
|
|
481
522
|
await this._sendSelfMessage(
|
|
482
523
|
{
|
|
483
524
|
type: "$registered",
|
|
484
525
|
data: {
|
|
485
526
|
presence,
|
|
486
527
|
sessionId,
|
|
528
|
+
state: encodedState,
|
|
487
529
|
timers: { presence: session.timers.presence },
|
|
488
530
|
},
|
|
489
531
|
},
|
|
@@ -491,9 +533,6 @@ export class IORoom<
|
|
|
491
533
|
);
|
|
492
534
|
|
|
493
535
|
try {
|
|
494
|
-
const doc = await this._doc;
|
|
495
|
-
const encodedState = doc.getEncodedState();
|
|
496
|
-
|
|
497
536
|
await Promise.resolve(
|
|
498
537
|
this._listeners.onUserConnected({
|
|
499
538
|
context: this._context,
|
|
@@ -509,7 +548,9 @@ export class IORoom<
|
|
|
509
548
|
}
|
|
510
549
|
|
|
511
550
|
private async _emitSyncState(): Promise<void> {
|
|
512
|
-
const connectionIds = await this._platform.persistence
|
|
551
|
+
const connectionIds = await this._platform.persistence
|
|
552
|
+
.getUsers(this.id)
|
|
553
|
+
.then((map) => Object.keys(map));
|
|
513
554
|
|
|
514
555
|
await this._broadcast({
|
|
515
556
|
message: {
|
|
@@ -525,7 +566,9 @@ export class IORoom<
|
|
|
525
566
|
throw new Error("Platform must use detached mode");
|
|
526
567
|
}
|
|
527
568
|
|
|
528
|
-
private _getAbstractWs(
|
|
569
|
+
private _getAbstractWs(
|
|
570
|
+
webSocket: WebSocketType<TPlatform>,
|
|
571
|
+
): AbstractWebSocket<any, TAuthorize> | null {
|
|
529
572
|
if ((webSocket as unknown as any) instanceof AbstractWebSocket) return webSocket;
|
|
530
573
|
|
|
531
574
|
const sessionId = this._platform.getSessionId(webSocket);
|
|
@@ -550,7 +593,8 @@ export class IORoom<
|
|
|
550
593
|
if (!ioAuthorize) return null as InferIOAuthorizeUser<InferIOAuthorize<this>>;
|
|
551
594
|
if (!token) return null as InferIOAuthorizeUser<InferIOAuthorize<this>>;
|
|
552
595
|
|
|
553
|
-
if (!ioAuthorize.secret)
|
|
596
|
+
if (!ioAuthorize.secret)
|
|
597
|
+
throw new Error("`authorize` was specified without a valid secret");
|
|
554
598
|
|
|
555
599
|
const payload = await authorize({
|
|
556
600
|
platform: this._platform,
|
|
@@ -590,13 +634,18 @@ export class IORoom<
|
|
|
590
634
|
return doc;
|
|
591
635
|
}
|
|
592
636
|
|
|
593
|
-
private _getIOAuthorize(
|
|
637
|
+
private _getIOAuthorize(
|
|
638
|
+
options: WebSocketRegisterConfig<TPlatform>,
|
|
639
|
+
): ResolvedPluvIOAuthorize<any, any> | null {
|
|
594
640
|
if (typeof this._authorize === "function") return this._authorize(options);
|
|
595
641
|
|
|
596
642
|
return this._authorize as ResolvedPluvIOAuthorize<any, any> | null;
|
|
597
643
|
}
|
|
598
644
|
|
|
599
|
-
private _getLatestPresence(userId: string): {
|
|
645
|
+
private _getLatestPresence(userId: string): {
|
|
646
|
+
timer: number | null;
|
|
647
|
+
presence: JsonObject | null;
|
|
648
|
+
} {
|
|
600
649
|
if (!this._authorize) return { timer: null, presence: null };
|
|
601
650
|
|
|
602
651
|
const sessionIds = Array.from(this._userSessionss.get(userId)?.values() ?? []);
|
|
@@ -628,11 +677,19 @@ export class IORoom<
|
|
|
628
677
|
|
|
629
678
|
private _getProcedure(
|
|
630
679
|
message: EventMessage<string, any>,
|
|
631
|
-
):
|
|
632
|
-
|
|
680
|
+
):
|
|
681
|
+
| (typeof this._router)["_defs"]["events"][keyof (typeof this._router)["_defs"]["events"]]
|
|
682
|
+
| null {
|
|
683
|
+
return (
|
|
684
|
+
this._router._defs.events[
|
|
685
|
+
message.type as keyof (typeof this._router)["_defs"]["events"]
|
|
686
|
+
] ?? null
|
|
687
|
+
);
|
|
633
688
|
}
|
|
634
689
|
|
|
635
|
-
private _getProcedureInputs(
|
|
690
|
+
private _getProcedureInputs(
|
|
691
|
+
message: EventMessage<string, any>,
|
|
692
|
+
): InferIOInput<this>[keyof TEvents] {
|
|
636
693
|
const procedure = this._getProcedure(message);
|
|
637
694
|
|
|
638
695
|
if (!procedure) return message.data;
|
|
@@ -706,7 +763,9 @@ export class IORoom<
|
|
|
706
763
|
|
|
707
764
|
await Promise.resolve(
|
|
708
765
|
this._listeners.onDestroy({
|
|
709
|
-
...("_meta" in this._platform && !!this._platform._meta
|
|
766
|
+
...("_meta" in this._platform && !!this._platform._meta
|
|
767
|
+
? { _meta: this._platform._meta }
|
|
768
|
+
: {}),
|
|
710
769
|
context: this._context,
|
|
711
770
|
encodedState,
|
|
712
771
|
room: this.id,
|
|
@@ -736,7 +795,9 @@ export class IORoom<
|
|
|
736
795
|
};
|
|
737
796
|
}
|
|
738
797
|
|
|
739
|
-
private _onMessage(
|
|
798
|
+
private _onMessage(
|
|
799
|
+
webSocket: AbstractWebSocket<any, TAuthorize>,
|
|
800
|
+
): (event: AbstractMessageEvent) => Promise<void> {
|
|
740
801
|
return async (event: AbstractMessageEvent): Promise<void> => {
|
|
741
802
|
if (!(await this._initialized)) return;
|
|
742
803
|
|
|
@@ -752,7 +813,12 @@ export class IORoom<
|
|
|
752
813
|
};
|
|
753
814
|
|
|
754
815
|
const doc = await this._doc;
|
|
755
|
-
const eventContext: EventResolverContext<
|
|
816
|
+
const eventContext: EventResolverContext<
|
|
817
|
+
EventResolverKind,
|
|
818
|
+
TPlatform,
|
|
819
|
+
TAuthorize,
|
|
820
|
+
TContext
|
|
821
|
+
> = {
|
|
756
822
|
context: this._context,
|
|
757
823
|
doc,
|
|
758
824
|
garbageCollect: async () => {
|
|
@@ -788,10 +854,15 @@ export class IORoom<
|
|
|
788
854
|
const procedure = this._getProcedure(message);
|
|
789
855
|
|
|
790
856
|
this._listeners.onMessage({
|
|
791
|
-
...("_meta" in this._platform && !!this._platform._meta
|
|
857
|
+
...("_meta" in this._platform && !!this._platform._meta
|
|
858
|
+
? { _meta: this._platform._meta }
|
|
859
|
+
: {}),
|
|
792
860
|
context: this._context,
|
|
793
861
|
encodedState: doc.getEncodedState(),
|
|
794
|
-
message: message as InferEventMessage<
|
|
862
|
+
message: message as InferEventMessage<
|
|
863
|
+
InferEventsOutput<TEvents>,
|
|
864
|
+
keyof InferEventsOutput<TEvents>
|
|
865
|
+
>,
|
|
795
866
|
room: this.id,
|
|
796
867
|
user: session.user,
|
|
797
868
|
webSocket: session.webSocket.webSocket,
|
|
@@ -875,7 +946,9 @@ export class IORoom<
|
|
|
875
946
|
};
|
|
876
947
|
}
|
|
877
948
|
|
|
878
|
-
private _parseMessage(message: {
|
|
949
|
+
private _parseMessage(message: {
|
|
950
|
+
data: string | ArrayBuffer;
|
|
951
|
+
}): EventMessage<string, any> | null {
|
|
879
952
|
try {
|
|
880
953
|
const parsed = this._platform.parseData(message.data);
|
|
881
954
|
|
|
@@ -923,7 +996,10 @@ export class IORoom<
|
|
|
923
996
|
});
|
|
924
997
|
}
|
|
925
998
|
|
|
926
|
-
private _removeUserSession(
|
|
999
|
+
private _removeUserSession(
|
|
1000
|
+
userId: string,
|
|
1001
|
+
sessionId: string,
|
|
1002
|
+
): Set<[sessionId: string][0]> | null {
|
|
927
1003
|
const set = this._userSessionss.get(userId);
|
|
928
1004
|
|
|
929
1005
|
if (!set) return null;
|
package/src/PluvIO.ts
CHANGED
|
@@ -9,7 +9,12 @@ import { PluvRouter } from "./PluvRouter";
|
|
|
9
9
|
import { PluvServer, PluvServerConfig } from "./PluvServer";
|
|
10
10
|
import type { JWTEncodeParams } from "./authorize";
|
|
11
11
|
import { authorize } from "./authorize";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
MAX_PRESENCE_SIZE_BYTES,
|
|
14
|
+
MAX_STORAGE_SIZE_BYTES,
|
|
15
|
+
MAX_USER_ID_LENGTH,
|
|
16
|
+
MAX_USER_SIZE_BYTES,
|
|
17
|
+
} from "./constants";
|
|
13
18
|
import type {
|
|
14
19
|
CrdtLibraryType,
|
|
15
20
|
GetInitialStorageFn,
|
|
@@ -37,7 +42,11 @@ export type PluvIOConfig<
|
|
|
37
42
|
|
|
38
43
|
type ResolvedServerConfig<
|
|
39
44
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
40
|
-
TAuthorize extends PluvIOAuthorize<
|
|
45
|
+
TAuthorize extends PluvIOAuthorize<
|
|
46
|
+
TPlatform,
|
|
47
|
+
any,
|
|
48
|
+
InferInitContextType<TPlatform>
|
|
49
|
+
> | null = any,
|
|
41
50
|
TContext extends Record<string, any> = {},
|
|
42
51
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
43
52
|
> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> &
|
|
@@ -47,29 +56,40 @@ type ResolvedServerConfig<
|
|
|
47
56
|
|
|
48
57
|
export type ServerConfig<
|
|
49
58
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
50
|
-
TAuthorize extends PluvIOAuthorize<
|
|
59
|
+
TAuthorize extends PluvIOAuthorize<
|
|
60
|
+
TPlatform,
|
|
61
|
+
any,
|
|
62
|
+
InferInitContextType<TPlatform>
|
|
63
|
+
> | null = any,
|
|
51
64
|
TContext extends Record<string, any> = {},
|
|
52
65
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
53
66
|
> = {
|
|
54
|
-
[P in keyof ResolvedServerConfig<
|
|
67
|
+
[P in keyof ResolvedServerConfig<
|
|
55
68
|
TPlatform,
|
|
56
69
|
TAuthorize,
|
|
57
70
|
TContext,
|
|
58
71
|
TEvents
|
|
59
|
-
>[P] extends undefined
|
|
72
|
+
> as ResolvedServerConfig<TPlatform, TAuthorize, TContext, TEvents>[P] extends undefined
|
|
60
73
|
? never
|
|
61
74
|
: P]: ResolvedServerConfig<TPlatform, TAuthorize, TContext, TEvents>[P];
|
|
62
75
|
};
|
|
63
76
|
|
|
64
77
|
export class PluvIO<
|
|
65
78
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
66
|
-
TAuthorize extends PluvIOAuthorize<
|
|
79
|
+
TAuthorize extends PluvIOAuthorize<
|
|
80
|
+
TPlatform,
|
|
81
|
+
any,
|
|
82
|
+
InferInitContextType<TPlatform>
|
|
83
|
+
> | null = any,
|
|
67
84
|
TContext extends Record<string, any> = {},
|
|
68
85
|
> {
|
|
69
86
|
public readonly version: string = __PLUV_VERSION as any;
|
|
70
87
|
|
|
71
88
|
private readonly _authorize: TAuthorize = null as TAuthorize;
|
|
72
|
-
private readonly _context: PluvContext<TPlatform, TContext> = {} as PluvContext<
|
|
89
|
+
private readonly _context: PluvContext<TPlatform, TContext> = {} as PluvContext<
|
|
90
|
+
TPlatform,
|
|
91
|
+
TContext
|
|
92
|
+
>;
|
|
73
93
|
private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
74
94
|
private readonly _debug: boolean;
|
|
75
95
|
private readonly _limits: PluvIOLimits;
|
|
@@ -97,8 +117,11 @@ export class PluvIO<
|
|
|
97
117
|
if (context) this._context = context;
|
|
98
118
|
}
|
|
99
119
|
|
|
100
|
-
public async createToken(
|
|
101
|
-
|
|
120
|
+
public async createToken(
|
|
121
|
+
params: JWTEncodeParams<InferIOAuthorizeUser<TAuthorize>, TPlatform>,
|
|
122
|
+
): Promise<string> {
|
|
123
|
+
if (!this._authorize)
|
|
124
|
+
throw new Error("IO does not specify authorize during initialization.");
|
|
102
125
|
|
|
103
126
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
104
127
|
const user = params.user as BaseUser;
|
|
@@ -130,7 +153,9 @@ export class PluvIO<
|
|
|
130
153
|
|
|
131
154
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
132
155
|
|
|
133
|
-
return await authorize({ platform: this._platform, secret }).encode(
|
|
156
|
+
return await authorize({ platform: this._platform, secret }).encode(
|
|
157
|
+
params as JWTEncodeParams<any, TPlatform>,
|
|
158
|
+
);
|
|
134
159
|
}
|
|
135
160
|
|
|
136
161
|
public mergeRouters<TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[]>(
|
|
@@ -171,7 +196,9 @@ export class PluvIO<
|
|
|
171
196
|
} as PluvServerConfig<TPlatform, TAuthorize, TContext, TEvents>);
|
|
172
197
|
}
|
|
173
198
|
|
|
174
|
-
private _getIOAuthorize(
|
|
199
|
+
private _getIOAuthorize(
|
|
200
|
+
options: WebSocketRegisterConfig<TPlatform>,
|
|
201
|
+
): ResolvedPluvIOAuthorize<any, any> | null {
|
|
175
202
|
if (typeof this._authorize === "function") {
|
|
176
203
|
return this._authorize(options);
|
|
177
204
|
}
|
package/src/PluvProcedure.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
EventRecord,
|
|
3
|
+
IOAuthorize,
|
|
4
|
+
InputZodLike,
|
|
5
|
+
JsonObject,
|
|
6
|
+
ProcedureLike,
|
|
7
|
+
} from "@pluv/types";
|
|
2
8
|
import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
|
|
3
9
|
import type { EventResolver, EventResolverKind, MergeEventRecords } from "./types";
|
|
4
10
|
|
|
@@ -9,7 +15,14 @@ export interface PluvProcedureConfig<
|
|
|
9
15
|
TInput extends JsonObject = {},
|
|
10
16
|
TOutput extends EventRecord<string, any> = {},
|
|
11
17
|
> {
|
|
12
|
-
broadcast?: EventResolver<
|
|
18
|
+
broadcast?: EventResolver<
|
|
19
|
+
"broadcast",
|
|
20
|
+
TPlatform,
|
|
21
|
+
TAuthorize,
|
|
22
|
+
TContext,
|
|
23
|
+
TInput,
|
|
24
|
+
Partial<TOutput>
|
|
25
|
+
> | null;
|
|
13
26
|
input?: InputZodLike<TInput>;
|
|
14
27
|
self?: EventResolver<"self", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;
|
|
15
28
|
sync?: EventResolver<"sync", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;
|
|
@@ -23,11 +36,31 @@ export class PluvProcedure<
|
|
|
23
36
|
TOutput extends EventRecord<string, any> = {},
|
|
24
37
|
TFilled extends "input" | "broadcast" | "self" | "sync" | "" = "",
|
|
25
38
|
> {
|
|
26
|
-
private _broadcast: EventResolver<
|
|
27
|
-
|
|
39
|
+
private _broadcast: EventResolver<
|
|
40
|
+
"broadcast",
|
|
41
|
+
TPlatform,
|
|
42
|
+
TAuthorize,
|
|
43
|
+
TContext,
|
|
44
|
+
TInput,
|
|
45
|
+
Partial<TOutput>
|
|
46
|
+
> | null = null;
|
|
28
47
|
private _input: InputZodLike<TInput> | null = null;
|
|
29
|
-
private _self: EventResolver<
|
|
30
|
-
|
|
48
|
+
private _self: EventResolver<
|
|
49
|
+
"self",
|
|
50
|
+
TPlatform,
|
|
51
|
+
TAuthorize,
|
|
52
|
+
TContext,
|
|
53
|
+
TInput,
|
|
54
|
+
Partial<TOutput>
|
|
55
|
+
> | null = null;
|
|
56
|
+
private _sync: EventResolver<
|
|
57
|
+
"sync",
|
|
58
|
+
TPlatform,
|
|
59
|
+
TAuthorize,
|
|
60
|
+
TContext,
|
|
61
|
+
TInput,
|
|
62
|
+
Partial<TOutput>
|
|
63
|
+
> | null = null;
|
|
31
64
|
|
|
32
65
|
public get config(): ProcedureLike<TInput, TOutput>["config"] {
|
|
33
66
|
return {
|
|
@@ -39,7 +72,9 @@ export class PluvProcedure<
|
|
|
39
72
|
} as ProcedureLike<TInput, TOutput>["config"];
|
|
40
73
|
}
|
|
41
74
|
|
|
42
|
-
constructor(
|
|
75
|
+
constructor(
|
|
76
|
+
config: PluvProcedureConfig<TPlatform, TAuthorize, TContext, TInput, TOutput> = {},
|
|
77
|
+
) {
|
|
43
78
|
const { broadcast, input, self, sync } = config;
|
|
44
79
|
|
|
45
80
|
this._broadcast = broadcast ?? null;
|
|
@@ -80,8 +115,13 @@ export class PluvProcedure<
|
|
|
80
115
|
|
|
81
116
|
public input<TData extends JsonObject>(
|
|
82
117
|
input: InputZodLike<TData>,
|
|
83
|
-
): Omit<
|
|
84
|
-
|
|
118
|
+
): Omit<
|
|
119
|
+
PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | "input">,
|
|
120
|
+
TFilled | "input"
|
|
121
|
+
> {
|
|
122
|
+
return new PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | "input">({
|
|
123
|
+
input,
|
|
124
|
+
});
|
|
85
125
|
}
|
|
86
126
|
|
|
87
127
|
public self<TResult extends EventRecord<string, any> = {}>(
|
|
@@ -144,7 +184,14 @@ export class PluvProcedure<
|
|
|
144
184
|
});
|
|
145
185
|
}
|
|
146
186
|
|
|
147
|
-
private _resolver(): EventResolver<
|
|
187
|
+
private _resolver(): EventResolver<
|
|
188
|
+
EventResolverKind,
|
|
189
|
+
TPlatform,
|
|
190
|
+
TAuthorize,
|
|
191
|
+
TContext,
|
|
192
|
+
TInput,
|
|
193
|
+
TOutput
|
|
194
|
+
> {
|
|
148
195
|
return (data, context) => {
|
|
149
196
|
return {
|
|
150
197
|
...this._broadcast?.(data, context),
|
package/src/PluvRouter.ts
CHANGED
|
@@ -5,7 +5,11 @@ import type { PluvIOAuthorize } from "./types";
|
|
|
5
5
|
|
|
6
6
|
export type PluvRouterEventConfig<
|
|
7
7
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
8
|
-
TAuthorize extends PluvIOAuthorize<
|
|
8
|
+
TAuthorize extends PluvIOAuthorize<
|
|
9
|
+
TPlatform,
|
|
10
|
+
any,
|
|
11
|
+
InferInitContextType<TPlatform>
|
|
12
|
+
> | null = any,
|
|
9
13
|
TContext extends Record<string, any> = {},
|
|
10
14
|
> = { [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, "config"> };
|
|
11
15
|
|
|
@@ -24,7 +28,11 @@ export type MergedRouter<
|
|
|
24
28
|
|
|
25
29
|
export class PluvRouter<
|
|
26
30
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
27
|
-
TAuthorize extends PluvIOAuthorize<
|
|
31
|
+
TAuthorize extends PluvIOAuthorize<
|
|
32
|
+
TPlatform,
|
|
33
|
+
any,
|
|
34
|
+
InferInitContextType<TPlatform>
|
|
35
|
+
> | null = any,
|
|
28
36
|
TContext extends Record<string, any> = {},
|
|
29
37
|
TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
|
|
30
38
|
> implements IORouterLike<TEvents>
|
|
@@ -46,7 +54,10 @@ export class PluvRouter<
|
|
|
46
54
|
public static merge<TRouters extends PluvRouter<any, any, any, any>[]>(
|
|
47
55
|
...routers: TRouters
|
|
48
56
|
): MergedRouter<TRouters> {
|
|
49
|
-
const events = Object.assign(
|
|
57
|
+
const events = Object.assign(
|
|
58
|
+
Object.create(null),
|
|
59
|
+
...routers.map((router) => router._defs.events),
|
|
60
|
+
);
|
|
50
61
|
|
|
51
62
|
return new PluvRouter<any, any, any, any>(events) as MergedRouter<TRouters>;
|
|
52
63
|
}
|