@pluv/io 3.1.6 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
 
2
- > @pluv/io@3.1.6 build /home/runner/work/pluv/pluv/packages/io
2
+ > @pluv/io@3.2.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@3.1.6 version /home/runner/work/pluv/pluv/packages/io
6
+ > @pluv/io@3.2.0 version /home/runner/work/pluv/pluv/packages/io
7
7
  > genversion ./src/version.generated.ts --esm
8
8
 
9
9
  CLI Building entry: src/index.ts
@@ -12,11 +12,11 @@
12
12
  CLI Target: es6
13
13
  ESM Build start
14
14
  CJS Build start
15
- CJS dist/index.js 55.16 KB
16
- CJS ⚡️ Build success in 136ms
17
- ESM dist/index.mjs 52.98 KB
18
- ESM ⚡️ Build success in 136ms
15
+ CJS dist/index.js 55.81 KB
16
+ CJS ⚡️ Build success in 118ms
17
+ ESM dist/index.mjs 53.63 KB
18
+ ESM ⚡️ Build success in 123ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 4063ms
21
- DTS dist/index.d.mts 33.98 KB
22
- DTS dist/index.d.ts 33.98 KB
20
+ DTS ⚡️ Build success in 4090ms
21
+ DTS dist/index.d.mts 34.23 KB
22
+ DTS dist/index.d.ts 34.23 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @pluv/io
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6d2b257: Support async `context` parameter in `createIO`.
8
+
9
+ ### Patch Changes
10
+
11
+ - @pluv/crdt@3.2.0
12
+ - @pluv/types@3.2.0
13
+
14
+ ## 3.1.7
15
+
16
+ ### Patch Changes
17
+
18
+ - f1d56d9: Fixed a case where in Cloudflare Durable Objects, the CRDT storage would clear upon hibernation.
19
+ - @pluv/crdt@3.1.7
20
+ - @pluv/types@3.1.7
21
+
3
22
  ## 3.1.6
4
23
 
5
24
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -121,7 +121,7 @@ declare class PluvRouter<TPlatform extends AbstractPlatform<any> = AbstractPlatf
121
121
  private _isValidEventName;
122
122
  }
123
123
 
124
- type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = TContext | ((params: InferRoomContextType<TPlatform>) => TContext);
124
+ type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = MaybePromise<TContext> | ((params: InferRoomContextType<TPlatform>) => MaybePromise<TContext>);
125
125
  type EventResolverKind = "broadcast" | "self" | "sync";
126
126
  type EventResolver<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}, TInput extends JsonObject = {}, TOutput extends EventRecord<string, any> = {}> = (data: TInput, context: EventResolverContext<TKind, TPlatform, TAuthorize, TContext>) => MaybePromise<TOutput | void>;
