@dabble/patches 0.7.14 → 0.7.16

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.
Files changed (83) hide show
  1. package/README.md +4 -4
  2. package/dist/{BaseDoc-vaMreJL5.d.ts → BaseDoc-BRIP2YZp.d.ts} +16 -35
  3. package/dist/algorithms/ot/server/commitChanges.d.ts +1 -1
  4. package/dist/client/BaseDoc.d.ts +2 -2
  5. package/dist/client/BaseDoc.js +13 -38
  6. package/dist/client/ClientAlgorithm.d.ts +2 -2
  7. package/dist/client/IndexedDBStore.d.ts +2 -2
  8. package/dist/client/IndexedDBStore.js +3 -8
  9. package/dist/client/LWWAlgorithm.d.ts +2 -2
  10. package/dist/client/LWWDoc.d.ts +3 -3
  11. package/dist/client/LWWDoc.js +10 -7
  12. package/dist/client/LWWIndexedDBStore.d.ts +1 -1
  13. package/dist/client/LWWIndexedDBStore.js +4 -4
  14. package/dist/client/OTAlgorithm.d.ts +2 -2
  15. package/dist/client/OTDoc.d.ts +2 -2
  16. package/dist/client/OTDoc.js +7 -8
  17. package/dist/client/OTIndexedDBStore.d.ts +1 -1
  18. package/dist/client/OTIndexedDBStore.js +3 -3
  19. package/dist/client/Patches.d.ts +9 -8
  20. package/dist/client/Patches.js +9 -6
  21. package/dist/client/PatchesDoc.d.ts +2 -2
  22. package/dist/client/PatchesHistoryClient.d.ts +5 -11
  23. package/dist/client/PatchesHistoryClient.js +14 -29
  24. package/dist/client/factories.d.ts +2 -2
  25. package/dist/client/index.d.ts +2 -2
  26. package/dist/index.d.ts +3 -3
  27. package/dist/index.js +23 -2
  28. package/dist/net/PatchesClient.d.ts +3 -3
  29. package/dist/net/PatchesClient.js +1 -1
  30. package/dist/net/PatchesSync.d.ts +14 -29
  31. package/dist/net/PatchesSync.js +84 -98
  32. package/dist/net/http/FetchTransport.d.ts +2 -2
  33. package/dist/net/http/FetchTransport.js +1 -1
  34. package/dist/net/index.d.ts +2 -2
  35. package/dist/net/protocol/JSONRPCClient.d.ts +1 -1
  36. package/dist/net/protocol/JSONRPCClient.js +1 -1
  37. package/dist/net/protocol/JSONRPCServer.d.ts +1 -1
  38. package/dist/net/protocol/JSONRPCServer.js +1 -1
  39. package/dist/net/protocol/types.d.ts +1 -1
  40. package/dist/net/protocol/utils.d.ts +1 -1
  41. package/dist/net/webrtc/WebRTCAwareness.d.ts +2 -2
  42. package/dist/net/webrtc/WebRTCAwareness.js +1 -1
  43. package/dist/net/webrtc/WebRTCTransport.d.ts +5 -5
  44. package/dist/net/webrtc/WebRTCTransport.js +1 -1
  45. package/dist/net/webrtc/index.d.ts +1 -1
  46. package/dist/net/websocket/PatchesWebSocket.d.ts +1 -1
  47. package/dist/net/websocket/PatchesWebSocket.js +1 -1
  48. package/dist/net/websocket/SignalingService.d.ts +1 -1
  49. package/dist/net/websocket/WebSocketServer.d.ts +1 -1
  50. package/dist/net/websocket/WebSocketTransport.d.ts +4 -3
  51. package/dist/net/websocket/WebSocketTransport.js +1 -1
  52. package/dist/net/websocket/onlineState.d.ts +2 -2
  53. package/dist/net/websocket/onlineState.js +1 -1
  54. package/dist/server/LWWBranchManager.d.ts +1 -1
  55. package/dist/server/LWWServer.d.ts +3 -3
  56. package/dist/server/LWWServer.js +1 -1
  57. package/dist/server/OTBranchManager.d.ts +1 -1
  58. package/dist/server/OTServer.d.ts +3 -3
  59. package/dist/server/OTServer.js +1 -1
  60. package/dist/server/PatchesHistoryManager.d.ts +1 -1
  61. package/dist/server/PatchesServer.d.ts +1 -1
  62. package/dist/server/branchUtils.d.ts +1 -1
  63. package/dist/server/index.d.ts +1 -1
  64. package/dist/shared/doc-manager.d.ts +2 -2
  65. package/dist/shared/utils.d.ts +2 -2
  66. package/dist/shared/utils.js +2 -2
  67. package/dist/solid/context.d.ts +2 -2
  68. package/dist/solid/doc-manager.d.ts +2 -2
  69. package/dist/solid/index.d.ts +2 -2
  70. package/dist/solid/managed-docs.js +1 -1
  71. package/dist/solid/primitives.d.ts +2 -2
  72. package/dist/solid/primitives.js +7 -7
  73. package/dist/types.d.ts +4 -4
  74. package/dist/vue/composables.d.ts +2 -2
  75. package/dist/vue/composables.js +7 -7
  76. package/dist/vue/doc-manager.d.ts +2 -2
  77. package/dist/vue/index.d.ts +2 -2
  78. package/dist/vue/managed-docs.d.ts +2 -2
  79. package/dist/vue/managed-docs.js +1 -1
  80. package/dist/vue/provider.d.ts +2 -2
  81. package/package.json +2 -1
  82. package/dist/event-signal.d.ts +0 -32
  83. package/dist/event-signal.js +0 -25
