browser-sqlite 1.0.0-rc.3 → 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.
Files changed (47) hide show
  1. package/NOTICE +56 -0
  2. package/README.md +112 -104
  3. package/dist/LICENSE +21 -0
  4. package/dist/NOTICE +56 -0
  5. package/dist/abandon.d.ts +77 -0
  6. package/dist/api.d.ts +426 -0
  7. package/dist/bulk.d.ts +57 -0
  8. package/dist/capabilities.d.ts +23 -0
  9. package/dist/client.d.ts +221 -0
  10. package/dist/credits.d.ts +31 -0
  11. package/dist/{esm/src/debug.d.ts → debug.d.ts} +21 -10
  12. package/dist/delete.d.ts +48 -0
  13. package/dist/epochs.d.ts +85 -0
  14. package/dist/errors.d.ts +76 -0
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.js +5 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/inspect.d.ts +96 -0
  19. package/dist/locks.d.ts +172 -0
  20. package/dist/logger.d.ts +24 -0
  21. package/dist/pool.d.ts +191 -0
  22. package/dist/queries.d.ts +65 -0
  23. package/dist/scheduler.d.ts +145 -0
  24. package/dist/sqlite-codes.d.ts +154 -0
  25. package/dist/supervisor.d.ts +17 -0
  26. package/dist/transaction.d.ts +61 -0
  27. package/dist/types.d.ts +642 -0
  28. package/dist/utils.d.ts +156 -0
  29. package/dist/worker/cloneable.d.ts +25 -0
  30. package/dist/worker/probes.d.ts +26 -0
  31. package/dist/worker/sqlite-code.d.ts +9 -0
  32. package/dist/worker/statement-cache.d.ts +36 -0
  33. package/dist/worker/wa-sqlite-async.wasm +0 -0
  34. package/dist/worker/wa-sqlite-jspi.wasm +0 -0
  35. package/dist/worker/wa-sqlite.wasm +0 -0
  36. package/dist/worker/worker.js +11 -0
  37. package/dist/worker/worker.js.map +1 -0
  38. package/package.json +46 -22
  39. package/dist/esm/index.js +0 -424
  40. package/dist/esm/rslib.config.d.ts +0 -2
  41. package/dist/esm/rstest.config.d.ts +0 -2
  42. package/dist/esm/src/client.d.ts +0 -332
  43. package/dist/esm/src/index.d.ts +0 -1
  44. package/dist/esm/src/orchestrator.d.ts +0 -87
  45. package/dist/esm/src/types.d.ts +0 -83
  46. package/dist/esm/src/utils.d.ts +0 -6
  47. /package/dist/{esm/src → worker}/worker.d.ts +0 -0
