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.
- package/NOTICE +56 -0
- package/README.md +112 -104
- package/dist/LICENSE +21 -0
- package/dist/NOTICE +56 -0
- package/dist/abandon.d.ts +77 -0
- package/dist/api.d.ts +426 -0
- package/dist/bulk.d.ts +57 -0
- package/dist/capabilities.d.ts +23 -0
- package/dist/client.d.ts +221 -0
- package/dist/credits.d.ts +31 -0
- package/dist/{esm/src/debug.d.ts → debug.d.ts} +21 -10
- package/dist/delete.d.ts +48 -0
- package/dist/epochs.d.ts +85 -0
- package/dist/errors.d.ts +76 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect.d.ts +96 -0
- package/dist/locks.d.ts +172 -0
- package/dist/logger.d.ts +24 -0
- package/dist/pool.d.ts +191 -0
- package/dist/queries.d.ts +65 -0
- package/dist/scheduler.d.ts +145 -0
- package/dist/sqlite-codes.d.ts +154 -0
- package/dist/supervisor.d.ts +17 -0
- package/dist/transaction.d.ts +61 -0
- package/dist/types.d.ts +642 -0
- package/dist/utils.d.ts +156 -0
- package/dist/worker/cloneable.d.ts +25 -0
- package/dist/worker/probes.d.ts +26 -0
- package/dist/worker/sqlite-code.d.ts +9 -0
- package/dist/worker/statement-cache.d.ts +36 -0
- package/dist/worker/wa-sqlite-async.wasm +0 -0
- package/dist/worker/wa-sqlite-jspi.wasm +0 -0
- package/dist/worker/wa-sqlite.wasm +0 -0
- package/dist/worker/worker.js +11 -0
- package/dist/worker/worker.js.map +1 -0
- package/package.json +46 -22
- package/dist/esm/index.js +0 -424
- package/dist/esm/rslib.config.d.ts +0 -2
- package/dist/esm/rstest.config.d.ts +0 -2
- package/dist/esm/src/client.d.ts +0 -332
- package/dist/esm/src/index.d.ts +0 -1
- package/dist/esm/src/orchestrator.d.ts +0 -87
- package/dist/esm/src/types.d.ts +0 -83
- package/dist/esm/src/utils.d.ts +0 -6
- /package/dist/{esm/src → worker}/worker.d.ts +0 -0
package/dist/client.d.ts
ADDED
|
@@ -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 {
|
|
2
|
+
import type { PoolWorker } from './pool';
|
|
3
3
|
import type { SQLiteVFS } from './types';
|
|
4
|
-
export declare const debugSQLQuery: (sql: string, params?:
|
|
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?:
|
|
7
|
+
params?: unknown[] | undefined;
|
|
9
8
|
startTime: number;
|
|
10
9
|
firstRowTime?: number;
|
|
11
10
|
endTime?: number;
|
|
12
|
-
error?:
|
|
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
|
-
|
|
39
|
-
|
|
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,
|
|
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?:
|
|
61
|
+
readonly createQueryDebugState: (workerIndex: number, sql: string, params?: unknown[]) => QueryDebugState;
|
|
51
62
|
};
|
|
52
63
|
export {};
|
package/dist/delete.d.ts
ADDED
|
@@ -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>;
|
package/dist/epochs.d.ts
ADDED
|
@@ -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;
|
package/dist/errors.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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';
|