browser-sqlite 1.0.0-rc.4 → 1.0.0-rc.5

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/types.ts","../src/capabilities.ts","../src/errors.ts","../src/locks.ts","../src/utils.ts","../src/bulk.ts","../src/epochs.ts","../src/pool.ts","../src/queries.ts","../src/transaction.ts","../src/client.ts","../src/scheduler.ts","../src/logger.ts","../src/debug.ts","../src/supervisor.ts","../src/delete.ts"],"sourcesContent":["export type SQLiteWorkerMessageData<_T = unknown> = {\n callId: number;\n terminate?: boolean;\n} & (\n | SQLWorkerResultData[keyof SQLWorkerResultData]\n | { type: 'error'; message: string }\n);\n\nexport type SQLWorkerResultData<T = unknown> = {\n open: { success: boolean };\n sql: { type: 'partial'; result: T[] } | { type: 'one'; sizes: number[] };\n abort: { type: 'done' };\n};\n\nexport const SharedArrayTypes = {\n INT: 0,\n STRING: 1,\n OBJECT: 2,\n};\n\ntype SQLOptions = {\n chunkSize?: number;\n /** Chunks the worker may send before waiting for a credit. Spec §3.2. */\n credits?: number;\n};\n\n/**\n * Where a worker fetches its `.wasm` from, when the consumer overrode it.\n *\n * Discriminated rather than a single string because the two forms differ in\n * what they leave to the Emscripten glue. `base` is a directory: the glue\n * supplies the file name (`locateFile('wa-sqlite-async.wasm')`), so nothing\n * here names the three builds' files — and nothing has to be renamed when\n * wa-sqlite renames one. `file` is the whole URL, typically content-hashed by\n * a bundler, so the glue's file name is discarded.\n *\n * Always absolute: `resolveWasmLocation` (`src/utils.ts`) resolves against the\n * page before the `open` message is posted, so the worker applies it without\n * knowing what it was relative to.\n */\nexport type WasmLocation = { base: string } | { file: string };\n\nexport type ClientMessageData =\n | {\n type: 'open';\n file: string;\n vfs: SQLiteVFS;\n build?: SQLiteBuild;\n pragmas?: Record<string, string>;\n /** Statements retained per worker; see `src/client.ts`. Internal. */\n statementCacheSize?: number;\n wasm?: WasmLocation;\n }\n | {\n type: 'query';\n callId: number;\n sql: string;\n params: unknown[];\n options?: SQLOptions;\n }\n | { type: 'close'; callId: number }\n | { type: 'credit'; callId: number; n: number }\n | { type: 'stop'; callId: number }\n | {\n type: 'delete';\n callId: number;\n file: string;\n vfs: SQLiteVFS;\n build?: SQLiteBuild;\n wasm?: WasmLocation;\n };\n\nexport type WorkerMessageData =\n | { type: 'ready'; callId: number }\n | { type: 'chunk'; callId: number; data: unknown[] }\n | {\n type: 'done';\n callId: number;\n affected: number;\n /**\n * Statements compiled while serving this query — zero on a cache hit.\n * Rides the same message as `affected` rather than opening a channel:\n * the effect this instruments is a count, not a duration (`mem:lessons`,\n * \"for a sub-millisecond effect, count the round trips\").\n */\n prepared: number;\n }\n | {\n type: 'error';\n callId: number;\n message: string;\n cause?: unknown;\n /** SQLite's numeric result code, when the failure came from SQLite. */\n sqliteCode?: number;\n }\n | { type: 'closed'; callId: number }\n | { type: 'deleted'; callId: number }\n | {\n type: 'open-error';\n callId: number;\n message: string;\n cause?: unknown;\n /** SQLite's numeric result code, when the failure came from SQLite. */\n sqliteCode?: number;\n };\n\n/** Which wa-sqlite WebAssembly build a worker loads. */\nexport type SQLiteBuild = 'sync' | 'async' | 'jspi';\n\n/**\n * What each build needs from the engine beyond plain WebAssembly.\n *\n * `satisfies Record<SQLiteBuild, …>` and not `SQLiteBuild = keyof typeof …`:\n * the check must run in this direction. Adding a build to the union then fails\n * to compile until its requirements are declared, where `keyof` would let a\n * forgotten entry mean silently that the build does not exist. `VFS_CAPABILITIES`\n * derives `SQLiteVFS` from its keys because it *is* the VFS registry; the build\n * registry is `WA_SQLITE_BUILDS` in the worker, and this table describes one\n * attribute of builds rather than the builds themselves.\n */\nexport const BUILD_REQUIREMENTS = {\n sync: [],\n async: [],\n jspi: ['jspi'],\n} as const satisfies Record<SQLiteBuild, readonly PlatformFeature[]>;\n\n/**\n * A platform feature a VFS may need. Which browser versions ship each one is\n * documentation data, not runtime data, so it lives in the README generator\n * (`scripts/render-vfs-matrix.ts`) with its sources — not here, where it would\n * ship to every consumer for nothing.\n */\nexport type PlatformFeature =\n | 'opfs'\n | 'readwrite-unsafe'\n | 'jspi'\n | 'writable-stream';\n\n/** Where a VFS keeps the database. */\nexport type VFSStorage = 'opfs' | 'indexeddb' | 'memory';\n\n/**\n * How a VFS arranges a database in its storage — which is not the same\n * question as `storage`, and cannot be derived from it: `AccessHandlePoolVFS`\n * is `storage: 'opfs'` yet keeps opaque, randomly named slot files whose\n * association with a SQLite path lives in a header inside each file.\n *\n * `deleteDatabase` reads this to decide whether the database is also an OPFS\n * entry it can remove by name after `jDelete` — the pass that covers the two\n * VFS whose `jDelete` does not delete. A wrong value here is a deletion that\n * reports success over an intact file.\n */\nexport type VFSLayout = 'opfs-path' | 'opfs-pool' | 'idb-store' | 'memory';\n\n/** How much of the database a VFS keeps resident in RAM. */\nexport type VFSMemoryModel = 'page-cache' | 'whole-database';\n\n/** What a VFS can and cannot do. One entry per VFS, and no second table. */\nexport type VFSCapability = {\n /** Builds this VFS can run on, most preferred first. */\n readonly builds: readonly [SQLiteBuild, ...SQLiteBuild[]];\n /** Largest pool this VFS supports; `null` when unbounded. */\n readonly maxPoolSize: number | null;\n /** Why the cap exists. Required whenever `maxPoolSize` is not null. */\n readonly poolLimitReason: string | null;\n /** Whether several connections may share one database. */\n readonly multiConnection: boolean;\n /** Whether data outlives `close()`. */\n readonly persistent: boolean;\n /**\n * `page-cache`: only SQLite's page cache is resident, bounded by\n * `PRAGMA cache_size`. `whole-database`: the entire database is resident,\n * and `poolSize` multiplies it.\n */\n readonly memoryModel: VFSMemoryModel;\n /** Where the database actually lives. */\n readonly storage: VFSStorage;\n /** How the database is arranged within that storage. */\n readonly layout: VFSLayout;\n /**\n * Platform features without which this VFS cannot work at all.\n *\n * `readwrite-unsafe` is the one that bites: WebIDL ignores the unknown\n * dictionary member on engines that do not implement it, so the handle\n * silently opens exclusive and the second connection hangs rather than\n * failing. Declaring it is what lets the conformance suite probe for it and\n * skip, instead of leaving it to surface as a 60-second timeout.\n */\n readonly requires: readonly PlatformFeature[];\n /**\n * Platform features this VFS uses when present and works without, at a cost.\n *\n * `OPFSAdaptiveVFS` is the case this field exists for. Without\n * `readwrite-unsafe` it rotates a single exclusive access handle between\n * connections instead of holding one each. That works — 102 of 104 browser\n * tests pass on Firefox — but it serializes the whole pool for the duration\n * of a long uninterruptible statement.\n *\n * Without this distinction, a support table derived from browser specs would\n * mark that VFS broken everywhere outside Chromium, when it merely degrades.\n */\n readonly degradesWithout: readonly PlatformFeature[];\n};\n\n/**\n * The single source of truth for VFS selection. `SQLiteVFS` is derived from its\n * keys, `worker/worker.ts` must supply a loader for every key, the guards in\n * `client.ts` read it, the conformance suite gates its scenarios on it, and the\n * README table is generated from it. Nothing may hold a second copy.\n *\n * Build order is a decision per VFS, not a rule: `sync` is both the fastest and\n * the most portable build, so it leads wherever supported; `OPFSAdaptiveVFS`\n * cannot use it and leads with `async` because `jspi` is Chromium-only.\n *\n * Every declared build combination is verified by running it against the pinned\n * wa-sqlite v1.1.2, never copied from upstream's table.\n */\nexport const VFS_CAPABILITIES = {\n OPFSAdaptiveVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n requires: ['opfs'],\n degradesWithout: ['readwrite-unsafe'],\n },\n OPFSWriteAheadVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n // Measured on Firefox 2026-08-27, HAS_UNSAFE_HANDLES false: all three\n // build pairs and all six invariants pass, concurrent writes included, at\n // poolSize 1, 2 and 4. `requires` used to name readwrite-unsafe, which made\n // the conformance suite skip the very pairs that would have falsified it.\n // Safari is still unmeasured for this VFS — see `mem:follow-ups`.\n requires: ['opfs'],\n degradesWithout: ['readwrite-unsafe'],\n },\n OPFSCoopSyncVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n requires: ['opfs'],\n degradesWithout: [],\n },\n AccessHandlePoolVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason: 'it cannot share access handles between connections',\n multiConnection: false,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-pool',\n requires: ['opfs'],\n degradesWithout: [],\n },\n IDBBatchAtomicVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'indexeddb',\n layout: 'idb-store',\n requires: [],\n degradesWithout: [],\n },\n IDBMirrorVFS: {\n builds: ['async', 'jspi'],\n // Measured 2026-08-25, not inferred: `CREATE TABLE` → `INSERT` → `SELECT`\n // at poolSize 2, 300 rounds under a loaded suite, failed 5 times — with\n // `no such table` (a connection not seeing a committed statement) and\n // `database is locked`. Nothing at all in 60 rounds unloaded, which is why\n // four sightings over two days never reproduced on demand. See MIRROR-1 in\n // mem:follow-ups for the method.\n //\n // It mirrors the whole database in memory PER WORKER and propagates\n // commits over BroadcastChannel, asynchronously — so a pool holds copies\n // that diverge, the same shape that had OPFSPermutedVFS removed from this\n // library. The commit barrier cannot rescue it: its prelude refreshes page\n // 1 through a real read transaction, and there is nothing fresher to read\n // on a connection whose mirror has not received the broadcast yet.\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages are mirrored per worker and commits propagate asynchronously, so a larger pool reads stale data or fails outright',\n multiConnection: false,\n persistent: true,\n // Upstream: \"keeps all files in memory, persisting database files to\n // IndexedDB\", and the whole database must fit in available memory.\n memoryModel: 'whole-database',\n storage: 'indexeddb',\n layout: 'idb-store',\n requires: [],\n degradesWithout: [],\n },\n OPFSAnyContextVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n requires: ['opfs', 'writable-stream'],\n degradesWithout: [],\n },\n MemoryVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages live in the worker that opened them, so a larger pool would open independent databases that diverge silently',\n multiConnection: false,\n persistent: false,\n memoryModel: 'whole-database',\n storage: 'memory',\n layout: 'memory',\n requires: [],\n degradesWithout: [],\n },\n MemoryAsyncVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages live in the worker that opened them, so a larger pool would open independent databases that diverge silently',\n multiConnection: false,\n persistent: false,\n memoryModel: 'whole-database',\n storage: 'memory',\n layout: 'memory',\n requires: [],\n degradesWithout: [],\n },\n} as const satisfies Record<string, VFSCapability>;\n\nexport type SQLiteVFS = keyof typeof VFS_CAPABILITIES;\n\n/** The build used when the caller does not name one. */\nexport const defaultBuildFor = (vfs: SQLiteVFS): SQLiteBuild =>\n VFS_CAPABILITIES[vfs].builds[0];\n\n/**\n * The VFS this project recommends when a caller has no reason to choose\n * another. It is NOT a default — `vfs` is required, precisely so that the name\n * lives in the consumer's own source and cannot move underneath their data.\n *\n * It lives here, beside the table, because the README generator marks this row\n * `(recommended)` and would otherwise hold a second copy. It is deliberately\n * not exported: a consumer writing `vfs: RECOMMENDED_VFS` would be exposed to\n * the same displacement the day the recommendation changes.\n */\nexport const RECOMMENDED_VFS: SQLiteVFS = 'OPFSAdaptiveVFS';\n","import {\n BUILD_REQUIREMENTS,\n type PlatformFeature,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n} from './types';\n\n/**\n * Synchronous platform probes, keyed by FEATURE rather than by VFS or by build.\n * That is what lets a VFS requirement and a build requirement travel one path.\n *\n * `WebAssembly.Suspending` is cast rather than declared globally: it is not in\n * lib.dom, and a global augmentation would leak the assertion into every file.\n */\nconst PROBES: Partial<Record<PlatformFeature, () => boolean>> = {\n opfs: () =>\n typeof navigator !== 'undefined' &&\n typeof navigator.storage?.getDirectory === 'function' &&\n typeof FileSystemFileHandle !== 'undefined',\n jspi: () =>\n typeof (WebAssembly as { Suspending?: unknown }).Suspending === 'function',\n 'writable-stream': () =>\n typeof FileSystemFileHandle !== 'undefined' &&\n typeof FileSystemFileHandle.prototype.createWritable === 'function',\n};\n\n/**\n * Features with no synchronous probe. Declared, never merely omitted.\n *\n * WebIDL ignores an unknown dictionary member, so asking whether\n * `readwrite-unsafe` is supported answers yes and is wrong. Detecting it means\n * opening two access handles on one file inside a dedicated worker — which the\n * benchmark page does, asynchronously. A feature in neither table is a mistake,\n * and `tests/unit/capabilities.test.ts` says so.\n */\nconst UNPROBEABLE = new Set<PlatformFeature>(['readwrite-unsafe']);\n\n/** Human-readable names for the error messages. */\nconst FEATURE_LABEL: Record<PlatformFeature, string> = {\n opfs: 'OPFS',\n jspi: 'JSPI',\n 'writable-stream': 'FileSystemWritableFileStream',\n 'readwrite-unsafe': 'readwrite-unsafe access handles',\n};\n\n/**\n * Every feature this module can decide: probed, or explicitly exempt. A\n * feature declared in a capability table and absent here is a mistake, and\n * tests/unit/capabilities.test.ts is what says so.\n */\nexport const KNOWN_FEATURES: ReadonlySet<PlatformFeature> = new Set([\n ...(Object.keys(PROBES) as PlatformFeature[]),\n ...UNPROBEABLE,\n]);\n\n/** What this engine can do, probed once by the caller. */\nexport const detectFeatures = (): ReadonlySet<PlatformFeature> => {\n const found = new Set<PlatformFeature>();\n for (const [feature, probe] of Object.entries(PROBES)) {\n if (probe()) found.add(feature as PlatformFeature);\n }\n return found;\n};\n\n/**\n * The first feature this pair needs and this engine lacks, or null.\n *\n * Pure, and takes `available` rather than probing, because the branches worth\n * testing are the negative ones and they are unreachable in a real browser:\n * JSPI cannot be taken away from Chromium.\n */\nexport const missingFeature = (\n vfs: SQLiteVFS,\n build: SQLiteBuild,\n available: ReadonlySet<PlatformFeature>,\n): PlatformFeature | null => {\n const required: readonly PlatformFeature[] = [\n ...VFS_CAPABILITIES[vfs].requires,\n ...BUILD_REQUIREMENTS[build],\n ];\n for (const feature of required) {\n if (UNPROBEABLE.has(feature)) continue;\n if (!available.has(feature)) return feature;\n }\n return null;\n};\n\n/**\n * The message for a missing feature, derived from the capability tables so it\n * cannot drift from them. Names an alternative build when the build is at\n * fault, and VFS that do not need the feature when the VFS is.\n */\nexport const describeMissing = (\n vfs: SQLiteVFS,\n build: SQLiteBuild,\n feature: PlatformFeature,\n): string => {\n const label = FEATURE_LABEL[feature];\n\n if (\n (BUILD_REQUIREMENTS[build] as readonly PlatformFeature[]).includes(feature)\n ) {\n const others = VFS_CAPABILITIES[vfs].builds.filter((b) => b !== build);\n const suffix = others.length\n ? ` ${vfs} also runs on: ${others.join(', ')}.`\n : '';\n return `This browser does not support ${label}, which the '${build}' build requires.${suffix}`;\n }\n\n const alternatives = (Object.keys(VFS_CAPABILITIES) as SQLiteVFS[]).filter(\n (name) =>\n !(VFS_CAPABILITIES[name].requires as readonly PlatformFeature[]).includes(\n feature,\n ),\n );\n const suffix = alternatives.length\n ? ` Without it, these store elsewhere: ${alternatives.join(', ')}.`\n : '';\n return `This browser does not support ${label}, which ${vfs} requires.${suffix}`;\n};\n","/**\n * Every failure this library raises on its own behalf. A caller discriminates\n * on `code`, or on `name` — they carry the same value, so `err.name` reads the\n * way `'AbortError'` does on the DOMException an aborted signal throws.\n */\nexport type SQLiteErrorCode =\n | 'NOT_A_READ_QUERY'\n | 'CLIENT_CLOSED'\n | 'WORKER_CRASHED'\n | 'TIMEOUT'\n | 'PROTOCOL_ERROR'\n | 'INVALID_IDENTIFIER'\n | 'INVALID_OPTION'\n | 'INVALID_PRAGMA'\n | 'BULK_WRITE_FAILED'\n | 'BUSY'\n | 'READ_ONLY_TRANSACTION';\n\nexport class SQLiteError extends Error {\n readonly code: SQLiteErrorCode;\n /**\n * SQLite's own numeric result code, present only when the failure came from\n * SQLite rather than from this library. `BUSY` covers both SQLITE_BUSY (5)\n * and SQLITE_LOCKED (6); this is how a caller tells them apart.\n */\n readonly sqliteCode?: number;\n\n constructor(\n code: SQLiteErrorCode,\n message: string,\n options?: { cause?: unknown; sqliteCode?: number },\n ) {\n super(message, options);\n this.code = code;\n this.name = code;\n if (options?.sqliteCode !== undefined) this.sqliteCode = options.sqliteCode;\n }\n}\n\n/**\n * A batch failed. Raised by `bulkWrite().close()` and by `output().close()`.\n *\n * The counters exist because the old behaviour was silent: batches were chained\n * on one shared promise, so after a rejection every later `.then` was skipped —\n * while their rows had already been spliced out of the buffer (B5). A caller now\n * learns how much of its data reached the database.\n */\nexport class SQLiteBulkWriteError extends SQLiteError {\n readonly rowsWritten: number;\n readonly rowsNotWritten: number;\n\n constructor(\n message: string,\n counts: { rowsWritten: number; rowsNotWritten: number },\n options?: { cause?: unknown },\n ) {\n super('BULK_WRITE_FAILED', message, options);\n this.rowsWritten = counts.rowsWritten;\n this.rowsNotWritten = counts.rowsNotWritten;\n }\n}\n","/**\n * A thin wrapper over `navigator.locks`, used by `output()` to make its staging\n * tables collectable across tabs (D3).\n *\n * The staging lock is NOT mutual exclusion — nothing contends for its name. It\n * is a liveness marker: a lock held for as long as a staging table exists is\n * what lets another tab's sweep tell an in-flight table from an orphan. A tab\n * that is killed has its locks released by the browser, so its orphans become\n * collectable immediately, with no timestamp and no grace period.\n */\n\n/** The slice of the Web Locks API this module uses. */\ntype LockManager = {\n request: (\n name: string,\n optionsOrCallback: any,\n callback?: (lock: unknown) => Promise<unknown>,\n ) => Promise<unknown>;\n query: () => Promise<{ held?: { name?: string }[] }>;\n};\n\nexport type Locks = {\n /** False when the Web Locks API is missing; every method then no-ops. */\n readonly available: boolean;\n /** Acquires `name` and resolves with the function that releases it. */\n hold: (name: string) => Promise<() => void>;\n /** Runs `fn` while holding `name` exclusively. */\n withLock: <T>(name: string, fn: () => Promise<T>) => Promise<T>;\n /**\n * Runs `fn` while holding `name`, or skips it entirely when the lock is held\n * elsewhere. Never waits — which is the point: the staging sweep is\n * opportunistic, and awaiting this lock inside an open transaction would\n * hold SQLite's write lock while waiting on a holder that may itself be\n * waiting for that write lock.\n *\n * Resolves `true` if `fn` ran, `false` if it was skipped.\n */\n tryWithLock: (name: string, fn: () => Promise<unknown>) => Promise<boolean>;\n /** Names currently held anywhere in this origin — every tab included. */\n heldNames: () => Promise<string[]>;\n};\n\nconst STAGING_PREFIX = '__bsq_staging_';\n\nexport const stagingTableName = (uuid: string) =>\n `${STAGING_PREFIX}${uuid.replace(/-/g, '_')}`;\n\nexport const isStagingTable = (table: string) =>\n table.startsWith(STAGING_PREFIX);\n\nexport const stagingLockName = (file: string, table: string) =>\n `bsq:staging:${file}:${table}`;\n\nexport const sweepLockName = (file: string) => `bsq:sweep:${file}`;\n\n/** Serializes database opening across the pool — replaces the SAB init mutex. */\nexport const initLockName = (file: string) => `bsq:init:${file}`;\n\n/**\n * Which staging tables no live `output()` is using — pure, so it is driven by\n * Node tests rather than by two browser tabs.\n */\nexport const staleStagingTables = (\n tables: string[],\n heldNames: string[],\n file: string,\n): string[] => {\n const held = new Set(heldNames);\n return tables.filter((table) => !held.has(stagingLockName(file, table)));\n};\n\n/** The no-op Locks value for environments where the Web Locks API is absent. */\nexport const noOpLocks: Locks = {\n available: false,\n hold: async () => () => {},\n withLock: async (_name, fn) => fn(),\n tryWithLock: async (_name, fn) => {\n await fn();\n return true;\n },\n heldNames: async () => [],\n};\n\nexport const createLocks = (\n manager: LockManager | undefined = globalThis.navigator?.locks as\n | LockManager\n | undefined,\n): Locks => {\n if (!manager) return noOpLocks;\n\n return {\n available: true,\n hold: (name) =>\n new Promise<() => void>((resolveReleaser, rejectOuter) => {\n let release!: () => void;\n const held = new Promise<void>((resolveHeld) => {\n release = resolveHeld;\n });\n manager\n .request(name, () => {\n resolveReleaser(release);\n return held;\n })\n .catch(rejectOuter);\n }),\n withLock: <T>(name: string, fn: () => Promise<T>) =>\n manager.request(name, { mode: 'exclusive' }, () => fn()) as Promise<T>,\n tryWithLock: async (name, fn) => {\n let ran = false;\n await manager.request(\n name,\n { mode: 'exclusive', ifAvailable: true },\n async (lock) => {\n // `ifAvailable` hands the callback null instead of waiting.\n if (!lock) return;\n ran = true;\n await fn();\n },\n );\n return ran;\n },\n heldNames: async () => {\n const snapshot = await manager.query();\n return (snapshot.held ?? [])\n .map((lock) => lock.name)\n .filter((name): name is string => typeof name === 'string');\n },\n };\n};\n","import { SQLiteError } from './errors';\nimport type { SQLiteBuild, WasmLocation } from './types';\n\nexport const sqlParams = () => {\n // `unknown`, not `any`: these are SQL bind values and they are never\n // inspected here — only counted, de-duplicated by identity, and handed on.\n // The public query surface has always said `unknown[]`; this was the one\n // place that quietly said less.\n const sqlParamsMap = new Map<unknown, number>();\n const sqlParams: unknown[] = [];\n\n const addParam = (v: unknown) => {\n let paramIndex = sqlParamsMap.get(v);\n if (!paramIndex) {\n paramIndex = sqlParams.length + 1;\n sqlParamsMap.set(v, paramIndex);\n sqlParams.push(v);\n }\n return `?${paramIndex.toString().padStart(3, '0')}`;\n };\n const addParamArray = (values: unknown[]) => {\n return values.map((v) => addParam(v)).join(',');\n };\n return {\n addParam,\n addParamArray,\n params: sqlParams,\n };\n};\n\n/**\n * Every statement SQLite treats as a write, or that must be serialized through\n * the single writer worker. Matched anywhere in the string, not just at the\n * start: the worker executes `;`-separated statements, so a write hiding after\n * a semicolon must still route to the writer.\n */\nconst WRITE_KEYWORDS =\n /\\b(INSERT|REPLACE|UPDATE|DELETE|CREATE|DROP|ALTER|VACUUM|ANALYZE|REINDEX|SAVEPOINT|RELEASE|BEGIN|COMMIT|ROLLBACK|ATTACH|DETACH|PRAGMA)\\b/i;\n\n/**\n * Routing predicate: is this statement provably a read?\n *\n * Two conditions, both required. The statement must OPEN with a read keyword,\n * and it must contain no write keyword anywhere. The first condition alone is\n * not enough — the worker executes `;`-separated statements, so `SELECT 1;\n * DROP TABLE t` opens as a read and is not one. The second alone is not enough\n * either — it would admit any unrecognised statement as a read.\n *\n * The previous blocklist missed VACUUM, ALTER, ANALYZE, REINDEX, SAVEPOINT and\n * a manual BEGIN, so those ran on the read pool: a VACUUM could execute on an\n * arbitrary worker while the writer held an open transaction, bypassing\n * exclusivity one layer above the pool.\n *\n * Misclassification now fails toward the writer — correct, merely slower. A\n * read whose text merely mentions a write keyword (`SELECT 'INSERT'`, or\n * `EXPLAIN INSERT ...`, which never executes) is serialized needlessly. That\n * is the accepted price of never routing a write to the read pool.\n */\n/**\n * A statement that is nothing but a single PRAGMA lookup: no assignment, no\n * argument, nothing after it. Anchoring at `$` is what makes this safe for\n * free — `PRAGMA journal_mode; DROP TABLE t` does not match, and neither does\n * `PRAGMA journal_mode=WAL`, whose `=` breaks the match.\n */\nconst READ_PRAGMA = /^\\s*PRAGMA\\s+(\\w+\\.)?\\w+\\s*;?\\s*$/i;\n\nexport const isReadQuery = (sql: string) =>\n READ_PRAGMA.test(sql) ||\n (/^\\s*(SELECT|EXPLAIN|VALUES|WITH)\\b/i.test(sql) &&\n !WRITE_KEYWORDS.test(sql));\n\nexport const isWriteQuery = (sql: string) => !isReadQuery(sql);\n\n/**\n * Combines two abort signals into one that fires with the reason of whichever\n * source aborted first, plus the `release()` that unsubscribes it.\n *\n * NOT `AbortSignal.any()`. That is Chrome 116 / Firefox 124 / Safari 17.4, far\n * above this library's floor (Chrome 92 / Firefox 95 / Safari 15.4), and\n * adopting it would raise every row of the generated README matrix for every\n * consumer.\n *\n * The common case allocates nothing: with one side absent, or one side already\n * aborted, the surviving signal is returned as itself — no listener, no\n * teardown owed, and the caller sees the original `reason` rather than a copy.\n */\nexport const mergeSignals = (\n a: AbortSignal | undefined,\n b: AbortSignal | undefined,\n): { signal: AbortSignal | undefined; release: () => void } => {\n const noop = () => {};\n if (!a || a === b) return { signal: b, release: noop };\n if (!b) return { signal: a, release: noop };\n if (a.aborted) return { signal: a, release: noop };\n if (b.aborted) return { signal: b, release: noop };\n\n const merged = new AbortController();\n const relay = (source: AbortSignal) => () => merged.abort(source.reason);\n const onA = relay(a);\n const onB = relay(b);\n a.addEventListener('abort', onA, { once: true });\n b.addEventListener('abort', onB, { once: true });\n return {\n signal: merged.signal,\n release: () => {\n a.removeEventListener('abort', onA);\n b.removeEventListener('abort', onB);\n },\n };\n};\n\n/**\n * Routing guard for the read-shaped methods (`read`, `chunk`, `stream`, `first`).\n * Throws before a lease is taken, so a rejected statement costs no pool capacity.\n *\n * A bare read pragma (`PRAGMA journal_mode`) is accepted; a pragma that assigns\n * (`PRAGMA journal_mode=WAL`), takes an argument, or is followed by anything\n * else must go through `write()`.\n */\nexport const assertReadable = (sql: string, method: string): void => {\n if (isReadQuery(sql)) return;\n const keyword = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n throw new SQLiteError(\n 'NOT_A_READ_QUERY',\n `${method}() only accepts statements that are provably reads; \"${keyword}\" must go through write(). ` +\n `Note that a PRAGMA that assigns a value or takes an argument is a write.`,\n );\n};\n\n/**\n * Quotes an SQL identifier so it can never be read as anything but a name.\n *\n * The library interpolates table, column and index names into generated SQL —\n * `bulkWrite`, `output` and their indexes. wa-sqlite's `statements()` executes\n * `;`-separated statements, so an unquoted name is a stacked-query injection\n * (B4). Quoting is what makes `t\"; DROP TABLE users; --` one identifier.\n *\n * Note that quoting preserves case in `sqlite_master`; SQLite still resolves\n * names case-insensitively.\n */\nexport const quoteIdent = (name: string): string => {\n if (!name)\n throw new SQLiteError('INVALID_IDENTIFIER', 'Identifier cannot be empty');\n if (name.includes('\\0'))\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Identifier contains a NUL character: ${JSON.stringify(name)}`,\n );\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n};\n\n/** `INTEGER`, `TEXT`, `VARCHAR(255)`, `DECIMAL(10, 2)` — nothing else. */\nconst COLUMN_TYPE = /^[A-Za-z][A-Za-z0-9 ]*(\\([0-9, ]+\\))?$/;\n\n/**\n * A column type is not an identifier and cannot be quoted — it is an SQL\n * fragment the caller writes. It is validated by shape instead: this is the\n * narrowed, not closed, channel documented in the spec (§1.2).\n */\nexport const assertColumnType = (type: string, column: string): string => {\n const trimmed = type.trim();\n if (!COLUMN_TYPE.test(trimmed))\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Column \"${column}\" declares an unsupported type ${JSON.stringify(type)}. ` +\n `A type must be a word, optionally followed by numeric arguments, e.g. \"INTEGER\" or \"VARCHAR(255)\".`,\n );\n return trimmed;\n};\n\n/**\n * A GENERATED ALWAYS AS expression is caller-authored SQL. It must at least be\n * parenthesised and free of statement separators, so it cannot escape its slot.\n */\nexport const assertGeneratedExpression = (\n expr: string,\n column: string,\n): string => {\n const trimmed = expr.trim();\n if (\n !trimmed.startsWith('(') ||\n !trimmed.endsWith(')') ||\n trimmed.includes(';')\n )\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Column \"${column}\" declares an invalid generated expression ${JSON.stringify(expr)}. ` +\n `It must be parenthesised and contain no \";\", e.g. \"(base * 2)\".`,\n );\n return trimmed;\n};\n\nconst PRAGMA_NAME = /^[A-Za-z_]\\w*$/;\nconst PRAGMA_INTEGER = /^-?\\d+$/;\nconst PRAGMA_LITERAL = /^'([^']|'')*'$/;\n\n/**\n * Renders the client's `pragmas` option into executable statements, rejecting\n * anything that is not provably a name and a scalar value (B4).\n *\n * Validation is syntactic rather than a closed list of the ~60 SQLite pragmas:\n * a fixed list makes every legitimate pragma outside it unreachable and drifts\n * with SQLite versions, for no additional protection — no \";\", no parenthesis\n * and no comment marker survives these three shapes either.\n *\n * Called twice: by the client at construction, so a bad configuration fails at\n * `createSQLiteClient()` rather than inside an unrelated query, and by the\n * worker at open, which is the only place the statements actually run.\n */\nexport const renderPragmas = (pragmas: Record<string, string>): string[] =>\n Object.entries(pragmas).map(([key, value]) => {\n if (!PRAGMA_NAME.test(key))\n throw new SQLiteError(\n 'INVALID_PRAGMA',\n `Invalid pragma name ${JSON.stringify(key)}: a pragma name must match ${PRAGMA_NAME}.`,\n );\n const raw = String(value).trim();\n if (PRAGMA_INTEGER.test(raw) || PRAGMA_NAME.test(raw))\n return `PRAGMA ${key}=${raw}`;\n // PRAGMA_LITERAL already guarantees raw is a well-formed SQLite string\n // literal (outer quotes present, internal single quotes doubled per '').\n // Using raw directly is correct and simpler than re-escaping the content.\n if (PRAGMA_LITERAL.test(raw)) return `PRAGMA ${key}=${raw}`;\n throw new SQLiteError(\n 'INVALID_PRAGMA',\n `Invalid value ${JSON.stringify(value)} for pragma \"${key}\": expected an integer, a bare word such as WAL, or a quoted literal.`,\n );\n });\n\n/**\n * The single definition of database identity — one string used everywhere:\n * the worker open call, the VFS, the epoch registry and every lock name.\n *\n * The form is **relative** (no leading `/`). `URL.pathname` is absolute by\n * construction, so stripping the slash is necessary: SQLite core checks\n * `nPathname + 8 > mxPathname` (64, `node_modules/wa-sqlite/src/VFS.js:10`)\n * before `xOpen`, and a leading `/` costs a character the budget cannot spare —\n * measured at task 1: it broke all 96 browser tests on 56-char names. The\n * strip gives that character back, so a 56-char name that the caller wrote\n * still fits after normalization. The VFS re-parse (`new URL(zName, 'file://')`\n * for four of five; `AccessHandlePoolVFS` via `'file://localhost/'`) produces\n * identical `pathname` whether the open call receives `'data'` or `'/data'`,\n * so the opened OPFS file is the same regardless.\n *\n * Idempotent: the VFS re-parse of an already-normalized name is a no-op.\n */\nexport const normalizeDatabaseFile = (file: string): string =>\n new URL(file, 'file://').pathname.replace(/^\\//, '');\n\n/**\n * Turns the `wasmUrl` client option into the absolute location posted in the\n * `open` message, or `undefined` when the option was not given.\n *\n * `undefined` is the load-bearing case: the worker sets Emscripten's\n * `locateFile` only when it receives a location, and `findWasmBinary` takes its\n * `new URL('wa-sqlite.wasm', import.meta.url)` branch whenever `locateFile` is\n * absent. So an omitted option leaves resolution byte-for-byte as it was\n * before this option existed — which is the entire contract of the escape\n * hatch.\n *\n * Resolution happens **here**, on the client, against the page: what the\n * consumer writes means what it means from the page they wrote it on, not from\n * the worker's own directory one level down. The callback is therefore called\n * once, at client construction, before any worker exists — its result is\n * reused by every worker in the pool and by every restart.\n *\n * A string is a **directory** and gets its missing trailing slash back before\n * resolution: URL resolution treats a last segment without a slash as a\n * document and replaces it, so `'/static/wasm'` would otherwise silently mean\n * `/static/`. A callback names a **file**, so nothing is appended to it.\n *\n * @throws `SQLiteError('INVALID_OPTION')` when the value cannot be parsed as a\n * URL — synchronously, at construction, rather than as an opaque open failure\n * from a worker that could not fetch its module.\n */\nexport const resolveWasmLocation = (\n wasmUrl: string | ((build: SQLiteBuild) => string) | undefined,\n build: SQLiteBuild,\n baseHref: string,\n): WasmLocation | undefined => {\n if (wasmUrl === undefined) return undefined;\n\n const isCallback = typeof wasmUrl === 'function';\n const raw = isCallback ? wasmUrl(build) : wasmUrl;\n const value = isCallback || raw.endsWith('/') ? raw : `${raw}/`;\n\n let href: string;\n try {\n href = new URL(value, baseHref).href;\n } catch {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `wasmUrl could not be parsed as a URL: ${JSON.stringify(raw)}. Pass a directory (relative, absolute, or a full URL), or a callback returning the full URL of one .wasm file.`,\n );\n }\n\n return isCallback ? { file: href } : { base: href };\n};\n","import type {\n Schema,\n SQLiteBulkWriteOptions,\n SQLiteOutputOptions,\n SQLiteOutputRow,\n SQLiteTransactionOptions,\n} from './api';\nimport { SQLiteBulkWriteError } from './errors';\nimport {\n type Locks,\n stagingLockName,\n stagingTableName,\n staleStagingTables,\n sweepLockName,\n} from './locks';\nimport type { Logger } from './logger';\nimport {\n assertColumnType,\n assertGeneratedExpression,\n quoteIdent,\n} from './utils';\n\n// Structural, and deliberately narrower than SQLiteQueryAPI: bulk needs only\n// these three calls, and requiring the full surface would make every unit test\n// build a complete stub to exercise a single INSERT.\n/**\n * The options these three actually pass is a signal and nothing else, so that\n * is what they ask for. `any` here accepted a misspelt option in silence, which\n * is the one thing a narrow type was never meant to buy.\n */\ntype BulkCallOptions = { signal?: AbortSignal | undefined };\n\nexport type WriteFn = (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n) => Promise<{ result: unknown[]; affected: number }>;\n\nexport type ReadFn = (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n) => Promise<unknown[]>;\n\nexport type TransactionFn = <T>(\n callback: (db: {\n write: (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n ) => Promise<{ result: unknown[]; affected: number }>;\n }) => Promise<T>,\n options?: SQLiteTransactionOptions,\n) => Promise<T>;\n\n/**\n * How long the best-effort staging DROP may wait for a worker before the\n * caller is let go. Not an option: a caller has nothing useful to tune here,\n * and the consequence of expiry is a table the sweep already collects.\n */\nconst DROP_STAGING_TIMEOUT = 5_000;\n\n/**\n * Returned by every `enqueue()` that does not have to wait. Shared rather than\n * created per call: the hot path allocates nothing.\n */\nconst ADMITTED = Promise.resolve();\n\n/** A promise and the handle that resolves it. */\nconst makeRoom = (): { promise: Promise<void>; resolve: () => void } => {\n let resolve!: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n return { promise, resolve };\n};\n\nexport const createBulk = (shared: {\n file: string;\n locks: Locks;\n logger: Logger;\n maxVariables?: number;\n}) => {\n const { file, locks, maxVariables = 32766, logger } = shared;\n\n // Net 2 of the three-net cleanup: orphans left by a closed tab or a crashed\n // session.\n //\n // It runs at the FIRST output() of this client, never at open(). The writer is\n // only designated lazily, on the first write, so a sweep at open would race\n // the n workers. That is the argument against making it eager to make the\n // first output() faster — an attractive idea that the two-stage split does\n // NOT rule out on its own.\n //\n // The memo lives HERE rather than in forTarget on purpose: a transaction\n // builds its own target, so a per-target memo would sweep on every\n // tx.output() instead of once per client.\n let swept: Promise<void> | undefined;\n\n return (target: {\n read: ReadFn;\n write: WriteFn;\n transaction: TransactionFn;\n }) => {\n const { read, write, transaction } = target;\n\n // bulkWrite, sweepOnce, indexStatements and output move in here VERBATIM.\n // Not one character of their bodies changes: they already read `read`,\n // `write`, `transaction`, `file`, `locks`, `logger` and `maxVariables` as\n // free variables, and all seven are still in scope. This task is a\n // relocation; any behavioural edit smuggled into it is a defect.\n\n /**\n * Creates a bulk write utility for efficiently inserting many rows.\n * Automatically batches inserts to stay within SQLite variable limits.\n *\n * @param table - Table name to insert into\n * @param keys - Column names for the insert\n * @returns Object with enqueue() to add rows and close() to flush remaining\n */\n const bulkWrite = <KEYS extends string>(\n table: string,\n keys: KEYS[],\n options?: SQLiteBulkWriteOptions,\n /** Internal: awaited before the first batch. `output()` passes its staging DDL. */\n before?: Promise<unknown>,\n ) => {\n const signal = options?.signal;\n const maxBufferSize = Math.floor(maxVariables / keys.length);\n // Two batches' worth by default: the batch is the unit that gets queued,\n // so anything smaller than one is meaningless and two is the smallest\n // window that lets a batch settle while another is being filled. Derived\n // rather than fixed because the same row count means a different number\n // of INSERTs on a wide table than on a narrow one. It bounds ROWS — what\n // they weigh is the caller's business, and `queueSize` is theirs to set.\n // Raised to 1 rather than trusted: a flush always queues at least one\n // row, so anything lower can never be satisfied and would park the\n // producer for ever. This is the one place an explicit value is not taken\n // as given — the spec's \"no clamping\" was about a value too HIGH, whose\n // worst case is the behaviour that predates this option.\n const queueSize = Math.max(1, options?.queueSize ?? 2 * maxBufferSize);\n\n const buffer: { [K in KEYS]: any }[] = [];\n\n let writePromise = Promise.resolve<number>(0);\n let failure: unknown;\n let closed = false;\n let rowsWritten = 0;\n let rowsNotWritten = 0;\n /** Rows handed to a batch that has not settled yet. */\n let queuedRows = 0;\n /** Shared by every enqueue() parked while the queue is full. */\n let room: { promise: Promise<void>; resolve: () => void } | undefined;\n\n const releaseRoom = () => {\n room?.resolve();\n room = undefined;\n };\n\n // The abort must release a producer parked on enqueue(): the batch it\n // waits for may never settle — the pool can stay empty on a VFS that\n // rotates one exclusive handle — and the release is what lets its next\n // enqueue() throw signal.reason. Removed by close(), so a signal the\n // caller keeps does not collect one listener per writer.\n signal?.addEventListener('abort', releaseRoom, { once: true });\n\n const fail = (): SQLiteBulkWriteError =>\n new SQLiteBulkWriteError(\n `bulkWrite into \"${table}\" failed after ${rowsWritten} row(s); ${rowsNotWritten} row(s) were not written.`,\n { rowsWritten, rowsNotWritten },\n { cause: failure },\n );\n\n const flush = () => {\n const toInsert = [...buffer];\n buffer.length = 0;\n queuedRows += toInsert.length;\n // The chain never rejects: a rejection here is what used to skip every\n // later `.then()` and drop already-spliced rows without a word (B5).\n const runBatch = async (currentAffected: number) => {\n if (failure) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n // Skips a batch the abort beat to the start, so no round trip is\n // paid for rows that will not be written.\n if (signal?.aborted) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n try {\n if (before) await before;\n // The signal goes DOWN to the write. An earlier version withheld\n // it, reasoning that an aborted batch would be caught below and\n // recorded as `failure`, making close() reject with\n // SQLiteBulkWriteError instead of the caller's reason. The premise\n // was right and the conclusion wrong: the catch is ours, and it\n // tells the two apart.\n //\n // Withholding it cost a hang. A batch already in flight had no way\n // to be rejected, so a write that never settles — OPFSCoopSyncVFS\n // on an engine without `readwrite-unsafe`, waiting on a handle\n // hand-over that never comes — left this chain pending for ever,\n // and close() with it. Observed on macOS Safari 27.0.\n const { affected } = await write(\n `INSERT INTO ${quoteIdent(table)} (${keys.map(quoteIdent).join(',')}) VALUES ${toInsert.map(() => `(${keys.map(() => '?')})`)}`,\n toInsert.flatMap((data) => keys.map((k) => data[k])),\n { signal },\n );\n rowsWritten += toInsert.length;\n return currentAffected + affected;\n } catch (error) {\n // An abort is not a failure. This branch is what keeps close()\n // rejecting with `signal.reason` rather than SQLiteBulkWriteError.\n if (signal?.aborted) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n failure = error;\n // A multi-row INSERT is statement-atomic: nothing of this batch landed.\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n };\n writePromise = writePromise.then(async (currentAffected) => {\n try {\n return await runBatch(currentAffected);\n } finally {\n // Every exit passes here — success, latched failure, and the batch\n // an abort skipped. One missed decrement and enqueue() never\n // resolves again.\n queuedRows -= toInsert.length;\n if (queuedRows < queueSize) releaseRoom();\n }\n });\n };\n\n const failClosed = (): SQLiteBulkWriteError =>\n new SQLiteBulkWriteError(`Bulk writer for \"${table}\" is closed.`, {\n rowsWritten,\n rowsNotWritten,\n });\n\n return {\n enqueue: (data: { [K in KEYS]: any }) => {\n if (closed) throw failClosed();\n // Before the failure guard: an aborted writer is not a failed one,\n // and the caller who aborted wants their own reason back, not a\n // report about rows they stopped caring about.\n signal?.throwIfAborted();\n if (failure) throw fail();\n buffer.push(data);\n if (buffer.length >= maxBufferSize) flush();\n if (queuedRows < queueSize) return ADMITTED;\n // One deferred for every caller while the queue is full: enqueue() is\n // not concurrent-safe today and this does not make it so.\n room ??= makeRoom();\n return room.promise;\n },\n close: async () => {\n if (closed) throw failClosed();\n try {\n if (buffer.length) flush();\n const affected = await writePromise;\n // Ordered ahead of the failure check for the same reason: a batch\n // skipped by the abort is not a batch that failed.\n signal?.throwIfAborted();\n if (failure) throw fail();\n closed = true;\n return affected;\n } finally {\n signal?.removeEventListener('abort', releaseRoom);\n }\n },\n };\n };\n\n const sweepOnce = () => {\n // MANDATORY guard: without the Web Locks API there is no way to tell an\n // in-flight staging table from an orphan, and `heldNames()` returns []. A\n // sweep in that state would drop another tab's live staging table — worse\n // than not sweeping. The sweep is opportunistic; skipping it is correct.\n if (!locks.available) {\n if (swept === undefined) {\n swept = Promise.resolve();\n logger.warn(\n 'navigator.locks is unavailable; skipping the staging sweep',\n );\n }\n return swept;\n }\n\n // tryWithLock, not withLock: awaiting this lock inside an open transaction\n // would hold SQLite's write lock while waiting on a holder that may itself\n // be waiting for that write lock — reachable with two clients in one tab.\n //\n // A refused attempt is memoized deliberately. If the lock was held, another\n // client was sweeping; retrying would put a lock request in front of every\n // output() for nothing.\n swept ??= locks\n .tryWithLock(sweepLockName(file), async () => {\n const rows = await read(\n `SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '__bsq_staging_%'`,\n );\n const tables = rows\n .map((row) => (row as { name?: unknown }).name)\n .filter(\n (name: unknown): name is string => typeof name === 'string',\n );\n if (!tables.length) return;\n const stale = staleStagingTables(\n tables,\n await locks.heldNames(),\n file,\n );\n for (const orphan of stale) {\n await write(`DROP TABLE IF EXISTS ${quoteIdent(orphan)}`);\n }\n })\n .then(() => undefined)\n .catch(() => {\n // A failed sweep must never fail the output() that triggered it.\n });\n return swept;\n };\n\n /** CREATE INDEX statements for the final table, built after the rename. */\n const indexStatements = <SCHEMA extends Schema>(\n table: string,\n options?: SQLiteOutputOptions<SCHEMA>,\n ): string[] => {\n const statements: string[] = [];\n for (const index of options?.indexes ?? []) {\n const columns = Array.isArray(index)\n ? index\n : typeof index === 'object'\n ? 'column' in index\n ? [index.column]\n : index.columns\n : [index];\n const unique =\n !Array.isArray(index) && typeof index === 'object' && !!index.unique;\n if (!columns?.length) continue;\n const names = columns.map(String);\n statements.push(\n `CREATE${unique ? ' UNIQUE' : ''} INDEX IF NOT EXISTS ${quoteIdent(`${table}_${names.join('_')}_${unique ? 'U' : 'IDX'}`)} ON ${quoteIdent(table)}(${names.map(quoteIdent).join(',')})`,\n );\n }\n return statements;\n };\n\n /**\n * Builds a table from scratch and swaps it in atomically — MongoDB's $out.\n *\n * Rows are loaded into __bsq_staging_<uuid> (a normal table in main, never\n * TEMP: a TEMP table lives in the temp database and cannot be renamed across\n * databases, and is invisible to the other pool workers). The final swap is\n * one short transaction: DROP the target, RENAME the staging table onto it,\n * then build the indexes with their final names — SQLite has no\n * ALTER INDEX ... RENAME, so indexes built before the swap would keep the\n * staging name forever (D3).\n *\n * Until close() succeeds the previous table stays intact and fully\n * populated. That is the guarantee output() did not have (B5): it used to\n * DROP and CREATE eagerly, so a failure anywhere in the load left the caller\n * with no table at all.\n */\n const output = <SCHEMA extends Schema>(\n table: string,\n schema: SCHEMA,\n options?: SQLiteOutputOptions<SCHEMA>,\n ) => {\n const staging = stagingTableName(crypto.randomUUID());\n\n const normalizedSchema = Object.entries(schema).map(([k, v]) => {\n const type = assertColumnType(typeof v === 'string' ? v : v.type, k);\n const unique = typeof v === 'object' && !!v.unique;\n const notnull = typeof v === 'object' && !!v.required;\n const generated =\n typeof v === 'object' && v.generated\n ? assertGeneratedExpression(v.generated, k)\n : undefined;\n return { name: k, type, unique, notnull, generated };\n });\n\n // Held for as long as the staging table exists: this is what tells another\n // tab's sweep that the table is in flight and must not be collected.\n const lockHeld = locks.hold(stagingLockName(file, staging));\n\n const createStaging = sweepOnce()\n .then(() =>\n write(`\n\t\t\tCREATE TABLE ${quoteIdent(staging)}(\n\t\t\t\t${normalizedSchema\n .map(({ name, type, unique, notnull, generated }) => {\n return `${quoteIdent(name)} ${type} ${unique ? 'UNIQUE' : ''} ${notnull ? 'NOT NULL' : ''} ${generated ? `GENERATED ALWAYS AS ${generated}` : ''}`;\n })\n .join(',')}\n\t\t\t)`),\n )\n .then(() => undefined);\n\n const { enqueue, close } = bulkWrite(\n staging,\n Object.keys(schema).filter(\n (col) => typeof schema[col] !== 'object' || !schema[col].generated,\n ),\n { signal: options?.signal, queueSize: options?.queueSize },\n createStaging,\n );\n\n const releaseLock = async () => {\n (await lockHeld)();\n };\n\n const dropStaging = () =>\n Promise.race([\n write(`DROP TABLE IF EXISTS ${quoteIdent(staging)}`),\n // Bounded, because this runs on the path whose whole point is to\n // stop quickly. The DROP is a write, so it needs a worker — and\n // after an abort the pool may still be finishing the batch the abort\n // skipped, or be stuck for the reason the caller aborted over.\n // Unbounded, a best-effort cleanup would hold close() open forever.\n //\n // Giving up here is safe by construction: the fallback is an orphan\n // staging table, and releasing the staging lock — which happens\n // AFTER this attempt, deliberately — is what tells another sweep it\n // may collect it.\n new Promise((resolve) => setTimeout(resolve, DROP_STAGING_TIMEOUT)),\n ]).catch(() => {\n // Net 2 (the sweep) collects what this could not.\n });\n\n return {\n enqueue: (data: SQLiteOutputRow<SCHEMA>) => enqueue(data as any),\n\n close: async () => {\n let affected: number;\n try {\n // Ensure the staging table exists even when no rows were enqueued —\n // bulkWrite.close() only awaits createStaging via flush(), and flush()\n // is skipped when the buffer is empty.\n await createStaging;\n affected = await close();\n } catch (error) {\n await dropStaging();\n await releaseLock();\n throw error;\n }\n\n try {\n await transaction(async (tx) => {\n await tx.write(`DROP TABLE IF EXISTS ${quoteIdent(table)}`);\n await tx.write(\n `ALTER TABLE ${quoteIdent(staging)} RENAME TO ${quoteIdent(table)}`,\n );\n for (const statement of indexStatements(table, options)) {\n await tx.write(statement);\n }\n });\n } catch (error) {\n await dropStaging();\n throw error;\n } finally {\n await releaseLock();\n }\n\n return affected;\n },\n };\n };\n\n return { bulkWrite, output };\n };\n};\n","/**\n * The commit epoch: a monotonic integer per database, counting commits\n * performed in this realm. Its absolute value means nothing — only the\n * comparison with a worker's `seen` does.\n *\n * The registry lives in the realm-wide symbol registry rather than in a module\n * variable on purpose. A module singleton is unique only when the bundler\n * loads one copy of the module; `Symbol.for` is unique per realm whatever the\n * bundler did. That is what makes \"two clients in one tab see each other\" true\n * by construction.\n *\n * The `v1` suffix separates incompatible shapes. Bump it ONLY if the shape\n * changes — bumping it per release recreates the fragmentation it prevents.\n */\n\n/**\n * The statement the barrier runs and discards.\n *\n * Measured 2026-08-20 in the forced configuration: 6/6 correct. `SELECT 1`\n * touches no page and is 6/6 stale; `PRAGMA data_version` and\n * `PRAGMA schema_version` are 8/8 stale; so is waiting. Only a statement that\n * opens a real read transaction on the file refreshes the connection's cached\n * page 1 — and it must be a SEPARATE statement, because the one that triggers\n * the refresh still returns the stale result.\n */\nexport const BARRIER_SQL = 'SELECT count(*) FROM sqlite_master';\n\nconst REGISTRY_KEY = Symbol.for('browser-sqlite.epochs.v1');\n\ntype Cell = { value: number };\ntype Registry = Map<string, Cell>;\n\nconst registry = (): Registry => {\n const host = globalThis as unknown as Record<symbol, Registry | undefined>;\n const existing = host[REGISTRY_KEY];\n if (existing) return existing;\n const created: Registry = new Map();\n host[REGISTRY_KEY] = created;\n return created;\n};\n\nexport type Epochs = {\n /** The number of commits observed in this realm for this database. */\n current: () => number;\n /** Records one commit and returns the new epoch. */\n bump: () => number;\n};\n\n/**\n * Handles onto the counter for `file`, which MUST already be normalized by\n * `normalizeDatabaseFile`. Entries are never removed: deleting one would\n * restart the counter at 0, and a worker still alive with `seen = 5` would\n * then read `5 > 0`, believe itself current forever, and serve stale data.\n */\nexport const epochsFor = (file: string): Epochs => {\n const map = registry();\n const existing = map.get(file);\n const cell: Cell = existing ?? { value: 0 };\n if (!existing) map.set(file, cell);\n return {\n current: () => cell.value,\n bump: () => {\n cell.value += 1;\n return cell.value;\n },\n };\n};\n\n/**\n * Where a worker's `seen` lands after the write it just served.\n *\n * `target` is the epoch captured when its lease was granted; `next` is the\n * epoch its own commit produced. Advancing requires both conditions:\n *\n * - `seen === target`: the worker was actually observing from `target` when its\n * lease was granted. If the worker was already behind (`seen < target`), it\n * must not be marked current regardless of what it just committed.\n * - `next === target + 1`: the commit is the immediate successor of `target`.\n * If another client committed during our lease, `next` skipped; our\n * connection never observed that commit and must stay marked behind.\n *\n * Marking a connection current when it is not is the only class of bug this\n * design must make impossible.\n */\nexport const advanceSeen = (\n seen: number,\n target: number,\n next: number,\n): number => (seen === target && next === target + 1 ? next : seen);\n","import { DEFAULT_CREDIT_WINDOW } from './credits';\nimport { SQLiteError } from './errors';\nimport type { Logger } from './logger';\nimport type {\n SQLiteBuild,\n SQLiteVFS,\n WasmLocation,\n WorkerMessageData,\n} from './types';\n\n/**\n * Query execution options forwarded to a pool worker.\n */\nexport type PoolWorkerQueryOptions = {\n chunkSize?: number | undefined;\n credits?: number | undefined;\n /**\n * When true, the query's completion does not call `deps.onServed`. Set for\n * the commit-propagation barrier: it is a synthetic probe, not user work, and\n * must not reset the supervisor's restart counter.\n * `createQueryDebugState` is intentionally NOT suppressed: barrier statements\n * still appear in the debug request tree, and a browser test counts them there\n * to prove the barrier stays conditional.\n */\n noServed?: boolean;\n};\n\n/**\n * A Worker extended with pool-specific properties.\n *\n * Note: no `available` field — availability lives in the Scheduler, not on\n * the worker itself. This makes it impossible to republish a borrowed worker\n * from outside the scheduler (the root cause of B1).\n */\nexport type PoolWorker = Worker & {\n index: number;\n /** Lifecycle label for the debug surface. Replaces the SAB status byte. */\n status: string;\n /**\n * The commit epoch this connection has absorbed. Starts at -1: a worker\n * opens the file — and reads page 1 — BEFORE it enters the pool, and a\n * commit can land in between. At poolSize 2 that is the nominal startup\n * ordering, not a rare race, so a new worker is always treated as behind and\n * pays exactly one barrier statement in its lifetime.\n */\n seen: number;\n /** The epoch captured when the current lease was granted. */\n epochTarget: number;\n query: <T extends Record<string, unknown> = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ) => AsyncGenerator<T[] | number>;\n /**\n * Ask the worker to stop. Also settles a `next()` already in flight, which\n * is what lets the consumer's queued `return()` reach the generator's\n * finally instead of waiting behind a chunk that may be minutes away.\n */\n interrupt: () => void;\n /** Resolves when no query is in flight on this worker. */\n quiesce: () => Promise<void>;\n /** Posts `close`, awaits the `closed` reply, then the caller must terminate. */\n close: () => Promise<void>;\n};\n\nconst STOP = Symbol('stop');\n\n/** SQLITE_BUSY and SQLITE_LOCKED — the two ways a lock conflict reports. */\nconst BUSY_CODES = new Set([5, 6]);\n\n/**\n * Returns a SQLiteError('BUSY', …) when data carries a lock-conflict result\n * code (5 or 6), else undefined. Shared by both the query-error and\n * open-error paths so the BUSY_CODES decision lives in exactly one place.\n */\nexport const busyFromCode = (data: {\n message: string;\n cause?: unknown;\n sqliteCode?: number;\n}): SQLiteError | undefined =>\n data.sqliteCode !== undefined && BUSY_CODES.has(data.sqliteCode)\n ? new SQLiteError('BUSY', data.message, {\n cause: data.cause,\n sqliteCode: data.sqliteCode,\n })\n : undefined;\n\n/**\n * Mints a typed error only for lock conflicts. Every other SQLite failure\n * keeps today's shape — a plain Error carrying SQLite's message — so no\n * existing consumer's error handling changes.\n */\nconst workerError = (data: {\n message: string;\n cause?: unknown;\n sqliteCode?: number;\n}) => busyFromCode(data) ?? new Error(data.message, { cause: data.cause });\n\n/**\n * The single `new Worker(new URL(…))` expression in this package.\n *\n * It must stay one literal, in one place: bundlers find the worker by static\n * analysis of exactly this shape, and a second copy would have them emit a\n * second, untransformed worker bundle. `pool.ts:191` records what that cost\n * when the expression was written a second time for an error message.\n */\nexport const spawnWorker = (name: string): Worker =>\n new Worker(\n /* webpackChunkName: \"browser-sqlite\" */ new URL(\n './worker/worker.js',\n import.meta.url,\n ),\n { name, type: 'module' },\n );\n\n/**\n * Creates a new pool worker and registers it in the pool array.\n * Sets up message routing via callId for query responses.\n *\n * Moved verbatim from `createWorker` in client.ts, with three changes:\n * 1. Closure variables become explicit `deps` parameters.\n * 2. Both `available` assignments are deleted (availability lives in the Scheduler).\n * 3. `worker.available = false/true` in the `query` generator are deleted.\n */\nexport const createPoolWorker = (deps: {\n index: number;\n pool: (PoolWorker | undefined)[];\n clientPrefix: string;\n file: string;\n vfs: SQLiteVFS;\n build: SQLiteBuild;\n /** Already resolved and absolute; relayed to the worker, never read here. */\n wasm?: WasmLocation | undefined;\n pragmas?: Record<string, string> | undefined;\n statementCacheSize?: number | undefined;\n onDeath?: (index: number, error: SQLiteError) => void;\n onServed?: (index: number) => void;\n drainTimeout: number;\n createWorkerDebugState?: ((index: number, name: string) => any) | undefined;\n createQueryDebugState?:\n | ((index: number, sql: string, params?: unknown[]) => any)\n | undefined;\n logger: Logger;\n}): Promise<PoolWorker> => {\n const {\n index,\n pool,\n clientPrefix,\n file,\n vfs,\n build,\n wasm,\n pragmas,\n statementCacheSize,\n } = deps;\n const { createWorkerDebugState, createQueryDebugState, logger } = deps;\n\n const deferredInit = Promise.withResolvers<PoolWorker>();\n\n const workerName = `${clientPrefix} / Worker ${index + 1}`;\n const worker = Object.assign(spawnWorker(workerName) as PoolWorker, {\n index,\n status: 'NEW',\n seen: -1,\n epochTarget: 0,\n });\n pool[index] = worker;\n logger.info(`worker ${index + 1} created`);\n\n const state = createWorkerDebugState?.(index, workerName);\n\n let currentCallId = 0;\n\n // Deferred promise for streaming query results one chunk at a time\n let deferredChunk: PromiseWithResolvers<unknown[] | number> | undefined;\n // Set by the query generator when options.noServed is true; cleared in\n // case 'done' after (possibly) suppressing onServed, and in the generator's\n // finally so a query that fails before 'done' does not leave it set.\n let suppressServed = false;\n\n // Deferred promise resolved when the worker replies 'closed'.\n let deferredClose: PromiseWithResolvers<void> | undefined;\n\n // Resolved while a query is in flight; `quiesce()` is how a lease learns the\n // worker is genuinely idle again.\n let idle: PromiseWithResolvers<void> | undefined;\n let stopRequested: PromiseWithResolvers<typeof STOP> | undefined;\n\n let dead = false;\n let ready = false;\n const deathDeferred = Promise.withResolvers<never>();\n // Nothing awaits this until a query runs; without a sink an early death is an\n // unhandled rejection.\n deathDeferred.promise.catch(() => {});\n\n // Per-query channel for a message that never arrived (onmessageerror). The\n // worker is alive, so the request rejects but the transport stays intact and\n // the generator's finally still stops and drains it.\n let lost: PromiseWithResolvers<never> | undefined;\n\n const die = (error: SQLiteError) => {\n if (dead) return;\n dead = true;\n worker.status = 'DEAD';\n deathDeferred.reject(error);\n deferredInit.reject(error); // no-op once resolved\n deps.onDeath?.(index, error);\n };\n\n worker.onerror = (event) => {\n const errorEvent = event as ErrorEvent;\n const detail =\n typeof event === 'object' && event !== null && 'message' in event\n ? String(errorEvent.message ?? '')\n : '';\n // Chrome leaves ErrorEvent.filename empty for worker script-load failures,\n // so this is usually absent — measured 2026-08-27, and it is why the\n // fallback below is not simply the worker's own URL.\n //\n // Deliberately NOT `new URL('./worker/worker.js', import.meta.url)`: that\n // expression is an asset reference every bundler follows, and Vite emits a\n // second, untransformed copy of the worker for it — 777 KB whose own\n // `new URL('wa-sqlite.wasm', …)` references dangle, and which nothing ever\n // loads. It existed only so this message could name a URL.\n //\n // A bare `import.meta.url` is not an asset reference, so naming where the\n // client itself was loaded from costs nothing, and it points at the\n // directory the worker should have been emitted beside — which is the\n // thing a consumer actually needs to check.\n const failedUrl = errorEvent.filename;\n logger.error(`worker ${index + 1} crashed: ${detail}`);\n die(\n new SQLiteError(\n 'WORKER_CRASHED',\n ready\n ? `Worker ${index + 1} failed: ${detail || 'uncaught error'}`\n : `browser-sqlite could not load its worker${\n failedUrl\n ? ` from ${failedUrl}`\n : `; the client itself was loaded from ${import.meta.url}, and the worker must be emitted beside it`\n }. ` +\n `If the worker URL 404s, your bundler did not emit the worker beside your build output — ` +\n `see the \"Bundler Configuration\" section of the browser-sqlite README. ${detail}`,\n { cause: event },\n ),\n );\n };\n\n worker.addEventListener('messageerror', () => {\n logger.error(`worker ${index + 1} sent an undeserializable message`);\n lost?.reject(\n new SQLiteError(\n 'PROTOCOL_ERROR',\n `Worker ${index + 1} sent a message that could not be deserialized; the request cannot be completed.`,\n ),\n );\n });\n\n // Message handler routes responses by callId\n worker.onmessage = ({ data }: MessageEvent<WorkerMessageData>) => {\n const { callId, type } = data;\n switch (type) {\n case 'ready': {\n if (callId === 0) {\n ready = true;\n worker.status = 'READY';\n if (state) state.initializationTime = Date.now();\n logger.info(`worker ${index + 1} ready`);\n deferredInit.resolve(worker);\n }\n break;\n }\n case 'open-error': {\n if (callId === 0) {\n logger.error(`worker ${index + 1} failed to open: ${data.message}`);\n die(\n busyFromCode(data) ??\n new SQLiteError('WORKER_CRASHED', data.message, {\n cause: data.cause,\n }),\n );\n }\n break;\n }\n case 'closed': {\n if (callId === 0) {\n logger.info(`worker ${index + 1} closed`);\n worker.status = 'CLOSED';\n deferredClose?.resolve();\n }\n break;\n }\n case 'chunk': {\n if (deferredChunk && callId === currentCallId) {\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.firstRowTime ??= Date.now();\n }\n deferredChunk.resolve(data.data);\n deferredChunk = Promise.withResolvers<unknown[] | number>();\n }\n break;\n }\n case 'done': {\n if (deferredChunk && callId === currentCallId) {\n const affected = data.affected;\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.affectedRows = affected;\n state.currentRequest.currentQuery.prepared = data.prepared;\n state.currentRequest.affectedRows += affected;\n state.currentRequest.currentQuery.endTime = Date.now();\n }\n deferredChunk.resolve(affected);\n deferredChunk = undefined;\n if (!suppressServed) deps.onServed?.(index);\n suppressServed = false;\n }\n break;\n }\n case 'error': {\n if (deferredChunk && callId === currentCallId) {\n const error = workerError(data);\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.error = error;\n state.currentRequest.currentQuery.endTime = Date.now();\n }\n deferredChunk.reject(error);\n // Do NOT null deferredChunk here. If the generator is suspended at\n // `yield` when the error arrives, nulling it would cause the while\n // loop to exit normally (silent truncation). Leaving the rejected\n // promise in place ensures the generator throws on its next\n // `await Promise.race([deferredChunk.promise, ...])` call, which\n // propagates the error to the consumer. The generator's `finally`\n // clears deferredChunk unconditionally.\n // Attach a no-op handler to suppress unhandled-rejection warnings:\n // the consumer may be suspended (e.g. in sleep()) when the error\n // arrives, and `await Promise.race` only attaches its handler on\n // the next generator resume, which may be a macrotask away.\n deferredChunk.promise.catch(() => {});\n }\n break;\n }\n case 'deleted': {\n // A connection worker never deletes; this message belongs to the\n // delete-worker path handled in src/delete.ts and cannot arrive here.\n break;\n }\n default: {\n const _unexpected: never = data;\n throw new Error(\n `Unhandled worker message: ${JSON.stringify(_unexpected)}`,\n );\n }\n }\n };\n\n /**\n * Generator function that executes a query and streams results.\n * Manages the deferredChunk protocol and abort signals.\n */\n const query = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ): AsyncGenerator<T[] | number> {\n try {\n if (deferredChunk) {\n console.error(`Previous query not finished on worker ${index + 1}`);\n throw new Error('Worker is already processing a query');\n }\n\n if (state?.currentRequest) {\n const queryState = createQueryDebugState?.(index, sql, params);\n state.currentRequest.currentQuery = queryState;\n }\n\n // Extract query options\n const {\n chunkSize = 500,\n credits = DEFAULT_CREDIT_WINDOW,\n noServed = false,\n } = options ?? {};\n suppressServed = noServed;\n\n // Prepare for streaming chunks\n deferredChunk = Promise.withResolvers<unknown[] | number>();\n lost = Promise.withResolvers<never>();\n lost.promise.catch(() => {});\n idle = Promise.withResolvers<void>();\n stopRequested = Promise.withResolvers<typeof STOP>();\n\n // Send query to worker with options\n worker.postMessage({\n type: 'query',\n callId: ++currentCallId,\n sql,\n params,\n options: { chunkSize, credits },\n });\n worker.status = 'RUNNING';\n\n // Stream chunks until query completes\n while (deferredChunk) {\n const chunk = await Promise.race([\n deferredChunk.promise,\n stopRequested.promise,\n lost.promise,\n deathDeferred.promise,\n ]);\n if (chunk === STOP) break;\n yield chunk as T[] | number;\n // Spec §3.3: the credit is issued once the CONSUMER has taken the\n // chunk. Crediting on arrival would let the worker run at full speed\n // and pile the chunks up in the message queue, which is the guarantee\n // this whole mechanism exists to make true.\n if (typeof chunk !== 'number') {\n worker.postMessage({ type: 'credit', callId: currentCallId, n: 1 });\n }\n }\n } finally {\n // If the consumer left early (break / return / throw) the worker is still\n // stepping rows. Tell it to stop, then wait for the reply it always sends,\n // so the worker is genuinely idle before the lease goes back to the pool.\n // Without this wait, the second half of B1 stands: a released worker still\n // inside sqlite.step().\n if (deferredChunk && !dead) {\n worker.status = 'ABORTING';\n // Spec §5.1: the worker may be parked waiting for a credit that this\n // unwinding client will never send. The flag above cannot reach it\n // there — only a message can.\n worker.postMessage({ type: 'stop', callId: currentCallId });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const expiry = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new SQLiteError(\n 'WORKER_CRASHED',\n `Worker ${index + 1} did not answer the stop request within ${deps.drainTimeout} ms; presumed dead.`,\n ),\n ),\n deps.drainTimeout,\n );\n });\n try {\n while (deferredChunk) {\n await Promise.race([deferredChunk.promise, expiry]);\n }\n } catch (error) {\n // A timeout is our own verdict and must be acted on. Any other error\n // is the worker reporting a failure while winding down; the caller is\n // already unwinding and surfacing it here would mask their reason.\n if (error instanceof SQLiteError && error.code === 'WORKER_CRASHED') {\n die(error);\n }\n } finally {\n clearTimeout(timer);\n }\n }\n deferredChunk = undefined;\n lost = undefined;\n stopRequested = undefined;\n // Reset in case the query failed before 'done' arrived — prevents\n // leaking noServed=true into the next query on this worker.\n suppressServed = false;\n worker.status = dead ? 'DEAD' : 'READY';\n idle?.resolve();\n idle = undefined;\n }\n };\n\n // Attach query method to worker\n Object.assign(worker, {\n query,\n /**\n * Ask the worker to stop. Also settles a `next()` already in flight, which\n * is what lets the consumer's queued `return()` reach the generator's\n * finally instead of waiting behind a chunk that may be minutes away.\n */\n interrupt: () => {\n stopRequested?.resolve(STOP);\n },\n quiesce: () => idle?.promise ?? Promise.resolve(),\n close: async () => {\n if (!deferredClose) {\n deferredClose = Promise.withResolvers<void>();\n worker.postMessage({ type: 'close', callId: 0 });\n }\n await deferredClose.promise;\n },\n });\n\n // Initialize worker with database file and configuration\n worker.postMessage({\n callId: 0,\n type: 'open',\n file,\n vfs,\n build,\n wasm,\n pragmas,\n statementCacheSize,\n });\n\n return deferredInit.promise;\n};\n","import type { OptionsWithSignal, SQLiteChunkOptions } from './api';\nimport type { PoolWorker } from './pool';\n\n/**\n * Wires an AbortSignal into a promise that rejects the instant the signal\n * fires, and returns a teardown that removes the listener. The rejection sink\n * (`aborted?.catch`) suppresses the unhandled-rejection when the query ends\n * normally and nobody is racing the promise any more.\n *\n * This is the only place in the module that reads an AbortSignal; both\n * `chunk()` and `writeWorker()` delegate here.\n */\nexport const makeAbortRace = (\n signal: AbortSignal | undefined,\n): { aborted: Promise<never> | undefined; teardown: () => void } => {\n if (!signal) return { aborted: undefined, teardown: () => {} };\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n });\n // Nothing consumes this rejection when the query ends normally.\n aborted.catch(() => {});\n return {\n aborted,\n teardown: () => {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n },\n };\n};\n\n/**\n * The single query primitive. Every other read path is a thin derivation, and\n * abort is implemented here exactly once.\n */\nexport const chunk = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteChunkOptions & { credits?: number },\n): AsyncGenerator<T[]> {\n const { signal, chunkSize, credits } = options ?? {};\n\n // B9: addEventListener never fires for a signal that is already aborted.\n if (signal?.aborted) throw signal.reason;\n\n const { aborted, teardown } = makeAbortRace(signal);\n const iterator = worker.query<T>(sql, params, { chunkSize, credits });\n try {\n while (true) {\n // Racing the pending chunk, not testing a flag after it: an ORDER BY\n // sorts entirely inside the first step(), so waiting for a chunk before\n // noticing the abort makes AbortSignal.timeout(n) return minutes late.\n const next = aborted\n ? await Promise.race([iterator.next(), aborted])\n : await iterator.next();\n if (next.done) break;\n // FLK-1: chunks already queued are not delivered once the signal fired.\n if (typeof next.value !== 'number') yield next.value;\n }\n } finally {\n teardown();\n // Start the stop-and-drain, never await it. The caller must not wait for a\n // sort that may still have minutes to run; the lease returns through\n // quiesce() instead. interrupt() first, so the queued return() is not\n // parked behind a next() that will not settle.\n worker.interrupt();\n void iterator.return(undefined).catch(() => {});\n }\n};\n\nexport const streamRows = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteChunkOptions,\n): AsyncGenerator<T> {\n for await (const rows of chunk<T>(worker, sql, params, options)) {\n for (const row of rows) yield row;\n }\n};\n\nexport const readWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteChunkOptions,\n): Promise<T[]> => {\n const result: T[] = [];\n for await (const rows of chunk<T>(worker, sql, params, options)) {\n result.push(...rows);\n }\n return result;\n};\n\n/**\n * First row, then stop. This BREAKS rather than aborting: a break triggers the\n * generator's return path, which runs chunk()'s finally and the transport's\n * stop-and-drain — the same worker-stop routine, reached without an exception.\n * That is why there is no internal AbortController here and no need to tell an\n * internal abort from the caller's.\n */\nexport const firstWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: OptionsWithSignal,\n): Promise<T | undefined> => {\n for await (const rows of chunk<T>(worker, sql, params, {\n ...options,\n chunkSize: 1,\n // Spec §4.1: with the default window of 2 the worker would produce a\n // second row before parking. One credit is the exact one-row bound the\n // JSDoc has always promised.\n credits: 1,\n })) {\n return rows[0];\n }\n return undefined;\n};\n\nexport const writeWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: OptionsWithSignal,\n): Promise<{ result: T[]; affected: number }> => {\n const { signal } = options ?? {};\n\n // B9: addEventListener never fires for a signal that is already aborted.\n if (signal?.aborted) throw signal.reason;\n\n const { aborted, teardown } = makeAbortRace(signal);\n const iterator = worker.query<T>(sql, params, {});\n const result: T[] = [];\n let affected = 0;\n try {\n while (true) {\n // Racing the pending chunk, not testing a flag after it: an ORDER BY\n // sorts entirely inside the first step(), so waiting for a chunk before\n // noticing the abort makes AbortSignal.timeout(n) return minutes late.\n const next = aborted\n ? await Promise.race([iterator.next(), aborted])\n : await iterator.next();\n if (next.done) break;\n // write() is the only caller that needs the affected count, which is why\n // the T[] | number union stays private to this module.\n if (typeof next.value === 'number') affected = next.value;\n else result.push(...next.value);\n }\n } finally {\n teardown();\n // Start the stop-and-drain, never await it. Same pattern as chunk().\n worker.interrupt();\n void iterator.return(undefined).catch(() => {});\n }\n return { result, affected };\n};\n","import type {\n OptionsWithSignal,\n SQLiteChunkOptions,\n SQLiteQueryAPI,\n SQLiteTransactionDB,\n SQLiteTransactionOptions,\n} from './api';\nimport type { ReadFn, TransactionFn, WriteFn } from './bulk';\nimport { SQLiteError } from './errors';\nimport type { PoolWorker } from './pool';\nimport {\n chunk as chunkWorker,\n firstWorker,\n makeAbortRace,\n readWorker,\n streamRows,\n writeWorker,\n} from './queries';\nimport type { Scheduler } from './scheduler';\nimport { isWriteQuery, mergeSignals } from './utils';\n\n// Drains a statement that returns no rows (BEGIN, COMMIT, ROLLBACK) without\n// the chunkSize-1 + break overhead of firstWorker.\nconst exec = async (worker: PoolWorker, sql: string): Promise<void> => {\n await readWorker(worker, sql);\n};\n\n/**\n * Returns the `transaction()` method for a SQLiteDB instance.\n *\n * The returned function acquires exactly one lease for the full lifetime of\n * the transaction. All SQLiteTransactionDB methods call worker-bound derivations\n * directly — never the public API — so no secondary lease acquisition can\n * occur during the callback.\n */\nexport const createTransaction =\n (deps: {\n scheduler: Scheduler<PoolWorker>;\n afterWrite: (worker: PoolWorker) => void;\n /**\n * Called when a connection may still hold an open transaction. The worker\n * is lost rather than repaired: a \"dirty worker\" state is one more\n * state the barrier would have to reason about, while a respawned\n * connection is transaction-free by construction.\n */\n onPoisoned: (index: number, error: SQLiteError) => void;\n /**\n * The client's bulk factory. Called per transaction with the transaction's\n * own read/write and a pass-through `transaction`, so output()'s swap runs\n * on the caller's transaction instead of opening a BEGIN SQLite does not\n * allow.\n */\n bulkFor: (target: {\n read: ReadFn;\n write: WriteFn;\n transaction: TransactionFn;\n }) => {\n bulkWrite: SQLiteQueryAPI['bulkWrite'];\n output: SQLiteQueryAPI['output'];\n };\n }) =>\n async <T = void>(\n callback: (db: SQLiteTransactionDB) => Promise<T>,\n options?: SQLiteTransactionOptions,\n ): Promise<T> => {\n const { readOnly = false, autoCommit = true, signal } = options ?? {};\n // The signal aborts the wait too: without it a transaction could not be\n // abandoned while the pool has nothing to lend, which is a state a VFS\n // rotating one exclusive OPFS handle can stay in indefinitely.\n const lease = await deps.scheduler.acquire(\n readOnly ? 'read' : 'write',\n signal,\n );\n const worker = lease.worker;\n\n const checksql = (sql: string): string => {\n if (readOnly && isWriteQuery(sql))\n throw new SQLiteError(\n 'READ_ONLY_TRANSACTION',\n 'Cannot write in a read-only transaction.',\n );\n return sql;\n };\n\n let done = false;\n // Set only once BEGIN has come back. A ROLLBACK sent to a connection that\n // opened no transaction fails, and that failure would lose a healthy\n // worker through onPoisoned.\n let begun = false;\n\n /**\n * The options a statement runs with: the transaction's signal, merged with\n * the caller's own when they gave one, so either may abort the statement\n * and the reason is always the source's. `release` is owed once the\n * statement has settled — the merge is the only thing here that subscribes\n * to a signal the caller may keep alive far longer than this transaction.\n */\n const withSignal = <O extends { signal?: AbortSignal | undefined }>(\n given: O | undefined,\n ): { options: O; release: () => void } => {\n const { signal: merged, release } = mergeSignals(signal, given?.signal);\n return { options: { ...given, signal: merged } as O, release };\n };\n\n /** Runs `release` when the consumer stops reading, however it stops. */\n const releasing = async function* <R>(\n source: AsyncGenerator<R>,\n release: () => void,\n ): AsyncGenerator<R> {\n try {\n yield* source;\n } finally {\n release();\n }\n };\n\n // Guarded at the call, not at the first flush. bulkWrite buffers, so the\n // failure would otherwise surface once the buffer overflows — and for\n // output() later still, trapped inside the createStaging promise.\n const refuse = (method: string) => (): never => {\n throw new SQLiteError(\n 'READ_ONLY_TRANSACTION',\n `${method}() writes, and this transaction is read-only.`,\n );\n };\n\n const bulk = readOnly\n ? {\n bulkWrite: refuse('bulkWrite') as SQLiteQueryAPI['bulkWrite'],\n output: refuse('output') as SQLiteQueryAPI['output'],\n }\n : deps.bulkFor({\n read: (sql, params, given) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return readWorker(worker, query, params, options).finally(release);\n },\n write: (sql, params, given) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return writeWorker(worker, query, params, options).finally(release);\n },\n // The caller's transaction is already open. No BEGIN, no COMMIT.\n // db is referenced before its const declaration, deliberately: this arrow\n // only runs when output().close() fires, by which point db is assigned.\n // Moving `bulk` below `const db` breaks the literal that consumes it.\n transaction: (fn) => fn(db),\n });\n\n const db: SQLiteTransactionDB = {\n read: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return readWorker<T>(worker, query, params, options).finally(release);\n },\n\n write: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: OptionsWithSignal,\n ) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return writeWorker<T>(worker, query, params, options).finally(release);\n },\n\n chunk: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return releasing(\n chunkWorker<T>(worker, query, params, options),\n release,\n );\n },\n\n stream: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return releasing(\n streamRows<T>(worker, query, params, options),\n release,\n );\n },\n\n first: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: OptionsWithSignal,\n ) => {\n const query = checksql(sql);\n const { options, release } = withSignal(given);\n return firstWorker<T>(worker, query, params, options).finally(release);\n },\n\n bulkWrite: bulk.bulkWrite,\n output: bulk.output,\n\n commit: async () => {\n // The only place a COMMIT is refused, and it covers both callers: the\n // explicit tx.commit() and the auto-commit below. Without it a callback\n // that swallowed its statement's rejection could still commit, and the\n // transaction's own rejection would arrive after the data landed.\n signal?.throwIfAborted();\n await exec(worker, 'COMMIT');\n done = true;\n },\n\n rollback: async () => {\n await exec(worker, 'ROLLBACK');\n done = true;\n },\n };\n\n const { aborted, teardown } = makeAbortRace(signal);\n\n try {\n signal?.throwIfAborted();\n // BEGIN carries no signal, and neither do COMMIT and ROLLBACK. Their\n // completion is what decides whether a rollback is owed: a BEGIN that ran\n // on the worker but rejected on the client would return a connection to\n // the pool holding an open transaction, which is the state onPoisoned\n // exists to prevent. The cost is a window — while BEGIN is in flight the\n // transaction cannot be abandoned, and on a VFS rotating one exclusive\n // handle that wait can be long. The abort lands the moment BEGIN settles.\n await exec(worker, 'BEGIN');\n begun = true;\n // That window, closed: the signal may have fired while BEGIN was in\n // flight, and the transaction is open now. The callback never runs.\n signal?.throwIfAborted();\n\n const running = callback(db);\n // Racing the callback, not only its statements: an abort landing while\n // the callback sits in user code — an await on anything that is not a\n // statement — would otherwise be invisible until it returns, which may be\n // never. The callback is not interrupted, it is abandoned; it cannot\n // reach the worker afterwards because every statement it issues inherits\n // the aborted signal and rejects before the round trip, and the lease\n // returns to the pool only after quiesce().\n running.catch(() => {\n // Nothing consumes this rejection when the abort wins the race.\n });\n const result = aborted\n ? await Promise.race([running, aborted])\n : await running;\n\n if (!done) {\n if (autoCommit) {\n await db.commit();\n } else {\n await db.rollback();\n }\n }\n return result;\n } catch (e) {\n // Only roll back if the transaction is still open. `done` is set after the\n // statement succeeds, so a COMMIT that failed leaves it false and the\n // transaction still active — that case must still roll back.\n if (begun && !done) {\n try {\n await db.rollback();\n } catch {\n // A failed rollback must not replace the caller's error, which is the\n // one that explains what actually went wrong. But the connection may\n // now hold an open transaction, and a read inside one reads that\n // transaction's snapshot — the barrier would refresh nothing and\n // report success. Evict instead of hoping.\n deps.onPoisoned(\n worker.index,\n new SQLiteError(\n 'WORKER_CRASHED',\n `Worker ${worker.index + 1} may hold an open transaction after a failed rollback.`,\n { cause: e },\n ),\n );\n }\n }\n throw e;\n } finally {\n teardown();\n // Same reasoning as write(): before the void, because release is\n // asynchronous. A read-only transaction commits nothing and must not\n // bump.\n if (!readOnly) deps.afterWrite(worker);\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n","import type { OptionsWithSignal, SQLiteChunkOptions, SQLiteDB } from './api';\nimport { createBulk } from './bulk';\nimport {\n describeMissing,\n detectFeatures,\n missingFeature,\n} from './capabilities';\nimport { createClientDebug } from './debug';\nimport { advanceSeen, BARRIER_SQL, epochsFor } from './epochs';\nimport { SQLiteError } from './errors';\nimport { createLocks } from './locks';\nimport { createLogger } from './logger';\nimport { createPoolWorker, type PoolWorker } from './pool';\nimport {\n chunk as chunkWorker,\n firstWorker,\n makeAbortRace,\n readWorker,\n streamRows,\n writeWorker,\n} from './queries';\nimport {\n createScheduler,\n type InternalSQLiteClientOptions,\n type WriterPolicy,\n} from './scheduler';\nimport { createSupervisor } from './supervisor';\nimport { createTransaction } from './transaction';\nimport {\n defaultBuildFor,\n RECOMMENDED_VFS,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n} from './types';\nimport {\n assertReadable,\n normalizeDatabaseFile,\n renderPragmas,\n resolveWasmLocation,\n} from './utils';\n\n/**\n * SQLite client for browser environments using a pool of Web Workers.\n *\n * Features:\n * - Worker pool management for concurrent SQLite operations\n * - Read/write query differentiation with exclusive write access\n * - Streaming results support for large datasets\n * - Transaction support with rollback capability\n */\n\nconst DEFAULT_POOL_SIZE = 2;\n\n/**\n * Statements retained per worker. Not a consumer option (spec §3.2): the\n * value is declared here rather than in the worker so that exposing it later\n * is one options line, not a move.\n */\nconst DEFAULT_STATEMENT_CACHE_SIZE = 32;\n\n/**\n * Configuration options for creating a SQLite client.\n */\nexport type CreateSQLiteClientOptions = {\n /**\n * Database file name within the OPFS origin private file system.\n * Each unique name maps to a distinct SQLite database file.\n * @defaultValue `\"SQLite\"` prefix + auto-incremented client index\n */\n name?: string;\n\n /**\n * Number of Web Workers spawned in the pool at initialization.\n * A larger pool allows more concurrent read operations but increases\n * memory consumption and OPFS file handle usage.\n * Must be `1` when using `AccessHandlePoolVFS` — any larger value throws at construction time.\n * @defaultValue `2`\n */\n poolSize?: number;\n\n /**\n * Which VFS stores the database. Required: a VFS decides *where* the bytes\n * live, and a database written through one VFS is not visible through\n * another. See the README's VFS Selection guide.\n */\n vfs: SQLiteVFS;\n /**\n * Which wa-sqlite WebAssembly build to load. Defaults to the first entry of\n * `VFS_CAPABILITIES[vfs]` — `sync` where the VFS supports it, since it is both the\n * fastest and the most portable, otherwise `async`. `jspi` needs engine\n * support; see the README's Builds section for versions.\n *\n * @throws at construction when the build is not one the chosen VFS supports.\n */\n build?: SQLiteBuild;\n\n /**\n * Where the workers fetch their `.wasm` from. **An escape hatch, not a\n * setting**: omit it and resolution is exactly what it was before this\n * option existed — the file is taken from beside `worker.js`, which is where\n * the package ships it and where every bundler emits it.\n *\n * Reach for it only when the `.wasm` have been separated from `worker.js`:\n * assets moved by hand with no bundler, or a build whose emitted URL is\n * wrong at runtime.\n *\n * A **string is a directory**, resolved against the page — relative\n * (`'wasm/'`), absolute (`'/static/wasm'`) or a full URL. A missing trailing\n * slash is added. The file name comes from wa-sqlite itself, so one base\n * serves whichever `build` is loaded.\n *\n * A **callback names one file** and receives the resolved `build`, for a\n * bundler-emitted asset whose name carries a content hash:\n * ```ts\n * import wasmUrl from 'browser-sqlite/dist/worker/wa-sqlite.wasm?url';\n * createSQLiteClient('app.db', { vfs, wasmUrl: () => wasmUrl });\n * ```\n * It is called once, at construction, and its answer is reused by every\n * worker and every restart.\n *\n * Serving the `.wasm` from another origin has two requirements beyond this\n * option, both enforced by the browser: the response needs CORS\n * (`Access-Control-Allow-Origin`), since the glue fetches it, and it must\n * carry `Content-Type: application/wasm` for streaming compilation.\n *\n * @throws at construction when the value cannot be parsed as a URL.\n */\n wasmUrl?: string | ((build: SQLiteBuild) => string);\n\n /**\n * SQLite PRAGMAs applied to each worker's database connection on open.\n * Keys are PRAGMA names, values are their string representations.\n * Example: `{ journal_mode: 'WAL', synchronous: 'NORMAL' }`.\n * If omitted, no PRAGMAs are applied beyond SQLite defaults.\n */\n pragmas?: Record<string, string>;\n\n /**\n * How many times a worker slot may be restarted after it has died.\n * A slot that never reached readiness is never restarted — an initial\n * failure is deterministic, and restarting only delays the diagnostic.\n * The counter resets once the replacement has actually served a request.\n * @defaultValue `1`\n */\n maxWorkerRestarts?: number;\n\n /**\n * Milliseconds a worker has to post `ready` after its `open` message is sent.\n * On expiry the slot is failed immediately — the most common cause is a\n * database held under an exclusive lock by another tab or client.\n * @defaultValue `30_000`\n */\n openTimeout?: number;\n\n /**\n * Milliseconds the drain loop (in the query generator's `finally`) may run\n * before the worker is presumed dead and the crash path is invoked.\n * @defaultValue `60_000`\n */\n drainTimeout?: number;\n\n /**\n * Turns on the introspection subsystem exposed as `db.debug`, and the\n * lifecycle log. A string is used as the log prefix; `true` falls back to the\n * client prefix (`\"<name> <index>\"`), which already names the workers.\n *\n * @defaultValue undefined — no collection, no output, `db.debug` undefined.\n */\n debug?: string | boolean;\n\n /**\n * Called whenever a worker slot is permanently lost. Receives the slot index,\n * the number of workers still alive after the loss, the requested pool size,\n * and the error that killed the slot.\n *\n * Guaranteed to be called **before** the client is failed when the last slot\n * is lost. Wrapped in try/catch — a throwing callback is reported through\n * `logger.always.warn` and does not break the pool.\n *\n * @defaultValue undefined\n */\n onWorkerLost?: (event: WorkerLostEvent) => void;\n};\n\n/**\n * What `onWorkerLost` receives. Named and exported rather than inlined in the\n * option: a consumer whose handler is a standalone function needs to be able\n * to type its parameter.\n */\nexport type WorkerLostEvent = {\n /** Zero-based index of the lost slot. */\n index: number;\n /** Number of workers still alive after this loss. */\n live: number;\n /** The requested pool size (`poolSize` option). */\n size: number;\n /** The error that killed the worker. */\n cause: SQLiteError;\n};\n\nlet clientCount = 0;\n\n/**\n * Creates a SQLite client backed by a pool of Web Workers, each running\n * a wa-sqlite instance in a dedicated thread.\n *\n * @remarks\n * **Browser requirements:** This client uses OPFS through Web Workers; no\n * special HTTP headers are required and cross-origin isolation is not needed.\n * The default `build` needs no browser opt-in; only `build: 'jspi'` does, and\n * JSPI is Chromium-only — an unrelated constraint, not a header requirement.\n *\n * **Worker pool side effect:** Calling this function immediately spawns\n * `poolSize` Web Worker threads and begins asynchronous database\n * initialization. Workers become queryable once they emit a `ready` message.\n *\n * @param file - SQLite database file name within the OPFS origin.\n * Each distinct name corresponds to a separate database file.\n * @param clientOptions - Pool and VFS configuration. Required: `vfs` has no\n * default, because a VFS decides where the database is written.\n * See {@link CreateSQLiteClientOptions} for field defaults.\n * @returns A {@link SQLiteDB} object providing `read`, `write`, `chunk`,\n * `stream`, `first`, `transaction`, `bulkWrite`, `output`, and `close` methods.\n *\n * @throws {SQLiteError} With code `INVALID_OPTION` when `build` is not one of\n * the builds the chosen `vfs` supports. The message names the supported\n * builds; the pairing is declared once, in `VFS_CAPABILITIES`.\n * @throws {SQLiteError} With code `INVALID_OPTION` when `poolSize` exceeds the\n * `maxPoolSize` the chosen `vfs` declares. The message names the cap and the\n * reason for it; both come from `VFS_CAPABILITIES`.\n *\n * @example\n * ```typescript\n * import { createSQLiteClient } from 'browser-sqlite';\n *\n * const db = createSQLiteClient('myapp.sqlite', {\n * poolSize: 3,\n * vfs: 'OPFSAdaptiveVFS',\n * pragmas: { journal_mode: 'WAL', synchronous: 'NORMAL' },\n * });\n *\n * const users = await db.read<{ id: number; name: string }>(\n * 'SELECT id, name FROM users WHERE active = ?',\n * [1],\n * );\n * ```\n */\nexport const createSQLiteClient = (\n file: string,\n clientOptions: CreateSQLiteClientOptions,\n) => {\n // One definition of database identity for the workers, the VFS, the epoch\n // registry, every lock name and the returned `db.debug.file`.\n const dbFile = normalizeDatabaseFile(file);\n\n // FIRST, before anything reads the options. `clientOptions` is required in\n // the type, but a JavaScript caller can still omit it entirely — and then\n // every access below would throw a bare TypeError naming nothing. The `?.`\n // here is the only one left in this function, and it is load-bearing: it is\n // what turns a missing argument into the error that says what to pass.\n //\n // Required, and thrown for rather than defaulted: a moving default would\n // leave a consumer reading an empty database while their bytes sat in a VFS\n // nothing queries.\n if (!clientOptions?.vfs) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `vfs is required. ${RECOMMENDED_VFS} is the recommended universal choice and was the previous default — pass it to keep reading a database created before this version. Compare VFS in the README's VFS Selection guide, and measure your own targets at https://lalexdotcom.github.io/browser-sqlite/`,\n );\n }\n\n const clientIndex = ++clientCount;\n\n const clientPrefix = `${clientOptions.name ?? 'SQLite'} ${clientIndex}`;\n\n const poolSize = clientOptions.poolSize ?? DEFAULT_POOL_SIZE;\n const pool: (PoolWorker | undefined)[] = [];\n\n const vfs = clientOptions.vfs;\n const build = clientOptions.build ?? defaultBuildFor(vfs);\n\n const capability = VFS_CAPABILITIES[vfs];\n\n // Synchronous: an unsupported combination must fail here and name itself,\n // not surface later as an opaque open-error from a worker that could not\n // instantiate its module.\n if (!(capability.builds as readonly SQLiteBuild[]).includes(build)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} cannot run on the '${build}' build. Supported: ${capability.builds.join(', ')}.`,\n );\n }\n\n // Resolved once, here, and reused by every worker in the pool and by every\n // restart — a callback must not be re-entered per slot. Undefined when the\n // option was not given, which is what leaves the worker's resolution alone.\n const wasm = resolveWasmLocation(clientOptions.wasmUrl, build, location.href);\n\n if (capability.maxPoolSize !== null && poolSize > capability.maxPoolSize) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} does not support pool sizes greater than ${capability.maxPoolSize}: ${capability.poolLimitReason}. Set poolSize: ${capability.maxPoolSize}.`,\n );\n }\n\n // The engine, not the declaration. Without this the mismatch surfaces later\n // as an opaque open-error from a worker that could not instantiate wasm.\n const absent = missingFeature(vfs, build, detectFeatures());\n if (absent) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n describeMissing(vfs, build, absent),\n );\n }\n\n // Fail at construction, not inside the first unrelated query.\n if (clientOptions.pragmas) renderPragmas(clientOptions.pragmas);\n\n // TEST-ONLY, UNSUPPORTED. Read once here, validated, and converted to a\n // typed internal value so no `any` travels further. Absent from the public\n // options type on purpose — see InternalSQLiteClientOptions in scheduler.ts.\n const testWriterPolicy = (clientOptions as InternalSQLiteClientOptions)\n .__unsafeTestWriterPolicy;\n const writerPolicy: WriterPolicy | undefined =\n typeof testWriterPolicy === 'function' ? testWriterPolicy : undefined;\n\n // ---------------------------------------------------------------------------\n // Startup state: the deferred first-settle verdict\n //\n // While inStartup is true the gate is still closed and handleDeath skips\n // supervisor entirely. The verdict (fast-fail or single retry round) fires\n // from the scheduler's onFirstSettle callback. inStartup is cleared in\n // onGateOpen (which fires after the retry round, or immediately when all\n // slots opened without failure).\n // ---------------------------------------------------------------------------\n let inStartup = true;\n let startupFirstError: SQLiteError | undefined;\n // Every startup death is recorded here keyed by slot index, and removed in\n // spawn's .then() when the slot becomes ready (retry round success). What\n // remains when the gate opens are the slots that permanently failed.\n const startupLosses = new Map<number, SQLiteError>();\n\n /**\n * Creates a new pool worker and adds it to the pool.\n * Sets up message routing via callId for query responses.\n */\n const scheduler = createScheduler<PoolWorker>(\n (() => {\n // onFirstSettle and onGateOpen are callbacks that fire asynchronously\n // (after all const declarations in this scope have been initialised), so\n // references to spawn / failClient / supervisor / emitWorkerLost are\n // safe even though those names appear later in the source.\n const onFirstSettle = (result: {\n openedCount: number;\n failedIndices: number[];\n }) => {\n if (result.openedCount === 0) {\n // Total startup failure: every slot failed to open. No retry —\n // when nothing opened the config is wrong and a retry only delays\n // the error.\n //\n // Emit loss for every failed slot before failing the client — the\n // contract requires the callback to fire before the client is failed.\n // The supervisor is not consulted here: no R1 restart or liveness\n // logic applies when nothing opened; the failure is total and\n // permanent.\n //\n // startupFirstError is always set when openedCount === 0 because\n // every settled-failed slot goes through handleDeath, which sets it.\n // The fallback is unreachable but satisfies the linter.\n inStartup = false;\n for (const [index, error] of startupLosses) {\n emitWorkerLost(index, error);\n }\n startupLosses.clear();\n failClient(\n startupFirstError ??\n new SQLiteError(\n 'WORKER_CRASHED',\n 'All workers failed to open the database.',\n ),\n );\n return;\n }\n // One retry round, hardcoded. The count is not an option yet because:\n // the startup contention that motivates this path (exclusive OPFS\n // handle rotating between workers on Firefox) is a transient, bounded\n // race, not a persistent fault. One round is enough to resolve it.\n // Exposing a knob before we have evidence the default is wrong would\n // make the option permanent to remove.\n for (const index of result.failedIndices) {\n scheduler.rearmSlot(index);\n spawn(index);\n }\n // If failedIndices is empty the gate opens immediately (no re-arming).\n // If not, it stays closed until retry slots settle.\n };\n\n const onGateOpen = () => {\n inStartup = false;\n // Report all startup deaths to the supervisor first so liveCount() is\n // correct for post-startup R1 decisions, and collect verdicts.\n let failClientError: SQLiteError | undefined;\n for (const [index, error] of startupLosses) {\n // 'lost', not 'died': the retry round is capped at one, so these\n // slots are not coming back and the consumer is about to be told so.\n // 'died' would return 'restart' for a slot that had opened, leave it\n // revivable, and spend a restart that never happens — the supervisor\n // would then disagree with the `onWorkerLost` we emit below.\n const verdict = supervisor.report(index, 'lost');\n // Honour a 'fail-client' verdict from the supervisor.\n if (verdict === 'fail-client') failClientError ??= error;\n }\n // Emit all losses BEFORE possibly failing the client — the contract\n // requires the callback to fire before the client is failed.\n for (const [index, error] of startupLosses) {\n emitWorkerLost(index, error);\n }\n startupLosses.clear();\n // Also fail the client when the pool is empty even if no verdict was\n // 'fail-client'. This handles the case where supervisor returns\n // 'restart' for an everReady slot (e.g., slot 0 opened in round 1 and\n // died during the retry round) while slot 1's verdict depends on\n // iteration order — the pool check is order-independent.\n if (\n failClientError !== undefined ||\n pool.filter(Boolean).length === 0\n ) {\n failClient(\n failClientError ??\n startupFirstError ??\n new SQLiteError(\n 'WORKER_CRASHED',\n 'All workers failed to open the database.',\n ),\n );\n }\n };\n\n return writerPolicy\n ? {\n canDesignateWriter: writerPolicy,\n poolSize,\n onFirstSettle,\n onGateOpen,\n }\n : { poolSize, onFirstSettle, onGateOpen };\n })(),\n );\n\n const debugOption = clientOptions.debug;\n\n const debugPrefix =\n typeof debugOption === 'string' ? debugOption : clientPrefix;\n\n const logger = createLogger(debugPrefix, !!debugOption);\n\n const clientDebug = debugOption\n ? createClientDebug(\n dbFile,\n pool,\n {\n vfs,\n pragmas: clientOptions.pragmas ?? {},\n name: clientOptions.name ?? 'SQLite',\n },\n () => scheduler.stats(),\n )\n : undefined;\n\n const debug = clientDebug?.state;\n\n const epochs = epochsFor(dbFile);\n\n /**\n * The barrier. Runs on a leased worker, so nothing can interleave a\n * statement between it and the query the lease was taken for — the lease\n * supplies the atomicity of the pair for free.\n *\n * `target` is captured BEFORE the statement: if another client commits while\n * it is in flight, this connection did not observe that commit and must not\n * be credited with it.\n */\n const applyBarrier = async (worker: PoolWorker) => {\n const target = epochs.current();\n worker.epochTarget = target;\n if (worker.seen >= target) return;\n // Drained, not just dispatched: it is the opening AND closing of the read\n // transaction that refreshes page 1. noServed: true prevents the barrier\n // from resetting the supervisor's restart counter — it is a synthetic probe,\n // not user work.\n const barrierIter = worker.query(BARRIER_SQL, undefined, {\n noServed: true,\n });\n while (!(await barrierIter.next()).done) {\n /* discard rows */\n }\n // Only on success — a failed barrier leaves the worker marked behind so\n // the next attempt re-posts it.\n worker.seen = target;\n };\n\n /** Records a commit. Called after the write, before its promise resolves. */\n const afterWrite = (worker: PoolWorker) => {\n worker.seen = advanceSeen(worker.seen, worker.epochTarget, epochs.bump());\n };\n\n /**\n * Debug-stamps the acquisition with request timing. Extracted from\n * acquireInstrumented so the barrier wrapper can cover both paths uniformly.\n */\n const acquireWithDebug = async (\n kind: 'read' | 'write',\n signal?: AbortSignal,\n ) => {\n // Called only when clientDebug is set — cast to NonNullable to avoid the\n // forbidden non-null assertion operator while preserving the correct type.\n const request = (\n clientDebug as NonNullable<typeof clientDebug>\n ).createRequestDebugState();\n const lease = await scheduler.acquire(kind, signal);\n request.assign(lease.worker.index);\n\n return {\n worker: lease.worker,\n release: () => {\n request.state.releaseTime = Date.now();\n lease.release();\n },\n };\n };\n\n /**\n * The single owner of the request level of the debug tree.\n *\n * There are six acquisition sites; instrumenting each is six chances to\n * miss one. This wrapper stamps `acquireTime` (through `assign`) and\n * `releaseTime`, and is a pass-through when debug is off. Nothing outside it\n * calls `scheduler.acquire`. The barrier runs on the acquired lease before\n * the caller sees it — the lease atomically covers the barrier statement and\n * the real query together.\n */\n const acquireInstrumented = async (\n kind: 'read' | 'write',\n signal?: AbortSignal,\n ) => {\n const lease = clientDebug\n ? await acquireWithDebug(kind, signal)\n : await scheduler.acquire(kind, signal);\n try {\n // Raced, not merely passed a signal. `applyBarrier` drains a real query\n // on the worker, and `PoolWorkerQueryOptions` carries no signal — so on\n // a worker that never answers, that loop is unbounded and every method\n // goes through it. Firefox 154 stopped here, on OPFSCoopSyncVFS, after\n // the two earlier abort paths were closed.\n //\n // This is the second and last phase of a call that was not already\n // abortable: `scheduler.acquire` now honours the signal while queued,\n // and the query phase has honoured it since wave 1. Guarding here rather\n // than at each public method is what makes that complete — an await\n // added to this function later is covered without being remembered.\n //\n // The race abandons the WAIT, not the WORK: the barrier statement runs\n // on. The catch below releases through `quiesce()`, which returns the\n // worker only once it is actually idle, so nothing is re-lent mid-flight.\n const { aborted, teardown } = makeAbortRace(signal);\n try {\n const barrier = applyBarrier(lease.worker);\n await (aborted ? Promise.race([barrier, aborted]) : barrier);\n } finally {\n teardown();\n }\n } catch (error) {\n // The caller never received the lease, so its try/finally cannot return\n // the worker. Release on the same path a normal caller would.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n throw error;\n }\n return lease;\n };\n\n /**\n * Executes a read query and returns all results.\n * Automatically acquires and releases a worker from the pool.\n *\n * @remarks\n * **Read-your-own-writes is guaranteed within the tab.** Any read issued after a\n * write resolves — from that client or from any other client in the same tab on\n * the same database — observes it, regardless of pool size. It is not guaranteed\n * across tabs.\n */\n const read = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: OptionsWithSignal,\n ) => {\n assertReadable(sql, 'read');\n const lease = await acquireInstrumented('read', options?.signal);\n try {\n return await readWorker<T>(lease.worker, sql, params, options);\n } finally {\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n /**\n * Executes a query and yields result rows in chunks.\n * The single abort-aware primitive — all other read paths derive from this.\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const chunk = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(sql: string, params?: unknown[], options?: SQLiteChunkOptions) {\n assertReadable(sql, 'chunk');\n const lease = await acquireInstrumented('read', options?.signal);\n try {\n yield* chunkWorker<T>(lease.worker, sql, params, options);\n } finally {\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n /**\n * Executes a query and streams individual rows (flattened from chunks).\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const stream = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(sql: string, params?: unknown[], options?: SQLiteChunkOptions) {\n assertReadable(sql, 'stream');\n const lease = await acquireInstrumented('read', options?.signal);\n try {\n yield* streamRows<T>(lease.worker, sql, params, options);\n } finally {\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n /**\n * Executes a write query and returns results with affected row count.\n * Automatically acquires and releases a worker from the pool.\n */\n const write = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: OptionsWithSignal,\n ) => {\n const lease = await acquireInstrumented('write', options?.signal);\n try {\n return await writeWorker<T>(lease.worker, sql, params, options);\n } finally {\n // Before the void: release is asynchronous, so write() resolves first. A\n // read chained on this promise would otherwise acquire before the\n // increment, observe the old epoch, and skip the barrier — the exact bug\n // being fixed. In `finally`, so a failed write bumps too: that costs a\n // barrier statement, never a wrong read.\n afterWrite(lease.worker);\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n /**\n * Executes a query and returns only the first row.\n * Breaks after the first chunk — no internal AbortController needed.\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const first = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: OptionsWithSignal,\n ) => {\n assertReadable(sql, 'first');\n const lease = await acquireInstrumented('read', options?.signal);\n try {\n return await firstWorker<T>(lease.worker, sql, params, options);\n } finally {\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n const bulkFor = createBulk({ file: dbFile, locks: createLocks(), logger });\n\n const transaction = createTransaction({\n scheduler: { ...scheduler, acquire: acquireInstrumented },\n afterWrite,\n // Wrapped, not passed by reference: handleDeath is declared further down\n // and would be in its temporal dead zone here.\n onPoisoned: (index, error) => handleDeath(index, error),\n bulkFor,\n });\n\n const { bulkWrite, output } = bulkFor({ read, write, transaction });\n\n /** Bounds any settlement that depends on a worker answering. */\n const bounded = async (promise: Promise<unknown>, ms: number) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n promise,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n };\n\n let closing: Promise<void> | undefined;\n\n /**\n * Drains in-flight work, rejects queued work, closes each database\n * connection, then terminates all workers. Bounded by `drainTimeout`.\n * A second call returns the same promise object — runs exactly once.\n */\n const close = (): Promise<void> => {\n if (closing) return closing;\n closing = (async () => {\n logger.info('client closing');\n // Shutting the front door first: queued waiters reject at once and no new\n // work can be acquired while the in-flight work drains.\n const draining = scheduler.shutdown(\n new SQLiteError('CLIENT_CLOSED', 'The SQLite client has been closed.'),\n );\n // A transaction's lease is held by user code, so this wait is bounded like\n // the rest: a callback that never returns must not make close() hang.\n await bounded(draining, drainTimeout);\n await Promise.all(\n pool.map(async (worker) => {\n if (!worker) return;\n await bounded(worker.close(), drainTimeout);\n worker.terminate();\n }),\n );\n pool.length = 0;\n })();\n return closing;\n };\n\n const openTimeout = clientOptions.openTimeout ?? 30_000;\n const drainTimeout = clientOptions.drainTimeout ?? 60_000;\n\n const supervisor = createSupervisor({\n size: poolSize,\n maxWorkerRestarts: clientOptions.maxWorkerRestarts,\n });\n\n let fatal: SQLiteError | undefined;\n\n const failClient = (error: SQLiteError) => {\n fatal ??= error;\n void scheduler.shutdown(fatal);\n for (const dying of pool) dying?.terminate();\n };\n\n const spawn = (index: number) => {\n // The slot holds a worker again from here — not from `ready`. Without this,\n // a restarted slot stays marked dead and the replacement's own death is\n // taken for a duplicate signal about the worker it replaced: no decision\n // comes back, nothing restarts, nothing fails, and the pool is empty and\n // silent for the rest of the client's life.\n supervisor.report(index, 'spawned');\n const timer = setTimeout(() => {\n handleDeath(\n index,\n new SQLiteError(\n 'TIMEOUT',\n `Worker ${index + 1} did not become ready within ${openTimeout} ms. ` +\n `The database may be held under an exclusive lock by another tab or another client.`,\n ),\n );\n }, openTimeout);\n\n void createPoolWorker({\n index,\n pool,\n clientPrefix,\n file: dbFile,\n vfs,\n build,\n wasm,\n pragmas: clientOptions.pragmas,\n statementCacheSize: DEFAULT_STATEMENT_CACHE_SIZE,\n onDeath: handleDeath,\n onServed: (served) => {\n supervisor.report(served, 'served');\n },\n drainTimeout,\n createWorkerDebugState: clientDebug?.createWorkerDebugState,\n createQueryDebugState: clientDebug?.createQueryDebugState,\n logger,\n })\n .then((worker) => {\n supervisor.report(index, 'ready');\n // If this slot was recorded in startupLosses (it failed in a prior\n // round and is now recovering in the retry), remove the record so it\n // is not reported as permanently lost in onGateOpen.\n startupLosses.delete(index);\n scheduler.add(worker);\n })\n .catch(() => {\n // The rejection is the death already reported through onDeath.\n })\n .finally(() => clearTimeout(timer));\n };\n\n /**\n * Permanently loses a worker slot: logs the loss via the always-on channel\n * and calls the onWorkerLost callback (if provided). Must be called BEFORE\n * failClient so the callback sees the event before the client is shut down.\n */\n const emitWorkerLost = (index: number, error: SQLiteError) => {\n // pool[index] is already undefined here (cleared by handleDeath or startup).\n const live = pool.filter(Boolean).length;\n logger.always.warn(\n `worker ${index + 1} lost; pool is now ${live} of ${poolSize}`,\n );\n const cb = clientOptions.onWorkerLost;\n if (cb) {\n try {\n cb({ index, live, size: poolSize, cause: error });\n } catch (cbError) {\n logger.always.warn(\n `onWorkerLost callback threw: ${cbError instanceof Error ? cbError.message : String(cbError)}`,\n );\n }\n }\n };\n\n const handleDeath = (index: number, error: SQLiteError) => {\n // Snapshot inStartup BEFORE scheduler.remove() may trigger onFirstSettle or\n // onGateOpen (both of which can change inStartup synchronously).\n const wasInStartup = inStartup;\n\n if (wasInStartup) {\n // During startup (gate still closed), defer the verdict to onFirstSettle.\n // A slot that fails before all others have settled must not be restarted\n // or marked lost immediately: we cannot yet distinguish \"only this slot\n // is broken\" from \"contention during startup resolved itself for others\".\n //\n // Set startupFirstError BEFORE scheduler.remove() so that onFirstSettle\n // (which fires synchronously inside remove()) reads the correct error.\n startupFirstError ??= error;\n // Record every startup death — not only retry-slot deaths (defect 1).\n // The entry is removed in spawn's .then() when the slot becomes ready,\n // so only permanently lost slots remain by the time onGateOpen fires.\n startupLosses.set(index, error);\n }\n\n // Terminate and clear BEFORE scheduler.remove() so that emitWorkerLost\n // (called from onGateOpen / post-startup path, both inside or after remove)\n // computes the correct live count from pool.\n pool[index]?.terminate();\n pool[index] = undefined;\n\n scheduler.remove(index); // may synchronously trigger onFirstSettle/onGateOpen\n\n if (wasInStartup) return; // startup: handled entirely by the scheduler callbacks\n\n // Post-startup: apply R1 exactly as before — supervisor decides.\n const decision = supervisor.report(index, 'died');\n if (decision === 'restart') {\n logger.warn(`restarting worker ${index + 1}`);\n void spawn(index);\n } else if (decision === 'lost') {\n // Slot permanently lost, but the pool still has workers.\n emitWorkerLost(index, error);\n } else if (decision === 'fail-client') {\n // Last worker gone — emit loss (with live=0) before failing the client.\n emitWorkerLost(index, error);\n failClient(error);\n }\n };\n\n // Initialize the worker pool with the requested number of workers\n for (let index = 0; index < poolSize; index += 1) spawn(index);\n\n // Return the public API\n const api = {\n chunk,\n read,\n write,\n stream,\n first,\n transaction,\n bulkWrite,\n output,\n close,\n\n debug,\n };\n return api;\n};\n","/**\n * Pure worker scheduling: availability, wait queues, writer designation.\n *\n * This module is deliberately free of `Worker` and DOM imports so\n * it can be exercised by fast Node tests. B1 survived for months because the\n * scheduler was only reachable through slow browser tests.\n */\n\nimport type { CreateSQLiteClientOptions } from './client';\n\n/**\n * A borrowed worker. `release()` is the only way back into the pool and is\n * idempotent — a second call is a no-op, not an error.\n */\nexport type Lease<W> = {\n readonly worker: W;\n release: () => void;\n};\n\n/**\n * Decides whether a worker index may hold the write designation. The default\n * accepts every index, so production behaviour is exactly what it was.\n */\nexport type WriterPolicy = (index: number) => boolean;\n\n/**\n * TEST-ONLY, UNSUPPORTED, removable without notice.\n *\n * The barrier's browser test needs the failing configuration — writer not on\n * the worker that serves the read — to be deterministic; at startup chance it\n * occurs ~3 runs in 10. This type is declared here, and NOT in `client.ts`,\n * because `src/index.ts` re-exports only `./client` and `./errors`: keeping it\n * out of that path keeps it out of the published `.d.ts` and out of every\n * consumer's autocompletion. `CreateSQLiteClientOptions` is pulled in with\n * `import type`, which is erased at build time and creates no runtime cycle.\n *\n * A predicate that refuses every index leaves writes queued forever — use it\n * with `poolSize >= 2`.\n */\nexport type InternalSQLiteClientOptions = CreateSQLiteClientOptions & {\n __unsafeTestWriterPolicy?: WriterPolicy;\n};\n\nexport type Scheduler<W> = {\n add: (worker: W) => void;\n /**\n * Leases a worker, queueing when none is free.\n *\n * `signal` aborts the WAIT, and only the wait: it rejects with\n * `signal.reason` while the request is still queued, and is ignored once a\n * lease has been granted — from that point the caller owns the worker and\n * owes a `release()`. Without it an abort could not land at all while the\n * pool had nothing to lend, which is the state a VFS rotating one exclusive\n * OPFS handle can stay in indefinitely.\n */\n acquire: (kind: 'read' | 'write', signal?: AbortSignal) => Promise<Lease<W>>;\n /**\n * Takes a worker out of the pool for good. A lease already outstanding on\n * that index becomes inert: its `release()` neither hands the worker back nor\n * counts towards `shutdown()`'s wait.\n */\n remove: (index: number) => void;\n /**\n * Closes the front door. Queued waiters reject with `reason`, later\n * acquisitions reject the same way, and the returned promise settles when the\n * last outstanding lease has come back.\n */\n shutdown: (reason: Error) => Promise<void>;\n /**\n * Read-only counters for the debug subsystem. The scheduler stays pure: it\n * exposes numbers and knows nothing about debug (spec §3.2).\n */\n stats: () => {\n read: number;\n write: number;\n available: number;\n leased: number;\n /**\n * Callers suspended on the readiness gate. They are in NEITHER wait queue —\n * the gate is awaited before `takeAvailable` is ever reached — so `read`\n * and `write` cannot see them, and without this the debug surface reports\n * an idle pool for the whole startup window.\n *\n * Waiting for the pool to *exist* is a different wait from waiting for a\n * free worker, which is why this is its own counter and not folded in.\n */\n gated: number;\n };\n /**\n * Removes a slot from the settled-set so that its next `add()` or `remove()`\n * call counts again toward opening the readiness gate. Only effective while\n * the gate is still closed; a no-op once the gate has opened.\n *\n * Used by the startup retry round: the client re-arms the failed slots so\n * the gate stays closed until the retry slots have settled.\n */\n rearmSlot: (index: number) => void;\n};\n\n/**\n * Creates a scheduler over workers identified by a numeric `index`.\n *\n * @param opts.onIdle - Called when a released worker returns to the available\n * set with nothing queued behind it. The scheduler itself knows nothing about\n * worker state.\n */\nexport const createScheduler = <W extends { index: number }>(\n opts: {\n onIdle?: (worker: W) => void;\n canDesignateWriter?: WriterPolicy;\n /**\n * Total number of worker slots the pool will spawn. Once every slot has\n * settled (via `add` when it becomes ready, or via `remove` when it dies or\n * fails to open), a one-shot gate is lifted and `acquire()` may proceed.\n * Omit or pass 0 for an immediately-open gate (tests and single-shot use).\n */\n poolSize?: number;\n /**\n * Called exactly once when every slot in [0, poolSize) has settled for the\n * first time. Fires before the gate opens so the callback can call\n * `rearmSlot()` to extend the wait for a retry round.\n *\n * `openedCount` — slots that settled via `add()` (became ready).\n * `failedIndices` — slots that settled via `remove()` (died / timed out).\n */\n onFirstSettle?: (result: {\n openedCount: number;\n failedIndices: number[];\n }) => void;\n /**\n * Called when the readiness gate resolves (opens). Not called when the gate\n * is rejected via `shutdown()`. Use this to clear any startup-pending flag\n * after the retry round (if any) has fully settled.\n */\n onGateOpen?: () => void;\n } = {},\n): Scheduler<W> => {\n const workers: (W | undefined)[] = [];\n\n // Availability lives HERE and nowhere else. No worker carries an `available`\n // flag, so no other module can republish a borrowed worker — which is exactly\n // how B1 happened.\n //\n // A second guarantee rests on this set, and nothing about it is visible from\n // here. A leased worker leaves `available` until `release()` puts it back, so\n // exactly one query is ever in flight per worker. `worker/statement-cache.ts`\n // is built on that and takes no lock of any kind: its statements outlive the\n // query that compiled them, and are reset and cleared on the way out. Lend a\n // worker to a second concurrent caller and that reset lands on a statement\n // another query is part-way through — rewound cursor, cleared bindings, wrong\n // rows — while losing a worker can finalise a handle that other query still\n // holds, which is a use-after-free on a `sqlite3_stmt` pointer. Before the\n // cache, breaking this was merely confusing.\n const available = new Set<number>();\n\n const dead = new Set<number>();\n const leased = new Set<number>();\n // Per-index generation counter. Bumped by remove() so that a release() from\n // a lease created before the remove can detect it is stale and do nothing.\n const generations = new Map<number, number>();\n const gen = (index: number) => generations.get(index) ?? 0;\n\n let shutdownReason: Error | undefined;\n let shutdownDeferred: PromiseWithResolvers<void> | undefined;\n\n // One-shot readiness gate: lifts once every slot in [0, poolSize) has\n // settled — either via add() (ready) or remove() (died / failed to open).\n // poolSize 0 or absent → gate is open from the start.\n //\n // Genuinely one-shot: once gateOpen is true it stays true. A worker that\n // restarts (remove → add) after the gate has lifted must not re-block callers\n // already in flight.\n const settledSlots = new Set<number>();\n let gateOpen = (opts.poolSize ?? 0) === 0;\n const gateDeferred = Promise.withResolvers<void>();\n if (gateOpen) gateDeferred.resolve();\n // Suppress unhandled-rejection when shutdown() fires before any acquire()\n // has attached a handler. Each awaiting acquire() still sees the rejection.\n void gateDeferred.promise.catch(() => {});\n\n // Tracks slots that settled via add() (became ready) in the first round,\n // used to compute openedCount/failedIndices for onFirstSettle.\n const firstSettleOpened = new Set<number>();\n let firstSettleFired = false;\n\n // Callers currently suspended on the gate. See `stats().gated`.\n let gatedWaiters = 0;\n\n const settleGateSlot = (index: number, kind: 'opened' | 'failed') => {\n if (gateOpen || settledSlots.has(index)) return;\n settledSlots.add(index);\n if (kind === 'opened') firstSettleOpened.add(index);\n if (settledSlots.size < (opts.poolSize ?? 0)) return;\n\n // All slots have now settled (first round or retry round).\n if (opts.onFirstSettle && !firstSettleFired) {\n firstSettleFired = true;\n const failedIndices = [...settledSlots].filter(\n (i) => !firstSettleOpened.has(i),\n );\n opts.onFirstSettle({\n openedCount: firstSettleOpened.size,\n failedIndices,\n });\n // After the callback the client may have:\n // (a) called rearmSlot() for retry slots → settledSlots.size < poolSize,\n // gate stays closed; or\n // (b) called shutdown() (opened===0 fast-fail) → shutdownReason is set.\n // In both cases skip the resolve/open below.\n if (settledSlots.size < (opts.poolSize ?? 0) || shutdownReason) return;\n }\n\n gateOpen = true;\n gateDeferred.resolve();\n opts.onGateOpen?.();\n };\n\n const readerQueue: Array<{\n resolve: (worker: W) => void;\n reject: (error: Error) => void;\n }> = [];\n const writerQueue: Array<{\n resolve: (worker: W) => void;\n reject: (error: Error) => void;\n }> = [];\n\n // Index of the worker designated for writes, or -1 when none is designated.\n //\n // The designation exists to serialize writes onto one connection, and it\n // lasts no longer than that: handOver releases it as soon as no write is\n // queued behind it, so `designated` and `leased` coincide. It was sticky for\n // the life of the worker until wave 4's barrier shipped — a write landing on\n // a worker that had not absorbed the previous commit failed at `prepare` with\n // `no such table`. `applyBarrier` covers `kind: 'write'`, so a newly\n // designated writer catches up before it prepares anything.\n //\n // Measured 2026-08-21: with a long read holding worker 0, five writes took\n // 30 ms spread over worker 1 against 934-1052 ms pinned behind the read.\n let currentWriterIndex = -1;\n\n /**\n * The worker that most recently held the write designation, kept after that\n * designation is released. `currentWriterIndex` answers \"who may write now\";\n * this answers \"who has already seen the last commit\", which outlives it.\n */\n let lastWriterIndex = -1;\n\n const canDesignate = opts.canDesignateWriter ?? (() => true);\n\n /**\n * Serves the writer queue from `worker` when it may hold the designation.\n * Extracted because `handOver` and `add` carried this branch twice, and a\n * predicate that lives in only one of the two copies is a silent hole.\n */\n const serveWriterFirst = (worker: W): boolean => {\n if (!writerQueue.length) return false;\n if (currentWriterIndex !== worker.index && currentWriterIndex !== -1)\n return false;\n // An already-designated writer is not re-judged; only a NEW designation is.\n if (currentWriterIndex === -1 && !canDesignate(worker.index)) return false;\n // Claim the designation before serving: without this, a later write\n // acquisition could designate a second writer while this one still runs.\n currentWriterIndex = worker.index;\n lastWriterIndex = worker.index;\n writerQueue.shift()?.resolve(worker);\n return true;\n };\n\n const checkShutdown = () => {\n if (shutdownDeferred && leased.size === 0) shutdownDeferred.resolve();\n };\n\n const handOver = (worker: W) => {\n if (serveWriterFirst(worker)) return;\n\n // Release the designation. Reaching this line proves no write is queued:\n // serveWriterFirst's only negative exit that leaves the designation on this\n // worker is an empty writerQueue. It sits ABOVE the reader branch because\n // that branch returns — the release has to happen on every exit, not only\n // the idle one.\n //\n // Measured, so nobody \"fixes\" it: moving this above serveWriterFirst is\n // behaviourally equivalent in production, since the call reclaims the\n // designation on the same worker at once. The two differ only under a\n // canDesignateWriter that refuses this index — tests only.\n if (currentWriterIndex === worker.index) currentWriterIndex = -1;\n\n if (readerQueue.length) {\n // Reads never alter the designation — rule 1.\n readerQueue.shift()?.resolve(worker);\n return;\n }\n\n available.add(worker.index);\n opts.onIdle?.(worker);\n };\n\n const makeLease = (worker: W): Lease<W> => {\n leased.add(worker.index);\n const myGen = gen(worker.index);\n let released = false;\n return {\n worker,\n release: () => {\n if (released) return;\n released = true;\n if (gen(worker.index) !== myGen) {\n // Stale lease: remove() was called after this lease was created,\n // bumping the generation. Handing the worker back would corrupt the\n // pool (it could be held by a new lease on the revived slot).\n checkShutdown();\n return;\n }\n leased.delete(worker.index);\n handOver(worker);\n checkShutdown();\n },\n };\n };\n\n const takeAvailable = (write: boolean): W | undefined => {\n if (write && currentWriterIndex > -1) {\n if (!available.has(currentWriterIndex)) return undefined;\n available.delete(currentWriterIndex);\n return workers[currentWriterIndex];\n }\n\n // Prefer the worker that wrote last, for a read and for a new designation\n // alike. A read served there skips the barrier, that worker having already\n // seen the commit; a write served there keeps a run of writes on one\n // connection instead of walking the pool between batches.\n //\n // A PREFERENCE, never a pin: it picks only among workers that are already\n // available, so it can never make anything wait. That is what keeps it\n // clear of the measurement above — which was about writes queued BEHIND a\n // busy designated writer, not about which free worker to choose.\n //\n // `workers[-1]` is undefined, so the unset case needs no separate guard.\n const preferred = workers[lastWriterIndex];\n if (\n preferred !== undefined &&\n available.has(lastWriterIndex) &&\n (!write || canDesignate(lastWriterIndex))\n ) {\n available.delete(lastWriterIndex);\n if (write) currentWriterIndex = lastWriterIndex;\n return preferred;\n }\n\n // Lowest-index-first for both reads and new writes (reads never touch\n // the designation; write designation is set below when a new one starts).\n const found = workers.find(\n (worker) =>\n worker !== undefined &&\n available.has(worker.index) &&\n (!write || canDesignate(worker.index)),\n );\n if (!found) return undefined;\n\n available.delete(found.index);\n if (write) {\n currentWriterIndex = found.index;\n lastWriterIndex = found.index;\n }\n return found;\n };\n\n return {\n add: (worker) => {\n // Settle this slot in the gate (first call per index only; restarts are\n // ignored because gateOpen is already true by then).\n settleGateSlot(worker.index, 'opened');\n\n dead.delete(worker.index);\n workers[worker.index] = worker;\n // Serve any requests that arrived before this worker was ready, preserving\n // the same writer-first priority as handOver. Does NOT call onIdle — the\n // worker is newly joining the pool, not returning from a lease.\n if (serveWriterFirst(worker)) return;\n if (readerQueue.length) {\n // Reads never alter the designation — rule 1.\n readerQueue.shift()?.resolve(worker);\n return;\n }\n available.add(worker.index);\n },\n\n remove: (index) => {\n // Settle this slot in the gate — a dead slot counts. First call per\n // index only; a restart after the gate is open is a no-op here.\n settleGateSlot(index, 'failed');\n\n dead.add(index);\n available.delete(index);\n leased.delete(index);\n workers[index] = undefined;\n // Bump the generation so any outstanding lease on this index knows it is\n // stale when its release() eventually fires.\n generations.set(index, gen(index) + 1);\n if (currentWriterIndex === index) currentWriterIndex = -1;\n // A respawned slot is a different connection with a fresh epoch, so the\n // freshness hint this index carried is void.\n if (lastWriterIndex === index) lastWriterIndex = -1;\n checkShutdown();\n },\n\n shutdown: (reason) => {\n // Reject the gate so any caller blocked on it gets the shutdown error.\n if (!gateOpen) {\n gateOpen = true;\n gateDeferred.reject(reason);\n }\n shutdownReason ??= reason;\n shutdownDeferred ??= Promise.withResolvers<void>();\n for (const waiter of readerQueue.splice(0)) waiter.reject(reason);\n for (const waiter of writerQueue.splice(0)) waiter.reject(reason);\n checkShutdown();\n return shutdownDeferred.promise;\n },\n\n stats: () => ({\n read: readerQueue.length,\n write: writerQueue.length,\n available: available.size,\n leased: leased.size,\n gated: gatedWaiters,\n }),\n\n rearmSlot: (index) => {\n if (!gateOpen) settledSlots.delete(index);\n },\n\n acquire: async (kind, signal) => {\n if (shutdownReason) throw shutdownReason;\n // Before the queue, not after: a caller who has already given up must not\n // take a place in line and be served a worker nobody will release.\n signal?.throwIfAborted();\n\n // Readiness gate: block until every slot has settled. The gate is\n // one-shot — once open it never closes, so this branch is never re-entered\n // by callers already in flight after a worker restarts.\n if (!gateOpen) {\n // In a `finally`, so an abort or a shutdown rejection decrements too:\n // a leaked count would make the pool look permanently congested.\n gatedWaiters += 1;\n try {\n // The tie is settled by microtask order, and it settles in favour of\n // the gate: `resolve()` queues its reaction before a synchronous\n // `abort()` queues `abortP`'s, so a caller aborted in the very tick\n // the last slot settles still gets its lease. That is the queue\n // path's behaviour too — `onAbort` there returns early once the\n // waiter has been shifted — so the two agree rather than diverge.\n if (signal) {\n const { promise: abortP, reject: abortReject } =\n Promise.withResolvers<void>();\n const onGateAbort = () => abortReject(signal.reason);\n signal.addEventListener('abort', onGateAbort, { once: true });\n try {\n await Promise.race([gateDeferred.promise, abortP]);\n } finally {\n signal.removeEventListener('abort', onGateAbort);\n }\n } else {\n await gateDeferred.promise;\n }\n } finally {\n gatedWaiters -= 1;\n }\n }\n\n // Re-check after the gate: shutdown() may have fired while we waited\n // (remove() settles the gate synchronously before failClient can run, so\n // the gate resolves a microtask before shutdown() sets shutdownReason).\n if (shutdownReason) throw shutdownReason;\n\n const write = kind === 'write';\n\n const immediate = takeAvailable(write);\n if (immediate) return makeLease(immediate);\n\n const { promise, resolve, reject } = Promise.withResolvers<W>();\n const queue = write ? writerQueue : readerQueue;\n const waiter = { resolve, reject };\n queue.push(waiter);\n\n if (!signal) return makeLease(await promise);\n\n const onAbort = () => {\n const at = queue.indexOf(waiter);\n // The guard is the whole correctness of this branch, in both\n // directions. A waiter still in the queue is REMOVED, never merely\n // rejected in place: the drains take the head with `shift()`, so a\n // dead entry left behind would be handed a worker that nobody then\n // releases. And a waiter already shifted is left alone: its lease is\n // real, the caller owes a release for it, and rejecting here would\n // strand that worker for the life of the client. The in-query abort\n // race in `queries.ts` covers what happens after the lease.\n //\n // Read-then-mutate needs no lock: this is one synchronous block with\n // no await and no yield, and the drains (`handOver`, `serveWriterFirst`)\n // are synchronous too, so nothing can shift this waiter out between the\n // lookup and the removal. `splice` before `reject` for the same reason\n // read the other way — `reject` only schedules a microtask, but the\n // queue is left consistent before anything else can observe it.\n const queued = at !== -1;\n if (!queued) return;\n queue.splice(at, 1);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n try {\n return makeLease(await promise);\n } finally {\n signal.removeEventListener('abort', onAbort);\n }\n },\n };\n};\n","/**\n * The prefixed logger the `debug` option turns on.\n *\n * Lifecycle events only — worker created, ready, open-error, crash, restart,\n * worker loss, close, skipped sweep. A line per query would be illegible under\n * real load and would put user values on the console; query throughput belongs\n * in `db.debug`, not here.\n */\nexport type Logger = {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n /**\n * Always writes through the sink regardless of the `enabled` flag.\n * Use for events that must be visible even when debug logging is off —\n * permanent pool shrinkage being the primary case.\n */\n always: {\n warn: (message: string) => void;\n };\n};\n\ntype Sink = Pick<Console, 'debug' | 'warn' | 'error'>;\n\nexport const createLogger = (\n prefix: string,\n enabled: boolean,\n sink: Sink = console,\n): Logger => {\n const line = (message: string) => `[${prefix}] ${message}`;\n // always.warn bypasses the enabled gate — pool shrinkage must be visible\n // even when debug logging is off, so the sink is the point (it is injectable\n // by tests, unlike a bare console.warn call).\n const always = { warn: (message: string) => sink.warn(line(message)) };\n\n if (!enabled)\n return { info: () => {}, warn: () => {}, error: () => {}, always };\n\n return {\n info: (message) => sink.debug(line(message)),\n warn: (message) => sink.warn(line(message)),\n error: (message) => sink.error(line(message)),\n always,\n };\n};\n","import type { CreateSQLiteClientOptions } from './client';\nimport type { PoolWorker } from './pool';\nimport type { SQLiteVFS } from './types';\n\nexport const debugSQLQuery = (sql: string, params?: unknown[]) => {\n if (!params || params.length === 0) return sql;\n\n let result = '';\n let paramIndex = 0;\n let i = 0;\n\n while (i < sql.length) {\n if (sql[i] === '?') {\n // Check if it's a positional parameter (?001, ?002, etc.)\n if (\n i + 3 < sql.length &&\n /\\d/.test(sql[i + 1]) &&\n /\\d/.test(sql[i + 2]) &&\n /\\d/.test(sql[i + 3])\n ) {\n const position = sql.substring(i + 1, i + 4);\n const numIndex = parseInt(position, 10) - 1;\n\n if (!Number.isNaN(numIndex) && params[numIndex] !== undefined) {\n result += formatValue(params[numIndex]);\n } else {\n result += 'NULL';\n }\n i += 4; // Skip ? and 3 digits\n } else {\n // Simple parameter (?)\n if (paramIndex < params.length) {\n result += formatValue(params[paramIndex++]);\n } else {\n result += 'NULL';\n }\n i++;\n }\n } else if (sql[i] === \"'\" || sql[i] === '\"') {\n // Skip string literals to avoid replacing ? inside them\n const quote = sql[i];\n result += sql[i++];\n while (i < sql.length) {\n result += sql[i];\n if (sql[i] === quote) {\n // Check for escaped quote\n if (i + 1 < sql.length && sql[i + 1] === quote) {\n result += sql[++i];\n } else {\n i++;\n break;\n }\n }\n i++;\n }\n } else {\n result += sql[i++];\n }\n }\n\n return result;\n\n function formatValue(value: unknown): string {\n if (value === null || value === undefined) {\n return 'NULL';\n }\n if (typeof value === 'string') {\n return `'${value.replace(/'/g, \"''\")}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`;\n }\n // `Buffer` does not exist in a browser. A Node Buffer is a Uint8Array\n // subclass, so this single branch still covers both.\n if (value instanceof Uint8Array) {\n let hex = '';\n for (const byte of value) {\n hex += byte.toString(16).padStart(2, '0');\n }\n return `X'${hex}'`;\n }\n return `'${JSON.stringify(value).replace(/'/g, \"''\")}'`;\n }\n};\n\ntype QueryDebugState = {\n sql: string;\n params?: unknown[] | undefined;\n startTime: number;\n firstRowTime?: number;\n endTime?: number;\n error?: unknown;\n affectedRows: number;\n prepared: number;\n};\n\ntype RequestDebugState = {\n startTime: number;\n acquireTime?: number;\n releaseTime?: number;\n affectedRows: number;\n queries: QueryDebugState[];\n currentQuery?: QueryDebugState;\n};\n\ntype WorkerDebugState = {\n index: number;\n name: string;\n creationTime: number;\n initializationTime?: number;\n requests: RequestDebugState[];\n currentRequest?: RequestDebugState;\n readonly status: string;\n};\n\nexport type ClientDebugState = {\n readonly file: string;\n readonly vfs: SQLiteVFS;\n readonly pragmas: Record<string, string>;\n readonly name: string;\n readonly queue: {\n readonly read: number;\n readonly write: number;\n /**\n * Callers suspended on the pool's readiness gate, waiting for the pool to\n * exist rather than for a free worker. They sit in neither wait queue, so\n * `read` and `write` are both 0 while they wait — during startup, and\n * during the retry round that follows a failed open.\n */\n readonly gated: number;\n };\n workers: WorkerDebugState[];\n};\n\nconst MAX_QUERY_HISTORY_LENGTH = 50;\nconst MAX_REQUEST_HISTORY_LENGTH = 50;\n\nexport const createClientDebug = (\n file: string,\n pool: (PoolWorker | undefined)[],\n clientOptions: Required<\n Pick<CreateSQLiteClientOptions, 'vfs' | 'pragmas' | 'name'>\n >,\n stats: () => { read: number; write: number; gated: number },\n) => {\n const { vfs, pragmas, name } = clientOptions;\n\n // Read through to the scheduler: the old counters were incremented by hand at\n // every acquire/release site and went stale the moment one was missed.\n const queue = {\n get read() {\n return stats().read;\n },\n get write() {\n return stats().write;\n },\n get gated() {\n return stats().gated;\n },\n };\n\n const clientState: ClientDebugState = {\n file,\n vfs,\n pragmas,\n name,\n queue,\n workers: [],\n };\n\n const createWorkerDebugState = (index: number, name: string) => {\n const state: WorkerDebugState = new Proxy(\n {\n index,\n name,\n requests: [],\n status: pool[index]?.status ?? 'EMPTY',\n creationTime: Date.now(),\n },\n {\n get: (target, prop) => {\n if (prop === 'status') {\n return pool[index]?.status ?? 'EMPTY';\n }\n return target[prop as keyof typeof target];\n },\n },\n );\n clientState.workers[index] = state;\n return state;\n };\n\n const createRequestDebugState = () => {\n const state: RequestDebugState = {\n queries: [],\n startTime: Date.now(),\n affectedRows: 0,\n };\n return {\n state,\n assign: (index: number) => {\n const worker = clientState.workers[index];\n if (worker) {\n state.acquireTime = Date.now();\n // Bounded: this array is pushed to on EVERY request and used to grow\n // with the client's total query count (D5 §1.3, the blocking fix).\n if (worker.requests.length >= MAX_REQUEST_HISTORY_LENGTH)\n worker.requests.shift();\n worker.requests.push(state);\n worker.currentRequest = state;\n }\n },\n };\n };\n\n const createQueryDebugState = (\n workerIndex: number,\n sql: string,\n params?: unknown[],\n ) => {\n const state: QueryDebugState = {\n sql,\n params,\n startTime: Date.now(),\n affectedRows: 0,\n prepared: 0,\n };\n const worker = clientState.workers[workerIndex];\n if (worker?.currentRequest) {\n if (worker.currentRequest.queries.length >= MAX_QUERY_HISTORY_LENGTH) {\n worker.currentRequest.queries.shift();\n }\n worker.currentRequest.queries.push(state);\n worker.currentRequest.currentQuery = state;\n }\n return state;\n };\n\n return {\n state: clientState,\n createWorkerDebugState,\n createRequestDebugState,\n createQueryDebugState,\n } as const;\n};\n","/**\n * Pure restart policy for worker slots.\n *\n * Deliberately free of `Worker` and DOM imports so Node tests can\n * drive it in milliseconds — the same reason `scheduler.ts` is pure. B1 lived\n * for months because the only way to reach the pool's decisions was a browser.\n *\n * The caller reports facts; this module returns a decision and never acts.\n */\nexport type SupervisorDecision = 'restart' | 'lost' | 'fail-client';\n\nexport type Supervisor = {\n report: (\n index: number,\n event: 'spawned' | 'ready' | 'served' | 'died' | 'lost',\n ) => SupervisorDecision | undefined;\n};\n\ntype Slot = {\n everReady: boolean;\n alive: boolean;\n lost: boolean;\n restarts: number;\n};\n\nexport const createSupervisor = (options: {\n size: number;\n maxWorkerRestarts?: number | undefined;\n}): Supervisor => {\n const { size, maxWorkerRestarts = 1 } = options;\n\n const slots: Slot[] = Array.from({ length: size }, () => ({\n everReady: false,\n alive: true,\n lost: false,\n restarts: 0,\n }));\n\n const liveCount = () => slots.filter((slot) => slot.alive).length;\n\n return {\n report: (index, event) => {\n const slot = slots[index];\n if (!slot) return undefined;\n\n if (event === 'spawned') {\n // A slot is alive from the moment a worker is created for it — which is\n // what the constructor's `alive: true` already encodes for the first\n // spawn. Without this event a restarted slot never re-enters that\n // state, so the replacement's death reads as a duplicate signal for the\n // worker that died before it: the guard below returns no decision, the\n // client neither restarts nor fails, and every queued request waits on\n // a pool that will never have a worker again.\n if (slot.lost) return undefined;\n slot.alive = true;\n return undefined;\n }\n\n if (event === 'ready') {\n // A lost slot cannot be revived: the loss was permanent and a\n // late ready would inflate liveCount, masking an empty pool.\n if (slot.lost) return undefined;\n slot.everReady = true;\n slot.alive = true;\n // Deliberately NOT resetting `restarts`: a worker that boots fine and\n // dies on every query would otherwise restart forever, silently.\n return undefined;\n }\n\n if (event === 'served') {\n // A stale done message can arrive after the slot was declared dead; if\n // it reset restarts then, it would silently refill the spent budget.\n if (!slot.alive) return undefined;\n slot.restarts = 0;\n return undefined;\n }\n\n if (event === 'lost') {\n // A death the caller has ALREADY judged terminal, which 'died' cannot\n // express. The startup retry round is capped at one, so a slot the\n // client has announced through `onWorkerLost` must never come back.\n // Reported as 'died' it would take the restart branch instead: `lost`\n // would stay false, leaving the slot revivable by a later\n // 'spawned'/'ready', and a restart would be charged against a budget\n // for a restart that never happens. The supervisor's view and the\n // consumer's would then disagree, with no source of truth to arbitrate.\n //\n // Same duplicate-signal guard as 'died': one report per slot.\n if (!slot.alive) return undefined;\n slot.alive = false;\n slot.lost = true;\n return liveCount() === 0 ? 'fail-client' : 'lost';\n }\n\n // 'died' — a slot already counted as dead reports once per signal\n // (onerror and a drain timeout can both fire), so ignore repeats.\n if (!slot.alive) return undefined;\n slot.alive = false;\n\n // R1: a slot that never worked is a configuration error, not an\n // accident. Restarting it only delays the diagnostic.\n if (slot.everReady && slot.restarts < maxWorkerRestarts) {\n slot.restarts += 1;\n return 'restart';\n }\n\n slot.lost = true;\n return liveCount() === 0 ? 'fail-client' : 'lost';\n },\n };\n};\n","import { SQLiteError } from './errors';\nimport { createLocks, initLockName } from './locks';\nimport { busyFromCode, spawnWorker } from './pool';\nimport {\n defaultBuildFor,\n RECOMMENDED_VFS,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n type WorkerMessageData,\n} from './types';\nimport { normalizeDatabaseFile, resolveWasmLocation } from './utils';\n\nexport type DeleteDatabaseOptions = {\n /**\n * Which VFS holds the database. Required for the same reason it is required\n * on `createSQLiteClient`: a VFS decides where the bytes live, so deleting\n * without naming one deletes in the wrong store — or nowhere, while\n * reporting success.\n */\n vfs: SQLiteVFS;\n /**\n * Which wa-sqlite build to load. It does **not** affect where the database\n * lives; it is here only because a VFS runs solely on the builds it\n * declares, and one of them must be loaded to instantiate the VFS at all.\n * @defaultValue the first build the VFS declares\n */\n build?: SQLiteBuild;\n /**\n * Where the worker fetches its `.wasm`, with the same meaning as on\n * `createSQLiteClient`. A deployment that needs it to open a database needs\n * it to delete one.\n */\n wasmUrl?: string | ((build: SQLiteBuild) => string);\n};\n\n/**\n * Deletes a database and the two siblings SQLite may leave beside it.\n *\n * Deleting a database that is not there is success — SQLite's own `xDelete`\n * behaves the same way, and a caller who wanted it gone has got what they\n * asked for.\n *\n * Nothing a VFS keeps for itself is touched: not the IndexedDB store, which is\n * shared by every database that VFS holds on this origin, and not the\n * `AccessHandlePoolVFS` directory, whose files *are* its reusable capacity.\n * The bytes of the named database are freed in both cases.\n *\n * @throws {SQLiteError} `INVALID_OPTION` when `vfs` is missing or the `build`\n * is not one the VFS supports — synchronously in spirit, as a rejection here.\n * @throws {SQLiteError} `BUSY` when the database is open or being opened, in\n * this tab or another. A connection already holding its handles cannot be\n * revoked from here; see the README's Known Limitations.\n */\nexport const deleteDatabase = async (\n file: string,\n options: DeleteDatabaseOptions,\n): Promise<void> => {\n if (!options?.vfs) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `vfs is required. Pass the VFS the database was created with — ${RECOMMENDED_VFS} is the recommended universal choice. A database written through one VFS is not visible through another, so deleting through the wrong one deletes nothing.`,\n );\n }\n\n const vfs = options.vfs;\n const build = options.build ?? defaultBuildFor(vfs);\n const capability = VFS_CAPABILITIES[vfs];\n\n if (!(capability.builds as readonly SQLiteBuild[]).includes(build)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} cannot run on the '${build}' build. Supported: ${capability.builds.join(', ')}.`,\n );\n }\n\n // Nothing was ever persisted, so there is nothing to delete and no worker\n // worth spawning to say so.\n if (capability.layout === 'memory') return;\n\n const dbFile = normalizeDatabaseFile(file);\n const wasm = resolveWasmLocation(options.wasmUrl, build, location.href);\n\n const ran = await createLocks().tryWithLock(initLockName(dbFile), () =>\n runDelete({ file: dbFile, vfs, build, wasm }),\n );\n\n if (!ran) {\n throw new SQLiteError(\n 'BUSY',\n `${dbFile} is being opened or deleted elsewhere. Close every client on it, in every tab, and try again.`,\n );\n }\n};\n\n/**\n * How long a delete may take before the worker is presumed unable to answer.\n * Matches `openTimeout`'s default, because the failure it catches is the same\n * one: a VFS that cannot acquire what it needs — `AccessHandlePoolVFS` whose\n * six slots are held elsewhere reaches neither success nor error. Not a public\n * option: a caller has nothing useful to tune here, and a delete that takes\n * thirty seconds has already failed.\n */\nconst DELETE_TIMEOUT = 30_000;\n\nconst runDelete = (message: {\n file: string;\n vfs: SQLiteVFS;\n build: SQLiteBuild;\n wasm: ReturnType<typeof resolveWasmLocation>;\n}): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n const worker = spawnWorker(`SQLite delete / ${message.file}`);\n\n const timer = setTimeout(() => {\n settle(\n new SQLiteError(\n 'TIMEOUT',\n `deleting ${message.file} timed out after ${DELETE_TIMEOUT} ms. The database is most likely held open by another client or tab.`,\n ),\n );\n }, DELETE_TIMEOUT);\n\n const settle = (error?: SQLiteError) => {\n clearTimeout(timer);\n worker.terminate();\n if (error) reject(error);\n else resolve();\n };\n\n worker.onmessage = (event: MessageEvent<WorkerMessageData>) => {\n const data = event.data;\n if (data.type === 'deleted') return settle();\n if (data.type === 'error') {\n return settle(\n busyFromCode(data) ??\n new SQLiteError('WORKER_CRASHED', data.message, {\n cause: data.cause,\n }),\n );\n }\n };\n\n worker.onerror = (event) => {\n settle(\n new SQLiteError(\n 'WORKER_CRASHED',\n `worker crashed while deleting ${message.file}: ${(event as ErrorEvent).message ?? ''}`,\n ),\n );\n };\n\n worker.postMessage({ type: 'delete', callId: 0, ...message });\n });\n"],"names":["BUILD_REQUIREMENTS","VFS_CAPABILITIES","defaultBuildFor","vfs","RECOMMENDED_VFS","PROBES","navigator","FileSystemFileHandle","WebAssembly","UNPROBEABLE","Set","FEATURE_LABEL","Object","detectFeatures","found","feature","probe","missingFeature","build","available","SQLiteError","Error","code","message","options","undefined","SQLiteBulkWriteError","counts","stagingLockName","file","table","noOpLocks","_name","fn","createLocks","manager","globalThis","name","Promise","resolveReleaser","rejectOuter","release","held","resolveHeld","ran","lock","snapshot","WRITE_KEYWORDS","READ_PRAGMA","isReadQuery","sql","assertReadable","method","keyword","quoteIdent","JSON","COLUMN_TYPE","PRAGMA_NAME","PRAGMA_INTEGER","PRAGMA_LITERAL","normalizeDatabaseFile","URL","resolveWasmLocation","wasmUrl","baseHref","href","isCallback","raw","value","ADMITTED","REGISTRY_KEY","Symbol","STOP","BUSY_CODES","busyFromCode","data","spawnWorker","Worker","makeAbortRace","signal","onAbort","aborted","_","reject","chunk","worker","params","chunkSize","credits","teardown","iterator","next","streamRows","rows","row","readWorker","result","firstWorker","writeWorker","affected","exec","clientCount","createSQLiteClient","clientOptions","startupFirstError","closing","fatal","onFirstSettle","onGateOpen","map","existing","cell","deps","dbFile","clientIndex","clientPrefix","poolSize","pool","capability","wasm","location","absent","describeMissing","label","others","b","suffix","alternatives","key","String","testWriterPolicy","writerPolicy","inStartup","startupLosses","Map","scheduler","createScheduler","opts","shutdownReason","shutdownDeferred","workers","dead","leased","generations","gen","index","settledSlots","gateOpen","gateDeferred","firstSettleOpened","firstSettleFired","gatedWaiters","settleGateSlot","kind","failedIndices","i","readerQueue","writerQueue","currentWriterIndex","lastWriterIndex","canDesignate","serveWriterFirst","checkShutdown","makeLease","myGen","released","handOver","reason","waiter","abortP","abortReject","onGateAbort","write","immediate","takeAvailable","preferred","promise","resolve","queue","at","error","emitWorkerLost","failClient","spawn","failClientError","verdict","supervisor","Boolean","debugOption","logger","createLogger","prefix","enabled","sink","console","line","always","clientDebug","createClientDebug","stats","pragmas","clientState","createWorkerDebugState","state","Proxy","Date","target","prop","createRequestDebugState","createQueryDebugState","workerIndex","debug","epochs","registry","host","created","applyBarrier","barrierIter","afterWrite","seen","acquireWithDebug","request","lease","acquireInstrumented","barrier","read","chunkWorker","stream","first","bulkFor","createBulk","shared","swept","locks","maxVariables","transaction","bulkWrite","keys","before","failure","room","maxBufferSize","Math","queueSize","buffer","writePromise","closed","rowsWritten","rowsNotWritten","queuedRows","releaseRoom","fail","flush","toInsert","runBatch","currentAffected","k","failClosed","r","output","schema","uuid","staging","crypto","normalizedSchema","v","type","assertColumnType","column","trimmed","unique","notnull","generated","assertGeneratedExpression","expr","lockHeld","createStaging","sweepOnce","heldNames","tables","orphan","enqueue","close","col","releaseLock","dropStaging","setTimeout","tx","statement","indexStatements","statements","columns","Array","names","handleDeath","callback","readOnly","autoCommit","checksql","done","begun","withSignal","given","merged","mergeSignals","a","noop","AbortController","relay","source","onA","onB","releasing","refuse","bulk","query","db","running","e","bounded","ms","timer","clearTimeout","openTimeout","drainTimeout","createSupervisor","size","maxWorkerRestarts","slots","liveCount","slot","event","dying","createPoolWorker","deferredChunk","deferredClose","idle","stopRequested","lost","statementCacheSize","deferredInit","workerName","currentCallId","suppressServed","ready","deathDeferred","die","detail","errorEvent","failedUrl","callId","queryState","DEFAULT_CREDIT_WINDOW","noServed","expiry","served","live","cb","cbError","wasInStartup","decision","draining","deleteDatabase","runDelete","settle"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/inspect.ts","../src/types.ts","../src/capabilities.ts","../src/errors.ts","../src/locks.ts","../src/utils.ts","../src/bulk.ts","../src/epochs.ts","../src/pool.ts","../src/abandon.ts","../src/queries.ts","../src/transaction.ts","../src/client.ts","../src/scheduler.ts","../src/logger.ts","../src/debug.ts","../src/supervisor.ts","../src/delete.ts","../src/sqlite-codes.ts"],"sourcesContent":["import { SQLiteError } from './errors';\nimport type { LockEntries, Locks } from './locks';\nimport {\n createLocks,\n parseClientMarker,\n sharesStorage,\n writeLockName,\n} from './locks';\nimport type { SQLiteVFS } from './types';\nimport { VFS_CAPABILITIES } from './types';\nimport { normalizeDatabaseFile } from './utils';\n\n/**\n * The Web Locks `clientId` of THIS realm.\n *\n * There is no API that returns it, so it is read back out of the registry: hold\n * a name nobody else can have, then find that name's entry. A realm's id is\n * stable for its whole life, so this is paid once, ever — and not even once\n * when a marker of ours is already in the snapshot.\n *\n * Module scope is exactly the right scope: an iframe has its own module\n * instance and its own id, which is what makes `sameTab` mean anything.\n */\nlet cachedRealmId: string | undefined;\n\nexport const resolveRealmId = async (\n locks: Locks,\n snapshot: LockEntries,\n ownMarkerName?: string,\n): Promise<string> => {\n if (cachedRealmId !== undefined) return cachedRealmId;\n\n if (ownMarkerName !== undefined) {\n const mine = snapshot.held.find((entry) => entry.name === ownMarkerName);\n if (mine) {\n cachedRealmId = mine.clientId;\n return cachedRealmId;\n }\n }\n\n const nonce = `bsq:realm:${crypto.randomUUID()}`;\n const release = await locks.hold(nonce, { mode: 'shared' });\n try {\n const fresh = await locks.entries();\n const mine = fresh.held.find((entry) => entry.name === nonce);\n if (!mine) {\n // A registry that does not report a lock we are holding cannot answer\n // `sameTab` either. Saying so beats reporting every client as elsewhere.\n throw new SQLiteError(\n 'UNSUPPORTED',\n 'This browser does not report lock holders, so clients cannot be located.',\n );\n }\n cachedRealmId = mine.clientId;\n return cachedRealmId;\n } finally {\n release();\n }\n};\n\n/** One live client on a database. */\nexport type DatabaseClient = {\n readonly id: string;\n readonly name: string;\n /** The realm holding it. Every client in one tab reports the same value. */\n readonly tab: string;\n /** That realm is the caller's. A same-origin iframe is another tab here. */\n readonly sameTab: boolean;\n /** Four VFS share the `opfs` namespace, and therefore the file. */\n readonly vfs: SQLiteVFS;\n};\n\nexport type InspectionBase = {\n readonly file: string;\n readonly vfs: SQLiteVFS;\n /** Distinct realms among the clients. */\n readonly tabs: number;\n readonly write: {\n /** The realm writing now, never the client. `null` when nobody writes. */\n readonly tab: string | null;\n /** Always false when `tab` is null. */\n readonly sameTab: boolean;\n /** Writers queued behind it, across the whole origin. */\n readonly waiting: number;\n };\n};\n\nexport type DatabaseInspection = InspectionBase & {\n readonly clients: readonly DatabaseClient[];\n};\n\nexport type ClientInspection = InspectionBase & {\n /**\n * This client, or `null` when this client's own marker is not in the\n * snapshot — the brief window before its Web Locks grant has landed, or when\n * the grant could not be taken at all.\n */\n readonly self: DatabaseClient | null;\n readonly siblings: readonly DatabaseClient[];\n};\n\n/**\n * The census, given locks that are already known to work.\n *\n * One `entries()` call supplies the roster, the writer and the queue together,\n * so those three always describe the same instant and cannot compose a state\n * that never existed. The one exception: a realm whose id has never been\n * resolved and that was given no marker pays one extra query to read its own\n * `clientId` back — once, and never again for that realm's lifetime.\n */\nexport const inspectWith = async (\n locks: Locks,\n file: string,\n vfs: SQLiteVFS,\n ownMarkerName?: string,\n): Promise<DatabaseInspection> => {\n if (!sharesStorage(vfs)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} keeps its pages in the worker that opened them, so two clients are two databases and there is nothing to inspect. Ask this of a persistent VFS.`,\n );\n }\n const snapshot = await locks.entries();\n const realm = await resolveRealmId(locks, snapshot, ownMarkerName);\n\n const clients: DatabaseClient[] = [];\n for (const entry of snapshot.held) {\n const marker = parseClientMarker(entry.name, vfs, file);\n if (!marker) continue;\n clients.push({\n id: marker.id,\n name: marker.name,\n tab: entry.clientId,\n sameTab: entry.clientId === realm,\n vfs: marker.vfs,\n });\n }\n\n const writeName = writeLockName(vfs, file);\n const writer = snapshot.held.find((entry) => entry.name === writeName);\n const waiting = snapshot.pending.filter(\n (entry) => entry.name === writeName,\n ).length;\n\n return {\n file,\n vfs,\n clients,\n tabs: new Set(clients.map((client) => client.tab)).size,\n write: {\n tab: writer?.clientId ?? null,\n sameTab: writer !== undefined && writer.clientId === realm,\n waiting,\n },\n };\n};\n\n/**\n * Whether any OTHER client of this library holds `file` on `vfs`.\n *\n * The discriminator behind the `openTimeout` message: a slot that never became\n * ready is usually blamed on another tab, and that is often false — a page\n * reloaded without `close()` leaves a dead context holding the database, and\n * no live client to find. One `entries()` call tells the two apart.\n *\n * Deliberately NOT `inspectWith`: this runs while the pool is half-open, so it\n * resolves no realm id (which would take a lock), touches nothing the client\n * owns, and never throws. `undefined` is \"could not be answered\" — Web Locks\n * missing, a VFS whose pages never leave their worker, a registry that rejects,\n * or one that does not answer within `deadlineMs`. A caller that reads\n * `undefined` as `false` would state the opposite of what was observed.\n *\n * `false` means no client of THIS library holds it — never that nobody does.\n * Another library, another origin's tooling and native code are all invisible\n * to the Web Locks registry.\n */\nexport const libraryClientsHold = async (\n locks: Locks,\n file: string,\n vfs: SQLiteVFS,\n ownId: string,\n deadlineMs = 250,\n): Promise<boolean | undefined> => {\n if (!locks.available || !sharesStorage(vfs)) return undefined;\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const snapshot = await Promise.race([\n locks.entries(),\n new Promise<undefined>((resolve) => {\n timer = setTimeout(resolve, deadlineMs, undefined);\n }),\n ]);\n if (!snapshot) return undefined;\n\n return snapshot.held.some((entry) => {\n const marker = parseClientMarker(entry.name, vfs, file);\n return marker !== undefined && marker.id !== ownId;\n });\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n};\n\nexport type InspectDatabaseOptions = {\n /**\n * The VFS the database was created with. Required, and not defaulted: four\n * VFS share one underlying file, and the others are separate stores\n * entirely, so guessing would report on a different database.\n */\n vfs: SQLiteVFS;\n};\n\n/**\n * Who is live on a database, without opening it.\n *\n * This is a snapshot, stale the instant it resolves. It informs a UI; it never\n * authorizes an action — `deleteDatabase` raising `DATABASE_IN_USE` is the only\n * authority on whether a database can be removed.\n *\n * Takes `file` positionally like `createSQLiteClient` and `deleteDatabase`:\n * every root export of this library names the database the same way.\n *\n * @throws {SQLiteError} `INVALID_OPTION` when `vfs` is missing, unknown, or a\n * memory VFS, where two clients are two databases and the question has no\n * meaning.\n * @throws {SQLiteError} `UNSUPPORTED` where the Web Locks API is unavailable.\n * Reporting zero there would be indistinguishable from a database nobody\n * holds.\n */\nexport const inspectDatabase = async (\n file: string,\n options: InspectDatabaseOptions,\n): Promise<DatabaseInspection> => {\n if (!options?.vfs) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `vfs is required. Pass the VFS the database was created with — VFS.md compares them. Four VFS share one underlying file, and the rest are separate stores, so the wrong one reports on a different database.`,\n );\n }\n\n const { vfs } = options;\n if (!Object.hasOwn(VFS_CAPABILITIES, vfs)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `Unknown vfs '${String(vfs)}'. Supported: ${Object.keys(VFS_CAPABILITIES).join(', ')}.`,\n );\n }\n if (!sharesStorage(vfs)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} keeps its pages in the worker that opened them, so two clients are two databases and there is nothing to inspect. Ask this of a persistent VFS.`,\n );\n }\n\n const locks = createLocks();\n if (!locks.available) {\n throw new SQLiteError(\n 'UNSUPPORTED',\n 'The Web Locks API is unavailable, so clients on a database cannot be counted. Reporting zero would be indistinguishable from a database nobody holds.',\n );\n }\n\n return inspectWith(locks, normalizeDatabaseFile(file), vfs);\n};\n","import type { SQLiteErrorCode } from './errors';\nimport type { SQLiteResultCode } from './sqlite-codes';\n\nexport type SQLiteWorkerMessageData<_T = unknown> = {\n callId: number;\n terminate?: boolean;\n} & (\n | SQLWorkerResultData[keyof SQLWorkerResultData]\n | { type: 'error'; message: string }\n);\n\nexport type SQLWorkerResultData<T = unknown> = {\n open: { success: boolean };\n sql: { type: 'partial'; result: T[] } | { type: 'one'; sizes: number[] };\n abort: { type: 'done' };\n};\n\nexport const SharedArrayTypes = {\n INT: 0,\n STRING: 1,\n OBJECT: 2,\n};\n\n/**\n * The savepoint a transaction asks the worker to handle around one query\n * (spec 2026-09-11, D4/D5). `conclude` settles the savepoint the previous\n * savepointed write left open — `release` keeps that write, `undo` rolls it\n * back first — and `open` starts one for this query's own statement. Both run\n * before the statement, conclusion first. Internal: no consumer sets it.\n */\nexport type SavepointOp = { conclude?: 'release' | 'undo'; open?: true };\n\ntype SQLOptions = {\n chunkSize?: number;\n /** Chunks the worker may send before waiting for a credit. Spec §3.2. */\n credits?: number;\n /** When true, the worker installs an async progress handler so an AbortSignal can stop a running step(). */\n abortable?: boolean;\n /** See `SavepointOp`. */\n savepoint?: SavepointOp;\n};\n\n/**\n * Where a worker fetches its `.wasm` from, when the consumer overrode it.\n *\n * Discriminated rather than a single string because the two forms differ in\n * what they leave to the Emscripten glue. `base` is a directory: the glue\n * supplies the file name (`locateFile('wa-sqlite-async.wasm')`), so nothing\n * here names the three builds' files — and nothing has to be renamed when\n * wa-sqlite renames one. `file` is the whole URL, typically content-hashed by\n * a bundler, so the glue's file name is discarded.\n *\n * Always absolute: `resolveWasmLocation` (`src/utils.ts`) resolves against the\n * page before the `open` message is posted, so the worker applies it without\n * knowing what it was relative to.\n */\nexport type WasmLocation = { base: string } | { file: string };\n\nexport type ClientMessageData =\n | {\n type: 'open';\n file: string;\n vfs: SQLiteVFS;\n build?: SQLiteBuild;\n pragmas?: Record<string, string>;\n /** Statements retained per worker; see `src/client.ts`. Internal. */\n statementCacheSize?: number;\n /** Bytes retained per worker; see `src/client.ts`. Internal. */\n statementCacheBytes?: number;\n wasm?: WasmLocation;\n /** Shared abort slots, one Int32 per worker. Isolated contexts only. */\n abortSlots?: SharedArrayBuffer;\n /** This worker's index into `abortSlots`. */\n abortIndex?: number;\n /**\n * Features this worker must find before opening; it declines instead of\n * opening when one is missing (spec 2026-09-13). Sent to slots of index\n * ≥ 1 only, and only by a VFS that declares `singleConnectionWithout`.\n */\n declineWithout?: readonly PlatformFeature[];\n /**\n * Features worker 0 probes before opening, where the VFS is exclusive\n * without one (spec 2026-09-15, §3.2): it reports them with `probed`,\n * then loads nothing until `proceed`. Sent to slot 0 only, and only by a\n * VFS that declares `exclusiveConnectionWithout`.\n */\n probeFirst?: readonly PlatformFeature[];\n }\n | {\n type: 'query';\n callId: number;\n sql: string;\n params: unknown[];\n options?: SQLOptions;\n }\n | { type: 'close'; callId: number }\n | { type: 'credit'; callId: number; n: number }\n | { type: 'stop'; callId: number }\n /** The client decided the connection lock; worker 0 may open (spec 2026-09-15). */\n | { type: 'proceed'; callId: number }\n | {\n type: 'delete';\n callId: number;\n file: string;\n vfs: SQLiteVFS;\n build?: SQLiteBuild;\n wasm?: WasmLocation;\n };\n\nexport type WorkerMessageData =\n | { type: 'ready'; callId: number }\n /**\n * The worker found a feature of `declineWithout` missing and opened nothing:\n * the environment caps the pool (spec 2026-09-13).\n */\n | { type: 'declined'; callId: number; missing: PlatformFeature }\n /**\n * Worker 0's answer to `probeFirst`: the first feature missing, or null. It\n * opens nothing until the client sends `proceed` (spec 2026-09-15, §3.2).\n */\n | { type: 'probed'; callId: number; missing: PlatformFeature | null }\n | { type: 'chunk'; callId: number; data: unknown[] }\n | {\n type: 'done';\n callId: number;\n affected: number;\n /**\n * Statements compiled while serving this query — zero on a cache hit.\n * Rides the same message as `affected` rather than opening a channel:\n * the effect this instruments is a count, not a duration (`mem:lessons`,\n * \"for a sub-millisecond effect, count the round trips\").\n */\n prepared: number;\n /**\n * Whether the connection is inside a transaction once this query has\n * ended — `sqlite3_get_autocommit() === 0`. SQLite can leave a\n * transaction by itself: an interrupted INSERT/UPDATE/DELETE rolls the\n * whole transaction back. Absent when the worker could not read it.\n */\n inTransaction?: boolean | undefined;\n }\n | {\n type: 'error';\n callId: number;\n message: string;\n cause?: unknown;\n /** SQLite's numeric result code, when the failure came from SQLite. */\n sqliteCode?: SQLiteResultCode;\n /**\n * SQLite's extended result code, read in the worker where the statement\n * failed (spec 2026-09-14, §5.1). Sent by the query path only, and\n * unfiltered: this is exactly what SQLite reported, including a value\n * equal to `sqliteCode` (no subtype). The client is what drops it in\n * that case (D9); when SQLite does report a subtype,\n * `(sqliteExtendedCode & 0xff) === sqliteCode`.\n */\n sqliteExtendedCode?: number;\n /**\n * A code this library minted, when the worker knows the cause. The\n * generic path by which a worker-side error keeps its code across the\n * boundary — `worker.ts` copies it off any thrown error carrying one, so\n * this is a structural contract and not a hook for one class.\n *\n * **Nothing sets it today.** `WorkerQueryTimeout` was its only producer\n * and it went with the execution budget when `timeout` became a\n * client-side wall-clock deadline. Kept rather than deleted: it is the\n * twin of `sqliteCode` above, which is load-bearing, and rebuilding it\n * would cost the same three sites it occupies.\n */\n errorCode?: SQLiteErrorCode;\n /**\n * Whether the connection is inside a transaction once this query has\n * ended — `sqlite3_get_autocommit() === 0`. SQLite can leave a\n * transaction by itself: an interrupted INSERT/UPDATE/DELETE rolls the\n * whole transaction back. Absent when the worker could not read it.\n */\n inTransaction?: boolean | undefined;\n }\n | { type: 'closed'; callId: number }\n | { type: 'deleted'; callId: number }\n /** The delete worker found nothing at that name; deleteDatabase turns it into DATABASE_NOT_FOUND. */\n | { type: 'not-found' }\n | {\n type: 'open-error';\n callId: number;\n message: string;\n cause?: unknown;\n /** SQLite's numeric result code, when the failure came from SQLite. */\n sqliteCode?: SQLiteResultCode;\n };\n\n/** Which wa-sqlite WebAssembly build a worker loads. */\nexport type SQLiteBuild = 'sync' | 'async' | 'jspi';\n\n/**\n * What each build needs from the engine beyond plain WebAssembly.\n *\n * `satisfies Record<SQLiteBuild, …>` and not `SQLiteBuild = keyof typeof …`:\n * the check must run in this direction. Adding a build to the union then fails\n * to compile until its requirements are declared, where `keyof` would let a\n * forgotten entry mean silently that the build does not exist. `VFS_CAPABILITIES`\n * derives `SQLiteVFS` from its keys because it *is* the VFS registry; the build\n * registry is `WA_SQLITE_BUILDS` in the worker, and this table describes one\n * attribute of builds rather than the builds themselves.\n */\nexport const BUILD_REQUIREMENTS = {\n sync: [],\n async: [],\n jspi: ['jspi'],\n} as const satisfies Record<SQLiteBuild, readonly PlatformFeature[]>;\n\n/**\n * Platform features a build USES when present and works without, at a cost —\n * the symmetric of `degradesWithout` on a VFS, at the level where this one\n * actually lives. The `sync` build cannot carry an abort into a running\n * `step()` without a `SharedArrayBuffer`, and there is no SharedArrayBuffer\n * outside a cross-origin isolated context: measured 2026-09-04, it is not\n * restricted there, it is absent. Nothing here names COOP/COEP or\n * Document-Isolation-Policy: any of them satisfies the probe, and one of them\n * is Chrome-only.\n */\nexport const BUILD_DEGRADES_WITHOUT = {\n sync: ['cross-origin-isolated'],\n async: [],\n jspi: [],\n} as const satisfies Record<SQLiteBuild, readonly PlatformFeature[]>;\n\n/**\n * A platform feature a VFS may need. Which browser versions ship each one is\n * documentation data, not runtime data, so it lives in the VFS.md generator\n * (`scripts/render-vfs-matrix.ts`) with its sources — not here, where it would\n * ship to every consumer for nothing.\n */\nexport type PlatformFeature =\n | 'opfs'\n | 'readwrite-unsafe'\n | 'jspi'\n | 'writable-stream'\n | 'cross-origin-isolated';\n\n/** Where a VFS keeps the database. */\nexport type VFSStorage = 'opfs' | 'indexeddb' | 'memory';\n\n/**\n * How a VFS arranges a database in its storage — which is not the same\n * question as `storage`, and cannot be derived from it: `AccessHandlePoolVFS`\n * is `storage: 'opfs'` yet keeps opaque, randomly named slot files whose\n * association with a SQLite path lives in a header inside each file.\n *\n * `deleteDatabase` reads this to decide whether the database is also an OPFS\n * entry it can remove by name after `jDelete` — the pass that covers the two\n * VFS whose `jDelete` does not delete. A wrong value here is a deletion that\n * reports success over an intact file.\n */\nexport type VFSLayout = 'opfs-path' | 'opfs-pool' | 'idb-store' | 'memory';\n\n/** How much of the database a VFS keeps resident in RAM. */\nexport type VFSMemoryModel = 'page-cache' | 'whole-database';\n\n/** What a VFS can and cannot do. One entry per VFS, and no second table. */\nexport type VFSCapability = {\n /** Builds this VFS can run on, most preferred first. */\n readonly builds: readonly [SQLiteBuild, ...SQLiteBuild[]];\n /** Largest pool this VFS supports; `null` when unbounded. */\n readonly maxPoolSize: number | null;\n /** Why the cap exists. Required whenever `maxPoolSize` is not null. */\n readonly poolLimitReason: string | null;\n /** Whether several connections may share one database. */\n readonly multiConnection: boolean;\n /** Whether data outlives `close()`. */\n readonly persistent: boolean;\n /**\n * `page-cache`: only SQLite's page cache is resident, bounded by\n * `PRAGMA cache_size`. `whole-database`: the entire database is resident,\n * and `poolSize` multiplies it.\n */\n readonly memoryModel: VFSMemoryModel;\n /** Where the database actually lives. */\n readonly storage: VFSStorage;\n /** How the database is arranged within that storage. */\n readonly layout: VFSLayout;\n /**\n * Whether this VFS takes its OPFS access handle in the EXCLUSIVE mode —\n * `createSyncAccessHandle()` with no `mode`, rather than\n * `mode: 'readwrite-unsafe'`.\n *\n * It decides whether an acquisition has to be retried. A terminated context\n * releases its Web Locks at once but keeps its OPFS access handles for up to\n * ~2 s on Chromium (HANDLE-CORPSE, `mem:measurements`), so a VFS taking an\n * exclusive handle can meet a file held by something that answers nothing:\n * no lock to wait on, no owner to ask, only time to wait out. Where\n * `readwrite-unsafe` is used a second handle is granted regardless, and a\n * dead holder blocks nobody.\n *\n * Declared rather than detected, because the error is not available where the\n * decision has to be made: wa-sqlite's `jOpen` swallows it (measured\n * 2026-09-18) and SQLite reports a bare `SQLITE_CANTOPEN`. Where the error IS\n * available — VFS instantiation — `createVfsInstance` tests it directly and\n * needs no declaration.\n *\n * NOT declared for `OPFSAdaptiveVFS` and `OPFSWriteAheadVFS`, which ask for\n * `readwrite-unsafe` and fall back to an exclusive handle only on an engine\n * that lacks it. That combination is real but out of reach: Firefox releases\n * a dead worker's handle in 1-6 ms (HANDLE-ORPHAN), a window nothing loses.\n */\n readonly exclusiveFileHandle: boolean;\n /**\n * Platform features without which this VFS cannot work at all.\n *\n * `readwrite-unsafe` is the one that bites: WebIDL ignores the unknown\n * dictionary member on engines that do not implement it, so the handle\n * silently opens exclusive, and a second connection then waits or fails\n * depending on the VFS — see `degradesWithout` and `singleConnectionWithout`.\n * Declaring it is what lets the conformance suite probe for it and skip,\n * instead of leaving it to surface as a 60-second timeout.\n */\n readonly requires: readonly PlatformFeature[];\n /**\n * Platform features this VFS uses when present and works without, at a cost.\n *\n * `OPFSAdaptiveVFS` is the case this field exists for. Without\n * `readwrite-unsafe` it rotates a single exclusive access handle between\n * connections instead of holding one each. That works — Firefox is the engine\n * the browser suite exercises it on — but a connection in a long\n * uninterruptible statement holds the handle, and every other connection to\n * the database, in another client or tab, waits for it. Within one client it\n * runs a single worker there: see `singleConnectionWithout`.\n *\n * Without this distinction, a support table derived from browser specs would\n * mark that VFS broken everywhere outside Chromium, when it merely degrades.\n */\n readonly degradesWithout: readonly PlatformFeature[];\n /**\n * Platform features without which a pool of more than one worker buys this\n * VFS nothing, so it runs on one (spec 2026-09-13, §3 and §10). Either the\n * VFS holds its database file exclusively for a connection's whole life and\n * a second worker cannot open at all (`OPFSWriteAheadVFS`), or it rotates one\n * exclusive access handle between connections and a second worker only waits\n * its turn (`OPFSAdaptiveVFS` — measured 2026-09-14 on Firefox: a pool of one\n * was faster at startup and on bursts of reads, and equal everywhere else).\n * The pool's surplus workers probe the feature before loading anything and\n * decline (`src/worker/probes.ts`); every feature listed needs a probe there.\n */\n readonly singleConnectionWithout: readonly PlatformFeature[];\n /**\n * Files this VFS keeps beside the database, by suffix, beyond the three every\n * layout may have (`''`, `-journal`, `-wal`). `deleteDatabase` removes them\n * with the rest; a file missing from this list outlives its database.\n *\n * `OPFSWriteAheadVFS` keeps its write-ahead log in two files of its own,\n * `-wa0` and `-wa1` (wa-sqlite's `#getWriteAheadNameFromDbName`) — measured\n * left behind by every deletion until 2026-09-14.\n */\n readonly extraFileSuffixes: readonly string[];\n /**\n * Whether every statement on this VFS must hand its worker back to the event\n * loop while it runs, abortable or not.\n *\n * `IDBBatchAtomicVFS` is why it exists. Its `jLock` opens a readwrite\n * IndexedDB transaction on reaching SHARED, and IndexedDB commits a\n * transaction only once its thread returns to the event loop. A worker inside\n * one long statement never did, so every other connection's read queued\n * behind it until the statement ended — measured 2026-09-14 on both engines\n * (`mem:measurements`, IDB-SIGNAL). The worker then runs its progress handler\n * on every statement, a task turn every `PROGRESS_OPS` VM ops, which measured\n * no cost. The `sync` build cannot yield and ignores it.\n */\n readonly yieldsDuringStatements: boolean;\n /**\n * PRAGMAs this library applies on open for this VFS.\n *\n * Merged UNDER the consumer's `pragmas`, so any key they set wins and they\n * never lose a default by setting an unrelated one — `foreign_keys` is the\n * common case, and replacing rather than merging would silently disable\n * everything below it.\n *\n * The bar is deliberately high, and almost nothing clears it: **more\n * performance without less reliability, sourced rather than guessed.** Three\n * things were weighed and rejected. `journal_mode=wal` universally, because\n * no VFS here implements `xShmMap` and upstream gives write-ahead logging to\n * `OPFSWriteAheadVFS` alone, inside the VFS and unreachable by pragma.\n * `synchronous=normal`, because relaxing durability spends the consumer's\n * data, not their milliseconds. And `cache_size`, because raising it changes\n * a mode without a measurable gain — Firefox showed none at all, and the\n * heap it can then reach is never given back (measured 2026-09-02).\n */\n readonly defaultPragmas: Readonly<Record<string, string>>;\n /**\n * Whether this VFS enforces an origin-wide exclusive connection lock for the\n * client's lifetime.\n *\n * When `true`, `createSQLiteClient` acquires a `bsq:conn:…` Web Lock on first\n * use. A second client that attempts to open the same database receives `DATABASE_IN_USE`\n * immediately on its first query instead of silently reading a frozen, broken\n * view. This field is the only thing standing between a consumer and an\n * unfalsifiable silent failure — `SELECT 1` and even\n * `SELECT count(*) FROM sqlite_master` pass on a broken second client.\n *\n * `true` only for `AccessHandlePoolVFS`, whose OPFS access-handle pool is not\n * sharable across connections (measured AHP-2TAB, 2026-09-01).\n * `false` for `IDBMirrorVFS` — despite `multiConnection: false` — because two\n * clients on that VFS DO share data over its origin-wide `BroadcastChannel`\n * (measured 2026-09-01, 3/3 both engines). `multiConnection: false` there marks\n * concurrent-writer unsafety, not isolation.\n * `false` for the memory VFS, which are isolated by construction and have\n * nothing to exclude.\n *\n * `VFS_CAPABILITIES` is the single source of truth the client guard, the\n * conformance suite, the VFS.md generator and the benchmark page all read.\n * The gate is by this declaration, not by VFS name.\n */\n readonly exclusiveConnection: boolean;\n /**\n * Platform features without which this VFS holds its database file\n * exclusively for a connection's whole life, across the origin — so the\n * client takes `bsq:conn` exclusively, as for `exclusiveConnection`, and a\n * second client gets `DATABASE_IN_USE` (spec 2026-09-15).\n *\n * `OPFSWriteAheadVFS` without `readwrite-unsafe`: upstream's VFS requires the\n * mode and keeps its three access handles for the connection's life, so\n * nothing else can open the file — every query of a second client failed\n * with WORKER_CRASHED on Firefox, 20/20 per shape (2026-09-15).\n *\n * The page cannot probe these features, so worker 0 probes them before\n * opening (`src/worker/probes.ts`): every feature listed needs a probe there,\n * and must also be in `singleConnectionWithout`, whose surplus workers\n * decline before they touch the file.\n */\n readonly exclusiveConnectionWithout: readonly PlatformFeature[];\n};\n\n/**\n * The single source of truth for VFS selection. `SQLiteVFS` is derived from its\n * keys, `worker/worker.ts` must supply a loader for every key, the guards in\n * `client.ts` read it, the conformance suite gates its scenarios on it, and the\n * VFS.md table is generated from it. Nothing may hold a second copy.\n *\n * Build order is a decision per VFS, not a rule: `sync` is both the fastest and\n * the most portable build, so it leads wherever supported; `OPFSAdaptiveVFS`\n * cannot use it and leads with `async` because `jspi` is Chromium-only.\n *\n * Every declared build combination is verified by running it against the pinned\n * wa-sqlite v1.1.2, never copied from upstream's table.\n */\nexport const VFS_CAPABILITIES = {\n OPFSWriteAheadVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n exclusiveFileHandle: false,\n // Measured on Firefox 2026-08-27, HAS_UNSAFE_HANDLES false: all three\n // build pairs and all six invariants pass. That campaign ran at an\n // EFFECTIVE pool of one: without readwrite-unsafe every worker but the\n // first failed to open, and conformance did not count live workers\n // (spec 2026-09-13). `requires` used to name readwrite-unsafe, which made\n // the conformance suite skip the very pairs that would have falsified it.\n // Safari behaves as Firefox — observed 2026-09-13, InvalidStateError.\n requires: ['opfs'],\n degradesWithout: ['readwrite-unsafe'],\n singleConnectionWithout: ['readwrite-unsafe'],\n extraFileSuffixes: ['-wa0', '-wa1'],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: ['readwrite-unsafe'],\n defaultPragmas: {},\n },\n OPFSAdaptiveVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n exclusiveFileHandle: false,\n requires: ['opfs'],\n degradesWithout: ['readwrite-unsafe'],\n singleConnectionWithout: ['readwrite-unsafe'],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n OPFSCoopSyncVFS: {\n builds: ['sync', 'async', 'jspi'],\n // Capped on every engine (spec 2026-09-13, §10, D9): a pool of one was faster at startup and on bursts of reads, equal elsewhere, on Chromium and Firefox (POOL-SIZE, 2026-09-14). The handle still rotates between clients and tabs, which is why the COOPSYNC-BUSY retry stays.\n maxPoolSize: 1,\n poolLimitReason:\n 'it rotates one exclusive access handle between connections, so another worker only waits its turn',\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n exclusiveFileHandle: true,\n requires: ['opfs'],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n AccessHandlePoolVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason: 'it cannot share access handles between connections',\n multiConnection: false,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-pool',\n exclusiveFileHandle: true,\n requires: ['opfs'],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n // Two clients on one database break each other silently (AHP-2TAB,\n // 2026-09-01): the second resolves SELECT 1 but cannot read any table. An\n // origin-wide connection lock ensures the second client fails fast with\n // DATABASE_IN_USE instead of appearing healthy and being useless.\n exclusiveConnection: true,\n exclusiveConnectionWithout: [],\n // The one VFS that clears the bar for a default. Upstream: \"there is no\n // drawback to using PRAGMA locking_mode=exclusive\" here, because this VFS\n // does not allow multiple connections anyway — and exclusive locking is\n // what lets SQLite use its own WAL without shared memory, which no VFS in\n // this set provides. Measured 2026-09-02 on both engines: ~4.7x faster on\n // 200 single-statement transactions (Chromium 4.2 -> 0.9 ms/write, Firefox\n // 2.0 -> 0.5), with the access-handle pool holding the same five databases\n // either way — SQLite removes the -wal on a clean close, so it costs no\n // slot at rest. `mem:measurements`.\n defaultPragmas: { locking_mode: 'exclusive', journal_mode: 'wal' },\n },\n IDBBatchAtomicVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'indexeddb',\n layout: 'idb-store',\n exclusiveFileHandle: false,\n requires: [],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: true,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n IDBMirrorVFS: {\n builds: ['async', 'jspi'],\n // Measured 2026-08-25, not inferred: `CREATE TABLE` → `INSERT` → `SELECT`\n // at poolSize 2, 300 rounds under a loaded suite, failed 5 times — with\n // `no such table` (a connection not seeing a committed statement) and\n // `database is locked`. Nothing at all in 60 rounds unloaded, which is why\n // four sightings over two days never reproduced on demand. See MIRROR-1 in\n // mem:follow-ups for the method.\n //\n // It mirrors the whole database in memory PER WORKER and propagates\n // commits over BroadcastChannel, asynchronously — so a pool holds copies\n // that diverge, the same shape that had OPFSPermutedVFS removed from this\n // library. The commit barrier cannot rescue it: its prelude refreshes page\n // 1 through a real read transaction, and there is nothing fresher to read\n // on a connection whose mirror has not received the broadcast yet.\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages are mirrored per worker and commits propagate asynchronously, so a larger pool reads stale data or fails outright',\n multiConnection: false,\n persistent: true,\n // Upstream: \"keeps all files in memory, persisting database files to\n // IndexedDB\", and the whole database must fit in available memory.\n memoryModel: 'whole-database',\n storage: 'indexeddb',\n layout: 'idb-store',\n exclusiveFileHandle: false,\n requires: [],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n // `multiConnection: false` marks concurrent-writer unsafety (MIRROR-1),\n // not isolation. Two clients share data over BroadcastChannel (measured\n // 2026-09-01, 3/3 both engines), so no exclusive lock is needed or correct.\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n OPFSAnyContextVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: null,\n poolLimitReason: null,\n multiConnection: true,\n persistent: true,\n memoryModel: 'page-cache',\n storage: 'opfs',\n layout: 'opfs-path',\n exclusiveFileHandle: false,\n requires: ['opfs', 'writable-stream'],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n MemoryVFS: {\n builds: ['sync', 'async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages live in the worker that opened them, so a larger pool would open independent databases that diverge silently',\n multiConnection: false,\n persistent: false,\n memoryModel: 'whole-database',\n storage: 'memory',\n layout: 'memory',\n exclusiveFileHandle: false,\n requires: [],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n MemoryAsyncVFS: {\n builds: ['async', 'jspi'],\n maxPoolSize: 1,\n poolLimitReason:\n 'its pages live in the worker that opened them, so a larger pool would open independent databases that diverge silently',\n multiConnection: false,\n persistent: false,\n memoryModel: 'whole-database',\n storage: 'memory',\n layout: 'memory',\n exclusiveFileHandle: false,\n requires: [],\n degradesWithout: [],\n singleConnectionWithout: [],\n extraFileSuffixes: [],\n yieldsDuringStatements: false,\n exclusiveConnection: false,\n exclusiveConnectionWithout: [],\n defaultPragmas: {},\n },\n} as const satisfies Record<string, VFSCapability>;\n\nexport type SQLiteVFS = keyof typeof VFS_CAPABILITIES;\n\n/** The build used when the caller does not name one. */\nexport const defaultBuildFor = (vfs: SQLiteVFS): SQLiteBuild =>\n VFS_CAPABILITIES[vfs].builds[0];\n","import {\n BUILD_REQUIREMENTS,\n type PlatformFeature,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n} from './types';\n\n/**\n * Synchronous platform probes, keyed by FEATURE rather than by VFS or by build.\n * That is what lets a VFS requirement and a build requirement travel one path.\n *\n * `WebAssembly.Suspending` is cast rather than declared globally: it is not in\n * lib.dom, and a global augmentation would leak the assertion into every file.\n */\nconst PROBES: Partial<Record<PlatformFeature, () => boolean>> = {\n opfs: () =>\n typeof navigator !== 'undefined' &&\n typeof navigator.storage?.getDirectory === 'function' &&\n typeof FileSystemFileHandle !== 'undefined',\n jspi: () =>\n typeof (WebAssembly as { Suspending?: unknown }).Suspending === 'function',\n 'writable-stream': () =>\n typeof FileSystemFileHandle !== 'undefined' &&\n typeof FileSystemFileHandle.prototype.createWritable === 'function',\n 'cross-origin-isolated': () => globalThis.crossOriginIsolated === true,\n};\n\n/**\n * Features with no synchronous probe FROM THE PAGE. Declared, never merely\n * omitted.\n *\n * WebIDL ignores an unknown dictionary member, so passing `readwrite-unsafe`\n * and seeing no error proves nothing. `FileSystemSyncAccessHandle`, whose\n * `mode` attribute would tell, is exposed to dedicated workers only — so the\n * pool's own workers probe it (`src/worker/probes.ts`), and the benchmark page\n * opens two handles in a worker of its own. A feature in neither table is a\n * mistake, and `tests/unit/capabilities.test.ts` says so.\n */\nconst UNPROBEABLE = new Set<PlatformFeature>(['readwrite-unsafe']);\n\n/** Human-readable names for the error messages. */\nconst FEATURE_LABEL: Record<PlatformFeature, string> = {\n opfs: 'OPFS',\n jspi: 'JSPI',\n 'writable-stream': 'FileSystemWritableFileStream',\n 'readwrite-unsafe': 'readwrite-unsafe access handles',\n 'cross-origin-isolated': 'cross-origin isolation',\n};\n\n/**\n * Every feature this module can decide: probed, or explicitly exempt. A\n * feature declared in a capability table and absent here is a mistake, and\n * tests/unit/capabilities.test.ts is what says so.\n */\nexport const KNOWN_FEATURES: ReadonlySet<PlatformFeature> = new Set([\n ...(Object.keys(PROBES) as PlatformFeature[]),\n ...UNPROBEABLE,\n]);\n\n/** What this engine can do, probed once by the caller. */\nexport const detectFeatures = (): ReadonlySet<PlatformFeature> => {\n const found = new Set<PlatformFeature>();\n for (const [feature, probe] of Object.entries(PROBES)) {\n if (probe()) found.add(feature as PlatformFeature);\n }\n return found;\n};\n\n/**\n * The first feature this pair needs and this engine lacks, or null.\n *\n * Pure, and takes `available` rather than probing, because the branches worth\n * testing are the negative ones and they are unreachable in a real browser:\n * JSPI cannot be taken away from Chromium.\n */\nexport const missingFeature = (\n vfs: SQLiteVFS,\n build: SQLiteBuild,\n available: ReadonlySet<PlatformFeature>,\n): PlatformFeature | null => {\n const required: readonly PlatformFeature[] = [\n ...VFS_CAPABILITIES[vfs].requires,\n ...BUILD_REQUIREMENTS[build],\n ];\n for (const feature of required) {\n if (UNPROBEABLE.has(feature)) continue;\n if (!available.has(feature)) return feature;\n }\n return null;\n};\n\n/**\n * The message for a missing feature, derived from the capability tables so it\n * cannot drift from them. Names an alternative build when the build is at\n * fault, and VFS that do not need the feature when the VFS is.\n */\nexport const describeMissing = (\n vfs: SQLiteVFS,\n build: SQLiteBuild,\n feature: PlatformFeature,\n): string => {\n const label = FEATURE_LABEL[feature];\n\n if (\n (BUILD_REQUIREMENTS[build] as readonly PlatformFeature[]).includes(feature)\n ) {\n const others = VFS_CAPABILITIES[vfs].builds.filter((b) => b !== build);\n const suffix = others.length\n ? ` ${vfs} also runs on: ${others.join(', ')}.`\n : '';\n return `This browser does not support ${label}, which the '${build}' build requires.${suffix}`;\n }\n\n const alternatives = (Object.keys(VFS_CAPABILITIES) as SQLiteVFS[]).filter(\n (name) =>\n !(VFS_CAPABILITIES[name].requires as readonly PlatformFeature[]).includes(\n feature,\n ),\n );\n const suffix = alternatives.length\n ? ` Without it, these store elsewhere: ${alternatives.join(', ')}.`\n : '';\n return `This browser does not support ${label}, which ${vfs} requires.${suffix}`;\n};\n","/**\n * Every failure this library raises on its own behalf. A caller discriminates\n * on `code`, or on `name` — they carry the same value, so `err.name` reads the\n * way `'AbortError'` does on the DOMException an aborted signal throws.\n * `DATABASE_IN_USE` is this library's own: a database that a live client holds,\n * as opposed to `BUSY`, which covers a transient conflict worth retrying.\n * `DATABASE_NOT_FOUND` is raised only by `deleteDatabase`: there is nothing at\n * that name to delete. `createSQLiteClient` creates what is absent, so it has\n * no such case.\n * `UNSUPPORTED` means the platform cannot answer the question — raised by\n * `inspectDatabase` where Web Locks is missing, because reporting zero clients\n * there would be indistinguishable from a database nobody holds.\n * `OPERATION_TIMEOUT` is the `timeout` a caller set on a call being spent. It is\n * deliberately not `TIMEOUT`, which means a deadline this library imposed on\n * itself — a worker that never became ready, a deletion that did not complete.\n * `STATEMENT_FAILED` is a statement SQLite refused or failed for any reason\n * but a lock conflict — a constraint, a syntax error, a full disk. `message` is\n * SQLite's own; `sqliteCode` carries its result code, and `sqliteExtendedCode`\n * its subtype when SQLite reports one.\n */\nimport type {\n SQLiteExtendedResultCode,\n SQLiteResultCode,\n} from './sqlite-codes';\n\nexport type SQLiteErrorCode =\n | 'NOT_A_READ_QUERY'\n | 'CLIENT_CLOSED'\n | 'WORKER_CRASHED'\n | 'TIMEOUT'\n | 'PROTOCOL_ERROR'\n | 'INVALID_IDENTIFIER'\n | 'INVALID_OPTION'\n | 'INVALID_PRAGMA'\n | 'BULK_WRITE_FAILED'\n | 'BUSY'\n | 'STATEMENT_FAILED'\n | 'DATABASE_IN_USE'\n | 'DATABASE_NOT_FOUND'\n | 'READ_ONLY_TRANSACTION'\n | 'UNSUPPORTED'\n | 'WORKER_BUSY'\n | 'OPERATION_TIMEOUT'\n | 'TRANSACTION_CLOSED';\n\nexport class SQLiteError extends Error {\n readonly code: SQLiteErrorCode;\n /**\n * SQLite's own numeric result code, present only when the failure came from\n * SQLite rather than from this library. Always the PRIMARY code. `BUSY`\n * covers both SQLITE_BUSY (5) and SQLITE_LOCKED (6); this is how a caller\n * tells them apart. Typed `SQLiteResultCode` (D10): since `sqliteCodeOf`,\n * it is always a primary code of the bundled SQLite, so comparing it with\n * an extended code does not compile.\n */\n readonly sqliteCode?: SQLiteResultCode;\n /**\n * SQLite's extended result code, present only when a statement SQLite ran\n * failed WITH A SUBTYPE — `STATEMENT_FAILED` or `BUSY` from a query, never\n * an open or a delete: 2067 (`SQLITE_EXTENDED_CODES.CONSTRAINT_UNIQUE`)\n * under `sqliteCode` 19. Absent when SQLite has no subtype for the failure\n * (a full disk, a syntax error), since `sqliteCode` already says it. For a\n * subtype SQLite reports, `(sqliteExtendedCode & 0xff) === sqliteCode` is\n * SQLite's own guarantee, not something this library enforces: the client\n * deliberately lets a differing value through (a 0 from a wrong read).\n * Typed open (`SQLiteExtendedResultCode | (number & {})`, D10): D9 lets a\n * wrong read through, which a strict type would misdescribe.\n */\n readonly sqliteExtendedCode?: SQLiteExtendedResultCode | (number & {});\n /**\n * The `timeout` that was exceeded, in milliseconds. Present only on\n * `OPERATION_TIMEOUT`, so a log need not parse the message for it.\n */\n readonly timeout?: number;\n\n constructor(\n code: SQLiteErrorCode,\n message: string,\n options?: {\n cause?: unknown;\n sqliteCode?: SQLiteResultCode;\n sqliteExtendedCode?: SQLiteExtendedResultCode | (number & {});\n timeout?: number;\n },\n ) {\n super(message, options);\n this.code = code;\n this.name = code;\n if (options?.sqliteCode !== undefined) this.sqliteCode = options.sqliteCode;\n if (options?.sqliteExtendedCode !== undefined)\n this.sqliteExtendedCode = options.sqliteExtendedCode;\n if (options?.timeout !== undefined) this.timeout = options.timeout;\n }\n}\n\n/**\n * A batch failed. Raised by `bulkWrite().close()` and by `output().close()`.\n *\n * The counters exist because the old behaviour was silent: batches were chained\n * on one shared promise, so after a rejection every later `.then` was skipped —\n * while their rows had already been spliced out of the buffer (B5). A caller now\n * learns how much of its data reached the database.\n */\nexport class SQLiteBulkWriteError extends SQLiteError {\n readonly rowsWritten: number;\n readonly rowsNotWritten: number;\n\n constructor(\n message: string,\n counts: { rowsWritten: number; rowsNotWritten: number },\n options?: { cause?: unknown },\n ) {\n super('BULK_WRITE_FAILED', message, options);\n this.rowsWritten = counts.rowsWritten;\n this.rowsNotWritten = counts.rowsNotWritten;\n }\n}\n","/**\n * A thin wrapper over `navigator.locks`, used by `output()` to make its staging\n * tables collectable across tabs (D3).\n *\n * The staging lock is NOT mutual exclusion — nothing contends for its name. It\n * is a liveness marker: a lock held for as long as a staging table exists is\n * what lets another tab's sweep tell an in-flight table from an orphan. A tab\n * that is killed has its locks released by the browser, so its orphans become\n * collectable immediately, with no timestamp and no grace period.\n */\n\nimport type { SQLiteVFS } from './types';\nimport { VFS_CAPABILITIES } from './types';\n\n/** One entry in the lock registry as returned by `query()`. */\ntype QueriedLock = { name?: string; mode?: string; clientId?: string };\n\n/** The slice of the Web Locks API this module uses. */\ntype LockManager = {\n request: (\n name: string,\n optionsOrCallback: any,\n callback?: (lock: unknown) => Promise<unknown>,\n ) => Promise<unknown>;\n query: () => Promise<{ held?: QueriedLock[]; pending?: QueriedLock[] }>;\n};\n\nexport type Locks = {\n /** False when the Web Locks API is missing; every method then no-ops. */\n readonly available: boolean;\n /**\n * Acquires `name` and resolves with the function that releases it.\n *\n * `mode: 'shared'` is what the epoch marker uses: many realms may hold the\n * same name at once, so publishing never waits and two realms can never\n * collide on one epoch number. `signal` aborts the WAIT — never the hold —\n * and makes the request reject with `AbortError`.\n *\n * `ifAvailable: true` mirrors `tryWithLock`'s semantics: the real API hands\n * the callback `null` rather than waiting when the lock is held elsewhere.\n * Resolves with `undefined` in that case instead of waiting. Never waits —\n * that is the point for the connection guard.\n */\n hold(\n name: string,\n options?: { mode?: 'exclusive' | 'shared'; signal?: AbortSignal },\n ): Promise<() => void>;\n hold(\n name: string,\n options: {\n mode?: 'exclusive' | 'shared';\n signal?: AbortSignal;\n ifAvailable: true;\n },\n ): Promise<(() => void) | undefined>;\n /** Runs `fn` while holding `name` exclusively. */\n withLock: <T>(name: string, fn: () => Promise<T>) => Promise<T>;\n /**\n * Runs `fn` while holding `name`, or skips it entirely when the lock is held\n * elsewhere. Never waits — which is the point: the staging sweep is\n * opportunistic, and awaiting this lock inside an open transaction would\n * hold SQLite's write lock while waiting on a holder that may itself be\n * waiting for that write lock.\n *\n * Resolves `true` if `fn` ran, `false` if it was skipped.\n */\n tryWithLock: (name: string, fn: () => Promise<unknown>) => Promise<boolean>;\n /** Names currently held anywhere in this origin — every tab included. */\n heldNames: () => Promise<string[]>;\n /**\n * The origin's whole lock registry: held AND pending, each with the realm\n * holding or awaiting it.\n *\n * `heldNames()` answers a different, cheaper question and keeps its own\n * shape — `epochsFor` only ever needs names.\n */\n entries: () => Promise<LockEntries>;\n};\n\n/** One entry of the origin's lock registry, held or pending. */\nexport type LockEntry = {\n readonly name: string;\n readonly mode: 'exclusive' | 'shared';\n readonly clientId: string;\n};\n\nexport type LockEntries = {\n readonly held: readonly LockEntry[];\n readonly pending: readonly LockEntry[];\n};\n\nconst STAGING_PREFIX = '__bsq_staging_';\n\nexport const stagingTableName = (uuid: string) =>\n `${STAGING_PREFIX}${uuid.replace(/-/g, '_')}`;\n\nexport const isStagingTable = (table: string) =>\n table.startsWith(STAGING_PREFIX);\n\nexport const stagingLockName = (file: string, table: string) =>\n `bsq:staging:${file}:${table}`;\n\nexport const sweepLockName = (file: string) => `bsq:sweep:${file}`;\n\n/**\n * The marker a client holds to publish that it is alive on a database.\n *\n * Held in SHARED mode and contended by NOBODY: like `bsq:staging` this is a\n * liveness marker, not mutual exclusion. `bsq:conn` stays the only occupancy\n * detector `deleteDatabase` rests on — a second one would diverge from it.\n *\n * The label is `encodeURIComponent`d, which escapes `:` as `%3A`. That is what\n * makes the tail split unambiguously into exactly three segments whatever the\n * consumer names their client. The FILE may itself contain a colon, which is\n * why the reader rebuilds the exact prefix instead of scanning for separators —\n * the same trap `epochsFor` documents.\n */\nexport const clientMarkerName = (\n vfs: SQLiteVFS,\n file: string,\n id: string,\n clientName: string,\n): string =>\n `bsq:client:${namespaceFor(vfs)}:${file}:${id}:${vfs}:${encodeURIComponent(clientName)}`;\n\nexport type ClientMarker = {\n readonly id: string;\n readonly vfs: SQLiteVFS;\n readonly name: string;\n};\n\nconst UUID_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Reads one of our markers, or `undefined` for anything else.\n *\n * Every rejection below is deliberate: a marker this version does not\n * understand — a future one carrying more segments, say — must be SKIPPED, not\n * guessed at. Guessing is how a reader reports another database's state.\n */\nexport const parseClientMarker = (\n lockName: string,\n vfs: SQLiteVFS,\n file: string,\n): ClientMarker | undefined => {\n const prefix = `bsq:client:${namespaceFor(vfs)}:${file}:`;\n if (!lockName.startsWith(prefix)) return undefined;\n\n const parts = lockName.slice(prefix.length).split(':');\n if (parts.length !== 3) return undefined;\n\n const [id, markerVfs, encoded] = parts as [string, string, string];\n if (!UUID_RE.test(id)) return undefined;\n if (!Object.hasOwn(VFS_CAPABILITIES, markerVfs)) return undefined;\n\n let name: string;\n try {\n name = decodeURIComponent(encoded);\n } catch {\n // Malformed percent-escapes throw URIError. An unreadable label is an\n // unreadable marker: skip it rather than report a mangled name.\n return undefined;\n }\n\n return { id, vfs: markerVfs as SQLiteVFS, name };\n};\n\n/**\n * The storage namespace a VFS writes into — derived from `layout`, NEVER from\n * the VFS name.\n *\n * `OPFSAdaptiveVFS`, `OPFSAnyContextVFS`, `OPFSCoopSyncVFS` and\n * `OPFSWriteAheadVFS` all walk from `navigator.storage.getDirectory()` and open\n * `getFileHandle(filename)`, so one database name is ONE file for all four. A\n * per-VFS key would let two of them write the same bytes without ever\n * excluding each other: a missed conflict corrupts, an invented one only slows.\n *\n * `idb-store` goes finer than its layout on purpose — its two VFS each own an\n * IndexedDB database named after their class, so grouping them would invent a\n * conflict for free. `opfs-pool` and `memory` are alone in their layout, so the\n * VFS name is already the namespace.\n *\n * `worker/worker.ts:627` gates on `layout` for the same reason, in those words.\n */\nexport const namespaceFor = (vfs: SQLiteVFS): string =>\n VFS_CAPABILITIES[vfs].layout === 'opfs-path' ? 'opfs' : vfs;\n\n/**\n * Whether two clients on this VFS can reach the same bytes at all.\n *\n * False for the memory VFS: its pages live in the worker that opened them and\n * `maxPoolSize` is 1, so two clients on one name are two independent\n * databases. Locking them against each other would be wrong as well as slow —\n * an origin round trip charged to the VFS chosen for speed. `delete.ts:79`\n * skips the same layout, for the same reason.\n */\nexport const sharesStorage = (vfs: SQLiteVFS): boolean =>\n VFS_CAPABILITIES[vfs].layout !== 'memory';\n\n/** Serializes database opening across the pool — replaces the SAB init mutex. */\nexport const initLockName = (vfs: SQLiteVFS, file: string) =>\n `bsq:init:${namespaceFor(vfs)}:${file}`;\n\n/**\n * Serializes WRITERS across every client and tab in the origin. Exclusive, so\n * at most one is held per database at any instant however many clients exist.\n */\nexport const writeLockName = (vfs: SQLiteVFS, file: string) =>\n `bsq:write:${namespaceFor(vfs)}:${file}`;\n\n/**\n * Origin-wide exclusive connection lock for VFS that cannot safely share a\n * database across clients (`exclusiveConnection: true` in `VFS_CAPABILITIES`).\n *\n * Held for the client's lifetime. A second `createSQLiteClient` that tries to\n * open the same database will fail its first query with `BUSY` instead of\n * silently reading a broken, frozen view — the failure mode measured as\n * AHP-2TAB (2026-09-01) where `SELECT 1` passes and `SELECT count(*) FROM\n * sqlite_master` returns 0 on an unfixable connection.\n *\n * The key uses `namespaceFor(vfs)` for the same reason `writeLockName` does:\n * the gate is by layout declaration, not by VFS name.\n */\nexport const connectionLockName = (vfs: SQLiteVFS, file: string) =>\n `bsq:conn:${namespaceFor(vfs)}:${file}`;\n\n/**\n * Which staging tables no live `output()` is using — pure, so it is driven by\n * Node tests rather than by two browser tabs.\n */\nexport const staleStagingTables = (\n tables: string[],\n heldNames: string[],\n file: string,\n): string[] => {\n const held = new Set(heldNames);\n return tables.filter((table) => !held.has(stagingLockName(file, table)));\n};\n\n/** The no-op Locks value for environments where the Web Locks API is absent. */\nexport const noOpLocks: Locks = {\n available: false,\n hold: async () => () => {},\n withLock: async (_name, fn) => fn(),\n tryWithLock: async (_name, fn) => {\n await fn();\n return true;\n },\n heldNames: async () => [],\n entries: async () => ({ held: [], pending: [] }),\n};\n\nexport const createLocks = (\n manager: LockManager | undefined = globalThis.navigator?.locks as\n | LockManager\n | undefined,\n): Locks => {\n if (!manager) return noOpLocks;\n\n return {\n available: true,\n hold: ((\n name: string,\n options?: {\n mode?: 'exclusive' | 'shared';\n signal?: AbortSignal;\n ifAvailable?: boolean;\n },\n ) =>\n new Promise<(() => void) | undefined>((resolveReleaser, rejectOuter) => {\n const ifAvail: boolean = options?.ifAvailable === true;\n let release!: () => void;\n const held = new Promise<void>((resolveHeld) => {\n release = resolveHeld;\n });\n // Built conditionally rather than with `signal: options?.signal`: an\n // explicit undefined is not reliably \"absent\" across engines, and Web\n // Locks refuses `signal` alongside `ifAvailable`.\n const requestOptions: {\n mode: string;\n signal?: AbortSignal;\n ifAvailable?: true;\n } = { mode: options?.mode ?? 'exclusive' };\n if (options?.signal) requestOptions.signal = options.signal;\n // `ifAvailable` must be absent (not merely false) when not requested —\n // Web Locks refuses `signal` alongside `ifAvailable`, so we only set it\n // when the caller explicitly asked for the non-blocking behaviour.\n if (ifAvail) requestOptions.ifAvailable = true;\n manager\n .request(name, requestOptions, (lock) => {\n // `ifAvailable` hands the callback null instead of waiting.\n if (ifAvail && !lock) {\n resolveReleaser(undefined);\n return Promise.resolve();\n }\n resolveReleaser(release);\n return held;\n })\n .catch(rejectOuter);\n })) as Locks['hold'],\n withLock: <T>(name: string, fn: () => Promise<T>) =>\n manager.request(name, { mode: 'exclusive' }, () => fn()) as Promise<T>,\n tryWithLock: async (name, fn) => {\n let ran = false;\n await manager.request(\n name,\n { mode: 'exclusive', ifAvailable: true },\n async (lock) => {\n // `ifAvailable` hands the callback null instead of waiting.\n if (!lock) return;\n ran = true;\n await fn();\n },\n );\n return ran;\n },\n heldNames: async () => {\n const snapshot = await manager.query();\n return (snapshot.held ?? [])\n .map((lock) => lock.name)\n .filter((name): name is string => typeof name === 'string');\n },\n entries: async () => {\n const snapshot = await manager.query();\n // An entry without a name or a clientId cannot be attributed, and a\n // half-read entry is worse than a missing one: it would join the roster\n // as an anonymous client nobody can close.\n const read = (list: QueriedLock[] | undefined): LockEntry[] =>\n (list ?? [])\n .filter(\n (lock): lock is QueriedLock & { name: string; clientId: string } =>\n typeof lock.name === 'string' &&\n typeof lock.clientId === 'string',\n )\n .map((lock) => ({\n name: lock.name,\n mode:\n lock.mode === 'shared'\n ? ('shared' as const)\n : ('exclusive' as const),\n clientId: lock.clientId,\n }));\n return { held: read(snapshot.held), pending: read(snapshot.pending) };\n },\n };\n};\n","import { SQLiteError } from './errors';\nimport {\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n type WasmLocation,\n} from './types';\n\nexport const sqlParams = () => {\n // `unknown`, not `any`: these are SQL bind values and they are never\n // inspected here — only counted, de-duplicated by identity, and handed on.\n // The public query surface has always said `unknown[]`; this was the one\n // place that quietly said less.\n const sqlParamsMap = new Map<unknown, number>();\n const sqlParams: unknown[] = [];\n\n const addParam = (v: unknown) => {\n let paramIndex = sqlParamsMap.get(v);\n if (!paramIndex) {\n paramIndex = sqlParams.length + 1;\n sqlParamsMap.set(v, paramIndex);\n sqlParams.push(v);\n }\n return `?${paramIndex.toString().padStart(3, '0')}`;\n };\n const addParamArray = (values: unknown[]) => {\n return values.map((v) => addParam(v)).join(',');\n };\n return {\n addParam,\n addParamArray,\n params: sqlParams,\n };\n};\n\n/**\n * Every statement SQLite treats as a write, or that must be serialized through\n * the single writer worker. Matched anywhere in the string, not just at the\n * start: the worker executes `;`-separated statements, so a write hiding after\n * a semicolon must still route to the writer.\n */\nconst WRITE_KEYWORDS =\n /\\b(INSERT|REPLACE|UPDATE|DELETE|CREATE|DROP|ALTER|VACUUM|ANALYZE|REINDEX|SAVEPOINT|RELEASE|BEGIN|COMMIT|ROLLBACK|ATTACH|DETACH|PRAGMA)\\b/i;\n\n/**\n * Routing predicate: is this statement provably a read?\n *\n * Two conditions, both required. The statement must OPEN with a read keyword,\n * and it must contain no write keyword anywhere. The first condition alone is\n * not enough — the worker executes `;`-separated statements, so `SELECT 1;\n * DROP TABLE t` opens as a read and is not one. The second alone is not enough\n * either — it would admit any unrecognised statement as a read.\n *\n * The previous blocklist missed VACUUM, ALTER, ANALYZE, REINDEX, SAVEPOINT and\n * a manual BEGIN, so those ran on the read pool: a VACUUM could execute on an\n * arbitrary worker while the writer held an open transaction, bypassing\n * exclusivity one layer above the pool.\n *\n * Misclassification now fails toward the writer — correct, merely slower. A\n * read whose text merely mentions a write keyword (`SELECT 'INSERT'`, or\n * `EXPLAIN INSERT ...`, which never executes) is serialized needlessly. That\n * is the accepted price of never routing a write to the read pool.\n */\n/**\n * A statement that is nothing but a single PRAGMA lookup: no assignment, no\n * argument, nothing after it. Anchoring at `$` is what makes this safe for\n * free — `PRAGMA journal_mode; DROP TABLE t` does not match, and neither does\n * `PRAGMA journal_mode=WAL`, whose `=` breaks the match.\n */\nconst READ_PRAGMA = /^\\s*PRAGMA\\s+(\\w+\\.)?\\w+\\s*;?\\s*$/i;\n\nexport const isReadQuery = (sql: string) =>\n READ_PRAGMA.test(sql) ||\n (/^\\s*(SELECT|EXPLAIN|VALUES|WITH)\\b/i.test(sql) &&\n !WRITE_KEYWORDS.test(sql));\n\nexport const isWriteQuery = (sql: string) => !isReadQuery(sql);\n\n/**\n * Whether `sql` manages the transaction or its savepoints rather than data\n * (spec 2026-09-11, D8). Never wrapped in the library's savepoint: there is\n * nothing to undo, and a `RELEASE u` run inside it would pop it along with\n * `u`. The leading keyword decides: these statements are never compound.\n */\nexport const isTransactionControl = (sql: string) =>\n /^\\s*(SAVEPOINT|RELEASE|ROLLBACK|BEGIN|COMMIT|END)\\b/i.test(sql);\n\n/**\n * Combines two abort signals into one that fires with the reason of whichever\n * source aborted first, plus the `release()` that unsubscribes it.\n *\n * NOT `AbortSignal.any()`. That is Chrome 116 / Firefox 124 / Safari 17.4, far\n * above this library's floor (Chrome 92 / Firefox 95 / Safari 15.4), and\n * adopting it would raise every row of the generated VFS.md matrix for every\n * consumer.\n *\n * The common case allocates nothing: with one side absent, or one side already\n * aborted, the surviving signal is returned as itself — no listener, no\n * teardown owed, and the caller sees the original `reason` rather than a copy.\n */\nexport const mergeSignals = (\n a: AbortSignal | undefined,\n b: AbortSignal | undefined,\n): { signal: AbortSignal | undefined; release: () => void } => {\n const noop = () => {};\n if (!a || a === b) return { signal: b, release: noop };\n if (!b) return { signal: a, release: noop };\n if (a.aborted) return { signal: a, release: noop };\n if (b.aborted) return { signal: b, release: noop };\n\n const merged = new AbortController();\n const relay = (source: AbortSignal) => () => merged.abort(source.reason);\n const onA = relay(a);\n const onB = relay(b);\n a.addEventListener('abort', onA, { once: true });\n b.addEventListener('abort', onB, { once: true });\n return {\n signal: merged.signal,\n release: () => {\n a.removeEventListener('abort', onA);\n b.removeEventListener('abort', onB);\n },\n };\n};\n\n/**\n * The `timeout` option: a wall-clock budget in milliseconds, counted from the\n * call. Returns the signal the call should actually use — the caller's own,\n * merged with one this library owns and aborts when the budget is spent.\n *\n * The abort reason IS the error the caller receives. Every abort path in this\n * library rejects with `signal.reason`, and `mergeSignals` relays a reason\n * verbatim, so nothing downstream has to ask which signal fired. That is why\n * this is an AbortController and a setTimeout rather than\n * `AbortSignal.timeout()`, which offers no way to set the reason.\n *\n * `release()` is owed exactly once, however the call ends.\n */\nexport const withDeadline = (\n options:\n | { signal?: AbortSignal | undefined; timeout?: number | undefined }\n | undefined,\n method: string,\n): { signal: AbortSignal | undefined; release: () => void } => {\n const budget = options?.timeout;\n if (budget === undefined)\n return { signal: options?.signal, release: () => {} };\n\n const controller = new AbortController();\n const timer = setTimeout(() => {\n controller.abort(\n new SQLiteError(\n 'OPERATION_TIMEOUT',\n `${method}() exceeded its timeout of ${budget} ms.`,\n { timeout: budget },\n ),\n );\n }, budget);\n const { signal, release } = mergeSignals(options?.signal, controller.signal);\n return {\n signal,\n release: () => {\n clearTimeout(timer);\n release();\n },\n };\n};\n\n/**\n * Routing guard for the read-shaped methods (`read`, `chunk`, `stream`, `first`).\n * Throws before a lease is taken, so a rejected statement costs no pool capacity.\n *\n * A bare read pragma (`PRAGMA journal_mode`) is accepted; a pragma that assigns\n * (`PRAGMA journal_mode=WAL`), takes an argument, or is followed by anything\n * else must go through `write()`.\n */\nexport const assertReadable = (sql: string, method: string): void => {\n if (isReadQuery(sql)) return;\n const keyword = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n throw new SQLiteError(\n 'NOT_A_READ_QUERY',\n `${method}() only accepts statements that are provably reads; \"${keyword}\" must go through write(). ` +\n `Note that a PRAGMA that assigns a value or takes an argument is a write.`,\n );\n};\n\n/**\n * Quotes an SQL identifier so it can never be read as anything but a name.\n *\n * The library interpolates table, column and index names into generated SQL —\n * `bulkWrite`, `output` and their indexes. wa-sqlite's `statements()` executes\n * `;`-separated statements, so an unquoted name is a stacked-query injection\n * (B4). Quoting is what makes `t\"; DROP TABLE users; --` one identifier.\n *\n * Note that quoting preserves case in `sqlite_master`; SQLite still resolves\n * names case-insensitively.\n */\nexport const quoteIdent = (name: string): string => {\n if (!name)\n throw new SQLiteError('INVALID_IDENTIFIER', 'Identifier cannot be empty');\n if (name.includes('\\0'))\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Identifier contains a NUL character: ${JSON.stringify(name)}`,\n );\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n};\n\n/** `INTEGER`, `TEXT`, `VARCHAR(255)`, `DECIMAL(10, 2)` — nothing else. */\nconst COLUMN_TYPE = /^[A-Za-z][A-Za-z0-9 ]*(\\([0-9, ]+\\))?$/;\n\n/**\n * A column type is not an identifier and cannot be quoted — it is an SQL\n * fragment the caller writes. It is validated by shape instead: this is the\n * narrowed, not closed, channel documented in the spec (§1.2).\n */\nexport const assertColumnType = (type: string, column: string): string => {\n const trimmed = type.trim();\n if (!COLUMN_TYPE.test(trimmed))\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Column \"${column}\" declares an unsupported type ${JSON.stringify(type)}. ` +\n `A type must be a word, optionally followed by numeric arguments, e.g. \"INTEGER\" or \"VARCHAR(255)\".`,\n );\n return trimmed;\n};\n\n/**\n * A GENERATED ALWAYS AS expression is caller-authored SQL. It must at least be\n * parenthesised and free of statement separators, so it cannot escape its slot.\n */\nexport const assertGeneratedExpression = (\n expr: string,\n column: string,\n): string => {\n const trimmed = expr.trim();\n if (\n !trimmed.startsWith('(') ||\n !trimmed.endsWith(')') ||\n trimmed.includes(';')\n )\n throw new SQLiteError(\n 'INVALID_IDENTIFIER',\n `Column \"${column}\" declares an invalid generated expression ${JSON.stringify(expr)}. ` +\n `It must be parenthesised and contain no \";\", e.g. \"(base * 2)\".`,\n );\n return trimmed;\n};\n\nconst PRAGMA_NAME = /^[A-Za-z_]\\w*$/;\nconst PRAGMA_INTEGER = /^-?\\d+$/;\nconst PRAGMA_LITERAL = /^'([^']|'')*'$/;\n\n/**\n * Renders the client's `pragmas` option into executable statements, rejecting\n * anything that is not provably a name and a scalar value (B4).\n *\n * Validation is syntactic rather than a closed list of the ~60 SQLite pragmas:\n * a fixed list makes every legitimate pragma outside it unreachable and drifts\n * with SQLite versions, for no additional protection — no \";\", no parenthesis\n * and no comment marker survives these three shapes either.\n *\n * Called twice: by the client at construction, so a bad configuration fails at\n * `createSQLiteClient()` rather than inside an unrelated query, and by the\n * worker at open, which is the only place the statements actually run.\n */\n/**\n * The PRAGMAs a client actually applies: the VFS's declared defaults with the\n * consumer's own layered over them.\n *\n * **Merged, never replaced.** A consumer who passes one pragma is answering a\n * question of their own — `foreign_keys` is the usual one — not declining the\n * VFS's defaults, and replacing would silently drop every default the moment\n * they set anything at all. A key they DO set always wins, which is how a\n * default is refused: pass `journal_mode` yourself and yours is what runs.\n *\n * Order matters and is the whole function: spread the defaults first.\n */\nexport const resolvePragmas = (\n vfs: SQLiteVFS,\n pragmas: Record<string, string> | undefined,\n): Record<string, string> => ({\n ...VFS_CAPABILITIES[vfs].defaultPragmas,\n ...pragmas,\n});\n\nexport const renderPragmas = (pragmas: Record<string, string>): string[] =>\n Object.entries(pragmas).map(([key, value]) => {\n if (!PRAGMA_NAME.test(key))\n throw new SQLiteError(\n 'INVALID_PRAGMA',\n `Invalid pragma name ${JSON.stringify(key)}: a pragma name must match ${PRAGMA_NAME}.`,\n );\n const raw = String(value).trim();\n if (PRAGMA_INTEGER.test(raw) || PRAGMA_NAME.test(raw))\n return `PRAGMA ${key}=${raw}`;\n // PRAGMA_LITERAL already guarantees raw is a well-formed SQLite string\n // literal (outer quotes present, internal single quotes doubled per '').\n // Using raw directly is correct and simpler than re-escaping the content.\n if (PRAGMA_LITERAL.test(raw)) return `PRAGMA ${key}=${raw}`;\n throw new SQLiteError(\n 'INVALID_PRAGMA',\n `Invalid value ${JSON.stringify(value)} for pragma \"${key}\": expected an integer, a bare word such as WAL, or a quoted literal.`,\n );\n });\n\n/**\n * The single definition of database identity — one string used everywhere:\n * the worker open call, the VFS, the epoch registry and every lock name.\n *\n * The form is **relative** (no leading `/`). `URL.pathname` is absolute by\n * construction, so stripping the slash is necessary: SQLite core checks\n * `nPathname + 8 > mxPathname` (64, `node_modules/wa-sqlite/src/VFS.js:10`)\n * before `xOpen`, and a leading `/` costs a character the budget cannot spare —\n * measured at task 1: it broke all 96 browser tests on 56-char names. The\n * strip gives that character back, so a 56-char name that the caller wrote\n * still fits after normalization. The VFS re-parse (`new URL(zName, 'file://')`\n * for four of five; `AccessHandlePoolVFS` via `'file://localhost/'`) produces\n * identical `pathname` whether the open call receives `'data'` or `'/data'`,\n * so the opened OPFS file is the same regardless.\n *\n * Idempotent: the VFS re-parse of an already-normalized name is a no-op.\n */\nexport const normalizeDatabaseFile = (file: string): string =>\n new URL(file, 'file://').pathname.replace(/^\\//, '');\n\n/**\n * Turns the `wasmUrl` client option into the absolute location posted in the\n * `open` message, or `undefined` when the option was not given.\n *\n * `undefined` is the load-bearing case: the worker sets Emscripten's\n * `locateFile` only when it receives a location, and `findWasmBinary` takes its\n * `new URL('wa-sqlite.wasm', import.meta.url)` branch whenever `locateFile` is\n * absent. So an omitted option leaves resolution byte-for-byte as it was\n * before this option existed — which is the entire contract of the escape\n * hatch.\n *\n * Resolution happens **here**, on the client, against the page: what the\n * consumer writes means what it means from the page they wrote it on, not from\n * the worker's own directory one level down. The callback is therefore called\n * once, at client construction, before any worker exists — its result is\n * reused by every worker in the pool and by every restart.\n *\n * A string is a **directory** and gets its missing trailing slash back before\n * resolution: URL resolution treats a last segment without a slash as a\n * document and replaces it, so `'/static/wasm'` would otherwise silently mean\n * `/static/`. A callback names a **file**, so nothing is appended to it.\n *\n * @throws `SQLiteError('INVALID_OPTION')` when the value cannot be parsed as a\n * URL — synchronously, at construction, rather than as an opaque open failure\n * from a worker that could not fetch its module.\n */\nexport const resolveWasmLocation = (\n wasmUrl: string | ((build: SQLiteBuild) => string) | undefined,\n build: SQLiteBuild,\n baseHref: string,\n): WasmLocation | undefined => {\n if (wasmUrl === undefined) return undefined;\n\n const isCallback = typeof wasmUrl === 'function';\n const raw = isCallback ? wasmUrl(build) : wasmUrl;\n const value = isCallback || raw.endsWith('/') ? raw : `${raw}/`;\n\n let href: string;\n try {\n href = new URL(value, baseHref).href;\n } catch {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `wasmUrl could not be parsed as a URL: ${JSON.stringify(raw)}. Pass a directory (relative, absolute, or a full URL), or a callback returning the full URL of one .wasm file.`,\n );\n }\n\n return isCallback ? { file: href } : { base: href };\n};\n","import type {\n Schema,\n SQLiteBulkWriteOptions,\n SQLiteOutputOptions,\n SQLiteOutputRow,\n SQLiteTransactionOptions,\n} from './api';\nimport { SQLiteBulkWriteError } from './errors';\nimport {\n type Locks,\n stagingLockName,\n stagingTableName,\n staleStagingTables,\n sweepLockName,\n} from './locks';\nimport type { Logger } from './logger';\nimport {\n assertColumnType,\n assertGeneratedExpression,\n quoteIdent,\n withDeadline,\n} from './utils';\n\n// Structural, and deliberately narrower than SQLiteQueryAPI: bulk needs only\n// these three calls, and requiring the full surface would make every unit test\n// build a complete stub to exercise a single INSERT.\n/**\n * The options these three actually pass is a signal and nothing else, so that\n * is what they ask for. `any` here accepted a misspelt option in silence, which\n * is the one thing a narrow type was never meant to buy.\n */\ntype BulkCallOptions = { signal?: AbortSignal | undefined };\n\nexport type WriteFn = (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n) => Promise<{ result: unknown[]; affected: number }>;\n\nexport type ReadFn = (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n) => Promise<unknown[]>;\n\nexport type TransactionFn = <T>(\n callback: (db: {\n write: (\n sql: string,\n params?: unknown[],\n options?: BulkCallOptions,\n ) => Promise<{ result: unknown[]; affected: number }>;\n }) => Promise<T>,\n options?: SQLiteTransactionOptions,\n) => Promise<T>;\n\n/**\n * How long the best-effort staging DROP may wait for a worker before the\n * caller is let go. Not an option: a caller has nothing useful to tune here,\n * and the consequence of expiry is a table the sweep already collects.\n */\nconst DROP_STAGING_TIMEOUT = 5_000;\n\n/**\n * Returned by every `enqueue()` that does not have to wait. Shared rather than\n * created per call: the hot path allocates nothing.\n */\nconst ADMITTED = Promise.resolve();\n\n/** A promise and the handle that resolves it. */\nconst makeRoom = (): { promise: Promise<void>; resolve: () => void } => {\n let resolve!: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n return { promise, resolve };\n};\n\nexport const createBulk = (shared: {\n file: string;\n locks: Locks;\n logger: Logger;\n maxVariables?: number;\n}) => {\n const { file, locks, maxVariables = 32766, logger } = shared;\n\n // Net 2 of the three-net cleanup: orphans left by a closed tab or a crashed\n // session.\n //\n // It runs at the FIRST output() of this client, never at open(). The writer is\n // only designated lazily, on the first write, so a sweep at open would race\n // the n workers. That is the argument against making it eager to make the\n // first output() faster — an attractive idea that the two-stage split does\n // NOT rule out on its own.\n //\n // The memo lives HERE rather than in forTarget on purpose: a transaction\n // builds its own target, so a per-target memo would sweep on every\n // tx.output() instead of once per client.\n let swept: Promise<void> | undefined;\n\n return (target: {\n read: ReadFn;\n write: WriteFn;\n transaction: TransactionFn;\n /**\n * Takes this batch's place in the caller's statement queue, SYNCHRONOUSLY,\n * at the moment `flush()` commits to it — from `close()` or from the\n * `enqueue()` that filled the buffer. `started` is what was issued before\n * it; `done()` gives the place back.\n *\n * Supplied by a transaction, where every statement shares one connection.\n * The client path supplies none: there each statement takes its own lease.\n * Without it the batch posts a microtask after `flush()` returns, so a\n * statement issued AFTER it runs first — a stale read rather than an error.\n */\n reserve?: () => { started: Promise<void>; done: () => void };\n }) => {\n const { read, write, transaction, reserve } = target;\n\n // bulkWrite, sweepOnce, indexStatements and output move in here VERBATIM.\n // Not one character of their bodies changes: they already read `read`,\n // `write`, `transaction`, `file`, `locks`, `logger` and `maxVariables` as\n // free variables, and all seven are still in scope. This task is a\n // relocation; any behavioural edit smuggled into it is a defect.\n\n /**\n * Creates a bulk write utility for efficiently inserting many rows.\n * Automatically batches inserts to stay within SQLite variable limits.\n *\n * @param table - Table name to insert into\n * @param keys - Column names for the insert\n * @returns Object with enqueue() to add rows and close() to flush remaining\n */\n const bulkWrite = <KEYS extends string>(\n table: string,\n keys: KEYS[],\n options?: SQLiteBulkWriteOptions,\n /** Internal: awaited before the first batch. `output()` passes its staging DDL. */\n before?: Promise<unknown>,\n ) => {\n const { signal, release: releaseDeadline } = withDeadline(\n options,\n 'bulkWrite',\n );\n const maxBufferSize = Math.floor(maxVariables / keys.length);\n // Two batches' worth by default: the batch is the unit that gets queued,\n // so anything smaller than one is meaningless and two is the smallest\n // window that lets a batch settle while another is being filled. Derived\n // rather than fixed because the same row count means a different number\n // of INSERTs on a wide table than on a narrow one. It bounds ROWS — what\n // they weigh is the caller's business, and `queueSize` is theirs to set.\n // Raised to 1 rather than trusted: a flush always queues at least one\n // row, so anything lower can never be satisfied and would park the\n // producer for ever. This is the one place an explicit value is not taken\n // as given — the spec's \"no clamping\" was about a value too HIGH, whose\n // worst case is the behaviour that predates this option.\n const queueSize = Math.max(1, options?.queueSize ?? 2 * maxBufferSize);\n\n const buffer: { [K in KEYS]: any }[] = [];\n\n let writePromise = Promise.resolve<number>(0);\n let failure: unknown;\n let closed = false;\n let rowsWritten = 0;\n let rowsNotWritten = 0;\n /** Rows handed to a batch that has not settled yet. */\n let queuedRows = 0;\n /** Shared by every enqueue() parked while the queue is full. */\n let room: { promise: Promise<void>; resolve: () => void } | undefined;\n\n const releaseRoom = () => {\n room?.resolve();\n room = undefined;\n };\n\n // The abort must release a producer parked on enqueue(): the batch it\n // waits for may never settle — the pool can stay empty on a VFS that\n // rotates one exclusive handle — and the release is what lets its next\n // enqueue() throw signal.reason. Removed by close(), so a signal the\n // caller keeps does not collect one listener per writer.\n signal?.addEventListener('abort', releaseRoom, { once: true });\n\n // Inside a transaction each batch is a `tx.write` carrying this signal,\n // so the transaction runs it inside its own savepoint (spec 2026-09-11,\n // D6): an abandoned load keeps the batches it completed — as it does\n // outside a transaction, where each is already committed — and the\n // batch in flight is undone. Nothing here needs to know which it is.\n\n const fail = (): SQLiteBulkWriteError =>\n new SQLiteBulkWriteError(\n `bulkWrite into \"${table}\" failed after ${rowsWritten} row(s); ${rowsNotWritten} row(s) were not written.`,\n { rowsWritten, rowsNotWritten },\n { cause: failure },\n );\n\n const flush = () => {\n const toInsert = [...buffer];\n buffer.length = 0;\n queuedRows += toInsert.length;\n // Synchronous with the decision to write this batch — that is the whole\n // point. Anything issued after this call queues behind the batch.\n const slot = reserve?.();\n // The chain never rejects: a rejection here is what used to skip every\n // later `.then()` and drop already-spliced rows without a word (B5).\n const runBatch = async (currentAffected: number) => {\n if (failure) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n // Skips a batch the abort beat to the start, so no round trip is\n // paid for rows that will not be written.\n if (signal?.aborted) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n try {\n if (before) await before;\n // The signal goes DOWN to the write. An earlier version withheld\n // it, reasoning that an aborted batch would be caught below and\n // recorded as `failure`, making close() reject with\n // SQLiteBulkWriteError instead of the caller's reason. The premise\n // was right and the conclusion wrong: the catch is ours, and it\n // tells the two apart.\n //\n // Withholding it cost a hang. A batch already in flight had no way\n // to be rejected, so a write that never settles — OPFSCoopSyncVFS\n // on an engine without `readwrite-unsafe`, waiting on a handle\n // hand-over that never comes — left this chain pending for ever,\n // and close() with it. Observed on macOS Safari 27.0.\n const { affected } = await write(\n `INSERT INTO ${quoteIdent(table)} (${keys.map(quoteIdent).join(',')}) VALUES ${toInsert.map(() => `(${keys.map(() => '?')})`)}`,\n toInsert.flatMap((data) => keys.map((k) => data[k])),\n { signal },\n );\n rowsWritten += toInsert.length;\n return currentAffected + affected;\n } catch (error) {\n // An abort is not a failure. This branch is what keeps close()\n // rejecting with `signal.reason` rather than SQLiteBulkWriteError.\n if (signal?.aborted) {\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n failure = error;\n // A multi-row INSERT is statement-atomic: nothing of this batch landed.\n rowsNotWritten += toInsert.length;\n return currentAffected;\n }\n };\n writePromise = writePromise.then(async (currentAffected) => {\n try {\n // Whatever was issued before this batch. Awaited here rather than\n // inside runBatch so every path — including the ones that skip the\n // write — keeps the order and releases the slot below.\n if (slot) await slot.started;\n return await runBatch(currentAffected);\n } finally {\n // Every exit passes here — success, latched failure, and the batch\n // an abort skipped. One missed decrement and enqueue() never\n // resolves again.\n slot?.done();\n queuedRows -= toInsert.length;\n if (queuedRows < queueSize) releaseRoom();\n }\n });\n };\n\n const failClosed = (): SQLiteBulkWriteError =>\n new SQLiteBulkWriteError(`Bulk writer for \"${table}\" is closed.`, {\n rowsWritten,\n rowsNotWritten,\n });\n\n return {\n enqueue: (data: { [K in KEYS]: any }) => {\n if (closed) throw failClosed();\n // Before the failure guard: an aborted writer is not a failed one,\n // and the caller who aborted wants their own reason back, not a\n // report about rows they stopped caring about.\n signal?.throwIfAborted();\n if (failure) throw fail();\n buffer.push(data);\n if (buffer.length >= maxBufferSize) flush();\n if (queuedRows < queueSize) return ADMITTED;\n // One deferred for every caller while the queue is full: enqueue() is\n // not concurrent-safe today and this does not make it so.\n room ??= makeRoom();\n return room.promise;\n },\n close: async () => {\n if (closed) throw failClosed();\n try {\n if (buffer.length) flush();\n const affected = await writePromise;\n // Ordered ahead of the failure check for the same reason: a batch\n // skipped by the abort is not a batch that failed.\n signal?.throwIfAborted();\n if (failure) throw fail();\n closed = true;\n return affected;\n } finally {\n signal?.removeEventListener('abort', releaseRoom);\n releaseDeadline();\n }\n },\n };\n };\n\n const sweepOnce = () => {\n // MANDATORY guard: without the Web Locks API there is no way to tell an\n // in-flight staging table from an orphan, and `heldNames()` returns []. A\n // sweep in that state would drop another tab's live staging table — worse\n // than not sweeping. The sweep is opportunistic; skipping it is correct.\n if (!locks.available) {\n if (swept === undefined) {\n swept = Promise.resolve();\n logger.warn(\n 'navigator.locks is unavailable; skipping the staging sweep',\n );\n }\n return swept;\n }\n\n // tryWithLock, not withLock: awaiting this lock inside an open transaction\n // would hold SQLite's write lock while waiting on a holder that may itself\n // be waiting for that write lock — reachable with two clients in one tab.\n //\n // A refused attempt is memoized deliberately. If the lock was held, another\n // client was sweeping; retrying would put a lock request in front of every\n // output() for nothing.\n swept ??= locks\n .tryWithLock(sweepLockName(file), async () => {\n const rows = await read(\n `SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '__bsq_staging_%'`,\n );\n const tables = rows\n .map((row) => (row as { name?: unknown }).name)\n .filter(\n (name: unknown): name is string => typeof name === 'string',\n );\n if (!tables.length) return;\n const stale = staleStagingTables(\n tables,\n await locks.heldNames(),\n file,\n );\n for (const orphan of stale) {\n await write(`DROP TABLE IF EXISTS ${quoteIdent(orphan)}`);\n }\n })\n .then(() => undefined)\n .catch(() => {\n // A failed sweep must never fail the output() that triggered it.\n });\n return swept;\n };\n\n /** CREATE INDEX statements for the final table, built after the rename. */\n const indexStatements = <SCHEMA extends Schema>(\n table: string,\n options?: SQLiteOutputOptions<SCHEMA>,\n ): string[] => {\n const statements: string[] = [];\n for (const index of options?.indexes ?? []) {\n const columns = Array.isArray(index)\n ? index\n : typeof index === 'object'\n ? 'column' in index\n ? [index.column]\n : index.columns\n : [index];\n const unique =\n !Array.isArray(index) && typeof index === 'object' && !!index.unique;\n if (!columns?.length) continue;\n const names = columns.map(String);\n statements.push(\n `CREATE${unique ? ' UNIQUE' : ''} INDEX IF NOT EXISTS ${quoteIdent(`${table}_${names.join('_')}_${unique ? 'U' : 'IDX'}`)} ON ${quoteIdent(table)}(${names.map(quoteIdent).join(',')})`,\n );\n }\n return statements;\n };\n\n /**\n * Builds a table from scratch and swaps it in atomically — MongoDB's $out.\n *\n * Rows are loaded into __bsq_staging_<uuid> (a normal table in main, never\n * TEMP: a TEMP table lives in the temp database and cannot be renamed across\n * databases, and is invisible to the other pool workers). The final swap is\n * one short transaction: DROP the target, RENAME the staging table onto it,\n * then build the indexes with their final names — SQLite has no\n * ALTER INDEX ... RENAME, so indexes built before the swap would keep the\n * staging name forever (D3).\n *\n * Until close() succeeds the previous table stays intact and fully\n * populated. That is the guarantee output() did not have (B5): it used to\n * DROP and CREATE eagerly, so a failure anywhere in the load left the caller\n * with no table at all.\n */\n const output = <SCHEMA extends Schema>(\n table: string,\n schema: SCHEMA,\n options?: SQLiteOutputOptions<SCHEMA>,\n ) => {\n const { signal, release: releaseDeadline } = withDeadline(\n options,\n 'output',\n );\n const staging = stagingTableName(crypto.randomUUID());\n\n const normalizedSchema = Object.entries(schema).map(([k, v]) => {\n const type = assertColumnType(typeof v === 'string' ? v : v.type, k);\n const unique = typeof v === 'object' && !!v.unique;\n const notnull = typeof v === 'object' && !!v.required;\n const generated =\n typeof v === 'object' && v.generated\n ? assertGeneratedExpression(v.generated, k)\n : undefined;\n return { name: k, type, unique, notnull, generated };\n });\n\n // Held for as long as the staging table exists: this is what tells another\n // tab's sweep that the table is in flight and must not be collected.\n const lockHeld = locks.hold(stagingLockName(file, staging));\n\n const createStaging = sweepOnce()\n .then(() =>\n write(`\n\t\t\tCREATE TABLE ${quoteIdent(staging)}(\n\t\t\t\t${normalizedSchema\n .map(({ name, type, unique, notnull, generated }) => {\n return `${quoteIdent(name)} ${type} ${unique ? 'UNIQUE' : ''} ${notnull ? 'NOT NULL' : ''} ${generated ? `GENERATED ALWAYS AS ${generated}` : ''}`;\n })\n .join(',')}\n\t\t\t)`),\n )\n .then(() => undefined);\n\n const { enqueue, close } = bulkWrite(\n staging,\n Object.keys(schema).filter(\n (col) => typeof schema[col] !== 'object' || !schema[col].generated,\n ),\n { signal, queueSize: options?.queueSize },\n createStaging,\n );\n\n const releaseLock = async () => {\n (await lockHeld)();\n };\n\n const dropStaging = () =>\n Promise.race([\n write(`DROP TABLE IF EXISTS ${quoteIdent(staging)}`),\n // Bounded, because this runs on the path whose whole point is to\n // stop quickly. The DROP is a write, so it needs a worker — and\n // after an abort the pool may still be finishing the batch the abort\n // skipped, or be stuck for the reason the caller aborted over.\n // Unbounded, a best-effort cleanup would hold close() open forever.\n //\n // Giving up here is safe by construction: the fallback is an orphan\n // staging table, and releasing the staging lock — which happens\n // AFTER this attempt, deliberately — is what tells another sweep it\n // may collect it.\n new Promise((resolve) => setTimeout(resolve, DROP_STAGING_TIMEOUT)),\n ]).catch(() => {\n // Net 2 (the sweep) collects what this could not.\n });\n\n return {\n enqueue: (data: SQLiteOutputRow<SCHEMA>) => enqueue(data as any),\n\n close: async () => {\n try {\n let affected: number;\n try {\n // Ensure the staging table exists even when no rows were enqueued —\n // bulkWrite.close() only awaits createStaging via flush(), and flush()\n // is skipped when the buffer is empty.\n await createStaging;\n affected = await close();\n } catch (error) {\n await dropStaging();\n await releaseLock();\n throw error;\n }\n\n try {\n await transaction(async (tx) => {\n await tx.write(`DROP TABLE IF EXISTS ${quoteIdent(table)}`);\n await tx.write(\n `ALTER TABLE ${quoteIdent(staging)} RENAME TO ${quoteIdent(table)}`,\n );\n for (const statement of indexStatements(table, options)) {\n await tx.write(statement);\n }\n });\n } catch (error) {\n await dropStaging();\n throw error;\n } finally {\n await releaseLock();\n }\n\n return affected;\n } finally {\n releaseDeadline();\n }\n },\n };\n };\n\n return { bulkWrite, output };\n };\n};\n","/**\n * The commit epoch: a monotonic integer per database, counting commits\n * performed in this realm. Its absolute value means nothing — only the\n * comparison with a worker's `seen` does.\n *\n * The registry lives in the realm-wide symbol registry rather than in a module\n * variable on purpose. A module singleton is unique only when the bundler\n * loads one copy of the module; `Symbol.for` is unique per realm whatever the\n * bundler did. That is what makes \"two clients in one tab see each other\" true\n * by construction.\n *\n * The `v1` suffix separates incompatible shapes. Bump it ONLY if the shape\n * changes — bumping it per release recreates the fragmentation it prevents.\n */\n\nimport type { Locks } from './locks';\nimport { namespaceFor } from './locks';\nimport type { SQLiteVFS } from './types';\n\n/**\n * The statement the barrier runs and discards.\n *\n * Measured 2026-08-20 in the forced configuration: 6/6 correct. `SELECT 1`\n * touches no page and is 6/6 stale; `PRAGMA data_version` and\n * `PRAGMA schema_version` are 8/8 stale; so is waiting. Only a statement that\n * opens a real read transaction on the file refreshes the connection's cached\n * page 1 — and it must be a SEPARATE statement, because the one that triggers\n * the refresh still returns the stale result.\n */\nexport const BARRIER_SQL = 'SELECT count(*) FROM sqlite_master';\n\n/**\n * The marker a realm holds to publish the epoch it last committed.\n *\n * Held in SHARED mode: many realms may hold one name at once, so publishing\n * never waits and two realms can never collide on a number. Nobody reads the\n * lock — the NAME is the state, which is why this beats a BroadcastChannel:\n * there is no message that can still be in flight.\n */\nexport const epochLockName = (ns: string, file: string, n: number) =>\n `bsq:epoch:${ns}:${file}:${n}`;\n\n/**\n * The highest epoch any realm in this origin has published under `prefix`.\n *\n * The tail after the prefix must be ALL digits, which is stricter than a\n * prefix match plus `lastIndexOf(':')` and is the point: a normalized file may\n * contain a colon (`new URL('./a:b', 'file://').pathname` is `a:b`), so the\n * loose form would read another database's epoch as this one's.\n */\nexport const maxEpochIn = (heldNames: string[], prefix: string): number => {\n let max = 0;\n for (const name of heldNames) {\n if (!name.startsWith(`${prefix}:`)) continue;\n const tail = name.slice(prefix.length + 1);\n if (!/^\\d+$/.test(tail)) continue;\n const n = Number(tail);\n if (n > max) max = n;\n }\n return max;\n};\n\nconst REGISTRY_KEY = Symbol.for('browser-sqlite.epochs.v1');\n\ntype Cell = { value: number; releaseMarker?: () => void };\ntype Registry = Map<string, Cell>;\n\nconst registry = (): Registry => {\n const host = globalThis as unknown as Record<symbol, Registry | undefined>;\n const existing = host[REGISTRY_KEY];\n if (existing) return existing;\n const created: Registry = new Map();\n host[REGISTRY_KEY] = created;\n return created;\n};\n\nexport type Epochs = {\n /** The number of commits observed for this database, floor included. */\n current: () => number;\n /** Records one commit and returns the new epoch. */\n bump: () => number;\n /** Raises the local floor. Never lowers it. */\n raiseTo: (n: number) => void;\n /** The highest epoch published by any realm in this origin. */\n originMax: () => Promise<number>;\n /** Publishes `n` for this realm, replacing its previous marker. */\n publish: (n: number) => Promise<void>;\n};\n\n/**\n * Handles onto the counter for `(namespace, file)`, which MUST already be\n * normalized by `normalizeDatabaseFile`. Entries are never removed: deleting\n * one would restart the counter at 0, and a worker still alive with `seen = 5`\n * would then read `5 > 0`, believe itself current forever, and serve stale\n * data.\n *\n * The cell is realm-wide, so every client in a tab shares one counter AND one\n * marker — publication is per realm, not per client.\n */\nexport const epochsFor = (\n vfs: SQLiteVFS,\n file: string,\n locks: Locks,\n): Epochs => {\n const ns = namespaceFor(vfs);\n const key = `${ns}:${file}`;\n const map = registry();\n const existing = map.get(key);\n const cell: Cell = existing ?? { value: 0 };\n if (!existing) map.set(key, cell);\n\n const prefix = `bsq:epoch:${ns}:${file}`;\n\n return {\n current: () => cell.value,\n bump: () => {\n cell.value += 1;\n return cell.value;\n },\n raiseTo: (n) => {\n if (n > cell.value) cell.value = n;\n },\n originMax: async () =>\n locks.available ? maxEpochIn(await locks.heldNames(), prefix) : 0,\n publish: async (n) => {\n if (!locks.available) return;\n const previous = cell.releaseMarker;\n // New before old, always: `max` must never dip between the two.\n cell.releaseMarker = await locks.hold(epochLockName(ns, file, n), {\n mode: 'shared',\n });\n previous?.();\n },\n };\n};\n\n/**\n * Where a worker's `seen` lands after the write it just served.\n *\n * `target` is the epoch captured when its lease was granted; `next` is the\n * epoch its own commit produced. Advancing requires both conditions:\n *\n * - `seen === target`: the worker was actually observing from `target` when its\n * lease was granted. If the worker was already behind (`seen < target`), it\n * must not be marked current regardless of what it just committed.\n * - `next === target + 1`: the commit is the immediate successor of `target`.\n * If another client committed during our lease, `next` skipped; our\n * connection never observed that commit and must stay marked behind.\n *\n * Marking a connection current when it is not is the only class of bug this\n * design must make impossible.\n */\nexport const advanceSeen = (\n seen: number,\n target: number,\n next: number,\n): number => (seen === target && next === target + 1 ? next : seen);\n","import { DEFAULT_CREDIT_WINDOW } from './credits';\nimport { SQLiteError, type SQLiteErrorCode } from './errors';\nimport type { Logger } from './logger';\nimport type { SQLiteResultCode } from './sqlite-codes';\nimport type {\n PlatformFeature,\n SavepointOp,\n SQLiteBuild,\n SQLiteVFS,\n WasmLocation,\n WorkerMessageData,\n} from './types';\n\n/**\n * Query execution options forwarded to a pool worker.\n */\nexport type PoolWorkerQueryOptions = {\n chunkSize?: number | undefined;\n credits?: number | undefined;\n timeout?: number | undefined;\n /** Forwarded to the worker so it installs the async progress handler (§4 D2). */\n abortable?: boolean | undefined;\n /**\n * When true, the query's completion does not call `deps.onServed`. Set for\n * the commit-propagation barrier: it is a synthetic probe, not user work, and\n * must not reset the supervisor's restart counter.\n * `createQueryDebugState` is intentionally NOT suppressed: barrier statements\n * still appear in the debug request tree, and a browser test counts them there\n * to prove the barrier stays conditional.\n */\n noServed?: boolean;\n /**\n * Read exactly once, when the query is POSTED — below the reuse guard,\n * never when the query is created. A transaction hands its pending savepoint\n * conclusion over in here, so a query the guard refuses must not consume it\n * (spec 2026-09-11, §4).\n */\n savepoint?: (() => SavepointOp | undefined) | undefined;\n};\n\n/**\n * A Worker extended with pool-specific properties.\n *\n * Note: no `available` field — availability lives in the Scheduler, not on\n * the worker itself. This makes it impossible to republish a borrowed worker\n * from outside the scheduler (the root cause of B1).\n */\nexport type PoolWorker = Worker & {\n index: number;\n /** Lifecycle label for the debug surface. Replaces the SAB status byte. */\n status: string;\n /**\n * The commit epoch this connection has absorbed. Starts at -1: a worker\n * opens the file — and reads page 1 — BEFORE it enters the pool, and a\n * commit can land in between. At poolSize 2 that is the nominal startup\n * ordering, not a rare race, so a new worker is always treated as behind and\n * pays exactly one barrier statement in its lifetime.\n */\n seen: number;\n /** The epoch captured when the current lease was granted. */\n epochTarget: number;\n /**\n * Whether the connection was inside a transaction when its last query\n * ended, as the worker read it (`sqlite3_get_autocommit`). `undefined` until\n * a query has reported it.\n *\n * CONNECTION state, not availability: nothing schedules on it and nothing\n * may. Availability lives in `scheduler.ts` alone — see the `available`\n * declaration there — and a flag on this object that the pool consulted would\n * reopen B1. Its one reader is `transaction.ts`, which asks it whether a\n * ROLLBACK is still owed and whether its transaction is still alive.\n */\n inTransaction?: boolean | undefined;\n query: <T extends Record<string, unknown> = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ) => AsyncGenerator<T[] | number>;\n /**\n * Ask the worker to stop. Also settles a `next()` already in flight, which\n * is what lets the consumer's queued `return()` reach the generator's\n * finally instead of waiting behind a chunk that may be minutes away.\n *\n * `on` is the transport iterator being stopped, and it is REQUIRED: the call\n * is a no-op unless the worker is still serving that transport, which is what\n * keeps a late stop off an unrelated query. It was briefly optional, and an\n * omitted argument meant \"stop whatever is running\" — the exact semantics that\n * let an abandoned generator's late cleanup truncate a healthy query. Nothing\n * needs that form, so nothing may ask for it.\n */\n interrupt: (on: object) => void;\n /** Resolves when no query is in flight on this worker. */\n quiesce: () => Promise<void>;\n /**\n * Resolves when this worker will accept another query — at `done`, which is\n * earlier than `quiesce()`. Use it to wait for the WORKER; use `quiesce()`\n * to wait for a statement to have been fully settled and judged.\n */\n free: () => Promise<void>;\n /** Posts `close`, awaits the `closed` reply, then the caller must terminate. */\n close: () => Promise<void>;\n /**\n * Stops the thread AND the transport — never the browser's `terminate()`\n * alone.\n *\n * `PoolWorker` IS the native `Worker`, so this used to be the engine's own\n * method: it killed the thread and told the transport nothing. A request\n * posted afterwards waited for a reply that could never come — a\n * transaction's next statement, and its fallback `ROLLBACK`, which carries no\n * signal by design and so could not even be aborted. Overriding the method,\n * rather than adding one beside it, is deliberate: there are several\n * terminate sites and one added later must not be able to forget this.\n *\n * `reason` is what in-flight and later requests reject with. It reports\n * nothing to the client: whoever terminates has already decided this worker's\n * fate, and going through `onDeath` here would re-enter that decision.\n */\n terminate: (reason?: SQLiteError) => void;\n};\n\n/** What `createPoolWorker` settles with when its worker declined to open. */\nexport type DeclinedWorker = { declined: PlatformFeature };\n\nconst STOP = Symbol('stop');\n\n/** SQLITE_BUSY and SQLITE_LOCKED — the two ways a lock conflict reports. */\nconst BUSY_CODES = new Set([5, 6]);\n\n/**\n * The extended code when it names a subtype, else undefined (spec D9). SQLite\n * reports the primary code again for a failure that has no subtype, and\n * `sqliteCode` already says that. Any other difference is kept: a 0 read after\n * a successful call is a wrong read, and must stay visible.\n */\nconst subtypeOf = (data: {\n sqliteCode?: SQLiteResultCode;\n sqliteExtendedCode?: number;\n}): number | undefined =>\n data.sqliteExtendedCode !== data.sqliteCode\n ? data.sqliteExtendedCode\n : undefined;\n\n/**\n * Returns a SQLiteError('BUSY', …) when data carries a lock-conflict result\n * code (5 or 6), else undefined. Shared by `statementError` and `startupError`\n * so the BUSY_CODES decision lives in exactly one place. The extended code\n * travels with it when it is a subtype (`subtypeOf`) — a query sends one, an\n * open does not.\n */\nexport const busyFromCode = (data: {\n message: string;\n cause?: unknown;\n sqliteCode?: SQLiteResultCode;\n sqliteExtendedCode?: number;\n}): SQLiteError | undefined => {\n if (data.sqliteCode === undefined || !BUSY_CODES.has(data.sqliteCode)) {\n return undefined;\n }\n const sqliteExtendedCode = subtypeOf(data);\n return new SQLiteError('BUSY', data.message, {\n cause: data.cause,\n sqliteCode: data.sqliteCode,\n ...(sqliteExtendedCode !== undefined ? { sqliteExtendedCode } : {}),\n });\n};\n\n/**\n * What a query's `error` message becomes (spec 2026-09-14 §5.3): a code the\n * worker minted; else `BUSY` for a lock conflict; else `STATEMENT_FAILED` for\n * any other code SQLite reported; else — a failure SQLite did not report, such\n * as a JS exception in the worker — a plain Error, as before. `BUSY` and\n * `STATEMENT_FAILED` carry `sqliteCode`, and `sqliteExtendedCode` when it is a\n * subtype (`subtypeOf`).\n */\nexport const statementError = (data: {\n message: string;\n cause?: unknown;\n sqliteCode?: SQLiteResultCode;\n sqliteExtendedCode?: number;\n errorCode?: SQLiteErrorCode;\n}): Error => {\n if (data.errorCode) {\n return new SQLiteError(data.errorCode, data.message, {\n cause: data.cause,\n });\n }\n const busy = busyFromCode(data);\n if (busy) return busy;\n if (data.sqliteCode === undefined) {\n return new Error(data.message, { cause: data.cause });\n }\n const sqliteExtendedCode = subtypeOf(data);\n return new SQLiteError('STATEMENT_FAILED', data.message, {\n cause: data.cause,\n sqliteCode: data.sqliteCode,\n ...(sqliteExtendedCode !== undefined ? { sqliteExtendedCode } : {}),\n });\n};\n\n/**\n * What a failed open or delete becomes: `BUSY` for a lock conflict, else\n * `WORKER_CRASHED` — the slot dies either way — carrying SQLite's primary code\n * when there is one (spec 2026-09-14, D2). No extended code: when\n * `sqlite3_open_v2` itself fails there is no connection to ask (D7).\n */\nexport const startupError = (data: {\n message: string;\n cause?: unknown;\n sqliteCode?: SQLiteResultCode;\n}): SQLiteError => {\n // D7: no extended code at open/delete, as a property of the client — not\n // merely because the worker never sends one. Only `message`, `cause` and\n // `sqliteCode` reach `busyFromCode`, whatever else `data` might carry.\n const busy = busyFromCode({\n message: data.message,\n ...(data.cause !== undefined ? { cause: data.cause } : {}),\n ...(data.sqliteCode !== undefined ? { sqliteCode: data.sqliteCode } : {}),\n });\n if (busy) return busy;\n return new SQLiteError('WORKER_CRASHED', data.message, {\n cause: data.cause,\n ...(data.sqliteCode !== undefined ? { sqliteCode: data.sqliteCode } : {}),\n });\n};\n\n/**\n * The single `new Worker(new URL(…))` expression in this package.\n *\n * It must stay one literal, in one place: bundlers find the worker by static\n * analysis of exactly this shape, and a second copy would have them emit a\n * second, untransformed worker bundle. `pool.ts:191` records what that cost\n * when the expression was written a second time for an error message.\n */\nexport const spawnWorker = (name: string): Worker =>\n new Worker(\n /* webpackChunkName: \"browser-sqlite\" */ new URL(\n './worker/worker.js',\n import.meta.url,\n ),\n { name, type: 'module' },\n );\n\n/**\n * Creates a new pool worker and registers it in the pool array.\n * Sets up message routing via callId for query responses.\n *\n * Moved verbatim from `createWorker` in client.ts, with three changes:\n * 1. Closure variables become explicit `deps` parameters.\n * 2. Both `available` assignments are deleted (availability lives in the Scheduler).\n * 3. `worker.available = false/true` in the `query` generator are deleted.\n */\nexport const createPoolWorker = (deps: {\n index: number;\n pool: (PoolWorker | undefined)[];\n clientName: string;\n file: string;\n vfs: SQLiteVFS;\n build: SQLiteBuild;\n /** Already resolved and absolute; relayed to the worker, never read here. */\n wasm?: WasmLocation | undefined;\n pragmas?: Record<string, string> | undefined;\n statementCacheSize?: number | undefined;\n statementCacheBytes?: number | undefined;\n onDeath?: (index: number, error: SQLiteError) => void;\n onServed?: (index: number) => void;\n drainTimeout: number;\n createWorkerDebugState?: ((index: number, name: string) => any) | undefined;\n createQueryDebugState?:\n | ((index: number, sql: string, params?: unknown[]) => any)\n | undefined;\n logger: Logger;\n abortSlots?: SharedArrayBuffer | undefined;\n declineWithout?: readonly PlatformFeature[] | undefined;\n /** Sent to slot 0 where exclusivity depends on a feature (spec 2026-09-15). */\n probeFirst?: readonly PlatformFeature[] | undefined;\n /** Worker 0's probe answer, with the function that lets it open. */\n onProbed?:\n | ((missing: PlatformFeature | null, proceed: () => void) => void)\n | undefined;\n}): Promise<PoolWorker | DeclinedWorker> => {\n const {\n index,\n pool,\n clientName,\n file,\n vfs,\n build,\n wasm,\n pragmas,\n statementCacheSize,\n statementCacheBytes,\n } = deps;\n const { createWorkerDebugState, createQueryDebugState, logger } = deps;\n const { abortSlots } = deps;\n const { declineWithout, probeFirst } = deps;\n\n const deferredInit = Promise.withResolvers<PoolWorker | DeclinedWorker>();\n\n const workerName = `${clientName} / Worker ${index + 1}`;\n const worker = Object.assign(spawnWorker(workerName) as PoolWorker, {\n index,\n status: 'NEW',\n seen: -1,\n epochTarget: 0,\n });\n pool[index] = worker;\n // A restarted worker inherits this slot, and its callIds restart at 0 — so\n // the predecessor's last abort would fire on the replacement's seventh call.\n // Zeroing here is the whole guard, and it belongs where the worker is born.\n if (abortSlots) new Int32Array(abortSlots)[index] = 0;\n logger.info(`worker ${index + 1} created`);\n\n const state = createWorkerDebugState?.(index, workerName);\n\n let currentCallId = 0;\n\n // Deferred promise for streaming query results one chunk at a time\n let deferredChunk: PromiseWithResolvers<unknown[] | number> | undefined;\n\n /**\n * Everything the worker has delivered and the generator has not yielded yet.\n *\n * `deferredChunk` is ONE slot, and the credit window puts `credits` chunks in\n * flight (2 by default). While the generator is suspended at its `yield` —\n * which is every moment the consumer is doing something — an arriving chunk\n * used to resolve a promise nobody would ever await, and the generator then\n * waited on the replacement. The chunk was gone: no error, no short read,\n * just fewer rows. Measured 2026-09-04, deterministic on both engines: 501 of\n * 1001 rows for a consumer that awaited a `setTimeout(0)` between chunks, 500\n * of 1001 at `credits: 4`. It reached `stream()` and `chunk()` and no other\n * surface, because `read()`, `first()` and `write()` never hand control back\n * between chunks — which is why four releases shipped with it.\n *\n * So the promise is now only a wake-up signal, and the VALUES live here. A\n * resolution nobody observes costs nothing; a chunk that was never queued\n * cannot be recovered.\n */\n let inbox: (unknown[] | number)[] = [];\n\n /**\n * Set by `interrupt()`. Without it, a stop arriving while the inbox holds\n * chunks would be outrun by the drain: the loop would keep yielding buffered\n * chunks and never look at `stopRequested` again. A stop must stop.\n */\n let stopped = false;\n\n /**\n * The transport generator whose query the worker is serving right now, or\n * `undefined` when it is idle.\n *\n * Identity, not a counter, and it answers one question the rest of this\n * closure cannot: *is this generator still the one this worker belongs to?*\n * Every other piece of per-query state here — `deferredChunk`, `inbox`,\n * `idle`, `currentCallId` — is overwritten by the next query, so a transport\n * that outlives its own query reads the LIVE query's state and cannot tell.\n * And one does outlive it: `done` clears `deferredChunk` from the message\n * handler while the generator is still suspended at its `yield`, so the reuse\n * guard lets the next query through and the stale generator stays parked\n * there until somebody calls `return()` on it — which is exactly what the\n * abandonment cleanup does, arbitrarily late.\n */\n let servingQuery: object | undefined;\n\n /**\n * The transport's failure, captured the moment it happens rather than\n * observed by awaiting.\n *\n * The delivery loop only awaits — and so only sees `lost` or a death — when\n * the inbox runs dry, and against a steady producer it never does: the\n * consumer takes a chunk, the credit brings the next one, and the queue is\n * refilled before the loop looks up. Measured while building this fix: a\n * `messageerror` raised mid-stream went unreported for the whole remaining\n * query, where it used to reject at once. Failing fast on this flag is what\n * keeps the two channels as urgent as they were.\n */\n let failure: unknown;\n // Set by the query generator when options.noServed is true; cleared in\n // case 'done' after (possibly) suppressing onServed, and in the generator's\n // finally so a query that fails before 'done' does not leave it set.\n let suppressServed = false;\n\n // Deferred promise resolved when the worker replies 'closed'.\n let deferredClose: PromiseWithResolvers<void> | undefined;\n\n // Resolved while a query is in flight; `quiesce()` is how a lease learns the\n // worker is genuinely idle again.\n let idle: PromiseWithResolvers<void> | undefined;\n\n /**\n * Resolved when this worker will accept another query — which is EARLIER\n * than `idle`, and the gap is the whole reason this exists.\n *\n * The reuse guard reads `deferredChunk`, cleared by the message handler the\n * moment `done` arrives. `idle` is resolved by the transport's own `finally`,\n * which runs only once the consumer pulls past the last value — so a\n * generator whose query has finished but whose consumer has walked away\n * leaves `deferredChunk` clear and `idle` pending, for ever. A caller that\n * waits for `quiesce()` there waits for nothing, while the worker is free\n * (`tests/browser/abandon.test.ts`, \"does not truncate the query the worker\n * has moved on to\").\n *\n * Resolved at both points, idempotently: where `deferredChunk` is cleared,\n * and alongside `idle` for the paths — an error, a death — that never reach\n * a `done`.\n */\n let freed: PromiseWithResolvers<void> | undefined;\n let stopRequested: PromiseWithResolvers<typeof STOP> | undefined;\n\n let dead = false;\n let ready = false;\n const deathDeferred = Promise.withResolvers<never>();\n // Nothing awaits this until a query runs; without a sink an early death is an\n // unhandled rejection. The sink is also where the delivery loop learns of a\n // death it is not currently awaiting — see `failure`.\n deathDeferred.promise.catch((error) => {\n failure ??= error;\n });\n\n // Per-query channel for a message that never arrived (onmessageerror). The\n // worker is alive, so the request rejects but the transport stays intact and\n // the generator's finally still stops and drains it.\n let lost: PromiseWithResolvers<never> | undefined;\n\n /**\n * Kills the TRANSPORT: whatever is in flight rejects, and so does every later\n * request, because `failure` survives on a dead worker. Reports nothing to\n * the client — `die` below is what reports. Returns false if already dead, so\n * neither path fires twice.\n */\n const poison = (error: SQLiteError) => {\n if (dead) return false;\n dead = true;\n worker.status = 'DEAD';\n deathDeferred.reject(error);\n deferredInit.reject(error); // no-op once resolved\n // A dead worker can never send the 'closed' reply close() is awaiting —\n // it either never received the 'close' message or is gone before it could\n // reply. Resolving (not rejecting) here is what lets close() return\n // promptly instead of running out its drainTimeout for a reply that will\n // never come.\n deferredClose?.resolve();\n return true;\n };\n\n const die = (error: SQLiteError) => {\n if (!poison(error)) return;\n deps.onDeath?.(index, error);\n };\n\n worker.onerror = (event) => {\n const errorEvent = event as ErrorEvent;\n const detail =\n typeof event === 'object' && event !== null && 'message' in event\n ? String(errorEvent.message ?? '')\n : '';\n // Chrome leaves ErrorEvent.filename empty for worker script-load failures,\n // so this is usually absent — measured 2026-08-27, and it is why the\n // fallback below is not simply the worker's own URL.\n //\n // Deliberately NOT `new URL('./worker/worker.js', import.meta.url)`: that\n // expression is an asset reference every bundler follows, and Vite emits a\n // second, untransformed copy of the worker for it — 777 KB whose own\n // `new URL('wa-sqlite.wasm', …)` references dangle, and which nothing ever\n // loads. It existed only so this message could name a URL.\n //\n // A bare `import.meta.url` is not an asset reference, so naming where the\n // client itself was loaded from costs nothing, and it points at the\n // directory the worker should have been emitted beside — which is the\n // thing a consumer actually needs to check.\n const failedUrl = errorEvent.filename;\n logger.error(`worker ${index + 1} crashed: ${detail}`);\n die(\n new SQLiteError(\n 'WORKER_CRASHED',\n ready\n ? `Worker ${index + 1} failed: ${detail || 'uncaught error'}`\n : `browser-sqlite could not load its worker${\n failedUrl\n ? ` from ${failedUrl}`\n : `; the client itself was loaded from ${import.meta.url}, and the worker must be emitted beside it`\n }. ` +\n `If the worker URL 404s, your bundler did not emit the worker beside your build output — ` +\n `see the \"Bundler Configuration\" section of the browser-sqlite README. ${detail}`,\n { cause: event },\n ),\n );\n };\n\n worker.addEventListener('messageerror', () => {\n logger.error(`worker ${index + 1} sent an undeserializable message`);\n lost?.reject(\n new SQLiteError(\n 'PROTOCOL_ERROR',\n `Worker ${index + 1} sent a message that could not be deserialized; the request cannot be completed.`,\n ),\n );\n });\n\n // Message handler routes responses by callId\n worker.onmessage = ({ data }: MessageEvent<WorkerMessageData>) => {\n const { type } = data;\n switch (type) {\n case 'ready': {\n const { callId } = data;\n if (callId === 0) {\n ready = true;\n worker.status = 'READY';\n if (state) state.initializationTime = Date.now();\n logger.info(`worker ${index + 1} ready`);\n deferredInit.resolve(worker);\n }\n break;\n }\n case 'open-error': {\n const { callId } = data;\n if (callId === 0) {\n logger.error(`worker ${index + 1} failed to open: ${data.message}`);\n die(startupError(data));\n }\n break;\n }\n case 'declined': {\n // Not a death: this worker opened nothing and never will. It settles\n // init WITHOUT `die`, so no `onDeath`; the client retires the slot and\n // terminates the thread (spec 2026-09-13).\n if (data.callId === 0) {\n logger.info(`worker ${index + 1} declined: no ${data.missing}`);\n deferredInit.resolve({ declined: data.missing });\n }\n break;\n }\n case 'probed': {\n // Worker 0 opens nothing until `proceed`: the client decides the\n // connection lock's mode from this answer first (spec 2026-09-15).\n if (data.callId === 0) {\n logger.info(\n `worker ${index + 1} probed: ${data.missing ? `no ${data.missing}` : 'nothing missing'}`,\n );\n deps.onProbed?.(data.missing, () =>\n worker.postMessage({ type: 'proceed', callId: 0 }),\n );\n }\n break;\n }\n case 'closed': {\n const { callId } = data;\n if (callId === 0) {\n logger.info(`worker ${index + 1} closed`);\n worker.status = 'CLOSED';\n deferredClose?.resolve();\n }\n break;\n }\n case 'chunk': {\n const { callId } = data;\n if (deferredChunk && callId === currentCallId) {\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.firstRowTime ??= Date.now();\n }\n // Queue first, then wake. The resolution may reach nobody — that is\n // the whole defect the inbox exists for — but the chunk is kept.\n inbox.push(data.data);\n deferredChunk.resolve(data.data);\n deferredChunk = Promise.withResolvers<unknown[] | number>();\n }\n break;\n }\n case 'done': {\n const { callId } = data;\n if (deferredChunk && callId === currentCallId) {\n worker.inTransaction = data.inTransaction;\n const affected = data.affected;\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.affectedRows = affected;\n state.currentRequest.currentQuery.prepared = data.prepared;\n state.currentRequest.affectedRows += affected;\n state.currentRequest.currentQuery.endTime = Date.now();\n }\n // The affected count is the last thing the generator yields, so it\n // queues behind whatever chunks are still waiting — a `done` that\n // jumped the queue would truncate them.\n inbox.push(affected);\n deferredChunk.resolve(affected);\n deferredChunk = undefined;\n // The guard's own condition has just gone: announce it here rather\n // than at `idle`, which a parked consumer may never reach.\n freed?.resolve();\n if (!suppressServed) deps.onServed?.(index);\n suppressServed = false;\n }\n break;\n }\n case 'error': {\n const { callId } = data;\n if (deferredChunk && callId === currentCallId) {\n worker.inTransaction = data.inTransaction;\n const error = statementError(data);\n if (state?.currentRequest?.currentQuery) {\n state.currentRequest.currentQuery.error = error;\n state.currentRequest.currentQuery.endTime = Date.now();\n }\n deferredChunk.reject(error);\n // Deliberately NOT `failure = error`, which is what a `messageerror`\n // and a death do. Those two mean the transport is broken, so nothing\n // queued behind them can be trusted and the drain stops at once. A\n // query error is the opposite: the worker produced those rows and\n // then failed, so the consumer receives what SQLite actually returned\n // and the error arrives after it. Setting the flag here would\n // suppress rows that exist.\n //\n // Do NOT null deferredChunk here. If the generator is suspended at\n // `yield` when the error arrives, nulling it would cause the while\n // loop to exit normally (silent truncation). Leaving the rejected\n // promise in place ensures the generator throws on its next\n // `await Promise.race([deferredChunk.promise, ...])` call, which\n // propagates the error to the consumer. The generator's `finally`\n // clears deferredChunk while it still owns the worker — which it does\n // here, an errored query being the one the worker is serving. That\n // `finally` became conditional when a stale transport was stopped from\n // resetting a live query's state; this path is not the stale case.\n // Attach a no-op handler to suppress unhandled-rejection warnings:\n // the consumer may be suspended (e.g. in sleep()) when the error\n // arrives, and `await Promise.race` only attaches its handler on\n // the next generator resume, which may be a macrotask away.\n deferredChunk.promise.catch(() => {});\n }\n break;\n }\n case 'deleted': {\n // A connection worker never deletes; this message belongs to the\n // delete-worker path handled in src/delete.ts and cannot arrive here.\n break;\n }\n case 'not-found': {\n // Same as deleted: only the delete-worker path (src/delete.ts) receives\n // this message. A connection worker never sends it.\n break;\n }\n default: {\n const _unexpected: never = data;\n throw new Error(\n `Unhandled worker message: ${JSON.stringify(_unexpected)}`,\n );\n }\n }\n };\n\n /**\n * Generator function that executes a query and streams results.\n * Manages the deferredChunk protocol and abort signals.\n */\n const runQuery = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n self: { gen?: AsyncGenerator<T[] | number> },\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ): AsyncGenerator<T[] | number> {\n try {\n if (deferredChunk) {\n // **A backstop, not a diagnosis for the caller.** It fires on \"a query\n // is already in flight on this worker\" — nothing narrower. It was\n // called GENERATOR_ABANDONED until 2026-09-22 and told the consumer to\n // close a generator, which was wrong twice over: two overlapping\n // `tx.read()`s reach here with no generator anywhere, and so does a\n // `tx.bulkWrite` batch still in flight\n // (tests/browser/multi-client.test.ts).\n //\n // Since the transaction serialises its statements, no consumer can\n // reach this any more: a client statement holds its lease until the\n // worker is idle, and a transaction queues. So the message names the\n // invariant rather than instructing anyone — if this is seen, the\n // serialisation is broken and the bug is ours.\n throw new SQLiteError(\n 'WORKER_BUSY',\n `Worker ${index + 1} already has a query in flight. One worker ` +\n 'serves one query at a time; a client statement holds its lease ' +\n 'until the worker is idle and a transaction queues its ' +\n 'statements, so reaching this means that serialisation was ' +\n 'broken. Please report it.',\n );\n }\n\n if (state?.currentRequest) {\n const queryState = createQueryDebugState?.(index, sql, params);\n state.currentRequest.currentQuery = queryState;\n }\n\n // Extract query options\n const {\n chunkSize = 500,\n credits = DEFAULT_CREDIT_WINDOW,\n noServed = false,\n timeout,\n abortable,\n savepoint,\n } = options ?? {};\n suppressServed = noServed;\n\n // Prepare for streaming chunks\n inbox = [];\n stopped = false;\n // Claim the worker. Whatever was serving it before this line is stale\n // from here on, and the `finally` below is what enforces that.\n servingQuery = self.gen;\n // A death is terminal for this worker, so its failure outlives the query\n // that observed it; a transport failure belongs to one query only.\n if (!dead) failure = undefined;\n deferredChunk = Promise.withResolvers<unknown[] | number>();\n lost = Promise.withResolvers<never>();\n lost.promise.catch((error) => {\n failure ??= error;\n });\n idle = Promise.withResolvers<void>();\n freed = Promise.withResolvers<void>();\n stopRequested = Promise.withResolvers<typeof STOP>();\n\n // Send query to worker with options\n // Read here and nowhere earlier: the reuse guard above has admitted this\n // query, so a transaction's pending conclusion leaves only with a message\n // that is actually sent (spec 2026-09-11, §4).\n const op = savepoint?.();\n worker.postMessage({\n type: 'query',\n callId: ++currentCallId,\n sql,\n params,\n options: {\n chunkSize,\n credits,\n timeout,\n abortable,\n ...(op ? { savepoint: op } : {}),\n },\n });\n worker.status = 'RUNNING';\n\n // Stream chunks until the query completes AND the inbox is empty. The\n // second half is not belt-and-braces: `done` clears `deferredChunk`, so a\n // loop that watched only the flag would exit on the last message and\n // drop whatever was still queued behind it.\n while (deferredChunk || inbox.length > 0) {\n if (inbox.length === 0) {\n // Nothing queued: wait to be woken. The resolved VALUE is ignored —\n // it is read from the inbox on the next turn, because a wake and a\n // delivery are no longer the same event.\n const waiting = deferredChunk;\n if (!waiting) break;\n const outcome = await Promise.race([\n waiting.promise,\n stopRequested.promise,\n lost.promise,\n deathDeferred.promise,\n ]);\n if (outcome === STOP) break;\n continue;\n }\n // A stop that arrives with chunks still queued must win: otherwise the\n // drain outruns it and the consumer keeps receiving rows it abandoned.\n if (stopped) break;\n // Same reasoning for the two failure channels, which the loop is no\n // longer awaiting while it has something to deliver.\n if (failure !== undefined) throw failure;\n const chunk = inbox.shift() as T[] | number;\n yield chunk;\n // Spec §3.3: the credit is issued once the CONSUMER has taken the\n // chunk. Crediting on arrival would let the worker run at full speed\n // and pile the chunks up in the message queue, which is the guarantee\n // this whole mechanism exists to make true.\n if (typeof chunk !== 'number') {\n worker.postMessage({ type: 'credit', callId: currentCallId, n: 1 });\n }\n }\n } finally {\n // Only the transport the worker is actually serving may run this\n // teardown. Every name it touches — `deferredChunk`, `currentCallId`,\n // `inbox`, `idle`, `status` — belongs to whatever query is in flight NOW,\n // so a stale transport running it would post a `stop` under someone\n // else's call id, drop their queued chunks and hand their worker back\n // mid-query. Two transports reach here without owning the worker: one\n // that never claimed it (the reuse guard above threw) and one whose query\n // ended while it stayed suspended at a `yield`, resumed arbitrarily later\n // by the abandonment cleanup's `return()`. Both owe nothing: they hold no\n // state of their own, all of it having been per-worker and reassigned.\n //\n // NOTE: an `if`, and never an early `return` — a `return` in a `finally`\n // discards the pending throw, which here is the reuse guard's own error.\n if (servingQuery === self.gen) {\n // If the consumer left early (break / return / throw) the worker is still\n // stepping rows. Tell it to stop, then wait for the reply it always sends,\n // so the worker is genuinely idle before the lease goes back to the pool.\n // Without this wait, the second half of B1 stands: a released worker still\n // inside sqlite.step().\n if (deferredChunk && !dead) {\n worker.status = 'ABORTING';\n // Spec §5.1: the worker may be parked waiting for a credit that this\n // unwinding client will never send. The flag above cannot reach it\n // there — only a message can.\n worker.postMessage({ type: 'stop', callId: currentCallId });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const expiry = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new SQLiteError(\n 'WORKER_CRASHED',\n `Worker ${index + 1} did not answer the stop request within ${deps.drainTimeout} ms; presumed dead.`,\n ),\n ),\n deps.drainTimeout,\n );\n });\n try {\n while (deferredChunk) {\n await Promise.race([deferredChunk.promise, expiry]);\n }\n } catch (error) {\n // A timeout is our own verdict and must be acted on. Any other error\n // is the worker reporting a failure while winding down; the caller is\n // already unwinding and surfacing it here would mask their reason.\n if (\n error instanceof SQLiteError &&\n error.code === 'WORKER_CRASHED'\n ) {\n die(error);\n }\n } finally {\n clearTimeout(timer);\n }\n }\n deferredChunk = undefined;\n lost = undefined;\n stopRequested = undefined;\n // Chunks the consumer abandoned. Left in place they would be yielded to\n // the NEXT query on this worker, which is the same defect wearing the\n // opposite sign: rows delivered to a caller that never asked for them.\n inbox = [];\n // Reset in case the query failed before 'done' arrived — prevents\n // leaking noServed=true into the next query on this worker.\n suppressServed = false;\n worker.status = dead ? 'DEAD' : 'READY';\n idle?.resolve();\n idle = undefined;\n // Idempotent: `done` usually got there first. This covers the paths\n // that never reach one — an error, a death, a stop.\n freed?.resolve();\n freed = undefined;\n servingQuery = undefined;\n }\n }\n };\n\n /**\n * The transport generator, created so that it can identify itself.\n *\n * A factory rather than the generator function directly, because the body\n * needs a reference to the object the caller holds: that identity is the only\n * thing that distinguishes the query this worker is serving from one it has\n * moved on from. Nothing runs here — an async generator's body starts on its\n * first `next()` — so the query message and the reuse guard still happen when\n * the consumer first pulls.\n */\n const query = <T extends Record<string, unknown> = Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ): AsyncGenerator<T[] | number> => {\n const self: { gen?: AsyncGenerator<T[] | number> } = {};\n self.gen = runQuery<T>(self, sql, params, options);\n return self.gen;\n };\n\n // Captured before the override below replaces it: this is the only reference\n // to the engine's own terminate left in the module.\n const nativeTerminate = worker.terminate.bind(worker);\n\n // Attach query method to worker\n Object.assign(worker, {\n query,\n terminate: (reason?: SQLiteError) => {\n poison(\n reason ??\n new SQLiteError(\n 'WORKER_CRASHED',\n `Worker ${index + 1} was terminated.`,\n ),\n );\n nativeTerminate();\n },\n /**\n * Ask the worker to stop. Also settles a `next()` already in flight, which\n * is what lets the consumer's queued `return()` reach the generator's\n * finally instead of waiting behind a chunk that may be minutes away.\n *\n * `on` is the transport the caller is stopping, and it is required. Without\n * it this acts on whatever query the worker is running now, so a caller that\n * no longer owns the worker breaks a healthy, unrelated statement — and its\n * consumer sees a short result with no error at all. That is not\n * hypothetical: it is the defect this parameter was added to close, found by\n * a whole-branch review and reproduced at 100 rows of 4000.\n */\n interrupt: (on: object) => {\n if (on !== servingQuery) return;\n stopped = true;\n stopRequested?.resolve(STOP);\n // The slot reaches a worker that is computing inside step() and reads no\n // messages until it yields — which the sync build never does. The message\n // that wakes a worker parked on a credit is sent by the query generator's\n // finally block; interrupt() owns only the slot write.\n if (abortSlots)\n Atomics.store(new Int32Array(abortSlots), index, currentCallId);\n },\n quiesce: () => idle?.promise ?? Promise.resolve(),\n free: () => freed?.promise ?? Promise.resolve(),\n close: async () => {\n // A dead worker will never reply 'closed' — posting to it would just\n // wait out deferredClose with nobody left to resolve it. `poison`\n // resolves an in-flight deferredClose when the worker dies mid-wait;\n // this is the other half, for a close() call that arrives afterwards.\n if (dead) return;\n if (!deferredClose) {\n deferredClose = Promise.withResolvers<void>();\n worker.postMessage({ type: 'close', callId: 0 });\n }\n await deferredClose.promise;\n },\n });\n\n // Initialize worker with database file and configuration\n worker.postMessage({\n callId: 0,\n type: 'open',\n file,\n vfs,\n build,\n wasm,\n pragmas,\n statementCacheSize,\n statementCacheBytes,\n abortSlots,\n abortIndex: abortSlots ? index : undefined,\n declineWithout,\n probeFirst,\n });\n\n return deferredInit.promise;\n};\n","import type { PoolWorker } from './pool';\n\n/**\n * Whether this cleanup has already run.\n *\n * A plain object rather than a boolean because the held value must observe a\n * change the generator makes after registration. Three routes reach the\n * cleanup — the registry, the abort listener and the generator's own `finally`\n * — and whichever arrives first closes the door on the other two.\n *\n * What it deliberately does NOT record is whether the query ever started. That\n * answers \"did this generator run\", where the only question that matters is\n * \"is the worker still serving this query\" — and the worker is the one that\n * knows, which is why `interrupt()` is asked rather than told.\n */\nexport type AbandonState = { done: boolean };\n\n/**\n * What the cleanup needs, and all it may hold.\n *\n * **It must never refer to the registered generator.** A `FinalizationRegistry`\n * held value that reaches its own target keeps the target alive and the\n * callback then never fires. Every field here points downward or sideways: the\n * worker and the transport iterator are reachable from the pool anyway, `state`\n * is a plain flag, `detach` closes over the caller's signal and its listener,\n * and `release` is the owning layer's teardown.\n */\nexport type Abandoned = {\n worker: Pick<PoolWorker, 'interrupt'>;\n iterator: { return: (value?: undefined) => Promise<unknown> };\n state: AbandonState;\n /**\n * Removes the abort listener that carries this very cleanup. Without it a\n * listener stays armed on a signal the CALLER owns, long after the query it\n * belonged to has ended — and fires against whatever the worker is doing\n * then.\n */\n detach: () => void;\n release?: (() => void) | undefined;\n};\n\n/**\n * What the `finally` of `queries.chunk` would have done, for a generator that\n * will never run it.\n *\n * The order is that `finally`'s and for its reason: `interrupt()` first, so the\n * queued `return()` is not parked behind a `next()` that will not settle.\n *\n * **Nothing here may assume the worker is still ours.** This runs at a moment\n * nobody chose — a collection, or the caller tidying up its own controller —\n * and by then the worker may be serving a query that has nothing to do with\n * this one. So the transport is named in both calls: `interrupt(iterator)` is a\n * no-op unless the worker is still serving it, and `iterator.return()` resumes\n * a transport whose own `finally` makes the same check. `return()` on a\n * generator whose body never ran is a no-op besides, the body having never\n * entered its `try`.\n *\n * `release` is the exception and runs unconditionally: it is the owning layer's\n * resource — a lease, a timer, a merge teardown — and it is owed whatever the\n * worker has since moved on to.\n */\nexport const reclaim = ({\n worker,\n iterator,\n state,\n detach,\n release,\n}: Abandoned): void => {\n if (state.done) return;\n state.done = true;\n detach();\n worker.interrupt(iterator);\n void iterator.return(undefined).catch(() => {});\n release?.();\n};\n\nexport type AbandonRegistry = {\n /** Watch `target`; `held` is what the cleanup receives, `token` unregisters. */\n watch: (target: object, held: Abandoned, token: object) => void;\n /** The generator ended by an ordinary route — there is nothing to reclaim. */\n forget: (token: object) => void;\n};\n\n/**\n * `run` is injected so that tests drive the cleanup without a collection.\n * Nothing else here is observable: a `FinalizationRegistry` fires when the\n * engine decides, which is not a schedule a test can assert against.\n */\nexport const createAbandonRegistry = (\n run: (held: Abandoned) => void = reclaim,\n): AbandonRegistry => {\n const registry = new FinalizationRegistry<Abandoned>(run);\n return {\n watch: (target, held, token) => registry.register(target, held, token),\n forget: (token) => registry.unregister(token),\n };\n};\n\n/** The one this library uses. */\nexport const abandonRegistry = createAbandonRegistry();\n","import {\n type Abandoned,\n type AbandonRegistry,\n type AbandonState,\n abandonRegistry,\n reclaim,\n} from './abandon';\nimport type { SQLiteChunkOptions, SQLiteQueryOptions } from './api';\nimport type { PoolWorker } from './pool';\n\n/**\n * Wires an AbortSignal into a promise that rejects the instant the signal\n * fires, and returns a teardown that removes the listener. The rejection sink\n * (`aborted?.catch`) suppresses the unhandled-rejection when the query ends\n * normally and nobody is racing the promise any more.\n *\n * This is the only place in the module that reads an AbortSignal; both\n * `chunk()` and `writeWorker()` delegate here.\n */\nexport const makeAbortRace = (\n signal: AbortSignal | undefined,\n): { aborted: Promise<never> | undefined; teardown: () => void } => {\n if (!signal) return { aborted: undefined, teardown: () => {} };\n let onAbort: (() => void) | undefined;\n const aborted = new Promise<never>((_, reject) => {\n onAbort = () => reject(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n });\n // Nothing consumes this rejection when the query ends normally.\n aborted.catch(() => {});\n return {\n aborted,\n teardown: () => {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n },\n };\n};\n\n/**\n * `SQLiteChunkOptions` plus what only this library passes. `registry` is\n * TEST-ONLY and unsupported; it exists so the abandonment path can be driven\n * without a garbage collection.\n */\nexport type InternalChunkOptions = SQLiteChunkOptions & {\n credits?: number;\n /** The owning layer's teardown, run if the generator is abandoned. */\n onAbandon?: (() => void) | undefined;\n /**\n * Handed the transport iterator, synchronously, before the factory returns.\n *\n * An owner that must close this generator from the outside needs it: a\n * method call on an async generator queues behind a `next()` already in\n * flight, so `return()` alone parks until a chunk arrives — which on an\n * `ORDER BY` is the whole sort. `worker.interrupt(transport)` is what\n * settles that `next()`, and it is a no-op unless the worker still serves\n * that transport, so only its true owner can be handed it. `src/transaction.ts`\n * is the only caller; the client path drops its generator instead of\n * closing it and needs nothing here.\n */\n onTransport?: ((iterator: AsyncGenerator<unknown>) => void) | undefined;\n registry?: AbandonRegistry;\n};\n\n/**\n * The single query primitive. Every other read path is a thin derivation, and\n * abort is implemented here exactly once.\n *\n * **A factory, not a generator function**, so that the transport iterator\n * exists before the generator does and can be handed to the abandonment\n * registry. Building it early costs nothing: `worker.query()` runs no code\n * until its first `next()`, so the query message and the reuse guard still\n * happen when the consumer first pulls.\n */\nexport const chunk = <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: InternalChunkOptions,\n): AsyncGenerator<T[]> => {\n const {\n signal,\n chunkSize,\n credits,\n onAbandon,\n onTransport,\n registry = abandonRegistry,\n } = options ?? {};\n const iterator = worker.query<T>(sql, params, {\n chunkSize,\n credits,\n abortable: signal !== undefined,\n });\n onTransport?.(iterator);\n const state: AbandonState = { done: false };\n const token = {};\n const held: Abandoned = {\n worker,\n iterator,\n state,\n detach: () => {},\n release: onAbandon,\n };\n\n /**\n * D7: an abort must reclaim, not merely reject. `makeAbortRace` inside the\n * generator rejects a promise that an abandoned consumer is no longer\n * awaiting, and that rejection is swallowed — so without this listener a\n * `timeout` buys an abandoned generator nothing at all.\n *\n * This closure captures the factory's scope and never the generator object,\n * so a signal the caller keeps alive does not prevent the collection the\n * registry depends on. `detach` lives on the held value for the same reason\n * it exists at all: the signal belongs to the CALLER and outlives this query,\n * so whichever route reaches the cleanup first must take the listener with\n * it — see `reclaim`.\n */\n if (signal) {\n const onAbort = () => {\n registry.forget(token);\n reclaim(held);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n held.detach = () => signal.removeEventListener('abort', onAbort);\n }\n\n const gen = drain<T>(iterator, worker, signal, held, registry, token);\n registry.watch(gen, held, token);\n return gen;\n};\n\nconst drain = async function* <T extends Record<string, unknown>>(\n iterator: AsyncGenerator<T[] | number>,\n worker: PoolWorker,\n signal: AbortSignal | undefined,\n held: Abandoned,\n registry: AbandonRegistry,\n token: object,\n): AsyncGenerator<T[]> {\n // B9: addEventListener never fires for a signal that is already aborted.\n // D2: this stays HERE and not in the factory above. Lifted, it would throw\n // at call time instead of on the first next(), which every caller feels.\n //\n // This path throws BEFORE the try, so the finally below never runs: it owes\n // its teardown itself.\n if (signal?.aborted) {\n held.state.done = true;\n held.detach();\n registry.forget(token);\n throw signal.reason;\n }\n\n const { aborted, teardown } = makeAbortRace(signal);\n try {\n while (true) {\n // Racing the pending chunk, not testing a flag after it: an ORDER BY\n // sorts entirely inside the first step(), so waiting for a chunk before\n // noticing the abort makes AbortSignal.timeout(n) return minutes late.\n // `aborted` first: D7's reclaim() may already have completed `iterator`\n // by the time this races again, so with both promises pre-settled,\n // array order breaks the tie. Putting `aborted` first keeps the abort\n // observed even though `iterator.next()` also resolves immediately.\n const next = aborted\n ? await Promise.race([aborted, iterator.next()])\n : await iterator.next();\n if (next.done) break;\n // FLK-1: chunks already queued are not delivered once the signal fired.\n if (typeof next.value !== 'number') yield next.value;\n }\n } finally {\n // First, so that neither a later abort nor a collection can run the\n // cleanup a second time on a worker already given back. `done` closes the\n // door that `forget` cannot: the abort listener is not the registry's.\n held.state.done = true;\n held.detach();\n registry.forget(token);\n teardown();\n // Start the stop-and-drain, never await it. The caller must not wait for a\n // sort that may still have minutes to run; the lease returns through\n // quiesce() instead. interrupt() first, so the queued return() is not\n // parked behind a next() that will not settle.\n //\n // Named, like reclaim's: a consumer that comes back to an already-reclaimed\n // generator reaches this finally with the worker long since re-lent.\n worker.interrupt(iterator);\n void iterator.return(undefined).catch(() => {});\n }\n};\n\nexport const streamRows = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: InternalChunkOptions,\n): AsyncGenerator<T> {\n for await (const rows of chunk<T>(worker, sql, params, options)) {\n for (const row of rows) yield row;\n }\n};\n\nexport const readWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteChunkOptions,\n): Promise<T[]> => {\n const result: T[] = [];\n for await (const rows of chunk<T>(worker, sql, params, options)) {\n result.push(...rows);\n }\n return result;\n};\n\n/**\n * First row, then stop. This BREAKS rather than aborting: a break triggers the\n * generator's return path, which runs chunk()'s finally and the transport's\n * stop-and-drain — the same worker-stop routine, reached without an exception.\n * That is why there is no internal AbortController here and no need to tell an\n * internal abort from the caller's.\n */\nexport const firstWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteQueryOptions,\n): Promise<T | undefined> => {\n for await (const rows of chunk<T>(worker, sql, params, {\n ...options,\n chunkSize: 1,\n // Spec §4.1: with the default window of 2 the worker would produce a\n // second row before parking. One credit is the exact one-row bound the\n // JSDoc has always promised.\n credits: 1,\n })) {\n return rows[0];\n }\n return undefined;\n};\n\nexport const writeWorker = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n>(\n worker: PoolWorker,\n sql: string,\n params?: unknown[],\n options?: SQLiteQueryOptions,\n): Promise<{ result: T[]; affected: number }> => {\n const { signal } = options ?? {};\n\n // B9: addEventListener never fires for a signal that is already aborted.\n if (signal?.aborted) throw signal.reason;\n\n const { aborted, teardown } = makeAbortRace(signal);\n const iterator = worker.query<T>(sql, params, {\n abortable: signal !== undefined,\n });\n const result: T[] = [];\n let affected = 0;\n try {\n while (true) {\n // Racing the pending chunk, not testing a flag after it: an ORDER BY\n // sorts entirely inside the first step(), so waiting for a chunk before\n // noticing the abort makes AbortSignal.timeout(n) return minutes late.\n const next = aborted\n ? await Promise.race([iterator.next(), aborted])\n : await iterator.next();\n if (next.done) break;\n // write() is the only caller that needs the affected count, which is why\n // the T[] | number union stays private to this module.\n if (typeof next.value === 'number') affected = next.value;\n else result.push(...next.value);\n }\n } finally {\n teardown();\n // Start the stop-and-drain, never await it. Same pattern as chunk(),\n // transport named for the same reason.\n worker.interrupt(iterator);\n void iterator.return(undefined).catch(() => {});\n }\n return { result, affected };\n};\n","import type {\n Interruptible,\n SQLiteChunkOptions,\n SQLiteQueryAPI,\n SQLiteTransactionDB,\n SQLiteTransactionOptions,\n} from './api';\nimport type { ReadFn, TransactionFn, WriteFn } from './bulk';\nimport { SQLiteError } from './errors';\nimport type { Logger } from './logger';\nimport type { PoolWorker, PoolWorkerQueryOptions } from './pool';\nimport {\n chunk as chunkWorker,\n firstWorker,\n makeAbortRace,\n readWorker,\n streamRows,\n writeWorker,\n} from './queries';\nimport type { Scheduler } from './scheduler';\nimport {\n isTransactionControl,\n isWriteQuery,\n mergeSignals,\n withDeadline,\n} from './utils';\n\n// Drains a statement that returns no rows (BEGIN, COMMIT, ROLLBACK) without\n// the chunkSize-1 + break overhead of firstWorker.\nconst exec = async (worker: PoolWorker, sql: string): Promise<void> => {\n await readWorker(worker, sql);\n};\n\n/**\n * A statement generator the callback still holds, paired with the transport it\n * drains.\n *\n * Both halves are needed to close it from the outside. `gen.return()` alone is\n * queued behind a `next()` the callback left in flight and does not settle\n * until that `next()` does; `worker.interrupt(transport)` is what settles it,\n * and it names the transport because the pool refuses a stop from anyone but\n * the query's current owner.\n *\n * Both fields are filled after the entry exists, which is why both are\n * optional. `gen` is set before the entry ever reaches `open`. `transport`\n * arrives with `onTransport`, which for `chunk()` fires synchronously inside\n * the factory and for `stream()` fires on the first `next()` — `streamRows` is\n * itself a generator, so it does not reach `chunk()` until then. An entry with\n * no transport yet has no query on the worker either, and there is nothing for\n * `interrupt()` to stop.\n */\ntype OpenStatement = {\n gen?: AsyncGenerator<unknown>;\n transport?: AsyncGenerator<unknown>;\n};\n\n/**\n * Returns the `transaction()` method for a SQLiteDB instance.\n *\n * The returned function acquires exactly one lease for the full lifetime of\n * the transaction. All SQLiteTransactionDB methods call worker-bound derivations\n * directly — never the public API — so no secondary lease acquisition can\n * occur during the callback.\n */\nexport const createTransaction =\n (deps: {\n scheduler: Scheduler<PoolWorker>;\n afterWrite: (worker: PoolWorker) => Promise<unknown>;\n /**\n * Called when a connection may still hold an open transaction. The worker\n * is lost rather than repaired: a \"dirty worker\" state is one more\n * state the barrier would have to reason about, while a respawned\n * connection is transaction-free by construction.\n */\n onPoisoned: (index: number, error: SQLiteError) => void;\n /**\n * Aborted when the client closes, with `CLIENT_CLOSED`.\n *\n * Merged into the transaction's own signal so that closing ABANDONS a\n * running transaction the way a caller's `signal` does — the callback is\n * not interrupted, it simply can no longer reach the database. Without it\n * the caller of a transaction whose callback sits on an `await` that is not\n * a statement waited for ever: nothing else in the transaction observes the\n * client going away.\n */\n closeSignal: AbortSignal;\n /**\n * The client's bulk factory. Called per transaction with the transaction's\n * own read/write and a pass-through `transaction`, so output()'s swap runs\n * on the caller's transaction instead of opening a BEGIN SQLite does not\n * allow.\n */\n bulkFor: (target: {\n read: ReadFn;\n write: WriteFn;\n transaction: TransactionFn;\n /** See `src/bulk.ts`: the batch's place in this transaction's queue. */\n reserve?: () => { started: Promise<void>; done: () => void };\n }) => {\n bulkWrite: SQLiteQueryAPI['bulkWrite'];\n output: SQLiteQueryAPI['output'];\n };\n /**\n * Reached only through `always`: `rollback()` on a transaction that has\n * already committed warns whatever the `debug` option says (spec R4) — a\n * warning visible only under debug would be the same as silence.\n */\n logger: Pick<Logger, 'always'>;\n }) =>\n async <T = void>(\n callback: (db: SQLiteTransactionDB) => Promise<T>,\n options?: SQLiteTransactionOptions,\n ): Promise<T> => {\n const { readOnly = false, autoCommit = true } = options ?? {};\n // The deadline is the transaction's own signal from here on: it reaches\n // the lease acquisition, every inner statement through withSignal, and the\n // race against the callback itself.\n const { signal: deadline, release: releaseDeadline } = withDeadline(\n options,\n 'transaction',\n );\n // The close signal joins the caller's own here, at the single place the\n // transaction's signal is built, so it reaches everything the comment above\n // lists without any of them being told about it.\n const { signal: outer, release: releaseClose } = mergeSignals(\n deadline,\n deps.closeSignal,\n );\n // The causes of death decided inside the transaction (spec R1) — an\n // abandoned write, a connection that left — join the three that come from\n // outside by aborting this, so the race, the statements in flight and the\n // handle's ending all see them the same way.\n const death = new AbortController();\n const { signal: merged, release: releaseDeath } = mergeSignals(\n outer,\n death.signal,\n );\n // Never undefined, since death.signal is not — mergeSignals cannot say so.\n const signal = merged ?? death.signal;\n /**\n * How this transaction ended, set once — except that a COMMIT that\n * succeeds records `committed` over a death that landed while it was in\n * flight (spec §4). Every public method of the handle reads it at its\n * entry: once it is set, nothing the handle does reaches the worker,\n * which by then may be serving someone else (spec §1.2).\n */\n type Ending =\n | { kind: 'committed' }\n | { kind: 'rolled-back' }\n | { kind: 'died'; cause: unknown };\n let ending: Ending | undefined;\n // The existing causes of death — the caller's signal, the timeout,\n // close() — all abort `signal`; this is where they become an ending.\n const onAbort = () => {\n ending ??= { kind: 'died', cause: signal?.reason };\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n const closedError = (end: Ending): SQLiteError =>\n new SQLiteError(\n 'TRANSACTION_CLOSED',\n end.kind === 'died'\n ? 'This transaction was abandoned; nothing more can run in it.'\n : `This transaction has already ${end.kind === 'committed' ? 'committed' : 'rolled back'}; nothing more can run in it.`,\n end.kind === 'died' ? { cause: end.cause } : undefined,\n );\n\n /** Kills the transaction with `cause` (spec R1). Nothing once it has ended. */\n const die = (cause: unknown) => {\n if (!ending) death.abort(cause);\n };\n try {\n // The signal aborts the wait too: without it a transaction could not be\n // abandoned while the pool has nothing to lend, which is a state a VFS\n // rotating one exclusive OPFS handle can stay in indefinitely.\n const lease = await deps.scheduler.acquire(\n readOnly ? 'read' : 'write',\n signal,\n );\n const worker = lease.worker;\n\n const checksql = (sql: string): string => {\n if (readOnly && isWriteQuery(sql))\n throw new SQLiteError(\n 'READ_ONLY_TRANSACTION',\n 'Cannot write in a read-only transaction.',\n );\n return sql;\n };\n\n let done = false;\n // Set only once BEGIN has come back. A ROLLBACK sent to a connection that\n // opened no transaction fails, and that failure would lose a healthy\n // worker through onPoisoned.\n let begun = false;\n /**\n * The conclusion owed to the savepoint the last savepointed write left\n * open (spec 2026-09-11, D5). The next message the transaction sends\n * carries it, and the worker runs it before anything else: `release`\n * keeps that write, `undo` rolls it back because its own signal abandoned\n * it. Undefined when no library savepoint is open.\n */\n let pending: 'release' | 'undo' | undefined;\n /**\n * Settles once a write abandoned by its own signal has ended on the\n * worker and been judged (spec 2026-09-11, R2). Every entry point waits\n * for it: nothing may reach the worker while that write still runs.\n * Never rejects.\n */\n let abandoned: Promise<void> | undefined;\n\n /**\n * The tail of this transaction's statement queue.\n *\n * Its statements share one connection with no scheduler lease between\n * them, so they must run one at a time. Issued sequentially they already\n * did — each one awaits `quiesce()` before it settles. Issued in the SAME\n * TICK they did not: both read the worker as free and the second met\n * `pool.ts`'s reuse guard, which lost the whole transaction to\n * `WORKER_BUSY` for what is baseline usage (`Promise.all` over\n * two reads).\n *\n * Each statement captures this SYNCHRONOUSLY and replaces it before its\n * first `await`. That is what makes the order the ISSUE order rather than\n * the resumption order: capturing after a wait would let concurrent\n * statements read the same tail and start together, which is the defect\n * moved rather than fixed.\n *\n * Never rejects — a statement's failure belongs to its caller, not to the\n * queue behind it.\n *\n * **`undefined` when no statement is in flight, and that is load-bearing,\n * not tidiness.** The uncontended path must post SYNCHRONOUSLY, as it\n * always has — the same invariant `entryWait` is careful about. Awaiting\n * an already-resolved tail still costs a microtask, and a statement whose\n * own signal aborts in that window never reaches the worker: it broke\n * spec R7 (`tests/unit/transaction.test.ts`, \"does not die when a read is\n * abandoned by its own signal\"), where the read must run on and be judged.\n */\n let tail: Promise<void> | undefined;\n\n // The SQL ends, for the transaction's own use. BEGIN, COMMIT and\n // ROLLBACK carry no signal, so a death can land while one is in flight.\n const commitNow = async () => {\n await exec(via(false), 'COMMIT');\n done = true;\n // A COMMIT that succeeded is what happened to the data, whatever died\n // meanwhile: overwrite, never keep an earlier death.\n ending = { kind: 'committed' };\n };\n const rollbackNow = async () => {\n // Straight to the worker, never through `via`: a full ROLLBACK\n // discards every savepoint, so there is nothing to conclude — and a\n // RELEASE sent to a connection that already left its transaction would\n // fail and evict a healthy worker (spec 2026-09-11, §4).\n pending = undefined;\n await exec(worker, 'ROLLBACK');\n done = true;\n // A rollback and a death both mean no effect, so a death that landed\n // first is kept, cause and all.\n ending ??= { kind: 'rolled-back' };\n };\n\n /**\n * The worker as one statement sees it (spec 2026-09-11, D9). Its `query`\n * hands the pool a thunk the pool reads only when it POSTS the query —\n * below the reuse guard — so a refused statement neither consumes the\n * pending conclusion nor claims a savepoint; and `mark` learns that the\n * statement reached the worker, which is what owes the idle wait (it\n * replaces `owesWait`: a statement the guard refused was never posted).\n * Everything else is the worker itself, through the prototype: the query\n * helpers call `query` and `interrupt`, and `interrupt` compares\n * transports by identity, which this leaves untouched.\n */\n const via = (open: boolean, mark?: { posted: boolean }): PoolWorker => {\n const facade: PoolWorker = Object.create(worker);\n facade.query = ((\n sql: string,\n params?: unknown[],\n options?: PoolWorkerQueryOptions,\n ) =>\n worker.query(sql, params, {\n ...options,\n savepoint: () => {\n if (mark) mark.posted = true;\n const conclude = pending;\n pending = open ? 'release' : undefined;\n if (!conclude && !open) return undefined;\n return {\n ...(conclude ? { conclude } : {}),\n ...(open ? { open: true as const } : {}),\n };\n },\n })) as PoolWorker['query'];\n return facade;\n };\n\n /**\n * R2 (spec 2026-09-11): a statement issued after a write abandoned by its\n * own signal waits until that write has ended and been judged. `waiting`\n * is the statement's merged signal: its own abort rejects it alone — it\n * has not reached the database — and the transaction's rejects it with\n * the cause. Call it only when `abandoned` is set, so that the common\n * path posts synchronously, as it always has.\n */\n const waitFor = async (\n current: Promise<void>,\n waiting: AbortSignal | undefined,\n ) => {\n // B9: addEventListener never fires for a signal already aborted.\n waiting?.throwIfAborted();\n const { aborted, teardown } = makeAbortRace(waiting);\n try {\n await (aborted ? Promise.race([current, aborted]) : current);\n } finally {\n teardown();\n }\n if (ending) throw closedError(ending);\n };\n\n const entryWait = async (waiting: AbortSignal | undefined) => {\n const current = abandoned;\n if (!current) return;\n await waitFor(current, waiting);\n };\n\n /**\n * Waiting one's turn in the statement queue, with a diagnosis attached.\n *\n * A wait that does not end has exactly one consumer-side cause — a\n * `chunk()`/`stream()` left open, which no statement after it can get\n * past — and that cause is indistinguishable from a consumer whose loop\n * body is merely slow: both leave the worker holding a query with nobody\n * pulling. So this WARNS rather than decides. An advisory may be wrong\n * about a slow consumer and cost nothing; an error may not, and refusing\n * the statement is what this whole change exists to stop doing.\n */\n const QUEUE_WARN_MS = 5_000;\n const queueWait = async (\n prior: Promise<void>,\n waiting: AbortSignal | undefined,\n ) => {\n const advisory = setTimeout(() => {\n deps.logger.always.warn(\n 'A statement has waited several seconds for its turn on this ' +\n \"transaction's connection. Statements in a transaction share one \" +\n 'connection and run one at a time, in issue order. A wait that ' +\n 'never ends is usually a chunk() or stream() generator left open ' +\n '— exhaust it, break out of it, or call its return().',\n );\n }, QUEUE_WARN_MS);\n try {\n await waitFor(prior, waiting);\n } finally {\n clearTimeout(advisory);\n }\n };\n\n /**\n * The write was abandoned by its own signal while it ran (spec\n * 2026-09-11, R1). It runs on, driven by the transaction's signal alone;\n * the next message rolls it back, and every entry point waits for it. If\n * the connection left the transaction meanwhile, the transaction dies as\n * after any statement (spec 2026-09-10, D6).\n */\n const abandon = (running: Promise<unknown>, method: string) => {\n pending = 'undo';\n const judged: Promise<void> = running\n .then(\n () => ({ failed: false, error: undefined as unknown }),\n (error: unknown) => ({ failed: true, error }),\n )\n .then(async ({ failed, error }) => {\n await worker.quiesce();\n dieIfConnectionLeft(failed, error, method);\n })\n .catch(() => {\n // Judging must never reject: every entry point awaits this, and\n // the caller already has its rejection.\n })\n .finally(() => {\n if (abandoned === judged) abandoned = undefined;\n });\n abandoned = judged;\n };\n\n /**\n * Consumes an abandoned generator write to its end, discarding its rows,\n * so the worker's credits keep flowing and the write can finish (spec\n * 2026-09-11, §4). A `next()` still pending from the lost race is queued\n * ahead of this one, as async generators do.\n */\n const drainToEnd = async (source: AsyncGenerator<unknown>) => {\n for (;;) {\n const next = await source.next();\n if (next.done) return;\n }\n };\n\n /**\n * Whether a statement runs inside the library's savepoint (spec\n * 2026-09-11, R1): a write the caller may abandon alone — it carries its\n * own signal or timeout, not already aborted at the call. Only those pay\n * (D4). Never a transaction-control statement (D8).\n */\n const opensSavepoint = (\n sql: string,\n own: AbortSignal | undefined,\n abortedAtCall: boolean,\n ) =>\n own !== undefined &&\n !abortedAtCall &&\n isWriteQuery(sql) &&\n !isTransactionControl(sql);\n\n /**\n * Kills the transaction when the connection reports it is no longer in\n * one (spec R1, D6) — read after quiesce(), once the worker's reply has\n * been processed. The cause is the statement's own error, or, when it\n * succeeded, a TRANSACTION_CLOSED naming it (spec R2).\n */\n const dieIfConnectionLeft = (\n failed: boolean,\n error: unknown,\n method: string,\n ) => {\n if (!begun || ending || worker.inTransaction !== false) return;\n die(\n failed\n ? error\n : new SQLiteError(\n 'TRANSACTION_CLOSED',\n `The connection left the transaction after ${method}().`,\n ),\n );\n };\n\n /**\n * The options a statement runs with: the transaction's signal, merged with\n * the caller's own when they gave one, so either may abort the statement\n * and the reason is always the source's. `release` is owed once the\n * statement has settled — the merge is the only thing here that subscribes\n * to a signal the caller may keep alive far longer than this transaction.\n *\n * `settled` is the second half, and every promise-returning statement must\n * return through it: **a statement does not resolve until the worker is\n * idle again.** Inside a transaction the statements share one worker with\n * no scheduler lease between them, so a statement that leaves its\n * transport without reaching `done` — `first()` on any query with a row\n * left to produce, a `read()`/`write()` cut short by an abort — leaves\n * `pool.ts`'s `deferredChunk` set: `queries.ts` posts the stop and fires\n * `iterator.return()` WITHOUT awaiting it, deliberately, because the\n * client path has a lease to do the waiting and no reason to block. Here\n * nobody does, so the next statement in the same callback meets the reuse\n * guard a microtask later and throws `WORKER_BUSY`.\n *\n * **It costs nothing when there is nothing to wait for.** `quiesce()` is\n * `idle?.promise ?? Promise.resolve()`, and on a query that ended by\n * itself `pool.ts`'s transport finally has already resolved `idle` before\n * `done` is observable here — so the round trip is paid only where the\n * worker really is parked. And it adds no wait that was not already\n * running: that same finally performs the whole stop-and-drain bounded by\n * `drainTimeout`; awaiting `quiesce()` only OBSERVES it.\n *\n * It also owns the statement's own `timeout`: `withDeadline` turns\n * `given.timeout` into a signal exactly like the client path does, and\n * that signal is merged in here alongside the transaction's own —\n * without this a per-statement `timeout` type-checked and bounded\n * nothing.\n *\n * `settled` takes the query helper as a function of the worker facade and\n * the options, so it chooses both. **One exception to the idle wait, by\n * design (spec 2026-09-11, R1/R2):** a savepointed write rejected by its\n * own signal resolves its caller at once and runs on; the wait moves to\n * `abandoned`, which the next entry point awaits. The wait is owed only\n * by a statement that was posted (`mark.posted`) — a statement the reuse\n * guard refused never was.\n */\n const withSignal = <\n O extends {\n signal?: AbortSignal | undefined;\n timeout?: number | undefined;\n },\n >(\n given: O | undefined,\n method: string,\n sql: string,\n /**\n * `false` for a statement that ALREADY holds a place in the queue — the\n * batches of a `bulkWrite`, which reserve theirs synchronously in\n * `flush()` (`src/bulk.ts`). Without this they would wait for the slot\n * they are themselves holding.\n */\n queued = true,\n ): {\n options: O;\n driving: O;\n release: () => void;\n settled: <R>(\n start: (target: PoolWorker, options: O) => Promise<R>,\n ) => Promise<R>;\n own: AbortSignal | undefined;\n abortedAtCall: boolean;\n savepointed: boolean;\n mark: { posted: boolean };\n } => {\n const own = withDeadline(given, method);\n // At the call, before anything can settle: D4 reversed decides on\n // this snapshot, not on whatever `own.signal.aborted` reads once the\n // statement has already rejected.\n const abortedAtCall = own.signal?.aborted === true;\n const merged = mergeSignals(signal, own.signal);\n const release = () => {\n merged.release();\n own.release();\n };\n const savepointed = opensSavepoint(sql, own.signal, abortedAtCall);\n const mark = { posted: false };\n const options = { ...given, signal: merged.signal } as O;\n // A savepointed write's QUERY runs with the transaction's signal alone,\n // so that only a death cuts it: SQLite closes every savepoint when it\n // interrupts a write (spec 2026-09-11, §1).\n const driving = savepointed ? ({ ...given, signal } as O) : options;\n const settled = async <R>(\n start: (target: PoolWorker, options: O) => Promise<R>,\n ): Promise<R> => {\n // Captured and replaced BEFORE the first await, so the queue keeps\n // the issue order even when statements are created in one tick.\n const prior = queued ? tail : undefined;\n const mine = Promise.withResolvers<void>();\n if (queued) tail = mine.promise;\n let failed = false;\n let error: unknown;\n // Set when the caller was rejected by its own signal while the write\n // ran on: from then on the wait belongs to `abandoned`.\n let left = false;\n try {\n if (prior) await queueWait(prior, options.signal);\n if (abandoned) await entryWait(options.signal);\n if (!savepointed) return await start(via(false, mark), options);\n // Its own signal may have fired during the wait: then it never\n // reached the worker, and rejects alone.\n own.signal?.throwIfAborted();\n const running = start(via(true, mark), driving);\n const { aborted, teardown } = makeAbortRace(own.signal);\n try {\n return await (aborted\n ? Promise.race([running, aborted])\n : running);\n } catch (e) {\n if (\n mark.posted &&\n own.signal?.aborted === true &&\n e === own.signal.reason\n ) {\n left = true;\n abandon(running, method);\n }\n throw e;\n } finally {\n teardown();\n }\n } catch (e) {\n failed = true;\n error = e;\n throw e;\n } finally {\n release();\n try {\n if (mark.posted && !left) {\n await worker.quiesce();\n dieIfConnectionLeft(failed, error, method);\n }\n } finally {\n // After quiesce, never before: the next statement in the queue\n // must find the worker genuinely idle. And in a `finally` of its\n // own, because `dieIfConnectionLeft` throws — a death must not\n // strand every statement queued behind it.\n //\n // **Resolved WITH `prior`, not empty.** A statement that leaves\n // the queue early — aborted by its own signal while it was still\n // waiting its turn — never waited for its own place, so handing\n // an empty resolution on would let the next statement start while\n // the one at the head was still in flight. It met the reuse guard\n // there, which is the defect this queue exists to remove\n // (`tx-concurrent.test.ts`, \"rejects a statement aborted while it\n // waits its turn\"). A place left early is passed on, not cancelled.\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n }\n }\n };\n return {\n options,\n driving,\n release,\n settled,\n own: own.signal,\n abortedAtCall,\n savepointed,\n mark,\n };\n };\n\n /**\n * Every statement generator this transaction has handed out and that has not\n * finished. The transaction closes what the callback left open before it\n * commits: an open generator holds a query on the transaction's worker, and\n * the next statement — the auto-COMMIT if nothing else — would trip pool.ts's\n * reuse guard, fail the ROLLBACK in turn and get the worker evicted. On\n * Firefox that eviction strands the rotated exclusive OPFS handle and wedges\n * the pool for good, which is the defect this exists to prevent.\n */\n const open = new Set<OpenStatement>();\n\n /** Runs `release` when the consumer stops reading, however it stops. */\n const releasing = <R>(\n source: AsyncGenerator<R>,\n entry: OpenStatement,\n st: {\n release: () => void;\n own: AbortSignal | undefined;\n abortedAtCall: boolean;\n savepointed: boolean;\n mark: { posted: boolean };\n options: { signal?: AbortSignal | undefined };\n },\n method: string,\n ): AsyncGenerator<R> => {\n // The entry is the box the generator's own `finally` needs: it must\n // remove itself from `open` and cannot name a generator that does not\n // exist until the expression below has returned. The same indirection\n // `src/pool.ts`'s `query` factory uses, for the same reason, and it is\n // also where the transport lands, whenever the factory gets to it.\n const gen = (async function* () {\n if (ending) {\n open.delete(entry);\n st.release();\n throw closedError(ending);\n }\n // The queue, joined at the FIRST next() rather than at creation:\n // this body does not run until the consumer pulls, and a generator\n // created but never pulled holds no worker — making it hold the queue\n // would deadlock every statement behind it. In a `Promise.all` the\n // `for await` pulls before the next statement is issued, so the issue\n // order still holds.\n const prior = tail;\n const mine = Promise.withResolvers<void>();\n tail = mine.promise;\n let failed = false;\n let error: unknown;\n // As in `settled`: set when the consumer was rejected by the\n // statement's own signal while the write ran on.\n let left = false;\n try {\n if (prior) await queueWait(prior, st.options.signal);\n if (abandoned) await entryWait(st.options.signal);\n\n // **The queue waits on the WORKER, not on this object.** A consumer\n // that stops pulling a query which has already reached `done`\n // leaves this body suspended at its `yield` for ever, so the\n // `finally` below never runs — while pool.ts has long since cleared\n // `deferredChunk` and the worker is free. Blocking the queue on\n // that is wrong, and `abandon.test.ts` (\"does not truncate the query\n // the worker has moved on to\") says so.\n //\n // So the release is `free()` — the pool's own \"the guard would let\n // the next one through\", resolved where `deferredChunk` is cleared.\n // NOT `quiesce()`, which waits for the transport's finally and so\n // never fires for a parked consumer. Armed at the FIRST value,\n // strictly after the query has posted, which makes it deterministic\n // rather than a bet on when a task runs. A generator abandoned\n // mid-stream never frees the worker, and that is the case the queue\n // is meant to hold.\n const releaseQueue = () => {\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n };\n let watching = false;\n const watchIdle = () => {\n if (watching) return;\n watching = true;\n void worker.free().then(releaseQueue, releaseQueue);\n };\n\n if (!st.savepointed) {\n try {\n for await (const value of source) {\n watchIdle();\n yield value;\n }\n } finally {\n // `yield*` forwarded the consumer's `return()` to the source on\n // its own; the explicit loop owes it by hand, exactly as the\n // savepointed branch below already does.\n await source.return(undefined);\n }\n return;\n }\n st.own?.throwIfAborted();\n const { aborted, teardown } = makeAbortRace(st.own);\n try {\n while (true) {\n const next = aborted\n ? await Promise.race([source.next(), aborted])\n : await source.next();\n if (next.done) return;\n watchIdle();\n yield next.value;\n }\n } catch (e) {\n if (\n st.mark.posted &&\n st.own?.aborted === true &&\n e === st.own.reason\n ) {\n left = true;\n abandon(drainToEnd(source), method);\n }\n throw e;\n } finally {\n teardown();\n // What `yield*` did for the other branch: the consumer's break or\n // return() reaches the query. Not for an abandoned write, which\n // drainToEnd now owns.\n if (!left) await source.return(undefined);\n }\n } catch (e) {\n failed = true;\n error = e;\n throw e;\n } finally {\n open.delete(entry);\n st.release();\n // The generator half of `settled`'s invariant, and the reason it\n // belongs HERE rather than at the callback's boundary: a generator\n // abandoned BETWEEN two statements — `break` out of a `for await`,\n // an explicit `return()` — is not what closeOpenStatements() sees,\n // since that runs once the callback is over. `drain`'s own finally\n // has already gone out with the interrupt by the time this runs,\n // because `yield*` forwards `return()` to the source and awaits it.\n // Not owed by an abandoned write either: drainToEnd now owns its\n // wait, and judging it is abandon()'s job, not this finally's.\n try {\n if (st.mark.posted && !left) {\n await worker.quiesce();\n dieIfConnectionLeft(failed, error, method);\n }\n } finally {\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n }\n }\n })();\n entry.gen = gen;\n open.add(entry);\n return gen;\n };\n\n /**\n * Close what the callback abandoned. `return()` sends the worker the stop\n * request and starts the drain, but queries.ts's `drain()` fires that off\n * without awaiting it — deliberately, for the registry-driven abandonment\n * path this also serves, where nobody is left waiting. Here somebody is:\n * the next thing this transaction does is talk to the same worker\n * directly, with no scheduler lease gate in between. `worker.quiesce()`\n * is the actual wait — it resolves when the worker's own finally clears\n * `deferredChunk`, which is the pool.ts state the reuse guard reads — so\n * the connection is genuinely idle before COMMIT rather than merely\n * believed to be.\n *\n * `interrupt()` comes first, for the reason `queries.ts`'s own finally\n * gives: a method call on an async generator is queued behind a `next()`\n * already in flight, so a callback that left one outstanding — `void\n * g.next()`, or a `Promise.race` that lost — would park this `return()`\n * for the whole of a sort that may never end. BEGIN, COMMIT and ROLLBACK\n * carry no signal, so nothing else would cut it and the transaction would\n * neither reject nor give its worker back.\n *\n * **What this can cost, and what decides it is the BUILD.** An earlier\n * version of this comment said a transaction carrying no `signal` and no\n * `timeout` passes `abortable: false`. That is wrong: `withSignal` merges\n * the transaction's signal into every statement, `mergeSignals` returns\n * the surviving side when one is absent, and `closeSignal` is always\n * defined — so a statement inside a transaction is ALWAYS abortable, and\n * worker.ts always installs its progress handler. Measured on 2026-09-10:\n * `first()` on a query whose second row costs a 3 M-row recursion returns\n * in 2.4 ms on the async build, against 683 ms for the same query on the\n * client path, which passes no signal and is genuinely not abortable.\n *\n * What is left is the case worker.ts cannot serve: on the `sync` build\n * WITHOUT cross-origin isolation it installs no progress handler at all —\n * no yield to read the stop, no abort slot to poll — so a worker inside\n * `step()` runs to the end of that statement. The same query measures\n * 360 ms there. The wait then runs until the statement ends by itself or\n * `drainTimeout` in pool.ts elapses — 60 s by default — after which the\n * worker is declared dead, `quiesce()` settles, and the slot is evicted.\n * `drainTimeout` is that bound already; stacking a second one on top of\n * it is one more thing to get wrong, not more safety. The origin-wide\n * write lock is held for the whole of it.\n *\n * **What this does not fix.** The eviction still happens — measured on\n * `drainTimeout: 2000` as `workers=3 terminated=2`, and on Firefox, where\n * a rotating exclusive OPFS handle turns it into the amendment A5 puts at\n * 9/40. That is strictly better than the leak this replaces, where the\n * same callback hung forever and never gave the write lock back — a\n * bounded wait and a clean eviction instead of no bound at all — but it\n * is a limit carried forward, not a regression to apologize for.\n */\n const closeOpenStatements = async () => {\n for (const { gen, transport } of [...open]) {\n try {\n if (transport) worker.interrupt(transport);\n await gen?.return(undefined);\n } catch {\n // A generator that throws on the way out must not replace the\n // caller's own error, and must not stop the others from closing.\n }\n }\n await worker.quiesce();\n };\n\n // Guarded at the call, not at the first flush. bulkWrite buffers, so the\n // failure would otherwise surface once the buffer overflows — and for\n // output() later still, trapped inside the createStaging promise.\n const refuse = (method: string) => (): never => {\n throw new SQLiteError(\n 'READ_ONLY_TRANSACTION',\n `${method}() writes, and this transaction is read-only.`,\n );\n };\n\n const bulk = readOnly\n ? {\n bulkWrite: refuse('bulkWrite') as SQLiteQueryAPI['bulkWrite'],\n output: refuse('output') as SQLiteQueryAPI['output'],\n }\n : deps.bulkFor({\n read: (sql, params, given) => {\n if (ending) return Promise.reject(closedError(ending));\n const query = checksql(sql);\n const { settled } = withSignal(given, 'read', query);\n return settled((target, options) =>\n readWorker(target, query, params, options),\n );\n },\n write: (sql, params, given) => {\n if (ending) return Promise.reject(closedError(ending));\n const query = checksql(sql);\n // Not queued: `flush()` took the slot synchronously, the moment\n // the batch was committed to. Asking for a second one here would\n // wait for the first.\n const { settled } = withSignal(given, 'write', query, false);\n return settled((target, options) =>\n writeWorker(target, query, params, options),\n );\n },\n /**\n * A batch takes its place in the queue the instant `flush()` is\n * called — synchronously, from `close()` or from the `enqueue()`\n * that filled the buffer — and holds it until the batch has been\n * written. Without it the batch posts a microtask later and a\n * statement issued after it runs FIRST, which is not an error but a\n * stale read: `tests/browser/tx-concurrent.test.ts` caught a count\n * of 2 where the rows were 4.\n */\n reserve: () => {\n const prior = tail;\n const mine = Promise.withResolvers<void>();\n tail = mine.promise;\n return {\n started: prior ?? Promise.resolve(),\n done: () => {\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n },\n };\n },\n // The caller's transaction is already open. No BEGIN, no COMMIT.\n // db is referenced before its const declaration, deliberately: this arrow\n // only runs when output().close() fires, by which point db is assigned.\n // Moving `bulk` below `const db` breaks the literal that consumes it.\n transaction: (fn) => fn(db),\n });\n\n const db: SQLiteTransactionDB = {\n read: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n if (ending) return Promise.reject(closedError(ending));\n const query = checksql(sql);\n const { settled } = withSignal(given, 'read', query);\n return settled((target, options) =>\n readWorker<T>(target, query, params, options),\n );\n },\n\n write: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: Interruptible,\n ) => {\n if (ending) return Promise.reject(closedError(ending));\n const query = checksql(sql);\n const { settled } = withSignal(given, 'write', query);\n return settled((target, options) =>\n writeWorker<T>(target, query, params, options),\n );\n },\n\n chunk: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n const query = checksql(sql);\n const st = withSignal(given, 'chunk', query);\n const entry: OpenStatement = {};\n // No lease work here: the transaction owns the lease, and\n // iterator.return() resolves `idle`, which settles the\n // quiesce().then(release) already pending in its own finally.\n const source = chunkWorker<T>(\n via(st.savepointed, st.mark),\n query,\n params,\n {\n ...(st.savepointed ? st.driving : st.options),\n onAbandon: st.release,\n onTransport: (iterator) => {\n entry.transport = iterator;\n },\n },\n );\n return releasing(source, entry, st, 'chunk');\n },\n\n stream: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: SQLiteChunkOptions,\n ) => {\n const query = checksql(sql);\n const st = withSignal(given, 'stream', query);\n // streamRows forwards its options straight to chunk(), but it is a\n // generator itself: the transport lands in `entry` on the first\n // next(), not here.\n const entry: OpenStatement = {};\n // No lease work here: the transaction owns the lease, and\n // iterator.return() resolves `idle`, which settles the\n // quiesce().then(release) already pending in its own finally.\n const source = streamRows<T>(\n via(st.savepointed, st.mark),\n query,\n params,\n {\n ...(st.savepointed ? st.driving : st.options),\n onAbandon: st.release,\n onTransport: (iterator) => {\n entry.transport = iterator;\n },\n },\n );\n return releasing(source, entry, st, 'stream');\n },\n\n first: <T extends Record<string, unknown>>(\n sql: string,\n params?: unknown[],\n given?: Interruptible,\n ) => {\n if (ending) return Promise.reject(closedError(ending));\n const query = checksql(sql);\n const { settled } = withSignal(given, 'first', query);\n return settled((target, options) =>\n firstWorker<T>(target, query, params, options),\n );\n },\n\n bulkWrite: ((...args: Parameters<SQLiteQueryAPI['bulkWrite']>) => {\n if (ending) throw closedError(ending);\n return bulk.bulkWrite(...args);\n }) as SQLiteQueryAPI['bulkWrite'],\n output: ((...args: Parameters<SQLiteQueryAPI['output']>) => {\n if (ending) throw closedError(ending);\n return bulk.output(...args);\n }) as SQLiteQueryAPI['output'],\n\n commit: async () => {\n // The only place a COMMIT is refused, and it covers both callers: the\n // explicit tx.commit() and the auto-commit below. Without it a callback\n // that swallowed its statement's rejection could still commit, and the\n // transaction's own rejection would arrive after the data landed. The\n // listener sets `ending` synchronously when the signal aborts, so this\n // guard covers what `throwIfAborted()` did.\n if (ending) {\n if (ending.kind === 'committed') return;\n throw closedError(ending);\n }\n // COMMIT is a statement on the same worker, so it takes its place in\n // the queue like any other: an explicit commit() created alongside\n // the write it concludes must follow it, not race it.\n const prior = tail;\n const mine = Promise.withResolvers<void>();\n tail = mine.promise;\n try {\n if (prior) await queueWait(prior, signal);\n if (abandoned) await entryWait(signal);\n await commitNow();\n } finally {\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n }\n },\n\n rollback: async () => {\n if (ending) {\n if (ending.kind === 'committed')\n deps.logger.always.warn(\n 'rollback() was called on a transaction that has already committed; nothing was rolled back.',\n );\n return;\n }\n const prior = tail;\n const mine = Promise.withResolvers<void>();\n tail = mine.promise;\n try {\n if (prior) await queueWait(prior, signal);\n if (abandoned) await entryWait(signal);\n await rollbackNow();\n } finally {\n if (tail === mine.promise) tail = undefined;\n mine.resolve(prior);\n }\n },\n // The merged signal itself (spec §4): it aborts on every cause of death with the cause as\n // reason, and the outer finally only detaches it, so a normal end leaves it un-aborted for good.\n // The death controller is never exposed — the consumer can listen, not abort.\n signal,\n };\n\n const { aborted, teardown } = makeAbortRace(signal);\n\n try {\n signal?.throwIfAborted();\n // BEGIN carries no signal, and neither do COMMIT and ROLLBACK — this\n // concerns BEGIN in both its deferred and IMMEDIATE forms. Their\n // completion is what decides whether a rollback is owed: a BEGIN that ran\n // on the worker but rejected on the client would return a connection to\n // the pool holding an open transaction, which is the state onPoisoned\n // exists to prevent. The cost is a window — while BEGIN is in flight the\n // transaction cannot be abandoned, and on a VFS rotating one exclusive\n // handle that wait can be long. The abort lands the moment BEGIN settles.\n //\n // A write transaction announces itself: OPFSWriteAheadVFS refuses one\n // that reaches its first write from a deferred BEGIN — \"Write\n // transaction cannot use BEGIN DEFERRED\" — and the client stayed broken\n // afterwards (spec 2026-09-15, A4). The origin write lock is already\n // held here, so IMMEDIATE only moves SQLite's RESERVED lock to the start\n // of a transaction no other writer can be in. A read-only one stays\n // deferred: it takes no write lock and must not ask SQLite for one.\n await exec(via(false), readOnly ? 'BEGIN' : 'BEGIN IMMEDIATE');\n begun = true;\n // That window, closed: the signal may have fired while BEGIN was in\n // flight, and the transaction is open now. The callback never runs.\n signal?.throwIfAborted();\n\n const running = callback(db);\n // Racing the callback, not only its statements: an abort landing while\n // the callback sits in user code — an await on anything that is not a\n // statement — would otherwise be invisible until it returns, which may be\n // never. The callback is not interrupted, it is abandoned; it cannot\n // reach the worker afterwards because every statement it issues inherits\n // the aborted signal and rejects before the round trip, and the lease\n // returns to the pool only after quiesce().\n running.catch(() => {\n // Nothing consumes this rejection when the abort wins the race.\n });\n const result = aborted\n ? await Promise.race([running, aborted])\n : await running;\n\n await closeOpenStatements();\n\n if (!done) {\n // An abort that landed after the callback returned — during\n // closeOpenStatements() — still refuses the COMMIT, and with the\n // cause rather than TRANSACTION_CLOSED: this is the transaction's own\n // outcome (spec R2), not a late statement.\n signal?.throwIfAborted();\n // Spec 2026-09-11, R2: the COMMIT waits for a write abandoned by its\n // own signal, and carries its undo.\n if (abandoned) await entryWait(signal);\n if (autoCommit) await commitNow();\n else await rollbackNow();\n }\n return result;\n } catch (e) {\n // First: tx.signal aborts whenever transaction() rejects, whatever the\n // reason (spec 2026-09-10, R8 amended). die() is a no-op once the\n // transaction has already ended or died, so a death that caused this\n // rejection keeps its own cause; otherwise the handle becomes `died`\n // with cause `e` here, before anything else runs.\n die(e);\n\n // Only roll back if the transaction is still open. `done` is set after the\n // statement succeeds, so a COMMIT that failed leaves it false and the\n // transaction still active — that case must still roll back.\n await closeOpenStatements();\n\n // SQLite may already have left the transaction by itself — an\n // interrupted write rolls the whole transaction back. A ROLLBACK then\n // fails, and failing it evicted a healthy worker (spec §1.1, R6). A\n // worker that has reported nothing yet counts as open: the default can\n // only cost a ROLLBACK that fails, never skip one that was owed.\n if (begun && !done && worker.inTransaction !== false) {\n try {\n await rollbackNow();\n } catch {\n // A failed rollback must not replace the caller's error, which is the\n // one that explains what actually went wrong. But the connection may\n // now hold an open transaction, and a read inside one reads that\n // transaction's snapshot — the barrier would refresh nothing and\n // report success. Evict instead of hoping.\n //\n // An abandoned `chunk()`/`stream()` generator no longer gets here:\n // closeOpenStatements() above drains it before this ROLLBACK is even\n // attempted, so the guard it used to trip never trips. What remains\n // is a connection broken for some other reason — a crashed worker, a\n // transport failure — where the ROLLBACK itself cannot be trusted to\n // have run, and eviction is the only sound response.\n deps.onPoisoned(\n worker.index,\n new SQLiteError(\n 'WORKER_CRASHED',\n `Worker ${worker.index + 1} may hold an open transaction after a failed rollback.`,\n { cause: e },\n ),\n );\n }\n }\n throw e;\n } finally {\n // No path out of transaction() may leave the handle open (spec §4).\n ending ??= { kind: 'rolled-back' };\n // Detached here, before afterWrite() publishes the commit epoch — not\n // after it — so a close() or a timeout landing during afterWrite\n // cannot abort tx.signal on a transaction that has already resolved\n // as committed (spec 2026-09-10, R8 amended). Kept in the outer\n // finally too (idempotent): it covers a failed lease acquisition,\n // which never reaches this inner finally at all.\n releaseDeath();\n signal?.removeEventListener('abort', onAbort);\n teardown();\n // Same reasoning as write(): before the void, because release is\n // asynchronous. A read-only transaction commits nothing and must not\n // bump.\n if (!readOnly) await deps.afterWrite(worker);\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n } finally {\n signal?.removeEventListener('abort', onAbort);\n releaseDeath();\n releaseClose();\n releaseDeadline();\n }\n };\n","import type { SQLiteChunkOptions, SQLiteDB, SQLiteQueryOptions } from './api';\nimport { createBulk } from './bulk';\nimport {\n describeMissing,\n detectFeatures,\n missingFeature,\n} from './capabilities';\nimport { createClientDebug } from './debug';\nimport { advanceSeen, BARRIER_SQL, epochsFor } from './epochs';\nimport { SQLiteError } from './errors';\nimport {\n type ClientInspection,\n type DatabaseInspection,\n inspectWith,\n libraryClientsHold,\n} from './inspect';\nimport {\n clientMarkerName,\n connectionLockName,\n createLocks,\n sharesStorage,\n writeLockName,\n} from './locks';\nimport { createLogger } from './logger';\nimport { createPoolWorker, type PoolWorker } from './pool';\nimport {\n chunk as chunkWorker,\n firstWorker,\n makeAbortRace,\n readWorker,\n streamRows,\n writeWorker,\n} from './queries';\nimport {\n createScheduler,\n type InternalSQLiteClientOptions,\n type WriterPolicy,\n} from './scheduler';\nimport { createSupervisor } from './supervisor';\nimport { createTransaction } from './transaction';\nimport {\n defaultBuildFor,\n type PlatformFeature,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n} from './types';\nimport {\n assertReadable,\n mergeSignals,\n normalizeDatabaseFile,\n renderPragmas,\n resolvePragmas,\n resolveWasmLocation,\n withDeadline,\n} from './utils';\n\n/**\n * SQLite client for browser environments using a pool of Web Workers.\n *\n * Features:\n * - Worker pool management for concurrent SQLite operations\n * - Read/write query differentiation with exclusive write access\n * - Streaming results support for large datasets\n * - Transaction support with rollback capability\n */\n\nconst DEFAULT_POOL_SIZE = 2;\n\n/**\n * Statements retained per worker. Not a consumer option (spec §3.2): the\n * value is declared here rather than in the worker so that exposing it later\n * is one options line, not a move.\n */\nconst DEFAULT_STATEMENT_CACHE_SIZE = 32;\n\n/**\n * Bytes retained per worker, `SQLITE_STMTSTATUS_MEMUSED`. Not a consumer\n * option, for the same reason as the entry count (spec §3.3).\n *\n * 8 MB is not a memory figure, it is a count of concurrent `bulkWrite`s\n * protected. The cache drops the key being re-set before it measures, so what\n * must fit is the sum of the OTHER entries: N alternating templates need\n * `(N - 1) x 3.4 MB`. 8 MB therefore covers three concurrent writers, with a\n * peak of this value plus the largest single statement — not a multiple of it.\n * A budget that cannot hold them does not degrade the cache, it cancels it\n * (+19 % Chromium, +110 % Firefox, measured 2026-09-02).\n */\nconst DEFAULT_STATEMENT_CACHE_BYTES = 8 * 1024 * 1024;\n\n/**\n * The second sentence of an `openTimeout` failure, chosen by the roster.\n *\n * `undefined` — the roster could not be read — keeps the sentence this error\n * has always carried: a guess, but the same guess as before, and never a claim\n * about a registry nobody managed to query.\n *\n * `false` says no client of THIS library holds the database, which is not the\n * same as nobody: a dead context, another library or native code hold nothing\n * this registry can see. The message must not promise more than that.\n */\nconst openTimeoutCause = (held: boolean | undefined): string => {\n if (held === undefined) {\n return 'The database may be held under an exclusive lock by another tab or another client.';\n }\n if (held) {\n return 'Other clients of this library still hold the database.';\n }\n return 'No client of this library holds it — a page reloaded without close(), or a holder outside this library, is the likely cause.';\n};\n\n/**\n * Why a write spent its `timeout` waiting for the origin's write lock.\n *\n * Three states, like `openTimeoutCause` above and for the same reason: the\n * snapshot is taken AFTER the deadline landed, so the lock may already be free\n * by the time anyone looks. Saying that is information; guessing is not.\n *\n * It names a TAB and never a client — the lock's name is the mutex and carries\n * no client identity. The message must not promise more than that.\n */\nconst writeLockCause = (write: DatabaseInspection['write']): string => {\n if (write.tab === null)\n return \" The origin's write lock was already free when this was checked, so the wait was contention between writers rather than one holder that never let go.\";\n if (write.sameTab)\n return \" The origin's write lock is held by this tab — a transaction here has not returned.\";\n return ` The origin's write lock is held by another tab${\n write.waiting > 0\n ? `, with ${write.waiting} writer(s) queued behind it`\n : ''\n }.`;\n};\n\n/**\n * Configuration options for creating a SQLite client.\n */\nexport type CreateSQLiteClientOptions = {\n /**\n * A label for this client, never for the database — the database file is\n * the FIRST argument to `createSQLiteClient`, and this option has no effect\n * on what is opened or where.\n *\n * It is reported as `db.debug.name`, and prefixes the `debug` logger's\n * console output as `\"<name> <n>\"`, where `n` counts the clients created in\n * this tab. Neither form is unique across the origin: the counter is\n * per-tab, so two tabs both produce `\"SQLite 1\"`.\n *\n * @defaultValue `\"SQLite\"`\n */\n name?: string;\n\n /**\n * Number of Web Workers spawned in the pool at initialization.\n * A larger pool allows more concurrent read operations but increases\n * memory consumption and OPFS file handle usage.\n * A VFS that holds a single connection, or gains nothing from a second one,\n * caps this at `1` (`OPFSCoopSyncVFS` among them), and passing more throws at\n * construction time. Omitting it never throws: the default is capped to what\n * the VFS allows.\n * The environment can cap it too: `OPFSWriteAheadVFS` and `OPFSAdaptiveVFS`\n * run on one worker wherever `readwrite-unsafe` is missing, with no error, and\n * warn once only when this option was passed. `db.poolSize` reports the size\n * the pool runs at.\n * @defaultValue `2`, or the VFS's maximum when it is lower\n */\n poolSize?: number;\n\n /**\n * Which VFS stores the database. Required: a VFS decides *where* the bytes\n * live, and a database written through one VFS is not visible through\n * another. See Browser compatibility and recommendations in VFS.md.\n */\n vfs: SQLiteVFS;\n /**\n * Which wa-sqlite WebAssembly build to load. Defaults to the first entry of\n * `VFS_CAPABILITIES[vfs]` — `sync` where the VFS supports it, since it is both the\n * fastest and the most portable, otherwise `async`. `jspi` needs engine\n * support; see the Builds section of VFS.md for versions.\n *\n * @throws at construction when the build is not one the chosen VFS supports.\n */\n build?: SQLiteBuild;\n\n /**\n * Where the workers fetch their `.wasm` from. **An escape hatch, not a\n * setting**: omit it and resolution is exactly what it was before this\n * option existed — the file is taken from beside `worker.js`, which is where\n * the package ships it and where every bundler emits it.\n *\n * Reach for it only when the `.wasm` have been separated from `worker.js`:\n * assets moved by hand with no bundler, or a build whose emitted URL is\n * wrong at runtime.\n *\n * A **string is a directory**, resolved against the page — relative\n * (`'wasm/'`), absolute (`'/static/wasm'`) or a full URL. A missing trailing\n * slash is added. The file name comes from wa-sqlite itself, so one base\n * serves whichever `build` is loaded.\n *\n * A **callback names one file** and receives the resolved `build`, for a\n * bundler-emitted asset whose name carries a content hash:\n * ```ts\n * import wasmUrl from 'browser-sqlite/dist/worker/wa-sqlite.wasm?url';\n * createSQLiteClient('app.db', { vfs, wasmUrl: () => wasmUrl });\n * ```\n * It is called once, at construction, and its answer is reused by every\n * worker and every restart.\n *\n * Serving the `.wasm` from another origin has two requirements beyond this\n * option, both enforced by the browser: the response needs CORS\n * (`Access-Control-Allow-Origin`), since the glue fetches it, and it must\n * carry `Content-Type: application/wasm` for streaming compilation.\n *\n * @throws at construction when the value cannot be parsed as a URL.\n */\n wasmUrl?: string | ((build: SQLiteBuild) => string);\n\n /**\n * SQLite PRAGMAs applied to each worker's database connection on open.\n * Keys are PRAGMA names, values are their string representations.\n * Example: `{ journal_mode: 'WAL', synchronous: 'NORMAL' }`.\n * If omitted, no PRAGMAs are applied beyond SQLite defaults.\n */\n pragmas?: Record<string, string>;\n\n /**\n * How many times a worker slot may be restarted after it has died.\n * A slot that never reached readiness is never restarted — an initial\n * failure is deterministic, and restarting only delays the diagnostic.\n * The counter resets once the replacement has actually served a request.\n * @defaultValue `1`\n */\n maxWorkerRestarts?: number;\n\n /**\n * Milliseconds a worker has to post `ready` after its `open` message is sent.\n * On expiry the slot is failed immediately, with a message naming the cause\n * the client roster supports: another live client of this library holding\n * the database, or a holder the roster cannot see — a page reloaded without\n * `close()`, another library, native code.\n * @defaultValue `30_000`\n */\n openTimeout?: number;\n\n /**\n * Milliseconds the drain loop (in the query generator's `finally`) may run\n * before the worker is presumed dead and the crash path is invoked.\n * @defaultValue `60_000`\n */\n drainTimeout?: number;\n\n /**\n * Turns on the introspection subsystem exposed as `db.debug`, and the\n * lifecycle log. A string is used as the log prefix; `true` falls back to the\n * client name (`\"<name> <index>\"`), which already names the workers.\n *\n * @defaultValue undefined — no collection, no output, `db.debug` undefined.\n */\n debug?: string | boolean;\n\n /**\n * Called whenever a worker slot is permanently lost. Receives the slot index,\n * the number of workers still alive after the loss, the pool's size\n * (`db.poolSize`), and the error that killed the slot.\n *\n * Guaranteed to be called **before** the client is failed when the last slot\n * is lost. Wrapped in try/catch — a throwing callback is reported through\n * `logger.always.warn` and does not break the pool.\n *\n * @defaultValue undefined\n */\n onWorkerLost?: (event: WorkerLostEvent) => void;\n};\n\n/**\n * What `onWorkerLost` receives. Named and exported rather than inlined in the\n * option: a consumer whose handler is a standalone function needs to be able\n * to type its parameter.\n */\nexport type WorkerLostEvent = {\n /** Zero-based index of the lost slot. */\n index: number;\n /** Number of workers still alive after this loss. */\n live: number;\n /** The number of workers the pool runs — `db.poolSize`, not the `poolSize` option: the two differ where the environment caps the pool. */\n size: number;\n /** The error that killed the worker. */\n cause: SQLiteError;\n};\n\nlet clientCount = 0;\n\n/**\n * Worker 0's probe answer, per realm and per feature list (spec 2026-09-15,\n * A1). The engine does not change under a page, and one shared promise makes\n * clients built together request `bsq:conn` in construction order — so the\n * first one constructed wins, whichever worker happens to answer first.\n */\nconst exclusivityProbes = new Map<\n string,\n PromiseWithResolvers<PlatformFeature | null>\n>();\n\n/**\n * Creates a SQLite client backed by a pool of Web Workers, each running\n * a wa-sqlite instance in a dedicated thread.\n *\n * @remarks\n * **Browser requirements:** This client uses OPFS through Web Workers; no\n * special HTTP headers are required and cross-origin isolation is not needed.\n * The default `build` needs no browser opt-in; only `build: 'jspi'` does, and\n * JSPI is Chromium-only — an unrelated constraint, not a header requirement.\n *\n * **Worker pool side effect:** Calling this function immediately spawns\n * `poolSize` Web Worker threads and begins asynchronous database\n * initialization. Workers become queryable once they emit a `ready` message.\n *\n * @param file - SQLite database file name within the OPFS origin.\n * Each distinct name corresponds to a separate database file.\n * @param clientOptions - Pool and VFS configuration. Required: `vfs` has no\n * default, because a VFS decides where the database is written.\n * See {@link CreateSQLiteClientOptions} for field defaults.\n * @returns A {@link SQLiteDB} object providing `read`, `write`, `chunk`,\n * `stream`, `first`, `transaction`, `bulkWrite`, `output`, and `close` methods.\n *\n * @throws {SQLiteError} With code `INVALID_OPTION` when `build` is not one of\n * the builds the chosen `vfs` supports. The message names the supported\n * builds; the pairing is declared once, in `VFS_CAPABILITIES`.\n * @throws {SQLiteError} With code `INVALID_OPTION` when `poolSize` exceeds the\n * `maxPoolSize` the chosen `vfs` declares. The message names the cap and the\n * reason for it; both come from `VFS_CAPABILITIES`.\n *\n * @example\n * ```typescript\n * import { createSQLiteClient } from 'browser-sqlite';\n *\n * const db = createSQLiteClient('myapp.sqlite', {\n * vfs: 'OPFSAdaptiveVFS',\n * pragmas: { journal_mode: 'WAL', synchronous: 'NORMAL' },\n * });\n *\n * const users = await db.read<{ id: number; name: string }>(\n * 'SELECT id, name FROM users WHERE active = ?',\n * [1],\n * );\n * ```\n */\nexport const createSQLiteClient = (\n file: string,\n clientOptions: CreateSQLiteClientOptions,\n) => {\n // One definition of database identity for the workers, the VFS, the epoch\n // registry, every lock name and the returned `db.debug.file`.\n const dbFile = normalizeDatabaseFile(file);\n\n // FIRST, before anything reads the options. `clientOptions` is required in\n // the type, but a JavaScript caller can still omit it entirely — and then\n // every access below would throw a bare TypeError naming nothing. The `?.`\n // here is the only one left in this function, and it is load-bearing: it is\n // what turns a missing argument into the error that says what to pass.\n //\n // Required, and thrown for rather than defaulted: a moving default would\n // leave a consumer reading an empty database while their bytes sat in a VFS\n // nothing queries.\n if (!clientOptions?.vfs) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `vfs is required. Compare VFS and measure your own targets at https://lalexdotcom.github.io/browser-sqlite/`,\n );\n }\n\n const clientIndex = ++clientCount;\n\n const clientName = `${clientOptions.name ?? 'SQLite'} ${clientIndex}`;\n // Identity for the roster: `clientName` is a label two tabs can both produce,\n // this is what tells two clients apart across the origin.\n const clientUuid = crypto.randomUUID();\n\n const vfs = clientOptions.vfs;\n const build = clientOptions.build ?? defaultBuildFor(vfs);\n\n const capability = VFS_CAPABILITIES[vfs];\n\n /**\n * Explicit wins, and is validated below — passing a size the VFS cannot take\n * is a mistake worth reporting. Omitted, the default is capped to what the\n * VFS allows: four VFS hold one connection, and defaulting them to 2 made\n * `{ vfs: 'MemoryVFS' }` alone throw on a number the caller never chose.\n */\n const poolSize =\n clientOptions.poolSize ??\n Math.min(DEFAULT_POOL_SIZE, capability.maxPoolSize ?? DEFAULT_POOL_SIZE);\n const pool: (PoolWorker | undefined)[] = [];\n\n /**\n * The size the pool actually runs at: `poolSize` minus the slots whose worker\n * declined because the environment caps the pool (spec 2026-09-13). What the\n * `poolSize` getter and `onWorkerLost`'s `size` report. Losses do not change\n * it — they are reported with `live`.\n */\n let effectivePoolSize = poolSize;\n let capAnnounced = false;\n\n /**\n * One Int32 per worker, holding the callId to abort. Allocated only in a\n * cross-origin isolated context, because `SharedArrayBuffer` does not exist\n * anywhere else — measured 2026-09-04 on both engines: absent, not\n * restricted. Everywhere else this stays undefined and the whole channel is\n * a branch not taken.\n */\n const abortSlots = detectFeatures().has('cross-origin-isolated')\n ? new SharedArrayBuffer(4 * poolSize)\n : undefined;\n\n // Synchronous: an unsupported combination must fail here and name itself,\n // not surface later as an opaque open-error from a worker that could not\n // instantiate its module.\n if (!(capability.builds as readonly SQLiteBuild[]).includes(build)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} cannot run on the '${build}' build. Supported: ${capability.builds.join(', ')}.`,\n );\n }\n\n // Resolved once, here, and reused by every worker in the pool and by every\n // restart — a callback must not be re-entered per slot. Undefined when the\n // option was not given, which is what leaves the worker's resolution alone.\n const wasm = resolveWasmLocation(clientOptions.wasmUrl, build, location.href);\n\n if (capability.maxPoolSize !== null && poolSize > capability.maxPoolSize) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} does not support pool sizes greater than ${capability.maxPoolSize}: ${capability.poolLimitReason}. Set poolSize: ${capability.maxPoolSize}.`,\n );\n }\n\n // The engine, not the declaration. Without this the mismatch surfaces later\n // as an opaque open-error from a worker that could not instantiate wasm.\n const absent = missingFeature(vfs, build, detectFeatures());\n if (absent) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n describeMissing(vfs, build, absent),\n );\n }\n\n // Fail at construction, not inside the first unrelated query.\n // The VFS's declared defaults with the consumer's layered over them. Every\n // later site reads THIS, never `clientOptions.pragmas` — including the debug\n // state, so what `db.debug` reports is what the workers actually ran.\n const pragmas = resolvePragmas(vfs, clientOptions.pragmas);\n\n // Fail at construction, not inside the first unrelated query. The merged set\n // is what gets validated: a bad default would otherwise reach a worker.\n renderPragmas(pragmas);\n\n // TEST-ONLY, UNSUPPORTED. Read once here, validated, and converted to a\n // typed internal value so no `any` travels further. Absent from the public\n // options type on purpose — see InternalSQLiteClientOptions in scheduler.ts.\n const testWriterPolicy = (clientOptions as InternalSQLiteClientOptions)\n .__unsafeTestWriterPolicy;\n const writerPolicy: WriterPolicy | undefined =\n typeof testWriterPolicy === 'function' ? testWriterPolicy : undefined;\n\n // TEST-ONLY, UNSUPPORTED. Read once here and validated, like the writer\n // policy above. See InternalSQLiteClientOptions in scheduler.ts.\n const testCacheBytes = (clientOptions as InternalSQLiteClientOptions)\n .__unsafeTestStatementCacheBytes;\n const statementCacheBytes =\n typeof testCacheBytes === 'number' && testCacheBytes >= 0\n ? testCacheBytes\n : DEFAULT_STATEMENT_CACHE_BYTES;\n\n // ---------------------------------------------------------------------------\n // Startup state: the deferred first-settle verdict\n //\n // While inStartup is true the gate is still closed and handleDeath skips\n // supervisor entirely. The verdict (fast-fail or single retry round) fires\n // from the scheduler's onFirstSettle callback. inStartup is cleared in\n // onGateOpen (which fires after the retry round, or immediately when all\n // slots opened without failure).\n // ---------------------------------------------------------------------------\n let inStartup = true;\n let startupFirstError: SQLiteError | undefined;\n // Every startup death is recorded here keyed by slot index, and removed in\n // spawn's .then() when the slot becomes ready (retry round success). What\n // remains when the gate opens are the slots that permanently failed.\n const startupLosses = new Map<number, SQLiteError>();\n\n /**\n * Creates a new pool worker and adds it to the pool.\n * Sets up message routing via callId for query responses.\n */\n const scheduler = createScheduler<PoolWorker>(\n (() => {\n // onFirstSettle and onGateOpen are callbacks that fire asynchronously\n // (after all const declarations in this scope have been initialised), so\n // references to spawn / failClient / supervisor / emitWorkerLost /\n // probeSettled are safe even though those names appear later in the\n // source.\n const onFirstSettle = (result: {\n openedCount: number;\n failedIndices: number[];\n }) => {\n if (result.openedCount === 0) {\n // Total startup failure: every slot failed to open. No retry —\n // when nothing opened the config is wrong and a retry only delays\n // the error.\n //\n // Emit loss for every failed slot before failing the client — the\n // contract requires the callback to fire before the client is failed.\n // The supervisor is not consulted here: no R1 restart or liveness\n // logic applies when nothing opened; the failure is total and\n // permanent.\n //\n // startupFirstError is always set when openedCount === 0 because\n // every settled-failed slot goes through handleDeath, which sets it.\n // The fallback is unreachable but satisfies the linter.\n inStartup = false;\n for (const [index, error] of startupLosses) {\n emitWorkerLost(index, error);\n }\n startupLosses.clear();\n failClient(\n startupFirstError ??\n new SQLiteError(\n 'WORKER_CRASHED',\n 'All workers failed to open the database.',\n ),\n );\n return;\n }\n // One retry round, hardcoded. The count is not an option yet because:\n // the startup contention that motivates this path (exclusive OPFS\n // handle rotating between workers on Firefox) is a transient, bounded\n // race, not a persistent fault. One round is enough to resolve it.\n // Exposing a knob before we have evidence the default is wrong would\n // make the option permanent to remove.\n for (const index of result.failedIndices) {\n scheduler.rearmSlot(index);\n spawn(index);\n }\n // If failedIndices is empty the gate opens immediately (no re-arming).\n // If not, it stays closed until retry slots settle.\n };\n\n const onGateOpen = () => {\n inStartup = false;\n // Report all startup deaths to the supervisor first so liveCount() is\n // correct for post-startup R1 decisions, and collect verdicts.\n let failClientError: SQLiteError | undefined;\n for (const [index, error] of startupLosses) {\n // 'lost', not 'died': the retry round is capped at one, so these\n // slots are not coming back and the consumer is about to be told so.\n // 'died' would return 'restart' for a slot that had opened, leave it\n // revivable, and spend a restart that never happens — the supervisor\n // would then disagree with the `onWorkerLost` we emit below.\n const verdict = supervisor.report(index, 'lost');\n // Honour a 'fail-client' verdict from the supervisor.\n if (verdict === 'fail-client') failClientError ??= error;\n // Worker 0 lost before it answered the probe: whatever is left of\n // the pool, no answer will come (see `probeSettled`).\n else if (index === 0 && !probeSettled) failClientError ??= error;\n }\n // Emit all losses BEFORE possibly failing the client — the contract\n // requires the callback to fire before the client is failed.\n for (const [index, error] of startupLosses) {\n emitWorkerLost(index, error);\n }\n startupLosses.clear();\n // Also fail the client when the pool is empty even if no verdict was\n // 'fail-client'. This handles the case where supervisor returns\n // 'restart' for an everReady slot (e.g., slot 0 opened in round 1 and\n // died during the retry round) while slot 1's verdict depends on\n // iteration order — the pool check is order-independent.\n if (\n failClientError !== undefined ||\n pool.filter(Boolean).length === 0\n ) {\n failClient(\n failClientError ??\n startupFirstError ??\n new SQLiteError(\n 'WORKER_CRASHED',\n 'All workers failed to open the database.',\n ),\n );\n }\n };\n\n return writerPolicy\n ? {\n canDesignateWriter: writerPolicy,\n poolSize,\n onFirstSettle,\n onGateOpen,\n }\n : { poolSize, onFirstSettle, onGateOpen };\n })(),\n );\n\n const debugOption = clientOptions.debug;\n\n const debugPrefix =\n typeof debugOption === 'string' ? debugOption : clientName;\n\n const logger = createLogger(debugPrefix, !!debugOption);\n\n const clientDebug = debugOption\n ? createClientDebug(\n dbFile,\n pool,\n {\n vfs,\n pragmas,\n name: clientName,\n },\n () => scheduler.stats(),\n )\n : undefined;\n\n const debug = clientDebug?.state;\n\n const locks = createLocks();\n /**\n * Undefined on the memory VFS, where two clients on one name are two\n * independent databases — see `sharesStorage`.\n */\n const writeLock = sharesStorage(vfs) ? writeLockName(vfs, dbFile) : undefined;\n\n /**\n * Every origin write lock this client holds right now, by its releaser.\n *\n * A lease's `release()` takes its own entry out. The registry exists for the\n * one case that never reaches it: a `transaction()` whose callback never\n * returns holds its lease for ever, so nothing releases the lock and every\n * write in the ORIGIN blocks — silently, in every tab, past `drainTimeout`\n * and past `close()`. `close()` drains this; until it did, the only escape a\n * consumer had reported success and changed nothing\n * (`mem:measurements`, WRITELOCK-STUCK).\n */\n const heldWriteLocks = new Set<() => void>();\n\n /**\n * The release function for `bsq:conn`, held for this client's lifetime on\n * every VFS that shares storage — exclusive or shared, decided at\n * construction or after worker 0's probe (spec 2026-09-15, §3.2). Its\n * absence does not mean refusal: it is also absent before the lock is\n * decided, on the memory VFS, and on a client that failed or closed before\n * its lock was decided. A refusal is `connRefused` (A3).\n */\n let connRelease: (() => void) | undefined;\n /**\n * True once an `ifAvailable` request for `bsq:conn` came back empty: another\n * client holds the database and this one is refused (spec 2026-09-15, A3).\n * Not \"no releaser\", which is also true of a client that failed or closed\n * before its lock was decided — that one reports its own failure.\n */\n let connRefused = false;\n /** The feature whose absence made this client exclusive, for the message. */\n let exclusiveWithout: PlatformFeature | null = null;\n\n const inUse = () =>\n new SQLiteError(\n 'DATABASE_IN_USE',\n `${vfs} supports one connection at a time across the whole origin` +\n (exclusiveWithout\n ? ` without ${exclusiveWithout}, which this browser lacks`\n : '') +\n `. Another tab or client is already connected to '${dbFile}'. ` +\n `Close that client to open a new one here.`,\n );\n\n /**\n * Where this VFS is exclusive only without a feature the page cannot probe,\n * worker 0 probes it and waits (spec 2026-09-15, §3.2). `undefined` means no\n * answer will come: the client failed or closed first.\n */\n const probeAnswer =\n sharesStorage(vfs) && capability.exclusiveConnectionWithout.length > 0\n ? Promise.withResolvers<PlatformFeature | null | undefined>()\n : undefined;\n let sharedProbe: PromiseWithResolvers<PlatformFeature | null> | undefined;\n /**\n * False while an answer is owed: until `probeAnswer` settles — with a worker\n * 0's answer, this client's or the realm's memo (A1), or as \"none\" by\n * `failClient` / `close()`. While it is false, losing slot 0 loses the only\n * worker that will ever answer: nothing would take the connection lock and\n * every method would wait on it for ever, however many workers are left, so\n * the client fails with that slot's error instead (A3). True from the start\n * where no answer is owed.\n */\n let probeSettled = probeAnswer === undefined;\n if (probeAnswer) {\n void probeAnswer.promise.then(() => {\n probeSettled = true;\n });\n const key = capability.exclusiveConnectionWithout.join(',');\n sharedProbe = exclusivityProbes.get(key);\n if (!sharedProbe) {\n sharedProbe = Promise.withResolvers<PlatformFeature | null>();\n exclusivityProbes.set(key, sharedProbe);\n }\n // Subscribed at construction, so in construction order (A1).\n void sharedProbe.promise.then(probeAnswer.resolve);\n }\n /** Set once the lock is ours; from then on slot 0 opens without probing. */\n let lockGranted = false;\n let proceedWorker0: (() => void) | undefined;\n /** Worker 0 opens once BOTH its answer and the lock are in, in either order. */\n const maybeProceed = () => {\n if (!lockGranted || !proceedWorker0 || closing) return;\n const proceed = proceedWorker0;\n proceedWorker0 = undefined;\n proceed();\n };\n\n const holdConnection = (exclusive: boolean): Promise<void> =>\n (\n locks.hold(connectionLockName(vfs, dbFile), {\n mode: exclusive ? 'exclusive' : 'shared',\n ...(exclusive ? { ifAvailable: true } : {}),\n }) as Promise<(() => void) | undefined>\n ).then((release) => {\n connRelease = release;\n connRefused = release === undefined;\n });\n\n /**\n * Settles as soon as the Web Locks API responds to the connection request —\n * after worker 0's probe answer, where the VFS declares\n * `exclusiveConnectionWithout`.\n *\n * The mode is the VFS's: `exclusive` where `exclusiveConnection` is declared,\n * or where worker 0 found a feature of `exclusiveConnectionWithout` missing,\n * so a second client is refused; `shared` everywhere else, so any number of\n * clients coexist while `deleteDatabase` — which asks for the same name\n * exclusively — is still kept out.\n *\n * **`ifAvailable` is exclusive-only, and the asymmetry is deliberate.** A\n * second client on an exclusive VFS must fail fast rather than wait for the\n * first one to close. A shared client must WAIT: the only thing it can queue\n * behind is a delete holding this name, and waiting that delete out is the\n * correct behaviour. Its workers are separately held at `open_v2` by\n * `bsq:init`, which the delete holds too.\n *\n * `undefined` on the memory VFS, where two clients are two databases.\n */\n const connLockPromise: Promise<void> | undefined = !sharesStorage(vfs)\n ? undefined\n : probeAnswer\n ? probeAnswer.promise.then((missing) => {\n // No answer: the client failed or closed first, and takes no lock.\n if (missing === undefined || closing) return;\n exclusiveWithout = missing;\n return holdConnection(missing !== null);\n })\n : holdConnection(capability.exclusiveConnection);\n /**\n * The roster marker: a liveness lock nobody contends, released by the browser\n * if this tab dies without closing. `undefined` on the memory VFS, on the\n * same condition as `bsq:conn` — two clients there are two databases.\n */\n const markerName: string | undefined = sharesStorage(vfs)\n ? clientMarkerName(vfs, dbFile, clientUuid, clientName)\n : undefined;\n let markerRelease: (() => void) | undefined;\n // Unlike `connLockPromise`, this acquisition is NOT awaited in `close()` —\n // the marker must never participate in the close path's timing. Instead, a\n // flag lets a grant that lands after `close()` self-release immediately.\n let markerClosed = false;\n if (markerName !== undefined) {\n void locks\n .hold(markerName, { mode: 'shared' })\n .then((release) => {\n if (markerClosed) {\n // `close()` already ran; release immediately so no phantom appears.\n release();\n } else {\n markerRelease = release;\n }\n })\n .catch(() => {\n // A marker that cannot be taken costs observability, never correctness:\n // occupancy is `bsq:conn`'s job. Never fail an open over it.\n });\n }\n /**\n * The in-flight epoch publication, awaited before the write lock is handed\n * back. Task 6 assigns it; until then it is always already settled.\n */\n let publishing: Promise<unknown> = Promise.resolve();\n /**\n * Aborted by `close()` the moment closing begins. Merged into every pending\n * write-lock request so that close() cancels writes still waiting on the\n * origin-wide lock. A lock already granted is unaffected — per the Web Locks\n * specification the signal cancels a pending request only.\n */\n const closeAbort = new AbortController();\n\n const epochs = epochsFor(vfs, dbFile, locks);\n\n /**\n * The barrier. Runs on a leased worker, so nothing can interleave a\n * statement between it and the query the lease was taken for — the lease\n * supplies the atomicity of the pair for free.\n *\n * `target` is captured BEFORE the statement: if another client commits while\n * it is in flight, this connection did not observe that commit and must not\n * be credited with it.\n */\n const applyBarrier = async (worker: PoolWorker) => {\n // The origin can only ever RAISE the target. That is what lets the local\n // cell stay synchronous — `epochs.bump()` is posted in the write path's\n // finally, and a read chained after write() must see it without awaiting\n // anything. `query()` is async and could never live there.\n const origin = await epochs.originMax();\n epochs.raiseTo(origin);\n\n const target = epochs.current();\n worker.epochTarget = target;\n if (worker.seen >= target) return;\n // Drained, not just dispatched: it is the opening AND closing of the read\n // transaction that refreshes page 1. noServed: true prevents the barrier\n // from resetting the supervisor's restart counter — it is a synthetic probe,\n // not user work.\n const barrierIter = worker.query(BARRIER_SQL, undefined, {\n noServed: true,\n });\n while (!(await barrierIter.next()).done) {\n /* discard rows */\n }\n // Only on success — a failed barrier leaves the worker marked behind so\n // the next attempt re-posts it.\n worker.seen = target;\n };\n\n /** Records a commit. Called after the write, before its promise resolves. */\n const afterWrite = (worker: PoolWorker): Promise<unknown> => {\n const next = epochs.bump();\n worker.seen = advanceSeen(worker.seen, worker.epochTarget, next);\n // Assigned, not awaited: the bump must stay synchronous. The write lock's\n // release awaits this, so no other tab can take the lock, run its query()\n // and miss this marker. A failure leaves this realm correct and the others\n // one commit behind; the next publish restores a higher max.\n publishing = epochs.publish(next).catch((error: unknown) => {\n logger.warn(`epoch publish failed: ${String(error)}`);\n });\n return publishing;\n };\n\n /**\n * Debug-stamps the acquisition with request timing. Extracted from\n * acquireInstrumented so the barrier wrapper can cover both paths uniformly.\n */\n const acquireWithDebug = async (\n kind: 'read' | 'write',\n signal?: AbortSignal,\n ) => {\n // Called only when clientDebug is set — cast to NonNullable to avoid the\n // forbidden non-null assertion operator while preserving the correct type.\n const request = (\n clientDebug as NonNullable<typeof clientDebug>\n ).createRequestDebugState();\n const lease = await scheduler.acquire(kind, signal);\n request.assign(lease.worker.index);\n\n return {\n worker: lease.worker,\n release: () => {\n request.state.releaseTime = Date.now();\n lease.release();\n },\n };\n };\n\n /**\n * Turns a bare `OPERATION_TIMEOUT` spent on the write lock into one that says\n * who was holding it.\n *\n * ONLY the deadline this library minted is enriched. A caller who supplied a\n * `signal` owns its rejection value and gets it back verbatim — that is lot\n * 10's ownership rule, and widening this would weaken it.\n *\n * One `locks.query()`, on the failure path only. If the snapshot cannot be\n * taken the original error is returned untouched: the cause is a courtesy and\n * must never replace the timeout the caller needs to see.\n */\n const explainWriteLockTimeout = async (error: unknown): Promise<unknown> => {\n if (!(error instanceof SQLiteError) || error.code !== 'OPERATION_TIMEOUT')\n return error;\n let cause: string;\n try {\n const inspection = await inspectWith(locks, dbFile, vfs, markerName);\n cause = writeLockCause(inspection.write);\n } catch {\n return error;\n }\n return new SQLiteError('OPERATION_TIMEOUT', `${error.message}${cause}`, {\n cause: error,\n // Spread rather than assigned: `exactOptionalPropertyTypes` refuses an\n // explicit `undefined` where the property is merely optional.\n ...(error.timeout !== undefined ? { timeout: error.timeout } : {}),\n });\n };\n\n /**\n * The single owner of the request level of the debug tree.\n *\n * There are six acquisition sites; instrumenting each is six chances to\n * miss one. This wrapper stamps `acquireTime` (through `assign`) and\n * `releaseTime`, and is a pass-through when debug is off. Nothing outside it\n * calls `scheduler.acquire`. The barrier runs on the acquired lease before\n * the caller sees it — the lease atomically covers the barrier statement and\n * the real query together.\n */\n const acquireInstrumented = async (\n kind: 'read' | 'write',\n signal?: AbortSignal,\n ) => {\n // Connection guard — first thing, before any pool or lock interaction.\n //\n // For VFS exclusive here — `exclusiveConnection: true`, or a feature of\n // `exclusiveConnectionWithout` missing (spec 2026-09-15) — the lock request\n // resolves exactly once: at construction for the first, once worker 0 has\n // answered its probe for the second. Subsequent awaits on an\n // already-settled promise are instant. If the lock was unavailable (another\n // client holds it), fail fast here on every method rather than returning a\n // client that looks healthy but cannot read any table — the silent failure\n // measured as AHP-2TAB (2026-09-01). `connRefused`, not \"no releaser\"\n // (A3): a client whose worker 0 failed before answering reports its own\n // failure.\n if (connLockPromise !== undefined) {\n await connLockPromise;\n if (connRefused) throw inUse();\n }\n\n // Lock BEFORE the lease, never after. The reverse holds a pool worker\n // while blocked on a cross-tab lock: at poolSize 2, two queued writes\n // would starve this tab's own reads behind a lock another tab holds.\n //\n // The wait is abortable by the caller's signal AND by the internal close\n // signal. The HOLD is not aborted — a lock released while SQLite still\n // holds its own would lie. Per the Web Locks spec, the signal cancels a\n // pending request only; once granted the hold is irrevocable from here.\n let releaseWrite: (() => void) | undefined;\n if (kind === 'write' && writeLock) {\n // Merge the caller's signal with the close-abort signal. mergeSignals is\n // used rather than AbortSignal.any to stay within the library's browser\n // floor (Chrome 92 / Firefox 95 / Safari 15.4).\n const { signal: lockSignal, release: releaseMerge } = mergeSignals(\n signal,\n closeAbort.signal,\n );\n let webRelease: () => void;\n try {\n webRelease = await locks.hold(\n writeLock,\n lockSignal ? { signal: lockSignal } : undefined,\n );\n } catch (error) {\n releaseMerge();\n // Surface CLIENT_CLOSED when the close signal caused the abort.\n // If the caller's signal fired first, rethrow the original error —\n // enriched with the holder when the deadline was this library's own.\n if (closeAbort.signal.aborted) throw closeAbort.signal.reason;\n throw await explainWriteLockTimeout(error);\n }\n releaseMerge();\n heldWriteLocks.add(webRelease);\n // Idempotent both ways: `close()` must not release a lock a lease has\n // already handed back, and a lease must not release one `close()` has\n // already reclaimed.\n releaseWrite = () => {\n if (!heldWriteLocks.delete(webRelease)) return;\n webRelease();\n };\n }\n\n let lease: Awaited<ReturnType<typeof scheduler.acquire>>;\n try {\n lease = clientDebug\n ? await acquireWithDebug(kind, signal)\n : await scheduler.acquire(kind, signal);\n } catch (error) {\n releaseWrite?.();\n throw error;\n }\n\n try {\n // Raced, not merely passed a signal. `applyBarrier` drains a real query\n // on the worker, and `PoolWorkerQueryOptions` carries no signal — so on\n // a worker that never answers, that loop is unbounded and every method\n // goes through it. Firefox 154 stopped here, on OPFSCoopSyncVFS, after\n // the two earlier abort paths were closed.\n //\n // This is the second and last phase of a call that was not already\n // abortable: `scheduler.acquire` now honours the signal while queued,\n // and the query phase has honoured it since wave 1. Guarding here rather\n // than at each public method is what makes that complete — an await\n // added to this function later is covered without being remembered.\n //\n // The race abandons the WAIT, not the WORK: the barrier statement runs\n // on. The catch below releases through `quiesce()`, which returns the\n // worker only once it is actually idle, so nothing is re-lent mid-flight.\n const { aborted, teardown } = makeAbortRace(signal);\n try {\n const barrier = applyBarrier(lease.worker);\n await (aborted ? Promise.race([barrier, aborted]) : barrier);\n } finally {\n teardown();\n }\n } catch (error) {\n // The caller never received the lease, so its try/finally cannot return\n // the worker. Release on the same path a normal caller would.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n releaseWrite?.();\n throw error;\n }\n\n if (!releaseWrite) return lease;\n\n // The write lock outlives the worker: it is released once the lease is\n // released AND the epoch this write published has been taken, so no other\n // tab can acquire the lock, run its query(), and miss our marker.\n let handedBack = false;\n return {\n ...lease,\n release: () => {\n lease.release();\n if (handedBack) return;\n handedBack = true;\n void publishing.then(releaseWrite, releaseWrite);\n },\n };\n };\n\n /**\n * A `BUSY` SQLite itself reported, as opposed to one this library minted to\n * mean \"stop and do something else\".\n *\n * A `BUSY` SQLite reported carries `sqliteCode` (5 or 6); a `BUSY` this\n * library mints carries none. The connection guard above mints\n * `DATABASE_IN_USE` without one, and so does `deleteDatabase`, with a `BUSY`\n * of its own beside it — deliberately: those say \"close the other client\",\n * and retrying them would delay exactly the fast failure they exist to\n * produce. Since final review, `sqliteCode` also rides on\n * `STATEMENT_FAILED` and `WORKER_CRASHED`, so the numeric code\n * alone no longer tells a retryable `BUSY` apart from those — the\n * `code === 'BUSY'` check is what does that; the presence of a numeric code\n * is what tells SQLite's `BUSY` apart from this library's own.\n */\n const isRetryableBusy = (error: unknown) =>\n error instanceof SQLiteError &&\n error.code === 'BUSY' &&\n typeof (error as { sqliteCode?: unknown }).sqliteCode === 'number';\n\n /**\n * One read on a fresh lease, returned the moment the worker is idle.\n *\n * **`acquireInstrumented` sits outside the `try` on purpose**: a failed\n * acquisition yields no lease, so there is nothing for a `finally` to\n * release. What that leaves uncovered here — the `timeout` deadline behind\n * `signal`, whose `release()` clears a timer and detaches the listeners\n * `mergeSignals` put on the caller's own signal — is owned one level up, by\n * the `try/finally` each public read method wraps around its call to this.\n * So an acquisition that throws still clears the timer; it is just not this\n * function that does it. `write()` had the same shape and got it wrong once,\n * releasing the deadline only on paths that reached its inner `finally`.\n */\n const onReadLease = async <R>(\n signal: AbortSignal | undefined,\n body: (worker: PoolWorker) => Promise<R>,\n ): Promise<R> => {\n const lease = await acquireInstrumented('read', signal);\n try {\n return await body(lease.worker);\n } finally {\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n };\n\n /**\n * A read, retried once on a SQLite-reported `BUSY`.\n *\n * `OPFSCoopSyncVFS` rotates one exclusive OPFS access handle between\n * workers, and its `jLock` returns `SQLITE_BUSY` while a transfer is in\n * flight — a step of its own protocol, which upstream documents as\n * requiring a retry the caller performs. Measured 2026-09-03 on both\n * engines: exactly one read per session fails this way, early, at the\n * default `poolSize`, and **the first immediate retry cleared it 7 times out\n * of 7** in 10-17 ms. Hence once, and no backoff: a second failure means\n * something other than a handle transfer.\n *\n * Re-issued through a fresh lease, which is what was measured — the retry\n * may land on the worker that now holds the handle.\n */\n const readWithRetry = async <R>(\n signal: AbortSignal | undefined,\n body: (worker: PoolWorker) => Promise<R>,\n ): Promise<R> => {\n try {\n return await onReadLease(signal, body);\n } catch (error) {\n if (!isRetryableBusy(error) || signal?.aborted) throw error;\n return await onReadLease(signal, body);\n }\n };\n\n /**\n * The same retry for the streaming reads, with the one restriction that\n * makes it safe: **only before a single row has been delivered.** Once the\n * consumer has seen a chunk, re-running the query would repeat rows, so a\n * `BUSY` after that point is raised like any other error. `read()` and\n * `first()` buffer, so they never meet this case.\n */\n const streamWithRetry = async function* <Y>(\n signal: AbortSignal | undefined,\n body: (\n worker: PoolWorker,\n onAbandon: () => void,\n ) => AsyncGenerator<Y, void, unknown>,\n ): AsyncGenerator<Y, void, unknown> {\n for (let attempt = 1; ; attempt++) {\n let delivered = false;\n const lease = await acquireInstrumented('read', signal);\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent. This\n // is the same teardown the finally below runs, and it is idempotent, so\n // an abandoned generator reaching it first costs nothing.\n const giveBack = () => {\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n };\n try {\n for await (const item of body(lease.worker, giveBack)) {\n delivered = true;\n yield item;\n }\n return;\n } catch (error) {\n if (\n delivered ||\n attempt > 1 ||\n !isRetryableBusy(error) ||\n signal?.aborted\n ) {\n throw error;\n }\n } finally {\n giveBack();\n }\n }\n };\n\n /**\n * Executes a read query and returns all results.\n * Automatically acquires and releases a worker from the pool.\n *\n * @remarks\n * **Read-your-own-writes is guaranteed within the tab.** Any read issued after a\n * write resolves — from that client or from any other client in the same tab on\n * the same database — observes it, regardless of pool size. It is not guaranteed\n * across tabs.\n */\n const read = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: SQLiteChunkOptions,\n ) => {\n assertReadable(sql, 'read');\n const { signal, release } = withDeadline(options, 'read');\n try {\n return await readWithRetry(signal, (worker) =>\n readWorker<T>(worker, sql, params, { ...options, signal }),\n );\n } finally {\n release();\n }\n };\n\n /**\n * Executes a query and yields result rows in chunks.\n * The single abort-aware primitive — all other read paths derive from this.\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const chunk = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(sql: string, params?: unknown[], options?: SQLiteChunkOptions) {\n assertReadable(sql, 'chunk');\n const { signal, release } = withDeadline(options, 'chunk');\n try {\n yield* streamWithRetry(signal, (worker, onAbandon) =>\n chunkWorker<T>(worker, sql, params, {\n ...options,\n signal,\n onAbandon: () => {\n onAbandon();\n release();\n },\n }),\n );\n } finally {\n release();\n }\n };\n\n /**\n * Executes a query and streams individual rows (flattened from chunks).\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const stream = async function* <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(sql: string, params?: unknown[], options?: SQLiteChunkOptions) {\n assertReadable(sql, 'stream');\n const { signal, release } = withDeadline(options, 'stream');\n try {\n yield* streamWithRetry(signal, (worker, onAbandon) =>\n streamRows<T>(worker, sql, params, {\n ...options,\n signal,\n onAbandon: () => {\n onAbandon();\n release();\n },\n }),\n );\n } finally {\n release();\n }\n };\n\n /**\n * Executes a write query and returns results with affected row count.\n * Automatically acquires and releases a worker from the pool.\n */\n const write = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: SQLiteQueryOptions,\n ) => {\n const { signal, release } = withDeadline(options, 'write');\n try {\n const lease = await acquireInstrumented('write', signal);\n try {\n return await writeWorker<T>(lease.worker, sql, params, {\n ...options,\n signal,\n });\n } finally {\n // Before the await: afterWrite bumps the epoch synchronously so that a\n // read chained after write() sees the new epoch and runs the barrier. In\n // `finally`, so a failed write bumps too: that costs a barrier statement,\n // never a wrong read.\n // Wait for the marker transition (new epoch acquired, previous released)\n // before write() resolves. A caller that queries held lock names\n // immediately after write() must see exactly one marker — the new one.\n await afterWrite(lease.worker);\n // The lease returns when the worker confirms it is idle, not when the\n // caller leaves: a worker still inside step() must not be re-lent, and\n // the caller must not wait for it.\n void lease.worker.quiesce().then(\n () => lease.release(),\n () => lease.release(),\n );\n }\n } finally {\n release();\n }\n };\n\n /**\n * Executes a query and returns only the first row.\n * Breaks after the first chunk — no internal AbortController needed.\n *\n * @remarks\n * **Worker freshness.** See the `read()` remarks — read-your-own-writes is\n * guaranteed within the tab, not across tabs.\n */\n const first = async <\n T extends Record<string, unknown> = Record<string, unknown>,\n >(\n sql: string,\n params?: unknown[],\n options?: SQLiteQueryOptions,\n ) => {\n assertReadable(sql, 'first');\n const { signal, release } = withDeadline(options, 'first');\n try {\n return await readWithRetry(signal, (worker) =>\n firstWorker<T>(worker, sql, params, { ...options, signal }),\n );\n } finally {\n release();\n }\n };\n\n const bulkFor = createBulk({ file: dbFile, locks: createLocks(), logger });\n\n const transaction = createTransaction({\n scheduler: { ...scheduler, acquire: acquireInstrumented },\n afterWrite,\n // Wrapped, not passed by reference: handleDeath is declared further down\n // and would be in its temporal dead zone here.\n onPoisoned: (index, error) => handleDeath(index, error),\n closeSignal: closeAbort.signal,\n bulkFor,\n logger,\n });\n\n const { bulkWrite, output } = bulkFor({ read, write, transaction });\n\n /** Bounds any settlement that depends on a worker answering. */\n const bounded = async (promise: Promise<unknown>, ms: number) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n promise,\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n };\n\n let closing: Promise<void> | undefined;\n\n /**\n * Drains in-flight work, rejects queued work, closes each database\n * connection, then terminates all workers. Bounded by `drainTimeout`.\n * A second call returns the same promise object — runs exactly once.\n */\n const close = (): Promise<void> => {\n if (closing) return closing;\n closing = (async () => {\n logger.info('client closing');\n // A client closed before worker 0 answered takes no lock (spec\n // 2026-09-15, A3): settle the answer as \"none\" so connLockPromise, which\n // this function awaits below, cannot wait on a worker being closed.\n probeAnswer?.resolve(undefined);\n const closingError = new SQLiteError(\n 'CLIENT_CLOSED',\n 'The SQLite client has been closed.',\n );\n // Cancel every pending write-lock request (those still waiting for the\n // browser's lock manager to grant the lock). Writes that already hold\n // the lock are past the lock phase and unaffected — per the Web Locks\n // spec the signal cancels a pending request only. Those writes hold a\n // scheduler lease and are drained by shutdown() below.\n closeAbort.abort(closingError);\n // Shutting the front door: queued scheduler waiters reject at once and\n // no new leases can be taken while in-flight work drains.\n const draining = scheduler.shutdown(closingError);\n // A transaction's lease is held by user code, so this wait is bounded like\n // the rest: a callback that never returns must not make close() hang.\n await bounded(draining, drainTimeout);\n await Promise.all(\n pool.map(async (worker) => {\n if (!worker) return;\n await bounded(worker.close(), drainTimeout);\n // The reason in-flight and later requests reject with. Without it a\n // transaction's callback met a silent hang instead of an error.\n worker.terminate(closingError);\n }),\n );\n pool.length = 0;\n\n // Only here: the workers are gone, so SQLite holds nothing of its own\n // and releasing lies about nothing — the condition acquireInstrumented's\n // comment sets on ever doing this at all. What is left in the registry is\n // a lock whose lease was never returned, which means a transaction whose\n // callback has not come back. Without this the origin stays unwritable\n // for every tab until this one is closed, and `close()` says `ok` on the\n // way out.\n for (const release of [...heldWriteLocks]) release();\n heldWriteLocks.clear();\n\n // Release the exclusive connection lock after workers are gone, so no\n // incoming client can grab the database while our OPFS handles are still\n // being closed. Await `connLockPromise` to handle the edge case where\n // `close()` is called before the first query ever ran (lock in flight).\n if (connLockPromise !== undefined) await connLockPromise;\n connRelease?.();\n markerClosed = true;\n markerRelease?.();\n // Yield one event-loop turn so the browser's lock manager processes the\n // release before `close()` resolves. Without this, a new client\n // constructed immediately after `await a.close()` may try its\n // `ifAvailable` lock request before the engine marks the lock free —\n // getting `null` (lock busy) instead of the lock, and surfacing BUSY on\n // a database that is actually available. A single setTimeout(0) is\n // enough: the lock release is a microtask queued synchronously by\n // `connRelease()`, so it settles within the same event-loop turn, and\n // the next turn (setTimeout callback) sees the freed lock.\n if (connRelease !== undefined) {\n await new Promise<void>((r) => setTimeout(r, 0));\n }\n })();\n return closing;\n };\n\n const openTimeout = clientOptions.openTimeout ?? 30_000;\n const drainTimeout = clientOptions.drainTimeout ?? 60_000;\n\n const supervisor = createSupervisor({\n size: poolSize,\n maxWorkerRestarts: clientOptions.maxWorkerRestarts,\n });\n\n let fatal: SQLiteError | undefined;\n\n const failClient = (error: SQLiteError) => {\n // Spec 2026-09-15, A3: queries awaiting the connection lock must reach the\n // scheduler's `fatal`, not wait on an answer that will never come.\n probeAnswer?.resolve(undefined);\n fatal ??= error;\n void scheduler.shutdown(fatal);\n for (const dying of pool) dying?.terminate(fatal);\n };\n\n const spawn = (index: number) => {\n // The slot holds a worker again from here — not from `ready`. Without this,\n // a restarted slot stays marked dead and the replacement's own death is\n // taken for a duplicate signal about the worker it replaced: no decision\n // comes back, nothing restarts, nothing fails, and the pool is empty and\n // silent for the rest of the client's life.\n supervisor.report(index, 'spawned');\n // The verdict is no longer decided in the same turn as the expiry: reading\n // the roster puts up to `libraryClientsHold`'s bound between the timer\n // firing and `handleDeath`, and a slot CAN settle inside that window. It\n // was atomic before — `clearTimeout` in the `.finally` below could only\n // lose the race by never running. Reporting the death of a worker that\n // became ready meanwhile would terminate a live worker and leave the\n // supervisor holding `ready` for a slot the pool no longer has; during\n // startup it would also put the slot back into `startupLosses`, which\n // `spawn`'s `.then` had just cleared, and `onGateOpen` would report it\n // permanently lost.\n //\n // Nothing in the suite reproduces the window: it needs a worker that opens\n // between `openTimeout` and `openTimeout + 250 ms`, which no test can time.\n let settled = false;\n const timer = setTimeout(() => {\n // The roster is read BEFORE the death is reported, because the message is\n // all the consumer ever sees — a sentence appended afterwards would\n // arrive after the error had been thrown. `libraryClientsHold` takes no\n // lock, touches nothing this client owns, never throws and is bounded, so\n // the report is delayed by at most that bound after a wait of\n // `openTimeout`.\n void libraryClientsHold(locks, dbFile, vfs, clientUuid).then((held) => {\n if (settled) return;\n handleDeath(\n index,\n new SQLiteError(\n 'TIMEOUT',\n `Worker ${index + 1} did not become ready within ${openTimeout} ms. ` +\n openTimeoutCause(held),\n ),\n );\n });\n }, openTimeout);\n\n void createPoolWorker({\n index,\n pool,\n clientName,\n file: dbFile,\n vfs,\n build,\n wasm,\n pragmas,\n statementCacheSize: DEFAULT_STATEMENT_CACHE_SIZE,\n statementCacheBytes,\n onDeath: handleDeath,\n onServed: (served) => {\n supervisor.report(served, 'served');\n },\n drainTimeout,\n createWorkerDebugState: clientDebug?.createWorkerDebugState,\n createQueryDebugState: clientDebug?.createQueryDebugState,\n logger,\n abortSlots,\n // Slot 0 never declines; only surplus workers may. Where a probe is owed,\n // slot 0 probes instead (`probeFirst` below) and opens once told to.\n declineWithout:\n index > 0 && capability.singleConnectionWithout.length > 0\n ? capability.singleConnectionWithout\n : undefined,\n // Spec 2026-09-15, §3.2: slot 0 waits for the lock until one is ours;\n // a slot 0 restarted after that opens straight away.\n probeFirst:\n index === 0 && probeAnswer !== undefined && !lockGranted\n ? capability.exclusiveConnectionWithout\n : undefined,\n onProbed: (missing, proceed) => {\n proceedWorker0 = proceed;\n sharedProbe?.resolve(missing);\n maybeProceed();\n },\n })\n .then((result) => {\n if ('declined' in result) {\n // Same reason as the ready branch below: this slot may have failed\n // in a prior round and be recorded in startupLosses, and declining\n // in the retry is not a loss — it must not be reported permanently\n // lost in onGateOpen.\n startupLosses.delete(index);\n retireSlot(index, result.declined);\n return;\n }\n supervisor.report(index, 'ready');\n // If this slot was recorded in startupLosses (it failed in a prior\n // round and is now recovering in the retry), remove the record so it\n // is not reported as permanently lost in onGateOpen.\n startupLosses.delete(index);\n scheduler.add(result);\n })\n .catch(() => {\n // The rejection is the death already reported through onDeath.\n })\n .finally(() => {\n // Both outcomes settle the slot: a worker that opened is alive, and one\n // that died has already been reported through `onDeath`. Either way the\n // timer's verdict is stale.\n settled = true;\n clearTimeout(timer);\n });\n };\n\n /**\n * Permanently loses a worker slot: logs the loss via the always-on channel\n * and calls the onWorkerLost callback (if provided). Must be called BEFORE\n * failClient so the callback sees the event before the client is shut down.\n */\n const emitWorkerLost = (index: number, error: SQLiteError) => {\n // pool[index] is already undefined here (cleared by handleDeath or startup).\n const live = pool.filter(Boolean).length;\n logger.always.warn(\n `worker ${index + 1} lost; pool is now ${live} of ${effectivePoolSize} (${error.message})`,\n );\n const cb = clientOptions.onWorkerLost;\n if (cb) {\n try {\n cb({ index, live, size: effectivePoolSize, cause: error });\n } catch (cbError) {\n logger.always.warn(\n `onWorkerLost callback threw: ${cbError instanceof Error ? cbError.message : String(cbError)}`,\n );\n }\n }\n };\n\n /**\n * A slot whose worker declined to open: the environment caps the pool\n * (spec 2026-09-13, D1). Not a loss — no `onWorkerLost`, no restart — and\n * announced once, as a warning only when the consumer asked for a pool size\n * (D2). The supervisor hears of it BEFORE the scheduler, because retire()\n * may open the gate synchronously and onGateOpen reads liveCount.\n */\n const retireSlot = (index: number, missing: PlatformFeature) => {\n pool[index]?.terminate();\n // Once the client is closing, effectivePoolSize and the cap no longer\n // matter, and pool[index] must not be written: close() truncates pool\n // with `pool.length = 0`, and a write here would re-extend it.\n if (!closing) {\n pool[index] = undefined;\n effectivePoolSize -= 1;\n }\n supervisor.report(index, 'retired');\n scheduler.retire(index);\n if (closing || capAnnounced) return;\n capAnnounced = true;\n const message = `${vfs} gains nothing from more than one worker without ${missing}: pool capped at 1 of ${poolSize}`;\n if (clientOptions.poolSize !== undefined) logger.always.warn(message);\n else logger.info(message);\n };\n\n const handleDeath = (index: number, error: SQLiteError) => {\n // Snapshot inStartup BEFORE scheduler.remove() may trigger onFirstSettle or\n // onGateOpen (both of which can change inStartup synchronously).\n const wasInStartup = inStartup;\n\n if (wasInStartup) {\n // During startup (gate still closed), defer the verdict to onFirstSettle.\n // A slot that fails before all others have settled must not be restarted\n // or marked lost immediately: we cannot yet distinguish \"only this slot\n // is broken\" from \"contention during startup resolved itself for others\".\n //\n // Set startupFirstError BEFORE scheduler.remove() so that onFirstSettle\n // (which fires synchronously inside remove()) reads the correct error.\n startupFirstError ??= error;\n // Record every startup death — not only retry-slot deaths (defect 1).\n // The entry is removed in spawn's .then() when the slot becomes ready,\n // so only permanently lost slots remain by the time onGateOpen fires.\n startupLosses.set(index, error);\n }\n\n // Terminate and clear BEFORE scheduler.remove() so that emitWorkerLost\n // (called from onGateOpen / post-startup path, both inside or after remove)\n // computes the correct live count from pool.\n pool[index]?.terminate(error);\n pool[index] = undefined;\n\n scheduler.remove(index); // may synchronously trigger onFirstSettle/onGateOpen\n\n if (wasInStartup) return; // startup: handled entirely by the scheduler callbacks\n\n // Post-startup: apply R1 exactly as before — supervisor decides.\n const decision = supervisor.report(index, 'died');\n if (decision === 'restart') {\n logger.warn(`restarting worker ${index + 1}`);\n void spawn(index);\n } else if (decision === 'lost') {\n // Slot permanently lost, but the pool still has workers — none of which\n // can stand in for worker 0 before it has answered (`probeSettled`).\n emitWorkerLost(index, error);\n if (index === 0 && !probeSettled) failClient(error);\n } else if (decision === 'fail-client') {\n // Last worker gone — emit loss (with live=0) before failing the client.\n emitWorkerLost(index, error);\n failClient(error);\n }\n };\n\n // Initialize the worker pool with the requested number of workers.\n //\n // When an exclusive connection lock is required (e.g. AccessHandlePoolVFS),\n // defer spawning until after the lock settles. On Firefox, workers that open\n // OPFS handles while another client already holds them crash with\n // WORKER_CRASHED (\"No modification allowed\") before `acquireInstrumented`\n // can surface a legible DATABASE_IN_USE error. Deferring prevents that:\n // workers never start when the lock is held elsewhere, and `failClient` sets\n // `fatal` so any query on B surfaces DATABASE_IN_USE via the\n // `acquireInstrumented` guard.\n const startWorkers = () => {\n for (let index = 0; index < poolSize; index += 1) spawn(index);\n };\n // Spec 2026-09-15, §3.2 and A2: the whole pool spawns now, as for every\n // shared VFS; worker 0 waits for this decision. Without the feature the\n // surplus workers decline on `singleConnectionWithout` before touching the\n // file; with it they open alongside.\n if (probeAnswer && connLockPromise !== undefined) {\n void connLockPromise.then(() => {\n if (connRefused) {\n failClient(inUse());\n } else if (connRelease !== undefined) {\n lockGranted = true;\n maybeProceed();\n }\n });\n }\n if (capability.exclusiveConnection && connLockPromise !== undefined) {\n void connLockPromise.then(() => {\n if (connRefused) {\n // Lock was held elsewhere — fail the client now so workers never open\n // the database. The DATABASE_IN_USE error here matches the one thrown\n // in `acquireInstrumented`, ensuring the first query on this client\n // fails with a legible message rather than a WORKER_CRASHED stall.\n //\n // This code is not independently covered by any test: both sites fire\n // on the same condition (`connRefused` after\n // connLockPromise settles), and acquireInstrumented's throw wins on\n // every method call because it runs before scheduler.acquire. A\n // silent revert of this code stays green.\n failClient(inUse());\n } else if (!closing) {\n // Guard: if close() was called before the lock settled, the pool\n // sweep has already run (pool.length = 0) and close() is now\n // awaiting this very promise. Spawning workers here would orphan\n // them — close() will not see them because it has already cleared\n // the pool. Checking `closing` prevents that: a closing client\n // has no use for workers, and not starting them is strictly better\n // than starting and immediately terminating them.\n //\n // Falsifiable: remove this `else if (!closing)` guard and replace\n // with a plain `else`. Then create an AHP client, call close()\n // immediately (before any query), and create a new client on the\n // same name — the orphaned worker holds OPFS handles, and the new\n // client's own worker gets WORKER_CRASHED.\n startWorkers();\n }\n });\n } else {\n startWorkers();\n }\n\n /**\n * The census, from this client's point of view.\n *\n * Throws `CLIENT_CLOSED` like every other method: a uniform contract on `db`\n * is worth more than one method a consumer must read the docs to know\n * survives. After closing, `inspectDatabase(file, { vfs })` answers the same\n * question, and `db.file` / `db.vfs` are what make it reachable.\n */\n const inspect = async (): Promise<ClientInspection> => {\n if (closing) {\n throw new SQLiteError(\n 'CLIENT_CLOSED',\n 'The SQLite client has been closed.',\n );\n }\n const { clients, ...base } = await inspectWith(\n locks,\n dbFile,\n vfs,\n markerName,\n );\n return {\n ...base,\n self: clients.find((client) => client.id === clientUuid) ?? null,\n siblings: clients.filter((client) => client.id !== clientUuid),\n };\n };\n\n // Return the public API\n const api = {\n chunk,\n read,\n write,\n stream,\n first,\n transaction,\n bulkWrite,\n output,\n close,\n\n get id() {\n return clientUuid;\n },\n get name() {\n return clientName;\n },\n get file() {\n return dbFile;\n },\n get vfs() {\n return vfs;\n },\n get build() {\n return build;\n },\n get poolSize() {\n return effectivePoolSize;\n },\n inspect,\n\n debug,\n };\n return api;\n};\n","/**\n * Pure worker scheduling: availability, wait queues, writer designation.\n *\n * This module is deliberately free of `Worker` and DOM imports so\n * it can be exercised by fast Node tests. B1 survived for months because the\n * scheduler was only reachable through slow browser tests.\n */\n\nimport type { CreateSQLiteClientOptions } from './client';\n\n/**\n * A borrowed worker. `release()` is the only way back into the pool and is\n * idempotent — a second call is a no-op, not an error.\n */\nexport type Lease<W> = {\n readonly worker: W;\n release: () => void;\n};\n\n/**\n * Decides whether a worker index may hold the write designation. The default\n * accepts every index, so production behaviour is exactly what it was.\n */\nexport type WriterPolicy = (index: number) => boolean;\n\n/**\n * TEST-ONLY, UNSUPPORTED, removable without notice.\n *\n * The barrier's browser test needs the failing configuration — writer not on\n * the worker that serves the read — to be deterministic; at startup chance it\n * occurs ~3 runs in 10. This type is declared here, and NOT in `client.ts`,\n * because `src/index.ts` re-exports only `./client` and `./errors`: keeping it\n * out of that path keeps it out of the published `.d.ts` and out of every\n * consumer's autocompletion. `CreateSQLiteClientOptions` is pulled in with\n * `import type`, which is erased at build time and creates no runtime cycle.\n *\n * A predicate that refuses every index leaves writes queued forever — use it\n * with `poolSize >= 2`.\n */\nexport type InternalSQLiteClientOptions = CreateSQLiteClientOptions & {\n __unsafeTestWriterPolicy?: WriterPolicy;\n /**\n * TEST-ONLY, UNSUPPORTED. The byte bound has no falsifier without it: at a\n * fixed default nothing in the suite can tell a working bound from one that\n * never fires. Absent from the public options type on purpose.\n */\n __unsafeTestStatementCacheBytes?: number;\n};\n\nexport type Scheduler<W> = {\n add: (worker: W) => void;\n /**\n * Leases a worker, queueing when none is free.\n *\n * `signal` aborts the WAIT, and only the wait: it rejects with\n * `signal.reason` while the request is still queued, and is ignored once a\n * lease has been granted — from that point the caller owns the worker and\n * owes a `release()`. Without it an abort could not land at all while the\n * pool had nothing to lend, which is the state a VFS rotating one exclusive\n * OPFS handle can stay in indefinitely.\n */\n acquire: (kind: 'read' | 'write', signal?: AbortSignal) => Promise<Lease<W>>;\n /**\n * Takes a worker out of the pool for good. A lease already outstanding on\n * that index becomes inert: its `release()` neither hands the worker back nor\n * counts towards `shutdown()`'s wait.\n */\n remove: (index: number) => void;\n /**\n * Takes a slot out of the pool for good because its worker DECLINED to open:\n * the environment caps the pool below its requested size (spec 2026-09-13).\n * Unlike `remove()`, the slot settles the readiness gate as neither opened\n * nor failed, so it never appears in `onFirstSettle`'s `failedIndices` and\n * never enters the startup retry round.\n */\n retire: (index: number) => void;\n /**\n * Closes the front door. Queued waiters reject with `reason`, later\n * acquisitions reject the same way, and the returned promise settles when the\n * last outstanding lease has come back.\n */\n shutdown: (reason: Error) => Promise<void>;\n /**\n * Read-only counters for the debug subsystem. The scheduler stays pure: it\n * exposes numbers and knows nothing about debug (spec §3.2).\n */\n stats: () => {\n read: number;\n write: number;\n available: number;\n leased: number;\n /**\n * Callers suspended on the readiness gate. They are in NEITHER wait queue —\n * the gate is awaited before `takeAvailable` is ever reached — so `read`\n * and `write` cannot see them, and without this the debug surface reports\n * an idle pool for the whole startup window.\n *\n * Waiting for the pool to *exist* is a different wait from waiting for a\n * free worker, which is why this is its own counter and not folded in.\n */\n gated: number;\n };\n /**\n * Removes a slot from the settled-set so that its next `add()` or `remove()`\n * call counts again toward opening the readiness gate. Only effective while\n * the gate is still closed; a no-op once the gate has opened.\n *\n * Used by the startup retry round: the client re-arms the failed slots so\n * the gate stays closed until the retry slots have settled.\n */\n rearmSlot: (index: number) => void;\n};\n\n/**\n * Creates a scheduler over workers identified by a numeric `index`.\n *\n * @param opts.onIdle - Called when a released worker returns to the available\n * set with nothing queued behind it. The scheduler itself knows nothing about\n * worker state.\n */\nexport const createScheduler = <W extends { index: number }>(\n opts: {\n onIdle?: (worker: W) => void;\n canDesignateWriter?: WriterPolicy;\n /**\n * Total number of worker slots the pool will spawn. Once every slot has\n * settled (via `add` when it becomes ready, or via `remove` when it dies or\n * fails to open), a one-shot gate is lifted and `acquire()` may proceed.\n * Omit or pass 0 for an immediately-open gate (tests and single-shot use).\n */\n poolSize?: number;\n /**\n * Called exactly once when every slot in [0, poolSize) has settled for the\n * first time. Fires before the gate opens so the callback can call\n * `rearmSlot()` to extend the wait for a retry round.\n *\n * `openedCount` — slots that settled via `add()` (became ready).\n * `failedIndices` — slots that settled via `remove()` (died / timed out).\n */\n onFirstSettle?: (result: {\n openedCount: number;\n failedIndices: number[];\n }) => void;\n /**\n * Called when the readiness gate resolves (opens). Not called when the gate\n * is rejected via `shutdown()`. Use this to clear any startup-pending flag\n * after the retry round (if any) has fully settled.\n */\n onGateOpen?: () => void;\n } = {},\n): Scheduler<W> => {\n const workers: (W | undefined)[] = [];\n\n // Availability lives HERE and nowhere else. No worker carries an `available`\n // flag, so no other module can republish a borrowed worker — which is exactly\n // how B1 happened.\n //\n // A second guarantee rests on this set, and nothing about it is visible from\n // here. A leased worker leaves `available` until `release()` puts it back, so\n // exactly one query is ever in flight per worker. `worker/statement-cache.ts`\n // is built on that and takes no lock of any kind: its statements outlive the\n // query that compiled them, and are reset and cleared on the way out. Lend a\n // worker to a second concurrent caller and that reset lands on a statement\n // another query is part-way through — rewound cursor, cleared bindings, wrong\n // rows — while losing a worker can finalise a handle that other query still\n // holds, which is a use-after-free on a `sqlite3_stmt` pointer. Before the\n // cache, breaking this was merely confusing.\n const available = new Set<number>();\n\n const dead = new Set<number>();\n const leased = new Set<number>();\n // Per-index generation counter. Bumped by remove() so that a release() from\n // a lease created before the remove can detect it is stale and do nothing.\n const generations = new Map<number, number>();\n const gen = (index: number) => generations.get(index) ?? 0;\n\n let shutdownReason: Error | undefined;\n let shutdownDeferred: PromiseWithResolvers<void> | undefined;\n\n // One-shot readiness gate: lifts once every slot in [0, poolSize) has\n // settled — either via add() (ready) or remove() (died / failed to open).\n // poolSize 0 or absent → gate is open from the start.\n //\n // Genuinely one-shot: once gateOpen is true it stays true. A worker that\n // restarts (remove → add) after the gate has lifted must not re-block callers\n // already in flight.\n const settledSlots = new Set<number>();\n let gateOpen = (opts.poolSize ?? 0) === 0;\n const gateDeferred = Promise.withResolvers<void>();\n if (gateOpen) gateDeferred.resolve();\n // Suppress unhandled-rejection when shutdown() fires before any acquire()\n // has attached a handler. Each awaiting acquire() still sees the rejection.\n void gateDeferred.promise.catch(() => {});\n\n // Tracks slots that settled via add() (became ready) in the first round,\n // used to compute openedCount/failedIndices for onFirstSettle.\n const firstSettleOpened = new Set<number>();\n let firstSettleFired = false;\n\n // Slots that settled via retire(): neither opened nor failed, so\n // onFirstSettle must not report them as failures.\n const declinedSlots = new Set<number>();\n\n // Callers currently suspended on the gate. See `stats().gated`.\n let gatedWaiters = 0;\n\n const settleGateSlot = (\n index: number,\n kind: 'opened' | 'failed' | 'declined',\n ) => {\n if (gateOpen || settledSlots.has(index)) return;\n settledSlots.add(index);\n if (kind === 'opened') firstSettleOpened.add(index);\n if (kind === 'declined') declinedSlots.add(index);\n if (settledSlots.size < (opts.poolSize ?? 0)) return;\n\n // All slots have now settled (first round or retry round).\n if (opts.onFirstSettle && !firstSettleFired) {\n firstSettleFired = true;\n const failedIndices = [...settledSlots].filter(\n (i) => !firstSettleOpened.has(i) && !declinedSlots.has(i),\n );\n opts.onFirstSettle({\n openedCount: firstSettleOpened.size,\n failedIndices,\n });\n // After the callback the client may have:\n // (a) called rearmSlot() for retry slots → settledSlots.size < poolSize,\n // gate stays closed; or\n // (b) called shutdown() (opened===0 fast-fail) → shutdownReason is set.\n // In both cases skip the resolve/open below.\n if (settledSlots.size < (opts.poolSize ?? 0) || shutdownReason) return;\n }\n\n gateOpen = true;\n gateDeferred.resolve();\n opts.onGateOpen?.();\n };\n\n const readerQueue: Array<{\n resolve: (worker: W) => void;\n reject: (error: Error) => void;\n }> = [];\n const writerQueue: Array<{\n resolve: (worker: W) => void;\n reject: (error: Error) => void;\n }> = [];\n\n // Index of the worker designated for writes, or -1 when none is designated.\n //\n // The designation exists to serialize writes onto one connection, and it\n // lasts no longer than that: handOver releases it as soon as no write is\n // queued behind it, so `designated` and `leased` coincide. It was sticky for\n // the life of the worker until wave 4's barrier shipped — a write landing on\n // a worker that had not absorbed the previous commit failed at `prepare` with\n // `no such table`. `applyBarrier` covers `kind: 'write'`, so a newly\n // designated writer catches up before it prepares anything.\n //\n // Measured 2026-08-21: with a long read holding worker 0, five writes took\n // 30 ms spread over worker 1 against 934-1052 ms pinned behind the read.\n let currentWriterIndex = -1;\n\n /**\n * The worker that most recently held the write designation, kept after that\n * designation is released. `currentWriterIndex` answers \"who may write now\";\n * this answers \"who has already seen the last commit\", which outlives it.\n */\n let lastWriterIndex = -1;\n\n const canDesignate = opts.canDesignateWriter ?? (() => true);\n\n /**\n * Serves the writer queue from `worker` when it may hold the designation.\n * Extracted because `handOver` and `add` carried this branch twice, and a\n * predicate that lives in only one of the two copies is a silent hole.\n */\n const serveWriterFirst = (worker: W): boolean => {\n if (!writerQueue.length) return false;\n if (currentWriterIndex !== worker.index && currentWriterIndex !== -1)\n return false;\n // An already-designated writer is not re-judged; only a NEW designation is.\n if (currentWriterIndex === -1 && !canDesignate(worker.index)) return false;\n // Claim the designation before serving: without this, a later write\n // acquisition could designate a second writer while this one still runs.\n currentWriterIndex = worker.index;\n lastWriterIndex = worker.index;\n writerQueue.shift()?.resolve(worker);\n return true;\n };\n\n const checkShutdown = () => {\n if (shutdownDeferred && leased.size === 0) shutdownDeferred.resolve();\n };\n\n const handOver = (worker: W) => {\n if (serveWriterFirst(worker)) return;\n\n // Release the designation. Reaching this line proves no write is queued:\n // serveWriterFirst's only negative exit that leaves the designation on this\n // worker is an empty writerQueue. It sits ABOVE the reader branch because\n // that branch returns — the release has to happen on every exit, not only\n // the idle one.\n //\n // Measured, so nobody \"fixes\" it: moving this above serveWriterFirst is\n // behaviourally equivalent in production, since the call reclaims the\n // designation on the same worker at once. The two differ only under a\n // canDesignateWriter that refuses this index — tests only.\n if (currentWriterIndex === worker.index) currentWriterIndex = -1;\n\n if (readerQueue.length) {\n // Reads never alter the designation — rule 1.\n readerQueue.shift()?.resolve(worker);\n return;\n }\n\n available.add(worker.index);\n opts.onIdle?.(worker);\n };\n\n const makeLease = (worker: W): Lease<W> => {\n leased.add(worker.index);\n const myGen = gen(worker.index);\n let released = false;\n return {\n worker,\n release: () => {\n if (released) return;\n released = true;\n if (gen(worker.index) !== myGen) {\n // Stale lease: remove() was called after this lease was created,\n // bumping the generation. Handing the worker back would corrupt the\n // pool (it could be held by a new lease on the revived slot).\n checkShutdown();\n return;\n }\n leased.delete(worker.index);\n handOver(worker);\n checkShutdown();\n },\n };\n };\n\n const takeAvailable = (write: boolean): W | undefined => {\n if (write && currentWriterIndex > -1) {\n if (!available.has(currentWriterIndex)) return undefined;\n available.delete(currentWriterIndex);\n return workers[currentWriterIndex];\n }\n\n // Prefer the worker that wrote last, for a read and for a new designation\n // alike. A read served there skips the barrier, that worker having already\n // seen the commit; a write served there keeps a run of writes on one\n // connection instead of walking the pool between batches.\n //\n // A PREFERENCE, never a pin: it picks only among workers that are already\n // available, so it can never make anything wait. That is what keeps it\n // clear of the measurement above — which was about writes queued BEHIND a\n // busy designated writer, not about which free worker to choose.\n //\n // `workers[-1]` is undefined, so the unset case needs no separate guard.\n const preferred = workers[lastWriterIndex];\n if (\n preferred !== undefined &&\n available.has(lastWriterIndex) &&\n (!write || canDesignate(lastWriterIndex))\n ) {\n available.delete(lastWriterIndex);\n if (write) currentWriterIndex = lastWriterIndex;\n return preferred;\n }\n\n // Lowest-index-first for both reads and new writes (reads never touch\n // the designation; write designation is set below when a new one starts).\n const found = workers.find(\n (worker) =>\n worker !== undefined &&\n available.has(worker.index) &&\n (!write || canDesignate(worker.index)),\n );\n if (!found) return undefined;\n\n available.delete(found.index);\n if (write) {\n currentWriterIndex = found.index;\n lastWriterIndex = found.index;\n }\n return found;\n };\n\n /** What `remove()` and `retire()` both do once the gate has been told. */\n const takeOut = (index: number) => {\n dead.add(index);\n available.delete(index);\n leased.delete(index);\n workers[index] = undefined;\n // Bump the generation so any outstanding lease on this index knows it is\n // stale when its release() eventually fires.\n generations.set(index, gen(index) + 1);\n if (currentWriterIndex === index) currentWriterIndex = -1;\n // A respawned slot is a different connection with a fresh epoch, so the\n // freshness hint this index carried is void.\n if (lastWriterIndex === index) lastWriterIndex = -1;\n checkShutdown();\n };\n\n return {\n add: (worker) => {\n // Settle this slot in the gate (first call per index only; restarts are\n // ignored because gateOpen is already true by then).\n settleGateSlot(worker.index, 'opened');\n\n dead.delete(worker.index);\n workers[worker.index] = worker;\n // Serve any requests that arrived before this worker was ready, preserving\n // the same writer-first priority as handOver. Does NOT call onIdle — the\n // worker is newly joining the pool, not returning from a lease.\n if (serveWriterFirst(worker)) return;\n if (readerQueue.length) {\n // Reads never alter the designation — rule 1.\n readerQueue.shift()?.resolve(worker);\n return;\n }\n available.add(worker.index);\n },\n\n remove: (index) => {\n // Settle this slot in the gate — a dead slot counts. First call per\n // index only; a restart after the gate is open is a no-op here.\n settleGateSlot(index, 'failed');\n takeOut(index);\n },\n\n retire: (index) => {\n settleGateSlot(index, 'declined');\n takeOut(index);\n },\n\n shutdown: (reason) => {\n // Reject the gate so any caller blocked on it gets the shutdown error.\n if (!gateOpen) {\n gateOpen = true;\n gateDeferred.reject(reason);\n }\n shutdownReason ??= reason;\n shutdownDeferred ??= Promise.withResolvers<void>();\n for (const waiter of readerQueue.splice(0)) waiter.reject(reason);\n for (const waiter of writerQueue.splice(0)) waiter.reject(reason);\n checkShutdown();\n return shutdownDeferred.promise;\n },\n\n stats: () => ({\n read: readerQueue.length,\n write: writerQueue.length,\n available: available.size,\n leased: leased.size,\n gated: gatedWaiters,\n }),\n\n rearmSlot: (index) => {\n if (!gateOpen) settledSlots.delete(index);\n },\n\n acquire: async (kind, signal) => {\n if (shutdownReason) throw shutdownReason;\n // Before the queue, not after: a caller who has already given up must not\n // take a place in line and be served a worker nobody will release.\n signal?.throwIfAborted();\n\n // Readiness gate: block until every slot has settled. The gate is\n // one-shot — once open it never closes, so this branch is never re-entered\n // by callers already in flight after a worker restarts.\n if (!gateOpen) {\n // In a `finally`, so an abort or a shutdown rejection decrements too:\n // a leaked count would make the pool look permanently congested.\n gatedWaiters += 1;\n try {\n // The tie is settled by microtask order, and it settles in favour of\n // the gate: `resolve()` queues its reaction before a synchronous\n // `abort()` queues `abortP`'s, so a caller aborted in the very tick\n // the last slot settles still gets its lease. That is the queue\n // path's behaviour too — `onAbort` there returns early once the\n // waiter has been shifted — so the two agree rather than diverge.\n if (signal) {\n const { promise: abortP, reject: abortReject } =\n Promise.withResolvers<void>();\n const onGateAbort = () => abortReject(signal.reason);\n signal.addEventListener('abort', onGateAbort, { once: true });\n try {\n await Promise.race([gateDeferred.promise, abortP]);\n } finally {\n signal.removeEventListener('abort', onGateAbort);\n }\n } else {\n await gateDeferred.promise;\n }\n } finally {\n gatedWaiters -= 1;\n }\n }\n\n // Re-check after the gate: shutdown() may have fired while we waited\n // (remove() settles the gate synchronously before failClient can run, so\n // the gate resolves a microtask before shutdown() sets shutdownReason).\n if (shutdownReason) throw shutdownReason;\n\n const write = kind === 'write';\n\n const immediate = takeAvailable(write);\n if (immediate) return makeLease(immediate);\n\n const { promise, resolve, reject } = Promise.withResolvers<W>();\n const queue = write ? writerQueue : readerQueue;\n const waiter = { resolve, reject };\n queue.push(waiter);\n\n if (!signal) return makeLease(await promise);\n\n const onAbort = () => {\n const at = queue.indexOf(waiter);\n // The guard is the whole correctness of this branch, in both\n // directions. A waiter still in the queue is REMOVED, never merely\n // rejected in place: the drains take the head with `shift()`, so a\n // dead entry left behind would be handed a worker that nobody then\n // releases. And a waiter already shifted is left alone: its lease is\n // real, the caller owes a release for it, and rejecting here would\n // strand that worker for the life of the client. The in-query abort\n // race in `queries.ts` covers what happens after the lease.\n //\n // Read-then-mutate needs no lock: this is one synchronous block with\n // no await and no yield, and the drains (`handOver`, `serveWriterFirst`)\n // are synchronous too, so nothing can shift this waiter out between the\n // lookup and the removal. `splice` before `reject` for the same reason\n // read the other way — `reject` only schedules a microtask, but the\n // queue is left consistent before anything else can observe it.\n const queued = at !== -1;\n if (!queued) return;\n queue.splice(at, 1);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n try {\n return makeLease(await promise);\n } finally {\n signal.removeEventListener('abort', onAbort);\n }\n },\n };\n};\n","/**\n * The prefixed logger the `debug` option turns on.\n *\n * Lifecycle events only — worker created, ready, open-error, crash, restart,\n * worker loss, close, skipped sweep. A line per query would be illegible under\n * real load and would put user values on the console; query throughput belongs\n * in `db.debug`, not here.\n */\nexport type Logger = {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n /**\n * Always writes through the sink regardless of the `enabled` flag.\n * Use for events that must be visible even when debug logging is off —\n * permanent pool shrinkage being the primary case.\n */\n always: {\n warn: (message: string) => void;\n };\n};\n\ntype Sink = Pick<Console, 'debug' | 'warn' | 'error'>;\n\nexport const createLogger = (\n prefix: string,\n enabled: boolean,\n sink: Sink = console,\n): Logger => {\n const line = (message: string) => `[${prefix}] ${message}`;\n // always.warn bypasses the enabled gate — pool shrinkage must be visible\n // even when debug logging is off, so the sink is the point (it is injectable\n // by tests, unlike a bare console.warn call).\n const always = { warn: (message: string) => sink.warn(line(message)) };\n\n if (!enabled)\n return { info: () => {}, warn: () => {}, error: () => {}, always };\n\n return {\n info: (message) => sink.debug(line(message)),\n warn: (message) => sink.warn(line(message)),\n error: (message) => sink.error(line(message)),\n always,\n };\n};\n","import type { CreateSQLiteClientOptions } from './client';\nimport type { PoolWorker } from './pool';\nimport type { SQLiteVFS } from './types';\n\nexport const debugSQLQuery = (sql: string, params?: unknown[]) => {\n if (!params || params.length === 0) return sql;\n\n let result = '';\n let paramIndex = 0;\n let i = 0;\n\n while (i < sql.length) {\n if (sql[i] === '?') {\n // Check if it's a positional parameter (?001, ?002, etc.)\n if (\n i + 3 < sql.length &&\n /\\d/.test(sql[i + 1]) &&\n /\\d/.test(sql[i + 2]) &&\n /\\d/.test(sql[i + 3])\n ) {\n const position = sql.substring(i + 1, i + 4);\n const numIndex = parseInt(position, 10) - 1;\n\n if (!Number.isNaN(numIndex) && params[numIndex] !== undefined) {\n result += formatValue(params[numIndex]);\n } else {\n result += 'NULL';\n }\n i += 4; // Skip ? and 3 digits\n } else {\n // Simple parameter (?)\n if (paramIndex < params.length) {\n result += formatValue(params[paramIndex++]);\n } else {\n result += 'NULL';\n }\n i++;\n }\n } else if (sql[i] === \"'\" || sql[i] === '\"') {\n // Skip string literals to avoid replacing ? inside them\n const quote = sql[i];\n result += sql[i++];\n while (i < sql.length) {\n result += sql[i];\n if (sql[i] === quote) {\n // Check for escaped quote\n if (i + 1 < sql.length && sql[i + 1] === quote) {\n result += sql[++i];\n } else {\n i++;\n break;\n }\n }\n i++;\n }\n } else {\n result += sql[i++];\n }\n }\n\n return result;\n\n function formatValue(value: unknown): string {\n if (value === null || value === undefined) {\n return 'NULL';\n }\n if (typeof value === 'string') {\n return `'${value.replace(/'/g, \"''\")}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (value instanceof Date) {\n return `'${value.toISOString()}'`;\n }\n // `Buffer` does not exist in a browser. A Node Buffer is a Uint8Array\n // subclass, so this single branch still covers both.\n if (value instanceof Uint8Array) {\n let hex = '';\n for (const byte of value) {\n hex += byte.toString(16).padStart(2, '0');\n }\n return `X'${hex}'`;\n }\n return `'${JSON.stringify(value).replace(/'/g, \"''\")}'`;\n }\n};\n\ntype QueryDebugState = {\n sql: string;\n params?: unknown[] | undefined;\n startTime: number;\n firstRowTime?: number;\n endTime?: number;\n error?: unknown;\n affectedRows: number;\n prepared: number;\n};\n\ntype RequestDebugState = {\n startTime: number;\n acquireTime?: number;\n releaseTime?: number;\n affectedRows: number;\n queries: QueryDebugState[];\n currentQuery?: QueryDebugState;\n};\n\ntype WorkerDebugState = {\n index: number;\n name: string;\n creationTime: number;\n initializationTime?: number;\n requests: RequestDebugState[];\n currentRequest?: RequestDebugState;\n readonly status: string;\n};\n\nexport type ClientDebugState = {\n readonly file: string;\n readonly vfs: SQLiteVFS;\n readonly pragmas: Record<string, string>;\n readonly name: string;\n readonly queue: {\n readonly read: number;\n readonly write: number;\n /**\n * Callers suspended on the pool's readiness gate, waiting for the pool to\n * exist rather than for a free worker. They sit in neither wait queue, so\n * `read` and `write` are both 0 while they wait — during startup, and\n * during the retry round that follows a failed open.\n */\n readonly gated: number;\n };\n workers: WorkerDebugState[];\n};\n\nconst MAX_QUERY_HISTORY_LENGTH = 50;\nconst MAX_REQUEST_HISTORY_LENGTH = 50;\n\nexport const createClientDebug = (\n file: string,\n pool: (PoolWorker | undefined)[],\n clientOptions: Required<\n Pick<CreateSQLiteClientOptions, 'vfs' | 'pragmas' | 'name'>\n >,\n stats: () => { read: number; write: number; gated: number },\n) => {\n const { vfs, pragmas, name } = clientOptions;\n\n // Read through to the scheduler: the old counters were incremented by hand at\n // every acquire/release site and went stale the moment one was missed.\n const queue = {\n get read() {\n return stats().read;\n },\n get write() {\n return stats().write;\n },\n get gated() {\n return stats().gated;\n },\n };\n\n const clientState: ClientDebugState = {\n file,\n vfs,\n pragmas,\n name,\n queue,\n workers: [],\n };\n\n const createWorkerDebugState = (index: number, name: string) => {\n const state: WorkerDebugState = new Proxy(\n {\n index,\n name,\n requests: [],\n status: pool[index]?.status ?? 'EMPTY',\n creationTime: Date.now(),\n },\n {\n get: (target, prop) => {\n if (prop === 'status') {\n return pool[index]?.status ?? 'EMPTY';\n }\n return target[prop as keyof typeof target];\n },\n },\n );\n clientState.workers[index] = state;\n return state;\n };\n\n const createRequestDebugState = () => {\n const state: RequestDebugState = {\n queries: [],\n startTime: Date.now(),\n affectedRows: 0,\n };\n return {\n state,\n assign: (index: number) => {\n const worker = clientState.workers[index];\n if (worker) {\n state.acquireTime = Date.now();\n // Bounded: this array is pushed to on EVERY request and used to grow\n // with the client's total query count (D5 §1.3, the blocking fix).\n if (worker.requests.length >= MAX_REQUEST_HISTORY_LENGTH)\n worker.requests.shift();\n worker.requests.push(state);\n worker.currentRequest = state;\n }\n },\n };\n };\n\n const createQueryDebugState = (\n workerIndex: number,\n sql: string,\n params?: unknown[],\n ) => {\n const state: QueryDebugState = {\n sql,\n params,\n startTime: Date.now(),\n affectedRows: 0,\n prepared: 0,\n };\n const worker = clientState.workers[workerIndex];\n if (worker?.currentRequest) {\n if (worker.currentRequest.queries.length >= MAX_QUERY_HISTORY_LENGTH) {\n worker.currentRequest.queries.shift();\n }\n worker.currentRequest.queries.push(state);\n worker.currentRequest.currentQuery = state;\n }\n return state;\n };\n\n return {\n state: clientState,\n createWorkerDebugState,\n createRequestDebugState,\n createQueryDebugState,\n } as const;\n};\n","/**\n * Pure restart policy for worker slots.\n *\n * Deliberately free of `Worker` and DOM imports so Node tests can\n * drive it in milliseconds — the same reason `scheduler.ts` is pure. B1 lived\n * for months because the only way to reach the pool's decisions was a browser.\n *\n * The caller reports facts; this module returns a decision and never acts.\n */\nexport type SupervisorDecision = 'restart' | 'lost' | 'fail-client';\n\nexport type Supervisor = {\n report: (\n index: number,\n event: 'spawned' | 'ready' | 'served' | 'died' | 'lost' | 'retired',\n ) => SupervisorDecision | undefined;\n};\n\ntype Slot = {\n everReady: boolean;\n alive: boolean;\n lost: boolean;\n restarts: number;\n};\n\nexport const createSupervisor = (options: {\n size: number;\n maxWorkerRestarts?: number | undefined;\n}): Supervisor => {\n const { size, maxWorkerRestarts = 1 } = options;\n\n const slots: Slot[] = Array.from({ length: size }, () => ({\n everReady: false,\n alive: true,\n lost: false,\n restarts: 0,\n }));\n\n const liveCount = () => slots.filter((slot) => slot.alive).length;\n\n return {\n report: (index, event) => {\n const slot = slots[index];\n if (!slot) return undefined;\n\n if (event === 'spawned') {\n // A slot is alive from the moment a worker is created for it — which is\n // what the constructor's `alive: true` already encodes for the first\n // spawn. Without this event a restarted slot never re-enters that\n // state, so the replacement's death reads as a duplicate signal for the\n // worker that died before it: the guard below returns no decision, the\n // client neither restarts nor fails, and every queued request waits on\n // a pool that will never have a worker again.\n if (slot.lost) return undefined;\n slot.alive = true;\n return undefined;\n }\n\n if (event === 'ready') {\n // A lost slot cannot be revived: the loss was permanent and a\n // late ready would inflate liveCount, masking an empty pool.\n if (slot.lost) return undefined;\n slot.everReady = true;\n slot.alive = true;\n // Deliberately NOT resetting `restarts`: a worker that boots fine and\n // dies on every query would otherwise restart forever, silently.\n return undefined;\n }\n\n if (event === 'served') {\n // A stale done message can arrive after the slot was declared dead; if\n // it reset restarts then, it would silently refill the spent budget.\n if (!slot.alive) return undefined;\n slot.restarts = 0;\n return undefined;\n }\n\n if (event === 'retired') {\n // A slot the environment refuses (spec 2026-09-13): its worker declined\n // to open and never will. Not a loss, so no decision — but it must\n // leave liveCount, or the last real worker's death would not fail the\n // client, and `lost` is what keeps a late 'spawned'/'ready' from\n // reviving it. Same duplicate-signal guard as the branches below.\n if (!slot.alive) return undefined;\n slot.alive = false;\n slot.lost = true;\n return undefined;\n }\n\n if (event === 'lost') {\n // A death the caller has ALREADY judged terminal, which 'died' cannot\n // express. The startup retry round is capped at one, so a slot the\n // client has announced through `onWorkerLost` must never come back.\n // Reported as 'died' it would take the restart branch instead: `lost`\n // would stay false, leaving the slot revivable by a later\n // 'spawned'/'ready', and a restart would be charged against a budget\n // for a restart that never happens. The supervisor's view and the\n // consumer's would then disagree, with no source of truth to arbitrate.\n //\n // Same duplicate-signal guard as 'died': one report per slot.\n if (!slot.alive) return undefined;\n slot.alive = false;\n slot.lost = true;\n return liveCount() === 0 ? 'fail-client' : 'lost';\n }\n\n // 'died' — a slot already counted as dead reports once per signal\n // (onerror and a drain timeout can both fire), so ignore repeats.\n if (!slot.alive) return undefined;\n slot.alive = false;\n\n // R1: a slot that never worked is a configuration error, not an\n // accident. Restarting it only delays the diagnostic.\n if (slot.everReady && slot.restarts < maxWorkerRestarts) {\n slot.restarts += 1;\n return 'restart';\n }\n\n slot.lost = true;\n return liveCount() === 0 ? 'fail-client' : 'lost';\n },\n };\n};\n","import { SQLiteError } from './errors';\nimport { connectionLockName, createLocks, initLockName } from './locks';\nimport { spawnWorker, startupError } from './pool';\nimport {\n defaultBuildFor,\n type SQLiteBuild,\n type SQLiteVFS,\n VFS_CAPABILITIES,\n type WorkerMessageData,\n} from './types';\nimport { normalizeDatabaseFile, resolveWasmLocation } from './utils';\n\nexport type DeleteDatabaseOptions = {\n /**\n * Which VFS holds the database. Required for the same reason it is required\n * on `createSQLiteClient`: a VFS decides where the bytes live, so deleting\n * without naming one deletes in the wrong store — or nowhere, while\n * reporting success.\n */\n vfs: SQLiteVFS;\n /**\n * Which wa-sqlite build to load. It does **not** affect where the database\n * lives; it is here only because a VFS runs solely on the builds it\n * declares, and one of them must be loaded to instantiate the VFS at all.\n * @defaultValue the first build the VFS declares\n */\n build?: SQLiteBuild;\n /**\n * Where the worker fetches its `.wasm`, with the same meaning as on\n * `createSQLiteClient`. A deployment that needs it to open a database needs\n * it to delete one.\n */\n wasmUrl?: string | ((build: SQLiteBuild) => string);\n};\n\n/**\n * Deletes a database and the two siblings SQLite may leave beside it.\n *\n * Deleting a database that is not there REJECTS with `DATABASE_NOT_FOUND`.\n * This paragraph said the opposite until 2026-09-16 — that absence was success,\n * as SQLite's own `xDelete` treats it — but the probe in `deleteDatabaseFiles`\n * has always reported absence, and `delete.test.ts` pins that with a falsifier.\n * The doc was the stale half, and it ships in the published `.d.ts`.\n *\n * Nothing a VFS keeps for itself is touched: not the IndexedDB store, which is\n * shared by every database that VFS holds on this origin, and not the\n * `AccessHandlePoolVFS` directory, whose files *are* its reusable capacity.\n * The bytes of the named database are freed in both cases.\n *\n * @throws {SQLiteError} `INVALID_OPTION` when `vfs` is missing or the `build`\n * is not one the VFS supports — synchronously in spirit, as a rejection here.\n * @throws {SQLiteError} `DATABASE_IN_USE` when the database is open, in this\n * tab or another. A connection already holding its handles cannot be\n * revoked from here; see the README's Known Limitations.\n * @throws {SQLiteError} `BUSY` when the database is being opened or deleted\n * elsewhere. Try again in a moment.\n * @throws {SQLiteError} `DATABASE_NOT_FOUND` when there is no such database.\n * A caller deleting speculatively should catch this one code.\n */\nexport const deleteDatabase = async (\n file: string,\n options: DeleteDatabaseOptions,\n): Promise<void> => {\n if (!options?.vfs) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `vfs is required. Pass the VFS the database was created with — VFS.md compares them. Four VFS share one underlying file: passing the wrong one deletes a real database without reporting anything.`,\n );\n }\n\n const vfs = options.vfs;\n const build = options.build ?? defaultBuildFor(vfs);\n const capability = VFS_CAPABILITIES[vfs];\n\n if (!(capability.builds as readonly SQLiteBuild[]).includes(build)) {\n throw new SQLiteError(\n 'INVALID_OPTION',\n `${vfs} cannot run on the '${build}' build. Supported: ${capability.builds.join(', ')}.`,\n );\n }\n\n // Nothing was ever persisted, so there is nothing to delete and no worker\n // worth spawning to say so.\n if (capability.layout === 'memory') return;\n\n const dbFile = normalizeDatabaseFile(file);\n const wasm = resolveWasmLocation(options.wasmUrl, build, location.href);\n\n const locks = createLocks();\n\n // The connection lock first: a live client is both the likelier refusal and\n // the more actionable one, and it gets its own code so a caller can tell\n // \"close it, possibly in another tab\" from \"retry in a moment\".\n //\n // `ifAvailable` on BOTH acquisitions is load-bearing. A client takes\n // bsq:conn and then bsq:init; this function takes them the other way round.\n // A request that never queues cannot deadlock — a blocking acquisition on\n // either name reintroduces the cycle.\n const connRelease = await locks.hold(connectionLockName(vfs, dbFile), {\n mode: 'exclusive',\n ifAvailable: true,\n });\n\n if (connRelease === undefined) {\n throw new SQLiteError(\n 'DATABASE_IN_USE',\n `${dbFile} is open. Close every client on it, in this tab and in any other, then delete it.`,\n );\n }\n\n try {\n const ran = await locks.tryWithLock(initLockName(vfs, dbFile), () =>\n runDelete({ file: dbFile, vfs, build, wasm }),\n );\n\n if (!ran) {\n throw new SQLiteError(\n 'BUSY',\n `${dbFile} is being opened or deleted elsewhere. Try again in a moment.`,\n );\n }\n } finally {\n connRelease();\n // The Web Locks API releases a lock by queuing a global task (not a\n // microtask). On Firefox the second `ifAvailable` request on this name\n // fires before that task runs if we return immediately, making it look as\n // if the lock is still held. One setTimeout(0) yields to the task queue\n // so any subsequent call — including an immediate retry in tests — sees\n // the lock as free. Chromium does not require this, but it is harmless\n // there.\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n }\n};\n\n/**\n * How long a delete may take before the worker is presumed unable to answer.\n * Matches `openTimeout`'s default, because the failure it catches is the same\n * one: a VFS that cannot acquire what it needs — `AccessHandlePoolVFS` whose\n * six slots are held elsewhere reaches neither success nor error. Not a public\n * option: a caller has nothing useful to tune here, and a delete that takes\n * thirty seconds has already failed.\n */\nconst DELETE_TIMEOUT = 30_000;\n\nconst runDelete = (message: {\n file: string;\n vfs: SQLiteVFS;\n build: SQLiteBuild;\n wasm: ReturnType<typeof resolveWasmLocation>;\n}): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n const worker = spawnWorker(`SQLite delete / ${message.file}`);\n\n const timer = setTimeout(() => {\n settle(\n new SQLiteError(\n 'TIMEOUT',\n `deleting ${message.file} timed out after ${DELETE_TIMEOUT} ms. The database is most likely held open by another client or tab.`,\n ),\n );\n }, DELETE_TIMEOUT);\n\n const settle = (error?: SQLiteError) => {\n clearTimeout(timer);\n worker.terminate();\n if (error) reject(error);\n else resolve();\n };\n\n worker.onmessage = (event: MessageEvent<WorkerMessageData>) => {\n const data = event.data;\n if (data.type === 'deleted') return settle();\n if (data.type === 'not-found') {\n return settle(\n new SQLiteError(\n 'DATABASE_NOT_FOUND',\n `There is no database named '${message.file}' for ${message.vfs} to delete.`,\n ),\n );\n }\n if (data.type === 'error') {\n return settle(startupError(data));\n }\n };\n\n worker.onerror = (event) => {\n settle(\n new SQLiteError(\n 'WORKER_CRASHED',\n `worker crashed while deleting ${message.file}: ${(event as ErrorEvent).message ?? ''}`,\n ),\n );\n };\n\n worker.postMessage({ type: 'delete', callId: 0, ...message });\n });\n","/**\n * The result codes of SQLite 3.53.0, keyed without the `SQLITE_` prefix, in two\n * tables that match `SQLiteError`'s two fields: test the family on `sqliteCode`\n * against `SQLITE_CODES` (`CONSTRAINT`, `FULL`), the subtype on\n * `sqliteExtendedCode` against `SQLITE_EXTENDED_CODES` (`CONSTRAINT_UNIQUE`).\n * An extended code's low byte is its family (`(2067 & 0xff) === 19`), and its key\n * begins with the family's key. What each one means:\n * https://sqlite.org/rescode.html.\n *\n * Transcribed from `src/sqlite.h.in` — the source of `sqlite3.h` — at SQLite's\n * tag `version-3.53.0`, checked 2026-09-14. That is the SQLite of the vendored\n * wa-sqlite, v1.1.2 (its `package.json` still reads 1.1.1: upstream did not\n * bump it): its source-id, read from the wasm, is `2026-04-09 11:41:38\n * 4525003a53a7fc63ca75`, and the tag's `manifest.uuid` begins with the same\n * hash. Not read from wa-sqlite's `sqlite-constants.js`, which has no\n * `BUSY_*`, `LOCKED_*`, `CANTOPEN_*`, `CORRUPT_*` or `READONLY_*` codes;\n * `tests/unit/sqlite-codes.test.ts` checks every name the two share.\n * Re-transcribe when wa-sqlite moves to another SQLite.\n *\n * `SQLiteError.sqliteCode` is typed `SQLiteResultCode` (strict): since\n * `sqliteCodeOf`, it is always a primary code of the bundled SQLite (D10).\n * `sqliteExtendedCode` stays open (`SQLiteExtendedResultCode | (number & {})`):\n * a wrong read must stay representable (D9).\n */\n\n/** The 31 primary result codes — what `SQLiteError.sqliteCode` holds. */\nexport const SQLITE_CODES = Object.freeze({\n OK: 0,\n ERROR: 1,\n INTERNAL: 2,\n PERM: 3,\n ABORT: 4,\n BUSY: 5,\n LOCKED: 6,\n NOMEM: 7,\n READONLY: 8,\n INTERRUPT: 9,\n IOERR: 10,\n CORRUPT: 11,\n NOTFOUND: 12,\n FULL: 13,\n CANTOPEN: 14,\n PROTOCOL: 15,\n EMPTY: 16,\n SCHEMA: 17,\n TOOBIG: 18,\n CONSTRAINT: 19,\n MISMATCH: 20,\n MISUSE: 21,\n NOLFS: 22,\n AUTH: 23,\n FORMAT: 24,\n RANGE: 25,\n NOTADB: 26,\n NOTICE: 27,\n WARNING: 28,\n ROW: 100,\n DONE: 101,\n} as const);\n\n/**\n * The 82 extended result codes — what `SQLiteError.sqliteExtendedCode` holds\n * when SQLite reports a subtype. No primary code is repeated here.\n */\nexport const SQLITE_EXTENDED_CODES = Object.freeze({\n ERROR_MISSING_COLLSEQ: 257,\n ERROR_RETRY: 513,\n ERROR_SNAPSHOT: 769,\n ERROR_RESERVESIZE: 1025,\n ERROR_KEY: 1281,\n ERROR_UNABLE: 1537,\n IOERR_READ: 266,\n IOERR_SHORT_READ: 522,\n IOERR_WRITE: 778,\n IOERR_FSYNC: 1034,\n IOERR_DIR_FSYNC: 1290,\n IOERR_TRUNCATE: 1546,\n IOERR_FSTAT: 1802,\n IOERR_UNLOCK: 2058,\n IOERR_RDLOCK: 2314,\n IOERR_DELETE: 2570,\n IOERR_BLOCKED: 2826,\n IOERR_NOMEM: 3082,\n IOERR_ACCESS: 3338,\n IOERR_CHECKRESERVEDLOCK: 3594,\n IOERR_LOCK: 3850,\n IOERR_CLOSE: 4106,\n IOERR_DIR_CLOSE: 4362,\n IOERR_SHMOPEN: 4618,\n IOERR_SHMSIZE: 4874,\n IOERR_SHMLOCK: 5130,\n IOERR_SHMMAP: 5386,\n IOERR_SEEK: 5642,\n IOERR_DELETE_NOENT: 5898,\n IOERR_MMAP: 6154,\n IOERR_GETTEMPPATH: 6410,\n IOERR_CONVPATH: 6666,\n IOERR_VNODE: 6922,\n IOERR_AUTH: 7178,\n IOERR_BEGIN_ATOMIC: 7434,\n IOERR_COMMIT_ATOMIC: 7690,\n IOERR_ROLLBACK_ATOMIC: 7946,\n IOERR_DATA: 8202,\n IOERR_CORRUPTFS: 8458,\n IOERR_IN_PAGE: 8714,\n IOERR_BADKEY: 8970,\n IOERR_CODEC: 9226,\n LOCKED_SHAREDCACHE: 262,\n LOCKED_VTAB: 518,\n BUSY_RECOVERY: 261,\n BUSY_SNAPSHOT: 517,\n BUSY_TIMEOUT: 773,\n CANTOPEN_NOTEMPDIR: 270,\n CANTOPEN_ISDIR: 526,\n CANTOPEN_FULLPATH: 782,\n CANTOPEN_CONVPATH: 1038,\n CANTOPEN_DIRTYWAL: 1294,\n CANTOPEN_SYMLINK: 1550,\n CORRUPT_VTAB: 267,\n CORRUPT_SEQUENCE: 523,\n CORRUPT_INDEX: 779,\n READONLY_RECOVERY: 264,\n READONLY_CANTLOCK: 520,\n READONLY_ROLLBACK: 776,\n READONLY_DBMOVED: 1032,\n READONLY_CANTINIT: 1288,\n READONLY_DIRECTORY: 1544,\n ABORT_ROLLBACK: 516,\n CONSTRAINT_CHECK: 275,\n CONSTRAINT_COMMITHOOK: 531,\n CONSTRAINT_FOREIGNKEY: 787,\n CONSTRAINT_FUNCTION: 1043,\n CONSTRAINT_NOTNULL: 1299,\n CONSTRAINT_PRIMARYKEY: 1555,\n CONSTRAINT_TRIGGER: 1811,\n CONSTRAINT_UNIQUE: 2067,\n CONSTRAINT_VTAB: 2323,\n CONSTRAINT_ROWID: 2579,\n CONSTRAINT_PINNED: 2835,\n CONSTRAINT_DATATYPE: 3091,\n NOTICE_RECOVER_WAL: 283,\n NOTICE_RECOVER_ROLLBACK: 539,\n NOTICE_RBU: 795,\n WARNING_AUTOINDEX: 284,\n AUTH_USER: 279,\n OK_LOAD_PERMANENTLY: 256,\n OK_SYMLINK: 512,\n} as const);\n\n/** A primary result code of SQLite 3.53.0 — the type of `SQLiteError.sqliteCode`. */\nexport type SQLiteResultCode = (typeof SQLITE_CODES)[keyof typeof SQLITE_CODES];\n\n/**\n * An extended result code of SQLite 3.53.0. `SQLiteError.sqliteExtendedCode`\n * is typed `SQLiteExtendedResultCode | (number & {})`: open, because a wrong\n * read must stay representable (spec D9), yet still completing these values.\n */\nexport type SQLiteExtendedResultCode =\n (typeof SQLITE_EXTENDED_CODES)[keyof typeof SQLITE_EXTENDED_CODES];\n"],"names":["cachedRealmId","BUILD_REQUIREMENTS","VFS_CAPABILITIES","defaultBuildFor","vfs","PROBES","navigator","FileSystemFileHandle","WebAssembly","globalThis","UNPROBEABLE","Set","FEATURE_LABEL","Object","detectFeatures","found","feature","probe","missingFeature","build","available","SQLiteError","Error","code","message","options","undefined","SQLiteBulkWriteError","counts","stagingLockName","file","table","UUID_RE","parseClientMarker","lockName","name","prefix","namespaceFor","parts","id","markerVfs","encoded","decodeURIComponent","sharesStorage","writeLockName","connectionLockName","noOpLocks","_name","fn","createLocks","manager","Promise","resolveReleaser","rejectOuter","release","ifAvail","held","resolveHeld","requestOptions","lock","ran","snapshot","read","list","WRITE_KEYWORDS","READ_PRAGMA","isReadQuery","sql","mergeSignals","a","b","noop","merged","AbortController","relay","source","onA","onB","withDeadline","method","budget","controller","timer","setTimeout","signal","clearTimeout","assertReadable","keyword","quoteIdent","JSON","COLUMN_TYPE","PRAGMA_NAME","PRAGMA_INTEGER","PRAGMA_LITERAL","normalizeDatabaseFile","URL","resolveWasmLocation","wasmUrl","baseHref","href","isCallback","raw","value","ADMITTED","REGISTRY_KEY","Symbol","resolveRealmId","locks","ownMarkerName","mine","entry","nonce","crypto","fresh","inspectWith","realm","clients","marker","writeName","writer","waiting","client","libraryClientsHold","ownId","deadlineMs","resolve","inspectDatabase","String","STOP","BUSY_CODES","subtypeOf","data","busyFromCode","sqliteExtendedCode","startupError","busy","spawnWorker","Worker","reclaim","worker","iterator","state","detach","abandonRegistry","createAbandonRegistry","run","registry","FinalizationRegistry","target","token","makeAbortRace","onAbort","aborted","_","reject","chunk","params","chunkSize","credits","onAbandon","onTransport","gen","drain","teardown","next","streamRows","rows","row","readWorker","result","firstWorker","writeWorker","affected","exec","clientCount","exclusivityProbes","Map","createSQLiteClient","clientOptions","startupFirstError","connRelease","sharedProbe","proceedWorker0","markerRelease","closing","fatal","pragmas","onFirstSettle","onGateOpen","ns","key","map","existing","cell","deps","dbFile","clientIndex","clientName","clientUuid","capability","poolSize","Math","pool","effectivePoolSize","capAnnounced","abortSlots","SharedArrayBuffer","wasm","location","absent","describeMissing","label","others","suffix","alternatives","testWriterPolicy","writerPolicy","testCacheBytes","statementCacheBytes","inStartup","startupLosses","scheduler","createScheduler","opts","shutdownReason","shutdownDeferred","workers","dead","leased","generations","index","settledSlots","gateOpen","gateDeferred","firstSettleOpened","firstSettleFired","declinedSlots","gatedWaiters","settleGateSlot","kind","failedIndices","i","readerQueue","writerQueue","currentWriterIndex","lastWriterIndex","canDesignate","serveWriterFirst","checkShutdown","makeLease","myGen","released","handOver","takeOut","reason","waiter","abortP","abortReject","onGateAbort","write","immediate","takeAvailable","preferred","promise","queue","at","error","emitWorkerLost","failClient","spawn","failClientError","verdict","supervisor","probeSettled","Boolean","debugOption","logger","createLogger","enabled","sink","console","line","always","clientDebug","createClientDebug","stats","clientState","createWorkerDebugState","Proxy","Date","prop","createRequestDebugState","createQueryDebugState","workerIndex","debug","writeLock","heldWriteLocks","connRefused","exclusiveWithout","inUse","probeAnswer","lockGranted","maybeProceed","proceed","holdConnection","exclusive","connLockPromise","missing","markerName","encodeURIComponent","markerClosed","publishing","closeAbort","epochs","host","created","n","maxEpochIn","heldNames","max","tail","Number","previous","applyBarrier","origin","barrierIter","afterWrite","seen","acquireWithDebug","request","lease","explainWriteLockTimeout","cause","inspection","acquireInstrumented","releaseWrite","webRelease","lockSignal","releaseMerge","barrier","handedBack","isRetryableBusy","onReadLease","body","readWithRetry","streamWithRetry","attempt","delivered","giveBack","item","chunkWorker","stream","first","bulkFor","createBulk","shared","swept","maxVariables","transaction","reserve","bulkWrite","keys","before","failure","room","releaseDeadline","maxBufferSize","queueSize","buffer","writePromise","closed","rowsWritten","rowsNotWritten","queuedRows","releaseRoom","fail","flush","toInsert","slot","runBatch","currentAffected","k","failClosed","r","output","schema","uuid","staging","normalizedSchema","v","type","assertColumnType","column","trimmed","unique","notnull","generated","assertGeneratedExpression","expr","lockHeld","createStaging","sweepOnce","tables","orphan","enqueue","close","col","releaseLock","dropStaging","tx","statement","indexStatements","statements","columns","Array","names","handleDeath","callback","ending","readOnly","autoCommit","deadline","outer","releaseClose","death","releaseDeath","closedError","end","die","pending","abandoned","checksql","done","begun","commitNow","via","rollbackNow","open","mark","facade","conclude","waitFor","current","entryWait","queueWait","prior","advisory","abandon","running","judged","failed","dieIfConnectionLeft","drainToEnd","withSignal","given","queued","own","abortedAtCall","savepointed","driving","settled","start","left","e","releasing","st","releaseQueue","watching","watchIdle","closeOpenStatements","transport","refuse","bulk","query","db","args","bounded","ms","openTimeout","drainTimeout","createSupervisor","size","maxWorkerRestarts","slots","liveCount","event","dying","createPoolWorker","deferredChunk","servingQuery","deferredClose","idle","freed","stopRequested","lost","statementCacheSize","declineWithout","probeFirst","deferredInit","workerName","Int32Array","currentCallId","inbox","stopped","suppressServed","ready","deathDeferred","poison","detail","errorEvent","failedUrl","callId","statementError","runQuery","self","queryState","DEFAULT_CREDIT_WINDOW","noServed","timeout","abortable","savepoint","op","outcome","expiry","nativeTerminate","on","Atomics","served","retireSlot","live","cb","cbError","wasInStartup","decision","startWorkers","closingError","draining","inspect","base","deleteDatabase","runDelete","settle","SQLITE_CODES","SQLITE_EXTENDED_CODES"],"mappings":"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"}