@prisma-idb/driver-idb 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime.d.mts +1 -0
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -0
- package/dist/runtime.mjs.map +1 -1
- package/package.json +1 -1
package/dist/runtime.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { ExecutionPlan } from "@prisma/orm-framework/components/runtime";
|
|
|
3
3
|
//#region src/core/plan-body.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Name of the object store that holds the contract marker.
|
|
6
|
+
* The client-idb marker compatibility test checks the migration writer and this reader.
|
|
6
7
|
*
|
|
7
8
|
* Created during `upgradeneeded` by the migration runner. `IdbRuntime.verifyMarker()`
|
|
8
9
|
* compares its stored hash with the contract's to detect schema drift.
|
package/dist/runtime.d.mts.map
CHANGED
|
@@ -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":"
|
|
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":";;;;;;;;;;;;;cAca;;;;;;;;;;;;;;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;;;KCzPrC,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
|
@@ -370,6 +370,7 @@ function runOpsSequentially(tx, ops, index, rows, onError) {
|
|
|
370
370
|
//#region src/core/plan-body.ts
|
|
371
371
|
/**
|
|
372
372
|
* Name of the object store that holds the contract marker.
|
|
373
|
+
* The client-idb marker compatibility test checks the migration writer and this reader.
|
|
373
374
|
*
|
|
374
375
|
* Created during `upgradeneeded` by the migration runner. `IdbRuntime.verifyMarker()`
|
|
375
376
|
* compares its stored hash with the contract's to detect schema drift.
|
package/dist/runtime.mjs.map
CHANGED
|
@@ -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 {\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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"}
|
|
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 * The client-idb marker compatibility test checks the migration writer and this reader.\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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"}
|