@pluv/client 2.1.0 → 2.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.
package/dist/index.mjs CHANGED
@@ -50,6 +50,7 @@ var AbstractStorageStore = class {
50
50
 
51
51
  // src/constants.ts
52
52
  var MAX_PRESENCE_SIZE_BYTES = 512;
53
+ var PLUV_PRESENCE_META_KEY = "__pluv";
53
54
 
54
55
  // src/PluvProcedure.ts
55
56
  var PluvProcedure = class _PluvProcedure {
@@ -87,7 +88,7 @@ var PluvProcedure = class _PluvProcedure {
87
88
  };
88
89
 
89
90
  // src/PluvRoom.ts
90
- import { ConnectionState } from "@pluv/types";
91
+ import { ConnectionState, StorageState } from "@pluv/types";
91
92
 
92
93
  // src/CrdtManager.ts
93
94
  import { noop } from "@pluv/crdt";
@@ -96,7 +97,7 @@ var CrdtManager = class {
96
97
  this.initialized = false;
97
98
  const { initialStorage = noop.doc({}) } = options;
98
99
  this._docFactory = initialStorage;
99
- this.doc = initialStorage.getEmpty();
100
+ this.doc = this._docFactory.getEmpty();
100
101
  }
101
102
  /**
102
103
  * !HACK
@@ -136,11 +137,7 @@ var CrdtManager = class {
136
137
  if (!updates.length) return this;
137
138
  const initialized = this._docFactory.getInitialized();
138
139
  const reference = initialized.get();
139
- this.doc = this._applyDocUpdates(
140
- this._docFactory.getEmpty(),
141
- updates,
142
- origin
143
- ).rebuildStorage(reference);
140
+ this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);
144
141
  this.initialized = true;
145
142
  initialized.destroy();
146
143
  return this;
@@ -200,6 +197,25 @@ var EventNotifier = class {
200
197
  }
201
198
  };
202
199
 
200
+ // src/ListenerManager.ts
201
+ import { makeSubject as makeSubject3, subscribe as subscribe3 } from "wonka";
202
+ var ListenerManager = class {
203
+ constructor() {
204
+ this.subjects = {
205
+ close: makeSubject3(),
206
+ error: makeSubject3(),
207
+ message: makeSubject3(),
208
+ open: makeSubject3()
209
+ };
210
+ }
211
+ subscribe(kind, callback) {
212
+ var _a;
213
+ const source = (_a = this.subjects[kind]) == null ? void 0 : _a.source;
214
+ if (!source) return () => void 0;
215
+ return subscribe3(callback)(source).unsubscribe;
216
+ }
217
+ };
218
+
203
219
  // src/PluvRouter.ts
204
220
  var PluvRouter = class _PluvRouter {
205
221
  constructor(events) {
@@ -228,22 +244,22 @@ var PluvRouter = class _PluvRouter {
228
244
  };
229
245
 
230
246
  // src/StateNotifier.ts
231
- import { makeSubject as makeSubject3, subscribe as subscribe3 } from "wonka";
247
+ import { makeSubject as makeSubject4, subscribe as subscribe4 } from "wonka";
232
248
  var StateNotifier = class {
233
249
  constructor() {
234
250
  this.subjects = {
235
- connection: makeSubject3(),
236
- "my-presence": makeSubject3(),
237
- myself: makeSubject3(),
238
- others: makeSubject3(),
239
- "storage-loaded": makeSubject3()
251
+ connection: makeSubject4(),
252
+ "my-presence": makeSubject4(),
253
+ myself: makeSubject4(),
254
+ others: makeSubject4(),
255
+ "storage-loaded": makeSubject4()
240
256
  };
241
257
  }
242
258
  subscribe(name, callback) {
243
259
  var _a;
244
260
  const source = (_a = this.subjects[name]) == null ? void 0 : _a.source;
245
261
  if (!source) throw new Error(`Could not subscribe to source: ${name}`);
246
- return subscribe3(callback)(source).unsubscribe;
262
+ return subscribe4(callback)(source).unsubscribe;
247
263
  }
248
264
  };
249
265
 
@@ -416,6 +432,9 @@ var UsersManager = class {
416
432
  );
417
433
  const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
418
434
  const validated = this._presence ? this._presence.parse(presence) : presence;
435
+ if (!!presence[PLUV_PRESENCE_META_KEY]) {
436
+ validated[PLUV_PRESENCE_META_KEY] = presence[PLUV_PRESENCE_META_KEY];
437
+ }
419
438
  this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
420
439
  return validated;
421
440
  }
@@ -494,10 +513,10 @@ var UsersManager = class {
494
513
  };
495
514
 
496
515
  // src/UsersNotifier.ts
497
- import { makeSubject as makeSubject4, subscribe as subscribe4 } from "wonka";
516
+ import { makeSubject as makeSubject5, subscribe as subscribe5 } from "wonka";
498
517
  var UsersNotifier = class {
499
518
  constructor() {
500
- this.others = makeSubject4();
519
+ this.others = makeSubject5();
501
520
  this._otherSubjects = /* @__PURE__ */ new Map();
502
521
  }
503
522
  clear() {
@@ -513,17 +532,17 @@ var UsersNotifier = class {
513
532
  other(clientId) {
514
533
  const subject = this._otherSubjects.get(clientId);
515
534
  if (subject) return subject;
516
- const newSubject = makeSubject4();
535
+ const newSubject = makeSubject5();
517
536
  this._otherSubjects.set(clientId, newSubject);
518
537
  return newSubject;
519
538
  }
520
539
  subscribeOther(clientId, callback) {
521
540
  const source = this.other(clientId).source;
522
- const subscription = subscribe4(callback)(source);
541
+ const subscription = subscribe5(callback)(source);
523
542
  return subscription.unsubscribe;
524
543
  }
525
544
  subscribeOthers(callback) {
526
- const subscription = subscribe4(
545
+ const subscription = subscribe5(
527
546
  ({ others, event }) => callback(others, event)
528
547
  )(this.others.source);
529
548
  return subscription.unsubscribe;
@@ -549,6 +568,7 @@ var PluvRoom = class {
549
568
  this._intervals = {
550
569
  heartbeat: null
551
570
  };
571
+ this._listenerManager = new ListenerManager();
552
572
  this._publicKey = null;
553
573
  this._stateNotifier = new StateNotifier();
554
574
  this._subscriptions = {
@@ -567,10 +587,12 @@ var PluvRoom = class {
567
587
  },
568
588
  connection: {
569
589
  attempts: 0,
570
- count: 0,
571
590
  id: null,
572
591
  state: ConnectionState.Untouched
573
592
  },
593
+ storage: {
594
+ state: StorageState.Unavailable
595
+ },
574
596
  webSocket: null
575
597
  };
576
598
  this._windowListeners = null;
@@ -647,6 +669,9 @@ var PluvRoom = class {
647
669
  if (typeof sharedType === "undefined") return null;
648
670
  return sharedType;
649
671
  };
672
+ this.getStorageLoaded = () => {
673
+ return this._state.storage.state === StorageState.Offline || this._state.storage.state === StorageState.Synced;
674
+ };
650
675
  /**
651
676
  * @deprecated To be removed in v3. Use PluvRoom.subscribe.other instead
652
677
  */
@@ -659,7 +684,7 @@ var PluvRoom = class {
659
684
  this._crdtManager.doc.redo();
660
685
  };
661
686
  /**
662
- * @deprecated To be removed in v3. To be removed in v3. Use PluvRoom.subscribe.storage instead
687
+ * @deprecated To be removed in v3. Use PluvRoom.subscribe.storage instead
663
688
  */
664
689
  this.storage = new Proxy(
665
690
  (key, callback) => this._crdtNotifier.subscribe(key, callback),
@@ -797,12 +822,18 @@ var PluvRoom = class {
797
822
  });
798
823
  this._crdtManager = new CrdtManager({ initialStorage });
799
824
  }
825
+ /**
826
+ * @deprecated To be removed in v3. Use `getStorageLoaded` instead.
827
+ */
800
828
  get storageLoaded() {
801
- return this._crdtManager.initialized;
829
+ return this.getStorageLoaded();
802
830
  }
803
831
  get webSocket() {
804
832
  return this._state.webSocket;
805
833
  }
834
+ addEventListener(kind, handler) {
835
+ return this._listenerManager.subscribe(kind, handler);
836
+ }
806
837
  connect(...args) {
807
838
  return __async(this, null, function* () {
808
839
  var _a;
@@ -844,6 +875,7 @@ var PluvRoom = class {
844
875
  this._updateState((oldState) => {
845
876
  oldState.connection.attempts = 0;
846
877
  oldState.authorization.token = authToken;
878
+ oldState.storage.state = StorageState.Loading;
847
879
  oldState.webSocket = webSocket;
848
880
  return oldState;
849
881
  });
@@ -864,6 +896,7 @@ var PluvRoom = class {
864
896
  this._updateState((oldState) => {
865
897
  oldState.authorization.token = null;
866
898
  oldState.connection.state = ConnectionState.Closed;
899
+ oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
867
900
  oldState.webSocket = null;
868
901
  return oldState;
869
902
  });
@@ -1110,7 +1143,6 @@ var PluvRoom = class {
1110
1143
  const data = message.data;
1111
1144
  const state = data.state;
1112
1145
  this._updateState((oldState) => {
1113
- oldState.connection.count += 1;
1114
1146
  oldState.connection.id = connectionId;
1115
1147
  oldState.authorization.user = user != null ? user : null;
1116
1148
  return oldState;
@@ -1162,6 +1194,10 @@ var PluvRoom = class {
1162
1194
  const encodedState = this._crdtManager.doc.getEncodedState();
1163
1195
  this._emitSharedTypes();
1164
1196
  this._observeCrdt();
1197
+ this._updateState((oldState) => {
1198
+ oldState.storage.state = StorageState.Synced;
1199
+ return oldState;
1200
+ });
1165
1201
  this._stateNotifier.subjects["storage-loaded"].next(true);
1166
1202
  this._sendMessage({
1167
1203
  type: "$updateStorage",
@@ -1274,11 +1310,13 @@ var PluvRoom = class {
1274
1310
  this._listeners.onAuthorizationFail(authError);
1275
1311
  this._updateState((oldState) => {
1276
1312
  oldState.connection.state = ConnectionState.Unavailable;
1313
+ oldState.storage.state = StorageState.Unavailable;
1277
1314
  return oldState;
1278
1315
  });
1279
1316
  }
1280
1317
  _onClose(event) {
1281
1318
  return __async(this, null, function* () {
1319
+ this._listenerManager.subjects.close.next(event);
1282
1320
  this._logDebug("WebSocket closed");
1283
1321
  if (!!event.reason) this._logDebug(event.reason);
1284
1322
  const shouldRetry = [
@@ -1306,6 +1344,7 @@ var PluvRoom = class {
1306
1344
  */
1307
1345
  ConnectionState.Closed
1308
1346
  );
1347
+ oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
1309
1348
  oldState.webSocket = null;
1310
1349
  return oldState;
1311
1350
  });
@@ -1322,9 +1361,11 @@ var PluvRoom = class {
1322
1361
  * @description Handle websocket errors in a meaningful way, should they occur
1323
1362
  * @date August 19, 2022
1324
1363
  */
1325
- _onError() {
1364
+ _onError(event) {
1365
+ this._listenerManager.subjects.error.next(event);
1326
1366
  }
1327
1367
  _onMessage(event) {
1368
+ this._listenerManager.subjects.message.next(event);
1328
1369
  const message = this._parseMessage(event);
1329
1370
  if (!message) return;
1330
1371
  const shouldLog = typeof this._debug === "boolean" ? this._debug : this._debug.output.find((value) => value === message.type);
@@ -1372,7 +1413,8 @@ var PluvRoom = class {
1372
1413
  default:
1373
1414
  }
1374
1415
  }
1375
- _onOpen() {
1416
+ _onOpen(event) {
1417
+ this._listenerManager.subjects.open.next(event);
1376
1418
  if (this._state.connection.state !== ConnectionState.Connecting) return;
1377
1419
  this._logDebug("WebSocket connected");
1378
1420
  this._updateState((oldState) => {
@@ -1390,6 +1432,7 @@ var PluvRoom = class {
1390
1432
  if (this._state.connection.state === ConnectionState.Closed) return;
1391
1433
  this._updateState((oldState) => {
1392
1434
  oldState.connection.state = ConnectionState.Unavailable;
1435
+ oldState.storage.state = oldState.storage.state === StorageState.Synced ? StorageState.Offline : StorageState.Unavailable;
1393
1436
  return oldState;
1394
1437
  });
1395
1438
  });
@@ -1489,6 +1532,7 @@ var PluvRoom = class {
1489
1532
  const oldState = __spreadProps(__spreadValues({}, this._state), {
1490
1533
  authorization,
1491
1534
  connection: JSON.parse(JSON.stringify(this._state.connection)),
1535
+ storage: JSON.parse(JSON.stringify(this._state.storage)),
1492
1536
  webSocket: this._state.webSocket
1493
1537
  });
1494
1538
  const newState = updater(oldState);
@@ -1602,9 +1646,13 @@ var createClient = (options) => {
1602
1646
  var infer = (callback) => callback;
1603
1647
 
1604
1648
  // src/MockedRoom.ts
1605
- import { ConnectionState as ConnectionState2 } from "@pluv/types";
1649
+ import { ConnectionState as ConnectionState2, StorageState as StorageState2 } from "@pluv/types";
1606
1650
  var MockedRoom = class {
1607
1651
  constructor(room, options) {
1652
+ /**
1653
+ * @deprecated To be removed in v3. Use `getStorageLoaded` instead.
1654
+ */
1655
+ this.storageLoaded = true;
1608
1656
  this._crdtNotifier = new CrdtNotifier();
1609
1657
  this._eventNotifier = new EventNotifier();
1610
1658
  this._usersNotifier = new UsersNotifier();
@@ -1615,10 +1663,12 @@ var MockedRoom = class {
1615
1663
  },
1616
1664
  connection: {
1617
1665
  attempts: 0,
1618
- count: 1,
1619
1666
  id: null,
1620
1667
  state: ConnectionState2.Untouched
1621
1668
  },
1669
+ storage: {
1670
+ state: StorageState2.Unavailable
1671
+ },
1622
1672
  webSocket: null
1623
1673
  };
1624
1674
  this._stateNotifier = new StateNotifier();
@@ -1793,8 +1843,8 @@ var MockedRoom = class {
1793
1843
  });
1794
1844
  this._observeCrdt();
1795
1845
  }
1796
- get storageLoaded() {
1797
- return true;
1846
+ addEventListener(kind, handler) {
1847
+ return () => void 0;
1798
1848
  }
1799
1849
  getDoc() {
1800
1850
  return this._crdtManager.doc;
@@ -1804,6 +1854,9 @@ var MockedRoom = class {
1804
1854
  if (typeof type === "undefined") return this._crdtManager.doc.toJson();
1805
1855
  return this._crdtManager.doc.toJson(type);
1806
1856
  }
1857
+ getStorageLoaded() {
1858
+ return true;
1859
+ }
1807
1860
  _observeCrdt() {
1808
1861
  var _a, _b;
1809
1862
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "2.1.0",
3
+ "version": "2.2.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": "^2.1.0",
22
- "@pluv/types": "^2.1.0"
21
+ "@pluv/crdt": "^2.2.0",
22
+ "@pluv/types": "^2.2.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "^9.26.0",
26
26
  "tsup": "^8.4.0",
27
27
  "typescript": "^5.8.3",
28
- "@pluv/tsconfig": "^2.1.0",
29
- "eslint-config-pluv": "^2.1.0"
28
+ "@pluv/tsconfig": "^2.2.0",
29
+ "eslint-config-pluv": "^2.2.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,7 +1,7 @@
1
- import type { AbstractCrdtDocFactory, CrdtDocLike, CrdtType, InferStorage } from "@pluv/crdt";
1
+ import type { AbstractCrdtDocFactory, InferDocLike, InferStorage } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
3
 
4
- export type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any>> = {
4
+ export type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any, any>> = {
5
5
  initialStorage?: TCrdt;
6
6
  };
7
7
 
@@ -15,8 +15,8 @@ interface ApplyUpdateParams {
15
15
  update: string | readonly string[];
16
16
  }
17
17
 
18
- export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
19
- public doc: CrdtDocLike<InferStorage<TCrdt>>;
18
+ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any, any>> {
19
+ public doc: InferDocLike<TCrdt>;
20
20
  public initialized: boolean = false;
21
21
 
22
22
  private readonly _docFactory: TCrdt;
@@ -25,7 +25,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
25
25
  const { initialStorage = noop.doc({}) as any } = options;
26
26
 
27
27
  this._docFactory = initialStorage;
28
- this.doc = initialStorage.getEmpty();
28
+ this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;
29
29
  }
30
30
 
31
31
  /**
@@ -49,7 +49,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
49
49
  public destroy(): void {
50
50
  this.initialized = false;
51
51
  this.doc.destroy();
52
- this.doc = this._docFactory.getEmpty();
52
+ this.doc = this._docFactory.getEmpty() as InferDocLike<TCrdt>;
53
53
  }
54
54
 
55
55
  public get<TKey extends keyof InferStorage<TCrdt>>(key: TKey): InferStorage<TCrdt>[TKey] {
@@ -94,11 +94,7 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
94
94
  const initialized = this._docFactory.getInitialized();
95
95
  const reference = initialized.get();
96
96
 
97
- this.doc = this._applyDocUpdates(
98
- this._docFactory.getEmpty(),
99
- updates,
100
- origin,
101
- ).rebuildStorage(reference);
97
+ this.doc = this._applyDocUpdates(this.doc, updates, origin).rebuildStorage(reference);
102
98
  this.initialized = true;
103
99
  initialized.destroy();
104
100
 
@@ -110,10 +106,10 @@ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
110
106
  }
111
107
 
112
108
  private _applyDocUpdates(
113
- doc: CrdtDocLike<InferStorage<TCrdt>>,
109
+ doc: InferDocLike<TCrdt>,
114
110
  updates: readonly string[],
115
111
  origin?: string,
116
- ): CrdtDocLike<InferStorage<TCrdt>> {
112
+ ): InferDocLike<TCrdt> {
117
113
  doc.transact(() => {
118
114
  doc.batchApplyEncodedState({ origin, updates });
119
115
  }, origin);
@@ -0,0 +1,21 @@
1
+ import { makeSubject, subscribe, TypeOfSource } from "wonka";
2
+
3
+ export class ListenerManager {
4
+ public subjects = {
5
+ close: makeSubject<CloseEvent>(),
6
+ error: makeSubject<Event>(),
7
+ message: makeSubject<MessageEvent<any>>(),
8
+ open: makeSubject<Event>(),
9
+ };
10
+
11
+ public subscribe<TKind extends keyof typeof this.subjects>(
12
+ kind: TKind,
13
+ callback: (event: TypeOfSource<(typeof this.subjects)[TKind]["source"]>) => void,
14
+ ): () => void {
15
+ const source = this.subjects[kind]?.source;
16
+
17
+ if (!source) return () => undefined;
18
+
19
+ return subscribe(callback)(source as any).unsubscribe;
20
+ }
21
+ }
package/src/MockedRoom.ts CHANGED
@@ -1,4 +1,10 @@
1
- import type { AbstractCrdtDocFactory, InferCrdtJson, InferStorage } from "@pluv/crdt";
1
+ import type {
2
+ AbstractCrdtDocFactory,
3
+ InferCrdtJson,
4
+ InferDoc,
5
+ InferDocLike,
6
+ InferStorage,
7
+ } from "@pluv/crdt";
2
8
  import type {
3
9
  BroadcastProxy,
4
10
  CrdtDocLike,
@@ -8,10 +14,10 @@ import type {
8
14
  Id,
9
15
  InferIOInput,
10
16
  InferIOOutput,
11
- JsonObject,
12
17
  MergeEvents,
13
18
  OtherSubscriptionCallback,
14
19
  OthersSubscriptionCallback,
20
+ RoomEventListenerMap,
15
21
  RoomLike,
16
22
  StateNotifierSubjects,
17
23
  StorageProxy,
@@ -21,9 +27,10 @@ import type {
21
27
  SubscriptionCallback,
22
28
  UpdateMyPresenceAction,
23
29
  UserInfo,
30
+ WebSocketConnection,
24
31
  WebSocketState,
25
32
  } from "@pluv/types";
26
- import { ConnectionState } from "@pluv/types";
33
+ import { ConnectionState, StorageState } from "@pluv/types";
27
34
  import type { CrdtManagerOptions } from "./CrdtManager";
28
35
  import { CrdtManager } from "./CrdtManager";
29
36
  import { CrdtNotifier } from "./CrdtNotifier";
@@ -41,7 +48,6 @@ import type {
41
48
  EventResolverContext,
42
49
  InternalSubscriptions,
43
50
  PluvClientLimits,
44
- WebSocketConnection,
45
51
  } from "./types";
46
52
 
47
53
  export type MockedRoomEvents<TIO extends IOLike> = Partial<{
@@ -50,8 +56,8 @@ export type MockedRoomEvents<TIO extends IOLike> = Partial<{
50
56
 
51
57
  export type MockedRoomConfig<
52
58
  TIO extends IOLike,
53
- TPresence extends JsonObject,
54
- TCrdt extends AbstractCrdtDocFactory<any>,
59
+ TPresence extends Record<string, any>,
60
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
55
61
  TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
56
62
  > = {
57
63
  events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
@@ -62,13 +68,18 @@ export type MockedRoomConfig<
62
68
 
63
69
  export class MockedRoom<
64
70
  TIO extends IOLike,
65
- TPresence extends JsonObject,
66
- TCrdt extends AbstractCrdtDocFactory<any>,
71
+ TPresence extends Record<string, any>,
72
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
67
73
  TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>>,
68
- > implements RoomLike<TIO, TPresence, InferStorage<TCrdt>>
74
+ > implements RoomLike<TIO, InferDoc<TCrdt>, TPresence, InferStorage<TCrdt>>
69
75
  {
70
76
  public readonly id: string;
71
77
 
78
+ /**
79
+ * @deprecated To be removed in v3. Use `getStorageLoaded` instead.
80
+ */
81
+ public readonly storageLoaded: boolean = true;
82
+
72
83
  private readonly _crdtManager: CrdtManager<TCrdt>;
73
84
  private readonly _crdtNotifier = new CrdtNotifier<InferStorage<TCrdt>>();
74
85
  private readonly _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
@@ -83,10 +94,12 @@ export class MockedRoom<
83
94
  },
84
95
  connection: {
85
96
  attempts: 0,
86
- count: 1,
87
97
  id: null,
88
98
  state: ConnectionState.Untouched,
89
99
  },
100
+ storage: {
101
+ state: StorageState.Unavailable,
102
+ },
90
103
  webSocket: null,
91
104
  };
92
105
  private readonly _stateNotifier = new StateNotifier<TIO, TPresence>();
@@ -121,8 +134,11 @@ export class MockedRoom<
121
134
  this._observeCrdt();
122
135
  }
123
136
 
124
- public get storageLoaded(): boolean {
125
- return true;
137
+ public addEventListener<TKind extends keyof RoomEventListenerMap>(
138
+ kind: TKind,
139
+ handler: RoomEventListenerMap[TKind],
140
+ ): () => void {
141
+ return () => undefined;
126
142
  }
127
143
 
128
144
  public broadcast = new Proxy(
@@ -140,7 +156,8 @@ export class MockedRoom<
140
156
  any,
141
157
  any,
142
158
  TPresence,
143
- InferStorage<TCrdt>
159
+ TCrdt,
160
+ ""
144
161
  > | null;
145
162
 
146
163
  if (!procedure?.config.broadcast) {
@@ -154,7 +171,7 @@ export class MockedRoom<
154
171
  if (!myself) return;
155
172
 
156
173
  const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
157
- const context: EventResolverContext<TIO, TPresence, InferStorage<TCrdt>> = {
174
+ const context: EventResolverContext<TIO, TPresence, InferDocLike<TCrdt>> = {
158
175
  doc: this._crdtManager.doc,
159
176
  others: this._usersManager.getOthers(),
160
177
  room: this.id,
@@ -213,7 +230,7 @@ export class MockedRoom<
213
230
  return Object.freeze(JSON.parse(JSON.stringify(this._state.connection)));
214
231
  };
215
232
 
216
- public getDoc(): CrdtDocLike<InferStorage<TCrdt>> {
233
+ public getDoc(): CrdtDocLike<InferDoc<TCrdt>, InferStorage<TCrdt>> {
217
234
  return this._crdtManager.doc;
218
235
  }
219
236
 
@@ -249,12 +266,15 @@ export class MockedRoom<
249
266
  ): InferCrdtJson<InferStorage<TCrdt>[TKey]> | null;
250
267
  public getStorageJson<TKey extends keyof InferStorage<TCrdt>>(type?: TKey) {
251
268
  if (this._state.connection.id === null) return null;
252
-
253
269
  if (typeof type === "undefined") return this._crdtManager.doc.toJson();
254
270
 
255
271
  return this._crdtManager.doc.toJson(type);
256
272
  }
257
273
 
274
+ public getStorageLoaded(): boolean {
275
+ return true;
276
+ }
277
+
258
278
  public other = (
259
279
  connectionId: string,
260
280
  callback: OtherSubscriptionCallback<TIO, TPresence>,
package/src/PluvClient.ts CHANGED
@@ -24,8 +24,8 @@ import type { PluvClientLimits, PublicKey, WithMetadata } from "./types";
24
24
 
25
25
  export type PluvClientOptions<
26
26
  TIO extends IOLike<any, any>,
27
- TPresence extends JsonObject,
28
- TCrdt extends AbstractCrdtDocFactory<any>,
27
+ TPresence extends Record<string, any>,
28
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
29
29
  TMetadata extends JsonObject,
30
30
  > = RoomEndpoints<TIO, TMetadata> & {
31
31
  debug?: boolean;
@@ -43,8 +43,8 @@ export type PluvClientOptions<
43
43
 
44
44
  export type CreateRoomOptions<
45
45
  TIO extends IOLike<any, any>,
46
- TPresence extends JsonObject,
47
- TCrdt extends AbstractCrdtDocFactory<any>,
46
+ TPresence extends Record<string, any>,
47
+ TCrdt extends AbstractCrdtDocFactory<any, any>,
48
48
  TMetadata extends JsonObject,
49
49
  TEvents extends PluvRouterEventConfig<TIO, TPresence, InferStorage<TCrdt>> = {},
50
50
  > = {
@@ -63,8 +63,8 @@ export type EnterRoomParams<TMetadata extends JsonObject = {}> = keyof TMetadata
63
63
 
64
64
  export class PluvClient<
65
65
  TIO extends IOLike<any, any>,
66
- TPresence extends JsonObject = {},
67
- TCrdt extends AbstractCrdtDocFactory<any> = NoopCrdtDocFactory,
66
+ TPresence extends Record<string, any> = {},
67
+ TCrdt extends AbstractCrdtDocFactory<any, any> = NoopCrdtDocFactory,
68
68
  TMetadata extends JsonObject = {},
69
69
  > {
70
70
  public readonly metadata?: InputZodLike<TMetadata>;
@@ -84,8 +84,8 @@ export class PluvClient<
84
84
  };
85
85
  }
86
86
 
87
- public get procedure(): PluvProcedure<TIO, {}, {}, TPresence, InferStorage<TCrdt>> {
88
- return new PluvProcedure<TIO, {}, {}, TPresence, InferStorage<TCrdt>>();
87
+ public get procedure(): PluvProcedure<TIO, {}, {}, TPresence, TCrdt, ""> {
88
+ return new PluvProcedure<TIO, {}, {}, TPresence, TCrdt, "">();
89
89
  }
90
90
 
91
91
  constructor(options: PluvClientOptions<TIO, TPresence, TCrdt, TMetadata>) {