@photostructure/sqlite 0.2.1 → 0.4.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/CHANGELOG.md +61 -15
- package/README.md +5 -4
- package/binding.gyp +2 -2
- package/dist/index.cjs +159 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +286 -91
- package/dist/index.d.mts +286 -91
- package/dist/index.d.ts +286 -91
- package/dist/index.mjs +156 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +74 -65
- package/prebuilds/darwin-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/test_extension.so +0 -0
- package/prebuilds/win32-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+sqlite.glibc.node +0 -0
- package/scripts/prebuild-linux-glibc.sh +6 -4
- package/src/aggregate_function.cpp +222 -114
- package/src/aggregate_function.h +5 -6
- package/src/binding.cpp +30 -21
- package/src/enhance.ts +228 -0
- package/src/index.ts +83 -9
- package/src/shims/node_errors.h +34 -15
- package/src/shims/sqlite_errors.h +34 -8
- package/src/sql-tag-store.ts +7 -10
- package/src/sqlite_impl.cpp +1044 -394
- package/src/sqlite_impl.h +46 -7
- package/src/transaction.ts +178 -0
- package/src/types/database-sync-instance.ts +6 -40
- package/src/types/pragma-options.ts +23 -0
- package/src/types/sql-tag-store-instance.ts +1 -1
- package/src/types/statement-sync-instance.ts +38 -12
- package/src/types/transaction.ts +72 -0
- package/src/upstream/node_sqlite.cc +143 -43
- package/src/upstream/node_sqlite.h +15 -11
- package/src/upstream/sqlite3.c +102 -58
- package/src/upstream/sqlite3.h +5 -5
- package/src/user_function.cpp +138 -141
- package/src/user_function.h +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.0] - to be released
|
|
6
|
+
|
|
7
|
+
API compatible with `node:sqlite` from Node.js v25.6.1.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`enhance()` function**: Adds better-sqlite3-style `.pragma()` and `.transaction()` methods to any compatible database instance
|
|
12
|
+
- **`isEnhanced()` type guard**: Check if a database has enhanced methods
|
|
13
|
+
- **Transaction helper**: Automatic BEGIN/COMMIT/ROLLBACK with savepoint support for nested transactions
|
|
14
|
+
- **Pragma convenience method**: Simple API for reading and setting SQLite pragmas with `simple` option
|
|
15
|
+
- **Node.js test sync script**: `npm run sync:tests` downloads and adapts upstream Node.js SQLite tests
|
|
16
|
+
- **Percentile extension**: `SQLITE_ENABLE_PERCENTILE` now enabled, adding `percentile()`, `median()`, `percentile_cont()`, `percentile_disc()` SQL functions (Node.js v25+)
|
|
17
|
+
- **Prepare options**: `db.prepare(sql, options)` now accepts per-statement options (`readBigInts`, `returnArrays`, `allowBareNamedParameters`, `allowUnknownNamedParameters`) to override database-level defaults. This is a Node.js v25+ feature; `node:sqlite` on v24 and earlier silently ignores these options.
|
|
18
|
+
- **StatementColumnMetadata type**: `stmt.columns()` now returns richer metadata including `column`, `database`, `table`, and `type` properties alongside `name`
|
|
19
|
+
- **SQLite 3.51.2**: Updated from 3.51.1
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **BREAKING**: Removed API extensions to achieve exact parity with `node:sqlite`:
|
|
24
|
+
- Removed `stmt.finalize()` method (use database close for cleanup)
|
|
25
|
+
- Removed `stmt.finalized` property
|
|
26
|
+
- Removed `stmt[Symbol.dispose]` (still available on `DatabaseSync` and `Session`)
|
|
27
|
+
- Removed `db.backup()` instance method (use standalone `backup(db, path)` function instead)
|
|
28
|
+
- **BREAKING**: `Session.changeset()` and `Session.patchset()` now return `Uint8Array` instead of `Buffer` to match `node:sqlite` API
|
|
29
|
+
- **BREAKING**: Defensive mode now defaults to `true` instead of `false` to match Node.js v25+ behavior. Use `{ defensive: false }` to restore old behavior.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **Alpine Linux / musl stability**: Fixed native crashes by removing N-API reference cleanup from destructors that corrupted V8 JIT state
|
|
34
|
+
- **Session lifecycle management**: Fixed use-after-free, double-free, and mutex deadlock when databases are garbage collected before their sessions
|
|
35
|
+
- **Worker thread stability**: Added cleanup hooks and exception handling for worker thread termination
|
|
36
|
+
- **Callback error preservation**: `applyChangeset()` now preserves the original error message when JavaScript callbacks throw
|
|
37
|
+
- `createTagStore()` now throws errors with `code: 'ERR_INVALID_STATE'` property when database is closed, matching Node.js error format
|
|
38
|
+
|
|
39
|
+
## [0.3.0] (2025-12-16)
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **BREAKING**: `SQLTagStore.size` changed from method to getter for Node.js API parity ([Node.js PR #60246](https://github.com/nodejs/node/pull/60246))
|
|
44
|
+
- Before: `sql.size()`
|
|
45
|
+
- After: `sql.size`
|
|
46
|
+
- **Note**: This change was merged into Node.js main on December 11, 2025 and will appear in a future Node.js release. Current Node.js v24.x still uses `sql.size()` as a method.
|
|
47
|
+
|
|
5
48
|
## [0.2.1] (2025-12-01)
|
|
6
49
|
|
|
7
50
|
### Added
|
|
@@ -22,11 +65,11 @@ All notable changes to this project will be documented in this file.
|
|
|
22
65
|
- **Defensive mode**: `enableDefensive()` method to prevent SQL from deliberately corrupting the database
|
|
23
66
|
- **Statement enhancements**: `setAllowUnknownNamedParameters()` method, `finalized` property
|
|
24
67
|
- **Type identification**: `sqlite-type` symbol property on DatabaseSync (Node.js PR #59405)
|
|
25
|
-
- **
|
|
68
|
+
- **SQLite error properties**: `sqliteCode`, `sqliteExtendedCode`, `code`, `sqliteErrorString`, `systemErrno`
|
|
26
69
|
- **ARM64 prebuilds**: macOS Apple Silicon and Windows ARM64 binaries
|
|
27
70
|
- **Tagged template literals**: `db.createTagStore()` for cached prepared statements (Node.js PR #58748)
|
|
28
71
|
- **Authorization API**: `db.setAuthorizer()` for security callbacks (Node.js PR #59928)
|
|
29
|
-
- **Standalone backup**: `backup(srcDb, destFile, options?)` for
|
|
72
|
+
- **Standalone backup**: `backup(srcDb, destFile, options?)` for database backups with progress callbacks
|
|
30
73
|
|
|
31
74
|
### Fixed
|
|
32
75
|
|
|
@@ -40,16 +83,16 @@ All notable changes to this project will be documented in this file.
|
|
|
40
83
|
|
|
41
84
|
### Added
|
|
42
85
|
|
|
43
|
-
- Initial release of `@photostructure/sqlite
|
|
44
|
-
-
|
|
86
|
+
- Initial release of `@photostructure/sqlite`, standalone SQLite for Node.js 20+
|
|
87
|
+
- Compatible with Node.js built-in SQLite module API
|
|
45
88
|
- Core SQLite operations with `DatabaseSync` and `StatementSync` classes
|
|
46
|
-
- User-defined scalar and aggregate functions with
|
|
47
|
-
- Database backup and restoration
|
|
89
|
+
- User-defined scalar and aggregate functions with window function support
|
|
90
|
+
- Database backup and restoration
|
|
48
91
|
- SQLite sessions and changesets for change tracking
|
|
49
|
-
- Extension loading
|
|
50
|
-
- TypeScript definitions
|
|
92
|
+
- Extension loading with automatic platform-specific file resolution
|
|
93
|
+
- TypeScript definitions
|
|
51
94
|
- Cross-platform prebuilt binaries for Windows, macOS, and Linux (x64, ARM64)
|
|
52
|
-
-
|
|
95
|
+
- Test suite with 89+ tests
|
|
53
96
|
- Memory safety validation with Valgrind and sanitizers
|
|
54
97
|
- Performance benchmarking suite comparing to better-sqlite3
|
|
55
98
|
- Automated synchronization from Node.js upstream SQLite implementation
|
|
@@ -57,15 +100,15 @@ All notable changes to this project will be documented in this file.
|
|
|
57
100
|
|
|
58
101
|
### Features
|
|
59
102
|
|
|
60
|
-
- **Synchronous API**:
|
|
61
|
-
- **Parameter binding**:
|
|
103
|
+
- **Synchronous API**: Blocking database operations for scripts and tools
|
|
104
|
+
- **Parameter binding**: All SQLite data types including BigInt
|
|
62
105
|
- **Error handling**: Detailed error messages with SQLite error codes
|
|
63
106
|
- **Resource limits**: Control memory usage and query complexity
|
|
64
107
|
- **Safe integer handling**: JavaScript-safe integer conversion with overflow detection
|
|
65
|
-
- **Multi-process support**:
|
|
66
|
-
- **Worker thread support**:
|
|
67
|
-
- **URI filename support**:
|
|
68
|
-
- **Strict tables**:
|
|
108
|
+
- **Multi-process support**: Concurrent access from multiple Node.js processes
|
|
109
|
+
- **Worker thread support**: Works in worker threads
|
|
110
|
+
- **URI filename support**: SQLite URI syntax for advanced database configuration
|
|
111
|
+
- **Strict tables**: SQLite strict table mode
|
|
69
112
|
- **Double-quoted strings**: Configurable SQL syntax compatibility
|
|
70
113
|
|
|
71
114
|
### Platform Support
|
|
@@ -75,5 +118,8 @@ All notable changes to this project will be documented in this file.
|
|
|
75
118
|
- macOS (x64, ARM64)
|
|
76
119
|
- Linux (x64, ARM64), (glibc 2.28+, musl)
|
|
77
120
|
|
|
121
|
+
[0.4.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.4.0
|
|
122
|
+
[0.3.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.3.0
|
|
123
|
+
[0.2.1]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.2.1
|
|
78
124
|
[0.2.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.2.0
|
|
79
125
|
[0.0.1]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.0.1
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@photostructure/sqlite)
|
|
4
4
|
[](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml)
|
|
5
5
|
|
|
6
|
-
Native SQLite for Node.js 20+ without the experimental flag. Drop-in replacement for `node:sqlite`.
|
|
6
|
+
Native SQLite for Node.js 20+ without the experimental flag. Drop-in replacement for `node:sqlite`. Synced with Node.js v25.6.1 for the latest features including native `Symbol.dispose` resource management.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -27,17 +27,18 @@ db.close();
|
|
|
27
27
|
|
|
28
28
|
## Features
|
|
29
29
|
|
|
30
|
-
- 100% compatible with Node.js v25 built-in `node:sqlite` module\*
|
|
30
|
+
- 100% compatible with Node.js v25.6.1 built-in `node:sqlite` module\*
|
|
31
31
|
- Zero dependencies - native SQLite implementation
|
|
32
32
|
- Synchronous API - no async overhead
|
|
33
|
-
- Performance
|
|
33
|
+
- Performance comparable to better-sqlite3
|
|
34
34
|
- Full SQLite feature set ([details](./doc/features.md))
|
|
35
35
|
- TypeScript support with complete type definitions
|
|
36
36
|
- Cross-platform prebuilt binaries (Windows/macOS/Linux, x64/ARM64)
|
|
37
37
|
- User-defined functions and aggregates
|
|
38
38
|
- Database backups and session/changeset support
|
|
39
39
|
- Session class exposed for advanced replication workflows
|
|
40
|
-
- Native Symbol.dispose for
|
|
40
|
+
- Native `Symbol.dispose` for resource management
|
|
41
|
+
- `enhance()` function for better-sqlite3 style `.pragma()` and `.transaction()` methods
|
|
41
42
|
- URI filename support for advanced configuration
|
|
42
43
|
- Worker thread safe
|
|
43
44
|
- [Compare with other libraries →](./doc/library-comparison.md)
|
package/binding.gyp
CHANGED
|
@@ -11,13 +11,12 @@
|
|
|
11
11
|
"src/upstream/sqlite3.c"
|
|
12
12
|
],
|
|
13
13
|
"include_dirs": [
|
|
14
|
-
"<!@(node -p \"require('node-addon-api').include\")",
|
|
15
14
|
"src",
|
|
16
15
|
"src/upstream",
|
|
17
16
|
"src/shims"
|
|
18
17
|
],
|
|
19
18
|
"dependencies": [
|
|
20
|
-
"<!(node -p \"require('node-addon-api').
|
|
19
|
+
"<!(node -p \"require('node-addon-api').targets + ':node_addon_api_except'\")"
|
|
21
20
|
],
|
|
22
21
|
# SQLite build flags - see doc/build-flags.md for comprehensive documentation
|
|
23
22
|
# including comparison with Node.js configuration and rationale for our choices
|
|
@@ -40,6 +39,7 @@
|
|
|
40
39
|
"SQLITE_ENABLE_JSON1",
|
|
41
40
|
"SQLITE_ENABLE_MATH_FUNCTIONS",
|
|
42
41
|
"SQLITE_ENABLE_NORMALIZE",
|
|
42
|
+
"SQLITE_ENABLE_PERCENTILE",
|
|
43
43
|
"SQLITE_ENABLE_PREUPDATE_HOOK",
|
|
44
44
|
"SQLITE_ENABLE_RBU",
|
|
45
45
|
"SQLITE_ENABLE_RTREE",
|
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,9 @@ __export(index_exports, {
|
|
|
36
36
|
StatementSync: () => StatementSync,
|
|
37
37
|
backup: () => backup,
|
|
38
38
|
constants: () => constants,
|
|
39
|
-
default: () => index_default
|
|
39
|
+
default: () => index_default,
|
|
40
|
+
enhance: () => enhance,
|
|
41
|
+
isEnhanced: () => isEnhanced
|
|
40
42
|
});
|
|
41
43
|
module.exports = __toCommonJS(index_exports);
|
|
42
44
|
var import_node_gyp_build = __toESM(require("node-gyp-build"));
|
|
@@ -187,7 +189,9 @@ var SQLTagStore = class {
|
|
|
187
189
|
maxCapacity;
|
|
188
190
|
constructor(db, capacity = DEFAULT_CAPACITY) {
|
|
189
191
|
if (!db.isOpen) {
|
|
190
|
-
|
|
192
|
+
const err = new Error("database is not open");
|
|
193
|
+
err.code = "ERR_INVALID_STATE";
|
|
194
|
+
throw err;
|
|
191
195
|
}
|
|
192
196
|
this.database = db;
|
|
193
197
|
this.maxCapacity = capacity;
|
|
@@ -208,7 +212,7 @@ var SQLTagStore = class {
|
|
|
208
212
|
/**
|
|
209
213
|
* Returns the current number of cached statements.
|
|
210
214
|
*/
|
|
211
|
-
size() {
|
|
215
|
+
get size() {
|
|
212
216
|
return this.cache.size();
|
|
213
217
|
}
|
|
214
218
|
/**
|
|
@@ -248,19 +252,15 @@ var SQLTagStore = class {
|
|
|
248
252
|
}
|
|
249
253
|
/**
|
|
250
254
|
* Get a cached statement or prepare a new one.
|
|
251
|
-
* If a cached statement has been finalized, it's evicted and a new one is prepared.
|
|
252
255
|
*/
|
|
253
256
|
getOrPrepare(strings) {
|
|
254
257
|
if (!this.database.isOpen) {
|
|
255
|
-
throw new Error("
|
|
258
|
+
throw new Error("database is not open");
|
|
256
259
|
}
|
|
257
260
|
const sql = this.buildSQL(strings);
|
|
258
261
|
const cached = this.cache.get(sql);
|
|
259
262
|
if (cached) {
|
|
260
|
-
|
|
261
|
-
return cached;
|
|
262
|
-
}
|
|
263
|
-
this.cache.delete(sql);
|
|
263
|
+
return cached;
|
|
264
264
|
}
|
|
265
265
|
const stmt = this.database.prepare(sql);
|
|
266
266
|
this.cache.set(sql, stmt);
|
|
@@ -278,13 +278,158 @@ var SQLTagStore = class {
|
|
|
278
278
|
}
|
|
279
279
|
};
|
|
280
280
|
|
|
281
|
+
// src/transaction.ts
|
|
282
|
+
var savepointCounter = 0;
|
|
283
|
+
function createTransaction(db, fn) {
|
|
284
|
+
if (typeof fn !== "function") {
|
|
285
|
+
throw new TypeError("Expected first argument to be a function");
|
|
286
|
+
}
|
|
287
|
+
const variants = {
|
|
288
|
+
deferred: createVariant(db, fn, "deferred"),
|
|
289
|
+
immediate: createVariant(db, fn, "immediate"),
|
|
290
|
+
exclusive: createVariant(db, fn, "exclusive")
|
|
291
|
+
};
|
|
292
|
+
const defaultFn = variants.deferred;
|
|
293
|
+
for (const variant of Object.values(variants)) {
|
|
294
|
+
Object.defineProperties(variant, {
|
|
295
|
+
deferred: { value: variants.deferred, enumerable: true },
|
|
296
|
+
immediate: { value: variants.immediate, enumerable: true },
|
|
297
|
+
exclusive: { value: variants.exclusive, enumerable: true },
|
|
298
|
+
database: { value: db, enumerable: true }
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return defaultFn;
|
|
302
|
+
}
|
|
303
|
+
function createVariant(db, fn, mode) {
|
|
304
|
+
const beginStatement = getBeginStatement(mode);
|
|
305
|
+
return function transactionWrapper(...args) {
|
|
306
|
+
const isNested = db.isTransaction;
|
|
307
|
+
let begin;
|
|
308
|
+
let commit;
|
|
309
|
+
let rollback;
|
|
310
|
+
if (isNested) {
|
|
311
|
+
const savepointName = `\`_txn_${++savepointCounter}\``;
|
|
312
|
+
begin = `SAVEPOINT ${savepointName}`;
|
|
313
|
+
commit = `RELEASE ${savepointName}`;
|
|
314
|
+
rollback = `ROLLBACK TO ${savepointName}`;
|
|
315
|
+
} else {
|
|
316
|
+
begin = beginStatement;
|
|
317
|
+
commit = "COMMIT";
|
|
318
|
+
rollback = "ROLLBACK";
|
|
319
|
+
}
|
|
320
|
+
db.exec(begin);
|
|
321
|
+
try {
|
|
322
|
+
const result = fn.apply(this, args);
|
|
323
|
+
if (result !== null && typeof result === "object" && "then" in result) {
|
|
324
|
+
throw new TypeError(
|
|
325
|
+
"Transaction function must not return a Promise. SQLite transactions are synchronous and cannot span across async operations. Use synchronous code within transactions."
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
db.exec(commit);
|
|
329
|
+
return result;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (db.isTransaction) {
|
|
332
|
+
db.exec(rollback);
|
|
333
|
+
if (isNested) {
|
|
334
|
+
db.exec(commit);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function getBeginStatement(mode) {
|
|
342
|
+
switch (mode) {
|
|
343
|
+
case "deferred":
|
|
344
|
+
return "BEGIN DEFERRED";
|
|
345
|
+
case "immediate":
|
|
346
|
+
return "BEGIN IMMEDIATE";
|
|
347
|
+
case "exclusive":
|
|
348
|
+
return "BEGIN EXCLUSIVE";
|
|
349
|
+
default:
|
|
350
|
+
throw new Error(`Unknown transaction mode: ${mode}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/enhance.ts
|
|
355
|
+
function pragmaImpl(source, options) {
|
|
356
|
+
if (typeof source !== "string") {
|
|
357
|
+
throw new TypeError("Expected first argument to be a string");
|
|
358
|
+
}
|
|
359
|
+
if (options != null && typeof options !== "object") {
|
|
360
|
+
throw new TypeError("Expected second argument to be an options object");
|
|
361
|
+
}
|
|
362
|
+
const simple = options?.simple === true;
|
|
363
|
+
if (options != null && "simple" in options && typeof options.simple !== "boolean") {
|
|
364
|
+
throw new TypeError('Expected the "simple" option to be a boolean');
|
|
365
|
+
}
|
|
366
|
+
const stmt = this.prepare(`PRAGMA ${source}`);
|
|
367
|
+
const rows = stmt.all();
|
|
368
|
+
if (simple) {
|
|
369
|
+
const firstRow = rows[0];
|
|
370
|
+
if (firstRow == null) {
|
|
371
|
+
return void 0;
|
|
372
|
+
}
|
|
373
|
+
const keys = Object.keys(firstRow);
|
|
374
|
+
const firstKey = keys[0];
|
|
375
|
+
if (firstKey == null) {
|
|
376
|
+
return void 0;
|
|
377
|
+
}
|
|
378
|
+
return firstRow[firstKey];
|
|
379
|
+
}
|
|
380
|
+
return rows;
|
|
381
|
+
}
|
|
382
|
+
function transactionImpl(fn) {
|
|
383
|
+
return createTransaction(this, fn);
|
|
384
|
+
}
|
|
385
|
+
function hasEnhancedMethods(db) {
|
|
386
|
+
return typeof db.pragma === "function" && typeof db.transaction === "function";
|
|
387
|
+
}
|
|
388
|
+
function enhance(db) {
|
|
389
|
+
if (hasEnhancedMethods(db)) {
|
|
390
|
+
return db;
|
|
391
|
+
}
|
|
392
|
+
Object.defineProperty(db, "pragma", {
|
|
393
|
+
value: pragmaImpl,
|
|
394
|
+
writable: true,
|
|
395
|
+
configurable: true,
|
|
396
|
+
enumerable: false
|
|
397
|
+
});
|
|
398
|
+
Object.defineProperty(db, "transaction", {
|
|
399
|
+
value: transactionImpl,
|
|
400
|
+
writable: true,
|
|
401
|
+
configurable: true,
|
|
402
|
+
enumerable: false
|
|
403
|
+
});
|
|
404
|
+
return db;
|
|
405
|
+
}
|
|
406
|
+
function isEnhanced(db) {
|
|
407
|
+
return hasEnhancedMethods(db);
|
|
408
|
+
}
|
|
409
|
+
|
|
281
410
|
// src/index.ts
|
|
282
411
|
var binding = (0, import_node_gyp_build.default)((0, import_node_path2.join)(_dirname(), ".."));
|
|
283
|
-
var
|
|
412
|
+
var _DatabaseSync = binding.DatabaseSync;
|
|
413
|
+
var DatabaseSync = function DatabaseSync2(...args) {
|
|
414
|
+
if (!new.target) {
|
|
415
|
+
const err = new TypeError("Cannot call constructor without `new`");
|
|
416
|
+
err.code = "ERR_CONSTRUCT_CALL_REQUIRED";
|
|
417
|
+
throw err;
|
|
418
|
+
}
|
|
419
|
+
return Reflect.construct(_DatabaseSync, args, new.target);
|
|
420
|
+
};
|
|
421
|
+
Object.setPrototypeOf(DatabaseSync, _DatabaseSync);
|
|
422
|
+
DatabaseSync.prototype = _DatabaseSync.prototype;
|
|
284
423
|
DatabaseSync.prototype.createTagStore = function(capacity) {
|
|
285
424
|
return new SQLTagStore(this, capacity);
|
|
286
425
|
};
|
|
287
|
-
var
|
|
426
|
+
var _StatementSync = binding.StatementSync;
|
|
427
|
+
var StatementSync = function StatementSync2() {
|
|
428
|
+
const err = new TypeError("Illegal constructor");
|
|
429
|
+
err.code = "ERR_ILLEGAL_CONSTRUCTOR";
|
|
430
|
+
throw err;
|
|
431
|
+
};
|
|
432
|
+
StatementSync.prototype = _StatementSync.prototype;
|
|
288
433
|
var Session = binding.Session;
|
|
289
434
|
var constants = binding.constants;
|
|
290
435
|
var backup = binding.backup;
|
|
@@ -296,6 +441,8 @@ var index_default = binding;
|
|
|
296
441
|
Session,
|
|
297
442
|
StatementSync,
|
|
298
443
|
backup,
|
|
299
|
-
constants
|
|
444
|
+
constants,
|
|
445
|
+
enhance,
|
|
446
|
+
isEnhanced
|
|
300
447
|
});
|
|
301
448
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts"],"sourcesContent":["// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Buffer containing the changeset data.\n */\n changeset(): Buffer;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Buffer containing the patchset data.\n */\n patchset(): Buffer;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\nexport const DatabaseSync =\n binding.DatabaseSync as SqliteModule[\"DatabaseSync\"];\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\nexport const StatementSync =\n binding.StatementSync as SqliteModule[\"StatementSync\"];\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n throw new Error(\"Database is not open\");\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n size(): number {\n return this.cache.size();\n }\n\n /**\n * Clears all cached statements.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Execute an INSERT, UPDATE, DELETE or other statement that doesn't return rows.\n * Returns an object with `changes` and `lastInsertRowid`.\n */\n run(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): { changes: number; lastInsertRowid: number | bigint } {\n const stmt = this.getOrPrepare(strings);\n return stmt.run(...values);\n }\n\n /**\n * Execute a query and return the first row, or undefined if no rows.\n */\n get(strings: TemplateStringsArray, ...values: unknown[]): unknown {\n const stmt = this.getOrPrepare(strings);\n return stmt.get(...values);\n }\n\n /**\n * Execute a query and return all rows as an array.\n */\n all(strings: TemplateStringsArray, ...values: unknown[]): unknown[] {\n const stmt = this.getOrPrepare(strings);\n return stmt.all(...values);\n }\n\n /**\n * Execute a query and return an iterator over the rows.\n */\n iterate(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): IterableIterator<unknown> {\n const stmt = this.getOrPrepare(strings);\n return stmt.iterate(...values);\n }\n\n /**\n * Get a cached statement or prepare a new one.\n * If a cached statement has been finalized, it's evicted and a new one is prepared.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"Database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache - evict if finalized\n const cached = this.cache.get(sql);\n if (cached) {\n if (!cached.finalized) {\n return cached;\n }\n // Statement was finalized externally - remove from cache\n this.cache.delete(sql);\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n"],"mappings":"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","names":["import_node_path","nodeGypBuild"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts","../src/transaction.ts","../src/enhance.ts"],"sourcesContent":["// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { PragmaOptions } from \"./types/pragma-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type {\n StatementColumnMetadata,\n StatementSyncInstance,\n} from \"./types/statement-sync-instance\";\nexport type { TransactionFunction, TransactionMode } from \"./types/transaction\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Enhancement utilities for adding better-sqlite3-style methods to any compatible database\nexport {\n enhance,\n isEnhanced,\n type EnhanceableDatabaseSync,\n type EnhancedDatabaseSync,\n type EnhancedMethods,\n} from \"./enhance\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n *\n * **Note:** The per-statement override options (`readBigInts`, `returnArrays`,\n * `allowBareNamedParameters`, `allowUnknownNamedParameters`) are a **Node.js v25+**\n * feature. On Node.js v24 and earlier, `node:sqlite` silently ignores these options.\n * This library implements them for forward compatibility with Node.js v25+.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n /**\n * If true, read integer values as JavaScript BigInt. Overrides database-level setting.\n * **Node.js v25+ feature** - silently ignored by `node:sqlite` on v24 and earlier.\n * @default database default\n */\n readonly readBigInts?: boolean;\n /**\n * If true, return results as arrays rather than objects. Overrides database-level setting.\n * **Node.js v25+ feature** - silently ignored by `node:sqlite` on v24 and earlier.\n * @default database default\n */\n readonly returnArrays?: boolean;\n /**\n * If true, allows bare named parameters (without prefix). Overrides database-level setting.\n * **Node.js v25+ feature** - silently ignored by `node:sqlite` on v24 and earlier.\n * @default database default\n */\n readonly allowBareNamedParameters?: boolean;\n /**\n * If true, unknown named parameters are ignored. Overrides database-level setting.\n * **Node.js v25+ feature** - silently ignored by `node:sqlite` on v24 and earlier.\n * @default database default\n */\n readonly allowUnknownNamedParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Uint8Array containing the changeset data.\n */\n changeset(): Uint8Array;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Uint8Array containing the patchset data.\n */\n patchset(): Uint8Array;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\n// Store the native binding's DatabaseSync\nconst _DatabaseSync = binding.DatabaseSync;\n// Wrapper around the native constructor to enforce usage of `new` with the correct error code.\n// We use a function wrapper instead of a Proxy for better performance and explicit prototype handling.\nexport const DatabaseSync = function DatabaseSync(this: any, ...args: any[]) {\n if (!new.target) {\n const err = new TypeError(\"Cannot call constructor without `new`\");\n (err as NodeJS.ErrnoException).code = \"ERR_CONSTRUCT_CALL_REQUIRED\";\n throw err;\n }\n return Reflect.construct(_DatabaseSync, args, new.target);\n} as unknown as SqliteModule[\"DatabaseSync\"];\nObject.setPrototypeOf(DatabaseSync, _DatabaseSync);\nDatabaseSync.prototype = _DatabaseSync.prototype;\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n// NOTE: .pragma() and .transaction() are NOT added to the prototype by default.\n// This keeps DatabaseSync 100% API-compatible with node:sqlite.\n// Users who want better-sqlite3-style methods should use enhance():\n//\n// import { DatabaseSync, enhance } from '@photostructure/sqlite';\n// const db = enhance(new DatabaseSync(':memory:'));\n// db.pragma('journal_mode', { simple: true });\n// db.transaction(() => { ... });\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\n// Store the native binding's StatementSync for internal use\nconst _StatementSync = binding.StatementSync;\n// Export a wrapper that throws ERR_ILLEGAL_CONSTRUCTOR when called directly\n// but preserves instanceof checks and prototype chain\nexport const StatementSync = function StatementSync() {\n const err = new TypeError(\"Illegal constructor\");\n (err as NodeJS.ErrnoException).code = \"ERR_ILLEGAL_CONSTRUCTOR\";\n throw err;\n} as unknown as SqliteModule[\"StatementSync\"];\n// Use the native prototype directly so instanceof checks work correctly\n// (stmt instanceof StatementSync will check if StatementSync.prototype is in stmt's chain)\nStatementSync.prototype = _StatementSync.prototype;\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n const err = new Error(\"database is not open\");\n (err as NodeJS.ErrnoException).code = \"ERR_INVALID_STATE\";\n throw err;\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n get size(): number {\n return this.cache.size();\n }\n\n /**\n * Clears all cached statements.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Execute an INSERT, UPDATE, DELETE or other statement that doesn't return rows.\n * Returns an object with `changes` and `lastInsertRowid`.\n */\n run(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): { changes: number; lastInsertRowid: number | bigint } {\n const stmt = this.getOrPrepare(strings);\n return stmt.run(...values);\n }\n\n /**\n * Execute a query and return the first row, or undefined if no rows.\n */\n get(strings: TemplateStringsArray, ...values: unknown[]): unknown {\n const stmt = this.getOrPrepare(strings);\n return stmt.get(...values);\n }\n\n /**\n * Execute a query and return all rows as an array.\n */\n all(strings: TemplateStringsArray, ...values: unknown[]): unknown[] {\n const stmt = this.getOrPrepare(strings);\n return stmt.all(...values);\n }\n\n /**\n * Execute a query and return an iterator over the rows.\n */\n iterate(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): IterableIterator<unknown> {\n const stmt = this.getOrPrepare(strings);\n return stmt.iterate(...values);\n }\n\n /**\n * Get a cached statement or prepare a new one.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache\n const cached = this.cache.get(sql);\n if (cached) {\n return cached;\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n","import type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { TransactionFunction, TransactionMode } from \"./types/transaction\";\n\n/**\n * Internal counter for generating unique savepoint names.\n * Using a global counter ensures uniqueness even with deeply nested transactions.\n */\nlet savepointCounter = 0;\n\n/**\n * Creates a transaction-wrapped version of a function.\n *\n * When the returned function is called, it will:\n * 1. Begin a transaction (or create a savepoint if already in a transaction)\n * 2. Execute the wrapped function\n * 3. Commit the transaction (or release the savepoint) on success\n * 4. Rollback the transaction (or rollback to savepoint) on error\n *\n * The wrapped function **must not** return a Promise. SQLite transactions are\n * synchronous, and allowing async operations would leave the transaction open\n * across event loop ticks, which is dangerous and can cause deadlocks.\n *\n * @example\n * ```typescript\n * const db = new DatabaseSync(':memory:');\n * db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)');\n *\n * const insert = db.prepare('INSERT INTO items (name) VALUES (?)');\n *\n * // Create a transaction function\n * const insertMany = db.transaction((names: string[]) => {\n * for (const name of names) {\n * insert.run(name);\n * }\n * return names.length;\n * });\n *\n * // Execute - automatically wrapped in BEGIN/COMMIT\n * const count = insertMany(['Alice', 'Bob', 'Charlie']);\n * console.log(count); // 3\n *\n * // If an error occurs, automatically rolls back\n * try {\n * insertMany(['Dave', 'FAIL']); // Assume this throws\n * } catch (e) {\n * // Transaction was rolled back, Dave was not inserted\n * }\n *\n * // Use different transaction modes\n * insertMany.immediate(['Eve']); // BEGIN IMMEDIATE\n * insertMany.exclusive(['Frank']); // BEGIN EXCLUSIVE\n * ```\n *\n * @param db - The database connection\n * @param fn - The function to wrap in a transaction\n * @returns A transaction function with `.deferred`, `.immediate`, and `.exclusive` variants\n */\nexport function createTransaction<F extends (...args: any[]) => any>(\n db: DatabaseSyncInstance,\n fn: F,\n): TransactionFunction<F> {\n if (typeof fn !== \"function\") {\n throw new TypeError(\"Expected first argument to be a function\");\n }\n\n // Create all four variants\n const variants = {\n deferred: createVariant(db, fn, \"deferred\"),\n immediate: createVariant(db, fn, \"immediate\"),\n exclusive: createVariant(db, fn, \"exclusive\"),\n };\n\n // The default function uses DEFERRED mode (SQLite's default)\n const defaultFn = variants.deferred as TransactionFunction<F>;\n\n // Set up the variant properties on each function\n // Each variant has access to all other variants\n for (const variant of Object.values(variants)) {\n Object.defineProperties(variant, {\n deferred: { value: variants.deferred, enumerable: true },\n immediate: { value: variants.immediate, enumerable: true },\n exclusive: { value: variants.exclusive, enumerable: true },\n database: { value: db, enumerable: true },\n });\n }\n\n return defaultFn;\n}\n\n/**\n * Creates a single transaction variant for a specific mode.\n */\nfunction createVariant<F extends (...args: any[]) => any>(\n db: DatabaseSyncInstance,\n fn: F,\n mode: TransactionMode,\n): (...args: Parameters<F>) => ReturnType<F> {\n const beginStatement = getBeginStatement(mode);\n\n return function transactionWrapper(\n this: unknown,\n ...args: Parameters<F>\n ): ReturnType<F> {\n // Check if we're already in a transaction (nested transaction)\n const isNested = db.isTransaction;\n\n let begin: string;\n let commit: string;\n let rollback: string;\n\n if (isNested) {\n // Use savepoints for nested transactions\n // The savepoint name uses backticks to allow special characters\n // and a counter to ensure uniqueness\n const savepointName = `\\`_txn_${++savepointCounter}\\``;\n begin = `SAVEPOINT ${savepointName}`;\n commit = `RELEASE ${savepointName}`;\n rollback = `ROLLBACK TO ${savepointName}`;\n } else {\n // Top-level transaction\n begin = beginStatement;\n commit = \"COMMIT\";\n rollback = \"ROLLBACK\";\n }\n\n // Begin the transaction or savepoint\n db.exec(begin);\n\n try {\n // Execute the wrapped function\n const result = fn.apply(this, args);\n\n // Check for promises - async functions break transaction semantics\n if (result !== null && typeof result === \"object\" && \"then\" in result) {\n throw new TypeError(\n \"Transaction function must not return a Promise. \" +\n \"SQLite transactions are synchronous and cannot span across async operations. \" +\n \"Use synchronous code within transactions.\",\n );\n }\n\n // Commit the transaction or release the savepoint\n db.exec(commit);\n\n return result;\n } catch (error) {\n // Only attempt rollback if we're still in a transaction\n // (SQLite may have already rolled back due to constraint violations, etc.)\n if (db.isTransaction) {\n db.exec(rollback);\n // For nested transactions, we also need to release the savepoint\n // after rolling back to it (the savepoint still exists after ROLLBACK TO)\n if (isNested) {\n db.exec(commit);\n }\n }\n\n throw error;\n }\n };\n}\n\n/**\n * Returns the appropriate BEGIN statement for a transaction mode.\n */\nfunction getBeginStatement(mode: TransactionMode): string {\n switch (mode) {\n case \"deferred\":\n return \"BEGIN DEFERRED\";\n case \"immediate\":\n return \"BEGIN IMMEDIATE\";\n case \"exclusive\":\n return \"BEGIN EXCLUSIVE\";\n default:\n // TypeScript should catch this, but just in case\n throw new Error(`Unknown transaction mode: ${mode}`);\n }\n}\n","/**\n * Enhancement utilities for adding better-sqlite3-style methods to any\n * compatible database, including `node:sqlite` DatabaseSync and this package's\n * DatabaseSync.\n *\n * This module provides the `enhance()` function which adds `.pragma()` and\n * `.transaction()` methods to database instances that don't have them (e.g.,\n * node:sqlite DatabaseSync).\n */\n\nimport { createTransaction } from \"./transaction\";\nimport type { PragmaOptions } from \"./types/pragma-options\";\nimport type { TransactionFunction } from \"./types/transaction\";\n\n/**\n * Minimal interface for a database that can be enhanced. This matches the\n * subset of functionality needed by pragma() and transaction().\n */\nexport interface EnhanceableDatabaseSync {\n /** Execute SQL without returning results */\n exec(sql: string): void;\n /** Prepare a statement that can return results */\n prepare(sql: string): { all(): unknown[] };\n /** Whether a transaction is currently active */\n readonly isTransaction: boolean;\n}\n\n/**\n * Interface for an enhanced database with pragma() and transaction() methods.\n */\nexport interface EnhancedMethods {\n /**\n * Executes a PRAGMA statement and returns its result.\n *\n * @param source The PRAGMA command (without \"PRAGMA\" prefix)\n * @param options Optional configuration\n * @returns Array of rows, or single value if `simple: true`\n *\n * @example\n * ```typescript\n * db.pragma('cache_size', { simple: true }); // -16000\n * db.pragma('journal_mode = wal');\n * ```\n */\n pragma(source: string, options?: PragmaOptions): unknown;\n\n /**\n * Creates a function that always runs inside a transaction.\n *\n * @param fn The function to wrap in a transaction\n * @returns A transaction function with `.deferred`, `.immediate`,\n * `.exclusive` variants\n *\n * @example\n * ```typescript\n * const insertMany = db.transaction((items) => {\n * for (const item of items) insert.run(item);\n * });\n * insertMany(['a', 'b', 'c']); // All in one transaction\n * ```\n */\n transaction<F extends (...args: any[]) => any>(fn: F): TransactionFunction<F>;\n}\n\n/**\n * A database instance that has been enhanced with pragma() and transaction() methods.\n */\nexport type EnhancedDatabaseSync<T extends EnhanceableDatabaseSync> = T &\n EnhancedMethods;\n\n/**\n * Implementation of pragma() that works on any EnhanceableDatabaseSync.\n */\nfunction pragmaImpl(\n this: EnhanceableDatabaseSync,\n source: string,\n options?: PragmaOptions,\n): unknown {\n if (typeof source !== \"string\") {\n throw new TypeError(\"Expected first argument to be a string\");\n }\n if (options != null && typeof options !== \"object\") {\n throw new TypeError(\"Expected second argument to be an options object\");\n }\n\n const simple = options?.simple === true;\n\n // Validate that simple is a boolean if provided\n if (\n options != null &&\n \"simple\" in options &&\n typeof options.simple !== \"boolean\"\n ) {\n throw new TypeError('Expected the \"simple\" option to be a boolean');\n }\n\n const stmt = this.prepare(`PRAGMA ${source}`);\n const rows = stmt.all() as Record<string, unknown>[];\n\n if (simple) {\n // Return the first column of the first row, or undefined if no rows\n const firstRow = rows[0];\n if (firstRow == null) {\n return undefined;\n }\n const keys = Object.keys(firstRow);\n const firstKey = keys[0];\n if (firstKey == null) {\n return undefined;\n }\n return firstRow[firstKey];\n }\n\n return rows;\n}\n\n/**\n * Implementation of transaction() that works on any EnhanceableDatabaseSync.\n */\nfunction transactionImpl<F extends (...args: any[]) => any>(\n this: EnhanceableDatabaseSync,\n fn: F,\n): TransactionFunction<F> {\n // createTransaction expects DatabaseSyncInstance but only uses the subset\n // defined in EnhanceableDatabaseSync, so this cast is safe\n return createTransaction(this as any, fn);\n}\n\n/**\n * Checks if a database instance already has the enhanced methods.\n */\nfunction hasEnhancedMethods(\n db: EnhanceableDatabaseSync,\n): db is EnhanceableDatabaseSync & EnhancedMethods {\n return (\n typeof (db as any).pragma === \"function\" &&\n typeof (db as any).transaction === \"function\"\n );\n}\n\n/**\n * Ensures that `.pragma()` and `.transaction()` methods are available on the\n * given database.\n *\n * This function can enhance:\n * - `node:sqlite` DatabaseSync instances (adds the methods)\n * - `@photostructure/sqlite` DatabaseSync instances (no-op, already has these\n * methods)\n * - Any object with compatible `exec()`, `prepare()`, and `isTransaction`\n *\n * The enhancement is done by adding methods directly to the instance, not the\n * prototype, so it won't affect other instances or the original class.\n *\n * @param db The database instance to enhance\n * @returns The same instance with `.pragma()` and `.transaction()` methods\n * guaranteed\n *\n * @example\n * ```typescript\n * // With node:sqlite\n * import { DatabaseSync } from 'node:sqlite';\n * import { enhance } from '@photostructure/sqlite';\n *\n * const db = enhance(new DatabaseSync(':memory:'));\n *\n * // Now you can use better-sqlite3-style methods\n * db.pragma('journal_mode = wal');\n * const insertMany = db.transaction((items) => {\n * for (const item of items) insert.run(item);\n * });\n * ```\n *\n * @example\n * ```typescript\n * // With @photostructure/sqlite (no-op, already enhanced)\n * import { DatabaseSync, enhance } from '@photostructure/sqlite';\n *\n * const db = enhance(new DatabaseSync(':memory:'));\n * // db already had these methods, enhance() just returns it unchanged\n * ```\n */\nexport function enhance<T extends EnhanceableDatabaseSync>(\n db: T,\n): EnhancedDatabaseSync<T> {\n // If already enhanced, return as-is\n if (hasEnhancedMethods(db)) {\n return db;\n }\n\n // Add methods directly to the instance\n // Using Object.defineProperty to make them non-enumerable like native methods\n Object.defineProperty(db, \"pragma\", {\n value: pragmaImpl,\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(db, \"transaction\", {\n value: transactionImpl,\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n return db as EnhancedDatabaseSync<T>;\n}\n\n/**\n * Type guard to check if a database has enhanced methods.\n *\n * @param db The database to check\n * @returns True if the database has `.pragma()` and `.transaction()` methods\n *\n * @example\n * ```typescript\n * import { isEnhanced } from '@photostructure/sqlite';\n *\n * if (isEnhanced(db)) {\n * db.pragma('cache_size', { simple: true });\n * }\n * ```\n */\nexport function isEnhanced(\n db: EnhanceableDatabaseSync,\n): db is EnhanceableDatabaseSync & EnhancedMethods {\n return hasEnhancedMethods(db);\n}\n"],"mappings":"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","names":["import_node_path","nodeGypBuild","DatabaseSync","StatementSync"]}
|