@pluv/io 0.40.2 → 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/src/PluvServer.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { AbstractCrdtDocFactory } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
- import type { IOLike, Id, InferIOAuthorize, InferIOAuthorizeUser, JsonObject, Maybe } from "@pluv/types";
3
+ import type { BaseUser, 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
6
  import type { IORoomListeners, WebSocketRegisterConfig } from "./IORoom";
@@ -10,7 +10,7 @@ import type { PluvRouterEventConfig } from "./PluvRouter";
10
10
  import { PluvRouter } from "./PluvRouter";
11
11
  import type { JWTEncodeParams } from "./authorize";
12
12
  import { authorize } from "./authorize";
13
- import { PING_TIMEOUT_MS } from "./constants";
13
+ import { MAX_PRESENCE_SIZE_BYTES, MAX_USER_SIZE_BYTES, PING_TIMEOUT_MS } from "./constants";
14
14
  import type {
15
15
  BasePluvIOListeners,
16
16
  GetInitialStorageFn,
@@ -27,7 +27,7 @@ export type InferIORoom<TServer extends PluvServer<any, any, any, any>> =
27
27
  : never;
28
28
 
29
29
  export type PluvServerConfig<
30
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
30
+ TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>,
31
31
  TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any,
32
32
  TContext extends Record<string, any> = {},
33
33
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
@@ -42,7 +42,7 @@ export type PluvServerConfig<
42
42
  };
43
43
 
44
44
  type BaseCreateRoomOptions<
45
- TPlatform extends AbstractPlatform<any>,
45
+ TPlatform extends AbstractPlatform<any, any>,
46
46
  TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,
47
47
  TContext extends Record<string, any>,
48
48
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
@@ -51,7 +51,7 @@ type BaseCreateRoomOptions<
51
51
  };
52
52
 
53
53
  export type CreateRoomOptions<
54
- TPlatform extends AbstractPlatform<any>,
54
+ TPlatform extends AbstractPlatform<any, any>,
55
55
  TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null,
56
56
  TContext extends Record<string, any>,
57
57
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>,
@@ -60,7 +60,7 @@ export type CreateRoomOptions<
60
60
  : [Id<BaseCreateRoomOptions<TPlatform, TAuthorize, TContext, TEvents> & InferRoomContextType<TPlatform>>];
61
61
 
62
62
  export class PluvServer<
63
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
63
+ TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>,
64
64
  TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any,
65
65
  TContext extends Record<string, any> = {},
66
66
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
@@ -88,12 +88,13 @@ export class PluvServer<
88
88
  user: JsonObject | null;
89
89
  };
90
90
  }>(
91
- (acc, { id, presence, user }) => ({
91
+ (acc, { id, presence, timers, user }) => ({
92
92
  ...acc,
93
93
  [id]: {
94
94
  connectionId: id,
95
95
  presence,
96
96
  room,
97
+ timers: { presence: timers.presence },
97
98
  user,
98
99
  },
99
100
  }),
@@ -105,6 +106,7 @@ export class PluvServer<
105
106
  $initializeSession: this._procedure
106
107
  .broadcast((data, { session }) => {
107
108
  const presence = (data as any)?.presence;
109
+ const timers = session.timers;
108
110
 
109
111
  if (!session) return {};
110
112
 
@@ -115,6 +117,7 @@ export class PluvServer<
115
117
  connectionId: session.id,
116
118
  user: session.user,
117
119
  presence,
120
+ timers: { presence: timers.presence },
118
121
  },
119
122
  };
120
123
  })
@@ -161,16 +164,29 @@ export class PluvServer<
161
164
 
162
165
  return { $pong: {} };
163
166
  }),
