@pluv/io 0.40.1 → 0.41.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.
@@ -1,9 +1,9 @@
1
1
 
2
- > @pluv/io@0.40.1 build /home/runner/work/pluv/pluv/packages/io
2
+ > @pluv/io@0.41.0 build /home/runner/work/pluv/pluv/packages/io
3
3
  > pnpm run version && tsup src/index.ts --format esm,cjs --dts
4
4
 
5
5
 
6
- > @pluv/io@0.40.1 version /home/runner/work/pluv/pluv/packages/io
6
+ > @pluv/io@0.41.0 version /home/runner/work/pluv/pluv/packages/io
7
7
  > genversion ./src/version.generated.ts --esm
8
8
 
9
9
  CLI Building entry: src/index.ts
@@ -12,11 +12,11 @@
12
12
  CLI Target: es6
13
13
  ESM Build start
14
14
  CJS Build start
15
- ESM dist/index.mjs 44.38 KB
16
- ESM ⚡️ Build success in 141ms
17
- CJS dist/index.js 46.56 KB
18
- CJS ⚡️ Build success in 141ms
15
+ CJS dist/index.js 51.27 KB
16
+ CJS ⚡️ Build success in 95ms
17
+ ESM dist/index.mjs 49.09 KB
18
+ ESM ⚡️ Build success in 106ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 2536ms
21
- DTS dist/index.d.mts 30.93 KB
22
- DTS dist/index.d.ts 30.93 KB
20
+ DTS ⚡️ Build success in 2982ms
21
+ DTS dist/index.d.mts 31.42 KB
22
+ DTS dist/index.d.ts 31.42 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # @pluv/io
2
2
 
3
+ ## 0.41.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 73adf21: **BREAKING** The `user` that is provided to `PluvServer.createToken` is now limited to be at most 512 bytes. If the `user` is any larger, `PluvServer.createToken` will now throw an error.
8
+ - f02a430: **BREAKING** Updated the way `user` is stored and read from the websocket, so now `user` is persisted in-memory rather than from persistent storage (improves performance and lowers cost in some cases). This change primarily affects pluv platforms, which are intended for internal use only, and therefore should be mostly non-breaking.
9
+ - 0b908b3: **BREAKING** User's presence is now limited to be at most 512 bytes. If the presence is any larger, the `PluvServer` will now throw an error.
10
+ - 555b88d: Users can now have multiple simultaneous connections. Previously, if a user tried to open a connection to a room while already being connected to the room, the new connection would be blocked. Now, when the user opens another connection, the new connection will be opened and initialized with their latest presence.
11
+
12
+ Users can only have 1 identity and 1 presence, which means that all presence updates will sync across all of that user's connections. This means that even when a user opens multiple connections, they will only appear once in `others`.
13
+
14
+ ### Patch Changes
15
+
16
+ - 6422f3b: In certain edge-cases, WebSocket connections can silently die in all runtimes, causing connected clients to see the presence of other connections that aren't connected anymore.
17
+
18
+ The `IORoom` now automatically garbage collects dead websockets that haven't been pinged in a long time. Garbage collection happens on WebSocket ping/pong messages every 60 seconds.
19
+
20
+ However, garbage collection does not automatically happen for Cloudflare Worker Durable Objects using websocket hiberation because ping/pong messages happen outside of the Durable Object's compute. Therefore `IORoom` now provides a method `garbageCollect` that can be called manually to clear any dead websockets whenever the user chooses.
21
+
22
+ For example, within a Cloudflare Worker Durable Object, an [alarm](https://developers.cloudflare.com/durable-objects/api/alarms/) can be set to periodically run garbage collection for you:
23
+
24
+ ```ts
25
+ import { InferIORoom } from "@pluv/io";
26
+
27
+ class RoomDurableObject extends DurableObject {
28
+ private _room: InferIORoom<typeof ioServer>;
29
+
30
+ constructor(state: DurableObjectState, env: Env) {
31
+ super(state, env);
32
+
33
+ this._room = io.createRoom(state.id.toString(), { env, state });
34
+ }
35
+
36
+ // ... WebSocket handlers here
37
+
38
+ async fetch(request: Request): Promise<Response> {
39
+ // ... WebSocket validation and pair creation here
40
+
41
+ const alarm = await this.ctx.storage.getAlarm();
42
+
43
+ // Start running the garbage collection interval
44
+ if (alarm !== null) await this.ctx.storage.setAlarm(Date.now() + 60_000);
45
+
46
+ await this._room.register(server, { env: this.env, request });
47
+
48
+ // ... Return response
49
+ }
50
+
51
+ async alarm(): Promise<void> {
52
+ await this._room.garbageCollect();
53
+
54
+ // Garbage collect every 1 minute
55
+ await this.ctx.storage.setAlarm(Date.now() + 60_000);
56
+ }
57
+ }
58
+ ```
59
+
60
+ - a663c65: Update websocket session connection ids to be deterministic based on the user's id if authorization is configured (i.e. connection id will now just be the user's id with a prefix).
61
+ - 555b88d: Added a new field `presence` to the server's event resolver context to view the message sender's currently known presence.
62
+ - @pluv/crdt@0.41.0
63
+ - @pluv/types@0.41.0
64
+
65
+ ## 0.40.2
66
+
67
+ ### Patch Changes
68
+
69
+ - @pluv/crdt@0.40.2
70
+ - @pluv/types@0.40.2
71
+
3
72
  ## 0.40.1
