sqlite3-compat 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tom Ryan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # sqlite3-compat
2
+
3
+ The [`sqlite3`](https://github.com/TryGhost/node-sqlite3) (node-sqlite3) callback API on Node's built-in [`node:sqlite`](https://nodejs.org/api/sqlite.html). No native addon, no `node-pre-gyp` download, no rebuild after a Node or Electron upgrade: the same `Database`, `Statement` and `Backup` classes, the same `serialize()`/`parallelize()` scheduling, `trace`/`profile` events, `cached.Database`, `verbose()` stack traces, error `code`/`errno` values and constants.
4
+
5
+ ```sh
6
+ npm install sqlite3-compat
7
+ ```
8
+
9
+ ```js
10
+ const sqlite3 = require('sqlite3-compat').verbose();
11
+ const db = new sqlite3.Database(':memory:');
12
+
13
+ db.serialize(() => {
14
+ db.run('CREATE TABLE lorem (info TEXT)');
15
+ const stmt = db.prepare('INSERT INTO lorem VALUES (?)');
16
+ for (let i = 0; i < 10; i++) stmt.run('Ipsum ' + i);
17
+ stmt.finalize();
18
+ db.each('SELECT rowid AS id, info FROM lorem', (err, row) => console.log(row.id + ': ' + row.info));
19
+ });
20
+ db.close();
21
+ ```
22
+
23
+ Requires Node.js 24.16 or later (Node 26 recommended). Deno and Bun do not provide `node:sqlite` with this surface.
24
+
25
+ ## Drop-in use
26
+
27
+ - **Direct**: `import sqlite3 from 'sqlite3-compat'` or `require('sqlite3-compat')`.
28
+ - **Alias** for libraries that `require('sqlite3')` internally (Sequelize, TypeORM, Knex, the `sqlite` wrapper, session stores, Node-RED nodes):
29
+
30
+ ```sh
31
+ npm install sqlite3@npm:sqlite3-compat
32
+ ```
33
+
34
+ Libraries that declare a peer range on `sqlite3` (TypeORM expects `^5.0.3`) see this package's own version through the alias; add an `overrides` entry or install with `--legacy-peer-deps` in that case (see [examples/alias.md](examples/alias.md)).
35
+
36
+ Verified with Sequelize 6.37, TypeORM 0.3.30, Knex 3.1 and `sqlite` 5.1 (see [docs/compatibility.md](docs/compatibility.md)). Electron 41 and later ship Node 24.18+, so Electron apps get the same API with no `electron-rebuild` step.
37
+
38
+ ## How compatible
39
+
40
+ node-sqlite3's own test suite runs unchanged in this repository's CI. On Node 24 and 26, **166 of its 171 tests** pass (node-sqlite3 6.0.1 itself passes 171 in the same environment). The five that do not need `sqlite3_update_hook` (`change` events, 3 tests), incremental backup steps (`step(pages)` copies everything, 1 test) and a backup from a file into an open connection (1 test); none of these has a `node:sqlite` primitive.
41
+
42
+ ## What is different
43
+
44
+ - Statements execute synchronously on the main thread (that is how `node:sqlite` works) and callbacks are delivered afterwards, in order. node-sqlite3 runs statements on libuv's thread pool. Short statements are faster here (no thread hop); a statement that runs for seconds blocks the event loop for that long.
45
+ - `serialize()`/`parallelize()` keep their API and ordering guarantees, but work is always executed in call order, never concurrently.
46
+ - `db.on('change', …)` never fires: `node:sqlite` has no update hook.
47
+ - `db.interrupt()` stops an `each()` between rows with `SQLITE_INTERRUPT`; it cannot stop a single long-running `get()`, `all()` or `run()`.
48
+ - `Backup#step(pages)` copies all remaining pages in one step (`step(0)` still reports `pageCount`/`remaining` without copying); `db.backup(file, source, dest, false)` (file into the open connection) is unavailable.
49
+ - A backup into a locked destination reports `SQLITE_BUSY` (the underlying `node:sqlite` error carries no code).
50
+ - `OPEN_SHAREDCACHE` is applied by adding `cache=shared` to the (URI) filename; `OPEN_FULLMUTEX`, `OPEN_PRIVATECACHE` and `OPEN_URI` are accepted (URI filenames always work).
51
+ - Integers beyond 2^53 come back as lossy JavaScript numbers, like node-sqlite3.
52
+
53
+ ## Performance
54
+
55
+ In-memory database, Node 24.21, one run each (`bench/perf.mjs` in the research notes): 200k `stmt.run()` inserts in one transaction 231 ms (node-sqlite3 2,063 ms); 20k sequential awaited `db.get()` 584 ms (542 ms); 20k `db.get()` issued at once 187 ms (434 ms); `all()` of 200k rows 154 ms (258 ms); `each()` over 100k rows 85 ms (82 ms); 20k `stmt.get()` on a reused statement 364 ms (228 ms); 20k `db.run()` updates 139 ms (469 ms). Statement-heavy workloads gain the most; single-row cursor reads on a reused statement are slower because each `get()` keeps a real cursor open.
56
+
57
+ ## API
58
+
59
+ Everything in node-sqlite3's [API reference](https://github.com/TryGhost/node-sqlite3/wiki/API) except the differences above: `new Database(filename[, mode][, callback])`, `close`, `run`, `get`, `all`, `each`, `map`, `exec`, `prepare`, `serialize`, `parallelize`, `configure('busyTimeout' | 'limit' | 'trace' | 'profile')`, `loadExtension`, `wait`, `interrupt`, `backup`, the `open`/`close`/`error`/`trace`/`profile` events; `Statement` `bind`, `reset`, `finalize`, `run`, `get`, `all`, `each`, `map` with `this.lastID`/`this.changes`; `Backup` `step`, `finish`, `idle`, `completed`, `failed`, `remaining`, `pageCount`, `retryErrors`; `sqlite3.cached.Database`, `sqlite3.verbose()`, the `OPEN_*`, result-code and `LIMIT_*` constants, `VERSION`, `VERSION_NUMBER` and `SOURCE_ID`. Errors carry `code` (`'SQLITE_ERROR'`) and `errno`, and read `SQLITE_ERROR: no such table: foo` like node-sqlite3's.
60
+
61
+ ## License
62
+
63
+ MIT. The parity suite under `test/parity/node-sqlite3` is node-sqlite3's test suite, BSD-3-Clause, Mapbox and Ghost Foundation; it is not part of the npm package.
package/dist/index.cjs ADDED
@@ -0,0 +1,725 @@
1
+ 'use strict';
2
+ // The node-sqlite3 (`sqlite3`) callback API implemented on node:sqlite. No native addon, no prebuilt binaries.
3
+ const {DatabaseSync, backup: nativeBackup} = require('node:sqlite');
4
+ const {EventEmitter} = require('node:events');
5
+ const {AsyncResource} = require('node:async_hooks');
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+ const util = require('node:util');
9
+
10
+ const constants = {
11
+ OPEN_READONLY: 1, OPEN_READWRITE: 2, OPEN_CREATE: 4, OPEN_URI: 0x40, OPEN_SHAREDCACHE: 0x20000, OPEN_PRIVATECACHE: 0x40000, OPEN_FULLMUTEX: 0x10000,
12
+ OK: 0, ERROR: 1, INTERNAL: 2, PERM: 3, ABORT: 4, BUSY: 5, LOCKED: 6, NOMEM: 7, READONLY: 8, INTERRUPT: 9, IOERR: 10, CORRUPT: 11, NOTFOUND: 12,
13
+ FULL: 13, CANTOPEN: 14, PROTOCOL: 15, EMPTY: 16, SCHEMA: 17, TOOBIG: 18, CONSTRAINT: 19, MISMATCH: 20, MISUSE: 21, NOLFS: 22, AUTH: 23,
14
+ FORMAT: 24, RANGE: 25, NOTADB: 26,
15
+ LIMIT_LENGTH: 0, LIMIT_SQL_LENGTH: 1, LIMIT_COLUMN: 2, LIMIT_EXPR_DEPTH: 3, LIMIT_COMPOUND_SELECT: 4, LIMIT_VDBE_OP: 5, LIMIT_FUNCTION_ARG: 6,
16
+ LIMIT_ATTACHED: 7, LIMIT_LIKE_PATTERN_LENGTH: 8, LIMIT_VARIABLE_NUMBER: 9, LIMIT_TRIGGER_DEPTH: 10, LIMIT_WORKER_THREADS: 11,
17
+ };
18
+ const codeNames = ['SQLITE_OK', 'SQLITE_ERROR', 'SQLITE_INTERNAL', 'SQLITE_PERM', 'SQLITE_ABORT', 'SQLITE_BUSY', 'SQLITE_LOCKED', 'SQLITE_NOMEM',
19
+ 'SQLITE_READONLY', 'SQLITE_INTERRUPT', 'SQLITE_IOERR', 'SQLITE_CORRUPT', 'SQLITE_NOTFOUND', 'SQLITE_FULL', 'SQLITE_CANTOPEN', 'SQLITE_PROTOCOL',
20
+ 'SQLITE_EMPTY', 'SQLITE_SCHEMA', 'SQLITE_TOOBIG', 'SQLITE_CONSTRAINT', 'SQLITE_MISMATCH', 'SQLITE_MISUSE', 'SQLITE_NOLFS', 'SQLITE_AUTH',
21
+ 'SQLITE_FORMAT', 'SQLITE_RANGE', 'SQLITE_NOTADB'];
22
+ const limitNames = ['length', 'sqlLength', 'column', 'exprDepth', 'compoundSelect', 'vdbeOp', 'functionArg', 'attach', 'likePatternLength', 'variableNumber', 'triggerDepth'];
23
+
24
+ const versionInfo = (() => {
25
+ const probe = new DatabaseSync(':memory:');
26
+ const row = probe.prepare('SELECT sqlite_version() AS v, sqlite_source_id() AS s').get();
27
+ probe.close();
28
+ const [major, minor, patch] = row.v.split('.').map(Number);
29
+ return {VERSION: row.v, SOURCE_ID: row.s, VERSION_NUMBER: major * 1000000 + minor * 1000 + (patch || 0)};
30
+ })();
31
+
32
+ function sqliteError(code, message) {
33
+ const name = codeNames[code] || 'SQLITE_ERROR';
34
+ const error = new Error(`${name}: ${message}`);
35
+ error.errno = code;
36
+ error.code = name;
37
+ return error;
38
+ }
39
+ function convertError(error) {
40
+ if (error && error.errno !== undefined && error.code && String(error.code).startsWith('SQLITE_')) return error;
41
+ if (error && typeof error.errcode === 'number') return sqliteError(error.errcode & 0xff, error.message || error.errstr);
42
+ if (error && error.code === 'ERR_SQLITE_ERROR') return sqliteError(constants.ERROR, error.message);
43
+ if (error && (error.code === 'ERR_INVALID_ARG_TYPE' || error.code === 'ERR_INVALID_STATE' || error.code === 'ERR_OUT_OF_RANGE' || error.code === 'ERR_INVALID_ARG_VALUE')) {
44
+ if (/not open|closed|finalized/i.test(error.message)) return sqliteError(constants.MISUSE, error.message);
45
+ return sqliteError(constants.RANGE, 'column index out of range');
46
+ }
47
+ return error;
48
+ }
49
+ const closedError = () => sqliteError(constants.MISUSE, 'Database handle is closed');
50
+ const isFunction = value => typeof value === 'function';
51
+
52
+ // --- binding -----------------------------------------------------------------------------------
53
+ function convertValue(value) {
54
+ if (value === null || value === undefined) return null;
55
+ switch (typeof value) {
56
+ case 'number': return Number.isInteger(value) && Math.abs(value) < 2 ** 63 ? BigInt(value) : value;
57
+ case 'string': return value;
58
+ case 'bigint': return value >= -(2n ** 63n) && value < 2n ** 63n ? value : Number(value);
59
+ case 'boolean': return value ? 1n : 0n;
60
+ case 'object': break;
61
+ default: return {unsupported: true};
62
+ }
63
+ if (Buffer.isBuffer(value)) return value;
64
+ if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength);
65
+ if (value instanceof ArrayBuffer) return Buffer.from(value);
66
+ if (value instanceof Date) return convertValue(value.valueOf());
67
+ if (value instanceof RegExp) return String(value);
68
+ try { return String(value); } catch { return null; }
69
+ }
70
+ function isPlainObject(value) {
71
+ if (value === null || typeof value !== 'object' || Array.isArray(value) || Buffer.isBuffer(value) || value instanceof Uint8Array || value instanceof ArrayBuffer || value instanceof Date || value instanceof RegExp) return false;
72
+ return true;
73
+ }
74
+ const isNameChar = c => /[A-Za-z0-9_$]/.test(c);
75
+ // Ordered 1-based parameter slots of a statement: prefixed name for named parameters, null for anonymous ones.
76
+ function scanParameters(sql) {
77
+ const slots = [];
78
+ const named = new Map();
79
+ let i = 0;
80
+ const n = sql.length;
81
+ while (i < n) {
82
+ const c = sql[i];
83
+ if (c === '-' && sql[i + 1] === '-') { const e = sql.indexOf('\n', i); i = e === -1 ? n : e + 1; continue; }
84
+ if (c === '/' && sql[i + 1] === '*') { const e = sql.indexOf('*/', i + 2); i = e === -1 ? n : e + 2; continue; }
85
+ if (c === "'" || c === '"' || c === '`') { let j = i + 1; while (j < n) { if (sql[j] === c) { if (sql[j + 1] === c) { j += 2; continue; } break; } j++; } i = j + 1; continue; }
86
+ if (c === '[') { const e = sql.indexOf(']', i); i = e === -1 ? n : e + 1; continue; }
87
+ if ((c === 'x' || c === 'X') && sql[i + 1] === "'") { const e = sql.indexOf("'", i + 2); i = e === -1 ? n : e + 1; continue; }
88
+ if (c === '?') {
89
+ let j = i + 1; while (j < n && sql[j] >= '0' && sql[j] <= '9') j++;
90
+ const index = j > i + 1 ? Number(sql.slice(i + 1, j)) : slots.length + 1;
91
+ while (slots.length < index) slots.push(undefined);
92
+ if (slots[index - 1] === undefined) slots[index - 1] = null;
93
+ i = j; continue;
94
+ }
95
+ if ((c === ':' || c === '@' || c === '$') && i + 1 < n && isNameChar(sql[i + 1])) {
96
+ let j = i + 1; while (j < n && (isNameChar(sql[j]) || (c === '$' && sql[j] === ':'))) j++;
97
+ const name = sql.slice(i, j);
98
+ if (!named.has(name)) { named.set(name, slots.length + 1); slots.push(name); }
99
+ i = j; continue;
100
+ }
101
+ i++;
102
+ }
103
+ return slots;
104
+ }
105
+ function positional(values, slots) {
106
+ const converted = values.map(convertValue);
107
+ if (!slots || !slots.some(slot => typeof slot === 'string')) return converted;
108
+ const named = {};
109
+ const anonymous = [];
110
+ for (let k = 0; k < slots.length; k++) {
111
+ const value = k < converted.length ? converted[k] : null;
112
+ if (typeof slots[k] === 'string') named[slots[k]] = value; else anonymous.push(value);
113
+ }
114
+ return [named, ...anonymous];
115
+ }
116
+ // Returns the argument list for StatementSync methods: [named?, ...anonymous].
117
+ function normalizeParams(args, slots) {
118
+ const values = args.filter(value => value !== undefined);
119
+ if (values.length === 0) return [];
120
+ if (values.length === 1 && Array.isArray(values[0])) return positional(values[0], slots);
121
+ if (values.length === 1 && isPlainObject(values[0])) {
122
+ const source = values[0];
123
+ const keys = Object.keys(source);
124
+ if (keys.length > 0 && keys.every(key => /^[1-9][0-9]*$/.test(key))) {
125
+ const out = [];
126
+ for (const key of keys) out[Number(key) - 1] = source[key];
127
+ for (let k = 0; k < out.length; k++) if (out[k] === undefined) out[k] = null;
128
+ return positional(out, slots);
129
+ }
130
+ const named = {};
131
+ for (const key of keys) named[key] = convertValue(source[key]);
132
+ return [named];
133
+ }
134
+ return positional(values, slots);
135
+ }
136
+ function convertRow(row) {
137
+ if (row === undefined || row === null) return row;
138
+ const out = {};
139
+ for (const key of Object.keys(row)) {
140
+ let value = row[key];
141
+ if (typeof value === 'bigint') value = Number(value);
142
+ else if (value instanceof Uint8Array && !Buffer.isBuffer(value)) value = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
143
+ out[key] = value;
144
+ }
145
+ return out;
146
+ }
147
+
148
+ // --- scheduling ------------------------------------------------------------------------------
149
+ function invoke(resource, callback, thisArg, args) {
150
+ if (resource) return resource.runInAsyncScope(callback, thisArg, ...args);
151
+ return callback.apply(thisArg, args);
152
+ }
153
+
154
+ function Database(filename, mode, callback) {
155
+ if (!new.target) throw new TypeError("Class constructors cannot be invoked without 'new'");
156
+ EventEmitter.call(this);
157
+ if (typeof filename !== 'string') throw new TypeError('Filename is required');
158
+ if (isFunction(mode)) { callback = mode; mode = undefined; }
159
+ if (mode === undefined) mode = constants.OPEN_READWRITE | constants.OPEN_CREATE | constants.OPEN_FULLMUTEX;
160
+ if (typeof mode !== 'number') throw new TypeError('Mode must be a number');
161
+ this.filename = filename;
162
+ this.mode = mode;
163
+ this.open = false;
164
+ Object.defineProperties(this, {
165
+ _native: {value: null, writable: true},
166
+ _queue: {value: [], writable: true},
167
+ _head: {value: 0, writable: true},
168
+ _draining: {value: false, writable: true},
169
+ _closing: {value: false, writable: true},
170
+ _closed: {value: false, writable: true},
171
+ _statements: {value: new Set(), writable: true},
172
+ _backups: {value: new Set(), writable: true},
173
+ _serialized: {value: false, writable: true},
174
+ _flags: {value: {trace: false, profile: false, change: false}, writable: true},
175
+ _interrupted: {value: false, writable: true},
176
+ _executing: {value: false, writable: true},
177
+ });
178
+ const resource = new AsyncResource('sqlite3.Database.open');
179
+ this._enqueue(() => {
180
+ try {
181
+ this._native = openNative(filename, mode);
182
+ } catch (error) {
183
+ const converted = convertError(error);
184
+ this._closed = true;
185
+ if (callback) invoke(resource, callback, this, [converted]);
186
+ else this.emit('error', converted);
187
+ return;
188
+ }
189
+ this.open = true;
190
+ this._executing = false;
191
+ if (callback) invoke(resource, callback, this, [null]);
192
+ this.emit('open');
193
+ });
194
+ }
195
+ util.inherits(Database, EventEmitter);
196
+
197
+ function openNative(filename, mode) {
198
+ const readOnly = (mode & constants.OPEN_READONLY) !== 0 && (mode & constants.OPEN_READWRITE) === 0;
199
+ const isUri = filename.startsWith('file:');
200
+ const anonymous = filename === '' || filename === ':memory:';
201
+ if (!anonymous && !isUri && !(mode & constants.OPEN_CREATE) && !fs.existsSync(filename)) throw sqliteError(constants.CANTOPEN, 'unable to open database file');
202
+ if (!anonymous && !isUri && (mode & constants.OPEN_CREATE) && !fs.existsSync(path.dirname(path.resolve(filename)))) throw sqliteError(constants.CANTOPEN, 'unable to open database file');
203
+ let target = filename;
204
+ if (!anonymous && (mode & constants.OPEN_SHAREDCACHE) && !/[?&]cache=/.test(filename)) {
205
+ const uri = isUri ? filename : `file:${encodeURI(path.resolve(filename))}`;
206
+ target = uri + (uri.includes('?') ? '&' : '?') + 'cache=shared';
207
+ }
208
+ try {
209
+ return new DatabaseSync(target, {readOnly, allowExtension: true});
210
+ } catch (error) {
211
+ throw convertError(error);
212
+ }
213
+ }
214
+
215
+ const proto = Database.prototype;
216
+
217
+ proto._enqueue = function (task) {
218
+ this._queue.push(task);
219
+ if (!this._draining) {
220
+ this._draining = true;
221
+ setImmediate(() => this._drain());
222
+ }
223
+ };
224
+ proto._next = function () {
225
+ if (this._head >= this._queue.length) return undefined;
226
+ const task = this._queue[this._head];
227
+ this._queue[this._head++] = undefined;
228
+ if (this._head === this._queue.length) { this._queue.length = 0; this._head = 0; }
229
+ else if (this._head > 4096 && this._head * 2 > this._queue.length) { this._queue = this._queue.slice(this._head); this._head = 0; }
230
+ return task;
231
+ };
232
+ proto._drain = function () {
233
+ let budget = 10000;
234
+ let task;
235
+ while (budget-- > 0 && (task = this._next()) !== undefined) {
236
+ this._executing = true;
237
+ try { task(); } catch (error) {
238
+ this._executing = false;
239
+ if (this._head < this._queue.length) setImmediate(() => this._drain()); else this._draining = false;
240
+ throw error;
241
+ }
242
+ this._executing = false;
243
+ }
244
+ if (this._head < this._queue.length) setImmediate(() => this._drain());
245
+ else this._draining = false;
246
+ };
247
+ proto._fail = function (error, callback, resource) {
248
+ if (callback) invoke(resource, callback, this, [error]);
249
+ else this.emit('error', error);
250
+ };
251
+
252
+ proto.serialize = function (callback) {
253
+ const previous = this._serialized;
254
+ this._serialized = true;
255
+ if (isFunction(callback)) { try { callback.call(this); } finally { this._serialized = previous; } }
256
+ return this;
257
+ };
258
+ proto.parallelize = function (callback) {
259
+ const previous = this._serialized;
260
+ this._serialized = false;
261
+ if (isFunction(callback)) { try { callback.call(this); } finally { this._serialized = previous; } }
262
+ return this;
263
+ };
264
+
265
+ proto.wait = function (callback) {
266
+ const resource = new AsyncResource('sqlite3.Database.wait');
267
+ this._enqueue(() => { if (callback) invoke(resource, callback, this, [null]); });
268
+ return this;
269
+ };
270
+
271
+ proto.close = function (callback) {
272
+ const resource = new AsyncResource('sqlite3.Database.close');
273
+ if (this.open && !this._executing && this._head >= this._queue.length) this._closing = true;
274
+ this._enqueue(() => {
275
+ this._closing = true;
276
+ if (this._closed || !this._native) {
277
+ this._closing = false;
278
+ return this._fail(sqliteError(constants.MISUSE, 'Database is closed'), callback, resource);
279
+ }
280
+ if (this._backups.size > 0) {
281
+ this._closing = false;
282
+ return this._fail(sqliteError(constants.BUSY, 'unable to close due to unfinalised statements or unfinished backups'), callback, resource);
283
+ }
284
+ if (this._statements.size > 0) {
285
+ this._closing = false;
286
+ return this._fail(sqliteError(constants.BUSY, 'unable to close due to unfinalised statements'), callback, resource);
287
+ }
288
+ try { this._native.close(); } catch (error) { this._closing = false; return this._fail(convertError(error), callback, resource); }
289
+ this._closed = true;
290
+ this._closing = false;
291
+ this.open = false;
292
+ this._executing = false;
293
+ if (callback) invoke(resource, callback, this, [null]);
294
+ this.emit('close');
295
+ });
296
+ return this;
297
+ };
298
+
299
+ proto.exec = function (sql, callback) {
300
+ const resource = new AsyncResource('sqlite3.Database.exec');
301
+ this._enqueue(() => {
302
+ if (this._closed || !this._native) return this._fail(closedError(), callback, resource);
303
+ try { this._native.exec(sql); } catch (error) { return this._fail(convertError(error), callback, resource); }
304
+ this._executing = false;
305
+ if (callback) invoke(resource, callback, this, [null]);
306
+ });
307
+ return this;
308
+ };
309
+
310
+ proto.configure = function (option, value, extra) {
311
+ switch (option) {
312
+ case 'trace': case 'profile': case 'change': this._flags[option] = Boolean(value); return;
313
+ case 'busyTimeout':
314
+ this._enqueue(() => { if (this._native && !this._closed) this._native.exec(`PRAGMA busy_timeout = ${Number(value) | 0}`); });
315
+ return;
316
+ case 'limit': {
317
+ const name = limitNames[value];
318
+ this._enqueue(() => { if (this._native && !this._closed && name) this._native.limits[name] = extra; });
319
+ return;
320
+ }
321
+ default: throw new Error(`Unknown option: ${option}`);
322
+ }
323
+ };
324
+
325
+ proto.loadExtension = function (filename, callback) {
326
+ const resource = new AsyncResource('sqlite3.Database.loadExtension');
327
+ this._enqueue(() => {
328
+ if (this._closed || !this._native) return this._fail(closedError(), callback, resource);
329
+ try { this._native.enableLoadExtension(true); this._native.loadExtension(filename); } catch (error) { return this._fail(convertError(error), callback, resource); }
330
+ finally { try { this._native.enableLoadExtension(false); } catch {} }
331
+ if (callback) invoke(resource, callback, this, [null]);
332
+ });
333
+ return this;
334
+ };
335
+
336
+ proto.interrupt = function () {
337
+ if (this._closing) throw new Error('Database is closing');
338
+ if (!this.open || this._closed) throw new Error('Database is not open');
339
+ this._interrupted = true;
340
+ };
341
+
342
+ const supportedEvents = ['trace', 'profile', 'change'];
343
+ proto.addListener = proto.on = function (type) {
344
+ const value = EventEmitter.prototype.addListener.apply(this, arguments);
345
+ if (supportedEvents.includes(type)) this.configure(type, true);
346
+ return value;
347
+ };
348
+ proto.removeListener = function (type) {
349
+ const value = EventEmitter.prototype.removeListener.apply(this, arguments);
350
+ if (supportedEvents.includes(type) && this.listenerCount(type) === 0) this.configure(type, false);
351
+ return value;
352
+ };
353
+ proto.removeAllListeners = function (type) {
354
+ const value = EventEmitter.prototype.removeAllListeners.apply(this, arguments);
355
+ if (supportedEvents.includes(type)) this.configure(type, false);
356
+ else if (type === undefined) for (const name of supportedEvents) this.configure(name, false);
357
+ return value;
358
+ };
359
+
360
+ // --- statements ---------------------------------------------------------------------------------
361
+ function Statement(db, sql, callback, resource) {
362
+ if (!new.target) throw new TypeError("Class constructors cannot be invoked without 'new'");
363
+ EventEmitter.call(this);
364
+ this.sql = sql;
365
+ this.lastID = 0;
366
+ this.changes = 0;
367
+ Object.defineProperties(this, {
368
+ _db: {value: db},
369
+ _native: {value: null, writable: true},
370
+ _params: {value: [], writable: true},
371
+ _cursor: {value: null, writable: true},
372
+ _exhausted: {value: false, writable: true},
373
+ _finalized: {value: false, writable: true},
374
+ _failed: {value: false, writable: true},
375
+ _resource: {value: resource || null, writable: true},
376
+ _slots: {value: null, writable: true},
377
+ });
378
+ resource = resource || new AsyncResource('sqlite3.Statement.prepare');
379
+ db._enqueue(() => {
380
+ if (db._closed || !db._native) { this._failed = true; return this._error(closedError(), callback, resource, db); }
381
+ if (typeof sql !== 'string') { this._failed = true; return this._error(sqliteError(constants.MISUSE, 'SQL query expected'), callback, resource); }
382
+ try {
383
+ this._native = db._native.prepare(sql);
384
+ this._native.setReadBigInts(true);
385
+ this._slots = scanParameters(sql);
386
+ } catch (error) {
387
+ this._failed = true;
388
+ return this._error(convertError(error), callback, resource);
389
+ }
390
+ db._statements.add(this);
391
+ db._executing = false;
392
+ if (callback) invoke(resource, callback, this, [null]);
393
+ });
394
+ }
395
+ util.inherits(Statement, EventEmitter);
396
+ const sproto = Statement.prototype;
397
+
398
+ sproto._error = function (error, callback, resource, emitter) {
399
+ if (callback) return invoke(resource, callback, this, [error]);
400
+ const target = emitter || this;
401
+ if (target.listenerCount('error') > 0) target.emit('error', error);
402
+ };
403
+ function splitCallback(args) {
404
+ let callback;
405
+ if (args.length && isFunction(args[args.length - 1])) callback = args.pop();
406
+ return callback;
407
+ }
408
+ sproto._schedule = function (name, args, work) {
409
+ const callback = splitCallback(args);
410
+ const resource = this._resource || new AsyncResource(`sqlite3.Statement.${name}`);
411
+ const db = this._db;
412
+ db._enqueue(() => {
413
+ if (this._failed) return;
414
+ if (this._finalized) return this._error(sqliteError(constants.MISUSE, 'Statement is already finalized'), callback, resource);
415
+ if (db._closed || !db._native) return this._error(closedError(), callback, resource);
416
+ let result;
417
+ try {
418
+ if (args.length) { this._params = normalizeParams(args, this._slots); this._resetCursor(); }
419
+ result = work(callback, resource);
420
+ } catch (error) {
421
+ db._executing = false;
422
+ return this._error(convertError(error), callback, resource);
423
+ }
424
+ db._executing = false;
425
+ if (result !== undefined && callback) invoke(resource, callback, this, result);
426
+ });
427
+ return this;
428
+ };
429
+ sproto._resetCursor = function () {
430
+ if (this._cursor) { try { this._cursor.return(); } catch {} }
431
+ this._cursor = null;
432
+ this._exhausted = false;
433
+ };
434
+ sproto._trace = function () {
435
+ return this._db._flags.profile ? process.hrtime.bigint() : null;
436
+ };
437
+ // Emits trace/profile once the parameters are bound, so the expanded SQL carries the bound values.
438
+ sproto._profile = function (started) {
439
+ const db = this._db;
440
+ if (db._flags.trace) db.emit('trace', this._native.expandedSQL);
441
+ if (started !== null) db.emit('profile', this._native.expandedSQL, Number(process.hrtime.bigint() - started) / 1e6);
442
+ };
443
+ sproto._checkInterrupt = function () {
444
+ if (this._db._interrupted) { this._db._interrupted = false; throw sqliteError(constants.INTERRUPT, 'interrupted'); }
445
+ };
446
+
447
+ sproto.bind = function () {
448
+ const args = Array.prototype.slice.call(arguments);
449
+ return this._schedule('bind', args, () => { if (!args.length) { this._params = []; this._resetCursor(); } return [null]; });
450
+ };
451
+ sproto.reset = function (callback) {
452
+ return this._schedule('reset', callback ? [callback] : [], () => { this._resetCursor(); return [null]; });
453
+ };
454
+ sproto.finalize = function (callback) {
455
+ const resource = this._resource || new AsyncResource('sqlite3.Statement.finalize');
456
+ const db = this._db;
457
+ db._enqueue(() => {
458
+ if (!this._finalized && !this._failed) {
459
+ this._resetCursor();
460
+ this._finalized = true;
461
+ db._statements.delete(this);
462
+ if (this._native && typeof this._native.close === 'function') { try { this._native.close(); } catch {} }
463
+ }
464
+ if (callback) invoke(resource, callback, this, [null]);
465
+ });
466
+ return db;
467
+ };
468
+ sproto.run = function () {
469
+ const args = Array.prototype.slice.call(arguments);
470
+ return this._schedule('run', args, () => {
471
+ this._resetCursor();
472
+ const started = this._trace();
473
+ const info = this._native.run(...this._params);
474
+ this._profile(started);
475
+ this.lastID = Number(info.lastInsertRowid);
476
+ this.changes = Number(info.changes);
477
+ return [null];
478
+ });
479
+ };
480
+ sproto.get = function () {
481
+ const args = Array.prototype.slice.call(arguments);
482
+ return this._schedule('get', args, () => {
483
+ if (this._exhausted) return [null, undefined];
484
+ if (!this._cursor) {
485
+ const started = this._trace();
486
+ this._cursor = this._native.iterate(...this._params);
487
+ this._profile(started);
488
+ }
489
+ const next = this._cursor.next();
490
+ if (next.done) { this._exhausted = true; this._cursor = null; return [null, undefined]; }
491
+ return [null, convertRow(next.value)];
492
+ });
493
+ };
494
+ sproto.all = function () {
495
+ const args = Array.prototype.slice.call(arguments);
496
+ return this._schedule('all', args, () => {
497
+ this._resetCursor();
498
+ const started = this._trace();
499
+ const rows = this._native.all(...this._params);
500
+ this._profile(started);
501
+ this._exhausted = true;
502
+ for (let k = 0; k < rows.length; k++) rows[k] = convertRow(rows[k]);
503
+ return [null, rows];
504
+ });
505
+ };
506
+ sproto.each = function () {
507
+ const args = Array.prototype.slice.call(arguments);
508
+ let complete;
509
+ let rowCallback;
510
+ if (args.length && isFunction(args[args.length - 1])) {
511
+ const last = args.pop();
512
+ if (args.length && isFunction(args[args.length - 1])) { complete = last; rowCallback = args.pop(); } else rowCallback = last;
513
+ }
514
+ const finalCallback = complete || rowCallback;
515
+ if (finalCallback) args.push(finalCallback);
516
+ return this._schedule('each', args, (callback, resource) => {
517
+ this._resetCursor();
518
+ const started = this._trace();
519
+ const iterator = this._native.iterate(...this._params);
520
+ this._profile(started);
521
+ let count = 0;
522
+ try {
523
+ for (const row of iterator) {
524
+ count++;
525
+ if (rowCallback) invoke(resource, rowCallback, this, [null, convertRow(row)]);
526
+ this._checkInterrupt();
527
+ }
528
+ } catch (error) {
529
+ try { iterator.return(); } catch {}
530
+ const converted = convertError(error);
531
+ this._exhausted = true;
532
+ if (complete) invoke(resource, complete, this, [converted, count]);
533
+ else if (rowCallback) invoke(resource, rowCallback, this, [converted]);
534
+ return undefined;
535
+ }
536
+ this._exhausted = true;
537
+ this._db._executing = false;
538
+ if (complete) invoke(resource, complete, this, [null, count]);
539
+ return undefined;
540
+ });
541
+ };
542
+ sproto.map = function () {
543
+ const params = Array.prototype.slice.call(arguments);
544
+ const callback = params.pop();
545
+ params.push(function (error, rows) {
546
+ if (error) return callback(error);
547
+ const result = {};
548
+ if (rows.length) {
549
+ const keys = Object.keys(rows[0]);
550
+ const key = keys[0];
551
+ if (keys.length > 2) for (const row of rows) result[row[key]] = row;
552
+ else { const value = keys[1]; for (const row of rows) result[row[key]] = row[value]; }
553
+ }
554
+ callback(error, result);
555
+ });
556
+ return this.all.apply(this, params);
557
+ };
558
+
559
+ // --- Database helpers built on Statement -------------------------------------------------------
560
+ function normalizeMethod(name, fn) {
561
+ return function (sql) {
562
+ let errBack;
563
+ const args = Array.prototype.slice.call(arguments, 1);
564
+ if (isFunction(args[args.length - 1])) {
565
+ const callback = args[args.length - 1];
566
+ errBack = function (error) { if (error) callback(error); };
567
+ }
568
+ const resource = new AsyncResource(`sqlite3.Database.${name}`);
569
+ const statement = new Statement(this, sql, errBack, resource);
570
+ return fn.call(this, statement, args);
571
+ };
572
+ }
573
+ proto.prepare = normalizeMethod('prepare', function (statement, params) { return params.length ? statement.bind.apply(statement, params) : statement; });
574
+ proto.run = normalizeMethod('run', function (statement, params) { statement.run.apply(statement, params).finalize(); return this; });
575
+ proto.get = normalizeMethod('get', function (statement, params) { statement.get.apply(statement, params).finalize(); return this; });
576
+ proto.all = normalizeMethod('all', function (statement, params) { statement.all.apply(statement, params).finalize(); return this; });
577
+ proto.each = normalizeMethod('each', function (statement, params) { statement.each.apply(statement, params).finalize(); return this; });
578
+ proto.map = normalizeMethod('map', function (statement, params) { statement.map.apply(statement, params).finalize(); return this; });
579
+
580
+ // --- backup --------------------------------------------------------------------------------------
581
+ function Backup(db, filename, sourceName, destName, filenameIsDest, callback) {
582
+ if (!new.target) throw new TypeError("Class constructors cannot be invoked without 'new'");
583
+ EventEmitter.call(this);
584
+ this.idle = true;
585
+ this.completed = false;
586
+ this.failed = false;
587
+ this.remaining = -1;
588
+ this.pageCount = -1;
589
+ this.retryErrors = [];
590
+ Object.defineProperties(this, {
591
+ _db: {value: db}, _filename: {value: filename}, _destName: {value: destName}, _sourceName: {value: sourceName},
592
+ _filenameIsDest: {value: filenameIsDest}, _finished: {value: false, writable: true}, _active: {value: false, writable: true},
593
+ });
594
+ const resource = new AsyncResource('sqlite3.Backup.initialize');
595
+ db._enqueue(() => {
596
+ if (db._closed || !db._native) return this._fail(closedError(), callback, resource);
597
+ if (!filenameIsDest) return this._fail(sqliteError(constants.MISUSE, 'backup from a file into an open connection is not available on node:sqlite'), callback, resource);
598
+ db._backups.add(this);
599
+ this._active = true;
600
+ db._executing = false;
601
+ if (callback) invoke(resource, callback, this, [null]);
602
+ });
603
+ }
604
+ util.inherits(Backup, EventEmitter);
605
+ Backup.prototype._fail = function (error, callback, resource) {
606
+ if (callback) return invoke(resource, callback, this, [error]);
607
+ if (this.listenerCount('error') > 0) this.emit('error', error);
608
+ };
609
+ Backup.prototype._releaseHandle = function () {
610
+ this._active = false;
611
+ this._db._backups.delete(this);
612
+ };
613
+ // Mirrors node-sqlite3: a step whose status is not OK releases the SQLite backup handle unless the status is listed in retryErrors.
614
+ Backup.prototype._afterStep = function (status) {
615
+ if (status !== constants.OK && this.retryErrors.length > 0 && !this.retryErrors.includes(status)) this._releaseHandle();
616
+ if (status === 101) this.completed = true;
617
+ else if (!this._active) this.failed = true;
618
+ };
619
+ Backup.prototype.step = function (pages, callback) {
620
+ const db = this._db;
621
+ const resource = new AsyncResource('sqlite3.Backup.step');
622
+ this.idle = false;
623
+ db._enqueue(() => {
624
+ if (this._finished) { this.idle = true; return this._fail(sqliteError(constants.MISUSE, 'Backup is already finished'), callback, resource); }
625
+ if (db._closed || !db._native) { this.idle = true; return this._fail(closedError(), callback, resource); }
626
+ const source = this._sourceName === 'main' ? 'main' : this._sourceName;
627
+ let pageCount;
628
+ try { pageCount = Number(db._native.prepare(`PRAGMA "${source.replace(/"/g, '""')}".page_count`).get().page_count); } catch (error) { this.idle = true; return this._fail(convertError(error), callback, resource); }
629
+ if (this.pageCount < 0) { this.pageCount = pageCount; this.remaining = pageCount; }
630
+ if (!this._active) { this.idle = true; this.failed = true; return this._fail(sqliteError(constants.MISUSE, 'Backup is already finished'), callback, resource); }
631
+ if (pages === 0) { this.idle = true; if (callback) invoke(resource, callback, this, [null, false]); return; }
632
+ let pending;
633
+ try { pending = nativeBackup(db._native, this._filename, {source, target: this._destName}); } catch (error) { this.idle = true; return this._fail(convertError(error), callback, resource); }
634
+ // Node 26.8 settles the backup promise only when another task wakes the event loop; keep it awake until then.
635
+ const keepAlive = setInterval(() => {}, 10);
636
+ pending.finally(() => clearInterval(keepAlive));
637
+ pending.then(() => {
638
+ this.remaining = 0;
639
+ this._afterStep(101);
640
+ this.idle = true;
641
+ if (callback) invoke(resource, callback, this, [null, true]);
642
+ }, error => {
643
+ const converted = error && error.errcode === 0 ? sqliteError(constants.BUSY, 'database is locked') : convertError(error);
644
+ this._afterStep(converted.errno);
645
+ this.idle = true;
646
+ if (callback) invoke(resource, callback, this, [converted]);
647
+ else if (this.listenerCount('error') > 0) this.emit('error', converted);
648
+ });
649
+ });
650
+ return this;
651
+ };
652
+ Backup.prototype.finish = function (callback) {
653
+ const db = this._db;
654
+ const resource = new AsyncResource('sqlite3.Backup.finish');
655
+ db._enqueue(() => {
656
+ if (!this._finished) {
657
+ if (!this.completed && !this.failed) this.failed = true;
658
+ this._finished = true;
659
+ this._releaseHandle();
660
+ }
661
+ db._executing = false;
662
+ if (callback) invoke(resource, callback, this, [null]);
663
+ });
664
+ return this;
665
+ };
666
+ proto.backup = function () {
667
+ let backup;
668
+ if (arguments.length <= 2) backup = new Backup(this, arguments[0], 'main', 'main', true, arguments[1]);
669
+ else backup = new Backup(this, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
670
+ backup.retryErrors = [constants.BUSY, constants.LOCKED];
671
+ return backup;
672
+ };
673
+
674
+ // --- module surface ------------------------------------------------------------------------------
675
+ const sqlite3 = {...constants, ...versionInfo, Database, Statement, Backup};
676
+ sqlite3.cached = {
677
+ Database: function (file, a, b) {
678
+ if (file === '' || file === ':memory:') return new Database(file, a, b);
679
+ let db;
680
+ file = path.resolve(file);
681
+ if (!sqlite3.cached.objects[file]) db = sqlite3.cached.objects[file] = new Database(file, a, b);
682
+ else {
683
+ db = sqlite3.cached.objects[file];
684
+ const callback = typeof a === 'number' ? b : a;
685
+ if (isFunction(callback)) {
686
+ const cb = () => callback.call(db, null);
687
+ if (db.open) process.nextTick(cb); else db.once('open', cb);
688
+ }
689
+ }
690
+ return db;
691
+ },
692
+ objects: {},
693
+ };
694
+ let isVerbose = false;
695
+ function extendTrace(object, property, pos) {
696
+ const old = object[property];
697
+ object[property] = function () {
698
+ const error = new Error();
699
+ const name = `${object.constructor.name}#${property}(${Array.prototype.slice.call(arguments).map(el => util.inspect(el, false, 0)).join(', ')})`;
700
+ if (pos === undefined) pos = -1;
701
+ if (pos < 0) pos += arguments.length;
702
+ const cb = arguments[pos];
703
+ if (isFunction(cb)) {
704
+ arguments[pos] = function replacement() {
705
+ const err = arguments[0];
706
+ if (err && err.stack && !err.__augmented) {
707
+ err.stack = filterStack(err).join('\n') + '\n--> in ' + name + '\n' + filterStack(error).slice(1).join('\n');
708
+ err.__augmented = true;
709
+ }
710
+ return cb.apply(this, arguments);
711
+ };
712
+ }
713
+ return old.apply(this, arguments);
714
+ };
715
+ }
716
+ function filterStack(error) { return error.stack.split('\n').filter(line => !line.includes(__filename)); }
717
+ sqlite3.verbose = function () {
718
+ if (!isVerbose) {
719
+ for (const name of ['prepare', 'get', 'run', 'all', 'each', 'map', 'close', 'exec']) extendTrace(Database.prototype, name);
720
+ for (const name of ['bind', 'get', 'run', 'all', 'each', 'map', 'reset', 'finalize']) extendTrace(Statement.prototype, name);
721
+ isVerbose = true;
722
+ }
723
+ return sqlite3;
724
+ };
725
+ module.exports = sqlite3;
@@ -0,0 +1,170 @@
1
+ /// <reference types="node" />
2
+ import type {EventEmitter} from 'node:events';
3
+
4
+ declare namespace sqlite3 {
5
+ const OPEN_READONLY: number;
6
+ const OPEN_READWRITE: number;
7
+ const OPEN_CREATE: number;
8
+ const OPEN_FULLMUTEX: number;
9
+ const OPEN_SHAREDCACHE: number;
10
+ const OPEN_PRIVATECACHE: number;
11
+ const OPEN_URI: number;
12
+ const VERSION: string;
13
+ const SOURCE_ID: string;
14
+ const VERSION_NUMBER: number;
15
+ const OK: number;
16
+ const ERROR: number;
17
+ const INTERNAL: number;
18
+ const PERM: number;
19
+ const ABORT: number;
20
+ const BUSY: number;
21
+ const LOCKED: number;
22
+ const NOMEM: number;
23
+ const READONLY: number;
24
+ const INTERRUPT: number;
25
+ const IOERR: number;
26
+ const CORRUPT: number;
27
+ const NOTFOUND: number;
28
+ const FULL: number;
29
+ const CANTOPEN: number;
30
+ const PROTOCOL: number;
31
+ const EMPTY: number;
32
+ const SCHEMA: number;
33
+ const TOOBIG: number;
34
+ const CONSTRAINT: number;
35
+ const MISMATCH: number;
36
+ const MISUSE: number;
37
+ const NOLFS: number;
38
+ const AUTH: number;
39
+ const FORMAT: number;
40
+ const RANGE: number;
41
+ const NOTADB: number;
42
+ const LIMIT_LENGTH: number;
43
+ const LIMIT_SQL_LENGTH: number;
44
+ const LIMIT_COLUMN: number;
45
+ const LIMIT_EXPR_DEPTH: number;
46
+ const LIMIT_COMPOUND_SELECT: number;
47
+ const LIMIT_VDBE_OP: number;
48
+ const LIMIT_FUNCTION_ARG: number;
49
+ const LIMIT_ATTACHED: number;
50
+ const LIMIT_LIKE_PATTERN_LENGTH: number;
51
+ const LIMIT_VARIABLE_NUMBER: number;
52
+ const LIMIT_TRIGGER_DEPTH: number;
53
+ const LIMIT_WORKER_THREADS: number;
54
+
55
+ /** Errors carry the SQLite result code as `errno` and its name (`SQLITE_ERROR`, `SQLITE_BUSY`, …) as `code`. */
56
+ interface SqliteError extends Error {
57
+ errno: number;
58
+ code: string;
59
+ }
60
+
61
+ interface RunResult extends Statement {
62
+ lastID: number;
63
+ changes: number;
64
+ }
65
+
66
+ class Statement extends EventEmitter {
67
+ readonly sql: string;
68
+ lastID: number;
69
+ changes: number;
70
+ bind(callback?: (err: Error | null) => void): this;
71
+ bind(...params: any[]): this;
72
+ reset(callback?: (err: null) => void): this;
73
+ finalize(callback?: (err: Error | null) => void): Database;
74
+ run(callback?: (this: RunResult, err: Error | null) => void): this;
75
+ run(params: any, callback?: (this: RunResult, err: Error | null) => void): this;
76
+ run(...params: any[]): this;
77
+ get<T = any>(callback?: (this: Statement, err: Error | null, row?: T) => void): this;
78
+ get<T = any>(params: any, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
79
+ get(...params: any[]): this;
80
+ all<T = any>(callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
81
+ all<T = any>(params: any, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
82
+ all(...params: any[]): this;
83
+ each<T = any>(callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
84
+ each<T = any>(params: any, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
85
+ each(...params: any[]): this;
86
+ map<T = any>(callback: (err: Error | null, result: Record<string, T>) => void): this;
87
+ map<T = any>(params: any, callback: (err: Error | null, result: Record<string, T>) => void): this;
88
+ map(...params: any[]): this;
89
+ }
90
+
91
+ class Backup extends EventEmitter {
92
+ readonly idle: boolean;
93
+ readonly completed: boolean;
94
+ readonly failed: boolean;
95
+ readonly remaining: number;
96
+ readonly pageCount: number;
97
+ retryErrors: number[];
98
+ step(pages: number, callback?: (this: Backup, err: Error | null, completed?: boolean) => void): this;
99
+ finish(callback?: (this: Backup, err: Error | null) => void): this;
100
+ }
101
+
102
+ class Database extends EventEmitter {
103
+ constructor(filename: string, callback?: (this: Database, err: Error | null) => void);
104
+ constructor(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void);
105
+ readonly filename: string;
106
+ readonly mode: number;
107
+ readonly open: boolean;
108
+ close(callback?: (this: Database, err: Error | null) => void): this;
109
+ run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this;
110
+ run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this;
111
+ run(sql: string, ...params: any[]): this;
112
+ get<T = any>(sql: string, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
113
+ get<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
114
+ get(sql: string, ...params: any[]): this;
115
+ all<T = any>(sql: string, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
116
+ all<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
117
+ all(sql: string, ...params: any[]): this;
118
+ each<T = any>(sql: string, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
119
+ each<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
120
+ each(sql: string, ...params: any[]): this;
121
+ map<T = any>(sql: string, callback: (err: Error | null, result: Record<string, T>) => void): this;
122
+ map<T = any>(sql: string, params: any, callback: (err: Error | null, result: Record<string, T>) => void): this;
123
+ map(sql: string, ...params: any[]): this;
124
+ exec(sql: string, callback?: (this: Database, err: Error | null) => void): this;
125
+ prepare(sql: string, callback?: (this: Statement, err: Error | null) => void): Statement;
126
+ prepare(sql: string, params: any, callback?: (this: Statement, err: Error | null) => void): Statement;
127
+ prepare(sql: string, ...params: any[]): Statement;
128
+ serialize(callback?: () => void): this;
129
+ parallelize(callback?: () => void): this;
130
+ on(event: 'trace', listener: (sql: string) => void): this;
131
+ on(event: 'profile', listener: (sql: string, time: number) => void): this;
132
+ on(event: 'change', listener: (type: string, database: string, table: string, rowid: number) => void): this;
133
+ on(event: 'error', listener: (err: Error) => void): this;
134
+ on(event: 'open' | 'close', listener: () => void): this;
135
+ on(event: string, listener: (...args: any[]) => void): this;
136
+ configure(option: 'busyTimeout', value: number): void;
137
+ configure(option: 'limit', id: number, value: number): void;
138
+ configure(option: 'trace' | 'profile' | 'change', enabled: boolean): void;
139
+ loadExtension(filename: string, callback?: (this: Database, err: Error | null) => void): this;
140
+ wait(callback?: (param: null) => void): this;
141
+ interrupt(): void;
142
+ backup(filename: string, callback?: (this: Backup, err: Error | null) => void): Backup;
143
+ backup(filename: string, sourceName: string, destName: string, filenameIsDest: boolean, callback?: (this: Backup, err: Error | null) => void): Backup;
144
+ }
145
+
146
+ const cached: {
147
+ Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database;
148
+ Database(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void): Database;
149
+ objects: Record<string, Database>;
150
+ };
151
+
152
+ function verbose(): sqlite3;
153
+
154
+ interface sqlite3 {
155
+ OPEN_READONLY: number; OPEN_READWRITE: number; OPEN_CREATE: number; OPEN_FULLMUTEX: number; OPEN_SHAREDCACHE: number; OPEN_PRIVATECACHE: number; OPEN_URI: number;
156
+ VERSION: string; SOURCE_ID: string; VERSION_NUMBER: number;
157
+ OK: number; ERROR: number; INTERNAL: number; PERM: number; ABORT: number; BUSY: number; LOCKED: number; NOMEM: number; READONLY: number; INTERRUPT: number;
158
+ IOERR: number; CORRUPT: number; NOTFOUND: number; FULL: number; CANTOPEN: number; PROTOCOL: number; EMPTY: number; SCHEMA: number; TOOBIG: number;
159
+ CONSTRAINT: number; MISMATCH: number; MISUSE: number; NOLFS: number; AUTH: number; FORMAT: number; RANGE: number; NOTADB: number;
160
+ LIMIT_LENGTH: number; LIMIT_SQL_LENGTH: number; LIMIT_COLUMN: number; LIMIT_EXPR_DEPTH: number; LIMIT_COMPOUND_SELECT: number; LIMIT_VDBE_OP: number;
161
+ LIMIT_FUNCTION_ARG: number; LIMIT_ATTACHED: number; LIMIT_LIKE_PATTERN_LENGTH: number; LIMIT_VARIABLE_NUMBER: number; LIMIT_TRIGGER_DEPTH: number; LIMIT_WORKER_THREADS: number;
162
+ cached: typeof cached;
163
+ Statement: typeof Statement;
164
+ Database: typeof Database;
165
+ Backup: typeof Backup;
166
+ verbose(): sqlite3;
167
+ }
168
+ }
169
+
170
+ export = sqlite3;
@@ -0,0 +1,169 @@
1
+ /// <reference types="node" />
2
+ import type {EventEmitter} from 'node:events';
3
+
4
+ export const OPEN_READONLY: number;
5
+ export const OPEN_READWRITE: number;
6
+ export const OPEN_CREATE: number;
7
+ export const OPEN_FULLMUTEX: number;
8
+ export const OPEN_SHAREDCACHE: number;
9
+ export const OPEN_PRIVATECACHE: number;
10
+ export const OPEN_URI: number;
11
+ export const VERSION: string;
12
+ export const SOURCE_ID: string;
13
+ export const VERSION_NUMBER: number;
14
+ export const OK: number;
15
+ export const ERROR: number;
16
+ export const INTERNAL: number;
17
+ export const PERM: number;
18
+ export const ABORT: number;
19
+ export const BUSY: number;
20
+ export const LOCKED: number;
21
+ export const NOMEM: number;
22
+ export const READONLY: number;
23
+ export const INTERRUPT: number;
24
+ export const IOERR: number;
25
+ export const CORRUPT: number;
26
+ export const NOTFOUND: number;
27
+ export const FULL: number;
28
+ export const CANTOPEN: number;
29
+ export const PROTOCOL: number;
30
+ export const EMPTY: number;
31
+ export const SCHEMA: number;
32
+ export const TOOBIG: number;
33
+ export const CONSTRAINT: number;
34
+ export const MISMATCH: number;
35
+ export const MISUSE: number;
36
+ export const NOLFS: number;
37
+ export const AUTH: number;
38
+ export const FORMAT: number;
39
+ export const RANGE: number;
40
+ export const NOTADB: number;
41
+ export const LIMIT_LENGTH: number;
42
+ export const LIMIT_SQL_LENGTH: number;
43
+ export const LIMIT_COLUMN: number;
44
+ export const LIMIT_EXPR_DEPTH: number;
45
+ export const LIMIT_COMPOUND_SELECT: number;
46
+ export const LIMIT_VDBE_OP: number;
47
+ export const LIMIT_FUNCTION_ARG: number;
48
+ export const LIMIT_ATTACHED: number;
49
+ export const LIMIT_LIKE_PATTERN_LENGTH: number;
50
+ export const LIMIT_VARIABLE_NUMBER: number;
51
+ export const LIMIT_TRIGGER_DEPTH: number;
52
+ export const LIMIT_WORKER_THREADS: number;
53
+
54
+ /** Errors carry the SQLite result code as `errno` and its name (`SQLITE_ERROR`, `SQLITE_BUSY`, …) as `code`. */
55
+ export interface SqliteError extends Error {
56
+ errno: number;
57
+ code: string;
58
+ }
59
+
60
+ export interface RunResult extends Statement {
61
+ lastID: number;
62
+ changes: number;
63
+ }
64
+
65
+ export class Statement extends EventEmitter {
66
+ readonly sql: string;
67
+ lastID: number;
68
+ changes: number;
69
+ bind(callback?: (err: Error | null) => void): this;
70
+ bind(...params: any[]): this;
71
+ reset(callback?: (err: null) => void): this;
72
+ finalize(callback?: (err: Error | null) => void): Database;
73
+ run(callback?: (this: RunResult, err: Error | null) => void): this;
74
+ run(params: any, callback?: (this: RunResult, err: Error | null) => void): this;
75
+ run(...params: any[]): this;
76
+ get<T = any>(callback?: (this: Statement, err: Error | null, row?: T) => void): this;
77
+ get<T = any>(params: any, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
78
+ get(...params: any[]): this;
79
+ all<T = any>(callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
80
+ all<T = any>(params: any, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
81
+ all(...params: any[]): this;
82
+ each<T = any>(callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
83
+ each<T = any>(params: any, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
84
+ each(...params: any[]): this;
85
+ map<T = any>(callback: (err: Error | null, result: Record<string, T>) => void): this;
86
+ map<T = any>(params: any, callback: (err: Error | null, result: Record<string, T>) => void): this;
87
+ map(...params: any[]): this;
88
+ }
89
+
90
+ export class Backup extends EventEmitter {
91
+ readonly idle: boolean;
92
+ readonly completed: boolean;
93
+ readonly failed: boolean;
94
+ readonly remaining: number;
95
+ readonly pageCount: number;
96
+ retryErrors: number[];
97
+ step(pages: number, callback?: (this: Backup, err: Error | null, completed?: boolean) => void): this;
98
+ finish(callback?: (this: Backup, err: Error | null) => void): this;
99
+ }
100
+
101
+ export class Database extends EventEmitter {
102
+ constructor(filename: string, callback?: (this: Database, err: Error | null) => void);
103
+ constructor(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void);
104
+ readonly filename: string;
105
+ readonly mode: number;
106
+ readonly open: boolean;
107
+ close(callback?: (this: Database, err: Error | null) => void): this;
108
+ run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this;
109
+ run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this;
110
+ run(sql: string, ...params: any[]): this;
111
+ get<T = any>(sql: string, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
112
+ get<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row?: T) => void): this;
113
+ get(sql: string, ...params: any[]): this;
114
+ all<T = any>(sql: string, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
115
+ all<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: T[]) => void): this;
116
+ all(sql: string, ...params: any[]): this;
117
+ each<T = any>(sql: string, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
118
+ each<T = any>(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: T) => void, complete?: (err: Error | null, count: number) => void): this;
119
+ each(sql: string, ...params: any[]): this;
120
+ map<T = any>(sql: string, callback: (err: Error | null, result: Record<string, T>) => void): this;
121
+ map<T = any>(sql: string, params: any, callback: (err: Error | null, result: Record<string, T>) => void): this;
122
+ map(sql: string, ...params: any[]): this;
123
+ exec(sql: string, callback?: (this: Database, err: Error | null) => void): this;
124
+ prepare(sql: string, callback?: (this: Statement, err: Error | null) => void): Statement;
125
+ prepare(sql: string, params: any, callback?: (this: Statement, err: Error | null) => void): Statement;
126
+ prepare(sql: string, ...params: any[]): Statement;
127
+ serialize(callback?: () => void): this;
128
+ parallelize(callback?: () => void): this;
129
+ on(event: 'trace', listener: (sql: string) => void): this;
130
+ on(event: 'profile', listener: (sql: string, time: number) => void): this;
131
+ on(event: 'change', listener: (type: string, database: string, table: string, rowid: number) => void): this;
132
+ on(event: 'error', listener: (err: Error) => void): this;
133
+ on(event: 'open' | 'close', listener: () => void): this;
134
+ on(event: string, listener: (...args: any[]) => void): this;
135
+ configure(option: 'busyTimeout', value: number): void;
136
+ configure(option: 'limit', id: number, value: number): void;
137
+ configure(option: 'trace' | 'profile' | 'change', enabled: boolean): void;
138
+ loadExtension(filename: string, callback?: (this: Database, err: Error | null) => void): this;
139
+ wait(callback?: (param: null) => void): this;
140
+ interrupt(): void;
141
+ backup(filename: string, callback?: (this: Backup, err: Error | null) => void): Backup;
142
+ backup(filename: string, sourceName: string, destName: string, filenameIsDest: boolean, callback?: (this: Backup, err: Error | null) => void): Backup;
143
+ }
144
+
145
+ export const cached: {
146
+ Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database;
147
+ Database(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void): Database;
148
+ objects: Record<string, Database>;
149
+ };
150
+
151
+ export function verbose(): sqlite3;
152
+
153
+ export interface sqlite3 {
154
+ OPEN_READONLY: number; OPEN_READWRITE: number; OPEN_CREATE: number; OPEN_FULLMUTEX: number; OPEN_SHAREDCACHE: number; OPEN_PRIVATECACHE: number; OPEN_URI: number;
155
+ VERSION: string; SOURCE_ID: string; VERSION_NUMBER: number;
156
+ OK: number; ERROR: number; INTERNAL: number; PERM: number; ABORT: number; BUSY: number; LOCKED: number; NOMEM: number; READONLY: number; INTERRUPT: number;
157
+ IOERR: number; CORRUPT: number; NOTFOUND: number; FULL: number; CANTOPEN: number; PROTOCOL: number; EMPTY: number; SCHEMA: number; TOOBIG: number;
158
+ CONSTRAINT: number; MISMATCH: number; MISUSE: number; NOLFS: number; AUTH: number; FORMAT: number; RANGE: number; NOTADB: number;
159
+ LIMIT_LENGTH: number; LIMIT_SQL_LENGTH: number; LIMIT_COLUMN: number; LIMIT_EXPR_DEPTH: number; LIMIT_COMPOUND_SELECT: number; LIMIT_VDBE_OP: number;
160
+ LIMIT_FUNCTION_ARG: number; LIMIT_ATTACHED: number; LIMIT_LIKE_PATTERN_LENGTH: number; LIMIT_VARIABLE_NUMBER: number; LIMIT_TRIGGER_DEPTH: number; LIMIT_WORKER_THREADS: number;
161
+ cached: typeof cached;
162
+ Statement: typeof Statement;
163
+ Database: typeof Database;
164
+ Backup: typeof Backup;
165
+ verbose(): sqlite3;
166
+ }
167
+
168
+ declare const sqlite3: sqlite3;
169
+ export default sqlite3;
package/dist/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import sqlite3 from './index.cjs';
2
+ export const {OPEN_READONLY, OPEN_READWRITE, OPEN_CREATE, OPEN_URI, OPEN_SHAREDCACHE, OPEN_PRIVATECACHE, OPEN_FULLMUTEX, OK, ERROR, INTERNAL, PERM, ABORT, BUSY, LOCKED, NOMEM, READONLY, INTERRUPT, IOERR, CORRUPT, NOTFOUND, FULL, CANTOPEN, PROTOCOL, EMPTY, SCHEMA, TOOBIG, CONSTRAINT, MISMATCH, MISUSE, NOLFS, AUTH, FORMAT, RANGE, NOTADB, LIMIT_LENGTH, LIMIT_SQL_LENGTH, LIMIT_COLUMN, LIMIT_EXPR_DEPTH, LIMIT_COMPOUND_SELECT, LIMIT_VDBE_OP, LIMIT_FUNCTION_ARG, LIMIT_ATTACHED, LIMIT_LIKE_PATTERN_LENGTH, LIMIT_VARIABLE_NUMBER, LIMIT_TRIGGER_DEPTH, LIMIT_WORKER_THREADS, VERSION, SOURCE_ID, VERSION_NUMBER, Database, Statement, Backup, cached, verbose} = sqlite3;
3
+ export default sqlite3;
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "sqlite3-compat",
3
+ "version": "0.1.0",
4
+ "description": "The node-sqlite3 (sqlite3) callback API on Node's built-in node:sqlite. No native addon, no prebuilt binaries: same Database, Statement, Backup, serialize/parallelize, events and error codes.",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.cts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "sideEffects": false,
26
+ "engines": {
27
+ "node": ">=24.16"
28
+ },
29
+ "license": "MIT",
30
+ "author": "Tom Ryan",
31
+ "keywords": [
32
+ "sqlite",
33
+ "sqlite3",
34
+ "node-sqlite3",
35
+ "node:sqlite",
36
+ "database",
37
+ "compat",
38
+ "no-native",
39
+ "prebuild",
40
+ "sequelize",
41
+ "typeorm",
42
+ "knex"
43
+ ],
44
+ "scripts": {
45
+ "build": "node scripts/build.mjs",
46
+ "test": "node --test test/api.test.mjs",
47
+ "test:parity": "node scripts/parity.mjs",
48
+ "test:types": "tsc -p test/tsconfig.json",
49
+ "test:pack": "node scripts/test-pack.mjs",
50
+ "verify": "npm run build && npm test && npm run test:parity && npm run test:types && npm run test:pack",
51
+ "prepack": "npm run build",
52
+ "prepublishOnly": "npm run verify"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "24.10.1",
56
+ "mocha": "12.0.0",
57
+ "typescript": "5.9.3"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public",
61
+ "registry": "https://registry.npmjs.org/"
62
+ },
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "git+https://github.com/Atomics-hub/sqlite3-compat.git"
66
+ },
67
+ "homepage": "https://github.com/Atomics-hub/sqlite3-compat#readme",
68
+ "bugs": {
69
+ "url": "https://github.com/Atomics-hub/sqlite3-compat/issues"
70
+ }
71
+ }