164
- $updatePresence: this._procedure.broadcast((data, { session }) => {
167
+ $updatePresence: this._procedure.broadcast((data, context) => {
165
168
  const presence = (data as any)?.presence;
169
+ const { session } = context;
166
170
 
167
171
  if (!session) return {};
168
172
 
169
173
  const updated = Object.assign(Object.create(null), session.presence, presence);
174
+ const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
170
175
 
171
- session.webSocket.presence = updated;
176
+ if (bytes > MAX_PRESENCE_SIZE_BYTES) {
177
+ throw new Error(
178
+ `Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
179
+ );
180
+ }
181
+
182
+ context.presence = updated;
172
183
 
173
- return { $presenceUpdated: { presence: updated } };
184
+ return {
185
+ $presenceUpdated: {
186
+ presence: updated,
187
+ timers: { presence: session.timers.presence },
188
+ },
189
+ };
174
190
  }),
175
191
  $updateStorage: this._procedure.broadcast((data, { context, doc, room }) => {
176
192
  const origin = (data as any)?.origin as Maybe<string>;
@@ -331,15 +347,26 @@ export class PluvServer<
331
347
  throw new Error("IO does not specify authorize during initialization.");
332
348
  }
333
349
 
350
+ const ioAuthorize = this._getIOAuthorize(params);
351
+ const user = params.user as BaseUser;
352
+ const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
353
+
354
+ const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
355
+
356
+ if (bytes > MAX_USER_SIZE_BYTES) {
357
+ throw new Error(
358
+ `createToken called with large payload. User must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
359
+ );
360
+ }
361
+
334
362
  /**
335
363
  * !HACK
336
364
  * @description Allow the platform to overwrite this behavior as needed. This is introduced
337
365
  * to support platformPluv
338
366
  * @date December 15, 2024
339
367
  */
340
- if (this._platform._createToken) return await this._platform._createToken(params);
368
+ if (this._platform._createToken) return await this._platform._createToken(params as any);
341
369
 
342
- const ioAuthorize = this._getIOAuthorize(params);
343
370
  const secret = ioAuthorize?.secret ?? null;
344
371
 
345
372
  if (!secret) throw new Error("`authorize` was specified without a valid secret");
package/src/authorize.ts CHANGED
@@ -15,20 +15,20 @@ export interface JWT<TUser extends BaseUser> {
15
15
  user: TUser;
16
16
  }
17
17
 
18
- export type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform> = {
18
+ export type JWTEncodeParams<TUser extends BaseUser | null, TPlatform extends AbstractPlatform<any, any>> = {
19
19
  maxAge?: number;
20
20
  room: string;
21
21
  user: TUser extends BaseUser ? TUser : "Error: Cannot create token without authorization!";
22
22
  } & InferInitContextType<TPlatform>;
23
23
 
24
24
  export interface AuthorizeParams {
25
- platform: AbstractPlatform;
25
+ platform: AbstractPlatform<any, any>;
26
26
  secret: string;
27
27
  }
28
28
 
29
29
  export interface AuthorizeModule {
30
30
  decode: <TUser extends BaseUser>(jwt: string) => Promise<TUser | null>;
31
- encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform>(
31
+ encode: <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(
32
32
  params: JWTEncodeParams<TUser, TPlatform>,
33
33
  ) => Promise<string>;
34
34
  }
@@ -38,7 +38,7 @@ const now = () => (Date.now() / 1_000) | 0;
38
38
  export const authorize = (params: AuthorizeParams) => {
39
39
  const { platform, secret } = params;
40
40
 
41
- const encode = async <TUser extends BaseUser, TPlatform extends AbstractPlatform>(
41
+ const encode = async <TUser extends BaseUser, TPlatform extends AbstractPlatform<any, any>>(
42
42
  encodeParams: JWTEncodeParams<TUser, TPlatform>,
43
43
  ): Promise<string> => {
44
44
  const { maxAge = DEFAULT_MAX_AGE_MS, room, user } = encodeParams;
package/src/constants.ts CHANGED
@@ -1 +1,4 @@
1
+ export const GARBAGE_COLLECT_INTERVAL_MS = 60_000;
2
+ export const MAX_PRESENCE_SIZE_BYTES = 512;
3
+ export const MAX_USER_SIZE_BYTES = 512;
1
4
  export const PING_TIMEOUT_MS = 30_000;
package/src/types.ts CHANGED
@@ -23,14 +23,6 @@ import type {
23
23
  import type { AbstractWebSocket } from "./AbstractWebSocket";
24
24
  import type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
25
25
 
26
- declare global {
27
- // eslint-disable-next-line no-var
28
- var console: {
29
- error: (...data: any[]) => void;
30
- log: (...data: any[]) => void;
31
- };
32
- }
33
-
34
26
  export type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> =
35
27
  | TContext
36
28
  | ((params: InferRoomContextType<TPlatform>) => TContext);
@@ -62,9 +54,12 @@ export interface EventResolverContext<
62
54
  > {
63
55
  context: TContext;
64
56
  doc: AbstractCrdtDoc<any>;
57
+ garbageCollect: () => Promise<void>;
58
+ presence: JsonObject | null;
65
59
  room: string;
66
60
  session: TKind extends "sync" ? WebSocketSession<TAuthorize> | null : WebSocketSession<TAuthorize>;
67
61
  sessions: readonly WebSocketSession<TAuthorize>[];
62
+ time: number;
68
63
  }
69
64
 
70
65
  export type SendMessageOptions =
@@ -74,6 +69,7 @@ export type SendMessageOptions =
74
69
 
75
70
  export interface WebSocketSessionTimers {
76
71
  ping: number;
72
+ presence: number | null;
77
73
  }
78
74
 
79
75
  export interface WebSocketSerializedState {
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '0.40.2'
2
+ export const version = '0.41.0'
package/tsconfig.json CHANGED
@@ -1,5 +1,9 @@
1
1
  {
2
2
  "extends": "@pluv/tsconfig/library.json",
3
3
  "include": ["next-env.d.ts", "src/*.ts"],
4
- "exclude": ["node_modules"]
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "lib": ["esnext", "dom"],
7
+ "types": [],
8
+ }
5
9
  }