@dabble/patches 0.7.4 → 0.7.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/client/ClientAlgorithm.d.ts +2 -4
- package/dist/client/IndexedDBStore.d.ts +29 -23
- package/dist/client/IndexedDBStore.js +90 -14
- package/dist/client/LWWAlgorithm.js +1 -1
- package/dist/client/LWWInMemoryStore.d.ts +1 -1
- package/dist/client/LWWInMemoryStore.js +1 -1
- package/dist/client/LWWIndexedDBStore.d.ts +38 -4
- package/dist/client/LWWIndexedDBStore.js +72 -21
- package/dist/client/OTAlgorithm.js +1 -1
- package/dist/client/{InMemoryStore.d.ts → OTInMemoryStore.d.ts} +4 -4
- package/dist/client/{InMemoryStore.js → OTInMemoryStore.js} +8 -4
- package/dist/client/OTIndexedDBStore.d.ts +40 -6
- package/dist/client/OTIndexedDBStore.js +66 -18
- package/dist/client/Patches.d.ts +2 -2
- package/dist/client/Patches.js +12 -1
- package/dist/client/PatchesStore.d.ts +9 -4
- package/dist/client/factories.d.ts +7 -6
- package/dist/client/factories.js +11 -9
- package/dist/client/index.d.ts +4 -4
- package/dist/client/index.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/net/PatchesSync.d.ts +2 -2
- package/dist/net/PatchesSync.js +27 -9
- package/dist/server/LWWServer.js +1 -1
- package/dist/solid/context.d.ts +2 -2
- package/dist/vue/provider.d.ts +2 -2
- package/package.json +1 -1
|
@@ -80,7 +80,7 @@ interface ClientAlgorithm {
|
|
|
80
80
|
* Called after successful server commit.
|
|
81
81
|
*/
|
|
82
82
|
confirmSent(docId: string, changes: Change[]): Promise<void>;
|
|
83
|
-
/** Registers documents for local tracking. */
|
|
83
|
+
/** Registers documents for local tracking with the algorithm for this instance. */
|
|
84
84
|
trackDocs(docIds: string[]): Promise<void>;
|
|
85
85
|
/** Removes documents from local tracking. */
|
|
86
86
|
untrackDocs(docIds: string[]): Promise<void>;
|
|
@@ -95,7 +95,5 @@ interface ClientAlgorithm {
|
|
|
95
95
|
/** Closes the algorithm and its store. */
|
|
96
96
|
close(): Promise<void>;
|
|
97
97
|
}
|
|
98
|
-
/** Available algorithm names */
|
|
99
|
-
type AlgorithmName = 'ot' | 'lww';
|
|
100
98
|
|
|
101
|
-
export type {
|
|
99
|
+
export type { ClientAlgorithm };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Signal } from '../event-signal.js';
|
|
1
2
|
import { PatchesSnapshot, PatchesState } from '../types.js';
|
|
2
3
|
import { Deferred } from '../utils/deferred.js';
|
|
3
4
|
import { PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
@@ -6,36 +7,34 @@ import '@dabble/delta';
|
|
|
6
7
|
import '../json-patch/types.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
+
* IndexedDB store providing common database operations for all sync strategies.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
+
* Can be used as a standalone store or as a shared database connection
|
|
13
|
+
* for multiple strategy-specific stores (OT, LWW).
|
|
14
|
+
*
|
|
15
|
+
* Provides:
|
|
12
16
|
* - Database lifecycle management (open, close, delete)
|
|
13
17
|
* - Transaction helpers
|
|
14
18
|
* - Document tracking (listDocs, trackDocs, untrackDocs)
|
|
15
19
|
* - Basic document operations (deleteDoc, confirmDeleteDoc)
|
|
16
20
|
* - Revision tracking
|
|
17
|
-
*
|
|
18
|
-
* Subclasses must implement strategy-specific methods for document
|
|
19
|
-
* state management and change handling.
|
|
21
|
+
* - Extensibility via onUpgrade signal for strategy-specific stores
|
|
20
22
|
*/
|
|
21
|
-
declare
|
|
23
|
+
declare class IndexedDBStore implements PatchesStore {
|
|
24
|
+
private static readonly DB_VERSION;
|
|
22
25
|
protected db: IDBDatabase | null;
|
|
23
26
|
protected dbName?: string;
|
|
24
27
|
protected dbPromise: Deferred<IDBDatabase>;
|
|
25
|
-
constructor(dbName?: string);
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
+
* Signal emitted during database upgrade, allowing strategy-specific stores
|
|
30
|
+
* to create their object stores.
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
readonly onUpgrade: Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
|
|
33
|
+
constructor(dbName?: string);
|
|
31
34
|
/**
|
|
32
|
-
*
|
|
33
|
-
* Called during database upgrade.
|
|
34
|
-
*
|
|
35
|
-
* @param db - The IDBDatabase instance
|
|
36
|
-
* @param oldVersion - The previous database version
|
|
35
|
+
* Creates shared object stores used by all sync strategies.
|
|
37
36
|
*/
|
|
38
|
-
protected
|
|
37
|
+
protected createSharedStores(db: IDBDatabase, _oldVersion: number, _transaction: IDBTransaction): void;
|
|
39
38
|
protected initDB(): Promise<void>;
|
|
40
39
|
protected getDB(): Promise<IDBDatabase>;
|
|
41
40
|
/**
|
|
@@ -50,21 +49,24 @@ declare abstract class IndexedDBStore implements PatchesStore {
|
|
|
50
49
|
*/
|
|
51
50
|
close(): Promise<void>;
|
|
52
51
|
deleteDB(): Promise<void>;
|
|
53
|
-
|
|
52
|
+
transaction(storeNames: string[], mode: IDBTransactionMode): Promise<[IDBTransactionWrapper, ...IDBStoreWrapper[]]>;
|
|
54
53
|
/**
|
|
55
54
|
* Retrieves the current document snapshot from storage.
|
|
56
55
|
* Implementation varies by sync strategy (OT vs LWW).
|
|
56
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
57
57
|
*/
|
|
58
|
-
|
|
58
|
+
getDoc(_docId: string): Promise<PatchesSnapshot | undefined>;
|
|
59
59
|
/**
|
|
60
60
|
* Saves the current document state to persistent storage.
|
|
61
61
|
* Implementation varies by sync strategy.
|
|
62
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
62
63
|
*/
|
|
63
|
-
|
|
64
|
+
saveDoc(_docId: string, _docState: PatchesState): Promise<void>;
|
|
64
65
|
/**
|
|
65
66
|
* Completely remove all data for this docId and mark it as deleted (tombstone).
|
|
67
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
66
68
|
*/
|
|
67
|
-
|
|
69
|
+
deleteDoc(_docId: string): Promise<void>;
|
|
68
70
|
/**
|
|
69
71
|
* Confirm the deletion of a document.
|
|
70
72
|
* @param docId - The ID of the document to delete.
|
|
@@ -73,19 +75,22 @@ declare abstract class IndexedDBStore implements PatchesStore {
|
|
|
73
75
|
/**
|
|
74
76
|
* List all documents in the store.
|
|
75
77
|
* @param includeDeleted - Whether to include deleted documents.
|
|
78
|
+
* @param algorithm - Optional algorithm filter ('ot' or 'lww'). If provided, uses index for efficient filtering.
|
|
76
79
|
* @returns The list of documents.
|
|
77
80
|
*/
|
|
78
|
-
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
81
|
+
listDocs(includeDeleted?: boolean, algorithm?: 'ot' | 'lww'): Promise<TrackedDoc[]>;
|
|
79
82
|
/**
|
|
80
83
|
* Track a document.
|
|
81
84
|
* @param docIds - The IDs of the documents to track.
|
|
85
|
+
* @param algorithm - The algorithm to use for this document.
|
|
82
86
|
*/
|
|
83
|
-
trackDocs(docIds: string[]): Promise<void>;
|
|
87
|
+
trackDocs(docIds: string[], algorithm?: 'ot' | 'lww'): Promise<void>;
|
|
84
88
|
/**
|
|
85
89
|
* Untrack a document.
|
|
86
90
|
* @param docIds - The IDs of the documents to untrack.
|
|
91
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
87
92
|
*/
|
|
88
|
-
|
|
93
|
+
untrackDocs(_docIds: string[]): Promise<void>;
|
|
89
94
|
/**
|
|
90
95
|
* Returns the last committed revision for a document.
|
|
91
96
|
* @param docId - The ID of the document.
|
|
@@ -105,6 +110,7 @@ declare class IDBStoreWrapper {
|
|
|
105
110
|
constructor(store: IDBObjectStore);
|
|
106
111
|
protected createRange(lower?: any, upper?: any): IDBKeyRange | undefined;
|
|
107
112
|
getAll<T>(lower?: any, upper?: any, count?: number): Promise<T[]>;
|
|
113
|
+
getAllByIndex<T>(indexName: string, query?: IDBValidKey | IDBKeyRange): Promise<T[]>;
|
|
108
114
|
get<T>(key: IDBValidKey): Promise<T | undefined>;
|
|
109
115
|
put<T>(value: T): Promise<IDBValidKey>;
|
|
110
116
|
delete(key: IDBValidKey): Promise<void>;
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
2
|
import { deferred } from "../utils/deferred.js";
|
|
3
|
+
import { signal } from "../event-signal.js";
|
|
3
4
|
class IndexedDBStore {
|
|
5
|
+
static DB_VERSION = 1;
|
|
4
6
|
db = null;
|
|
5
7
|
dbName;
|
|
6
8
|
dbPromise;
|
|
9
|
+
/**
|
|
10
|
+
* Signal emitted during database upgrade, allowing strategy-specific stores
|
|
11
|
+
* to create their object stores.
|
|
12
|
+
*/
|
|
13
|
+
onUpgrade = signal();
|
|
7
14
|
constructor(dbName) {
|
|
8
15
|
this.dbName = dbName;
|
|
9
16
|
this.dbPromise = deferred();
|
|
17
|
+
this.onUpgrade((db, oldVersion, transaction) => {
|
|
18
|
+
this.createSharedStores(db, oldVersion, transaction);
|
|
19
|
+
});
|
|
10
20
|
if (this.dbName) {
|
|
11
21
|
this.initDB();
|
|
12
22
|
}
|
|
13
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Creates shared object stores used by all sync strategies.
|
|
26
|
+
*/
|
|
27
|
+
createSharedStores(db, _oldVersion, _transaction) {
|
|
28
|
+
if (!db.objectStoreNames.contains("docs")) {
|
|
29
|
+
const docsStore = db.createObjectStore("docs", { keyPath: "docId" });
|
|
30
|
+
docsStore.createIndex("algorithm", "algorithm", { unique: false });
|
|
31
|
+
}
|
|
32
|
+
if (!db.objectStoreNames.contains("snapshots")) {
|
|
33
|
+
db.createObjectStore("snapshots", { keyPath: "docId" });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
14
36
|
async initDB() {
|
|
15
37
|
if (!this.dbName) return;
|
|
16
|
-
const request = indexedDB.open(this.dbName,
|
|
38
|
+
const request = indexedDB.open(this.dbName, IndexedDBStore.DB_VERSION);
|
|
17
39
|
request.onerror = () => this.dbPromise.reject(request.error);
|
|
18
40
|
request.onsuccess = () => {
|
|
19
41
|
this.db = request.result;
|
|
@@ -21,14 +43,9 @@ class IndexedDBStore {
|
|
|
21
43
|
};
|
|
22
44
|
request.onupgradeneeded = (event) => {
|
|
23
45
|
const db = event.target.result;
|
|
46
|
+
const transaction = event.target.transaction;
|
|
24
47
|
const oldVersion = event.oldVersion;
|
|
25
|
-
|
|
26
|
-
db.createObjectStore("snapshots", { keyPath: "docId" });
|
|
27
|
-
}
|
|
28
|
-
if (!db.objectStoreNames.contains("docs")) {
|
|
29
|
-
db.createObjectStore("docs", { keyPath: "docId" });
|
|
30
|
-
}
|
|
31
|
-
this.onUpgrade(db, oldVersion);
|
|
48
|
+
this.onUpgrade.emit(db, oldVersion, transaction);
|
|
32
49
|
};
|
|
33
50
|
}
|
|
34
51
|
getDB() {
|
|
@@ -77,6 +94,31 @@ class IndexedDBStore {
|
|
|
77
94
|
const stores = storeNames.map((name) => tx.getStore(name));
|
|
78
95
|
return [tx, ...stores];
|
|
79
96
|
}
|
|
97
|
+
// ─── Strategy-Specific Methods ───────────────────────────────────────────
|
|
98
|
+
// These are implemented by strategy-specific stores (OT, LWW)
|
|
99
|
+
/**
|
|
100
|
+
* Retrieves the current document snapshot from storage.
|
|
101
|
+
* Implementation varies by sync strategy (OT vs LWW).
|
|
102
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
103
|
+
*/
|
|
104
|
+
async getDoc(_docId) {
|
|
105
|
+
throw new Error("getDoc must be implemented by strategy-specific store");
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Saves the current document state to persistent storage.
|
|
109
|
+
* Implementation varies by sync strategy.
|
|
110
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
111
|
+
*/
|
|
112
|
+
async saveDoc(_docId, _docState) {
|
|
113
|
+
throw new Error("saveDoc must be implemented by strategy-specific store");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Completely remove all data for this docId and mark it as deleted (tombstone).
|
|
117
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
118
|
+
*/
|
|
119
|
+
async deleteDoc(_docId) {
|
|
120
|
+
throw new Error("deleteDoc must be implemented by strategy-specific store");
|
|
121
|
+
}
|
|
80
122
|
/**
|
|
81
123
|
* Confirm the deletion of a document.
|
|
82
124
|
* @param docId - The ID of the document to delete.
|
|
@@ -89,34 +131,60 @@ class IndexedDBStore {
|
|
|
89
131
|
/**
|
|
90
132
|
* List all documents in the store.
|
|
91
133
|
* @param includeDeleted - Whether to include deleted documents.
|
|
134
|
+
* @param algorithm - Optional algorithm filter ('ot' or 'lww'). If provided, uses index for efficient filtering.
|
|
92
135
|
* @returns The list of documents.
|
|
93
136
|
*/
|
|
94
|
-
async listDocs(includeDeleted = false) {
|
|
137
|
+
async listDocs(includeDeleted = false, algorithm) {
|
|
95
138
|
const [tx, docsStore] = await this.transaction(["docs"], "readonly");
|
|
96
|
-
|
|
139
|
+
let docs;
|
|
140
|
+
if (algorithm === "lww") {
|
|
141
|
+
docs = await docsStore.getAllByIndex("algorithm", "lww");
|
|
142
|
+
} else if (algorithm === "ot") {
|
|
143
|
+
const otDocs = await docsStore.getAllByIndex("algorithm", "ot");
|
|
144
|
+
const allDocs = await docsStore.getAll();
|
|
145
|
+
const noAlgoDocs = allDocs.filter((doc) => !doc.algorithm);
|
|
146
|
+
docs = [...otDocs, ...noAlgoDocs];
|
|
147
|
+
} else {
|
|
148
|
+
docs = await docsStore.getAll();
|
|
149
|
+
}
|
|
97
150
|
await tx.complete();
|
|
98
|
-
return includeDeleted ?
|
|
151
|
+
return includeDeleted ? docs : docs.filter((doc) => !doc.deleted);
|
|
99
152
|
}
|
|
100
153
|
/**
|
|
101
154
|
* Track a document.
|
|
102
155
|
* @param docIds - The IDs of the documents to track.
|
|
156
|
+
* @param algorithm - The algorithm to use for this document.
|
|
103
157
|
*/
|
|
104
|
-
async trackDocs(docIds) {
|
|
158
|
+
async trackDocs(docIds, algorithm) {
|
|
105
159
|
const [tx, docsStore] = await this.transaction(["docs"], "readwrite");
|
|
106
160
|
await Promise.all(
|
|
107
161
|
docIds.map(async (docId) => {
|
|
108
162
|
const existing = await docsStore.get(docId);
|
|
109
163
|
if (existing) {
|
|
110
164
|
if (existing.deleted) {
|
|
111
|
-
await docsStore.put({
|
|
165
|
+
await docsStore.put({
|
|
166
|
+
...existing,
|
|
167
|
+
deleted: void 0,
|
|
168
|
+
...algorithm && { algorithm }
|
|
169
|
+
});
|
|
170
|
+
} else if (algorithm && existing.algorithm !== algorithm) {
|
|
171
|
+
await docsStore.put({ ...existing, algorithm });
|
|
112
172
|
}
|
|
113
173
|
} else {
|
|
114
|
-
await docsStore.put({ docId, committedRev: 0 });
|
|
174
|
+
await docsStore.put({ docId, committedRev: 0, ...algorithm && { algorithm } });
|
|
115
175
|
}
|
|
116
176
|
})
|
|
117
177
|
);
|
|
118
178
|
await tx.complete();
|
|
119
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Untrack a document.
|
|
182
|
+
* @param docIds - The IDs of the documents to untrack.
|
|
183
|
+
* This base implementation throws an error - override in strategy-specific stores.
|
|
184
|
+
*/
|
|
185
|
+
async untrackDocs(_docIds) {
|
|
186
|
+
throw new Error("untrackDocs must be implemented by strategy-specific store");
|
|
187
|
+
}
|
|
120
188
|
/**
|
|
121
189
|
* Returns the last committed revision for a document.
|
|
122
190
|
* @param docId - The ID of the document.
|
|
@@ -162,6 +230,14 @@ class IDBStoreWrapper {
|
|
|
162
230
|
request.onerror = () => reject(request.error);
|
|
163
231
|
});
|
|
164
232
|
}
|
|
233
|
+
async getAllByIndex(indexName, query) {
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
const index = this.store.index(indexName);
|
|
236
|
+
const request = index.getAll(query);
|
|
237
|
+
request.onsuccess = () => resolve(request.result);
|
|
238
|
+
request.onerror = () => reject(request.error);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
165
241
|
async get(key) {
|
|
166
242
|
return new Promise((resolve, reject) => {
|
|
167
243
|
const request = this.store.get(key);
|
|
@@ -37,7 +37,7 @@ declare class LWWInMemoryStore implements LWWClientStore {
|
|
|
37
37
|
/**
|
|
38
38
|
* Track documents.
|
|
39
39
|
*/
|
|
40
|
-
trackDocs(docIds: string[]): Promise<void>;
|
|
40
|
+
trackDocs(docIds: string[], _algorithm?: 'ot' | 'lww'): Promise<void>;
|
|
41
41
|
/**
|
|
42
42
|
* Untrack documents by removing all their data.
|
|
43
43
|
*/
|
|
@@ -2,10 +2,11 @@ import { JSONPatchOp } from '../json-patch/types.js';
|
|
|
2
2
|
import { PatchesSnapshot, PatchesState, Change } from '../types.js';
|
|
3
3
|
import { IndexedDBStore } from './IndexedDBStore.js';
|
|
4
4
|
import { LWWClientStore } from './LWWClientStore.js';
|
|
5
|
+
import { TrackedDoc } from './PatchesStore.js';
|
|
5
6
|
import '../json-patch/JSONPatch.js';
|
|
6
7
|
import '@dabble/delta';
|
|
8
|
+
import '../event-signal.js';
|
|
7
9
|
import '../utils/deferred.js';
|
|
8
|
-
import './PatchesStore.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* IndexedDB store implementation for Last-Writer-Wins (LWW) sync strategy.
|
|
@@ -24,9 +25,42 @@ import './PatchesStore.js';
|
|
|
24
25
|
*
|
|
25
26
|
* Every 200 ops, committed ops are compacted into the snapshot.
|
|
26
27
|
*/
|
|
27
|
-
declare class LWWIndexedDBStore
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
declare class LWWIndexedDBStore implements LWWClientStore {
|
|
29
|
+
db: IndexedDBStore;
|
|
30
|
+
constructor(db?: string | IndexedDBStore);
|
|
31
|
+
/**
|
|
32
|
+
* Creates LWW-specific object stores during database upgrade.
|
|
33
|
+
*/
|
|
34
|
+
protected createLWWStores(db: IDBDatabase): void;
|
|
35
|
+
/**
|
|
36
|
+
* List documents for the LWW algorithm.
|
|
37
|
+
* Uses the algorithm index for efficient querying.
|
|
38
|
+
*/
|
|
39
|
+
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Track documents using the LWW algorithm.
|
|
42
|
+
*/
|
|
43
|
+
trackDocs(docIds: string[]): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Close the database connection.
|
|
46
|
+
*/
|
|
47
|
+
close(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Delete the database.
|
|
50
|
+
*/
|
|
51
|
+
deleteDB(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Set the database name.
|
|
54
|
+
*/
|
|
55
|
+
setName(dbName: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Confirm the deletion of a document.
|
|
58
|
+
*/
|
|
59
|
+
confirmDeleteDoc(docId: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Get the committed revision for a document.
|
|
62
|
+
*/
|
|
63
|
+
getCommittedRev(docId: string): Promise<number>;
|
|
30
64
|
/**
|
|
31
65
|
* Rebuilds a document state from snapshot + committed ops + sending + pending.
|
|
32
66
|
*
|
|
@@ -2,24 +2,29 @@ import {
|
|
|
2
2
|
__decorateElement,
|
|
3
3
|
__decoratorMetadata,
|
|
4
4
|
__decoratorStart,
|
|
5
|
+
__publicField,
|
|
5
6
|
__runInitializers
|
|
6
7
|
} from "../chunk-IZ2YBCUP.js";
|
|
7
|
-
var _applyServerChanges_dec, _confirmSendingChange_dec, _saveSendingChange_dec, _getSendingChange_dec, _savePendingOps_dec, _getPendingOps_dec, _deleteDoc_dec, _saveDoc_dec, _getDoc_dec,
|
|
8
|
+
var _applyServerChanges_dec, _confirmSendingChange_dec, _saveSendingChange_dec, _getSendingChange_dec, _savePendingOps_dec, _getPendingOps_dec, _deleteDoc_dec, _saveDoc_dec, _getDoc_dec, _init;
|
|
8
9
|
import { createChange } from "../data/change.js";
|
|
9
10
|
import { applyPatch } from "../json-patch/applyPatch.js";
|
|
10
11
|
import { blockable } from "../utils/concurrency.js";
|
|
11
12
|
import { IDBStoreWrapper, IndexedDBStore } from "./IndexedDBStore.js";
|
|
12
|
-
const DB_VERSION = 1;
|
|
13
13
|
const SNAPSHOT_INTERVAL = 200;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
_getDoc_dec = [blockable], _saveDoc_dec = [blockable], _deleteDoc_dec = [blockable], _getPendingOps_dec = [blockable], _savePendingOps_dec = [blockable], _getSendingChange_dec = [blockable], _saveSendingChange_dec = [blockable], _confirmSendingChange_dec = [blockable], _applyServerChanges_dec = [blockable];
|
|
15
|
+
class LWWIndexedDBStore {
|
|
16
|
+
constructor(db) {
|
|
17
17
|
__runInitializers(_init, 5, this);
|
|
18
|
+
__publicField(this, "db");
|
|
19
|
+
this.db = !db || typeof db === "string" ? new IndexedDBStore(db) : db;
|
|
20
|
+
this.db.onUpgrade((db2, _oldVersion, _transaction) => {
|
|
21
|
+
this.createLWWStores(db2);
|
|
22
|
+
});
|
|
18
23
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Creates LWW-specific object stores during database upgrade.
|
|
26
|
+
*/
|
|
27
|
+
createLWWStores(db) {
|
|
23
28
|
if (!db.objectStoreNames.contains("committedOps")) {
|
|
24
29
|
db.createObjectStore("committedOps", { keyPath: ["docId", "path"] });
|
|
25
30
|
}
|
|
@@ -30,8 +35,51 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
30
35
|
db.createObjectStore("sendingChanges", { keyPath: "docId" });
|
|
31
36
|
}
|
|
32
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* List documents for the LWW algorithm.
|
|
40
|
+
* Uses the algorithm index for efficient querying.
|
|
41
|
+
*/
|
|
42
|
+
async listDocs(includeDeleted = false) {
|
|
43
|
+
return this.db.listDocs(includeDeleted, "lww");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Track documents using the LWW algorithm.
|
|
47
|
+
*/
|
|
48
|
+
async trackDocs(docIds) {
|
|
49
|
+
return this.db.trackDocs(docIds, "lww");
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Close the database connection.
|
|
53
|
+
*/
|
|
54
|
+
async close() {
|
|
55
|
+
return this.db.close();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Delete the database.
|
|
59
|
+
*/
|
|
60
|
+
async deleteDB() {
|
|
61
|
+
return this.db.deleteDB();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set the database name.
|
|
65
|
+
*/
|
|
66
|
+
setName(dbName) {
|
|
67
|
+
return this.db.setName(dbName);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Confirm the deletion of a document.
|
|
71
|
+
*/
|
|
72
|
+
async confirmDeleteDoc(docId) {
|
|
73
|
+
return this.db.confirmDeleteDoc(docId);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get the committed revision for a document.
|
|
77
|
+
*/
|
|
78
|
+
async getCommittedRev(docId) {
|
|
79
|
+
return this.db.getCommittedRev(docId);
|
|
80
|
+
}
|
|
33
81
|
async getDoc(docId) {
|
|
34
|
-
const [tx, docsStore, snapshots, committedOps, pendingOps, sendingChanges] = await this.transaction(
|
|
82
|
+
const [tx, docsStore, snapshots, committedOps, pendingOps, sendingChanges] = await this.db.transaction(
|
|
35
83
|
["docs", "snapshots", "committedOps", "pendingOps", "sendingChanges"],
|
|
36
84
|
"readonly"
|
|
37
85
|
);
|
|
@@ -74,7 +122,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
74
122
|
};
|
|
75
123
|
}
|
|
76
124
|
async saveDoc(docId, docState) {
|
|
77
|
-
const [tx, snapshots, committedOps, pendingOps, sendingChanges, docsStore] = await this.transaction(
|
|
125
|
+
const [tx, snapshots, committedOps, pendingOps, sendingChanges, docsStore] = await this.db.transaction(
|
|
78
126
|
["snapshots", "committedOps", "pendingOps", "sendingChanges", "docs"],
|
|
79
127
|
"readwrite"
|
|
80
128
|
);
|
|
@@ -89,7 +137,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
89
137
|
await tx.complete();
|
|
90
138
|
}
|
|
91
139
|
async deleteDoc(docId) {
|
|
92
|
-
const [tx, snapshots, committedOps, pendingOps, sendingChanges, docsStore] = await this.transaction(
|
|
140
|
+
const [tx, snapshots, committedOps, pendingOps, sendingChanges, docsStore] = await this.db.transaction(
|
|
93
141
|
["snapshots", "committedOps", "pendingOps", "sendingChanges", "docs"],
|
|
94
142
|
"readwrite"
|
|
95
143
|
);
|
|
@@ -107,7 +155,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
107
155
|
* Untracks documents by removing all their data.
|
|
108
156
|
*/
|
|
109
157
|
async untrackDocs(docIds) {
|
|
110
|
-
const [tx, docsStore, snapshots, committedOps, pendingOps, sendingChanges] = await this.transaction(
|
|
158
|
+
const [tx, docsStore, snapshots, committedOps, pendingOps, sendingChanges] = await this.db.transaction(
|
|
111
159
|
["docs", "snapshots", "committedOps", "pendingOps", "sendingChanges"],
|
|
112
160
|
"readwrite"
|
|
113
161
|
);
|
|
@@ -125,7 +173,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
125
173
|
await tx.complete();
|
|
126
174
|
}
|
|
127
175
|
async getPendingOps(docId, pathPrefixes) {
|
|
128
|
-
const [tx, pendingOpsStore] = await this.transaction(["pendingOps"], "readonly");
|
|
176
|
+
const [tx, pendingOpsStore] = await this.db.transaction(["pendingOps"], "readonly");
|
|
129
177
|
let pending;
|
|
130
178
|
if (!pathPrefixes || pathPrefixes.length === 0) {
|
|
131
179
|
pending = await pendingOpsStore.getAll([docId, ""], [docId, "\uFFFF"]);
|
|
@@ -144,10 +192,10 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
144
192
|
}));
|
|
145
193
|
}
|
|
146
194
|
async savePendingOps(docId, ops, pathsToDelete) {
|
|
147
|
-
const [tx, pendingOpsStore, docsStore] = await this.transaction(["pendingOps", "docs"], "readwrite");
|
|
195
|
+
const [tx, pendingOpsStore, docsStore] = await this.db.transaction(["pendingOps", "docs"], "readwrite");
|
|
148
196
|
let docMeta = await docsStore.get(docId);
|
|
149
197
|
if (!docMeta) {
|
|
150
|
-
docMeta = { docId, committedRev: 0 };
|
|
198
|
+
docMeta = { docId, committedRev: 0, algorithm: "lww" };
|
|
151
199
|
await docsStore.put(docMeta);
|
|
152
200
|
} else if (docMeta.deleted) {
|
|
153
201
|
delete docMeta.deleted;
|
|
@@ -170,19 +218,22 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
170
218
|
await tx.complete();
|
|
171
219
|
}
|
|
172
220
|
async getSendingChange(docId) {
|
|
173
|
-
const [tx, sendingChanges] = await this.transaction(["sendingChanges"], "readonly");
|
|
221
|
+
const [tx, sendingChanges] = await this.db.transaction(["sendingChanges"], "readonly");
|
|
174
222
|
const sending = await sendingChanges.get(docId);
|
|
175
223
|
await tx.complete();
|
|
176
224
|
return sending?.change ?? null;
|
|
177
225
|
}
|
|
178
226
|
async saveSendingChange(docId, change) {
|
|
179
|
-
const [tx, pendingOpsStore, sendingChanges] = await this.transaction(
|
|
227
|
+
const [tx, pendingOpsStore, sendingChanges] = await this.db.transaction(
|
|
228
|
+
["pendingOps", "sendingChanges"],
|
|
229
|
+
"readwrite"
|
|
230
|
+
);
|
|
180
231
|
await sendingChanges.put({ docId, change });
|
|
181
232
|
await this.deleteFieldsForDoc(pendingOpsStore, docId);
|
|
182
233
|
await tx.complete();
|
|
183
234
|
}
|
|
184
235
|
async confirmSendingChange(docId) {
|
|
185
|
-
const [tx, sendingChanges, committedOps, docsStore] = await this.transaction(
|
|
236
|
+
const [tx, sendingChanges, committedOps, docsStore] = await this.db.transaction(
|
|
186
237
|
["sendingChanges", "committedOps", "docs"],
|
|
187
238
|
"readwrite"
|
|
188
239
|
);
|
|
@@ -200,7 +251,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
200
251
|
await tx.complete();
|
|
201
252
|
}
|
|
202
253
|
async applyServerChanges(docId, serverChanges) {
|
|
203
|
-
const [tx, committedOps, snapshots, docsStore] = await this.transaction(
|
|
254
|
+
const [tx, committedOps, snapshots, docsStore] = await this.db.transaction(
|
|
204
255
|
["committedOps", "snapshots", "docs"],
|
|
205
256
|
"readwrite"
|
|
206
257
|
);
|
|
@@ -259,7 +310,7 @@ class LWWIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
259
310
|
await this.deleteFieldsForDoc(committedOps, docId);
|
|
260
311
|
}
|
|
261
312
|
}
|
|
262
|
-
_init = __decoratorStart(
|
|
313
|
+
_init = __decoratorStart(null);
|
|
263
314
|
__decorateElement(_init, 1, "getDoc", _getDoc_dec, LWWIndexedDBStore);
|
|
264
315
|
__decorateElement(_init, 1, "saveDoc", _saveDoc_dec, LWWIndexedDBStore);
|
|
265
316
|
__decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, LWWIndexedDBStore);
|
|
@@ -6,11 +6,11 @@ import '@dabble/delta';
|
|
|
6
6
|
import '../json-patch/types.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* A trivial in‑memory implementation of
|
|
9
|
+
* A trivial in‑memory implementation of OTClientStore.
|
|
10
10
|
* All data lives in JS objects – nothing survives a page reload.
|
|
11
11
|
* Useful for unit tests or when you want the old 'stateless realtime' behaviour.
|
|
12
12
|
*/
|
|
13
|
-
declare class
|
|
13
|
+
declare class OTInMemoryStore implements OTClientStore {
|
|
14
14
|
private docs;
|
|
15
15
|
getDoc(docId: string): Promise<PatchesSnapshot | undefined>;
|
|
16
16
|
getPendingChanges(docId: string): Promise<Change[]>;
|
|
@@ -19,11 +19,11 @@ declare class InMemoryStore implements OTClientStore {
|
|
|
19
19
|
saveDoc(docId: string, snapshot: PatchesState): Promise<void>;
|
|
20
20
|
savePendingChanges(docId: string, changes: Change[]): Promise<void>;
|
|
21
21
|
applyServerChanges(docId: string, serverChanges: Change[], rebasedPendingChanges: Change[]): Promise<void>;
|
|
22
|
-
trackDocs(docIds: string[]): Promise<void>;
|
|
22
|
+
trackDocs(docIds: string[], _algorithm?: 'ot' | 'lww'): Promise<void>;
|
|
23
23
|
untrackDocs(docIds: string[]): Promise<void>;
|
|
24
24
|
deleteDoc(docId: string): Promise<void>;
|
|
25
25
|
confirmDeleteDoc(docId: string): Promise<void>;
|
|
26
26
|
close(): Promise<void>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export {
|
|
29
|
+
export { OTInMemoryStore };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
2
|
import { applyChanges } from "../algorithms/ot/shared/applyChanges.js";
|
|
3
|
-
class
|
|
3
|
+
class OTInMemoryStore {
|
|
4
4
|
docs = /* @__PURE__ */ new Map();
|
|
5
5
|
// ─── Reconstruction ────────────────────────────────────────────────────
|
|
6
6
|
async getDoc(docId) {
|
|
@@ -31,7 +31,11 @@ class InMemoryStore {
|
|
|
31
31
|
}
|
|
32
32
|
// ─── Writes ────────────────────────────────────────────────────────────
|
|
33
33
|
async saveDoc(docId, snapshot) {
|
|
34
|
-
this.docs.set(docId, {
|
|
34
|
+
this.docs.set(docId, {
|
|
35
|
+
snapshot,
|
|
36
|
+
committed: [],
|
|
37
|
+
pending: []
|
|
38
|
+
});
|
|
35
39
|
}
|
|
36
40
|
async savePendingChanges(docId, changes) {
|
|
37
41
|
const buf = this.docs.get(docId) ?? { committed: [], pending: [] };
|
|
@@ -45,7 +49,7 @@ class InMemoryStore {
|
|
|
45
49
|
buf.pending = [...rebasedPendingChanges];
|
|
46
50
|
}
|
|
47
51
|
// ─── Metadata / Tracking ───────────────────────────────────────────
|
|
48
|
-
async trackDocs(docIds) {
|
|
52
|
+
async trackDocs(docIds, _algorithm) {
|
|
49
53
|
for (const docId of docIds) {
|
|
50
54
|
const buf = this.docs.get(docId) ?? { committed: [], pending: [] };
|
|
51
55
|
buf.deleted = void 0;
|
|
@@ -74,5 +78,5 @@ class InMemoryStore {
|
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
export {
|
|
77
|
-
|
|
81
|
+
OTInMemoryStore
|
|
78
82
|
};
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { PatchesSnapshot, PatchesState, Change } from '../types.js';
|
|
2
2
|
import { IndexedDBStore } from './IndexedDBStore.js';
|
|
3
3
|
import { OTClientStore } from './OTClientStore.js';
|
|
4
|
+
import { TrackedDoc } from './PatchesStore.js';
|
|
4
5
|
import '../json-patch/JSONPatch.js';
|
|
5
6
|
import '@dabble/delta';
|
|
6
7
|
import '../json-patch/types.js';
|
|
8
|
+
import '../event-signal.js';
|
|
7
9
|
import '../utils/deferred.js';
|
|
8
|
-
import './PatchesStore.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* IndexedDB store implementation for Operational Transformation (OT) sync strategy.
|
|
12
13
|
*
|
|
13
14
|
* Creates stores:
|
|
14
|
-
* - snapshots<{ docId: string; rev: number; state: any }> (primary key: docId)
|
|
15
|
+
* - snapshots<{ docId: string; rev: number; state: any }> (primary key: docId) [shared]
|
|
15
16
|
* - committedChanges<Change & { docId: string; }> (primary key: [docId, rev])
|
|
16
17
|
* - pendingChanges<Change & { docId: string; }> (primary key: [docId, rev])
|
|
17
|
-
* - docs<{ docId: string; committedRev: number; deleted?: boolean }> (primary key: docId)
|
|
18
|
+
* - docs<{ docId: string; committedRev: number; deleted?: boolean }> (primary key: docId) [shared]
|
|
18
19
|
*
|
|
19
20
|
* Under the hood, this class stores snapshots of the document only for committed state.
|
|
20
21
|
* It does not update the committed state on *every* received committed change as this
|
|
@@ -25,9 +26,42 @@ import './PatchesStore.js';
|
|
|
25
26
|
* A snapshot will not be created if there are pending changes based on revisions older
|
|
26
27
|
* than the 200th committed change until those pending changes are committed.
|
|
27
28
|
*/
|
|
28
|
-
declare class OTIndexedDBStore
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
declare class OTIndexedDBStore implements OTClientStore {
|
|
30
|
+
db: IndexedDBStore;
|
|
31
|
+
constructor(db?: string | IndexedDBStore);
|
|
32
|
+
/**
|
|
33
|
+
* Creates OT-specific object stores during database upgrade.
|
|
34
|
+
*/
|
|
35
|
+
protected createOTStores(db: IDBDatabase): void;
|
|
36
|
+
/**
|
|
37
|
+
* List documents for the OT algorithm.
|
|
38
|
+
* Uses the algorithm index for efficient querying.
|
|
39
|
+
*/
|
|
40
|
+
listDocs(includeDeleted?: boolean): Promise<TrackedDoc[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Track documents using the OT algorithm.
|
|
43
|
+
*/
|
|
44
|
+
trackDocs(docIds: string[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Close the database connection.
|
|
47
|
+
*/
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Delete the database.
|
|
51
|
+
*/
|
|
52
|
+
deleteDB(): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Set the database name.
|
|
55
|
+
*/
|
|
56
|
+
setName(dbName: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Confirm the deletion of a document.
|
|
59
|
+
*/
|
|
60
|
+
confirmDeleteDoc(docId: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Get the committed revision for a document.
|
|
63
|
+
*/
|
|
64
|
+
getCommittedRev(docId: string): Promise<number>;
|
|
31
65
|
/**
|
|
32
66
|
* Rebuilds a document snapshot + pending queue *without* loading
|
|
33
67
|
* the full PatchesDoc into memory.
|
|
@@ -2,23 +2,28 @@ import {
|
|
|
2
2
|
__decorateElement,
|
|
3
3
|
__decoratorMetadata,
|
|
4
4
|
__decoratorStart,
|
|
5
|
+
__publicField,
|
|
5
6
|
__runInitializers
|
|
6
7
|
} from "../chunk-IZ2YBCUP.js";
|
|
7
|
-
var _applyServerChanges_dec, _getPendingChanges_dec, _savePendingChanges_dec, _saveDoc_dec, _deleteDoc_dec, _getDoc_dec,
|
|
8
|
+
var _applyServerChanges_dec, _getPendingChanges_dec, _savePendingChanges_dec, _saveDoc_dec, _deleteDoc_dec, _getDoc_dec, _init;
|
|
8
9
|
import { applyChanges } from "../algorithms/ot/shared/applyChanges.js";
|
|
9
10
|
import { blockable } from "../utils/concurrency.js";
|
|
10
11
|
import { IndexedDBStore } from "./IndexedDBStore.js";
|
|
11
|
-
const DB_VERSION = 1;
|
|
12
12
|
const SNAPSHOT_INTERVAL = 200;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
_getDoc_dec = [blockable], _deleteDoc_dec = [blockable], _saveDoc_dec = [blockable], _savePendingChanges_dec = [blockable], _getPendingChanges_dec = [blockable], _applyServerChanges_dec = [blockable];
|
|
14
|
+
class OTIndexedDBStore {
|
|
15
|
+
constructor(db) {
|
|
16
16
|
__runInitializers(_init, 5, this);
|
|
17
|
+
__publicField(this, "db");
|
|
18
|
+
this.db = !db || typeof db === "string" ? new IndexedDBStore(db) : db;
|
|
19
|
+
this.db.onUpgrade((db2, _oldVersion, _transaction) => {
|
|
20
|
+
this.createOTStores(db2);
|
|
21
|
+
});
|
|
17
22
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Creates OT-specific object stores during database upgrade.
|
|
25
|
+
*/
|
|
26
|
+
createOTStores(db) {
|
|
22
27
|
if (!db.objectStoreNames.contains("committedChanges")) {
|
|
23
28
|
db.createObjectStore("committedChanges", { keyPath: ["docId", "rev"] });
|
|
24
29
|
}
|
|
@@ -26,8 +31,51 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
26
31
|
db.createObjectStore("pendingChanges", { keyPath: ["docId", "rev"] });
|
|
27
32
|
}
|
|
28
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* List documents for the OT algorithm.
|
|
36
|
+
* Uses the algorithm index for efficient querying.
|
|
37
|
+
*/
|
|
38
|
+
async listDocs(includeDeleted = false) {
|
|
39
|
+
return this.db.listDocs(includeDeleted, "ot");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Track documents using the OT algorithm.
|
|
43
|
+
*/
|
|
44
|
+
async trackDocs(docIds) {
|
|
45
|
+
return this.db.trackDocs(docIds, "ot");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Close the database connection.
|
|
49
|
+
*/
|
|
50
|
+
async close() {
|
|
51
|
+
return this.db.close();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Delete the database.
|
|
55
|
+
*/
|
|
56
|
+
async deleteDB() {
|
|
57
|
+
return this.db.deleteDB();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Set the database name.
|
|
61
|
+
*/
|
|
62
|
+
setName(dbName) {
|
|
63
|
+
return this.db.setName(dbName);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Confirm the deletion of a document.
|
|
67
|
+
*/
|
|
68
|
+
async confirmDeleteDoc(docId) {
|
|
69
|
+
return this.db.confirmDeleteDoc(docId);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the committed revision for a document.
|
|
73
|
+
*/
|
|
74
|
+
async getCommittedRev(docId) {
|
|
75
|
+
return this.db.getCommittedRev(docId);
|
|
76
|
+
}
|
|
29
77
|
async getDoc(docId) {
|
|
30
|
-
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.transaction(
|
|
78
|
+
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.db.transaction(
|
|
31
79
|
["docs", "snapshots", "committedChanges", "pendingChanges"],
|
|
32
80
|
"readonly"
|
|
33
81
|
);
|
|
@@ -49,7 +97,7 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
49
97
|
};
|
|
50
98
|
}
|
|
51
99
|
async deleteDoc(docId) {
|
|
52
|
-
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.transaction(
|
|
100
|
+
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.db.transaction(
|
|
53
101
|
["snapshots", "committedChanges", "pendingChanges", "docs"],
|
|
54
102
|
"readwrite"
|
|
55
103
|
);
|
|
@@ -63,7 +111,7 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
63
111
|
await tx.complete();
|
|
64
112
|
}
|
|
65
113
|
async saveDoc(docId, docState) {
|
|
66
|
-
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.transaction(
|
|
114
|
+
const [tx, snapshots, committedChanges, pendingChanges, docsStore] = await this.db.transaction(
|
|
67
115
|
["snapshots", "committedChanges", "pendingChanges", "docs"],
|
|
68
116
|
"readwrite"
|
|
69
117
|
);
|
|
@@ -77,10 +125,10 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
77
125
|
await tx.complete();
|
|
78
126
|
}
|
|
79
127
|
async savePendingChanges(docId, changes) {
|
|
80
|
-
const [tx, pendingChanges, docsStore] = await this.transaction(["pendingChanges", "docs"], "readwrite");
|
|
128
|
+
const [tx, pendingChanges, docsStore] = await this.db.transaction(["pendingChanges", "docs"], "readwrite");
|
|
81
129
|
let docMeta = await docsStore.get(docId);
|
|
82
130
|
if (!docMeta) {
|
|
83
|
-
docMeta = { docId, committedRev: 0 };
|
|
131
|
+
docMeta = { docId, committedRev: 0, algorithm: "ot" };
|
|
84
132
|
await docsStore.put(docMeta);
|
|
85
133
|
} else if (docMeta.deleted) {
|
|
86
134
|
delete docMeta.deleted;
|
|
@@ -91,13 +139,13 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
91
139
|
await tx.complete();
|
|
92
140
|
}
|
|
93
141
|
async getPendingChanges(docId) {
|
|
94
|
-
const [tx, pendingChanges] = await this.transaction(["pendingChanges"], "readonly");
|
|
142
|
+
const [tx, pendingChanges] = await this.db.transaction(["pendingChanges"], "readonly");
|
|
95
143
|
const result = await pendingChanges.getAll([docId, 0], [docId, Infinity]);
|
|
96
144
|
await tx.complete();
|
|
97
145
|
return result;
|
|
98
146
|
}
|
|
99
147
|
async applyServerChanges(docId, serverChanges, rebasedPendingChanges) {
|
|
100
|
-
const [tx, committedChangesStore, pendingChangesStore, snapshots, docsStore] = await this.transaction(
|
|
148
|
+
const [tx, committedChangesStore, pendingChangesStore, snapshots, docsStore] = await this.db.transaction(
|
|
101
149
|
["committedChanges", "pendingChanges", "snapshots", "docs"],
|
|
102
150
|
"readwrite"
|
|
103
151
|
);
|
|
@@ -133,7 +181,7 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
133
181
|
* @param docIds - The IDs of the documents to untrack.
|
|
134
182
|
*/
|
|
135
183
|
async untrackDocs(docIds) {
|
|
136
|
-
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.transaction(
|
|
184
|
+
const [tx, docsStore, snapshots, committedChanges, pendingChanges] = await this.db.transaction(
|
|
137
185
|
["docs", "snapshots", "committedChanges", "pendingChanges"],
|
|
138
186
|
"readwrite"
|
|
139
187
|
);
|
|
@@ -150,7 +198,7 @@ class OTIndexedDBStore extends (_a = IndexedDBStore, _getDoc_dec = [blockable],
|
|
|
150
198
|
await tx.complete();
|
|
151
199
|
}
|
|
152
200
|
}
|
|
153
|
-
_init = __decoratorStart(
|
|
201
|
+
_init = __decoratorStart(null);
|
|
154
202
|
__decorateElement(_init, 1, "getDoc", _getDoc_dec, OTIndexedDBStore);
|
|
155
203
|
__decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, OTIndexedDBStore);
|
|
156
204
|
__decorateElement(_init, 1, "saveDoc", _saveDoc_dec, OTIndexedDBStore);
|
package/dist/client/Patches.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Unsubscriber, Signal } from '../event-signal.js';
|
|
2
2
|
import { JSONPatchOp } from '../json-patch/types.js';
|
|
3
3
|
import { Change } from '../types.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
5
5
|
import { P as PatchesDocOptions, a as PatchesDoc } from '../BaseDoc-DkP3tUhT.js';
|
|
6
|
+
import { AlgorithmName } from './PatchesStore.js';
|
|
6
7
|
import '../json-patch/JSONPatch.js';
|
|
7
8
|
import '@dabble/delta';
|
|
8
|
-
import './PatchesStore.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Options for creating a Patches instance.
|
package/dist/client/Patches.js
CHANGED
|
@@ -114,7 +114,18 @@ class Patches {
|
|
|
114
114
|
async openDoc(docId, opts = {}) {
|
|
115
115
|
const existing = this.docs.get(docId);
|
|
116
116
|
if (existing) return existing.doc;
|
|
117
|
-
|
|
117
|
+
let algorithmName = opts.algorithm;
|
|
118
|
+
if (!algorithmName) {
|
|
119
|
+
for (const algo of Object.values(this.algorithms)) {
|
|
120
|
+
const docs = await algo.listDocs(false);
|
|
121
|
+
const tracked = docs.find((d) => d.docId === docId);
|
|
122
|
+
if (tracked?.algorithm) {
|
|
123
|
+
algorithmName = tracked.algorithm;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
algorithmName = algorithmName ?? this.defaultAlgorithm;
|
|
128
|
+
}
|
|
118
129
|
const algorithm = this._getAlgorithm(algorithmName);
|
|
119
130
|
await this.trackDocs([docId], algorithmName);
|
|
120
131
|
const snapshot = await algorithm.loadDoc(docId);
|
|
@@ -3,6 +3,8 @@ import '../json-patch/JSONPatch.js';
|
|
|
3
3
|
import '@dabble/delta';
|
|
4
4
|
import '../json-patch/types.js';
|
|
5
5
|
|
|
6
|
+
/** Available algorithm names */
|
|
7
|
+
type AlgorithmName = 'ot' | 'lww';
|
|
6
8
|
/** Represents metadata for a document tracked by the store. */
|
|
7
9
|
interface TrackedDoc {
|
|
8
10
|
docId: string;
|
|
@@ -10,6 +12,8 @@ interface TrackedDoc {
|
|
|
10
12
|
committedRev: number;
|
|
11
13
|
/** Optional flag indicating the document has been locally deleted. */
|
|
12
14
|
deleted?: true;
|
|
15
|
+
/** The sync algorithm this document uses. */
|
|
16
|
+
algorithm?: AlgorithmName;
|
|
13
17
|
}
|
|
14
18
|
/**
|
|
15
19
|
* Pluggable persistence layer contract used by Patches + PatchesSync.
|
|
@@ -24,14 +28,15 @@ interface PatchesStore {
|
|
|
24
28
|
* Sets initial committedRev to 0 for new documents.
|
|
25
29
|
*
|
|
26
30
|
* @param docIds Array of document IDs to start tracking
|
|
31
|
+
* @param algorithm The algorithm to use for this document ('ot' or 'lww')
|
|
27
32
|
* @example
|
|
28
33
|
* // Start tracking two documents
|
|
29
|
-
* await store.trackDocs(['doc1', 'doc2']);
|
|
34
|
+
* await store.trackDocs(['doc1', 'doc2'], 'ot');
|
|
30
35
|
*
|
|
31
36
|
* // Reactivate a previously deleted document
|
|
32
|
-
* await store.trackDocs(['previously-deleted-doc']);
|
|
37
|
+
* await store.trackDocs(['previously-deleted-doc'], 'lww');
|
|
33
38
|
*/
|
|
34
|
-
trackDocs(docIds: string[]): Promise<void>;
|
|
39
|
+
trackDocs(docIds: string[], algorithm?: AlgorithmName): Promise<void>;
|
|
35
40
|
/**
|
|
36
41
|
* Permanently removes documents from local tracking and storage.
|
|
37
42
|
*
|
|
@@ -153,4 +158,4 @@ interface PatchesStore {
|
|
|
153
158
|
close(): Promise<void>;
|
|
154
159
|
}
|
|
155
160
|
|
|
156
|
-
export type { PatchesStore, TrackedDoc };
|
|
161
|
+
export type { AlgorithmName, PatchesStore, TrackedDoc };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { AlgorithmName } from './
|
|
1
|
+
import { AlgorithmName } from './PatchesStore.js';
|
|
2
2
|
import { Patches } from './Patches.js';
|
|
3
3
|
import { P as PatchesDocOptions } from '../BaseDoc-DkP3tUhT.js';
|
|
4
|
-
import '../json-patch/types.js';
|
|
5
4
|
import '../types.js';
|
|
6
5
|
import '../json-patch/JSONPatch.js';
|
|
7
6
|
import '@dabble/delta';
|
|
8
|
-
import '
|
|
7
|
+
import '../json-patch/types.js';
|
|
9
8
|
import '../event-signal.js';
|
|
9
|
+
import './ClientAlgorithm.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Options for factory functions that create Patches instances.
|
|
@@ -62,11 +62,12 @@ declare function createLWWIndexedDBPatches(options: IndexedDBFactoryOptions): Pa
|
|
|
62
62
|
* Creates a Patches instance with both OT and LWW algorithms using in-memory stores.
|
|
63
63
|
* Useful for testing or applications that need both algorithms without persistence.
|
|
64
64
|
*/
|
|
65
|
-
declare function
|
|
65
|
+
declare function createMultiAlgorithmPatches(options?: MultiAlgorithmFactoryOptions): Patches;
|
|
66
66
|
/**
|
|
67
67
|
* Creates a Patches instance with both OT and LWW algorithms using IndexedDB stores.
|
|
68
68
|
* For persistent storage in browser environments with support for both algorithms.
|
|
69
|
+
* Both algorithms share the same IndexedDB database with unified stores.
|
|
69
70
|
*/
|
|
70
|
-
declare function
|
|
71
|
+
declare function createMultiAlgorithmIndexedDBPatches(options: MultiAlgorithmIndexedDBFactoryOptions): Patches;
|
|
71
72
|
|
|
72
|
-
export { type IndexedDBFactoryOptions, type MultiAlgorithmFactoryOptions, type MultiAlgorithmIndexedDBFactoryOptions, type PatchesFactoryOptions,
|
|
73
|
+
export { type IndexedDBFactoryOptions, type MultiAlgorithmFactoryOptions, type MultiAlgorithmIndexedDBFactoryOptions, type PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches };
|
package/dist/client/factories.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import "../chunk-IZ2YBCUP.js";
|
|
2
|
-
import {
|
|
2
|
+
import { OTInMemoryStore } from "./OTInMemoryStore.js";
|
|
3
3
|
import { LWWInMemoryStore } from "./LWWInMemoryStore.js";
|
|
4
|
+
import { IndexedDBStore } from "./IndexedDBStore.js";
|
|
4
5
|
import { LWWIndexedDBStore } from "./LWWIndexedDBStore.js";
|
|
5
6
|
import { LWWAlgorithm } from "./LWWAlgorithm.js";
|
|
6
7
|
import { OTIndexedDBStore } from "./OTIndexedDBStore.js";
|
|
7
8
|
import { OTAlgorithm } from "./OTAlgorithm.js";
|
|
8
9
|
import { Patches } from "./Patches.js";
|
|
9
10
|
function createOTPatches(options = {}) {
|
|
10
|
-
const store = new
|
|
11
|
+
const store = new OTInMemoryStore();
|
|
11
12
|
const otAlgorithm = new OTAlgorithm(store, options.docOptions);
|
|
12
13
|
return new Patches({
|
|
13
14
|
algorithms: { ot: otAlgorithm },
|
|
@@ -46,8 +47,8 @@ function createLWWIndexedDBPatches(options) {
|
|
|
46
47
|
docOptions: options.docOptions
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
|
-
function
|
|
50
|
-
const otStore = new
|
|
50
|
+
function createMultiAlgorithmPatches(options = {}) {
|
|
51
|
+
const otStore = new OTInMemoryStore();
|
|
51
52
|
const lwwStore = new LWWInMemoryStore();
|
|
52
53
|
const otAlgorithm = new OTAlgorithm(otStore, options.docOptions);
|
|
53
54
|
const lwwAlgorithm = new LWWAlgorithm(lwwStore);
|
|
@@ -58,9 +59,10 @@ function createAllPatches(options = {}) {
|
|
|
58
59
|
docOptions: options.docOptions
|
|
59
60
|
});
|
|
60
61
|
}
|
|
61
|
-
function
|
|
62
|
-
const
|
|
63
|
-
const
|
|
62
|
+
function createMultiAlgorithmIndexedDBPatches(options) {
|
|
63
|
+
const baseStore = new IndexedDBStore(options.dbName);
|
|
64
|
+
const otStore = new OTIndexedDBStore(baseStore);
|
|
65
|
+
const lwwStore = new LWWIndexedDBStore(baseStore);
|
|
64
66
|
const otAlgorithm = new OTAlgorithm(otStore, options.docOptions);
|
|
65
67
|
const lwwAlgorithm = new LWWAlgorithm(lwwStore);
|
|
66
68
|
return new Patches({
|
|
@@ -71,10 +73,10 @@ function createAllIndexedDBPatches(options) {
|
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
75
|
export {
|
|
74
|
-
createAllIndexedDBPatches,
|
|
75
|
-
createAllPatches,
|
|
76
76
|
createLWWIndexedDBPatches,
|
|
77
77
|
createLWWPatches,
|
|
78
|
+
createMultiAlgorithmIndexedDBPatches,
|
|
79
|
+
createMultiAlgorithmPatches,
|
|
78
80
|
createOTIndexedDBPatches,
|
|
79
81
|
createOTPatches
|
|
80
82
|
};
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { B as BaseDoc, O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from '../BaseDoc-DkP3tUhT.js';
|
|
2
|
-
export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions,
|
|
2
|
+
export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches } from './factories.js';
|
|
3
3
|
export { IDBStoreWrapper, IDBTransactionWrapper, IndexedDBStore } from './IndexedDBStore.js';
|
|
4
4
|
export { OTIndexedDBStore } from './OTIndexedDBStore.js';
|
|
5
5
|
export { LWWIndexedDBStore } from './LWWIndexedDBStore.js';
|
|
6
|
-
export {
|
|
6
|
+
export { OTInMemoryStore } from './OTInMemoryStore.js';
|
|
7
7
|
export { LWWInMemoryStore } from './LWWInMemoryStore.js';
|
|
8
8
|
export { LWWDoc } from './LWWDoc.js';
|
|
9
9
|
export { LWWAlgorithm } from './LWWAlgorithm.js';
|
|
@@ -11,10 +11,10 @@ export { LWWBatcher } from './LWWBatcher.js';
|
|
|
11
11
|
export { OTAlgorithm } from './OTAlgorithm.js';
|
|
12
12
|
export { Patches, PatchesOptions } from './Patches.js';
|
|
13
13
|
export { PatchesHistoryClient } from './PatchesHistoryClient.js';
|
|
14
|
-
export { PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
14
|
+
export { AlgorithmName, PatchesStore, TrackedDoc } from './PatchesStore.js';
|
|
15
15
|
export { OTClientStore } from './OTClientStore.js';
|
|
16
16
|
export { LWWClientStore } from './LWWClientStore.js';
|
|
17
|
-
export {
|
|
17
|
+
export { ClientAlgorithm } from './ClientAlgorithm.js';
|
|
18
18
|
import '../event-signal.js';
|
|
19
19
|
import '../json-patch/types.js';
|
|
20
20
|
import '../types.js';
|
package/dist/client/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./factories.js";
|
|
|
3
3
|
export * from "./IndexedDBStore.js";
|
|
4
4
|
export * from "./OTIndexedDBStore.js";
|
|
5
5
|
export * from "./LWWIndexedDBStore.js";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./OTInMemoryStore.js";
|
|
7
7
|
export * from "./LWWInMemoryStore.js";
|
|
8
8
|
export * from "./LWWDoc.js";
|
|
9
9
|
export * from "./LWWAlgorithm.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { Delta } from '@dabble/delta';
|
|
2
2
|
export { B as BaseDoc, O as OTDoc, a as PatchesDoc, O as PatchesDocClass, P as PatchesDocOptions } from './BaseDoc-DkP3tUhT.js';
|
|
3
|
-
export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions,
|
|
3
|
+
export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches } from './client/factories.js';
|
|
4
4
|
export { IDBStoreWrapper, IDBTransactionWrapper, IndexedDBStore } from './client/IndexedDBStore.js';
|
|
5
5
|
export { OTIndexedDBStore } from './client/OTIndexedDBStore.js';
|
|
6
6
|
export { LWWIndexedDBStore } from './client/LWWIndexedDBStore.js';
|
|
7
|
-
export {
|
|
7
|
+
export { OTInMemoryStore } from './client/OTInMemoryStore.js';
|
|
8
8
|
export { LWWInMemoryStore } from './client/LWWInMemoryStore.js';
|
|
9
9
|
export { LWWDoc } from './client/LWWDoc.js';
|
|
10
10
|
export { LWWAlgorithm } from './client/LWWAlgorithm.js';
|
|
@@ -12,10 +12,10 @@ export { LWWBatcher } from './client/LWWBatcher.js';
|
|
|
12
12
|
export { OTAlgorithm } from './client/OTAlgorithm.js';
|
|
13
13
|
export { Patches, PatchesOptions } from './client/Patches.js';
|
|
14
14
|
export { PatchesHistoryClient } from './client/PatchesHistoryClient.js';
|
|
15
|
-
export { PatchesStore, TrackedDoc } from './client/PatchesStore.js';
|
|
15
|
+
export { AlgorithmName, PatchesStore, TrackedDoc } from './client/PatchesStore.js';
|
|
16
16
|
export { OTClientStore } from './client/OTClientStore.js';
|
|
17
17
|
export { LWWClientStore } from './client/LWWClientStore.js';
|
|
18
|
-
export {
|
|
18
|
+
export { ClientAlgorithm } from './client/ClientAlgorithm.js';
|
|
19
19
|
export { createChange } from './data/change.js';
|
|
20
20
|
export { createVersionId, createVersionMetadata } from './data/version.js';
|
|
21
21
|
export { ErrorSubscriber, Signal, SignalSubscriber, Unsubscriber, signal } from './event-signal.js';
|
|
@@ -6,14 +6,14 @@ import { PatchesWebSocket } from './websocket/PatchesWebSocket.js';
|
|
|
6
6
|
import { WebSocketOptions } from './websocket/WebSocketTransport.js';
|
|
7
7
|
import { SizeCalculator } from '../algorithms/ot/shared/changeBatching.js';
|
|
8
8
|
import { Patches } from '../client/Patches.js';
|
|
9
|
-
import { AlgorithmName
|
|
9
|
+
import { AlgorithmName } from '../client/PatchesStore.js';
|
|
10
|
+
import { ClientAlgorithm } from '../client/ClientAlgorithm.js';
|
|
10
11
|
import '../json-patch/JSONPatch.js';
|
|
11
12
|
import '@dabble/delta';
|
|
12
13
|
import '../json-patch/types.js';
|
|
13
14
|
import './PatchesClient.js';
|
|
14
15
|
import '../utils/deferred.js';
|
|
15
16
|
import '../BaseDoc-DkP3tUhT.js';
|
|
16
|
-
import '../client/PatchesStore.js';
|
|
17
17
|
|
|
18
18
|
interface PatchesSyncState {
|
|
19
19
|
online: boolean;
|
package/dist/net/PatchesSync.js
CHANGED
|
@@ -138,13 +138,19 @@ class PatchesSync {
|
|
|
138
138
|
if (!this.state.connected) return;
|
|
139
139
|
this.updateState({ syncing: "updating" });
|
|
140
140
|
try {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const allTracked = [];
|
|
142
|
+
for (const algorithm of Object.values(this.patches.algorithms)) {
|
|
143
|
+
if (!algorithm) continue;
|
|
144
|
+
const docs = await algorithm.listDocs(true);
|
|
145
|
+
allTracked.push(...docs);
|
|
146
|
+
for (const doc of docs) {
|
|
147
|
+
if (doc.algorithm) {
|
|
148
|
+
this.docAlgorithms.set(doc.docId, doc.algorithm);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
144
151
|
}
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const deletedDocs = tracked.filter((t) => t.deleted);
|
|
152
|
+
const activeDocs = allTracked.filter((t) => !t.deleted);
|
|
153
|
+
const deletedDocs = allTracked.filter((t) => t.deleted);
|
|
148
154
|
const activeDocIds = activeDocs.map((t) => t.docId);
|
|
149
155
|
this.trackedDocs = new Set(activeDocIds);
|
|
150
156
|
if (activeDocIds.length > 0) {
|
|
@@ -313,6 +319,17 @@ class PatchesSync {
|
|
|
313
319
|
const newIds = docIds.filter((id) => !this.trackedDocs.has(id));
|
|
314
320
|
if (!newIds.length) return;
|
|
315
321
|
newIds.forEach((id) => this.trackedDocs.add(id));
|
|
322
|
+
for (const docId of newIds) {
|
|
323
|
+
for (const algorithm of Object.values(this.patches.algorithms)) {
|
|
324
|
+
if (!algorithm) continue;
|
|
325
|
+
const docs = await algorithm.listDocs(false);
|
|
326
|
+
const tracked = docs.find((d) => d.docId === docId);
|
|
327
|
+
if (tracked?.algorithm) {
|
|
328
|
+
this.docAlgorithms.set(docId, tracked.algorithm);
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
316
333
|
let subscribeIds = newIds;
|
|
317
334
|
if (this.options?.subscribeFilter) {
|
|
318
335
|
const alreadyTracked = this.options.subscribeFilter([...this.trackedDocs]);
|
|
@@ -334,11 +351,12 @@ class PatchesSync {
|
|
|
334
351
|
const existingIds = docIds.filter((id) => this.trackedDocs.has(id));
|
|
335
352
|
if (!existingIds.length) return;
|
|
336
353
|
existingIds.forEach((id) => this.trackedDocs.delete(id));
|
|
337
|
-
|
|
354
|
+
const unsubscribeIds = this.options?.subscribeFilter?.(existingIds) || existingIds;
|
|
355
|
+
if (this.state.connected && unsubscribeIds.length) {
|
|
338
356
|
try {
|
|
339
|
-
await this.ws.unsubscribe(
|
|
357
|
+
await this.ws.unsubscribe(unsubscribeIds);
|
|
340
358
|
} catch (err) {
|
|
341
|
-
console.warn(`Failed to unsubscribe docs: ${
|
|
359
|
+
console.warn(`Failed to unsubscribe docs: ${unsubscribeIds.join(", ")}`, err);
|
|
342
360
|
}
|
|
343
361
|
}
|
|
344
362
|
}
|
package/dist/server/LWWServer.js
CHANGED
|
@@ -87,7 +87,7 @@ class LWWServer {
|
|
|
87
87
|
const change = changes[0];
|
|
88
88
|
const serverNow = Date.now();
|
|
89
89
|
const clientRev = change.rev;
|
|
90
|
-
const newOps = change.ops.map((op) => op.ts ? op : { ...op, ts: serverNow });
|
|
90
|
+
const newOps = change.ops.map((op) => op.ts ? op : { ...op, ts: change.createdAt ?? serverNow });
|
|
91
91
|
const existingOps = await this.store.listOps(docId);
|
|
92
92
|
const { opsToSave, pathsToDelete, opsToReturn } = consolidateOps(existingOps, newOps);
|
|
93
93
|
const opsToStore = convertDeltaOps(opsToSave);
|
package/dist/solid/context.d.ts
CHANGED
|
@@ -38,9 +38,9 @@ interface PatchesProviderProps {
|
|
|
38
38
|
* @example
|
|
39
39
|
* ```tsx
|
|
40
40
|
* import { PatchesProvider } from '@dabble/patches/solid';
|
|
41
|
-
* import { Patches,
|
|
41
|
+
* import { Patches, OTInMemoryStore } from '@dabble/patches/client';
|
|
42
42
|
*
|
|
43
|
-
* const patches = new Patches({ store: new
|
|
43
|
+
* const patches = new Patches({ store: new OTInMemoryStore() });
|
|
44
44
|
*
|
|
45
45
|
* <PatchesProvider patches={patches}>
|
|
46
46
|
* <App />
|
package/dist/vue/provider.d.ts
CHANGED
|
@@ -40,11 +40,11 @@ interface PatchesContext {
|
|
|
40
40
|
* @example
|
|
41
41
|
* ```typescript
|
|
42
42
|
* import { createApp } from 'vue'
|
|
43
|
-
* import { Patches,
|
|
43
|
+
* import { Patches, OTInMemoryStore } from '@dabble/patches/client'
|
|
44
44
|
* import { PatchesSync } from '@dabble/patches/net'
|
|
45
45
|
* import { providePatchesContext } from '@dabble/patches/vue'
|
|
46
46
|
*
|
|
47
|
-
* const patches = new Patches({ store: new
|
|
47
|
+
* const patches = new Patches({ store: new OTInMemoryStore() })
|
|
48
48
|
* const sync = new PatchesSync(patches, 'wss://your-server.com')
|
|
49
49
|
*
|
|
50
50
|
* const app = createApp(App)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Immutable JSON Patch implementation based on RFC 6902 supporting operational transformation and last-writer-wins",
|
|
5
5
|
"author": "Jacob Wright <jacwright@gmail.com>",
|
|
6
6
|
"bugs": {
|