@pluv/client 0.42.0 → 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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/client@0.42.0 build /home/runner/work/pluv/pluv/packages/client
2
+ > @pluv/client@0.43.0 build /home/runner/work/pluv/pluv/packages/client
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,11 +8,11 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 55.91 KB
12
- ESM ⚡️ Build success in 112ms
13
- CJS dist/index.js 57.22 KB
14
- CJS ⚡️ Build success in 118ms
11
+ CJS dist/index.js 57.65 KB
12
+ CJS ⚡️ Build success in 115ms
13
+ ESM dist/index.mjs 56.33 KB
14
+ ESM ⚡️ Build success in 116ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 3444ms
17
- DTS dist/index.d.mts 24.61 KB
18
- DTS dist/index.d.ts 24.61 KB
16
+ DTS ⚡️ Build success in 3550ms
17
+ DTS dist/index.d.mts 25.24 KB
18
+ DTS dist/index.d.ts 25.24 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @pluv/client
2
2
 
3
+ ## 0.43.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 65d9172: Added configurable limits for client-side validation. These should only be updated if you control the server and the server limits have been changed.
8
+
9
+ ```ts
10
+ // defaults
11
+ createClient({
12
+ limits: {
13
+ presenceMaxSize: 512,
14
+ },
15
+ });
16
+ ```
17
+
18
+ - @pluv/crdt@0.43.0
19
+ - @pluv/types@0.43.0
20
+
3
21
  ## 0.42.0
4
22
 
5
23
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -84,6 +84,7 @@ type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSub
84
84
 
85
85
  type UsersManagerConfig<TPresence extends JsonObject = {}> = {
86
86
  initialPresence?: TPresence;
87
+ limits: PluvClientLimits;
87
88
  presence?: InputZodLike<TPresence>;
88
89
  };
89
90
 
@@ -130,6 +131,7 @@ type ReconnectTimeoutMs = number | ((params: ReconnectTimeoutMsParams) => number
130
131
  type RoomConfig<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = Id<{
131
132
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
132
133
  debug?: boolean | PluvRoomDebug<TIO>;
134
+ limits: PluvClientLimits;
133
135
  onAuthorizationFail?: (error: Error) => void;
134
136
  metadata?: InputZodLike<TMetadata>;
135
137
  publicKey?: PublicKey<TMetadata>;
@@ -144,6 +146,7 @@ declare class PluvRoom<TIO extends IOLike<any, any>, TMetadata extends JsonObjec
144
146
  private readonly _debug;
145
147
  private readonly _eventNotifier;
146
148
  private readonly _intervals;
149
+ private readonly _limits;
147
150
  private readonly _listeners;
148
151
  private readonly _otherNotifier;
149
152
  private readonly _publicKey;
@@ -235,6 +238,11 @@ declare class PluvRoom<TIO extends IOLike<any, any>, TMetadata extends JsonObjec
235
238
  type PluvClientOptions<TIO extends IOLike<any, any>, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TMetadata extends JsonObject> = RoomEndpoints<TIO, TMetadata> & {
236
239
  debug?: boolean;
237
240
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
241
+ /**
242
+ * @description Configurable limits defined for client-side validation. You should only set
243
+ * this if you control the server and have changed the limits there.
244
+ */
245
+ limits?: PluvClientLimits;
238
246
  metadata?: InputZodLike<TMetadata>;
239
247
  presence?: InputZodLike<TPresence>;
240
248
  publicKey?: PublicKey<TMetadata>;
@@ -255,6 +263,7 @@ declare class PluvClient<TIO extends IOLike<any, any>, TPresence extends JsonObj
255
263
  private readonly _authEndpoint;
256
264
  private readonly _debug;
257
265
  private readonly _initialStorage?;
266
+ private readonly _limits;
258
267
  private readonly _presence?;
259
268
  private readonly _publicKey;
260
269
  private readonly _rooms;
@@ -291,6 +300,12 @@ type MergeEventRecords<TEventRecords extends EventRecord<string, any>[], TRoot e
291
300
  ] ? MergeEventRecords<ITail, Omit<TRoot, keyof IHead> & {
292
301
  [P in keyof IHead]: TRoot extends Record<P, any> ? TRoot[P] | IHead[P] : IHead[P];
293
302
  }> : Id<TRoot>;
303
+ interface PluvClientLimits {
304
+ /**
305
+ * @description Maximum size of presence object in bytes
306
+ */
307
+ presenceMaxSize?: number | null;
308
+ }
294
309
  interface PublicKeyParams<TMetadata extends JsonObject> {
295
310
  metadata: TMetadata;
296
311
  }
@@ -368,19 +383,21 @@ type MockedRoomEvents<TIO extends IOLike> = Partial<{
368
383
  }>;
369
384
  type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
370
385
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
386
+ limits?: PluvClientLimits;
371
387
  router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
372
- } & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> & UsersManagerConfig<TPresence>;
388
+ } & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> & Omit<UsersManagerConfig<TPresence>, "limits">;
373
389
  declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
