@dabble/patches 0.4.5 → 0.4.7

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 (181) hide show
  1. package/dist/algorithms/client/applyCommittedChanges.d.ts +8 -2
  2. package/dist/algorithms/client/applyCommittedChanges.js +30 -38
  3. package/dist/algorithms/client/batching.d.ts +8 -2
  4. package/dist/algorithms/client/batching.js +38 -37
  5. package/dist/algorithms/client/breakChange.d.ts +8 -2
  6. package/dist/algorithms/client/breakChange.js +191 -240
  7. package/dist/algorithms/client/createStateFromSnapshot.d.ts +8 -2
  8. package/dist/algorithms/client/createStateFromSnapshot.js +7 -8
  9. package/dist/algorithms/client/getJSONByteSize.d.ts +3 -1
  10. package/dist/algorithms/client/getJSONByteSize.js +12 -11
  11. package/dist/algorithms/client/makeChange.d.ts +8 -2
  12. package/dist/algorithms/client/makeChange.js +28 -36
  13. package/dist/algorithms/server/commitChanges.d.ts +9 -3
  14. package/dist/algorithms/server/commitChanges.js +69 -78
  15. package/dist/algorithms/server/createVersion.d.ts +9 -3
  16. package/dist/algorithms/server/createVersion.js +21 -27
  17. package/dist/algorithms/server/getSnapshotAtRevision.d.ts +9 -3
  18. package/dist/algorithms/server/getSnapshotAtRevision.js +27 -28
  19. package/dist/algorithms/server/getStateAtRevision.d.ts +9 -3
  20. package/dist/algorithms/server/getStateAtRevision.js +13 -17
  21. package/dist/algorithms/server/handleOfflineSessionsAndBatches.d.ts +9 -3
  22. package/dist/algorithms/server/handleOfflineSessionsAndBatches.js +60 -77
  23. package/dist/algorithms/server/transformIncomingChanges.d.ts +8 -2
  24. package/dist/algorithms/server/transformIncomingChanges.js +27 -39
  25. package/dist/algorithms/shared/applyChanges.d.ts +8 -2
  26. package/dist/algorithms/shared/applyChanges.js +11 -16
  27. package/dist/algorithms/shared/rebaseChanges.d.ts +8 -2
  28. package/dist/algorithms/shared/rebaseChanges.js +30 -49
  29. package/dist/chunk-IZ2YBCUP.js +56 -0
  30. package/dist/client/InMemoryStore.d.ts +9 -3
  31. package/dist/client/InMemoryStore.js +92 -101
  32. package/dist/client/IndexedDBStore.d.ts +9 -3
  33. package/dist/client/IndexedDBStore.js +378 -491
  34. package/dist/client/Patches.d.ts +18 -13
  35. package/dist/client/Patches.js +152 -207
  36. package/dist/client/PatchesDoc.d.ts +14 -8
  37. package/dist/client/PatchesDoc.js +147 -154
  38. package/dist/client/PatchesHistoryClient.d.ts +12 -5
  39. package/dist/client/PatchesHistoryClient.js +110 -117
  40. package/dist/client/PatchesStore.d.ts +9 -3
  41. package/dist/client/PatchesStore.js +0 -1
  42. package/dist/client/index.d.ts +12 -6
  43. package/dist/client/index.js +5 -5
  44. package/dist/data/change.d.ts +9 -3
  45. package/dist/data/change.js +23 -15
  46. package/dist/data/version.d.ts +9 -3
  47. package/dist/data/version.js +11 -15
  48. package/dist/event-signal.d.ts +7 -6
  49. package/dist/event-signal.js +24 -39
  50. package/dist/index-CvQws3AB.d.ts +36 -0
  51. package/dist/index.d.ts +27 -5
  52. package/dist/index.js +10 -4
  53. package/dist/json-patch/JSONPatch.d.ts +9 -5
  54. package/dist/json-patch/JSONPatch.js +175 -183
  55. package/dist/json-patch/applyPatch.d.ts +5 -2
  56. package/dist/json-patch/applyPatch.js +27 -35
  57. package/dist/json-patch/composePatch.d.ts +5 -2
  58. package/dist/json-patch/composePatch.js +34 -34
  59. package/dist/json-patch/createJSONPatch.d.ts +7 -2
  60. package/dist/json-patch/createJSONPatch.js +11 -38
  61. package/dist/json-patch/index.d.ts +14 -6
  62. package/dist/json-patch/index.js +20 -9
  63. package/dist/json-patch/invertPatch.d.ts +5 -2
  64. package/dist/json-patch/invertPatch.js +31 -30
  65. package/dist/json-patch/ops/add.d.ts +5 -2
  66. package/dist/json-patch/ops/add.js +53 -51
  67. package/dist/json-patch/ops/bitmask.d.ts +8 -5
  68. package/dist/json-patch/ops/bitmask.js +41 -44
  69. package/dist/json-patch/ops/copy.d.ts +5 -2
  70. package/dist/json-patch/ops/copy.js +32 -33
  71. package/dist/json-patch/ops/increment.d.ts +5 -2
  72. package/dist/json-patch/ops/increment.js +21 -20
  73. package/dist/json-patch/ops/index.d.ts +10 -21
  74. package/dist/json-patch/ops/index.js +34 -24
  75. package/dist/json-patch/ops/move.d.ts +5 -2
  76. package/dist/json-patch/ops/move.js +132 -198
  77. package/dist/json-patch/ops/remove.d.ts +5 -2
  78. package/dist/json-patch/ops/remove.js +33 -30
  79. package/dist/json-patch/ops/replace.d.ts +5 -2
  80. package/dist/json-patch/ops/replace.js +45 -43
  81. package/dist/json-patch/ops/test.d.ts +5 -2
  82. package/dist/json-patch/ops/test.js +25 -21
  83. package/dist/json-patch/ops/text.d.ts +5 -2
  84. package/dist/json-patch/ops/text.js +54 -54
  85. package/dist/json-patch/pathProxy.d.ts +9 -3
  86. package/dist/json-patch/pathProxy.js +27 -48
  87. package/dist/json-patch/state.d.ts +5 -2
  88. package/dist/json-patch/state.js +11 -7
  89. package/dist/json-patch/transformPatch.d.ts +6 -2
  90. package/dist/json-patch/transformPatch.js +21 -24
  91. package/dist/json-patch/types.d.ts +9 -7
  92. package/dist/json-patch/types.js +0 -1
  93. package/dist/json-patch/utils/deepEqual.d.ts +3 -1
  94. package/dist/json-patch/utils/deepEqual.js +32 -28
  95. package/dist/json-patch/utils/exit.d.ts +5 -2
  96. package/dist/json-patch/utils/exit.js +7 -3
  97. package/dist/json-patch/utils/get.d.ts +5 -2
  98. package/dist/json-patch/utils/get.js +8 -4
  99. package/dist/json-patch/utils/getOpData.d.ts +5 -2
  100. package/dist/json-patch/utils/getOpData.js +12 -9
  101. package/dist/json-patch/utils/getType.d.ts +6 -3
  102. package/dist/json-patch/utils/getType.js +9 -4
  103. package/dist/json-patch/utils/index.d.ts +15 -14
  104. package/dist/json-patch/utils/index.js +14 -14
  105. package/dist/json-patch/utils/log.d.ts +4 -5
  106. package/dist/json-patch/utils/log.js +8 -3
  107. package/dist/json-patch/utils/ops.d.ts +8 -5
  108. package/dist/json-patch/utils/ops.js +83 -100
  109. package/dist/json-patch/utils/paths.d.ts +12 -9
  110. package/dist/json-patch/utils/paths.js +54 -51
  111. package/dist/json-patch/utils/pluck.d.ts +8 -5
  112. package/dist/json-patch/utils/pluck.js +32 -26
  113. package/dist/json-patch/utils/shallowCopy.d.ts +3 -1
  114. package/dist/json-patch/utils/shallowCopy.js +22 -18
  115. package/dist/json-patch/utils/softWrites.d.ts +6 -3
  116. package/dist/json-patch/utils/softWrites.js +17 -16
  117. package/dist/json-patch/utils/toArrayIndex.d.ts +3 -1
  118. package/dist/json-patch/utils/toArrayIndex.js +14 -10
  119. package/dist/json-patch/utils/toKeys.d.ts +3 -1
  120. package/dist/json-patch/utils/toKeys.js +15 -11
  121. package/dist/json-patch/utils/updateArrayIndexes.d.ts +5 -2
  122. package/dist/json-patch/utils/updateArrayIndexes.js +33 -37
  123. package/dist/json-patch/utils/updateArrayPath.d.ts +5 -2
  124. package/dist/json-patch/utils/updateArrayPath.js +29 -42
  125. package/dist/net/PatchesClient.d.ts +128 -0
  126. package/dist/net/PatchesClient.js +161 -0
  127. package/dist/net/PatchesSync.d.ts +19 -9
  128. package/dist/net/PatchesSync.js +291 -386
  129. package/dist/net/error.d.ts +3 -1
  130. package/dist/net/error.js +9 -6
  131. package/dist/net/http/FetchTransport.d.ts +21 -0
  132. package/dist/net/http/FetchTransport.js +34 -0
  133. package/dist/net/index.d.ts +26 -12
  134. package/dist/net/index.js +12 -10
  135. package/dist/net/protocol/JSONRPCClient.d.ts +11 -4
  136. package/dist/net/protocol/JSONRPCClient.js +95 -103
  137. package/dist/net/protocol/JSONRPCServer.d.ts +15 -8
  138. package/dist/net/protocol/JSONRPCServer.js +101 -123
  139. package/dist/net/protocol/types.d.ts +21 -15
  140. package/dist/net/protocol/types.js +0 -1
  141. package/dist/net/protocol/utils.d.ts +12 -0
  142. package/dist/net/protocol/utils.js +15 -0
  143. package/dist/net/types.d.ts +4 -2
  144. package/dist/net/types.js +0 -1
  145. package/dist/net/webrtc/WebRTCAwareness.d.ts +14 -4
  146. package/dist/net/webrtc/WebRTCAwareness.js +111 -120
  147. package/dist/net/webrtc/WebRTCTransport.d.ts +16 -8
  148. package/dist/net/webrtc/WebRTCTransport.js +149 -157
  149. package/dist/net/webrtc/index.d.ts +10 -2
  150. package/dist/net/webrtc/index.js +2 -2
  151. package/dist/net/websocket/AuthorizationProvider.d.ts +7 -5
  152. package/dist/net/websocket/AuthorizationProvider.js +12 -17
  153. package/dist/net/websocket/PatchesWebSocket.d.ts +14 -109
  154. package/dist/net/websocket/PatchesWebSocket.js +37 -184
  155. package/dist/net/websocket/RPCServer.d.ts +19 -10
  156. package/dist/net/websocket/RPCServer.js +190 -192
  157. package/dist/net/websocket/SignalingService.d.ts +12 -32
  158. package/dist/net/websocket/SignalingService.js +126 -133
  159. package/dist/net/websocket/WebSocketServer.d.ts +17 -4
  160. package/dist/net/websocket/WebSocketServer.js +64 -72
  161. package/dist/net/websocket/WebSocketTransport.d.ts +13 -5
  162. package/dist/net/websocket/WebSocketTransport.js +178 -207
  163. package/dist/net/websocket/onlineState.d.ts +6 -3
  164. package/dist/net/websocket/onlineState.js +25 -21
  165. package/dist/server/PatchesBranchManager.d.ts +12 -5
  166. package/dist/server/PatchesBranchManager.js +132 -142
  167. package/dist/server/PatchesHistoryManager.d.ts +11 -3
  168. package/dist/server/PatchesHistoryManager.js +81 -84
  169. package/dist/server/PatchesServer.d.ts +16 -10
  170. package/dist/server/PatchesServer.js +131 -137
  171. package/dist/server/index.d.ts +7 -2
  172. package/dist/server/index.js +9 -3
  173. package/dist/server/types.d.ts +9 -3
  174. package/dist/server/types.js +0 -1
  175. package/dist/types.d.ts +38 -19
  176. package/dist/types.js +1 -1
  177. package/dist/utils/concurrency.d.ts +7 -5
  178. package/dist/utils/concurrency.js +43 -53
  179. package/dist/utils/deferred.d.ts +4 -2
  180. package/dist/utils/deferred.js +25 -21
  181. package/package.json +13 -15
