@pluv/client 0.11.1 → 0.12.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
@@ -61,14 +61,21 @@ import { doc } from "@pluv/crdt-yjs";
61
61
  var CrdtManager = class {
62
62
  constructor(options = {}) {
63
63
  this.initialized = false;
64
- const { encodedState, initialStorage } = options;
64
+ const {
65
+ captureTimeout,
66
+ encodedState,
67
+ initialStorage,
68
+ trackedOrigins = []
69
+ } = options;
65
70
  const _doc = encodedState ? doc().applyUpdate(encodedState) : null;
66
71
  if (_doc && !!Object.keys(_doc.toJSON()).length) {
67
72
  this.doc = _doc;
73
+ this.trackOrigins({ captureTimeout, trackedOrigins });
68
74
  return;
69
75
  }
70
76
  _doc == null ? void 0 : _doc.destroy();
71
77
  this.doc = doc(initialStorage == null ? void 0 : initialStorage());
78
+ this.trackOrigins({ captureTimeout, trackedOrigins });
72
79
  }
73
80
  /**
74
81
  * !HACK
@@ -90,8 +97,15 @@ var CrdtManager = class {
90
97
  return this.doc.get(key);
91
98
  }
92
99
  initialize(params) {
93
- const { onInitialized, origin, update } = params;
100
+ const {
101
+ captureTimeout,
102
+ onInitialized,
103
+ origin,
104
+ trackedOrigins,
105
+ update
106
+ } = params;
94
107
  const updates = typeof update === "string" ? [update] : update;
108
+ this.trackOrigins({ captureTimeout, trackedOrigins });
95
109
  if (!updates.length)
96
110
  return this;
97
111
  if (this.initialized) {
@@ -106,8 +120,13 @@ var CrdtManager = class {
106
120
  _doc.destroy();
107
121
  }
108
122
  onInitialized == null ? void 0 : onInitialized();
123
+ this.trackOrigins({ captureTimeout, trackedOrigins });
109
124
  return this;
110
125
  }
126
+ trackOrigins(options) {
127
+ const { captureTimeout, trackedOrigins } = options;
128
+ this.doc.trackOrigins({ captureTimeout, trackedOrigins });
129
+ }
111
130
  _applyDocUpdates(yjsDoc, updates, origin) {
112
131
  yjsDoc.transact(() => {
113
132
  updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
@@ -120,6 +139,7 @@ var CrdtManager = class {
120
139
  import { makeSubject, subscribe } from "wonka";
121
140
  var CrdtNotifier = class {
122
141
  constructor() {
142
+ this.rootSubject = makeSubject();
123
143
  this.subjects = /* @__PURE__ */ new Map();
124
144
  }
125
145
  subject(key) {
@@ -131,6 +151,9 @@ var CrdtNotifier = class {
131
151
  this.subjects.set(key, newSubject);
132
152
  return newSubject;
133
153
  }
154
+ subcribeRoot(callback) {
155
+ return subscribe(callback)(this.rootSubject.source).unsubscribe;
156
+ }
134
157
  subscribe(key, callback) {
135
158
  const subject = this.subject(key);
136
159
  return subscribe(callback)(subject.source).unsubscribe;
@@ -353,7 +376,8 @@ var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
353
376
  var HEARTBEAT_INTERVAL_MS = 1e4;
354
377
  var PONG_TIMEOUT_MS = 2e3;
355
378
  var RECONNECT_TIMEOUT_MS = 3e4;
356
- var Y_ORIGIN_INITIALIZED = "$INITIALIZED";
379
+ var ORIGIN_INITIALIZED = "$INITIALIZED";
380
+ var ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
357
381
  var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
358
382
  storage: new StorageStore(input.room.id)
359
383
  });
