@photostructure/sqlite 0.2.1 → 0.3.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 +10 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- 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.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/test_extension.so +0 -0
- package/prebuilds/win32-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+sqlite.glibc.node +0 -0
- package/scripts/prebuild-linux-glibc.sh +6 -4
- package/src/sql-tag-store.ts +1 -1
- package/src/types/sql-tag-store-instance.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] (2025-12-16)
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **BREAKING**: `SQLTagStore.size` changed from method to getter for Node.js API parity (Node.js PR #60246)
|
|
10
|
+
- Before: `sql.size()`
|
|
11
|
+
- After: `sql.size`
|
|
12
|
+
|
|
5
13
|
## [0.2.1] (2025-12-01)
|
|
6
14
|
|
|
7
15
|
### Added
|
|
@@ -75,5 +83,7 @@ All notable changes to this project will be documented in this file.
|
|
|
75
83
|
- macOS (x64, ARM64)
|
|
76
84
|
- Linux (x64, ARM64), (glibc 2.28+, musl)
|
|
77
85
|
|
|
86
|
+
[0.3.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.3.0
|
|
87
|
+
[0.2.1]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.2.1
|
|
78
88
|
[0.2.0]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.2.0
|
|
79
89
|
[0.0.1]: https://github.com/PhotoStructure/node-sqlite/releases/tag/v0.0.1
|
package/dist/index.cjs
CHANGED
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts"],"sourcesContent":["// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Buffer containing the changeset data.\n */\n changeset(): Buffer;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Buffer containing the patchset data.\n */\n patchset(): Buffer;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\nexport const DatabaseSync =\n binding.DatabaseSync as SqliteModule[\"DatabaseSync\"];\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\nexport const StatementSync =\n binding.StatementSync as SqliteModule[\"StatementSync\"];\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n throw new Error(\"Database is not open\");\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n size(): number {\n return this.cache.size();\n }\n\n /**\n * Clears all cached statements.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Execute an INSERT, UPDATE, DELETE or other statement that doesn't return rows.\n * Returns an object with `changes` and `lastInsertRowid`.\n */\n run(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): { changes: number; lastInsertRowid: number | bigint } {\n const stmt = this.getOrPrepare(strings);\n return stmt.run(...values);\n }\n\n /**\n * Execute a query and return the first row, or undefined if no rows.\n */\n get(strings: TemplateStringsArray, ...values: unknown[]): unknown {\n const stmt = this.getOrPrepare(strings);\n return stmt.get(...values);\n }\n\n /**\n * Execute a query and return all rows as an array.\n */\n all(strings: TemplateStringsArray, ...values: unknown[]): unknown[] {\n const stmt = this.getOrPrepare(strings);\n return stmt.all(...values);\n }\n\n /**\n * Execute a query and return an iterator over the rows.\n */\n iterate(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): IterableIterator<unknown> {\n const stmt = this.getOrPrepare(strings);\n return stmt.iterate(...values);\n }\n\n /**\n * Get a cached statement or prepare a new one.\n * If a cached statement has been finalized, it's evicted and a new one is prepared.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"Database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache - evict if finalized\n const cached = this.cache.get(sql);\n if (cached) {\n if (!cached.finalized) {\n return cached;\n }\n // Statement was finalized externally - remove from cache\n this.cache.delete(sql);\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n"],"mappings":"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","names":["import_node_path","nodeGypBuild"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts"],"sourcesContent":["// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Buffer containing the changeset data.\n */\n changeset(): Buffer;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Buffer containing the patchset data.\n */\n patchset(): Buffer;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\nexport const DatabaseSync =\n binding.DatabaseSync as SqliteModule[\"DatabaseSync\"];\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\nexport const StatementSync =\n binding.StatementSync as SqliteModule[\"StatementSync\"];\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n throw new Error(\"Database is not open\");\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n 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 * If a cached statement has been finalized, it's evicted and a new one is prepared.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"Database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache - evict if finalized\n const cached = this.cache.get(sql);\n if (cached) {\n if (!cached.finalized) {\n return cached;\n }\n // Statement was finalized externally - remove from cache\n this.cache.delete(sql);\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n"],"mappings":"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","names":["import_node_path","nodeGypBuild"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -63,7 +63,7 @@ interface SQLTagStoreInstance {
|
|
|
63
63
|
/**
|
|
64
64
|
* Returns the current number of cached statements.
|
|
65
65
|
*/
|
|
66
|
-
size
|
|
66
|
+
readonly size: number;
|
|
67
67
|
/**
|
|
68
68
|
* Clears all cached statements.
|
|
69
69
|
*/
|
|
@@ -395,7 +395,7 @@ declare class SQLTagStore {
|
|
|
395
395
|
/**
|
|
396
396
|
* Returns the current number of cached statements.
|
|
397
397
|
*/
|
|
398
|
-
size(): number;
|
|
398
|
+
get size(): number;
|
|
399
399
|
/**
|
|
400
400
|
* Clears all cached statements.
|
|
401
401
|
*/
|
package/dist/index.d.mts
CHANGED
|
@@ -63,7 +63,7 @@ interface SQLTagStoreInstance {
|
|
|
63
63
|
/**
|
|
64
64
|
* Returns the current number of cached statements.
|
|
65
65
|
*/
|
|
66
|
-
size
|
|
66
|
+
readonly size: number;
|
|
67
67
|
/**
|
|
68
68
|
* Clears all cached statements.
|
|
69
69
|
*/
|
|
@@ -395,7 +395,7 @@ declare class SQLTagStore {
|
|
|
395
395
|
/**
|
|
396
396
|
* Returns the current number of cached statements.
|
|
397
397
|
*/
|
|
398
|
-
size(): number;
|
|
398
|
+
get size(): number;
|
|
399
399
|
/**
|
|
400
400
|
* Clears all cached statements.
|
|
401
401
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ interface SQLTagStoreInstance {
|
|
|
63
63
|
/**
|
|
64
64
|
* Returns the current number of cached statements.
|
|
65
65
|
*/
|
|
66
|
-
size
|
|
66
|
+
readonly size: number;
|
|
67
67
|
/**
|
|
68
68
|
* Clears all cached statements.
|
|
69
69
|
*/
|
|
@@ -395,7 +395,7 @@ declare class SQLTagStore {
|
|
|
395
395
|
/**
|
|
396
396
|
* Returns the current number of cached statements.
|
|
397
397
|
*/
|
|
398
|
-
size(): number;
|
|
398
|
+
get size(): number;
|
|
399
399
|
/**
|
|
400
400
|
* Clears all cached statements.
|
|
401
401
|
*/
|
package/dist/index.mjs
CHANGED
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Buffer containing the changeset data.\n */\n changeset(): Buffer;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Buffer containing the patchset data.\n */\n patchset(): Buffer;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\nexport const DatabaseSync =\n binding.DatabaseSync as SqliteModule[\"DatabaseSync\"];\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\nexport const StatementSync =\n binding.StatementSync as SqliteModule[\"StatementSync\"];\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n throw new Error(\"Database is not open\");\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n size(): number {\n return this.cache.size();\n }\n\n /**\n * Clears all cached statements.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Execute an INSERT, UPDATE, DELETE or other statement that doesn't return rows.\n * Returns an object with `changes` and `lastInsertRowid`.\n */\n run(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): { changes: number; lastInsertRowid: number | bigint } {\n const stmt = this.getOrPrepare(strings);\n return stmt.run(...values);\n }\n\n /**\n * Execute a query and return the first row, or undefined if no rows.\n */\n get(strings: TemplateStringsArray, ...values: unknown[]): unknown {\n const stmt = this.getOrPrepare(strings);\n return stmt.get(...values);\n }\n\n /**\n * Execute a query and return all rows as an array.\n */\n all(strings: TemplateStringsArray, ...values: unknown[]): unknown[] {\n const stmt = this.getOrPrepare(strings);\n return stmt.all(...values);\n }\n\n /**\n * Execute a query and return an iterator over the rows.\n */\n iterate(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): IterableIterator<unknown> {\n const stmt = this.getOrPrepare(strings);\n return stmt.iterate(...values);\n }\n\n /**\n * Get a cached statement or prepare a new one.\n * If a cached statement has been finalized, it's evicted and a new one is prepared.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"Database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache - evict if finalized\n const cached = this.cache.get(sql);\n if (cached) {\n if (!cached.finalized) {\n return cached;\n }\n // Statement was finalized externally - remove from cache\n this.cache.delete(sql);\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n"],"mappings":";AACA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,IAAM,cAAc,MAAM,cAAc,YAAY,GAAG;AACvD,IAAM,aAAa,MAAM,KAAK,QAAQ,YAAY,CAAC;AAE5C,IAAM,YAA4B,2BAAW;;;ACNpD,OAAO,kBAAkB;AACzB,SAAS,YAAY;;;ACFrB,SAAS,eAAe;AAEjB,SAAS,mBAA2B;AACzC,QAAM,IAAI,IAAI,MAAM;AACpB,MAAI,EAAE,SAAS,MAAM;AACnB,UAAM,kBAAkB,CAAC;AAAA,EAC3B;AACA,SAAO,QAAQ,kBAAkB,EAAE,KAAe,CAAC;AACrD;AAGA,IAAM,WACJ,QAAQ,aAAa,UACjB;AAAA;AAAA,EAEE;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AACF,IACA;AAAA;AAAA,EAEE;AAAA;AAAA;AAAA,EAGA;AACF;AAEN,IAAM,aAAa;AAGZ,SAAS,kBAAkB,OAAuB;AACvD,QAAM,SAAS,MAAM,MAAM,IAAI,EAAE,OAAO,OAAO;AAG/C,QAAM,cAAc,OAAO;AAAA,IAAU,CAAC,UACpC,MAAM,SAAS,kBAAkB;AAAA,EACnC;AACA,MAAI,gBAAgB,IAAI;AACtB,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,WAAS,IAAI,cAAc,GAAG,IAAI,OAAO,QAAQ,KAAK;AAEpD,UAAM,QAAQ,OAAO,CAAC;AACtB,eAAW,WAAW,UAAU;AAC9B,YAAM,IAAI,OAAO,KAAK,EAAE,MAAM,OAAO,GAAG;AACxC,UAAI,KAAK,QAAQ,EAAE,MAAM,GAAG;AAC1B,cAAMA,QAAO,EAAE,MAAM;AAGrB,YAAI,WAAW,KAAKA,KAAI,GAAG;AACzB,cAAI;AACF,mBAAO,IAAI,IAAIA,KAAI,EAAE;AAAA,UACvB,QAAQ;AAAA,UAER;AAAA,QACF;AACA,eAAOA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,QAAM,IAAI,MAAM,gDAAgD;AAClE;;;ACjEO,SAAS,WAAW;AACzB,MAAI;AACF,QAAI,OAAO,cAAc,YAAa,QAAO;AAAA,EAC/C,QAAQ;AAAA,EAER;AAEA,SAAO,iBAAiB;AAC1B;;;ACRO,IAAM,WAAN,MAAqB;AAAA,EAClB,QAAQ,oBAAI,IAAU;AAAA,EACb;AAAA,EAEjB,YAAY,UAAkB;AAC5B,QAAI,WAAW,GAAG;AAChB,YAAM,IAAI,WAAW,uCAAuC;AAAA,IAC9D;AACA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,KAAuB;AACzB,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,UAAU,QAAW;AAEvB,WAAK,MAAM,OAAO,GAAG;AACrB,WAAK,MAAM,IAAI,KAAK,KAAK;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,KAAQ,OAAgB;AAC1B,QAAI,KAAK,MAAM,IAAI,GAAG,GAAG;AAEvB,WAAK,MAAM,OAAO,GAAG;AAAA,IACvB,WAAW,KAAK,MAAM,QAAQ,KAAK,aAAa;AAE9C,YAAM,YAAY,KAAK,MAAM,KAAK,EAAE,KAAK,EAAE;AAC3C,UAAI,cAAc,QAAW;AAC3B,aAAK,MAAM,OAAO,SAAS;AAAA,MAC7B;AAAA,IACF;AACA,SAAK,MAAM,IAAI,KAAK,KAAK;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,KAAiB;AACtB,WAAO,KAAK,MAAM,OAAO,GAAG;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,KAAiB;AACnB,WAAO,KAAK,MAAM,IAAI,GAAG;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe;AACb,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AACF;;;AC3EA,IAAM,mBAAmB;AAYlB,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,IAA0B,WAAmB,kBAAkB;AACzE,QAAI,CAAC,GAAG,QAAQ;AACd,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AACA,SAAK,WAAW;AAChB,SAAK,cAAc;AACnB,SAAK,QAAQ,IAAI,SAAS,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,WAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe;AACb,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IACE,YACG,QACoD;AACvD,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,WAAO,KAAK,IAAI,GAAG,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAkC,QAA4B;AAChE,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,WAAO,KAAK,IAAI,GAAG,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAkC,QAA8B;AAClE,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,WAAO,KAAK,IAAI,GAAG,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,QACE,YACG,QACwB;AAC3B,UAAM,OAAO,KAAK,aAAa,OAAO;AACtC,WAAO,KAAK,QAAQ,GAAG,MAAM;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,SAAsD;AACzE,QAAI,CAAC,KAAK,SAAS,QAAQ;AACzB,YAAM,IAAI,MAAM,sBAAsB;AAAA,IACxC;AAEA,UAAM,MAAM,KAAK,SAAS,OAAO;AAGjC,UAAM,SAAS,KAAK,MAAM,IAAI,GAAG;AACjC,QAAI,QAAQ;AACV,UAAI,CAAC,OAAO,WAAW;AACrB,eAAO;AAAA,MACT;AAEA,WAAK,MAAM,OAAO,GAAG;AAAA,IACvB;AAGA,UAAM,OAAO,KAAK,SAAS,QAAQ,GAAG;AACtC,SAAK,MAAM,IAAI,KAAK,IAAI;AACxB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,SAAuC;AACtD,QAAI,MAAM,QAAQ,CAAC,KAAK;AACxB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AAEvC,aAAO,OAAO,QAAQ,CAAC,KAAK;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AACF;;;AJ7GA,IAAM,UAAU,aAAa,KAAK,SAAS,GAAG,IAAI,CAAC;AAyG5C,IAAM,eACX,QAAQ;AAOT,aAAa,UAAmC,iBAAiB,SAEhE,UACqB;AACrB,SAAO,IAAI,YAAY,MAAM,QAAQ;AACvC;AAaO,IAAM,gBACX,QAAQ;AAcH,IAAM,UAAU,QAAQ;AA4BxB,IAAM,YAA6B,QAAQ;AA2C3C,IAAM,SAIU,QAAQ;AAG/B,IAAO,gBAAQ;","names":["path"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/index.ts","../src/stack-path.ts","../src/dirname.ts","../src/lru-cache.ts","../src/sql-tag-store.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","// Load the native binding with support for both CJS and ESM\nimport nodeGypBuild from \"node-gyp-build\";\nimport { join } from \"node:path\";\nimport { _dirname } from \"./dirname\";\nimport { SQLTagStore } from \"./sql-tag-store\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport { DatabaseSyncOptions } from \"./types/database-sync-options\";\nimport { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nimport { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nimport { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nimport { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nimport { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nimport { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nimport { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\nexport type { AggregateOptions } from \"./types/aggregate-options\";\nexport type { ChangesetApplyOptions } from \"./types/changeset-apply-options\";\nexport type { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nexport type { DatabaseSyncOptions } from \"./types/database-sync-options\";\nexport type { SessionOptions } from \"./types/session-options\";\nexport type { SQLTagStoreInstance } from \"./types/sql-tag-store-instance\";\nexport type { SqliteAuthorizationActions } from \"./types/sqlite-authorization-actions\";\nexport type { SqliteAuthorizationResults } from \"./types/sqlite-authorization-results\";\nexport type { SqliteChangesetConflictTypes } from \"./types/sqlite-changeset-conflict-types\";\nexport type { SqliteChangesetResolution } from \"./types/sqlite-changeset-resolution\";\nexport type { SqliteOpenFlags } from \"./types/sqlite-open-flags\";\nexport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\nexport type { UserFunctionOptions } from \"./types/user-functions-options\";\n\n// Use _dirname() helper that works in both CJS/ESM and Jest\nconst binding = nodeGypBuild(join(_dirname(), \"..\"));\n\n/**\n * All SQLite constants exported by this module.\n *\n * This is a union of all constant category interfaces:\n * - {@link SqliteOpenFlags} - Database open flags (extension beyond `node:sqlite`)\n * - {@link SqliteChangesetResolution} - Changeset conflict resolution values\n * - {@link SqliteChangesetConflictTypes} - Changeset conflict type codes\n * - {@link SqliteAuthorizationResults} - Authorization return values\n * - {@link SqliteAuthorizationActions} - Authorization action codes\n *\n * **Note:** The categorized interfaces (`SqliteOpenFlags`, etc.) are extensions\n * provided by `@photostructure/sqlite`. The `node:sqlite` module exports only\n * a flat `constants` object without these type categories.\n */\nexport type SqliteConstants = SqliteOpenFlags &\n SqliteChangesetResolution &\n SqliteChangesetConflictTypes &\n SqliteAuthorizationResults &\n SqliteAuthorizationActions;\n\n/**\n * Options for creating a prepared statement.\n */\nexport interface StatementOptions {\n /** If true, the prepared statement's expandedSQL property will contain the expanded SQL. @default false */\n readonly expandedSQL?: boolean;\n /** If true, anonymous parameters are enabled for the statement. @default false */\n readonly anonymousParameters?: boolean;\n}\n\nexport interface Session {\n /**\n * Generate a changeset containing all changes recorded by the session.\n * @returns A Buffer containing the changeset data.\n */\n changeset(): Buffer;\n /**\n * Generate a patchset containing all changes recorded by the session.\n * @returns A Buffer containing the patchset data.\n */\n patchset(): Buffer;\n /**\n * Close the session and release its resources.\n */\n close(): void;\n}\n\n/**\n * The main SQLite module interface.\n */\nexport interface SqliteModule {\n /**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All operations are performed synchronously, blocking until completion.\n */\n DatabaseSync: new (\n location?: string | Buffer | URL,\n options?: DatabaseSyncOptions,\n ) => DatabaseSyncInstance;\n /**\n * The StatementSync class represents a synchronous prepared statement.\n * This class should not be instantiated directly; use Database.prepare() instead.\n */\n StatementSync: new (\n database: DatabaseSyncInstance,\n sql: string,\n options?: StatementOptions,\n ) => StatementSyncInstance;\n /**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use Database.createSession() instead.\n */\n Session: new () => Session;\n /**\n * SQLite constants for various operations and flags.\n * @see {@link SqliteConstants} for the type definition\n * @see {@link SqliteOpenFlags} for database open flags (extension beyond `node:sqlite`)\n * @see {@link SqliteChangesetResolution} for changeset conflict resolution values\n * @see {@link SqliteChangesetConflictTypes} for changeset conflict type codes\n * @see {@link SqliteAuthorizationResults} for authorization return values\n * @see {@link SqliteAuthorizationActions} for authorization action codes\n */\n constants: SqliteConstants;\n}\n\n/**\n * The DatabaseSync class represents a synchronous connection to a SQLite database.\n * All database operations are performed synchronously, blocking the thread until completion.\n *\n * @example\n * ```typescript\n * import { DatabaseSync } from '@photostructure/sqlite';\n *\n * // Create an in-memory database\n * const db = new DatabaseSync(':memory:');\n *\n * // Create a file-based database\n * const fileDb = new DatabaseSync('./mydata.db');\n *\n * // Create with options\n * const readOnlyDb = new DatabaseSync('./data.db', { readOnly: true });\n * ```\n */\nexport const DatabaseSync =\n binding.DatabaseSync as SqliteModule[\"DatabaseSync\"];\n\n// node:sqlite implements createTagStore and SQLTagStore entirely in native C++.\n// We use a TypeScript implementation instead, attached via prototype extension.\n// This maintains API compatibility with node:sqlite while avoiding the complexity\n// of a native LRU cache. Performance is equivalent since the real cost is SQLite\n// execution, not cache lookups - V8's Map is highly optimized for string keys.\n(DatabaseSync.prototype as DatabaseSyncInstance).createTagStore = function (\n this: DatabaseSyncInstance,\n capacity?: number,\n): SQLTagStoreInstance {\n return new SQLTagStore(this, capacity);\n};\n\n/**\n * The StatementSync class represents a prepared SQL statement.\n * This class should not be instantiated directly; use DatabaseSync.prepare() instead.\n *\n * @example\n * ```typescript\n * const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\n * const user = stmt.get(123);\n * stmt.finalize();\n * ```\n */\nexport const StatementSync =\n binding.StatementSync as SqliteModule[\"StatementSync\"];\n\n/**\n * The Session class for recording database changes.\n * This class should not be instantiated directly; use DatabaseSync.createSession() instead.\n *\n * @example\n * ```typescript\n * const session = db.createSession({ table: 'users' });\n * // Make some changes to the users table\n * const changeset = session.changeset();\n * session.close();\n * ```\n */\nexport const Session = binding.Session as SqliteModule[\"Session\"];\n\n/**\n * The SQLTagStore class for cached prepared statements via tagged template syntax.\n * This class should not be instantiated directly; use DatabaseSync.createTagStore() instead.\n *\n * @example\n * ```typescript\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport { SQLTagStore };\n\n/**\n * SQLite constants for various operations and flags.\n *\n * @example\n * ```typescript\n * import { constants } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./data.db', {\n * readOnly: true,\n * // Uses SQLITE_OPEN_READONLY internally\n * });\n * ```\n */\nexport const constants: SqliteConstants = binding.constants;\n\n/**\n * Options for the backup() function.\n */\nexport interface BackupOptions {\n /** Number of pages to be transmitted in each batch of the backup. @default 100 */\n rate?: number;\n /** Name of the source database. Can be 'main' or any attached database. @default 'main' */\n source?: string;\n /** Name of the target database. Can be 'main' or any attached database. @default 'main' */\n target?: string;\n /** Callback function that will be called with progress information. */\n progress?: (info: { totalPages: number; remainingPages: number }) => void;\n}\n\n/**\n * Standalone function to make a backup of a database.\n *\n * This function matches the Node.js `node:sqlite` module API which exports\n * `backup()` as a standalone function in addition to the `db.backup()` method.\n *\n * @param sourceDb The database to backup from.\n * @param destination The path where the backup will be created.\n * @param options Optional configuration for the backup operation.\n * @returns A promise that resolves when the backup is completed.\n *\n * @example\n * ```typescript\n * import { DatabaseSync, backup } from '@photostructure/sqlite';\n *\n * const db = new DatabaseSync('./source.db');\n * await backup(db, './backup.db');\n *\n * // With options\n * await backup(db, './backup.db', {\n * rate: 10,\n * progress: ({ totalPages, remainingPages }) => {\n * console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);\n * }\n * });\n * ```\n */\nexport const backup: (\n sourceDb: DatabaseSyncInstance,\n destination: string | Buffer | URL,\n options?: BackupOptions,\n) => Promise<number> = binding.backup;\n\n// Default export for CommonJS compatibility\nexport default binding as SqliteModule;\n","import { dirname } from \"node:path\";\n\nexport function getCallerDirname(): string {\n const e = new Error();\n if (e.stack == null) {\n Error.captureStackTrace(e);\n }\n return dirname(extractCallerPath(e.stack as string));\n}\n\n// Comprehensive regex patterns for different stack frame formats\nconst patterns =\n process.platform === \"win32\"\n ? [\n // File URLs: \"at functionName (file:///C:/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>file:\\/\\/\\/.+?):\\d+:\\d+\\)$/,\n // File URLs direct: \"at file:///C:/path/file.js:1:1\"\n /\\bat\\s(?<path>file:\\/\\/\\/.+?):\\d+:\\d+$/,\n // Standard: \"at functionName (C:\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>[A-Z]:\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at C:\\path\\file.js:1:1\"\n /\\bat\\s(?<path>[A-Z]:\\\\.+):\\d+:\\d+$/,\n // UNC: \"at functionName (\\\\server\\share\\path\\file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\\\\\\\.+):\\d+:\\d+\\)$/,\n // direct: \"at \\\\server\\share\\path\\file.js:1:1\"\n /\\bat\\s(?<path>\\\\\\\\.+):\\d+:\\d+$/,\n ]\n : [\n // Standard: \"at functionName (/path/file.js:1:1)\"\n /\\bat\\s.+?\\((?<path>\\/.+?):\\d+:\\d+\\)$/,\n // Anonymous or direct: \"at /path/file.js:1:1\"\n // eslint-disable-next-line security/detect-unsafe-regex -- Pattern is safe: no nested quantifiers\n /\\bat\\s(?:[^\\s()]+\\s)?(?<path>\\/[^:]+):\\d+:\\d+$/,\n ];\n\nconst MaybeUrlRE = /^[a-z]{2,5}:\\/\\//i;\n\n// only exposed for tests:\nexport function extractCallerPath(stack: string): string {\n const frames = stack.split(\"\\n\").filter(Boolean);\n\n // First find getCallerDirname() in the stack:\n const callerFrame = frames.findIndex((frame) =>\n frame.includes(\"getCallerDirname\"),\n );\n if (callerFrame === -1) {\n throw new Error(\"Invalid stack trace format: missing caller frame\");\n }\n for (let i = callerFrame + 1; i < frames.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n const frame = frames[i];\n for (const pattern of patterns) {\n const g = frame?.trim().match(pattern)?.groups;\n if (g != null && g[\"path\"]) {\n const path = g[\"path\"];\n // Windows requires us to check if it's a reasonable URL, as URL accepts\n // \"C:\\\\path\\\\file.txt\" as valid (!!)\n if (MaybeUrlRE.test(path)) {\n try {\n return new URL(path).pathname;\n } catch {\n // ignore\n }\n }\n return path;\n }\n }\n }\n throw new Error(\"Invalid stack trace format: no parsable frames\");\n}\n","import { getCallerDirname } from \"./stack-path\";\n\n// Thanks to tsup shims, __dirname should always be defined except when run by\n// jest (which will use the stack_path shim)\nexport function _dirname() {\n try {\n if (typeof __dirname !== \"undefined\") return __dirname;\n } catch {\n // ignore\n }\n // we must be in jest. Use the stack_path ~~hack~~ shim:\n return getCallerDirname();\n}\n","/**\n * Simple LRU (Least Recently Used) cache implementation.\n * Uses Map's insertion order to track recency - first key is oldest.\n */\nexport class LRUCache<K, V> {\n private cache = new Map<K, V>();\n private readonly maxCapacity: number;\n\n constructor(capacity: number) {\n if (capacity < 1) {\n throw new RangeError(\"LRU cache capacity must be at least 1\");\n }\n this.maxCapacity = capacity;\n }\n\n /**\n * Get a value from the cache.\n * If found, moves the entry to the end (most recently used).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (most recently used) by reinserting\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Set a value in the cache.\n * If key exists, updates and moves to end.\n * If at capacity, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n if (this.cache.has(key)) {\n // Update existing - delete and reinsert at end\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxCapacity) {\n // Evict oldest (first key in Map iteration order)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n this.cache.set(key, value);\n }\n\n /**\n * Delete an entry from the cache.\n */\n delete(key: K): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Check if a key exists in the cache.\n * Does NOT update recency.\n */\n has(key: K): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get the current number of entries in the cache.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get the maximum capacity of the cache.\n */\n capacity(): number {\n return this.maxCapacity;\n }\n}\n","import { LRUCache } from \"./lru-cache\";\nimport { DatabaseSyncInstance } from \"./types/database-sync-instance\";\nimport type { StatementSyncInstance } from \"./types/statement-sync-instance\";\n\n/**\n * Default capacity for the statement cache.\n * Matches Node.js SQLTagStore default.\n */\nconst DEFAULT_CAPACITY = 1000;\n\n/**\n * SQLTagStore provides cached prepared statements via tagged template syntax.\n *\n * @example\n * ```js\n * const sql = db.createTagStore();\n * sql.run`INSERT INTO users VALUES (${id}, ${name})`;\n * const user = sql.get`SELECT * FROM users WHERE id = ${id}`;\n * ```\n */\nexport class SQLTagStore {\n private readonly database: DatabaseSyncInstance;\n private readonly cache: LRUCache<string, StatementSyncInstance>;\n private readonly maxCapacity: number;\n\n constructor(db: DatabaseSyncInstance, capacity: number = DEFAULT_CAPACITY) {\n if (!db.isOpen) {\n throw new Error(\"Database is not open\");\n }\n this.database = db;\n this.maxCapacity = capacity;\n this.cache = new LRUCache(capacity);\n }\n\n /**\n * Returns the associated database instance.\n */\n get db(): DatabaseSyncInstance {\n return this.database;\n }\n\n /**\n * Returns the maximum capacity of the statement cache.\n */\n get capacity(): number {\n return this.maxCapacity;\n }\n\n /**\n * Returns the current number of cached statements.\n */\n 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 * If a cached statement has been finalized, it's evicted and a new one is prepared.\n */\n private getOrPrepare(strings: TemplateStringsArray): StatementSyncInstance {\n if (!this.database.isOpen) {\n throw new Error(\"Database is not open\");\n }\n\n const sql = this.buildSQL(strings);\n\n // Check cache - evict if finalized\n const cached = this.cache.get(sql);\n if (cached) {\n if (!cached.finalized) {\n return cached;\n }\n // Statement was finalized externally - remove from cache\n this.cache.delete(sql);\n }\n\n // Prepare new statement and cache it\n const stmt = this.database.prepare(sql);\n this.cache.set(sql, stmt);\n return stmt;\n }\n\n /**\n * Build the SQL string by joining template parts with `?` placeholders.\n */\n private buildSQL(strings: TemplateStringsArray): string {\n let sql = strings[0] ?? \"\";\n for (let i = 1; i < strings.length; i++) {\n // eslint-disable-next-line security/detect-object-injection -- Index is from controlled for-loop\n sql += \"?\" + (strings[i] ?? \"\");\n }\n return sql;\n }\n}\n"],"mappings":"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","names":["path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photostructure/sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Drop-in replacement for node:sqlite",
|
|
5
5
|
"homepage": "https://photostructure.github.io/node-sqlite/",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@types/jest": "^30.0.0",
|
|
128
128
|
"@types/node": "^24.10.1",
|
|
129
|
-
"better-sqlite3": "^12.
|
|
129
|
+
"better-sqlite3": "^12.5.0",
|
|
130
130
|
"cross-env": "^10.1.0",
|
|
131
131
|
"del-cli": "^7.0.0",
|
|
132
132
|
"eslint": "^9.39.1",
|
|
@@ -136,19 +136,19 @@
|
|
|
136
136
|
"jest": "^30.2.0",
|
|
137
137
|
"node-gyp": "^12.1.0",
|
|
138
138
|
"npm-check-updates": "^19",
|
|
139
|
-
"npm-run-
|
|
139
|
+
"npm-run-all2": "8.0.4",
|
|
140
140
|
"prebuildify": "^6.0.1",
|
|
141
|
-
"prettier": "^3.
|
|
141
|
+
"prettier": "^3.7.4",
|
|
142
142
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
143
|
-
"ts-jest": "^29.4.
|
|
143
|
+
"ts-jest": "^29.4.6",
|
|
144
144
|
"tsup": "^8.5.1",
|
|
145
|
-
"tsx": "^4.
|
|
146
|
-
"typedoc": "^0.28.
|
|
145
|
+
"tsx": "^4.21.0",
|
|
146
|
+
"typedoc": "^0.28.15",
|
|
147
147
|
"typescript": "^5.9.3",
|
|
148
|
-
"typescript-eslint": "^8.
|
|
148
|
+
"typescript-eslint": "^8.49.0"
|
|
149
149
|
},
|
|
150
150
|
"versions": {
|
|
151
|
-
"nodejs": "v25.2.2@
|
|
151
|
+
"nodejs": "v25.2.2@c57a40c",
|
|
152
152
|
"sqlite": "3.51.1"
|
|
153
153
|
}
|
|
154
154
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -97,10 +97,12 @@ docker exec "$CONTAINER_NAME" sh -c "
|
|
|
97
97
|
$BUILD_CMD
|
|
98
98
|
"
|
|
99
99
|
|
|
100
|
-
# Copy artifacts back with
|
|
101
|
-
docker
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
# Copy artifacts back with correct ownership (docker cp creates root-owned files)
|
|
101
|
+
docker exec "$CONTAINER_NAME" tar -cf - -C /tmp/project \
|
|
102
|
+
prebuilds \
|
|
103
|
+
build \
|
|
104
|
+
config.gypi \
|
|
105
|
+
2>/dev/null | tar -xf - --owner="$(id -u)" --group="$(id -g)" || true
|
|
104
106
|
|
|
105
107
|
# Clean up container
|
|
106
108
|
docker rm -f "$CONTAINER_NAME" >/dev/null
|
package/src/sql-tag-store.ts
CHANGED