@dabble/patches 0.4.4 → 0.4.6
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/algorithms/client/applyCommittedChanges.d.ts +8 -2
- package/dist/algorithms/client/applyCommittedChanges.js +30 -38
- package/dist/algorithms/client/batching.d.ts +8 -2
- package/dist/algorithms/client/batching.js +38 -37
- package/dist/algorithms/client/breakChange.d.ts +8 -2
- package/dist/algorithms/client/breakChange.js +191 -240
- package/dist/algorithms/client/createStateFromSnapshot.d.ts +8 -2
- package/dist/algorithms/client/createStateFromSnapshot.js +7 -8
- package/dist/algorithms/client/getJSONByteSize.d.ts +3 -1
- package/dist/algorithms/client/getJSONByteSize.js +12 -11
- package/dist/algorithms/client/makeChange.d.ts +8 -2
- package/dist/algorithms/client/makeChange.js +28 -36
- package/dist/algorithms/server/commitChanges.d.ts +9 -3
- package/dist/algorithms/server/commitChanges.js +69 -78
- package/dist/algorithms/server/createVersion.d.ts +9 -3
- package/dist/algorithms/server/createVersion.js +21 -27
- package/dist/algorithms/server/getSnapshotAtRevision.d.ts +9 -3
- package/dist/algorithms/server/getSnapshotAtRevision.js +27 -28
- package/dist/algorithms/server/getStateAtRevision.d.ts +9 -3
- package/dist/algorithms/server/getStateAtRevision.js +13 -17
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.d.ts +9 -3
- package/dist/algorithms/server/handleOfflineSessionsAndBatches.js +60 -77
- package/dist/algorithms/server/transformIncomingChanges.d.ts +8 -2
- package/dist/algorithms/server/transformIncomingChanges.js +27 -39
- package/dist/algorithms/shared/applyChanges.d.ts +8 -2
- package/dist/algorithms/shared/applyChanges.js +11 -16
- package/dist/algorithms/shared/rebaseChanges.d.ts +8 -2
- package/dist/algorithms/shared/rebaseChanges.js +30 -49
- package/dist/chunk-IZ2YBCUP.js +56 -0
- package/dist/client/InMemoryStore.d.ts +9 -3
- package/dist/client/InMemoryStore.js +92 -101
- package/dist/client/IndexedDBStore.d.ts +9 -3
- package/dist/client/IndexedDBStore.js +378 -491
- package/dist/client/Patches.d.ts +18 -13
- package/dist/client/Patches.js +152 -207
- package/dist/client/PatchesDoc.d.ts +14 -8
- package/dist/client/PatchesDoc.js +147 -154
- package/dist/client/PatchesHistoryClient.d.ts +12 -5
- package/dist/client/PatchesHistoryClient.js +110 -117
- package/dist/client/PatchesStore.d.ts +9 -3
- package/dist/client/PatchesStore.js +0 -1
- package/dist/client/index.d.ts +12 -6
- package/dist/client/index.js +5 -5
- package/dist/data/change.d.ts +9 -3
- package/dist/data/change.js +23 -15
- package/dist/data/version.d.ts +9 -3
- package/dist/data/version.js +11 -15
- package/dist/event-signal.d.ts +7 -6
- package/dist/event-signal.js +24 -39
- package/dist/index-CvQws3AB.d.ts +36 -0
- package/dist/index.d.ts +27 -5
- package/dist/index.js +10 -4
- package/dist/json-patch/JSONPatch.d.ts +9 -5
- package/dist/json-patch/JSONPatch.js +175 -183
- package/dist/json-patch/applyPatch.d.ts +5 -2
- package/dist/json-patch/applyPatch.js +27 -35
- package/dist/json-patch/composePatch.d.ts +5 -2
- package/dist/json-patch/composePatch.js +34 -34
- package/dist/json-patch/createJSONPatch.d.ts +7 -2
- package/dist/json-patch/createJSONPatch.js +11 -38
- package/dist/json-patch/index.d.ts +14 -6
- package/dist/json-patch/index.js +20 -9
- package/dist/json-patch/invertPatch.d.ts +5 -2
- package/dist/json-patch/invertPatch.js +31 -30
- package/dist/json-patch/ops/add.d.ts +5 -2
- package/dist/json-patch/ops/add.js +53 -51
- package/dist/json-patch/ops/bitmask.d.ts +8 -5
- package/dist/json-patch/ops/bitmask.js +41 -44
- package/dist/json-patch/ops/copy.d.ts +5 -2
- package/dist/json-patch/ops/copy.js +32 -33
- package/dist/json-patch/ops/increment.d.ts +5 -2
- package/dist/json-patch/ops/increment.js +21 -20
- package/dist/json-patch/ops/index.d.ts +10 -21
- package/dist/json-patch/ops/index.js +34 -24
- package/dist/json-patch/ops/move.d.ts +5 -2
- package/dist/json-patch/ops/move.js +132 -198
- package/dist/json-patch/ops/remove.d.ts +5 -2
- package/dist/json-patch/ops/remove.js +33 -30
- package/dist/json-patch/ops/replace.d.ts +5 -2
- package/dist/json-patch/ops/replace.js +45 -43
- package/dist/json-patch/ops/test.d.ts +5 -2
- package/dist/json-patch/ops/test.js +25 -21
- package/dist/json-patch/ops/text.d.ts +5 -2
- package/dist/json-patch/ops/text.js +54 -54
- package/dist/json-patch/pathProxy.d.ts +9 -3
- package/dist/json-patch/pathProxy.js +27 -48
- package/dist/json-patch/state.d.ts +5 -2
- package/dist/json-patch/state.js +11 -7
- package/dist/json-patch/transformPatch.d.ts +6 -2
- package/dist/json-patch/transformPatch.js +21 -24
- package/dist/json-patch/types.d.ts +9 -7
- package/dist/json-patch/types.js +0 -1
- package/dist/json-patch/utils/deepEqual.d.ts +3 -1
- package/dist/json-patch/utils/deepEqual.js +32 -28
- package/dist/json-patch/utils/exit.d.ts +5 -2
- package/dist/json-patch/utils/exit.js +7 -3
- package/dist/json-patch/utils/get.d.ts +5 -2
- package/dist/json-patch/utils/get.js +8 -4
- package/dist/json-patch/utils/getOpData.d.ts +5 -2
- package/dist/json-patch/utils/getOpData.js +12 -9
- package/dist/json-patch/utils/getType.d.ts +6 -3
- package/dist/json-patch/utils/getType.js +9 -4
- package/dist/json-patch/utils/index.d.ts +15 -14
- package/dist/json-patch/utils/index.js +14 -14
- package/dist/json-patch/utils/log.d.ts +4 -2
- package/dist/json-patch/utils/log.js +8 -3
- package/dist/json-patch/utils/ops.d.ts +8 -5
- package/dist/json-patch/utils/ops.js +83 -100
- package/dist/json-patch/utils/paths.d.ts +12 -9
- package/dist/json-patch/utils/paths.js +54 -51
- package/dist/json-patch/utils/pluck.d.ts +8 -5
- package/dist/json-patch/utils/pluck.js +32 -26
- package/dist/json-patch/utils/shallowCopy.d.ts +3 -1
- package/dist/json-patch/utils/shallowCopy.js +22 -18
- package/dist/json-patch/utils/softWrites.d.ts +6 -3
- package/dist/json-patch/utils/softWrites.js +17 -16
- package/dist/json-patch/utils/toArrayIndex.d.ts +3 -1
- package/dist/json-patch/utils/toArrayIndex.js +14 -10
- package/dist/json-patch/utils/toKeys.d.ts +3 -1
- package/dist/json-patch/utils/toKeys.js +15 -11
- package/dist/json-patch/utils/updateArrayIndexes.d.ts +5 -2
- package/dist/json-patch/utils/updateArrayIndexes.js +33 -37
- package/dist/json-patch/utils/updateArrayPath.d.ts +5 -2
- package/dist/json-patch/utils/updateArrayPath.js +29 -42
- package/dist/net/PatchesClient.d.ts +128 -0
- package/dist/net/PatchesClient.js +161 -0
- package/dist/net/PatchesSync.d.ts +19 -9
- package/dist/net/PatchesSync.js +291 -386
- package/dist/net/error.d.ts +3 -1
- package/dist/net/error.js +9 -6
- package/dist/net/http/FetchTransport.d.ts +21 -0
- package/dist/net/http/FetchTransport.js +34 -0
- package/dist/net/index.d.ts +26 -12
- package/dist/net/index.js +12 -10
- package/dist/net/protocol/JSONRPCClient.d.ts +11 -4
- package/dist/net/protocol/JSONRPCClient.js +95 -103
- package/dist/net/protocol/JSONRPCServer.d.ts +15 -8
- package/dist/net/protocol/JSONRPCServer.js +101 -123
- package/dist/net/protocol/types.d.ts +21 -15
- package/dist/net/protocol/types.js +0 -1
- package/dist/net/protocol/utils.d.ts +12 -0
- package/dist/net/protocol/utils.js +15 -0
- package/dist/net/types.d.ts +4 -2
- package/dist/net/types.js +0 -1
- package/dist/net/webrtc/WebRTCAwareness.d.ts +14 -4
- package/dist/net/webrtc/WebRTCAwareness.js +111 -120
- package/dist/net/webrtc/WebRTCTransport.d.ts +16 -8
- package/dist/net/webrtc/WebRTCTransport.js +149 -157
- package/dist/net/webrtc/index.d.ts +10 -2
- package/dist/net/webrtc/index.js +2 -2
- package/dist/net/websocket/AuthorizationProvider.d.ts +7 -5
- package/dist/net/websocket/AuthorizationProvider.js +12 -17
- package/dist/net/websocket/PatchesWebSocket.d.ts +14 -109
- package/dist/net/websocket/PatchesWebSocket.js +37 -184
- package/dist/net/websocket/RPCServer.d.ts +19 -10
- package/dist/net/websocket/RPCServer.js +190 -192
- package/dist/net/websocket/SignalingService.d.ts +12 -32
- package/dist/net/websocket/SignalingService.js +126 -133
- package/dist/net/websocket/WebSocketServer.d.ts +17 -4
- package/dist/net/websocket/WebSocketServer.js +64 -72
- package/dist/net/websocket/WebSocketTransport.d.ts +13 -5
- package/dist/net/websocket/WebSocketTransport.js +178 -207
- package/dist/net/websocket/onlineState.d.ts +6 -3
- package/dist/net/websocket/onlineState.js +25 -21
- package/dist/server/PatchesBranchManager.d.ts +12 -5
- package/dist/server/PatchesBranchManager.js +132 -142
- package/dist/server/PatchesHistoryManager.d.ts +11 -3
- package/dist/server/PatchesHistoryManager.js +81 -84
- package/dist/server/PatchesServer.d.ts +16 -10
- package/dist/server/PatchesServer.js +131 -137
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.js +9 -3
- package/dist/server/types.d.ts +9 -3
- package/dist/server/types.js +0 -1
- package/dist/types.d.ts +49 -19
- package/dist/types.js +1 -1
- package/dist/utils/concurrency.d.ts +7 -5
- package/dist/utils/concurrency.js +43 -53
- package/dist/utils/deferred.d.ts +4 -2
- package/dist/utils/deferred.js +25 -21
- package/package.json +5 -7
package/dist/client/Patches.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
1
|
+
import { Unsubscriber, Signal } from '../event-signal.js';
|
|
2
|
+
import { Change } from '../types.js';
|
|
3
|
+
import { PatchesDocOptions, PatchesDoc } from './PatchesDoc.js';
|
|
4
|
+
import { PatchesStore } from './PatchesStore.js';
|
|
5
|
+
import '../json-patch/JSONPatch.js';
|
|
6
|
+
import '@dabble/delta';
|
|
7
|
+
import '../json-patch/types.js';
|
|
8
|
+
|
|
9
|
+
interface PatchesOptions {
|
|
6
10
|
/** Persistence layer instance (e.g., new IndexedDBStore('my-db') or new InMemoryStore()). */
|
|
7
11
|
store: PatchesStore;
|
|
8
12
|
/** Initial metadata to attach to changes from this client (merged with per-doc metadata). */
|
|
@@ -19,20 +23,20 @@ interface ManagedDoc<T extends object> {
|
|
|
19
23
|
* Manages document instances (`PatchesDoc`) and persistence (`PatchesStore`).
|
|
20
24
|
* Can be used standalone or with PatchesSync for network synchronization.
|
|
21
25
|
*/
|
|
22
|
-
|
|
26
|
+
declare class Patches {
|
|
23
27
|
protected options: PatchesOptions;
|
|
24
28
|
protected docs: Map<string, ManagedDoc<any>>;
|
|
25
29
|
readonly docOptions: PatchesDocOptions;
|
|
26
30
|
readonly store: PatchesStore;
|
|
27
31
|
readonly trackedDocs: Set<string>;
|
|
28
|
-
readonly onError:
|
|
32
|
+
readonly onError: Signal<(error: Error, context?: {
|
|
29
33
|
docId?: string;
|
|
30
34
|
}) => void>;
|
|
31
|
-
readonly onServerCommit:
|
|
32
|
-
readonly onTrackDocs:
|
|
33
|
-
readonly onUntrackDocs:
|
|
34
|
-
readonly onDeleteDoc:
|
|
35
|
-
readonly onChange:
|
|
35
|
+
readonly onServerCommit: Signal<(docId: string, changes: Change[]) => void>;
|
|
36
|
+
readonly onTrackDocs: Signal<(docIds: string[]) => void>;
|
|
37
|
+
readonly onUntrackDocs: Signal<(docIds: string[]) => void>;
|
|
38
|
+
readonly onDeleteDoc: Signal<(docId: string) => void>;
|
|
39
|
+
readonly onChange: Signal<(docId: string, changes: Change[]) => void>;
|
|
36
40
|
constructor(opts: PatchesOptions);
|
|
37
41
|
/**
|
|
38
42
|
* Tracks the given document IDs, adding them to the set of tracked documents and notifying listeners.
|
|
@@ -91,4 +95,5 @@ export declare class Patches {
|
|
|
91
95
|
*/
|
|
92
96
|
protected _savePendingChanges(docId: string, changes: Change[]): Promise<void>;
|
|
93
97
|
}
|
|
94
|
-
|
|
98
|
+
|
|
99
|
+
export { Patches, type PatchesOptions };
|
package/dist/client/Patches.js
CHANGED
|
@@ -1,210 +1,155 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
__decorateElement,
|
|
3
|
+
__decoratorMetadata,
|
|
4
|
+
__decoratorStart,
|
|
5
|
+
__publicField,
|
|
6
|
+
__runInitializers
|
|
7
|
+
} from "../chunk-IZ2YBCUP.js";
|
|
8
|
+
var _openDoc_dec, _init;
|
|
9
|
+
import { signal } from "../event-signal.js";
|
|
10
|
+
import { singleInvocation } from "../utils/concurrency.js";
|
|
11
|
+
import { PatchesDoc } from "./PatchesDoc.js";
|
|
12
|
+
_openDoc_dec = [singleInvocation(true)];
|
|
13
|
+
class Patches {
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
__runInitializers(_init, 5, this);
|
|
16
|
+
__publicField(this, "options");
|
|
17
|
+
__publicField(this, "docs", /* @__PURE__ */ new Map());
|
|
18
|
+
__publicField(this, "docOptions");
|
|
19
|
+
__publicField(this, "store");
|
|
20
|
+
__publicField(this, "trackedDocs", /* @__PURE__ */ new Set());
|
|
21
|
+
// Public signals
|
|
22
|
+
__publicField(this, "onError", signal());
|
|
23
|
+
__publicField(this, "onServerCommit", signal());
|
|
24
|
+
__publicField(this, "onTrackDocs", signal());
|
|
25
|
+
__publicField(this, "onUntrackDocs", signal());
|
|
26
|
+
__publicField(this, "onDeleteDoc", signal());
|
|
27
|
+
__publicField(this, "onChange", signal());
|
|
28
|
+
this.options = opts;
|
|
29
|
+
this.store = opts.store;
|
|
30
|
+
this.docOptions = opts.docOptions ?? {};
|
|
31
|
+
this.store.listDocs().then((docs) => {
|
|
32
|
+
this.trackDocs(docs.map(({ docId }) => docId));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// --- Public API Methods ---
|
|
36
|
+
/**
|
|
37
|
+
* Tracks the given document IDs, adding them to the set of tracked documents and notifying listeners.
|
|
38
|
+
* Tracked docs are kept in sync with the server, even when not open locally.
|
|
39
|
+
* This allows for background syncing and updates of unopened documents.
|
|
40
|
+
* @param docIds - Array of document IDs to track.
|
|
41
|
+
*/
|
|
42
|
+
async trackDocs(docIds) {
|
|
43
|
+
docIds = docIds.filter((id) => !this.trackedDocs.has(id));
|
|
44
|
+
if (!docIds.length) return;
|
|
45
|
+
docIds.forEach(this.trackedDocs.add, this.trackedDocs);
|
|
46
|
+
this.onTrackDocs.emit(docIds);
|
|
47
|
+
await this.store.trackDocs(docIds);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Untracks the given document IDs, removing them from the set of tracked documents and notifying listeners.
|
|
51
|
+
* Untracked docs will no longer be kept in sync with the server, even if not open locally.
|
|
52
|
+
* Closes any open docs and removes them from the store.
|
|
53
|
+
* @param docIds - Array of document IDs to untrack.
|
|
54
|
+
*/
|
|
55
|
+
async untrackDocs(docIds) {
|
|
56
|
+
docIds = docIds.filter((id) => this.trackedDocs.has(id));
|
|
57
|
+
if (!docIds.length) return;
|
|
58
|
+
docIds.forEach(this.trackedDocs.delete, this.trackedDocs);
|
|
59
|
+
this.onUntrackDocs.emit(docIds);
|
|
60
|
+
const closedPromises = docIds.filter((id) => this.docs.has(id)).map((id) => this.closeDoc(id));
|
|
61
|
+
await Promise.all(closedPromises);
|
|
62
|
+
await this.store.untrackDocs(docIds);
|
|
63
|
+
}
|
|
64
|
+
// ensure a second call to openDoc with the same docId returns the same promise while opening
|
|
65
|
+
async openDoc(docId, opts = {}) {
|
|
66
|
+
const existing = this.docs.get(docId);
|
|
67
|
+
if (existing) return existing.doc;
|
|
68
|
+
await this.trackDocs([docId]);
|
|
69
|
+
const snapshot = await this.store.getDoc(docId);
|
|
70
|
+
const initialState = snapshot?.state ?? {};
|
|
71
|
+
const mergedMetadata = { ...this.options.metadata, ...opts.metadata };
|
|
72
|
+
const doc = new PatchesDoc(initialState, mergedMetadata, this.docOptions);
|
|
73
|
+
doc.setId(docId);
|
|
74
|
+
if (snapshot) {
|
|
75
|
+
doc.import(snapshot);
|
|
5
76
|
}
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
77
|
+
const unsubscribe = doc.onChange((changes) => this._savePendingChanges(docId, changes));
|
|
78
|
+
this.docs.set(docId, { doc, unsubscribe });
|
|
79
|
+
return doc;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Closes an open document by ID, removing listeners and optionally untracking it.
|
|
83
|
+
* @param docId - The document ID to close.
|
|
84
|
+
* @param options - Optional: set untrack to true to also untrack the doc.
|
|
85
|
+
*/
|
|
86
|
+
async closeDoc(docId, { untrack = false } = {}) {
|
|
87
|
+
const managed = this.docs.get(docId);
|
|
88
|
+
if (managed) {
|
|
89
|
+
managed.unsubscribe();
|
|
90
|
+
this.docs.delete(docId);
|
|
91
|
+
if (untrack) {
|
|
92
|
+
await this.untrackDocs([docId]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Deletes a document by ID, closing it if open, untracking it, and removing it from the store.
|
|
98
|
+
* Emits the onDeleteDoc signal.
|
|
99
|
+
* @param docId - The document ID to delete.
|
|
100
|
+
*/
|
|
101
|
+
async deleteDoc(docId) {
|
|
102
|
+
if (this.docs.has(docId)) {
|
|
103
|
+
await this.closeDoc(docId);
|
|
104
|
+
}
|
|
105
|
+
if (this.trackedDocs.has(docId)) {
|
|
106
|
+
await this.untrackDocs([docId]);
|
|
107
|
+
}
|
|
108
|
+
await this.store.deleteDoc(docId);
|
|
109
|
+
this.onDeleteDoc.emit(docId);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Gets an open document instance by ID, if it exists.
|
|
113
|
+
* Used by PatchesSync for applying server changes to open docs.
|
|
114
|
+
* @param docId - The document ID to get.
|
|
115
|
+
* @returns The PatchesDoc instance or undefined if not open.
|
|
116
|
+
*/
|
|
117
|
+
getOpenDoc(docId) {
|
|
118
|
+
return this.docs.get(docId)?.doc;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Closes all open documents and cleans up listeners and store connections.
|
|
122
|
+
* Should be called when shutting down the client.
|
|
123
|
+
*/
|
|
124
|
+
close() {
|
|
125
|
+
this.docs.forEach((managed) => managed.unsubscribe());
|
|
126
|
+
this.docs.clear();
|
|
127
|
+
this.store.close();
|
|
128
|
+
this.onChange.clear();
|
|
129
|
+
this.onDeleteDoc.clear();
|
|
130
|
+
this.onUntrackDocs.clear();
|
|
131
|
+
this.onTrackDocs.clear();
|
|
132
|
+
this.onServerCommit.clear();
|
|
133
|
+
this.onError.clear();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Internal handler for saving pending changes to the store.
|
|
137
|
+
* @param docId - The document ID to save the changes for.
|
|
138
|
+
* @param changes - The changes to save.
|
|
139
|
+
*/
|
|
140
|
+
async _savePendingChanges(docId, changes) {
|
|
141
|
+
try {
|
|
142
|
+
await this.store.savePendingChanges(docId, changes);
|
|
143
|
+
this.onChange.emit(docId, changes);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(`Error saving pending changes for doc ${docId}:`, err);
|
|
146
|
+
this.onError.emit(err, { docId });
|
|
31
147
|
}
|
|
32
|
-
|
|
33
|
-
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
_init = __decoratorStart(null);
|
|
151
|
+
__decorateElement(_init, 1, "openDoc", _openDoc_dec, Patches);
|
|
152
|
+
__decoratorMetadata(_init, Patches);
|
|
153
|
+
export {
|
|
154
|
+
Patches
|
|
34
155
|
};
|
|
35
|
-
import { signal } from '../event-signal.js';
|
|
36
|
-
import { singleInvocation } from '../utils/concurrency.js';
|
|
37
|
-
import { PatchesDoc } from './PatchesDoc.js';
|
|
38
|
-
/**
|
|
39
|
-
* Main client-side entry point for the Patches library.
|
|
40
|
-
* Manages document instances (`PatchesDoc`) and persistence (`PatchesStore`).
|
|
41
|
-
* Can be used standalone or with PatchesSync for network synchronization.
|
|
42
|
-
*/
|
|
43
|
-
let Patches = (() => {
|
|
44
|
-
let _instanceExtraInitializers = [];
|
|
45
|
-
let _openDoc_decorators;
|
|
46
|
-
return class Patches {
|
|
47
|
-
static {
|
|
48
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
49
|
-
_openDoc_decorators = [singleInvocation(true)];
|
|
50
|
-
__esDecorate(this, null, _openDoc_decorators, { kind: "method", name: "openDoc", static: false, private: false, access: { has: obj => "openDoc" in obj, get: obj => obj.openDoc }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
51
|
-
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
52
|
-
}
|
|
53
|
-
options = __runInitializers(this, _instanceExtraInitializers);
|
|
54
|
-
docs = new Map();
|
|
55
|
-
docOptions;
|
|
56
|
-
store;
|
|
57
|
-
trackedDocs = new Set();
|
|
58
|
-
// Public signals
|
|
59
|
-
onError = signal();
|
|
60
|
-
onServerCommit = signal();
|
|
61
|
-
onTrackDocs = signal();
|
|
62
|
-
onUntrackDocs = signal();
|
|
63
|
-
onDeleteDoc = signal();
|
|
64
|
-
onChange = signal();
|
|
65
|
-
constructor(opts) {
|
|
66
|
-
this.options = opts;
|
|
67
|
-
this.store = opts.store;
|
|
68
|
-
this.docOptions = opts.docOptions ?? {};
|
|
69
|
-
this.store.listDocs().then(docs => {
|
|
70
|
-
this.trackDocs(docs.map(({ docId }) => docId));
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
// --- Public API Methods ---
|
|
74
|
-
/**
|
|
75
|
-
* Tracks the given document IDs, adding them to the set of tracked documents and notifying listeners.
|
|
76
|
-
* Tracked docs are kept in sync with the server, even when not open locally.
|
|
77
|
-
* This allows for background syncing and updates of unopened documents.
|
|
78
|
-
* @param docIds - Array of document IDs to track.
|
|
79
|
-
*/
|
|
80
|
-
async trackDocs(docIds) {
|
|
81
|
-
docIds = docIds.filter(id => !this.trackedDocs.has(id));
|
|
82
|
-
if (!docIds.length)
|
|
83
|
-
return;
|
|
84
|
-
docIds.forEach(this.trackedDocs.add, this.trackedDocs);
|
|
85
|
-
this.onTrackDocs.emit(docIds);
|
|
86
|
-
await this.store.trackDocs(docIds);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Untracks the given document IDs, removing them from the set of tracked documents and notifying listeners.
|
|
90
|
-
* Untracked docs will no longer be kept in sync with the server, even if not open locally.
|
|
91
|
-
* Closes any open docs and removes them from the store.
|
|
92
|
-
* @param docIds - Array of document IDs to untrack.
|
|
93
|
-
*/
|
|
94
|
-
async untrackDocs(docIds) {
|
|
95
|
-
docIds = docIds.filter(id => this.trackedDocs.has(id));
|
|
96
|
-
if (!docIds.length)
|
|
97
|
-
return;
|
|
98
|
-
docIds.forEach(this.trackedDocs.delete, this.trackedDocs);
|
|
99
|
-
this.onUntrackDocs.emit(docIds);
|
|
100
|
-
// Close any open PatchesDoc instances first
|
|
101
|
-
const closedPromises = docIds.filter(id => this.docs.has(id)).map(id => this.closeDoc(id)); // closeDoc removes from this.docs map
|
|
102
|
-
await Promise.all(closedPromises);
|
|
103
|
-
// Remove from store
|
|
104
|
-
await this.store.untrackDocs(docIds);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Opens a document by ID, loading its state from the store and setting up change listeners.
|
|
108
|
-
* If the doc is already open, returns the existing instance.
|
|
109
|
-
* @param docId - The document ID to open.
|
|
110
|
-
* @param opts - Optional metadata to merge with the doc's metadata.
|
|
111
|
-
* @returns The opened PatchesDoc instance.
|
|
112
|
-
*/
|
|
113
|
-
async openDoc(docId, opts = {}) {
|
|
114
|
-
const existing = this.docs.get(docId);
|
|
115
|
-
if (existing)
|
|
116
|
-
return existing.doc;
|
|
117
|
-
// Ensure the doc is tracked before proceeding
|
|
118
|
-
await this.trackDocs([docId]);
|
|
119
|
-
// Load initial state from store
|
|
120
|
-
const snapshot = await this.store.getDoc(docId);
|
|
121
|
-
const initialState = (snapshot?.state ?? {});
|
|
122
|
-
const mergedMetadata = { ...this.options.metadata, ...opts.metadata };
|
|
123
|
-
const doc = new PatchesDoc(initialState, mergedMetadata, this.docOptions);
|
|
124
|
-
doc.setId(docId);
|
|
125
|
-
if (snapshot) {
|
|
126
|
-
doc.import(snapshot);
|
|
127
|
-
}
|
|
128
|
-
// Set up local listener -> store
|
|
129
|
-
const unsubscribe = doc.onChange(changes => this._savePendingChanges(docId, changes));
|
|
130
|
-
this.docs.set(docId, { doc, unsubscribe });
|
|
131
|
-
return doc;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Closes an open document by ID, removing listeners and optionally untracking it.
|
|
135
|
-
* @param docId - The document ID to close.
|
|
136
|
-
* @param options - Optional: set untrack to true to also untrack the doc.
|
|
137
|
-
*/
|
|
138
|
-
async closeDoc(docId, { untrack = false } = {}) {
|
|
139
|
-
const managed = this.docs.get(docId);
|
|
140
|
-
if (managed) {
|
|
141
|
-
managed.unsubscribe();
|
|
142
|
-
this.docs.delete(docId);
|
|
143
|
-
if (untrack) {
|
|
144
|
-
await this.untrackDocs([docId]);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Deletes a document by ID, closing it if open, untracking it, and removing it from the store.
|
|
150
|
-
* Emits the onDeleteDoc signal.
|
|
151
|
-
* @param docId - The document ID to delete.
|
|
152
|
-
*/
|
|
153
|
-
async deleteDoc(docId) {
|
|
154
|
-
// Close if open locally
|
|
155
|
-
if (this.docs.has(docId)) {
|
|
156
|
-
await this.closeDoc(docId);
|
|
157
|
-
}
|
|
158
|
-
// Unsubscribe from server if tracked (deletes the doc from the store before the next step adds a tombstone)
|
|
159
|
-
if (this.trackedDocs.has(docId)) {
|
|
160
|
-
await this.untrackDocs([docId]);
|
|
161
|
-
}
|
|
162
|
-
// Mark document as deleted in store (adds a tombstone until sync commits it)
|
|
163
|
-
await this.store.deleteDoc(docId);
|
|
164
|
-
this.onDeleteDoc.emit(docId);
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Gets an open document instance by ID, if it exists.
|
|
168
|
-
* Used by PatchesSync for applying server changes to open docs.
|
|
169
|
-
* @param docId - The document ID to get.
|
|
170
|
-
* @returns The PatchesDoc instance or undefined if not open.
|
|
171
|
-
*/
|
|
172
|
-
getOpenDoc(docId) {
|
|
173
|
-
return this.docs.get(docId)?.doc;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Closes all open documents and cleans up listeners and store connections.
|
|
177
|
-
* Should be called when shutting down the client.
|
|
178
|
-
*/
|
|
179
|
-
close() {
|
|
180
|
-
// Clean up local PatchesDoc listeners
|
|
181
|
-
this.docs.forEach(managed => managed.unsubscribe());
|
|
182
|
-
this.docs.clear();
|
|
183
|
-
// Close store connection
|
|
184
|
-
this.store.close();
|
|
185
|
-
this.onChange.clear();
|
|
186
|
-
this.onDeleteDoc.clear();
|
|
187
|
-
this.onUntrackDocs.clear();
|
|
188
|
-
this.onTrackDocs.clear();
|
|
189
|
-
this.onServerCommit.clear();
|
|
190
|
-
this.onError.clear();
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Internal handler for saving pending changes to the store.
|
|
194
|
-
* @param docId - The document ID to save the changes for.
|
|
195
|
-
* @param changes - The changes to save.
|
|
196
|
-
*/
|
|
197
|
-
async _savePendingChanges(docId, changes) {
|
|
198
|
-
try {
|
|
199
|
-
await this.store.savePendingChanges(docId, changes);
|
|
200
|
-
// Only after it is persisted, emit the change (for PatchesSync to flush)
|
|
201
|
-
this.onChange.emit(docId, changes);
|
|
202
|
-
}
|
|
203
|
-
catch (err) {
|
|
204
|
-
console.error(`Error saving pending changes for doc ${docId}:`, err);
|
|
205
|
-
this.onError.emit(err, { docId });
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
})();
|
|
210
|
-
export { Patches };
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { Signal, Unsubscriber } from '../event-signal.js';
|
|
2
|
+
import { PatchesSnapshot, SyncingState, Change, ChangeMutator } from '../types.js';
|
|
3
|
+
import '../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../json-patch/types.js';
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Options for creating a PatchesDoc instance
|
|
5
9
|
*/
|
|
6
|
-
|
|
10
|
+
interface PatchesDocOptions {
|
|
7
11
|
/**
|
|
8
12
|
* Maximum size in bytes for a single payload (network message).
|
|
9
13
|
* Changes exceeding this will be split into multiple smaller changes.
|
|
@@ -15,7 +19,7 @@ export interface PatchesDocOptions {
|
|
|
15
19
|
* Manages committed state, pending (local-only) changes, and
|
|
16
20
|
* changes currently being sent to the server.
|
|
17
21
|
*/
|
|
18
|
-
|
|
22
|
+
declare class PatchesDoc<T extends object = object> {
|
|
19
23
|
protected _id: string | null;
|
|
20
24
|
protected _state: T;
|
|
21
25
|
protected _snapshot: PatchesSnapshot<T>;
|
|
@@ -23,13 +27,13 @@ export declare class PatchesDoc<T extends object = object> {
|
|
|
23
27
|
protected _syncing: SyncingState;
|
|
24
28
|
protected readonly _maxPayloadBytes?: number;
|
|
25
29
|
/** Subscribe to be notified before local state changes. */
|
|
26
|
-
readonly onBeforeChange:
|
|
30
|
+
readonly onBeforeChange: Signal<(change: Change) => void>;
|
|
27
31
|
/** Subscribe to be notified after local state changes are applied. */
|
|
28
|
-
readonly onChange:
|
|
32
|
+
readonly onChange: Signal<(changes: Change[]) => void>;
|
|
29
33
|
/** Subscribe to be notified whenever state changes from any source. */
|
|
30
|
-
readonly onUpdate:
|
|
34
|
+
readonly onUpdate: Signal<(newState: T) => void>;
|
|
31
35
|
/** Subscribe to be notified when syncing state changes. */
|
|
32
|
-
readonly onSyncing:
|
|
36
|
+
readonly onSyncing: Signal<(newSyncing: SyncingState) => void>;
|
|
33
37
|
/**
|
|
34
38
|
* Creates an instance of PatchesDoc.
|
|
35
39
|
* @param initialState Optional initial state.
|
|
@@ -95,3 +99,5 @@ export declare class PatchesDoc<T extends object = object> {
|
|
|
95
99
|
updateSyncing(newSyncing: SyncingState): void;
|
|
96
100
|
toJSON(): PatchesSnapshot<T>;
|
|
97
101
|
}
|
|
102
|
+
|
|
103
|
+
export { PatchesDoc, type PatchesDocOptions };
|