@pluv/io 0.41.7 → 0.43.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 +45 -0
- package/dist/index.d.mts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +73 -34
- package/dist/index.mjs +73 -34
- package/package.json +6 -6
- package/src/AbstractPersistence.ts +3 -1
- package/src/IORoom.ts +12 -0
- package/src/Persistence.ts +11 -3
- package/src/PluvIO.ts +40 -2
- package/src/PluvServer.ts +28 -44
- package/src/constants.ts +2 -0
- package/src/createIO.ts +4 -2
- package/src/index.ts +1 -0
- package/src/types.ts +19 -0
- package/src/version.generated.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/io@0.
|
|
2
|
+
> @pluv/io@0.43.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.43.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
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
17
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
15
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m50.72 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 150ms
|
|
17
|
+
[32mCJS[39m [1mdist/index.js [22m[32m52.90 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 151ms
|
|
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 2855ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m32.19 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m32.19 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# @pluv/io
|
|
2
2
|
|
|
3
|
+
## 0.43.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0063e80: **BREAKING** Added a configurable limit for the max-size (in bytes) for storage (defaulting to 30MB). To unset this limit, you can set this limit to `0` or `null`.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
createIO({
|
|
11
|
+
limits: {
|
|
12
|
+
storageMaxSize: null,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- 4b0fbb3: **BREAKING** Added the ability to configure the limits for user object size, presence size, and user id length. User id is now defaulted to be limited to 128 characters. To unset this limit, you can set this limit to `0` or `null`.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// defaults
|
|
21
|
+
createIO(
|
|
22
|
+
platformCloudflare({
|
|
23
|
+
limits: {
|
|
24
|
+
presenceMaxSize: 512,
|
|
25
|
+
storageMaxSize: 31_457_280,
|
|
26
|
+
userIdMaxLength: 128,
|
|
27
|
+
userMaxSize: 512,
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- e60a677: Fix 1 and 2 character long room names not being supported.
|
|
36
|
+
- 244a973: Fixed not applying user size limits when calling `PluvIO.authorize` and not allowing it to be overwritten for `platformPluv`.
|
|
37
|
+
- @pluv/crdt@0.43.0
|
|
38
|
+
- @pluv/types@0.43.0
|
|
39
|
+
|
|
40
|
+
## 0.42.0
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- cf1529a: Each time garbage collection is run, a sync-state event will be emitted to connected sessions with info of currently connected users.
|
|
45
|
+
- @pluv/crdt@0.42.0
|
|
46
|
+
- @pluv/types@0.42.0
|
|
47
|
+
|
|
3
48
|
## 0.41.7
|
|
4
49
|
|
|
5
50
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -9,8 +9,9 @@ declare abstract class AbstractPersistence {
|
|
|
9
9
|
abstract deleteUsers(room: string): Promise<void>;
|
|
10
10
|
abstract getStorageState(room: string): Promise<string | null>;
|
|
11
11
|
abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
12
|
-
abstract getUsers(room: string): Promise<
|
|
12
|
+
abstract getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
13
13
|
abstract getUsersSize(room: string): Promise<number>;
|
|
14
|
+
abstract initialize(roomContext: any): AbstractPersistence;
|
|
14
15
|
abstract setStorageState(room: string, state: string): Promise<void>;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -194,6 +195,24 @@ type ResolvedPluvIOAuthorize<TPlatform extends AbstractPlatform<any, any, any, a
|
|
|
194
195
|
secret?: string;
|
|
195
196
|
}, Exclude<"secret", InferPlatformAuthorizeProperties<TPlatform>>>;
|
|
196
197
|
type PluvIOAuthorize<TPlatform extends AbstractPlatform<any, any, any, any>, TUser extends BaseUser | null = any, TContext extends Record<string, unknown> = {}> = (TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null) | ((context: TContext) => TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null);
|
|
198
|
+
interface PluvIOLimits {
|
|
199
|
+
/**
|
|
200
|
+
* @description Maximum size of presence object in bytes
|
|
201
|
+
*/
|
|
202
|
+
presenceMaxSize?: number | null;
|
|
203
|
+
/**
|
|
204
|
+
* @description Maximum size of storage state in bytes
|
|
205
|
+
*/
|
|
206
|
+
storageMaxSize?: number | null;
|
|
207
|
+
/**
|
|
208
|
+
* @description Maximum length of user id in characters
|
|
209
|
+
*/
|
|
210
|
+
userIdMaxLength?: number | null;
|
|
211
|
+
/**
|
|
212
|
+
* @description Maximum size of user object in bytes
|
|
213
|
+
*/
|
|
214
|
+
userMaxSize?: number | null;
|
|
215
|
+
}
|
|
197
216
|
type BasePluvIOListeners<TPlatform extends AbstractPlatform<any, any, any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = {
|
|
198
217
|
onRoomDeleted: (event: IORoomListenerEvent<TContext>) => void;
|
|
199
218
|
onRoomMessage: (event: IORoomMessageEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;
|
|
@@ -385,6 +404,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
385
404
|
private _closeWebSockets;
|
|
386
405
|
private _emitQuitters;
|
|
387
406
|
private _emitRegistered;
|
|
407
|
+
private _emitSyncState;
|
|
388
408
|
private _ensureDetached;
|
|
389
409
|
private _getAbstractWs;
|
|
390
410
|
private _getAuthorizedUser;
|
|
@@ -417,6 +437,8 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPla
|
|
|
417
437
|
};
|
|
418
438
|
debug?: boolean;
|
|
419
439
|
getInitialStorage?: GetInitialStorageFn<TContext>;
|
|
440
|
+
limits: PluvIOLimits;
|
|
441
|
+
io: PluvIO<TPlatform, TAuthorize, TContext>;
|
|
420
442
|
platform: TPlatform;
|
|
421
443
|
router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
422
444
|
};
|
|
@@ -432,6 +454,8 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
|
|
|
432
454
|
private readonly _crdt;
|
|
433
455
|
private readonly _debug;
|
|
434
456
|
private readonly _getInitialStorage;
|
|
457
|
+
private readonly _io;
|
|
458
|
+
private readonly _limits;
|
|
435
459
|
private readonly _platform;
|
|
436
460
|
private readonly _router;
|
|
437
461
|
get fetch(): (...args: any[]) => Promise<any>;
|
|
@@ -450,7 +474,6 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
|
|
|
450
474
|
constructor(options: PluvServerConfig<TPlatform, TAuthorize, TContext, TEvents>);
|
|
451
475
|
createRoom(room: string, ...options: CreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>): IORoom<TPlatform, TAuthorize, TContext, TEvents>;
|
|
452
476
|
createToken(params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>): Promise<string>;
|
|
453
|
-
private _getIOAuthorize;
|
|
454
477
|
private _getListeners;
|
|
455
478
|
private _logDebug;
|
|
456
479
|
}
|
|
@@ -460,6 +483,7 @@ type PluvIOConfig<TPlatform extends AbstractPlatform<any>, TAuthorize extends IO
|
|
|
460
483
|
context?: PluvContext<TPlatform, TContext>;
|
|
461
484
|
crdt?: CrdtLibraryType;
|
|
462
485
|
debug?: boolean;
|
|
486
|
+
limits?: PluvIOLimits;
|
|
463
487
|
platform: TPlatform;
|
|
464
488
|
};
|
|
465
489
|
type ResolvedServerConfig<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>> & PluvIORouter<TPlatform, TAuthorize, TContext, TEvents> & {
|
|
@@ -474,6 +498,7 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
474
498
|
private readonly _context;
|
|
475
499
|
private readonly _crdt;
|
|
476
500
|
private readonly _debug;
|
|
501
|
+
private readonly _limits;
|
|
477
502
|
private readonly _platform;
|
|
478
503
|
get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}>;
|
|
479
504
|
constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext>);
|
|
@@ -489,8 +514,9 @@ type CreateIOParams<TPlatform extends AbstractPlatform<any> = AbstractPlatform<a
|
|
|
489
514
|
context?: PluvContext<TPlatform, TContext>;
|
|
490
515
|
crdt?: CrdtLibraryType;
|
|
491
516
|
debug?: boolean;
|
|
517
|
+
limits?: PluvIOLimits;
|
|
492
518
|
platform: TPlatform;
|
|
493
519
|
};
|
|
494
520
|
declare const createIO: <TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null>(params: CreateIOParams<TPlatform, TContext, TUser>) => PluvIO<TPlatform, PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>, TContext>;
|
|
495
521
|
|
|
496
|
-
export { type AbstractCloseEvent, type AbstractErrorEvent, type AbstractEvent, type AbstractEventMap, type AbstractListener, type AbstractMessageEvent, AbstractPersistence, AbstractPlatform, type AbstractPlatformConfig, AbstractPubSub, AbstractWebSocket, type AbstractWebSocketConfig, type AbstractWebSocketHandleErrorParams, type AuthorizeModule, type AuthorizeParams, type ConvertWebSocketConfig, type CrdtLibraryType, type CreateIOParams, type CreateRoomOptions, type GetInitialStorageFn, type HandleMode, type IOPubSubEventMessage, IORoom, type IORoomListenerEvent, type IORoomMessageEvent, type IOStorageUpdatedEvent, type IOUserConnectedEvent, type IOUserDisconnectedEvent, type InferIORoom, type InferInitContextType, type InferPlatformWebSocketType, type InferRoomContextType, type JWT, type JWTEncodeParams, type MergedRouter, type PlatformConfig, type PluvContext, PluvIO, type PluvIOAuthorize, type PluvIOConfig, type PluvIOListeners, PluvProcedure, type PluvProcedureConfig, PluvRouter, type PluvRouterEventConfig, PluvServer, type PluvServerConfig, type ServerConfig, type WebSocketRegistrationMode, type WebSocketSerializedState, type WebSocketSession, authorize, createIO };
|
|
522
|
+
export { type AbstractCloseEvent, type AbstractErrorEvent, type AbstractEvent, type AbstractEventMap, type AbstractListener, type AbstractMessageEvent, AbstractPersistence, AbstractPlatform, type AbstractPlatformConfig, AbstractPubSub, AbstractWebSocket, type AbstractWebSocketConfig, type AbstractWebSocketHandleErrorParams, type AuthorizeModule, type AuthorizeParams, type ConvertWebSocketConfig, type CrdtLibraryType, type CreateIOParams, type CreateRoomOptions, type GetInitialStorageFn, type HandleMode, type IOPubSubEventMessage, IORoom, type IORoomListenerEvent, type IORoomMessageEvent, type IOStorageUpdatedEvent, type IOUserConnectedEvent, type IOUserDisconnectedEvent, type InferIORoom, type InferInitContextType, type InferPlatformWebSocketType, type InferRoomContextType, type JWT, type JWTEncodeParams, type MergedRouter, type PlatformConfig, type PluvContext, PluvIO, type PluvIOAuthorize, type PluvIOConfig, type PluvIOLimits, type PluvIOListeners, PluvProcedure, type PluvProcedureConfig, PluvRouter, type PluvRouterEventConfig, PluvServer, type PluvServerConfig, type ServerConfig, type WebSocketRegistrationMode, type WebSocketSerializedState, type WebSocketSession, authorize, createIO };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,8 +9,9 @@ declare abstract class AbstractPersistence {
|
|
|
9
9
|
abstract deleteUsers(room: string): Promise<void>;
|
|
10
10
|
abstract getStorageState(room: string): Promise<string | null>;
|
|
11
11
|
abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
12
|
-
abstract getUsers(room: string): Promise<
|
|
12
|
+
abstract getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
13
13
|
abstract getUsersSize(room: string): Promise<number>;
|
|
14
|
+
abstract initialize(roomContext: any): AbstractPersistence;
|
|
14
15
|
abstract setStorageState(room: string, state: string): Promise<void>;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -194,6 +195,24 @@ type ResolvedPluvIOAuthorize<TPlatform extends AbstractPlatform<any, any, any, a
|
|
|
194
195
|
secret?: string;
|
|
195
196
|
}, Exclude<"secret", InferPlatformAuthorizeProperties<TPlatform>>>;
|
|
196
197
|
type PluvIOAuthorize<TPlatform extends AbstractPlatform<any, any, any, any>, TUser extends BaseUser | null = any, TContext extends Record<string, unknown> = {}> = (TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null) | ((context: TContext) => TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null);
|
|
198
|
+
interface PluvIOLimits {
|
|
199
|
+
/**
|
|
200
|
+
* @description Maximum size of presence object in bytes
|
|
201
|
+
*/
|
|
202
|
+
presenceMaxSize?: number | null;
|
|
203
|
+
/**
|
|
204
|
+
* @description Maximum size of storage state in bytes
|
|
205
|
+
*/
|
|
206
|
+
storageMaxSize?: number | null;
|
|
207
|
+
/**
|
|
208
|
+
* @description Maximum length of user id in characters
|
|
209
|
+
*/
|
|
210
|
+
userIdMaxLength?: number | null;
|
|
211
|
+
/**
|
|
212
|
+
* @description Maximum size of user object in bytes
|
|
213
|
+
*/
|
|
214
|
+
userMaxSize?: number | null;
|
|
215
|
+
}
|
|
197
216
|
type BasePluvIOListeners<TPlatform extends AbstractPlatform<any, any, any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null, TContext extends Record<string, any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>> = {
|
|
198
217
|
onRoomDeleted: (event: IORoomListenerEvent<TContext>) => void;
|
|
199
218
|
onRoomMessage: (event: IORoomMessageEvent<TPlatform, TAuthorize, TContext, TEvents>) => void;
|
|
@@ -385,6 +404,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
385
404
|
private _closeWebSockets;
|
|
386
405
|
private _emitQuitters;
|
|
387
406
|
private _emitRegistered;
|
|
407
|
+
private _emitSyncState;
|
|
388
408
|
private _ensureDetached;
|
|
389
409
|
private _getAbstractWs;
|
|
390
410
|
private _getAuthorizedUser;
|
|
@@ -417,6 +437,8 @@ type PluvServerConfig<TPlatform extends AbstractPlatform<any, any> = AbstractPla
|
|
|
417
437
|
};
|
|
418
438
|
debug?: boolean;
|
|
419
439
|
getInitialStorage?: GetInitialStorageFn<TContext>;
|
|
440
|
+
limits: PluvIOLimits;
|
|
441
|
+
io: PluvIO<TPlatform, TAuthorize, TContext>;
|
|
420
442
|
platform: TPlatform;
|
|
421
443
|
router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
422
444
|
};
|
|
@@ -432,6 +454,8 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
|
|
|
432
454
|
private readonly _crdt;
|
|
433
455
|
private readonly _debug;
|
|
434
456
|
private readonly _getInitialStorage;
|
|
457
|
+
private readonly _io;
|
|
458
|
+
private readonly _limits;
|
|
435
459
|
private readonly _platform;
|
|
436
460
|
private readonly _router;
|
|
437
461
|
get fetch(): (...args: any[]) => Promise<any>;
|
|
@@ -450,7 +474,6 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
|
|
|
450
474
|
constructor(options: PluvServerConfig<TPlatform, TAuthorize, TContext, TEvents>);
|
|
451
475
|
createRoom(room: string, ...options: CreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents>): IORoom<TPlatform, TAuthorize, TContext, TEvents>;
|
|
452
476
|
createToken(params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>): Promise<string>;
|
|
453
|
-
private _getIOAuthorize;
|
|
454
477
|
private _getListeners;
|
|
455
478
|
private _logDebug;
|
|
456
479
|
}
|
|
@@ -460,6 +483,7 @@ type PluvIOConfig<TPlatform extends AbstractPlatform<any>, TAuthorize extends IO
|
|
|
460
483
|
context?: PluvContext<TPlatform, TContext>;
|
|
461
484
|
crdt?: CrdtLibraryType;
|
|
462
485
|
debug?: boolean;
|
|
486
|
+
limits?: PluvIOLimits;
|
|
463
487
|
platform: TPlatform;
|
|
464
488
|
};
|
|
465
489
|
type ResolvedServerConfig<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>> & PluvIORouter<TPlatform, TAuthorize, TContext, TEvents> & {
|
|
@@ -474,6 +498,7 @@ declare class PluvIO<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
|
|
|
474
498
|
private readonly _context;
|
|
475
499
|
private readonly _crdt;
|
|
476
500
|
private readonly _debug;
|
|
501
|
+
private readonly _limits;
|
|
477
502
|
private readonly _platform;
|
|
478
503
|
get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}>;
|
|
479
504
|
constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext>);
|
|
@@ -489,8 +514,9 @@ type CreateIOParams<TPlatform extends AbstractPlatform<any> = AbstractPlatform<a
|
|
|
489
514
|
context?: PluvContext<TPlatform, TContext>;
|
|
490
515
|
crdt?: CrdtLibraryType;
|
|
491
516
|
debug?: boolean;
|
|
517
|
+
limits?: PluvIOLimits;
|
|
492
518
|
platform: TPlatform;
|
|
493
519
|
};
|
|
494
520
|
declare const createIO: <TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TContext extends Record<string, any> = {}, TUser extends BaseUser | null = null>(params: CreateIOParams<TPlatform, TContext, TUser>) => PluvIO<TPlatform, PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>, TContext>;
|
|
495
521
|
|
|
496
|
-
export { type AbstractCloseEvent, type AbstractErrorEvent, type AbstractEvent, type AbstractEventMap, type AbstractListener, type AbstractMessageEvent, AbstractPersistence, AbstractPlatform, type AbstractPlatformConfig, AbstractPubSub, AbstractWebSocket, type AbstractWebSocketConfig, type AbstractWebSocketHandleErrorParams, type AuthorizeModule, type AuthorizeParams, type ConvertWebSocketConfig, type CrdtLibraryType, type CreateIOParams, type CreateRoomOptions, type GetInitialStorageFn, type HandleMode, type IOPubSubEventMessage, IORoom, type IORoomListenerEvent, type IORoomMessageEvent, type IOStorageUpdatedEvent, type IOUserConnectedEvent, type IOUserDisconnectedEvent, type InferIORoom, type InferInitContextType, type InferPlatformWebSocketType, type InferRoomContextType, type JWT, type JWTEncodeParams, type MergedRouter, type PlatformConfig, type PluvContext, PluvIO, type PluvIOAuthorize, type PluvIOConfig, type PluvIOListeners, PluvProcedure, type PluvProcedureConfig, PluvRouter, type PluvRouterEventConfig, PluvServer, type PluvServerConfig, type ServerConfig, type WebSocketRegistrationMode, type WebSocketSerializedState, type WebSocketSession, authorize, createIO };
|
|
522
|
+
export { type AbstractCloseEvent, type AbstractErrorEvent, type AbstractEvent, type AbstractEventMap, type AbstractListener, type AbstractMessageEvent, AbstractPersistence, AbstractPlatform, type AbstractPlatformConfig, AbstractPubSub, AbstractWebSocket, type AbstractWebSocketConfig, type AbstractWebSocketHandleErrorParams, type AuthorizeModule, type AuthorizeParams, type ConvertWebSocketConfig, type CrdtLibraryType, type CreateIOParams, type CreateRoomOptions, type GetInitialStorageFn, type HandleMode, type IOPubSubEventMessage, IORoom, type IORoomListenerEvent, type IORoomMessageEvent, type IOStorageUpdatedEvent, type IOUserConnectedEvent, type IOUserDisconnectedEvent, type InferIORoom, type InferInitContextType, type InferPlatformWebSocketType, type InferRoomContextType, type JWT, type JWTEncodeParams, type MergedRouter, type PlatformConfig, type PluvContext, PluvIO, type PluvIOAuthorize, type PluvIOConfig, type PluvIOLimits, type PluvIOListeners, PluvProcedure, type PluvProcedureConfig, PluvRouter, type PluvRouterEventConfig, PluvServer, type PluvServerConfig, type ServerConfig, type WebSocketRegistrationMode, type WebSocketSerializedState, type WebSocketSession, authorize, createIO };
|
package/dist/index.js
CHANGED
|
@@ -145,9 +145,19 @@ var Persistence = class extends AbstractPersistence {
|
|
|
145
145
|
return Promise.resolve(user != null ? user : null);
|
|
146
146
|
}
|
|
147
147
|
getUsers(room) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
return __async(this, null, function* () {
|
|
149
|
+
const users = this._users.get(room);
|
|
150
|
+
if (!users) return /* @__PURE__ */ new Map();
|
|
151
|
+
return users;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
getUsersSize(room) {
|
|
155
|
+
return __async(this, null, function* () {
|
|
156
|
+
return this._users.size;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
initialize(roomContext) {
|
|
160
|
+
return this;
|
|
151
161
|
}
|
|
152
162
|
setStorageState(room, state) {
|
|
153
163
|
this._storages.set(room, state);
|
|
@@ -389,6 +399,8 @@ var import_kleur = __toESM(require("kleur"));
|
|
|
389
399
|
// src/constants.ts
|
|
390
400
|
var GARBAGE_COLLECT_INTERVAL_MS = 6e4;
|
|
391
401
|
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
402
|
+
var MAX_STORAGE_SIZE_BYTES = 31457280;
|
|
403
|
+
var MAX_USER_ID_LENGTH = 128;
|
|
392
404
|
var MAX_USER_SIZE_BYTES = 512;
|
|
393
405
|
var PING_TIMEOUT_MS = 3e4;
|
|
394
406
|
|
|
@@ -503,6 +515,7 @@ var IORoom = class {
|
|
|
503
515
|
return __async(this, null, function* () {
|
|
504
516
|
this._lastGarbageCollectMs = Date.now();
|
|
505
517
|
this._emitQuitters();
|
|
518
|
+
this._emitSyncState();
|
|
506
519
|
});
|
|
507
520
|
}
|
|
508
521
|
getSize() {
|
|
@@ -695,6 +708,17 @@ var IORoom = class {
|
|
|
695
708
|
}
|
|
696
709
|
});
|
|
697
710
|
}
|
|
711
|
+
_emitSyncState() {
|
|
712
|
+
return __async(this, null, function* () {
|
|
713
|
+
const connectionIds = yield this._platform.persistence.getUsers(this.id).then((map) => Object.keys(map));
|
|
714
|
+
yield this._broadcast({
|
|
715
|
+
message: {
|
|
716
|
+
type: "$syncStateReceived",
|
|
717
|
+
data: { connectionIds }
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
}
|
|
698
722
|
_ensureDetached() {
|
|
699
723
|
if (this._platform._config.registrationMode === "detached") return;
|
|
700
724
|
throw new Error("Platform must use detached mode");
|
|
@@ -1117,7 +1141,7 @@ var pickBy = (obj, by) => {
|
|
|
1117
1141
|
};
|
|
1118
1142
|
|
|
1119
1143
|
// src/version.generated.ts
|
|
1120
|
-
var version = "0.
|
|
1144
|
+
var version = "0.43.0";
|
|
1121
1145
|
|
|
1122
1146
|
// src/PluvServer.ts
|
|
1123
1147
|
var PluvServer = class {
|
|
@@ -1168,18 +1192,22 @@ var PluvServer = class {
|
|
|
1168
1192
|
update
|
|
1169
1193
|
];
|
|
1170
1194
|
if (updates.some((_update) => !!_update)) {
|
|
1171
|
-
const
|
|
1172
|
-
yield this._platform.persistence.setStorageState(room,
|
|
1195
|
+
const encodedState2 = doc.batchApplyEncodedState(updates).getEncodedState();
|
|
1196
|
+
yield this._platform.persistence.setStorageState(room, encodedState2);
|
|
1173
1197
|
this._getListeners().onStorageUpdated({
|
|
1174
1198
|
context,
|
|
1175
|
-
encodedState,
|
|
1199
|
+
encodedState: encodedState2,
|
|
1176
1200
|
room,
|
|
1177
1201
|
user: session == null ? void 0 : session.user,
|
|
1178
1202
|
webSocket: session == null ? void 0 : session.webSocket.webSocket
|
|
1179
1203
|
});
|
|
1180
1204
|
}
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1205
|
+
const encodedState = (_b = yield this._platform.persistence.getStorageState(room)) != null ? _b : doc.getEncodedState();
|
|
1206
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1207
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1208
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1209
|
+
}
|
|
1210
|
+
return { $storageReceived: { state: encodedState } };
|
|
1183
1211
|
})),
|
|
1184
1212
|
$ping: this._procedure.self((data, { session }) => {
|
|
1185
1213
|
if (!session) return {};
|
|
@@ -1199,7 +1227,7 @@ var PluvServer = class {
|
|
|
1199
1227
|
const cleanedPatch = pickBy(presence != null ? presence : {}, (value) => typeof value !== "undefined");
|
|
1200
1228
|
const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, cleanedPatch);
|
|
1201
1229
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
1202
|
-
if (bytes >
|
|
1230
|
+
if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
|
|
1203
1231
|
throw new Error(
|
|
1204
1232
|
`Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1205
1233
|
);
|
|
@@ -1219,6 +1247,10 @@ var PluvServer = class {
|
|
|
1219
1247
|
if (origin === "$initialized" && Object.keys(doc.toJson()).length) return {};
|
|
1220
1248
|
const updated = update === null ? doc : doc.applyEncodedState({ update });
|
|
1221
1249
|
const encodedState = updated.getEncodedState();
|
|
1250
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1251
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1252
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1253
|
+
}
|
|
1222
1254
|
this._platform.persistence.setStorageState(room, encodedState).then(() => {
|
|
1223
1255
|
this._getListeners().onStorageUpdated({
|
|
1224
1256
|
context,
|
|
@@ -1237,6 +1269,8 @@ var PluvServer = class {
|
|
|
1237
1269
|
crdt = import_crdt2.noop,
|
|
1238
1270
|
debug = false,
|
|
1239
1271
|
getInitialStorage,
|
|
1272
|
+
io,
|
|
1273
|
+
limits,
|
|
1240
1274
|
platform,
|
|
1241
1275
|
router = new PluvRouter({})
|
|
1242
1276
|
} = options;
|
|
@@ -1244,6 +1278,8 @@ var PluvServer = class {
|
|
|
1244
1278
|
platform.validateConfig(options);
|
|
1245
1279
|
this._crdt = crdt;
|
|
1246
1280
|
this._debug = debug;
|
|
1281
|
+
this._io = io;
|
|
1282
|
+
this._limits = limits;
|
|
1247
1283
|
this._platform = platform;
|
|
1248
1284
|
this._router = router ? PluvRouter.merge(this._baseRouter, router) : this._baseRouter;
|
|
1249
1285
|
if (authorize2) this._authorize = authorize2;
|
|
@@ -1285,7 +1321,7 @@ var PluvServer = class {
|
|
|
1285
1321
|
if (this._platform._config.handleMode !== "io") {
|
|
1286
1322
|
throw new Error(`\`createRoom\` is unsupported for \`${this._platform._name}\``);
|
|
1287
1323
|
}
|
|
1288
|
-
if (!/^[a-z0-9]
|
|
1324
|
+
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room)) throw new Error("Unsupported room name");
|
|
1289
1325
|
const roomContext = platformRoomContext;
|
|
1290
1326
|
const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
|
|
1291
1327
|
const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
|
|
@@ -1319,29 +1355,9 @@ var PluvServer = class {
|
|
|
1319
1355
|
}
|
|
1320
1356
|
createToken(params) {
|
|
1321
1357
|
return __async(this, null, function* () {
|
|
1322
|
-
|
|
1323
|
-
if (!this._authorize) {
|
|
1324
|
-
throw new Error("IO does not specify authorize during initialization.");
|
|
1325
|
-
}
|
|
1326
|
-
const ioAuthorize = this._getIOAuthorize(params);
|
|
1327
|
-
const user = params.user;
|
|
1328
|
-
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1329
|
-
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1330
|
-
if (bytes > MAX_USER_SIZE_BYTES) {
|
|
1331
|
-
throw new Error(
|
|
1332
|
-
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1336
|
-
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1337
|
-
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1338
|
-
return yield authorize({ platform: this._platform, secret }).encode(params);
|
|
1358
|
+
return yield this._io.createToken(params);
|
|
1339
1359
|
});
|
|
1340
1360
|
}
|
|
1341
|
-
_getIOAuthorize(options) {
|
|
1342
|
-
if (typeof this._authorize === "function") return this._authorize(options);
|
|
1343
|
-
return this._authorize;
|
|
1344
|
-
}
|
|
1345
1361
|
_getListeners() {
|
|
1346
1362
|
return this._listeners;
|
|
1347
1363
|
}
|
|
@@ -1356,9 +1372,15 @@ var PluvIO = class {
|
|
|
1356
1372
|
this.version = version;
|
|
1357
1373
|
this._authorize = null;
|
|
1358
1374
|
this._context = {};
|
|
1359
|
-
const { authorize: authorize2, context, crdt = import_crdt3.noop, debug = false, platform } = options;
|
|
1375
|
+
const { authorize: authorize2, context, crdt = import_crdt3.noop, debug = false, limits, platform } = options;
|
|
1360
1376
|
this._crdt = crdt;
|
|
1361
1377
|
this._debug = debug;
|
|
1378
|
+
this._limits = __spreadValues({
|
|
1379
|
+
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
|
|
1380
|
+
storageMaxSize: MAX_STORAGE_SIZE_BYTES,
|
|
1381
|
+
userIdMaxLength: MAX_USER_ID_LENGTH,
|
|
1382
|
+
userMaxSize: MAX_USER_SIZE_BYTES
|
|
1383
|
+
}, limits);
|
|
1362
1384
|
this._platform = platform;
|
|
1363
1385
|
if (authorize2) this._authorize = authorize2;
|
|
1364
1386
|
if (context) this._context = context;
|
|
@@ -1371,6 +1393,20 @@ var PluvIO = class {
|
|
|
1371
1393
|
var _a;
|
|
1372
1394
|
if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
|
|
1373
1395
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
1396
|
+
const user = params.user;
|
|
1397
|
+
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1398
|
+
if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {
|
|
1399
|
+
throw new Error(
|
|
1400
|
+
`createToken was called with a long user id. User ID must be at most 128 characters. Current length: ${user.id.length.toLocaleString()}`
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1404
|
+
if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {
|
|
1405
|
+
throw new Error(
|
|
1406
|
+
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1374
1410
|
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1375
1411
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1376
1412
|
return yield authorize({ platform: this._platform, secret }).encode(params);
|
|
@@ -1393,6 +1429,8 @@ var PluvIO = class {
|
|
|
1393
1429
|
context: this._context,
|
|
1394
1430
|
crdt: this._crdt,
|
|
1395
1431
|
debug: this._debug,
|
|
1432
|
+
io: this,
|
|
1433
|
+
limits: this._limits,
|
|
1396
1434
|
platform: this._platform
|
|
1397
1435
|
}));
|
|
1398
1436
|
}
|
|
@@ -1406,12 +1444,13 @@ var PluvIO = class {
|
|
|
1406
1444
|
|
|
1407
1445
|
// src/createIO.ts
|
|
1408
1446
|
var createIO = (params) => {
|
|
1409
|
-
const { authorize: authorize2, context, crdt, debug, platform } = params;
|
|
1447
|
+
const { authorize: authorize2, context, crdt, debug, limits, platform } = params;
|
|
1410
1448
|
return new PluvIO({
|
|
1411
1449
|
authorize: authorize2,
|
|
1412
1450
|
context,
|
|
1413
1451
|
crdt,
|
|
1414
1452
|
debug,
|
|
1453
|
+
limits,
|
|
1415
1454
|
platform
|
|
1416
1455
|
});
|
|
1417
1456
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -103,9 +103,19 @@ var Persistence = class extends AbstractPersistence {
|
|
|
103
103
|
return Promise.resolve(user != null ? user : null);
|
|
104
104
|
}
|
|
105
105
|
getUsers(room) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
return __async(this, null, function* () {
|
|
107
|
+
const users = this._users.get(room);
|
|
108
|
+
if (!users) return /* @__PURE__ */ new Map();
|
|
109
|
+
return users;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
getUsersSize(room) {
|
|
113
|
+
return __async(this, null, function* () {
|
|
114
|
+
return this._users.size;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
initialize(roomContext) {
|
|
118
|
+
return this;
|
|
109
119
|
}
|
|
110
120
|
setStorageState(room, state) {
|
|
111
121
|
this._storages.set(room, state);
|
|
@@ -347,6 +357,8 @@ import colors from "kleur";
|
|
|
347
357
|
// src/constants.ts
|
|
348
358
|
var GARBAGE_COLLECT_INTERVAL_MS = 6e4;
|
|
349
359
|
var MAX_PRESENCE_SIZE_BYTES = 512;
|
|
360
|
+
var MAX_STORAGE_SIZE_BYTES = 31457280;
|
|
361
|
+
var MAX_USER_ID_LENGTH = 128;
|
|
350
362
|
var MAX_USER_SIZE_BYTES = 512;
|
|
351
363
|
var PING_TIMEOUT_MS = 3e4;
|
|
352
364
|
|
|
@@ -461,6 +473,7 @@ var IORoom = class {
|
|
|
461
473
|
return __async(this, null, function* () {
|
|
462
474
|
this._lastGarbageCollectMs = Date.now();
|
|
463
475
|
this._emitQuitters();
|
|
476
|
+
this._emitSyncState();
|
|
464
477
|
});
|
|
465
478
|
}
|
|
466
479
|
getSize() {
|
|
@@ -653,6 +666,17 @@ var IORoom = class {
|
|
|
653
666
|
}
|
|
654
667
|
});
|
|
655
668
|
}
|
|
669
|
+
_emitSyncState() {
|
|
670
|
+
return __async(this, null, function* () {
|
|
671
|
+
const connectionIds = yield this._platform.persistence.getUsers(this.id).then((map) => Object.keys(map));
|
|
672
|
+
yield this._broadcast({
|
|
673
|
+
message: {
|
|
674
|
+
type: "$syncStateReceived",
|
|
675
|
+
data: { connectionIds }
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
}
|
|
656
680
|
_ensureDetached() {
|
|
657
681
|
if (this._platform._config.registrationMode === "detached") return;
|
|
658
682
|
throw new Error("Platform must use detached mode");
|
|
@@ -1075,7 +1099,7 @@ var pickBy = (obj, by) => {
|
|
|
1075
1099
|
};
|
|
1076
1100
|
|
|
1077
1101
|
// src/version.generated.ts
|
|
1078
|
-
var version = "0.
|
|
1102
|
+
var version = "0.43.0";
|
|
1079
1103
|
|
|
1080
1104
|
// src/PluvServer.ts
|
|
1081
1105
|
var PluvServer = class {
|
|
@@ -1126,18 +1150,22 @@ var PluvServer = class {
|
|
|
1126
1150
|
update
|
|
1127
1151
|
];
|
|
1128
1152
|
if (updates.some((_update) => !!_update)) {
|
|
1129
|
-
const
|
|
1130
|
-
yield this._platform.persistence.setStorageState(room,
|
|
1153
|
+
const encodedState2 = doc.batchApplyEncodedState(updates).getEncodedState();
|
|
1154
|
+
yield this._platform.persistence.setStorageState(room, encodedState2);
|
|
1131
1155
|
this._getListeners().onStorageUpdated({
|
|
1132
1156
|
context,
|
|
1133
|
-
encodedState,
|
|
1157
|
+
encodedState: encodedState2,
|
|
1134
1158
|
room,
|
|
1135
1159
|
user: session == null ? void 0 : session.user,
|
|
1136
1160
|
webSocket: session == null ? void 0 : session.webSocket.webSocket
|
|
1137
1161
|
});
|
|
1138
1162
|
}
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1163
|
+
const encodedState = (_b = yield this._platform.persistence.getStorageState(room)) != null ? _b : doc.getEncodedState();
|
|
1164
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1165
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1166
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1167
|
+
}
|
|
1168
|
+
return { $storageReceived: { state: encodedState } };
|
|
1141
1169
|
})),
|
|
1142
1170
|
$ping: this._procedure.self((data, { session }) => {
|
|
1143
1171
|
if (!session) return {};
|
|
@@ -1157,7 +1185,7 @@ var PluvServer = class {
|
|
|
1157
1185
|
const cleanedPatch = pickBy(presence != null ? presence : {}, (value) => typeof value !== "undefined");
|
|
1158
1186
|
const updated = Object.assign(/* @__PURE__ */ Object.create(null), session.presence, cleanedPatch);
|
|
1159
1187
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
1160
|
-
if (bytes >
|
|
1188
|
+
if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
|
|
1161
1189
|
throw new Error(
|
|
1162
1190
|
`Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1163
1191
|
);
|
|
@@ -1177,6 +1205,10 @@ var PluvServer = class {
|
|
|
1177
1205
|
if (origin === "$initialized" && Object.keys(doc.toJson()).length) return {};
|
|
1178
1206
|
const updated = update === null ? doc : doc.applyEncodedState({ update });
|
|
1179
1207
|
const encodedState = updated.getEncodedState();
|
|
1208
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
1209
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
1210
|
+
throw new Error("Storage has exceeded the size limit");
|
|
1211
|
+
}
|
|
1180
1212
|
this._platform.persistence.setStorageState(room, encodedState).then(() => {
|
|
1181
1213
|
this._getListeners().onStorageUpdated({
|
|
1182
1214
|
context,
|
|
@@ -1195,6 +1227,8 @@ var PluvServer = class {
|
|
|
1195
1227
|
crdt = noop2,
|
|
1196
1228
|
debug = false,
|
|
1197
1229
|
getInitialStorage,
|
|
1230
|
+
io,
|
|
1231
|
+
limits,
|
|
1198
1232
|
platform,
|
|
1199
1233
|
router = new PluvRouter({})
|
|
1200
1234
|
} = options;
|
|
@@ -1202,6 +1236,8 @@ var PluvServer = class {
|
|
|
1202
1236
|
platform.validateConfig(options);
|
|
1203
1237
|
this._crdt = crdt;
|
|
1204
1238
|
this._debug = debug;
|
|
1239
|
+
this._io = io;
|
|
1240
|
+
this._limits = limits;
|
|
1205
1241
|
this._platform = platform;
|
|
1206
1242
|
this._router = router ? PluvRouter.merge(this._baseRouter, router) : this._baseRouter;
|
|
1207
1243
|
if (authorize2) this._authorize = authorize2;
|
|
@@ -1243,7 +1279,7 @@ var PluvServer = class {
|
|
|
1243
1279
|
if (this._platform._config.handleMode !== "io") {
|
|
1244
1280
|
throw new Error(`\`createRoom\` is unsupported for \`${this._platform._name}\``);
|
|
1245
1281
|
}
|
|
1246
|
-
if (!/^[a-z0-9]
|
|
1282
|
+
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room)) throw new Error("Unsupported room name");
|
|
1247
1283
|
const roomContext = platformRoomContext;
|
|
1248
1284
|
const context = typeof this._context === "function" ? this._context(roomContext) : this._context;
|
|
1249
1285
|
const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
|
|
@@ -1277,29 +1313,9 @@ var PluvServer = class {
|
|
|
1277
1313
|
}
|
|
1278
1314
|
createToken(params) {
|
|
1279
1315
|
return __async(this, null, function* () {
|
|
1280
|
-
|
|
1281
|
-
if (!this._authorize) {
|
|
1282
|
-
throw new Error("IO does not specify authorize during initialization.");
|
|
1283
|
-
}
|
|
1284
|
-
const ioAuthorize = this._getIOAuthorize(params);
|
|
1285
|
-
const user = params.user;
|
|
1286
|
-
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1287
|
-
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1288
|
-
if (bytes > MAX_USER_SIZE_BYTES) {
|
|
1289
|
-
throw new Error(
|
|
1290
|
-
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1291
|
-
);
|
|
1292
|
-
}
|
|
1293
|
-
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1294
|
-
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1295
|
-
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1296
|
-
return yield authorize({ platform: this._platform, secret }).encode(params);
|
|
1316
|
+
return yield this._io.createToken(params);
|
|
1297
1317
|
});
|
|
1298
1318
|
}
|
|
1299
|
-
_getIOAuthorize(options) {
|
|
1300
|
-
if (typeof this._authorize === "function") return this._authorize(options);
|
|
1301
|
-
return this._authorize;
|
|
1302
|
-
}
|
|
1303
1319
|
_getListeners() {
|
|
1304
1320
|
return this._listeners;
|
|
1305
1321
|
}
|
|
@@ -1314,9 +1330,15 @@ var PluvIO = class {
|
|
|
1314
1330
|
this.version = version;
|
|
1315
1331
|
this._authorize = null;
|
|
1316
1332
|
this._context = {};
|
|
1317
|
-
const { authorize: authorize2, context, crdt = noop3, debug = false, platform } = options;
|
|
1333
|
+
const { authorize: authorize2, context, crdt = noop3, debug = false, limits, platform } = options;
|
|
1318
1334
|
this._crdt = crdt;
|
|
1319
1335
|
this._debug = debug;
|
|
1336
|
+
this._limits = __spreadValues({
|
|
1337
|
+
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
|
|
1338
|
+
storageMaxSize: MAX_STORAGE_SIZE_BYTES,
|
|
1339
|
+
userIdMaxLength: MAX_USER_ID_LENGTH,
|
|
1340
|
+
userMaxSize: MAX_USER_SIZE_BYTES
|
|
1341
|
+
}, limits);
|
|
1320
1342
|
this._platform = platform;
|
|
1321
1343
|
if (authorize2) this._authorize = authorize2;
|
|
1322
1344
|
if (context) this._context = context;
|
|
@@ -1329,6 +1351,20 @@ var PluvIO = class {
|
|
|
1329
1351
|
var _a;
|
|
1330
1352
|
if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
|
|
1331
1353
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
1354
|
+
const user = params.user;
|
|
1355
|
+
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
1356
|
+
if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
`createToken was called with a long user id. User ID must be at most 128 characters. Current length: ${user.id.length.toLocaleString()}`
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
1362
|
+
if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {
|
|
1363
|
+
throw new Error(
|
|
1364
|
+
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
if (this._platform._createToken) return yield this._platform._createToken(params);
|
|
1332
1368
|
const secret = (_a = ioAuthorize == null ? void 0 : ioAuthorize.secret) != null ? _a : null;
|
|
1333
1369
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
1334
1370
|
return yield authorize({ platform: this._platform, secret }).encode(params);
|
|
@@ -1351,6 +1387,8 @@ var PluvIO = class {
|
|
|
1351
1387
|
context: this._context,
|
|
1352
1388
|
crdt: this._crdt,
|
|
1353
1389
|
debug: this._debug,
|
|
1390
|
+
io: this,
|
|
1391
|
+
limits: this._limits,
|
|
1354
1392
|
platform: this._platform
|
|
1355
1393
|
}));
|
|
1356
1394
|
}
|
|
@@ -1364,12 +1402,13 @@ var PluvIO = class {
|
|
|
1364
1402
|
|
|
1365
1403
|
// src/createIO.ts
|
|
1366
1404
|
var createIO = (params) => {
|
|
1367
|
-
const { authorize: authorize2, context, crdt, debug, platform } = params;
|
|
1405
|
+
const { authorize: authorize2, context, crdt, debug, limits, platform } = params;
|
|
1368
1406
|
return new PluvIO({
|
|
1369
1407
|
authorize: authorize2,
|
|
1370
1408
|
context,
|
|
1371
1409
|
crdt,
|
|
1372
1410
|
debug,
|
|
1411
|
+
limits,
|
|
1373
1412
|
platform
|
|
1374
1413
|
});
|
|
1375
1414
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "@pluv/io server-side websocket manager",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"jose": "^5.10.0",
|
|
22
22
|
"kleur": "^4.1.5",
|
|
23
23
|
"wonka": "^6.3.5",
|
|
24
|
-
"@pluv/crdt": "^0.
|
|
25
|
-
"@pluv/types": "^0.
|
|
24
|
+
"@pluv/crdt": "^0.43.0",
|
|
25
|
+
"@pluv/types": "^0.43.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"eslint": "^9.25.
|
|
28
|
+
"eslint": "^9.25.1",
|
|
29
29
|
"genversion": "^3.2.0",
|
|
30
30
|
"tsup": "^8.4.0",
|
|
31
31
|
"typescript": "^5.8.3",
|
|
32
|
-
"@pluv/tsconfig": "^0.
|
|
33
|
-
"eslint-config-pluv": "^0.
|
|
32
|
+
"@pluv/tsconfig": "^0.43.0",
|
|
33
|
+
"eslint-config-pluv": "^0.43.0"
|
|
34
34
|
},
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
|
|
@@ -13,9 +13,11 @@ export abstract class AbstractPersistence {
|
|
|
13
13
|
|
|
14
14
|
public abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
15
15
|
|
|
16
|
-
public abstract getUsers(room: string): Promise<
|
|
16
|
+
public abstract getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
17
17
|
|
|
18
18
|
public abstract getUsersSize(room: string): Promise<number>;
|
|
19
19
|
|
|
20
|
+
public abstract initialize(roomContext: any): AbstractPersistence;
|
|
21
|
+
|
|
20
22
|
public abstract setStorageState(room: string, state: string): Promise<void>;
|
|
21
23
|
}
|
package/src/IORoom.ts
CHANGED
|
@@ -256,6 +256,7 @@ export class IORoom<
|
|
|
256
256
|
public async garbageCollect(): Promise<void> {
|
|
257
257
|
this._lastGarbageCollectMs = Date.now();
|
|
258
258
|
this._emitQuitters();
|
|
259
|
+
this._emitSyncState();
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
public getSize(): number {
|
|
@@ -507,6 +508,17 @@ export class IORoom<
|
|
|
507
508
|
}
|
|
508
509
|
}
|
|
509
510
|
|
|
511
|
+
private async _emitSyncState(): Promise<void> {
|
|
512
|
+
const connectionIds = await this._platform.persistence.getUsers(this.id).then((map) => Object.keys(map));
|
|
513
|
+
|
|
514
|
+
await this._broadcast({
|
|
515
|
+
message: {
|
|
516
|
+
type: "$syncStateReceived",
|
|
517
|
+
data: { connectionIds },
|
|
518
|
+
},
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
510
522
|
private _ensureDetached(): void {
|
|
511
523
|
if (this._platform._config.registrationMode === "detached") return;
|
|
512
524
|
|
package/src/Persistence.ts
CHANGED
|
@@ -57,12 +57,20 @@ export class Persistence extends AbstractPersistence {
|
|
|
57
57
|
return Promise.resolve(user ?? null);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
public getUsers(room: string): Promise<
|
|
60
|
+
public async getUsers(room: string): Promise<Map<string, JsonObject | null>> {
|
|
61
61
|
const users = this._users.get(room);
|
|
62
62
|
|
|
63
|
-
if (!users) return
|
|
63
|
+
if (!users) return new Map();
|
|
64
64
|
|
|
65
|
-
return
|
|
65
|
+
return users;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public async getUsersSize(room: string): Promise<number> {
|
|
69
|
+
return this._users.size;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public initialize(roomContext: any): this {
|
|
73
|
+
return this;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
public setStorageState(room: string, state: string): Promise<void> {
|
package/src/PluvIO.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
2
2
|
import { noop } from "@pluv/crdt";
|
|
3
|
-
import type { IOAuthorize, InferIOAuthorizeUser } from "@pluv/types";
|
|
3
|
+
import type { BaseUser, IOAuthorize, InferIOAuthorizeUser } from "@pluv/types";
|
|
4
4
|
import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
|
|
5
5
|
import type { WebSocketRegisterConfig } from "./IORoom";
|
|
6
6
|
import { PluvProcedure } from "./PluvProcedure";
|
|
@@ -9,11 +9,13 @@ import { PluvRouter } from "./PluvRouter";
|
|
|
9
9
|
import { PluvServer, PluvServerConfig } from "./PluvServer";
|
|
10
10
|
import type { JWTEncodeParams } from "./authorize";
|
|
11
11
|
import { authorize } from "./authorize";
|
|
12
|
+
import { MAX_PRESENCE_SIZE_BYTES, MAX_STORAGE_SIZE_BYTES, MAX_USER_ID_LENGTH, MAX_USER_SIZE_BYTES } from "./constants";
|
|
12
13
|
import type {
|
|
13
14
|
CrdtLibraryType,
|
|
14
15
|
GetInitialStorageFn,
|
|
15
16
|
PluvContext,
|
|
16
17
|
PluvIOAuthorize,
|
|
18
|
+
PluvIOLimits,
|
|
17
19
|
PluvIOListeners,
|
|
18
20
|
PluvIORouter,
|
|
19
21
|
ResolvedPluvIOAuthorize,
|
|
@@ -29,6 +31,7 @@ export type PluvIOConfig<
|
|
|
29
31
|
context?: PluvContext<TPlatform, TContext>;
|
|
30
32
|
crdt?: CrdtLibraryType;
|
|
31
33
|
debug?: boolean;
|
|
34
|
+
limits?: PluvIOLimits;
|
|
32
35
|
platform: TPlatform;
|
|
33
36
|
};
|
|
34
37
|
|
|
@@ -69,6 +72,7 @@ export class PluvIO<
|
|
|
69
72
|
private readonly _context: PluvContext<TPlatform, TContext> = {} as PluvContext<TPlatform, TContext>;
|
|
70
73
|
private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
71
74
|
private readonly _debug: boolean;
|
|
75
|
+
private readonly _limits: PluvIOLimits;
|
|
72
76
|
private readonly _platform: TPlatform;
|
|
73
77
|
|
|
74
78
|
public get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {
|
|
@@ -76,10 +80,17 @@ export class PluvIO<
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext>) {
|
|
79
|
-
const { authorize, context, crdt = noop, debug = false, platform } = options;
|
|
83
|
+
const { authorize, context, crdt = noop, debug = false, limits, platform } = options;
|
|
80
84
|
|
|
81
85
|
this._crdt = crdt;
|
|
82
86
|
this._debug = debug;
|
|
87
|
+
this._limits = {
|
|
88
|
+
presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
|
|
89
|
+
storageMaxSize: MAX_STORAGE_SIZE_BYTES,
|
|
90
|
+
userIdMaxLength: MAX_USER_ID_LENGTH,
|
|
91
|
+
userMaxSize: MAX_USER_SIZE_BYTES,
|
|
92
|
+
...limits,
|
|
93
|
+
};
|
|
83
94
|
this._platform = platform;
|
|
84
95
|
|
|
85
96
|
if (authorize) this._authorize = authorize;
|
|
@@ -90,6 +101,31 @@ export class PluvIO<
|
|
|
90
101
|
if (!this._authorize) throw new Error("IO does not specify authorize during initialization.");
|
|
91
102
|
|
|
92
103
|
const ioAuthorize = this._getIOAuthorize(params);
|
|
104
|
+
const user = params.user as BaseUser;
|
|
105
|
+
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
106
|
+
|
|
107
|
+
if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`createToken was called with a long user id. User ID must be at most 128 characters. Current length: ${user.id.length.toLocaleString()}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
114
|
+
|
|
115
|
+
if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* !HACK
|
|
123
|
+
* @description Allow the platform to overwrite this behavior as needed. This is introduced
|
|
124
|
+
* to support platformPluv
|
|
125
|
+
* @date December 15, 2024
|
|
126
|
+
*/
|
|
127
|
+
if (this._platform._createToken) return await this._platform._createToken(params as any);
|
|
128
|
+
|
|
93
129
|
const secret = ioAuthorize?.secret ?? null;
|
|
94
130
|
|
|
95
131
|
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
@@ -129,6 +165,8 @@ export class PluvIO<
|
|
|
129
165
|
context: this._context,
|
|
130
166
|
crdt: this._crdt,
|
|
131
167
|
debug: this._debug,
|
|
168
|
+
io: this as PluvIO<TPlatform, TAuthorize, TContext>,
|
|
169
|
+
limits: this._limits,
|
|
132
170
|
platform: this._platform,
|
|
133
171
|
} as PluvServerConfig<TPlatform, TAuthorize, TContext, TEvents>);
|
|
134
172
|
}
|
package/src/PluvServer.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import type { AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
2
2
|
import { noop } from "@pluv/crdt";
|
|
3
|
-
import type {
|
|
3
|
+
import type { IOLike, Id, InferIOAuthorize, InferIOAuthorizeUser, JsonObject, Maybe } from "@pluv/types";
|
|
4
4
|
import colors from "kleur";
|
|
5
5
|
import type { AbstractPlatform, InferInitContextType, InferRoomContextType } from "./AbstractPlatform";
|
|
6
|
-
import type { IORoomListeners
|
|
6
|
+
import type { IORoomListeners } from "./IORoom";
|
|
7
7
|
import { IORoom } from "./IORoom";
|
|
8
|
+
import type { PluvIO } from "./PluvIO";
|
|
8
9
|
import { PluvProcedure } from "./PluvProcedure";
|
|
9
10
|
import type { PluvRouterEventConfig } from "./PluvRouter";
|
|
10
11
|
import { PluvRouter } from "./PluvRouter";
|
|
11
12
|
import type { JWTEncodeParams } from "./authorize";
|
|
12
|
-
import {
|
|
13
|
-
import { MAX_PRESENCE_SIZE_BYTES, MAX_USER_SIZE_BYTES, PING_TIMEOUT_MS } from "./constants";
|
|
13
|
+
import { PING_TIMEOUT_MS } from "./constants";
|
|
14
14
|
import type {
|
|
15
15
|
BasePluvIOListeners,
|
|
16
16
|
GetInitialStorageFn,
|
|
17
17
|
PluvContext,
|
|
18
18
|
PluvIOAuthorize,
|
|
19
|
+
PluvIOLimits,
|
|
19
20
|
PluvIOListeners,
|
|
20
|
-
ResolvedPluvIOAuthorize,
|
|
21
21
|
} from "./types";
|
|
22
22
|
import { pickBy } from "./utils";
|
|
23
23
|
import { __PLUV_VERSION } from "./version";
|
|
@@ -38,6 +38,8 @@ export type PluvServerConfig<
|
|
|
38
38
|
crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
39
39
|
debug?: boolean;
|
|
40
40
|
getInitialStorage?: GetInitialStorageFn<TContext>;
|
|
41
|
+
limits: PluvIOLimits;
|
|
42
|
+
io: PluvIO<TPlatform, TAuthorize, TContext>;
|
|
41
43
|
platform: TPlatform;
|
|
42
44
|
router?: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
43
45
|
};
|
|
@@ -145,9 +147,14 @@ export class PluvServer<
|
|
|
145
147
|
});
|
|
146
148
|
}
|
|
147
149
|
|
|
148
|
-
const
|
|
150
|
+
const encodedState = (await this._platform.persistence.getStorageState(room)) ?? doc.getEncodedState();
|
|
151
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
154
|
+
throw new Error("Storage has exceeded the size limit");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { $storageReceived: { state: encodedState } };
|
|
151
158
|
}),
|
|
152
159
|
$ping: this._procedure.self((data, { session }) => {
|
|
153
160
|
if (!session) return {};
|
|
@@ -175,7 +182,7 @@ export class PluvServer<
|
|
|
175
182
|
const updated = Object.assign(Object.create(null), session.presence, cleanedPatch);
|
|
176
183
|
const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
|
|
177
184
|
|
|
178
|
-
if (bytes >
|
|
185
|
+
if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
|
|
179
186
|
throw new Error(
|
|
180
187
|
`Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
|
|
181
188
|
);
|
|
@@ -198,6 +205,11 @@ export class PluvServer<
|
|
|
198
205
|
|
|
199
206
|
const updated = update === null ? doc : doc.applyEncodedState({ update });
|
|
200
207
|
const encodedState = updated.getEncodedState();
|
|
208
|
+
const storageSize = new TextEncoder().encode(encodedState).length;
|
|
209
|
+
|
|
210
|
+
if (!!this._limits.storageMaxSize && storageSize > this._limits.storageMaxSize) {
|
|
211
|
+
throw new Error("Storage has exceeded the size limit");
|
|
212
|
+
}
|
|
201
213
|
|
|
202
214
|
this._platform.persistence.setStorageState(room, encodedState).then(() => {
|
|
203
215
|
this._getListeners().onStorageUpdated({
|
|
@@ -214,6 +226,8 @@ export class PluvServer<
|
|
|
214
226
|
private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any> };
|
|
215
227
|
private readonly _debug: boolean;
|
|
216
228
|
private readonly _getInitialStorage: GetInitialStorageFn<TContext> | null = null;
|
|
229
|
+
private readonly _io: PluvIO<TPlatform, TAuthorize, TContext>;
|
|
230
|
+
private readonly _limits: PluvIOLimits;
|
|
217
231
|
private readonly _platform: TPlatform;
|
|
218
232
|
private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
|
|
219
233
|
|
|
@@ -255,6 +269,8 @@ export class PluvServer<
|
|
|
255
269
|
crdt = noop,
|
|
256
270
|
debug = false,
|
|
257
271
|
getInitialStorage,
|
|
272
|
+
io,
|
|
273
|
+
limits,
|
|
258
274
|
platform,
|
|
259
275
|
router = new PluvRouter<TPlatform, TAuthorize, TContext, TEvents>({} as TEvents),
|
|
260
276
|
} = options;
|
|
@@ -266,6 +282,8 @@ export class PluvServer<
|
|
|
266
282
|
|
|
267
283
|
this._crdt = crdt;
|
|
268
284
|
this._debug = debug;
|
|
285
|
+
this._io = io;
|
|
286
|
+
this._limits = limits;
|
|
269
287
|
this._platform = platform;
|
|
270
288
|
this._router = (router ? PluvRouter.merge(this._baseRouter, router) : this._baseRouter) as PluvRouter<
|
|
271
289
|
TPlatform,
|
|
@@ -302,7 +320,7 @@ export class PluvServer<
|
|
|
302
320
|
throw new Error(`\`createRoom\` is unsupported for \`${this._platform._name}\``);
|
|
303
321
|
}
|
|
304
322
|
|
|
305
|
-
if (!/^[a-z0-9]
|
|
323
|
+
if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room)) throw new Error("Unsupported room name");
|
|
306
324
|
|
|
307
325
|
const roomContext = platformRoomContext as InferRoomContextType<TPlatform>;
|
|
308
326
|
const context: TContext = typeof this._context === "function" ? this._context(roomContext) : this._context;
|
|
@@ -345,41 +363,7 @@ export class PluvServer<
|
|
|
345
363
|
public async createToken(
|
|
346
364
|
params: JWTEncodeParams<InferIOAuthorizeUser<InferIOAuthorize<this>>, TPlatform>,
|
|
347
365
|
): Promise<string> {
|
|
348
|
-
|
|
349
|
-
throw new Error("IO does not specify authorize during initialization.");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const ioAuthorize = this._getIOAuthorize(params);
|
|
353
|
-
const user = params.user as BaseUser;
|
|
354
|
-
const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
|
|
355
|
-
|
|
356
|
-
const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
|
|
357
|
-
|
|
358
|
-
if (bytes > MAX_USER_SIZE_BYTES) {
|
|
359
|
-
throw new Error(
|
|
360
|
-
`createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* !HACK
|
|
366
|
-
* @description Allow the platform to overwrite this behavior as needed. This is introduced
|
|
367
|
-
* to support platformPluv
|
|
368
|
-
* @date December 15, 2024
|
|
369
|
-
*/
|
|
370
|
-
if (this._platform._createToken) return await this._platform._createToken(params as any);
|
|
371
|
-
|
|
372
|
-
const secret = ioAuthorize?.secret ?? null;
|
|
373
|
-
|
|
374
|
-
if (!secret) throw new Error("`authorize` was specified without a valid secret");
|
|
375
|
-
|
|
376
|
-
return await authorize({ platform: this._platform, secret }).encode(params as JWTEncodeParams<any, TPlatform>);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private _getIOAuthorize(options: WebSocketRegisterConfig<TPlatform>): ResolvedPluvIOAuthorize<any, any> | null {
|
|
380
|
-
if (typeof this._authorize === "function") return this._authorize(options);
|
|
381
|
-
|
|
382
|
-
return this._authorize as ResolvedPluvIOAuthorize<any, any> | null;
|
|
366
|
+
return await this._io.createToken(params);
|
|
383
367
|
}
|
|
384
368
|
|
|
385
369
|
private _getListeners(): BasePluvIOListeners<TPlatform, TAuthorize, TContext, TEvents> {
|
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const GARBAGE_COLLECT_INTERVAL_MS = 60_000;
|
|
2
2
|
export const MAX_PRESENCE_SIZE_BYTES = 512;
|
|
3
|
+
export const MAX_STORAGE_SIZE_BYTES = 31_457_280;
|
|
4
|
+
export const MAX_USER_ID_LENGTH = 128;
|
|
3
5
|
export const MAX_USER_SIZE_BYTES = 512;
|
|
4
6
|
export const PING_TIMEOUT_MS = 30_000;
|
package/src/createIO.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BaseUser } from "@pluv/types";
|
|
2
2
|
import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
|
|
3
3
|
import { PluvIO } from "./PluvIO";
|
|
4
|
-
import type { CrdtLibraryType, PluvContext, PluvIOAuthorize } from "./types";
|
|
4
|
+
import type { CrdtLibraryType, PluvContext, PluvIOAuthorize, PluvIOLimits } from "./types";
|
|
5
5
|
|
|
6
6
|
export type CreateIOParams<
|
|
7
7
|
TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
|
|
@@ -12,6 +12,7 @@ export type CreateIOParams<
|
|
|
12
12
|
context?: PluvContext<TPlatform, TContext>;
|
|
13
13
|
crdt?: CrdtLibraryType;
|
|
14
14
|
debug?: boolean;
|
|
15
|
+
limits?: PluvIOLimits;
|
|
15
16
|
platform: TPlatform;
|
|
16
17
|
};
|
|
17
18
|
|
|
@@ -22,13 +23,14 @@ export const createIO = <
|
|
|
22
23
|
>(
|
|
23
24
|
params: CreateIOParams<TPlatform, TContext, TUser>,
|
|
24
25
|
): PluvIO<TPlatform, PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>, TContext> => {
|
|
25
|
-
const { authorize, context, crdt, debug, platform } = params;
|
|
26
|
+
const { authorize, context, crdt, debug, limits, platform } = params;
|
|
26
27
|
|
|
27
28
|
return new PluvIO<TPlatform, PluvIOAuthorize<TPlatform, TUser, InferInitContextType<TPlatform>>, TContext>({
|
|
28
29
|
authorize,
|
|
29
30
|
context,
|
|
30
31
|
crdt,
|
|
31
32
|
debug,
|
|
33
|
+
limits,
|
|
32
34
|
platform,
|
|
33
35
|
});
|
|
34
36
|
};
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -145,6 +145,25 @@ export type PluvIOAuthorize<
|
|
|
145
145
|
| (TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null)
|
|
146
146
|
| ((context: TContext) => TUser extends BaseUser ? ResolvedPluvIOAuthorize<TPlatform, TUser> : null);
|
|
147
147
|
|
|
148
|
+
export interface PluvIOLimits {
|
|
149
|
+
/**
|
|
150
|
+
* @description Maximum size of presence object in bytes
|
|
151
|
+
*/
|
|
152
|
+
presenceMaxSize?: number | null;
|
|
153
|
+
/**
|
|
154
|
+
* @description Maximum size of storage state in bytes
|
|
155
|
+
*/
|
|
156
|
+
storageMaxSize?: number | null;
|
|
157
|
+
/**
|
|
158
|
+
* @description Maximum length of user id in characters
|
|
159
|
+
*/
|
|
160
|
+
userIdMaxLength?: number | null;
|
|
161
|
+
/**
|
|
162
|
+
* @description Maximum size of user object in bytes
|
|
163
|
+
*/
|
|
164
|
+
userMaxSize?: number | null;
|
|
165
|
+
}
|
|
166
|
+
|
|
148
167
|
export type BasePluvIOListeners<
|
|
149
168
|
TPlatform extends AbstractPlatform<any, any, any, any>,
|
|
150
169
|
TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,
|
package/src/version.generated.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '0.
|
|
2
|
+
export const version = '0.43.0'
|