@pluv/client 0.11.1 → 0.12.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.
package/dist/index.js CHANGED
@@ -87,14 +87,21 @@ var import_crdt_yjs = require("@pluv/crdt-yjs");
87
87
  var CrdtManager = class {
88
88
  constructor(options = {}) {
89
89
  this.initialized = false;
90
- const { encodedState, initialStorage } = options;
90
+ const {
91
+ captureTimeout,
92
+ encodedState,
93
+ initialStorage,
94
+ trackedOrigins = []
95
+ } = options;
91
96
  const _doc = encodedState ? (0, import_crdt_yjs.doc)().applyUpdate(encodedState) : null;
92
97
  if (_doc && !!Object.keys(_doc.toJSON()).length) {
93
98
  this.doc = _doc;
99
+ this.trackOrigins({ captureTimeout, trackedOrigins });
94
100
  return;
95
101
  }
96
102
  _doc == null ? void 0 : _doc.destroy();
97
103
  this.doc = (0, import_crdt_yjs.doc)(initialStorage == null ? void 0 : initialStorage());
104
+ this.trackOrigins({ captureTimeout, trackedOrigins });
98
105
  }
99
106
  /**
100
107
  * !HACK
@@ -116,8 +123,15 @@ var CrdtManager = class {
116
123
  return this.doc.get(key);
117
124
  }
118
125
  initialize(params) {
119
- const { onInitialized, origin, update } = params;
126
+ const {
127
+ captureTimeout,
128
+ onInitialized,
129
+ origin,
130
+ trackedOrigins,
131
+ update
132
+ } = params;
120
133
  const updates = typeof update === "string" ? [update] : update;
134
+ this.trackOrigins({ captureTimeout, trackedOrigins });
121
135
  if (!updates.length)
122
136
  return this;
123
137
  if (this.initialized) {
@@ -132,8 +146,13 @@ var CrdtManager = class {
132
146
  _doc.destroy();
133
147
  }
134
148
  onInitialized == null ? void 0 : onInitialized();
149
+ this.trackOrigins({ captureTimeout, trackedOrigins });
135
150
  return this;
136
151
  }
152
+ trackOrigins(options) {
153
+ const { captureTimeout, trackedOrigins } = options;
154
+ this.doc.trackOrigins({ captureTimeout, trackedOrigins });
155
+ }
137
156
  _applyDocUpdates(yjsDoc, updates, origin) {
138
157
  yjsDoc.transact(() => {
139
158
  updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
@@ -146,6 +165,7 @@ var CrdtManager = class {
146
165
  var import_wonka = require("wonka");
147
166
  var CrdtNotifier = class {
148
167
  constructor() {
168
+ this.rootSubject = (0, import_wonka.makeSubject)();
149
169
  this.subjects = /* @__PURE__ */ new Map();
150
170
  }
151
171
  subject(key) {
@@ -157,6 +177,9 @@ var CrdtNotifier = class {
157
177
  this.subjects.set(key, newSubject);
158
178
  return newSubject;
159
179
  }
180
+ subcribeRoot(callback) {
181
+ return (0, import_wonka.subscribe)(callback)(this.rootSubject.source).unsubscribe;
182
+ }
160
183
  subscribe(key, callback) {
161
184
  const subject = this.subject(key);
162
185
  return (0, import_wonka.subscribe)(callback)(subject.source).unsubscribe;
@@ -379,7 +402,8 @@ var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
379
402
  var HEARTBEAT_INTERVAL_MS = 1e4;
380
403
  var PONG_TIMEOUT_MS = 2e3;
381
404
  var RECONNECT_TIMEOUT_MS = 3e4;
382
- var Y_ORIGIN_INITIALIZED = "$INITIALIZED";
405
+ var ORIGIN_INITIALIZED = "$INITIALIZED";
406
+ var ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
383
407
  var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
384
408
  storage: new StorageStore(input.room.id)
385
409
  });
