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/README.md +66 -430
- package/dist/abandon.d.ts +77 -0
- package/dist/api.d.ts +62 -12
- package/dist/bulk.d.ts +15 -0
- package/dist/client.d.ts +43 -20
- package/dist/delete.d.ts +11 -5
- package/dist/epochs.d.ts +36 -6
- package/dist/errors.d.ts +44 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +96 -0
- package/dist/locks.d.ts +124 -6
- package/dist/pool.d.ts +101 -8
- package/dist/queries.d.ts +36 -7
- package/dist/scheduler.d.ts +14 -0
- package/dist/sqlite-codes.d.ts +154 -0
- package/dist/supervisor.d.ts +1 -1
- package/dist/transaction.d.ts +24 -1
- package/dist/types.d.ts +322 -28
- package/dist/utils.d.ts +42 -2
- package/dist/worker/probes.d.ts +26 -0
- package/dist/worker/sqlite-code.d.ts +9 -0
- package/dist/worker/statement-cache.d.ts +17 -3
- package/dist/worker/worker.js +1 -1
- package/dist/worker/worker.js.map +1 -1
- package/package.json +15 -7
package/dist/supervisor.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export type SupervisorDecision = 'restart' | 'lost' | 'fail-client';
|
|
11
11
|
export type Supervisor = {
|
|
12
|
-
report: (index: number, event: 'spawned' | 'ready' | 'served' | 'died' | 'lost') => SupervisorDecision | undefined;
|
|
12
|
+
report: (index: number, event: 'spawned' | 'ready' | 'served' | 'died' | 'lost' | 'retired') => SupervisorDecision | undefined;
|
|
13
13
|
};
|
|
14
14
|
export declare const createSupervisor: (options: {
|
|
15
15
|
size: number;
|
package/dist/transaction.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SQLiteQueryAPI, SQLiteTransactionDB, SQLiteTransactionOptions } from './api';
|
|
2
2
|
import type { ReadFn, TransactionFn, WriteFn } from './bulk';
|
|
3
3
|
import { SQLiteError } from './errors';
|
|
4
|
+
import type { Logger } from './logger';
|
|
4
5
|
import type { PoolWorker } from './pool';
|
|
5
6
|
import type { Scheduler } from './scheduler';
|
|
6
7
|
/**
|
|
@@ -13,7 +14,7 @@ import type { Scheduler } from './scheduler';
|
|
|
13
14
|
*/
|
|
14
15
|
export declare const createTransaction: (deps: {
|
|
15
16
|
scheduler: Scheduler<PoolWorker>;
|
|
16
|
-
afterWrite: (worker: PoolWorker) =>
|
|
17
|
+
afterWrite: (worker: PoolWorker) => Promise<unknown>;
|
|
17
18
|
/**
|
|
18
19
|
* Called when a connection may still hold an open transaction. The worker
|
|
19
20
|
* is lost rather than repaired: a "dirty worker" state is one more
|
|
@@ -21,6 +22,17 @@ export declare const createTransaction: (deps: {
|
|
|
21
22
|
* connection is transaction-free by construction.
|
|
22
23
|
*/
|
|
23
24
|
onPoisoned: (index: number, error: SQLiteError) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Aborted when the client closes, with `CLIENT_CLOSED`.
|
|
27
|
+
*
|
|
28
|
+
* Merged into the transaction's own signal so that closing ABANDONS a
|
|
29
|
+
* running transaction the way a caller's `signal` does — the callback is
|
|
30
|
+
* not interrupted, it simply can no longer reach the database. Without it
|
|
31
|
+
* the caller of a transaction whose callback sits on an `await` that is not
|
|
32
|
+
* a statement waited for ever: nothing else in the transaction observes the
|
|
33
|
+
* client going away.
|
|
34
|
+
*/
|
|
35
|
+
closeSignal: AbortSignal;
|
|
24
36
|
/**
|
|
25
37
|
* The client's bulk factory. Called per transaction with the transaction's
|
|
26
38
|
* own read/write and a pass-through `transaction`, so output()'s swap runs
|
|
@@ -31,8 +43,19 @@ export declare const createTransaction: (deps: {
|
|
|
31
43
|
read: ReadFn;
|
|
32
44
|
write: WriteFn;
|
|
33
45
|
transaction: TransactionFn;
|
|
46
|
+
/** See `src/bulk.ts`: the batch's place in this transaction's queue. */
|
|
47
|
+
reserve?: () => {
|
|
48
|
+
started: Promise<void>;
|
|
49
|
+
done: () => void;
|
|
50
|
+
};
|
|
34
51
|
}) => {
|
|
35
52
|
bulkWrite: SQLiteQueryAPI['bulkWrite'];
|
|
36
53
|
output: SQLiteQueryAPI['output'];
|
|
37
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Reached only through `always`: `rollback()` on a transaction that has
|
|
57
|
+
* already committed warns whatever the `debug` option says (spec R4) — a
|
|
58
|
+
* warning visible only under debug would be the same as silence.
|
|
59
|
+
*/
|
|
60
|
+
logger: Pick<Logger, 'always'>;
|
|
38
61
|
}) => <T = void>(callback: (db: SQLiteTransactionDB) => Promise<T>, options?: SQLiteTransactionOptions) => Promise<T>;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SQLiteErrorCode } from './errors';
|
|
2
|
+
import type { SQLiteResultCode } from './sqlite-codes';
|
|
1
3
|
export type SQLiteWorkerMessageData<_T = unknown> = {
|
|
2
4
|
callId: number;
|
|
3
5
|
terminate?: boolean;
|
|
@@ -25,10 +27,25 @@ export declare const SharedArrayTypes: {
|
|
|
25
27
|
STRING: number;
|
|
26
28
|
OBJECT: number;
|
|
27
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* The savepoint a transaction asks the worker to handle around one query
|
|
32
|
+
* (spec 2026-09-11, D4/D5). `conclude` settles the savepoint the previous
|
|
33
|
+
* savepointed write left open — `release` keeps that write, `undo` rolls it
|
|
34
|
+
* back first — and `open` starts one for this query's own statement. Both run
|
|
35
|
+
* before the statement, conclusion first. Internal: no consumer sets it.
|
|
36
|
+
*/
|
|
37
|
+
export type SavepointOp = {
|
|
38
|
+
conclude?: 'release' | 'undo';
|
|
39
|
+
open?: true;
|
|
40
|
+
};
|
|
28
41
|
type SQLOptions = {
|
|
29
42
|
chunkSize?: number;
|
|
30
43
|
/** Chunks the worker may send before waiting for a credit. Spec §3.2. */
|
|
31
44
|
credits?: number;
|
|
45
|
+
/** When true, the worker installs an async progress handler so an AbortSignal can stop a running step(). */
|
|
46
|
+
abortable?: boolean;
|
|
47
|
+
/** See `SavepointOp`. */
|
|
48
|
+
savepoint?: SavepointOp;
|
|
32
49
|
};
|
|
33
50
|
/**
|
|
34
51
|
* Where a worker fetches its `.wasm` from, when the consumer overrode it.
|
|
@@ -57,7 +74,26 @@ export type ClientMessageData = {
|
|
|
57
74
|
pragmas?: Record<string, string>;
|
|
58
75
|
/** Statements retained per worker; see `src/client.ts`. Internal. */
|
|
59
76
|
statementCacheSize?: number;
|
|
77
|
+
/** Bytes retained per worker; see `src/client.ts`. Internal. */
|
|
78
|
+
statementCacheBytes?: number;
|
|
60
79
|
wasm?: WasmLocation;
|
|
80
|
+
/** Shared abort slots, one Int32 per worker. Isolated contexts only. */
|
|
81
|
+
abortSlots?: SharedArrayBuffer;
|
|
82
|
+
/** This worker's index into `abortSlots`. */
|
|
83
|
+
abortIndex?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Features this worker must find before opening; it declines instead of
|
|
86
|
+
* opening when one is missing (spec 2026-09-13). Sent to slots of index
|
|
87
|
+
* ≥ 1 only, and only by a VFS that declares `singleConnectionWithout`.
|
|
88
|
+
*/
|
|
89
|
+
declineWithout?: readonly PlatformFeature[];
|
|
90
|
+
/**
|
|
91
|
+
* Features worker 0 probes before opening, where the VFS is exclusive
|
|
92
|
+
* without one (spec 2026-09-15, §3.2): it reports them with `probed`,
|
|
93
|
+
* then loads nothing until `proceed`. Sent to slot 0 only, and only by a
|
|
94
|
+
* VFS that declares `exclusiveConnectionWithout`.
|
|
95
|
+
*/
|
|
96
|
+
probeFirst?: readonly PlatformFeature[];
|
|
61
97
|
} | {
|
|
62
98
|
type: 'query';
|
|
63
99
|
callId: number;
|
|
@@ -74,6 +110,11 @@ export type ClientMessageData = {
|
|
|
74
110
|
} | {
|
|
75
111
|
type: 'stop';
|
|
76
112
|
callId: number;
|
|
113
|
+
}
|
|
114
|
+
/** The client decided the connection lock; worker 0 may open (spec 2026-09-15). */
|
|
115
|
+
| {
|
|
116
|
+
type: 'proceed';
|
|
117
|
+
callId: number;
|
|
77
118
|
} | {
|
|
78
119
|
type: 'delete';
|
|
79
120
|
callId: number;
|
|
@@ -85,6 +126,24 @@ export type ClientMessageData = {
|
|
|
85
126
|
export type WorkerMessageData = {
|
|
86
127
|
type: 'ready';
|
|
87
128
|
callId: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The worker found a feature of `declineWithout` missing and opened nothing:
|
|
132
|
+
* the environment caps the pool (spec 2026-09-13).
|
|
133
|
+
*/
|
|
134
|
+
| {
|
|
135
|
+
type: 'declined';
|
|
136
|
+
callId: number;
|
|
137
|
+
missing: PlatformFeature;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Worker 0's answer to `probeFirst`: the first feature missing, or null. It
|
|
141
|
+
* opens nothing until the client sends `proceed` (spec 2026-09-15, §3.2).
|
|
142
|
+
*/
|
|
143
|
+
| {
|
|
144
|
+
type: 'probed';
|
|
145
|
+
callId: number;
|
|
146
|
+
missing: PlatformFeature | null;
|
|
88
147
|
} | {
|
|
89
148
|
type: 'chunk';
|
|
90
149
|
callId: number;
|
|
@@ -100,26 +159,66 @@ export type WorkerMessageData = {
|
|
|
100
159
|
* "for a sub-millisecond effect, count the round trips").
|
|
101
160
|
*/
|
|
102
161
|
prepared: number;
|
|
162
|
+
/**
|
|
163
|
+
* Whether the connection is inside a transaction once this query has
|
|
164
|
+
* ended — `sqlite3_get_autocommit() === 0`. SQLite can leave a
|
|
165
|
+
* transaction by itself: an interrupted INSERT/UPDATE/DELETE rolls the
|
|
166
|
+
* whole transaction back. Absent when the worker could not read it.
|
|
167
|
+
*/
|
|
168
|
+
inTransaction?: boolean | undefined;
|
|
103
169
|
} | {
|
|
104
170
|
type: 'error';
|
|
105
171
|
callId: number;
|
|
106
172
|
message: string;
|
|
107
173
|
cause?: unknown;
|
|
108
174
|
/** SQLite's numeric result code, when the failure came from SQLite. */
|
|
109
|
-
sqliteCode?:
|
|
175
|
+
sqliteCode?: SQLiteResultCode;
|
|
176
|
+
/**
|
|
177
|
+
* SQLite's extended result code, read in the worker where the statement
|
|
178
|
+
* failed (spec 2026-09-14, §5.1). Sent by the query path only, and
|
|
179
|
+
* unfiltered: this is exactly what SQLite reported, including a value
|
|
180
|
+
* equal to `sqliteCode` (no subtype). The client is what drops it in
|
|
181
|
+
* that case (D9); when SQLite does report a subtype,
|
|
182
|
+
* `(sqliteExtendedCode & 0xff) === sqliteCode`.
|
|
183
|
+
*/
|
|
184
|
+
sqliteExtendedCode?: number;
|
|
185
|
+
/**
|
|
186
|
+
* A code this library minted, when the worker knows the cause. The
|
|
187
|
+
* generic path by which a worker-side error keeps its code across the
|
|
188
|
+
* boundary — `worker.ts` copies it off any thrown error carrying one, so
|
|
189
|
+
* this is a structural contract and not a hook for one class.
|
|
190
|
+
*
|
|
191
|
+
* **Nothing sets it today.** `WorkerQueryTimeout` was its only producer
|
|
192
|
+
* and it went with the execution budget when `timeout` became a
|
|
193
|
+
* client-side wall-clock deadline. Kept rather than deleted: it is the
|
|
194
|
+
* twin of `sqliteCode` above, which is load-bearing, and rebuilding it
|
|
195
|
+
* would cost the same three sites it occupies.
|
|
196
|
+
*/
|
|
197
|
+
errorCode?: SQLiteErrorCode;
|
|
198
|
+
/**
|
|
199
|
+
* Whether the connection is inside a transaction once this query has
|
|
200
|
+
* ended — `sqlite3_get_autocommit() === 0`. SQLite can leave a
|
|
201
|
+
* transaction by itself: an interrupted INSERT/UPDATE/DELETE rolls the
|
|
202
|
+
* whole transaction back. Absent when the worker could not read it.
|
|
203
|
+
*/
|
|
204
|
+
inTransaction?: boolean | undefined;
|
|
110
205
|
} | {
|
|
111
206
|
type: 'closed';
|
|
112
207
|
callId: number;
|
|
113
208
|
} | {
|
|
114
209
|
type: 'deleted';
|
|
115
210
|
callId: number;
|
|
211
|
+
}
|
|
212
|
+
/** The delete worker found nothing at that name; deleteDatabase turns it into DATABASE_NOT_FOUND. */
|
|
213
|
+
| {
|
|
214
|
+
type: 'not-found';
|
|
116
215
|
} | {
|
|
117
216
|
type: 'open-error';
|
|
118
217
|
callId: number;
|
|
119
218
|
message: string;
|
|
120
219
|
cause?: unknown;
|
|
121
220
|
/** SQLite's numeric result code, when the failure came from SQLite. */
|
|
122
|
-
sqliteCode?:
|
|
221
|
+
sqliteCode?: SQLiteResultCode;
|
|
123
222
|
};
|
|
124
223
|
/** Which wa-sqlite WebAssembly build a worker loads. */
|
|
125
224
|
export type SQLiteBuild = 'sync' | 'async' | 'jspi';
|
|
@@ -139,13 +238,28 @@ export declare const BUILD_REQUIREMENTS: {
|
|
|
139
238
|
readonly async: readonly [];
|
|
140
239
|
readonly jspi: readonly ["jspi"];
|
|
141
240
|
};
|
|
241
|
+
/**
|
|
242
|
+
* Platform features a build USES when present and works without, at a cost —
|
|
243
|
+
* the symmetric of `degradesWithout` on a VFS, at the level where this one
|
|
244
|
+
* actually lives. The `sync` build cannot carry an abort into a running
|
|
245
|
+
* `step()` without a `SharedArrayBuffer`, and there is no SharedArrayBuffer
|
|
246
|
+
* outside a cross-origin isolated context: measured 2026-09-04, it is not
|
|
247
|
+
* restricted there, it is absent. Nothing here names COOP/COEP or
|
|
248
|
+
* Document-Isolation-Policy: any of them satisfies the probe, and one of them
|
|
249
|
+
* is Chrome-only.
|
|
250
|
+
*/
|
|
251
|
+
export declare const BUILD_DEGRADES_WITHOUT: {
|
|
252
|
+
readonly sync: readonly ["cross-origin-isolated"];
|
|
253
|
+
readonly async: readonly [];
|
|
254
|
+
readonly jspi: readonly [];
|
|
255
|
+
};
|
|
142
256
|
/**
|
|
143
257
|
* A platform feature a VFS may need. Which browser versions ship each one is
|
|
144
|
-
* documentation data, not runtime data, so it lives in the
|
|
258
|
+
* documentation data, not runtime data, so it lives in the VFS.md generator
|
|
145
259
|
* (`scripts/render-vfs-matrix.ts`) with its sources — not here, where it would
|
|
146
260
|
* ship to every consumer for nothing.
|
|
147
261
|
*/
|
|
148
|
-
export type PlatformFeature = 'opfs' | 'readwrite-unsafe' | 'jspi' | 'writable-stream';
|
|
262
|
+
export type PlatformFeature = 'opfs' | 'readwrite-unsafe' | 'jspi' | 'writable-stream' | 'cross-origin-isolated';
|
|
149
263
|
/** Where a VFS keeps the database. */
|
|
150
264
|
export type VFSStorage = 'opfs' | 'indexeddb' | 'memory';
|
|
151
265
|
/**
|
|
@@ -184,14 +298,40 @@ export type VFSCapability = {
|
|
|
184
298
|
readonly storage: VFSStorage;
|
|
185
299
|
/** How the database is arranged within that storage. */
|
|
186
300
|
readonly layout: VFSLayout;
|
|
301
|
+
/**
|
|
302
|
+
* Whether this VFS takes its OPFS access handle in the EXCLUSIVE mode —
|
|
303
|
+
* `createSyncAccessHandle()` with no `mode`, rather than
|
|
304
|
+
* `mode: 'readwrite-unsafe'`.
|
|
305
|
+
*
|
|
306
|
+
* It decides whether an acquisition has to be retried. A terminated context
|
|
307
|
+
* releases its Web Locks at once but keeps its OPFS access handles for up to
|
|
308
|
+
* ~2 s on Chromium (HANDLE-CORPSE, `mem:measurements`), so a VFS taking an
|
|
309
|
+
* exclusive handle can meet a file held by something that answers nothing:
|
|
310
|
+
* no lock to wait on, no owner to ask, only time to wait out. Where
|
|
311
|
+
* `readwrite-unsafe` is used a second handle is granted regardless, and a
|
|
312
|
+
* dead holder blocks nobody.
|
|
313
|
+
*
|
|
314
|
+
* Declared rather than detected, because the error is not available where the
|
|
315
|
+
* decision has to be made: wa-sqlite's `jOpen` swallows it (measured
|
|
316
|
+
* 2026-09-18) and SQLite reports a bare `SQLITE_CANTOPEN`. Where the error IS
|
|
317
|
+
* available — VFS instantiation — `createVfsInstance` tests it directly and
|
|
318
|
+
* needs no declaration.
|
|
319
|
+
*
|
|
320
|
+
* NOT declared for `OPFSAdaptiveVFS` and `OPFSWriteAheadVFS`, which ask for
|
|
321
|
+
* `readwrite-unsafe` and fall back to an exclusive handle only on an engine
|
|
322
|
+
* that lacks it. That combination is real but out of reach: Firefox releases
|
|
323
|
+
* a dead worker's handle in 1-6 ms (HANDLE-ORPHAN), a window nothing loses.
|
|
324
|
+
*/
|
|
325
|
+
readonly exclusiveFileHandle: boolean;
|
|
187
326
|
/**
|
|
188
327
|
* Platform features without which this VFS cannot work at all.
|
|
189
328
|
*
|
|
190
329
|
* `readwrite-unsafe` is the one that bites: WebIDL ignores the unknown
|
|
191
330
|
* dictionary member on engines that do not implement it, so the handle
|
|
192
|
-
* silently opens exclusive and
|
|
193
|
-
*
|
|
194
|
-
*
|
|
331
|
+
* silently opens exclusive, and a second connection then waits or fails
|
|
332
|
+
* depending on the VFS — see `degradesWithout` and `singleConnectionWithout`.
|
|
333
|
+
* Declaring it is what lets the conformance suite probe for it and skip,
|
|
334
|
+
* instead of leaving it to surface as a 60-second timeout.
|
|
195
335
|
*/
|
|
196
336
|
readonly requires: readonly PlatformFeature[];
|
|
197
337
|
/**
|
|
@@ -199,20 +339,119 @@ export type VFSCapability = {
|
|
|
199
339
|
*
|
|
200
340
|
* `OPFSAdaptiveVFS` is the case this field exists for. Without
|
|
201
341
|
* `readwrite-unsafe` it rotates a single exclusive access handle between
|
|
202
|
-
* connections instead of holding one each. That works —
|
|
203
|
-
*
|
|
204
|
-
*
|
|
342
|
+
* connections instead of holding one each. That works — Firefox is the engine
|
|
343
|
+
* the browser suite exercises it on — but a connection in a long
|
|
344
|
+
* uninterruptible statement holds the handle, and every other connection to
|
|
345
|
+
* the database, in another client or tab, waits for it. Within one client it
|
|
346
|
+
* runs a single worker there: see `singleConnectionWithout`.
|
|
205
347
|
*
|
|
206
348
|
* Without this distinction, a support table derived from browser specs would
|
|
207
349
|
* mark that VFS broken everywhere outside Chromium, when it merely degrades.
|
|
208
350
|
*/
|
|
209
351
|
readonly degradesWithout: readonly PlatformFeature[];
|
|
352
|
+
/**
|
|
353
|
+
* Platform features without which a pool of more than one worker buys this
|
|
354
|
+
* VFS nothing, so it runs on one (spec 2026-09-13, §3 and §10). Either the
|
|
355
|
+
* VFS holds its database file exclusively for a connection's whole life and
|
|
356
|
+
* a second worker cannot open at all (`OPFSWriteAheadVFS`), or it rotates one
|
|
357
|
+
* exclusive access handle between connections and a second worker only waits
|
|
358
|
+
* its turn (`OPFSAdaptiveVFS` — measured 2026-09-14 on Firefox: a pool of one
|
|
359
|
+
* was faster at startup and on bursts of reads, and equal everywhere else).
|
|
360
|
+
* The pool's surplus workers probe the feature before loading anything and
|
|
361
|
+
* decline (`src/worker/probes.ts`); every feature listed needs a probe there.
|
|
362
|
+
*/
|
|
363
|
+
readonly singleConnectionWithout: readonly PlatformFeature[];
|
|
364
|
+
/**
|
|
365
|
+
* Files this VFS keeps beside the database, by suffix, beyond the three every
|
|
366
|
+
* layout may have (`''`, `-journal`, `-wal`). `deleteDatabase` removes them
|
|
367
|
+
* with the rest; a file missing from this list outlives its database.
|
|
368
|
+
*
|
|
369
|
+
* `OPFSWriteAheadVFS` keeps its write-ahead log in two files of its own,
|
|
370
|
+
* `-wa0` and `-wa1` (wa-sqlite's `#getWriteAheadNameFromDbName`) — measured
|
|
371
|
+
* left behind by every deletion until 2026-09-14.
|
|
372
|
+
*/
|
|
373
|
+
readonly extraFileSuffixes: readonly string[];
|
|
374
|
+
/**
|
|
375
|
+
* Whether every statement on this VFS must hand its worker back to the event
|
|
376
|
+
* loop while it runs, abortable or not.
|
|
377
|
+
*
|
|
378
|
+
* `IDBBatchAtomicVFS` is why it exists. Its `jLock` opens a readwrite
|
|
379
|
+
* IndexedDB transaction on reaching SHARED, and IndexedDB commits a
|
|
380
|
+
* transaction only once its thread returns to the event loop. A worker inside
|
|
381
|
+
* one long statement never did, so every other connection's read queued
|
|
382
|
+
* behind it until the statement ended — measured 2026-09-14 on both engines
|
|
383
|
+
* (`mem:measurements`, IDB-SIGNAL). The worker then runs its progress handler
|
|
384
|
+
* on every statement, a task turn every `PROGRESS_OPS` VM ops, which measured
|
|
385
|
+
* no cost. The `sync` build cannot yield and ignores it.
|
|
386
|
+
*/
|
|
387
|
+
readonly yieldsDuringStatements: boolean;
|
|
388
|
+
/**
|
|
389
|
+
* PRAGMAs this library applies on open for this VFS.
|
|
390
|
+
*
|
|
391
|
+
* Merged UNDER the consumer's `pragmas`, so any key they set wins and they
|
|
392
|
+
* never lose a default by setting an unrelated one — `foreign_keys` is the
|
|
393
|
+
* common case, and replacing rather than merging would silently disable
|
|
394
|
+
* everything below it.
|
|
395
|
+
*
|
|
396
|
+
* The bar is deliberately high, and almost nothing clears it: **more
|
|
397
|
+
* performance without less reliability, sourced rather than guessed.** Three
|
|
398
|
+
* things were weighed and rejected. `journal_mode=wal` universally, because
|
|
399
|
+
* no VFS here implements `xShmMap` and upstream gives write-ahead logging to
|
|
400
|
+
* `OPFSWriteAheadVFS` alone, inside the VFS and unreachable by pragma.
|
|
401
|
+
* `synchronous=normal`, because relaxing durability spends the consumer's
|
|
402
|
+
* data, not their milliseconds. And `cache_size`, because raising it changes
|
|
403
|
+
* a mode without a measurable gain — Firefox showed none at all, and the
|
|
404
|
+
* heap it can then reach is never given back (measured 2026-09-02).
|
|
405
|
+
*/
|
|
406
|
+
readonly defaultPragmas: Readonly<Record<string, string>>;
|
|
407
|
+
/**
|
|
408
|
+
* Whether this VFS enforces an origin-wide exclusive connection lock for the
|
|
409
|
+
* client's lifetime.
|
|
410
|
+
*
|
|
411
|
+
* When `true`, `createSQLiteClient` acquires a `bsq:conn:…` Web Lock on first
|
|
412
|
+
* use. A second client that attempts to open the same database receives `DATABASE_IN_USE`
|
|
413
|
+
* immediately on its first query instead of silently reading a frozen, broken
|
|
414
|
+
* view. This field is the only thing standing between a consumer and an
|
|
415
|
+
* unfalsifiable silent failure — `SELECT 1` and even
|
|
416
|
+
* `SELECT count(*) FROM sqlite_master` pass on a broken second client.
|
|
417
|
+
*
|
|
418
|
+
* `true` only for `AccessHandlePoolVFS`, whose OPFS access-handle pool is not
|
|
419
|
+
* sharable across connections (measured AHP-2TAB, 2026-09-01).
|
|
420
|
+
* `false` for `IDBMirrorVFS` — despite `multiConnection: false` — because two
|
|
421
|
+
* clients on that VFS DO share data over its origin-wide `BroadcastChannel`
|
|
422
|
+
* (measured 2026-09-01, 3/3 both engines). `multiConnection: false` there marks
|
|
423
|
+
* concurrent-writer unsafety, not isolation.
|
|
424
|
+
* `false` for the memory VFS, which are isolated by construction and have
|
|
425
|
+
* nothing to exclude.
|
|
426
|
+
*
|
|
427
|
+
* `VFS_CAPABILITIES` is the single source of truth the client guard, the
|
|
428
|
+
* conformance suite, the VFS.md generator and the benchmark page all read.
|
|
429
|
+
* The gate is by this declaration, not by VFS name.
|
|
430
|
+
*/
|
|
431
|
+
readonly exclusiveConnection: boolean;
|
|
432
|
+
/**
|
|
433
|
+
* Platform features without which this VFS holds its database file
|
|
434
|
+
* exclusively for a connection's whole life, across the origin — so the
|
|
435
|
+
* client takes `bsq:conn` exclusively, as for `exclusiveConnection`, and a
|
|
436
|
+
* second client gets `DATABASE_IN_USE` (spec 2026-09-15).
|
|
437
|
+
*
|
|
438
|
+
* `OPFSWriteAheadVFS` without `readwrite-unsafe`: upstream's VFS requires the
|
|
439
|
+
* mode and keeps its three access handles for the connection's life, so
|
|
440
|
+
* nothing else can open the file — every query of a second client failed
|
|
441
|
+
* with WORKER_CRASHED on Firefox, 20/20 per shape (2026-09-15).
|
|
442
|
+
*
|
|
443
|
+
* The page cannot probe these features, so worker 0 probes them before
|
|
444
|
+
* opening (`src/worker/probes.ts`): every feature listed needs a probe there,
|
|
445
|
+
* and must also be in `singleConnectionWithout`, whose surplus workers
|
|
446
|
+
* decline before they touch the file.
|
|
447
|
+
*/
|
|
448
|
+
readonly exclusiveConnectionWithout: readonly PlatformFeature[];
|
|
210
449
|
};
|
|
211
450
|
/**
|
|
212
451
|
* The single source of truth for VFS selection. `SQLiteVFS` is derived from its
|
|
213
452
|
* keys, `worker/worker.ts` must supply a loader for every key, the guards in
|
|
214
453
|
* `client.ts` read it, the conformance suite gates its scenarios on it, and the
|
|
215
|
-
*
|
|
454
|
+
* VFS.md table is generated from it. Nothing may hold a second copy.
|
|
216
455
|
*
|
|
217
456
|
* Build order is a decision per VFS, not a rule: `sync` is both the fastest and
|
|
218
457
|
* the most portable build, so it leads wherever supported; `OPFSAdaptiveVFS`
|
|
@@ -222,8 +461,8 @@ export type VFSCapability = {
|
|
|
222
461
|
* wa-sqlite v1.1.2, never copied from upstream's table.
|
|
223
462
|
*/
|
|
224
463
|
export declare const VFS_CAPABILITIES: {
|
|
225
|
-
readonly
|
|
226
|
-
readonly builds: readonly ["async", "jspi"];
|
|
464
|
+
readonly OPFSWriteAheadVFS: {
|
|
465
|
+
readonly builds: readonly ["sync", "async", "jspi"];
|
|
227
466
|
readonly maxPoolSize: null;
|
|
228
467
|
readonly poolLimitReason: null;
|
|
229
468
|
readonly multiConnection: true;
|
|
@@ -231,11 +470,18 @@ export declare const VFS_CAPABILITIES: {
|
|
|
231
470
|
readonly memoryModel: 'page-cache';
|
|
232
471
|
readonly storage: 'opfs';
|
|
233
472
|
readonly layout: 'opfs-path';
|
|
473
|
+
readonly exclusiveFileHandle: false;
|
|
234
474
|
readonly requires: readonly ["opfs"];
|
|
235
475
|
readonly degradesWithout: readonly ["readwrite-unsafe"];
|
|
476
|
+
readonly singleConnectionWithout: readonly ["readwrite-unsafe"];
|
|
477
|
+
readonly extraFileSuffixes: readonly ["-wa0", "-wa1"];
|
|
478
|
+
readonly yieldsDuringStatements: false;
|
|
479
|
+
readonly exclusiveConnection: false;
|
|
480
|
+
readonly exclusiveConnectionWithout: readonly ["readwrite-unsafe"];
|
|
481
|
+
readonly defaultPragmas: {};
|
|
236
482
|
};
|
|
237
|
-
readonly
|
|
238
|
-
readonly builds: readonly ["
|
|
483
|
+
readonly OPFSAdaptiveVFS: {
|
|
484
|
+
readonly builds: readonly ["async", "jspi"];
|
|
239
485
|
readonly maxPoolSize: null;
|
|
240
486
|
readonly poolLimitReason: null;
|
|
241
487
|
readonly multiConnection: true;
|
|
@@ -243,20 +489,34 @@ export declare const VFS_CAPABILITIES: {
|
|
|
243
489
|
readonly memoryModel: 'page-cache';
|
|
244
490
|
readonly storage: 'opfs';
|
|
245
491
|
readonly layout: 'opfs-path';
|
|
492
|
+
readonly exclusiveFileHandle: false;
|
|
246
493
|
readonly requires: readonly ["opfs"];
|
|
247
494
|
readonly degradesWithout: readonly ["readwrite-unsafe"];
|
|
495
|
+
readonly singleConnectionWithout: readonly ["readwrite-unsafe"];
|
|
496
|
+
readonly extraFileSuffixes: readonly [];
|
|
497
|
+
readonly yieldsDuringStatements: false;
|
|
498
|
+
readonly exclusiveConnection: false;
|
|
499
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
500
|
+
readonly defaultPragmas: {};
|
|
248
501
|
};
|
|
249
502
|
readonly OPFSCoopSyncVFS: {
|
|
250
503
|
readonly builds: readonly ["sync", "async", "jspi"];
|
|
251
|
-
readonly maxPoolSize:
|
|
252
|
-
readonly poolLimitReason:
|
|
504
|
+
readonly maxPoolSize: 1;
|
|
505
|
+
readonly poolLimitReason: 'it rotates one exclusive access handle between connections, so another worker only waits its turn';
|
|
253
506
|
readonly multiConnection: true;
|
|
254
507
|
readonly persistent: true;
|
|
255
508
|
readonly memoryModel: 'page-cache';
|
|
256
509
|
readonly storage: 'opfs';
|
|
257
510
|
readonly layout: 'opfs-path';
|
|
511
|
+
readonly exclusiveFileHandle: true;
|
|
258
512
|
readonly requires: readonly ["opfs"];
|
|
259
513
|
readonly degradesWithout: readonly [];
|
|
514
|
+
readonly singleConnectionWithout: readonly [];
|
|
515
|
+
readonly extraFileSuffixes: readonly [];
|
|
516
|
+
readonly yieldsDuringStatements: false;
|
|
517
|
+
readonly exclusiveConnection: false;
|
|
518
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
519
|
+
readonly defaultPragmas: {};
|
|
260
520
|
};
|
|
261
521
|
readonly AccessHandlePoolVFS: {
|
|
262
522
|
readonly builds: readonly ["sync", "async", "jspi"];
|
|
@@ -267,8 +527,18 @@ export declare const VFS_CAPABILITIES: {
|
|
|
267
527
|
readonly memoryModel: 'page-cache';
|
|
268
528
|
readonly storage: 'opfs';
|
|
269
529
|
readonly layout: 'opfs-pool';
|
|
530
|
+
readonly exclusiveFileHandle: true;
|
|
270
531
|
readonly requires: readonly ["opfs"];
|
|
271
532
|
readonly degradesWithout: readonly [];
|
|
533
|
+
readonly singleConnectionWithout: readonly [];
|
|
534
|
+
readonly extraFileSuffixes: readonly [];
|
|
535
|
+
readonly yieldsDuringStatements: false;
|
|
536
|
+
readonly exclusiveConnection: true;
|
|
537
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
538
|
+
readonly defaultPragmas: {
|
|
539
|
+
readonly locking_mode: 'exclusive';
|
|
540
|
+
readonly journal_mode: 'wal';
|
|
541
|
+
};
|
|
272
542
|
};
|
|
273
543
|
readonly IDBBatchAtomicVFS: {
|
|
274
544
|
readonly builds: readonly ["async", "jspi"];
|
|
@@ -279,8 +549,15 @@ export declare const VFS_CAPABILITIES: {
|
|
|
279
549
|
readonly memoryModel: 'page-cache';
|
|
280
550
|
readonly storage: 'indexeddb';
|
|
281
551
|
readonly layout: 'idb-store';
|
|
552
|
+
readonly exclusiveFileHandle: false;
|
|
282
553
|
readonly requires: readonly [];
|
|
283
554
|
readonly degradesWithout: readonly [];
|
|
555
|
+
readonly singleConnectionWithout: readonly [];
|
|
556
|
+
readonly extraFileSuffixes: readonly [];
|
|
557
|
+
readonly yieldsDuringStatements: true;
|
|
558
|
+
readonly exclusiveConnection: false;
|
|
559
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
560
|
+
readonly defaultPragmas: {};
|
|
284
561
|
};
|
|
285
562
|
readonly IDBMirrorVFS: {
|
|
286
563
|
readonly builds: readonly ["async", "jspi"];
|
|
@@ -291,8 +568,15 @@ export declare const VFS_CAPABILITIES: {
|
|
|
291
568
|
readonly memoryModel: 'whole-database';
|
|
292
569
|
readonly storage: 'indexeddb';
|
|
293
570
|
readonly layout: 'idb-store';
|
|
571
|
+
readonly exclusiveFileHandle: false;
|
|
294
572
|
readonly requires: readonly [];
|
|
295
573
|
readonly degradesWithout: readonly [];
|
|
574
|
+
readonly singleConnectionWithout: readonly [];
|
|
575
|
+
readonly extraFileSuffixes: readonly [];
|
|
576
|
+
readonly yieldsDuringStatements: false;
|
|
577
|
+
readonly exclusiveConnection: false;
|
|
578
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
579
|
+
readonly defaultPragmas: {};
|
|
296
580
|
};
|
|
297
581
|
readonly OPFSAnyContextVFS: {
|
|
298
582
|
readonly builds: readonly ["async", "jspi"];
|
|
@@ -303,8 +587,15 @@ export declare const VFS_CAPABILITIES: {
|
|
|
303
587
|
readonly memoryModel: 'page-cache';
|
|
304
588
|
readonly storage: 'opfs';
|
|
305
589
|
readonly layout: 'opfs-path';
|
|
590
|
+
readonly exclusiveFileHandle: false;
|
|
306
591
|
readonly requires: readonly ["opfs", "writable-stream"];
|
|
307
592
|
readonly degradesWithout: readonly [];
|
|
593
|
+
readonly singleConnectionWithout: readonly [];
|
|
594
|
+
readonly extraFileSuffixes: readonly [];
|
|
595
|
+
readonly yieldsDuringStatements: false;
|
|
596
|
+
readonly exclusiveConnection: false;
|
|
597
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
598
|
+
readonly defaultPragmas: {};
|
|
308
599
|
};
|
|
309
600
|
readonly MemoryVFS: {
|
|
310
601
|
readonly builds: readonly ["sync", "async", "jspi"];
|
|
@@ -315,8 +606,15 @@ export declare const VFS_CAPABILITIES: {
|
|
|
315
606
|
readonly memoryModel: 'whole-database';
|
|
316
607
|
readonly storage: 'memory';
|
|
317
608
|
readonly layout: 'memory';
|
|
609
|
+
readonly exclusiveFileHandle: false;
|
|
318
610
|
readonly requires: readonly [];
|
|
319
611
|
readonly degradesWithout: readonly [];
|
|
612
|
+
readonly singleConnectionWithout: readonly [];
|
|
613
|
+
readonly extraFileSuffixes: readonly [];
|
|
614
|
+
readonly yieldsDuringStatements: false;
|
|
615
|
+
readonly exclusiveConnection: false;
|
|
616
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
617
|
+
readonly defaultPragmas: {};
|
|
320
618
|
};
|
|
321
619
|
readonly MemoryAsyncVFS: {
|
|
322
620
|
readonly builds: readonly ["async", "jspi"];
|
|
@@ -327,22 +625,18 @@ export declare const VFS_CAPABILITIES: {
|
|
|
327
625
|
readonly memoryModel: 'whole-database';
|
|
328
626
|
readonly storage: 'memory';
|
|
329
627
|
readonly layout: 'memory';
|
|
628
|
+
readonly exclusiveFileHandle: false;
|
|
330
629
|
readonly requires: readonly [];
|
|
331
630
|
readonly degradesWithout: readonly [];
|
|
631
|
+
readonly singleConnectionWithout: readonly [];
|
|
632
|
+
readonly extraFileSuffixes: readonly [];
|
|
633
|
+
readonly yieldsDuringStatements: false;
|
|
634
|
+
readonly exclusiveConnection: false;
|
|
635
|
+
readonly exclusiveConnectionWithout: readonly [];
|
|
636
|
+
readonly defaultPragmas: {};
|
|
332
637
|
};
|
|
333
638
|
};
|
|
334
639
|
export type SQLiteVFS = keyof typeof VFS_CAPABILITIES;
|
|
335
640
|
/** The build used when the caller does not name one. */
|
|
336
641
|
export declare const defaultBuildFor: (vfs: SQLiteVFS) => SQLiteBuild;
|
|
337
|
-
/**
|
|
338
|
-
* The VFS this project recommends when a caller has no reason to choose
|
|
339
|
-
* another. It is NOT a default — `vfs` is required, precisely so that the name
|
|
340
|
-
* lives in the consumer's own source and cannot move underneath their data.
|
|
341
|
-
*
|
|
342
|
-
* It lives here, beside the table, because the README generator marks this row
|
|
343
|
-
* `(recommended)` and would otherwise hold a second copy. It is deliberately
|
|
344
|
-
* not exported: a consumer writing `vfs: RECOMMENDED_VFS` would be exposed to
|
|
345
|
-
* the same displacement the day the recommendation changes.
|
|
346
|
-
*/
|
|
347
|
-
export declare const RECOMMENDED_VFS: SQLiteVFS;
|
|
348
642
|
export {};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SQLiteBuild, type SQLiteVFS, type WasmLocation } from './types';
|
|
2
2
|
export declare const sqlParams: () => {
|
|
3
3
|
addParam: (v: unknown) => string;
|
|
4
4
|
addParamArray: (values: unknown[]) => string;
|
|
@@ -6,13 +6,20 @@ export declare const sqlParams: () => {
|
|
|
6
6
|
};
|
|
7
7
|
export declare const isReadQuery: (sql: string) => boolean;
|
|
8
8
|
export declare const isWriteQuery: (sql: string) => boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether `sql` manages the transaction or its savepoints rather than data
|
|
11
|
+
* (spec 2026-09-11, D8). Never wrapped in the library's savepoint: there is
|
|
12
|
+
* nothing to undo, and a `RELEASE u` run inside it would pop it along with
|
|
13
|
+
* `u`. The leading keyword decides: these statements are never compound.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isTransactionControl: (sql: string) => boolean;
|
|
9
16
|
/**
|
|
10
17
|
* Combines two abort signals into one that fires with the reason of whichever
|
|
11
18
|
* source aborted first, plus the `release()` that unsubscribes it.
|
|
12
19
|
*
|
|
13
20
|
* NOT `AbortSignal.any()`. That is Chrome 116 / Firefox 124 / Safari 17.4, far
|
|
14
21
|
* above this library's floor (Chrome 92 / Firefox 95 / Safari 15.4), and
|
|
15
|
-
* adopting it would raise every row of the generated
|
|
22
|
+
* adopting it would raise every row of the generated VFS.md matrix for every
|
|
16
23
|
* consumer.
|
|
17
24
|
*
|
|
18
25
|
* The common case allocates nothing: with one side absent, or one side already
|
|
@@ -23,6 +30,26 @@ export declare const mergeSignals: (a: AbortSignal | undefined, b: AbortSignal |
|
|
|
23
30
|
signal: AbortSignal | undefined;
|
|
24
31
|
release: () => void;
|
|
25
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* The `timeout` option: a wall-clock budget in milliseconds, counted from the
|
|
35
|
+
* call. Returns the signal the call should actually use — the caller's own,
|
|
36
|
+
* merged with one this library owns and aborts when the budget is spent.
|
|
37
|
+
*
|
|
38
|
+
* The abort reason IS the error the caller receives. Every abort path in this
|
|
39
|
+
* library rejects with `signal.reason`, and `mergeSignals` relays a reason
|
|
40
|
+
* verbatim, so nothing downstream has to ask which signal fired. That is why
|
|
41
|
+
* this is an AbortController and a setTimeout rather than
|
|
42
|
+
* `AbortSignal.timeout()`, which offers no way to set the reason.
|
|
43
|
+
*
|
|
44
|
+
* `release()` is owed exactly once, however the call ends.
|
|
45
|
+
*/
|
|
46
|
+
export declare const withDeadline: (options: {
|
|
47
|
+
signal?: AbortSignal | undefined;
|
|
48
|
+
timeout?: number | undefined;
|
|
49
|
+
} | undefined, method: string) => {
|
|
50
|
+
signal: AbortSignal | undefined;
|
|
51
|
+
release: () => void;
|
|
52
|
+
};
|
|
26
53
|
/**
|
|
27
54
|
* Routing guard for the read-shaped methods (`read`, `chunk`, `stream`, `first`).
|
|
28
55
|
* Throws before a lease is taken, so a rejected statement costs no pool capacity.
|
|
@@ -68,6 +95,19 @@ export declare const assertGeneratedExpression: (expr: string, column: string) =
|
|
|
68
95
|
* `createSQLiteClient()` rather than inside an unrelated query, and by the
|
|
69
96
|
* worker at open, which is the only place the statements actually run.
|
|
70
97
|
*/
|
|
98
|
+
/**
|
|
99
|
+
* The PRAGMAs a client actually applies: the VFS's declared defaults with the
|
|
100
|
+
* consumer's own layered over them.
|
|
101
|
+
*
|
|
102
|
+
* **Merged, never replaced.** A consumer who passes one pragma is answering a
|
|
103
|
+
* question of their own — `foreign_keys` is the usual one — not declining the
|
|
104
|
+
* VFS's defaults, and replacing would silently drop every default the moment
|
|
105
|
+
* they set anything at all. A key they DO set always wins, which is how a
|
|
106
|
+
* default is refused: pass `journal_mode` yourself and yours is what runs.
|
|
107
|
+
*
|
|
108
|
+
* Order matters and is the whole function: spread the defaults first.
|
|
109
|
+
*/
|
|
110
|
+
export declare const resolvePragmas: (vfs: SQLiteVFS, pragmas: Record<string, string> | undefined) => Record<string, string>;
|
|
71
111
|
export declare const renderPragmas: (pragmas: Record<string, string>) => string[];
|
|
72
112
|
/**
|
|
73
113
|
* The single definition of database identity — one string used everywhere:
|