4
73
 
5
74
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { JsonObject, MaybePromise, IOAuthorize, IOEventMessage, EventRecord, ProcedureLike, InputZodLike, IORouterLike, InferIOAuthorizeUser, BaseUser, UndefinedProps, Id, InferEventMessage, InferEventsOutput, Maybe, IOLike, InferIOOutput, InferIOInput, InferIOAuthorize } from '@pluv/types';
1
+ import { JsonObject, IOAuthorize, InferIOAuthorizeUser, MaybePromise, IOEventMessage, EventRecord, ProcedureLike, InputZodLike, IORouterLike, BaseUser, UndefinedProps, Id, InferEventMessage, InferEventsOutput, Maybe, IOLike, InferIOOutput, InferIOInput, InferIOAuthorize } from '@pluv/types';
2
2
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
3
3
  import { AbstractCrdtDocFactory, AbstractCrdtDoc } from '@pluv/crdt';
4
4
 
@@ -45,7 +45,7 @@ interface AbstractWebSocketConfig {
45
45
  platform: AbstractPlatform<any>;
46
46
  room: string;
47
47
  }
48
- declare abstract class AbstractWebSocket<TWebSocket = any> {
48
+ declare abstract class AbstractWebSocket<TWebSocket = any, TAuthorize extends IOAuthorize<any, any> | null = null> {
49
49
  /** The connection is not yet open. */
50
50
  readonly CONNECTING = 0;
51
51
  /** The connection is open and ready to communicate. */
@@ -57,13 +57,14 @@ declare abstract class AbstractWebSocket<TWebSocket = any> {
57
57
  readonly persistence: AbstractPersistence;
58
58
  readonly room: string;
59
59
  readonly webSocket: TWebSocket;
60
- private _user;
61
60
  protected readonly _platform: AbstractPlatform<this>;
62
61
  abstract set presence(presence: JsonObject | null);
63
62
  abstract get readyState(): 0 | 1 | 2 | 3;
63
+ abstract get session(): WebSocketSession<TAuthorize>;
64
64
  abstract get sessionId(): string;
65
65
  abstract get state(): WebSocketSerializedState;
66
66
  abstract set state(state: WebSocketSerializedState);
67
+ abstract set user(user: InferIOAuthorizeUser<TAuthorize>);
67
68
  constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig);
68
69
  /**
69
70
  * TODO
@@ -78,7 +79,6 @@ declare abstract class AbstractWebSocket<TWebSocket = any> {
78
79
  abstract close(code?: number | undefined, reason?: string | undefined): void;
79
80
  abstract send(message: ArrayBuffer | ArrayBufferView | string): void;
80
81
  abstract terminate(): void;
81
- getSession<TAuthorize extends IOAuthorize<any, any> | null>(): Promise<WebSocketSession<TAuthorize>>;
82
82
  handleError(params: AbstractWebSocketHandleErrorParams): void;
83
83
  sendMessage<TMessage extends IOEventMessage<any> = IOEventMessage<any>>(data: TMessage): void;
84
84
  }
@@ -119,12 +119,6 @@ declare class PluvRouter<TPlatform extends AbstractPlatform<any> = AbstractPlatf
119
119
  private _isValidEventName;
120
120
  }
121
121
 
122
- declare global {
123
- var console: {
124
- error: (...data: any[]) => void;
125
- log: (...data: any[]) => void;
126
- };
127
- }
128
122
  type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = TContext | ((params: InferRoomContextType<TPlatform>) => TContext);
129
123
  interface CrdtLibraryType {
130
124
  doc: (value: any) => AbstractCrdtDocFactory<any>;
@@ -135,9 +129,12 @@ type EventResolver<TKind extends EventResolverKind = EventResolverKind, TPlatfor
135
129
  interface EventResolverContext<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}> {
136
130
  context: TContext;
137
131
  doc: AbstractCrdtDoc<any>;
132
+ garbageCollect: () => Promise<void>;
133
+ presence: JsonObject | null;
138
134
  room: string;
139
135
  session: TKind extends "sync" ? WebSocketSession<TAuthorize> | null : WebSocketSession<TAuthorize>;
140
136
  sessions: readonly WebSocketSession<TAuthorize>[];
137
+ time: number;
141
138
  }
142
139
  type SendMessageOptions = {
143
140
  type?: "broadcast";
@@ -149,6 +146,7 @@ type SendMessageOptions = {
149
146
  };
150
147
  interface WebSocketSessionTimers {
151
148
  ping: number;
149
+ presence: number | null;
152
150
  }
153
151
  interface WebSocketSerializedState {
154
152
  presence: JsonObject | null;
@@ -254,22 +252,22 @@ interface JWT<TUser extends BaseUser> {
254
252
  sub: string;
255
253
  user: TUser;
256
254
  }
257
- type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform> = {
255
+ type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform<any, any>> = {
258
256
  maxAge?: number;
259
257
  room: string;
260
258
  user: TUser extends BaseUser ? TUser : "Error: Cannot create token without authorization!";
261
259
  } & InferInitContextType<TPlatform>;
262
260
  interface AuthorizeParams {
263
- platform: AbstractPlatform;
261
+ platform: AbstractPlatform<any, any>;
264
262
  secret: string;
265
263
  }
266
264
  interface AuthorizeModule {
267
265
  decode: <TUser extends BaseUser>(jwt: string) => Promise<TUser | null>;
268
- encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform>(params: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
266
+ encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(params: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
269
267
  }
270
268
  declare const authorize: (params: AuthorizeParams) => {
271
269
  decode: <TUser extends BaseUser>(jwt: string) => Promise<JWT<TUser> | null>;
272
- encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform>(encodeParams: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
270
+ encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(encodeParams: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
273
271
  };
274
272
 
275
273
  type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> = TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
@@ -284,7 +282,7 @@ type AbstractPlatformConfig<TRoomContext extends Record<string, any> = {}> = {
284
282
  interface ConvertWebSocketConfig {
285
283
  room: string;
286
284
  }
287
- declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket = AbstractWebSocket, TInitContext extends Record<string, any> = {}, TRoomContext extends Record<string, any> = {}, TConfig extends PlatformConfig = PlatformConfig> {
285
+ declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket<any, any> = AbstractWebSocket<any, any>, TInitContext extends Record<string, any> = {}, TRoomContext extends Record<string, any> = {}, TConfig extends PlatformConfig = PlatformConfig> {
288
286
  protected readonly _initContext: TInitContext | undefined;
289
287
  protected readonly _roomContext: TRoomContext | undefined;
290
288
  private _initialized;
@@ -296,8 +294,8 @@ declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket = A
296
294
  abstract readonly _name: string;
297
295
  constructor(config?: AbstractPlatformConfig<TRoomContext>);
298
296
  abstract acceptWebSocket(webSocket: AbstractWebSocket): Promise<void>;
299
- abstract convertWebSocket(webSocket: InferWebSocketSource<TWebSocket>, config: ConvertWebSocketConfig): AbstractWebSocket;
300
- abstract getLastPing(webSocket: AbstractWebSocket): number | null;
297
+ abstract convertWebSocket(webSocket: InferWebSocketSource<TWebSocket>, config: ConvertWebSocketConfig): AbstractWebSocket<any, any>;
298
+ abstract getLastPing(webSocket: AbstractWebSocket<any, any>): number | null;
301
299
  abstract getSerializedState(webSocket: InferWebSocketSource<TWebSocket>): WebSocketSerializedState | null;
302
300
  abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;
303
301
  abstract getWebSockets(): readonly InferWebSocketSource<TWebSocket>[];
@@ -335,6 +333,7 @@ type WebSocketRegisterConfig<TPlatform extends AbstractPlatform<any> = AbstractP
335
333
  declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
336
334
  readonly id: string;
337
335
  private _doc;
336
+ private _lastGarbageCollectMs;
338
337
  private _uninitialize;
339
338
  private readonly _authorize;
340
339
  private readonly _context;
@@ -344,6 +343,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
344
343
  private readonly _platform;
345
344
  private readonly _router;
346
345
  private readonly _sessions;
346
+ private readonly _userSessionss;
347
347
  /**
348
348
  * @ignore
349
349
  * @readonly
@@ -368,6 +368,11 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
368
368
  * the room
369
369
  */
370
370
  evictAll(): Promise<void>;
371
+ /**
372
+ * @description The IORoom will garbage collect occasionally as connections ping/pong the
373
+ * server
374
+ */
375
+ garbageCollect(): Promise<void>;
371
376
  getSize(): number;
372
377
  onClose(webSocket: WebSocketType<TPlatform>): (event: AbstractCloseEvent) => Promise<void>;
373
378
  onError(webSocket: WebSocketType<TPlatform>): (event: AbstractErrorEvent) => Promise<void>;
@@ -375,6 +380,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
375
380
  register(webSocket: InferPlatformWebSocketSource<TPlatform>, ...options: keyof InferInitContextType<TPlatform> extends never ? [{
376
381
  token?: string;
377
382
  }?] : [WebSocketRegisterConfig<TPlatform>]): Promise<void>;
383
+ private _addUserSession;
378
384
  private _broadcast;
379
385
  private _closeWebSockets;
380
386
  private _emitQuitters;
@@ -384,6 +390,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
384
390
  private _getAuthorizedUser;
385
391
  private _getInitialDoc;
386
392
  private _getIOAuthorize;
393
+ private _getLatestPresence;
387
394
  private _getProcedure;
388
395
  private _getProcedureInputs;
389
396
  private _getSession;
@@ -393,6 +400,8 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
393
400
  private _onClose;
394
401
  private _onMessage;
395
402
  private _parseMessage;
403
+ private _setPresence;
404
+ private _removeUserSession;
396
405
  private _sendMessage;
397
406
  private _sendBroadcastMessage;
398
407
  private _sendSelfMessage;
@@ -400,7 +409,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
400
409
  }
401
410
 
402
411
  type InferIORoom<TServer extends PluvServer<any, any, any, any>> = TServer extends PluvServer<infer IPlatform, infer IAuthorize, infer IContext, infer IEvents> ? IORoom<IPlatform, IAuthorize, IContext, IEvents> : never;
403
- type PluvServerConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
412
+ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
404
413
  authorize?: TAuthorize;
405
414
  context?: PluvContext<TPlatform, TContext>;
406
415
  crdt?: {
@@ -411,11 +420,11 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform
411
420
  platform: TPlatform;
412
421
  router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
413
422
  };
414
- type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
423
+ type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
415
424
  debug?: boolean;
416
425
  };
