@orkestrel/sqlite 0.0.1 → 0.0.2
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 +8 -4
- package/dist/src/server/index.cjs +73 -19
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +316 -0
- package/dist/src/server/index.d.ts +316 -7
- package/dist/src/server/index.js +339 -0
- package/dist/src/server/index.js.map +1 -0
- package/package.json +13 -6
- package/dist/src/server/SQLiteDatabase.d.ts +0 -29
- package/dist/src/server/SQLiteStatement.d.ts +0 -23
- package/dist/src/server/constants.d.ts +0 -18
- package/dist/src/server/errors.d.ts +0 -34
- package/dist/src/server/factories.d.ts +0 -25
- package/dist/src/server/helpers.d.ts +0 -38
- package/dist/src/server/types.d.ts +0 -101
package/README.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# @orkestrel/sqlite
|
|
2
2
|
|
|
3
|
-
A typed, synchronous SQLite wrapper for the `@orkestrel` line — a thin
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
A typed, synchronous SQLite wrapper for the `@orkestrel` line — a thin skin
|
|
4
|
+
over Node's built-in `node:sqlite` (`DatabaseSync` / `StatementSync`) giving
|
|
5
|
+
prepared statements, transactions, and pragmas, with a single runtime
|
|
6
|
+
dependency: `@orkestrel/contract`, used for its boundary narrowing.
|
|
7
|
+
|
|
8
|
+
`node:sqlite` is experimental on current Node versions — importing this
|
|
9
|
+
package emits an `ExperimentalWarning` at runtime until Node stabilizes the
|
|
10
|
+
module.
|
|
7
11
|
|
|
8
12
|
## Install
|
|
9
13
|
|
|
@@ -112,21 +112,32 @@ function bindParameters(parameters) {
|
|
|
112
112
|
* wrapper runs SQL.
|
|
113
113
|
*
|
|
114
114
|
* @remarks
|
|
115
|
-
* Created by `database.prepare(sql)
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
115
|
+
* Created by `database.prepare(sql)`, which threads a liveness check (internal —
|
|
116
|
+
* this constructor's second parameter is not part of the documented surface).
|
|
117
|
+
* Bare named parameters are enabled on construction so a record's keys bind
|
|
118
|
+
* without the SQL prefix character. Each method first gates on that liveness
|
|
119
|
+
* check, throwing a `CLOSED` `SQLiteError` once the owning connection has been
|
|
120
|
+
* closed — a statement prepared on a connection that is later closed and then
|
|
121
|
+
* reconnected stays `CLOSED`; a fresh statement must be prepared on the new
|
|
122
|
+
* connection. Each method then binds the optional parameters (an array spread
|
|
123
|
+
* to `?` placeholders, a record passed as a single named object) and executes
|
|
124
|
+
* synchronously, mapping any native fault to a `SQLiteError` — including a
|
|
125
|
+
* mid-stream fault from `iterate`'s lazy native iterator, stepped inside its own
|
|
126
|
+
* try/catch so a fault on a later row is mapped exactly like an eager one. Row
|
|
127
|
+
* values arrive as the native {@link SQLiteRow} types; the typed layer above
|
|
128
|
+
* (the driver) imposes a precise shape through a contract rather than
|
|
129
|
+
* re-narrowing here (AGENTS §14).
|
|
122
130
|
*/
|
|
123
131
|
var SQLiteStatement = class {
|
|
124
132
|
#statement;
|
|
125
|
-
|
|
133
|
+
#closed;
|
|
134
|
+
constructor(statement, closed) {
|
|
126
135
|
this.#statement = statement;
|
|
136
|
+
this.#closed = closed;
|
|
127
137
|
this.#statement.setAllowBareNamedParameters(true);
|
|
128
138
|
}
|
|
129
139
|
run(parameters) {
|
|
140
|
+
this.#require();
|
|
130
141
|
try {
|
|
131
142
|
const bound = bindParameters(parameters);
|
|
132
143
|
const result = "named" in bound ? this.#statement.run(bound.named) : this.#statement.run(...bound.positional);
|
|
@@ -139,6 +150,7 @@ var SQLiteStatement = class {
|
|
|
139
150
|
}
|
|
140
151
|
}
|
|
141
152
|
get(parameters) {
|
|
153
|
+
this.#require();
|
|
142
154
|
try {
|
|
143
155
|
const bound = bindParameters(parameters);
|
|
144
156
|
return "named" in bound ? this.#statement.get(bound.named) : this.#statement.get(...bound.positional);
|
|
@@ -147,6 +159,7 @@ var SQLiteStatement = class {
|
|
|
147
159
|
}
|
|
148
160
|
}
|
|
149
161
|
all(parameters) {
|
|
162
|
+
this.#require();
|
|
150
163
|
try {
|
|
151
164
|
const bound = bindParameters(parameters);
|
|
152
165
|
return "named" in bound ? this.#statement.all(bound.named) : this.#statement.all(...bound.positional);
|
|
@@ -155,12 +168,30 @@ var SQLiteStatement = class {
|
|
|
155
168
|
}
|
|
156
169
|
}
|
|
157
170
|
iterate(parameters) {
|
|
171
|
+
this.#require();
|
|
172
|
+
let native;
|
|
158
173
|
try {
|
|
159
174
|
const bound = bindParameters(parameters);
|
|
160
|
-
|
|
175
|
+
native = "named" in bound ? this.#statement.iterate(bound.named) : this.#statement.iterate(...bound.positional);
|
|
161
176
|
} catch (error) {
|
|
162
177
|
throw wrapError(error);
|
|
163
178
|
}
|
|
179
|
+
return this.#stream(native);
|
|
180
|
+
}
|
|
181
|
+
#require() {
|
|
182
|
+
if (this.#closed()) throw new SQLiteError("CLOSED", "Statement is closed — its connection was closed; prepare a new statement");
|
|
183
|
+
}
|
|
184
|
+
*#stream(native) {
|
|
185
|
+
for (;;) {
|
|
186
|
+
let result;
|
|
187
|
+
try {
|
|
188
|
+
result = native.next();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
throw wrapError(error);
|
|
191
|
+
}
|
|
192
|
+
if (result.done === true) return;
|
|
193
|
+
yield result.value;
|
|
194
|
+
}
|
|
164
195
|
}
|
|
165
196
|
};
|
|
166
197
|
//#endregion
|
|
@@ -174,10 +205,13 @@ var SQLiteStatement = class {
|
|
|
174
205
|
* private gate that throws a `CLOSED` `SQLiteError` before `connect` or after
|
|
175
206
|
* `close`. `exec` runs result-less SQL; `prepare` compiles a `SQLiteStatement`;
|
|
176
207
|
* `transaction` wraps a scope in `BEGIN` / `COMMIT`, rolling back on a throw;
|
|
177
|
-
* `
|
|
208
|
+
* `begin` / `commit` / `rollback` expose those same primitives directly for a
|
|
209
|
+
* long-lived or externally-driven transaction; `pragma` reads (or sets then
|
|
210
|
+
* reads) one PRAGMA value — `name` is trusted
|
|
178
211
|
* internal use only, never untrusted input, since pragma names cannot be bound
|
|
179
|
-
* as parameters.
|
|
180
|
-
*
|
|
212
|
+
* as parameters. `transacting` reports whether a transaction is currently open
|
|
213
|
+
* (node:sqlite's `isTransaction`), `false` when not connected. A native fault
|
|
214
|
+
* surfaces as a `SQLiteError` mapped at the boundary.
|
|
181
215
|
*/
|
|
182
216
|
var SQLiteDatabase = class {
|
|
183
217
|
#path;
|
|
@@ -199,6 +233,9 @@ var SQLiteDatabase = class {
|
|
|
199
233
|
get connected() {
|
|
200
234
|
return this.#database !== void 0;
|
|
201
235
|
}
|
|
236
|
+
get transacting() {
|
|
237
|
+
return this.#database?.isTransaction ?? false;
|
|
238
|
+
}
|
|
202
239
|
connect() {
|
|
203
240
|
if (this.#database === void 0) this.#database = new node_sqlite.DatabaseSync(this.#path, {
|
|
204
241
|
readOnly: this.#readonly,
|
|
@@ -224,23 +261,40 @@ var SQLiteDatabase = class {
|
|
|
224
261
|
}
|
|
225
262
|
prepare(sql) {
|
|
226
263
|
try {
|
|
227
|
-
|
|
264
|
+
const database = this.#require();
|
|
265
|
+
return new SQLiteStatement(database.prepare(sql), () => this.#database !== database);
|
|
228
266
|
} catch (error) {
|
|
229
267
|
throw wrapError(error);
|
|
230
268
|
}
|
|
231
269
|
}
|
|
232
270
|
transaction(scope) {
|
|
233
|
-
this.
|
|
271
|
+
this.begin();
|
|
272
|
+
let result;
|
|
234
273
|
try {
|
|
235
|
-
|
|
236
|
-
this.exec("COMMIT");
|
|
237
|
-
return result;
|
|
274
|
+
result = scope();
|
|
238
275
|
} catch (error) {
|
|
239
276
|
try {
|
|
240
|
-
this.
|
|
277
|
+
this.rollback();
|
|
241
278
|
} catch {}
|
|
242
279
|
throw error;
|
|
243
280
|
}
|
|
281
|
+
if ((0, _orkestrel_contract.isPromiseLike)(result)) {
|
|
282
|
+
try {
|
|
283
|
+
this.rollback();
|
|
284
|
+
} catch {}
|
|
285
|
+
throw new SQLiteError("UNKNOWN", "transaction(scope) requires a synchronous scope — an async or Promise-returning scope cannot commit or roll back before its awaited work runs; use begin() / commit() / rollback() directly for a transaction that spans async caller code");
|
|
286
|
+
}
|
|
287
|
+
this.commit();
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
begin() {
|
|
291
|
+
this.exec("BEGIN");
|
|
292
|
+
}
|
|
293
|
+
commit() {
|
|
294
|
+
this.exec("COMMIT");
|
|
295
|
+
}
|
|
296
|
+
rollback() {
|
|
297
|
+
this.exec("ROLLBACK");
|
|
244
298
|
}
|
|
245
299
|
pragma(name, value) {
|
|
246
300
|
if (value !== void 0) this.exec("PRAGMA " + name + " = " + value);
|
|
@@ -268,7 +322,7 @@ var SQLiteDatabase = class {
|
|
|
268
322
|
*
|
|
269
323
|
* @example
|
|
270
324
|
* ```ts
|
|
271
|
-
* import { createSQLiteDatabase } from '@
|
|
325
|
+
* import { createSQLiteDatabase } from '@orkestrel/sqlite'
|
|
272
326
|
*
|
|
273
327
|
* const db = createSQLiteDatabase({ path: ':memory:' })
|
|
274
328
|
* db.connect()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#statement","#path","#readonly","#timeout","#foreignKeys","#bigints","#database","#require"],"sources":["../../../src/server/constants.ts","../../../src/server/errors.ts","../../../src/server/helpers.ts","../../../src/server/SQLiteStatement.ts","../../../src/server/SQLiteDatabase.ts","../../../src/server/factories.ts"],"sourcesContent":["// The wrapper's numeric SQLite result codes (AGENTS §5 constants file).\n\n/**\n * SQLite result code for a constraint violation (`SQLITE_CONSTRAINT`).\n *\n * @remarks\n * A native `errcode` packs the primary result in its low byte, with extended codes\n * in the high bits (e.g. `SQLITE_CONSTRAINT_UNIQUE`), so the low byte is masked off\n * before comparing. Read only by `wrapError`.\n */\nexport const SQLITE_CONSTRAINT = 19\n\n/**\n * SQLite result code for a locked-database fault (`SQLITE_BUSY`).\n *\n * @remarks\n * A native `errcode` packs the primary result in its low byte, with extended codes\n * in the high bits, so the low byte is masked off before comparing. Read only by\n * `wrapError`.\n */\nexport const SQLITE_BUSY = 5\n","import type { SQLiteErrorCode } from './types.js'\n\n// Errors for the SQLite wrapper. A single `SQLiteError` carries a\n// machine-readable `code` mapped from the native `node:sqlite` fault at the\n// boundary (`wrapError`), so a `catch` branches on `error.code` rather than\n// parsing a message. Mirrors the IndexedDB wrapper's `IndexedDBError`; its three\n// codes are deliberately lean — the wrapper sits right on the raw SQLite surface,\n// where the constraint fault is the one worth naming, `CLOSED` is the\n// wrapper-lifecycle fault, and everything else is `UNKNOWN` (AGENTS §12).\n\n/**\n * An error thrown by the SQLite wrapper.\n *\n * @remarks\n * Carries a {@link SQLiteErrorCode} and an optional `context` record (e.g. the\n * native SQLite `errcode`). Construct it directly for the `CLOSED`\n * wrapper-lifecycle fault; the internal `wrapError` maps a native `node:sqlite`\n * error to the right code at the boundary. Narrow a caught value with\n * {@link isSQLiteError}.\n *\n * @example\n * ```ts\n * try {\n * \tstatement.run({ id: 'u1' })\n * } catch (error) {\n * \tif (isSQLiteError(error) && error.code === 'CONSTRAINT') {\n * \t\t// a UNIQUE / PRIMARY KEY violation\n * \t}\n * }\n * ```\n */\nexport class SQLiteError extends Error {\n\treadonly code: SQLiteErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: SQLiteErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'SQLiteError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/**\n * Whether a value is a {@link SQLiteError}.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `SQLiteError`\n */\nexport function isSQLiteError(value: unknown): value is SQLiteError {\n\treturn value instanceof SQLiteError\n}\n","import type { SQLiteParameters, SQLiteValue } from './types.js'\nimport { isArray, isObject } from '@orkestrel/contract'\nimport { SQLITE_BUSY, SQLITE_CONSTRAINT } from './constants.js'\nimport { SQLiteError } from './errors.js'\n\n// The wrapper's boundary helpers, shared by `SQLiteDatabase` and `SQLiteStatement`:\n// `wrapError` maps a thrown native `node:sqlite` error to a typed `SQLiteError`\n// (the one honest narrowing point — `isObject` + `in`, never `as`, per AGENTS\n// §14), and `bindParameters` normalizes the wrapper's `SQLiteParameters` to the\n// shape a native `StatementSync` call expects (positional spread vs. a single\n// named record). Both are pure.\n\n/**\n * Convert a thrown native `node:sqlite` error into a typed {@link SQLiteError}.\n *\n * @remarks\n * The single boundary mapping for the wrapper. The thrown value arrives as\n * `unknown` and is narrowed with `isObject` + the `in` operator (never `as`, and\n * not `isRecord` — a native `node:sqlite` error is an `Error` instance, so its\n * prototype fails the plain-record test) to read its `errcode` — a numeric SQLite\n * result code whose low byte identifies a constraint violation\n * (`SQLITE_CONSTRAINT`), mapped to code `'CONSTRAINT'`; a locked-database fault\n * (`SQLITE_BUSY`) is mapped to `'BUSY'`; anything else is `'UNKNOWN'`. The\n * original message is preserved and `{ errcode }` carried in `context` when\n * present. An already-typed `SQLiteError` is returned unchanged.\n *\n * @param error - The thrown value to convert\n * @returns The equivalent `SQLiteError`\n */\nexport function wrapError(error: unknown): SQLiteError {\n\tif (error instanceof SQLiteError) return error\n\tconst errcode =\n\t\tisObject(error) && 'errcode' in error && typeof error.errcode === 'number'\n\t\t\t? error.errcode\n\t\t\t: undefined\n\tconst message = error instanceof Error ? error.message : 'Unknown SQLite error'\n\tconst code =\n\t\terrcode !== undefined && (errcode & 0xff) === SQLITE_CONSTRAINT\n\t\t\t? 'CONSTRAINT'\n\t\t\t: errcode !== undefined && (errcode & 0xff) === SQLITE_BUSY\n\t\t\t\t? 'BUSY'\n\t\t\t\t: 'UNKNOWN'\n\treturn new SQLiteError(code, message, errcode !== undefined ? { errcode } : undefined)\n}\n\n/**\n * Normalize {@link SQLiteParameters} to the binding shape a native `StatementSync`\n * call expects.\n *\n * @remarks\n * Positional parameters (an array) bind to `?` placeholders and are spread into\n * the call; named parameters (a record) bind to bare `:name` placeholders and are\n * passed as a single leading object. Returning a discriminated result keeps the\n * `SQLiteStatement` dispatch typed against the native overloads without `as`.\n *\n * @param parameters - The wrapper parameters, or `undefined` for none\n * @returns `{ positional }` for an array (empty when omitted) or `{ named }` for a record\n */\nexport function bindParameters(\n\tparameters?: SQLiteParameters,\n):\n\t| { readonly positional: readonly SQLiteValue[] }\n\t| { readonly named: Readonly<Record<string, SQLiteValue>> } {\n\tif (parameters === undefined) return { positional: [] }\n\tif (isArray<SQLiteValue>(parameters)) return { positional: parameters }\n\treturn { named: parameters }\n}\n","import type { StatementSync } from 'node:sqlite'\nimport type {\n\tSQLiteParameters,\n\tSQLiteRow,\n\tSQLiteRunResult,\n\tSQLiteStatementInterface,\n} from './types.js'\nimport { bindParameters, wrapError } from './helpers.js'\n\n/**\n * A prepared statement over `node:sqlite`'s `StatementSync` — the only way the\n * wrapper runs SQL.\n *\n * @remarks\n * Created by `database.prepare(sql)`. Bare named parameters are enabled on\n * construction so a record's keys bind without the SQL prefix character. Each\n * method binds the optional parameters (an array spread to `?` placeholders, a\n * record passed as a single named object) and executes synchronously, mapping any\n * native fault to a `SQLiteError`. Row values arrive as the native\n * {@link SQLiteRow} types; the typed layer above (the driver) imposes a precise\n * shape through a contract rather than re-narrowing here (AGENTS §14).\n */\nexport class SQLiteStatement implements SQLiteStatementInterface {\n\treadonly #statement: StatementSync\n\n\tconstructor(statement: StatementSync) {\n\t\tthis.#statement = statement\n\t\tthis.#statement.setAllowBareNamedParameters(true)\n\t}\n\n\trun(parameters?: SQLiteParameters): SQLiteRunResult {\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\tconst result =\n\t\t\t\t'named' in bound\n\t\t\t\t\t? this.#statement.run(bound.named)\n\t\t\t\t\t: this.#statement.run(...bound.positional)\n\t\t\treturn { changes: Number(result.changes), rowid: Number(result.lastInsertRowid) }\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tget(parameters?: SQLiteParameters): SQLiteRow | undefined {\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\treturn 'named' in bound\n\t\t\t\t? this.#statement.get(bound.named)\n\t\t\t\t: this.#statement.get(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tall(parameters?: SQLiteParameters): readonly SQLiteRow[] {\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\treturn 'named' in bound\n\t\t\t\t? this.#statement.all(bound.named)\n\t\t\t\t: this.#statement.all(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\titerate(parameters?: SQLiteParameters): IterableIterator<SQLiteRow> {\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\treturn 'named' in bound\n\t\t\t\t? this.#statement.iterate(bound.named)\n\t\t\t\t: this.#statement.iterate(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport type {\n\tSQLiteDatabaseInterface,\n\tSQLiteDatabaseOptions,\n\tSQLiteStatementInterface,\n\tSQLiteValue,\n} from './types.js'\nimport { SQLiteError } from './errors.js'\nimport { wrapError } from './helpers.js'\nimport { SQLiteStatement } from './SQLiteStatement.js'\n\n/**\n * A synchronous SQLite database over `node:sqlite`'s `DatabaseSync`.\n *\n * @remarks\n * Created by `createSQLiteDatabase`. It connects lazily (`connect` opens the\n * underlying `DatabaseSync`, idempotent) and every operation routes through a\n * private gate that throws a `CLOSED` `SQLiteError` before `connect` or after\n * `close`. `exec` runs result-less SQL; `prepare` compiles a `SQLiteStatement`;\n * `transaction` wraps a scope in `BEGIN` / `COMMIT`, rolling back on a throw;\n * `pragma` reads (or sets then reads) one PRAGMA value — `name` is trusted\n * internal use only, never untrusted input, since pragma names cannot be bound\n * as parameters. A native fault surfaces as a `SQLiteError` mapped at the\n * boundary.\n */\nexport class SQLiteDatabase implements SQLiteDatabaseInterface {\n\treadonly #path: string\n\treadonly #readonly: boolean | undefined\n\treadonly #timeout: number | undefined\n\treadonly #foreignKeys: boolean | undefined\n\treadonly #bigints: boolean | undefined\n\t#database: DatabaseSync | undefined\n\n\tconstructor(options: SQLiteDatabaseOptions) {\n\t\tthis.#path = options.path ?? ':memory:'\n\t\tthis.#readonly = options.readonly\n\t\tthis.#timeout = options.timeout\n\t\tthis.#foreignKeys = options.foreignKeys\n\t\tthis.#bigints = options.bigints\n\t}\n\n\tget path(): string {\n\t\treturn this.#path\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#database !== undefined\n\t}\n\n\tconnect(): void {\n\t\tif (this.#database === undefined) {\n\t\t\tthis.#database = new DatabaseSync(this.#path, {\n\t\t\t\treadOnly: this.#readonly,\n\t\t\t\ttimeout: this.#timeout,\n\t\t\t\tenableForeignKeyConstraints: this.#foreignKeys,\n\t\t\t\treadBigInts: this.#bigints,\n\t\t\t})\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.#database?.close()\n\t\tthis.#database = undefined\n\t}\n\n\t/** Close the connection — enables `using db = createSQLiteDatabase(...)`. */\n\t[Symbol.dispose](): void {\n\t\tthis.close()\n\t}\n\n\texec(sql: string): void {\n\t\ttry {\n\t\t\tthis.#require().exec(sql)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tprepare(sql: string): SQLiteStatementInterface {\n\t\ttry {\n\t\t\treturn new SQLiteStatement(this.#require().prepare(sql))\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\ttransaction<R>(scope: () => R): R {\n\t\tthis.exec('BEGIN')\n\t\ttry {\n\t\t\tconst result = scope()\n\t\t\tthis.exec('COMMIT')\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\t// A ROLLBACK fault (e.g. the database was closed by the scope) must never\n\t\t\t// mask the scope's own error — the caller needs to see why it failed.\n\t\t\ttry {\n\t\t\t\tthis.exec('ROLLBACK')\n\t\t\t} catch {\n\t\t\t\t// swallowed — the original `error` is what the caller needs to see\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t// Pragmas cannot use bind parameters, so the value is interpolated — safe for\n\t// the trusted internal names this layer is called with.\n\tpragma(name: string, value?: string | number): SQLiteValue | undefined {\n\t\tif (value !== undefined) this.exec('PRAGMA ' + name + ' = ' + value)\n\t\tconst row = this.prepare('PRAGMA ' + name).get()\n\t\treturn row ? Object.values(row)[0] : undefined\n\t}\n\n\t// The single connection gate — every operation requires an open database.\n\t#require(): DatabaseSync {\n\t\tif (this.#database === undefined) {\n\t\t\tthrow new SQLiteError('CLOSED', 'Database is not connected — call connect() first')\n\t\t}\n\t\treturn this.#database\n\t}\n}\n","import type { SQLiteDatabaseInterface, SQLiteDatabaseOptions } from './types.js'\nimport { SQLiteDatabase } from './SQLiteDatabase.js'\n\n/**\n * Create a synchronous SQLite database over `node:sqlite`.\n *\n * @remarks\n * The wrapper connects lazily — call `connect` (or it is required by the first\n * operation, which throws `CLOSED` until then). This is the lower-level native\n * handle a higher-level typed database engine would be built on; here it ships\n * as the standalone, server-native SQLite surface.\n *\n * @param options - The database `path` (a file path, or `':memory:'` by default)\n * @returns A typed {@link SQLiteDatabaseInterface}\n *\n * @example\n * ```ts\n * import { createSQLiteDatabase } from '@src/server'\n *\n * const db = createSQLiteDatabase({ path: ':memory:' })\n * db.connect()\n * db.exec('CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT)')\n * db.prepare('INSERT INTO users VALUES (?, ?)').run(['u1', 'Ada'])\n * db.prepare('SELECT name FROM users WHERE id = ?').get(['u1']) // { name: 'Ada' }\n * ```\n */\nexport function createSQLiteDatabase(options?: SQLiteDatabaseOptions): SQLiteDatabaseInterface {\n\treturn new SQLiteDatabase(options ?? {})\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["#statement","#closed","#require","#stream","#path","#readonly","#timeout","#foreignKeys","#bigints","#database","#require"],"sources":["../../../src/server/constants.ts","../../../src/server/errors.ts","../../../src/server/helpers.ts","../../../src/server/SQLiteStatement.ts","../../../src/server/SQLiteDatabase.ts","../../../src/server/factories.ts"],"sourcesContent":["// The wrapper's numeric SQLite result codes (AGENTS §5 constants file).\n\n/**\n * SQLite result code for a constraint violation (`SQLITE_CONSTRAINT`).\n *\n * @remarks\n * A native `errcode` packs the primary result in its low byte, with extended codes\n * in the high bits (e.g. `SQLITE_CONSTRAINT_UNIQUE`), so the low byte is masked off\n * before comparing. Read only by `wrapError`.\n */\nexport const SQLITE_CONSTRAINT = 19\n\n/**\n * SQLite result code for a locked-database fault (`SQLITE_BUSY`).\n *\n * @remarks\n * A native `errcode` packs the primary result in its low byte, with extended codes\n * in the high bits, so the low byte is masked off before comparing. Read only by\n * `wrapError`.\n */\nexport const SQLITE_BUSY = 5\n","import type { SQLiteErrorCode } from './types.js'\n\n// Errors for the SQLite wrapper. A single `SQLiteError` carries a\n// machine-readable `code` mapped from the native `node:sqlite` fault at the\n// boundary (`wrapError`), so a `catch` branches on `error.code` rather than\n// parsing a message. Mirrors the IndexedDB wrapper's `IndexedDBError`; its three\n// codes are deliberately lean — the wrapper sits right on the raw SQLite surface,\n// where the constraint fault is the one worth naming, `CLOSED` is the\n// wrapper-lifecycle fault, and everything else is `UNKNOWN` (AGENTS §12).\n\n/**\n * An error thrown by the SQLite wrapper.\n *\n * @remarks\n * Carries a {@link SQLiteErrorCode} and an optional `context` record (e.g. the\n * native SQLite `errcode`). Construct it directly for the `CLOSED`\n * wrapper-lifecycle fault; the internal `wrapError` maps a native `node:sqlite`\n * error to the right code at the boundary. Narrow a caught value with\n * {@link isSQLiteError}.\n *\n * @example\n * ```ts\n * try {\n * \tstatement.run({ id: 'u1' })\n * } catch (error) {\n * \tif (isSQLiteError(error) && error.code === 'CONSTRAINT') {\n * \t\t// a UNIQUE / PRIMARY KEY violation\n * \t}\n * }\n * ```\n */\nexport class SQLiteError extends Error {\n\treadonly code: SQLiteErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\tconstructor(code: SQLiteErrorCode, message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message)\n\t\tthis.name = 'SQLiteError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/**\n * Whether a value is a {@link SQLiteError}.\n *\n * @param value - The value to test\n * @returns `true` when `value` is a `SQLiteError`\n */\nexport function isSQLiteError(value: unknown): value is SQLiteError {\n\treturn value instanceof SQLiteError\n}\n","import type { SQLiteParameters, SQLiteValue } from './types.js'\nimport { isArray, isObject } from '@orkestrel/contract'\nimport { SQLITE_BUSY, SQLITE_CONSTRAINT } from './constants.js'\nimport { SQLiteError } from './errors.js'\n\n// The wrapper's boundary helpers, shared by `SQLiteDatabase` and `SQLiteStatement`:\n// `wrapError` maps a thrown native `node:sqlite` error to a typed `SQLiteError`\n// (the one honest narrowing point — `isObject` + `in`, never `as`, per AGENTS\n// §14), and `bindParameters` normalizes the wrapper's `SQLiteParameters` to the\n// shape a native `StatementSync` call expects (positional spread vs. a single\n// named record). Both are pure.\n\n/**\n * Convert a thrown native `node:sqlite` error into a typed {@link SQLiteError}.\n *\n * @remarks\n * The single boundary mapping for the wrapper. The thrown value arrives as\n * `unknown` and is narrowed with `isObject` + the `in` operator (never `as`, and\n * not `isRecord` — a native `node:sqlite` error is an `Error` instance, so its\n * prototype fails the plain-record test) to read its `errcode` — a numeric SQLite\n * result code whose low byte identifies a constraint violation\n * (`SQLITE_CONSTRAINT`), mapped to code `'CONSTRAINT'`; a locked-database fault\n * (`SQLITE_BUSY`) is mapped to `'BUSY'`; anything else is `'UNKNOWN'`. The\n * original message is preserved and `{ errcode }` carried in `context` when\n * present. An already-typed `SQLiteError` is returned unchanged.\n *\n * @param error - The thrown value to convert\n * @returns The equivalent `SQLiteError`\n */\nexport function wrapError(error: unknown): SQLiteError {\n\tif (error instanceof SQLiteError) return error\n\tconst errcode =\n\t\tisObject(error) && 'errcode' in error && typeof error.errcode === 'number'\n\t\t\t? error.errcode\n\t\t\t: undefined\n\tconst message = error instanceof Error ? error.message : 'Unknown SQLite error'\n\tconst code =\n\t\terrcode !== undefined && (errcode & 0xff) === SQLITE_CONSTRAINT\n\t\t\t? 'CONSTRAINT'\n\t\t\t: errcode !== undefined && (errcode & 0xff) === SQLITE_BUSY\n\t\t\t\t? 'BUSY'\n\t\t\t\t: 'UNKNOWN'\n\treturn new SQLiteError(code, message, errcode !== undefined ? { errcode } : undefined)\n}\n\n/**\n * Normalize {@link SQLiteParameters} to the binding shape a native `StatementSync`\n * call expects.\n *\n * @remarks\n * Positional parameters (an array) bind to `?` placeholders and are spread into\n * the call; named parameters (a record) bind to bare `:name` placeholders and are\n * passed as a single leading object. Returning a discriminated result keeps the\n * `SQLiteStatement` dispatch typed against the native overloads without `as`.\n *\n * @param parameters - The wrapper parameters, or `undefined` for none\n * @returns `{ positional }` for an array (empty when omitted) or `{ named }` for a record\n */\nexport function bindParameters(\n\tparameters?: SQLiteParameters,\n):\n\t| { readonly positional: readonly SQLiteValue[] }\n\t| { readonly named: Readonly<Record<string, SQLiteValue>> } {\n\tif (parameters === undefined) return { positional: [] }\n\tif (isArray<SQLiteValue>(parameters)) return { positional: parameters }\n\treturn { named: parameters }\n}\n","import type { StatementSync } from 'node:sqlite'\nimport type {\n\tSQLiteParameters,\n\tSQLiteRow,\n\tSQLiteRunResult,\n\tSQLiteStatementInterface,\n} from './types.js'\nimport { SQLiteError } from './errors.js'\nimport { bindParameters, wrapError } from './helpers.js'\n\n/**\n * A prepared statement over `node:sqlite`'s `StatementSync` — the only way the\n * wrapper runs SQL.\n *\n * @remarks\n * Created by `database.prepare(sql)`, which threads a liveness check (internal —\n * this constructor's second parameter is not part of the documented surface).\n * Bare named parameters are enabled on construction so a record's keys bind\n * without the SQL prefix character. Each method first gates on that liveness\n * check, throwing a `CLOSED` `SQLiteError` once the owning connection has been\n * closed — a statement prepared on a connection that is later closed and then\n * reconnected stays `CLOSED`; a fresh statement must be prepared on the new\n * connection. Each method then binds the optional parameters (an array spread\n * to `?` placeholders, a record passed as a single named object) and executes\n * synchronously, mapping any native fault to a `SQLiteError` — including a\n * mid-stream fault from `iterate`'s lazy native iterator, stepped inside its own\n * try/catch so a fault on a later row is mapped exactly like an eager one. Row\n * values arrive as the native {@link SQLiteRow} types; the typed layer above\n * (the driver) imposes a precise shape through a contract rather than\n * re-narrowing here (AGENTS §14).\n */\nexport class SQLiteStatement implements SQLiteStatementInterface {\n\treadonly #statement: StatementSync\n\treadonly #closed: () => boolean\n\n\tconstructor(statement: StatementSync, closed: () => boolean) {\n\t\tthis.#statement = statement\n\t\tthis.#closed = closed\n\t\tthis.#statement.setAllowBareNamedParameters(true)\n\t}\n\n\trun(parameters?: SQLiteParameters): SQLiteRunResult {\n\t\tthis.#require()\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\tconst result =\n\t\t\t\t'named' in bound\n\t\t\t\t\t? this.#statement.run(bound.named)\n\t\t\t\t\t: this.#statement.run(...bound.positional)\n\t\t\treturn { changes: Number(result.changes), rowid: Number(result.lastInsertRowid) }\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tget(parameters?: SQLiteParameters): SQLiteRow | undefined {\n\t\tthis.#require()\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\treturn 'named' in bound\n\t\t\t\t? this.#statement.get(bound.named)\n\t\t\t\t: this.#statement.get(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tall(parameters?: SQLiteParameters): readonly SQLiteRow[] {\n\t\tthis.#require()\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\treturn 'named' in bound\n\t\t\t\t? this.#statement.all(bound.named)\n\t\t\t\t: this.#statement.all(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\titerate(parameters?: SQLiteParameters): IterableIterator<SQLiteRow> {\n\t\tthis.#require()\n\t\tlet native: IterableIterator<SQLiteRow>\n\t\ttry {\n\t\t\tconst bound = bindParameters(parameters)\n\t\t\tnative =\n\t\t\t\t'named' in bound\n\t\t\t\t\t? this.#statement.iterate(bound.named)\n\t\t\t\t\t: this.#statement.iterate(...bound.positional)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t\treturn this.#stream(native)\n\t}\n\n\t// The single liveness gate — every operation requires the owning connection\n\t// to still be open (mirrors SQLiteDatabase#require).\n\t#require(): void {\n\t\tif (this.#closed()) {\n\t\t\tthrow new SQLiteError(\n\t\t\t\t'CLOSED',\n\t\t\t\t'Statement is closed — its connection was closed; prepare a new statement',\n\t\t\t)\n\t\t}\n\t}\n\n\t// Pulls from the native lazy iterator one step at a time, each step inside\n\t// its own try/catch — a mid-stream native fault (e.g. an out-of-range\n\t// integer on a later row) is mapped to a SQLiteError exactly like an eager\n\t// fault, instead of escaping raw from the caller's for...of.\n\t*#stream(native: IterableIterator<SQLiteRow>): IterableIterator<SQLiteRow> {\n\t\tfor (;;) {\n\t\t\tlet result: IteratorResult<SQLiteRow>\n\t\t\ttry {\n\t\t\t\tresult = native.next()\n\t\t\t} catch (error) {\n\t\t\t\tthrow wrapError(error)\n\t\t\t}\n\t\t\tif (result.done === true) return\n\t\t\tyield result.value\n\t\t}\n\t}\n}\n","import { DatabaseSync } from 'node:sqlite'\nimport type {\n\tSQLiteDatabaseInterface,\n\tSQLiteDatabaseOptions,\n\tSQLiteStatementInterface,\n\tSQLiteValue,\n} from './types.js'\nimport { isPromiseLike } from '@orkestrel/contract'\nimport { SQLiteError } from './errors.js'\nimport { wrapError } from './helpers.js'\nimport { SQLiteStatement } from './SQLiteStatement.js'\n\n/**\n * A synchronous SQLite database over `node:sqlite`'s `DatabaseSync`.\n *\n * @remarks\n * Created by `createSQLiteDatabase`. It connects lazily (`connect` opens the\n * underlying `DatabaseSync`, idempotent) and every operation routes through a\n * private gate that throws a `CLOSED` `SQLiteError` before `connect` or after\n * `close`. `exec` runs result-less SQL; `prepare` compiles a `SQLiteStatement`;\n * `transaction` wraps a scope in `BEGIN` / `COMMIT`, rolling back on a throw;\n * `begin` / `commit` / `rollback` expose those same primitives directly for a\n * long-lived or externally-driven transaction; `pragma` reads (or sets then\n * reads) one PRAGMA value — `name` is trusted\n * internal use only, never untrusted input, since pragma names cannot be bound\n * as parameters. `transacting` reports whether a transaction is currently open\n * (node:sqlite's `isTransaction`), `false` when not connected. A native fault\n * surfaces as a `SQLiteError` mapped at the boundary.\n */\nexport class SQLiteDatabase implements SQLiteDatabaseInterface {\n\treadonly #path: string\n\treadonly #readonly: boolean | undefined\n\treadonly #timeout: number | undefined\n\treadonly #foreignKeys: boolean | undefined\n\treadonly #bigints: boolean | undefined\n\t#database: DatabaseSync | undefined\n\n\tconstructor(options: SQLiteDatabaseOptions) {\n\t\tthis.#path = options.path ?? ':memory:'\n\t\tthis.#readonly = options.readonly\n\t\tthis.#timeout = options.timeout\n\t\tthis.#foreignKeys = options.foreignKeys\n\t\tthis.#bigints = options.bigints\n\t}\n\n\tget path(): string {\n\t\treturn this.#path\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#database !== undefined\n\t}\n\n\tget transacting(): boolean {\n\t\treturn this.#database?.isTransaction ?? false\n\t}\n\n\tconnect(): void {\n\t\tif (this.#database === undefined) {\n\t\t\tthis.#database = new DatabaseSync(this.#path, {\n\t\t\t\treadOnly: this.#readonly,\n\t\t\t\ttimeout: this.#timeout,\n\t\t\t\tenableForeignKeyConstraints: this.#foreignKeys,\n\t\t\t\treadBigInts: this.#bigints,\n\t\t\t})\n\t\t}\n\t}\n\n\tclose(): void {\n\t\tthis.#database?.close()\n\t\tthis.#database = undefined\n\t}\n\n\t/** Close the connection — enables `using db = createSQLiteDatabase(...)`. */\n\t[Symbol.dispose](): void {\n\t\tthis.close()\n\t}\n\n\texec(sql: string): void {\n\t\ttry {\n\t\t\tthis.#require().exec(sql)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\tprepare(sql: string): SQLiteStatementInterface {\n\t\ttry {\n\t\t\tconst database = this.#require()\n\t\t\t// Capture this specific connection instance — a later `close()` clears\n\t\t\t// `#database`, and a subsequent `connect()` creates a NEW instance, so\n\t\t\t// identity comparison (not just \"is a connection open\") keeps a statement\n\t\t\t// prepared on the OLD connection permanently CLOSED after reconnect.\n\t\t\treturn new SQLiteStatement(database.prepare(sql), () => this.#database !== database)\n\t\t} catch (error) {\n\t\t\tthrow wrapError(error)\n\t\t}\n\t}\n\n\ttransaction<R>(scope: () => R): R {\n\t\tthis.begin()\n\t\tlet result: R\n\t\ttry {\n\t\t\tresult = scope()\n\t\t} catch (error) {\n\t\t\t// A ROLLBACK fault (e.g. the database was closed by the scope) must never\n\t\t\t// mask the scope's own error — the caller needs to see why it failed.\n\t\t\ttry {\n\t\t\t\tthis.rollback()\n\t\t\t} catch {\n\t\t\t\t// swallowed — the original `error` is what the caller needs to see\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t\tif (isPromiseLike(result)) {\n\t\t\t// An async scope returns before its awaited work runs, so committing here\n\t\t\t// would commit prematurely — roll back and reject instead of silently\n\t\t\t// racing the caller's still-pending work.\n\t\t\ttry {\n\t\t\t\tthis.rollback()\n\t\t\t} catch {\n\t\t\t\t// swallowed — the guard error below is what the caller needs to see\n\t\t\t}\n\t\t\tthrow new SQLiteError(\n\t\t\t\t'UNKNOWN',\n\t\t\t\t'transaction(scope) requires a synchronous scope — an async or Promise-returning scope cannot commit or roll back before its awaited work runs; use begin() / commit() / rollback() directly for a transaction that spans async caller code',\n\t\t\t)\n\t\t}\n\t\tthis.commit()\n\t\treturn result\n\t}\n\n\tbegin(): void {\n\t\tthis.exec('BEGIN')\n\t}\n\n\tcommit(): void {\n\t\tthis.exec('COMMIT')\n\t}\n\n\trollback(): void {\n\t\tthis.exec('ROLLBACK')\n\t}\n\n\t// Pragmas cannot use bind parameters, so the value is interpolated — safe for\n\t// the trusted internal names this layer is called with.\n\tpragma(name: string, value?: string | number): SQLiteValue | undefined {\n\t\tif (value !== undefined) this.exec('PRAGMA ' + name + ' = ' + value)\n\t\tconst row = this.prepare('PRAGMA ' + name).get()\n\t\treturn row ? Object.values(row)[0] : undefined\n\t}\n\n\t// The single connection gate — every operation requires an open database.\n\t#require(): DatabaseSync {\n\t\tif (this.#database === undefined) {\n\t\t\tthrow new SQLiteError('CLOSED', 'Database is not connected — call connect() first')\n\t\t}\n\t\treturn this.#database\n\t}\n}\n","import type { SQLiteDatabaseInterface, SQLiteDatabaseOptions } from './types.js'\nimport { SQLiteDatabase } from './SQLiteDatabase.js'\n\n/**\n * Create a synchronous SQLite database over `node:sqlite`.\n *\n * @remarks\n * The wrapper connects lazily — call `connect` (or it is required by the first\n * operation, which throws `CLOSED` until then). This is the lower-level native\n * handle a higher-level typed database engine would be built on; here it ships\n * as the standalone, server-native SQLite surface.\n *\n * @param options - The database `path` (a file path, or `':memory:'` by default)\n * @returns A typed {@link SQLiteDatabaseInterface}\n *\n * @example\n * ```ts\n * import { createSQLiteDatabase } from '@orkestrel/sqlite'\n *\n * const db = createSQLiteDatabase({ path: ':memory:' })\n * db.connect()\n * db.exec('CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT)')\n * db.prepare('INSERT INTO users VALUES (?, ?)').run(['u1', 'Ada'])\n * db.prepare('SELECT name FROM users WHERE id = ?').get(['u1']) // { name: 'Ada' }\n * ```\n */\nexport function createSQLiteDatabase(options?: SQLiteDatabaseOptions): SQLiteDatabaseInterface {\n\treturn new SQLiteDatabase(options ?? {})\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { StatementSync } from 'node:sqlite';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalize {@link SQLiteParameters} to the binding shape a native `StatementSync`
|
|
5
|
+
* call expects.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Positional parameters (an array) bind to `?` placeholders and are spread into
|
|
9
|
+
* the call; named parameters (a record) bind to bare `:name` placeholders and are
|
|
10
|
+
* passed as a single leading object. Returning a discriminated result keeps the
|
|
11
|
+
* `SQLiteStatement` dispatch typed against the native overloads without `as`.
|
|
12
|
+
*
|
|
13
|
+
* @param parameters - The wrapper parameters, or `undefined` for none
|
|
14
|
+
* @returns `{ positional }` for an array (empty when omitted) or `{ named }` for a record
|
|
15
|
+
*/
|
|
16
|
+
export declare function bindParameters(parameters?: SQLiteParameters): {
|
|
17
|
+
readonly positional: readonly SQLiteValue[];
|
|
18
|
+
} | {
|
|
19
|
+
readonly named: Readonly<Record<string, SQLiteValue>>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a synchronous SQLite database over `node:sqlite`.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* The wrapper connects lazily — call `connect` (or it is required by the first
|
|
27
|
+
* operation, which throws `CLOSED` until then). This is the lower-level native
|
|
28
|
+
* handle a higher-level typed database engine would be built on; here it ships
|
|
29
|
+
* as the standalone, server-native SQLite surface.
|
|
30
|
+
*
|
|
31
|
+
* @param options - The database `path` (a file path, or `':memory:'` by default)
|
|
32
|
+
* @returns A typed {@link SQLiteDatabaseInterface}
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { createSQLiteDatabase } from '@orkestrel/sqlite'
|
|
37
|
+
*
|
|
38
|
+
* const db = createSQLiteDatabase({ path: ':memory:' })
|
|
39
|
+
* db.connect()
|
|
40
|
+
* db.exec('CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT)')
|
|
41
|
+
* db.prepare('INSERT INTO users VALUES (?, ?)').run(['u1', 'Ada'])
|
|
42
|
+
* db.prepare('SELECT name FROM users WHERE id = ?').get(['u1']) // { name: 'Ada' }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function createSQLiteDatabase(options?: SQLiteDatabaseOptions): SQLiteDatabaseInterface;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether a value is a {@link SQLiteError}.
|
|
49
|
+
*
|
|
50
|
+
* @param value - The value to test
|
|
51
|
+
* @returns `true` when `value` is a `SQLiteError`
|
|
52
|
+
*/
|
|
53
|
+
export declare function isSQLiteError(value: unknown): value is SQLiteError;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* SQLite result code for a locked-database fault (`SQLITE_BUSY`).
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* A native `errcode` packs the primary result in its low byte, with extended codes
|
|
60
|
+
* in the high bits, so the low byte is masked off before comparing. Read only by
|
|
61
|
+
* `wrapError`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SQLITE_BUSY = 5;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* SQLite result code for a constraint violation (`SQLITE_CONSTRAINT`).
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* A native `errcode` packs the primary result in its low byte, with extended codes
|
|
70
|
+
* in the high bits (e.g. `SQLITE_CONSTRAINT_UNIQUE`), so the low byte is masked off
|
|
71
|
+
* before comparing. Read only by `wrapError`.
|
|
72
|
+
*/
|
|
73
|
+
export declare const SQLITE_CONSTRAINT = 19;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A synchronous SQLite database over `node:sqlite`'s `DatabaseSync`.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* Created by `createSQLiteDatabase`. It connects lazily (`connect` opens the
|
|
80
|
+
* underlying `DatabaseSync`, idempotent) and every operation routes through a
|
|
81
|
+
* private gate that throws a `CLOSED` `SQLiteError` before `connect` or after
|
|
82
|
+
* `close`. `exec` runs result-less SQL; `prepare` compiles a `SQLiteStatement`;
|
|
83
|
+
* `transaction` wraps a scope in `BEGIN` / `COMMIT`, rolling back on a throw;
|
|
84
|
+
* `begin` / `commit` / `rollback` expose those same primitives directly for a
|
|
85
|
+
* long-lived or externally-driven transaction; `pragma` reads (or sets then
|
|
86
|
+
* reads) one PRAGMA value — `name` is trusted
|
|
87
|
+
* internal use only, never untrusted input, since pragma names cannot be bound
|
|
88
|
+
* as parameters. `transacting` reports whether a transaction is currently open
|
|
89
|
+
* (node:sqlite's `isTransaction`), `false` when not connected. A native fault
|
|
90
|
+
* surfaces as a `SQLiteError` mapped at the boundary.
|
|
91
|
+
*/
|
|
92
|
+
export declare class SQLiteDatabase implements SQLiteDatabaseInterface {
|
|
93
|
+
#private;
|
|
94
|
+
constructor(options: SQLiteDatabaseOptions);
|
|
95
|
+
get path(): string;
|
|
96
|
+
get connected(): boolean;
|
|
97
|
+
get transacting(): boolean;
|
|
98
|
+
connect(): void;
|
|
99
|
+
close(): void;
|
|
100
|
+
/** Close the connection — enables `using db = createSQLiteDatabase(...)`. */
|
|
101
|
+
[Symbol.dispose](): void;
|
|
102
|
+
exec(sql: string): void;
|
|
103
|
+
prepare(sql: string): SQLiteStatementInterface;
|
|
104
|
+
transaction<R>(scope: () => R): R;
|
|
105
|
+
begin(): void;
|
|
106
|
+
commit(): void;
|
|
107
|
+
rollback(): void;
|
|
108
|
+
pragma(name: string, value?: string | number): SQLiteValue | undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A synchronous SQLite database over `node:sqlite`'s `DatabaseSync` — a lean,
|
|
113
|
+
* typed, zero-dependency layer exposing prepared statements, transactions, and
|
|
114
|
+
* pragmas. Synchronous because `node:sqlite` is; the SQLite *driver* (Chunk 3)
|
|
115
|
+
* adapts it to the async `DriverInterface`.
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* Connects lazily — `connect` opens the underlying `DatabaseSync` (idempotent),
|
|
119
|
+
* and every operation requires an open connection, throwing a `CLOSED`
|
|
120
|
+
* {@link SQLiteError} before `connect` or after `close`. `exec` runs SQL with no
|
|
121
|
+
* results (DDL, pragmas); `prepare` compiles a {@link SQLiteStatementInterface};
|
|
122
|
+
* `transaction` runs a scope between `BEGIN` and `COMMIT`, rolling back on a
|
|
123
|
+
* throw; `pragma` reads (or sets then reads) a single PRAGMA value — `name` is
|
|
124
|
+
* trusted internal use only, never untrusted input, since pragma names cannot
|
|
125
|
+
* be bound as parameters. `transacting` reports whether a transaction is
|
|
126
|
+
* currently open on this connection — node:sqlite's `isTransaction` (wraps
|
|
127
|
+
* `sqlite3_get_autocommit()`); `false` when not connected. `begin` / `commit` /
|
|
128
|
+
* `rollback` are the same `BEGIN` / `COMMIT` / `ROLLBACK` primitives
|
|
129
|
+
* `transaction` composes internally, exposed directly for a long-lived or
|
|
130
|
+
* externally-driven transaction that a single synchronous scope cannot
|
|
131
|
+
* express — `transaction(scope)` remains the right tool whenever the whole
|
|
132
|
+
* transaction fits in one synchronous scope. `[Symbol.dispose]` closes the
|
|
133
|
+
* connection (same as `close`), enabling `using` to release it
|
|
134
|
+
* deterministically at the end of a block.
|
|
135
|
+
*/
|
|
136
|
+
export declare interface SQLiteDatabaseInterface {
|
|
137
|
+
readonly path: string;
|
|
138
|
+
readonly connected: boolean;
|
|
139
|
+
readonly transacting: boolean;
|
|
140
|
+
connect(): void;
|
|
141
|
+
close(): void;
|
|
142
|
+
exec(sql: string): void;
|
|
143
|
+
prepare(sql: string): SQLiteStatementInterface;
|
|
144
|
+
transaction<R>(scope: () => R): R;
|
|
145
|
+
/**
|
|
146
|
+
* Open a transaction (`BEGIN`). Throws the native fault — including a
|
|
147
|
+
* nested `BEGIN` while one is already open — as a {@link SQLiteError}; a
|
|
148
|
+
* caller composing its own transaction alongside others should branch on
|
|
149
|
+
* {@link SQLiteDatabaseInterface.transacting} first rather than catch this
|
|
150
|
+
* (see the Practices section in `guides/src/sqlite.md`).
|
|
151
|
+
*/
|
|
152
|
+
begin(): void;
|
|
153
|
+
/** Commit the currently open transaction (`COMMIT`); throws the native fault as a {@link SQLiteError} when none is open. */
|
|
154
|
+
commit(): void;
|
|
155
|
+
/** Roll back the currently open transaction (`ROLLBACK`); throws the native fault as a {@link SQLiteError} when none is open. */
|
|
156
|
+
rollback(): void;
|
|
157
|
+
pragma(name: string, value?: string | number): SQLiteValue | undefined;
|
|
158
|
+
[Symbol.dispose](): void;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Options for `createSQLiteDatabase`.
|
|
163
|
+
*
|
|
164
|
+
* @remarks
|
|
165
|
+
* `path` is the database file path, or the special name `':memory:'` for an
|
|
166
|
+
* in-memory database (the default when omitted). `readonly` opens the
|
|
167
|
+
* connection read-only (native `readOnly`) — an absent file fails to open
|
|
168
|
+
* rather than being created. `timeout` is the busy-timeout in milliseconds
|
|
169
|
+
* (native `timeout`) — how long SQLite retries a locked database before
|
|
170
|
+
* failing with a `BUSY` {@link SQLiteError}; defaults to `0` (fail
|
|
171
|
+
* immediately) when omitted. `foreignKeys` enables foreign-key constraint
|
|
172
|
+
* enforcement (native `enableForeignKeyConstraints`); `node:sqlite` defaults
|
|
173
|
+
* this to `true` when omitted. `bigints` reads `INTEGER` columns back as
|
|
174
|
+
* `bigint` (native `readBigInts`) — writes already accept `bigint` regardless
|
|
175
|
+
* of this option, so a stored integer beyond `Number.MAX_SAFE_INTEGER` throws
|
|
176
|
+
* on read unless `bigints` is enabled; enabling it returns EVERY integer
|
|
177
|
+
* column as `bigint`, not just out-of-range ones, closing that read/write
|
|
178
|
+
* asymmetry at the cost of `bigint` values for ordinary small integers too.
|
|
179
|
+
*/
|
|
180
|
+
export declare interface SQLiteDatabaseOptions {
|
|
181
|
+
readonly path?: string;
|
|
182
|
+
readonly readonly?: boolean;
|
|
183
|
+
readonly timeout?: number;
|
|
184
|
+
readonly foreignKeys?: boolean;
|
|
185
|
+
readonly bigints?: boolean;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* An error thrown by the SQLite wrapper.
|
|
190
|
+
*
|
|
191
|
+
* @remarks
|
|
192
|
+
* Carries a {@link SQLiteErrorCode} and an optional `context` record (e.g. the
|
|
193
|
+
* native SQLite `errcode`). Construct it directly for the `CLOSED`
|
|
194
|
+
* wrapper-lifecycle fault; the internal `wrapError` maps a native `node:sqlite`
|
|
195
|
+
* error to the right code at the boundary. Narrow a caught value with
|
|
196
|
+
* {@link isSQLiteError}.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* try {
|
|
201
|
+
* statement.run({ id: 'u1' })
|
|
202
|
+
* } catch (error) {
|
|
203
|
+
* if (isSQLiteError(error) && error.code === 'CONSTRAINT') {
|
|
204
|
+
* // a UNIQUE / PRIMARY KEY violation
|
|
205
|
+
* }
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export declare class SQLiteError extends Error {
|
|
210
|
+
readonly code: SQLiteErrorCode;
|
|
211
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
212
|
+
constructor(code: SQLiteErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* A machine-readable {@link SQLiteError} code.
|
|
217
|
+
*
|
|
218
|
+
* @remarks
|
|
219
|
+
* `'BUSY'` is retryable — it means a locked database was still held by another
|
|
220
|
+
* connection when the `timeout` (see {@link SQLiteDatabaseOptions}) elapsed; a
|
|
221
|
+
* caller may retry the operation, typically after backing off briefly.
|
|
222
|
+
*/
|
|
223
|
+
export declare type SQLiteErrorCode = 'CLOSED' | 'CONSTRAINT' | 'BUSY' | 'UNKNOWN';
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Bind parameters for a prepared statement — positional (an array, bound to `?`)
|
|
227
|
+
* or named (a record, bound to bare `:name` placeholders).
|
|
228
|
+
*/
|
|
229
|
+
export declare type SQLiteParameters = readonly SQLiteValue[] | Readonly<Record<string, SQLiteValue>>;
|
|
230
|
+
|
|
231
|
+
/** A result row — a record of column name to {@link SQLiteValue}. */
|
|
232
|
+
export declare type SQLiteRow = Record<string, SQLiteValue>;
|
|
233
|
+
|
|
234
|
+
/** The outcome of a non-query statement (`INSERT` / `UPDATE` / `DELETE` / DDL). */
|
|
235
|
+
export declare interface SQLiteRunResult {
|
|
236
|
+
readonly changes: number;
|
|
237
|
+
readonly rowid: number;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A prepared statement over `node:sqlite`'s `StatementSync` — the only way the
|
|
242
|
+
* wrapper runs SQL.
|
|
243
|
+
*
|
|
244
|
+
* @remarks
|
|
245
|
+
* Created by `database.prepare(sql)`, which threads a liveness check (internal —
|
|
246
|
+
* this constructor's second parameter is not part of the documented surface).
|
|
247
|
+
* Bare named parameters are enabled on construction so a record's keys bind
|
|
248
|
+
* without the SQL prefix character. Each method first gates on that liveness
|
|
249
|
+
* check, throwing a `CLOSED` `SQLiteError` once the owning connection has been
|
|
250
|
+
* closed — a statement prepared on a connection that is later closed and then
|
|
251
|
+
* reconnected stays `CLOSED`; a fresh statement must be prepared on the new
|
|
252
|
+
* connection. Each method then binds the optional parameters (an array spread
|
|
253
|
+
* to `?` placeholders, a record passed as a single named object) and executes
|
|
254
|
+
* synchronously, mapping any native fault to a `SQLiteError` — including a
|
|
255
|
+
* mid-stream fault from `iterate`'s lazy native iterator, stepped inside its own
|
|
256
|
+
* try/catch so a fault on a later row is mapped exactly like an eager one. Row
|
|
257
|
+
* values arrive as the native {@link SQLiteRow} types; the typed layer above
|
|
258
|
+
* (the driver) imposes a precise shape through a contract rather than
|
|
259
|
+
* re-narrowing here (AGENTS §14).
|
|
260
|
+
*/
|
|
261
|
+
export declare class SQLiteStatement implements SQLiteStatementInterface {
|
|
262
|
+
#private;
|
|
263
|
+
constructor(statement: StatementSync, closed: () => boolean);
|
|
264
|
+
run(parameters?: SQLiteParameters): SQLiteRunResult;
|
|
265
|
+
get(parameters?: SQLiteParameters): SQLiteRow | undefined;
|
|
266
|
+
all(parameters?: SQLiteParameters): readonly SQLiteRow[];
|
|
267
|
+
iterate(parameters?: SQLiteParameters): IterableIterator<SQLiteRow>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* A prepared statement — the only way the wrapper runs SQL (no query DSL; the
|
|
272
|
+
* core database layer owns querying, exactly as the IndexedDB wrapper does).
|
|
273
|
+
*
|
|
274
|
+
* @remarks
|
|
275
|
+
* Reached through `database.prepare(sql)`. Each method binds the optional
|
|
276
|
+
* `parameters` (an array spread to positional `?` placeholders, or a record bound
|
|
277
|
+
* to bare named placeholders) and executes synchronously: `run` for a non-query,
|
|
278
|
+
* `get` for the first row, `all` for every row, `iterate` for a lazy stream. A
|
|
279
|
+
* native fault surfaces as a {@link SQLiteError}.
|
|
280
|
+
*/
|
|
281
|
+
export declare interface SQLiteStatementInterface {
|
|
282
|
+
run(parameters?: SQLiteParameters): SQLiteRunResult;
|
|
283
|
+
get(parameters?: SQLiteParameters): SQLiteRow | undefined;
|
|
284
|
+
all(parameters?: SQLiteParameters): readonly SQLiteRow[];
|
|
285
|
+
iterate(parameters?: SQLiteParameters): IterableIterator<SQLiteRow>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* A value SQLite stores and returns natively — the SQL ↔ JS bridge.
|
|
290
|
+
*
|
|
291
|
+
* @remarks
|
|
292
|
+
* `node:sqlite` maps `NULL` / `INTEGER` / `REAL` / `TEXT` / `BLOB` to exactly
|
|
293
|
+
* these JS types (integers arrive as `number`, or `bigint` only past 2^53).
|
|
294
|
+
*/
|
|
295
|
+
export declare type SQLiteValue = null | number | bigint | string | Uint8Array;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Convert a thrown native `node:sqlite` error into a typed {@link SQLiteError}.
|
|
299
|
+
*
|
|
300
|
+
* @remarks
|
|
301
|
+
* The single boundary mapping for the wrapper. The thrown value arrives as
|
|
302
|
+
* `unknown` and is narrowed with `isObject` + the `in` operator (never `as`, and
|
|
303
|
+
* not `isRecord` — a native `node:sqlite` error is an `Error` instance, so its
|
|
304
|
+
* prototype fails the plain-record test) to read its `errcode` — a numeric SQLite
|
|
305
|
+
* result code whose low byte identifies a constraint violation
|
|
306
|
+
* (`SQLITE_CONSTRAINT`), mapped to code `'CONSTRAINT'`; a locked-database fault
|
|
307
|
+
* (`SQLITE_BUSY`) is mapped to `'BUSY'`; anything else is `'UNKNOWN'`. The
|
|
308
|
+
* original message is preserved and `{ errcode }` carried in `context` when
|
|
309
|
+
* present. An already-typed `SQLiteError` is returned unchanged.
|
|
310
|
+
*
|
|
311
|
+
* @param error - The thrown value to convert
|
|
312
|
+
* @returns The equivalent `SQLiteError`
|
|
313
|
+
*/
|
|
314
|
+
export declare function wrapError(error: unknown): SQLiteError;
|
|
315
|
+
|
|
316
|
+
export { }
|