@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/PluvIO.ts
CHANGED
|
@@ -1,39 +1,57 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
BaseIOAuthorize,
|
|
2
3
|
EventRecord,
|
|
4
|
+
IOAuthorize,
|
|
5
|
+
IOLike,
|
|
3
6
|
InferIOAuthorize,
|
|
4
7
|
InferIOAuthorizeUser,
|
|
5
8
|
InferIOInput,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
InferIOOutputBroadcast,
|
|
10
|
+
InferIOOutputSelf,
|
|
11
|
+
InferIOOutputSync,
|
|
9
12
|
JsonObject,
|
|
10
|
-
Spread,
|
|
11
|
-
BaseIOAuthorize,
|
|
12
|
-
MaybePromise,
|
|
13
13
|
Maybe,
|
|
14
|
+
MaybePromise,
|
|
15
|
+
Spread,
|
|
14
16
|
} from "@pluv/types";
|
|
15
17
|
import colors from "kleur";
|
|
16
18
|
import type { AbstractPlatform } from "./AbstractPlatform";
|
|
19
|
+
import { IORoom } from "./IORoom";
|
|
17
20
|
import type { JWTEncodeParams } from "./authorize";
|
|
18
21
|
import { authorize } from "./authorize";
|
|
19
|
-
import { IORoom } from "./IORoom";
|
|
20
22
|
import type { EventConfig, InferEventConfig } from "./types";
|
|
21
23
|
|
|
22
|
-
export type InferIOContext<
|
|
23
|
-
TIO extends PluvIO<any, any,
|
|
24
|
+
export type InferIOContext<
|
|
25
|
+
TIO extends PluvIO<any, any, any, any, any, any, any>
|
|
26
|
+
> = TIO extends PluvIO<any, any, infer IContext, any, any, any, any>
|
|
27
|
+
? IContext
|
|
28
|
+
: never;
|
|
24
29
|
|
|
25
|
-
export type InferIOPlatform<
|
|
26
|
-
TIO extends PluvIO<
|
|
30
|
+
export type InferIOPlatform<
|
|
31
|
+
TIO extends PluvIO<any, any, any, any, any, any, any>
|
|
32
|
+
> = TIO extends PluvIO<infer IPlatform, any, any, any, any, any, any>
|
|
33
|
+
? IPlatform
|
|
34
|
+
: never;
|
|
27
35
|
|
|
28
|
-
export type InferIORoom<TIO extends PluvIO<any, any, any, any>> =
|
|
36
|
+
export type InferIORoom<TIO extends PluvIO<any, any, any, any, any, any, any>> =
|
|
29
37
|
TIO extends PluvIO<
|
|
30
38
|
infer IPlatform,
|
|
31
39
|
infer IAuthorize,
|
|
32
40
|
infer IContext,
|
|
33
41
|
infer IInput,
|
|
34
|
-
infer
|
|
42
|
+
infer IOutputBroadcast,
|
|
43
|
+
infer IOutputSelf,
|
|
44
|
+
infer IOutputSync
|
|
35
45
|
>
|
|
36
|
-
? IORoom<
|
|
46
|
+
? IORoom<
|
|
47
|
+
IPlatform,
|
|
48
|
+
IAuthorize,
|
|
49
|
+
IContext,
|
|
50
|
+
IInput,
|
|
51
|
+
IOutputBroadcast,
|
|
52
|
+
IOutputSelf,
|
|
53
|
+
IOutputSync
|
|
54
|
+
>
|
|
37
55
|
: never;
|
|
38
56
|
|
|
39
57
|
export interface PluvIORegisterOptions {
|
|
@@ -50,12 +68,20 @@ export type PluvIOConfig<
|
|
|
50
68
|
TAuthorize extends IOAuthorize<any, any> | null = null,
|
|
51
69
|
TContext extends JsonObject = {},
|
|
52
70
|
TInput extends EventRecord<string, any> = {},
|
|
53
|
-
|
|
71
|
+
TOutputBroadcast extends EventRecord<string, any> = {},
|
|
72
|
+
TOutputSelf extends EventRecord<string, any> = {},
|
|
73
|
+
TOutputSync extends EventRecord<string, any> = {}
|
|
54
74
|
> = Partial<PluvIOListeners> & {
|
|
55
75
|
authorize?: TAuthorize;
|
|
56
76
|
context?: TContext;
|
|
57
77
|
debug?: boolean;
|
|
58
|
-
events?: InferEventConfig<
|
|
78
|
+
events?: InferEventConfig<
|
|
79
|
+
TContext,
|
|
80
|
+
TInput,
|
|
81
|
+
TOutputBroadcast,
|
|
82
|
+
TOutputSelf,
|
|
83
|
+
TOutputSync
|
|
84
|
+
>;
|
|
59
85
|
initialStorage?: (room: string) => MaybePromise<Maybe<string>>;
|
|
60
86
|
platform: TPlatform;
|
|
61
87
|
};
|
|
@@ -69,8 +95,11 @@ export class PluvIO<
|
|
|
69
95
|
TAuthorize extends IOAuthorize<any, any> = BaseIOAuthorize,
|
|
70
96
|
TContext extends JsonObject = {},
|
|
71
97
|
TInput extends EventRecord<string, any> = {},
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
TOutputBroadcast extends EventRecord<string, any> = {},
|
|
99
|
+
TOutputSelf extends EventRecord<string, any> = {},
|
|
100
|
+
TOutputSync extends EventRecord<string, any> = {}
|
|
101
|
+
> implements
|
|
102
|
+
IOLike<TAuthorize, TInput, TOutputBroadcast, TOutputSelf, TOutputSync>
|
|
74
103
|
{
|
|
75
104
|
private _context: TContext = {} as TContext;
|
|
76
105
|
private _initialStorage:
|
|
@@ -79,50 +108,112 @@ export class PluvIO<
|
|
|
79
108
|
private _listeners: PluvIOListeners;
|
|
80
109
|
private _rooms = new Map<
|
|
81
110
|
string,
|
|
82
|
-
IORoom<
|
|
111
|
+
IORoom<
|
|
112
|
+
TPlatform,
|
|
113
|
+
TAuthorize,
|
|
114
|
+
TContext,
|
|
115
|
+
TInput,
|
|
116
|
+
TOutputBroadcast,
|
|
117
|
+
TOutputSelf,
|
|
118
|
+
TOutputSync
|
|
119
|
+
>
|
|
83
120
|
>();
|
|
84
121
|
|
|
85
122
|
readonly _authorize: TAuthorize | null = null;
|
|
86
123
|
readonly _debug: boolean;
|
|
87
|
-
readonly _events: InferEventConfig<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
readonly _events: InferEventConfig<
|
|
125
|
+
TContext,
|
|
126
|
+
TInput,
|
|
127
|
+
TOutputBroadcast,
|
|
128
|
+
TOutputSelf,
|
|
129
|
+
TOutputSync
|
|
130
|
+
> = {
|
|
131
|
+
$GET_OTHERS: {
|
|
132
|
+
resolver: {
|
|
133
|
+
sync: (data, { room, session, sessions }) => {
|
|
134
|
+
const others = Array.from(sessions.entries())
|
|
135
|
+
.filter(([, wsSession]) => wsSession.id !== session?.id)
|
|
136
|
+
.reduce<{
|
|
137
|
+
[connectionId: string]: {
|
|
138
|
+
connectionId: string;
|
|
139
|
+
room: string | null;
|
|
140
|
+
user: InferIOAuthorizeUser<TAuthorize>;
|
|
141
|
+
};
|
|
142
|
+
}>(
|
|
143
|
+
(acc, [connectionId, { presence, user }]) => ({
|
|
144
|
+
...acc,
|
|
145
|
+
[connectionId]: {
|
|
146
|
+
connectionId,
|
|
147
|
+
presence,
|
|
148
|
+
room,
|
|
149
|
+
user: user as InferIOAuthorizeUser<TAuthorize>,
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
{}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return { $OTHERS_RECEIVED: { others } };
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
$INITIALIZE_SESSION: {
|
|
160
|
+
resolver: {
|
|
161
|
+
broadcast: ({ presence }, { session }) => {
|
|
162
|
+
if (!session) return {};
|
|
163
|
+
|
|
164
|
+
session.presence = presence;
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
$USER_JOINED: {
|
|
168
|
+
connectionId: session.id,
|
|
169
|
+
user: session.user,
|
|
170
|
+
presence,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
self: async ({ update }, { doc, room }) => {
|
|
175
|
+
const oldState =
|
|
176
|
+
await this._platform.persistance.getStorageState(room);
|
|
177
|
+
|
|
178
|
+
const updates: readonly Maybe<string>[] = [
|
|
179
|
+
oldState ? null : await this._initialStorage?.(room),
|
|
180
|
+
update,
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
if (updates.some((_update) => !!_update)) {
|
|
184
|
+
const state = updates
|
|
185
|
+
.reduce((_doc, _up) => _doc.applyUpdate(_up), doc)
|
|
186
|
+
.encodeStateAsUpdate();
|
|
187
|
+
|
|
188
|
+
await this._platform.persistance.setStorageState(
|
|
189
|
+
room,
|
|
190
|
+
state
|
|
191
|
+
);
|
|
91
192
|
|
|
92
|
-
|
|
193
|
+
this._listeners.onStorageUpdated(room, state);
|
|
194
|
+
}
|
|
93
195
|
|
|
94
|
-
|
|
196
|
+
const state =
|
|
197
|
+
(await this._platform.persistance.getStorageState(
|
|
198
|
+
room
|
|
199
|
+
)) ?? doc.encodeStateAsUpdate();
|
|
95
200
|
|
|
96
|
-
|
|
201
|
+
return { $STORAGE_RECEIVED: { state } };
|
|
202
|
+
},
|
|
97
203
|
},
|
|
98
|
-
options: { type: "self" },
|
|
99
204
|
},
|
|
100
|
-
$
|
|
101
|
-
resolver:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
connectionId: string;
|
|
107
|
-
room: string | null;
|
|
108
|
-
user: InferIOAuthorizeUser<TAuthorize>;
|
|
109
|
-
};
|
|
110
|
-
}>(
|
|
111
|
-
(acc, [connectionId, { presence, user }]) => ({
|
|
112
|
-
...acc,
|
|
113
|
-
[connectionId]: {
|
|
114
|
-
connectionId,
|
|
115
|
-
presence,
|
|
116
|
-
room,
|
|
117
|
-
user: user as InferIOAuthorizeUser<TAuthorize>,
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
{}
|
|
121
|
-
);
|
|
205
|
+
$PING: {
|
|
206
|
+
resolver: {
|
|
207
|
+
self: (data, { session }) => {
|
|
208
|
+
if (!session) return {};
|
|
209
|
+
|
|
210
|
+
const currentTime = new Date().getTime();
|
|
122
211
|
|
|
123
|
-
|
|
212
|
+
session.timers.ping = currentTime;
|
|
213
|
+
|
|
214
|
+
return { $PONG: {} };
|
|
215
|
+
},
|
|
124
216
|
},
|
|
125
|
-
options: { type: "sync" },
|
|
126
217
|
},
|
|
127
218
|
$UPDATE_PRESENCE: {
|
|
128
219
|
resolver: ({ presence }, { session }) => {
|
|
@@ -158,11 +249,25 @@ export class PluvIO<
|
|
|
158
249
|
return { $STORAGE_UPDATED: { state } };
|
|
159
250
|
},
|
|
160
251
|
},
|
|
161
|
-
} as InferEventConfig<
|
|
252
|
+
} as InferEventConfig<
|
|
253
|
+
TContext,
|
|
254
|
+
TInput,
|
|
255
|
+
TOutputBroadcast,
|
|
256
|
+
TOutputSelf,
|
|
257
|
+
TOutputSync
|
|
258
|
+
>;
|
|
162
259
|
readonly _platform: TPlatform;
|
|
163
260
|
|
|
164
261
|
constructor(
|
|
165
|
-
options: PluvIOConfig<
|
|
262
|
+
options: PluvIOConfig<
|
|
263
|
+
TPlatform,
|
|
264
|
+
TAuthorize,
|
|
265
|
+
TContext,
|
|
266
|
+
TInput,
|
|
267
|
+
TOutputBroadcast,
|
|
268
|
+
TOutputSelf,
|
|
269
|
+
TOutputSync
|
|
270
|
+
>
|
|
166
271
|
) {
|
|
167
272
|
const {
|
|
168
273
|
authorize,
|
|
@@ -211,16 +316,26 @@ export class PluvIO<
|
|
|
211
316
|
public event<
|
|
212
317
|
TEvent extends string,
|
|
213
318
|
TData extends JsonObject = {},
|
|
214
|
-
|
|
319
|
+
TResultBroadcast extends EventRecord<string, any> = {},
|
|
320
|
+
TResultSelf extends EventRecord<string, any> = {},
|
|
321
|
+
TResultSync extends EventRecord<string, any> = {}
|
|
215
322
|
>(
|
|
216
323
|
event: TEvent,
|
|
217
|
-
config: EventConfig<
|
|
324
|
+
config: EventConfig<
|
|
325
|
+
TContext,
|
|
326
|
+
TData,
|
|
327
|
+
TResultBroadcast,
|
|
328
|
+
TResultSelf,
|
|
329
|
+
TResultSync
|
|
330
|
+
>
|
|
218
331
|
): PluvIO<
|
|
219
332
|
TPlatform,
|
|
220
333
|
TAuthorize,
|
|
221
334
|
TContext,
|
|
222
335
|
Spread<[TInput, EventRecord<TEvent, TData>]>,
|
|
223
|
-
Spread<[
|
|
336
|
+
Spread<[TOutputBroadcast, TResultBroadcast]>,
|
|
337
|
+
Spread<[TOutputSelf, TResultSelf]>,
|
|
338
|
+
Spread<[TOutputSync, TResultSync]>
|
|
224
339
|
> {
|
|
225
340
|
if (event.startsWith("$")) {
|
|
226
341
|
throw new Error('Event name must not start with "$".');
|
|
@@ -231,7 +346,11 @@ export class PluvIO<
|
|
|
231
346
|
} as InferEventConfig<
|
|
232
347
|
TContext,
|
|
233
348
|
EventRecord<TEvent, TData>,
|
|
234
|
-
|
|
349
|
+
TResultBroadcast extends EventRecord<string, any>
|
|
350
|
+
? TResultBroadcast
|
|
351
|
+
: {},
|
|
352
|
+
TResultSelf extends EventRecord<string, any> ? TResultSelf : {},
|
|
353
|
+
TResultSync extends EventRecord<string, any> ? TResultSync : {}
|
|
235
354
|
>;
|
|
236
355
|
|
|
237
356
|
/**
|
|
@@ -259,7 +378,15 @@ export class PluvIO<
|
|
|
259
378
|
public getRoom(
|
|
260
379
|
room: string,
|
|
261
380
|
options: GetRoomOptions = {}
|
|
262
|
-
): IORoom<
|
|
381
|
+
): IORoom<
|
|
382
|
+
TPlatform,
|
|
383
|
+
TAuthorize,
|
|
384
|
+
TContext,
|
|
385
|
+
TInput,
|
|
386
|
+
TOutputBroadcast,
|
|
387
|
+
TOutputSelf,
|
|
388
|
+
TOutputSync
|
|
389
|
+
> {
|
|
263
390
|
const { debug } = options;
|
|
264
391
|
|
|
265
392
|
this._purgeEmptyRooms();
|
|
@@ -278,13 +405,14 @@ export class PluvIO<
|
|
|
278
405
|
TAuthorize,
|
|
279
406
|
TContext,
|
|
280
407
|
TInput,
|
|
281
|
-
|
|
408
|
+
TOutputBroadcast,
|
|
409
|
+
TOutputSelf,
|
|
410
|
+
TOutputSync
|
|
282
411
|
>(room, {
|
|
283
412
|
authorize: this._authorize ?? undefined,
|
|
284
413
|
context: this._context,
|
|
285
414
|
debug: debug ?? this._debug,
|
|
286
415
|
events: this._events,
|
|
287
|
-
initialStorage: () => this._initialStorage?.(room),
|
|
288
416
|
onDestroy: (encodedState) => {
|
|
289
417
|
this._logDebug(
|
|
290
418
|
`${colors.blue("Deleting empty room:")} ${room}`
|
|
@@ -327,7 +455,9 @@ export class PluvIO<
|
|
|
327
455
|
InferIOAuthorize<TIO1>,
|
|
328
456
|
InferIOContext<TIO1>,
|
|
329
457
|
Spread<[InferIOInput<TIO1>, InferIOInput<TIO2>]>,
|
|
330
|
-
Spread<[
|
|
458
|
+
Spread<[InferIOOutputBroadcast<TIO1>, InferIOOutputBroadcast<TIO2>]>,
|
|
459
|
+
Spread<[InferIOOutputSelf<TIO1>, InferIOOutputSelf<TIO2>]>,
|
|
460
|
+
Spread<[InferIOOutputSync<TIO1>, InferIOOutputSync<TIO2>]>
|
|
331
461
|
> {
|
|
332
462
|
const authorize = (first._authorize ??
|
|
333
463
|
undefined) as InferIOAuthorize<TIO1>;
|
package/src/types.ts
CHANGED
|
@@ -25,11 +25,20 @@ declare global {
|
|
|
25
25
|
export interface EventConfig<
|
|
26
26
|
TContext extends JsonObject = {},
|
|
27
27
|
TData extends JsonObject = {},
|
|
28
|
-
|
|
28
|
+
TResultBroadcast extends EventRecord<string, any> = {},
|
|
29
|
+
TResultSelf extends EventRecord<string, any> = {},
|
|
30
|
+
TResultSync extends EventRecord<string, any> = {}
|
|
29
31
|
> {
|
|
30
32
|
input?: InputZodLike<TData>;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
resolver:
|
|
34
|
+
| EventResolver<TContext, TData, TResultBroadcast>
|
|
35
|
+
| EventResolverObject<
|
|
36
|
+
TContext,
|
|
37
|
+
TData,
|
|
38
|
+
TResultBroadcast,
|
|
39
|
+
TResultSelf,
|
|
40
|
+
TResultSync
|
|
41
|
+
>;
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
export type EventConfigType = "broadcast" | "self" | "sync";
|
|
@@ -37,11 +46,11 @@ export type EventConfigType = "broadcast" | "self" | "sync";
|
|
|
37
46
|
export type EventResolver<
|
|
38
47
|
TContext extends JsonObject = {},
|
|
39
48
|
TData extends JsonObject = {},
|
|
40
|
-
|
|
49
|
+
TResultBroadcast extends EventRecord<string, any> = {}
|
|
41
50
|
> = (
|
|
42
51
|
data: TData,
|
|
43
52
|
context: EventResolverContext<TContext>
|
|
44
|
-
) => MaybePromise<
|
|
53
|
+
) => MaybePromise<TResultBroadcast | void>;
|
|
45
54
|
|
|
46
55
|
export interface EventResolverContext<TContext extends JsonObject = {}> {
|
|
47
56
|
context: TContext;
|
|
@@ -63,13 +72,35 @@ export interface EventResolverContext<TContext extends JsonObject = {}> {
|
|
|
63
72
|
sessions: Map<string, WebSocketSession>;
|
|
64
73
|
}
|
|
65
74
|
|
|
75
|
+
export type EventResolverObject<
|
|
76
|
+
TContext extends JsonObject = {},
|
|
77
|
+
TData extends JsonObject = {},
|
|
78
|
+
TResultBroadcast extends EventRecord<string, any> = {},
|
|
79
|
+
TResultSelf extends EventRecord<string, any> = {},
|
|
80
|
+
TResultSync extends EventRecord<string, any> = {}
|
|
81
|
+
> = {
|
|
82
|
+
broadcast?: EventResolver<TContext, TData, TResultBroadcast>;
|
|
83
|
+
self?: EventResolver<TContext, TData, TResultSelf>;
|
|
84
|
+
sync?: EventResolver<TContext, TData, TResultSync>;
|
|
85
|
+
};
|
|
86
|
+
|
|
66
87
|
export type InferEventConfig<
|
|
67
88
|
TContext extends JsonObject = {},
|
|
68
89
|
TInput extends EventRecord<string, any> = {},
|
|
69
|
-
|
|
90
|
+
TOutputBroadcast extends EventRecord<string, any> = {},
|
|
91
|
+
TOutputSelf extends EventRecord<string, any> = {},
|
|
92
|
+
TOutputSync extends EventRecord<string, any> = {}
|
|
70
93
|
> = {
|
|
71
94
|
[P in keyof TInput]: P extends string
|
|
72
|
-
? Id<
|
|
95
|
+
? Id<
|
|
96
|
+
EventConfig<
|
|
97
|
+
TContext,
|
|
98
|
+
TInput[P],
|
|
99
|
+
TOutputBroadcast,
|
|
100
|
+
TOutputSelf,
|
|
101
|
+
TOutputSync
|
|
102
|
+
>
|
|
103
|
+
>
|
|
73
104
|
: never;
|
|
74
105
|
};
|
|
75
106
|
|