@pluv/client 1.0.0 → 1.0.1

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@1.0.0 build /home/runner/work/pluv/pluv/packages/client
2
+ > @pluv/client@1.0.1 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
- CJS dist/index.js 58.23 KB
12
- CJS ⚡️ Build success in 110ms
13
- ESM dist/index.mjs 56.91 KB
14
- ESM ⚡️ Build success in 114ms
11
+ CJS dist/index.js 58.07 KB
12
+ CJS ⚡️ Build success in 125ms
13
+ ESM dist/index.mjs 56.75 KB
14
+ ESM ⚡️ Build success in 127ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 3353ms
17
- DTS dist/index.d.mts 25.38 KB
18
- DTS dist/index.d.ts 25.38 KB
16
+ DTS ⚡️ Build success in 3647ms
17
+ DTS dist/index.d.mts 25.34 KB
18
+ DTS dist/index.d.ts 25.34 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @pluv/client
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - c583be9: Fixed storage race condition that occurred when 2 users joined a room near simultaneously that would cause users to have mismatched storage states from the server (and thereby preventing updates from properly registering) until the user reconnected.
8
+ - @pluv/crdt@1.0.1
9
+ - @pluv/types@1.0.1
10
+
3
11
  ## 1.0.0
4
12
 
5
13
  ### Major Changes
package/dist/index.d.mts CHANGED
@@ -48,7 +48,6 @@ declare abstract class AbstractStorageStore {
48
48
  }
49
49
 
50
50
  type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
51
- encodedState?: string | Uint8Array | null;
52
51
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
53
52
  };
54
53
 
package/dist/index.d.ts CHANGED
@@ -48,7 +48,6 @@ declare abstract class AbstractStorageStore {
48
48
  }
49
49
 
50
50
  type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
51
- encodedState?: string | Uint8Array | null;
52
51
  initialStorage?: AbstractCrdtDocFactory<TStorage>;
53
52
  };
54
53
 
package/dist/index.js CHANGED
@@ -126,9 +126,9 @@ var import_crdt = require("@pluv/crdt");
126
126
  var CrdtManager = class {
127
127
  constructor(options) {
128
128
  this.initialized = false;
129
- const { encodedState, initialStorage = import_crdt.noop.doc({}) } = options;
129
+ const { initialStorage = import_crdt.noop.doc({}) } = options;
130
130
  this._docFactory = initialStorage;
131
- this.doc = encodedState ? initialStorage.getFresh().applyEncodedState({ update: encodedState }) : initialStorage.getFresh();
131
+ this.doc = initialStorage.getEmpty();
132
132
  }
133
133
  /**
134
134
  * !HACK
@@ -148,25 +148,30 @@ var CrdtManager = class {
148
148
  get(key) {
149
149
  return this.doc.get(key);
150
150
  }
151
+ getInitialState() {
152
+ return this._docFactory.getInitialized().getEncodedState();
153
+ }
151
154
  initialize(params) {
152
155
  const { onInitialized, origin, update } = params;
153
- const updates = typeof update === "string" ? [update] : update;
154
- if (!updates.length) return this;
155
156
  if (this.initialized) {
156
- this.doc = this._applyDocUpdates(this.doc, updates, origin).track();
157
+ console.warn("Attempted to initialize storage multiple times");
157
158
  return this;
158
159
  }
159
- const onEmpty = this._applyDocUpdates(this._docFactory.getEmpty(), updates, origin);
160
- if (!onEmpty.isEmpty()) {
161
- onEmpty.destroy();
162
- this.doc = this._applyDocUpdates(this._docFactory.getFresh(), updates, origin).track();
163
- this.initialized = true;
164
- onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
160
+ if (!this.doc.isEmpty()) {
161
+ console.warn("Attempted to initialize over a non-empty storage");
165
162
  return this;
166
163
  }
167
- onEmpty.destroy();
168
- this.doc = this._docFactory.getInitialized().track();
164
+ const updates = typeof update === "string" ? [update] : update;
165
+ if (!updates.length) return this;
166
+ const initialized = this._docFactory.getInitialized();
167
+ const reference = initialized.get();
168
+ this.doc = this._applyDocUpdates(
169
+ this._docFactory.getEmpty(),
170
+ updates,
171
+ origin
172
+ ).rebuildStorage(reference);
169
173
  this.initialized = true;
174
+ initialized.destroy();
170
175
  onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
171
176
  return this;
172
177
  }
@@ -264,7 +269,10 @@ var PluvRouter = class _PluvRouter {
264
269
  this._defs = { events };
265
270
  }
266
271
  static merge(...routers) {
267
- const events = Object.assign(/* @__PURE__ */ Object.create(null), ...routers.map((router) => router._defs.events));
272
+ const events = Object.assign(
273
+ /* @__PURE__ */ Object.create(null),
274
+ ...routers.map((router) => router._defs.events)
275
+ );
268
276
  if (Object.keys(events).some((key) => key.startsWith("$"))) {
269
277
  throw new Error('Procedures may not start with "$"');
270
278
  }
