browser-sqlite 1.0.0-rc.3 → 1.0.0-rc.4
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 +435 -63
- package/dist/LICENSE +21 -0
- package/dist/NOTICE +56 -0
- package/dist/api.d.ts +376 -0
- package/dist/bulk.d.ts +42 -0
- package/dist/capabilities.d.ts +23 -0
- package/dist/client.d.ts +198 -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 +42 -0
- package/dist/epochs.d.ts +55 -0
- package/dist/errors.d.ts +37 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/locks.d.ts +54 -0
- package/dist/logger.d.ts +24 -0
- package/dist/pool.d.ts +98 -0
- package/dist/queries.d.ts +36 -0
- package/dist/scheduler.d.ts +131 -0
- package/dist/supervisor.d.ts +17 -0
- package/dist/transaction.d.ts +38 -0
- package/dist/types.d.ts +348 -0
- package/dist/utils.d.ts +116 -0
- package/dist/worker/cloneable.d.ts +25 -0
- package/dist/worker/statement-cache.d.ts +22 -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 +36 -20
- 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/logger.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prefixed logger the `debug` option turns on.
|
|
3
|
+
*
|
|
4
|
+
* Lifecycle events only — worker created, ready, open-error, crash, restart,
|
|
5
|
+
* worker loss, close, skipped sweep. A line per query would be illegible under
|
|
6
|
+
* real load and would put user values on the console; query throughput belongs
|
|
7
|
+
* in `db.debug`, not here.
|
|
8
|
+
*/
|
|
9
|
+
export type Logger = {
|
|
10
|
+
info: (message: string) => void;
|
|
11
|
+
warn: (message: string) => void;
|
|
12
|
+
error: (message: string) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Always writes through the sink regardless of the `enabled` flag.
|
|
15
|
+
* Use for events that must be visible even when debug logging is off —
|
|
16
|
+
* permanent pool shrinkage being the primary case.
|
|
17
|
+
*/
|
|
18
|
+
always: {
|
|
19
|
+
warn: (message: string) => void;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
type Sink = Pick<Console, 'debug' | 'warn' | 'error'>;
|
|
23
|
+
export declare const createLogger: (prefix: string, enabled: boolean, sink?: Sink) => Logger;
|
|
24
|
+
export {};
|
package/dist/pool.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { SQLiteError } from './errors';
|
|
2
|
+
import type { Logger } from './logger';
|
|
3
|
+
import type { SQLiteBuild, SQLiteVFS, WasmLocation } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Query execution options forwarded to a pool worker.
|
|
6
|
+
*/
|
|
7
|
+
export type PoolWorkerQueryOptions = {
|
|
8
|
+
chunkSize?: number | undefined;
|
|
9
|
+
credits?: number | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* When true, the query's completion does not call `deps.onServed`. Set for
|
|
12
|
+
* the commit-propagation barrier: it is a synthetic probe, not user work, and
|
|
13
|
+
* must not reset the supervisor's restart counter.
|
|
14
|
+
* `createQueryDebugState` is intentionally NOT suppressed: barrier statements
|
|
15
|
+
* still appear in the debug request tree, and a browser test counts them there
|
|
16
|
+
* to prove the barrier stays conditional.
|
|
17
|
+
*/
|
|
18
|
+
noServed?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A Worker extended with pool-specific properties.
|
|
22
|
+
*
|
|
23
|
+
* Note: no `available` field — availability lives in the Scheduler, not on
|
|
24
|
+
* the worker itself. This makes it impossible to republish a borrowed worker
|
|
25
|
+
* from outside the scheduler (the root cause of B1).
|
|
26
|
+
*/
|
|
27
|
+
export type PoolWorker = Worker & {
|
|
28
|
+
index: number;
|
|
29
|
+
/** Lifecycle label for the debug surface. Replaces the SAB status byte. */
|
|
30
|
+
status: string;
|
|
31
|
+
/**
|
|
32
|
+
* The commit epoch this connection has absorbed. Starts at -1: a worker
|
|
33
|
+
* opens the file — and reads page 1 — BEFORE it enters the pool, and a
|
|
34
|
+
* commit can land in between. At poolSize 2 that is the nominal startup
|
|
35
|
+
* ordering, not a rare race, so a new worker is always treated as behind and
|
|
36
|
+
* pays exactly one barrier statement in its lifetime.
|
|
37
|
+
*/
|
|
38
|
+
seen: number;
|
|
39
|
+
/** The epoch captured when the current lease was granted. */
|
|
40
|
+
epochTarget: number;
|
|
41
|
+
query: <T extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: unknown[], options?: PoolWorkerQueryOptions) => AsyncGenerator<T[] | number>;
|
|
42
|
+
/**
|
|
43
|
+
* Ask the worker to stop. Also settles a `next()` already in flight, which
|
|
44
|
+
* is what lets the consumer's queued `return()` reach the generator's
|
|
45
|
+
* finally instead of waiting behind a chunk that may be minutes away.
|
|
46
|
+
*/
|
|
47
|
+
interrupt: () => void;
|
|
48
|
+
/** Resolves when no query is in flight on this worker. */
|
|
49
|
+
quiesce: () => Promise<void>;
|
|
50
|
+
/** Posts `close`, awaits the `closed` reply, then the caller must terminate. */
|
|
51
|
+
close: () => Promise<void>;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Returns a SQLiteError('BUSY', …) when data carries a lock-conflict result
|
|
55
|
+
* code (5 or 6), else undefined. Shared by both the query-error and
|
|
56
|
+
* open-error paths so the BUSY_CODES decision lives in exactly one place.
|
|
57
|
+
*/
|
|
58
|
+
export declare const busyFromCode: (data: {
|
|
59
|
+
message: string;
|
|
60
|
+
cause?: unknown;
|
|
61
|
+
sqliteCode?: number;
|
|
62
|
+
}) => SQLiteError | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The single `new Worker(new URL(…))` expression in this package.
|
|
65
|
+
*
|
|
66
|
+
* It must stay one literal, in one place: bundlers find the worker by static
|
|
67
|
+
* analysis of exactly this shape, and a second copy would have them emit a
|
|
68
|
+
* second, untransformed worker bundle. `pool.ts:191` records what that cost
|
|
69
|
+
* when the expression was written a second time for an error message.
|
|
70
|
+
*/
|
|
71
|
+
export declare const spawnWorker: (name: string) => Worker;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new pool worker and registers it in the pool array.
|
|
74
|
+
* Sets up message routing via callId for query responses.
|
|
75
|
+
*
|
|
76
|
+
* Moved verbatim from `createWorker` in client.ts, with three changes:
|
|
77
|
+
* 1. Closure variables become explicit `deps` parameters.
|
|
78
|
+
* 2. Both `available` assignments are deleted (availability lives in the Scheduler).
|
|
79
|
+
* 3. `worker.available = false/true` in the `query` generator are deleted.
|
|
80
|
+
*/
|
|
81
|
+
export declare const createPoolWorker: (deps: {
|
|
82
|
+
index: number;
|
|
83
|
+
pool: (PoolWorker | undefined)[];
|
|
84
|
+
clientPrefix: string;
|
|
85
|
+
file: string;
|
|
86
|
+
vfs: SQLiteVFS;
|
|
87
|
+
build: SQLiteBuild;
|
|
88
|
+
/** Already resolved and absolute; relayed to the worker, never read here. */
|
|
89
|
+
wasm?: WasmLocation | undefined;
|
|
90
|
+
pragmas?: Record<string, string> | undefined;
|
|
91
|
+
statementCacheSize?: number | undefined;
|
|
92
|
+
onDeath?: (index: number, error: SQLiteError) => void;
|
|
93
|
+
onServed?: (index: number) => void;
|
|
94
|
+
drainTimeout: number;
|
|
95
|
+
createWorkerDebugState?: ((index: number, name: string) => any) | undefined;
|
|
96
|
+
createQueryDebugState?: ((index: number, sql: string, params?: unknown[]) => any) | undefined;
|
|
97
|
+
logger: Logger;
|
|
98
|
+
}) => Promise<PoolWorker>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OptionsWithSignal, SQLiteChunkOptions } from './api';
|
|
2
|
+
import type { PoolWorker } from './pool';
|
|
3
|
+
/**
|
|
4
|
+
* Wires an AbortSignal into a promise that rejects the instant the signal
|
|
5
|
+
* fires, and returns a teardown that removes the listener. The rejection sink
|
|
6
|
+
* (`aborted?.catch`) suppresses the unhandled-rejection when the query ends
|
|
7
|
+
* normally and nobody is racing the promise any more.
|
|
8
|
+
*
|
|
9
|
+
* This is the only place in the module that reads an AbortSignal; both
|
|
10
|
+
* `chunk()` and `writeWorker()` delegate here.
|
|
11
|
+
*/
|
|
12
|
+
export declare const makeAbortRace: (signal: AbortSignal | undefined) => {
|
|
13
|
+
aborted: Promise<never> | undefined;
|
|
14
|
+
teardown: () => void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The single query primitive. Every other read path is a thin derivation, and
|
|
18
|
+
* abort is implemented here exactly once.
|
|
19
|
+
*/
|
|
20
|
+
export declare const chunk: <T extends Record<string, unknown> = Record<string, unknown>>(worker: PoolWorker, sql: string, params?: unknown[], options?: SQLiteChunkOptions & {
|
|
21
|
+
credits?: number;
|
|
22
|
+
}) => AsyncGenerator<T[]>;
|
|
23
|
+
export declare const streamRows: <T extends Record<string, unknown> = Record<string, unknown>>(worker: PoolWorker, sql: string, params?: unknown[], options?: SQLiteChunkOptions) => AsyncGenerator<T>;
|
|
24
|
+
export declare const readWorker: <T extends Record<string, unknown> = Record<string, unknown>>(worker: PoolWorker, sql: string, params?: unknown[], options?: SQLiteChunkOptions) => Promise<T[]>;
|
|
25
|
+
/**
|
|
26
|
+
* First row, then stop. This BREAKS rather than aborting: a break triggers the
|
|
27
|
+
* generator's return path, which runs chunk()'s finally and the transport's
|
|
28
|
+
* stop-and-drain — the same worker-stop routine, reached without an exception.
|
|
29
|
+
* That is why there is no internal AbortController here and no need to tell an
|
|
30
|
+
* internal abort from the caller's.
|
|
31
|
+
*/
|
|
32
|
+
export declare const firstWorker: <T extends Record<string, unknown> = Record<string, unknown>>(worker: PoolWorker, sql: string, params?: unknown[], options?: OptionsWithSignal) => Promise<T | undefined>;
|
|
33
|
+
export declare const writeWorker: <T extends Record<string, unknown> = Record<string, unknown>>(worker: PoolWorker, sql: string, params?: unknown[], options?: OptionsWithSignal) => Promise<{
|
|
34
|
+
result: T[];
|
|
35
|
+
affected: number;
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure worker scheduling: availability, wait queues, writer designation.
|
|
3
|
+
*
|
|
4
|
+
* This module is deliberately free of `Worker` and DOM imports so
|
|
5
|
+
* it can be exercised by fast Node tests. B1 survived for months because the
|
|
6
|
+
* scheduler was only reachable through slow browser tests.
|
|
7
|
+
*/
|
|
8
|
+
import type { CreateSQLiteClientOptions } from './client';
|
|
9
|
+
/**
|
|
10
|
+
* A borrowed worker. `release()` is the only way back into the pool and is
|
|
11
|
+
* idempotent — a second call is a no-op, not an error.
|
|
12
|
+
*/
|
|
13
|
+
export type Lease<W> = {
|
|
14
|
+
readonly worker: W;
|
|
15
|
+
release: () => void;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Decides whether a worker index may hold the write designation. The default
|
|
19
|
+
* accepts every index, so production behaviour is exactly what it was.
|
|
20
|
+
*/
|
|
21
|
+
export type WriterPolicy = (index: number) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* TEST-ONLY, UNSUPPORTED, removable without notice.
|
|
24
|
+
*
|
|
25
|
+
* The barrier's browser test needs the failing configuration — writer not on
|
|
26
|
+
* the worker that serves the read — to be deterministic; at startup chance it
|
|
27
|
+
* occurs ~3 runs in 10. This type is declared here, and NOT in `client.ts`,
|
|
28
|
+
* because `src/index.ts` re-exports only `./client` and `./errors`: keeping it
|
|
29
|
+
* out of that path keeps it out of the published `.d.ts` and out of every
|
|
30
|
+
* consumer's autocompletion. `CreateSQLiteClientOptions` is pulled in with
|
|
31
|
+
* `import type`, which is erased at build time and creates no runtime cycle.
|
|
32
|
+
*
|
|
33
|
+
* A predicate that refuses every index leaves writes queued forever — use it
|
|
34
|
+
* with `poolSize >= 2`.
|
|
35
|
+
*/
|
|
36
|
+
export type InternalSQLiteClientOptions = CreateSQLiteClientOptions & {
|
|
37
|
+
__unsafeTestWriterPolicy?: WriterPolicy;
|
|
38
|
+
};
|
|
39
|
+
export type Scheduler<W> = {
|
|
40
|
+
add: (worker: W) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Leases a worker, queueing when none is free.
|
|
43
|
+
*
|
|
44
|
+
* `signal` aborts the WAIT, and only the wait: it rejects with
|
|
45
|
+
* `signal.reason` while the request is still queued, and is ignored once a
|
|
46
|
+
* lease has been granted — from that point the caller owns the worker and
|
|
47
|
+
* owes a `release()`. Without it an abort could not land at all while the
|
|
48
|
+
* pool had nothing to lend, which is the state a VFS rotating one exclusive
|
|
49
|
+
* OPFS handle can stay in indefinitely.
|
|
50
|
+
*/
|
|
51
|
+
acquire: (kind: 'read' | 'write', signal?: AbortSignal) => Promise<Lease<W>>;
|
|
52
|
+
/**
|
|
53
|
+
* Takes a worker out of the pool for good. A lease already outstanding on
|
|
54
|
+
* that index becomes inert: its `release()` neither hands the worker back nor
|
|
55
|
+
* counts towards `shutdown()`'s wait.
|
|
56
|
+
*/
|
|
57
|
+
remove: (index: number) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Closes the front door. Queued waiters reject with `reason`, later
|
|
60
|
+
* acquisitions reject the same way, and the returned promise settles when the
|
|
61
|
+
* last outstanding lease has come back.
|
|
62
|
+
*/
|
|
63
|
+
shutdown: (reason: Error) => Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Read-only counters for the debug subsystem. The scheduler stays pure: it
|
|
66
|
+
* exposes numbers and knows nothing about debug (spec §3.2).
|
|
67
|
+
*/
|
|
68
|
+
stats: () => {
|
|
69
|
+
read: number;
|
|
70
|
+
write: number;
|
|
71
|
+
available: number;
|
|
72
|
+
leased: number;
|
|
73
|
+
/**
|
|
74
|
+
* Callers suspended on the readiness gate. They are in NEITHER wait queue —
|
|
75
|
+
* the gate is awaited before `takeAvailable` is ever reached — so `read`
|
|
76
|
+
* and `write` cannot see them, and without this the debug surface reports
|
|
77
|
+
* an idle pool for the whole startup window.
|
|
78
|
+
*
|
|
79
|
+
* Waiting for the pool to *exist* is a different wait from waiting for a
|
|
80
|
+
* free worker, which is why this is its own counter and not folded in.
|
|
81
|
+
*/
|
|
82
|
+
gated: number;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Removes a slot from the settled-set so that its next `add()` or `remove()`
|
|
86
|
+
* call counts again toward opening the readiness gate. Only effective while
|
|
87
|
+
* the gate is still closed; a no-op once the gate has opened.
|
|
88
|
+
*
|
|
89
|
+
* Used by the startup retry round: the client re-arms the failed slots so
|
|
90
|
+
* the gate stays closed until the retry slots have settled.
|
|
91
|
+
*/
|
|
92
|
+
rearmSlot: (index: number) => void;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Creates a scheduler over workers identified by a numeric `index`.
|
|
96
|
+
*
|
|
97
|
+
* @param opts.onIdle - Called when a released worker returns to the available
|
|
98
|
+
* set with nothing queued behind it. The scheduler itself knows nothing about
|
|
99
|
+
* worker state.
|
|
100
|
+
*/
|
|
101
|
+
export declare const createScheduler: <W extends {
|
|
102
|
+
index: number;
|
|
103
|
+
}>(opts?: {
|
|
104
|
+
onIdle?: (worker: W) => void;
|
|
105
|
+
canDesignateWriter?: WriterPolicy;
|
|
106
|
+
/**
|
|
107
|
+
* Total number of worker slots the pool will spawn. Once every slot has
|
|
108
|
+
* settled (via `add` when it becomes ready, or via `remove` when it dies or
|
|
109
|
+
* fails to open), a one-shot gate is lifted and `acquire()` may proceed.
|
|
110
|
+
* Omit or pass 0 for an immediately-open gate (tests and single-shot use).
|
|
111
|
+
*/
|
|
112
|
+
poolSize?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Called exactly once when every slot in [0, poolSize) has settled for the
|
|
115
|
+
* first time. Fires before the gate opens so the callback can call
|
|
116
|
+
* `rearmSlot()` to extend the wait for a retry round.
|
|
117
|
+
*
|
|
118
|
+
* `openedCount` — slots that settled via `add()` (became ready).
|
|
119
|
+
* `failedIndices` — slots that settled via `remove()` (died / timed out).
|
|
120
|
+
*/
|
|
121
|
+
onFirstSettle?: (result: {
|
|
122
|
+
openedCount: number;
|
|
123
|
+
failedIndices: number[];
|
|
124
|
+
}) => void;
|
|
125
|
+
/**
|
|
126
|
+
* Called when the readiness gate resolves (opens). Not called when the gate
|
|
127
|
+
* is rejected via `shutdown()`. Use this to clear any startup-pending flag
|
|
128
|
+
* after the retry round (if any) has fully settled.
|
|
129
|
+
*/
|
|
130
|
+
onGateOpen?: () => void;
|
|
131
|
+
}) => Scheduler<W>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure restart policy for worker slots.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately free of `Worker` and DOM imports so Node tests can
|
|
5
|
+
* drive it in milliseconds — the same reason `scheduler.ts` is pure. B1 lived
|
|
6
|
+
* for months because the only way to reach the pool's decisions was a browser.
|
|
7
|
+
*
|
|
8
|
+
* The caller reports facts; this module returns a decision and never acts.
|
|
9
|
+
*/
|
|
10
|
+
export type SupervisorDecision = 'restart' | 'lost' | 'fail-client';
|
|
11
|
+
export type Supervisor = {
|
|
12
|
+
report: (index: number, event: 'spawned' | 'ready' | 'served' | 'died' | 'lost') => SupervisorDecision | undefined;
|
|
13
|
+
};
|
|
14
|
+
export declare const createSupervisor: (options: {
|
|
15
|
+
size: number;
|
|
16
|
+
maxWorkerRestarts?: number | undefined;
|
|
17
|
+
}) => Supervisor;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SQLiteQueryAPI, SQLiteTransactionDB, SQLiteTransactionOptions } from './api';
|
|
2
|
+
import type { ReadFn, TransactionFn, WriteFn } from './bulk';
|
|
3
|
+
import { SQLiteError } from './errors';
|
|
4
|
+
import type { PoolWorker } from './pool';
|
|
5
|
+
import type { Scheduler } from './scheduler';
|
|
6
|
+
/**
|
|
7
|
+
* Returns the `transaction()` method for a SQLiteDB instance.
|
|
8
|
+
*
|
|
9
|
+
* The returned function acquires exactly one lease for the full lifetime of
|
|
10
|
+
* the transaction. All SQLiteTransactionDB methods call worker-bound derivations
|
|
11
|
+
* directly — never the public API — so no secondary lease acquisition can
|
|
12
|
+
* occur during the callback.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createTransaction: (deps: {
|
|
15
|
+
scheduler: Scheduler<PoolWorker>;
|
|
16
|
+
afterWrite: (worker: PoolWorker) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Called when a connection may still hold an open transaction. The worker
|
|
19
|
+
* is lost rather than repaired: a "dirty worker" state is one more
|
|
20
|
+
* state the barrier would have to reason about, while a respawned
|
|
21
|
+
* connection is transaction-free by construction.
|
|
22
|
+
*/
|
|
23
|
+
onPoisoned: (index: number, error: SQLiteError) => void;
|
|
24
|
+
/**
|
|
25
|
+
* The client's bulk factory. Called per transaction with the transaction's
|
|
26
|
+
* own read/write and a pass-through `transaction`, so output()'s swap runs
|
|
27
|
+
* on the caller's transaction instead of opening a BEGIN SQLite does not
|
|
28
|
+
* allow.
|
|
29
|
+
*/
|
|
30
|
+
bulkFor: (target: {
|
|
31
|
+
read: ReadFn;
|
|
32
|
+
write: WriteFn;
|
|
33
|
+
transaction: TransactionFn;
|
|
34
|
+
}) => {
|
|
35
|
+
bulkWrite: SQLiteQueryAPI['bulkWrite'];
|
|
36
|
+
output: SQLiteQueryAPI['output'];
|
|
37
|
+
};
|
|
38
|
+
}) => <T = void>(callback: (db: SQLiteTransactionDB) => Promise<T>, options?: SQLiteTransactionOptions) => Promise<T>;
|