@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.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +69 -0
- package/dist/index.d.mts +30 -21
- package/dist/index.d.ts +30 -21
- package/dist/index.js +198 -75
- package/dist/index.mjs +198 -75
- package/package.json +7 -7
- package/src/AbstractPlatform.ts +3 -3
- package/src/AbstractWebSocket.ts +3 -20
- package/src/IORoom.ts +248 -65
- package/src/PluvServer.ts +39 -12
- package/src/authorize.ts +4 -4
- package/src/constants.ts +3 -0
- package/src/types.ts +4 -8
- package/src/version.generated.ts +1 -1
- package/tsconfig.json +5 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/io@0.
|
|
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.
|
|
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
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
[34mCLI[39m Target: es6
|
|
13
13
|
[34mESM[39m Build start
|
|
14
14
|
[34mCJS[39m Build start
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
15
|
+
[32mCJS[39m [1mdist/index.js [22m[32m51.27 KB[39m
|
|
16
|
+
[32mCJS[39m ⚡️ Build success in 95ms
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m49.09 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 106ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 2982ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m31.42 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m31.42 KB[39m
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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;
|