@@ -0,0 +1,221 @@
1
+ import type { SQLiteChunkOptions, SQLiteQueryOptions } from './api';
2
+ import { SQLiteError } from './errors';
3
+ import { type ClientInspection } from './inspect';
4
+ import { type SQLiteBuild, type SQLiteVFS } from './types';
5
+ /**
6
+ * Configuration options for creating a SQLite client.
7
+ */
8
+ export type CreateSQLiteClientOptions = {
9
+ /**
10
+ * A label for this client, never for the database — the database file is
11
+ * the FIRST argument to `createSQLiteClient`, and this option has no effect
12
+ * on what is opened or where.
13
+ *
14
+ * It is reported as `db.debug.name`, and prefixes the `debug` logger's
15
+ * console output as `"<name> <n>"`, where `n` counts the clients created in
16
+ * this tab. Neither form is unique across the origin: the counter is
17
+ * per-tab, so two tabs both produce `"SQLite 1"`.
18
+ *
19
+ * @defaultValue `"SQLite"`
20
+ */
21
+ name?: string;
22
+ /**
23
+ * Number of Web Workers spawned in the pool at initialization.
24
+ * A larger pool allows more concurrent read operations but increases
25
+ * memory consumption and OPFS file handle usage.
26
+ * A VFS that holds a single connection, or gains nothing from a second one,
27
+ * caps this at `1` (`OPFSCoopSyncVFS` among them), and passing more throws at
28
+ * construction time. Omitting it never throws: the default is capped to what
29
+ * the VFS allows.
30
+ * The environment can cap it too: `OPFSWriteAheadVFS` and `OPFSAdaptiveVFS`
31
+ * run on one worker wherever `readwrite-unsafe` is missing, with no error, and
32
+ * warn once only when this option was passed. `db.poolSize` reports the size
33
+ * the pool runs at.
34
+ * @defaultValue `2`, or the VFS's maximum when it is lower
35
+ */
36
+ poolSize?: number;
37
+ /**
38
+ * Which VFS stores the database. Required: a VFS decides *where* the bytes
39
+ * live, and a database written through one VFS is not visible through
40
+ * another. See Browser compatibility and recommendations in VFS.md.
41
+ */
42
+ vfs: SQLiteVFS;
43
+ /**
44
+ * Which wa-sqlite WebAssembly build to load. Defaults to the first entry of
45
+ * `VFS_CAPABILITIES[vfs]` — `sync` where the VFS supports it, since it is both the
46
+ * fastest and the most portable, otherwise `async`. `jspi` needs engine
47
+ * support; see the Builds section of VFS.md for versions.
48
+ *
49
+ * @throws at construction when the build is not one the chosen VFS supports.
50
+ */
51
+ build?: SQLiteBuild;
52
+ /**
53
+ * Where the workers fetch their `.wasm` from. **An escape hatch, not a
54
+ * setting**: omit it and resolution is exactly what it was before this
55
+ * option existed — the file is taken from beside `worker.js`, which is where
56
+ * the package ships it and where every bundler emits it.
57
+ *
58
+ * Reach for it only when the `.wasm` have been separated from `worker.js`:
59
+ * assets moved by hand with no bundler, or a build whose emitted URL is
60
+ * wrong at runtime.
61
+ *
62
+ * A **string is a directory**, resolved against the page — relative
63
+ * (`'wasm/'`), absolute (`'/static/wasm'`) or a full URL. A missing trailing
64
+ * slash is added. The file name comes from wa-sqlite itself, so one base
65
+ * serves whichever `build` is loaded.
66
+ *
67
+ * A **callback names one file** and receives the resolved `build`, for a
68
+ * bundler-emitted asset whose name carries a content hash:
69
+ * ```ts
70
+ * import wasmUrl from 'browser-sqlite/dist/worker/wa-sqlite.wasm?url';
71
+ * createSQLiteClient('app.db', { vfs, wasmUrl: () => wasmUrl });
72
+ * ```
73
+ * It is called once, at construction, and its answer is reused by every
74
+ * worker and every restart.
75
+ *
76
+ * Serving the `.wasm` from another origin has two requirements beyond this
77
+ * option, both enforced by the browser: the response needs CORS
78
+ * (`Access-Control-Allow-Origin`), since the glue fetches it, and it must
79
+ * carry `Content-Type: application/wasm` for streaming compilation.
80
+ *
81
+ * @throws at construction when the value cannot be parsed as a URL.
82
+ */
83
+ wasmUrl?: string | ((build: SQLiteBuild) => string);
84
+ /**
85
+ * SQLite PRAGMAs applied to each worker's database connection on open.
86
+ * Keys are PRAGMA names, values are their string representations.
87
+ * Example: `{ journal_mode: 'WAL', synchronous: 'NORMAL' }`.
88
+ * If omitted, no PRAGMAs are applied beyond SQLite defaults.
89
+ */
90
+ pragmas?: Record<string, string>;
91
+ /**
92
+ * How many times a worker slot may be restarted after it has died.
93
+ * A slot that never reached readiness is never restarted — an initial
94
+ * failure is deterministic, and restarting only delays the diagnostic.
95
+ * The counter resets once the replacement has actually served a request.
96
+ * @defaultValue `1`
97
+ */
98
+ maxWorkerRestarts?: number;
99
+ /**
100
+ * Milliseconds a worker has to post `ready` after its `open` message is sent.
101
+ * On expiry the slot is failed immediately, with a message naming the cause
102
+ * the client roster supports: another live client of this library holding
103
+ * the database, or a holder the roster cannot see — a page reloaded without
104
+ * `close()`, another library, native code.
105
+ * @defaultValue `30_000`
106
+ */
107
+ openTimeout?: number;
108
+ /**
109
+ * Milliseconds the drain loop (in the query generator's `finally`) may run
110
+ * before the worker is presumed dead and the crash path is invoked.
111
+ * @defaultValue `60_000`
112
+ */
113
+ drainTimeout?: number;
114
+ /**
115
+ * Turns on the introspection subsystem exposed as `db.debug`, and the
116
+ * lifecycle log. A string is used as the log prefix; `true` falls back to the
117
+ * client name (`"<name> <index>"`), which already names the workers.
118
+ *
119
+ * @defaultValue undefined — no collection, no output, `db.debug` undefined.
120
+ */
121
+ debug?: string | boolean;
122
+ /**
123
+ * Called whenever a worker slot is permanently lost. Receives the slot index,
124
+ * the number of workers still alive after the loss, the pool's size
125
+ * (`db.poolSize`), and the error that killed the slot.
126
+ *
127
+ * Guaranteed to be called **before** the client is failed when the last slot
128
+ * is lost. Wrapped in try/catch — a throwing callback is reported through
129
+ * `logger.always.warn` and does not break the pool.
130
+ *
131
+ * @defaultValue undefined
132
+ */
133
+ onWorkerLost?: (event: WorkerLostEvent) => void;
134
+ };
135
+ /**
136
+ * What `onWorkerLost` receives. Named and exported rather than inlined in the
137
+ * option: a consumer whose handler is a standalone function needs to be able
138
+ * to type its parameter.
139
+ */
140
+ export type WorkerLostEvent = {
141
+ /** Zero-based index of the lost slot. */
142
+ index: number;
143
+ /** Number of workers still alive after this loss. */
144
+ live: number;
145
+ /** The number of workers the pool runs — `db.poolSize`, not the `poolSize` option: the two differ where the environment caps the pool. */
146
+ size: number;
147
+ /** The error that killed the worker. */
148
+ cause: SQLiteError;
149
+ };
150
+ /**
151
+ * Creates a SQLite client backed by a pool of Web Workers, each running
152
+ * a wa-sqlite instance in a dedicated thread.
153
+ *
154
+ * @remarks
155
+ * **Browser requirements:** This client uses OPFS through Web Workers; no
156
+ * special HTTP headers are required and cross-origin isolation is not needed.
157
+ * The default `build` needs no browser opt-in; only `build: 'jspi'` does, and
158
+ * JSPI is Chromium-only — an unrelated constraint, not a header requirement.
159
+ *
160
+ * **Worker pool side effect:** Calling this function immediately spawns
161
+ * `poolSize` Web Worker threads and begins asynchronous database
162
+ * initialization. Workers become queryable once they emit a `ready` message.
163
+ *
164
+ * @param file - SQLite database file name within the OPFS origin.
165
+ * Each distinct name corresponds to a separate database file.
166
+ * @param clientOptions - Pool and VFS configuration. Required: `vfs` has no
167
+ * default, because a VFS decides where the database is written.
168
+ * See {@link CreateSQLiteClientOptions} for field defaults.
169
+ * @returns A {@link SQLiteDB} object providing `read`, `write`, `chunk`,
170
+ * `stream`, `first`, `transaction`, `bulkWrite`, `output`, and `close` methods.
171
+ *
172
+ * @throws {SQLiteError} With code `INVALID_OPTION` when `build` is not one of
173
+ * the builds the chosen `vfs` supports. The message names the supported
174
+ * builds; the pairing is declared once, in `VFS_CAPABILITIES`.
175
+ * @throws {SQLiteError} With code `INVALID_OPTION` when `poolSize` exceeds the
176
+ * `maxPoolSize` the chosen `vfs` declares. The message names the cap and the
177
+ * reason for it; both come from `VFS_CAPABILITIES`.
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { createSQLiteClient } from 'browser-sqlite';
182
+ *
183
+ * const db = createSQLiteClient('myapp.sqlite', {
184
+ * vfs: 'OPFSAdaptiveVFS',
185
+ * pragmas: { journal_mode: 'WAL', synchronous: 'NORMAL' },
186
+ * });
187
+ *
188
+ * const users = await db.read<{ id: number; name: string }>(
189
+ * 'SELECT id, name FROM users WHERE active = ?',
190
+ * [1],
191
+ * );
192
+ * ```
193
+ */
194
+ export declare const createSQLiteClient: (file: string, clientOptions: CreateSQLiteClientOptions) => {
195
+ chunk: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: SQLiteChunkOptions) => AsyncGenerator<T[], void, unknown>;
196
+ read: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: SQLiteChunkOptions) => Promise<T[]>;
197
+ write: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: SQLiteQueryOptions) => Promise<{
198
+ result: T[];
199
+ affected: number;
200
+ }>;
201
+ stream: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: SQLiteChunkOptions) => AsyncGenerator<T, void, unknown>;
202
+ first: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: SQLiteQueryOptions) => Promise<T | undefined>;
203
+ transaction: <T = void>(callback: (db: import("./api").SQLiteTransactionDB) => Promise<T>, options?: import("./api").SQLiteTransactionOptions) => Promise<T>;
204
+ bulkWrite: <KEYS extends string>(table: string, keys: KEYS[], options?: import("./api").SQLiteBulkWriteOptions, before?: Promise<unknown>) => {
205
+ enqueue: (data: { [K in KEYS]: any; }) => Promise<void>;
206
+ close: () => Promise<number>;
207
+ };
208
+ output: <SCHEMA extends import("./api").Schema>(table: string, schema: SCHEMA, options?: import("./api").SQLiteOutputOptions<SCHEMA>) => {
209
+ enqueue: (data: import("./api").SQLiteOutputRow<SCHEMA>) => Promise<void>;
210
+ close: () => Promise<number>;
211
+ };
212
+ close: () => Promise<void>;
213
+ readonly id: `${string}-${string}-${string}-${string}-${string}`;
214
+ readonly name: string;
215
+ readonly file: string;
216
+ readonly vfs: "AccessHandlePoolVFS" | "IDBBatchAtomicVFS" | "IDBMirrorVFS" | "MemoryAsyncVFS" | "MemoryVFS" | "OPFSAdaptiveVFS" | "OPFSAnyContextVFS" | "OPFSCoopSyncVFS" | "OPFSWriteAheadVFS";
217
+ readonly build: SQLiteBuild;
218
+ readonly poolSize: number;
219
+ inspect: () => Promise<ClientInspection>;
220
+ debug: import("./debug").ClientDebugState | undefined;
221
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The credit gate: back-pressure for chunk production, and the task turn that
3
+ * makes a worker reachable by `postMessage` while it is inside a query.
4
+ *
5
+ * Pure and Node-testable on purpose. B1 survived for months because the
6
+ * scheduler was reachable only through slow browser tests; this module has the
7
+ * same profile — subtle state transitions otherwise buried behind a worker, a
8
+ * VFS and a real database.
9
+ */
10
+ /** One turn of the task queue. Injected so Node tests can drive it. */
11
+ export type Tick = () => Promise<void>;
12
+ /** Chunks a worker may send before waiting for a credit. Spec §3.4. */
13
+ export declare const DEFAULT_CREDIT_WINDOW = 2;
14
+ export type CreditGate = {
15
+ /** Begin a query: `window` credits, not stopped, credit counter cleared. */
16
+ reset: (callId: number, window: number) => void;
17
+ /** Add credits for `callId`. A stale `callId` is ignored (§5.4). */
18
+ grant: (callId: number, n: number) => void;
19
+ /** Stop the current query, waking any wait in progress (§5.1). */
20
+ stop: () => void;
21
+ /** Spend one credit. Always costs one task turn first. */
22
+ take: (callId: number) => Promise<'go' | 'stopped'>;
23
+ isStopped: () => boolean;
24
+ tick: Tick;
25
+ };
26
+ /**
27
+ * A task turn via MessageChannel. NOT setTimeout: nested setTimeout is clamped
28
+ * to 4 ms, which would cost seconds over a few hundred chunks (spec §3.1).
29
+ */
30
+ export declare const createMessageChannelTick: () => Tick;
31
+ export declare const createCreditGate: (tick: Tick) => CreditGate;
@@ -1,16 +1,16 @@
1
1
  import type { CreateSQLiteClientOptions } from './client';
