@dabble/patches 0.8.11 → 0.8.13

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.
@@ -13,6 +13,12 @@ import '../json-patch/types.js';
13
13
  * Can be used as a standalone store or as a shared database connection
14
14
  * for multiple algorithm-specific stores (OT, LWW).
15
15
  *
16
+ * Supports two modes:
17
+ * - **Managed mode** (pass a `dbName`): Opens and owns the database lifecycle.
18
+ * - **External mode** (pass an `IDBDatabase` or `Promise<IDBDatabase>`): Uses a
19
+ * caller-provided database. The caller owns the lifecycle; `close()` detaches
20
+ * without closing, `deleteDB()` is a no-op, and `setName()` throws.
21
+ *
16
22
  * Provides:
17
23
  * - Database lifecycle management (open, close, delete)
18
24
  * - Transaction helpers
@@ -26,29 +32,37 @@ declare class IndexedDBStore implements PatchesStore, BranchClientStore {
26
32
  protected db: IDBDatabase | null;
27
33
  protected dbName?: string;
28
34
  protected dbPromise: Deferred<IDBDatabase>;
35
+ protected external: boolean;
29
36
  /**
30
37
  * Signal emitted during database upgrade, allowing algorithm-specific stores
31
38
  * to create their object stores.
32
39
  */
33
40
  readonly onUpgrade: easy_signal.Signal<(db: IDBDatabase, oldVersion: number, transaction: IDBTransaction) => void>;
34
- constructor(dbName?: string);
41
+ constructor(dbOrName?: string | IDBDatabase | Promise<IDBDatabase>);
35
42
  /**
36
- * Creates shared object stores used by all sync algorithms.
43
+ * Creates shared object stores (docs, snapshots, branches) during database upgrade.
37
44
  */
38
- protected createSharedStores(db: IDBDatabase, _oldVersion: number, _transaction: IDBTransaction): void;
45
+ static upgradeSharedStores(db: IDBDatabase, transaction: IDBTransaction): void;
39
46
  protected initDB(): Promise<void>;
40
47
  protected getDB(): Promise<IDBDatabase>;
41
48
  /**
42
49
  * Set the name of the database, loads a new database connection.
43
50
  * @param dbName - The new name of the database.
51
+ * @throws When using an externally-provided database.
44
52
  */
45
53
  setName(dbName: string): void;
46
54
  /**
47
55
  * Closes the database connection. After calling this method, the store
48
56
  * will no longer be usable. A new instance must be created to reopen
49
57
  * the database.
58
+ *
59
+ * When using an externally-provided database, this detaches from the
60
+ * database without closing it (the caller owns the lifecycle).
50
61
  */
51
62
  close(): Promise<void>;
63
+ /**
64
+ * Deletes the database. No-op when using an externally-provided database.
65
+ */
52
66
  deleteDB(): Promise<void>;
53
67
  transaction(storeNames: string[], mode: IDBTransactionMode): Promise<[IDBTransactionWrapper, ...IDBStoreWrapper[]]>;
54
68
  /**
@@ -7,25 +7,38 @@ class IndexedDBStore {
7
7
  db = null;
8
8
  dbName;
9
9
  dbPromise;
10
+ external;
10
11
  /**
11
12
  * Signal emitted during database upgrade, allowing algorithm-specific stores
12
13
  * to create their object stores.
13
14
  */
14
15
  onUpgrade = signal();
15
- constructor(dbName) {
16
- this.dbName = dbName;
16
+ constructor(dbOrName) {
17
17
  this.dbPromise = deferred();
18
- this.onUpgrade((db, oldVersion, transaction) => {
19
- this.createSharedStores(db, oldVersion, transaction);
20
- });
21
- if (this.dbName) {
22
- this.initDB();
18
+ if (dbOrName != null && typeof dbOrName !== "string") {
19
+ this.external = true;
20
+ Promise.resolve(dbOrName).then(
21
+ (db) => {
22
+ this.db = db;
23
+ this.dbPromise.resolve(db);
24
+ },
25
+ (err) => this.dbPromise.reject(err)
26
+ );
27
+ } else {
28
+ this.external = false;
29
+ this.dbName = dbOrName;
30
+ this.onUpgrade((db, _oldVersion, transaction) => {
31
+ IndexedDBStore.upgradeSharedStores(db, transaction);
32
+ });
33
+ if (this.dbName) {
34
+ this.initDB();
35
+ }
23
36
  }
24
37
  }
25
38
  /**
26
- * Creates shared object stores used by all sync algorithms.
39
+ * Creates shared object stores (docs, snapshots, branches) during database upgrade.
27
40
  */
28
- createSharedStores(db, _oldVersion, _transaction) {
41
+ static upgradeSharedStores(db, transaction) {
29
42
  if (!db.objectStoreNames.contains("docs")) {
30
43
  const docsStore = db.createObjectStore("docs", { keyPath: "docId" });
31
44
  docsStore.createIndex("algorithm", "algorithm", { unique: false });
@@ -38,7 +51,7 @@ class IndexedDBStore {
38
51
  branchStore.createIndex("_docId", "_docId", { unique: false });
39
52
  branchStore.createIndex("_pending", "_pending", { unique: false });
40
53
  } else {
41
- const branchStore = _transaction.objectStore("branches");
54
+ const branchStore = transaction.objectStore("branches");
42
55
  if (!branchStore.indexNames.contains("_pending")) {
43
56
  branchStore.createIndex("_pending", "_pending", { unique: false });
44
57
  }
@@ -65,8 +78,12 @@ class IndexedDBStore {
65
78
  /**
66
79
  * Set the name of the database, loads a new database connection.
67
80
  * @param dbName - The new name of the database.
81
+ * @throws When using an externally-provided database.
68
82
  */
69
83
  setName(dbName) {
84
+ if (this.external) {
85
+ throw new Error("Cannot set name on an externally-provided database");
86
+ }
70
87
  this.dbName = dbName;
71
88
  if (this.db) {
72
89
  this.db.close();
@@ -79,18 +96,27 @@ class IndexedDBStore {
79
96
  * Closes the database connection. After calling this method, the store
80
97
  * will no longer be usable. A new instance must be created to reopen
81
98
  * the database.
99
+ *
100
+ * When using an externally-provided database, this detaches from the
101
+ * database without closing it (the caller owns the lifecycle).
82
102
  */
83
103
  async close() {
104
+ if (!this.db) return;
84
105
  await this.dbPromise.promise;
85
106
  if (this.db) {
86
- this.db.close();
107
+ if (!this.external) {
108
+ this.db.close();
109
+ }
87
110
  this.db = null;
88
111
  this.dbPromise = deferred();
89
112
  this.dbPromise.reject(new Error("Store has been closed"));
90
113
  }
91
114
  }
115
+ /**
116
+ * Deletes the database. No-op when using an externally-provided database.
117
+ */
92
118
  async deleteDB() {
93
- if (!this.dbName) return;
119
+ if (this.external || !this.dbName) return;
94
120
  await this.close();
95
121
  await new Promise((resolve, reject) => {
96
122
  const request = indexedDB.deleteDatabase(this.dbName);
@@ -32,7 +32,7 @@ declare class LWWIndexedDBStore implements LWWClientStore {
32
32
  /**
33
33
  * Creates LWW-specific object stores during database upgrade.
34
34
  */
35
- protected createLWWStores(db: IDBDatabase): void;
35
+ static upgradeStores(db: IDBDatabase, _transaction: IDBTransaction): void;
36
36
  /**
37
37
  * List documents for the LWW algorithm.
38
38
  * Uses the algorithm index for efficient querying.
@@ -12,19 +12,19 @@ import { blockable } from "../utils/concurrency.js";
12
12
  import { IDBStoreWrapper, IndexedDBStore } from "./IndexedDBStore.js";
13
13
  const SNAPSHOT_INTERVAL = 200;
14
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 {
15
+ const _LWWIndexedDBStore = class _LWWIndexedDBStore {
16
16
  constructor(db) {
17
17
  __runInitializers(_init, 5, this);
18
18
  __publicField(this, "db");
19
19
  this.db = !db || typeof db === "string" ? new IndexedDBStore(db) : db;
20
- this.db.onUpgrade((db2, _oldVersion, _transaction) => {
21
- this.createLWWStores(db2);
20
+ this.db.onUpgrade((db2, _oldVersion, transaction) => {
21
+ _LWWIndexedDBStore.upgradeStores(db2, transaction);
22
22
  });
23
23
  }
24
24
  /**
25
25
  * Creates LWW-specific object stores during database upgrade.
26
26
  */
27
- createLWWStores(db) {
27
+ static upgradeStores(db, _transaction) {
28
28
  if (!db.objectStoreNames.contains("committedOps")) {
29
29
  db.createObjectStore("committedOps", { keyPath: ["docId", "path"] });
30
30
  }
@@ -316,18 +316,19 @@ class LWWIndexedDBStore {
316
316
  await snapshots.put({ docId, state, rev });
317
317
  await this.deleteFieldsForDoc(committedOps, docId);
318
318
  }
319
- }
319
+ };
320
320
  _init = __decoratorStart(null);
321
- __decorateElement(_init, 1, "getDoc", _getDoc_dec, LWWIndexedDBStore);
322
- __decorateElement(_init, 1, "saveDoc", _saveDoc_dec, LWWIndexedDBStore);
323
- __decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, LWWIndexedDBStore);
324
- __decorateElement(_init, 1, "getPendingOps", _getPendingOps_dec, LWWIndexedDBStore);
325
- __decorateElement(_init, 1, "savePendingOps", _savePendingOps_dec, LWWIndexedDBStore);
326
- __decorateElement(_init, 1, "getSendingChange", _getSendingChange_dec, LWWIndexedDBStore);
327
- __decorateElement(_init, 1, "saveSendingChange", _saveSendingChange_dec, LWWIndexedDBStore);
328
- __decorateElement(_init, 1, "confirmSendingChange", _confirmSendingChange_dec, LWWIndexedDBStore);
329
- __decorateElement(_init, 1, "applyServerChanges", _applyServerChanges_dec, LWWIndexedDBStore);
330
- __decoratorMetadata(_init, LWWIndexedDBStore);
321
+ __decorateElement(_init, 1, "getDoc", _getDoc_dec, _LWWIndexedDBStore);
322
+ __decorateElement(_init, 1, "saveDoc", _saveDoc_dec, _LWWIndexedDBStore);
323
+ __decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, _LWWIndexedDBStore);
324
+ __decorateElement(_init, 1, "getPendingOps", _getPendingOps_dec, _LWWIndexedDBStore);
325
+ __decorateElement(_init, 1, "savePendingOps", _savePendingOps_dec, _LWWIndexedDBStore);
326
+ __decorateElement(_init, 1, "getSendingChange", _getSendingChange_dec, _LWWIndexedDBStore);
327
+ __decorateElement(_init, 1, "saveSendingChange", _saveSendingChange_dec, _LWWIndexedDBStore);
328
+ __decorateElement(_init, 1, "confirmSendingChange", _confirmSendingChange_dec, _LWWIndexedDBStore);
329
+ __decorateElement(_init, 1, "applyServerChanges", _applyServerChanges_dec, _LWWIndexedDBStore);
330
+ __decoratorMetadata(_init, _LWWIndexedDBStore);
331
+ let LWWIndexedDBStore = _LWWIndexedDBStore;
331
332
  export {
332
333
  LWWIndexedDBStore
333
334
  };
@@ -33,7 +33,7 @@ declare class OTIndexedDBStore implements OTClientStore {
33
33
  /**
34
34
  * Creates OT-specific object stores during database upgrade.
35
35
  */
36
- protected createOTStores(db: IDBDatabase): void;
36
+ static upgradeStores(db: IDBDatabase, _transaction: IDBTransaction): void;
37
37
  /**
38
38
  * List documents for the OT algorithm.
39
39
  * Uses the algorithm index for efficient querying.
@@ -11,19 +11,19 @@ import { blockable } from "../utils/concurrency.js";
11
11
  import { IndexedDBStore } from "./IndexedDBStore.js";
12
12
  const SNAPSHOT_INTERVAL = 200;
13
13
  _getDoc_dec = [blockable], _deleteDoc_dec = [blockable], _saveDoc_dec = [blockable], _savePendingChanges_dec = [blockable], _getPendingChanges_dec = [blockable], _listChanges_dec = [blockable], _applyServerChanges_dec = [blockable];
14
- class OTIndexedDBStore {
14
+ const _OTIndexedDBStore = class _OTIndexedDBStore {
15
15
  constructor(db) {
16
16
  __runInitializers(_init, 5, this);
17
17
  __publicField(this, "db");
18
18
  this.db = !db || typeof db === "string" ? new IndexedDBStore(db) : db;
19
- this.db.onUpgrade((db2, _oldVersion, _transaction) => {
20
- this.createOTStores(db2);
19
+ this.db.onUpgrade((db2, _oldVersion, transaction) => {
20
+ _OTIndexedDBStore.upgradeStores(db2, transaction);
21
21
  });
22
22
  }
23
23
  /**
24
24
  * Creates OT-specific object stores during database upgrade.
25
25
  */
26
- createOTStores(db) {
26
+ static upgradeStores(db, _transaction) {
27
27
  if (!db.objectStoreNames.contains("committedChanges")) {
28
28
  db.createObjectStore("committedChanges", { keyPath: ["docId", "rev"] });
29
29
  }
@@ -207,16 +207,17 @@ class OTIndexedDBStore {
207
207
  );
208
208
  await tx.complete();
209
209
  }
210
- }
210
+ };
211
211
  _init = __decoratorStart(null);
212
- __decorateElement(_init, 1, "getDoc", _getDoc_dec, OTIndexedDBStore);
213
- __decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, OTIndexedDBStore);
214
- __decorateElement(_init, 1, "saveDoc", _saveDoc_dec, OTIndexedDBStore);
215
- __decorateElement(_init, 1, "savePendingChanges", _savePendingChanges_dec, OTIndexedDBStore);
216
- __decorateElement(_init, 1, "getPendingChanges", _getPendingChanges_dec, OTIndexedDBStore);
217
- __decorateElement(_init, 1, "listChanges", _listChanges_dec, OTIndexedDBStore);
218
- __decorateElement(_init, 1, "applyServerChanges", _applyServerChanges_dec, OTIndexedDBStore);
219
- __decoratorMetadata(_init, OTIndexedDBStore);
212
+ __decorateElement(_init, 1, "getDoc", _getDoc_dec, _OTIndexedDBStore);
213
+ __decorateElement(_init, 1, "deleteDoc", _deleteDoc_dec, _OTIndexedDBStore);
214
+ __decorateElement(_init, 1, "saveDoc", _saveDoc_dec, _OTIndexedDBStore);
215
+ __decorateElement(_init, 1, "savePendingChanges", _savePendingChanges_dec, _OTIndexedDBStore);
216
+ __decorateElement(_init, 1, "getPendingChanges", _getPendingChanges_dec, _OTIndexedDBStore);
217
+ __decorateElement(_init, 1, "listChanges", _listChanges_dec, _OTIndexedDBStore);
218
+ __decorateElement(_init, 1, "applyServerChanges", _applyServerChanges_dec, _OTIndexedDBStore);
219
+ __decoratorMetadata(_init, _OTIndexedDBStore);
220
+ let OTIndexedDBStore = _OTIndexedDBStore;
220
221
  export {
221
222
  OTIndexedDBStore
222
223
  };
@@ -123,12 +123,6 @@ declare class Patches {
123
123
  * Should be called when shutting down the client.
124
124
  */
125
125
  close(): Promise<void>;
126
- /**
127
- * Submits ops for a document through the serialized change queue.
128
- * Used by PatchesBranchClient to merge branch changes without racing
129
- * against concurrent user edits on the same document.
130
- */
131
- submitDocChange(docId: string, ops: JSONPatchOp[], metadata?: Record<string, any>): Promise<void>;
132
126
  /**
133
127
  * Internal handler for doc changes. Called when doc.onChange emits ops.
134
128
  * Serializes calls per docId to prevent concurrent handleDocChange from
@@ -197,17 +197,6 @@ class Patches {
197
197
  this.onServerCommit.clear();
198
198
  this.onError.clear();
199
199
  }
200
- /**
201
- * Submits ops for a document through the serialized change queue.
202
- * Used by PatchesBranchClient to merge branch changes without racing
203
- * against concurrent user edits on the same document.
204
- */
205
- submitDocChange(docId, ops, metadata = {}) {
206
- const managed = this.docs.get(docId);
207
- const algorithm = this.getDocAlgorithm(docId) ?? this.algorithms[this.defaultAlgorithm];
208
- if (!algorithm) throw new Error(`No algorithm found for document ${docId}`);
209
- return this._handleDocChange(docId, ops, managed?.doc, algorithm, metadata);
210
- }
211
200
  /**
212
201
  * Internal handler for doc changes. Called when doc.onChange emits ops.
213
202
  * Serializes calls per docId to prevent concurrent handleDocChange from
@@ -1,7 +1,7 @@
1
1
  import { Store } from 'easy-signal';
2
2
  import { SizeCalculator } from '../algorithms/ot/shared/changeBatching.js';
3
3
  import { BranchAPI } from '../net/protocol/types.js';
4
- import { Branch, ListBranchesOptions, CreateBranchMetadata } from '../types.js';
4
+ import { Branch, ListBranchesOptions, CreateBranchMetadata, EditableBranchMetadata } from '../types.js';
5
5
  import { BranchClientStore } from './BranchClientStore.js';
6
6
  import { Patches } from './Patches.js';
7
7
  import { AlgorithmName } from './PatchesStore.js';
@@ -26,8 +26,8 @@ interface PatchesBranchClientOptions {
26
26
  * (offline-first, local store handles caching/pending/tombstones). The API shape
27
27
  * determines merge behavior:
28
28
  *
29
- * - `BranchAPI` has `mergeBranch` — server performs the merge
30
- * - `BranchClientStore` has `updateBranch` client merges locally, updates `lastMergedRev`
29
+ * - `BranchAPI` — server performs the merge via `mergeBranch`
30
+ * - `BranchClientStore` merge is not supported; call the server merge endpoint directly
31
31
  */
32
32
  declare class PatchesBranchClient {
33
33
  private readonly api;
@@ -64,6 +64,10 @@ declare class PatchesBranchClient {
64
64
  * When `initialState` is omitted, the branch is created directly via the API.
65
65
  */
66
66
  createBranch(rev: number, metadata?: CreateBranchMetadata, initialState?: any): Promise<string>;
67
+ /**
68
+ * Update branch metadata (e.g. name).
69
+ */
70
+ updateBranch(branchId: string, metadata: EditableBranchMetadata): Promise<void>;
67
71
  /**
68
72
  * Delete a branch.
69
73
  * The API implementation handles tombstones (offline store) or direct deletion (online).
@@ -77,16 +81,15 @@ declare class PatchesBranchClient {
77
81
  /**
78
82
  * Merge a branch's changes back into this document.
79
83
  *
80
- * Online (BranchAPI with `mergeBranch`): server performs the merge.
81
- * Offline (BranchClientStore with `updateBranch`): client reads branch changes,
82
- * re-stamps them with `batchId: branchId`, submits via algorithm.handleDocChange
83
- * on the source doc, then updates `lastMergedRev` locally.
84
+ * Requires a `BranchAPI` (online mode) — the server performs the merge.
85
+ * Throws if the API is a `BranchClientStore` (offline-first mode) because
86
+ * client stores don't maintain full change history needed for correct merging.
87
+ * Offline-first consumers should call the server merge endpoint directly.
84
88
  */
85
89
  mergeBranch(branchId: string): Promise<void>;
86
90
  /** Clear state */
87
91
  clear(): void;
88
92
  private _createBranchOffline;
89
- private _mergeBranchLocally;
90
93
  }
91
94
 
92
95
  export { PatchesBranchClient, type PatchesBranchClientOptions };
@@ -2,6 +2,7 @@ import "../chunk-IZ2YBCUP.js";
2
2
  import { store } from "easy-signal";
3
3
  import { breakChanges } from "../algorithms/ot/shared/changeBatching.js";
4
4
  import { createChange } from "../data/change.js";
5
+ const OFFLINE_MERGE_ERROR = "Branch merging requires a server connection. Use a BranchAPI or call the server merge endpoint directly.";
5
6
  class PatchesBranchClient {
6
7
  constructor(id, api, patches, options) {
7
8
  this.api = api;
@@ -61,6 +62,13 @@ class PatchesBranchClient {
61
62
  await this.listBranches();
62
63
  return branchId;
63
64
  }
65
+ /**
66
+ * Update branch metadata (e.g. name).
67
+ */
68
+ async updateBranch(branchId, metadata) {
69
+ await this.api.updateBranch(branchId, metadata);
70
+ this.branches.state = this.branches.state.map((b) => b.id === branchId ? { ...b, ...metadata } : b);
71
+ }
64
72
  /**
65
73
  * Delete a branch.
66
74
  * The API implementation handles tombstones (offline store) or direct deletion (online).
@@ -80,18 +88,17 @@ class PatchesBranchClient {
80
88
  /**
81
89
  * Merge a branch's changes back into this document.
82
90
  *
83
- * Online (BranchAPI with `mergeBranch`): server performs the merge.
84
- * Offline (BranchClientStore with `updateBranch`): client reads branch changes,
85
- * re-stamps them with `batchId: branchId`, submits via algorithm.handleDocChange
86
- * on the source doc, then updates `lastMergedRev` locally.
91
+ * Requires a `BranchAPI` (online mode) — the server performs the merge.
92
+ * Throws if the API is a `BranchClientStore` (offline-first mode) because
93
+ * client stores don't maintain full change history needed for correct merging.
94
+ * Offline-first consumers should call the server merge endpoint directly.
87
95
  */
88
96
  async mergeBranch(branchId) {
89
- if (!this.isOffline) {
90
- await this.api.mergeBranch(branchId);
91
- await this.listBranches();
92
- return;
97
+ if (this.isOffline) {
98
+ throw new Error(OFFLINE_MERGE_ERROR);
93
99
  }
94
- await this._mergeBranchLocally(branchId);
100
+ await this.api.mergeBranch(branchId);
101
+ await this.listBranches();
95
102
  }
96
103
  /** Clear state */
97
104
  clear() {
@@ -141,29 +148,6 @@ class PatchesBranchClient {
141
148
  this.patches.onChange.emit(branchDocId);
142
149
  return branchDocId;
143
150
  }
144
- async _mergeBranchLocally(branchId) {
145
- const offlineApi = this.api;
146
- const branch = this.branches.state.find((b) => b.id === branchId);
147
- if (!branch) throw new Error(`Branch ${branchId} not found`);
148
- const sourceDocId = branch.docId;
149
- const algorithmName = this.options?.algorithm ?? this.patches.defaultAlgorithm;
150
- const algorithm = this.patches.algorithms[algorithmName];
151
- if (!algorithm?.listChanges) {
152
- throw new Error("Offline merge requires an algorithm with listChanges support");
153
- }
154
- const startAfter = branch.lastMergedRev ?? (branch.contentStartRev ?? 2) - 1;
155
- const branchChanges = await algorithm.listChanges(branchId, { startAfter });
156
- if (branchChanges.length === 0) return;
157
- const lastBranchRev = branchChanges[branchChanges.length - 1].rev;
158
- for (const change of branchChanges) {
159
- await this.patches.submitDocChange(sourceDocId, change.ops, { batchId: branchId });
160
- }
161
- await offlineApi.updateBranch(branchId, { lastMergedRev: lastBranchRev });
162
- this.patches.onChange.emit(sourceDocId);
163
- this.branches.state = this.branches.state.map(
164
- (b) => b.id === branchId ? { ...b, lastMergedRev: lastBranchRev } : b
165
- );
166
- }
167
151
  }
168
152
  export {
169
153
  PatchesBranchClient
@@ -69,5 +69,20 @@ declare function createMultiAlgorithmPatches(options?: MultiAlgorithmFactoryOpti
69
69
  * Both algorithms share the same IndexedDB database with unified stores.
70
70
  */
71
71
  declare function createMultiAlgorithmIndexedDBPatches(options: MultiAlgorithmIndexedDBFactoryOptions): Patches;
72
+ /**
73
+ * Creates a Patches instance with both OT and LWW algorithms using an externally-managed
74
+ * IDBDatabase. The caller is responsible for opening the database and calling
75
+ * `upgradePatchesDB()` during `onupgradeneeded` to create the required stores.
76
+ *
77
+ * Use this when you want to host Patches stores inside your own IndexedDB database
78
+ * rather than having Patches open a separate one.
79
+ */
80
+ declare function createMultiAlgorithmExternalDBPatches(db: IDBDatabase | Promise<IDBDatabase>, options?: MultiAlgorithmFactoryOptions): Patches;
81
+ /**
82
+ * Creates all Patches object stores in an externally-managed database.
83
+ * Call this from your `onupgradeneeded` handler when hosting Patches stores
84
+ * inside your own IndexedDB database.
85
+ */
86
+ declare function upgradePatchesDB(db: IDBDatabase, transaction: IDBTransaction): void;
72
87
 
73
- export { type IndexedDBFactoryOptions, type MultiAlgorithmFactoryOptions, type MultiAlgorithmIndexedDBFactoryOptions, type PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches };
88
+ export { type IndexedDBFactoryOptions, type MultiAlgorithmFactoryOptions, type MultiAlgorithmIndexedDBFactoryOptions, type PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmExternalDBPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches, upgradePatchesDB };
@@ -72,11 +72,31 @@ function createMultiAlgorithmIndexedDBPatches(options) {
72
72
  docOptions: options.docOptions
73
73
  });
74
74
  }
75
+ function createMultiAlgorithmExternalDBPatches(db, options = {}) {
76
+ const baseStore = new IndexedDBStore(db);
77
+ const otStore = new OTIndexedDBStore(baseStore);
78
+ const lwwStore = new LWWIndexedDBStore(baseStore);
79
+ const otAlgorithm = new OTAlgorithm(otStore, options.docOptions);
80
+ const lwwAlgorithm = new LWWAlgorithm(lwwStore);
81
+ return new Patches({
82
+ algorithms: { ot: otAlgorithm, lww: lwwAlgorithm },
83
+ defaultAlgorithm: options.defaultAlgorithm ?? "ot",
84
+ metadata: options.metadata,
85
+ docOptions: options.docOptions
86
+ });
87
+ }
88
+ function upgradePatchesDB(db, transaction) {
89
+ IndexedDBStore.upgradeSharedStores(db, transaction);
90
+ OTIndexedDBStore.upgradeStores(db, transaction);
91
+ LWWIndexedDBStore.upgradeStores(db, transaction);
92
+ }
75
93
  export {
76
94
  createLWWIndexedDBPatches,
77
95
  createLWWPatches,
96
+ createMultiAlgorithmExternalDBPatches,
78
97
  createMultiAlgorithmIndexedDBPatches,
79
98
  createMultiAlgorithmPatches,
80
99
  createOTIndexedDBPatches,
81
- createOTPatches
100
+ createOTPatches,
101
+ upgradePatchesDB
82
102
  };
@@ -1,5 +1,5 @@
1
1
  export { B as BaseDoc, O as OTDoc, P as PatchesDoc, a as PatchesDocOptions } from '../BaseDoc-BT18xPxU.js';
2
- export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches } from './factories.js';
2
+ export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmExternalDBPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches, upgradePatchesDB } 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';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Delta } from '@dabble/delta';
2
2
  export { B as BaseDoc, O as OTDoc, P as PatchesDoc, a as PatchesDocOptions } from './BaseDoc-BT18xPxU.js';
3
- export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches } from './client/factories.js';
3
+ export { IndexedDBFactoryOptions, MultiAlgorithmFactoryOptions, MultiAlgorithmIndexedDBFactoryOptions, PatchesFactoryOptions, createLWWIndexedDBPatches, createLWWPatches, createMultiAlgorithmExternalDBPatches, createMultiAlgorithmIndexedDBPatches, createMultiAlgorithmPatches, createOTIndexedDBPatches, createOTPatches, upgradePatchesDB } 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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dabble/patches",
3
- "version": "0.8.11",
3
+ "version": "0.8.13",
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": {