@@ -1,393 +1,298 @@
1
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
2
- var useValue = arguments.length > 2;
3
- for (var i = 0; i < initializers.length; i++) {
4
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
1
+ import {
2
+ __decorateElement,
3
+ __decoratorMetadata,
4
+ __decoratorStart,
5
+ __publicField,
6
+ __runInitializers
7
+ } from "../chunk-IZ2YBCUP.js";
8
+ var __receiveCommittedChanges_dec, _syncDoc_dec, _init;
9
+ import { isEqual } from "@dabble/delta";
10
+ import { applyCommittedChanges } from "../algorithms/client/applyCommittedChanges.js";
11
+ import { breakIntoBatches } from "../algorithms/client/batching.js";
12
+ import { Patches } from "../client/Patches.js";
13
+ import { signal } from "../event-signal.js";
14
+ import { blockable } from "../utils/concurrency.js";
15
+ import { PatchesWebSocket } from "./websocket/PatchesWebSocket.js";
16
+ import { onlineState } from "./websocket/onlineState.js";
17
+ _syncDoc_dec = [blockable], __receiveCommittedChanges_dec = [blockable];
18
+ class PatchesSync {
19
+ constructor(patches, url, options) {
20
+ this.options = options;
21
+ __runInitializers(_init, 5, this);
22
+ __publicField(this, "ws");
23
+ __publicField(this, "patches");
24
+ __publicField(this, "store");
25
+ __publicField(this, "maxPayloadBytes");
26
+ __publicField(this, "trackedDocs");
27
+ __publicField(this, "_state", { online: false, connected: false, syncing: null });
28
+ /**
29
+ * Signal emitted when the sync state changes.
30
+ */
31
+ __publicField(this, "onStateChange", signal());
32
+ /**
33
+ * Signal emitted when an error occurs.
34
+ */
35
+ __publicField(this, "onError", signal());
36
+ this.patches = patches;
37
+ this.store = patches.store;
38
+ this.maxPayloadBytes = patches.docOptions?.maxPayloadBytes;
39
+ this.ws = new PatchesWebSocket(url, options?.websocket);
40
+ this._state.online = onlineState.isOnline;
41
+ this.trackedDocs = new Set(patches.trackedDocs);
42
+ onlineState.onOnlineChange((online) => this.updateState({ online }));
43
+ this.ws.onStateChange(this._handleConnectionChange.bind(this));
44
+ this.ws.onChangesCommitted(this._receiveCommittedChanges.bind(this));
45
+ patches.onTrackDocs(this._handleDocsTracked.bind(this));
46
+ patches.onUntrackDocs(this._handleDocsUntracked.bind(this));
47
+ patches.onDeleteDoc(this._handleDocDeleted.bind(this));
48
+ }
49
+ /**
50
+ * Gets the current sync state.
51
+ */
52
+ get state() {
53
+ return this._state;
54
+ }
55
+ /**
56
+ * Updates the sync state.
57
+ * @param update - The partial state to update.
58
+ */
59
+ updateState(update) {
60
+ const newState = { ...this._state, ...update };
61
+ if (!isEqual(this._state, newState)) {
62
+ this._state = newState;
63
+ this.onStateChange.emit(this._state);
5
64
  }
6
- return useValue ? value : void 0;
7
- };
8
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
9
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
10
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
11
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
12
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
13
- var _, done = false;
14
- for (var i = decorators.length - 1; i >= 0; i--) {
15
- var context = {};
16
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
17
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
18
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
19
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
20
- if (kind === "accessor") {
21
- if (result === void 0) continue;
22
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
23
- if (_ = accept(result.get)) descriptor.get = _;
24
- if (_ = accept(result.set)) descriptor.set = _;
25
- if (_ = accept(result.init)) initializers.unshift(_);
26
- }
27
- else if (_ = accept(result)) {
28
- if (kind === "field") initializers.unshift(_);
29
- else descriptor[key] = _;
30
- }
65
+ }
66
+ /**
67
+ * Connects to the WebSocket server and starts syncing if online. If not online, it will wait for online state.
68
+ */
69
+ async connect() {
70
+ try {
71
+ await this.ws.connect();
72
+ } catch (err) {
73
+ console.error("PatchesSync connection failed:", err);
74
+ this.updateState({ connected: false, syncing: err instanceof Error ? err : new Error(String(err)) });
75
+ this.onError.emit(err);
76
+ throw err;
31
77
  }
32
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
33
- done = true;
34
- };
35
- import { isEqual } from '@dabble/delta';
36
- import { applyCommittedChanges } from '../algorithms/client/applyCommittedChanges.js';
37
- import { breakIntoBatches } from '../algorithms/client/batching.js';
38
- import { Patches } from '../client/Patches.js';
39
- import { signal } from '../event-signal.js';
40
- import { blockable } from '../utils/concurrency.js';
41
- import { PatchesWebSocket } from './websocket/PatchesWebSocket.js';
42
- import { onlineState } from './websocket/onlineState.js';
43
- /**
44
- * Handles WebSocket connection, document subscriptions, and syncing logic between
45
- * the Patches instance and the server.
46
- */
47
- let PatchesSync = (() => {
48
- let _instanceExtraInitializers = [];
49
- let _syncDoc_decorators;
50
- let __receiveCommittedChanges_decorators;
51
- return class PatchesSync {
52
- static {
53
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
54
- _syncDoc_decorators = [blockable];
55
- __receiveCommittedChanges_decorators = [blockable];
56
- __esDecorate(this, null, _syncDoc_decorators, { kind: "method", name: "syncDoc", static: false, private: false, access: { has: obj => "syncDoc" in obj, get: obj => obj.syncDoc }, metadata: _metadata }, null, _instanceExtraInitializers);
57
- __esDecorate(this, null, __receiveCommittedChanges_decorators, { kind: "method", name: "_receiveCommittedChanges", static: false, private: false, access: { has: obj => "_receiveCommittedChanges" in obj, get: obj => obj._receiveCommittedChanges }, metadata: _metadata }, null, _instanceExtraInitializers);
58
- if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
59
- }
60
- options = __runInitializers(this, _instanceExtraInitializers);
61
- ws;
62
- patches;
63
- store;
64
- maxPayloadBytes;
65
- trackedDocs;
66
- _state = { online: false, connected: false, syncing: null };
67
- /**
68
- * Signal emitted when the sync state changes.
69
- */
70
- onStateChange = signal();
71
- /**
72
- * Signal emitted when an error occurs.
73
- */
74
- onError = signal();
75
- constructor(patches, url, options) {
76
- this.options = options;
77
- this.patches = patches;
78
- this.store = patches.store;
79
- this.maxPayloadBytes = patches.docOptions?.maxPayloadBytes;
80
- this.ws = new PatchesWebSocket(url, options?.websocket);
81
- this._state.online = onlineState.isOnline;
82
- this.trackedDocs = new Set(patches.trackedDocs);
83
- // --- Event Listeners ---
84
- onlineState.onOnlineChange(online => this.updateState({ online }));
85
- this.ws.onStateChange(this._handleConnectionChange.bind(this));
86
- this.ws.onChangesCommitted(this._receiveCommittedChanges.bind(this));
87
- // Listen to Patches for tracking changes
88
- patches.onTrackDocs(this._handleDocsTracked.bind(this));
89
- patches.onUntrackDocs(this._handleDocsUntracked.bind(this));
90
- patches.onDeleteDoc(this._handleDocDeleted.bind(this));
91
- }
92
- /**
93
- * Gets the current sync state.
94
- */
95
- get state() {
96
- return this._state;
97
- }
98
- /**
99
- * Updates the sync state.
100
- * @param update - The partial state to update.
101
- */
102
- updateState(update) {
103
- const newState = { ...this._state, ...update };
104
- if (!isEqual(this._state, newState)) {
105
- this._state = newState;
106
- this.onStateChange.emit(this._state);
107
- }
108
- }
109
- /**
110
- * Connects to the WebSocket server and starts syncing if online. If not online, it will wait for online state.
111
- */
112
- async connect() {
113
- try {
114
- await this.ws.connect();
115
- }
116
- catch (err) {
117
- console.error('PatchesSync connection failed:', err);
118
- this.updateState({ connected: false, syncing: err instanceof Error ? err : new Error(String(err)) });
119
- this.onError.emit(err);
120
- throw err;
121
- }
78
+ }
79
+ /**
80
+ * Disconnects from the WebSocket server and stops syncing.
81
+ */
82
+ disconnect() {
83
+ this.ws.disconnect();
84
+ this.updateState({ connected: false, syncing: null });
85
+ }
86
+ /**
87
+ * Syncs all known docs when initially connected.
88
+ */
89
+ async syncAllKnownDocs() {
90
+ if (!this.state.connected) return;
91
+ this.updateState({ syncing: "updating" });
92
+ try {
93
+ const tracked = await this.store.listDocs(true);
94
+ const activeDocs = tracked.filter((t) => !t.deleted);
95
+ const deletedDocs = tracked.filter((t) => t.deleted);
96
+ const activeDocIds = activeDocs.map((t) => t.docId);
97
+ this.trackedDocs = new Set(activeDocIds);
98
+ if (activeDocIds.length > 0) {
99
+ try {
100
+ const subscribeIds = this.options?.subscribeFilter?.(activeDocIds) || activeDocIds;
101
+ if (subscribeIds.length) {
102
+ await this.ws.subscribe(subscribeIds);
103
+ }
104
+ } catch (err) {
105
+ console.warn("Error subscribing to active docs during sync:", err);
106
+ this.onError.emit(err);
122
107
  }
123
- /**
124
- * Disconnects from the WebSocket server and stops syncing.
125
- */
126
- disconnect() {
127
- this.ws.disconnect();
128
- this.updateState({ connected: false, syncing: null });
108
+ }
109
+ const activeSyncPromises = activeDocIds.map((id) => this.syncDoc(id));
110
+ const deletePromises = deletedDocs.map(async ({ docId }) => {
111
+ try {
112
+ console.info(`Attempting server delete for tombstoned doc: ${docId}`);
113
+ await this.ws.deleteDoc(docId);
114
+ await this.store.confirmDeleteDoc(docId);
115
+ console.info(`Successfully deleted and untracked doc: ${docId}`);
116
+ } catch (err) {
117
+ console.warn(`Server delete failed for ${docId}, keeping tombstone:`, err);
118
+ this.onError.emit(err, { docId });
129
119
  }
130
- /**
131
- * Syncs all known docs when initially connected.
132
- */
133
- async syncAllKnownDocs() {
134
- if (!this.state.connected)
135
- return;
136
- this.updateState({ syncing: 'updating' });
137
- try {
138
- const tracked = await this.store.listDocs(true); // Include deleted docs
139
- const activeDocs = tracked.filter(t => !t.deleted);
140
- const deletedDocs = tracked.filter(t => t.deleted);
141
- const activeDocIds = activeDocs.map((t) => t.docId);
142
- // Ensure tracked set reflects only active docs for subscription purposes
143
- this.trackedDocs = new Set(activeDocIds);
144
- // Subscribe to active docs
145
- if (activeDocIds.length > 0) {
146
- try {
147
- const subscribeIds = this.options?.subscribeFilter?.(activeDocIds) || activeDocIds;
148
- if (subscribeIds.length) {
149
- await this.ws.subscribe(subscribeIds);
150
- }
151
- }
152
- catch (err) {
153
- console.warn('Error subscribing to active docs during sync:', err);
154
- this.onError.emit(err);
155
- }
156
- }
157
- // Sync each active doc
158
- const activeSyncPromises = activeDocIds.map((id) => this.syncDoc(id));
159
- // Attempt to delete docs marked with tombstones
160
- const deletePromises = deletedDocs.map(async ({ docId }) => {
161
- try {
162
- console.info(`Attempting server delete for tombstoned doc: ${docId}`);
163
- await this.ws.deleteDoc(docId);
164
- // If server delete succeeds, remove tombstone and all data locally
165
- await this.store.confirmDeleteDoc(docId);
166
- console.info(`Successfully deleted and untracked doc: ${docId}`);
167
- }
168
- catch (err) {
169
- // If server delete fails (e.g., offline, already deleted), keep tombstone for retry
170
- console.warn(`Server delete failed for ${docId}, keeping tombstone:`, err);
171
- this.onError.emit(err, { docId });
172
- }
173
- });
174
- // Wait for all sync and delete operations
175
- await Promise.all([...activeSyncPromises, ...deletePromises]);
176
- this.updateState({ syncing: null });
177
- }
178
- catch (error) {
179
- console.error('Error during global sync:', error);
180
- this.updateState({ syncing: error instanceof Error ? error : new Error(String(error)) });
181
- this.onError.emit(error);
182
- }
183
- }
184
- /**
185
- * Syncs a single document.
186
- * @param docId The ID of the document to sync.
187
- */
188
- async syncDoc(docId) {
189
- if (!this.state.connected)
190
- return;
191
- const doc = this.patches.getOpenDoc(docId);
192
- if (doc) {
193
- doc.updateSyncing('updating');
194
- }
195
- try {
196
- const pending = await this.store.getPendingChanges(docId);
197
- if (pending.length > 0) {
198
- await this.flushDoc(docId); // flushDoc handles setting flushing state
199
- }
200
- else {
201
- // No pending, just check for server changes
202
- const [committedRev] = await this.store.getLastRevs(docId);
203
- if (committedRev) {
204
- const serverChanges = await this.ws.getChangesSince(docId, committedRev);
205
- if (serverChanges.length > 0) {
206
- // Apply server changes with proper rebasing
207
- await this._applyServerChangesToDoc(docId, serverChanges);
208
- }
209
- }
210
- else {
211
- const snapshot = await this.ws.getDoc(docId);
212
- await this.store.saveDoc(docId, snapshot);
213
- }
214
- }
215
- if (doc) {
216
- doc.updateSyncing(null);
217
- }
218
- }
219
- catch (err) {
220
- console.error(`Error syncing doc ${docId}:`, err);
221
- this.onError.emit(err, { docId });
222
- if (doc) {
223
- doc.updateSyncing(err instanceof Error ? err : new Error(String(err)));
224
- }
225
- }
226
- }
227
- /**
228
- * Flushes a document to the server.
229
- * @param docId The ID of the document to flush.
230
- */
231
- async flushDoc(docId) {
232
- if (!this.trackedDocs.has(docId)) {
233
- throw new Error(`Document ${docId} is not tracked`);
234
- }
235
- if (!this.state.connected) {
236
- throw new Error('Not connected to server');
237
- }
238
- try {
239
- // Get changes from store or memory
240
- let pending = await this.store.getPendingChanges(docId);
241
- if (!pending.length) {
242
- return; // Nothing to flush
243
- }
244
- const batches = breakIntoBatches(pending, this.maxPayloadBytes);
245
- for (const batch of batches) {
246
- if (!this.state.connected) {
247
- throw new Error('Disconnected during flush');
248
- }
249
- const range = [batch[0].rev, batch[batch.length - 1].rev];
250
- const committed = await this.ws.commitChanges(docId, batch);
251
- // Apply the committed changes using the sync algorithm (already saved to store)
252
- await this._applyServerChangesToDoc(docId, committed, range);
253
- // Fetch remaining pending for next batch or check completion
254
- pending = await this.store.getPendingChanges(docId);
255
- }
256
- }
257
- catch (err) {
258
- console.error(`Flush failed for doc ${docId}:`, err);
259
- this.onError.emit(err, { docId });
260
- throw err; // Re-throw so caller (like syncAll) knows it failed
261
- }
262
- }
263
- /**
264
- * Receives committed changes from the server and applies them to the document. This is a blockable function, so it
265
- * is separate from applyServerChangesToDoc, which is called by other blockable functions. Ensuring this is blockable
266
- * ensures that while a doc is sending changes to the server, it isn't receiving changes from the server which could
267
- * cause a race condition.
268
- */
269
- async _receiveCommittedChanges(docId, serverChanges) {
270
- try {
271
- await this._applyServerChangesToDoc(docId, serverChanges);
272
- }
273
- catch (err) {
274
- this.onError.emit(err, { docId });
275
- }
276
- }
277
- /**
278
- * Applies server changes to a document using the centralized sync algorithm.
279
- * This ensures consistent OT behavior regardless of whether the doc is open in memory.
280
- */
281
- async _applyServerChangesToDoc(docId, serverChanges, sentPendingRange) {
282
- // 1. Get current document snapshot from store
283
- const currentSnapshot = await this.store.getDoc(docId);
284
- if (!currentSnapshot) {
285
- console.warn(`Cannot apply server changes to non-existent doc: ${docId}`);
286
- return;
287
- }
288
- const doc = this.patches.getOpenDoc(docId);
289
- if (doc) {
290
- // Ensure we have all the changes, stored and in-memory (newly created but not yet persisted)
291
- const inMemoryPendingChanges = doc?.getPendingChanges();
292
- const latestRev = currentSnapshot.changes[currentSnapshot.changes.length - 1]?.rev || currentSnapshot.rev;
293
- const newChanges = inMemoryPendingChanges.filter(change => change.rev > latestRev);
294
- currentSnapshot.changes.push(...newChanges);
295
- }
296
- // 2. Use the pure algorithm to calculate the new state
297
- const { state, rev, changes: rebasedPendingChanges } = applyCommittedChanges(currentSnapshot, serverChanges);
298
- // 3. If the doc is open in memory, update it with the new state
299
- if (doc) {
300
- if (doc.committedRev === serverChanges[0].rev - 1) {
301
- // We can update the doc's snapshot
302
- doc.applyCommittedChanges(serverChanges, rebasedPendingChanges);
303
- }
304
- else {
305
- // We have to do a full state update
306
- doc.import({ state, rev, changes: rebasedPendingChanges });
307
- }
308
- }
309
- // 4. Save changes to store (if not already saved)
310
- await Promise.all([
311
- this.store.saveCommittedChanges(docId, serverChanges, sentPendingRange),
312
- this.store.replacePendingChanges(docId, rebasedPendingChanges),
313
- ]);
314
- }
315
- /**
316
- * Initiates the deletion process for a document both locally and on the server.
317
- * This now delegates the local tombstone marking to Patches.
318
- */
319
- async _handleDocDeleted(docId) {
320
- // Attempt server delete if online
321
- if (this.state.connected) {
322
- try {
323
- await this.ws.deleteDoc(docId);
324
- await this.store.confirmDeleteDoc(docId);
325
- }
326
- catch (err) {
327
- console.error(`Server delete failed for doc ${docId}, will retry on reconnect/resync.`, err);
328
- this.onError.emit(err, { docId });
329
- throw err;
330
- }
331
- }
332
- else {
333
- console.warn(`Offline: Server delete for doc ${docId} deferred.`);
334
- }
335
- }
336
- _handleConnectionChange(connectionState) {
337
- const isConnected = connectionState === 'connected';
338
- const isConnecting = connectionState === 'connecting';
339
- // Preserve syncing state if moving from connecting -> connected
340
- // Reset syncing if disconnected or errored
341
- const newSyncingState = isConnected
342
- ? this._state.syncing // Preserve
343
- : isConnecting
344
- ? this._state.syncing // Preserve during connecting phase too
345
- : null; // Reset
346
- this.updateState({ connected: isConnected, syncing: newSyncingState });
347
- if (isConnected) {
348
- // Sync everything on connect/reconnect
349
- void this.syncAllKnownDocs();
350
- }
120
+ });
121
+ await Promise.all([...activeSyncPromises, ...deletePromises]);
122
+ this.updateState({ syncing: null });
123
+ } catch (error) {
124
+ console.error("Error during global sync:", error);
125
+ this.updateState({ syncing: error instanceof Error ? error : new Error(String(error)) });
126
+ this.onError.emit(error);
127
+ }
128
+ }
129
+ async syncDoc(docId) {
130
+ if (!this.state.connected) return;
131
+ const doc = this.patches.getOpenDoc(docId);
132
+ if (doc) {
133
+ doc.updateSyncing("updating");
134
+ }
135
+ try {
136
+ const pending = await this.store.getPendingChanges(docId);
137
+ if (pending.length > 0) {
138
+ await this.flushDoc(docId);
139
+ } else {
140
+ const [committedRev] = await this.store.getLastRevs(docId);
141
+ if (committedRev) {
142
+ const serverChanges = await this.ws.getChangesSince(docId, committedRev);
143
+ if (serverChanges.length > 0) {
144
+ await this._applyServerChangesToDoc(docId, serverChanges);
145
+ }
146
+ } else {
147
+ const snapshot = await this.ws.getDoc(docId);
148
+ await this.store.saveDoc(docId, snapshot);
351
149
  }
352
- async _handleDocsTracked(docIds) {
353
- const newIds = docIds.filter(id => !this.trackedDocs.has(id));
354
- if (!newIds.length)
355
- return;
356
- newIds.forEach(id => this.trackedDocs.add(id));
357
- let subscribeIds = newIds;
358
- // If a subscribe filter is provided, filter out docs that are already tracked
359
- if (this.options?.subscribeFilter) {
360
- const alreadyTracked = this.options.subscribeFilter([...this.trackedDocs]);
361
- subscribeIds = subscribeIds.filter(id => !alreadyTracked.includes(id));
362
- }
363
- if (this.state.connected) {
364
- try {
365
- if (subscribeIds.length) {
366
- await this.ws.subscribe(subscribeIds);
367
- }
368
- // Trigger sync for newly tracked docs immediately
369
- await Promise.all(newIds.map(id => this.syncDoc(id)));
370
- }
371
- catch (err) {
372
- console.warn(`Failed to subscribe/sync newly tracked docs: ${newIds.join(', ')}`, err);
373
- this.onError.emit(err);
374
- }
375
- }
150
+ }
151
+ if (doc) {
152
+ doc.updateSyncing(null);
153
+ }
154
+ } catch (err) {
155
+ console.error(`Error syncing doc ${docId}:`, err);
156
+ this.onError.emit(err, { docId });
157
+ if (doc) {
158
+ doc.updateSyncing(err instanceof Error ? err : new Error(String(err)));
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * Flushes a document to the server.
164
+ * @param docId The ID of the document to flush.
165
+ */
166
+ async flushDoc(docId) {
167
+ if (!this.trackedDocs.has(docId)) {
168
+ throw new Error(`Document ${docId} is not tracked`);
169
+ }
170
+ if (!this.state.connected) {
171
+ throw new Error("Not connected to server");
172
+ }
173
+ try {
174
+ let pending = await this.store.getPendingChanges(docId);
175
+ if (!pending.length) {
176
+ return;
177
+ }
178
+ const batches = breakIntoBatches(pending, this.maxPayloadBytes);
179
+ for (const batch of batches) {
180
+ if (!this.state.connected) {
181
+ throw new Error("Disconnected during flush");
376
182
  }
377
- async _handleDocsUntracked(docIds) {
378
- const existingIds = docIds.filter(id => this.trackedDocs.has(id));
379
- if (!existingIds.length)
380
- return;
381
- existingIds.forEach(id => this.trackedDocs.delete(id));
382
- if (this.state.connected) {
383
- try {
384
- await this.ws.unsubscribe(existingIds);
385
- }
386
- catch (err) {
387
- console.warn(`Failed to unsubscribe docs: ${existingIds.join(', ')}`, err);
388
- }
389
- }
183
+ const range = [batch[0].rev, batch[batch.length - 1].rev];
184
+ const committed = await this.ws.commitChanges(docId, batch);
185
+ await this._applyServerChangesToDoc(docId, committed, range);
186
+ pending = await this.store.getPendingChanges(docId);
187
+ }
188
+ } catch (err) {
189
+ console.error(`Flush failed for doc ${docId}:`, err);
190
+ this.onError.emit(err, { docId });
191
+ throw err;
192
+ }
193
+ }
194
+ async _receiveCommittedChanges(docId, serverChanges) {
195
+ try {
196
+ await this._applyServerChangesToDoc(docId, serverChanges);
197
+ } catch (err) {
198
+ this.onError.emit(err, { docId });
199
+ }
200
+ }
201
+ /**
202
+ * Applies server changes to a document using the centralized sync algorithm.
203
+ * This ensures consistent OT behavior regardless of whether the doc is open in memory.
204
+ */
205
+ async _applyServerChangesToDoc(docId, serverChanges, sentPendingRange) {
206
+ const currentSnapshot = await this.store.getDoc(docId);
207
+ if (!currentSnapshot) {
208
+ console.warn(`Cannot apply server changes to non-existent doc: ${docId}`);
209
+ return;
210
+ }
211
+ const doc = this.patches.getOpenDoc(docId);
212
+ if (doc) {
213
+ const inMemoryPendingChanges = doc?.getPendingChanges();
214
+ const latestRev = currentSnapshot.changes[currentSnapshot.changes.length - 1]?.rev || currentSnapshot.rev;
215
+ const newChanges = inMemoryPendingChanges.filter((change) => change.rev > latestRev);
216
+ currentSnapshot.changes.push(...newChanges);
217
+ }
218
+ const { state, rev, changes: rebasedPendingChanges } = applyCommittedChanges(currentSnapshot, serverChanges);
219
+ if (doc) {
220
+ if (doc.committedRev === serverChanges[0].rev - 1) {
221
+ doc.applyCommittedChanges(serverChanges, rebasedPendingChanges);
222
+ } else {
223
+ doc.import({ state, rev, changes: rebasedPendingChanges });
224
+ }
225
+ }
226
+ await Promise.all([
227
+ this.store.saveCommittedChanges(docId, serverChanges, sentPendingRange),
228
+ this.store.replacePendingChanges(docId, rebasedPendingChanges)
229
+ ]);
230
+ }
231
+ /**
232
+ * Initiates the deletion process for a document both locally and on the server.
233
+ * This now delegates the local tombstone marking to Patches.
234
+ */
235
+ async _handleDocDeleted(docId) {
236
+ if (this.state.connected) {
237
+ try {
238
+ await this.ws.deleteDoc(docId);
239
+ await this.store.confirmDeleteDoc(docId);
240
+ } catch (err) {
241
+ console.error(`Server delete failed for doc ${docId}, will retry on reconnect/resync.`, err);
242
+ this.onError.emit(err, { docId });
243
+ throw err;
244
+ }
245
+ } else {
246
+ console.warn(`Offline: Server delete for doc ${docId} deferred.`);
247
+ }
248
+ }
249
+ _handleConnectionChange(connectionState) {
250
+ const isConnected = connectionState === "connected";
251
+ const isConnecting = connectionState === "connecting";
252
+ const newSyncingState = isConnected ? this._state.syncing : isConnecting ? this._state.syncing : null;
253
+ this.updateState({ connected: isConnected, syncing: newSyncingState });
254
+ if (isConnected) {
255
+ void this.syncAllKnownDocs();
256
+ }
257
+ }
258
+ async _handleDocsTracked(docIds) {
259
+ const newIds = docIds.filter((id) => !this.trackedDocs.has(id));
260
+ if (!newIds.length) return;
261
+ newIds.forEach((id) => this.trackedDocs.add(id));
262
+ let subscribeIds = newIds;
263
+ if (this.options?.subscribeFilter) {
264
+ const alreadyTracked = this.options.subscribeFilter([...this.trackedDocs]);
265
+ subscribeIds = subscribeIds.filter((id) => !alreadyTracked.includes(id));
266
+ }
267
+ if (this.state.connected) {
268
+ try {
269
+ if (subscribeIds.length) {
270
+ await this.ws.subscribe(subscribeIds);
390
271
  }
391
- };
392
- })();
393
- export { PatchesSync };
272
+ await Promise.all(newIds.map((id) => this.syncDoc(id)));
273
+ } catch (err) {
274
+ console.warn(`Failed to subscribe/sync newly tracked docs: ${newIds.join(", ")}`, err);
275
+ this.onError.emit(err);
276
+ }
277
+ }
278
+ }
279
+ async _handleDocsUntracked(docIds) {
280
+ const existingIds = docIds.filter((id) => this.trackedDocs.has(id));
281
+ if (!existingIds.length) return;
282
+ existingIds.forEach((id) => this.trackedDocs.delete(id));
283
+ if (this.state.connected) {
284
+ try {
285
+ await this.ws.unsubscribe(existingIds);
286
+ } catch (err) {
287
+ console.warn(`Failed to unsubscribe docs: ${existingIds.join(", ")}`, err);
288
+ }
289
+ }
290
+ }
291
+ }
292
+ _init = __decoratorStart(null);
293
+ __decorateElement(_init, 1, "syncDoc", _syncDoc_dec, PatchesSync);
294
+ __decorateElement(_init, 1, "_receiveCommittedChanges", __receiveCommittedChanges_dec, PatchesSync);
295
+ __decoratorMetadata(_init, PatchesSync);
296
+ export {
297
+ PatchesSync
298
+ };