@dabble/patches 0.8.11 → 0.8.12

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
  };
@@ -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';
@@ -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).
@@ -61,6 +61,13 @@ class PatchesBranchClient {
61
61
  await this.listBranches();
62
62
  return branchId;
63
63
  }
64
+ /**
65
+ * Update branch metadata (e.g. name).
66
+ */
67
+ async updateBranch(branchId, metadata) {
68
+ await this.api.updateBranch(branchId, metadata);
69
+ this.branches.state = this.branches.state.map((b) => b.id === branchId ? { ...b, ...metadata } : b);
70
+ }
64
71
  /**
65
72
  * Delete a branch.
66
73
  * The API implementation handles tombstones (offline store) or direct deletion (online).
@@ -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.12",
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": {