@@ -388,14 +412,17 @@ var PluvRoom = class extends AbstractRoom {
388
412
  const {
389
413
  addons = [],
390
414
  authEndpoint,
415
+ captureTimeout,
391
416
  debug = false,
392
417
  initialPresence,
393
418
  initialStorage,
394
419
  onAuthorizationFail,
395
420
  presence,
421
+ trackedOrigins,
396
422
  wsEndpoint
397
423
  } = options;
398
424
  super(room);
425
+ this._captureTimeout = null;
399
426
  this._crdtNotifier = new CrdtNotifier();
400
427
  this._eventNotifier = new EventNotifier();
401
428
  this._intervals = {
@@ -422,8 +449,15 @@ var PluvRoom = class extends AbstractRoom {
422
449
  pong: null,
423
450
  reconnect: null
424
451
  };
452
+ this._trackedOrigins = null;
425
453
  this._windowListeners = null;
426
454
  this._wsListeners = null;
455
+ this.canRedo = () => {
456
+ return !!this._crdtManager.doc.canRedo();
457
+ };
458
+ this.canUndo = () => {
459
+ return !!this._crdtManager.doc.canUndo();
460
+ };
427
461
  this.event = (event, callback) => {
428
462
  return this._eventNotifier.subscribe(event, callback);
429
463
  };
@@ -440,18 +474,38 @@ var PluvRoom = class extends AbstractRoom {
440
474
  return this._usersManager.getOthers();
441
475
  };
442
476
  this.getStorage = (key) => {
443
- var _a, _b;
444
- return (_b = (_a = this._crdtManager) == null ? void 0 : _a.get(key)) != null ? _b : null;
477
+ const sharedType = this._crdtManager.get(key);
478
+ if (typeof sharedType === "undefined") {
479
+ throw new Error(`Could not find storege: ${key.toString()}`);
480
+ }
481
+ return sharedType;
445
482
  };
446
483
  this.other = (connectionId, callback) => {
447
484
  return this._otherNotifier.subscribe(connectionId, callback);
448
485
  };
486
+ this.redo = () => {
487
+ this._crdtManager.doc.redo();
488
+ };
449
489
  this.storage = (key, fn) => {
450
490
  return this._crdtNotifier.subscribe(key, fn);
451
491
  };
492
+ this.storageRoot = (fn) => {
493
+ return this._crdtNotifier.subcribeRoot(fn);
494
+ };
452
495
  this.subscribe = (name, callback) => {
453
496
  return this._stateNotifier.subscribe(name, callback);
454
497
  };
498
+ this.transact = (fn, origin) => {
499
+ const _origin = origin != null ? origin : this._state.connection.id;
500
+ if (typeof _origin !== "string")
501
+ return;
502
+ this._crdtManager.doc.transact(() => {
503
+ fn(this._crdtManager.doc.storage);
504
+ }, _origin);
505
+ };
506
+ this.undo = () => {
507
+ this._crdtManager.doc.undo();
508
+ };
455
509
  this.updateMyPresence = (presence) => {
456
510
  var _a;
457
511
  this._usersManager.updateMyPresence(presence);
@@ -501,7 +555,13 @@ var PluvRoom = class extends AbstractRoom {
501
555
  initialPresence,
502
556
  presence
503
557
  });
504
- this._crdtManager = new CrdtManager({ initialStorage });
558
+ this._crdtManager = new CrdtManager({
559
+ captureTimeout,
560
+ initialStorage,
561
+ trackedOrigins
562
+ });
563
+ this._captureTimeout = captureTimeout != null ? captureTimeout : null;
564
+ this._trackedOrigins = trackedOrigins != null ? trackedOrigins : null;
505
565
  }
506
566
  get webSocket() {
507
567
  return this._state.webSocket;
@@ -532,7 +592,6 @@ var PluvRoom = class extends AbstractRoom {
532
592
  });
533
593
  yield this._storageStore.initialize();
534
594
  yield this._applyStorageStore();
535
- console.log(this._getWsEndpoint(this.id));
536
595
  const url = new URL(this._getWsEndpoint(this.id));
537
596
  let authToken = null;
538
597
  let webSocket;
@@ -556,7 +615,6 @@ var PluvRoom = class extends AbstractRoom {
556
615
  }
557
616
  disconnect() {
558
617
  return __async(this, null, function* () {
559
- var _a;
560
618
  if (!this._endpoints.wsEndpoint) {
561
619
  throw new Error("Must provide an wsEndpoint.");
562
620
  }
@@ -571,7 +629,7 @@ var PluvRoom = class extends AbstractRoom {
571
629
  this._clearInterval(this._intervals.heartbeat);
572
630
  this._closeWs();
573
631
  yield this._storageStore.destroy();
574
- (_a = this._crdtManager) == null ? void 0 : _a.destroy();
632
+ this._crdtManager.destroy();
575
633
  this._stateNotifier.subjects["my-presence"].next(null);
576
634
  });
577
635
  }
@@ -588,12 +646,16 @@ var PluvRoom = class extends AbstractRoom {
588
646
  }
589
647
  _applyStorageStore() {
590
648
  return __async(this, null, function* () {
649
+ var _a;
591
650
  const updates = yield this._storageStore.getUpdates();
651
+ const trackedOrigins = this._getTrackedOrigins();
592
652
  this._crdtManager.initialize({
653
+ captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
593
654
  onInitialized: () => {
594
655
  this._observeCrdt();
595
656
  },
596
- origin: Y_ORIGIN_INITIALIZED,
657
+ origin: ORIGIN_INITIALIZED,
658
+ trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
597
659
  update: updates
598
660
  });
599
661
  this._emitSharedTypes();
@@ -712,10 +774,15 @@ var PluvRoom = class extends AbstractRoom {
712
774
  }
713
775
  _emitSharedTypes() {
714
776
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
715
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
716
- const serialized = sharedType.toJSON();
717
- this._crdtNotifier.subject(prop).next(serialized);
718
- });
777
+ const storageRoot = Object.entries(sharedTypes).reduce(
778
+ (acc, [prop, sharedType]) => {
779
+ const serialized = sharedType.toJSON();
780
+ this._crdtNotifier.subject(prop).next(serialized);
781
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
782
+ },
783
+ {}
784
+ );
785
+ this._crdtNotifier.rootSubject.next(storageRoot);
719
786
  }
720
787
  _getAuthFetchOptions(room) {
721
788
  if (typeof this._endpoints.authEndpoint === "undefined")
@@ -754,6 +821,11 @@ var PluvRoom = class extends AbstractRoom {
754
821
  }
755
822
  });
756
823
  }
824
+ _getTrackedOrigins() {
825
+ var _a;
826
+ const connectionId = this._state.connection.id;
827
+ return !connectionId ? this._trackedOrigins : [connectionId, ...(_a = this._trackedOrigins) != null ? _a : []];
828
+ }
757
829
  _getWsEndpoint(room) {
758
830
  switch (typeof this._endpoints.wsEndpoint) {
759
831
  case "undefined":
@@ -822,7 +894,7 @@ var PluvRoom = class extends AbstractRoom {
822
894
  this._stateNotifier.subjects["others"].next(others);
823
895
  }
824
896
  _handleRegisteredMessage(message) {
825
- var _a, _b, _c;
897
+ var _a, _b;
826
898
  const { connectionId, user } = message;
827
899
  if (!connectionId)
828
900
  return;
@@ -842,7 +914,7 @@ var PluvRoom = class extends AbstractRoom {
842
914
  const myself = (_a = this._usersManager.myself) != null ? _a : null;
843
915
  this._stateNotifier.subjects["my-presence"].next(presence);
844
916
  this._stateNotifier.subjects["myself"].next(myself);
845
- const update = this._state.connection.count > 1 ? (_c = (_b = this._crdtManager) == null ? void 0 : _b.doc.encodeStateAsUpdate()) != null ? _c : null : null;
917
+ const update = this._state.connection.count > 1 ? (_b = this._crdtManager.doc.encodeStateAsUpdate()) != null ? _b : null : null;
846
918
  this._sendMessage({
847
919
  type: "$INITIALIZE_SESSION",
848
920
  data: { presence, update }
@@ -850,17 +922,21 @@ var PluvRoom = class extends AbstractRoom {
850
922
  }
851
923
  _handleStorageReceivedMessage(message) {
852
924
  return __async(this, null, function* () {
925
+ var _a;
853
926
  const { connectionId } = message;
854
927
  if (!connectionId)
855
928
  return;
856
929
  if (!this._state.webSocket)
857
930
  throw new Error("Could not find WebSocket");
858
931
  const data = message.data;
932
+ const trackedOrigins = this._getTrackedOrigins();
859
933
  this._crdtManager.initialize({
934
+ captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
860
935
  onInitialized: () => __async(this, null, function* () {
861
936
  this._observeCrdt();
862
937
  }),
863
- origin: Y_ORIGIN_INITIALIZED,
938
+ origin: ORIGIN_INITIALIZED,
939
+ trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
864
940
  update: data.state
865
941
  });
866
942
  const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
@@ -869,7 +945,7 @@ var PluvRoom = class extends AbstractRoom {
869
945
  const update = this._crdtManager.doc.encodeStateAsUpdate();
870
946
  this._sendMessage({
871
947
  type: "$UPDATE_STORAGE",
872
- data: { origin: Y_ORIGIN_INITIALIZED, update }
948
+ data: { origin: ORIGIN_INITIALIZED, update }
873
949
  });
874
950
  });
875
951
  }
@@ -882,14 +958,19 @@ var PluvRoom = class extends AbstractRoom {
882
958
  const data = message.data;
883
959
  if (!this._crdtManager)
884
960
  return;
885
- this._crdtManager.doc.applyUpdate(data.state, "$STORAGE_UPDATED");
961
+ this._crdtManager.doc.applyUpdate(data.state, ORIGIN_STORAGE_UPDATED);
886
962
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
887
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
888
- if (!this._crdtManager)
889
- return;
890
- const serialized = sharedType.toJSON();
891
- this._crdtNotifier.subject(prop).next(serialized);
892
- });
963
+ const storageRoot = Object.entries(sharedTypes).reduce(
964
+ (acc, [prop, sharedType]) => {
965
+ if (!this._crdtManager)
966
+ return acc;
967
+ const serialized = sharedType.toJSON();
968
+ this._crdtNotifier.subject(prop).next(serialized);
969
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
970
+ },
971
+ {}
972
+ );
973
+ this._crdtNotifier.rootSubject.next(storageRoot);
893
974
  }
894
975
  _handleUserJoinedMessage(message) {
895
976
  const { connectionId } = message;
@@ -938,7 +1019,7 @@ var PluvRoom = class extends AbstractRoom {
938
1019
  const origin = _origin != null ? _origin : null;
939
1020
  this._addToStorageStore(update);
940
1021
  this._emitSharedTypes();
941
- if (origin === Y_ORIGIN_INITIALIZED || origin === this)
1022
+ if (origin === ORIGIN_INITIALIZED || origin === this)
942
1023
  return;
943
1024
  if (!this._state.webSocket)
944
1025
  return;
@@ -946,7 +1027,7 @@ var PluvRoom = class extends AbstractRoom {
946
1027
  return;
947
1028
  if (this._state.webSocket.readyState !== WebSocket.OPEN)
948
1029
  return;
949
- if (origin === "$STORAGE_UPDATED")
1030
+ if (origin === ORIGIN_STORAGE_UPDATED)
950
1031
  return;
951
1032
  this._sendMessage({
952
1033
  type: "$UPDATE_STORAGE",
@@ -1216,7 +1297,6 @@ var MockedRoom = class extends AbstractRoom {
1216
1297
  constructor(room, options) {
1217
1298
  const { events, initialPresence, initialStorage, presence } = options;
1218
1299
  super(room);
1219
- this._crdtManager = null;
1220
1300
  this._crdtNotifier = new CrdtNotifier();
1221
1301
  this._eventNotifier = new EventNotifier();
1222
1302
  this._otherNotifier = new OtherNotifier();
@@ -1236,6 +1316,12 @@ var MockedRoom = class extends AbstractRoom {
1236
1316
  this._subscriptions = {
1237
1317
  observeCrdt: null
1238
1318
  };
1319
+ this.canRedo = () => {
1320
+ return !!this._crdtManager.doc.canRedo();
1321
+ };
1322
+ this.canUndo = () => {
1323
+ return !!this._crdtManager.doc.canUndo();
1324
+ };
1239
1325
  this.event = (event, callback) => {
1240
1326
  return this._eventNotifier.subscribe(event, callback);
1241
1327
  };
@@ -1252,18 +1338,41 @@ var MockedRoom = class extends AbstractRoom {
1252
1338
  return this._usersManager.getOthers();
1253
1339
  };
1254
1340
  this.getStorage = (key) => {
1255
- var _a, _b;
1256
- return (_b = (_a = this._crdtManager) == null ? void 0 : _a.get(key)) != null ? _b : null;
1341
+ const sharedType = this._crdtManager.get(key);
1342
+ if (typeof sharedType === "undefined") {
1343
+ throw new Error(`Could not find storege: ${key.toString()}`);
1344
+ }
1345
+ return sharedType;
1257
1346
  };
1258
1347
  this.other = (connectionId, callback) => {
1259
1348
  return this._otherNotifier.subscribe(connectionId, callback);
1260
1349
  };
1350
+ this.redo = () => {
1351
+ this._crdtManager.doc.redo();
1352
+ };
1261
1353
  this.storage = (key, fn) => {
1262
1354
  return this._crdtNotifier.subscribe(key, fn);
1263
1355
  };
1356
+ this.storageRoot = (fn) => {
1357
+ return this._crdtNotifier.subcribeRoot(fn);
1358
+ };
1264
1359
  this.subscribe = (name, callback) => {
1265
1360
  return this._stateNotifier.subscribe(name, callback);
1266
1361
  };
1362
+ this.transact = (fn, origin) => {
1363
+ const _origin = origin != null ? origin : this._state.connection.id;
1364
+ const crdtManager = this._crdtManager;
1365
+ if (typeof _origin !== "string")
1366
+ return;
1367
+ if (!crdtManager)
1368
+ return;
1369
+ crdtManager.doc.transact(() => {
1370
+ fn(crdtManager.doc.storage);
1371
+ }, _origin);
1372
+ };
1373
+ this.undo = () => {
1374
+ this._crdtManager.doc.undo();
1375
+ };
1267
1376
  this.updateMyPresence = (presence) => {
1268
1377
  var _a;
1269
1378
  this._usersManager.updateMyPresence(presence);
@@ -1315,12 +1424,17 @@ var MockedRoom = class extends AbstractRoom {
1315
1424
  if (origin === "$STORAGE_UPDATED")
1316
1425
  return;
1317
1426
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
1318
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
1319
- if (!this._crdtManager)
1320
- return;
1321
- const serialized = sharedType.toJSON();
1322
- this._crdtNotifier.subject(prop).next(serialized);
1323
- });
1427
+ const storageRoot = Object.entries(sharedTypes).reduce(
1428
+ (acc, [prop, sharedType]) => {
1429
+ if (!this._crdtManager)
1430
+ return acc;
1431
+ const serialized = sharedType.toJSON();
1432
+ this._crdtNotifier.subject(prop).next(serialized);
1433
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
1434
+ },
1435
+ {}
1436
+ );
1437
+ this._crdtNotifier.rootSubject.next(storageRoot);
1324
1438
  }
1325
1439
  );
1326
1440
  this._subscriptions.observeCrdt = unsubscribe;