@prisma-idb/driver-idb 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,8 @@ import { ExecutionPlan } from "@prisma/orm-framework/components/runtime";
4
4
  /**
5
5
  * Name of the object store that holds the contract marker.
6
6
  *
7
- * Created during `upgradeneeded` by the migration runner and verified by the
8
- * runtime before every query execution to detect schema drift.
7
+ * Created during `upgradeneeded` by the migration runner. `IdbRuntime.verifyMarker()`
8
+ * compares its stored hash with the contract's to detect schema drift.
9
9
  *
10
10
  * Upstream equivalent: `_prisma_marker` table in SQLite/Postgres,
11
11
  * `_prisma_marker` collection in MongoDB.
@@ -127,7 +127,7 @@ interface IdbPutPlan extends ExecutionPlan {
127
127
  *
128
128
  * Used for Prisma `update` where only a subset of fields change. The driver:
129
129
  * 1. Reads the current record via `store.get(key)`.
130
- * 2. Deep-merges `patch` onto the existing record with `{ ...existing, ...patch }`.
130
+ * 2. Shallow-merges `patch` onto the existing record with `{ ...existing, ...patch }`.
131
131
  * 3. Writes the merged record back via `store.put(merged)`.
132
132
  * 4. Echoes the merged record as the result row.
133
133
  *
@@ -288,7 +288,7 @@ declare class IdbRuntimeDriverInstance implements RuntimeDriverInstance<"idb", "
288
288
  * The live IDBDatabase connection. Resolves once `upgradeneeded` (if any)
289
289
  * completes and the database is ready for use.
290
290
  *
291
- * The adapter awaits this inside `runDriver()` before opening transactions.
291
+ * `execute()` and `transaction()` await this before opening a transaction.
292
292
  * The Promise is shared — all concurrent callers get the same database object.
293
293
  */
294
294
  readonly db: Promise<IDBDatabase>;
@@ -300,7 +300,7 @@ declare class IdbRuntimeDriverInstance implements RuntimeDriverInstance<"idb", "
300
300
  * Returns `null` when the marker store does not exist (fresh database
301
301
  * that hasn't been initialised yet) or when no marker record is present.
302
302
  *
303
- * Called by `IdbRuntimeImpl` at init time to verify that the live IDB
303
+ * Called by `IdbRuntimeImpl.verifyMarker()` to check that the live IDB
304
304
  * schema matches the contract.
305
305
  */
306
306
  readMarker(): Promise<IdbMarkerRecord | null>;
@@ -362,13 +362,13 @@ declare class IdbExecuteError extends Error {
362
362
  /**
363
363
  * Creates a runtime driver descriptor for IndexedDB.
364
364
  *
365
- * The returned descriptor's `create()` opens the named IDB database
366
- * lazily — the connection resolves in the background and is awaited by
367
- * the adapter inside `runDriver()` for the first query.
365
+ * The returned descriptor's `create()` starts opening the named IDB database
366
+ * in the background; the driver awaits the connection before its first query.
368
367
  *
369
368
  * @param dbName - The IDB database name to open.
370
- * @param version - The IDB version number (default: 1). Bumping this
371
- * triggers `upgradeneeded`-based migrations via `IdbMigrationRunner`.
369
+ * @param version - The IDB version number. When omitted, IndexedDB opens the
370
+ * database at its current version (version 1 for a new database). The
371
+ * driver never runs migrations; `openAndUpgrade` (target-idb) bumps the version.
372
372
  *
373
373
  * @example
374
374
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/core/plan-body.ts","../src/core/transaction-scope.ts","../src/core/idb-driver.ts","../src/core/execute/error.ts","../src/exports/runtime.ts"],"mappings":";;;;;;;;;;;;cAaa;;;;;;;;;;;;;;UAeI;;WAEN;;WAEA;;WAEA;;WAEA,YAAY;WACZ;WACA;WACA;WACA;WACA,OAAO;;;;;;KAON,gBAAgB,KAAK;;;;;KAMrB,oBAAoB,GAAG,yBAAyB,GAAG;;;;;;;;;;;UAY9C,0BAA0B;WAChC;WACA;WACA;WACA,QAAQ;WACR,YAAY;WACZ,SAAS;WACT,aAAa;WACb;WACA;;;;;;;UAQM,sBAAsB;WAC5B;WACA;WACA,KAAK;;;;;;;;UASC,wBAAwB;WAC9B;WACA;WACA;WACA,OAAO;;;;;;;;;;;UAYD,mBAAmB;WACzB;WACA;WACA,QAAQ;WACR,MAAM;;;;;;;;;;;UAYA,mBAAmB;WACzB;WACA;WACA,QAAQ;WACR,MAAM;;;;;;;;;;;;;;;;;UAkBA,sBAAsB;WAC5B;WACA;WACA,KAAK;WACL,OAAO;;;;;;;;;;UAWD,sBAAsB;WAC5B;WACA;WACA,KAAK,cAAc;;;;;;;;;;;;;;;;;UAkBb,yBAAyB;WAC/B;WACA;WACA,SAAS;WACT;WACA;WACA,QAAQ;;;;;;;;;;;;;UAcF,qBAAqB;WAC3B;WACA;WACA;WACA,QAAQ;;;;;;;;;;;;;;;;;;UAmBF,oBAAoB;WAC1B;WACA;WACA;WACA,QAAQ;WACR;;;;;KAMC,gBACR,oBACA,gBACA,kBACA,eACA,cACA,aACA,aACA,gBACA,gBACA;;;;;;;;;;;;;;UAea,qBAAqB;WAC3B;WACA,YAAY;WACZ,KAAK,cAAc;;;;;KAMlB,cAAc,gBAAgB;;;KCxPrC,MAAM;UAIM;;;;;;;;EAQf,QAAQ,MAAM,gBAAgB,QAAQ;;;;;;;EAQtC,UAAU;;;;;;;EAQV;;;;;;;;iBAoFc,uBACd,IAAI,aACJ,sBACA,OAAM,qBACL;;;cCxIU,oCAAoC;WACtC;WACA;;;;;;;;WAQA,IAAI,QAAQ;EAET,YAAA,gBAAgB,kBAAkB,UAAU;EAIlD,SAAS;;;;;;;;;;EAaT,cAAc,QAAQ;;;;;;;;;EA+BtB,YAAY,sBAAsB,OAAM,qBAAmC,QAAQ;;;;;;;;;;EAczF,QAAQ,MAAM,cAAc,cAAc;;;;;;;;;;;KCxEhC;;;;;;;;cAqBC,wBAAwB;WAC1B,MAAM;WACN;WACA;;WAEA;;WAEA;WACS;EAGhB,YAAA;aACW,MAAM;aACN;aACA;aACA;KAEX;;;;;;;;;;;;;;;;;;;;;;;;iBCIY,uBACd,gBACA,kBACA;WAAqB,UAAU;IAC9B,4CAA4C"}
1
+ {"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/core/plan-body.ts","../src/core/transaction-scope.ts","../src/core/idb-driver.ts","../src/core/execute/error.ts","../src/exports/runtime.ts"],"mappings":";;;;;;;;;;;;cAaa;;;;;;;;;;;;;;UAeI;;WAEN;;WAEA;;WAEA;;WAEA,YAAY;WACZ;WACA;WACA;WACA;WACA,OAAO;;;;;;KAON,gBAAgB,KAAK;;;;;KAMrB,oBAAoB,GAAG,yBAAyB,GAAG;;;;;;;;;;;UAY9C,0BAA0B;WAChC;WACA;WACA;WACA,QAAQ;WACR,YAAY;WACZ,SAAS;WACT,aAAa;WACb;WACA;;;;;;;UAQM,sBAAsB;WAC5B;WACA;WACA,KAAK;;;;;;;;UASC,wBAAwB;WAC9B;WACA;WACA;WACA,OAAO;;;;;;;;;;;UAYD,mBAAmB;WACzB;WACA;WACA,QAAQ;WACR,MAAM;;;;;;;;;;;UAYA,mBAAmB;WACzB;WACA;WACA,QAAQ;WACR,MAAM;;;;;;;;;;;;;;;;;UAkBA,sBAAsB;WAC5B;WACA;WACA,KAAK;WACL,OAAO;;;;;;;;;;UAWD,sBAAsB;WAC5B;WACA;WACA,KAAK,cAAc;;;;;;;;;;;;;;;;;UAkBb,yBAAyB;WAC/B;WACA;WACA,SAAS;WACT;WACA;WACA,QAAQ;;;;;;;;;;;;;UAcF,qBAAqB;WAC3B;WACA;WACA;WACA,QAAQ;;;;;;;;;;;;;;;;;;UAmBF,oBAAoB;WAC1B;WACA;WACA;WACA,QAAQ;WACR;;;;;KAMC,gBACR,oBACA,gBACA,kBACA,eACA,cACA,aACA,aACA,gBACA,gBACA;;;;;;;;;;;;;;UAea,qBAAqB;WAC3B;WACA,YAAY;WACZ,KAAK,cAAc;;;;;KAMlB,cAAc,gBAAgB;;;KCxPrC,MAAM;UAIM;;;;;;;;EAQf,QAAQ,MAAM,gBAAgB,QAAQ;;;;;;;EAQtC,UAAU;;;;;;;EAQV;;;;;;;;iBAoFc,uBACd,IAAI,aACJ,sBACA,OAAM,qBACL;;;cCxIU,oCAAoC;WACtC;WACA;;;;;;;;WAQA,IAAI,QAAQ;EAET,YAAA,gBAAgB,kBAAkB,UAAU;EAIlD,SAAS;;;;;;;;;;EAaT,cAAc,QAAQ;;;;;;;;;EA4BtB,YAAY,sBAAsB,OAAM,qBAAmC,QAAQ;;;;;;;;;;EAczF,QAAQ,MAAM,cAAc,cAAc;;;;;;;;;;;KCrEhC;;;;;;;;cAqBC,wBAAwB;WAC1B,MAAM;WACN;WACA;;WAEA;;WAEA;WACS;EAGhB,YAAA;aACW,MAAM;aACN;aACA;aACA;KAEX;;;;;;;;;;;;;;;;;;;;;;;;iBCIY,uBACd,gBACA,kBACA;WAAqB,UAAU;IAC9B,4CAA4C"}
package/dist/runtime.mjs CHANGED
@@ -47,6 +47,23 @@ function transactionInactiveError(planKind, storeName, cause) {
47
47
  //#endregion
48
48
  //#region src/core/execute/ops.ts
49
49
  /**
50
+ * The error for a failed request: `IDB <what> failed on <target>: <cause>`.
51
+ * `target` defaults to the plan's store; pass it when the message also names an index.
52
+ */
53
+ function opError(code, plan, what, cause, target = `store "${plan.storeName}"`) {
54
+ return new IdbExecuteError({
55
+ code,
56
+ planKind: plan.kind,
57
+ storeName: plan.storeName,
58
+ cause
59
+ }, `IDB ${what} failed on ${target}: ${String(cause)}`);
60
+ }
61
+ /** `store "users"` or `store "users" (index "by_name")`, for plans that may read through an index. */
62
+ function storeTarget(plan) {
63
+ const index = plan.indexName !== void 0 ? ` (index "${plan.indexName}")` : "";
64
+ return `store "${plan.storeName}"${index}`;
65
+ }
66
+ /**
50
67
  * Dispatch an atomic plan to its IDB operation within a live transaction.
51
68
  *
52
69
  * All IDB requests are issued synchronously. `onComplete` is called once all
@@ -86,42 +103,22 @@ function execKeyGet(store, plan, onComplete, onError) {
86
103
  const value = req.result;
87
104
  onComplete(value !== void 0 ? [value] : []);
88
105
  };
89
- req.onerror = () => onError(new IdbExecuteError({
90
- code: "KEY_GET_FAILED",
91
- planKind: "key-get",
92
- storeName: plan.storeName,
93
- cause: req.error
94
- }, `IDB key-get failed on store "${plan.storeName}": ${String(req.error)}`));
106
+ req.onerror = () => onError(opError("KEY_GET_FAILED", plan, "key-get", req.error));
95
107
  }
96
108
  function execIndexGet(store, plan, onComplete, onError) {
97
109
  const req = store.index(plan.indexName).getAll(plan.range);
98
110
  req.onsuccess = () => onComplete(req.result);
99
- req.onerror = () => onError(new IdbExecuteError({
100
- code: "INDEX_GET_FAILED",
101
- planKind: "index-get",
102
- storeName: plan.storeName,
103
- cause: req.error
104
- }, `IDB index-get failed on "${plan.storeName}"/"${plan.indexName}": ${String(req.error)}`));
111
+ req.onerror = () => onError(opError("INDEX_GET_FAILED", plan, "index-get", req.error, `"${plan.storeName}"/"${plan.indexName}"`));
105
112
  }
106
113
  function execCount(store, plan, onComplete, onError) {
107
114
  const source = plan.indexName !== void 0 ? store.index(plan.indexName) : store;
108
115
  const req = plan.range !== void 0 ? source.count(plan.range) : source.count();
109
116
  req.onsuccess = () => onComplete([{ count: req.result }]);
110
- req.onerror = () => onError(new IdbExecuteError({
111
- code: "COUNT_FAILED",
112
- planKind: "count",
113
- storeName: plan.storeName,
114
- cause: req.error
115
- }, `IDB count failed on store "${plan.storeName}"${plan.indexName !== void 0 ? ` (index "${plan.indexName}")` : ""}: ${String(req.error)}`));
117
+ req.onerror = () => onError(opError("COUNT_FAILED", plan, "count", req.error, storeTarget(plan)));
116
118
  }
117
119
  function execKeys(store, plan, onComplete, onError) {
118
120
  const source = plan.indexName !== void 0 ? store.index(plan.indexName) : store;
119
- const fail = (cause) => onError(new IdbExecuteError({
120
- code: "KEYS_FAILED",
121
- planKind: "keys",
122
- storeName: plan.storeName,
123
- cause
124
- }, `IDB keys read failed on store "${plan.storeName}"${plan.indexName !== void 0 ? ` (index "${plan.indexName}")` : ""}: ${String(cause)}`));
121
+ const fail = (cause) => onError(opError("KEYS_FAILED", plan, "keys read", cause, storeTarget(plan)));
125
122
  if (plan.take === 1 && plan.range !== void 0) {
126
123
  const req = source.getKey(plan.range);
127
124
  req.onsuccess = () => onComplete(req.result === void 0 ? [] : [{ key: req.result }]);
@@ -168,12 +165,7 @@ function execCursorScan(store, plan, onComplete, onError) {
168
165
  }
169
166
  cursor.continue();
170
167
  };
171
- req.onerror = () => onError(new IdbExecuteError({
172
- code: "CURSOR_SCAN_FAILED",
173
- planKind: "cursor-scan",
174
- storeName: plan.storeName,
175
- cause: req.error
176
- }, `IDB cursor-scan failed on store "${plan.storeName}": ${String(req.error)}`));
168
+ req.onerror = () => onError(opError("CURSOR_SCAN_FAILED", plan, "cursor-scan", req.error));
177
169
  }
178
170
  /**
179
171
  * The record as stored: `record` itself, plus the generated key when an
@@ -191,22 +183,12 @@ function withGeneratedKey(store, record, key) {
191
183
  function execAdd(store, plan, onComplete, onError) {
192
184
  const req = plan.key !== void 0 ? store.add(plan.record, plan.key) : store.add(plan.record);
193
185
  req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);
194
- req.onerror = () => onError(new IdbExecuteError({
195
- code: "ADD_FAILED",
196
- planKind: "add",
197
- storeName: plan.storeName,
198
- cause: req.error
199
- }, `IDB add failed on store "${plan.storeName}": ${String(req.error)}`));
186
+ req.onerror = () => onError(opError("ADD_FAILED", plan, "add", req.error));
200
187
  }
201
188
  function execPut(store, plan, onComplete, onError) {
202
189
  const req = plan.key !== void 0 ? store.put(plan.record, plan.key) : store.put(plan.record);
203
190
  req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);
204
- req.onerror = () => onError(new IdbExecuteError({
205
- code: "PUT_FAILED",
206
- planKind: "put",
207
- storeName: plan.storeName,
208
- cause: req.error
209
- }, `IDB put failed on store "${plan.storeName}": ${String(req.error)}`));
191
+ req.onerror = () => onError(opError("PUT_FAILED", plan, "put", req.error));
210
192
  }
211
193
  function execUpdate(store, plan, onComplete, onError) {
212
194
  const getReq = store.get(plan.key);
@@ -217,19 +199,9 @@ function execUpdate(store, plan, onComplete, onError) {
217
199
  };
218
200
  const putReq = store.put(merged);
219
201
  putReq.onsuccess = () => onComplete([merged]);
220
- putReq.onerror = () => onError(new IdbExecuteError({
221
- code: "PUT_FAILED",
222
- planKind: "update",
223
- storeName: plan.storeName,
224
- cause: putReq.error
225
- }, `IDB update (put phase) failed on store "${plan.storeName}": ${String(putReq.error)}`));
202
+ putReq.onerror = () => onError(opError("PUT_FAILED", plan, "update (put phase)", putReq.error));
226
203
  };
227
- getReq.onerror = () => onError(new IdbExecuteError({
228
- code: "KEY_GET_FAILED",
229
- planKind: "update",
230
- storeName: plan.storeName,
231
- cause: getReq.error
232
- }, `IDB update (get phase) failed on store "${plan.storeName}": ${String(getReq.error)}`));
204
+ getReq.onerror = () => onError(opError("KEY_GET_FAILED", plan, "update (get phase)", getReq.error));
233
205
  }
234
206
  function execDelete(store, plan, onComplete, onError) {
235
207
  const req = store.openCursor(plan.key);
@@ -243,19 +215,9 @@ function execDelete(store, plan, onComplete, onError) {
243
215
  collected.push(cursor.value);
244
216
  const delReq = cursor.delete();
245
217
  delReq.onsuccess = () => cursor.continue();
246
- delReq.onerror = () => onError(new IdbExecuteError({
247
- code: "DELETE_FAILED",
248
- planKind: "delete",
249
- storeName: plan.storeName,
250
- cause: delReq.error
251
- }, `IDB delete failed on store "${plan.storeName}": ${String(delReq.error)}`));
218
+ delReq.onerror = () => onError(opError("DELETE_FAILED", plan, "delete", delReq.error));
252
219
  };
253
- req.onerror = () => onError(new IdbExecuteError({
254
- code: "DELETE_FAILED",
255
- planKind: "delete",
256
- storeName: plan.storeName,
257
- cause: req.error
258
- }, `IDB delete failed on store "${plan.storeName}": ${String(req.error)}`));
220
+ req.onerror = () => onError(opError("DELETE_FAILED", plan, "delete", req.error));
259
221
  }
260
222
  function execScanWrite(store, plan, onComplete, onError) {
261
223
  const req = store.openCursor(null, "next");
@@ -281,12 +243,7 @@ function execScanWrite(store, plan, onComplete, onError) {
281
243
  }
282
244
  cursor.continue();
283
245
  };
284
- delReq.onerror = () => onError(new IdbExecuteError({
285
- code: "DELETE_FAILED",
286
- planKind: "scan-write",
287
- storeName: plan.storeName,
288
- cause: delReq.error
289
- }, `IDB scan-write (delete) failed on store "${plan.storeName}": ${String(delReq.error)}`));
246
+ delReq.onerror = () => onError(opError("DELETE_FAILED", plan, "scan-write (delete)", delReq.error));
290
247
  } else {
291
248
  const merged = {
292
249
  ...row,
@@ -301,20 +258,10 @@ function execScanWrite(store, plan, onComplete, onError) {
301
258
  }
302
259
  cursor.continue();
303
260
  };
304
- updReq.onerror = () => onError(new IdbExecuteError({
305
- code: "PUT_FAILED",
306
- planKind: "scan-write",
307
- storeName: plan.storeName,
308
- cause: updReq.error
309
- }, `IDB scan-write (put-merged) failed on store "${plan.storeName}": ${String(updReq.error)}`));
261
+ updReq.onerror = () => onError(opError("PUT_FAILED", plan, "scan-write (put-merged)", updReq.error));
310
262
  }
311
263
  };
312
- req.onerror = () => onError(new IdbExecuteError({
313
- code: "CURSOR_SCAN_FAILED",
314
- planKind: "scan-write",
315
- storeName: plan.storeName,
316
- cause: req.error
317
- }, `IDB scan-write cursor failed on store "${plan.storeName}": ${String(req.error)}`));
264
+ req.onerror = () => onError(opError("CURSOR_SCAN_FAILED", plan, "scan-write cursor", req.error));
318
265
  }
319
266
  //#endregion
320
267
  //#region src/core/execute/index.ts
@@ -363,7 +310,7 @@ function executeAtomicPlan(db, plan) {
363
310
  }
364
311
  function executeBatchPlan(db, plan) {
365
312
  return new Promise((resolve, reject) => {
366
- const mode = plan.ops.some((op) => op.kind === "add" || op.kind === "put" || op.kind === "delete" || op.kind === "update" || op.kind === "scan-write") ? "readwrite" : "readonly";
313
+ const mode = plan.ops.some((op) => planTxMode(op) === "readwrite") ? "readwrite" : "readonly";
367
314
  let tx;
368
315
  try {
369
316
  tx = db.transaction([...plan.storeNames], mode);
@@ -424,8 +371,8 @@ function runOpsSequentially(tx, ops, index, rows, onError) {
424
371
  /**
425
372
  * Name of the object store that holds the contract marker.
426
373
  *
427
- * Created during `upgradeneeded` by the migration runner and verified by the
428
- * runtime before every query execution to detect schema drift.
374
+ * Created during `upgradeneeded` by the migration runner. `IdbRuntime.verifyMarker()`
375
+ * compares its stored hash with the contract's to detect schema drift.
429
376
  *
430
377
  * Upstream equivalent: `_prisma_marker` table in SQLite/Postgres,
431
378
  * `_prisma_marker` collection in MongoDB.
@@ -504,7 +451,7 @@ var IdbRuntimeDriverInstance = class {
504
451
  * The live IDBDatabase connection. Resolves once `upgradeneeded` (if any)
505
452
  * completes and the database is ready for use.
506
453
  *
507
- * The adapter awaits this inside `runDriver()` before opening transactions.
454
+ * `execute()` and `transaction()` await this before opening a transaction.
508
455
  * The Promise is shared — all concurrent callers get the same database object.
509
456
  */
510
457
  db;
@@ -520,7 +467,7 @@ var IdbRuntimeDriverInstance = class {
520
467
  * Returns `null` when the marker store does not exist (fresh database
521
468
  * that hasn't been initialised yet) or when no marker record is present.
522
469
  *
523
- * Called by `IdbRuntimeImpl` at init time to verify that the live IDB
470
+ * Called by `IdbRuntimeImpl.verifyMarker()` to check that the live IDB
524
471
  * schema matches the contract.
525
472
  */
526
473
  async readMarker() {
@@ -534,7 +481,6 @@ var IdbRuntimeDriverInstance = class {
534
481
  resolve(result ?? null);
535
482
  };
536
483
  appReq.onerror = () => reject(appReq.error);
537
- tx.oncomplete = () => {};
538
484
  tx.onerror = () => reject(tx.error);
539
485
  });
540
486
  }
@@ -570,9 +516,9 @@ var IdbRuntimeDriverInstance = class {
570
516
  /**
571
517
  * Opens an IDB database and resolves once it is ready for use.
572
518
  *
573
- * Wraps the IDB event-based open API in a Promise. The `upgradeneeded`
574
- * handler is a no-op at this level — migrations are orchestrated by
575
- * `IdbMigrationRunner` (target-idb) which opens the database at a specific
519
+ * Wraps the IDB event-based open API in a Promise. There is no `upgradeneeded`
520
+ * handler: the driver does not own schema. Migrations are applied by
521
+ * `openAndUpgrade` (target-idb), which opens the database at a specific
576
522
  * version and runs DDL inside the version-change transaction.
577
523
  *
578
524
  * When version is omitted the IDB spec opens the database at its current version
@@ -589,7 +535,6 @@ function openIdbDatabase(dbName, version, factory = indexedDB) {
589
535
  req.onblocked = () => {
590
536
  reject(/* @__PURE__ */ new Error(`IDB open blocked for "${dbName}": another connection is open with an older version.`));
591
537
  };
592
- req.onupgradeneeded = handleUpgradeNeeded;
593
538
  req.onsuccess = () => {
594
539
  const db = req.result;
595
540
  db.onversionchange = () => {
@@ -599,26 +544,18 @@ function openIdbDatabase(dbName, version, factory = indexedDB) {
599
544
  };
600
545
  });
601
546
  }
