@dabble/patches 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -208
- package/dist/BaseDoc-DkP3tUhT.d.ts +206 -0
- package/dist/algorithms/lww/consolidateOps.d.ts +40 -0
- package/dist/algorithms/lww/consolidateOps.js +103 -0
- package/dist/algorithms/lww/mergeServerWithLocal.d.ts +22 -0
- package/dist/algorithms/lww/mergeServerWithLocal.js +32 -0
- package/dist/algorithms/{client → ot/client}/applyCommittedChanges.d.ts +10 -3
- package/dist/algorithms/{client → ot/client}/applyCommittedChanges.js +7 -4
- package/dist/algorithms/{client → ot/client}/createStateFromSnapshot.d.ts +3 -3
- package/dist/algorithms/{client → ot/client}/createStateFromSnapshot.js +1 -1
- package/dist/algorithms/ot/server/commitChanges.d.ts +43 -0
- package/dist/algorithms/{server → ot/server}/commitChanges.js +22 -7
- package/dist/algorithms/{server → ot/server}/createVersion.d.ts +5 -5
- package/dist/algorithms/{server → ot/server}/createVersion.js +2 -2
- package/dist/algorithms/{server → ot/server}/getSnapshotAtRevision.d.ts +5 -5
- package/dist/algorithms/{server → ot/server}/getSnapshotAtRevision.js +1 -1
- package/dist/algorithms/{server → ot/server}/getStateAtRevision.d.ts +5 -5
- package/dist/algorithms/{server → ot/server}/getStateAtRevision.js +1 -1
- package/dist/algorithms/{server → ot/server}/handleOfflineSessionsAndBatches.d.ts +5 -5
- package/dist/algorithms/{server → ot/server}/handleOfflineSessionsAndBatches.js +3 -3
- package/dist/algorithms/{server → ot/server}/transformIncomingChanges.d.ts +3 -3
- package/dist/algorithms/{server → ot/server}/transformIncomingChanges.js +3 -3
- package/dist/algorithms/{shared → ot/shared}/applyChanges.d.ts +3 -3
- package/dist/algorithms/{shared → ot/shared}/applyChanges.js +2 -2
- package/dist/algorithms/{shared → ot/shared}/changeBatching.d.ts +3 -3
- package/dist/algorithms/{shared → ot/shared}/changeBatching.js +2 -2
- package/dist/algorithms/{shared → ot/shared}/rebaseChanges.d.ts +3 -3
- package/dist/algorithms/{shared → ot/shared}/rebaseChanges.js +2 -2
- package/dist/client/BaseDoc.d.ts +6 -0
- package/dist/client/BaseDoc.js +70 -0
- package/dist/client/ClientAlgorithm.d.ts +101 -0
- package/dist/client/ClientAlgorithm.js +0 -0
- package/dist/client/InMemoryStore.d.ts +5 -7
- package/dist/client/InMemoryStore.js +7 -36
- package/dist/client/IndexedDBStore.d.ts +39 -73
- package/dist/client/IndexedDBStore.js +17 -220
- package/dist/client/LWWAlgorithm.d.ts +43 -0
- package/dist/client/LWWAlgorithm.js +87 -0
- package/dist/client/LWWClientStore.d.ts +73 -0
- package/dist/client/LWWClientStore.js +0 -0
- package/dist/client/LWWDoc.d.ts +56 -0
- package/dist/client/LWWDoc.js +84 -0
- package/dist/client/LWWInMemoryStore.d.ts +88 -0
- package/dist/client/LWWInMemoryStore.js +208 -0
- package/dist/client/LWWIndexedDBStore.d.ts +91 -0
- package/dist/client/LWWIndexedDBStore.js +275 -0
- package/dist/client/OTAlgorithm.d.ts +42 -0
- package/dist/client/OTAlgorithm.js +113 -0
- package/dist/client/OTClientStore.d.ts +50 -0
- package/dist/client/OTClientStore.js +0 -0
- package/dist/client/OTDoc.d.ts +6 -0
- package/dist/client/OTDoc.js +97 -0
- package/dist/client/OTIndexedDBStore.d.ts +84 -0
- package/dist/client/OTIndexedDBStore.js +163 -0
- package/dist/client/Patches.d.ts +36 -16
- package/dist/client/Patches.js +60 -27
- package/dist/client/PatchesDoc.d.ts +4 -113
- package/dist/client/PatchesDoc.js +3 -153
- package/dist/client/PatchesHistoryClient.js +1 -1
- package/dist/client/PatchesStore.d.ts +8 -105
- package/dist/client/factories.d.ts +72 -0
- package/dist/client/factories.js +80 -0
- package/dist/client/index.d.ts +14 -5
- package/dist/client/index.js +9 -0
- package/dist/compression/index.d.ts +2 -2
- package/dist/compression/index.js +1 -1
- package/dist/{algorithms/shared → compression}/lz.js +1 -1
- package/dist/data/change.js +2 -0
- package/dist/fractionalIndex.d.ts +67 -0
- package/dist/fractionalIndex.js +241 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.js +1 -0
- package/dist/json-patch/types.d.ts +2 -0
- package/dist/net/PatchesClient.js +15 -15
- package/dist/net/PatchesSync.d.ts +20 -8
- package/dist/net/PatchesSync.js +57 -65
- package/dist/net/index.d.ts +7 -11
- package/dist/net/index.js +6 -1
- package/dist/net/protocol/JSONRPCClient.d.ts +4 -4
- package/dist/net/protocol/JSONRPCClient.js +6 -4
- package/dist/net/protocol/JSONRPCServer.d.ts +45 -9
- package/dist/net/protocol/JSONRPCServer.js +63 -8
- package/dist/net/serverContext.d.ts +38 -0
- package/dist/net/serverContext.js +20 -0
- package/dist/net/webrtc/WebRTCTransport.js +1 -1
- package/dist/net/websocket/AuthorizationProvider.d.ts +3 -3
- package/dist/net/websocket/WebSocketServer.d.ts +29 -20
- package/dist/net/websocket/WebSocketServer.js +23 -12
- package/dist/server/BranchManager.d.ts +50 -0
- package/dist/server/BranchManager.js +0 -0
- package/dist/server/CompressedStoreBackend.d.ts +10 -8
- package/dist/server/CompressedStoreBackend.js +7 -13
- package/dist/server/LWWBranchManager.d.ts +82 -0
- package/dist/server/LWWBranchManager.js +99 -0
- package/dist/server/LWWMemoryStoreBackend.d.ts +78 -0
- package/dist/server/LWWMemoryStoreBackend.js +182 -0
- package/dist/server/LWWServer.d.ts +130 -0
- package/dist/server/LWWServer.js +214 -0
- package/dist/server/{PatchesBranchManager.d.ts → OTBranchManager.d.ts} +32 -12
- package/dist/server/{PatchesBranchManager.js → OTBranchManager.js} +27 -42
- package/dist/server/OTServer.d.ts +108 -0
- package/dist/server/OTServer.js +141 -0
- package/dist/server/PatchesHistoryManager.d.ts +21 -16
- package/dist/server/PatchesHistoryManager.js +23 -11
- package/dist/server/PatchesServer.d.ts +70 -81
- package/dist/server/PatchesServer.js +0 -175
- package/dist/server/branchUtils.d.ts +82 -0
- package/dist/server/branchUtils.js +66 -0
- package/dist/server/index.d.ts +18 -7
- package/dist/server/index.js +33 -4
- package/dist/server/tombstone.d.ts +29 -0
- package/dist/server/tombstone.js +32 -0
- package/dist/server/types.d.ts +109 -27
- package/dist/server/utils.d.ts +12 -0
- package/dist/server/utils.js +23 -0
- package/dist/solid/context.d.ts +4 -3
- package/dist/solid/doc-manager.d.ts +3 -3
- package/dist/solid/index.d.ts +4 -3
- package/dist/solid/primitives.d.ts +2 -3
- package/dist/types.d.ts +4 -2
- package/dist/vue/composables.d.ts +2 -3
- package/dist/vue/doc-manager.d.ts +3 -3
- package/dist/vue/index.d.ts +4 -3
- package/dist/vue/provider.d.ts +4 -3
- package/package.json +1 -1
- package/dist/algorithms/client/collapsePendingChanges.d.ts +0 -30
- package/dist/algorithms/client/collapsePendingChanges.js +0 -78
- package/dist/algorithms/client/makeChange.d.ts +0 -9
- package/dist/algorithms/client/makeChange.js +0 -29
- package/dist/algorithms/server/commitChanges.d.ts +0 -19
- package/dist/net/websocket/RPCServer.d.ts +0 -141
- package/dist/net/websocket/RPCServer.js +0 -204
- /package/dist/{algorithms/shared → compression}/lz.d.ts +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { JSONPatchOp } from '../json-patch/types.js';
|
|
2
|
+
import { PatchesSnapshot, Change } from '../types.js';
|
|
3
|
+
import { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
4
|
+
import { OTClientStore } from './OTClientStore.js';
|
|
5
|
+
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-DkP3tUhT.js';
|
|
6
|
+
import { TrackedDoc } from './PatchesStore.js';
|
|
7
|
+
import '../json-patch/JSONPatch.js';
|
|
8
|
+
import '@dabble/delta';
|
|
9
|
+
import '../event-signal.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* OT (Operational Transformation) algorithm implementation.
|
|
13
|
+
*
|
|
14
|
+
* OT uses revision-based history and rebasing for concurrent edits.
|
|
15
|
+
* This algorithm owns an OT-compatible store and handles all OT-specific
|
|
16
|
+
* logic.
|
|
17
|
+
*/
|
|
18
|
+
declare class OTAlgorithm implements ClientAlgorithm {
|
|
19
|
+
readonly name = "ot";
|
|
20
|
+
readonly store: OTClientStore;
|
|
21
|
+
protected readonly _options: PatchesDocOptions;
|
|
22
|
+
constructor(store: OTClientStore, options?: PatchesDocOptions);
|
|
23
|
+
createDoc<T extends object>(docId: string, snapshot?: PatchesSnapshot<T>): PatchesDoc<T>;
|
|
24
|
+
loadDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
25
|
+
handleDocChange<T extends object>(docId: string, ops: JSONPatchOp[], doc: PatchesDoc<T> | undefined, metadata: Record<string, any>): Promise<Change[]>;
|
|
26
|
+
getPendingToSend(docId: string): Promise<Change[] | null>;
|
|
27
|
+
applyServerChanges<T extends object>(docId: string, serverChanges: Change[], doc: PatchesDoc<T> | undefined): Promise<Change[]>;
|
|
28
|
+
confirmSent(_docId: string, _changes: Change[]): Promise<void>;
|
|
29
|
+
trackDocs(docIds: string[]): Promise<void>;
|
|
30
|
+
untrackDocs(docIds: string[]): Promise<void>;
|
|
31
|
+
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
32
|
+
getCommittedRev(docId: string): Promise<number>;
|
|
33
|
+
deleteDoc(docId: string): Promise<void>;
|
|
34
|
+
confirmDeleteDoc(docId: string): Promise<void>;
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Creates Change objects from raw ops.
|
|
38
|
+
*/
|
|
39
|
+
protected _createChangesFromOps(committedRev: number, pendingRev: number, ops: JSONPatchOp[], metadata: Record<string, any>): Change[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { OTAlgorithm };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import "../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { applyCommittedChanges } from "../algorithms/ot/client/applyCommittedChanges.js";
|
|
3
|
+
import { breakChanges } from "../algorithms/ot/shared/changeBatching.js";
|
|
4
|
+
import { createChange } from "../data/change.js";
|
|
5
|
+
import { OTDoc } from "./OTDoc.js";
|
|
6
|
+
class OTAlgorithm {
|
|
7
|
+
name = "ot";
|
|
8
|
+
store;
|
|
9
|
+
_options;
|
|
10
|
+
constructor(store, options = {}) {
|
|
11
|
+
this.store = store;
|
|
12
|
+
this._options = options;
|
|
13
|
+
}
|
|
14
|
+
createDoc(docId, snapshot) {
|
|
15
|
+
return new OTDoc(docId, snapshot);
|
|
16
|
+
}
|
|
17
|
+
async loadDoc(docId) {
|
|
18
|
+
return this.store.getDoc(docId);
|
|
19
|
+
}
|
|
20
|
+
async handleDocChange(docId, ops, doc, metadata) {
|
|
21
|
+
if (ops.length === 0) return [];
|
|
22
|
+
let committedRev;
|
|
23
|
+
let pendingRev;
|
|
24
|
+
if (doc) {
|
|
25
|
+
const otDoc = doc;
|
|
26
|
+
const pendingChanges = otDoc.getPendingChanges();
|
|
27
|
+
committedRev = otDoc.committedRev;
|
|
28
|
+
pendingRev = pendingChanges[pendingChanges.length - 1]?.rev ?? committedRev;
|
|
29
|
+
} else {
|
|
30
|
+
const snapshot = await this.store.getDoc(docId);
|
|
31
|
+
committedRev = snapshot?.rev ?? 0;
|
|
32
|
+
const pendingChanges = snapshot?.changes ?? [];
|
|
33
|
+
pendingRev = pendingChanges[pendingChanges.length - 1]?.rev ?? committedRev;
|
|
34
|
+
}
|
|
35
|
+
const changes = this._createChangesFromOps(committedRev, pendingRev, ops, metadata);
|
|
36
|
+
if (changes.length === 0) return [];
|
|
37
|
+
await this.store.savePendingChanges(docId, changes);
|
|
38
|
+
if (doc) {
|
|
39
|
+
doc.applyChanges(changes);
|
|
40
|
+
}
|
|
41
|
+
return changes;
|
|
42
|
+
}
|
|
43
|
+
async getPendingToSend(docId) {
|
|
44
|
+
const pending = await this.store.getPendingChanges(docId);
|
|
45
|
+
return pending.length > 0 ? pending : null;
|
|
46
|
+
}
|
|
47
|
+
async applyServerChanges(docId, serverChanges, doc) {
|
|
48
|
+
if (serverChanges.length === 0) return [];
|
|
49
|
+
const currentSnapshot = await this.store.getDoc(docId);
|
|
50
|
+
if (!currentSnapshot) {
|
|
51
|
+
console.warn(`Cannot apply server changes to non-existent doc: ${docId}`);
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
if (doc) {
|
|
55
|
+
const otDoc = doc;
|
|
56
|
+
const inMemoryPending = otDoc.getPendingChanges();
|
|
57
|
+
const latestRev = currentSnapshot.changes[currentSnapshot.changes.length - 1]?.rev ?? currentSnapshot.rev;
|
|
58
|
+
const newChanges = inMemoryPending.filter((change) => change.rev > latestRev);
|
|
59
|
+
currentSnapshot.changes.push(...newChanges);
|
|
60
|
+
}
|
|
61
|
+
const newSnapshot = applyCommittedChanges(currentSnapshot, serverChanges);
|
|
62
|
+
await this.store.applyServerChanges(docId, serverChanges, newSnapshot.changes);
|
|
63
|
+
const changesToBroadcast = [...serverChanges, ...newSnapshot.changes];
|
|
64
|
+
if (doc) {
|
|
65
|
+
const otDoc = doc;
|
|
66
|
+
if (otDoc.committedRev === serverChanges[0].rev - 1) {
|
|
67
|
+
otDoc.applyChanges(changesToBroadcast);
|
|
68
|
+
} else {
|
|
69
|
+
otDoc.import(newSnapshot);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return changesToBroadcast;
|
|
73
|
+
}
|
|
74
|
+
async confirmSent(_docId, _changes) {
|
|
75
|
+
}
|
|
76
|
+
// --- Store forwarding methods ---
|
|
77
|
+
async trackDocs(docIds) {
|
|
78
|
+
return this.store.trackDocs(docIds);
|
|
79
|
+
}
|
|
80
|
+
async untrackDocs(docIds) {
|
|
81
|
+
return this.store.untrackDocs(docIds);
|
|
82
|
+
}
|
|
83
|
+
async listDocs(includeDeleted) {
|
|
84
|
+
return this.store.listDocs(includeDeleted);
|
|
85
|
+
}
|
|
86
|
+
async getCommittedRev(docId) {
|
|
87
|
+
return this.store.getCommittedRev(docId);
|
|
88
|
+
}
|
|
89
|
+
async deleteDoc(docId) {
|
|
90
|
+
return this.store.deleteDoc(docId);
|
|
91
|
+
}
|
|
92
|
+
async confirmDeleteDoc(docId) {
|
|
93
|
+
return this.store.confirmDeleteDoc(docId);
|
|
94
|
+
}
|
|
95
|
+
async close() {
|
|
96
|
+
return this.store.close();
|
|
97
|
+
}
|
|
98
|
+
// --- Private helpers ---
|
|
99
|
+
/**
|
|
100
|
+
* Creates Change objects from raw ops.
|
|
101
|
+
*/
|
|
102
|
+
_createChangesFromOps(committedRev, pendingRev, ops, metadata) {
|
|
103
|
+
const rev = pendingRev + 1;
|
|
104
|
+
let changes = [createChange(committedRev, rev, ops, metadata)];
|
|
105
|
+
if (this._options.maxStorageBytes) {
|
|
106
|
+
changes = breakChanges(changes, this._options.maxStorageBytes, this._options.sizeCalculator);
|
|
107
|
+
}
|
|
108
|
+
return changes;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
OTAlgorithm
|
|
113
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Change } from '../types.js';
|
|
2
|
+
import { PatchesStore } from './PatchesStore.js';
|
|
3
|
+
import '../json-patch/JSONPatch.js';
|
|
4
|
+
import '@dabble/delta';
|
|
5
|
+
import '../json-patch/types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OT-specific client store interface.
|
|
9
|
+
* Extends PatchesStore with methods for managing pending change lists
|
|
10
|
+
* and applying server changes with rebasing.
|
|
11
|
+
*/
|
|
12
|
+
interface OTClientStore extends PatchesStore {
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves all pending (unconfirmed) changes for a document.
|
|
15
|
+
*
|
|
16
|
+
* Pending changes are local edits that haven't been confirmed by the server yet.
|
|
17
|
+
* Returns changes in chronological order as they were created locally.
|
|
18
|
+
* Used during sync to resend unconfirmed operations.
|
|
19
|
+
*
|
|
20
|
+
* @param docId Document identifier
|
|
21
|
+
* @returns Array of pending changes in chronological order
|
|
22
|
+
*/
|
|
23
|
+
getPendingChanges(docId: string): Promise<Change[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Appends new pending changes to the document's local change queue.
|
|
26
|
+
*
|
|
27
|
+
* Adds changes to the end of the pending changes list without replacing existing ones.
|
|
28
|
+
* Called when the user makes local edits that haven't been sent to the server yet.
|
|
29
|
+
* Changes should have sequential revision numbers starting after the last pending change.
|
|
30
|
+
*
|
|
31
|
+
* @param docId Document identifier
|
|
32
|
+
* @param changes Array of new changes to append
|
|
33
|
+
*/
|
|
34
|
+
savePendingChanges(docId: string, changes: Change[]): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Atomically applies server-confirmed changes and updates pending changes.
|
|
37
|
+
*
|
|
38
|
+
* This is the core sync operation that must be atomic: server changes become
|
|
39
|
+
* committed history, and pending changes are replaced with their rebased versions.
|
|
40
|
+
* Implementations must ensure both operations complete together (single transaction
|
|
41
|
+
* for databases) to prevent inconsistent state if the app crashes mid-operation.
|
|
42
|
+
*
|
|
43
|
+
* @param docId Document identifier
|
|
44
|
+
* @param serverChanges Changes confirmed by the server to add to committed history
|
|
45
|
+
* @param rebasedPendingChanges Pending changes after OT rebasing (replaces all existing pending)
|
|
46
|
+
*/
|
|
47
|
+
applyServerChanges(docId: string, serverChanges: Change[], rebasedPendingChanges: Change[]): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type { OTClientStore };
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import "../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { createStateFromSnapshot } from "../algorithms/ot/client/createStateFromSnapshot.js";
|
|
3
|
+
import { applyChanges as applyChangesToState } from "../algorithms/ot/shared/applyChanges.js";
|
|
4
|
+
import { BaseDoc } from "./BaseDoc.js";
|
|
5
|
+
class OTDoc extends BaseDoc {
|
|
6
|
+
/** Base state from the server at the committed revision. */
|
|
7
|
+
_committedState;
|
|
8
|
+
/** Last committed revision number from the server. */
|
|
9
|
+
_committedRev;
|
|
10
|
+
/** Local changes not yet committed by server. */
|
|
11
|
+
_pendingChanges;
|
|
12
|
+
/**
|
|
13
|
+
* Creates an instance of OTDoc.
|
|
14
|
+
* @param id The unique identifier for this document.
|
|
15
|
+
* @param snapshot Optional snapshot to initialize from (state, rev, pending changes).
|
|
16
|
+
*/
|
|
17
|
+
constructor(id, snapshot) {
|
|
18
|
+
const initialState = snapshot?.state ?? {};
|
|
19
|
+
super(id, initialState);
|
|
20
|
+
this._committedState = this._state;
|
|
21
|
+
this._committedRev = snapshot?.rev ?? 0;
|
|
22
|
+
this._pendingChanges = snapshot?.changes ?? [];
|
|
23
|
+
if (this._pendingChanges.length > 0) {
|
|
24
|
+
this._state = applyChangesToState(this._committedState, this._pendingChanges);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Last committed revision number from the server. */
|
|
28
|
+
get committedRev() {
|
|
29
|
+
return this._committedRev;
|
|
30
|
+
}
|
|
31
|
+
/** Are there local changes that haven't been committed yet? */
|
|
32
|
+
get hasPending() {
|
|
33
|
+
return this._pendingChanges.length > 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns the pending changes for this document.
|
|
37
|
+
* @returns The pending changes.
|
|
38
|
+
*/
|
|
39
|
+
getPendingChanges() {
|
|
40
|
+
return this._pendingChanges;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Imports document state from a snapshot (e.g., for recovery when out of sync).
|
|
44
|
+
* Resets state and treats all imported changes as pending.
|
|
45
|
+
*/
|
|
46
|
+
import(snapshot) {
|
|
47
|
+
this._committedState = snapshot.state;
|
|
48
|
+
this._committedRev = snapshot.rev;
|
|
49
|
+
this._pendingChanges = snapshot.changes;
|
|
50
|
+
this._state = createStateFromSnapshot(snapshot);
|
|
51
|
+
this.onUpdate.emit(this._state);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Unified entry point for applying changes.
|
|
55
|
+
* Used for Worker→Tab communication where only changes are sent over the wire.
|
|
56
|
+
*
|
|
57
|
+
* The method distinguishes between committed and pending changes using `committedAt`:
|
|
58
|
+
* - `committedAt > 0`: Server-committed change (apply to committed state)
|
|
59
|
+
* - `committedAt === 0`: Pending local change (append to pending)
|
|
60
|
+
*
|
|
61
|
+
* For server changes, all committed changes come first, followed by rebased pending.
|
|
62
|
+
*
|
|
63
|
+
* @param changes Array of changes to apply
|
|
64
|
+
*/
|
|
65
|
+
applyChanges(changes) {
|
|
66
|
+
if (changes.length === 0) return;
|
|
67
|
+
if (changes[0].committedAt > 0) {
|
|
68
|
+
const serverEndIndex = changes.findIndex((c) => c.committedAt === 0);
|
|
69
|
+
const serverChanges = serverEndIndex === -1 ? changes : changes.slice(0, serverEndIndex);
|
|
70
|
+
const rebasedPending = serverEndIndex === -1 ? [] : changes.slice(serverEndIndex);
|
|
71
|
+
if (this._committedRev !== serverChanges[0].rev - 1) {
|
|
72
|
+
throw new Error("Cannot apply committed changes to a doc that is not at the correct revision");
|
|
73
|
+
}
|
|
74
|
+
this._committedState = applyChangesToState(this._committedState, serverChanges);
|
|
75
|
+
this._committedRev = serverChanges[serverChanges.length - 1].rev;
|
|
76
|
+
this._pendingChanges = rebasedPending;
|
|
77
|
+
this._state = applyChangesToState(this._committedState, this._pendingChanges);
|
|
78
|
+
} else {
|
|
79
|
+
this._state = applyChangesToState(this._state, changes);
|
|
80
|
+
this._pendingChanges.push(...changes);
|
|
81
|
+
}
|
|
82
|
+
this.onUpdate.emit(this._state);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns the document snapshot for serialization.
|
|
86
|
+
*/
|
|
87
|
+
toJSON() {
|
|
88
|
+
return {
|
|
89
|
+
state: this._committedState,
|
|
90
|
+
rev: this._committedRev,
|
|
91
|
+
changes: this._pendingChanges
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
OTDoc
|
|
97
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { PatchesSnapshot, PatchesState, Change } from '../types.js';
|
|
2
|
+
import { IndexedDBStore } from './IndexedDBStore.js';
|
|
3
|
+
import { OTClientStore } from './OTClientStore.js';
|
|
4
|
+
import '../json-patch/JSONPatch.js';
|
|
5
|
+
import '@dabble/delta';
|
|
6
|
+
import '../json-patch/types.js';
|
|
7
|
+
import '../utils/deferred.js';
|
|
8
|
+
import './PatchesStore.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* IndexedDB store implementation for Operational Transformation (OT) sync strategy.
|
|
12
|
+
*
|
|
13
|
+
* Creates stores:
|
|
14
|
+
* - snapshots<{ docId: string; rev: number; state: any }> (primary key: docId)
|
|
15
|
+
* - committedChanges<Change & { docId: string; }> (primary key: [docId, rev])
|
|
16
|
+
* - pendingChanges<Change & { docId: string; }> (primary key: [docId, rev])
|
|
17
|
+
* - docs<{ docId: string; committedRev: number; deleted?: boolean }> (primary key: docId)
|
|
18
|
+
*
|
|
19
|
+
* Under the hood, this class stores snapshots of the document only for committed state.
|
|
20
|
+
* It does not update the committed state on *every* received committed change as this
|
|
21
|
+
* can cause issues with IndexedDB with many small updates.
|
|
22
|
+
*
|
|
23
|
+
* After every 200 committed changes (SNAPSHOT_INTERVAL), the class saves the current
|
|
24
|
+
* state to the snapshot store and deletes the committed changes that went into it.
|
|
25
|
+
* A snapshot will not be created if there are pending changes based on revisions older
|
|
26
|
+
* than the 200th committed change until those pending changes are committed.
|
|
27
|
+
*/
|
|
28
|
+
declare class OTIndexedDBStore extends IndexedDBStore implements OTClientStore {
|
|
29
|
+
protected getDBVersion(): number;
|
|
30
|
+
protected onUpgrade(db: IDBDatabase, _oldVersion: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* Rebuilds a document snapshot + pending queue *without* loading
|
|
33
|
+
* the full PatchesDoc into memory.
|
|
34
|
+
*
|
|
35
|
+
* 1. load the last snapshot (state + rev)
|
|
36
|
+
* 2. load committedChanges[rev > snapshot.rev]
|
|
37
|
+
* 3. load pendingChanges
|
|
38
|
+
* 4. apply committed changes, rebase pending
|
|
39
|
+
* 5. return { state, rev, changes: pending }
|
|
40
|
+
*/
|
|
41
|
+
getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Completely remove all data for this docId and mark it as deleted (tombstone).
|
|
44
|
+
*/
|
|
45
|
+
deleteDoc(docId: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Save a document's state to the store.
|
|
48
|
+
* @param docId - The ID of the document to save.
|
|
49
|
+
* @param docState - The state of the document to save.
|
|
50
|
+
*/
|
|
51
|
+
saveDoc(docId: string, docState: PatchesState): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Append an array of local changes to the pending queue.
|
|
54
|
+
* Called *before* you attempt to send them to the server.
|
|
55
|
+
*/
|
|
56
|
+
savePendingChanges(docId: string, changes: Change[]): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Read back all pending changes for this docId (in order).
|
|
59
|
+
* @param docId - The ID of the document to get the pending changes for.
|
|
60
|
+
* @returns The pending changes.
|
|
61
|
+
*/
|
|
62
|
+
getPendingChanges(docId: string): Promise<Change[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Atomically applies server-confirmed changes and updates pending changes.
|
|
65
|
+
* This is the core sync operation that must be atomic.
|
|
66
|
+
*
|
|
67
|
+
* Will:
|
|
68
|
+
* - persist server changes in the committedChanges store
|
|
69
|
+
* - replace all pending changes with the rebased versions
|
|
70
|
+
* - optionally compact a new snapshot after N changes (hidden internally)
|
|
71
|
+
*
|
|
72
|
+
* @param docId - The ID of the document
|
|
73
|
+
* @param serverChanges - Changes confirmed by the server
|
|
74
|
+
* @param rebasedPendingChanges - Pending changes after OT rebasing
|
|
75
|
+
*/
|
|
76
|
+
applyServerChanges(docId: string, serverChanges: Change[], rebasedPendingChanges: Change[]): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Untrack a document.
|
|
79
|
+
* @param docIds - The IDs of the documents to untrack.
|
|
80
|
+
*/
|
|
81
|
+
untrackDocs(docIds: string[]): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { OTIndexedDBStore };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__decorateElement,
|
|
3
|
+
__decoratorMetadata,
|
|
4
|
+
__decoratorStart,
|
|
5
|
+
__runInitializers
|
|
6
|
+
} from "../chunk-IZ2YBCUP.js";
|
|
7
|
+
var _applyServerChanges_dec, _getPendingChanges_dec, _savePendingChanges_dec, _saveDoc_dec, _deleteDoc_dec, _getDoc_dec, _a, _init;
|
|
8
|
+
import { applyChanges } from "../algorithms/ot/shared/applyChanges.js";
|
|
9
|
+
import { blockable } from "../utils/concurrency.js";
|
|
10
|
+
import { IndexedDBStore } from "./IndexedDBStore.js";
|
|
11
|
+
const DB_VERSION = 1;
|
|
12
|
+
const SNAPSHOT_INTERVAL = 200;
|
|
13
|
+
class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable], _deleteDoc_dec = [blockable], _saveDoc_dec = [blockable], _savePendingChanges_dec = [blockable], _getPendingChanges_dec = [blockable], _applyServerChanges_dec = [blockable], _a) {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
__runInitializers(_init, 5, this);
|
|
17
|
+
}
|
|
18
|
+
getDBVersion() {
|
|
19
|
+
return DB_VERSION;
|
|
20
|
+
}
|
|
21
|
+
onUpgrade(db, _oldVersion) {
|
|
22
|
+
if (!db.objectStoreNames.contains("committedChanges")) {
|
|
23
|
+
db.createObjectStore("committedChanges", { keyPath: ["docId", "rev"] });
|
|
24
|
+
}
|
|
25
|
+
if (!db.objectStoreNames.contains("pendingChanges")) {
|
|
26
|
+
db.createObjectStore("pendingChanges", { keyPath: ["docId", "rev"] });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async getDoc(docId) {
|
|
30
|
+
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.transaction(
|
|
31
|
+
["docs", "snapshots", "committedChanges", "pendingChanges"],
|
|
32
|
+
"readonly"
|
|
33
|
+
);
|
|
34
|
+
const docMeta = await docsStore.get(docId);
|
|
35
|
+
if (docMeta?.deleted) {
|
|
36
|
+
await tx.complete();
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
const snapshot = await snapshots.get(docId);
|
|
40
|
+
const committed = await committedChanges.getAll([docId, snapshot?.rev ?? 0], [docId, Infinity]);
|
|
41
|
+
const pending = await pendingChanges.getAll([docId, 0], [docId, Infinity]);
|
|
42
|
+
if (!snapshot && !committed.length && !pending.length) return void 0;
|
|
43
|
+
const state = applyChanges(snapshot?.state, committed);
|
|
44
|
+
await tx.complete();
|
|
45
|
+
return {
|
|
46
|
+
state,
|
|
47
|
+
rev: committed[committed.length - 1]?.rev ?? snapshot?.rev ?? 0,
|
|
48
|
+
changes: pending
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async deleteDoc(docId) {
|
|
52
|
+
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.transaction(
|
|
53
|
+
["snapshots", "committedChanges", "pendingChanges", "docs"],
|
|
54
|
+
"readwrite"
|
|
55
|
+
);
|
|
56
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
57
|
+
await docsStore.put({ ...docMeta, deleted: true });
|
|
58
|
+
await Promise.all([
|
|
59
|
+
snapshots.delete(docId),
|
|
60
|
+
committedChanges.delete([docId, 0], [docId, Infinity]),
|
|
61
|
+
pendingChanges.delete([docId, 0], [docId, Infinity])
|
|
62
|
+
]);
|
|
63
|
+
await tx.complete();
|
|
64
|
+
}
|
|
65
|
+
async saveDoc(docId, docState) {
|
|
66
|
+
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.transaction(
|
|
67
|
+
["snapshots", "committedChanges", "pendingChanges", "docs"],
|
|
68
|
+
"readwrite"
|
|
69
|
+
);
|
|
70
|
+
const { rev, state } = docState;
|
|
71
|
+
await Promise.all([
|
|
72
|
+
docsStore.put({ docId, committedRev: rev }),
|
|
73
|
+
snapshots.put({ docId, state, rev }),
|
|
74
|
+
committedChanges.delete([docId, 0], [docId, Infinity]),
|
|
75
|
+
pendingChanges.delete([docId, 0], [docId, Infinity])
|
|
76
|
+
]);
|
|
77
|
+
await tx.complete();
|
|
78
|
+
}
|
|
79
|
+
async savePendingChanges(docId, changes) {
|
|
80
|
+
const [tx, pendingChanges, docsStore] = await this.transaction(["pendingChanges", "docs"], "readwrite");
|
|
81
|
+
let docMeta = await docsStore.get(docId);
|
|
82
|
+
if (!docMeta) {
|
|
83
|
+
docMeta = { docId, committedRev: 0 };
|
|
84
|
+
await docsStore.put(docMeta);
|
|
85
|
+
} else if (docMeta.deleted) {
|
|
86
|
+
delete docMeta.deleted;
|
|
87
|
+
await docsStore.put(docMeta);
|
|
88
|
+
console.warn(`Revived document ${docId} by saving pending changes.`);
|
|
89
|
+
}
|
|
90
|
+
await Promise.all(changes.map((change) => pendingChanges.put({ ...change, docId })));
|
|
91
|
+
await tx.complete();
|
|
92
|
+
}
|
|
93
|
+
async getPendingChanges(docId) {
|
|
94
|
+
const [tx, pendingChanges] = await this.transaction(["pendingChanges"], "readonly");
|
|
95
|
+
const result = await pendingChanges.getAll([docId, 0], [docId, Infinity]);
|
|
96
|
+
await tx.complete();
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
async applyServerChanges(docId, serverChanges, rebasedPendingChanges) {
|
|
100
|
+
const [tx, committedChangesStore, pendingChangesStore, snapshots, docsStore] = await this.transaction(
|
|
101
|
+
["committedChanges", "pendingChanges", "snapshots", "docs"],
|
|
102
|
+
"readwrite"
|
|
103
|
+
);
|
|
104
|
+
await Promise.all(serverChanges.map((change) => committedChangesStore.put({ ...change, docId })));
|
|
105
|
+
await pendingChangesStore.delete([docId, 0], [docId, Infinity]);
|
|
106
|
+
await Promise.all(rebasedPendingChanges.map((change) => pendingChangesStore.put({ ...change, docId })));
|
|
107
|
+
const count = await committedChangesStore.count([docId, 0], [docId, Infinity]);
|
|
108
|
+
if (count >= SNAPSHOT_INTERVAL) {
|
|
109
|
+
const [snapshot, committed] = await Promise.all([
|
|
110
|
+
snapshots.get(docId),
|
|
111
|
+
committedChangesStore.getAll([docId, 0], [docId, Infinity], SNAPSHOT_INTERVAL)
|
|
112
|
+
]);
|
|
113
|
+
const lastRev = committed[committed.length - 1]?.rev;
|
|
114
|
+
if (lastRev) {
|
|
115
|
+
const state = applyChanges(snapshot?.state, committed);
|
|
116
|
+
await Promise.all([
|
|
117
|
+
snapshots.put({ docId, rev: lastRev, state }),
|
|
118
|
+
committedChangesStore.delete([docId, 0], [docId, lastRev])
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const lastCommittedRev = serverChanges.at(-1)?.rev;
|
|
123
|
+
if (lastCommittedRev !== void 0) {
|
|
124
|
+
const docMeta = await docsStore.get(docId) ?? { docId, committedRev: 0 };
|
|
125
|
+
if (lastCommittedRev > docMeta.committedRev) {
|
|
126
|
+
await docsStore.put({ ...docMeta, committedRev: lastCommittedRev, deleted: void 0 });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await tx.complete();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Untrack a document.
|
|
133
|
+
* @param docIds - The IDs of the documents to untrack.
|
|
134
|
+
*/
|
|
135
|
+
async untrackDocs(docIds) {
|
|
136
|
+
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.transaction(
|
|
137
|
+
["docs", "snapshots", "committedChanges", "pendingChanges"],
|
|
138
|
+
"readwrite"
|
|
139
|
+
);
|
|
140
|
+
await Promise.all(
|
|
141
|
+
docIds.map((docId) => {
|
|
142
|
+
return Promise.all([
|
|
143
|
+
docsStore.delete(docId),
|
|
144
|
+
snapshots.delete(docId),
|
|
145
|
+
committedChanges.delete([docId, 0], [docId, Infinity]),
|
|
146
|
+
pendingChanges.delete([docId, 0], [docId, Infinity])
|
|
147
|
+
]);
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
await tx.complete();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
_init = __decoratorStart(_a);
|
|
154
|
+
__decorateElement(_init, 1, "getDoc", _getDoc_dec, OTIndexedDBStore);
|
|
155
|
+
__decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, OTIndexedDBStore);
|
|
156
|
+
__decorateElement(_init, 1, "saveDoc", _saveDoc_dec, OTIndexedDBStore);
|
|
157
|
+
__decorateElement(_init, 1, "savePendingChanges", _savePendingChanges_dec, OTIndexedDBStore);
|
|
158
|
+
__decorateElement(_init, 1, "getPendingChanges", _getPendingChanges_dec, OTIndexedDBStore);
|
|
159
|
+
__decorateElement(_init, 1, "applyServerChanges", _applyServerChanges_dec, OTIndexedDBStore);
|
|
160
|
+
__decoratorMetadata(_init, OTIndexedDBStore);
|
|
161
|
+
export {
|
|
162
|
+
OTIndexedDBStore
|
|
163
|
+
};
|
package/dist/client/Patches.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { Unsubscriber, Signal } from '../event-signal.js';
|
|
2
|
+
import { JSONPatchOp } from '../json-patch/types.js';
|
|
2
3
|
import { Change } from '../types.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { AlgorithmName, ClientAlgorithm } from './ClientAlgorithm.js';
|
|
5
|
+
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-DkP3tUhT.js';
|
|
5
6
|
import '../json-patch/JSONPatch.js';
|
|
6
7
|
import '@dabble/delta';
|
|
7
|
-
import '
|
|
8
|
-
import '../algorithms/shared/changeBatching.js';
|
|
8
|
+
import './PatchesStore.js';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Options for creating a Patches instance.
|
|
12
|
+
* Provides algorithms map and optional default algorithm.
|
|
13
|
+
*/
|
|
10
14
|
interface PatchesOptions {
|
|
11
|
-
/**
|
|
12
|
-
|
|
15
|
+
/** Map of algorithm name to algorithm instance. Each algorithm owns its store. */
|
|
16
|
+
algorithms: Partial<Record<AlgorithmName, ClientAlgorithm>>;
|
|
17
|
+
/** Default algorithm to use when opening docs. Must be a key in algorithms map. */
|
|
18
|
+
defaultAlgorithm?: AlgorithmName;
|
|
13
19
|
/** Initial metadata to attach to changes from this client (merged with per-doc metadata). */
|
|
14
20
|
metadata?: Record<string, any>;
|
|
15
21
|
/** Document-level options to pass to each PatchesDoc instance */
|
|
@@ -17,18 +23,22 @@ interface PatchesOptions {
|
|
|
17
23
|
}
|
|
18
24
|
interface ManagedDoc<T extends object> {
|
|
19
25
|
doc: PatchesDoc<T>;
|
|
26
|
+
algorithm: ClientAlgorithm;
|
|
20
27
|
unsubscribe: Unsubscriber;
|
|
21
28
|
}
|
|
22
29
|
/**
|
|
23
30
|
* Main client-side entry point for the Patches library.
|
|
24
|
-
* Manages document instances (`PatchesDoc`) and
|
|
31
|
+
* Manages document instances (`PatchesDoc`) and coordinates with algorithms.
|
|
25
32
|
* Can be used standalone or with PatchesSync for network synchronization.
|
|
33
|
+
*
|
|
34
|
+
* Patches owns docs. Algorithms own their stores.
|
|
26
35
|
*/
|
|
27
36
|
declare class Patches {
|
|
28
37
|
protected options: PatchesOptions;
|
|
29
38
|
protected docs: Map<string, ManagedDoc<any>>;
|
|
30
39
|
readonly docOptions: PatchesDocOptions;
|
|
31
|
-
readonly
|
|
40
|
+
readonly algorithms: Partial<Record<AlgorithmName, ClientAlgorithm>>;
|
|
41
|
+
readonly defaultAlgorithm: AlgorithmName;
|
|
32
42
|
readonly trackedDocs: Set<string>;
|
|
33
43
|
readonly onError: Signal<(error: Error, context?: {
|
|
34
44
|
docId?: string;
|
|
@@ -37,15 +47,25 @@ declare class Patches {
|
|
|
37
47
|
readonly onTrackDocs: Signal<(docIds: string[]) => void>;
|
|
38
48
|
readonly onUntrackDocs: Signal<(docIds: string[]) => void>;
|
|
39
49
|
readonly onDeleteDoc: Signal<(docId: string) => void>;
|
|
40
|
-
|
|
50
|
+
/** Emitted when a doc has pending changes ready to send */
|
|
51
|
+
readonly onChange: Signal<(docId: string) => void>;
|
|
41
52
|
constructor(opts: PatchesOptions);
|
|
53
|
+
/**
|
|
54
|
+
* Gets an algorithm by name, throwing if not found.
|
|
55
|
+
*/
|
|
56
|
+
protected _getAlgorithm(name: AlgorithmName): ClientAlgorithm;
|
|
57
|
+
/**
|
|
58
|
+
* Gets the algorithm for an open document.
|
|
59
|
+
*/
|
|
60
|
+
getDocAlgorithm(docId: string): ClientAlgorithm | undefined;
|
|
42
61
|
/**
|
|
43
62
|
* Tracks the given document IDs, adding them to the set of tracked documents and notifying listeners.
|
|
44
63
|
* Tracked docs are kept in sync with the server, even when not open locally.
|
|
45
64
|
* This allows for background syncing and updates of unopened documents.
|
|
46
65
|
* @param docIds - Array of document IDs to track.
|
|
66
|
+
* @param algorithmName - Algorithm to use for tracking (defaults to defaultAlgorithm).
|
|
47
67
|
*/
|
|
48
|
-
trackDocs(docIds: string[]): Promise<void>;
|
|
68
|
+
trackDocs(docIds: string[], algorithmName?: AlgorithmName): Promise<void>;
|
|
49
69
|
/**
|
|
50
70
|
* Untracks the given document IDs, removing them from the set of tracked documents and notifying listeners.
|
|
51
71
|
* Untracked docs will no longer be kept in sync with the server.
|
|
@@ -57,11 +77,12 @@ declare class Patches {
|
|
|
57
77
|
* Opens a document by ID, loading its state from the store and setting up change listeners.
|
|
58
78
|
* If the doc is already open, returns the existing instance.
|
|
59
79
|
* @param docId - The document ID to open.
|
|
60
|
-
* @param opts - Optional metadata
|
|
80
|
+
* @param opts - Optional metadata and algorithm override.
|
|
61
81
|
* @returns The opened PatchesDoc instance.
|
|
62
82
|
*/
|
|
63
83
|
openDoc<T extends object>(docId: string, opts?: {
|
|
64
84
|
metadata?: Record<string, any>;
|
|
85
|
+
algorithm?: AlgorithmName;
|
|
65
86
|
}): Promise<PatchesDoc<T>>;
|
|
66
87
|
/**
|
|
67
88
|
* Closes an open document by ID, removing listeners and optionally untracking it.
|
|
@@ -88,13 +109,12 @@ declare class Patches {
|
|
|
88
109
|
* Closes all open documents and cleans up listeners and store connections.
|
|
89
110
|
* Should be called when shutting down the client.
|
|
90
111
|
*/
|
|
91
|
-
close(): void
|
|
112
|
+
close(): Promise<void>;
|
|
92
113
|
/**
|
|
93
|
-
* Internal handler for
|
|
94
|
-
*
|
|
95
|
-
* @param changes - The changes to save.
|
|
114
|
+
* Internal handler for doc changes. Called when doc.onChange emits ops.
|
|
115
|
+
* Delegates to algorithm for packaging and persisting.
|
|
96
116
|
*/
|
|
97
|
-
protected
|
|
117
|
+
protected _handleDocChange<T extends object>(docId: string, ops: JSONPatchOp[], doc: PatchesDoc<T>, algorithm: ClientAlgorithm, metadata: Record<string, any>): Promise<void>;
|
|
98
118
|
}
|
|
99
119
|
|
|
100
120
|
export { Patches, type PatchesOptions };
|