127
127
  interface EventResolverContext<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}> {
@@ -339,11 +339,12 @@ type BroadcastProxy<TIO extends IORoom<any, any, any, any, any>> = (<TEvent exte
339
339
  };
340
340
  type IORoomConfig<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<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
341
341
  authorize?: TAuthorize;
342
- context: TContext;
342
+ context: PluvContext<TPlatform, TContext>;
343
343
  crdt?: {
344
344
  doc: (value: any) => AbstractCrdtDocFactory<any, any>;
345
345
  };
346
346
  debug: boolean;
347
+ getInitialStorage: GetInitialStorageFn<TContext>;
347
348
  platform: TPlatform;
348
349
  roomContext: InferRoomContextType<TPlatform>;
349
350
  router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
@@ -361,8 +362,10 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
361
362
  private readonly _crdt;
362
363
  private readonly _debug;
363
364
  private readonly _docFactory;
365
+ private readonly _getInitialStorage;
364
366
  private readonly _listeners;
365
367
  private readonly _platform;
368
+ private readonly _roomContext;
366
369
  private readonly _router;
367
370
  private readonly _sessions;
368
371
  private readonly _userSessionss;
@@ -412,6 +415,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
412
415
  private _ensureDetached;
413
416
  private _getAbstractWs;
414
417
  private _getAuthorizedUser;
418
+ private _getContext;
415
419
  private _getInitialDoc;
416
420
  private _getIOAuthorize;
417
421
  private _getLatestPresence;
@@ -458,6 +462,7 @@ type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize
458
462
  declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TCrdt extends CrdtLibraryType$1<any> = CrdtLibraryType$1<any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TCrdt, TEvents> {
459
463
  readonly version: string;
460
464
  private readonly _config;
465
+ private readonly _docFactory;
461
466
  get fetch(): (...args: any[]) => Promise<any>;
462
467
  /**
463
468
  * @ignore
@@ -466,7 +471,7 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
466
471
  */
467
472
  get _defs(): {
468
473
  authorize: TAuthorize;
469
- context: TContext;
474
+ context: PluvContext<TPlatform, TContext>;
470
475
  crdt: TCrdt;
471
476
  events: TEvents;
472
477
  platform: TPlatform;
package/dist/index.d.ts CHANGED
@@ -121,7 +121,7 @@ declare class PluvRouter<TPlatform extends AbstractPlatform<any> = AbstractPlatf
121
121
  private _isValidEventName;
122
122
  }
123
123
 
124
- type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = TContext | ((params: InferRoomContextType<TPlatform>) => TContext);
124
+ type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = MaybePromise<TContext> | ((params: InferRoomContextType<TPlatform>) => MaybePromise<TContext>);
125
125
  type EventResolverKind = "broadcast" | "self" | "sync";
126
126
  type EventResolver<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}, TInput extends JsonObject = {}, TOutput extends EventRecord<string, any> = {}> = (data: TInput, context: EventResolverContext<TKind, TPlatform, TAuthorize, TContext>) => MaybePromise<TOutput | void>;
127
127
  interface EventResolverContext<TKind extends EventResolverKind = EventResolverKind, TPlatform extends AbstractPlatform = AbstractPlatform, TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null = null, TContext extends Record<string, any> = {}> {
@@ -339,11 +339,12 @@ type BroadcastProxy<TIO extends IORoom<any, any, any, any, any>> = (<TEvent exte
339
339
  };
340
340
  type IORoomConfig<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<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
341
341
  authorize?: TAuthorize;
342
- context: TContext;
342
+ context: PluvContext<TPlatform, TContext>;
343
343
  crdt?: {
344
344
  doc: (value: any) => AbstractCrdtDocFactory<any, any>;
345
345
  };
346
346
  debug: boolean;
347
+ getInitialStorage: GetInitialStorageFn<TContext>;
347
348
  platform: TPlatform;
348
349
  roomContext: InferRoomContextType<TPlatform>;
349
350
  router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
@@ -361,8 +362,10 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
361
362
  private readonly _crdt;
362
363
  private readonly _debug;
363
364
  private readonly _docFactory;
365
+ private readonly _getInitialStorage;
364
366
  private readonly _listeners;
365
367
  private readonly _platform;
368
+ private readonly _roomContext;
366
369
  private readonly _router;
367
370
  private readonly _sessions;
368
371
  private readonly _userSessionss;
@@ -412,6 +415,7 @@ declare class IORoom<TPlatform extends AbstractPlatform<any> = AbstractPlatform<
412
415
  private _ensureDetached;
413
416
  private _getAbstractWs;
414
417
  private _getAuthorizedUser;
418
+ private _getContext;
415
419
  private _getInitialDoc;
416
420
  private _getIOAuthorize;
417
421
  private _getLatestPresence;
@@ -458,6 +462,7 @@ type CreateRoomOptions<TPlatform extends AbstractPlatform<any, any>, TAuthorize
458
462
  declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>, TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null = any, TContext extends Record<string, any> = {}, TCrdt extends CrdtLibraryType$1<any> = CrdtLibraryType$1<any>, TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}> implements IOLike<TAuthorize, TCrdt, TEvents> {
459
463
  readonly version: string;
460
464
  private readonly _config;
465
+ private readonly _docFactory;
461
466
  get fetch(): (...args: any[]) => Promise<any>;
462
467
  /**
463
468
  * @ignore
@@ -466,7 +471,7 @@ declare class PluvServer<TPlatform extends AbstractPlatform<any, any> = Abstract
466
471
  */
467
472
  get _defs(): {
468
473
  authorize: TAuthorize;
469
- context: TContext;
474
+ context: PluvContext<TPlatform, TContext>;
470
475
  crdt: TCrdt;
471
476
  events: TEvents;
472
477
  platform: TPlatform;
package/dist/index.js CHANGED
@@ -440,6 +440,7 @@ var IORoom = class {
440
440
  context,
441
441
  crdt = import_crdt.noop,
442
442
  debug,
443
+ getInitialStorage,
443
444
  onDestroy,
444
445
  onMessage,
445
446
  onUserConnected,
@@ -453,6 +454,8 @@ var IORoom = class {
453
454
  this._crdt = crdt;
454
455
  this._debug = debug;
455
456
  this._docFactory = crdt.doc(() => ({}));
457
+ this._getInitialStorage = getInitialStorage;
458
+ this._roomContext = roomContext;
456
459
  this._router = router;
457
460
  this._platform = platform.initialize(__spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), { roomContext }));
458
461
  this._listeners = {
@@ -669,11 +672,11 @@ var IORoom = class {
669
672
  const user = session.user;
670
673
  if (!!user) this._removeUserSession(user.id, sessionId);
671
674
  try {
672
- const doc = yield this._doc;
675
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
673
676
  const encodedState = doc.getEncodedState();
674
677
  yield Promise.resolve(
675
678
  this._listeners.onUserDisconnected({
676
- context: this._context,
679
+ context,
677
680
  encodedState,
678
681
  platform: this._platform,
679
682
  room: this.id,
@@ -714,7 +717,7 @@ var IORoom = class {
714
717
  const sessionId = session.id;
715
718
  const presence = session.presence;
716
719
  const user = session.user;
717
- const doc = yield this._doc;
720
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
718
721
  const encodedState = doc.isEmpty() ? null : doc.getEncodedState();
719
722
  yield this._sendSelfMessage(
720
723
  {
@@ -731,7 +734,7 @@ var IORoom = class {
731
734
  try {
732
735
  yield Promise.resolve(
733
736
  this._listeners.onUserConnected({
734
- context: this._context,
737
+ context,
735
738
  encodedState,
736
739
  platform: this._platform,
737
740
  room: this.id,
@@ -801,10 +804,27 @@ var IORoom = class {
801
804
  }
802
805
  });
803
806
  }
807
+ _getContext() {
808
+ return __async(this, null, function* () {
809
+ return typeof this._context === "function" ? yield Promise.resolve(this._context(this._roomContext)) : yield Promise.resolve(this._context);
810
+ });
811
+ }
804
812
  _getInitialDoc() {
805
813
  return __async(this, null, function* () {
806
814
  const doc = this._docFactory.getEmpty();
807
- const encodedState = yield this._platform.persistence.getStorageState(this.id);
815
+ const [encodedState, context] = yield Promise.all([
816
+ this._platform.persistence.getStorageState(this.id),
817
+ this._getContext()
818
+ ]);
819
+ if (!encodedState) {
820
+ const loadedState = yield this._getInitialStorage({
821
+ context,
822
+ room: this.id
823
+ });
824
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
825
+ doc.applyEncodedState({ update: loadedState });
826
+ }
827
+ }
808
828
  if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
809
829
  return doc;
810
830
  });
@@ -889,13 +909,13 @@ var IORoom = class {
889
909
  const doc = yield this._getInitialDoc();
890
910
  const uninitialize = () => __async(this, null, function* () {
891
911
  this._platform.pubSub.unsubscribe(pubSubId);
892
- const doc2 = yield this._doc;
912
+ const [doc2, context] = yield Promise.all([this._doc, this._getContext()]);
893
913
  const encodedState = doc2.getEncodedState();
894
914
  doc2.destroy();
895
915
  this._doc = Promise.resolve(this._docFactory.getEmpty());
896
916
  yield Promise.resolve(
897
917
  this._listeners.onDestroy(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
898
- context: this._context,
918
+ context,
899
919
  encodedState,
900
920
  platform: this._platform,
901
921
  room: this.id
@@ -929,9 +949,9 @@ var IORoom = class {
929
949
  const setPresence = (params) => {
930
950
  this._setPresence.bind(this)(params);
931
951
  };
932
- const doc = yield this._doc;
952
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
933
953
  const eventContext = {
934
- context: this._context,
954
+ context,
935
955
  doc,
936
956
  garbageCollect: () => __async(this, null, function* () {
937
957
  yield this.garbageCollect();
@@ -960,7 +980,7 @@ var IORoom = class {
960
980
  if (!message) return;
961
981
  const procedure = this._getProcedure(message);
962
982
  this._listeners.onMessage(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
963
- context: this._context,
983
+ context,
964
984
  encodedState: doc.getEncodedState(),
965
985
  message,
966
986
  platform: this._platform,
@@ -1123,9 +1143,9 @@ var IORoom = class {
1123
1143
  if (!sender) return;
1124
1144
  const connectionId = sender.sessionId;
1125
1145
  if (typeof connectionId !== "string") return;
1126
- const doc = yield this._doc;
1127
- const context = {
1128
- context: this._context,
1146
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
1147
+ const resolverCtx = {
1148
+ context,
1129
1149
  doc,
1130
1150
  garbageCollect: () => __async(this, null, function* () {
1131
1151
  yield this.garbageCollect();
@@ -1150,7 +1170,7 @@ var IORoom = class {
1150
1170
  } catch (e) {
1151
1171
  return;
1152
1172
  }
1153
- Promise.resolve(resolver(inputs, context)).then((output) => {
1173
+ Promise.resolve(resolver(inputs, resolverCtx)).then((output) => {
1154
1174
  if (!output) return;
1155
1175
  Object.keys(output).forEach((type) => {
1156
1176
  var _a2, _b;
@@ -1170,7 +1190,7 @@ var IORoom = class {
1170
1190
  };
1171
1191
 
1172
1192
  // src/version.generated.ts
1173
- var version = "3.1.6";
1193
+ var version = "3.2.0";
1174
1194
 
1175
1195
  // src/PluvServer.ts
1176
1196
  var PluvServer = class {
@@ -1193,6 +1213,7 @@ var PluvServer = class {
1193
1213
  onUserConnected,
1194
1214
  onUserDisconnected
1195
1215
  } = options;
1216
+ this._docFactory = this._config.crdt.doc(() => ({}));
1196
1217
  this._listeners = {
1197
1218
  onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
1198
1219
  onRoomMessage: (event) => onRoomMessage == null ? void 0 : onRoomMessage(event),
@@ -1267,11 +1288,8 @@ var PluvServer = class {
1267
1288
  const update = data == null ? void 0 : data.update;
1268
1289
  if (!oldState) {
1269
1290
  const loadedState = yield this._getInitialStorage({ context, room });
1270
- const checkDoc = this._config.crdt.doc(() => ({})).getEmpty().applyEncodedState({ update: loadedState });
1271
- const isEmpty = checkDoc.isEmpty();
1272
- checkDoc.destroy();
1273
- if (!!loadedState && !isEmpty) {
1274
- doc.applyEncodedState({ update: loadedState }).getEncodedState();
1291
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
1292
+ doc.applyEncodedState({ update: loadedState });
1275
1293
  }
1276
1294
  }
1277
1295
  if (!doc.isEmpty()) {
@@ -1384,14 +1402,14 @@ var PluvServer = class {
1384
1402
  if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
1385
1403
  throw new Error("Unsupported room name");
1386
1404
  const roomContext = platformRoomContext;
1387
- const context = typeof this._config.context === "function" ? this._config.context(roomContext) : this._config.context;
1388
1405
  const listeners = this._getListeners();
1389
1406
  const logDebug = this._logDebug.bind(this);
1390
1407
  const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
1391
1408
  authorize: (_c = this._config.authorize) != null ? _c : void 0,
1392
- context,
1409
+ context: this._config.context,
1393
1410
  crdt: this._config.crdt,
1394
1411
  debug: debug != null ? debug : this._config.debug,
1412
+ getInitialStorage: this._getInitialStorage,
1395
1413
  onDestroy(event) {
1396
1414
  return __async(this, null, function* () {
1397
1415
  logDebug(`${import_kleur2.default.blue("Deleting empty room:")} ${room}`);
package/dist/index.mjs CHANGED
@@ -398,6 +398,7 @@ var IORoom = class {
398
398
  context,
399
399
  crdt = noop,
400
400
  debug,
401
+ getInitialStorage,
401
402
  onDestroy,
402
403
  onMessage,
403
404
  onUserConnected,
@@ -411,6 +412,8 @@ var IORoom = class {
411
412
  this._crdt = crdt;
412
413
  this._debug = debug;
413
414
  this._docFactory = crdt.doc(() => ({}));
415
+ this._getInitialStorage = getInitialStorage;
416
+ this._roomContext = roomContext;
414
417
  this._router = router;
415
418
  this._platform = platform.initialize(__spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), { roomContext }));
416
419
  this._listeners = {
@@ -627,11 +630,11 @@ var IORoom = class {
627
630
  const user = session.user;
628
631
  if (!!user) this._removeUserSession(user.id, sessionId);
629
632
  try {
630
- const doc = yield this._doc;
633
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
631
634
  const encodedState = doc.getEncodedState();
632
635
  yield Promise.resolve(
633
636
  this._listeners.onUserDisconnected({
634
- context: this._context,
637
+ context,
635
638
  encodedState,
636
639
  platform: this._platform,
637
640
  room: this.id,
@@ -672,7 +675,7 @@ var IORoom = class {
672
675
  const sessionId = session.id;
673
676
  const presence = session.presence;
674
677
  const user = session.user;
675
- const doc = yield this._doc;
678
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
676
679
  const encodedState = doc.isEmpty() ? null : doc.getEncodedState();
677
680
  yield this._sendSelfMessage(
678
681
  {
@@ -689,7 +692,7 @@ var IORoom = class {
689
692
  try {
690
693
  yield Promise.resolve(
691
694
  this._listeners.onUserConnected({
692
- context: this._context,
695
+ context,
693
696
  encodedState,
694
697
  platform: this._platform,
695
698
  room: this.id,
@@ -759,10 +762,27 @@ var IORoom = class {
759
762
  }
760
763
  });
761
764
  }
765
+ _getContext() {
766
+ return __async(this, null, function* () {
767
+ return typeof this._context === "function" ? yield Promise.resolve(this._context(this._roomContext)) : yield Promise.resolve(this._context);
768
+ });
769
+ }
762
770
  _getInitialDoc() {
763
771
  return __async(this, null, function* () {
764
772
  const doc = this._docFactory.getEmpty();
765
- const encodedState = yield this._platform.persistence.getStorageState(this.id);
773
+ const [encodedState, context] = yield Promise.all([
774
+ this._platform.persistence.getStorageState(this.id),
775
+ this._getContext()
776
+ ]);
777
+ if (!encodedState) {
778
+ const loadedState = yield this._getInitialStorage({
779
+ context,
780
+ room: this.id
781
+ });
782
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
783
+ doc.applyEncodedState({ update: loadedState });
784
+ }
785
+ }
766
786
  if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
767
787
  return doc;
768
788
  });
@@ -847,13 +867,13 @@ var IORoom = class {
847
867
  const doc = yield this._getInitialDoc();
848
868
  const uninitialize = () => __async(this, null, function* () {
849
869
  this._platform.pubSub.unsubscribe(pubSubId);
850
- const doc2 = yield this._doc;
870
+ const [doc2, context] = yield Promise.all([this._doc, this._getContext()]);
851
871
  const encodedState = doc2.getEncodedState();
852
872
  doc2.destroy();
853
873
  this._doc = Promise.resolve(this._docFactory.getEmpty());
854
874
  yield Promise.resolve(
855
875
  this._listeners.onDestroy(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
856
- context: this._context,
876
+ context,
857
877
  encodedState,
858
878
  platform: this._platform,
859
879
  room: this.id
@@ -887,9 +907,9 @@ var IORoom = class {
887
907
  const setPresence = (params) => {
888
908
  this._setPresence.bind(this)(params);
889
909
  };
890
- const doc = yield this._doc;
910
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
891
911
  const eventContext = {
892
- context: this._context,
912
+ context,
893
913
  doc,
894
914
  garbageCollect: () => __async(this, null, function* () {
895
915
  yield this.garbageCollect();
@@ -918,7 +938,7 @@ var IORoom = class {
918
938
  if (!message) return;
919
939
  const procedure = this._getProcedure(message);
920
940
  this._listeners.onMessage(__spreadProps(__spreadValues({}, "_meta" in this._platform && !!this._platform._meta ? { _meta: this._platform._meta } : {}), {
921
- context: this._context,
941
+ context,
922
942
  encodedState: doc.getEncodedState(),
923
943
  message,
924
944
  platform: this._platform,
@@ -1081,9 +1101,9 @@ var IORoom = class {
1081
1101
  if (!sender) return;
1082
1102
  const connectionId = sender.sessionId;
1083
1103
  if (typeof connectionId !== "string") return;
1084
- const doc = yield this._doc;
1085
- const context = {
1086
- context: this._context,
1104
+ const [doc, context] = yield Promise.all([this._doc, this._getContext()]);
1105
+ const resolverCtx = {
1106
+ context,
1087
1107
  doc,
1088
1108
  garbageCollect: () => __async(this, null, function* () {
1089
1109
  yield this.garbageCollect();
@@ -1108,7 +1128,7 @@ var IORoom = class {
1108
1128
  } catch (e) {
1109
1129
  return;
1110
1130
  }
1111
- Promise.resolve(resolver(inputs, context)).then((output) => {
1131
+ Promise.resolve(resolver(inputs, resolverCtx)).then((output) => {
1112
1132
  if (!output) return;
1113
1133
  Object.keys(output).forEach((type) => {
1114
1134
  var _a2, _b;
@@ -1128,7 +1148,7 @@ var IORoom = class {
1128
1148
  };
1129
1149
 
1130
1150
  // src/version.generated.ts
1131
- var version = "3.1.6";
1151
+ var version = "3.2.0";
1132
1152
 
1133
1153
  // src/PluvServer.ts
1134
1154
  var PluvServer = class {
@@ -1151,6 +1171,7 @@ var PluvServer = class {
1151
1171
  onUserConnected,
1152
1172
  onUserDisconnected
1153
1173
  } = options;
1174
+ this._docFactory = this._config.crdt.doc(() => ({}));
1154
1175
  this._listeners = {
1155
1176
  onRoomDeleted: (event) => onRoomDeleted == null ? void 0 : onRoomDeleted(event),
1156
1177
  onRoomMessage: (event) => onRoomMessage == null ? void 0 : onRoomMessage(event),
@@ -1225,11 +1246,8 @@ var PluvServer = class {
1225
1246
  const update = data == null ? void 0 : data.update;
1226
1247
  if (!oldState) {
1227
1248
  const loadedState = yield this._getInitialStorage({ context, room });
1228
- const checkDoc = this._config.crdt.doc(() => ({})).getEmpty().applyEncodedState({ update: loadedState });
1229
- const isEmpty = checkDoc.isEmpty();
1230
- checkDoc.destroy();
1231
- if (!!loadedState && !isEmpty) {
1232
- doc.applyEncodedState({ update: loadedState }).getEncodedState();
1249
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
1250
+ doc.applyEncodedState({ update: loadedState });
1233
1251
  }
1234
1252
  }
1235
1253
  if (!doc.isEmpty()) {
@@ -1342,14 +1360,14 @@ var PluvServer = class {
1342
1360
  if (!/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/i.test(room))
1343
1361
  throw new Error("Unsupported room name");
1344
1362
  const roomContext = platformRoomContext;
1345
- const context = typeof this._config.context === "function" ? this._config.context(roomContext) : this._config.context;
1346
1363
  const listeners = this._getListeners();
1347
1364
  const logDebug = this._logDebug.bind(this);
1348
1365
  const newRoom = new IORoom(room, __spreadProps(__spreadValues({}, !!_meta ? { _meta } : {}), {
1349
1366
  authorize: (_c = this._config.authorize) != null ? _c : void 0,
1350
- context,
1367
+ context: this._config.context,
1351
1368
  crdt: this._config.crdt,
1352
1369
  debug: debug != null ? debug : this._config.debug,
1370
+ getInitialStorage: this._getInitialStorage,
1353
1371
  onDestroy(event) {
1354
1372
  return __async(this, null, function* () {
1355
1373
  logDebug(`${colors2.blue("Deleting empty room:")} ${room}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/io",
3
- "version": "3.1.6",
3
+ "version": "3.2.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": "^3.1.6",
25
- "@pluv/types": "^3.1.6"
24
+ "@pluv/crdt": "^3.2.0",
25
+ "@pluv/types": "^3.2.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint": "^9.27.0",
28
+ "eslint": "^9.28.0",
29
29
  "genversion": "^3.2.0",
30
30
  "tsup": "^8.5.0",
31
31
  "typescript": "^5.8.3",
32
- "@pluv/tsconfig": "^3.1.6",
33
- "eslint-config-pluv": "^3.1.6"
32
+ "@pluv/tsconfig": "^3.2.0",
33
+ "eslint-config-pluv": "^3.2.0"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "pnpm run version && tsup src/index.ts --format esm,cjs --dts",
package/src/IORoom.ts CHANGED
@@ -36,10 +36,12 @@ import { GARBAGE_COLLECT_INTERVAL_MS, PING_TIMEOUT_MS } from "./constants";
36
36
  import type {
37
37
  EventResolverContext,
38
38
  EventResolverKind,
39
+ GetInitialStorageFn,
39
40
  IORoomListenerEvent,
40
41
  IORoomMessageEvent,
41
42
  IOUserConnectedEvent,
42
43
  IOUserDisconnectedEvent,
44
+ PluvContext,
43
45
  PluvIOAuthorize,
44
46
  ResolvedPluvIOAuthorize,
45
47
  WebSocketSession,
@@ -88,9 +90,10 @@ export type IORoomConfig<
88
90
  TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
89
91
  > = Partial<IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>> & {
90
92
  authorize?: TAuthorize;
91
- context: TContext;
93
+ context: PluvContext<TPlatform, TContext>;
92
94
  crdt?: { doc: (value: any) => AbstractCrdtDocFactory<any, any> };
93
95
  debug: boolean;
96
+ getInitialStorage: GetInitialStorageFn<TContext>;
94
97
  platform: TPlatform;
95
98
  roomContext: InferRoomContextType<TPlatform>;
96
99
  router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
@@ -132,12 +135,14 @@ export class IORoom<
132
135
  private _uninitialize: Promise<() => Promise<void>> | null = null;
133
136
 
134
137
  private readonly _authorize: TAuthorize = null as TAuthorize;
135
- private readonly _context: TContext;
138
+ private readonly _context: PluvContext<TPlatform, TContext>;
136
139
  private readonly _crdt: TCrdt;
137
140
  private readonly _debug: boolean;
138
141
  private readonly _docFactory: AbstractCrdtDocFactory<any, any>;
142
+ private readonly _getInitialStorage: GetInitialStorageFn<TContext>;
139
143
  private readonly _listeners: IORoomListeners<TPlatform, TAuthorize, TContext, TEvents>;
140
144
  private readonly _platform: TPlatform;
145
+ private readonly _roomContext: InferRoomContextType<TPlatform>;
141
146
  private readonly _router: PluvRouter<TPlatform, TAuthorize, TContext, TEvents>;
142
147
  private readonly _sessions = new Map<
143
148
  [sessionId: string][0],
@@ -194,6 +199,7 @@ export class IORoom<
194
199
  context,
195
200
  crdt = noop,
196
201
  debug,
202
+ getInitialStorage,
197
203
  onDestroy,
198
204
  onMessage,
199
205
  onUserConnected,
@@ -209,6 +215,8 @@ export class IORoom<
209
215
  this._crdt = crdt as TCrdt;
210
216
  this._debug = debug;
211
217
  this._docFactory = crdt.doc(() => ({}));
218
+ this._getInitialStorage = getInitialStorage;
219
+ this._roomContext = roomContext;
212
220
  this._router = router;
213
221
  this._platform = platform.initialize({ ...(!!_meta ? { _meta } : {}), roomContext });
214
222
 
@@ -473,12 +481,12 @@ export class IORoom<
473
481
  if (!!user) this._removeUserSession(user.id, sessionId);
474
482
 
475
483
  try {
476
- const doc = await this._doc;
484
+ const [doc, context] = await Promise.all([this._doc, this._getContext()]);
477
485
  const encodedState = doc.getEncodedState();
478
486
 
479
487
  await Promise.resolve(
480
488
  this._listeners.onUserDisconnected({
481
- context: this._context,
489
+ context,
482
490
  encodedState,
483
491
  platform: this._platform,
484
492
  room: this.id,
@@ -526,7 +534,7 @@ export class IORoom<
526
534
  const presence = session.presence;
527
535
  const user = session.user;
528
536
 
529
- const doc = await this._doc;
537
+ const [doc, context] = await Promise.all([this._doc, this._getContext()]);
530
538
  const encodedState = doc.isEmpty() ? null : doc.getEncodedState();
531
539
 
532
540
  await this._sendSelfMessage(
@@ -545,7 +553,7 @@ export class IORoom<
545
553
  try {
546
554
  await Promise.resolve(
547
555
  this._listeners.onUserConnected({
548
- context: this._context,
556
+ context,
549
557
  encodedState,
550
558
  platform: this._platform,
551
559
  room: this.id,
@@ -636,9 +644,30 @@ export class IORoom<
636
644
  }
637
645
  }
638
646
 
647
+ private async _getContext(): Promise<TContext> {
648
+ return typeof this._context === "function"
649
+ ? await Promise.resolve(this._context(this._roomContext))
650
+ : await Promise.resolve(this._context);
651
+ }
652
+
639
653
  private async _getInitialDoc(): Promise<CrdtDocLike<any, any>> {
640
654
  const doc = this._docFactory.getEmpty();
641
- const encodedState = await this._platform.persistence.getStorageState(this.id);
655
+
656
+ const [encodedState, context] = await Promise.all([
657
+ this._platform.persistence.getStorageState(this.id),
658
+ this._getContext(),
659
+ ]);
660
+
661
+ if (!encodedState) {
662
+ const loadedState = await this._getInitialStorage({
663
+ context,
664
+ room: this.id,
665
+ });
666
+
667
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
668
+ doc.applyEncodedState({ update: loadedState });
669
+ }
670
+ }
642
671
 
643
672
  if (typeof encodedState === "string") doc.applyEncodedState({ update: encodedState });
644
673
 
@@ -766,7 +795,7 @@ export class IORoom<
766
795
  const uninitialize = async () => {
767
796
  this._platform.pubSub.unsubscribe(pubSubId);
768
797
 
769
- const doc = await this._doc;
798
+ const [doc, context] = await Promise.all([this._doc, this._getContext()]);
770
799
  const encodedState = doc.getEncodedState();
771
800
 
772
801
  doc.destroy();
@@ -777,7 +806,7 @@ export class IORoom<
777
806
  ...("_meta" in this._platform && !!this._platform._meta
778
807
  ? { _meta: this._platform._meta }
779
808
  : {}),
780
- context: this._context,
809
+ context,
781
810
  encodedState,
782
811
  platform: this._platform,
783
812
  room: this.id,
@@ -824,14 +853,14 @@ export class IORoom<
824
853
  this._setPresence.bind(this)(params);
825
854
  };
826
855
 
827
- const doc = await this._doc;
856
+ const [doc, context] = await Promise.all([this._doc, this._getContext()]);
828
857
  const eventContext: EventResolverContext<
829
858
  EventResolverKind,
830
859
  TPlatform,
831
860
  TAuthorize,
832
861
  TContext
833
862
  > = {
834
- context: this._context,
863
+ context,
835
864
  doc,
836
865
  garbageCollect: async () => {
837
866
  await this.garbageCollect();
@@ -870,7 +899,7 @@ export class IORoom<
870
899
  ...("_meta" in this._platform && !!this._platform._meta
871
900
  ? { _meta: this._platform._meta }
872
901
  : {}),
873
- context: this._context,
902
+ context,
874
903
  encodedState: doc.getEncodedState(),
875
904
  message: message as InferEventMessage<
876
905
  InferEventsOutput<TEvents>,
@@ -1111,9 +1140,9 @@ export class IORoom<
1111
1140
 
1112
1141
  if (typeof connectionId !== "string") return;
1113
1142
 
1114
- const doc = await this._doc;
1115
- const context: EventResolverContext<"sync", TPlatform, TAuthorize, TContext> = {
1116
- context: this._context,
1143
+ const [doc, context] = await Promise.all([this._doc, this._getContext()]);
1144
+ const resolverCtx: EventResolverContext<"sync", TPlatform, TAuthorize, TContext> = {
1145
+ context,
1117
1146
  doc,
1118
1147
  garbageCollect: async () => {
1119
1148
  await this.garbageCollect();
@@ -1143,7 +1172,7 @@ export class IORoom<
1143
1172
  return;
1144
1173
  }
1145
1174
 
1146
- Promise.resolve(resolver(inputs, context)).then((output) => {
1175
+ Promise.resolve(resolver(inputs, resolverCtx)).then((output) => {
1147
1176
  if (!output) return;
1148
1177
 
1149
1178
  Object.keys(output).forEach((type: string) => {
package/src/PluvServer.ts CHANGED
@@ -108,6 +108,7 @@ export class PluvServer<
108
108
  private readonly _config: NonNilProps<
109
109
  PluvServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>
110
110
  >;
111
+ private readonly _docFactory: AbstractCrdtDocFactory<any, any>;
111
112
 
112
113
  public get fetch(): (...args: any[]) => Promise<any> {
113
114
  return (...args: any[]): Promise<any> => {
@@ -136,7 +137,7 @@ export class PluvServer<
136
137
  platform: this._config.platform(),
137
138
  } as {
138
139
  authorize: TAuthorize;
139
- context: TContext;
140
+ context: PluvContext<TPlatform, TContext>;
140
141
  crdt: TCrdt;
141
142
  events: TEvents;
142
143
  platform: TPlatform;
@@ -211,15 +212,8 @@ export class PluvServer<
211
212
  if (!oldState) {
212
213
  const loadedState = await this._getInitialStorage({ context, room });
213
214
 
214
- const checkDoc = this._config.crdt
215
- .doc(() => ({}))
216
- .getEmpty()
217
- .applyEncodedState({ update: loadedState });
218
- const isEmpty = checkDoc.isEmpty();
219
- checkDoc.destroy();
220
-
221
- if (!!loadedState && !isEmpty) {
222
- doc.applyEncodedState({ update: loadedState }).getEncodedState();
215
+ if (!!loadedState && !this._docFactory.isEmpty(loadedState)) {
216
+ doc.applyEncodedState({ update: loadedState });
223
217
  }
224
218
  }
225
219
 
@@ -390,6 +384,7 @@ export class PluvServer<
390
384
  onUserDisconnected,
391
385
  } = options as Partial<BasePluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>>;
392
386
 
387
+ this._docFactory = this._config.crdt.doc(() => ({}));
393
388
  (this as any)._listeners = {
394
389
  onRoomDeleted: (event) => onRoomDeleted?.(event),
395
390
  onRoomMessage: (event) => onRoomMessage?.(event),
@@ -418,19 +413,16 @@ export class PluvServer<
418
413
  throw new Error("Unsupported room name");
419
414
 
420
415
  const roomContext = platformRoomContext as InferRoomContextType<TPlatform>;
421
- const context: TContext =
422
- typeof this._config.context === "function"
423
- ? this._config.context(roomContext)
424
- : this._config.context;
425
416
  const listeners = this._getListeners();
426
417
  const logDebug = this._logDebug.bind(this);
427
418
 
428
419
  const newRoom = new IORoom<TPlatform, TAuthorize, TContext, TCrdt, TEvents>(room, {
429
420
  ...(!!_meta ? { _meta } : {}),
430
421
  authorize: this._config.authorize ?? undefined,
431
- context,
422
+ context: this._config.context,
432
423
  crdt: this._config.crdt,
433
424
  debug: debug ?? this._config.debug,
425
+ getInitialStorage: this._getInitialStorage,
434
426
  async onDestroy(event) {
435
427
  logDebug(`${colors.blue("Deleting empty room:")} ${room}`);
436
428
 
package/src/types.ts CHANGED
@@ -25,8 +25,8 @@ import type { AbstractWebSocket } from "./AbstractWebSocket";
25
25
  import type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
26
26
 
27
27
  export type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> =
28
- | TContext
29
- | ((params: InferRoomContextType<TPlatform>) => TContext);
28
+ | MaybePromise<TContext>
29
+ | ((params: InferRoomContextType<TPlatform>) => MaybePromise<TContext>);
30
30
 
31
31
  export type EventResolverKind = "broadcast" | "self" | "sync";
32
32
 
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '3.1.6'
2
+ export const version = '3.2.0'