417
- type CreateRoomOptions<TPlatform extends AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
418
- declare class PluvServer<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
426
+ type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
427
+ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
419
428
  readonly version: string;
420
429
  private readonly _authorize;
421
430
  private readonly _baseRouter;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { JsonObject, MaybePromise, IOAuthorize, IOEventMessage, EventRecord, ProcedureLike, InputZodLike, IORouterLike, InferIOAuthorizeUser, BaseUser, UndefinedProps, Id, InferEventMessage, InferEventsOutput, Maybe, IOLike, InferIOOutput, InferIOInput, InferIOAuthorize } from '@pluv/types';
1
+ import { JsonObject, IOAuthorize, InferIOAuthorizeUser, MaybePromise, IOEventMessage, EventRecord, ProcedureLike, InputZodLike, IORouterLike, BaseUser, UndefinedProps, Id, InferEventMessage, InferEventsOutput, Maybe, IOLike, InferIOOutput, InferIOInput, InferIOAuthorize } from '@pluv/types';
2
2
  export { BaseUser, EventMessage, EventRecord, IOEventMessage } from '@pluv/types';
3
3
  import { AbstractCrdtDocFactory, AbstractCrdtDoc } from '@pluv/crdt';
4
4
 
@@ -45,7 +45,7 @@ interface AbstractWebSocketConfig {
45
45
  platform: AbstractPlatform<any>;
46
46
  room: string;
47
47
  }