@@ -362,14 +386,17 @@ var PluvRoom = class extends AbstractRoom {
362
386
  const {
363
387
  addons = [],
364
388
  authEndpoint,
389
+ captureTimeout,
365
390
  debug = false,
366
391
  initialPresence,
367
392
  initialStorage,
368
393
  onAuthorizationFail,
369
394
  presence,
395
+ trackedOrigins,
370
396
  wsEndpoint
371
397
  } = options;
372
398
  super(room);
399
+ this._captureTimeout = null;
373
400
  this._crdtNotifier = new CrdtNotifier();
374
401
  this._eventNotifier = new EventNotifier();
375
402
  this._intervals = {
@@ -396,8 +423,15 @@ var PluvRoom = class extends AbstractRoom {
396
423
  pong: null,
397
424
  reconnect: null
398
425
  };
426
+ this._trackedOrigins = null;
399
427
  this._windowListeners = null;
400
428
  this._wsListeners = null;
429
+ this.canRedo = () => {
430
+ return !!this._crdtManager.doc.canRedo();
431
+ };
432
+ this.canUndo = () => {
433
+ return !!this._crdtManager.doc.canUndo();
434
+ };
401
435
  this.event = (event, callback) => {
402
436
  return this._eventNotifier.subscribe(event, callback);
403
437
  };
@@ -420,12 +454,29 @@ var PluvRoom = class extends AbstractRoom {
420
454
  this.other = (connectionId, callback) => {
421
455
  return this._otherNotifier.subscribe(connectionId, callback);
422
456
  };
457
+ this.redo = () => {
458
+ this._crdtManager.doc.redo();
459
+ };
423
460
  this.storage = (key, fn) => {
424
461
  return this._crdtNotifier.subscribe(key, fn);
425
462
  };
463
+ this.storageRoot = (fn) => {
464
+ return this._crdtNotifier.subcribeRoot(fn);
465
+ };
426
466
  this.subscribe = (name, callback) => {
427
467
  return this._stateNotifier.subscribe(name, callback);
428
468
  };
469
+ this.transact = (fn, origin) => {
470
+ const _origin = origin != null ? origin : this._state.connection.id;
471
+ if (typeof _origin !== "string")
472
+ return;
473
+ this._crdtManager.doc.transact(() => {
474
+ fn(this._crdtManager.doc.storage);
475
+ }, _origin);
476
+ };
477
+ this.undo = () => {
478
+ this._crdtManager.doc.undo();
479
+ };
429
480
  this.updateMyPresence = (presence) => {
430
481
  var _a;
431
482
  this._usersManager.updateMyPresence(presence);
@@ -475,7 +526,13 @@ var PluvRoom = class extends AbstractRoom {
475
526
  initialPresence,
476
527
  presence
477
528
  });
478
- this._crdtManager = new CrdtManager({ initialStorage });
529
+ this._crdtManager = new CrdtManager({
530
+ captureTimeout,
531
+ initialStorage,
532
+ trackedOrigins
533
+ });
534
+ this._captureTimeout = captureTimeout != null ? captureTimeout : null;
535
+ this._trackedOrigins = trackedOrigins != null ? trackedOrigins : null;
479
536
  }
480
537
  get webSocket() {
481
538
  return this._state.webSocket;
@@ -506,7 +563,6 @@ var PluvRoom = class extends AbstractRoom {
506
563
  });
507
564
  yield this._storageStore.initialize();
508
565
  yield this._applyStorageStore();
509
- console.log(this._getWsEndpoint(this.id));
510
566
  const url = new URL(this._getWsEndpoint(this.id));
511
567
  let authToken = null;
512
568
  let webSocket;
@@ -562,12 +618,16 @@ var PluvRoom = class extends AbstractRoom {
562
618
  }
563
619
  _applyStorageStore() {
564
620
  return __async(this, null, function* () {
621
+ var _a;
565
622
  const updates = yield this._storageStore.getUpdates();
623
+ const trackedOrigins = this._getTrackedOrigins();
566
624
  this._crdtManager.initialize({
625
+ captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
567
626
  onInitialized: () => {
568
627
  this._observeCrdt();
569
628
  },
570
- origin: Y_ORIGIN_INITIALIZED,
629
+ origin: ORIGIN_INITIALIZED,
630
+ trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
571
631
  update: updates
572
632
  });
573
633
  this._emitSharedTypes();
@@ -686,10 +746,15 @@ var PluvRoom = class extends AbstractRoom {
686
746
  }
687
747
  _emitSharedTypes() {
688
748
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
689
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
690
- const serialized = sharedType.toJSON();
691
- this._crdtNotifier.subject(prop).next(serialized);
692
- });
749
+ const storageRoot = Object.entries(sharedTypes).reduce(
750
+ (acc, [prop, sharedType]) => {
751
+ const serialized = sharedType.toJSON();
752
+ this._crdtNotifier.subject(prop).next(serialized);
753
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
754
+ },
755
+ {}
756
+ );
757
+ this._crdtNotifier.rootSubject.next(storageRoot);
693
758
  }
694
759
  _getAuthFetchOptions(room) {
695
760
  if (typeof this._endpoints.authEndpoint === "undefined")
@@ -728,6 +793,11 @@ var PluvRoom = class extends AbstractRoom {
728
793
  }
729
794
  });
730
795
  }
796
+ _getTrackedOrigins() {
797
+ var _a;
798
+ const connectionId = this._state.connection.id;
799
+ return !connectionId ? this._trackedOrigins : [connectionId, ...(_a = this._trackedOrigins) != null ? _a : []];
800
+ }
731
801
  _getWsEndpoint(room) {
732
802
  switch (typeof this._endpoints.wsEndpoint) {
733
803
  case "undefined":
@@ -824,17 +894,21 @@ var PluvRoom = class extends AbstractRoom {
824
894
  }
825
895
  _handleStorageReceivedMessage(message) {
826
896
  return __async(this, null, function* () {
897
+ var _a;
827
898
  const { connectionId } = message;
828
899
  if (!connectionId)
829
900
  return;
830
901
  if (!this._state.webSocket)
831
902
  throw new Error("Could not find WebSocket");
832
903
  const data = message.data;
904
+ const trackedOrigins = this._getTrackedOrigins();
833
905
  this._crdtManager.initialize({
906
+ captureTimeout: (_a = this._captureTimeout) != null ? _a : void 0,
834
907
  onInitialized: () => __async(this, null, function* () {
835
908
  this._observeCrdt();
836
909
  }),
837
- origin: Y_ORIGIN_INITIALIZED,
910
+ origin: ORIGIN_INITIALIZED,
911
+ trackedOrigins: trackedOrigins != null ? trackedOrigins : void 0,
838
912
  update: data.state
839
913
  });
840
914
  const encodedState = this._crdtManager.doc.encodeStateAsUpdate();
@@ -843,7 +917,7 @@ var PluvRoom = class extends AbstractRoom {
843
917
  const update = this._crdtManager.doc.encodeStateAsUpdate();
844
918
  this._sendMessage({
845
919
  type: "$UPDATE_STORAGE",
846
- data: { origin: Y_ORIGIN_INITIALIZED, update }
920
+ data: { origin: ORIGIN_INITIALIZED, update }
847
921
  });
848
922
  });
849
923
  }
@@ -856,14 +930,19 @@ var PluvRoom = class extends AbstractRoom {
856
930
  const data = message.data;
857
931
  if (!this._crdtManager)
858
932
  return;
859
- this._crdtManager.doc.applyUpdate(data.state, "$STORAGE_UPDATED");
933
+ this._crdtManager.doc.applyUpdate(data.state, ORIGIN_STORAGE_UPDATED);
860
934
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
861
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
862
- if (!this._crdtManager)
863
- return;
864
- const serialized = sharedType.toJSON();
865
- this._crdtNotifier.subject(prop).next(serialized);
866
- });
935
+ const storageRoot = Object.entries(sharedTypes).reduce(
936
+ (acc, [prop, sharedType]) => {
937
+ if (!this._crdtManager)
938
+ return acc;
939
+ const serialized = sharedType.toJSON();
940
+ this._crdtNotifier.subject(prop).next(serialized);
941
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
942
+ },
943
+ {}
944
+ );
945
+ this._crdtNotifier.rootSubject.next(storageRoot);
867
946
  }
868
947
  _handleUserJoinedMessage(message) {
869
948
  const { connectionId } = message;
@@ -912,7 +991,7 @@ var PluvRoom = class extends AbstractRoom {
912
991
  const origin = _origin != null ? _origin : null;
913
992
  this._addToStorageStore(update);
914
993
  this._emitSharedTypes();
915
- if (origin === Y_ORIGIN_INITIALIZED || origin === this)
994
+ if (origin === ORIGIN_INITIALIZED || origin === this)
916
995
  return;
917
996
  if (!this._state.webSocket)
918
997
  return;
@@ -920,7 +999,7 @@ var PluvRoom = class extends AbstractRoom {
920
999
  return;
921
1000
  if (this._state.webSocket.readyState !== WebSocket.OPEN)
922
1001
  return;
923
- if (origin === "$STORAGE_UPDATED")
1002
+ if (origin === ORIGIN_STORAGE_UPDATED)
924
1003
  return;
925
1004
  this._sendMessage({
926
1005
  type: "$UPDATE_STORAGE",
@@ -1210,6 +1289,14 @@ var MockedRoom = class extends AbstractRoom {
1210
1289
  this._subscriptions = {
1211
1290
  observeCrdt: null
1212
1291
  };
1292
+ this.canRedo = () => {
1293
+ var _a;
1294
+ return !!((_a = this._crdtManager) == null ? void 0 : _a.doc.canRedo());
1295
+ };
1296
+ this.canUndo = () => {
1297
+ var _a;
1298
+ return !!((_a = this._crdtManager) == null ? void 0 : _a.doc.canUndo());
1299
+ };
1213
1300
  this.event = (event, callback) => {
1214
1301
  return this._eventNotifier.subscribe(event, callback);
1215
1302
  };
@@ -1232,12 +1319,34 @@ var MockedRoom = class extends AbstractRoom {
1232
1319
  this.other = (connectionId, callback) => {
1233
1320
  return this._otherNotifier.subscribe(connectionId, callback);
1234
1321
  };
1322
+ this.redo = () => {
1323
+ var _a;
1324
+ (_a = this._crdtManager) == null ? void 0 : _a.doc.redo();
1325
+ };
1235
1326
  this.storage = (key, fn) => {
1236
1327
  return this._crdtNotifier.subscribe(key, fn);
1237
1328
  };
1329
+ this.storageRoot = (fn) => {
1330
+ return this._crdtNotifier.subcribeRoot(fn);
1331
+ };
1238
1332
  this.subscribe = (name, callback) => {
1239
1333
  return this._stateNotifier.subscribe(name, callback);
1240
1334
  };
1335
+ this.transact = (fn, origin) => {
1336
+ const _origin = origin != null ? origin : this._state.connection.id;
1337
+ const crdtManager = this._crdtManager;
1338
+ if (typeof _origin !== "string")
1339
+ return;
1340
+ if (!crdtManager)
1341
+ return;
1342
+ crdtManager.doc.transact(() => {
1343
+ fn(crdtManager.doc.storage);
1344
+ }, _origin);
1345
+ };
1346
+ this.undo = () => {
1347
+ var _a;
1348
+ (_a = this._crdtManager) == null ? void 0 : _a.doc.undo();
1349
+ };
1241
1350
  this.updateMyPresence = (presence) => {
1242
1351
  var _a;
1243
1352
  this._usersManager.updateMyPresence(presence);
@@ -1289,12 +1398,17 @@ var MockedRoom = class extends AbstractRoom {
1289
1398
  if (origin === "$STORAGE_UPDATED")
1290
1399
  return;
1291
1400
  const sharedTypes = this._crdtManager.doc.getSharedTypes();
1292
- Object.entries(sharedTypes).forEach(([prop, sharedType]) => {
1293
- if (!this._crdtManager)
1294
- return;
1295
- const serialized = sharedType.toJSON();
1296
- this._crdtNotifier.subject(prop).next(serialized);
1297
- });
1401
+ const storageRoot = Object.entries(sharedTypes).reduce(
1402
+ (acc, [prop, sharedType]) => {
1403
+ if (!this._crdtManager)
1404
+ return acc;
1405
+ const serialized = sharedType.toJSON();
1406
+ this._crdtNotifier.subject(prop).next(serialized);
1407
+ return __spreadProps(__spreadValues({}, acc), { [prop]: serialized });
1408
+ },
1409
+ {}
1410
+ );
1411
+ this._crdtNotifier.rootSubject.next(storageRoot);
1298
1412
  }
1299
1413
  );
1300
1414
  this._subscriptions.observeCrdt = unsubscribe;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,19 +18,19 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.4",
21
- "@pluv/crdt-yjs": "^0.11.1",
22
- "@pluv/types": "^0.11.1"
21
+ "@pluv/crdt-yjs": "^0.12.0",
22
+ "@pluv/types": "^0.12.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "yjs": "^13.0.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint": "^8.49.0",
28
+ "eslint": "^8.50.0",
29
29
  "tsup": "^7.2.0",
30
30
  "typescript": "^5.2.2",
31
- "yjs": "^13.6.7",
32
- "@pluv/tsconfig": "^0.11.1",
33
- "eslint-config-pluv": "^0.11.1"
31
+ "yjs": "^13.6.8",
32
+ "@pluv/tsconfig": "^0.12.0",
33
+ "eslint-config-pluv": "^0.12.0"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -18,7 +18,7 @@ import type { UserInfo, WebSocketConnection } from "./types";
18
18
  export abstract class AbstractRoom<
19
19
  TIO extends IOLike,
20
20
  TPresence extends JsonObject = {},
21
- TStorage extends Record<string, AbstractType<any>> = {}
21
+ TStorage extends Record<string, AbstractType<any>> = {},
22
22
  > {
23
23
  public readonly id: string;
24
24
 
@@ -28,12 +28,16 @@ export abstract class AbstractRoom<
28
28
 
29
29
  public abstract broadcast<TEvent extends keyof InferIOInput<TIO>>(
30
30
  event: TEvent,
31
- data: Id<InferIOInput<TIO>[TEvent]>
31
+ data: Id<InferIOInput<TIO>[TEvent]>,
32
32
  ): void;
33
33
 
34
+ public abstract canRedo(): boolean;
35
+
36
+ public abstract canUndo(): boolean;
37
+
34
38
  public abstract event<TEvent extends keyof InferIOOutput<TIO>>(
35
39
  event: TEvent,
36
- callback: EventNotifierSubscriptionCallback<TIO, TEvent>
40
+ callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
37
41
  ): () => void;
38
42
 
39
43
  public abstract getConnection(): WebSocketConnection;
@@ -43,31 +47,46 @@ export abstract class AbstractRoom<
43
47
  public abstract getMyself(): Id<UserInfo<TIO, TPresence>> | null;
44
48
 
45
49
  public abstract getOther(
46
- connectionId: string
50
+ connectionId: string,
47
51
  ): Id<UserInfo<TIO, TPresence>> | null;
48
52
 
49
53
  public abstract getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
50
54
 
51
55
  public abstract getStorage<TKey extends keyof TStorage>(
52
- key: TKey
56
+ key: TKey,
53
57
  ): TStorage[TKey] | null;
54
58
 
55
59
  public abstract other(
56
60
  connectionId: string,
57
- callback: OtherNotifierSubscriptionCallback<TIO>
61
+ callback: OtherNotifierSubscriptionCallback<TIO>,
58
62
  ): () => void;
59
63
 
64
+ public abstract redo(): void;
65
+
60
66
  public abstract storage<TKey extends keyof TStorage>(
61
67
  key: TKey,
62
- fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
68
+ fn: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
69
+ ): () => void;
70
+
71
+ public abstract storageRoot(
72
+ fn: (value: {
73
+ [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
74
+ }) => void,
63
75
  ): () => void;
64
76
 
65
77
  public abstract subscribe<
66
- TSubject extends keyof StateNotifierSubjects<TIO, TPresence>
78
+ TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
67
79
  >(
68
80
  name: TSubject,
69
- callback: SubscriptionCallback<TIO, TPresence, TSubject>
81
+ callback: SubscriptionCallback<TIO, TPresence, TSubject>,
70
82
  ): () => void;
71
83
 
84
+ public abstract transact(
85
+ fn: (storage: TStorage) => void,
86
+ origin?: string,
87
+ ): void;
88
+
89
+ public abstract undo(): void;
90
+
72
91
  public abstract updateMyPresence(presence: Partial<TPresence>): void;
73
92
  }
@@ -1,5 +1,3 @@
1
- import { MaybePromise } from "@pluv/types";
2
-
3
1
  export abstract class AbstractStorageStore {
4
2
  public room: string;
5
3
 
@@ -1,26 +1,31 @@
1
- import type { YjsDoc } from "@pluv/crdt-yjs";
1
+ import type { TrackOriginOptions, YjsDoc } from "@pluv/crdt-yjs";
2
2
  import { doc } from "@pluv/crdt-yjs";
3
3
  import type { AbstractType } from "yjs";
4
4
 
5
- export interface CrdtManagerOptions<
6
- TStorage extends Record<string, AbstractType<any>> = {}
7
- > {
5
+ export type CrdtManagerOptions<
6
+ TStorage extends Record<string, AbstractType<any>> = {},
7
+ > = {
8
8
  encodedState?: string | Uint8Array | null;
9
9
  initialStorage?: () => TStorage;
10
- }
10
+ } & TrackOriginOptions;
11
11
 
12
- interface CrdtManagerInitializeParams {
12
+ type CrdtManagerInitializeParams = {
13
13
  onInitialized?: () => void;
14
14
  origin?: any;
15
15
  update: string | readonly string[];
16
- }
16
+ } & TrackOriginOptions;
17
17
 
18
18
  export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
19
19
  public doc: YjsDoc<TStorage>;
20
20
  public initialized: boolean = false;
21
21
 
22
22
  constructor(options: CrdtManagerOptions<TStorage> = {}) {
23
- const { encodedState, initialStorage } = options;
23
+ const {
24
+ captureTimeout,
25
+ encodedState,
26
+ initialStorage,
27
+ trackedOrigins = [],
28
+ } = options;
24
29
 
25
30
  const _doc = encodedState
26
31
  ? doc<TStorage>().applyUpdate(encodedState)
@@ -28,6 +33,7 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
28
33
 
29
34
  if (_doc && !!Object.keys(_doc.toJSON()).length) {
30
35
  this.doc = _doc;
36
+ this.trackOrigins({ captureTimeout, trackedOrigins });
31
37
 
32
38
  return;
33
39
  }
@@ -35,6 +41,7 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
35
41
  _doc?.destroy();
36
42
 
37
43
  this.doc = doc<TStorage>(initialStorage?.());
44
+ this.trackOrigins({ captureTimeout, trackedOrigins });
38
45
  }
39
46
 
40
47
  /**
@@ -63,10 +70,18 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
63
70
  }
64
71
 
65
72
  public initialize(params: CrdtManagerInitializeParams): this {
66
- const { onInitialized, origin, update } = params;
73
+ const {
74
+ captureTimeout,
75
+ onInitialized,
76
+ origin,
77
+ trackedOrigins,
78
+ update,
79
+ } = params;
67
80
 
68
81
  const updates = typeof update === "string" ? [update] : update;
69
82
 
83
+ this.trackOrigins({ captureTimeout, trackedOrigins });
84
+
70
85
  if (!updates.length) return this;
71
86
 
72
87
  if (this.initialized) {
@@ -87,13 +102,21 @@ export class CrdtManager<TStorage extends Record<string, AbstractType<any>>> {
87
102
 
88
103
  onInitialized?.();
89
104
 
105
+ this.trackOrigins({ captureTimeout, trackedOrigins });
106
+
90
107
  return this;
91
108
  }
92
109
 
110
+ public trackOrigins(options: TrackOriginOptions): void {
111
+ const { captureTimeout, trackedOrigins } = options;
112
+
113
+ this.doc.trackOrigins({ captureTimeout, trackedOrigins });
114
+ }
115
+
93
116
  private _applyDocUpdates(
94
117
  yjsDoc: YjsDoc<TStorage>,
95
118
  updates: readonly string[],
96
- origin?: string
119
+ origin?: string,
97
120
  ): YjsDoc<TStorage> {
98
121
  yjsDoc.transact(() => {
99
122
  updates.reduce((acc, _update) => acc.applyUpdate(_update), yjsDoc);
@@ -3,13 +3,17 @@ import { makeSubject, Subject, subscribe } from "wonka";
3
3
  import type { AbstractType } from "yjs";
4
4
 
5
5
  export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
6
+ public rootSubject = makeSubject<{
7
+ [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
8
+ }>();
9
+
6
10
  public subjects = new Map<
7
11
  keyof TStorage,
8
12
  Subject<InferYjsSharedTypeJson<TStorage[keyof TStorage]>> | null
9
13
  >();
10
14
 
11
15
  public subject<TKey extends keyof TStorage>(
12
- key: TKey
16
+ key: TKey,
13
17
  ): Subject<InferYjsSharedTypeJson<TStorage[TKey]>> {
14
18
  const subject = this.subjects.get(key);
15
19
 
@@ -25,9 +29,17 @@ export class CrdtNotifier<TStorage extends Record<string, AbstractType<any>>> {
25
29
  return newSubject as Subject<InferYjsSharedTypeJson<TStorage[TKey]>>;
26
30
  }
27
31
 
32
+ public subcribeRoot(
33
+ callback: (value: {
34
+ [P in keyof TStorage]: InferYjsSharedTypeJson<TStorage[P]>;
35
+ }) => void,
36
+ ): () => void {
37
+ return subscribe(callback)(this.rootSubject.source).unsubscribe;
38
+ }
39
+
28
40
  public subscribe<TKey extends keyof TStorage>(
29
41
  key: TKey,
30
- callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void
42
+ callback: (value: InferYjsSharedTypeJson<TStorage[TKey]>) => void,
31
43
  ): () => void {
32
44
  const subject = this.subject(key);
33
45
 
@@ -4,7 +4,7 @@ import { makeSubject, subscribe } from "wonka";
4
4
 
5
5
  export type EventNotifierSubscriptionCallback<
6
6
  TIO extends IOLike,
7
- TEvent extends keyof InferIOOutput<TIO>
7
+ TEvent extends keyof InferIOOutput<TIO>,
8
8
  > = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
9
9
 
10
10
  export class EventNotifier<TIO extends IOLike> {
@@ -14,7 +14,7 @@ export class EventNotifier<TIO extends IOLike> {
14
14
  >();
15
15
 
16
16
  public subject<TEvent extends keyof InferIOOutput<TIO>>(
17
- name: TEvent
17
+ name: TEvent,
18
18
  ): Subject<Id<IOEventMessage<TIO, TEvent>>> {
19
19
  const subject = this.subjects.get(name);
20
20
 
@@ -29,7 +29,7 @@ export class EventNotifier<TIO extends IOLike> {
29
29
 
30
30
  public subscribe<TEvent extends keyof InferIOOutput<TIO>>(
31
31
  name: TEvent,
32
- callback: EventNotifierSubscriptionCallback<TIO, TEvent>
32
+ callback: EventNotifierSubscriptionCallback<TIO, TEvent>,
33
33
  ): () => void {
34
34
  const subject = this.subject(name);
35
35