374
- private _crdtManager;
375
- private _crdtNotifier;
376
- private _eventNotifier;
377
- private _events?;
378
- private _otherNotifier;
379
- private _router;
390
+ private readonly _crdtManager;
391
+ private readonly _crdtNotifier;
392
+ private readonly _eventNotifier;
393
+ private readonly _events?;
394
+ private readonly _limits;
395
+ private readonly _otherNotifier;
396
+ private readonly _router;
380
397
  private _state;
381
- private _stateNotifier;
382
- private _subscriptions;
383
- private _usersManager;
398
+ private readonly _stateNotifier;
399
+ private readonly _subscriptions;
400
+ private readonly _usersManager;
384
401
  constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage, TEvents>);
385
402
  get storageLoaded(): boolean;
386
403
  broadcast: (<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(event: TEvent, data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>) => Promise<void>) & { [event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => Promise<void>; };
package/dist/index.d.ts CHANGED
@@ -84,6 +84,7 @@ type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSub
84
84
 
85
85
  type UsersManagerConfig<TPresence extends JsonObject = {}> = {
86
86
  initialPresence?: TPresence;
87
+ limits: PluvClientLimits;
87
88
  presence?: InputZodLike<TPresence>;
88
89
  };
89
90
 
@@ -130,6 +131,7 @@ type ReconnectTimeoutMs = number | ((params: ReconnectTimeoutMsParams) => number
130
131
  type RoomConfig<TIO extends IOLike, TMetadata extends JsonObject = {}, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = Id<{
131
132
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
132
133
  debug?: boolean | PluvRoomDebug<TIO>;
134
+ limits: PluvClientLimits;
133
135
  onAuthorizationFail?: (error: Error) => void;
134
136
  metadata?: InputZodLike<TMetadata>;
135
137
  publicKey?: PublicKey<TMetadata>;
@@ -144,6 +146,7 @@ declare class PluvRoom<TIO extends IOLike<any, any>, TMetadata extends JsonObjec
144
146
  private readonly _debug;
145
147
  private readonly _eventNotifier;
146
148
  private readonly _intervals;
149
+ private readonly _limits;
147
150
  private readonly _listeners;
148
151
  private readonly _otherNotifier;
149
152
  private readonly _publicKey;
@@ -235,6 +238,11 @@ declare class PluvRoom<TIO extends IOLike<any, any>, TMetadata extends JsonObjec
235
238
  type PluvClientOptions<TIO extends IOLike<any, any>, TPresence extends JsonObject, TStorage extends Record<string, CrdtType<any, any>>, TMetadata extends JsonObject> = RoomEndpoints<TIO, TMetadata> & {
236
239
  debug?: boolean;
237
240
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
241
+ /**
242
+ * @description Configurable limits defined for client-side validation. You should only set
243
+ * this if you control the server and have changed the limits there.
244
+ */
245
+ limits?: PluvClientLimits;
238
246
  metadata?: InputZodLike<TMetadata>;
239
247
  presence?: InputZodLike<TPresence>;
240
248
  publicKey?: PublicKey<TMetadata>;
@@ -255,6 +263,7 @@ declare class PluvClient<TIO extends IOLike<any, any>, TPresence extends JsonObj
255
263
  private readonly _authEndpoint;
256
264
  private readonly _debug;
257
265
  private readonly _initialStorage?;
266
+ private readonly _limits;
258
267
  private readonly _presence?;
259
268
  private readonly _publicKey;
260
269
  private readonly _rooms;
@@ -291,6 +300,12 @@ type MergeEventRecords<TEventRecords extends EventRecord<string, any>[], TRoot e
291
300
  ] ? MergeEventRecords<ITail, Omit<TRoot, keyof IHead> & {
292
301
  [P in keyof IHead]: TRoot extends Record<P, any> ? TRoot[P] | IHead[P] : IHead[P];
293
302
  }> : Id<TRoot>;
303
+ interface PluvClientLimits {
304
+ /**
305
+ * @description Maximum size of presence object in bytes
306
+ */
307
+ presenceMaxSize?: number | null;
308
+ }
294
309
  interface PublicKeyParams<TMetadata extends JsonObject> {
295
310
  metadata: TMetadata;
296
311
  }
@@ -368,19 +383,21 @@ type MockedRoomEvents<TIO extends IOLike> = Partial<{
368
383
  }>;
369
384
  type MockedRoomConfig<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> = {
370
385
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
386
+ limits?: PluvClientLimits;
371
387
  router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
372
- } & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> & UsersManagerConfig<TPresence>;
388
+ } & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> & Omit<UsersManagerConfig<TPresence>, "limits">;
373
389
  declare class MockedRoom<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}> extends AbstractRoom<TIO, TPresence, TStorage> {
374
- private _crdtManager;
375
- private _crdtNotifier;
376
- private _eventNotifier;
377
- private _events?;
378
- private _otherNotifier;
379
- private _router;
390
+ private readonly _crdtManager;
391
+ private readonly _crdtNotifier;
392
+ private readonly _eventNotifier;
393
+ private readonly _events?;
394
+ private readonly _limits;
395
+ private readonly _otherNotifier;
396
+ private readonly _router;
380
397
  private _state;
381
- private _stateNotifier;
382
- private _subscriptions;
383
- private _usersManager;
398
+ private readonly _stateNotifier;
399
+ private readonly _subscriptions;
400
+ private readonly _usersManager;
384
401
  constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage, TEvents>);
385
402
  get storageLoaded(): boolean;
386
403
  broadcast: (<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(event: TEvent, data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>) => Promise<void>) & { [event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => Promise<void>; };
package/dist/index.js CHANGED
@@ -83,6 +83,9 @@ var AbstractStorageStore = class {
83
83
  }
84
84
  };
85
85
 
86
+ // src/constants.ts
87
+ var MAX_PRESENCE_SIZE_BYTES = 512;
88
+
86
89
  // src/PluvProcedure.ts
87
90
  var PluvProcedure = class _PluvProcedure {
88
91
  constructor(config = {}) {
@@ -317,9 +320,6 @@ var StorageStore = class extends AbstractStorageStore {
317
320
  }
318
321
  };
319
322
 
320
- // src/constants.ts
321
- var MAX_PRESENCE_SIZE_BYTES = 512;
322
-
323
323
  // src/utils/debounce.ts
324
324
  var debounce = (fn, options) => {
325
325
  const { wait, immediate = false } = options != null ? options : {};
@@ -364,8 +364,9 @@ var UsersManager = class {
364
364
  this._myself = null;
365
365
  this._others = /* @__PURE__ */ new Map();
366
366
  this._presence = null;
367
- const { initialPresence, presence = null } = config;
367
+ const { initialPresence, limits, presence = null } = config;
368
368
  this.initialPresence = initialPresence;
369
+ this._limits = limits;
369
370
  this._myPresence = initialPresence;
370
371
  this._presence = presence;
371
372
  }
@@ -498,7 +499,7 @@ var UsersManager = class {
498
499
  const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
499
500
  const updated = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
500
501
  const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
501
- if (bytes > MAX_PRESENCE_SIZE_BYTES) {
502
+ if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
502
503
  throw new Error(
503
504
  `Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
504
505
  );
@@ -563,6 +564,7 @@ var PluvRoom = class extends AbstractRoom {
563
564
  debug = false,
564
565
  initialPresence,
565
566
  initialStorage,
567
+ limits,
566
568
  metadata,
567
569
  onAuthorizationFail,
568
570
  presence,
@@ -738,6 +740,7 @@ var PluvRoom = class extends AbstractRoom {
738
740
  this.metadata = metadata;
739
741
  this._debug = debug;
740
742
  this._endpoints = { authEndpoint, wsEndpoint };
743
+ this._limits = limits;
741
744
  this._reconnectTimeoutMs = reconnectTimeoutMs;
742
745
  this._storageStore = storage;
743
746
  if (!!publicKey) this._publicKey = publicKey;
@@ -747,7 +750,7 @@ var PluvRoom = class extends AbstractRoom {
747
750
  }
748
751
  };
749
752
  this._router = router != null ? router : new PluvRouter({});
750
- this._usersManager = new UsersManager({ initialPresence, presence });
753
+ this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
751
754
  this._crdtManager = new CrdtManager({ initialStorage });
752
755
  }
753
756
  get storageLoaded() {
@@ -1402,6 +1405,7 @@ var PluvClient = class {
1402
1405
  debug: options.debug,
1403
1406
  initialPresence: options.initialPresence,
1404
1407
  initialStorage: this._initialStorage,
1408
+ limits: this._limits,
1405
1409
  metadata: this.metadata,
1406
1410
  onAuthorizationFail: options.onAuthorizationFail,
1407
1411
  presence: this._presence,
@@ -1436,11 +1440,23 @@ var PluvClient = class {
1436
1440
  this._rooms.delete(toLeave.id);
1437
1441
  this._logDebug(`Left and deleted room: ${room}`);
1438
1442
  });
1439
- const { authEndpoint, debug = false, initialStorage, metadata, presence, publicKey, wsEndpoint } = options;
1443
+ const {
1444
+ authEndpoint,
1445
+ debug = false,
1446
+ initialStorage,
1447
+ limits,
1448
+ metadata,
1449
+ presence,
1450
+ publicKey,
1451
+ wsEndpoint
1452
+ } = options;
1440
1453
  this.metadata = metadata;
1441
1454
  this._authEndpoint = authEndpoint;
1442
1455
  this._debug = debug;
1443
1456
  this._initialStorage = initialStorage;
1457
+ this._limits = __spreadValues({
1458
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1459
+ }, limits);
1444
1460
  this._presence = presence;
1445
1461
  this._wsEndpoint = wsEndpoint;
1446
1462
  if (!!publicKey) this._publicKey = publicKey;
@@ -1478,7 +1494,7 @@ var infer = (callback) => callback;
1478
1494
  // src/MockedRoom.ts
1479
1495
  var MockedRoom = class extends AbstractRoom {
1480
1496
  constructor(room, options) {
1481
- const { events, initialPresence, initialStorage, presence, router } = options;
1497
+ const { events, initialPresence, initialStorage, limits, presence, router } = options;
1482
1498
  super(room);
1483
1499
  this._crdtNotifier = new CrdtNotifier();
1484
1500
  this._eventNotifier = new EventNotifier();
@@ -1609,11 +1625,11 @@ var MockedRoom = class extends AbstractRoom {
1609
1625
  if (!!myself) this._stateNotifier.subjects["myself"].next(myself);
1610
1626
  };
1611
1627
  this._events = events;
1628
+ this._limits = __spreadValues({
1629
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1630
+ }, limits);
1612
1631
  this._router = router != null ? router : new PluvRouter({});
1613
- this._usersManager = new UsersManager({
1614
- initialPresence,
1615
- presence
1616
- });
1632
+ this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
1617
1633
  this._crdtManager = new CrdtManager({
1618
1634
  initialStorage
1619
1635
  });
package/dist/index.mjs CHANGED
@@ -52,6 +52,9 @@ var AbstractStorageStore = class {
52
52
  }
53
53
  };
54
54
 
55
+ // src/constants.ts
56
+ var MAX_PRESENCE_SIZE_BYTES = 512;
57
+
55
58
  // src/PluvProcedure.ts
56
59
  var PluvProcedure = class _PluvProcedure {
57
60
  constructor(config = {}) {
@@ -286,9 +289,6 @@ var StorageStore = class extends AbstractStorageStore {
286
289
  }
287
290
  };
288
291
 
289
- // src/constants.ts
290
- var MAX_PRESENCE_SIZE_BYTES = 512;
291
-
292
292
  // src/utils/debounce.ts
293
293
  var debounce = (fn, options) => {
294
294
  const { wait, immediate = false } = options != null ? options : {};
@@ -333,8 +333,9 @@ var UsersManager = class {
333
333
  this._myself = null;
334
334
  this._others = /* @__PURE__ */ new Map();
335
335
  this._presence = null;
336
- const { initialPresence, presence = null } = config;
336
+ const { initialPresence, limits, presence = null } = config;
337
337
  this.initialPresence = initialPresence;
338
+ this._limits = limits;
338
339
  this._myPresence = initialPresence;
339
340
  this._presence = presence;
340
341
  }
@@ -467,7 +468,7 @@ var UsersManager = class {
467
468
  const cleanedPresence = pickBy(this._myPresence, (value) => typeof value !== "undefined");
468
469
  const updated = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
469
470
  const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
470
- if (bytes > MAX_PRESENCE_SIZE_BYTES) {
471
+ if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
471
472
  throw new Error(
472
473
  `Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`
473
474
  );
@@ -532,6 +533,7 @@ var PluvRoom = class extends AbstractRoom {
532
533
  debug = false,
533
534
  initialPresence,
534
535
  initialStorage,
536
+ limits,
535
537
  metadata,
536
538
  onAuthorizationFail,
537
539
  presence,
@@ -707,6 +709,7 @@ var PluvRoom = class extends AbstractRoom {
707
709
  this.metadata = metadata;
708
710
  this._debug = debug;
709
711
  this._endpoints = { authEndpoint, wsEndpoint };
712
+ this._limits = limits;
710
713
  this._reconnectTimeoutMs = reconnectTimeoutMs;
711
714
  this._storageStore = storage;
712
715
  if (!!publicKey) this._publicKey = publicKey;
@@ -716,7 +719,7 @@ var PluvRoom = class extends AbstractRoom {
716
719
  }
717
720
  };
718
721
  this._router = router != null ? router : new PluvRouter({});
719
- this._usersManager = new UsersManager({ initialPresence, presence });
722
+ this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
720
723
  this._crdtManager = new CrdtManager({ initialStorage });
721
724
  }
722
725
  get storageLoaded() {
@@ -1371,6 +1374,7 @@ var PluvClient = class {
1371
1374
  debug: options.debug,
1372
1375
  initialPresence: options.initialPresence,
1373
1376
  initialStorage: this._initialStorage,
1377
+ limits: this._limits,
1374
1378
  metadata: this.metadata,
1375
1379
  onAuthorizationFail: options.onAuthorizationFail,
1376
1380
  presence: this._presence,
@@ -1405,11 +1409,23 @@ var PluvClient = class {
1405
1409
  this._rooms.delete(toLeave.id);
1406
1410
  this._logDebug(`Left and deleted room: ${room}`);
1407
1411
  });
1408
- const { authEndpoint, debug = false, initialStorage, metadata, presence, publicKey, wsEndpoint } = options;
1412
+ const {
1413
+ authEndpoint,
1414
+ debug = false,
1415
+ initialStorage,
1416
+ limits,
1417
+ metadata,
1418
+ presence,
1419
+ publicKey,
1420
+ wsEndpoint
1421
+ } = options;
1409
1422
  this.metadata = metadata;
1410
1423
  this._authEndpoint = authEndpoint;
1411
1424
  this._debug = debug;
1412
1425
  this._initialStorage = initialStorage;
1426
+ this._limits = __spreadValues({
1427
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1428
+ }, limits);
1413
1429
  this._presence = presence;
1414
1430
  this._wsEndpoint = wsEndpoint;
1415
1431
  if (!!publicKey) this._publicKey = publicKey;
@@ -1447,7 +1463,7 @@ var infer = (callback) => callback;
1447
1463
  // src/MockedRoom.ts
1448
1464
  var MockedRoom = class extends AbstractRoom {
1449
1465
  constructor(room, options) {
1450
- const { events, initialPresence, initialStorage, presence, router } = options;
1466
+ const { events, initialPresence, initialStorage, limits, presence, router } = options;
1451
1467
  super(room);
1452
1468
  this._crdtNotifier = new CrdtNotifier();
1453
1469
  this._eventNotifier = new EventNotifier();
@@ -1578,11 +1594,11 @@ var MockedRoom = class extends AbstractRoom {
1578
1594
  if (!!myself) this._stateNotifier.subjects["myself"].next(myself);
1579
1595
  };
1580
1596
  this._events = events;
1597
+ this._limits = __spreadValues({
1598
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1599
+ }, limits);
1581
1600
  this._router = router != null ? router : new PluvRouter({});
1582
- this._usersManager = new UsersManager({
1583
- initialPresence,
1584
- presence
1585
- });
1601
+ this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
1586
1602
  this._crdtManager = new CrdtManager({
1587
1603
  initialStorage
1588
1604
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,15 +18,15 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.5",
21
- "@pluv/crdt": "^0.42.0",
22
- "@pluv/types": "^0.42.0"
21
+ "@pluv/crdt": "^0.43.0",
22
+ "@pluv/types": "^0.43.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "^9.25.1",
26
26
  "tsup": "^8.4.0",
27
27
  "typescript": "^5.8.3",
28
- "@pluv/tsconfig": "^0.42.0",
29
- "eslint-config-pluv": "^0.42.0"
28
+ "@pluv/tsconfig": "^0.43.0",
29
+ "eslint-config-pluv": "^0.43.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
package/src/MockedRoom.ts CHANGED
@@ -20,11 +20,13 @@ import type {
20
20
  EventResolver,
21
21
  EventResolverContext,
22
22
  InternalSubscriptions,
23
+ PluvClientLimits,
23
24
  UpdateMyPresenceAction,
24
25
  UserInfo,
25
26
  WebSocketConnection,
26
27
  WebSocketState,
27
28
  } from "./types";
29
+ import { MAX_PRESENCE_SIZE_BYTES } from "./constants";
28
30
 
29
31
  export type MockedRoomEvents<TIO extends IOLike> = Partial<{
30
32
  [P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
@@ -37,9 +39,10 @@ export type MockedRoomConfig<
37
39
  TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
38
40
  > = {
39
41
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
42
+ limits?: PluvClientLimits;
40
43
  router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
41
44
  } & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> &
42
- UsersManagerConfig<TPresence>;
45
+ Omit<UsersManagerConfig<TPresence>, "limits">;
43
46
 
44
47
  export class MockedRoom<
45
48
  TIO extends IOLike,
@@ -47,12 +50,13 @@ export class MockedRoom<
47
50
  TStorage extends Record<string, CrdtType<any, any>> = {},
48
51
  TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
49
52
  > extends AbstractRoom<TIO, TPresence, TStorage> {
50
- private _crdtManager: CrdtManager<TStorage>;
51
- private _crdtNotifier = new CrdtNotifier<TStorage>();
52
- private _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
53
- private _events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
54
- private _otherNotifier = new OtherNotifier<TIO, TPresence>();
55
- private _router: PluvRouter<TIO, TPresence, TStorage, TEvents>;
53
+ private readonly _crdtManager: CrdtManager<TStorage>;
54
+ private readonly _crdtNotifier = new CrdtNotifier<TStorage>();
55
+ private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
56
+ private readonly _events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
57
+ private readonly _limits: PluvClientLimits;
58
+ private readonly _otherNotifier = new OtherNotifier<TIO, TPresence>();
59
+ private readonly _router: PluvRouter<TIO, TPresence, TStorage, TEvents>;
56
60
  private _state: WebSocketState<TIO> = {
57
61
  authorization: {
58
62
  token: null,
@@ -66,24 +70,24 @@ export class MockedRoom<
66
70
  },
67
71
  webSocket: null,
68
72
  };
69
- private _stateNotifier = new StateNotifier<TIO, TPresence>();
70
- private _subscriptions: InternalSubscriptions = {
73
+ private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();
74
+ private readonly _subscriptions: InternalSubscriptions = {
71
75
  observeCrdt: null,
72
76
  };
73
- private _usersManager: UsersManager<TIO, TPresence>;
77
+ private readonly _usersManager: UsersManager<TIO, TPresence>;
74
78
 
75
79
  constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage, TEvents>) {
76
- const { events, initialPresence, initialStorage, presence, router } = options;
80
+ const { events, initialPresence, initialStorage, limits, presence, router } = options;
77
81
 
78
82
  super(room);
79
83
 
80
84
  this._events = events;
81
-
85
+ this._limits = {
86
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
87
+ ...limits,
88
+ };
82
89
  this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
83
- this._usersManager = new UsersManager<TIO, TPresence>({
84
- initialPresence,
85
- presence,
86
- });
90
+ this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, limits: this._limits, presence });
87
91
 
88
92
  this._crdtManager = new CrdtManager<TStorage>({
89
93
  initialStorage,
package/src/PluvClient.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
2
2
  import type { InputZodLike, IOLike, JsonObject } from "@pluv/types";
3
+ import { MAX_PRESENCE_SIZE_BYTES } from "./constants";
3
4
  import type { InferCallback } from "./infer";
4
5
  import { PluvProcedure } from "./PluvProcedure";
5
6
  import type {
@@ -14,7 +15,7 @@ import type {
14
15
  import { PluvRoom } from "./PluvRoom";
15
16
  import type { PluvRouterEventConfig } from "./PluvRouter";
16
17
  import { PluvRouter } from "./PluvRouter";
17
- import type { PublicKey, WithMetadata } from "./types";
18
+ import type { PluvClientLimits, PublicKey, WithMetadata } from "./types";
18
19
 
19
20
  export type PluvClientOptions<
20
21
  TIO extends IOLike<any, any>,
@@ -24,6 +25,11 @@ export type PluvClientOptions<
24
25
  > = RoomEndpoints<TIO, TMetadata> & {
25
26
  debug?: boolean;
26
27
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
28
+ /**
29
+ * @description Configurable limits defined for client-side validation. You should only set
30
+ * this if you control the server and have changed the limits there.
31
+ */
32
+ limits?: PluvClientLimits;
27
33
  metadata?: InputZodLike<TMetadata>;
28
34
  presence?: InputZodLike<TPresence>;
29
35
  publicKey?: PublicKey<TMetadata>;
@@ -61,6 +67,7 @@ export class PluvClient<
61
67
  private readonly _authEndpoint: AuthEndpoint<TMetadata> | undefined;
62
68
  private readonly _debug: boolean;
63
69
  private readonly _initialStorage?: AbstractCrdtDocFactory<TStorage>;
70
+ private readonly _limits: PluvClientLimits;
64
71
  private readonly _presence?: InputZodLike<TPresence>;
65
72
  private readonly _publicKey: PublicKey<TMetadata> | null = null;
66
73
  private readonly _rooms = new Map<string, PluvRoom<TIO, TMetadata, TPresence, TStorage, any>>();
@@ -77,13 +84,26 @@ export class PluvClient<
77
84
  }
78
85
 
79
86
  constructor(options: PluvClientOptions<TIO, TPresence, TStorage, TMetadata>) {
80
- const { authEndpoint, debug = false, initialStorage, metadata, presence, publicKey, wsEndpoint } = options;
87
+ const {
88
+ authEndpoint,
89
+ debug = false,
90
+ initialStorage,
91
+ limits,
92
+ metadata,
93
+ presence,
94
+ publicKey,
95
+ wsEndpoint,
96
+ } = options;
81
97
 
82
98
  this.metadata = metadata;
83
99
 
84
100
  this._authEndpoint = authEndpoint as AuthEndpoint<TMetadata>;
85
101
  this._debug = debug;
86
102
  this._initialStorage = initialStorage;
103
+ this._limits = {
104
+ presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
105
+ ...limits,
106
+ };
87
107
  this._presence = presence;
88
108
  this._wsEndpoint = wsEndpoint;
89
109
 
@@ -104,6 +124,7 @@ export class PluvClient<
104
124
  debug: options.debug,
105
125
  initialPresence: options.initialPresence,
106
126
  initialStorage: this._initialStorage,
127
+ limits: this._limits,
107
128
  metadata: this.metadata,
108
129
  onAuthorizationFail: options.onAuthorizationFail,
109
130
  presence: this._presence,
package/src/PluvRoom.ts CHANGED
@@ -37,6 +37,7 @@ import type {
37
37
  EventResolver,
38
38
  EventResolverContext,
39
39
  InternalSubscriptions,
40
+ PluvClientLimits,
40
41
  PublicKey,
41
42
  UpdateMyPresenceAction,
42
43
  UserInfo,
@@ -169,6 +170,7 @@ export type RoomConfig<
169
170
  {
170
171
  addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
171
172
  debug?: boolean | PluvRoomDebug<TIO>;
173
+ limits: PluvClientLimits;
172
174
  onAuthorizationFail?: (error: Error) => void;
173
175
  metadata?: InputZodLike<TMetadata>;
174
176
  publicKey?: PublicKey<TMetadata>;
@@ -197,6 +199,7 @@ export class PluvRoom<
197
199
  private readonly _intervals: IntervalIds = {
198
200
  heartbeat: null,
199
201
  };
202
+ private readonly _limits: PluvClientLimits;
200
203
  private readonly _listeners: InternalListeners;
201
204
  private readonly _otherNotifier = new OtherNotifier<TIO, TPresence>();
202
205
  private readonly _publicKey: PublicKey<TMetadata> | null = null;
@@ -237,6 +240,7 @@ export class PluvRoom<
237
240
  debug = false,
238
241
  initialPresence,
239
242
  initialStorage,
243
+ limits,
240
244
  metadata,
241
245
  onAuthorizationFail,
242
246
  presence,
@@ -259,6 +263,7 @@ export class PluvRoom<
259
263
 
260
264
  this._debug = debug;
261
265
  this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO, TMetadata>;
266
+ this._limits = limits;
262
267
  this._reconnectTimeoutMs = reconnectTimeoutMs;
263
268
  this._storageStore = storage;
264
269
 
@@ -271,7 +276,7 @@ export class PluvRoom<
271
276
  };
272
277
 
273
278
  this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
274
- this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, presence });
279
+ this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, limits: this._limits, presence });
275
280
  this._crdtManager = new CrdtManager<TStorage>({ initialStorage });
276
281
  }
277
282
 
@@ -1,12 +1,12 @@
1
1
  import type { InputZodLike, IOLike, JsonObject, OptionalProps } from "@pluv/types";
2
- import { MAX_PRESENCE_SIZE_BYTES } from "./constants";
3
- import type { UserInfo } from "./types";
2
+ import type { PluvClientLimits, UserInfo } from "./types";
4
3
  import { pickBy } from "./utils";
5
4
 
6
5
  export type Presence = Record<string, unknown>;
7
6
 
8
7
  export type UsersManagerConfig<TPresence extends JsonObject = {}> = {
9
8
  initialPresence?: TPresence;
9
+ limits: PluvClientLimits;
10
10
  presence?: InputZodLike<TPresence>;
11
11
  };
12
12
 
@@ -24,12 +24,13 @@ export type DeleteConnectionResult<TIO extends IOLike, TPresence extends JsonObj
24
24
  };
25
25
 
26
26
  export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}> {
27
- private _idMap = {
27
+ public readonly initialPresence: TPresence;
28
+
29
+ private readonly _idMap = {
28
30
  fromConnectionId: new Map<[connectionId: string][0], [clientId: string][0]>(),
29
31
  fromClientId: new Map<[clientId: string][0], Set<[connectionId: string][0]>>(),
30
32
  };
31
-
32
- public readonly initialPresence: TPresence;
33
+ private _limits: PluvClientLimits;
33
34
  /**
34
35
  * @description This presence can be updated while the user is not
35
36
  * connected.
@@ -43,10 +44,11 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
43
44
  private _presence: InputZodLike<TPresence> | null = null;
44
45
 
45
46
  constructor(config: UsersManagerConfig<TPresence>) {
46
- const { initialPresence, presence = null } = config;
47
+ const { initialPresence, limits, presence = null } = config;
47
48
 
48
49
  this.initialPresence = initialPresence as TPresence;
49
50
 
51
+ this._limits = limits;
50
52
  this._myPresence = initialPresence as TPresence;
51
53
  this._presence = presence;
52
54
  }
@@ -256,7 +258,7 @@ export class UsersManager<TIO extends IOLike, TPresence extends JsonObject = {}>
256
258
  const updated = { ...this.initialPresence, ...cleanedPresence, ...cleanedPatch } as TPresence;
257
259
  const bytes = new TextEncoder().encode(JSON.stringify(updated)).length;
258
260
 
259
- if (bytes > MAX_PRESENCE_SIZE_BYTES) {
261
+ if (!!this._limits.presenceMaxSize && bytes > this._limits.presenceMaxSize) {
260
262
  throw new Error(
261
263
  `Large presence. Presence must be at most 512 bytes. Current size: ${bytes.toLocaleString()}`,
262
264
  );
package/src/types.ts CHANGED
@@ -57,6 +57,13 @@ export type MergeEventRecords<
57
57
  >
58
58
  : Id<TRoot>;
59
59
 
60
+ export interface PluvClientLimits {
61
+ /**
62
+ * @description Maximum size of presence object in bytes
63
+ */
64
+ presenceMaxSize?: number | null;
65
+ }
66
+
60
67
  export interface PublicKeyParams<TMetadata extends JsonObject> {
61
68
  metadata: TMetadata;
62
69
  }