602
- /**
603
- * No-op upgrade handler.
604
- *
605
- * The driver does not own schema. Object stores and indexes are created
606
- * by `IdbMigrationRunner` (target-idb) when it opens the database at a
607
- * bumped version number with a migration plan.
608
- */
609
- function handleUpgradeNeeded(_event) {}
610
547
  //#endregion
611
548
  //#region src/exports/runtime.ts
612
549
  /**
613
550
  * Creates a runtime driver descriptor for IndexedDB.
614
551
  *
615
- * The returned descriptor's `create()` opens the named IDB database
616
- * lazily — the connection resolves in the background and is awaited by
617
- * the adapter inside `runDriver()` for the first query.
552
+ * The returned descriptor's `create()` starts opening the named IDB database
553
+ * in the background; the driver awaits the connection before its first query.
618
554
  *
619
555
  * @param dbName - The IDB database name to open.
620
- * @param version - The IDB version number (default: 1). Bumping this
621
- * triggers `upgradeneeded`-based migrations via `IdbMigrationRunner`.
556
+ * @param version - The IDB version number. When omitted, IndexedDB opens the
557
+ * database at its current version (version 1 for a new database). The
558
+ * driver never runs migrations; `openAndUpgrade` (target-idb) bumps the version.
622
559
  *
623
560
  * @example
624
561
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.mjs","names":["unknown","#tx","#onComplete"],"sources":["../src/core/execute/error.ts","../src/core/execute/ops.ts","../src/core/execute/index.ts","../src/core/plan-body.ts","../src/core/transaction-scope.ts","../src/core/idb-driver.ts","../src/exports/runtime.ts"],"sourcesContent":["/**\n * Stable error codes for IDB execution failures.\n *\n * Each code corresponds to a specific IDB operation. Callers that need to\n * handle specific failure modes (e.g. retry on TRANSACTION_ABORTED) can\n * branch on `error.code` without string-matching the message.\n */\nexport type IdbExecuteErrorCode =\n | \"STORE_NOT_FOUND\" // IDB store requested by plan does not exist in the database\n | \"KEY_GET_FAILED\" // store.get(key) request failed\n | \"INDEX_GET_FAILED\" // store.index(name).getAll(range) request failed\n | \"CURSOR_SCAN_FAILED\" // openCursor iteration failed\n | \"KEYS_FAILED\" // getKey(range) / getAllKeys(range) request failed\n | \"COUNT_FAILED\" // store.count(range) / index.count(range) request failed\n | \"ADD_FAILED\" // store.add(record) request failed\n | \"PUT_FAILED\" // store.put(record) request failed\n | \"DELETE_FAILED\" // store.delete(key) request failed\n | \"BATCH_FAILED\" // one op inside a batch plan failed\n | \"TRANSACTION_ABORTED\" // IDB transaction aborted before oncomplete fired\n | \"TRANSACTION_INACTIVE\"; // request issued on a transaction that already auto-committed / finished\n\n/**\n * Structured error raised by the IDB driver's `execute()` path.\n *\n * Follows the pattern used in the upstream sql-runtime error normalizers so\n * that callers can rely on stable, machine-readable codes rather than\n * inspecting message strings.\n */\nexport class IdbExecuteError extends Error {\n readonly code: IdbExecuteErrorCode;\n readonly category = \"DRIVER\" as const;\n readonly severity = \"error\" as const;\n /** The `kind` field of the plan that was being executed. */\n readonly planKind: string;\n /** The object store name, when known at the point of failure. */\n readonly storeName: string | undefined;\n override readonly cause: unknown;\n\n constructor(\n details: {\n readonly code: IdbExecuteErrorCode;\n readonly planKind: string;\n readonly storeName?: string;\n readonly cause?: unknown;\n },\n message: string\n ) {\n super(message);\n this.name = \"IdbExecuteError\";\n this.code = details.code;\n this.planKind = details.planKind;\n this.storeName = details.storeName;\n this.cause = details.cause;\n }\n}\n\n/**\n * `true` for the synchronous DOMExceptions IDB throws when a request is issued\n * on a transaction that is no longer active (`TransactionInactiveError`) or has\n * already finished (`InvalidStateError`) — the signature of an `await` on\n * something that isn't an IDB request between two operations (see ADR 005/007).\n */\nexport function isTransactionInactiveError(err: unknown): boolean {\n const name = (err as { name?: unknown } | null)?.name;\n return name === \"TransactionInactiveError\" || name === \"InvalidStateError\";\n}\n\n/** Builds the actionable `TRANSACTION_INACTIVE` error for a failed request/objectStore call. */\nexport function transactionInactiveError(planKind: string, storeName: string, cause: unknown): IdbExecuteError {\n return new IdbExecuteError(\n { code: \"TRANSACTION_INACTIVE\", planKind, storeName, cause },\n `IDB transaction is no longer active when running \"${planKind}\" on store \"${storeName}\". ` +\n `IndexedDB transactions auto-commit as soon as the microtask queue drains with no pending request, ` +\n `so code between two operations must only \\`await\\` IDB-request-resolving promises ` +\n `(no fetch, timers or other async work inside a transaction). See ADR 005/007. Cause: ${String(cause)}`\n );\n}\n","/**\n * Callback-based IDB operation executors.\n *\n * All functions are purely event-driven — no Promises or async/await inside\n * IDB transaction event handlers. This is required because IDB transactions\n * auto-commit when no pending requests exist and the microtask queue drains;\n * interleaving a microtask boundary (await) between IDB requests would risk\n * premature auto-commit.\n *\n * Pattern: each `exec*` function\n * 1. Issues one or more IDB requests **synchronously**.\n * 2. Calls `onComplete(rows)` when the op's work is done (inside onsuccess).\n * 3. Calls `onError(err)` if a request or the transaction fails.\n *\n * The caller (execute/index.ts) resolves the outer Promise from `tx.oncomplete`\n * so write durability is guaranteed before rows are delivered.\n */\nimport type {\n IdbAddPlan,\n IdbAtomicPlan,\n IdbCountPlan,\n IdbCursorScanPlan,\n IdbDeletePlan,\n IdbIndexGetPlan,\n IdbKeyGetPlan,\n IdbKeysPlan,\n IdbPutPlan,\n IdbScanWritePlan,\n IdbUpdatePlan,\n} from \"../plan-body\";\nimport { IdbExecuteError, isTransactionInactiveError, transactionInactiveError } from \"./error\";\n\ntype Row = Record<string, unknown>;\ntype OnComplete = (rows: Row[]) => void;\ntype OnError = (err: unknown) => void;\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Dispatch an atomic plan to its IDB operation within a live transaction.\n *\n * All IDB requests are issued synchronously. `onComplete` is called once all\n * requests for this op have resolved (still inside IDB event handlers — no\n * async gap). `onError` is called on the first request failure.\n */\nexport function executeOpInTx(\n store: IDBObjectStore,\n plan: IdbAtomicPlan,\n onComplete: OnComplete,\n onError: OnError\n): void {\n try {\n dispatchOp(store, plan, onComplete, onError);\n } catch (err) {\n // Requests are issued synchronously, so a dead transaction surfaces here as a\n // thrown DOMException. Anything else (DataError, unknown index, ...) is unchanged.\n if (isTransactionInactiveError(err)) return onError(transactionInactiveError(plan.kind, plan.storeName, err));\n throw err;\n }\n}\n\nfunction dispatchOp(store: IDBObjectStore, plan: IdbAtomicPlan, onComplete: OnComplete, onError: OnError): void {\n switch (plan.kind) {\n case \"key-get\":\n return execKeyGet(store, plan, onComplete, onError);\n case \"index-get\":\n return execIndexGet(store, plan, onComplete, onError);\n case \"cursor-scan\":\n return execCursorScan(store, plan, onComplete, onError);\n case \"count\":\n return execCount(store, plan, onComplete, onError);\n case \"keys\":\n return execKeys(store, plan, onComplete, onError);\n case \"add\":\n return execAdd(store, plan, onComplete, onError);\n case \"put\":\n return execPut(store, plan, onComplete, onError);\n case \"update\":\n return execUpdate(store, plan, onComplete, onError);\n case \"delete\":\n return execDelete(store, plan, onComplete, onError);\n case \"scan-write\":\n return execScanWrite(store, plan, onComplete, onError);\n default: {\n // Unreachable for a well-typed plan; a stale driver build receiving a newer plan kind\n // would otherwise never call onComplete/onError and hang the caller.\n const unknown: never = plan;\n return onError(new Error(`Unknown IDB plan kind: ${String((unknown as { kind?: unknown }).kind)}`));\n }\n }\n}\n\n/** Returns the IDB transaction mode appropriate for a given atomic plan. */\nexport function planTxMode(plan: IdbAtomicPlan): IDBTransactionMode {\n return plan.kind === \"add\" ||\n plan.kind === \"put\" ||\n plan.kind === \"update\" ||\n plan.kind === \"delete\" ||\n plan.kind === \"scan-write\"\n ? \"readwrite\"\n : \"readonly\";\n}\n\n// ── Per-operation executors ──────────────────────────────────────────────────\n\nfunction execKeyGet(store: IDBObjectStore, plan: IdbKeyGetPlan, onComplete: OnComplete, onError: OnError): void {\n const req = store.get(plan.key);\n req.onsuccess = () => {\n const value = req.result as Row | undefined;\n onComplete(value !== undefined ? [value] : []);\n };\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"KEY_GET_FAILED\", planKind: \"key-get\", storeName: plan.storeName, cause: req.error },\n `IDB key-get failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n\nfunction execIndexGet(store: IDBObjectStore, plan: IdbIndexGetPlan, onComplete: OnComplete, onError: OnError): void {\n const req = store.index(plan.indexName).getAll(plan.range);\n req.onsuccess = () => onComplete(req.result as Row[]);\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n {\n code: \"INDEX_GET_FAILED\",\n planKind: \"index-get\",\n storeName: plan.storeName,\n cause: req.error,\n },\n `IDB index-get failed on \"${plan.storeName}\"/\"${plan.indexName}\": ${String(req.error)}`\n )\n );\n}\n\nfunction execCount(store: IDBObjectStore, plan: IdbCountPlan, onComplete: OnComplete, onError: OnError): void {\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n const req = plan.range !== undefined ? source.count(plan.range) : source.count();\n req.onsuccess = () => onComplete([{ count: req.result }]);\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"COUNT_FAILED\", planKind: \"count\", storeName: plan.storeName, cause: req.error },\n `IDB count failed on store \"${plan.storeName}\"${plan.indexName !== undefined ? ` (index \"${plan.indexName}\")` : \"\"}: ${String(req.error)}`\n )\n );\n}\n\nfunction execKeys(store: IDBObjectStore, plan: IdbKeysPlan, onComplete: OnComplete, onError: OnError): void {\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n const fail = (cause: unknown) =>\n onError(\n new IdbExecuteError(\n { code: \"KEYS_FAILED\", planKind: \"keys\", storeName: plan.storeName, cause },\n `IDB keys read failed on store \"${plan.storeName}\"${plan.indexName !== undefined ? ` (index \"${plan.indexName}\")` : \"\"}: ${String(cause)}`\n )\n );\n\n // getKey() requires a query (null/absent throws), so a range-less single-key\n // read is expressed as getAllKeys(undefined, 1) instead.\n if (plan.take === 1 && plan.range !== undefined) {\n const req = source.getKey(plan.range);\n req.onsuccess = () => onComplete(req.result === undefined ? [] : [{ key: req.result }]);\n req.onerror = () => fail(req.error);\n return;\n }\n\n if (plan.take === 0) {\n onComplete([]);\n return;\n }\n\n const req = source.getAllKeys(plan.range, plan.take);\n req.onsuccess = () => onComplete((req.result as IDBValidKey[]).map((key) => ({ key })));\n req.onerror = () => fail(req.error);\n}\n\nfunction execCursorScan(\n store: IDBObjectStore,\n plan: IdbCursorScanPlan,\n onComplete: OnComplete,\n onError: OnError\n): void {\n // Capture plan fields to avoid repeated property access inside the hot loop.\n const comparator = plan.comparator;\n const filter = plan.filter;\n const skip = plan.skip;\n const take = plan.take;\n\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n\n // openCursor accepts null to mean \"no range restriction\".\n const req = source.openCursor(plan.range ?? null, plan.direction ?? \"next\");\n const collected: Row[] = [];\n let skippedCount = 0;\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n\n if (cursor === null) {\n // Cursor exhausted. If a comparator is set we collected all matching rows\n // before sorting; apply sort + skip/take now.\n if (comparator !== undefined) {\n collected.sort(comparator);\n const offset = skip ?? 0;\n onComplete(take !== undefined ? collected.slice(offset, offset + take) : collected.slice(offset));\n } else {\n onComplete(collected);\n }\n return;\n }\n\n const row = cursor.value as Row;\n\n if (filter === undefined || filter(row)) {\n if (comparator !== undefined) {\n // Must collect ALL matching rows before sorting — can't apply skip/take\n // inline because earlier rows might be sorted out.\n collected.push(row);\n } else {\n // No comparator: apply skip/take inline so we avoid collecting rows\n // that will be discarded.\n if (skip !== undefined && skippedCount < skip) {\n skippedCount++;\n } else if (take === undefined || collected.length < take) {\n collected.push(row);\n if (take !== undefined && collected.length === take) {\n // Take limit reached — stop the cursor early. The transaction has\n // no more pending requests and will auto-commit, triggering\n // tx.oncomplete → resolve(rows) in the outer wrapper.\n onComplete(collected);\n return; // intentionally no cursor.continue()\n }\n }\n }\n }\n\n cursor.continue();\n };\n\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n {\n code: \"CURSOR_SCAN_FAILED\",\n planKind: \"cursor-scan\",\n storeName: plan.storeName,\n cause: req.error,\n },\n `IDB cursor-scan failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n\n/**\n * The record as stored: `record` itself, plus the generated key when an\n * `autoIncrement` store with an inline `keyPath` filled it in. IDB writes the\n * generated key into its own stored copy, never into the caller's object, so\n * the echo needs it set from `req.result`.\n */\nfunction withGeneratedKey(\n store: IDBObjectStore,\n record: Record<string, unknown>,\n key: IDBValidKey\n): Record<string, unknown> {\n if (!store.autoIncrement || typeof store.keyPath !== \"string\" || record[store.keyPath] !== undefined) return record;\n return { ...record, [store.keyPath]: key };\n}\n\nfunction execAdd(store: IDBObjectStore, plan: IdbAddPlan, onComplete: OnComplete, onError: OnError): void {\n // Use the optional out-of-line key when provided; otherwise IDB derives the\n // key from the record via the store's keyPath.\n const req = plan.key !== undefined ? store.add(plan.record, plan.key) : store.add(plan.record);\n // Echo the record back — IDB has no RETURNING clause.\n req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"ADD_FAILED\", planKind: \"add\", storeName: plan.storeName, cause: req.error },\n `IDB add failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n\nfunction execPut(store: IDBObjectStore, plan: IdbPutPlan, onComplete: OnComplete, onError: OnError): void {\n // Use the optional out-of-line key when provided; otherwise IDB derives the\n // key from the record via the store's keyPath.\n const req = plan.key !== undefined ? store.put(plan.record, plan.key) : store.put(plan.record);\n // Echo the record back — IDB has no RETURNING clause.\n req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"PUT_FAILED\", planKind: \"put\", storeName: plan.storeName, cause: req.error },\n `IDB put failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n\nfunction execUpdate(store: IDBObjectStore, plan: IdbUpdatePlan, onComplete: OnComplete, onError: OnError): void {\n // Step 1: read the current record.\n const getReq = store.get(plan.key);\n getReq.onsuccess = () => {\n const existing = (getReq.result as Row | undefined) ?? {};\n // Step 2: shallow-merge patch onto existing record.\n const merged: Row = { ...existing, ...plan.patch };\n // Step 3: write the merged record back.\n const putReq = store.put(merged);\n putReq.onsuccess = () => onComplete([merged]);\n putReq.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"PUT_FAILED\", planKind: \"update\", storeName: plan.storeName, cause: putReq.error },\n `IDB update (put phase) failed on store \"${plan.storeName}\": ${String(putReq.error)}`\n )\n );\n };\n getReq.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"KEY_GET_FAILED\", planKind: \"update\", storeName: plan.storeName, cause: getReq.error },\n `IDB update (get phase) failed on store \"${plan.storeName}\": ${String(getReq.error)}`\n )\n );\n}\n\nfunction execDelete(store: IDBObjectStore, plan: IdbDeletePlan, onComplete: OnComplete, onError: OnError): void {\n // `plan.key` may be a single key or an `IDBKeyRange` spanning several\n // records (deleteMany). Walk a cursor over it so every matched record is\n // captured (and its key individually deleted) before moving on — this\n // both echoes the deleted row(s), mirroring add/put/update, and gives an\n // accurate affected-row count for both the single-key and range cases via\n // the same drain-and-count `runExecute()` already uses for every other op.\n const req = store.openCursor(plan.key);\n const collected: Row[] = [];\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n if (cursor === null) {\n onComplete(collected);\n return;\n }\n collected.push(cursor.value as Row);\n const delReq = cursor.delete();\n delReq.onsuccess = () => cursor.continue();\n delReq.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"DELETE_FAILED\", planKind: \"delete\", storeName: plan.storeName, cause: delReq.error },\n `IDB delete failed on store \"${plan.storeName}\": ${String(delReq.error)}`\n )\n );\n };\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"DELETE_FAILED\", planKind: \"delete\", storeName: plan.storeName, cause: req.error },\n `IDB delete failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n\nfunction execScanWrite(store: IDBObjectStore, plan: IdbScanWritePlan, onComplete: OnComplete, onError: OnError): void {\n // Cursor must be opened on a readwrite transaction (enforced by planTxMode).\n const req = store.openCursor(null, \"next\");\n const collected: Row[] = [];\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n\n if (cursor === null) {\n // Cursor exhausted — deliver all collected rows.\n onComplete(collected);\n return;\n }\n\n const row = cursor.value as Row;\n\n // Skip rows that don't match the filter.\n if (plan.filter !== undefined && !plan.filter(row)) {\n cursor.continue();\n return;\n }\n\n if (plan.write === \"delete\") {\n // Capture the row value before deleting (so deleteAll can return it).\n collected.push(row);\n const delReq = cursor.delete();\n delReq.onsuccess = () => {\n if (plan.take !== undefined && collected.length >= plan.take) {\n onComplete(collected);\n return; // intentionally no cursor.continue() — transaction auto-commits\n }\n cursor.continue();\n };\n delReq.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"DELETE_FAILED\", planKind: \"scan-write\", storeName: plan.storeName, cause: delReq.error },\n `IDB scan-write (delete) failed on store \"${plan.storeName}\": ${String(delReq.error)}`\n )\n );\n } else {\n // put-merged: shallow-merge patch onto existing row, write back in-place.\n const merged: Row = { ...row, ...plan.patch };\n const updReq = cursor.update(merged);\n updReq.onsuccess = () => {\n collected.push(merged);\n if (plan.take !== undefined && collected.length >= plan.take) {\n onComplete(collected);\n return; // intentionally no cursor.continue()\n }\n cursor.continue();\n };\n updReq.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"PUT_FAILED\", planKind: \"scan-write\", storeName: plan.storeName, cause: updReq.error },\n `IDB scan-write (put-merged) failed on store \"${plan.storeName}\": ${String(updReq.error)}`\n )\n );\n }\n };\n\n req.onerror = () =>\n onError(\n new IdbExecuteError(\n { code: \"CURSOR_SCAN_FAILED\", planKind: \"scan-write\", storeName: plan.storeName, cause: req.error },\n `IDB scan-write cursor failed on store \"${plan.storeName}\": ${String(req.error)}`\n )\n );\n}\n","/**\n * Top-level IDB plan dispatcher.\n *\n * `executeIdbPlan(db, plan)` is the single entry point for the IDB driver's\n * `execute()` path. It:\n * 1. Opens an IDB transaction scoped to the store(s) the plan touches.\n * 2. Dispatches to the appropriate op executor (ops.ts).\n * 3. Resolves the returned Promise only when `tx.oncomplete` fires —\n * guaranteeing write durability before rows are delivered.\n *\n * Transaction modes:\n * - Atomic read plans (key-get, index-get, cursor-scan): `readonly`\n * - Atomic write plans (add, put, delete): `readwrite`\n * - Batch plans: `readwrite` if any op is a write, `readonly` otherwise\n */\nimport type { IdbAtomicPlan, IdbBatchPlan, IdbPlanBody } from \"../plan-body\";\nimport { IdbExecuteError, isTransactionInactiveError, transactionInactiveError } from \"./error\";\nimport { executeOpInTx, planTxMode } from \"./ops\";\n\ntype Row = Record<string, unknown>;\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Execute an IDB plan against a live database and collect all result rows.\n *\n * Batch plans run all ops inside a single transaction; atomic plans open a\n * single-store transaction. Resolves after `tx.oncomplete` so writes are\n * durable before the caller receives rows.\n */\nexport function executeIdbPlan(db: IDBDatabase, plan: IdbPlanBody): Promise<Row[]> {\n if (plan.kind === \"batch\") return executeBatchPlan(db, plan);\n return executeAtomicPlan(db, plan);\n}\n\n// ── Atomic plans ─────────────────────────────────────────────────────────────\n\nfunction executeAtomicPlan(db: IDBDatabase, plan: IdbAtomicPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n let tx: IDBTransaction;\n try {\n tx = db.transaction([plan.storeName], planTxMode(plan));\n } catch (err) {\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: plan.kind, storeName: plan.storeName, cause: err },\n `IDB store \"${plan.storeName}\" not found in database`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n\n tx.oncomplete = () => resolve(rows);\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n {\n code: \"TRANSACTION_ABORTED\",\n planKind: plan.kind,\n storeName: plan.storeName,\n cause: tx.error,\n },\n `IDB transaction failed on store \"${plan.storeName}\": ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: plan.kind, storeName: plan.storeName },\n `IDB transaction aborted on store \"${plan.storeName}\"`\n )\n );\n\n const store = tx.objectStore(plan.storeName);\n executeOpInTx(\n store,\n plan,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n },\n (err) => reject(err)\n );\n });\n}\n\n// ── Batch plans ──────────────────────────────────────────────────────────────\n\nfunction executeBatchPlan(db: IDBDatabase, plan: IdbBatchPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n const mode: IDBTransactionMode = plan.ops.some(\n (op) =>\n op.kind === \"add\" ||\n op.kind === \"put\" ||\n op.kind === \"delete\" ||\n op.kind === \"update\" ||\n op.kind === \"scan-write\"\n )\n ? \"readwrite\"\n : \"readonly\";\n\n let tx: IDBTransaction;\n try {\n tx = db.transaction([...plan.storeNames], mode);\n } catch (err) {\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: \"batch\", cause: err },\n `IDB batch transaction failed to open: ${String(err)}`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n\n tx.oncomplete = () => resolve(rows);\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: \"batch\", cause: tx.error },\n `IDB batch transaction failed: ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError({ code: \"TRANSACTION_ABORTED\", planKind: \"batch\" }, \"IDB batch transaction was aborted\")\n );\n\n // Run ops sequentially using recursive callbacks. Each op's `onComplete`\n // triggers the next op synchronously (still inside IDB event handlers),\n // which ensures we never cross a microtask boundary between IDB requests.\n runOpsSequentially(tx, plan.ops, 0, rows, reject);\n });\n}\n\n/**\n * Recursively runs batch ops one at a time — op N+1 is initiated from inside\n * op N's `onComplete` callback, which fires synchronously within the IDB\n * event handler. This keeps the transaction alive and prevents auto-commit\n * gaps between ops.\n */\nfunction runOpsSequentially(\n tx: IDBTransaction,\n ops: ReadonlyArray<IdbAtomicPlan>,\n index: number,\n rows: Row[],\n onError: (err: unknown) => void\n): void {\n const op = ops[index];\n if (op === undefined) return; // all ops initiated; tx.oncomplete will resolve\n\n let store: IDBObjectStore;\n try {\n store = tx.objectStore(op.storeName);\n } catch (err) {\n if (isTransactionInactiveError(err)) {\n onError(transactionInactiveError(\"batch\", op.storeName, err));\n return;\n }\n onError(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: \"batch\", storeName: op.storeName, cause: err },\n `IDB batch: store \"${op.storeName}\" not found`\n )\n );\n return;\n }\n\n executeOpInTx(\n store,\n op,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n runOpsSequentially(tx, ops, index + 1, rows, onError);\n },\n onError\n );\n}\n","import type { ExecutionPlan } from \"@prisma/orm-framework/components/runtime\";\n\n// ── Marker store ─────────────────────────────────────────────────────────────\n\n/**\n * Name of the object store that holds the contract marker.\n *\n * Created during `upgradeneeded` by the migration runner and verified by the\n * runtime before every query execution to detect schema drift.\n *\n * Upstream equivalent: `_prisma_marker` table in SQLite/Postgres,\n * `_prisma_marker` collection in MongoDB.\n */\nexport const MARKER_STORE_NAME = \"_prisma_next_marker\";\n\n/**\n * Shape of a marker record stored in {@link MARKER_STORE_NAME}.\n *\n * Mirrors the framework's `ContractMarkerRecord` plus the `space` keying\n * field. Records are keyed by `space` (`\"app\"` for the single app space) so\n * the storage layout can accommodate future IDB extension spaces.\n *\n * `updatedAt` is a JavaScript `Date` (not an ISO string) because IndexedDB\n * serialises Dates natively via structured-clone.\n *\n * Non-key fields are optional for forward compatibility with future contract\n * versions that may add or remove marker metadata.\n */\nexport interface IdbMarkerRecord {\n /** Contract-space identifier (`\"app\"` for the single app space). */\n readonly space?: string;\n /** The `storageHash` from the contract that was last signed. */\n readonly storageHash: string;\n /** The `profileHash` from the contract that was last signed. */\n readonly profileHash?: string;\n /** Date the marker was last written. */\n readonly updatedAt?: Date | string;\n readonly invariants?: readonly string[];\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n}\n\n/**\n * In-memory predicate applied to each row while iterating a cursor.\n * Returns true if the row should be included in the result set.\n */\nexport type IdbRowFilter = (row: Record<string, unknown>) => boolean;\n\n/**\n * In-memory comparator for ORDER BY.\n * Follows the same contract as Array.prototype.sort: negative / zero / positive.\n */\nexport type IdbRowComparator = (a: Record<string, unknown>, b: Record<string, unknown>) => number;\n\n/**\n * Full cursor scan over an object store or index.\n *\n * Used for `findMany`, `findFirst`, and any query where an exact key lookup\n * isn't possible. Supports key-range restriction, in-memory filtering,\n * in-memory ordering, and OFFSET + LIMIT.\n *\n * When `indexName` is set, the cursor iterates the named secondary index\n * instead of the primary key order.\n */\nexport interface IdbCursorScanPlan extends ExecutionPlan {\n readonly kind: \"cursor-scan\";\n readonly storeName: string;\n readonly indexName?: string; // if set, iterate via this index\n readonly range?: IDBKeyRange; // restrict the cursor's key range\n readonly direction?: IDBCursorDirection; // \"next\" | \"nextunique\" | \"prev\" | \"prevunique\"\n readonly filter?: IdbRowFilter; // in-memory WHERE\n readonly comparator?: IdbRowComparator; // in-memory ORDER BY (when index order isn't enough)\n readonly skip?: number; // OFFSET\n readonly take?: number; // LIMIT (undefined = no limit)\n}\n\n/**\n * Primary-key O(1) lookup via `store.get(key)`.\n *\n * Used for `findUnique` on `@id` fields.\n */\nexport interface IdbKeyGetPlan extends ExecutionPlan {\n readonly kind: \"key-get\";\n readonly storeName: string;\n readonly key: IDBValidKey;\n}\n\n/**\n * Index-based range lookup.\n *\n * Used for `findUnique` on `@@unique` fields and index-accelerated\n * `findMany` on `@@index` fields.\n */\nexport interface IdbIndexGetPlan extends ExecutionPlan {\n readonly kind: \"index-get\";\n readonly storeName: string;\n readonly indexName: string;\n readonly range: IDBKeyRange;\n}\n\n/**\n * Insert a single record via `store.add(record[, key])`.\n *\n * Used for Prisma `create` semantics. IndexedDB rejects `add()` when the\n * primary key already exists, which preserves the \"create never overwrites\"\n * contract expected by callers.\n *\n * `key` is only needed for out-of-line key stores (`keyPath: null`).\n */\nexport interface IdbAddPlan extends ExecutionPlan {\n readonly kind: \"add\";\n readonly storeName: string;\n readonly record: Record<string, unknown>;\n readonly key?: IDBValidKey;\n}\n\n/**\n * Upsert or replace a single record via `store.put(record[, key])`.\n *\n * Used for upsert paths where the full replacement record is already known.\n * The driver echoes `record` back as the result row — IDB does not have\n * RETURNING.\n *\n * `key` is only needed for out-of-line key stores (`keyPath: null`).\n */\nexport interface IdbPutPlan extends ExecutionPlan {\n readonly kind: \"put\";\n readonly storeName: string;\n readonly record: Record<string, unknown>;\n readonly key?: IDBValidKey;\n}\n\n/**\n * Atomic partial update: get → merge → put in a single readwrite transaction.\n *\n * Used for Prisma `update` where only a subset of fields change. The driver:\n * 1. Reads the current record via `store.get(key)`.\n * 2. Deep-merges `patch` onto the existing record with `{ ...existing, ...patch }`.\n * 3. Writes the merged record back via `store.put(merged)`.\n * 4. Echoes the merged record as the result row.\n *\n * This preserves fields not mentioned in `patch` — unlike `IdbPutPlan` which\n * does a full replacement. The get and put are issued inside the same\n * readwrite transaction so no other writer can interleave.\n *\n * If no record exists for `key`, the driver echoes `patch` as-is (insert semantics).\n */\nexport interface IdbUpdatePlan extends ExecutionPlan {\n readonly kind: \"update\";\n readonly storeName: string;\n readonly key: IDBValidKey;\n readonly patch: Record<string, unknown>;\n}\n\n/**\n * Delete one or more records by key or key range.\n *\n * Used for `delete` and `deleteMany`. The driver walks a cursor over\n * `key` (a single key or a range) and yields each deleted row, so callers\n * can echo them back and `execute()` can report an accurate affected-row\n * count — same convention as `add`/`put`/`update`.\n */\nexport interface IdbDeletePlan extends ExecutionPlan {\n readonly kind: \"delete\";\n readonly storeName: string;\n readonly key: IDBValidKey | IDBKeyRange;\n}\n\n/**\n * Cursor scan with in-place write for each matching row.\n *\n * Used for `update` (take:1), `updateAll`, `updateCount`, `deleteAll`,\n * `deleteCount`. Opens a readwrite cursor and applies `write` to each\n * row that passes `filter`:\n *\n * - `\"put-merged\"`: shallow-merges `patch` onto the row and calls\n * `cursor.update(merged)`. Yields merged rows.\n * - `\"delete\"`: captures `cursor.value` then calls `cursor.delete()`.\n * Yields the deleted rows (so callers can echo them back).\n *\n * With `take`, the cursor stops after the first `take` matches (no\n * `cursor.continue()` call after the limit is reached).\n */\nexport interface IdbScanWritePlan extends ExecutionPlan {\n readonly kind: \"scan-write\";\n readonly storeName: string;\n readonly filter?: IdbRowFilter;\n readonly take?: number;\n readonly write: \"put-merged\" | \"delete\";\n readonly patch?: Record<string, unknown>;\n}\n\n/**\n * Native count via `store.count(range)` / `index.count(range)`.\n *\n * Used by the ORM `.count()` terminal when the whole `where` is captured by a\n * key range (or there is no `where`), so no row needs to be deserialized or\n * filtered in memory. Counts index *entries*, so it must not be used with a\n * `multiEntry` index (one record can occupy several entries).\n *\n * Result: exactly one row, `{ count: number }` — the driver's `Row[]` result\n * contract is unchanged; the caller unwraps `rows[0].count`.\n */\nexport interface IdbCountPlan extends ExecutionPlan {\n readonly kind: \"count\";\n readonly storeName: string;\n readonly indexName?: string; // count via this index instead of the store's primary keys\n readonly range?: IDBKeyRange; // omit to count every entry\n}\n\n/**\n * Key-only read via `getKey(range)` / `getAllKeys(range, count)` — returns\n * PRIMARY keys without deserializing any row value.\n *\n * Meant for existence checks (\"does any row match this key range?\") where the\n * caller never reads a field. Because no value is fetched, it can only\n * express criteria that are a key range on the store's primary key or on an\n * index — never an in-memory row filter.\n *\n * - `take: 1` with a `range` → `getKey(range)` (one request, no cursor).\n * - otherwise → `getAllKeys(range, take)`; `take` omitted returns every key.\n *\n * Result rows: one `{ key }` per match (the primary key — an array for a\n * compound key) or `[]` when nothing matches. Same synthetic-row convention as\n * {@link IdbCountPlan}.\n */\nexport interface IdbKeysPlan extends ExecutionPlan {\n readonly kind: \"keys\";\n readonly storeName: string;\n readonly indexName?: string; // resolve through this index; returned keys are still primary keys\n readonly range?: IDBKeyRange; // omit to cover the whole store/index\n readonly take?: number; // max keys to return (undefined = all)\n}\n\n/**\n * All single-store atomic op types — valid both standalone and inside a batch.\n */\nexport type IdbAtomicPlan =\n | IdbCursorScanPlan\n | IdbKeyGetPlan\n | IdbIndexGetPlan\n | IdbCountPlan\n | IdbKeysPlan\n | IdbAddPlan\n | IdbPutPlan\n | IdbUpdatePlan\n | IdbDeletePlan\n | IdbScanWritePlan;\n\n/**\n * Multi-op atomic batch executed inside a single IDB transaction.\n *\n * Used whenever a Prisma operation must touch multiple stores atomically\n * (e.g. `create` with nested relations, `update` + cascade `delete`).\n *\n * The driver opens ONE readwrite transaction scoped to `storeNames`, runs\n * every op in `ops` sequentially, and collects rows from read ops. Write ops\n * (`put`/`delete`) echo their result inline (see `IdbPutPlan`).\n *\n * `storeNames` MUST include every store referenced by any op in `ops`.\n * IDB requires the full store scope to be declared when opening a transaction.\n */\nexport interface IdbBatchPlan extends ExecutionPlan {\n readonly kind: \"batch\";\n readonly storeNames: ReadonlyArray<string>;\n readonly ops: ReadonlyArray<IdbAtomicPlan>;\n}\n\n/**\n * The full union of plan shapes the adapter produces and the driver executes.\n */\nexport type IdbPlanBody = IdbAtomicPlan | IdbBatchPlan;\n","/**\n * Multi-store IDB transaction scope.\n *\n * A single `IDBTransaction` spanning one or more object stores, exposed as a\n * typed execution surface for the ORM's `withMutationScope()` helper.\n *\n * Design notes:\n * - `execute(plan)` runs `executeOpInTx` directly in the pre-opened transaction —\n * NO new transaction is opened per call, and the middleware chain is bypassed\n * entirely. This keeps the shared IDB transaction alive across multiple awaited\n * execute() calls (IDB auto-commits in a macro-task, not a microtask; each\n * execute() issues new requests before the auto-commit check fires).\n * - `commit()` resolves when `tx.oncomplete` fires — the caller awaits this after\n * all scope operations are complete to get write-durability confirmation.\n * - `rollback()` is idempotent: calling it on an already-aborted transaction is\n * a no-op (the DOMException from IDB is swallowed).\n *\n * Issue #6 (scope hard-coding): since this scope's execute() bypasses both of\n * `RuntimeCore`'s middleware chains (query()'s and execute()'s alike), the\n * cache middleware never fires inside a scope → reads are always fresh.\n */\nimport type { IdbAtomicPlan } from \"./plan-body\";\nimport { IdbExecuteError, isTransactionInactiveError, transactionInactiveError } from \"./execute/error\";\nimport { executeOpInTx } from \"./execute/ops\";\n\ntype Row = Record<string, unknown>;\n\n// ── Public interface ──────────────────────────────────────────────────────────\n\nexport interface IdbTransactionScope {\n /**\n * Execute a single atomic plan inside the shared IDB transaction.\n *\n * The plan runs synchronously inside IDB event callbacks; the returned\n * Promise resolves once the operation's `onsuccess` fires. The transaction\n * stays open — the next `execute()` call can be issued right after `await`.\n */\n execute(plan: IdbAtomicPlan): Promise<Row[]>;\n\n /**\n * Wait for the IDB transaction to fully commit.\n *\n * Resolves when `tx.oncomplete` fires (all writes are durable). Call this\n * after all `execute()` calls; `withMutationScope` does this automatically.\n */\n commit(): Promise<void>;\n\n /**\n * Abort the transaction, rolling back all uncommitted writes.\n *\n * Idempotent — safe to call when the transaction has already been aborted\n * (e.g. due to a request error). `withMutationScope` calls this on catch.\n */\n rollback(): void;\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\nclass IdbTransactionScopeImpl implements IdbTransactionScope {\n readonly #tx: IDBTransaction;\n readonly #onComplete: Promise<void>;\n\n constructor(tx: IDBTransaction) {\n this.#tx = tx;\n this.#onComplete = new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: \"batch\", cause: tx.error },\n `IDB transaction scope failed: ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError({ code: \"TRANSACTION_ABORTED\", planKind: \"batch\" }, \"IDB transaction scope aborted\")\n );\n });\n // Suppress \"unhandled rejection\" when the scope is rolled back without\n // commit() being awaited (e.g. withMutationScope catches an error and\n // calls rollback() without ever calling commit()).\n this.#onComplete.catch(() => {});\n }\n\n execute(plan: IdbAtomicPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n let store: IDBObjectStore;\n try {\n store = this.#tx.objectStore(plan.storeName);\n } catch (err) {\n if (isTransactionInactiveError(err)) {\n reject(transactionInactiveError(plan.kind, plan.storeName, err));\n return;\n }\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: plan.kind, storeName: plan.storeName, cause: err },\n `IDB transaction scope: store \"${plan.storeName}\" not found`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n executeOpInTx(\n store,\n plan,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n resolve(rows);\n },\n reject\n );\n });\n }\n\n commit(): Promise<void> {\n return this.#onComplete;\n }\n\n rollback(): void {\n try {\n this.#tx.abort();\n } catch {\n // Transaction already committed or aborted — no-op.\n }\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Open an IDB transaction spanning `storeNames` and wrap it in a scope.\n *\n * Throws synchronously if any store in `storeNames` is not found in the\n * database (IDB throws when opening a transaction with unknown store names).\n */\nexport function createTransactionScope(\n db: IDBDatabase,\n storeNames: string[],\n mode: IDBTransactionMode = \"readwrite\"\n): IdbTransactionScope {\n const tx = db.transaction(storeNames, mode);\n return new IdbTransactionScopeImpl(tx);\n}\n","import type { RuntimeDriverInstance } from \"@prisma/orm-framework/components/execution\";\nimport { executeIdbPlan } from \"./execute\";\nimport { MARKER_STORE_NAME, type IdbMarkerRecord, type IdbPlanBody } from \"./plan-body\";\nimport { createTransactionScope, type IdbTransactionScope } from \"./transaction-scope\";\n\nexport class IdbRuntimeDriverInstance implements RuntimeDriverInstance<\"idb\", \"idb\"> {\n readonly familyId = \"idb\" as const;\n readonly targetId = \"idb\" as const;\n /**\n * The live IDBDatabase connection. Resolves once `upgradeneeded` (if any)\n * completes and the database is ready for use.\n *\n * The adapter awaits this inside `runDriver()` before opening transactions.\n * The Promise is shared — all concurrent callers get the same database object.\n */\n readonly db: Promise<IDBDatabase>;\n\n constructor(dbName: string, version?: number, factory?: IDBFactory) {\n this.db = openIdbDatabase(dbName, version, factory);\n }\n\n async close(): Promise<void> {\n (await this.db).close();\n }\n\n /**\n * Read the contract marker from the `_prisma_next_marker` store.\n *\n * Returns `null` when the marker store does not exist (fresh database\n * that hasn't been initialised yet) or when no marker record is present.\n *\n * Called by `IdbRuntimeImpl` at init time to verify that the live IDB\n * schema matches the contract.\n */\n async readMarker(): Promise<IdbMarkerRecord | null> {\n const db = await this.db;\n if (!db.objectStoreNames.contains(MARKER_STORE_NAME)) {\n return null;\n }\n return new Promise((resolve, reject) => {\n const tx = db.transaction(MARKER_STORE_NAME, \"readonly\");\n const store = tx.objectStore(MARKER_STORE_NAME);\n\n const appReq = store.get(\"app\");\n appReq.onsuccess = () => {\n const result = appReq.result as IdbMarkerRecord | undefined;\n resolve(result ?? null);\n };\n appReq.onerror = () => reject(appReq.error);\n\n tx.oncomplete = () => {\n /* resolve already called */\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Open a multi-store readwrite transaction and wrap it in an\n * {@link IdbTransactionScope}.\n *\n * Awaits the live `IDBDatabase` connection before opening the transaction.\n * Used by `withMutationScope()` in `client-idb` to run cross-store writes\n * atomically.\n */\n async transaction(storeNames: string[], mode: IDBTransactionMode = \"readwrite\"): Promise<IdbTransactionScope> {\n const db = await this.db;\n return createTransactionScope(db, storeNames, mode);\n }\n\n /**\n * Execute an IDB plan body and yield rows as an async iterable.\n *\n * Opens a new IDB transaction for each call, collects all rows inside the\n * transaction (collect-then-yield; see execute/index.ts for rationale),\n * and yields them after the transaction commits.\n *\n * Called by `IdbRuntimeImpl.runDriver()` in `@prisma-idb/runtime-idb`.\n */\n execute(plan: IdbPlanBody): AsyncIterable<Record<string, unknown>> {\n const dbPromise = this.db;\n return {\n [Symbol.asyncIterator]() {\n return (async function* () {\n const db = await dbPromise;\n const rows = await executeIdbPlan(db, plan);\n yield* rows;\n })();\n },\n };\n }\n}\n\n/**\n * Opens an IDB database and resolves once it is ready for use.\n *\n * Wraps the IDB event-based open API in a Promise. The `upgradeneeded`\n * handler is a no-op at this level — migrations are orchestrated by\n * `IdbMigrationRunner` (target-idb) which opens the database at a specific\n * version and runs DDL inside the version-change transaction.\n *\n * When version is omitted the IDB spec opens the database at its current version\n * (or version 1 for a brand-new database). This is the correct runtime behaviour\n * per ADR 001: the migration runner owns version bumping; the runtime just connects\n * to whatever schema is already in place.\n */\nfunction openIdbDatabase(dbName: string, version?: number, factory: IDBFactory = indexedDB): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n const req = factory.open(dbName, version);\n\n req.onerror = () => {\n reject(req.error);\n };\n\n req.onblocked = () => {\n reject(new Error(`IDB open blocked for \"${dbName}\": another connection is open with an older version.`));\n };\n\n // Migration runner hooks here to create / drop object stores during\n // version-change transactions. At the driver level this is a no-op\n // because the driver does not own schema — migrations are the\n // responsibility of `IdbMigrationRunner` in target-idb.\n req.onupgradeneeded = handleUpgradeNeeded;\n\n req.onsuccess = () => {\n const db = req.result;\n // Multi-tab safety: when another tab opens this database at a higher\n // version, the IDB spec fires `versionchange` on every other open\n // connection. If we don't close, the new tab's open request hangs\n // indefinitely on `blocked`. Closing here releases the lock; the\n // application layer can listen for `db.onclose` to surface a\n // \"please reload\" toast if it cares.\n db.onversionchange = () => {\n db.close();\n };\n resolve(db);\n };\n });\n}\n\n/**\n * No-op upgrade handler.\n *\n * The driver does not own schema. Object stores and indexes are created\n * by `IdbMigrationRunner` (target-idb) when it opens the database at a\n * bumped version number with a migration plan.\n */\nfunction handleUpgradeNeeded(_event: IDBVersionChangeEvent): void {\n // No-op — DDL is handled by IdbMigrationRunner.\n}\n","import type { RuntimeDriverDescriptor } from \"@prisma/orm-framework/components/execution\";\nimport { idbDriverDescriptorMeta } from \"../core/descriptor-meta\";\nimport { IdbRuntimeDriverInstance } from \"../core/idb-driver\";\n\nexport type {\n IdbPlanBody,\n IdbAtomicPlan,\n IdbBatchPlan,\n IdbAddPlan,\n IdbCursorScanPlan,\n IdbCountPlan,\n IdbKeysPlan,\n IdbKeyGetPlan,\n IdbIndexGetPlan,\n IdbPutPlan,\n IdbUpdatePlan,\n IdbDeletePlan,\n IdbScanWritePlan,\n IdbRowFilter,\n IdbRowComparator,\n IdbMarkerRecord,\n} from \"../core/plan-body\";\nexport { MARKER_STORE_NAME } from \"../core/plan-body\";\nexport type { IdbRuntimeDriverInstance } from \"../core/idb-driver\";\nexport { IdbExecuteError } from \"../core/execute/error\";\nexport type { IdbExecuteErrorCode } from \"../core/execute/error\";\nexport type { IdbTransactionScope } from \"../core/transaction-scope\";\nexport { createTransactionScope } from \"../core/transaction-scope\";\n\n/**\n * Creates a runtime driver descriptor for IndexedDB.\n *\n * The returned descriptor's `create()` opens the named IDB database\n * lazily — the connection resolves in the background and is awaited by\n * the adapter inside `runDriver()` for the first query.\n *\n * @param dbName - The IDB database name to open.\n * @param version - The IDB version number (default: 1). Bumping this\n * triggers `upgradeneeded`-based migrations via `IdbMigrationRunner`.\n *\n * @example\n * ```ts\n * const stack = createRuntimeStack({\n * target: idbRuntimeTargetDescriptor,\n * adapter: idbRuntimeAdapterDescriptor,\n * driver: createIDBRuntimeDriver(\"my-app\"),\n * });\n * ```\n */\nexport function createIDBRuntimeDriver(\n dbName: string,\n version?: number,\n options?: { readonly factory?: IDBFactory }\n): RuntimeDriverDescriptor<\"idb\", \"idb\", void, IdbRuntimeDriverInstance> {\n return {\n ...idbDriverDescriptorMeta,\n create(): IdbRuntimeDriverInstance {\n return new IdbRuntimeDriverInstance(dbName, version, options?.factory);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runtime.mjs","names":["unknown","#tx","#onComplete"],"sources":["../src/core/execute/error.ts","../src/core/execute/ops.ts","../src/core/execute/index.ts","../src/core/plan-body.ts","../src/core/transaction-scope.ts","../src/core/idb-driver.ts","../src/exports/runtime.ts"],"sourcesContent":["/**\n * Stable error codes for IDB execution failures.\n *\n * Each code corresponds to a specific IDB operation. Callers that need to\n * handle specific failure modes (e.g. retry on TRANSACTION_ABORTED) can\n * branch on `error.code` without string-matching the message.\n */\nexport type IdbExecuteErrorCode =\n | \"STORE_NOT_FOUND\" // IDB store requested by plan does not exist in the database\n | \"KEY_GET_FAILED\" // store.get(key) request failed\n | \"INDEX_GET_FAILED\" // store.index(name).getAll(range) request failed\n | \"CURSOR_SCAN_FAILED\" // openCursor iteration failed\n | \"KEYS_FAILED\" // getKey(range) / getAllKeys(range) request failed\n | \"COUNT_FAILED\" // store.count(range) / index.count(range) request failed\n | \"ADD_FAILED\" // store.add(record) request failed\n | \"PUT_FAILED\" // store.put(record) request failed\n | \"DELETE_FAILED\" // store.delete(key) request failed\n | \"BATCH_FAILED\" // one op inside a batch plan failed\n | \"TRANSACTION_ABORTED\" // IDB transaction aborted before oncomplete fired\n | \"TRANSACTION_INACTIVE\"; // request issued on a transaction that already auto-committed / finished\n\n/**\n * Structured error raised by the IDB driver's `execute()` path.\n *\n * Follows the pattern used in the upstream sql-runtime error normalizers so\n * that callers can rely on stable, machine-readable codes rather than\n * inspecting message strings.\n */\nexport class IdbExecuteError extends Error {\n readonly code: IdbExecuteErrorCode;\n readonly category = \"DRIVER\" as const;\n readonly severity = \"error\" as const;\n /** The `kind` field of the plan that was being executed. */\n readonly planKind: string;\n /** The object store name, when known at the point of failure. */\n readonly storeName: string | undefined;\n override readonly cause: unknown;\n\n constructor(\n details: {\n readonly code: IdbExecuteErrorCode;\n readonly planKind: string;\n readonly storeName?: string;\n readonly cause?: unknown;\n },\n message: string\n ) {\n super(message);\n this.name = \"IdbExecuteError\";\n this.code = details.code;\n this.planKind = details.planKind;\n this.storeName = details.storeName;\n this.cause = details.cause;\n }\n}\n\n/**\n * `true` for the synchronous DOMExceptions IDB throws when a request is issued\n * on a transaction that is no longer active (`TransactionInactiveError`) or has\n * already finished (`InvalidStateError`) — the signature of an `await` on\n * something that isn't an IDB request between two operations (see ADR 005/007).\n */\nexport function isTransactionInactiveError(err: unknown): boolean {\n const name = (err as { name?: unknown } | null)?.name;\n return name === \"TransactionInactiveError\" || name === \"InvalidStateError\";\n}\n\n/** Builds the actionable `TRANSACTION_INACTIVE` error for a failed request/objectStore call. */\nexport function transactionInactiveError(planKind: string, storeName: string, cause: unknown): IdbExecuteError {\n return new IdbExecuteError(\n { code: \"TRANSACTION_INACTIVE\", planKind, storeName, cause },\n `IDB transaction is no longer active when running \"${planKind}\" on store \"${storeName}\". ` +\n `IndexedDB transactions auto-commit as soon as the microtask queue drains with no pending request, ` +\n `so code between two operations must only \\`await\\` IDB-request-resolving promises ` +\n `(no fetch, timers or other async work inside a transaction). See ADR 005/007. Cause: ${String(cause)}`\n );\n}\n","/**\n * Callback-based IDB operation executors.\n *\n * All functions are purely event-driven — no Promises or async/await inside\n * IDB transaction event handlers. This is required because IDB transactions\n * auto-commit when no pending requests exist and the microtask queue drains;\n * interleaving a microtask boundary (await) between IDB requests would risk\n * premature auto-commit.\n *\n * Pattern: each `exec*` function\n * 1. Issues one or more IDB requests **synchronously**.\n * 2. Calls `onComplete(rows)` when the op's work is done (inside onsuccess).\n * 3. Calls `onError(err)` if a request or the transaction fails.\n *\n * The caller (execute/index.ts) resolves the outer Promise from `tx.oncomplete`\n * so write durability is guaranteed before rows are delivered.\n */\nimport type {\n IdbAddPlan,\n IdbAtomicPlan,\n IdbCountPlan,\n IdbCursorScanPlan,\n IdbDeletePlan,\n IdbIndexGetPlan,\n IdbKeyGetPlan,\n IdbKeysPlan,\n IdbPutPlan,\n IdbScanWritePlan,\n IdbUpdatePlan,\n} from \"../plan-body\";\nimport {\n IdbExecuteError,\n isTransactionInactiveError,\n transactionInactiveError,\n type IdbExecuteErrorCode,\n} from \"./error\";\n\ntype Row = Record<string, unknown>;\ntype OnComplete = (rows: Row[]) => void;\ntype OnError = (err: unknown) => void;\n\n/**\n * The error for a failed request: `IDB <what> failed on <target>: <cause>`.\n * `target` defaults to the plan's store; pass it when the message also names an index.\n */\nfunction opError(\n code: IdbExecuteErrorCode,\n plan: IdbAtomicPlan,\n what: string,\n cause: unknown,\n target = `store \"${plan.storeName}\"`\n): IdbExecuteError {\n return new IdbExecuteError(\n { code, planKind: plan.kind, storeName: plan.storeName, cause },\n `IDB ${what} failed on ${target}: ${String(cause)}`\n );\n}\n\n/** `store \"users\"` or `store \"users\" (index \"by_name\")`, for plans that may read through an index. */\nfunction storeTarget(plan: { storeName: string; indexName?: string }): string {\n const index = plan.indexName !== undefined ? ` (index \"${plan.indexName}\")` : \"\";\n return `store \"${plan.storeName}\"${index}`;\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Dispatch an atomic plan to its IDB operation within a live transaction.\n *\n * All IDB requests are issued synchronously. `onComplete` is called once all\n * requests for this op have resolved (still inside IDB event handlers — no\n * async gap). `onError` is called on the first request failure.\n */\nexport function executeOpInTx(\n store: IDBObjectStore,\n plan: IdbAtomicPlan,\n onComplete: OnComplete,\n onError: OnError\n): void {\n try {\n dispatchOp(store, plan, onComplete, onError);\n } catch (err) {\n // Requests are issued synchronously, so a dead transaction surfaces here as a\n // thrown DOMException. Anything else (DataError, unknown index, ...) is unchanged.\n if (isTransactionInactiveError(err)) return onError(transactionInactiveError(plan.kind, plan.storeName, err));\n throw err;\n }\n}\n\nfunction dispatchOp(store: IDBObjectStore, plan: IdbAtomicPlan, onComplete: OnComplete, onError: OnError): void {\n switch (plan.kind) {\n case \"key-get\":\n return execKeyGet(store, plan, onComplete, onError);\n case \"index-get\":\n return execIndexGet(store, plan, onComplete, onError);\n case \"cursor-scan\":\n return execCursorScan(store, plan, onComplete, onError);\n case \"count\":\n return execCount(store, plan, onComplete, onError);\n case \"keys\":\n return execKeys(store, plan, onComplete, onError);\n case \"add\":\n return execAdd(store, plan, onComplete, onError);\n case \"put\":\n return execPut(store, plan, onComplete, onError);\n case \"update\":\n return execUpdate(store, plan, onComplete, onError);\n case \"delete\":\n return execDelete(store, plan, onComplete, onError);\n case \"scan-write\":\n return execScanWrite(store, plan, onComplete, onError);\n default: {\n // Unreachable for a well-typed plan; a stale driver build receiving a newer plan kind\n // would otherwise never call onComplete/onError and hang the caller.\n const unknown: never = plan;\n return onError(new Error(`Unknown IDB plan kind: ${String((unknown as { kind?: unknown }).kind)}`));\n }\n }\n}\n\n/** Returns the IDB transaction mode appropriate for a given atomic plan. */\nexport function planTxMode(plan: IdbAtomicPlan): IDBTransactionMode {\n return plan.kind === \"add\" ||\n plan.kind === \"put\" ||\n plan.kind === \"update\" ||\n plan.kind === \"delete\" ||\n plan.kind === \"scan-write\"\n ? \"readwrite\"\n : \"readonly\";\n}\n\n// ── Per-operation executors ──────────────────────────────────────────────────\n\nfunction execKeyGet(store: IDBObjectStore, plan: IdbKeyGetPlan, onComplete: OnComplete, onError: OnError): void {\n const req = store.get(plan.key);\n req.onsuccess = () => {\n const value = req.result as Row | undefined;\n onComplete(value !== undefined ? [value] : []);\n };\n req.onerror = () => onError(opError(\"KEY_GET_FAILED\", plan, \"key-get\", req.error));\n}\n\nfunction execIndexGet(store: IDBObjectStore, plan: IdbIndexGetPlan, onComplete: OnComplete, onError: OnError): void {\n const req = store.index(plan.indexName).getAll(plan.range);\n req.onsuccess = () => onComplete(req.result as Row[]);\n req.onerror = () =>\n onError(opError(\"INDEX_GET_FAILED\", plan, \"index-get\", req.error, `\"${plan.storeName}\"/\"${plan.indexName}\"`));\n}\n\nfunction execCount(store: IDBObjectStore, plan: IdbCountPlan, onComplete: OnComplete, onError: OnError): void {\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n const req = plan.range !== undefined ? source.count(plan.range) : source.count();\n req.onsuccess = () => onComplete([{ count: req.result }]);\n req.onerror = () => onError(opError(\"COUNT_FAILED\", plan, \"count\", req.error, storeTarget(plan)));\n}\n\nfunction execKeys(store: IDBObjectStore, plan: IdbKeysPlan, onComplete: OnComplete, onError: OnError): void {\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n const fail = (cause: unknown) => onError(opError(\"KEYS_FAILED\", plan, \"keys read\", cause, storeTarget(plan)));\n\n // getKey() requires a query (null/absent throws), so a range-less single-key\n // read is expressed as getAllKeys(undefined, 1) instead.\n if (plan.take === 1 && plan.range !== undefined) {\n const req = source.getKey(plan.range);\n req.onsuccess = () => onComplete(req.result === undefined ? [] : [{ key: req.result }]);\n req.onerror = () => fail(req.error);\n return;\n }\n\n if (plan.take === 0) {\n onComplete([]);\n return;\n }\n\n const req = source.getAllKeys(plan.range, plan.take);\n req.onsuccess = () => onComplete((req.result as IDBValidKey[]).map((key) => ({ key })));\n req.onerror = () => fail(req.error);\n}\n\nfunction execCursorScan(\n store: IDBObjectStore,\n plan: IdbCursorScanPlan,\n onComplete: OnComplete,\n onError: OnError\n): void {\n // Capture plan fields to avoid repeated property access inside the hot loop.\n const comparator = plan.comparator;\n const filter = plan.filter;\n const skip = plan.skip;\n const take = plan.take;\n\n const source: IDBObjectStore | IDBIndex = plan.indexName !== undefined ? store.index(plan.indexName) : store;\n\n // openCursor accepts null to mean \"no range restriction\".\n const req = source.openCursor(plan.range ?? null, plan.direction ?? \"next\");\n const collected: Row[] = [];\n let skippedCount = 0;\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n\n if (cursor === null) {\n // Cursor exhausted. If a comparator is set we collected all matching rows\n // before sorting; apply sort + skip/take now.\n if (comparator !== undefined) {\n collected.sort(comparator);\n const offset = skip ?? 0;\n onComplete(take !== undefined ? collected.slice(offset, offset + take) : collected.slice(offset));\n } else {\n onComplete(collected);\n }\n return;\n }\n\n const row = cursor.value as Row;\n\n if (filter === undefined || filter(row)) {\n if (comparator !== undefined) {\n // Must collect ALL matching rows before sorting — can't apply skip/take\n // inline because earlier rows might be sorted out.\n collected.push(row);\n } else {\n // No comparator: apply skip/take inline so we avoid collecting rows\n // that will be discarded.\n if (skip !== undefined && skippedCount < skip) {\n skippedCount++;\n } else if (take === undefined || collected.length < take) {\n collected.push(row);\n if (take !== undefined && collected.length === take) {\n // Take limit reached — stop the cursor early. The transaction has\n // no more pending requests and will auto-commit, triggering\n // tx.oncomplete → resolve(rows) in the outer wrapper.\n onComplete(collected);\n return; // intentionally no cursor.continue()\n }\n }\n }\n }\n\n cursor.continue();\n };\n\n req.onerror = () => onError(opError(\"CURSOR_SCAN_FAILED\", plan, \"cursor-scan\", req.error));\n}\n\n/**\n * The record as stored: `record` itself, plus the generated key when an\n * `autoIncrement` store with an inline `keyPath` filled it in. IDB writes the\n * generated key into its own stored copy, never into the caller's object, so\n * the echo needs it set from `req.result`.\n */\nfunction withGeneratedKey(\n store: IDBObjectStore,\n record: Record<string, unknown>,\n key: IDBValidKey\n): Record<string, unknown> {\n if (!store.autoIncrement || typeof store.keyPath !== \"string\" || record[store.keyPath] !== undefined) return record;\n return { ...record, [store.keyPath]: key };\n}\n\nfunction execAdd(store: IDBObjectStore, plan: IdbAddPlan, onComplete: OnComplete, onError: OnError): void {\n // Use the optional out-of-line key when provided; otherwise IDB derives the\n // key from the record via the store's keyPath.\n const req = plan.key !== undefined ? store.add(plan.record, plan.key) : store.add(plan.record);\n // Echo the record back — IDB has no RETURNING clause.\n req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);\n req.onerror = () => onError(opError(\"ADD_FAILED\", plan, \"add\", req.error));\n}\n\nfunction execPut(store: IDBObjectStore, plan: IdbPutPlan, onComplete: OnComplete, onError: OnError): void {\n // Use the optional out-of-line key when provided; otherwise IDB derives the\n // key from the record via the store's keyPath.\n const req = plan.key !== undefined ? store.put(plan.record, plan.key) : store.put(plan.record);\n // Echo the record back — IDB has no RETURNING clause.\n req.onsuccess = () => onComplete([withGeneratedKey(store, plan.record, req.result)]);\n req.onerror = () => onError(opError(\"PUT_FAILED\", plan, \"put\", req.error));\n}\n\nfunction execUpdate(store: IDBObjectStore, plan: IdbUpdatePlan, onComplete: OnComplete, onError: OnError): void {\n // Step 1: read the current record.\n const getReq = store.get(plan.key);\n getReq.onsuccess = () => {\n const existing = (getReq.result as Row | undefined) ?? {};\n // Step 2: shallow-merge patch onto existing record.\n const merged: Row = { ...existing, ...plan.patch };\n // Step 3: write the merged record back.\n const putReq = store.put(merged);\n putReq.onsuccess = () => onComplete([merged]);\n putReq.onerror = () => onError(opError(\"PUT_FAILED\", plan, \"update (put phase)\", putReq.error));\n };\n getReq.onerror = () => onError(opError(\"KEY_GET_FAILED\", plan, \"update (get phase)\", getReq.error));\n}\n\nfunction execDelete(store: IDBObjectStore, plan: IdbDeletePlan, onComplete: OnComplete, onError: OnError): void {\n // `plan.key` may be a single key or an `IDBKeyRange` spanning several\n // records (deleteMany). Walk a cursor over it so every matched record is\n // captured (and its key individually deleted) before moving on — this\n // both echoes the deleted row(s), mirroring add/put/update, and gives an\n // accurate affected-row count for both the single-key and range cases via\n // the same drain-and-count `runExecute()` already uses for every other op.\n const req = store.openCursor(plan.key);\n const collected: Row[] = [];\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n if (cursor === null) {\n onComplete(collected);\n return;\n }\n collected.push(cursor.value as Row);\n const delReq = cursor.delete();\n delReq.onsuccess = () => cursor.continue();\n delReq.onerror = () => onError(opError(\"DELETE_FAILED\", plan, \"delete\", delReq.error));\n };\n req.onerror = () => onError(opError(\"DELETE_FAILED\", plan, \"delete\", req.error));\n}\n\nfunction execScanWrite(store: IDBObjectStore, plan: IdbScanWritePlan, onComplete: OnComplete, onError: OnError): void {\n // Cursor must be opened on a readwrite transaction (enforced by planTxMode).\n const req = store.openCursor(null, \"next\");\n const collected: Row[] = [];\n\n req.onsuccess = () => {\n const cursor = req.result as IDBCursorWithValue | null;\n\n if (cursor === null) {\n // Cursor exhausted — deliver all collected rows.\n onComplete(collected);\n return;\n }\n\n const row = cursor.value as Row;\n\n // Skip rows that don't match the filter.\n if (plan.filter !== undefined && !plan.filter(row)) {\n cursor.continue();\n return;\n }\n\n if (plan.write === \"delete\") {\n // Capture the row value before deleting (so deleteAll can return it).\n collected.push(row);\n const delReq = cursor.delete();\n delReq.onsuccess = () => {\n if (plan.take !== undefined && collected.length >= plan.take) {\n onComplete(collected);\n return; // intentionally no cursor.continue() — transaction auto-commits\n }\n cursor.continue();\n };\n delReq.onerror = () => onError(opError(\"DELETE_FAILED\", plan, \"scan-write (delete)\", delReq.error));\n } else {\n // put-merged: shallow-merge patch onto existing row, write back in-place.\n const merged: Row = { ...row, ...plan.patch };\n const updReq = cursor.update(merged);\n updReq.onsuccess = () => {\n collected.push(merged);\n if (plan.take !== undefined && collected.length >= plan.take) {\n onComplete(collected);\n return; // intentionally no cursor.continue()\n }\n cursor.continue();\n };\n updReq.onerror = () => onError(opError(\"PUT_FAILED\", plan, \"scan-write (put-merged)\", updReq.error));\n }\n };\n\n req.onerror = () => onError(opError(\"CURSOR_SCAN_FAILED\", plan, \"scan-write cursor\", req.error));\n}\n","/**\n * Top-level IDB plan dispatcher.\n *\n * `executeIdbPlan(db, plan)` is the single entry point for the IDB driver's\n * `execute()` path. It:\n * 1. Opens an IDB transaction scoped to the store(s) the plan touches.\n * 2. Dispatches to the appropriate op executor (ops.ts).\n * 3. Resolves the returned Promise only when `tx.oncomplete` fires —\n * guaranteeing write durability before rows are delivered.\n *\n * Transaction modes:\n * - Atomic read plans (key-get, index-get, cursor-scan): `readonly`\n * - Atomic write plans (add, put, delete): `readwrite`\n * - Batch plans: `readwrite` if any op is a write, `readonly` otherwise\n */\nimport type { IdbAtomicPlan, IdbBatchPlan, IdbPlanBody } from \"../plan-body\";\nimport { IdbExecuteError, isTransactionInactiveError, transactionInactiveError } from \"./error\";\nimport { executeOpInTx, planTxMode } from \"./ops\";\n\ntype Row = Record<string, unknown>;\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Execute an IDB plan against a live database and collect all result rows.\n *\n * Batch plans run all ops inside a single transaction; atomic plans open a\n * single-store transaction. Resolves after `tx.oncomplete` so writes are\n * durable before the caller receives rows.\n */\nexport function executeIdbPlan(db: IDBDatabase, plan: IdbPlanBody): Promise<Row[]> {\n if (plan.kind === \"batch\") return executeBatchPlan(db, plan);\n return executeAtomicPlan(db, plan);\n}\n\n// ── Atomic plans ─────────────────────────────────────────────────────────────\n\nfunction executeAtomicPlan(db: IDBDatabase, plan: IdbAtomicPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n let tx: IDBTransaction;\n try {\n tx = db.transaction([plan.storeName], planTxMode(plan));\n } catch (err) {\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: plan.kind, storeName: plan.storeName, cause: err },\n `IDB store \"${plan.storeName}\" not found in database`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n\n tx.oncomplete = () => resolve(rows);\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n {\n code: \"TRANSACTION_ABORTED\",\n planKind: plan.kind,\n storeName: plan.storeName,\n cause: tx.error,\n },\n `IDB transaction failed on store \"${plan.storeName}\": ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: plan.kind, storeName: plan.storeName },\n `IDB transaction aborted on store \"${plan.storeName}\"`\n )\n );\n\n const store = tx.objectStore(plan.storeName);\n executeOpInTx(\n store,\n plan,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n },\n (err) => reject(err)\n );\n });\n}\n\n// ── Batch plans ──────────────────────────────────────────────────────────────\n\nfunction executeBatchPlan(db: IDBDatabase, plan: IdbBatchPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n const mode: IDBTransactionMode = plan.ops.some((op) => planTxMode(op) === \"readwrite\") ? \"readwrite\" : \"readonly\";\n\n let tx: IDBTransaction;\n try {\n tx = db.transaction([...plan.storeNames], mode);\n } catch (err) {\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: \"batch\", cause: err },\n `IDB batch transaction failed to open: ${String(err)}`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n\n tx.oncomplete = () => resolve(rows);\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: \"batch\", cause: tx.error },\n `IDB batch transaction failed: ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError({ code: \"TRANSACTION_ABORTED\", planKind: \"batch\" }, \"IDB batch transaction was aborted\")\n );\n\n // Run ops sequentially using recursive callbacks. Each op's `onComplete`\n // triggers the next op synchronously (still inside IDB event handlers),\n // which ensures we never cross a microtask boundary between IDB requests.\n runOpsSequentially(tx, plan.ops, 0, rows, reject);\n });\n}\n\n/**\n * Recursively runs batch ops one at a time — op N+1 is initiated from inside\n * op N's `onComplete` callback, which fires synchronously within the IDB\n * event handler. This keeps the transaction alive and prevents auto-commit\n * gaps between ops.\n */\nfunction runOpsSequentially(\n tx: IDBTransaction,\n ops: ReadonlyArray<IdbAtomicPlan>,\n index: number,\n rows: Row[],\n onError: (err: unknown) => void\n): void {\n const op = ops[index];\n if (op === undefined) return; // all ops initiated; tx.oncomplete will resolve\n\n let store: IDBObjectStore;\n try {\n store = tx.objectStore(op.storeName);\n } catch (err) {\n if (isTransactionInactiveError(err)) {\n onError(transactionInactiveError(\"batch\", op.storeName, err));\n return;\n }\n onError(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: \"batch\", storeName: op.storeName, cause: err },\n `IDB batch: store \"${op.storeName}\" not found`\n )\n );\n return;\n }\n\n executeOpInTx(\n store,\n op,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n runOpsSequentially(tx, ops, index + 1, rows, onError);\n },\n onError\n );\n}\n","import type { ExecutionPlan } from \"@prisma/orm-framework/components/runtime\";\n\n// ── Marker store ─────────────────────────────────────────────────────────────\n\n/**\n * Name of the object store that holds the contract marker.\n *\n * Created during `upgradeneeded` by the migration runner. `IdbRuntime.verifyMarker()`\n * compares its stored hash with the contract's to detect schema drift.\n *\n * Upstream equivalent: `_prisma_marker` table in SQLite/Postgres,\n * `_prisma_marker` collection in MongoDB.\n */\nexport const MARKER_STORE_NAME = \"_prisma_next_marker\";\n\n/**\n * Shape of a marker record stored in {@link MARKER_STORE_NAME}.\n *\n * Mirrors the framework's `ContractMarkerRecord` plus the `space` keying\n * field. Records are keyed by `space` (`\"app\"` for the single app space) so\n * the storage layout can accommodate future IDB extension spaces.\n *\n * `updatedAt` is a JavaScript `Date` (not an ISO string) because IndexedDB\n * serialises Dates natively via structured-clone.\n *\n * Non-key fields are optional for forward compatibility with future contract\n * versions that may add or remove marker metadata.\n */\nexport interface IdbMarkerRecord {\n /** Contract-space identifier (`\"app\"` for the single app space). */\n readonly space?: string;\n /** The `storageHash` from the contract that was last signed. */\n readonly storageHash: string;\n /** The `profileHash` from the contract that was last signed. */\n readonly profileHash?: string;\n /** Date the marker was last written. */\n readonly updatedAt?: Date | string;\n readonly invariants?: readonly string[];\n readonly contractJson?: unknown;\n readonly canonicalVersion?: number | null;\n readonly appTag?: string | null;\n readonly meta?: Record<string, unknown>;\n}\n\n/**\n * In-memory predicate applied to each row while iterating a cursor.\n * Returns true if the row should be included in the result set.\n */\nexport type IdbRowFilter = (row: Record<string, unknown>) => boolean;\n\n/**\n * In-memory comparator for ORDER BY.\n * Follows the same contract as Array.prototype.sort: negative / zero / positive.\n */\nexport type IdbRowComparator = (a: Record<string, unknown>, b: Record<string, unknown>) => number;\n\n/**\n * Full cursor scan over an object store or index.\n *\n * Used for `findMany`, `findFirst`, and any query where an exact key lookup\n * isn't possible. Supports key-range restriction, in-memory filtering,\n * in-memory ordering, and OFFSET + LIMIT.\n *\n * When `indexName` is set, the cursor iterates the named secondary index\n * instead of the primary key order.\n */\nexport interface IdbCursorScanPlan extends ExecutionPlan {\n readonly kind: \"cursor-scan\";\n readonly storeName: string;\n readonly indexName?: string; // if set, iterate via this index\n readonly range?: IDBKeyRange; // restrict the cursor's key range\n readonly direction?: IDBCursorDirection; // \"next\" | \"nextunique\" | \"prev\" | \"prevunique\"\n readonly filter?: IdbRowFilter; // in-memory WHERE\n readonly comparator?: IdbRowComparator; // in-memory ORDER BY (when index order isn't enough)\n readonly skip?: number; // OFFSET\n readonly take?: number; // LIMIT (undefined = no limit)\n}\n\n/**\n * Primary-key O(1) lookup via `store.get(key)`.\n *\n * Used for `findUnique` on `@id` fields.\n */\nexport interface IdbKeyGetPlan extends ExecutionPlan {\n readonly kind: \"key-get\";\n readonly storeName: string;\n readonly key: IDBValidKey;\n}\n\n/**\n * Index-based range lookup.\n *\n * Used for `findUnique` on `@@unique` fields and index-accelerated\n * `findMany` on `@@index` fields.\n */\nexport interface IdbIndexGetPlan extends ExecutionPlan {\n readonly kind: \"index-get\";\n readonly storeName: string;\n readonly indexName: string;\n readonly range: IDBKeyRange;\n}\n\n/**\n * Insert a single record via `store.add(record[, key])`.\n *\n * Used for Prisma `create` semantics. IndexedDB rejects `add()` when the\n * primary key already exists, which preserves the \"create never overwrites\"\n * contract expected by callers.\n *\n * `key` is only needed for out-of-line key stores (`keyPath: null`).\n */\nexport interface IdbAddPlan extends ExecutionPlan {\n readonly kind: \"add\";\n readonly storeName: string;\n readonly record: Record<string, unknown>;\n readonly key?: IDBValidKey;\n}\n\n/**\n * Upsert or replace a single record via `store.put(record[, key])`.\n *\n * Used for upsert paths where the full replacement record is already known.\n * The driver echoes `record` back as the result row — IDB does not have\n * RETURNING.\n *\n * `key` is only needed for out-of-line key stores (`keyPath: null`).\n */\nexport interface IdbPutPlan extends ExecutionPlan {\n readonly kind: \"put\";\n readonly storeName: string;\n readonly record: Record<string, unknown>;\n readonly key?: IDBValidKey;\n}\n\n/**\n * Atomic partial update: get → merge → put in a single readwrite transaction.\n *\n * Used for Prisma `update` where only a subset of fields change. The driver:\n * 1. Reads the current record via `store.get(key)`.\n * 2. Shallow-merges `patch` onto the existing record with `{ ...existing, ...patch }`.\n * 3. Writes the merged record back via `store.put(merged)`.\n * 4. Echoes the merged record as the result row.\n *\n * This preserves fields not mentioned in `patch` — unlike `IdbPutPlan` which\n * does a full replacement. The get and put are issued inside the same\n * readwrite transaction so no other writer can interleave.\n *\n * If no record exists for `key`, the driver echoes `patch` as-is (insert semantics).\n */\nexport interface IdbUpdatePlan extends ExecutionPlan {\n readonly kind: \"update\";\n readonly storeName: string;\n readonly key: IDBValidKey;\n readonly patch: Record<string, unknown>;\n}\n\n/**\n * Delete one or more records by key or key range.\n *\n * Used for `delete` and `deleteMany`. The driver walks a cursor over\n * `key` (a single key or a range) and yields each deleted row, so callers\n * can echo them back and `execute()` can report an accurate affected-row\n * count — same convention as `add`/`put`/`update`.\n */\nexport interface IdbDeletePlan extends ExecutionPlan {\n readonly kind: \"delete\";\n readonly storeName: string;\n readonly key: IDBValidKey | IDBKeyRange;\n}\n\n/**\n * Cursor scan with in-place write for each matching row.\n *\n * Used for `update` (take:1), `updateAll`, `updateCount`, `deleteAll`,\n * `deleteCount`. Opens a readwrite cursor and applies `write` to each\n * row that passes `filter`:\n *\n * - `\"put-merged\"`: shallow-merges `patch` onto the row and calls\n * `cursor.update(merged)`. Yields merged rows.\n * - `\"delete\"`: captures `cursor.value` then calls `cursor.delete()`.\n * Yields the deleted rows (so callers can echo them back).\n *\n * With `take`, the cursor stops after the first `take` matches (no\n * `cursor.continue()` call after the limit is reached).\n */\nexport interface IdbScanWritePlan extends ExecutionPlan {\n readonly kind: \"scan-write\";\n readonly storeName: string;\n readonly filter?: IdbRowFilter;\n readonly take?: number;\n readonly write: \"put-merged\" | \"delete\";\n readonly patch?: Record<string, unknown>;\n}\n\n/**\n * Native count via `store.count(range)` / `index.count(range)`.\n *\n * Used by the ORM `.count()` terminal when the whole `where` is captured by a\n * key range (or there is no `where`), so no row needs to be deserialized or\n * filtered in memory. Counts index *entries*, so it must not be used with a\n * `multiEntry` index (one record can occupy several entries).\n *\n * Result: exactly one row, `{ count: number }` — the driver's `Row[]` result\n * contract is unchanged; the caller unwraps `rows[0].count`.\n */\nexport interface IdbCountPlan extends ExecutionPlan {\n readonly kind: \"count\";\n readonly storeName: string;\n readonly indexName?: string; // count via this index instead of the store's primary keys\n readonly range?: IDBKeyRange; // omit to count every entry\n}\n\n/**\n * Key-only read via `getKey(range)` / `getAllKeys(range, count)` — returns\n * PRIMARY keys without deserializing any row value.\n *\n * Meant for existence checks (\"does any row match this key range?\") where the\n * caller never reads a field. Because no value is fetched, it can only\n * express criteria that are a key range on the store's primary key or on an\n * index — never an in-memory row filter.\n *\n * - `take: 1` with a `range` → `getKey(range)` (one request, no cursor).\n * - otherwise → `getAllKeys(range, take)`; `take` omitted returns every key.\n *\n * Result rows: one `{ key }` per match (the primary key — an array for a\n * compound key) or `[]` when nothing matches. Same synthetic-row convention as\n * {@link IdbCountPlan}.\n */\nexport interface IdbKeysPlan extends ExecutionPlan {\n readonly kind: \"keys\";\n readonly storeName: string;\n readonly indexName?: string; // resolve through this index; returned keys are still primary keys\n readonly range?: IDBKeyRange; // omit to cover the whole store/index\n readonly take?: number; // max keys to return (undefined = all)\n}\n\n/**\n * All single-store atomic op types — valid both standalone and inside a batch.\n */\nexport type IdbAtomicPlan =\n | IdbCursorScanPlan\n | IdbKeyGetPlan\n | IdbIndexGetPlan\n | IdbCountPlan\n | IdbKeysPlan\n | IdbAddPlan\n | IdbPutPlan\n | IdbUpdatePlan\n | IdbDeletePlan\n | IdbScanWritePlan;\n\n/**\n * Multi-op atomic batch executed inside a single IDB transaction.\n *\n * Used whenever a Prisma operation must touch multiple stores atomically\n * (e.g. `create` with nested relations, `update` + cascade `delete`).\n *\n * The driver opens ONE readwrite transaction scoped to `storeNames`, runs\n * every op in `ops` sequentially, and collects rows from read ops. Write ops\n * (`put`/`delete`) echo their result inline (see `IdbPutPlan`).\n *\n * `storeNames` MUST include every store referenced by any op in `ops`.\n * IDB requires the full store scope to be declared when opening a transaction.\n */\nexport interface IdbBatchPlan extends ExecutionPlan {\n readonly kind: \"batch\";\n readonly storeNames: ReadonlyArray<string>;\n readonly ops: ReadonlyArray<IdbAtomicPlan>;\n}\n\n/**\n * The full union of plan shapes the adapter produces and the driver executes.\n */\nexport type IdbPlanBody = IdbAtomicPlan | IdbBatchPlan;\n","/**\n * Multi-store IDB transaction scope.\n *\n * A single `IDBTransaction` spanning one or more object stores, exposed as a\n * typed execution surface for the ORM's `withMutationScope()` helper.\n *\n * Design notes:\n * - `execute(plan)` runs `executeOpInTx` directly in the pre-opened transaction —\n * NO new transaction is opened per call, and the middleware chain is bypassed\n * entirely. This keeps the shared IDB transaction alive across multiple awaited\n * execute() calls (IDB auto-commits in a macro-task, not a microtask; each\n * execute() issues new requests before the auto-commit check fires).\n * - `commit()` resolves when `tx.oncomplete` fires — the caller awaits this after\n * all scope operations are complete to get write-durability confirmation.\n * - `rollback()` is idempotent: calling it on an already-aborted transaction is\n * a no-op (the DOMException from IDB is swallowed).\n *\n * Issue #6 (scope hard-coding): since this scope's execute() bypasses both of\n * `RuntimeCore`'s middleware chains (query()'s and execute()'s alike), the\n * cache middleware never fires inside a scope → reads are always fresh.\n */\nimport type { IdbAtomicPlan } from \"./plan-body\";\nimport { IdbExecuteError, isTransactionInactiveError, transactionInactiveError } from \"./execute/error\";\nimport { executeOpInTx } from \"./execute/ops\";\n\ntype Row = Record<string, unknown>;\n\n// ── Public interface ──────────────────────────────────────────────────────────\n\nexport interface IdbTransactionScope {\n /**\n * Execute a single atomic plan inside the shared IDB transaction.\n *\n * The plan runs synchronously inside IDB event callbacks; the returned\n * Promise resolves once the operation's `onsuccess` fires. The transaction\n * stays open — the next `execute()` call can be issued right after `await`.\n */\n execute(plan: IdbAtomicPlan): Promise<Row[]>;\n\n /**\n * Wait for the IDB transaction to fully commit.\n *\n * Resolves when `tx.oncomplete` fires (all writes are durable). Call this\n * after all `execute()` calls; `withMutationScope` does this automatically.\n */\n commit(): Promise<void>;\n\n /**\n * Abort the transaction, rolling back all uncommitted writes.\n *\n * Idempotent — safe to call when the transaction has already been aborted\n * (e.g. due to a request error). `withMutationScope` calls this on catch.\n */\n rollback(): void;\n}\n\n// ── Implementation ────────────────────────────────────────────────────────────\n\nclass IdbTransactionScopeImpl implements IdbTransactionScope {\n readonly #tx: IDBTransaction;\n readonly #onComplete: Promise<void>;\n\n constructor(tx: IDBTransaction) {\n this.#tx = tx;\n this.#onComplete = new Promise<void>((resolve, reject) => {\n tx.oncomplete = () => resolve();\n tx.onerror = () =>\n reject(\n new IdbExecuteError(\n { code: \"TRANSACTION_ABORTED\", planKind: \"batch\", cause: tx.error },\n `IDB transaction scope failed: ${String(tx.error)}`\n )\n );\n tx.onabort = () =>\n reject(\n new IdbExecuteError({ code: \"TRANSACTION_ABORTED\", planKind: \"batch\" }, \"IDB transaction scope aborted\")\n );\n });\n // Suppress \"unhandled rejection\" when the scope is rolled back without\n // commit() being awaited (e.g. withMutationScope catches an error and\n // calls rollback() without ever calling commit()).\n this.#onComplete.catch(() => {});\n }\n\n execute(plan: IdbAtomicPlan): Promise<Row[]> {\n return new Promise<Row[]>((resolve, reject) => {\n let store: IDBObjectStore;\n try {\n store = this.#tx.objectStore(plan.storeName);\n } catch (err) {\n if (isTransactionInactiveError(err)) {\n reject(transactionInactiveError(plan.kind, plan.storeName, err));\n return;\n }\n reject(\n new IdbExecuteError(\n { code: \"STORE_NOT_FOUND\", planKind: plan.kind, storeName: plan.storeName, cause: err },\n `IDB transaction scope: store \"${plan.storeName}\" not found`\n )\n );\n return;\n }\n\n const rows: Row[] = [];\n executeOpInTx(\n store,\n plan,\n (opRows) => {\n for (const row of opRows) rows.push(row);\n resolve(rows);\n },\n reject\n );\n });\n }\n\n commit(): Promise<void> {\n return this.#onComplete;\n }\n\n rollback(): void {\n try {\n this.#tx.abort();\n } catch {\n // Transaction already committed or aborted — no-op.\n }\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Open an IDB transaction spanning `storeNames` and wrap it in a scope.\n *\n * Throws synchronously if any store in `storeNames` is not found in the\n * database (IDB throws when opening a transaction with unknown store names).\n */\nexport function createTransactionScope(\n db: IDBDatabase,\n storeNames: string[],\n mode: IDBTransactionMode = \"readwrite\"\n): IdbTransactionScope {\n const tx = db.transaction(storeNames, mode);\n return new IdbTransactionScopeImpl(tx);\n}\n","import type { RuntimeDriverInstance } from \"@prisma/orm-framework/components/execution\";\nimport { executeIdbPlan } from \"./execute\";\nimport { MARKER_STORE_NAME, type IdbMarkerRecord, type IdbPlanBody } from \"./plan-body\";\nimport { createTransactionScope, type IdbTransactionScope } from \"./transaction-scope\";\n\nexport class IdbRuntimeDriverInstance implements RuntimeDriverInstance<\"idb\", \"idb\"> {\n readonly familyId = \"idb\" as const;\n readonly targetId = \"idb\" as const;\n /**\n * The live IDBDatabase connection. Resolves once `upgradeneeded` (if any)\n * completes and the database is ready for use.\n *\n * `execute()` and `transaction()` await this before opening a transaction.\n * The Promise is shared — all concurrent callers get the same database object.\n */\n readonly db: Promise<IDBDatabase>;\n\n constructor(dbName: string, version?: number, factory?: IDBFactory) {\n this.db = openIdbDatabase(dbName, version, factory);\n }\n\n async close(): Promise<void> {\n (await this.db).close();\n }\n\n /**\n * Read the contract marker from the `_prisma_next_marker` store.\n *\n * Returns `null` when the marker store does not exist (fresh database\n * that hasn't been initialised yet) or when no marker record is present.\n *\n * Called by `IdbRuntimeImpl.verifyMarker()` to check that the live IDB\n * schema matches the contract.\n */\n async readMarker(): Promise<IdbMarkerRecord | null> {\n const db = await this.db;\n if (!db.objectStoreNames.contains(MARKER_STORE_NAME)) {\n return null;\n }\n return new Promise((resolve, reject) => {\n const tx = db.transaction(MARKER_STORE_NAME, \"readonly\");\n const store = tx.objectStore(MARKER_STORE_NAME);\n\n const appReq = store.get(\"app\");\n appReq.onsuccess = () => {\n const result = appReq.result as IdbMarkerRecord | undefined;\n resolve(result ?? null);\n };\n appReq.onerror = () => reject(appReq.error);\n\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Open a multi-store readwrite transaction and wrap it in an\n * {@link IdbTransactionScope}.\n *\n * Awaits the live `IDBDatabase` connection before opening the transaction.\n * Used by `withMutationScope()` in `client-idb` to run cross-store writes\n * atomically.\n */\n async transaction(storeNames: string[], mode: IDBTransactionMode = \"readwrite\"): Promise<IdbTransactionScope> {\n const db = await this.db;\n return createTransactionScope(db, storeNames, mode);\n }\n\n /**\n * Execute an IDB plan body and yield rows as an async iterable.\n *\n * Opens a new IDB transaction for each call, collects all rows inside the\n * transaction (collect-then-yield; see execute/index.ts for rationale),\n * and yields them after the transaction commits.\n *\n * Called by `IdbRuntimeImpl.runDriver()` in `@prisma-idb/runtime-idb`.\n */\n execute(plan: IdbPlanBody): AsyncIterable<Record<string, unknown>> {\n const dbPromise = this.db;\n return {\n [Symbol.asyncIterator]() {\n return (async function* () {\n const db = await dbPromise;\n const rows = await executeIdbPlan(db, plan);\n yield* rows;\n })();\n },\n };\n }\n}\n\n/**\n * Opens an IDB database and resolves once it is ready for use.\n *\n * Wraps the IDB event-based open API in a Promise. There is no `upgradeneeded`\n * handler: the driver does not own schema. Migrations are applied by\n * `openAndUpgrade` (target-idb), which opens the database at a specific\n * version and runs DDL inside the version-change transaction.\n *\n * When version is omitted the IDB spec opens the database at its current version\n * (or version 1 for a brand-new database). This is the correct runtime behaviour\n * per ADR 001: the migration runner owns version bumping; the runtime just connects\n * to whatever schema is already in place.\n */\nfunction openIdbDatabase(dbName: string, version?: number, factory: IDBFactory = indexedDB): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n const req = factory.open(dbName, version);\n\n req.onerror = () => {\n reject(req.error);\n };\n\n req.onblocked = () => {\n reject(new Error(`IDB open blocked for \"${dbName}\": another connection is open with an older version.`));\n };\n\n req.onsuccess = () => {\n const db = req.result;\n // Multi-tab safety: when another tab opens this database at a higher\n // version, the IDB spec fires `versionchange` on every other open\n // connection. If we don't close, the new tab's open request hangs\n // indefinitely on `blocked`. Closing here releases the lock; the\n // application layer can listen for `db.onclose` to surface a\n // \"please reload\" toast if it cares.\n db.onversionchange = () => {\n db.close();\n };\n resolve(db);\n };\n });\n}\n","import type { RuntimeDriverDescriptor } from \"@prisma/orm-framework/components/execution\";\nimport { idbDriverDescriptorMeta } from \"../core/descriptor-meta\";\nimport { IdbRuntimeDriverInstance } from \"../core/idb-driver\";\n\nexport type {\n IdbPlanBody,\n IdbAtomicPlan,\n IdbBatchPlan,\n IdbAddPlan,\n IdbCursorScanPlan,\n IdbCountPlan,\n IdbKeysPlan,\n IdbKeyGetPlan,\n IdbIndexGetPlan,\n IdbPutPlan,\n IdbUpdatePlan,\n IdbDeletePlan,\n IdbScanWritePlan,\n IdbRowFilter,\n IdbRowComparator,\n IdbMarkerRecord,\n} from \"../core/plan-body\";\nexport { MARKER_STORE_NAME } from \"../core/plan-body\";\nexport type { IdbRuntimeDriverInstance } from \"../core/idb-driver\";\nexport { IdbExecuteError } from \"../core/execute/error\";\nexport type { IdbExecuteErrorCode } from \"../core/execute/error\";\nexport type { IdbTransactionScope } from \"../core/transaction-scope\";\nexport { createTransactionScope } from \"../core/transaction-scope\";\n\n/**\n * Creates a runtime driver descriptor for IndexedDB.\n *\n * The returned descriptor's `create()` starts opening the named IDB database\n * in the background; the driver awaits the connection before its first query.\n *\n * @param dbName - The IDB database name to open.\n * @param version - The IDB version number. When omitted, IndexedDB opens the\n * database at its current version (version 1 for a new database). The\n * driver never runs migrations; `openAndUpgrade` (target-idb) bumps the version.\n *\n * @example\n * ```ts\n * const stack = createRuntimeStack({\n * target: idbRuntimeTargetDescriptor,\n * adapter: idbRuntimeAdapterDescriptor,\n * driver: createIDBRuntimeDriver(\"my-app\"),\n * });\n * ```\n */\nexport function createIDBRuntimeDriver(\n dbName: string,\n version?: number,\n options?: { readonly factory?: IDBFactory }\n): RuntimeDriverDescriptor<\"idb\", \"idb\", void, IdbRuntimeDriverInstance> {\n return {\n ...idbDriverDescriptorMeta,\n create(): IdbRuntimeDriverInstance {\n return new IdbRuntimeDriverInstance(dbName, version, options?.factory);\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-idb/driver-idb",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Prisma 8 IDB family — driver (wraps window.indexedDB for the execution stack)",
5
5
  "license": "MIT",
6
6
  "files": [