@photostructure/sqlite 0.5.0 → 1.0.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 +16 -0
- package/README.md +4 -2
- package/binding.gyp +2 -0
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -1
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.mjs +65 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- 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/src/enhance.ts +7 -4
- package/src/index.ts +84 -2
- package/src/sqlite_impl.cpp +133 -10
- package/src/sqlite_impl.h +19 -0
- package/src/types/database-sync-instance.ts +43 -0
- package/src/types/database-sync-options.ts +19 -0
- package/src/upstream/node_sqlite.cc +312 -17
- package/src/upstream/node_sqlite.h +80 -0
- package/src/upstream/sqlite.js +0 -3
- package/src/upstream/sqlite3.c +5027 -3518
- package/src/upstream/sqlite3.h +195 -58
- package/src/upstream/sqlite3ext.h +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.0] (2026-03-07)
|
|
6
|
+
|
|
7
|
+
Promotion to v1.0.0 following API stabilization and 0.5.0 release.
|
|
8
|
+
|
|
9
|
+
API compatible with `node:sqlite` from Node.js v25.8.1.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`db.limits` property**: Get and set SQLite limits (length, sqlLength, column, exprDepth, compoundSelect, vdbeOp, functionArg, attach, likePatternLength, variableNumber, triggerDepth) at runtime. Supports `Infinity` to reset to compile-time maximum. Also accepts `limits` option in `DatabaseSync` constructor.
|
|
14
|
+
- **Statement iterator invalidation**: Calling `stmt.run()`, `stmt.get()`, `stmt.all()`, or `stmt.iterate()` now invalidates any active iterator on the same statement, throwing `ERR_INVALID_STATE`
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **SQLite 3.52.0**: Updated from 3.51.2
|
|
19
|
+
|
|
5
20
|
## [0.5.0] (2026-02-06)
|
|
6
21
|
|
|
7
22
|
### Added
|
|
@@ -130,6 +145,7 @@ API compatible with `node:sqlite` from Node.js v25.6.1.
|
|
|
130
145
|
- macOS (x64, ARM64)
|
|
131
146
|
- Linux (x64, ARM64), (glibc 2.28+, musl)
|
|
132
147
|
|
|
148
|
+
[1.0.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v1.0.0
|
|
133
149
|
[0.5.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.5.0
|
|
134
150
|
[0.4.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.4.0
|
|
135
151
|
[0.3.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.3.0
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/photostructure/node-sqlite/main/doc/logo.svg" alt="PhotoStructure SQLite logo" width="200">
|
|
2
|
+
|
|
1
3
|
# @photostructure/sqlite
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@photostructure/sqlite)
|
|
4
6
|
[](https://github.com/photostructure/node-sqlite/actions/workflows/build.yml)
|
|
5
7
|
|
|
6
|
-
Native SQLite for Node.js 20
|
|
8
|
+
Native SQLite for Node.js 20+. Drop-in replacement for `node:sqlite`. Synced with Node.js v25.8.1 for the latest features including native `Symbol.dispose` resource management.
|
|
7
9
|
|
|
8
10
|
## Installation
|
|
9
11
|
|
|
@@ -27,7 +29,7 @@ db.close();
|
|
|
27
29
|
|
|
28
30
|
## Features
|
|
29
31
|
|
|
30
|
-
- 100% compatible with Node.js v25.
|
|
32
|
+
- 100% compatible with Node.js v25.8.1 built-in `node:sqlite` module\*
|
|
31
33
|
- Zero dependencies - native SQLite implementation
|
|
32
34
|
- Synchronous API - no async overhead
|
|
33
35
|
- Performance comparable to better-sqlite3
|
package/binding.gyp
CHANGED
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
],
|
|
88
88
|
"msvs_settings": {
|
|
89
89
|
"VCCLCompilerTool": {
|
|
90
|
+
"WarningLevel": 4,
|
|
90
91
|
"AdditionalOptions": [
|
|
91
92
|
"/Qspectre",
|
|
92
93
|
"/guard:cf",
|
|
@@ -115,6 +116,7 @@
|
|
|
115
116
|
],
|
|
116
117
|
"msvs_settings": {
|
|
117
118
|
"VCCLCompilerTool": {
|
|
119
|
+
"WarningLevel": 4,
|
|
118
120
|
"AdditionalOptions": [
|
|
119
121
|
"/guard:cf",
|
|
120
122
|
"/ZH:SHA_256",
|
package/dist/index.cjs
CHANGED
|
@@ -579,6 +579,71 @@ DatabaseSync.prototype = _DatabaseSync.prototype;
|
|
|
579
579
|
DatabaseSync.prototype.createTagStore = function(capacity) {
|
|
580
580
|
return new SQLTagStore(this, capacity);
|
|
581
581
|
};
|
|
582
|
+
var LIMIT_MAPPING = [
|
|
583
|
+
{ name: "length", id: 0 },
|
|
584
|
+
{ name: "sqlLength", id: 1 },
|
|
585
|
+
{ name: "column", id: 2 },
|
|
586
|
+
{ name: "exprDepth", id: 3 },
|
|
587
|
+
{ name: "compoundSelect", id: 4 },
|
|
588
|
+
{ name: "vdbeOp", id: 5 },
|
|
589
|
+
{ name: "functionArg", id: 6 },
|
|
590
|
+
{ name: "attach", id: 7 },
|
|
591
|
+
{ name: "likePatternLength", id: 8 },
|
|
592
|
+
{ name: "variableNumber", id: 9 },
|
|
593
|
+
{ name: "triggerDepth", id: 10 }
|
|
594
|
+
];
|
|
595
|
+
var INT_MAX = 2147483647;
|
|
596
|
+
var limitsCache = /* @__PURE__ */ new WeakMap();
|
|
597
|
+
function validateLimitValue(value) {
|
|
598
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
599
|
+
throw new TypeError(
|
|
600
|
+
"Limit value must be a non-negative integer or Infinity."
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
if (value === Infinity) {
|
|
604
|
+
return INT_MAX;
|
|
605
|
+
}
|
|
606
|
+
if (!Number.isFinite(value) || value !== Math.trunc(value)) {
|
|
607
|
+
throw new TypeError(
|
|
608
|
+
"Limit value must be a non-negative integer or Infinity."
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (value < 0) {
|
|
612
|
+
throw new RangeError("Limit value must be non-negative.");
|
|
613
|
+
}
|
|
614
|
+
return value;
|
|
615
|
+
}
|
|
616
|
+
function createLimitsObject(db) {
|
|
617
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
618
|
+
for (const { name, id } of LIMIT_MAPPING) {
|
|
619
|
+
Object.defineProperty(obj, name, {
|
|
620
|
+
get() {
|
|
621
|
+
return db.getLimit(id);
|
|
622
|
+
},
|
|
623
|
+
set(value) {
|
|
624
|
+
const validated = validateLimitValue(value);
|
|
625
|
+
db.setLimit(id, validated);
|
|
626
|
+
},
|
|
627
|
+
enumerable: true,
|
|
628
|
+
configurable: false
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return obj;
|
|
632
|
+
}
|
|
633
|
+
if (!Object.getOwnPropertyDescriptor(DatabaseSync.prototype, "limits")) {
|
|
634
|
+
Object.defineProperty(DatabaseSync.prototype, "limits", {
|
|
635
|
+
get() {
|
|
636
|
+
let obj = limitsCache.get(this);
|
|
637
|
+
if (obj == null) {
|
|
638
|
+
obj = createLimitsObject(this);
|
|
639
|
+
limitsCache.set(this, obj);
|
|
640
|
+
}
|
|
641
|
+
return obj;
|
|
642
|
+
},
|
|
643
|
+
enumerable: true,
|
|
644
|
+
configurable: true
|
|
645
|
+
});
|
|
646
|
+
}
|
|
582
647
|
var _StatementSync = binding.StatementSync;
|
|
583
648
|
var StatementSync = function StatementSync2() {
|
|
584
649
|
const err = new TypeError("Illegal constructor");
|
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","../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 type EnhancedStatementMethods,\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()`,\n * `.transaction()`, and statement modes (`.pluck()`, `.raw()`, `.expand()`)\n * to database instances that don't have them (e.g., 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 the database connection is open */\n readonly isOpen?: boolean;\n /** Whether a transaction is currently active */\n readonly isTransaction: boolean;\n}\n\n/**\n * Statement mode matching better-sqlite3's mutually exclusive modes.\n * - \"flat\": Default — rows as `{ column: value }` objects\n * - \"pluck\": First column value only\n * - \"raw\": Rows as arrays of values\n * - \"expand\": Rows namespaced by table, e.g. `{ users: { id: 1 }, posts: { title: \"...\" } }`\n */\ntype StatementMode = \"flat\" | \"pluck\" | \"raw\" | \"expand\";\n\n/**\n * A statement enhanced with better-sqlite3-style `.pluck()`, `.raw()`, and\n * `.expand()` methods. These are mutually exclusive — enabling one disables\n * the others.\n */\nexport interface EnhancedStatementMethods {\n /**\n * Causes the statement to return only the first column value of each row.\n *\n * When plucking is turned on, raw and expand modes are turned off.\n *\n * @param toggle Enable (true) or disable (false) pluck mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const count = db.prepare(\"SELECT COUNT(*) FROM users\").pluck().get();\n * // Returns: 42 (not { \"COUNT(*)\": 42 })\n *\n * const names = db.prepare(\"SELECT name FROM users\").pluck().all();\n * // Returns: [\"Alice\", \"Bob\"] (not [{ name: \"Alice\" }, { name: \"Bob\" }])\n * ```\n */\n pluck(toggle?: boolean): this;\n\n /**\n * Causes the statement to return rows as arrays of values instead of objects.\n *\n * When raw mode is turned on, pluck and expand modes are turned off.\n *\n * @param toggle Enable (true) or disable (false) raw mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const rows = db.prepare(\"SELECT id, name FROM users\").raw().all();\n * // Returns: [[1, \"Alice\"], [2, \"Bob\"]] (not [{ id: 1, name: \"Alice\" }, ...])\n * ```\n */\n raw(toggle?: boolean): this;\n\n /**\n * Causes the statement to return data namespaced by table. Each key in a row\n * object will be a table name, and each corresponding value will be a nested\n * object containing that table's columns. Columns from expressions or\n * subqueries are placed under the special `$` namespace.\n *\n * When expand mode is turned on, pluck and raw modes are turned off.\n *\n * Requires the statement to have a `.columns()` method (available on real\n * statements but not minimal mocks).\n *\n * @param toggle Enable (true) or disable (false) expand mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const rows = db.prepare(\"SELECT u.id, u.name, p.title FROM users u JOIN posts p ON ...\").expand().all();\n * // Returns: [{ users: { id: 1, name: \"Alice\" }, posts: { title: \"Hello\" } }]\n * ```\n */\n expand(toggle?: boolean): this;\n\n /** The database instance this statement was prepared from. */\n readonly database: EnhanceableDatabaseSync;\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(), transaction(),\n * and statement modes (pluck/raw/expand) on statements returned by prepare().\n */\nexport type EnhancedDatabaseSync<T extends EnhanceableDatabaseSync> = Omit<\n T,\n \"prepare\"\n> &\n EnhancedMethods & {\n prepare(\n ...args: Parameters<T[\"prepare\"]>\n ): ReturnType<T[\"prepare\"]> & EnhancedStatementMethods;\n };\n\n/** Symbol to track whether prepare() has been wrapped */\nconst ENHANCED_PREPARE = Symbol.for(\"@photostructure/sqlite:enhancedPrepare\");\n\n/**\n * Extract the first column value from a row object or array.\n */\nfunction extractFirstColumn(row: unknown): unknown {\n if (row == null) return row;\n if (Array.isArray(row)) return row[0];\n const keys = Object.keys(row as Record<string, unknown>);\n return keys.length > 0\n ? (row as Record<string, unknown>)[keys[0]!]\n : undefined;\n}\n\n/**\n * Build a column-to-table mapping from statement metadata, used by expand mode.\n * Returns an array parallel to column order: each entry is the table name\n * (or \"$\" for expressions/subqueries) and the column name.\n */\nfunction buildColumnTableMap(\n stmt: any,\n): Array<{ table: string; column: string }> | undefined {\n if (typeof stmt.columns !== \"function\") return undefined;\n const cols: Array<{ name: string; table: string | null }> = stmt.columns();\n return cols.map((c) => ({\n table: c.table ?? \"$\",\n column: c.name,\n }));\n}\n\n/**\n * Transform a row array into a table-namespaced expanded object.\n * Uses array indices to match columns, avoiding data loss from duplicate names.\n */\nfunction expandRowFromArray(\n row: unknown[],\n columnMap: Array<{ table: string; column: string }>,\n): Record<string, Record<string, unknown>> {\n const result: Record<string, Record<string, unknown>> = {};\n for (let i = 0; i < columnMap.length && i < row.length; i++) {\n const { table, column } = columnMap[i]!;\n result[table] ??= {};\n result[table]![column] = row[i];\n }\n return result;\n}\n\n/**\n * Transform a flat row object into a table-namespaced expanded object.\n * Fallback for mocks without setReturnArrays — cannot handle duplicate column\n * names correctly, but mocks typically don't produce them.\n */\nfunction expandRowFromObject(\n row: Record<string, unknown>,\n columnMap: Array<{ table: string; column: string }>,\n): Record<string, Record<string, unknown>> {\n const result: Record<string, Record<string, unknown>> = {};\n const keys = Object.keys(row);\n for (let i = 0; i < keys.length && i < columnMap.length; i++) {\n const { table, column } = columnMap[i]!;\n result[table] ??= {};\n result[table]![column] = row[keys[i]!];\n }\n return result;\n}\n\n/**\n * Validate a boolean toggle argument, matching better-sqlite3's pattern.\n */\nfunction validateToggle(value: unknown): boolean {\n const use = value === undefined ? true : value;\n if (typeof use !== \"boolean\") {\n throw new TypeError(\"Expected first argument to be a boolean\");\n }\n return use;\n}\n\n/**\n * Transform a row based on the current statement mode.\n */\nfunction transformRow(\n row: unknown,\n mode: StatementMode,\n columnMap: Array<{ table: string; column: string }> | undefined,\n): unknown {\n if (row == null || mode === \"flat\") return row;\n if (mode === \"pluck\") return extractFirstColumn(row);\n if (mode === \"expand\") {\n // columnMap is guaranteed non-null here — setMode() throws if columns() is\n // unavailable, so we'll never reach this branch with columnMap == null.\n // Prefer array-based expand (used when setReturnArrays is available)\n // to correctly handle duplicate column names across tables.\n // Fall back to object-based expand for mocks without setReturnArrays.\n if (Array.isArray(row)) {\n return expandRowFromArray(row, columnMap!);\n }\n return expandRowFromObject(row as Record<string, unknown>, columnMap!);\n }\n // \"raw\" mode is handled natively by setReturnArrays(), so no transform needed\n return row;\n}\n\n/**\n * Add `.pluck()`, `.raw()`, and `.expand()` methods to a statement instance.\n * These modes are mutually exclusive — enabling one disables the others,\n * matching better-sqlite3's behavior.\n */\nfunction enhanceStatement<S extends { all(): unknown[] }>(\n stmt: S,\n): S & EnhancedStatementMethods {\n // Idempotency: if already enhanced, return as-is\n if (typeof (stmt as any).pluck === \"function\") {\n return stmt as S & EnhancedStatementMethods;\n }\n\n let mode: StatementMode = \"flat\";\n let columnMap: Array<{ table: string; column: string }> | undefined;\n\n // Cast to any to avoid TypeScript strictness around bound method signatures.\n // At runtime these are native C++ methods that accept variadic bind parameters.\n const originalGet: any =\n typeof (stmt as any).get === \"function\"\n ? (stmt as any).get.bind(stmt)\n : undefined;\n const originalAll: any = (stmt as any).all.bind(stmt);\n const originalIterate: any =\n typeof (stmt as any).iterate === \"function\"\n ? (stmt as any).iterate.bind(stmt)\n : undefined;\n\n // Toggle helper matching better-sqlite3's mode switching:\n // enable(true) sets to target mode; enable(false) resets to flat ONLY if\n // currently in that mode, otherwise leaves mode unchanged.\n function setMode(target: StatementMode, use: boolean): void {\n if (use) {\n mode = target;\n // Cache column map on first expand() call\n if (target === \"expand\" && columnMap == null) {\n columnMap = buildColumnTableMap(stmt);\n if (columnMap == null) {\n throw new TypeError(\n \"expand() requires the statement to have a columns() method\",\n );\n }\n }\n } else if (mode === target) {\n mode = \"flat\";\n }\n // If use=false and mode !== target, no-op (matches better-sqlite3)\n\n // Sync native array mode: both raw and expand need arrays from the native layer.\n // Expand needs arrays to correctly handle duplicate column names across tables.\n if (typeof (stmt as any).setReturnArrays === \"function\") {\n (stmt as any).setReturnArrays(mode === \"raw\" || mode === \"expand\");\n }\n }\n\n Object.defineProperty(stmt, \"pluck\", {\n value: function pluck(toggle?: boolean): S {\n setMode(\"pluck\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(stmt, \"raw\", {\n value: function raw(toggle?: boolean): S {\n setMode(\"raw\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(stmt, \"expand\", {\n value: function expand(toggle?: boolean): S {\n setMode(\"expand\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n if (originalGet != null) {\n Object.defineProperty(stmt, \"get\", {\n value: (...params: any[]) => {\n const row = originalGet(...params);\n return transformRow(row, mode, columnMap);\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n }\n\n Object.defineProperty(stmt, \"all\", {\n value: (...params: any[]) => {\n const rows = originalAll(...params);\n if (mode === \"flat\" || mode === \"raw\") return rows;\n return rows.map((row: unknown) => transformRow(row, mode, columnMap));\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n if (originalIterate != null) {\n Object.defineProperty(stmt, \"iterate\", {\n value: function* (...params: any[]) {\n const iter = originalIterate(...params);\n for (const row of iter) {\n yield transformRow(row, mode, columnMap);\n }\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n }\n\n return stmt as S & EnhancedStatementMethods;\n}\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\n if (simple) {\n return extractFirstColumn(stmt.all()[0]);\n }\n\n return stmt.all();\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()`, `.transaction()`, and statement modes\n * (`.pluck()`, `.raw()`, `.expand()`) are available on the given database.\n *\n * This function can enhance:\n * - `node:sqlite` DatabaseSync instances (adds the methods)\n * - `@photostructure/sqlite` DatabaseSync instances (adds the 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()`, `.transaction()`, and\n * `.pluck()` / `.raw()` / `.expand()` (on prepared statements) guaranteed\n *\n * @example\n * ```typescript\n * import { DatabaseSync, enhance } from '@photostructure/sqlite';\n *\n * const db = enhance(new DatabaseSync(':memory:'));\n *\n * // better-sqlite3-style pragma\n * db.pragma('journal_mode = wal');\n *\n * // better-sqlite3-style transactions\n * const insertMany = db.transaction((items) => {\n * for (const item of items) insert.run(item);\n * });\n *\n * // better-sqlite3-style pluck\n * const count = db.prepare(\"SELECT COUNT(*) FROM users\").pluck().get();\n * const names = db.prepare(\"SELECT name FROM users\").pluck().all();\n * ```\n */\nexport function enhance<T extends EnhanceableDatabaseSync>(\n db: T,\n): EnhancedDatabaseSync<T> {\n // Add pragma and transaction if not already present\n if (!hasEnhancedMethods(db)) {\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\n // Wrap prepare() to add pluck() to returned statements\n if (!(db as any)[ENHANCED_PREPARE]) {\n const originalPrepare: any = db.prepare.bind(db);\n\n Object.defineProperty(db, \"prepare\", {\n value: (...args: any[]) => {\n const stmt = originalPrepare(...args);\n enhanceStatement(stmt);\n // Add stmt.database back-reference for better-sqlite3 compat\n Object.defineProperty(stmt, \"database\", {\n value: db,\n writable: false,\n configurable: true,\n enumerable: false,\n });\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(db, ENHANCED_PREPARE, {\n value: true,\n writable: false,\n configurable: false,\n enumerable: false,\n });\n }\n\n return db as unknown 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"]}
|
|
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 {\n DatabaseSyncInstance,\n DatabaseSyncLimits,\n} 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 {\n DatabaseSyncInstance,\n DatabaseSyncLimits,\n} 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 type EnhancedStatementMethods,\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// Limit name to SQLite limit ID mapping (matches upstream kLimitMapping order)\nconst LIMIT_MAPPING: ReadonlyArray<{\n name: keyof DatabaseSyncLimits;\n id: number;\n}> = [\n { name: \"length\", id: 0 },\n { name: \"sqlLength\", id: 1 },\n { name: \"column\", id: 2 },\n { name: \"exprDepth\", id: 3 },\n { name: \"compoundSelect\", id: 4 },\n { name: \"vdbeOp\", id: 5 },\n { name: \"functionArg\", id: 6 },\n { name: \"attach\", id: 7 },\n { name: \"likePatternLength\", id: 8 },\n { name: \"variableNumber\", id: 9 },\n { name: \"triggerDepth\", id: 10 },\n];\n\nconst INT_MAX = 2147483647;\n\n// WeakMap to cache limits objects per database instance\nconst limitsCache = new WeakMap<DatabaseSyncInstance, DatabaseSyncLimits>();\n\nfunction validateLimitValue(value: unknown): number {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new TypeError(\n \"Limit value must be a non-negative integer or Infinity.\",\n );\n }\n if (value === Infinity) {\n return INT_MAX;\n }\n if (!Number.isFinite(value) || value !== Math.trunc(value)) {\n throw new TypeError(\n \"Limit value must be a non-negative integer or Infinity.\",\n );\n }\n if (value < 0) {\n throw new RangeError(\"Limit value must be non-negative.\");\n }\n return value;\n}\n\nfunction createLimitsObject(db: DatabaseSyncInstance): DatabaseSyncLimits {\n const obj = Object.create(null) as DatabaseSyncLimits;\n for (const { name, id } of LIMIT_MAPPING) {\n Object.defineProperty(obj, name, {\n get() {\n return db.getLimit(id);\n },\n set(value: unknown) {\n const validated = validateLimitValue(value);\n db.setLimit(id, validated);\n },\n enumerable: true,\n configurable: false,\n });\n }\n return obj;\n}\n\nif (!Object.getOwnPropertyDescriptor(DatabaseSync.prototype, \"limits\")) {\n Object.defineProperty(DatabaseSync.prototype, \"limits\", {\n get(this: DatabaseSyncInstance) {\n let obj = limitsCache.get(this);\n if (obj == null) {\n obj = createLimitsObject(this);\n limitsCache.set(this, obj);\n }\n return obj;\n },\n enumerable: true,\n configurable: true,\n });\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()`,\n * `.transaction()`, and statement modes (`.pluck()`, `.raw()`, `.expand()`)\n * to database instances that don't have them (e.g., 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 the database connection is open */\n readonly isOpen?: boolean;\n /** Whether a transaction is currently active */\n readonly isTransaction: boolean;\n}\n\n/**\n * Statement mode matching better-sqlite3's mutually exclusive modes.\n * - \"flat\": Default — rows as `{ column: value }` objects\n * - \"pluck\": First column value only\n * - \"raw\": Rows as arrays of values\n * - \"expand\": Rows namespaced by table, e.g. `{ users: { id: 1 }, posts: { title: \"...\" } }`\n */\ntype StatementMode = \"flat\" | \"pluck\" | \"raw\" | \"expand\";\n\n/**\n * A statement enhanced with better-sqlite3-style `.pluck()`, `.raw()`, and\n * `.expand()` methods. These are mutually exclusive — enabling one disables\n * the others.\n */\nexport interface EnhancedStatementMethods {\n /**\n * Causes the statement to return only the first column value of each row.\n *\n * When plucking is turned on, raw and expand modes are turned off.\n *\n * @param toggle Enable (true) or disable (false) pluck mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const count = db.prepare(\"SELECT COUNT(*) FROM users\").pluck().get();\n * // Returns: 42 (not { \"COUNT(*)\": 42 })\n *\n * const names = db.prepare(\"SELECT name FROM users\").pluck().all();\n * // Returns: [\"Alice\", \"Bob\"] (not [{ name: \"Alice\" }, { name: \"Bob\" }])\n * ```\n */\n pluck(toggle?: boolean): this;\n\n /**\n * Causes the statement to return rows as arrays of values instead of objects.\n *\n * When raw mode is turned on, pluck and expand modes are turned off.\n *\n * @param toggle Enable (true) or disable (false) raw mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const rows = db.prepare(\"SELECT id, name FROM users\").raw().all();\n * // Returns: [[1, \"Alice\"], [2, \"Bob\"]] (not [{ id: 1, name: \"Alice\" }, ...])\n * ```\n */\n raw(toggle?: boolean): this;\n\n /**\n * Causes the statement to return data namespaced by table. Each key in a row\n * object will be a table name, and each corresponding value will be a nested\n * object containing that table's columns. Columns from expressions or\n * subqueries are placed under the special `$` namespace.\n *\n * When expand mode is turned on, pluck and raw modes are turned off.\n *\n * Requires the statement to have a `.columns()` method (available on real\n * statements but not minimal mocks).\n *\n * @param toggle Enable (true) or disable (false) expand mode. Defaults to true.\n * @returns The same statement for chaining.\n *\n * @example\n * ```typescript\n * const rows = db.prepare(\"SELECT u.id, u.name, p.title FROM users u JOIN posts p ON ...\").expand().all();\n * // Returns: [{ users: { id: 1, name: \"Alice\" }, posts: { title: \"Hello\" } }]\n * ```\n */\n expand(toggle?: boolean): this;\n\n /** The database instance this statement was prepared from. */\n readonly database: EnhanceableDatabaseSync;\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(), transaction(),\n * and statement modes (pluck/raw/expand) on statements returned by prepare().\n */\nexport type EnhancedDatabaseSync<T extends EnhanceableDatabaseSync> = Omit<\n T,\n \"prepare\"\n> &\n EnhancedMethods & {\n prepare(\n ...args: Parameters<T[\"prepare\"]>\n ): ReturnType<T[\"prepare\"]> & EnhancedStatementMethods;\n };\n\n/** Symbol to track whether prepare() has been wrapped */\nconst ENHANCED_PREPARE = Symbol.for(\"@photostructure/sqlite:enhancedPrepare\");\n\n/**\n * Extract the first column value from a row object or array.\n */\nfunction extractFirstColumn(row: unknown): unknown {\n if (row == null) return row;\n if (Array.isArray(row)) return row[0];\n const keys = Object.keys(row as Record<string, unknown>);\n return keys.length > 0\n ? (row as Record<string, unknown>)[keys[0]!]\n : undefined;\n}\n\n/**\n * Build a column-to-table mapping from statement metadata, used by expand mode.\n * Returns an array parallel to column order: each entry is the table name\n * (or \"$\" for expressions/subqueries) and the column name.\n */\nfunction buildColumnTableMap(\n stmt: any,\n): Array<{ table: string; column: string }> | undefined {\n if (typeof stmt.columns !== \"function\") return undefined;\n const cols: Array<{ name: string; table: string | null }> = stmt.columns();\n return cols.map((c) => ({\n table: c.table ?? \"$\",\n column: c.name,\n }));\n}\n\n/**\n * Transform a row array into a table-namespaced expanded object.\n * Uses array indices to match columns, avoiding data loss from duplicate names.\n */\nfunction expandRowFromArray(\n row: unknown[],\n columnMap: Array<{ table: string; column: string }>,\n): Record<string, Record<string, unknown>> {\n const result: Record<string, Record<string, unknown>> = {};\n for (let i = 0; i < columnMap.length && i < row.length; i++) {\n const { table, column } = columnMap[i]!; // eslint-disable-line security/detect-object-injection\n // eslint-disable-next-line security/detect-object-injection -- table/column from our own columnMap\n result[table] ??= {};\n result[table]![column] = row[i]; // eslint-disable-line security/detect-object-injection\n }\n return result;\n}\n\n/**\n * Transform a flat row object into a table-namespaced expanded object.\n * Fallback for mocks without setReturnArrays — cannot handle duplicate column\n * names correctly, but mocks typically don't produce them.\n */\nfunction expandRowFromObject(\n row: Record<string, unknown>,\n columnMap: Array<{ table: string; column: string }>,\n): Record<string, Record<string, unknown>> {\n const result: Record<string, Record<string, unknown>> = {};\n const keys = Object.keys(row);\n for (let i = 0; i < keys.length && i < columnMap.length; i++) {\n const { table, column } = columnMap[i]!; // eslint-disable-line security/detect-object-injection\n // eslint-disable-next-line security/detect-object-injection -- table/column from our own columnMap\n result[table] ??= {};\n result[table]![column] = row[keys[i]!]; // eslint-disable-line security/detect-object-injection\n }\n return result;\n}\n\n/**\n * Validate a boolean toggle argument, matching better-sqlite3's pattern.\n */\nfunction validateToggle(value: unknown): boolean {\n const use = value === undefined ? true : value;\n if (typeof use !== \"boolean\") {\n throw new TypeError(\"Expected first argument to be a boolean\");\n }\n return use;\n}\n\n/**\n * Transform a row based on the current statement mode.\n */\nfunction transformRow(\n row: unknown,\n mode: StatementMode,\n columnMap: Array<{ table: string; column: string }> | undefined,\n): unknown {\n if (row == null || mode === \"flat\") return row;\n if (mode === \"pluck\") return extractFirstColumn(row);\n if (mode === \"expand\") {\n // columnMap is guaranteed non-null here — setMode() throws if columns() is\n // unavailable, so we'll never reach this branch with columnMap == null.\n // Prefer array-based expand (used when setReturnArrays is available)\n // to correctly handle duplicate column names across tables.\n // Fall back to object-based expand for mocks without setReturnArrays.\n if (Array.isArray(row)) {\n return expandRowFromArray(row, columnMap!);\n }\n return expandRowFromObject(row as Record<string, unknown>, columnMap!);\n }\n // \"raw\" mode is handled natively by setReturnArrays(), so no transform needed\n return row;\n}\n\n/**\n * Add `.pluck()`, `.raw()`, and `.expand()` methods to a statement instance.\n * These modes are mutually exclusive — enabling one disables the others,\n * matching better-sqlite3's behavior.\n */\nfunction enhanceStatement<S extends { all(): unknown[] }>(\n stmt: S,\n): S & EnhancedStatementMethods {\n // Idempotency: if already enhanced, return as-is\n if (typeof (stmt as any).pluck === \"function\") {\n return stmt as S & EnhancedStatementMethods;\n }\n\n let mode: StatementMode = \"flat\";\n let columnMap: Array<{ table: string; column: string }> | undefined;\n\n // Cast to any to avoid TypeScript strictness around bound method signatures.\n // At runtime these are native C++ methods that accept variadic bind parameters.\n const originalGet: any =\n typeof (stmt as any).get === \"function\"\n ? (stmt as any).get.bind(stmt)\n : undefined;\n const originalAll: any = (stmt as any).all.bind(stmt);\n const originalIterate: any =\n typeof (stmt as any).iterate === \"function\"\n ? (stmt as any).iterate.bind(stmt)\n : undefined;\n\n // Toggle helper matching better-sqlite3's mode switching:\n // enable(true) sets to target mode; enable(false) resets to flat ONLY if\n // currently in that mode, otherwise leaves mode unchanged.\n function setMode(target: StatementMode, use: boolean): void {\n if (use) {\n mode = target;\n // Cache column map on first expand() call\n if (target === \"expand\" && columnMap == null) {\n columnMap = buildColumnTableMap(stmt);\n if (columnMap == null) {\n throw new TypeError(\n \"expand() requires the statement to have a columns() method\",\n );\n }\n }\n } else if (mode === target) {\n mode = \"flat\";\n }\n // If use=false and mode !== target, no-op (matches better-sqlite3)\n\n // Sync native array mode: both raw and expand need arrays from the native layer.\n // Expand needs arrays to correctly handle duplicate column names across tables.\n if (typeof (stmt as any).setReturnArrays === \"function\") {\n (stmt as any).setReturnArrays(mode === \"raw\" || mode === \"expand\");\n }\n }\n\n Object.defineProperty(stmt, \"pluck\", {\n value: function pluck(toggle?: boolean): S {\n setMode(\"pluck\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(stmt, \"raw\", {\n value: function raw(toggle?: boolean): S {\n setMode(\"raw\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(stmt, \"expand\", {\n value: function expand(toggle?: boolean): S {\n setMode(\"expand\", validateToggle(toggle));\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n if (originalGet != null) {\n Object.defineProperty(stmt, \"get\", {\n value: (...params: any[]) => {\n const row = originalGet(...params);\n return transformRow(row, mode, columnMap);\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n }\n\n Object.defineProperty(stmt, \"all\", {\n value: (...params: any[]) => {\n const rows = originalAll(...params);\n if (mode === \"flat\" || mode === \"raw\") return rows;\n return rows.map((row: unknown) => transformRow(row, mode, columnMap));\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n if (originalIterate != null) {\n Object.defineProperty(stmt, \"iterate\", {\n value: function* (...params: any[]) {\n const iter = originalIterate(...params);\n for (const row of iter) {\n yield transformRow(row, mode, columnMap);\n }\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n }\n\n return stmt as S & EnhancedStatementMethods;\n}\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\n if (simple) {\n return extractFirstColumn(stmt.all()[0]);\n }\n\n return stmt.all();\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()`, `.transaction()`, and statement modes\n * (`.pluck()`, `.raw()`, `.expand()`) are available on the given database.\n *\n * This function can enhance:\n * - `node:sqlite` DatabaseSync instances (adds the methods)\n * - `@photostructure/sqlite` DatabaseSync instances (adds the 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()`, `.transaction()`, and\n * `.pluck()` / `.raw()` / `.expand()` (on prepared statements) guaranteed\n *\n * @example\n * ```typescript\n * import { DatabaseSync, enhance } from '@photostructure/sqlite';\n *\n * const db = enhance(new DatabaseSync(':memory:'));\n *\n * // better-sqlite3-style pragma\n * db.pragma('journal_mode = wal');\n *\n * // better-sqlite3-style transactions\n * const insertMany = db.transaction((items) => {\n * for (const item of items) insert.run(item);\n * });\n *\n * // better-sqlite3-style pluck\n * const count = db.prepare(\"SELECT COUNT(*) FROM users\").pluck().get();\n * const names = db.prepare(\"SELECT name FROM users\").pluck().all();\n * ```\n */\nexport function enhance<T extends EnhanceableDatabaseSync>(\n db: T,\n): EnhancedDatabaseSync<T> {\n // Add pragma and transaction if not already present\n if (!hasEnhancedMethods(db)) {\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\n // Wrap prepare() to add pluck() to returned statements\n // eslint-disable-next-line security/detect-object-injection -- ENHANCED_PREPARE is a Symbol\n if (!(db as any)[ENHANCED_PREPARE]) {\n const originalPrepare: any = db.prepare.bind(db);\n\n Object.defineProperty(db, \"prepare\", {\n value: (...args: any[]) => {\n const stmt = originalPrepare(...args);\n enhanceStatement(stmt);\n // Add stmt.database back-reference for better-sqlite3 compat\n Object.defineProperty(stmt, \"database\", {\n value: db,\n writable: false,\n configurable: true,\n enumerable: false,\n });\n return stmt;\n },\n writable: true,\n configurable: true,\n enumerable: false,\n });\n\n Object.defineProperty(db, ENHANCED_PREPARE, {\n value: true,\n writable: false,\n configurable: false,\n enumerable: false,\n });\n }\n\n return db as unknown 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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -349,9 +349,49 @@ interface DatabaseSyncInstance {
|
|
|
349
349
|
* @see https://sqlite.org/c3ref/set_authorizer.html
|
|
350
350
|
*/
|
|
351
351
|
setAuthorizer(callback: ((actionCode: number, param1: string | null, param2: string | null, param3: string | null, param4: string | null) => number) | null): void;
|
|
352
|
+
/**
|
|
353
|
+
* An object with getters and setters for each SQLite limit.
|
|
354
|
+
* Setting a property changes the limit immediately.
|
|
355
|
+
* Setting a property to `Infinity` resets the limit to its compile-time maximum.
|
|
356
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
357
|
+
*/
|
|
358
|
+
readonly limits: DatabaseSyncLimits;
|
|
359
|
+
/** @internal Native method to get a SQLite limit by ID. */
|
|
360
|
+
getLimit(limitId: number): number;
|
|
361
|
+
/** @internal Native method to set a SQLite limit by ID. Returns old value. */
|
|
362
|
+
setLimit(limitId: number, value: number): number;
|
|
352
363
|
/** Dispose of the database resources using the explicit resource management protocol. */
|
|
353
364
|
[Symbol.dispose](): void;
|
|
354
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Represents the configurable SQLite limits for a database connection.
|
|
368
|
+
* Each property corresponds to a SQLite limit constant.
|
|
369
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
370
|
+
*/
|
|
371
|
+
interface DatabaseSyncLimits {
|
|
372
|
+
/** Maximum length of any string or BLOB or table row, in bytes. */
|
|
373
|
+
length: number;
|
|
374
|
+
/** Maximum length of an SQL statement, in bytes. */
|
|
375
|
+
sqlLength: number;
|
|
376
|
+
/** Maximum number of columns in a table, result set, or index. */
|
|
377
|
+
column: number;
|
|
378
|
+
/** Maximum depth of the parse tree on any expression. */
|
|
379
|
+
exprDepth: number;
|
|
380
|
+
/** Maximum number of terms in a compound SELECT statement. */
|
|
381
|
+
compoundSelect: number;
|
|
382
|
+
/** Maximum number of instructions in a virtual machine program. */
|
|
383
|
+
vdbeOp: number;
|
|
384
|
+
/** Maximum number of arguments on a function. */
|
|
385
|
+
functionArg: number;
|
|
386
|
+
/** Maximum number of attached databases. */
|
|
387
|
+
attach: number;
|
|
388
|
+
/** Maximum length of the pattern argument to the LIKE or GLOB operators. */
|
|
389
|
+
likePatternLength: number;
|
|
390
|
+
/** Maximum index number of any parameter in an SQL statement. */
|
|
391
|
+
variableNumber: number;
|
|
392
|
+
/** Maximum depth of recursion for triggers. */
|
|
393
|
+
triggerDepth: number;
|
|
394
|
+
}
|
|
355
395
|
|
|
356
396
|
/**
|
|
357
397
|
* SQLTagStore provides cached prepared statements via tagged template syntax.
|
|
@@ -482,6 +522,25 @@ interface DatabaseSyncOptions {
|
|
|
482
522
|
* @default true
|
|
483
523
|
*/
|
|
484
524
|
readonly open?: boolean;
|
|
525
|
+
/**
|
|
526
|
+
* An object specifying initial SQLite limits to set when opening the database.
|
|
527
|
+
* Each property corresponds to a SQLite limit constant. Only integer values are
|
|
528
|
+
* accepted (no Infinity). Omitted properties retain their default values.
|
|
529
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
530
|
+
*/
|
|
531
|
+
readonly limits?: {
|
|
532
|
+
readonly length?: number;
|
|
533
|
+
readonly sqlLength?: number;
|
|
534
|
+
readonly column?: number;
|
|
535
|
+
readonly exprDepth?: number;
|
|
536
|
+
readonly compoundSelect?: number;
|
|
537
|
+
readonly vdbeOp?: number;
|
|
538
|
+
readonly functionArg?: number;
|
|
539
|
+
readonly attach?: number;
|
|
540
|
+
readonly likePatternLength?: number;
|
|
541
|
+
readonly variableNumber?: number;
|
|
542
|
+
readonly triggerDepth?: number;
|
|
543
|
+
};
|
|
485
544
|
}
|
|
486
545
|
|
|
487
546
|
/**
|
|
@@ -1114,4 +1173,4 @@ interface BackupOptions {
|
|
|
1114
1173
|
declare const backup: (sourceDb: DatabaseSyncInstance, destination: string | Buffer | URL, options?: BackupOptions) => Promise<number>;
|
|
1115
1174
|
declare const _default: SqliteModule;
|
|
1116
1175
|
|
|
1117
|
-
export { type AggregateOptions, type BackupOptions, type ChangesetApplyOptions, DatabaseSync, type DatabaseSyncInstance, type DatabaseSyncOptions, type EnhanceableDatabaseSync, type EnhancedDatabaseSync, type EnhancedMethods, type EnhancedStatementMethods, type PragmaOptions, SQLTagStore, type SQLTagStoreInstance, Session, type SessionOptions, type SqliteAuthorizationActions, type SqliteAuthorizationResults, type SqliteChangesetConflictTypes, type SqliteChangesetResolution, type SqliteConstants, type SqliteModule, type SqliteOpenFlags, type StatementColumnMetadata, type StatementOptions, StatementSync, type StatementSyncInstance, type TransactionFunction, type TransactionMode, type UserFunctionOptions, backup, constants, _default as default, enhance, isEnhanced };
|
|
1176
|
+
export { type AggregateOptions, type BackupOptions, type ChangesetApplyOptions, DatabaseSync, type DatabaseSyncInstance, type DatabaseSyncLimits, type DatabaseSyncOptions, type EnhanceableDatabaseSync, type EnhancedDatabaseSync, type EnhancedMethods, type EnhancedStatementMethods, type PragmaOptions, SQLTagStore, type SQLTagStoreInstance, Session, type SessionOptions, type SqliteAuthorizationActions, type SqliteAuthorizationResults, type SqliteChangesetConflictTypes, type SqliteChangesetResolution, type SqliteConstants, type SqliteModule, type SqliteOpenFlags, type StatementColumnMetadata, type StatementOptions, StatementSync, type StatementSyncInstance, type TransactionFunction, type TransactionMode, type UserFunctionOptions, backup, constants, _default as default, enhance, isEnhanced };
|
package/dist/index.d.mts
CHANGED
|
@@ -349,9 +349,49 @@ interface DatabaseSyncInstance {
|
|
|
349
349
|
* @see https://sqlite.org/c3ref/set_authorizer.html
|
|
350
350
|
*/
|
|
351
351
|
setAuthorizer(callback: ((actionCode: number, param1: string | null, param2: string | null, param3: string | null, param4: string | null) => number) | null): void;
|
|
352
|
+
/**
|
|
353
|
+
* An object with getters and setters for each SQLite limit.
|
|
354
|
+
* Setting a property changes the limit immediately.
|
|
355
|
+
* Setting a property to `Infinity` resets the limit to its compile-time maximum.
|
|
356
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
357
|
+
*/
|
|
358
|
+
readonly limits: DatabaseSyncLimits;
|
|
359
|
+
/** @internal Native method to get a SQLite limit by ID. */
|
|
360
|
+
getLimit(limitId: number): number;
|
|
361
|
+
/** @internal Native method to set a SQLite limit by ID. Returns old value. */
|
|
362
|
+
setLimit(limitId: number, value: number): number;
|
|
352
363
|
/** Dispose of the database resources using the explicit resource management protocol. */
|
|
353
364
|
[Symbol.dispose](): void;
|
|
354
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Represents the configurable SQLite limits for a database connection.
|
|
368
|
+
* Each property corresponds to a SQLite limit constant.
|
|
369
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
370
|
+
*/
|
|
371
|
+
interface DatabaseSyncLimits {
|
|
372
|
+
/** Maximum length of any string or BLOB or table row, in bytes. */
|
|
373
|
+
length: number;
|
|
374
|
+
/** Maximum length of an SQL statement, in bytes. */
|
|
375
|
+
sqlLength: number;
|
|
376
|
+
/** Maximum number of columns in a table, result set, or index. */
|
|
377
|
+
column: number;
|
|
378
|
+
/** Maximum depth of the parse tree on any expression. */
|
|
379
|
+
exprDepth: number;
|
|
380
|
+
/** Maximum number of terms in a compound SELECT statement. */
|
|
381
|
+
compoundSelect: number;
|
|
382
|
+
/** Maximum number of instructions in a virtual machine program. */
|
|
383
|
+
vdbeOp: number;
|
|
384
|
+
/** Maximum number of arguments on a function. */
|
|
385
|
+
functionArg: number;
|
|
386
|
+
/** Maximum number of attached databases. */
|
|
387
|
+
attach: number;
|
|
388
|
+
/** Maximum length of the pattern argument to the LIKE or GLOB operators. */
|
|
389
|
+
likePatternLength: number;
|
|
390
|
+
/** Maximum index number of any parameter in an SQL statement. */
|
|
391
|
+
variableNumber: number;
|
|
392
|
+
/** Maximum depth of recursion for triggers. */
|
|
393
|
+
triggerDepth: number;
|
|
394
|
+
}
|
|
355
395
|
|
|
356
396
|
/**
|
|
357
397
|
* SQLTagStore provides cached prepared statements via tagged template syntax.
|
|
@@ -482,6 +522,25 @@ interface DatabaseSyncOptions {
|
|
|
482
522
|
* @default true
|
|
483
523
|
*/
|
|
484
524
|
readonly open?: boolean;
|
|
525
|
+
/**
|
|
526
|
+
* An object specifying initial SQLite limits to set when opening the database.
|
|
527
|
+
* Each property corresponds to a SQLite limit constant. Only integer values are
|
|
528
|
+
* accepted (no Infinity). Omitted properties retain their default values.
|
|
529
|
+
* @see https://sqlite.org/c3ref/limit.html
|
|
530
|
+
*/
|
|
531
|
+
readonly limits?: {
|
|
532
|
+
readonly length?: number;
|
|
533
|
+
readonly sqlLength?: number;
|
|
534
|
+
readonly column?: number;
|
|
535
|
+
readonly exprDepth?: number;
|
|
536
|
+
readonly compoundSelect?: number;
|
|
537
|
+
readonly vdbeOp?: number;
|
|
538
|
+
readonly functionArg?: number;
|
|
539
|
+
readonly attach?: number;
|
|
540
|
+
readonly likePatternLength?: number;
|
|
541
|
+
readonly variableNumber?: number;
|
|
542
|
+
readonly triggerDepth?: number;
|
|
543
|
+
};
|
|
485
544
|
}
|
|
486
545
|
|
|
487
546
|
/**
|
|
@@ -1114,4 +1173,4 @@ interface BackupOptions {
|
|
|
1114
1173
|
declare const backup: (sourceDb: DatabaseSyncInstance, destination: string | Buffer | URL, options?: BackupOptions) => Promise<number>;
|
|
1115
1174
|
declare const _default: SqliteModule;
|
|
1116
1175
|
|
|
1117
|
-
export { type AggregateOptions, type BackupOptions, type ChangesetApplyOptions, DatabaseSync, type DatabaseSyncInstance, type DatabaseSyncOptions, type EnhanceableDatabaseSync, type EnhancedDatabaseSync, type EnhancedMethods, type EnhancedStatementMethods, type PragmaOptions, SQLTagStore, type SQLTagStoreInstance, Session, type SessionOptions, type SqliteAuthorizationActions, type SqliteAuthorizationResults, type SqliteChangesetConflictTypes, type SqliteChangesetResolution, type SqliteConstants, type SqliteModule, type SqliteOpenFlags, type StatementColumnMetadata, type StatementOptions, StatementSync, type StatementSyncInstance, type TransactionFunction, type TransactionMode, type UserFunctionOptions, backup, constants, _default as default, enhance, isEnhanced };
|
|
1176
|
+
export { type AggregateOptions, type BackupOptions, type ChangesetApplyOptions, DatabaseSync, type DatabaseSyncInstance, type DatabaseSyncLimits, type DatabaseSyncOptions, type EnhanceableDatabaseSync, type EnhancedDatabaseSync, type EnhancedMethods, type EnhancedStatementMethods, type PragmaOptions, SQLTagStore, type SQLTagStoreInstance, Session, type SessionOptions, type SqliteAuthorizationActions, type SqliteAuthorizationResults, type SqliteChangesetConflictTypes, type SqliteChangesetResolution, type SqliteConstants, type SqliteModule, type SqliteOpenFlags, type StatementColumnMetadata, type StatementOptions, StatementSync, type StatementSyncInstance, type TransactionFunction, type TransactionMode, type UserFunctionOptions, backup, constants, _default as default, enhance, isEnhanced };
|