@@ -417,7 +425,11 @@ var UsersManager = class {
417
425
  const userInfo = (_a = this._others.get(clientId)) != null ? _a : null;
418
426
  if (!userInfo) return null;
419
427
  const remaining = (_c = (_b = this._getConnectionIds(clientId)) == null ? void 0 : _b.size) != null ? _c : 0;
420
- const result = { clientId, remaining, data: userInfo };
428
+ const result = {
429
+ clientId,
430
+ remaining,
431
+ data: userInfo
432
+ };
421
433
  if (!remaining) this._others.delete(clientId);
422
434
  return result;
423
435
  }
@@ -454,7 +466,10 @@ var UsersManager = class {
454
466
  const other = this._others.get(clientId);
455
467
  if (!other) return null;
456
468
  const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
457
- const cleanedPresence = pickBy((_a = other.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
469
+ const cleanedPresence = pickBy(
470
+ (_a = other.presence) != null ? _a : {},
471
+ (value) => typeof value !== "undefined"
472
+ );
458
473
  const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
459
474
  const validated = this._presence ? this._presence.parse(presence) : presence;
460
475
  this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
@@ -623,10 +638,7 @@ var PluvRoom = class extends AbstractRoom {
623
638
  room: this.id,
624
639
  user: myself
625
640
  };
626
- const output = yield procedure.config.broadcast(
627
- parsed,
628
- context
629
- );
641
+ const output = yield procedure.config.broadcast(parsed, context);
630
642
  Object.entries(output).forEach(([_type, _data]) => {
631
643
  this._sendMessage({ data: _data, type: _type });
632
644
  });
@@ -749,7 +761,11 @@ var PluvRoom = class extends AbstractRoom {
749
761
  }
750
762
  };
751
763
  this._router = router != null ? router : new PluvRouter({});
752
- this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
764
+ this._usersManager = new UsersManager({
765
+ initialPresence,
766
+ limits: this._limits,
767
+ presence
768
+ });
753
769
  this._crdtManager = new CrdtManager({ initialStorage });
754
770
  }
755
771
  get storageLoaded() {
@@ -764,17 +780,17 @@ var PluvRoom = class extends AbstractRoom {
764
780
  const params = (_a = args[0]) != null ? _a : {};
765
781
  const metadata = params.metadata;
766
782
  this._setMetadata(metadata);
767
- const canConnect = [ConnectionState.Closed, ConnectionState.Unavailable, ConnectionState.Untouched].some(
768
- (state) => this._state.connection.state === state
769
- );
783
+ const canConnect = [
784
+ ConnectionState.Closed,
785
+ ConnectionState.Unavailable,
786
+ ConnectionState.Untouched
787
+ ].some((state) => this._state.connection.state === state);
770
788
  if (!canConnect) return;
771
789
  this._clearTimeout(this._timeouts.reconnect);
772
790
  this._updateState((oldState) => {
773
791
  oldState.connection.state = ConnectionState.Connecting;
774
792
  return oldState;
775
793
  });
776
- yield this._storageStore.initialize();
777
- yield this._applyStorageStore();
778
794
  const wsEndpoint = this._getWsEndpoint(this.id, params);
779
795
  const url = new URL(wsEndpoint);
780
796
  let authToken = null;
@@ -1043,12 +1059,13 @@ var PluvRoom = class extends AbstractRoom {
1043
1059
  this._stateNotifier.subjects.others.next(others);
1044
1060
  }
1045
1061
  _handleRegisteredMessage(message) {
1046
- var _a, _b, _c;
1062
+ var _a, _b;
1047
1063
  const { connectionId } = message;
1048
1064
  const user = message.user;
1049
1065
  if (!connectionId) return;
1050
1066
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1051
1067
  const data = message.data;
1068
+ const state = data.state;
1052
1069
  this._updateState((oldState) => {
1053
1070
  oldState.connection.count += 1;
1054
1071
  oldState.connection.id = connectionId;
@@ -1065,7 +1082,7 @@ var PluvRoom = class extends AbstractRoom {
1065
1082
  const myself = (_b = this._usersManager.myself) != null ? _b : null;
1066
1083
  this._stateNotifier.subjects["my-presence"].next(presence);
1067
1084
  this._stateNotifier.subjects.myself.next(myself);
1068
- const update = this._state.connection.count > 1 ? (_c = this._crdtManager.doc.getEncodedState()) != null ? _c : null : null;
1085
+ const update = this._crdtManager.getInitialState();
1069
1086
  this._sendMessage({
1070
1087
  type: "$initializeSession",
1071
1088
  data: { presence, update }
@@ -1077,15 +1094,16 @@ var PluvRoom = class extends AbstractRoom {
1077
1094
  if (!connectionId) return;
1078
1095
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1079
1096
  const data = message.data;
1097
+ const state = data.state;
1080
1098
  this._crdtManager.initialize({
1081
1099
  onInitialized: (update2) => __async(this, null, function* () {
1082
- this._addToStorageStore(update2);
1100
+ yield this._addToStorageStore(update2);
1083
1101
  this._emitSharedTypes();
1084
1102
  this._observeCrdt();
1085
1103
  this._stateNotifier.subjects["storage-loaded"].next(true);
1086
1104
  }),
1087
1105
  origin: ORIGIN_INITIALIZED,
1088
- update: data.state
1106
+ update: state
1089
1107
  });
1090
1108
  const encodedState = this._crdtManager.doc.getEncodedState();
1091
1109
  yield this._storageStore.addUpdate(encodedState);
@@ -1102,7 +1120,6 @@ var PluvRoom = class extends AbstractRoom {
1102
1120
  if (!connectionId) return;
1103
1121
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1104
1122
  const data = message.data;
1105
- if (!this._crdtManager) return;
1106
1123
  this._crdtManager.doc.applyEncodedState({
1107
1124
  origin: ORIGIN_STORAGE_UPDATED,
1108
1125
  update: data.state
@@ -1110,7 +1127,6 @@ var PluvRoom = class extends AbstractRoom {
1110
1127
  const sharedTypes = this._crdtManager.doc.get();
1111
1128
  const storageRoot = Object.keys(sharedTypes).reduce(
1112
1129
  (acc, prop) => {
1113
- if (!this._crdtManager) return acc;
1114
1130
  const serialized = this._crdtManager.doc.toJson(prop);
1115
1131
  this._crdtNotifier.subject(prop).next(serialized);
1116
1132
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
@@ -1157,7 +1173,10 @@ var PluvRoom = class extends AbstractRoom {
1157
1173
  }
1158
1174
  _heartbeat() {
1159
1175
  this._clearTimeout(this._timeouts.pong);
1160
- this._timeouts.pong = setTimeout(this._reconnect.bind(this), PONG_TIMEOUT_MS);
1176
+ this._timeouts.pong = setTimeout(
1177
+ this._reconnect.bind(this),
1178
+ PONG_TIMEOUT_MS
1179
+ );
1161
1180
  this._sendMessage('{"type":"$ping","data":{}}');
1162
1181
  }
1163
1182
  _logDebug(...data) {
@@ -1168,7 +1187,6 @@ var PluvRoom = class extends AbstractRoom {
1168
1187
  _observeCrdt() {
1169
1188
  var _a, _b;
1170
1189
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
1171
- if (!this._crdtManager) return;
1172
1190
  const unsubscribe = this._crdtManager.doc.subscribe((event) => {
1173
1191
  var _a2;
1174
1192
  const origin = (_a2 = event.origin) != null ? _a2 : null;
@@ -1298,7 +1316,10 @@ var PluvRoom = class extends AbstractRoom {
1298
1316
  return oldState;
1299
1317
  });
1300
1318
  this._clearInterval(this._intervals.heartbeat);
1301
- this._intervals.heartbeat = setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
1319
+ this._intervals.heartbeat = setInterval(
1320
+ this._heartbeat.bind(this),
1321
+ HEARTBEAT_INTERVAL_MS
1322
+ );
1302
1323
  }
1303
1324
  _onNavigatorOffline() {
1304
1325
  return __async(this, null, function* () {
@@ -1353,8 +1374,14 @@ var PluvRoom = class extends AbstractRoom {
1353
1374
  _pollReconnect() {
1354
1375
  this._clearTimeout(this._timeouts.reconnect);
1355
1376
  const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
1356
- const clampedMs = Math.max(Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs), MAX_RECONNECT_TIMEOUT_MS);
1357
- this._timeouts.reconnect = setTimeout(this._reconnect.bind(this), clampedMs);
1377
+ const clampedMs = Math.max(
1378
+ Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
1379
+ MAX_RECONNECT_TIMEOUT_MS
1380
+ );
1381
+ this._timeouts.reconnect = setTimeout(
1382
+ this._reconnect.bind(this),
1383
+ clampedMs
1384
+ );
1358
1385
  }
1359
1386
  _reconnect() {
1360
1387
  return __async(this, null, function* () {
@@ -1553,10 +1580,7 @@ var MockedRoom = class extends AbstractRoom {
1553
1580
  room: this.id,
1554
1581
  user: myself
1555
1582
  };
1556
- const output = yield procedure.config.broadcast(
1557
- parsed,
1558
- context
1559
- );
1583
+ const output = yield procedure.config.broadcast(parsed, context);
1560
1584
  Object.entries(output).forEach(([_type, _data]) => {
1561
1585
  this._simulateEvent(_type, _data);
1562
1586
  });
@@ -1647,7 +1671,11 @@ var MockedRoom = class extends AbstractRoom {
1647
1671
  presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1648
1672
  }, limits);
1649
1673
  this._router = router != null ? router : new PluvRouter({});
1650
- this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
1674
+ this._usersManager = new UsersManager({
1675
+ initialPresence,
1676
+ limits: this._limits,
1677
+ presence
1678
+ });
1651
1679
  this._crdtManager = new CrdtManager({
1652
1680
  initialStorage
1653
1681
  });
package/dist/index.mjs CHANGED
@@ -95,9 +95,9 @@ import { noop } from "@pluv/crdt";
95
95
  var CrdtManager = class {
96
96
  constructor(options) {
97
97
  this.initialized = false;
98
- const { encodedState, initialStorage = noop.doc({}) } = options;
98
+ const { initialStorage = noop.doc({}) } = options;
99
99
  this._docFactory = initialStorage;
100
- this.doc = encodedState ? initialStorage.getFresh().applyEncodedState({ update: encodedState }) : initialStorage.getFresh();
100
+ this.doc = initialStorage.getEmpty();
101
101
  }
102
102
  /**
103
103
  * !HACK
@@ -117,25 +117,30 @@ var CrdtManager = class {
117
117
  get(key) {
118
118
  return this.doc.get(key);
119
119
  }
120
+ getInitialState() {
121
+ return this._docFactory.getInitialized().getEncodedState();
122
+ }
120
123
  initialize(params) {
121
124
  const { onInitialized, origin, update } = params;
122
- const updates = typeof update === "string" ? [update] : update;
123
- if (!updates.length) return this;
124
125
  if (this.initialized) {
125
- this.doc = this._applyDocUpdates(this.doc, updates, origin).track();
126
+ console.warn("Attempted to initialize storage multiple times");
126
127
  return this;
127
128
  }
128
- const onEmpty = this._applyDocUpdates(this._docFactory.getEmpty(), updates, origin);
129
- if (!onEmpty.isEmpty()) {
130
- onEmpty.destroy();
131
- this.doc = this._applyDocUpdates(this._docFactory.getFresh(), updates, origin).track();
132
- this.initialized = true;
133
- onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
129
+ if (!this.doc.isEmpty()) {
130
+ console.warn("Attempted to initialize over a non-empty storage");
134
131
  return this;
135
132
  }
136
- onEmpty.destroy();
137
- this.doc = this._docFactory.getInitialized().track();
133
+ const updates = typeof update === "string" ? [update] : update;
134
+ if (!updates.length) return this;
135
+ const initialized = this._docFactory.getInitialized();
136
+ const reference = initialized.get();
137
+ this.doc = this._applyDocUpdates(
138
+ this._docFactory.getEmpty(),
139
+ updates,
140
+ origin
141
+ ).rebuildStorage(reference);
138
142
  this.initialized = true;
143
+ initialized.destroy();
139
144
  onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
140
145
  return this;
141
146
  }
@@ -233,7 +238,10 @@ var PluvRouter = class _PluvRouter {
233
238
  this._defs = { events };
234
239
  }
235
240
  static merge(...routers) {
236
- const events = Object.assign(/* @__PURE__ */ Object.create(null), ...routers.map((router) => router._defs.events));
241
+ const events = Object.assign(
242
+ /* @__PURE__ */ Object.create(null),
243
+ ...routers.map((router) => router._defs.events)
244
+ );
237
245
  if (Object.keys(events).some((key) => key.startsWith("$"))) {
238
246
  throw new Error('Procedures may not start with "$"');
239
247
  }
@@ -386,7 +394,11 @@ var UsersManager = class {
386
394
  const userInfo = (_a = this._others.get(clientId)) != null ? _a : null;
387
395
  if (!userInfo) return null;
388
396
  const remaining = (_c = (_b = this._getConnectionIds(clientId)) == null ? void 0 : _b.size) != null ? _c : 0;
389
- const result = { clientId, remaining, data: userInfo };
397
+ const result = {
398
+ clientId,
399
+ remaining,
400
+ data: userInfo
401
+ };
390
402
  if (!remaining) this._others.delete(clientId);
391
403
  return result;
392
404
  }
@@ -423,7 +435,10 @@ var UsersManager = class {
423
435
  const other = this._others.get(clientId);
424
436
  if (!other) return null;
425
437
  const cleanedPatch = pickBy(patch != null ? patch : {}, (value) => typeof value !== "undefined");
426
- const cleanedPresence = pickBy((_a = other.presence) != null ? _a : {}, (value) => typeof value !== "undefined");
438
+ const cleanedPresence = pickBy(
439
+ (_a = other.presence) != null ? _a : {},
440
+ (value) => typeof value !== "undefined"
441
+ );
427
442
  const presence = __spreadValues(__spreadValues(__spreadValues({}, this.initialPresence), cleanedPresence), cleanedPatch);
428
443
  const validated = this._presence ? this._presence.parse(presence) : presence;
429
444
  this._others.set(clientId, __spreadProps(__spreadValues({}, other), { presence: validated }));
@@ -592,10 +607,7 @@ var PluvRoom = class extends AbstractRoom {
592
607
  room: this.id,
593
608
  user: myself
594
609
  };
595
- const output = yield procedure.config.broadcast(
596
- parsed,
597
- context
598
- );
610
+ const output = yield procedure.config.broadcast(parsed, context);
599
611
  Object.entries(output).forEach(([_type, _data]) => {
600
612
  this._sendMessage({ data: _data, type: _type });
601
613
  });
@@ -718,7 +730,11 @@ var PluvRoom = class extends AbstractRoom {
718
730
  }
719
731
  };
720
732
  this._router = router != null ? router : new PluvRouter({});
721
- this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
733
+ this._usersManager = new UsersManager({
734
+ initialPresence,
735
+ limits: this._limits,
736
+ presence
737
+ });
722
738
  this._crdtManager = new CrdtManager({ initialStorage });
723
739
  }
724
740
  get storageLoaded() {
@@ -733,17 +749,17 @@ var PluvRoom = class extends AbstractRoom {
733
749
  const params = (_a = args[0]) != null ? _a : {};
734
750
  const metadata = params.metadata;
735
751
  this._setMetadata(metadata);
736
- const canConnect = [ConnectionState.Closed, ConnectionState.Unavailable, ConnectionState.Untouched].some(
737
- (state) => this._state.connection.state === state
738
- );
752
+ const canConnect = [
753
+ ConnectionState.Closed,
754
+ ConnectionState.Unavailable,
755
+ ConnectionState.Untouched
756
+ ].some((state) => this._state.connection.state === state);
739
757
  if (!canConnect) return;
740
758
  this._clearTimeout(this._timeouts.reconnect);
741
759
  this._updateState((oldState) => {
742
760
  oldState.connection.state = ConnectionState.Connecting;
743
761
  return oldState;
744
762
  });
745
- yield this._storageStore.initialize();
746
- yield this._applyStorageStore();
747
763
  const wsEndpoint = this._getWsEndpoint(this.id, params);
748
764
  const url = new URL(wsEndpoint);
749
765
  let authToken = null;
@@ -1012,12 +1028,13 @@ var PluvRoom = class extends AbstractRoom {
1012
1028
  this._stateNotifier.subjects.others.next(others);
1013
1029
  }
1014
1030
  _handleRegisteredMessage(message) {
1015
- var _a, _b, _c;
1031
+ var _a, _b;
1016
1032
  const { connectionId } = message;
1017
1033
  const user = message.user;
1018
1034
  if (!connectionId) return;
1019
1035
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1020
1036
  const data = message.data;
1037
+ const state = data.state;
1021
1038
  this._updateState((oldState) => {
1022
1039
  oldState.connection.count += 1;
1023
1040
  oldState.connection.id = connectionId;
@@ -1034,7 +1051,7 @@ var PluvRoom = class extends AbstractRoom {
1034
1051
  const myself = (_b = this._usersManager.myself) != null ? _b : null;
1035
1052
  this._stateNotifier.subjects["my-presence"].next(presence);
1036
1053
  this._stateNotifier.subjects.myself.next(myself);
1037
- const update = this._state.connection.count > 1 ? (_c = this._crdtManager.doc.getEncodedState()) != null ? _c : null : null;
1054
+ const update = this._crdtManager.getInitialState();
1038
1055
  this._sendMessage({
1039
1056
  type: "$initializeSession",
1040
1057
  data: { presence, update }
@@ -1046,15 +1063,16 @@ var PluvRoom = class extends AbstractRoom {
1046
1063
  if (!connectionId) return;
1047
1064
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1048
1065
  const data = message.data;
1066
+ const state = data.state;
1049
1067
  this._crdtManager.initialize({
1050
1068
  onInitialized: (update2) => __async(this, null, function* () {
1051
- this._addToStorageStore(update2);
1069
+ yield this._addToStorageStore(update2);
1052
1070
  this._emitSharedTypes();
1053
1071
  this._observeCrdt();
1054
1072
  this._stateNotifier.subjects["storage-loaded"].next(true);
1055
1073
  }),
1056
1074
  origin: ORIGIN_INITIALIZED,
1057
- update: data.state
1075
+ update: state
1058
1076
  });
1059
1077
  const encodedState = this._crdtManager.doc.getEncodedState();
1060
1078
  yield this._storageStore.addUpdate(encodedState);
@@ -1071,7 +1089,6 @@ var PluvRoom = class extends AbstractRoom {
1071
1089
  if (!connectionId) return;
1072
1090
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1073
1091
  const data = message.data;
1074
- if (!this._crdtManager) return;
1075
1092
  this._crdtManager.doc.applyEncodedState({
1076
1093
  origin: ORIGIN_STORAGE_UPDATED,
1077
1094
  update: data.state
@@ -1079,7 +1096,6 @@ var PluvRoom = class extends AbstractRoom {
1079
1096
  const sharedTypes = this._crdtManager.doc.get();
1080
1097
  const storageRoot = Object.keys(sharedTypes).reduce(
1081
1098
  (acc, prop) => {
1082
- if (!this._crdtManager) return acc;
1083
1099
  const serialized = this._crdtManager.doc.toJson(prop);
1084
1100
  this._crdtNotifier.subject(prop).next(serialized);
1085
1101
  return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
@@ -1126,7 +1142,10 @@ var PluvRoom = class extends AbstractRoom {
1126
1142
  }
1127
1143
  _heartbeat() {
1128
1144
  this._clearTimeout(this._timeouts.pong);
1129
- this._timeouts.pong = setTimeout(this._reconnect.bind(this), PONG_TIMEOUT_MS);
1145
+ this._timeouts.pong = setTimeout(
1146
+ this._reconnect.bind(this),
1147
+ PONG_TIMEOUT_MS
1148
+ );
1130
1149
  this._sendMessage('{"type":"$ping","data":{}}');
1131
1150
  }
1132
1151
  _logDebug(...data) {
@@ -1137,7 +1156,6 @@ var PluvRoom = class extends AbstractRoom {
1137
1156
  _observeCrdt() {
1138
1157
  var _a, _b;
1139
1158
  (_b = (_a = this._subscriptions).observeCrdt) == null ? void 0 : _b.call(_a);
1140
- if (!this._crdtManager) return;
1141
1159
  const unsubscribe = this._crdtManager.doc.subscribe((event) => {
1142
1160
  var _a2;
1143
1161
  const origin = (_a2 = event.origin) != null ? _a2 : null;
@@ -1267,7 +1285,10 @@ var PluvRoom = class extends AbstractRoom {
1267
1285
  return oldState;
1268
1286
  });
1269
1287
  this._clearInterval(this._intervals.heartbeat);
1270
- this._intervals.heartbeat = setInterval(this._heartbeat.bind(this), HEARTBEAT_INTERVAL_MS);
1288
+ this._intervals.heartbeat = setInterval(
1289
+ this._heartbeat.bind(this),
1290
+ HEARTBEAT_INTERVAL_MS
1291
+ );
1271
1292
  }
1272
1293
  _onNavigatorOffline() {
1273
1294
  return __async(this, null, function* () {
@@ -1322,8 +1343,14 @@ var PluvRoom = class extends AbstractRoom {
1322
1343
  _pollReconnect() {
1323
1344
  this._clearTimeout(this._timeouts.reconnect);
1324
1345
  const timeoutMs = typeof this._reconnectTimeoutMs === "number" ? this._reconnectTimeoutMs : this._reconnectTimeoutMs({ attempts: this._state.connection.attempts });
1325
- const clampedMs = Math.max(Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs), MAX_RECONNECT_TIMEOUT_MS);
1326
- this._timeouts.reconnect = setTimeout(this._reconnect.bind(this), clampedMs);
1346
+ const clampedMs = Math.max(
1347
+ Math.min(MIN_RECONNECT_TIMEOUT_MS, timeoutMs),
1348
+ MAX_RECONNECT_TIMEOUT_MS
1349
+ );
1350
+ this._timeouts.reconnect = setTimeout(
1351
+ this._reconnect.bind(this),
1352
+ clampedMs
1353
+ );
1327
1354
  }
1328
1355
  _reconnect() {
1329
1356
  return __async(this, null, function* () {
@@ -1522,10 +1549,7 @@ var MockedRoom = class extends AbstractRoom {
1522
1549
  room: this.id,
1523
1550
  user: myself
1524
1551
  };
1525
- const output = yield procedure.config.broadcast(
1526
- parsed,
1527
- context
1528
- );
1552
+ const output = yield procedure.config.broadcast(parsed, context);
1529
1553
  Object.entries(output).forEach(([_type, _data]) => {
1530
1554
  this._simulateEvent(_type, _data);
1531
1555
  });
@@ -1616,7 +1640,11 @@ var MockedRoom = class extends AbstractRoom {
1616
1640
  presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
1617
1641
  }, limits);
1618
1642
  this._router = router != null ? router : new PluvRouter({});
1619
- this._usersManager = new UsersManager({ initialPresence, limits: this._limits, presence });
1643
+ this._usersManager = new UsersManager({
1644
+ initialPresence,
1645
+ limits: this._limits,
1646
+ presence
1647
+ });
1620
1648
  this._crdtManager = new CrdtManager({
1621
1649
  initialStorage
1622
1650
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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": "^1.0.0",
22
- "@pluv/types": "^1.0.0"
21
+ "@pluv/crdt": "^1.0.1",
22
+ "@pluv/types": "^1.0.1"
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": "^1.0.0",
29
- "eslint-config-pluv": "^1.0.0"
28
+ "@pluv/tsconfig": "^1.0.1",
29
+ "eslint-config-pluv": "^1.0.1"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -49,7 +49,9 @@ export abstract class AbstractRoom<
49
49
  public abstract getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
50
50
 
51
51
  public abstract getStorageJson(): InferCrdtJson<TStorage> | null;
52
- public abstract getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
52
+ public abstract getStorageJson<TKey extends keyof TStorage>(
53
+ type: TKey,
54
+ ): InferCrdtJson<TStorage[TKey]> | null;
53
55
 
54
56
  public abstract other(
55
57
  connectionId: string,