@pluv/client 1.0.1 → 2.0.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/PluvRoom.ts CHANGED
@@ -1,8 +1,16 @@
1
- import type { AbstractCrdtDoc, CrdtType, InferCrdtJson } from "@pluv/crdt";
1
+ import type {
2
+ AbstractCrdtDocFactory,
3
+ CrdtType,
4
+ InferCrdtJson,
5
+ InferStorage,
6
+ NoopCrdtDocFactory,
7
+ } from "@pluv/crdt";
2
8
  import type {
3
9
  BaseIOEventRecord,
4
10
  BaseUser,
11
+ CrdtDocLike,
5
12
  EventMessage,
13
+ EventNotifierSubscriptionCallback,
6
14
  IOEventMessage,
7
15
  IOLike,
8
16
  Id,
@@ -12,26 +20,27 @@ import type {
12
20
  InferIOOutput,
13
21
  InputZodLike,
14
22
  JsonObject,
23
+ MergeEvents,
15
24
  OptionalProps,
25
+ OtherNotifierSubscriptionCallback,
26
+ RoomLike,
27
+ UpdateMyPresenceAction,
28
+ UserInfo,
29
+ WebSocketState,
16
30
  } from "@pluv/types";
17
- import { AbstractRoom } from "./AbstractRoom";
31
+ import { ConnectionState } from "@pluv/types";
18
32
  import type { AbstractStorageStore } from "./AbstractStorageStore";
19
33
  import type { CrdtManagerOptions } from "./CrdtManager";
20
34
  import { CrdtManager } from "./CrdtManager";
21
35
  import { CrdtNotifier } from "./CrdtNotifier";
22
- import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
23
36
  import { EventNotifier } from "./EventNotifier";
24
- import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
25
37
  import { OtherNotifier } from "./OtherNotifier";
26
38
  import { PluvProcedure } from "./PluvProcedure";
27
- import type { MergeEvents, PluvRouterEventConfig } from "./PluvRouter";
39
+ import type { PluvRouterEventConfig } from "./PluvRouter";
28
40
  import { PluvRouter } from "./PluvRouter";
29
41
  import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
30
42
  import { StateNotifier } from "./StateNotifier";
31
43
  import { StorageStore } from "./StorageStore";
32
- import type { UsersManagerConfig } from "./UsersManager";
33
- import { UsersManager } from "./UsersManager";
34
- import { ConnectionState } from "./enums";
35
44
  import type {
36
45
  AuthorizationState,
37
46
  EventResolver,
@@ -39,12 +48,11 @@ import type {
39
48
  InternalSubscriptions,
40
49
  PluvClientLimits,
41
50
  PublicKey,
42
- UpdateMyPresenceAction,
43
- UserInfo,
44
51
  WebSocketConnection,
45
- WebSocketState,
46
52
  WithMetadata,
47
53
  } from "./types";
54
+ import type { UsersManagerConfig } from "./UsersManager";
55
+ import { UsersManager } from "./UsersManager";
48
56
  import { debounce } from "./utils";
49
57
 
50
58
  const ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1_000;
@@ -68,12 +76,12 @@ declare global {
68
76
  }
69
77
 
70
78
  export const DEFAULT_PLUV_CLIENT_ADDON = <
71
- TIO extends IOLike = IOLike,
72
- TMetadata extends JsonObject = {},
73
- TPresence extends JsonObject = {},
74
- TStorage extends Record<string, CrdtType<any, any>> = {},
79
+ TIO extends IOLike,
80
+ TMetadata extends JsonObject,
81
+ TPresence extends JsonObject,
82
+ TCrdt extends AbstractCrdtDocFactory<any>,
75
83
  >(
76
- input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>,
84
+ input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>,
77
85
  ): PluvRoomAddonResult => ({
78
86
  storage: new StorageStore(input.room.id),
79
87
  });
@@ -126,21 +134,19 @@ interface InternalListeners {
126
134
  }
127
135
 
128
136
  export type PluvRoomAddon<
129
- TIO extends IOLike = IOLike,
130
- TMetadata extends JsonObject = {},
131
- TPresence extends JsonObject = {},
132
- TStorage extends Record<string, CrdtType<any, any>> = {},
133
- > = (
134
- input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>,
135
- ) => Partial<PluvRoomAddonResult>;
137
+ TIO extends IOLike,
138
+ TMetadata extends JsonObject,
139
+ TPresence extends JsonObject,
140
+ TCrdt extends AbstractCrdtDocFactory<any>,
141
+ > = (input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TCrdt>) => Partial<PluvRoomAddonResult>;
136
142
 
137
143
  export interface PluvRoomAddonInput<
138
- TIO extends IOLike = IOLike,
139
- TMetadata extends JsonObject = {},
140
- TPresence extends JsonObject = {},
141
- TStorage extends Record<string, CrdtType<any, any>> = {},
144
+ TIO extends IOLike,
145
+ TMetadata extends JsonObject,
146
+ TPresence extends JsonObject,
147
+ TCrdt extends AbstractCrdtDocFactory<any>,
142
148
  > {
143
- room: PluvRoom<TIO, TMetadata, TPresence, TStorage>;
149
+ room: PluvRoom<TIO, TMetadata, TPresence, TCrdt>;
144
150
  }
145
151
 
146
152
  export interface PluvRoomAddonResult {
@@ -167,22 +173,22 @@ export type ReconnectTimeoutMs = number | ((params: ReconnectTimeoutMsParams) =>
167
173
 
168
174
  export type RoomConfig<
169
175
  TIO extends IOLike,
170
- TMetadata extends JsonObject = {},
171
- TPresence extends JsonObject = {},
172
- TStorage extends Record<string, CrdtType<any, any>> = {},
173
- TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
176
+ TMetadata extends JsonObject,
177
+ TPresence extends JsonObject,
178
+ TCrdt extends AbstractCrdtDocFactory<any>,
179
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
174
180
  > = Id<
175
181
  {
176
- addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
182
+ addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[];
177
183
  debug?: boolean | PluvRoomDebug<TIO>;
178
184
  limits: PluvClientLimits;
179
185
  onAuthorizationFail?: (error: Error) => void;
180
186
  metadata?: InputZodLike<TMetadata>;
181
187
  publicKey?: PublicKey<TMetadata>;
182
188
  reconnectTimeoutMs?: ReconnectTimeoutMs;
183
- router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
189
+ router?: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
184
190
  } & RoomEndpoints<TIO, TMetadata> &
185
- Pick<CrdtManagerOptions<TStorage>, "initialStorage"> &
191
+ Pick<CrdtManagerOptions<TCrdt>, "initialStorage"> &
186
192
  UsersManagerConfig<TPresence>
187
193
  >;
188
194
 
@@ -190,15 +196,17 @@ export class PluvRoom<
190
196
  TIO extends IOLike<any, any>,
191
197
  TMetadata extends JsonObject = {},
192
198
  TPresence extends JsonObject = {},
193
- TStorage extends Record<string, CrdtType<any, any>> = {},
194
- TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
195
- > extends AbstractRoom<TIO, TPresence, TStorage> {
199
+ TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
200
+ TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
201
+ > implements RoomLike<TIO, TPresence, InferStorage<TCrdt>>
202
+ {
196
203
  readonly _endpoints: RoomEndpoints<TIO, TMetadata>;
197
204
 
205
+ public readonly id: string;
198
206
  public readonly metadata?: InputZodLike<TMetadata>;
199
207
 
200
- private readonly _crdtManager: CrdtManager<TStorage>;
201
- private readonly _crdtNotifier = new CrdtNotifier<TStorage>();
208
+ private readonly _crdtManager: CrdtManager<TCrdt>;
209
+ private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();
202
210
  private readonly _debug: boolean | PluvRoomDebug<TIO>;
203
211
  private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
204
212
  private readonly _intervals: IntervalIds = {
@@ -209,7 +217,7 @@ export class PluvRoom<
209
217
  private readonly _otherNotifier = new OtherNotifier<TIO, TPresence>();
210
218
  private readonly _publicKey: PublicKey<TMetadata> | null = null;
211
219
  private readonly _reconnectTimeoutMs: ReconnectTimeoutMs;
212
- private readonly _router: PluvRouter<TIO, TPresence, TStorage, TEvents>;
220
+ private readonly _router: PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>;
213
221
  private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();
214
222
  private readonly _storageStore: AbstractStorageStore;
215
223
  private readonly _subscriptions: InternalSubscriptions = {
@@ -238,7 +246,7 @@ export class PluvRoom<
238
246
  private _windowListeners: WindowListeners | null = null;
239
247
  private _wsListeners: WebSocketListeners | null = null;
240
248
 
241
- constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence, TStorage, TEvents>) {
249
+ constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence, TCrdt, TEvents>) {
242
250
  const {
243
251
  addons = [],
244
252
  authEndpoint,
@@ -255,22 +263,20 @@ export class PluvRoom<
255
263
  wsEndpoint,
256
264
  } = options;
257
265
 
258
- super(room);
266
+ this.id = room;
267
+ this.metadata = metadata;
259
268
 
260
269
  const addon = this._getAddon(addons);
261
-
262
270
  const { storage } = {
263
271
  ...DEFAULT_PLUV_CLIENT_ADDON({ room: this }),
264
272
  ...addon({ room: this }),
265
273
  };
266
-
267
- this.metadata = metadata;
274
+ this._storageStore = storage;
268
275
 
269
276
  this._debug = debug;
270
277
  this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO, TMetadata>;
271
278
  this._limits = limits;
272
279
  this._reconnectTimeoutMs = reconnectTimeoutMs;
273
- this._storageStore = storage;
274
280
 
275
281
  if (!!publicKey) this._publicKey = publicKey;
276
282
 
@@ -281,13 +287,14 @@ export class PluvRoom<
281
287
  };
282
288
 
283
289
  this._router =
284
- router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
290
+ router ??
291
+ (new PluvRouter({}) as PluvRouter<TIO, TPresence, InferStorage<TCrdt>, TEvents>);
285
292
  this._usersManager = new UsersManager<TIO, TPresence>({
286
293
  initialPresence,
287
294
  limits: this._limits,
288
295
  presence,
289
296
  });
290
- this._crdtManager = new CrdtManager<TStorage>({ initialStorage });
297
+ this._crdtManager = new CrdtManager<TCrdt>({ initialStorage });
291
298
  }
292
299
 
293
300
  public get storageLoaded(): boolean {
@@ -313,7 +320,7 @@ export class PluvRoom<
313
320
  any,
314
321
  any,
315
322
  TPresence,
316
- TStorage
323
+ InferStorage<TCrdt>
317
324
  > | null;
318
325
 
319
326
  if (!procedure?.config.broadcast) {
@@ -327,7 +334,7 @@ export class PluvRoom<
327
334
  if (!myself) return;
328
335
 
329
336
  const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
330
- const context: EventResolverContext<TIO, TPresence, TStorage> = {
337
+ const context: EventResolverContext<TIO, TPresence, InferStorage<TCrdt>> = {
331
338
  doc: this._crdtManager.doc,
332
339
  others: this._usersManager.getOthers(),
333
340
  room: this.id,
@@ -335,7 +342,13 @@ export class PluvRoom<
335
342
  };
336
343
 
337
344
  const output = await (
338
- procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>
345
+ procedure.config.broadcast as EventResolver<
346
+ TIO,
347
+ any,
348
+ any,
349
+ TPresence,
350
+ InferStorage<TCrdt>
351
+ >
339
352
  )(parsed, context);
340
353
 
341
354
  Object.entries(output).forEach(([_type, _data]) => {
@@ -389,8 +402,7 @@ export class PluvRoom<
389
402
  return oldState;
390
403
  });
391
404
 
392
- // await this._storageStore.initialize();
393
- // await this._applyStorageStore();
405
+ await this._storageStore.initialize();
394
406
 
395
407
  const wsEndpoint = this._getWsEndpoint(this.id, params);
396
408
  const url = new URL(wsEndpoint);
@@ -484,7 +496,7 @@ export class PluvRoom<
484
496
  return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
485
497
  };
486
498
 
487
- public getDoc = (): AbstractCrdtDoc<TStorage> => {
499
+ public getDoc = (): CrdtDocLike<InferStorage<TCrdt>> => {
488
500
  return this._crdtManager.doc;
489
501
  };
490
502
 
@@ -504,7 +516,9 @@ export class PluvRoom<
504
516
  return this._usersManager.getOthers();
505
517
  };
506
518
 
507
- public getStorage = <TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null => {
519
+ public getStorage = <TKey extends keyof InferStorage<TCrdt>>(
520
+ type: TKey,
521
+ ): InferStorage<TCrdt>[TKey] | null => {
508
522
  const sharedType = this._crdtManager.get(type);
509
523
 
510
524
  if (typeof sharedType === "undefined") return null;
@@ -512,11 +526,11 @@ export class PluvRoom<
512
526
  return sharedType;
513
527
  };
514
528
 
515
- public getStorageJson(): InferCrdtJson<TStorage> | null;
516
- public getStorageJson<TKey extends keyof TStorage>(
529
+ public getStorageJson(): InferCrdtJson<InferStorage<TCrdt>> | null;
530
+ public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(
517
531
  type: TKey,
518
- ): InferCrdtJson<TStorage[TKey]> | null;
519
- public getStorageJson<TKey extends keyof TStorage>(type?: TKey) {
532
+ ): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;
533
+ public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {
520
534
  if (this._state.connection.id === null) return null;
521
535
 
522
536
  if (typeof type === "undefined") return this._crdtManager.doc.toJson();
@@ -543,16 +557,16 @@ export class PluvRoom<
543
557
  this._crdtManager.doc.redo();
544
558
  };
545
559
 
546
- public storage = <TKey extends keyof TStorage>(
560
+ public storage = <TKey extends keyof InferStorage<TCrdt>>(
547
561
  key: TKey,
548
- fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
562
+ fn: (value: InferCrdtJson<InferStorage<TCrdt>[TKey]>) => void,
549
563
  ): (() => void) => {
550
564
  return this._crdtNotifier.subscribe(key, fn);
551
565
  };
552
566
 
553
567
  public storageRoot = (
554
568
  fn: (value: {
555
- [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
569
+ [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;
556
570
  }) => void,
557
571
  ): (() => void) => {
558
572
  return this._crdtNotifier.subcribeRoot(fn);
@@ -565,7 +579,7 @@ export class PluvRoom<
565
579
  return this._stateNotifier.subscribe(name, callback);
566
580
  };
567
581
 
568
- public transact = (fn: (storage: TStorage) => void, origin?: string): void => {
582
+ public transact = (fn: (storage: InferStorage<TCrdt>) => void, origin?: string): void => {
569
583
  const _origin = origin ?? this._state.connection.id;
570
584
 
571
585
  /**
@@ -612,22 +626,6 @@ export class PluvRoom<
612
626
  this._flattenStorageStore();
613
627
  }
614
628
 
615
- private async _applyStorageStore(): Promise<void> {
616
- const updates = await this._storageStore.getUpdates();
617
-
618
- this._crdtManager.initialize({
619
- onInitialized: () => {
620
- this._stateNotifier.subjects["storage-loaded"].next(true);
621
-
622
- this._observeCrdt();
623
- },
624
- origin: ORIGIN_INITIALIZED,
625
- update: updates,
626
- });
627
-
628
- this._emitSharedTypes();
629
- }
630
-
631
629
  private _attachWindowListeners(): void {
632
630
  if (typeof window === "undefined") return;
633
631
  if (typeof document === "undefined") return;
@@ -724,7 +722,7 @@ export class PluvRoom<
724
722
 
725
723
  return { ...acc, [prop]: serialized };
726
724
  },
727
- {} as { [P in keyof TStorage]: InferCrdtJson<TStorage[P]> },
725
+ {} as { [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]> },
728
726
  );
729
727
 
730
728
  this._crdtNotifier.rootSubject.next(storageRoot);
@@ -738,7 +736,7 @@ export class PluvRoom<
738
736
 
739
737
  const updates = await this._storageStore.getUpdates();
740
738
 
741
- this._crdtManager.applyUpdate(updates, this);
739
+ this._crdtManager.applyUpdate({ update: updates, origin: this });
742
740
 
743
741
  const encodedState = this._crdtManager.doc.getEncodedState();
744
742
 
@@ -748,9 +746,9 @@ export class PluvRoom<
748
746
  );
749
747
 
750
748
  private _getAddon = (
751
- addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[],
752
- ): PluvRoomAddon<TIO, TMetadata, TPresence, TStorage> => {
753
- return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>>(
749
+ addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>[],
750
+ ): PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt> => {
751
+ return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence, TCrdt>>(
754
752
  (acc, addon) => () => ({
755
753
  ...acc({ room: this }),
756
754
  ...addon({ room: this }),
@@ -941,7 +939,7 @@ export class PluvRoom<
941
939
  this._stateNotifier.subjects.others.next(others);
942
940
  }
943
941
 
944
- private _handleRegisteredMessage(message: IOEventMessage<TIO>): void {
942
+ private async _handleRegisteredMessage(message: IOEventMessage<TIO>): Promise<void> {
945
943
  const { connectionId } = message;
946
944
  const user = message.user as Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
947
945
 
@@ -950,11 +948,6 @@ export class PluvRoom<
950
948
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
951
949
 
952
950
  const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$registered"];
953
- /**
954
- * TODO
955
- * @description Maybe we can use this to resolve storage initialization more efficiently
956
- * @date May 7, 2025
957
- */
958
951
  const state = data.state;
959
952
 
960
953
  this._updateState((oldState) => {
@@ -978,7 +971,10 @@ export class PluvRoom<
978
971
  this._stateNotifier.subjects["my-presence"].next(presence);
979
972
  this._stateNotifier.subjects.myself.next(myself);
980
973
 
981
- const update = this._crdtManager.getInitialState();
974
+ const update = !state
975
+ ? (this._crdtManager.resolveEncodedState(await this._storageStore.getUpdates()) ??
976
+ this._crdtManager.getInitialState())
977
+ : this._crdtManager.getInitialState();
982
978
 
983
979
  this._sendMessage({
984
980
  type: "$initializeSession",
@@ -994,30 +990,45 @@ export class PluvRoom<
994
990
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
995
991
 
996
992
  const data = message.data as BaseIOEventRecord<InferIOAuthorize<TIO>>["$storageReceived"];
993
+ const changeKind = data.changeKind;
997
994
  const state = data.state;
998
995
 
999
- this._crdtManager.initialize({
1000
- onInitialized: async (update) => {
1001
- await this._addToStorageStore(update);
1002
- this._emitSharedTypes();
1003
- this._observeCrdt();
1004
- this._stateNotifier.subjects["storage-loaded"].next(true);
1005
- },
1006
- origin: ORIGIN_INITIALIZED,
1007
- update: state,
1008
- });
996
+ const updates = await this._storageStore.getUpdates();
997
+ const origin = ORIGIN_INITIALIZED;
1009
998
 
1010
- const encodedState = this._crdtManager.doc.getEncodedState();
999
+ const updateStorage = (update: string | readonly string[]) => {
1000
+ return this._crdtManager.initialized
1001
+ ? this._crdtManager.applyUpdate({ update, origin })
1002
+ : this._crdtManager.initialize({ update, origin });
1003
+ };
1011
1004
 
1012
- await this._storageStore.addUpdate(encodedState);
1005
+ if (changeKind === "empty") {
1006
+ this._crdtManager.destroy();
1007
+ this._crdtManager.initialize({ origin, update: updates });
1008
+ } else if (changeKind === "initialized") {
1009
+ if (!!updates.length) updateStorage(updates);
1010
+ else {
1011
+ const encodedState = updateStorage(state).doc.getEncodedState();
1013
1012
 
1014
- this._emitSharedTypes();
1013
+ this._addToStorageStore(encodedState);
1014
+ }
1015
+ } else {
1016
+ const encodedState = updateStorage(state).doc.getEncodedState();
1017
+
1018
+ if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });
1019
+
1020
+ await this._addToStorageStore(encodedState);
1021
+ }
1022
+
1023
+ const encodedState = this._crdtManager.doc.getEncodedState();
1015
1024
 
1016
- const update = this._crdtManager.doc.getEncodedState();
1025
+ this._emitSharedTypes();
1026
+ this._observeCrdt();
1027
+ this._stateNotifier.subjects["storage-loaded"].next(true);
1017
1028
 
1018
1029
  this._sendMessage({
1019
1030
  type: "$updateStorage",
1020
- data: { origin: ORIGIN_INITIALIZED, update },
1031
+ data: { origin, update: encodedState },
1021
1032
  });
1022
1033
  }
1023
1034
 
@@ -1048,7 +1059,7 @@ export class PluvRoom<
1048
1059
  return { ...acc, [prop]: serialized };
1049
1060
  },
1050
1061
  {} as {
1051
- [P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
1062
+ [P in keyof InferStorage<TCrdt>]: InferCrdtJson<InferStorage<TCrdt>[P]>;
1052
1063
  },
1053
1064
  );
1054
1065
 
@@ -1125,7 +1136,8 @@ export class PluvRoom<
1125
1136
  }
1126
1137
 
1127
1138
  private _logDebug(...data: any[]): void {
1128
- if ((process as unknown as any)?.env?.NODE_ENV === "production") return;
1139
+ if (typeof process === "undefined") return;
1140
+ if (process.env?.NODE_ENV === "production") return;
1129
1141
  if (this._debug) console.log(...data);
1130
1142
  }
1131
1143
 
@@ -1135,13 +1147,14 @@ export class PluvRoom<
1135
1147
  const unsubscribe = this._crdtManager.doc.subscribe((event) => {
1136
1148
  const origin = event.origin ?? null;
1137
1149
 
1138
- this._addToStorageStore(event.update);
1139
1150
  this._emitSharedTypes();
1140
1151
 
1141
1152
  if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
1142
1153
  return;
1143
1154
  }
1144
1155
 
1156
+ this._addToStorageStore(event.update);
1157
+
1145
1158
  if (!this._state.webSocket) return;
1146
1159
  if (!this._state.connection.id) return;
1147
1160
  if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
package/src/PluvRouter.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { CrdtType } from "@pluv/crdt";
2
- import type { Id, IOLike, IORouterLike, JsonObject, ProcedureLike } from "@pluv/types";
2
+ import type { IOLike, IORouterLike, JsonObject } from "@pluv/types";
3
3
  import type { PluvProcedure } from "./PluvProcedure";
4
4
 
5
5
  export type PluvRouterEventConfig<
@@ -21,28 +21,6 @@ export type MergedRouter<
21
21
  ? MergedRouter<ITail, TIO, TPresence, TStorage, TRoot & IHead["_defs"]["events"]>
22
22
  : PluvRouter<TIO, TPresence, TStorage, TRoot>;
23
23
 
24
- export type MergeEvents<
25
- TClientEvents extends PluvRouterEventConfig<any, any, any>,
26
- TServerIO extends IOLike<any, any>,
27
- > =
28
- TServerIO extends IOLike<infer IAuthorize, infer IServerEvents>
29
- ? IOLike<
30
- IAuthorize,
31
- {
32
- [P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<
33
- infer IClientInput,
34
- infer IClientOutput
35
- >
36
- ? {
37
- [K in keyof IClientOutput]: K extends keyof IServerEvents
38
- ? IServerEvents[K]
39
- : ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
40
- }[keyof IClientOutput]
41
- : never;
42
- } & IServerEvents
43
- >
44
- : never;
45
-
46
24
  export class PluvRouter<
47
25
  TIO extends IOLike = IOLike,
48
26
  TPresence extends JsonObject = {},
@@ -1,7 +1,6 @@
1
- import type { Id, IOLike, JsonObject } from "@pluv/types";
1
+ import type { Id, IOLike, JsonObject, UserInfo, WebSocketState } from "@pluv/types";
2
2
  import type { Subject } from "wonka";
3
3
  import { makeSubject, subscribe } from "wonka";
4
- import type { UserInfo, WebSocketState } from "./types";
5
4
 
6
5
  export interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
7
6
  connection: Subject<Id<WebSocketState<TIO>>>;
@@ -1,5 +1,5 @@
1
- import type { InputZodLike, IOLike, JsonObject, OptionalProps } from "@pluv/types";
2
- import type { PluvClientLimits, UserInfo } from "./types";
1
+ import type { InputZodLike, IOLike, JsonObject, OptionalProps, UserInfo } from "@pluv/types";
2
+ import type { PluvClientLimits } from "./types";
3
3
  import { pickBy } from "./utils";
4
4
 
5
5
  export type Presence = Record<string, unknown>;
@@ -1,4 +1,4 @@
1
- import type { CrdtType } from "@pluv/crdt";
1
+ import type { AbstractCrdtDocFactory, NoopCrdtDocFactory } from "@pluv/crdt";
2
2
  import type { IOLike, JsonObject } from "@pluv/types";
3
3
  import type { PluvClientOptions } from "./PluvClient";
4
4
  import { PluvClient } from "./PluvClient";
@@ -6,10 +6,10 @@ import { PluvClient } from "./PluvClient";
6
6
  export const createClient = <
7
7
  TIO extends IOLike<any, any>,
8
8
  TPresence extends JsonObject = {},
9
- TStorage extends Record<string, CrdtType<any, any>> = {},
9
+ TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
10
10
  TMetadata extends JsonObject = {},
11
11
  >(
12
- options: PluvClientOptions<TIO, TPresence, TStorage, TMetadata>,
13
- ): PluvClient<TIO, TPresence, TStorage, TMetadata> => {
14
- return new PluvClient<TIO, TPresence, TStorage, TMetadata>(options);
12
+ options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>,
13
+ ): PluvClient<TIO, TPresence, TCrdt, TMetadata> => {
14
+ return new PluvClient<TIO, TPresence, TCrdt, TMetadata>(options);
15
15
  };
package/src/index.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
2
- export type { BaseUser, EventMessage, EventRecord, IOEventMessage } from "@pluv/types";
3
- export { AbstractRoom } from "./AbstractRoom";
2
+ export type {
3
+ BaseUser,
4
+ ConnectionState,
5
+ EventMessage,
6
+ EventNotifierSubscriptionCallback,
7
+ EventRecord,
8
+ IOEventMessage,
9
+ MergeEvents,
10
+ RoomLike,
11
+ UserInfo,
12
+ WebSocketState,
13
+ } from "@pluv/types";
4
14
  export { AbstractStorageStore } from "./AbstractStorageStore";
5
15
  export { createClient } from "./createClient";
6
- export { ConnectionState } from "./enums";
7
16
  export { infer } from "./infer";
8
17
  export type { InferCallback } from "./infer";
9
18
  export { MockedRoom } from "./MockedRoom";
@@ -20,14 +29,7 @@ export type {
20
29
  RoomConnectParams,
21
30
  RoomEndpoints,
22
31
  } from "./PluvRoom";
23
- export type { MergeEvents, PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
32
+ export type { PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
24
33
  export { register } from "./register";
25
34
  export type { RegisterParams } from "./register";
26
- export type {
27
- InferMetadata,
28
- PublicKey,
29
- PublicKeyParams,
30
- UserInfo,
31
- WebSocketConnection,
32
- WebSocketState,
33
- } from "./types";
35
+ export type { InferMetadata, PublicKey, PublicKeyParams, WebSocketConnection } from "./types";