48
- declare abstract class AbstractWebSocket<TWebSocket = any> {
48
+ declare abstract class AbstractWebSocket<TWebSocket = any, TAuthorize extends IOAuthorize<any, any> | null = null> {
49
49
  /** The connection is not yet open. */
50
50
  readonly CONNECTING = 0;
51
51
  /** The connection is open and ready to communicate. */
@@ -57,13 +57,14 @@ declare abstract class AbstractWebSocket<TWebSocket = any> {
57
57
  readonly persistence: AbstractPersistence;
58
58
  readonly room: string;
59
59
  readonly webSocket: TWebSocket;
60
- private _user;
61
60
  protected readonly _platform: AbstractPlatform<this>;
62
61
  abstract set presence(presence: JsonObject | null);
63
62
  abstract get readyState(): 0 | 1 | 2 | 3;
63
+ abstract get session(): WebSocketSession<TAuthorize>;
64
64
  abstract get sessionId(): string;
65
65
  abstract get state(): WebSocketSerializedState;
66
66
  abstract set state(state: WebSocketSerializedState);
67
+ abstract set user(user: InferIOAuthorizeUser<TAuthorize>);
67
68
  constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig);
68
69
  /**
69
70
  * TODO
@@ -78,7 +79,6 @@ declare abstract class AbstractWebSocket<TWebSocket = any> {
78
79
  abstract close(code?: number | undefined, reason?: string | undefined): void;
79
80
  abstract send(message: ArrayBuffer | ArrayBufferView | string): void;
80
81
  abstract terminate(): void;
81
- getSession<TAuthorize extends IOAuthorize<any, any> | null>(): Promise<WebSocketSession<TAuthorize>>;
82
82
  handleError(params: AbstractWebSocketHandleErrorParams): void;
83
83
  sendMessage<TMessage extends IOEventMessage<any> = IOEventMessage<any>>(data: TMessage): void;
84
84
  }
@@ -119,12 +119,6 @@ declare class PluvRouter<TPlatform extends AbstractPlatform<any> = AbstractPlatf
119
119
  private _isValidEventName;
120
120
  }
121
121
 
122
- declare global {
123
- var console: {
124
- error: (...data: any[]) => void;
125
- log: (...data: any[]) => void;
126
- };
127
- }
128
122
  type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = TContext | ((params: InferRoomContextType<TPlatform>) => TContext);
129
123
  interface CrdtLibraryType {
130
124
  doc: (value: any) => AbstractCrdtDocFactory<any>;
@@ -135,9 +129,12 @@ type EventResolver<TKind extends EventResolverKind = EventResolverKind, TPlatfor
135
129
  interface EventResolverContext<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}> {
136
130
  context: TContext;
137
131
  doc: AbstractCrdtDoc<any>;
132
+ garbageCollect: () => Promise<void>;
133
+ presence: JsonObject | null;
138
134
  room: string;
139
135
  session: TKind extends "sync" ? WebSocketSession<TAuthorize> | null : WebSocketSession<TAuthorize>;
140
136
  sessions: readonly WebSocketSession<TAuthorize>[];
137
+ time: number;
141
138
  }
142
139
  type SendMessageOptions = {
143
140
  type?: "broadcast";
@@ -149,6 +146,7 @@ type SendMessageOptions = {
149
146
  };
150
147
  interface WebSocketSessionTimers {
151
148
  ping: number;
149
+ presence: number | null;
152
150
  }
153
151
  interface WebSocketSerializedState {
154
152
  presence: JsonObject | null;
@@ -254,22 +252,22 @@ interface JWT<TUser extends BaseUser> {
254
252
  sub: string;
255
253
  user: TUser;
256
254
  }
257
- type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform> = {
255
+ type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform<any, any>> = {
258
256
  maxAge?: number;
259
257
  room: string;
260
258
  user: TUser extends BaseUser ? TUser : "Error: Cannot create token without authorization!";
261
259
  } & InferInitContextType<TPlatform>;
262
260
  interface AuthorizeParams {
263
- platform: AbstractPlatform;
261
+ platform: AbstractPlatform<any, any>;
264
262
  secret: string;
265
263
  }
266
264
  interface AuthorizeModule {
267
265
  decode: <TUser extends BaseUser>(jwt: string) => Promise<TUser | null>;
268
- encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform>(params: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
266
+ encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(params: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
269
267
  }
270
268
  declare const authorize: (params: AuthorizeParams) => {
271
269
  decode: <TUser extends BaseUser>(jwt: string) => Promise<JWT<TUser> | null>;
272
- encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform>(encodeParams: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
270
+ encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(encodeParams: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
273
271
  };
274
272
 
275
273
  type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> = TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
@@ -284,7 +282,7 @@ type AbstractPlatformConfig<TRoomContext extends Record<string, any> = {}> = {
284
282
  interface ConvertWebSocketConfig {
285
283
  room: string;
286
284
  }
287
- declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket = AbstractWebSocket, TInitContext extends Record<string, any> = {}, TRoomContext extends Record<string, any> = {}, TConfig extends PlatformConfig = PlatformConfig> {
285
+ declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket<any, any> = AbstractWebSocket<any, any>, TInitContext extends Record<string, any> = {}, TRoomContext extends Record<string, any> = {}, TConfig extends PlatformConfig = PlatformConfig> {
288
286
  protected readonly _initContext: TInitContext | undefined;
289
287
  protected readonly _roomContext: TRoomContext | undefined;
290
288
  private _initialized;
@@ -296,8 +294,8 @@ declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket = A
296
294
  abstract readonly _name: string;
297
295
  constructor(config?: AbstractPlatformConfig<TRoomContext>);
298
296
  abstract acceptWebSocket(webSocket: AbstractWebSocket): Promise<void>;
299
- abstract convertWebSocket(webSocket: InferWebSocketSource<TWebSocket>, config: ConvertWebSocketConfig): AbstractWebSocket;
300
- abstract getLastPing(webSocket: AbstractWebSocket): number | null;
297
+ abstract convertWebSocket(webSocket: InferWebSocketSource<TWebSocket>, config: ConvertWebSocketConfig): AbstractWebSocket<any, any>;
298
+ abstract getLastPing(webSocket: AbstractWebSocket<any, any>): number | null;
301
299
  abstract getSerializedState(webSocket: InferWebSocketSource<TWebSocket>): WebSocketSerializedState | null;
302
300
  abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;
303
301
  abstract getWebSockets(): readonly InferWebSocketSource<TWebSocket>[];
@@ -335,6 +333,7 @@ type WebSocketRegisterConfig<TPlatform extends AbstractPlatform<any> = AbstractP
335
333
  declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
336
334
  readonly id: string;
337
335
  private _doc;
336
+ private _lastGarbageCollectMs;
338
337
  private _uninitialize;
339
338
  private readonly _authorize;
340
339
  private readonly _context;
@@ -344,6 +343,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
344
343
  private readonly _platform;
345
344
  private readonly _router;
346
345
  private readonly _sessions;
346
+ private readonly _userSessionss;
347
347
  /**
348
348
  * @ignore
349
349
  * @readonly
@@ -368,6 +368,11 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
368
368
  * the room
369
369
  */
370
370
  evictAll(): Promise<void>;
371
+ /**
372
+ * @description The IORoom will garbage collect occasionally as connections ping/pong the
373
+ * server
374
+ */
375
+ garbageCollect(): Promise<void>;
371
376
  getSize(): number;
372
377
  onClose(webSocket: WebSocketType<TPlatform>): (event: AbstractCloseEvent) => Promise<void>;
373
378
  onError(webSocket: WebSocketType<TPlatform>): (event: AbstractErrorEvent) => Promise<void>;
@@ -375,6 +380,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
375
380
  register(webSocket: InferPlatformWebSocketSource<TPlatform>, ...options: keyof InferInitContextType<TPlatform> extends never ? [{
376
381
  token?: string;
377
382
  }?] : [WebSocketRegisterConfig<TPlatform>]): Promise<void>;
383
+ private _addUserSession;
378
384
  private _broadcast;
379
385
  private _closeWebSockets;
380
386
  private _emitQuitters;
@@ -384,6 +390,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
384
390
  private _getAuthorizedUser;
385
391
  private _getInitialDoc;
386
392
  private _getIOAuthorize;
393
+ private _getLatestPresence;
387
394
  private _getProcedure;
388
395
  private _getProcedureInputs;
389
396
  private _getSession;
@@ -393,6 +400,8 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
393
400
  private _onClose;
394
401
  private _onMessage;
395
402
  private _parseMessage;
403
+ private _setPresence;
404
+ private _removeUserSession;
396
405
  private _sendMessage;
397
406
  private _sendBroadcastMessage;
398
407
  private _sendSelfMessage;
@@ -400,7 +409,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
400
409
  }
401
410
 
402
411
  type InferIORoom<TServer extends PluvServer<any, any, any, any>> = TServer extends PluvServer<infer IPlatform, infer IAuthorize, infer IContext, infer IEvents> ? IORoom<IPlatform, IAuthorize, IContext, IEvents> : never;
403
- type PluvServerConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
412
+ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
404
413
  authorize?: TAuthorize;
405
414
  context?: PluvContext<TPlatform, TContext>;
406
415
  crdt?: {
@@ -411,11 +420,11 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform
411
420
  platform: TPlatform;
412
421
  router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
413
422
  };
414
- type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
423
+ type BaseCreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
415
424
  debug?: boolean;
416
425
  };
417
- type CreateRoomOptions<TPlatform extends AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
418
- declare class PluvServer<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
426
+ type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = keyof InferRoomContextType<TPlatform> extends never ? [BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>] | [] : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
427
+ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TEvents> {
419
428
  readonly version: string;
420
429
  private readonly _authorize;
421
430
  private readonly _baseRouter;