package/README.md CHANGED
@@ -114,7 +114,7 @@ await sync.connect();
114
114
  const doc = await patches.openDoc<MyDoc>('my-doc-1');
115
115
 
116
116
  // 5. React to updates
117
- doc.onUpdate(newState => {
117
+ doc.subscribe(newState => {
118
118
  console.log('Document updated:', newState);
119
119
  // Update your UI here
120
120
  });
@@ -265,7 +265,7 @@ See [Awareness documentation](./docs/awareness.md) for implementation details.
265
265
  1. Create a `Patches` instance with strategies
266
266
  2. Connect `PatchesSync` to your server
267
267
  3. Open documents with `patches.openDoc(docId)`
268
- 4. Subscribe to updates with `doc.onUpdate()`
268
+ 4. Subscribe to updates with `doc.subscribe()`
269
269
  5. Make changes with `doc.change()` - they sync automatically
270
270
 
271
271
  ### Server
@@ -297,7 +297,7 @@ const patches = new Patches({
297
297
  const sync = new PatchesSync(patches, 'wss://api.example.com/sync');
298
298
 
299
299
  // Handle connection state
300
- sync.onStateChange(state => {
300
+ sync.subscribe(state => {
301
301
  if (state.connected) {
302
302
  console.log('Connected and syncing');
303
303
  } else if (!state.online) {
@@ -315,7 +315,7 @@ await sync.connect();
315
315
  // Open and use a document
316
316
  const doc = await patches.openDoc<MyDoc>('doc-123');
317
317
 
318
- doc.onUpdate(state => {
318
+ doc.subscribe(state => {
319
319
  renderUI(state);
320
320
  });
321
321
 
@@ -1,6 +1,7 @@
1
- import { Signal, Unsubscriber } from './event-signal.js';
1
+ import * as easy_signal from 'easy-signal';
2
+ import { ReadonlyStore, Store, Signal, ReadonlyStoreClass } from 'easy-signal';
2
3
  import { JSONPatchOp } from './json-patch/types.js';
3
- import { Change, PatchesSnapshot, SyncedDoc, DocSyncStatus, ChangeMutator } from './types.js';
4
+ import { Change, PatchesSnapshot, DocSyncStatus, ChangeMutator } from './types.js';
4
5
 
5
6
  /**
6
7
  * OT (Operational Transformation) document implementation.
@@ -14,7 +15,7 @@ import { Change, PatchesSnapshot, SyncedDoc, DocSyncStatus, ChangeMutator } from
14
15
  * ## State Model
15
16
  * - `_committedState`: Base state from server (at `_committedRev`)
16
17
  * - `_pendingChanges`: Local changes not yet committed by server
17
- * - `_state` (from BaseDoc): Live state = committedState + pendingChanges applied
18
+ * - `state` (getter from BaseDoc): Live state = committedState + pendingChanges applied
18
19
  *
19
20
  * ## Wire Efficiency
20
21
  * For Worker-Tab communication, only changes are sent over the wire (not full state).
@@ -98,33 +99,25 @@ interface PatchesDocOptions {
98
99
  *
99
100
  * Internal methods (updateSyncStatus, applyChanges, import) are on BaseDoc, not this interface.
100
101
  */
101
- interface PatchesDoc<T extends object = object> extends SyncedDoc {
102
+ interface PatchesDoc<T extends object = object> extends ReadonlyStore<T> {
102
103
  /** The unique identifier for this document. */
103
104
  readonly id: string;
104
- /** Current local state (committed + pending merged). */
105
- readonly state: T;
106
105
  /** Last committed revision number from the server. */
107
106
  readonly committedRev: number;
108
107
  /** Are there local changes that haven't been committed yet? */
109
108
  readonly hasPending: boolean;
110
109
  /** Current sync status of this document. */
111
- readonly syncStatus: DocSyncStatus;
110
+ readonly syncStatus: Store<DocSyncStatus>;
112
111
  /** Error from the last failed sync attempt, if any. */
113
- readonly syncError?: Error;
112
+ readonly syncError: Store<Error | undefined>;
114
113
  /** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
115
- readonly isLoaded: boolean;
114
+ readonly isLoaded: Store<boolean>;
116
115
  /**
117
116
  * Subscribe to be notified when the user makes local changes.
118
117
  * Emits the JSON Patch ops captured from the change() call.
119
118
  * The algorithm handles packaging these into Changes.
120
119
  */
121
120
  readonly onChange: Signal<(ops: JSONPatchOp[]) => void>;
122
- /** Subscribe to be notified whenever state changes from any source. */
123
- readonly onUpdate: Signal<(newState: T) => void>;
124
- /** Subscribe to be notified when sync status changes. */
125
- readonly onSyncStatus: Signal<(newStatus: DocSyncStatus) => void>;
126
- /** Subscribe to be notified whenever the state changes (calls immediately with current state). */
127
- subscribe(onUpdate: (newValue: T) => void): Unsubscriber;
128
121
  /**
129
122
  * Captures an update to the document, emitting JSON Patch ops via onChange.
130
123
  * Does NOT apply locally - the algorithm handles state updates.
@@ -144,22 +137,20 @@ interface PatchesDoc<T extends object = object> extends SyncedDoc {
144
137
  * Internal methods (updateSyncStatus, applyChanges, import) are on this class but not
145
138
  * on the PatchesDoc interface, as they're only used by Algorithm and PatchesSync.
146
139
  */
147
- declare abstract class BaseDoc<T extends object = object> implements PatchesDoc<T> {
140
+ declare abstract class BaseDoc<T extends object = object> extends ReadonlyStoreClass<T> implements PatchesDoc<T> {
148
141
  protected _id: string;
149
- protected _state: T;
150
- protected _syncStatus: DocSyncStatus;
151
- protected _syncError?: Error;
152
- protected _isLoaded: boolean;
142
+ /** Current sync status of this document. */
143
+ readonly syncStatus: Store<DocSyncStatus>;
144
+ /** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
145
+ readonly isLoaded: Store<boolean>;
146
+ /** Error from the last failed sync attempt, if any. */
147
+ readonly syncError: Store<Error | undefined>;
153
148
  /**
154
149
  * Subscribe to be notified when the user makes local changes.
155
150
  * Emits the JSON Patch ops captured from the change() call.
156
151
  * The algorithm handles packaging these into Changes.
157
152
  */
158
- readonly onChange: Signal<(ops: JSONPatchOp[]) => void>;
159
- /** Subscribe to be notified whenever state changes from any source. */
160
- readonly onUpdate: Signal<(newState: T) => void>;
161
- /** Subscribe to be notified when sync status changes. */
162
- readonly onSyncStatus: Signal<(newStatus: DocSyncStatus) => void>;
153
+ readonly onChange: easy_signal.Signal<(ops: JSONPatchOp[]) => void>;
163
154
  /**
164
155
  * Creates an instance of BaseDoc.
165
156
  * @param id The unique identifier for this document.
@@ -168,20 +159,10 @@ declare abstract class BaseDoc<T extends object = object> implements PatchesDoc<
168
159
  constructor(id: string, initialState?: T);
169
160
  /** The unique identifier for this document. */
170
161
  get id(): string;
171
- /** Current local state (committed + pending merged). */
172
- get state(): T;
173
- /** Current sync status of this document. */
174
- get syncStatus(): DocSyncStatus;
175
- /** Error from the last failed sync attempt, if any. */
176
- get syncError(): Error | undefined;
177
- /** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
178
- get isLoaded(): boolean;
179
162
  /** Last committed revision number from the server. */
180
163
  abstract get committedRev(): number;
181
164
  /** Are there local changes that haven't been committed yet? */
182
165
  abstract get hasPending(): boolean;
183
- /** Subscribe to be notified whenever the state changes (calls immediately with current state). */
184
- subscribe(onUpdate: (newValue: T) => void): Unsubscriber;
185
166
  /**
186
167
  * Captures an update to the document, emitting JSON Patch ops via onChange.
187
168
  * Does NOT apply locally - the algorithm handles state updates via applyChanges.
@@ -2,7 +2,7 @@ import { CommitResult } from '../../../server/PatchesServer.js';
2
2
  import { OTStoreBackend } from '../../../server/types.js';
3
3
  import { ChangeInput, CommitChangesOptions } from '../../../types.js';
4
4
  import '../../../net/protocol/JSONRPCServer.js';
5
- import '../../../event-signal.js';
5
+ import 'easy-signal';
6
6
  import '../../../net/websocket/AuthorizationProvider.js';
7
7
  import '../../../json-patch/types.js';
8
8
  import '../../../json-patch/JSONPatch.js';
@@ -1,6 +1,6 @@
1
- import '../event-signal.js';
1
+ import 'easy-signal';
2
2
  import '../json-patch/types.js';
3
3
  import '../types.js';
4
- export { B as BaseDoc } from '../BaseDoc-vaMreJL5.js';
4
+ export { B as BaseDoc } from '../BaseDoc-BRIP2YZp.js';
5
5
  import '../json-patch/JSONPatch.js';
6
6
  import '@dabble/delta';
@@ -1,58 +1,34 @@
1
1
  import "../chunk-IZ2YBCUP.js";
2
- import { signal } from "../event-signal.js";
2
+ import { ReadonlyStoreClass, signal, store } from "easy-signal";
3
3
  import { createJSONPatch } from "../json-patch/createJSONPatch.js";
4
4
  import { isDocLoaded } from "../shared/utils.js";
5
- class BaseDoc {
5
+ class BaseDoc extends ReadonlyStoreClass {
6
6
  _id;
7
- _state;
8
- _syncStatus = "unsynced";
9
- _syncError;
10
- _isLoaded = false;
7
+ /** Current sync status of this document. */
8
+ syncStatus = store("unsynced");
9
+ /** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
10
+ isLoaded = store(false);
11
+ /** Error from the last failed sync attempt, if any. */
12
+ syncError = store(void 0);
11
13
  /**
12
14
  * Subscribe to be notified when the user makes local changes.
13
15
  * Emits the JSON Patch ops captured from the change() call.
14
16
  * The algorithm handles packaging these into Changes.
15
17
  */
16
18
  onChange = signal();
17
- /** Subscribe to be notified whenever state changes from any source. */
18
- onUpdate = signal();
19
- /** Subscribe to be notified when sync status changes. */
20
- onSyncStatus = signal();
21
19
  /**
22
20
  * Creates an instance of BaseDoc.
23
21
  * @param id The unique identifier for this document.
24
22
  * @param initialState Optional initial state.
25
23
  */
26
24
  constructor(id, initialState = {}) {
25
+ super(initialState);
27
26
  this._id = id;
28
- this._state = initialState;
29
27
  }
30
28
  /** The unique identifier for this document. */
31
29
  get id() {
32
30
  return this._id;
33
31
  }
34
- /** Current local state (committed + pending merged). */
35
- get state() {
36
- return this._state;
37
- }
38
- /** Current sync status of this document. */
39
- get syncStatus() {
40
- return this._syncStatus;
41
- }
42
- /** Error from the last failed sync attempt, if any. */
43
- get syncError() {
44
- return this._syncError;
45
- }
46
- /** Whether the document has completed its initial load. Sticky: once true, never reverts to false. */
47
- get isLoaded() {
48
- return this._isLoaded;
49
- }
50
- /** Subscribe to be notified whenever the state changes (calls immediately with current state). */
51
- subscribe(onUpdate) {
52
- const unsub = this.onUpdate(onUpdate);
53
- onUpdate(this._state);
54
- return unsub;
55
- }
56
32
  /**
57
33
  * Captures an update to the document, emitting JSON Patch ops via onChange.
58
34
  * Does NOT apply locally - the algorithm handles state updates via applyChanges.
@@ -73,15 +49,14 @@ class BaseDoc {
73
49
  * @param error Optional error when status is 'error'.
74
50
  */
75
51
  updateSyncStatus(status, error) {
76
- this._syncStatus = status;
77
- this._syncError = status === "error" ? error : void 0;
52
+ this.syncError.state = status === "error" ? error : void 0;
53
+ this.syncStatus.state = status;
78
54
  this._checkLoaded();
79
- this.onSyncStatus.emit(status);
80
55
  }
81
56
  /** Latches _isLoaded to true when the doc has data or sync has resolved. */
82
57
  _checkLoaded() {
83
- if (!this._isLoaded && isDocLoaded(this)) {
84
- this._isLoaded = true;
58
+ if (!this.isLoaded.state && isDocLoaded(this.committedRev, this.hasPending, this.syncStatus.state)) {
59
+ this.isLoaded.state = true;
85
60
  }
86
61
  }
87
62
  }
@@ -1,10 +1,10 @@
1
1
  import { JSONPatchOp } from '../json-patch/types.js';
2
2
  import { PatchesSnapshot, Change } from '../types.js';
3
- import { a as PatchesDoc } from '../BaseDoc-vaMreJL5.js';
3
+ import { a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
4
4
  import { PatchesStore, TrackedDoc } from './PatchesStore.js';
5
5
  import '../json-patch/JSONPatch.js';
6
6
  import '@dabble/delta';
7
- import '../event-signal.js';
7
+ import 'easy-signal';
8
8
 
9
9
  /**
10
10
  * Algorithm interface for client-side sync algorithms (OT or LWW).
@@ -1,4 +1,4 @@
1
- import { Signal } from '../event-signal.js';
1
+ import * as easy_signal from 'easy-signal';
2
2
  import { PatchesSnapshot, PatchesState } from '../types.js';
3
3
  import { Deferred } from '../utils/deferred.js';
4
4
  import { PatchesStore, TrackedDoc } from './PatchesStore.js';
@@ -29,7 +29,7 @@ declare class IndexedDBStore implements PatchesStore {
29
29
  * Signal emitted during database upgrade, allowing algorithm-specific stores
30
30
  * to create their object stores.
31
31
  */
32
- readonly onUpgrade: Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
32
+ readonly onUpgrade: easy_signal.Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
33
33
  constructor(dbName?: string);
34
34
  /**
35
35
  * Creates shared object stores used by all sync algorithms.
@@ -1,6 +1,6 @@
1
1
  import "../chunk-IZ2YBCUP.js";
2
2
  import { deferred } from "../utils/deferred.js";
3
- import { signal } from "../event-signal.js";
3
+ import { signal } from "easy-signal";
4
4
  class IndexedDBStore {
5
5
  static DB_VERSION = 1;
6
6
  db = null;
@@ -137,13 +137,8 @@ class IndexedDBStore {
137
137
  async listDocs(includeDeleted = false, algorithm) {
138
138
  const [tx, docsStore] = await this.transaction(["docs"], "readonly");
139
139
  let docs;
140
- if (algorithm === "lww") {
141
- docs = await docsStore.getAllByIndex("algorithm", "lww");
142
- } else if (algorithm === "ot") {
143
- const otDocs = await docsStore.getAllByIndex("algorithm", "ot");
144
- const allDocs = await docsStore.getAll();
145
- const noAlgoDocs = allDocs.filter((doc) => !doc.algorithm);
146
- docs = [...otDocs, ...noAlgoDocs];
140
+ if (algorithm) {
141
+ docs = await docsStore.getAllByIndex("algorithm", algorithm);
147
142
  } else {
148
143
  docs = await docsStore.getAll();
149
144
  }
@@ -2,11 +2,11 @@ import { JSONPatchOp } from '../json-patch/types.js';
2
2
  import { PatchesSnapshot, Change } from '../types.js';
3
3
  import { ClientAlgorithm } from './ClientAlgorithm.js';
4
4
  import { LWWClientStore } from './LWWClientStore.js';
5
- import { a as PatchesDoc } from '../BaseDoc-vaMreJL5.js';
5
+ import { a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
6
6
  import { TrackedDoc } from './PatchesStore.js';
7
7
  import '../json-patch/JSONPatch.js';
8
8
  import '@dabble/delta';
9
- import '../event-signal.js';
9
+ import 'easy-signal';
10
10
 
11
11
  /**
12
12
  * LWW (Last-Write-Wins) algorithm implementation.
@@ -1,9 +1,9 @@
1
1
  import { PatchesSnapshot, Change } from '../types.js';
2
- import { B as BaseDoc } from '../BaseDoc-vaMreJL5.js';
2
+ import { B as BaseDoc } from '../BaseDoc-BRIP2YZp.js';
3
3
  import '../json-patch/JSONPatch.js';
4
4
  import '@dabble/delta';
5
5
  import '../json-patch/types.js';
6
- import '../event-signal.js';
6
+ import 'easy-signal';
7
7
 
8
8
  /**
9
9
  * LWW (Last-Write-Wins) document implementation.
@@ -41,7 +41,7 @@ declare class LWWDoc<T extends object = object> extends BaseDoc<T> {
41
41
  import(snapshot: PatchesSnapshot<T>): void;
42
42
  /**
43
43
  * Applies changes to the document state.
44
- * Used for WorkerTab communication where only changes are sent over the wire.
44
+ * Used for Worker-Tab communication where only changes are sent over the wire.
45
45
  *
46
46
  * For LWW, all ops are applied in order. The method distinguishes between
47
47
  * committed and pending changes using `committedAt`:
@@ -15,11 +15,13 @@ class LWWDoc extends BaseDoc {
15
15
  this._committedRev = snapshot?.rev ?? 0;
16
16
  this._hasPending = (snapshot?.changes?.length ?? 0) > 0;
17
17
  if (snapshot?.changes && snapshot.changes.length > 0) {
18
+ let currentState = this.state;
18
19
  for (const change of snapshot.changes) {
19
20
  for (const op of change.ops) {
20
- this._state = applyPatch(this._state, [op], { partial: true });
21
+ currentState = applyPatch(currentState, [op], { partial: true });
21
22
  }
22
23
  }
24
+ this.state = currentState;
23
25
  }
24
26
  this._checkLoaded();
25
27
  }
@@ -36,22 +38,22 @@ class LWWDoc extends BaseDoc {
36
38
  * Resets state completely from the snapshot.
37
39
  */
38
40
  import(snapshot) {
39
- this._state = snapshot.state;
40
41
  this._committedRev = snapshot.rev;
41
42
  this._hasPending = (snapshot.changes?.length ?? 0) > 0;
43
+ let currentState = snapshot.state;
42
44
  if (snapshot.changes && snapshot.changes.length > 0) {
43
45
  for (const change of snapshot.changes) {
44
46
  for (const op of change.ops) {
45
- this._state = applyPatch(this._state, [op], { partial: true });
47
+ currentState = applyPatch(currentState, [op], { partial: true });
46
48
  }
47
49
  }
48
50
  }
49
51
  this._checkLoaded();
50
- this.onUpdate.emit(this._state);
52
+ this.state = currentState;
51
53
  }
52
54
  /**
53
55
  * Applies changes to the document state.
54
- * Used for WorkerTab communication where only changes are sent over the wire.
56
+ * Used for Worker-Tab communication where only changes are sent over the wire.
55
57
  *
56
58
  * For LWW, all ops are applied in order. The method distinguishes between
57
59
  * committed and pending changes using `committedAt`:
@@ -64,9 +66,10 @@ class LWWDoc extends BaseDoc {
64
66
  */
65
67
  applyChanges(changes, hasPending) {
66
68
  if (changes.length === 0) return;
69
+ let currentState = this.state;
67
70
  for (const change of changes) {
68
71
  for (const op of change.ops) {
69
- this._state = applyPatch(this._state, [op], { partial: true });
72
+ currentState = applyPatch(currentState, [op], { partial: true });
70
73
  }
71
74
  }
72
75
  let lastCommittedRev = this._committedRev;
@@ -81,7 +84,7 @@ class LWWDoc extends BaseDoc {
81
84
  this._committedRev = lastCommittedRev;
82
85
  this._hasPending = hasPending ?? hasPendingChanges;
83
86
  this._checkLoaded();
84
- this.onUpdate.emit(this._state);
87
+ this.state = currentState;
85
88
  }
86
89
  }
87
90
  export {
@@ -5,7 +5,7 @@ import { LWWClientStore } from './LWWClientStore.js';
5
5
  import { TrackedDoc } from './PatchesStore.js';
6
6
  import '../json-patch/JSONPatch.js';
7
7
  import '@dabble/delta';
8
- import '../event-signal.js';
8
+ import 'easy-signal';
9
9
  import '../utils/deferred.js';
10
10
 
11
11
  /**
@@ -128,7 +128,7 @@ class LWWIndexedDBStore {
128
128
  );
129
129
  const { rev, state } = docState;
130
130
  await Promise.all([
131
- docsStore.put({ docId, committedRev: rev }),
131
+ docsStore.put({ docId, committedRev: rev, algorithm: "lww" }),
132
132
  snapshots.put({ docId, state, rev }),
133
133
  this.deleteFieldsForDoc(committedOps, docId),
134
134
  this.deleteFieldsForDoc(pendingOps, docId),
@@ -141,7 +141,7 @@ class LWWIndexedDBStore {
141
141
  ["snapshots", "committedOps", "pendingOps", "sendingChanges", "docs"],
142
142
  "readwrite"
143
143
  );
144
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
144
+ const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
145
145
  await docsStore.put({ ...docMeta, deleted: true });
146
146
  await Promise.all([
147
147
  snapshots.delete(docId),
@@ -243,7 +243,7 @@ class LWWIndexedDBStore {
243
243
  return;
244
244
  }
245
245
  await Promise.all(sending.change.ops.map((op) => committedOps.put({ ...op, docId })));
246
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
246
+ const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
247
247
  if (sending.change.rev > docMeta.committedRev) {
248
248
  await docsStore.put({ ...docMeta, committedRev: sending.change.rev });
249
249
  }
@@ -259,7 +259,7 @@ class LWWIndexedDBStore {
259
259
  await Promise.all(allOps.map((op) => committedOps.put({ ...op, docId })));
260
260
  const lastCommittedRev = serverChanges.at(-1)?.rev;
261
261
  if (lastCommittedRev !== void 0) {
262
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
262
+ const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "lww" };
263
263
  if (lastCommittedRev > docMeta.committedRev) {
264
264
  await docsStore.put({ ...docMeta, committedRev: lastCommittedRev });
265
265
  }
@@ -2,11 +2,11 @@ import { JSONPatchOp } from '../json-patch/types.js';
2
2
  import { PatchesSnapshot, Change } from '../types.js';
3
3
  import { ClientAlgorithm } from './ClientAlgorithm.js';
4
4
  import { OTClientStore } from './OTClientStore.js';
5
- import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-vaMreJL5.js';
5
+ import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
6
6
  import { TrackedDoc } from './PatchesStore.js';
7
7
  import '../json-patch/JSONPatch.js';
8
8
  import '@dabble/delta';
9
- import '../event-signal.js';
9
+ import 'easy-signal';
10
10
 
11
11
  /**
12
12
  * OT (Operational Transformation) algorithm implementation.
@@ -1,6 +1,6 @@
1
1
  import '../types.js';
2
- export { O as OTDoc } from '../BaseDoc-vaMreJL5.js';
2
+ export { O as OTDoc } from '../BaseDoc-BRIP2YZp.js';
3
3
  import '../json-patch/JSONPatch.js';
4
4
  import '@dabble/delta';
5
5
  import '../json-patch/types.js';
6
- import '../event-signal.js';
6
+ import 'easy-signal';
@@ -17,11 +17,11 @@ class OTDoc extends BaseDoc {
17
17
  constructor(id, snapshot) {
18
18
  const initialState = snapshot?.state ?? {};
19
19
  super(id, initialState);
20
- this._committedState = this._state;
20
+ this._committedState = this.state;
21
21
  this._committedRev = snapshot?.rev ?? 0;
22
22
  this._pendingChanges = snapshot?.changes ?? [];
23
23
  if (this._pendingChanges.length > 0) {
24
- this._state = applyChangesToState(this._committedState, this._pendingChanges);
24
+ this.state = applyChangesToState(this._committedState, this._pendingChanges);
25
25
  }
26
26
  this._checkLoaded();
27
27
  }
@@ -48,9 +48,8 @@ class OTDoc extends BaseDoc {
48
48
  this._committedState = snapshot.state;
49
49
  this._committedRev = snapshot.rev;
50
50
  this._pendingChanges = snapshot.changes;
51
- this._state = createStateFromSnapshot(snapshot);
52
51
  this._checkLoaded();
53
- this.onUpdate.emit(this._state);
52
+ this.state = createStateFromSnapshot(snapshot);
54
53
  }
55
54
  /**
56
55
  * Unified entry point for applying changes.
@@ -76,13 +75,13 @@ class OTDoc extends BaseDoc {
76
75
  this._committedState = applyChangesToState(this._committedState, serverChanges);
77
76
  this._committedRev = serverChanges[serverChanges.length - 1].rev;
78
77
  this._pendingChanges = rebasedPending;
79
- this._state = applyChangesToState(this._committedState, this._pendingChanges);
78
+ this._checkLoaded();
79
+ this.state = applyChangesToState(this._committedState, this._pendingChanges);
80
80
  } else {
81
- this._state = applyChangesToState(this._state, changes);
82
81
  this._pendingChanges.push(...changes);
82
+ this._checkLoaded();
83
+ this.state = applyChangesToState(this.state, changes);
83
84
  }
84
- this._checkLoaded();
85
- this.onUpdate.emit(this._state);
86
85
  }
87
86
  /**
88
87
  * Returns the document snapshot for serialization.
@@ -5,7 +5,7 @@ import { TrackedDoc } from './PatchesStore.js';
5
5
  import '../json-patch/JSONPatch.js';
6
6
  import '@dabble/delta';
7
7
  import '../json-patch/types.js';
8
- import '../event-signal.js';
8
+ import 'easy-signal';
9
9
  import '../utils/deferred.js';
10
10
 
11
11
  /**
@@ -101,7 +101,7 @@ class OTIndexedDBStore {
101
101
  ["snapshots", "committedChanges", "pendingChanges", "docs"],
102
102
  "readwrite"
103
103
  );
104
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
104
+ const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "ot" };
105
105
  await docsStore.put({ ...docMeta, deleted: true });
106
106
  await Promise.all([
107
107
  snapshots.delete(docId),
@@ -117,7 +117,7 @@ class OTIndexedDBStore {
117
117
  );
118
118
  const { rev, state } = docState;
119
119
  await Promise.all([
120
- docsStore.put({ docId, committedRev: rev }),
120
+ docsStore.put({ docId, committedRev: rev, algorithm: "ot" }),
121
121
  snapshots.put({ docId, state, rev }),
122
122
  committedChanges.delete([docId, 0], [docId, Infinity]),
123
123
  pendingChanges.delete([docId, 0], [docId, Infinity])
@@ -169,7 +169,7 @@ class OTIndexedDBStore {
169
169
  }
170
170
  const lastCommittedRev = serverChanges.at(-1)?.rev;
171
171
  if (lastCommittedRev !== void 0) {
172
- const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
172
+ const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0, algorithm: "ot" };
173
173
  if (lastCommittedRev > docMeta.committedRev) {
174
174
  await docsStore.put({ ...docMeta, committedRev: lastCommittedRev, deleted: void 0 });
175
175
  }
@@ -1,8 +1,9 @@
1
- import { Unsubscriber, Signal } from '../event-signal.js';
1
+ import * as easy_signal from 'easy-signal';
2
+ import { Unsubscriber } from 'easy-signal';
2
3
  import { JSONPatchOp } from '../json-patch/types.js';
3
4
  import { Change } from '../types.js';
4
5
  import { ClientAlgorithm } from './ClientAlgorithm.js';
5
- import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-vaMreJL5.js';
6
+ import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-BRIP2YZp.js';
6
7
  import { AlgorithmName } from './PatchesStore.js';
7
8
  import '../json-patch/JSONPatch.js';
8
9
  import '@dabble/delta';
@@ -49,15 +50,15 @@ declare class Patches {
49
50
  readonly algorithms: Partial<Record<AlgorithmName, ClientAlgorithm>>;
50
51
  readonly defaultAlgorithm: AlgorithmName;
51
52
  readonly trackedDocs: Set<string>;
52
- readonly onError: Signal<(error: Error, context?: {
53
+ readonly onError: easy_signal.Signal<(error: Error, context?: {
53
54
  docId?: string;
54
55
  }) => void>;
55
- readonly onServerCommit: Signal<(docId: string, changes: Change[]) => void>;
56
- readonly onTrackDocs: Signal<(docIds: string[]) => void>;
57
- readonly onUntrackDocs: Signal<(docIds: string[]) => void>;
58
- readonly onDeleteDoc: Signal<(docId: string) => void>;
56
+ readonly onServerCommit: easy_signal.Signal<(docId: string, changes: Change[]) => void>;
57
+ readonly onTrackDocs: easy_signal.Signal<(docIds: string[], algorithmName: AlgorithmName) => void>;
58
+ readonly onUntrackDocs: easy_signal.Signal<(docIds: string[]) => void>;
59
+ readonly onDeleteDoc: easy_signal.Signal<(docId: string) => void>;
59
60
  /** Emitted when a doc has pending changes ready to send */
60
- readonly onChange: Signal<(docId: string) => void>;
61
+ readonly onChange: easy_signal.Signal<(docId: string) => void>;
61
62
  constructor(opts: PatchesOptions);
62
63
  /**
63
64
  * Loads tracked docs from all registered algorithm stores.
@@ -6,7 +6,7 @@ import {
6
6
  __runInitializers
7
7
  } from "../chunk-IZ2YBCUP.js";
8
8
  var _openDoc_dec, _init;
9
- import { signal } from "../event-signal.js";
9
+ import { signal } from "easy-signal";
10
10
  import { singleInvocation } from "../utils/concurrency.js";
11
11
  _openDoc_dec = [singleInvocation(true)];
12
12
  class Patches {
@@ -44,11 +44,14 @@ class Patches {
44
44
  * Extracted as a protected method so subclasses can override initialization behavior.
45
45
  */
46
46
  init() {
47
- const algorithms = Object.values(this.algorithms).filter(Boolean);
47
+ const entries = Object.entries(this.algorithms).filter(([, a]) => Boolean(a));
48
48
  Promise.all(
49
- algorithms.map(
50
- (algorithm) => algorithm.listDocs().then((docs) => {
51
- this.trackDocs(docs.map(({ docId }) => docId));
49
+ entries.map(
50
+ ([name, algorithm]) => algorithm.listDocs().then((docs) => {
51
+ this.trackDocs(
52
+ docs.map(({ docId }) => docId),
53
+ name
54
+ );
52
55
  })
53
56
  )
54
57
  ).catch((err) => {
@@ -83,7 +86,7 @@ class Patches {
83
86
  docIds = docIds.filter((id) => !this.trackedDocs.has(id));
84
87
  if (!docIds.length) return;
85
88
  docIds.forEach(this.trackedDocs.add, this.trackedDocs);
86
- this.onTrackDocs.emit(docIds);
89
+ this.onTrackDocs.emit(docIds, algorithmName ?? this.defaultAlgorithm);
87
90
  const algorithm = this._getAlgorithm(algorithmName ?? this.defaultAlgorithm);
88
91
  await algorithm.trackDocs(docIds);
89
92
  }
@@ -1,6 +1,6 @@
1
- import '../event-signal.js';
1
+ import 'easy-signal';
2
2
  import '../json-patch/types.js';
3
3
  import '../types.js';
4
- export { O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from '../BaseDoc-vaMreJL5.js';
4
+ export { O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from '../BaseDoc-BRIP2YZp.js';
5
5
  import '../json-patch/JSONPatch.js';
6
6
  import '@dabble/delta';