2
- import { type WorkerOrchestrator } from './orchestrator';
2
+ import type { PoolWorker } from './pool';
3
3
  import type { SQLiteVFS } from './types';
4
- export declare const debugSQLQuery: (sql: string, params?: any[]) => string;
5
- export declare const statusToLabel: (status: number) => string;
4
+ export declare const debugSQLQuery: (sql: string, params?: unknown[]) => string;
6
5
  type QueryDebugState = {
7
6
  sql: string;
8
- params?: any[];
7
+ params?: unknown[] | undefined;
9
8
  startTime: number;
10
9
  firstRowTime?: number;
11
10
  endTime?: number;
12
- error?: any;
11
+ error?: unknown;
13
12
  affectedRows: number;
13
+ prepared: number;
14
14
  };
15
15
  type RequestDebugState = {
16
16
  startTime: number;
@@ -29,24 +29,35 @@ type WorkerDebugState = {
29
29
  currentRequest?: RequestDebugState;
30
30
  readonly status: string;
31
31
  };
32
- type ClientDebugState = {
32
+ export type ClientDebugState = {
33
33
  readonly file: string;
34
34
  readonly vfs: SQLiteVFS;
35
35
  readonly pragmas: Record<string, string>;
36
36
  readonly name: string;
37
37
  readonly queue: {
38
- write: number;
39
- read: number;
38
+ readonly read: number;
39
+ readonly write: number;
40
+ /**
41
+ * Callers suspended on the pool's readiness gate, waiting for the pool to
42
+ * exist rather than for a free worker. They sit in neither wait queue, so
43
+ * `read` and `write` are both 0 while they wait — during startup, and
44
+ * during the retry round that follows a failed open.
45
+ */
46
+ readonly gated: number;
40
47
  };
41
48
  workers: WorkerDebugState[];
42
49
  };
43
- export declare const createClientDebug: (file: string, orchestrator: WorkerOrchestrator, clientOptions: Required<Pick<CreateSQLiteClientOptions, "vfs" | "pragmas" | "name">>) => {
50
+ export declare const createClientDebug: (file: string, pool: (PoolWorker | undefined)[], clientOptions: Required<Pick<CreateSQLiteClientOptions, 'vfs' | 'pragmas' | 'name'>>, stats: () => {
51
+ read: number;
52
+ write: number;
53
+ gated: number;
54
+ }) => {
44
55
  readonly state: ClientDebugState;
45
56
  readonly createWorkerDebugState: (index: number, name: string) => WorkerDebugState;
46
57
  readonly createRequestDebugState: () => {
47
58
  state: RequestDebugState;
48
59
  assign: (index: number) => void;
49
60
  };
50
- readonly createQueryDebugState: (workerIndex: number, sql: string, params?: any[]) => QueryDebugState;
61
+ readonly createQueryDebugState: (workerIndex: number, sql: string, params?: unknown[]) => QueryDebugState;
51
62
  };
52
63
  export {};
@@ -0,0 +1,48 @@
1
+ import { type SQLiteBuild, type SQLiteVFS } from './types';
2
+ export type DeleteDatabaseOptions = {
3
+ /**
4
+ * Which VFS holds the database. Required for the same reason it is required
5
+ * on `createSQLiteClient`: a VFS decides where the bytes live, so deleting
6
+ * without naming one deletes in the wrong store — or nowhere, while
7
+ * reporting success.
8
+ */
9
+ vfs: SQLiteVFS;
10
+ /**
11
+ * Which wa-sqlite build to load. It does **not** affect where the database
12
+ * lives; it is here only because a VFS runs solely on the builds it
13
+ * declares, and one of them must be loaded to instantiate the VFS at all.
14
+ * @defaultValue the first build the VFS declares
15
+ */
16
+ build?: SQLiteBuild;
17
+ /**
18
+ * Where the worker fetches its `.wasm`, with the same meaning as on
19
+ * `createSQLiteClient`. A deployment that needs it to open a database needs
20
+ * it to delete one.
21
+ */
22
+ wasmUrl?: string | ((build: SQLiteBuild) => string);
23
+ };
24
+ /**
25
+ * Deletes a database and the two siblings SQLite may leave beside it.
26
+ *
27
+ * Deleting a database that is not there REJECTS with `DATABASE_NOT_FOUND`.
28
+ * This paragraph said the opposite until 2026-09-16 — that absence was success,
29
+ * as SQLite's own `xDelete` treats it — but the probe in `deleteDatabaseFiles`
30
+ * has always reported absence, and `delete.test.ts` pins that with a falsifier.
31
+ * The doc was the stale half, and it ships in the published `.d.ts`.
32
+ *
33
+ * Nothing a VFS keeps for itself is touched: not the IndexedDB store, which is
34
+ * shared by every database that VFS holds on this origin, and not the
35
+ * `AccessHandlePoolVFS` directory, whose files *are* its reusable capacity.
36
+ * The bytes of the named database are freed in both cases.
37
+ *
38
+ * @throws {SQLiteError} `INVALID_OPTION` when `vfs` is missing or the `build`
39
+ * is not one the VFS supports — synchronously in spirit, as a rejection here.
40
+ * @throws {SQLiteError} `DATABASE_IN_USE` when the database is open, in this
41
+ * tab or another. A connection already holding its handles cannot be
42
+ * revoked from here; see the README's Known Limitations.
43
+ * @throws {SQLiteError} `BUSY` when the database is being opened or deleted
44
+ * elsewhere. Try again in a moment.
45
+ * @throws {SQLiteError} `DATABASE_NOT_FOUND` when there is no such database.
46
+ * A caller deleting speculatively should catch this one code.
47
+ */
48
+ export declare const deleteDatabase: (file: string, options: DeleteDatabaseOptions) => Promise<void>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The commit epoch: a monotonic integer per database, counting commits
3
+ * performed in this realm. Its absolute value means nothing — only the
4
+ * comparison with a worker's `seen` does.
5
+ *
6
+ * The registry lives in the realm-wide symbol registry rather than in a module
7
+ * variable on purpose. A module singleton is unique only when the bundler
8
+ * loads one copy of the module; `Symbol.for` is unique per realm whatever the
9
+ * bundler did. That is what makes "two clients in one tab see each other" true
10
+ * by construction.
11
+ *
12
+ * The `v1` suffix separates incompatible shapes. Bump it ONLY if the shape
13
+ * changes — bumping it per release recreates the fragmentation it prevents.
14
+ */
15
+ import type { Locks } from './locks';
16
+ import type { SQLiteVFS } from './types';
17
+ /**
18
+ * The statement the barrier runs and discards.
19
+ *
20
+ * Measured 2026-08-20 in the forced configuration: 6/6 correct. `SELECT 1`
21
+ * touches no page and is 6/6 stale; `PRAGMA data_version` and
22
+ * `PRAGMA schema_version` are 8/8 stale; so is waiting. Only a statement that
23
+ * opens a real read transaction on the file refreshes the connection's cached
24
+ * page 1 — and it must be a SEPARATE statement, because the one that triggers
25
+ * the refresh still returns the stale result.
26
+ */
27
+ export declare const BARRIER_SQL = "SELECT count(*) FROM sqlite_master";
28
+ /**
29
+ * The marker a realm holds to publish the epoch it last committed.
30
+ *
31
+ * Held in SHARED mode: many realms may hold one name at once, so publishing
32
+ * never waits and two realms can never collide on a number. Nobody reads the
33
+ * lock — the NAME is the state, which is why this beats a BroadcastChannel:
34
+ * there is no message that can still be in flight.
35
+ */
36
+ export declare const epochLockName: (ns: string, file: string, n: number) => string;
37
+ /**
38
+ * The highest epoch any realm in this origin has published under `prefix`.
39
+ *
40
+ * The tail after the prefix must be ALL digits, which is stricter than a
41
+ * prefix match plus `lastIndexOf(':')` and is the point: a normalized file may
42
+ * contain a colon (`new URL('./a:b', 'file://').pathname` is `a:b`), so the
43
+ * loose form would read another database's epoch as this one's.
44
+ */
45
+ export declare const maxEpochIn: (heldNames: string[], prefix: string) => number;
46
+ export type Epochs = {
47
+ /** The number of commits observed for this database, floor included. */
48
+ current: () => number;
49
+ /** Records one commit and returns the new epoch. */
50
+ bump: () => number;
51
+ /** Raises the local floor. Never lowers it. */
52
+ raiseTo: (n: number) => void;
53
+ /** The highest epoch published by any realm in this origin. */
54
+ originMax: () => Promise<number>;
55
+ /** Publishes `n` for this realm, replacing its previous marker. */
56
+ publish: (n: number) => Promise<void>;
57
+ };
58
+ /**
59
+ * Handles onto the counter for `(namespace, file)`, which MUST already be
60
+ * normalized by `normalizeDatabaseFile`. Entries are never removed: deleting
61
+ * one would restart the counter at 0, and a worker still alive with `seen = 5`
62
+ * would then read `5 > 0`, believe itself current forever, and serve stale
63
+ * data.
64
+ *
65
+ * The cell is realm-wide, so every client in a tab shares one counter AND one
66
+ * marker — publication is per realm, not per client.
67
+ */
68
+ export declare const epochsFor: (vfs: SQLiteVFS, file: string, locks: Locks) => Epochs;
69
+ /**
70
+ * Where a worker's `seen` lands after the write it just served.
71
+ *
72
+ * `target` is the epoch captured when its lease was granted; `next` is the
73
+ * epoch its own commit produced. Advancing requires both conditions:
74
+ *
75
+ * - `seen === target`: the worker was actually observing from `target` when its
76
+ * lease was granted. If the worker was already behind (`seen < target`), it
77
+ * must not be marked current regardless of what it just committed.
78
+ * - `next === target + 1`: the commit is the immediate successor of `target`.
79
+ * If another client committed during our lease, `next` skipped; our
80
+ * connection never observed that commit and must stay marked behind.
81
+ *
82
+ * Marking a connection current when it is not is the only class of bug this
83
+ * design must make impossible.
84
+ */
85
+ export declare const advanceSeen: (seen: number, target: number, next: number) => number;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Every failure this library raises on its own behalf. A caller discriminates
3
+ * on `code`, or on `name` — they carry the same value, so `err.name` reads the
4
+ * way `'AbortError'` does on the DOMException an aborted signal throws.
5
+ * `DATABASE_IN_USE` is this library's own: a database that a live client holds,
6
+ * as opposed to `BUSY`, which covers a transient conflict worth retrying.
7
+ * `DATABASE_NOT_FOUND` is raised only by `deleteDatabase`: there is nothing at
8
+ * that name to delete. `createSQLiteClient` creates what is absent, so it has
9
+ * no such case.
10
+ * `UNSUPPORTED` means the platform cannot answer the question — raised by
11
+ * `inspectDatabase` where Web Locks is missing, because reporting zero clients
12
+ * there would be indistinguishable from a database nobody holds.
13
+ * `OPERATION_TIMEOUT` is the `timeout` a caller set on a call being spent. It is
14
+ * deliberately not `TIMEOUT`, which means a deadline this library imposed on
15
+ * itself — a worker that never became ready, a deletion that did not complete.
16
+ * `STATEMENT_FAILED` is a statement SQLite refused or failed for any reason
17
+ * but a lock conflict — a constraint, a syntax error, a full disk. `message` is
18
+ * SQLite's own; `sqliteCode` carries its result code, and `sqliteExtendedCode`
19
+ * its subtype when SQLite reports one.
20
+ */
21
+ import type { SQLiteExtendedResultCode, SQLiteResultCode } from './sqlite-codes';
22
+ export type SQLiteErrorCode = 'NOT_A_READ_QUERY' | 'CLIENT_CLOSED' | 'WORKER_CRASHED' | 'TIMEOUT' | 'PROTOCOL_ERROR' | 'INVALID_IDENTIFIER' | 'INVALID_OPTION' | 'INVALID_PRAGMA' | 'BULK_WRITE_FAILED' | 'BUSY' | 'STATEMENT_FAILED' | 'DATABASE_IN_USE' | 'DATABASE_NOT_FOUND' | 'READ_ONLY_TRANSACTION' | 'UNSUPPORTED' | 'WORKER_BUSY' | 'OPERATION_TIMEOUT' | 'TRANSACTION_CLOSED';
23
+ export declare class SQLiteError extends Error {
24
+ readonly code: SQLiteErrorCode;
25
+ /**
26
+ * SQLite's own numeric result code, present only when the failure came from
27
+ * SQLite rather than from this library. Always the PRIMARY code. `BUSY`
28
+ * covers both SQLITE_BUSY (5) and SQLITE_LOCKED (6); this is how a caller
29
+ * tells them apart. Typed `SQLiteResultCode` (D10): since `sqliteCodeOf`,
30
+ * it is always a primary code of the bundled SQLite, so comparing it with
31
+ * an extended code does not compile.
32
+ */
33
+ readonly sqliteCode?: SQLiteResultCode;
34
+ /**
35
+ * SQLite's extended result code, present only when a statement SQLite ran
36
+ * failed WITH A SUBTYPE — `STATEMENT_FAILED` or `BUSY` from a query, never
37
+ * an open or a delete: 2067 (`SQLITE_EXTENDED_CODES.CONSTRAINT_UNIQUE`)
38
+ * under `sqliteCode` 19. Absent when SQLite has no subtype for the failure
39
+ * (a full disk, a syntax error), since `sqliteCode` already says it. For a
40
+ * subtype SQLite reports, `(sqliteExtendedCode & 0xff) === sqliteCode` is
41
+ * SQLite's own guarantee, not something this library enforces: the client
42
+ * deliberately lets a differing value through (a 0 from a wrong read).
43
+ * Typed open (`SQLiteExtendedResultCode | (number & {})`, D10): D9 lets a
44
+ * wrong read through, which a strict type would misdescribe.
45
+ */
46
+ readonly sqliteExtendedCode?: SQLiteExtendedResultCode | (number & {});
47
+ /**
48
+ * The `timeout` that was exceeded, in milliseconds. Present only on
49
+ * `OPERATION_TIMEOUT`, so a log need not parse the message for it.
50
+ */
51
+ readonly timeout?: number;
52
+ constructor(code: SQLiteErrorCode, message: string, options?: {
53
+ cause?: unknown;
54
+ sqliteCode?: SQLiteResultCode;
55
+ sqliteExtendedCode?: SQLiteExtendedResultCode | (number & {});
56
+ timeout?: number;
57
+ });
58
+ }
59
+ /**
60
+ * A batch failed. Raised by `bulkWrite().close()` and by `output().close()`.
61
+ *
62
+ * The counters exist because the old behaviour was silent: batches were chained
63
+ * on one shared promise, so after a rejection every later `.then` was skipped —
64
+ * while their rows had already been spliced out of the buffer (B5). A caller now
65
+ * learns how much of its data reached the database.
66
+ */
67
+ export declare class SQLiteBulkWriteError extends SQLiteError {
68
+ readonly rowsWritten: number;
69
+ readonly rowsNotWritten: number;
70
+ constructor(message: string, counts: {
71
+ rowsWritten: number;
72
+ rowsNotWritten: number;
73
+ }, options?: {
74
+ cause?: unknown;
75
+ });
76
+ }
@@ -0,0 +1,8 @@
1
+ export * from './api';
2
+ export { detectFeatures, missingFeature } from './capabilities';
3
+ export * from './client';
4
+ export * from './delete';
5
+ export * from './errors';
6
+ export { type ClientInspection, type DatabaseClient, type DatabaseInspection, type InspectDatabaseOptions, type InspectionBase, inspectDatabase, } from './inspect';
7
+ export { SQLITE_CODES, SQLITE_EXTENDED_CODES, type SQLiteExtendedResultCode, type SQLiteResultCode, } from './sqlite-codes';
8
+ export { defaultBuildFor, type PlatformFeature, type SQLiteBuild, type SQLiteVFS, VFS_CAPABILITIES, type VFSCapability, type VFSLayout, type VFSMemoryModel, type VFSStorage, } from './types';