@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/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
- InferIOOutput,
7
- IOAuthorize,
8
- IOLike,
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<TIO extends PluvIO<any, any, any, any, any>> =
23
- TIO extends PluvIO<any, any, infer IContext, any, any> ? IContext : never;
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<TIO extends PluvIO<any, any, any, any>> =
26
- TIO extends PluvIO<infer IPlatform, any, any, any, any> ? IPlatform : never;
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 IOutput
42
+ infer IOutputBroadcast,
43
+ infer IOutputSelf,
44
+ infer IOutputSync
35
45
  >
36
- ? IORoom<IPlatform, IAuthorize, IContext, IInput, IOutput>
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
- TOutput extends EventRecord<string, any> = {}
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<TContext, TInput, TOutput>;
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
- TOutput extends EventRecord<string, any> = {}
73
- > implements IOLike<TAuthorize, TInput, TOutput>
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<TPlatform, TAuthorize, TContext, TInput, TOutput>
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<TContext, TInput, TOutput> = {
88
- $PING: {
89
- resolver: (data, { session }) => {
90
- if (!session) return {};
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
- const currentTime = new Date().getTime();
193
+ this._listeners.onStorageUpdated(room, state);
194
+ }
93
195
 
94
- session.timers.ping = currentTime;
196
+ const state =
197
+ (await this._platform.persistance.getStorageState(
198
+ room
199
+ )) ?? doc.encodeStateAsUpdate();
95
200
 
96
- return { $PONG: {} };
201
+ return { $STORAGE_RECEIVED: { state } };
202
+ },
97
203
  },
98
- options: { type: "self" },
99
204
  },
100
- $GET_OTHERS: {
101
- resolver: (data, { room, session, sessions }) => {
102
- const others = Array.from(sessions.entries())
103
- .filter(([, wsSession]) => wsSession.id !== session?.id)
104
- .reduce<{
105
- [connectionId: string]: {
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
- return { $OTHERS_RECEIVED: { others } };
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<TContext, TInput, TOutput>;
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<TPlatform, TAuthorize, TContext, TInput, TOutput>
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
- TResult extends EventRecord<string, any> = {}
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<TContext, TData, TResult>
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<[TOutput, TResult]>
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
- TResult extends EventRecord<string, any> ? TResult : {}
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<TPlatform, TAuthorize, TContext, TInput, TOutput> {
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
- TOutput
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<[InferIOOutput<TIO1>, InferIOOutput<TIO2>]>
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
- TResult extends EventRecord<string, any> = {}
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
- options?: SendMessageOptions;
32
- resolver: EventResolver<TContext, TData, TResult>;
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
- TResult extends EventRecord<string, any> = {}
49
+ TResultBroadcast extends EventRecord<string, any> = {}
41
50
  > = (
42
51
  data: TData,
43
52
  context: EventResolverContext<TContext>
44
- ) => MaybePromise<TResult | void>;
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
- TOutput extends EventRecord<string, any> = {}
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<EventConfig<TContext, TInput[P], TOutput>>
95
+ ? Id<
96
+ EventConfig<
97
+ TContext,
98
+ TInput[P],
99
+ TOutputBroadcast,
100
+ TOutputSelf,
101
+ TOutputSync
102
+ >
103
+ >
73
104
  : never;
74
105
  };
75
106