@happyvertical/sql 0.86.1 → 0.86.3

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.
Files changed (43) hide show
  1. package/README.md +41 -0
  2. package/dist/chunks/{alter-utils-DeDDAATm.js → connection-cache-6EMfKWpo.js} +196 -2
  3. package/dist/chunks/connection-cache-6EMfKWpo.js.map +1 -0
  4. package/dist/chunks/{duckdb-jSSa2MbC.js → duckdb-D2Nah7NF.js} +22 -10
  5. package/dist/chunks/duckdb-D2Nah7NF.js.map +1 -0
  6. package/dist/chunks/{duckdb-schema-utils-B-XBP8QF.js → duckdb-resources-Cci6Lo6H.js} +43 -2
  7. package/dist/chunks/duckdb-resources-Cci6Lo6H.js.map +1 -0
  8. package/dist/chunks/redact-database-url-CfMDvSdy.js +33 -0
  9. package/dist/chunks/redact-database-url-CfMDvSdy.js.map +1 -0
  10. package/dist/chunks/{sqlite-Crx5PQ3G.js → sqlite-B2bnGN2x.js} +106 -85
  11. package/dist/chunks/sqlite-B2bnGN2x.js.map +1 -0
  12. package/dist/chunks/sqlite-connection-cache-0iFbrrI3.js +65 -0
  13. package/dist/chunks/sqlite-connection-cache-0iFbrrI3.js.map +1 -0
  14. package/dist/chunks/{sqlite-native-DCAaADpP.js → sqlite-native-Dc8eFStu.js} +44 -31
  15. package/dist/chunks/sqlite-native-Dc8eFStu.js.map +1 -0
  16. package/dist/chunks/types-uVFHgaSU.js.map +1 -1
  17. package/dist/duckdb.d.ts.map +1 -1
  18. package/dist/index.js +119 -118
  19. package/dist/index.js.map +1 -1
  20. package/dist/json.d.ts +5 -5
  21. package/dist/json.d.ts.map +1 -1
  22. package/dist/postgres.d.ts +18 -5
  23. package/dist/postgres.d.ts.map +1 -1
  24. package/dist/shared/connection-cache.d.ts +13 -0
  25. package/dist/shared/connection-cache.d.ts.map +1 -0
  26. package/dist/shared/duckdb-resources.d.ts +8 -0
  27. package/dist/shared/duckdb-resources.d.ts.map +1 -0
  28. package/dist/shared/redact-database-url.d.ts +3 -0
  29. package/dist/shared/redact-database-url.d.ts.map +1 -0
  30. package/dist/shared/sqlite-connection-cache.d.ts +4 -0
  31. package/dist/shared/sqlite-connection-cache.d.ts.map +1 -0
  32. package/dist/shared/types.d.ts +37 -4
  33. package/dist/shared/types.d.ts.map +1 -1
  34. package/dist/sqlite-native.d.ts +8 -0
  35. package/dist/sqlite-native.d.ts.map +1 -1
  36. package/dist/sqlite.d.ts +4 -4
  37. package/dist/sqlite.d.ts.map +1 -1
  38. package/package.json +2 -2
  39. package/dist/chunks/alter-utils-DeDDAATm.js.map +0 -1
  40. package/dist/chunks/duckdb-jSSa2MbC.js.map +0 -1
  41. package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js.map +0 -1
  42. package/dist/chunks/sqlite-Crx5PQ3G.js.map +0 -1
  43. package/dist/chunks/sqlite-native-DCAaADpP.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-B2bnGN2x.js","names":[],"sources":["../../src/sqlite.ts"],"sourcesContent":["import { DatabaseError } from '@happyvertical/utils';\nimport type { Client } from '@libsql/client';\nimport { DatabaseSchemaManager } from './schema-manager';\nimport {\n generateAddColumnStatement,\n generateCreateIndexStatement,\n validateColumnName,\n validateColumnNames,\n validateIndexName,\n validateTableName,\n} from './shared/alter-utils';\nimport { validateDatabaseCacheOptions } from './shared/connection-cache';\nimport { redactDatabaseUrl } from './shared/redact-database-url';\nimport { getCachedSqliteDatabase } from './shared/sqlite-connection-cache';\nimport { createTransactionLock } from './shared/transaction-lock';\nimport type {\n ColumnDefinition,\n ColumnDefinitionWithName,\n DatabaseInterface,\n DatabaseOptions,\n IndexDefinition,\n QueryResult,\n SchemaInitializationOptions,\n SqliteCapabilitiesOptions,\n TableInterface,\n TableSchemaInfo,\n TransactionHandle,\n UpsertOptions,\n} from './shared/types';\nimport { resolveSchemas } from './shared/types';\nimport {\n buildWhere,\n formatDbError,\n resolveInsertColumns,\n} from './shared/utils';\n\n/**\n * Connection cache for in-memory databases with memoryId\n * Enables sharing of :memory: databases across multiple getDatabase() calls\n */\nconst NULL_AWARE_UPSERT_MAX_ATTEMPTS = 8;\nconst NULL_AWARE_UPSERT_BASE_DELAY_MS = 25;\nconst nullAwareUpsertLocks = new Map<string, Promise<void>>();\n\ntype SqliteExecutor = Pick<Client, 'execute'>;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetriableSqliteTransactionError(error: unknown): boolean {\n const formatted = formatDbError(error).toLowerCase();\n return (\n formatted.includes('busy') ||\n formatted.includes('locked') ||\n formatted.includes('database is locked')\n );\n}\n\nasync function withNullAwareUpsertLock<T>(\n lockKey: string,\n callback: () => Promise<T>,\n): Promise<T> {\n const previous = nullAwareUpsertLocks.get(lockKey) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const next = previous.catch(() => {}).then(() => current);\n\n nullAwareUpsertLocks.set(lockKey, next);\n await previous.catch(() => {});\n\n try {\n return await callback();\n } finally {\n release();\n\n if (nullAwareUpsertLocks.get(lockKey) === next) {\n nullAwareUpsertLocks.delete(lockKey);\n }\n }\n}\n\n/**\n * Generates a unique identifier for in-memory databases\n * @returns A unique database identifier string\n */\nfunction generateDbId(): string {\n return `memory-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Creates a LibSQL client using the default client implementation\n * Supports in-memory databases and remote LibSQL URLs\n *\n * @param options - SQLite connection options\n * @returns Promise resolving to a LibSQL client instance\n */\nasync function createLibSQLClient(options: SqliteOptions): Promise<Client> {\n const { url = ':memory:', authToken, encryptionKey } = options;\n\n // Normalize URLs: add file:// prefix for local paths\n let libsqlUrl = url;\n const remoteUrl = url.replace(/[\\t\\n\\r]/g, '').trim();\n const hasRemoteScheme = /^(?:https?|libsql):/i.test(remoteUrl);\n if (hasRemoteScheme) {\n libsqlUrl = remoteUrl;\n let parsedRemoteUrl: URL;\n try {\n parsedRemoteUrl = new URL(remoteUrl);\n } catch {\n const safeUrl = redactDatabaseUrl(remoteUrl);\n throw new DatabaseError(`Invalid remote database URL: ${safeUrl}`, {\n url: safeUrl,\n });\n }\n if (!/^(?:https?|libsql):\\/\\//i.test(remoteUrl) || !parsedRemoteUrl.host) {\n const safeUrl = redactDatabaseUrl(remoteUrl);\n throw new DatabaseError(`Invalid remote database URL: ${safeUrl}`, {\n url: safeUrl,\n });\n }\n }\n if (url !== ':memory:' && !hasRemoteScheme && !url.startsWith('file:')) {\n // Local file path - resolve to absolute and add file:// prefix\n const { resolve } = await import('node:path');\n const absolutePath = resolve(url);\n // Use file:// format (file URL scheme with authority component omitted)\n libsqlUrl = `file://${absolutePath}`;\n }\n\n try {\n // Use explicit external import to avoid bundling\n const libsqlClient = '@libsql/client';\n const { createClient } = await import(/* @vite-ignore */ libsqlClient);\n return createClient({ url: libsqlUrl, authToken, encryptionKey });\n } catch (error) {\n const errorMessage = redactDatabaseUrl(\n error instanceof Error ? error.message : String(error),\n );\n const safeUrl = redactDatabaseUrl(libsqlUrl);\n\n // Provide helpful error messages for common issues\n if (errorMessage?.includes('URL_SCHEME_NOT_SUPPORTED')) {\n throw new DatabaseError(\n `Unsupported URL scheme. Use ':memory:' for in-memory databases or 'libsql://' for remote LibSQL databases. URL: ${safeUrl}`,\n { url: safeUrl, originalError: errorMessage },\n );\n }\n\n // Re-throw other errors with context\n throw new DatabaseError(`Failed to create LibSQL client: ${errorMessage}`, {\n url: safeUrl,\n originalError: errorMessage,\n });\n }\n}\n\n/**\n * Configuration options for SQLite database connections\n */\nexport interface SqliteOptions extends DatabaseOptions {\n /**\n * Connection URL for SQLite database\n * Supported schemes:\n * - ':memory:' for in-memory databases\n * - 'file:path/to/database.db' for local file databases\n * - 'libsql://...' for remote LibSQL/Turso databases\n */\n url?: string;\n\n /**\n * Authentication token for Turso/LibSQL remote connections\n */\n authToken?: string;\n\n /**\n * Encryption key for encrypted SQLite databases (LibSQL feature)\n */\n encryptionKey?: string;\n\n /**\n * Unique identifier for in-memory databases to enable connection sharing\n * Calls using the same dbid and the same LibSQL/native adapter path receive\n * the same database connection instance. `clearCache` evicts both paths.\n *\n * Auto-generated for :memory: databases if not provided.\n */\n dbid?: string;\n\n /**\n * Optional native SQLite capabilities for local development and testing.\n *\n * When any capability is enabled, the adapter uses the internal native\n * SQLite implementation instead of the LibSQL client path. Native\n * capabilities are only supported for local SQLite databases.\n */\n capabilities?: SqliteCapabilitiesOptions;\n\n /**\n * How long a queued transaction waits for the connection, in milliseconds.\n *\n * Applies to the single-connection adapters — SQLite (both the LibSQL and\n * native paths), DuckDB and JSON. Those drive one connection, so transactions\n * run one at a time and an overlapping `transaction()` waits its turn.\n * PostgreSQL pools and ignores this.\n *\n * The clock starts when the call queues, not when the connection frees, so\n * this bounds the total wait rather than any single transaction: raise it for\n * workloads with long transactions *or* with sustained bursts on one\n * connection. Must be positive and finite.\n *\n * Read once, when the connection is created. Adapters that cache connections\n * hand a later `getDatabase()` for the same database the existing one, which\n * keeps the timeout the first caller asked for.\n *\n * @default 30000\n */\n transactionQueueTimeout?: number;\n\n /**\n * Schema definitions for tables.\n * Accepts a record or a lazy function (see SchemasOption).\n *\n * When provided, these schemas will be used for table creation.\n * Accepts a lazy function to defer schema building until needed.\n */\n schemas?: import('./shared/types').SchemasOption;\n}\n\nfunction hasNativeSqliteCapabilities(options: SqliteOptions): boolean {\n return Boolean(\n options.capabilities?.notifications || options.capabilities?.vector,\n );\n}\n\n/**\n * Creates tables from provided schema definitions\n *\n * @param client - LibSQL client\n * @param schemas - Schema definitions to create\n */\nasync function createTablesFromSchemas(\n client: any,\n schemas: Record<string, import('./shared/types').SchemaProvider>,\n): Promise<void> {\n for (const [tableName, schema] of Object.entries(schemas)) {\n try {\n // Check if table already exists\n const result = await client.execute({\n sql: `SELECT name FROM sqlite_master WHERE type='table' AND name=?`,\n args: [tableName],\n });\n\n if (result.rows.length > 0) {\n console.log(`[sqlite] Table ${tableName} already exists, skipping`);\n continue;\n }\n\n console.log(`[sqlite] Creating table ${tableName} from provided schema`);\n\n // Create table from DDL\n await client.execute(schema.ddl);\n\n // Create indexes\n if (schema.indexes && schema.indexes.length > 0) {\n for (const indexSQL of schema.indexes) {\n try {\n await client.execute(indexSQL);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create index for ${tableName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n\n // Create triggers (if supported)\n if (schema.triggers && schema.triggers.length > 0) {\n for (const triggerSQL of schema.triggers) {\n try {\n await client.execute(triggerSQL);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create trigger for ${tableName}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n }\n } catch (error) {\n console.error(\n `[sqlite] Error creating table ${tableName}:`,\n error instanceof Error ? error.message : String(error),\n );\n console.error(`[sqlite] DDL was:`, schema.ddl?.substring(0, 200) + '...');\n throw new DatabaseError(\n `Failed to create table ${tableName} from schema: ${error instanceof Error ? error.message : String(error)}`,\n {\n tableName,\n schema,\n originalError: error instanceof Error ? error.message : String(error),\n },\n );\n }\n }\n}\n\n/**\n * Creates a SQLite database adapter\n *\n * @param options - SQLite connection options\n * @returns Database interface for SQLite\n */\nexport async function getDatabase(\n options: SqliteOptions = {},\n): Promise<DatabaseInterface> {\n validateDatabaseCacheOptions(options);\n const url = options.url || ':memory:';\n\n // Auto-generate dbid for :memory: databases if not provided\n // Mutate options object to ensure child objects reuse the same connection\n if (url === ':memory:' && !options.dbid && options.cache !== false) {\n options.dbid = generateDbId();\n }\n\n if (hasNativeSqliteCapabilities(options)) {\n const sqliteNative = await import('./sqlite-native.js');\n return sqliteNative.getNativeSqliteDatabase(options);\n }\n\n return getCachedSqliteDatabase('libsql', options, () =>\n createDatabase(options, url),\n );\n}\n\nasync function createDatabase(\n options: SqliteOptions,\n url: string,\n): Promise<DatabaseInterface> {\n return (async () => {\n const client = await createLibSQLClient(options);\n\n // One lock per connection. Nothing else in this closure escapes to another\n // caller, and `getDatabase` hands cached callers this same closure, so\n // \"created here\" and \"per connection\" are the same scope.\n const connectionLock = createTransactionLock(\n 'sqlite',\n options.transactionQueueTimeout,\n );\n\n // Initialize tables from provided schemas (resolves lazy function if needed)\n const resolvedSchemas = resolveSchemas(options.schemas);\n if (resolvedSchemas && Object.keys(resolvedSchemas).length > 0) {\n try {\n await createTablesFromSchemas(client, resolvedSchemas);\n } catch (error) {\n try {\n client.close();\n } catch (cleanupError) {\n throw new AggregateError(\n [error, cleanupError],\n 'LibSQL schema initialization and cleanup failed',\n );\n }\n throw error;\n }\n }\n\n /**\n * Serializes a value for SQLite storage\n * Converts objects and arrays to JSON strings\n * Converts Dates to ISO strings\n * Passes through primitives unchanged\n */\n const serializeValue = (value: any): any => {\n if (value === null || value === undefined) {\n return value;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === 'object') {\n return JSON.stringify(value);\n }\n return value;\n };\n\n /**\n * Serializes all values in an object\n */\n const serializeRecord = (\n record: Record<string, any>,\n ): Record<string, any> => {\n const serialized: Record<string, any> = {};\n for (const [key, value] of Object.entries(record)) {\n // Skip undefined values - they cannot be passed to the database\n if (value === undefined) {\n continue;\n }\n serialized[key] = serializeValue(value);\n }\n return serialized;\n };\n\n const hasNullConflictValue = (\n conflictColumns: string[],\n data: Record<string, any>,\n ): boolean => conflictColumns.some((col) => data[col] === null);\n\n const buildNullAwareUpsertLockKey = (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n ): string => {\n const conflictValues = conflictColumns\n .map((col) => `${col}:${JSON.stringify(data[col])}`)\n .join('|');\n return `${url}:${table}:${conflictValues}`;\n };\n\n const validateUpsertConflictColumns = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): void => {\n const missingColumns = conflictColumns.filter(\n (col) => !(col in serializedData),\n );\n\n if (missingColumns.length > 0) {\n throw new DatabaseError('Conflict columns missing from data', {\n table,\n conflictColumns,\n missingColumns,\n availableColumns: Object.keys(serializedData),\n hint: 'All columns specified in ON CONFLICT must be present in the data being inserted. Undefined values are filtered out during serialization - consider using null or an empty string instead.',\n });\n }\n };\n\n const executeStandardUpsert = async (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): Promise<QueryResult> => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const placeholders = keys.map(() => '?').join(', ');\n const updateSet = keys\n .map((key) => `${key} = excluded.${key}`)\n .join(', ');\n const conflict = conflictColumns.join(', ');\n const sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders}) ON CONFLICT(${conflict}) DO UPDATE SET ${updateSet}`;\n\n const result = await client.execute({ sql, args: values });\n return { operation: 'upsert', affected: result.rowsAffected };\n };\n\n const executeNullAwareUpsertAttempt = async (\n executor: SqliteExecutor,\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): Promise<QueryResult> => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const updateSet = keys.map((key) => `${key} = ?`).join(', ');\n const whereClause = conflictColumns\n .map((col) => `${col} IS ?`)\n .join(' AND ');\n const whereValues = conflictColumns.map((col) => serializedData[col]);\n\n const updateSql = `UPDATE ${table} SET ${updateSet} WHERE ${whereClause}`;\n const updateResult = await executor.execute({\n sql: updateSql,\n args: [...values, ...whereValues],\n });\n\n if ((updateResult.rowsAffected ?? 0) > 0) {\n return { operation: 'upsert', affected: updateResult.rowsAffected };\n }\n\n const placeholders = keys.map(() => '?').join(', ');\n const insertSql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`;\n const insertResult = await executor.execute({\n sql: insertSql,\n args: values,\n });\n\n return { operation: 'upsert', affected: insertResult.rowsAffected };\n };\n\n const executeNullAwareUpsertWithRetry = async (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): Promise<QueryResult> => {\n if (url === ':memory:') {\n return executeNullAwareUpsertAttempt(\n client,\n table,\n conflictColumns,\n serializedData,\n );\n }\n\n let lastError: unknown;\n\n for (\n let attempt = 0;\n attempt < NULL_AWARE_UPSERT_MAX_ATTEMPTS;\n attempt++\n ) {\n let transaction: Awaited<ReturnType<Client['transaction']>> | undefined;\n\n // This opens a transaction on the shared connection like any other, so\n // it takes the same lock — otherwise a null-aware upsert overlapping a\n // `transaction()` call reintroduces exactly the corruption the lock\n // exists to prevent. Acquired per attempt so the retry backoff below\n // waits without holding the connection.\n const releaseConnection = await connectionLock.acquire();\n try {\n transaction = await client.transaction('write');\n\n const result = await executeNullAwareUpsertAttempt(\n transaction,\n table,\n conflictColumns,\n serializedData,\n );\n\n await transaction.commit();\n return result;\n } catch (error) {\n lastError = error;\n\n if (transaction && !transaction.closed) {\n try {\n await transaction.rollback();\n } catch (_rollbackError) {\n // Preserve the original failure; rollback errors are secondary here.\n }\n }\n } finally {\n transaction?.close();\n releaseConnection();\n }\n\n if (\n lastError &&\n attempt < NULL_AWARE_UPSERT_MAX_ATTEMPTS - 1 &&\n isRetriableSqliteTransactionError(lastError)\n ) {\n await delay(NULL_AWARE_UPSERT_BASE_DELAY_MS * 2 ** attempt);\n continue;\n }\n\n break;\n }\n\n throw lastError;\n };\n\n /**\n * Rejects hostile table/column identifiers before an upsert builds SQL.\n *\n * Called from the public entry points rather than from `executeUpsert`, so\n * the failure reaches the caller naming the bad identifier instead of being\n * flattened into the wrapper's generic \"Failed to upsert record into table\".\n *\n * Validates the keys of the already-serialized snapshot — the exact array\n * the executors interpolate — not a fresh enumeration of the caller's\n * `data`. Enumerating `data` again could disagree with the snapshot if it is\n * a Proxy whose `ownKeys` trap returns different keys on successive reads.\n */\n const validateUpsertIdentifiers = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): void => {\n validateTableName(table);\n validateColumnNames([...conflictColumns, ...Object.keys(serializedData)]);\n };\n\n const executeUpsert = async (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n options: UpsertOptions | undefined,\n acquireTransaction: boolean,\n ): Promise<QueryResult> => {\n validateUpsertConflictColumns(table, conflictColumns, serializedData);\n\n if (\n options?.nullsDistinct ||\n !hasNullConflictValue(conflictColumns, serializedData)\n ) {\n return executeStandardUpsert(table, conflictColumns, serializedData);\n }\n\n if (!acquireTransaction) {\n return executeNullAwareUpsertAttempt(\n client,\n table,\n conflictColumns,\n serializedData,\n );\n }\n\n return withNullAwareUpsertLock(\n buildNullAwareUpsertLockKey(table, conflictColumns, serializedData),\n () =>\n executeNullAwareUpsertWithRetry(\n table,\n conflictColumns,\n serializedData,\n ),\n );\n };\n\n /**\n * Inserts one or more records into a table\n *\n * @param table - Table name\n * @param data - Single record or array of records to insert\n * @returns Promise resolving to operation result\n * @throws Error if the insert operation fails\n */\n const insert = async (\n table: string,\n data: Record<string, any> | Record<string, any>[],\n ): Promise<QueryResult> => {\n validateTableName(table);\n let sql: string;\n let values: any[];\n\n if (Array.isArray(data)) {\n if (data.length === 0) {\n return { operation: 'insert', affected: 0 };\n }\n // Serialize all records in the array\n const serializedRecords: Array<Record<string, any>> = data.map(\n (record) => serializeRecord(record),\n );\n const keys = resolveInsertColumns(table, serializedRecords);\n validateColumnNames(keys);\n const placeholders = serializedRecords\n .map(() => `(${keys.map(() => '?').join(', ')})`)\n .join(', ');\n sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES ${placeholders}`;\n // Project through `keys` rather than each record's own key order, so a\n // record whose keys were inserted in a different order still binds each\n // value to its own column.\n values = serializedRecords.flatMap((record) =>\n keys.map((key) => record[key]),\n );\n } else {\n // Serialize the single record\n const serializedData = serializeRecord(data);\n const keys = Object.keys(serializedData);\n validateColumnNames(keys);\n const placeholders = keys.map(() => '?').join(', ');\n sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`;\n values = Object.values(serializedData);\n }\n try {\n const result = await client.execute({ sql: sql, args: values });\n return { operation: 'insert', affected: result.rowsAffected };\n } catch (e) {\n throw new DatabaseError('Failed to insert records into table', {\n table,\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Retrieves a single record matching the where criteria\n *\n * @param table - Table name\n * @param where - Criteria to match records\n * @returns Promise resolving to matching record or null if not found\n * @throws Error if the query fails\n */\n const get = async (\n table: string,\n where: Record<string, any>,\n ): Promise<Record<string, any> | null> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildWhere(where, 1, 'sqlite');\n if (!whereClause) {\n throw new DatabaseError(\n 'GET requires at least one WHERE condition to prevent returning an arbitrary record',\n { table },\n );\n }\n\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n const result = await client.execute({ sql: sql, args: values });\n return result.rows[0] || null;\n } catch (e) {\n throw new DatabaseError('Failed to retrieve record from table', {\n table,\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Retrieves multiple records matching the where criteria\n *\n * @param table - Table name\n * @param where - Criteria to match records\n * @returns Promise resolving to array of matching records\n * @throws Error if the query fails\n */\n const list = async (\n table: string,\n where: Record<string, any>,\n ): Promise<Record<string, any>[]> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildWhere(where, 1, 'sqlite');\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n const result = await client.execute({ sql, args: values });\n return result.rows;\n } catch (e) {\n throw new DatabaseError('Failed to list records from table', {\n table,\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Updates records matching the where criteria\n *\n * @param table - Table name\n * @param where - Criteria to match records to update\n * @param data - New data to set\n * @returns Promise resolving to operation result\n * @throws Error if the update operation fails\n */\n const update = async (\n table: string,\n where: Record<string, any>,\n data: Record<string, any>,\n ): Promise<QueryResult> => {\n validateTableName(table);\n // Serialize the data to update\n const serializedData = serializeRecord(data);\n const keys = Object.keys(serializedData);\n validateColumnNames(keys);\n const values = Object.values(serializedData);\n const setClause = keys.map((key, i) => `${key} = $${i + 1}`).join(', ');\n const { sql: whereClause, values: whereValues } = buildWhere(\n where,\n values.length + 1,\n 'sqlite',\n );\n if (!whereClause) {\n throw new DatabaseError(\n 'UPDATE requires at least one WHERE condition to prevent accidental update of all records',\n { table },\n );\n }\n\n const sql = `UPDATE ${table} SET ${setClause} ${whereClause}`;\n try {\n const result = await client.execute({\n sql,\n args: [...values, ...whereValues],\n });\n return { operation: 'update', affected: result.rowsAffected };\n } catch (e) {\n throw new DatabaseError('Failed to update records in table', {\n table,\n sql,\n values: [...values, ...whereValues],\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Inserts a record or updates it if it already exists (UPSERT)\n *\n * @param table - Table name\n * @param conflictColumns - Columns that define the uniqueness constraint\n * @param data - Data to insert or update\n * @returns Promise resolving to operation result\n * @throws Error if the upsert operation fails\n */\n const upsert = async (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n options?: UpsertOptions,\n ): Promise<QueryResult> => {\n // Snapshot the record and the conflict columns once and thread both\n // through validation and SQL, so neither a hostile record nor a hostile\n // conflict-column array can present different identifiers to each.\n const serializedData = serializeRecord(data);\n const conflictCols = [...conflictColumns];\n validateUpsertIdentifiers(table, conflictCols, serializedData);\n try {\n return await executeUpsert(\n table,\n conflictCols,\n serializedData,\n options,\n true,\n );\n } catch (e) {\n if (\n e instanceof DatabaseError &&\n e.message === 'Conflict columns missing from data'\n ) {\n throw e;\n }\n\n throw new DatabaseError('Failed to upsert record into table', {\n table,\n values: data,\n conflictColumns,\n originalError: formatDbError(e),\n });\n }\n };\n\n // Names nested savepoints. Only ever appended to an identifier the adapter\n // generates, never caller input.\n let savepointSequence = 0;\n\n const upsertInCurrentTransaction = async (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n options?: UpsertOptions,\n ): Promise<QueryResult> => {\n // Snapshot the record and the conflict columns once and thread both\n // through validation and SQL, so neither a hostile record nor a hostile\n // conflict-column array can present different identifiers to each.\n const serializedData = serializeRecord(data);\n const conflictCols = [...conflictColumns];\n validateUpsertIdentifiers(table, conflictCols, serializedData);\n try {\n return await executeUpsert(\n table,\n conflictCols,\n serializedData,\n options,\n false,\n );\n } catch (e) {\n if (\n e instanceof DatabaseError &&\n e.message === 'Conflict columns missing from data'\n ) {\n throw e;\n }\n\n throw new DatabaseError('Failed to upsert record into table', {\n table,\n values: data,\n conflictColumns,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Gets a record matching the where criteria or inserts it if not found\n *\n * @param table - Table name\n * @param where - Criteria to match existing record\n * @param data - Data to insert if no record found\n * @returns Promise resolving to the record (either retrieved or newly inserted)\n * @throws Error if the operation fails or if the record cannot be retrieved after insert\n */\n const getOrInsert = async (\n table: string,\n where: Record<string, any>,\n data: Record<string, any>,\n ): Promise<Record<string, any>> => {\n const result = await get(table, where);\n if (result) return result;\n await insert(table, data);\n\n const inserted = await get(table, where);\n if (!inserted) {\n throw new DatabaseError('Failed to insert and retrieve record', {\n table,\n where,\n data,\n });\n }\n return inserted;\n };\n\n /**\n * Deletes records from a table matching the where criteria\n *\n * @param table - Table name\n * @param where - Criteria to match records for deletion\n * @returns Promise resolving to operation result with count of deleted rows\n * @throws Error if the delete operation fails\n */\n const deleteRecords = async (\n table: string,\n where: Record<string, any>,\n ): Promise<QueryResult> => {\n validateTableName(table);\n\n const keys = Object.keys(where);\n if (keys.length === 0) {\n throw new DatabaseError(\n 'DELETE requires at least one WHERE condition to prevent accidental deletion of all records',\n { table },\n );\n }\n\n const { sql: whereClause, values } = buildWhere(where, 1, 'sqlite');\n\n try {\n await client.execute({\n sql: `DELETE FROM ${table} ${whereClause}`,\n args: values,\n });\n\n // SQLite doesn't reliably return affected rows, so we can't provide an exact count\n return { operation: 'delete', affected: 1 };\n } catch (e) {\n throw new DatabaseError('Failed to delete records from table', {\n table,\n where,\n originalError: e,\n });\n }\n };\n\n /**\n * Counts records in a table matching the where criteria\n *\n * @param table - Table name\n * @param where - Criteria to match records (optional, counts all if omitted)\n * @returns Promise resolving to count of matching records\n * @throws Error if the count operation fails\n */\n const count = async (\n table: string,\n where?: Record<string, any>,\n ): Promise<number> => {\n validateTableName(table);\n\n try {\n if (!where || Object.keys(where).length === 0) {\n // Count all records. Built as a plain string rather than through the\n // `pluck` tagged template, which would bind the table name as a\n // parameter and emit `SELECT COUNT(*) FROM ?`. Interpolating the\n // identifier is safe because validateTableName ran above.\n const result = await client.execute({\n sql: `SELECT COUNT(*) as count FROM ${table}`,\n args: [],\n });\n return Number(result.rows[0]?.count) || 0;\n }\n\n // Count with conditions\n const { sql: whereClause, values } = buildWhere(where, 1, 'sqlite');\n\n const result = await client.execute({\n sql: `SELECT COUNT(*) as count FROM ${table} ${whereClause}`,\n args: values,\n });\n\n return Number(result.rows[0]?.count) || 0;\n } catch (e) {\n throw new DatabaseError('Failed to count records in table', {\n table,\n where,\n originalError: e,\n });\n }\n };\n\n /**\n * Checks if a table exists in the database\n *\n * @param tableName - Name of the table to check\n * @returns Promise resolving to boolean indicating if the table exists\n */\n const tableExists = async (tableName: string): Promise<boolean> => {\n const tableExists =\n !!(await pluck`SELECT name FROM sqlite_master WHERE type='table' AND name=${tableName}`);\n return tableExists;\n };\n\n /**\n * Synchronizes database schema with provided SQL DDL\n * Creates tables if they don't exist and adds missing columns\n *\n * @param schema - SQL schema definition with CREATE TABLE statements\n * @returns Promise that resolves when schema is synchronized\n */\n const syncSchema = async (schema: string): Promise<void> => {\n console.log(\n '[sqlite.syncSchema] Starting schema sync for dbid:',\n options.dbid,\n );\n console.log('[sqlite.syncSchema] Schema length:', schema.length, 'chars');\n\n // List of SQL reserved keywords that need to stay quoted\n const reservedKeywords = new Set([\n 'references',\n 'order',\n 'group',\n 'table',\n 'index',\n 'select',\n 'from',\n 'where',\n 'join',\n 'left',\n 'right',\n 'inner',\n 'outer',\n 'on',\n 'as',\n 'and',\n 'or',\n 'not',\n 'like',\n 'in',\n 'between',\n 'case',\n 'when',\n 'then',\n 'else',\n 'end',\n 'union',\n 'distinct',\n 'having',\n 'limit',\n 'offset',\n 'default',\n 'check',\n 'unique',\n 'primary',\n 'foreign',\n 'key',\n 'constraint',\n 'cascade',\n ]);\n\n // Normalize whitespace and simplify CAST expressions\n let normalizedSchema = schema\n .replace(/\\s+/g, ' ') // Replace multiple whitespace with single space\n .replace(/DEFAULT CAST\\(([^)]+)\\s+AS\\s+\\w+\\)/gi, 'DEFAULT $1') // Simplify CAST in DEFAULT clauses\n .trim();\n\n // Remove quotes around identifiers EXCEPT for reserved keywords\n normalizedSchema = normalizedSchema.replace(\n /\"(\\w+)\"/g,\n (match, identifier) => {\n return reservedKeywords.has(identifier.toLowerCase())\n ? match\n : identifier;\n },\n );\n\n console.log('[sqlite.syncSchema] Normalized schema:', normalizedSchema);\n\n // Split into individual commands\n const commands = normalizedSchema\n .split(';')\n .map((cmd) => cmd.trim())\n .filter((cmd) => cmd.length > 0);\n\n console.log(\n '[sqlite.syncSchema] Found',\n commands.length,\n 'commands to process',\n );\n\n // Execute each command\n for (const command of commands) {\n try {\n console.log(\n '[sqlite.syncSchema] Executing:',\n `${command.substring(0, 50)}...`,\n );\n await client.execute(command);\n console.log('[sqlite.syncSchema] Successfully executed command');\n } catch (error) {\n console.error(\n '[sqlite.syncSchema] Failed to execute command:',\n command,\n );\n console.error('[sqlite.syncSchema] Error:', error);\n throw error;\n }\n }\n };\n\n /**\n * Executes a callback within a database transaction\n * Automatically commits on success or rolls back on error\n *\n * @param callback - Function to execute within transaction\n * @returns Promise resolving to callback result\n */\n const transaction = async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> =>\n // Held across the whole BEGIN … COMMIT/ROLLBACK span. Without it two\n // overlapping calls raced on the one connection: the second BEGIN threw,\n // its catch ran ROLLBACK, and that rollback ended the *first*\n // transaction — half of its writes durable, half lost, and its promise\n // rejected, so the caller was told nothing had happened.\n connectionLock.run(async () => {\n try {\n await client.execute({ sql: 'BEGIN TRANSACTION', args: [] });\n\n const result = await callback(createTransactionScope());\n await client.execute({ sql: 'COMMIT', args: [] });\n return result;\n } catch (error) {\n // A failing ROLLBACK must not replace the caller's error — SQLite\n // reports \"cannot rollback - no transaction is active\" whenever the\n // transaction is already gone, which says nothing about what failed.\n try {\n await client.execute({ sql: 'ROLLBACK', args: [] });\n } catch {\n // Nothing left to roll back.\n }\n throw error;\n }\n });\n\n /**\n * Builds the transaction-scoped interface handed to a transaction callback.\n *\n * The only difference from the top-level interface is `transaction`: this\n * one re-enters the transaction already in progress under a savepoint\n * instead of issuing a second BEGIN. SQLite shares one connection, so the\n * old behaviour of re-exposing the top-level `transaction` sent a nested\n * call into `BEGIN` on a connection that was already in a transaction; that\n * throws, and the nested call's own ROLLBACK then discarded the *enclosing*\n * transaction's work while later writes silently committed in autocommit.\n */\n const createTransactionScope = (): DatabaseInterface => {\n // SQLite doesn't have separate transaction clients, so we reuse the same client\n const txDb: DatabaseInterface = {\n url,\n client,\n insert,\n get,\n list,\n update,\n upsert: upsertInCurrentTransaction,\n getOrInsert,\n delete: deleteRecords,\n count,\n table,\n many,\n single,\n pluck,\n execute,\n query,\n oo: many,\n oO: single,\n ox: pluck,\n xx: execute,\n tableExists,\n syncSchema,\n transaction: async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> => {\n savepointSequence += 1;\n const name = `hv_sp_${savepointSequence}`;\n await client.execute({ sql: `SAVEPOINT ${name}`, args: [] });\n try {\n const result = await callback(createTransactionScope());\n await client.execute({\n sql: `RELEASE SAVEPOINT ${name}`,\n args: [],\n });\n return result;\n } catch (error) {\n try {\n // ROLLBACK TO leaves the savepoint in place, so release it as\n // well or it accumulates for the life of the transaction.\n await client.execute({\n sql: `ROLLBACK TO SAVEPOINT ${name}`,\n args: [],\n });\n await client.execute({\n sql: `RELEASE SAVEPOINT ${name}`,\n args: [],\n });\n } catch {\n // The enclosing transaction is already unwinding; let its own\n // teardown deal with the connection.\n }\n throw error;\n }\n },\n };\n\n return txDb;\n };\n\n /**\n * Begins a new transaction and returns a handle for manual control\n *\n * Unlike transaction(), this gives you explicit control over commit/rollback.\n * Ideal for test isolation where you want to rollback after each test.\n *\n * @returns Promise resolving to a TransactionHandle\n */\n const beginTransaction = async (): Promise<TransactionHandle> => {\n // The handle owns the connection until the caller ends it, so the lock\n // is held across the gap rather than around a single call. A handle that\n // is never committed or rolled back therefore blocks every later\n // transaction until the queue timeout reports it.\n const releaseConnection = await connectionLock.acquire();\n try {\n await client.execute({ sql: 'BEGIN TRANSACTION', args: [] });\n } catch (error) {\n // No transaction was opened, so there is no handle to end it and\n // nothing left to release the connection.\n releaseConnection();\n throw error;\n }\n\n let active = true;\n\n // COMMIT and ROLLBACK can both throw, and the transaction is over either\n // way, so the connection goes back before the error is rethrown.\n const end = async (command: 'COMMIT' | 'ROLLBACK'): Promise<void> => {\n if (!active) {\n throw new DatabaseError('Transaction already ended', {});\n }\n try {\n await client.execute({ sql: command, args: [] });\n } catch (error) {\n // COMMIT can fail and leave the transaction *open* — SQLite documents\n // exactly that for SQLITE_BUSY. Releasing the connection then would hand\n // the next queued caller a connection still inside a transaction: its\n // BEGIN would throw, and its catch would ROLLBACK, discarding this\n // transaction's work. So normalize before releasing, the way the pooled\n // adapter's discardTxClient does.\n try {\n await client.execute({ sql: 'ROLLBACK', args: [] });\n } catch {\n // Already gone; nothing left to normalize.\n }\n throw error;\n } finally {\n active = false;\n releaseConnection();\n }\n };\n\n const commit = (): Promise<void> => end('COMMIT');\n\n const rollback = (): Promise<void> => end('ROLLBACK');\n\n const isActive = (): boolean => active;\n\n // Create a transaction-scoped database interface with commit/rollback.\n // The handle is inside a transaction for the same reason a callback scope\n // is, so it gets the same savepoint-based nesting rather than the\n // top-level `transaction`.\n const txHandle: TransactionHandle = {\n ...createTransactionScope(),\n commit,\n rollback,\n isActive,\n };\n\n return txHandle;\n };\n\n /**\n * Creates a table-specific interface for simplified table operations\n *\n * @param tableName - Table name\n * @returns TableInterface for the specified table\n */\n const table = (tableName: string): TableInterface => ({\n insert: (data) => insert(tableName, data),\n get: (where) => get(tableName, where),\n list: (where) => list(tableName, where),\n });\n\n /**\n * Parses a tagged template literal into a SQL query and values\n *\n * @param strings - Template strings\n * @param vars - Variables to interpolate into the query\n * @returns Object with SQL query and values array\n */\n const parseTemplate = (strings: TemplateStringsArray, ...vars: any[]) => {\n let sql = strings[0];\n const values = [];\n for (let i = 0; i < vars.length; i++) {\n values.push(vars[i]);\n sql += `?${strings[i + 1]}`;\n }\n return { sql, values };\n };\n\n /**\n * Executes a SQL query using template literals and returns a single value\n *\n * @param strings - Template strings\n * @param vars - Variables to interpolate into the query\n * @returns Promise resolving to a single value (first column of first row)\n * @throws Error if the query fails\n */\n const pluck = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<any> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n const result = await client.execute({ sql, args: values });\n return result.rows[0]?.[Object.keys(result.rows[0])[0]] ?? null;\n } catch (e) {\n throw new DatabaseError('Failed to execute pluck query', {\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Executes a SQL query using template literals and returns a single row\n *\n * @param strings - Template strings\n * @param vars - Variables to interpolate into the query\n * @returns Promise resolving to a single result record or null\n * @throws Error if the query fails\n */\n const single = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any> | null> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n const result = await client.execute({ sql, args: values });\n return result.rows[0] || null;\n } catch (e) {\n throw new DatabaseError('Failed to execute single query', {\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Executes a SQL query using template literals and returns multiple rows\n *\n * @param strings - Template strings\n * @param vars - Variables to interpolate into the query\n * @returns Promise resolving to array of result records\n * @throws Error if the query fails\n */\n const many = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any>[]> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n const result = await client.execute({ sql, args: values });\n return result.rows;\n } catch (e) {\n throw new DatabaseError('Failed to execute many query', {\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Executes a SQL query using template literals without returning results\n *\n * @param strings - Template strings\n * @param vars - Variables to interpolate into the query\n * @returns Promise that resolves when the query completes\n * @throws Error if the query fails\n */\n const execute = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<void> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n await client.execute({ sql, args: values });\n } catch (e) {\n throw new DatabaseError('Failed to execute query', {\n sql,\n values,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * Executes a raw SQL query with parameterized values\n *\n * @param str - SQL query string\n * @param values - Variables to use as parameters\n * @returns Promise resolving to query result with rows and metadata\n * @throws Error if the query fails\n */\n const query = async (str: string, ...values: any[]) => {\n const sql = str;\n const args = Array.isArray(values[0]) ? values[0] : values;\n try {\n const result = await client.execute({ sql, args });\n return {\n command: sql.split(' ')[0].toUpperCase(),\n rowCount: result.rowsAffected ?? result.rows.length,\n oid: null,\n fields: Object.keys(result.rows[0] || {}).map((name) => ({\n name,\n tableID: 0,\n columnID: 0,\n dataTypeID: 0,\n dataTypeSize: -1,\n dataTypeModifier: -1,\n format: 'text',\n })),\n rows: result.rows,\n };\n } catch (e) {\n throw new DatabaseError('Failed to execute raw query', {\n sql,\n args,\n originalError: formatDbError(e),\n });\n }\n };\n\n // Shorthand aliases for query methods\n const oo = many; // (o)bjective-(o)bjects: returns multiple rows\n const oO = single; // (o)bjective-(O)bject: returns a single row\n const ox = pluck; // (o)bjective-(x): returns a single value\n const xx = execute; // (x)ecute-(x)ecute: executes without returning\n\n /**\n * Initialize database schemas from JSON manifest\n * Supports dependency resolution and schema overrides\n *\n * @param options - Schema initialization options\n * @returns Promise that resolves when schemas are initialized\n */\n const initializeSchemas = async (\n options: SchemaInitializationOptions,\n ): Promise<void> => {\n const schemaManager = new DatabaseSchemaManager();\n const currentDb: DatabaseInterface = {\n url,\n client,\n query,\n insert,\n update,\n upsert,\n get,\n list,\n getOrInsert,\n delete: deleteRecords,\n count,\n table,\n tableExists,\n many,\n single,\n pluck,\n execute,\n oo,\n oO,\n ox,\n xx,\n syncSchema,\n transaction,\n };\n\n await schemaManager.initializeSchemas(currentDb, options);\n };\n\n /**\n * Retrieves the schema information for a table\n *\n * @param table - Table name\n * @returns Promise resolving to table schema info or null if table doesn't exist\n * @throws Error if the query fails\n */\n const getTableSchema = async (\n table: string,\n ): Promise<TableSchemaInfo | null> => {\n validateTableName(table);\n\n try {\n // Check if table exists\n const exists = await tableExists(table);\n if (!exists) {\n return null;\n }\n\n // Get column information from pragma_table_info\n const columnRows =\n await many`SELECT * FROM pragma_table_info(${table})`;\n\n const columns: Record<string, ColumnDefinition> = {};\n for (const row of columnRows) {\n const colName = row.name as string;\n columns[colName] = {\n type: row.type as string,\n primaryKey: row.pk === 1,\n notNull: row.notnull === 1,\n defaultValue: row.dflt_value,\n };\n }\n\n // Get index information from sqlite_master\n const indexRows = await many`\n SELECT name, sql FROM sqlite_master\n WHERE type = 'index'\n AND tbl_name = ${table}\n AND name NOT LIKE 'sqlite_%'\n `;\n\n const indexes: IndexDefinition[] = [];\n for (const row of indexRows) {\n const indexName = row.name as string;\n\n // Get index columns from pragma_index_info\n const indexInfoRows =\n await many`SELECT * FROM pragma_index_info(${indexName})`;\n\n const indexColumns: string[] = [];\n for (const infoRow of indexInfoRows) {\n indexColumns.push(infoRow.name as string);\n }\n\n // Check if index is unique from pragma_index_list\n const indexListRow =\n await single`SELECT * FROM pragma_index_list(${table}) WHERE name = ${indexName}`;\n\n indexes.push({\n name: indexName,\n columns: indexColumns,\n unique: indexListRow?.unique === 1,\n });\n }\n\n // Get foreign key information from pragma_foreign_key_list\n const fkRows =\n await many`SELECT * FROM pragma_foreign_key_list(${table})`;\n\n const foreignKeys: Array<{\n column: string;\n referencesTable: string;\n referencesColumn: string;\n onDelete?: string;\n onUpdate?: string;\n }> = [];\n\n for (const fkRow of fkRows) {\n foreignKeys.push({\n column: fkRow.from as string,\n referencesTable: fkRow.table as string,\n referencesColumn: fkRow.to as string,\n onDelete: fkRow.on_delete as string | undefined,\n onUpdate: fkRow.on_update as string | undefined,\n });\n }\n\n return {\n tableName: table,\n columns,\n indexes,\n foreignKeys,\n };\n } catch (e) {\n throw new DatabaseError('Failed to retrieve table schema', {\n table,\n originalError: formatDbError(e),\n });\n }\n };\n\n /**\n * ALTER TABLE operations for schema evolution\n */\n const alterTable = {\n /**\n * Adds a new column to an existing table\n *\n * @param table - Table name\n * @param column - Column definition with name\n * @returns Promise that resolves when column is added\n * @throws Error if the alter operation fails\n */\n addColumn: async (\n table: string,\n column: ColumnDefinitionWithName,\n ): Promise<void> => {\n validateTableName(table);\n validateColumnName(column.name);\n\n try {\n const sql = generateAddColumnStatement(table, column, 'sqlite');\n await client.execute({ sql, args: [] });\n } catch (e) {\n throw new DatabaseError('Failed to add column to table', {\n table,\n column: column.name,\n originalError: formatDbError(e),\n });\n }\n },\n\n /**\n * Adds a new index to an existing table\n *\n * @param table - Table name\n * @param index - Index definition\n * @returns Promise that resolves when index is created\n * @throws Error if the create index operation fails\n */\n addIndex: async (\n table: string,\n index: IndexDefinition,\n ): Promise<void> => {\n validateTableName(table);\n validateIndexName(index.name);\n\n for (const col of index.columns) {\n validateColumnName(col);\n }\n\n try {\n const sql = generateCreateIndexStatement(table, index);\n await client.execute({ sql, args: [] });\n } catch (e) {\n throw new DatabaseError('Failed to create index on table', {\n table,\n index: index.name,\n originalError: formatDbError(e),\n });\n }\n },\n };\n\n let closed = false;\n const db: DatabaseInterface = {\n url,\n client,\n query,\n insert,\n update,\n upsert,\n get,\n list,\n getOrInsert,\n delete: deleteRecords,\n count,\n table,\n tableExists,\n many,\n single,\n pluck,\n execute,\n oo,\n oO,\n ox,\n xx,\n syncSchema,\n initializeSchemas,\n transaction,\n beginTransaction,\n getTableSchema,\n alterTable,\n close: async () => {\n if (closed) return;\n client.close();\n closed = true;\n },\n };\n return db;\n })();\n}\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { t as ConnectionCache } from "./connection-cache-6EMfKWpo.js";
2
+ //#region src/shared/sqlite-connection-cache.ts
3
+ var sqliteConnectionCache = new ConnectionCache();
4
+ var sqliteCacheGroups = /* @__PURE__ */ new Map();
5
+ var cacheKey = (dbid, path) => `sqlite:${path}:${dbid}`;
6
+ async function closeSqliteDatabase(db) {
7
+ await db.close?.();
8
+ }
9
+ async function clearSqliteCacheGroup(dbid) {
10
+ const group = sqliteCacheGroups.get(dbid) ?? { hasFailure: false };
11
+ sqliteCacheGroups.set(dbid, group);
12
+ const barrier = (group.barrier?.catch(() => {}) ?? Promise.resolve()).then(async () => {
13
+ const failures = (await Promise.allSettled([sqliteConnectionCache.evict(cacheKey(dbid, "libsql"), closeSqliteDatabase), sqliteConnectionCache.evict(cacheKey(dbid, "native"), closeSqliteDatabase)])).filter((result) => result.status === "rejected");
14
+ if (failures.length) {
15
+ group.failure = failures.length === 1 ? failures[0].reason : new AggregateError(failures.map((failure) => failure.reason), "Multiple SQLite adapter paths failed to clear");
16
+ group.hasFailure = true;
17
+ throw group.failure;
18
+ }
19
+ group.failure = void 0;
20
+ group.hasFailure = false;
21
+ });
22
+ group.barrier = barrier;
23
+ try {
24
+ await barrier;
25
+ } finally {
26
+ if (group.barrier === barrier) {
27
+ group.barrier = void 0;
28
+ if (!group.hasFailure) sqliteCacheGroups.delete(dbid);
29
+ }
30
+ }
31
+ }
32
+ async function awaitSqliteCacheGroup(dbid) {
33
+ const group = sqliteCacheGroups.get(dbid);
34
+ if (!group) return;
35
+ if (group.barrier) await group.barrier;
36
+ if (group.hasFailure) throw group.failure;
37
+ }
38
+ async function getCachedSqliteDatabase(path, options, create) {
39
+ const { dbid } = options;
40
+ if (dbid && options.clearCache) await clearSqliteCacheGroup(dbid);
41
+ else if (dbid) await awaitSqliteCacheGroup(dbid);
42
+ return sqliteConnectionCache.getOrCreate(dbid ? cacheKey(dbid, path) : void 0, {
43
+ ...options,
44
+ clearCache: false
45
+ }, async () => {
46
+ const db = await create();
47
+ const originalClose = db.close?.bind(db);
48
+ let closePromise;
49
+ db.close = async () => {
50
+ sqliteConnectionCache.forget(db);
51
+ if (!closePromise) closePromise = originalClose?.() ?? Promise.resolve();
52
+ try {
53
+ await closePromise;
54
+ } catch (error) {
55
+ closePromise = void 0;
56
+ throw error;
57
+ }
58
+ };
59
+ return db;
60
+ }, closeSqliteDatabase);
61
+ }
62
+ //#endregion
63
+ export { getCachedSqliteDatabase as t };
64
+
65
+ //# sourceMappingURL=sqlite-connection-cache-0iFbrrI3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-connection-cache-0iFbrrI3.js","names":[],"sources":["../../src/shared/sqlite-connection-cache.ts"],"sourcesContent":["import { ConnectionCache } from './connection-cache';\nimport type { DatabaseInterface, DatabaseOptions } from './types';\n\nexport type SqliteAdapterPath = 'libsql' | 'native';\n\nconst sqliteConnectionCache = new ConnectionCache<DatabaseInterface>();\ntype SqliteCacheGroup = {\n barrier?: Promise<void>;\n failure?: unknown;\n hasFailure: boolean;\n};\nconst sqliteCacheGroups = new Map<string, SqliteCacheGroup>();\n\nconst cacheKey = (dbid: string, path: SqliteAdapterPath) =>\n `sqlite:${path}:${dbid}`;\n\nasync function closeSqliteDatabase(db: DatabaseInterface): Promise<void> {\n await db.close?.();\n}\n\nasync function clearSqliteCacheGroup(dbid: string): Promise<void> {\n const group = sqliteCacheGroups.get(dbid) ?? { hasFailure: false };\n sqliteCacheGroups.set(dbid, group);\n const previous = group.barrier;\n const barrier = (previous?.catch(() => {}) ?? Promise.resolve()).then(\n async () => {\n const results = await Promise.allSettled([\n sqliteConnectionCache.evict(\n cacheKey(dbid, 'libsql'),\n closeSqliteDatabase,\n ),\n sqliteConnectionCache.evict(\n cacheKey(dbid, 'native'),\n closeSqliteDatabase,\n ),\n ]);\n const failures = results.filter(\n (result): result is PromiseRejectedResult =>\n result.status === 'rejected',\n );\n if (failures.length) {\n group.failure =\n failures.length === 1\n ? failures[0].reason\n : new AggregateError(\n failures.map((failure) => failure.reason),\n 'Multiple SQLite adapter paths failed to clear',\n );\n group.hasFailure = true;\n throw group.failure;\n }\n group.failure = undefined;\n group.hasFailure = false;\n },\n );\n group.barrier = barrier;\n try {\n await barrier;\n } finally {\n if (group.barrier === barrier) {\n group.barrier = undefined;\n if (!group.hasFailure) sqliteCacheGroups.delete(dbid);\n }\n }\n}\n\nasync function awaitSqliteCacheGroup(dbid: string): Promise<void> {\n const group = sqliteCacheGroups.get(dbid);\n if (!group) return;\n if (group.barrier) await group.barrier;\n if (group.hasFailure) throw group.failure;\n}\n\nexport async function getCachedSqliteDatabase(\n path: SqliteAdapterPath,\n options: DatabaseOptions,\n create: () => Promise<DatabaseInterface>,\n): Promise<DatabaseInterface> {\n const { dbid } = options;\n if (dbid && options.clearCache) {\n await clearSqliteCacheGroup(dbid);\n } else if (dbid) {\n await awaitSqliteCacheGroup(dbid);\n }\n\n return sqliteConnectionCache.getOrCreate(\n dbid ? cacheKey(dbid, path) : undefined,\n { ...options, clearCache: false },\n async () => {\n const db = await create();\n const originalClose = db.close?.bind(db);\n let closePromise: Promise<void> | undefined;\n db.close = async () => {\n sqliteConnectionCache.forget(db);\n if (!closePromise) {\n closePromise = originalClose?.() ?? Promise.resolve();\n }\n try {\n await closePromise;\n } catch (error) {\n closePromise = undefined;\n throw error;\n }\n };\n return db;\n },\n closeSqliteDatabase,\n );\n}\n"],"mappings":";;AAKA,IAAM,wBAAwB,IAAI,gBAAmC;AAMrE,IAAM,oCAAoB,IAAI,IAA8B;AAE5D,IAAM,YAAY,MAAc,SAC9B,UAAU,KAAK,GAAG;AAEpB,eAAe,oBAAoB,IAAsC;CACvE,MAAM,GAAG,QAAQ;AACnB;AAEA,eAAe,sBAAsB,MAA6B;CAChE,MAAM,QAAQ,kBAAkB,IAAI,IAAI,KAAK,EAAE,YAAY,MAAM;CACjE,kBAAkB,IAAI,MAAM,KAAK;CAEjC,MAAM,WADW,MAAM,SACI,YAAY,CAAC,CAAC,KAAK,QAAQ,QAAQ,EAAA,CAAG,KAC/D,YAAY;EAWV,MAAM,YAAW,MAVK,QAAQ,WAAW,CACvC,sBAAsB,MACpB,SAAS,MAAM,QAAQ,GACvB,mBACF,GACA,sBAAsB,MACpB,SAAS,MAAM,QAAQ,GACvB,mBACF,CACF,CAAC,EAAA,CACwB,QACtB,WACC,OAAO,WAAW,UACtB;EACA,IAAI,SAAS,QAAQ;GACnB,MAAM,UACJ,SAAS,WAAW,IAChB,SAAS,EAAE,CAAC,SACZ,IAAI,eACF,SAAS,KAAK,YAAY,QAAQ,MAAM,GACxC,+CACF;GACN,MAAM,aAAa;GACnB,MAAM,MAAM;EACd;EACA,MAAM,UAAU,KAAA;EAChB,MAAM,aAAa;CACrB,CACF;CACA,MAAM,UAAU;CAChB,IAAI;EACF,MAAM;CACR,UAAU;EACR,IAAI,MAAM,YAAY,SAAS;GAC7B,MAAM,UAAU,KAAA;GAChB,IAAI,CAAC,MAAM,YAAY,kBAAkB,OAAO,IAAI;EACtD;CACF;AACF;AAEA,eAAe,sBAAsB,MAA6B;CAChE,MAAM,QAAQ,kBAAkB,IAAI,IAAI;CACxC,IAAI,CAAC,OAAO;CACZ,IAAI,MAAM,SAAS,MAAM,MAAM;CAC/B,IAAI,MAAM,YAAY,MAAM,MAAM;AACpC;AAEA,eAAsB,wBACpB,MACA,SACA,QAC4B;CAC5B,MAAM,EAAE,SAAS;CACjB,IAAI,QAAQ,QAAQ,YAClB,MAAM,sBAAsB,IAAI;MAC3B,IAAI,MACT,MAAM,sBAAsB,IAAI;CAGlC,OAAO,sBAAsB,YAC3B,OAAO,SAAS,MAAM,IAAI,IAAI,KAAA,GAC9B;EAAE,GAAG;EAAS,YAAY;CAAM,GAChC,YAAY;EACV,MAAM,KAAK,MAAM,OAAO;EACxB,MAAM,gBAAgB,GAAG,OAAO,KAAK,EAAE;EACvC,IAAI;EACJ,GAAG,QAAQ,YAAY;GACrB,sBAAsB,OAAO,EAAE;GAC/B,IAAI,CAAC,cACH,eAAe,gBAAgB,KAAK,QAAQ,QAAQ;GAEtD,IAAI;IACF,MAAM;GACR,SAAS,OAAO;IACd,eAAe,KAAA;IACf,MAAM;GACR;EACF;EACA,OAAO;CACT,GACA,mBACF;AACF"}
@@ -1,12 +1,43 @@
1
- import { a as validateColumnName, c as validateTableName, d as formatDbError, i as generateCreateIndexStatement, l as DatabaseSchemaManager, m as resolveInsertColumns, o as validateColumnNames, r as generateAddColumnStatement, s as validateIndexName, u as buildWhere } from "./alter-utils-DeDDAATm.js";
1
+ import { a as generateAddColumnStatement, c as validateColumnNames, d as DatabaseSchemaManager, f as buildWhere, g as resolveInsertColumns, l as validateIndexName, o as generateCreateIndexStatement, p as formatDbError, s as validateColumnName, u as validateTableName } from "./connection-cache-6EMfKWpo.js";
2
2
  import { n as resolveSchemas, r as createTransactionLock, t as NestedTransactionError } from "./types-uVFHgaSU.js";
3
+ import { t as getCachedSqliteDatabase } from "./sqlite-connection-cache-0iFbrrI3.js";
3
4
  import { DatabaseError } from "@happyvertical/utils";
4
5
  import { resolve } from "node:path";
5
6
  import { fileURLToPath } from "node:url";
6
7
  import { createRequire } from "node:module";
7
8
  //#region src/sqlite-native.ts
8
- var nativeMemoryConnectionCache = /* @__PURE__ */ new Map();
9
- var nativePendingConnections = /* @__PURE__ */ new Map();
9
+ /** @internal Best-effort, retryable closer used by native SQLite. */
10
+ function createNativeSqliteCloser(notificationCloseables, honkerDb, database) {
11
+ const completed = /* @__PURE__ */ new Set();
12
+ let active;
13
+ return async () => {
14
+ active ??= (async () => {
15
+ const resources = [
16
+ ...notificationCloseables,
17
+ ...honkerDb ? [honkerDb] : [],
18
+ database
19
+ ];
20
+ const errors = [];
21
+ for (const resource of resources) {
22
+ if (completed.has(resource)) continue;
23
+ try {
24
+ resource.close?.();
25
+ completed.add(resource);
26
+ notificationCloseables.delete(resource);
27
+ } catch (error) {
28
+ errors.push(error);
29
+ }
30
+ }
31
+ if (errors.length === 1) throw errors[0];
32
+ if (errors.length > 1) throw new AggregateError(errors, "Failed to close native SQLite resources");
33
+ })();
34
+ try {
35
+ await active;
36
+ } finally {
37
+ active = void 0;
38
+ }
39
+ };
40
+ }
10
41
  var nativeNullAwareUpsertLocks = /* @__PURE__ */ new Map();
11
42
  var nodeSqliteModulePromise = null;
12
43
  var requireOptionalPackage = createRequire(import.meta.url);
@@ -438,21 +469,7 @@ function createVectorCapabilities(database, options) {
438
469
  }
439
470
  async function getNativeSqliteDatabase(options = {}) {
440
471
  const url = options.url || ":memory:";
441
- if (options.dbid) {
442
- const cached = nativeMemoryConnectionCache.get(options.dbid);
443
- if (cached) return cached;
444
- const pending = nativePendingConnections.get(options.dbid);
445
- if (pending) return pending;
446
- }
447
- const connectionPromise = createNativeSqliteDatabase(options, url);
448
- if (options.dbid) nativePendingConnections.set(options.dbid, connectionPromise);
449
- try {
450
- const db = await connectionPromise;
451
- if (options.dbid) nativeMemoryConnectionCache.set(options.dbid, db);
452
- return db;
453
- } finally {
454
- if (options.dbid) nativePendingConnections.delete(options.dbid);
455
- }
472
+ return getCachedSqliteDatabase("native", options, () => createNativeSqliteDatabase(options, url));
456
473
  }
457
474
  async function createNativeSqliteDatabase(options, url) {
458
475
  const location = resolveNativeLocation(url);
@@ -467,11 +484,11 @@ async function createNativeSqliteDatabase(options, url) {
467
484
  const { DatabaseSync } = await loadNodeSqlite();
468
485
  const database = new DatabaseSync(location.location, { allowExtension: wantsVector });
469
486
  const connectionLock = createTransactionLock("sqlite (native)", options.transactionQueueTimeout);
487
+ let closeResources;
470
488
  try {
471
489
  if (wantsVector) await loadSqliteVectorExtension(database);
472
490
  const honkerDb = wantsNotifications && location.filePath ? await openHonkerDatabase(location.filePath, notificationsOptions) : null;
473
491
  const notificationCloseables = /* @__PURE__ */ new Set();
474
- let closed = false;
475
492
  const vectorCapabilities = wantsVector ? createVectorCapabilities(database, vectorOptions) : void 0;
476
493
  const notificationCapabilities = honkerDb ? createNotificationCapabilities(honkerDb, notificationCloseables) : void 0;
477
494
  const query = async (sql, ...values) => {
@@ -930,15 +947,8 @@ async function createNativeSqliteDatabase(options, url) {
930
947
  executeNativeQuery(database, sql);
931
948
  }
932
949
  };
933
- const close = async () => {
934
- if (closed) return;
935
- closed = true;
936
- for (const closeable of notificationCloseables) closeable.close?.();
937
- notificationCloseables.clear();
938
- honkerDb?.close?.();
939
- database.close();
940
- if (options.dbid) nativeMemoryConnectionCache.delete(options.dbid);
941
- };
950
+ const close = createNativeSqliteCloser(notificationCloseables, honkerDb, database);
951
+ closeResources = close;
942
952
  function buildDatabaseInterface(upsertImpl, transactionImpl = transaction, beginTransactionImpl = beginTransaction) {
943
953
  return {
944
954
  url,
@@ -979,12 +989,15 @@ async function createNativeSqliteDatabase(options, url) {
979
989
  return db;
980
990
  } catch (error) {
981
991
  try {
982
- database.close();
983
- } catch (_closeError) {}
992
+ if (closeResources) await closeResources();
993
+ else database.close();
994
+ } catch (cleanupError) {
995
+ throw new AggregateError([error, cleanupError], "Native SQLite initialization and cleanup failed");
996
+ }
984
997
  throw error;
985
998
  }
986
999
  }
987
1000
  //#endregion
988
1001
  export { getNativeSqliteDatabase };
989
1002
 
990
- //# sourceMappingURL=sqlite-native-DCAaADpP.js.map
1003
+ //# sourceMappingURL=sqlite-native-Dc8eFStu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-native-Dc8eFStu.js","names":[],"sources":["../../src/sqlite-native.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DatabaseError } from '@happyvertical/utils';\nimport { DatabaseSchemaManager } from './schema-manager.js';\nimport {\n generateAddColumnStatement,\n generateCreateIndexStatement,\n validateColumnName,\n validateColumnNames,\n validateIndexName,\n validateTableName,\n} from './shared/alter-utils.js';\nimport { getCachedSqliteDatabase } from './shared/sqlite-connection-cache.js';\nimport { createTransactionLock } from './shared/transaction-lock.js';\nimport type {\n ColumnDefinition,\n ColumnDefinitionWithName,\n DatabaseInterface,\n IndexDefinition,\n NotificationCapabilities,\n NotificationListenOptions,\n NotificationPruneOptions,\n NotificationWaitOptions,\n QueryResult,\n SchemaInitializationOptions,\n SchemaProvider,\n SqliteNotificationCapabilityOptions,\n SqliteVectorCapabilityOptions,\n TableInterface,\n TableSchemaInfo,\n TransactionHandle,\n UpsertOptions,\n VectorCapabilities,\n VectorIndexOptions,\n VectorSearchOptions,\n VectorSearchResult,\n WhereClause,\n} from './shared/types.js';\nimport { NestedTransactionError, resolveSchemas } from './shared/types.js';\nimport {\n buildWhere,\n formatDbError,\n resolveInsertColumns,\n} from './shared/utils.js';\nimport type { SqliteOptions } from './sqlite.js';\n\ntype NativeDatabase = any;\ntype NativeStatement = any;\ntype NativeCloseable = { close?: () => void };\n\n/** @internal Best-effort, retryable closer used by native SQLite. */\nexport function createNativeSqliteCloser(\n notificationCloseables: Set<NativeCloseable>,\n honkerDb: NativeCloseable | null,\n database: { close(): void },\n): () => Promise<void> {\n const completed = new Set<object>();\n let active: Promise<void> | undefined;\n return async () => {\n active ??= (async () => {\n const resources: NativeCloseable[] = [\n ...notificationCloseables,\n ...(honkerDb ? [honkerDb] : []),\n database,\n ];\n const errors: unknown[] = [];\n for (const resource of resources) {\n if (completed.has(resource)) continue;\n try {\n resource.close?.();\n completed.add(resource);\n notificationCloseables.delete(resource);\n } catch (error) {\n errors.push(error);\n }\n }\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n 'Failed to close native SQLite resources',\n );\n }\n })();\n try {\n await active;\n } finally {\n active = undefined;\n }\n };\n}\n\nconst nativeNullAwareUpsertLocks = new Map<string, Promise<void>>();\nlet nodeSqliteModulePromise: Promise<any> | null = null;\n// sqlite-vector's ESM wrapper uses dynamic require for platform packages.\nconst requireOptionalPackage = createRequire(import.meta.url);\n\ninterface NativeSqliteLocation {\n location: string;\n filePath: string | null;\n isMemory: boolean;\n}\n\ninterface NativeQueryResult {\n rows: Record<string, any>[];\n rowCount: number;\n command?: string;\n fields?: Array<{ name: string }>;\n}\n\nfunction getCapabilityOptions<T extends Record<string, any>>(\n value: boolean | T | undefined,\n): T {\n return typeof value === 'object' && value ? value : ({} as T);\n}\n\nfunction resolveNativeLocation(url = ':memory:'): NativeSqliteLocation {\n if (\n url.startsWith('libsql://') ||\n url.startsWith('http://') ||\n url.startsWith('https://')\n ) {\n throw new DatabaseError(\n 'SQLite native capabilities are only supported for local SQLite databases',\n {\n url,\n hint: \"Use a local file path, 'file:...' URL, or ':memory:' when enabling SQLite capabilities.\",\n },\n );\n }\n\n if (url === ':memory:' || url.startsWith('file::memory:')) {\n return { location: url, filePath: null, isMemory: true };\n }\n\n if (url.startsWith('file://')) {\n const filePath = fileURLToPath(new URL(url));\n return { location: filePath, filePath, isMemory: false };\n }\n\n if (url.startsWith('file:')) {\n const path = url.slice('file:'.length);\n if (!path || path.includes('?')) {\n throw new DatabaseError(\n 'SQLite native capabilities do not support file: URLs with query parameters',\n { url },\n );\n }\n const filePath = resolve(path);\n return { location: filePath, filePath, isMemory: false };\n }\n\n const filePath = resolve(url);\n return { location: filePath, filePath, isMemory: false };\n}\n\nasync function loadNodeSqlite(): Promise<any> {\n nodeSqliteModulePromise ??= importNodeSqlite().catch((error) => {\n nodeSqliteModulePromise = null;\n throw error;\n });\n return nodeSqliteModulePromise;\n}\n\nasync function importNodeSqlite(): Promise<any> {\n const originalEmitWarning = process.emitWarning;\n\n try {\n process.emitWarning = function suppressNodeSqliteExperimentalWarning(\n warning: string | Error,\n ...args: any[]\n ) {\n if (\n args[0] === 'ExperimentalWarning' &&\n String(warning).includes('SQLite is an experimental feature')\n ) {\n return;\n }\n\n return originalEmitWarning.call(process, warning as any, ...args);\n } as typeof process.emitWarning;\n\n const moduleName = 'node:sqlite';\n return (await import(/* @vite-ignore */ moduleName)) as any;\n } catch (error) {\n throw new DatabaseError('Node native SQLite is not available', {\n originalError: formatDbError(error),\n hint: 'SQLite native capabilities require a Node.js runtime with node:sqlite support.',\n });\n } finally {\n process.emitWarning = originalEmitWarning;\n }\n}\n\nasync function loadSqliteVectorExtension(\n database: NativeDatabase,\n): Promise<void> {\n try {\n const moduleName = '@sqliteai/sqlite-vector';\n const module = requireOptionalPackage(moduleName) as any;\n const getExtensionPath =\n module.getExtensionPath ?? module.default?.getExtensionPath;\n\n if (typeof getExtensionPath !== 'function') {\n throw new Error('getExtensionPath export was not found');\n }\n\n database.enableLoadExtension(true);\n database.loadExtension(getExtensionPath());\n } catch (error) {\n throw new DatabaseError(\n 'Failed to load optional SQLite vector capability: install @sqliteai/sqlite-vector',\n {\n packageName: '@sqliteai/sqlite-vector',\n originalError: formatDbError(error),\n hint: 'Install @sqliteai/sqlite-vector to use SQLite vector capabilities.',\n },\n );\n } finally {\n try {\n database.enableLoadExtension(false);\n } catch (_error) {\n // Leave the original extension-loading failure intact.\n }\n }\n}\n\nasync function openHonkerDatabase(\n filePath: string,\n options: SqliteNotificationCapabilityOptions,\n): Promise<any> {\n try {\n const moduleName = '@russellthehippo/honker-node';\n const module = requireOptionalPackage(moduleName) as any;\n const open = module.open ?? module.default?.open;\n\n if (typeof open !== 'function') {\n throw new Error('open export was not found');\n }\n\n return open(filePath, options.maxReaders, options.watcherBackend);\n } catch (error) {\n throw new DatabaseError(\n 'Failed to load optional SQLite notification capability: install @russellthehippo/honker-node',\n {\n packageName: '@russellthehippo/honker-node',\n originalError: formatDbError(error),\n hint: 'Install @russellthehippo/honker-node to use SQLite notification capabilities.',\n },\n );\n }\n}\n\nfunction toPlainRow(row: Record<string, any>): Record<string, any> {\n return Object.fromEntries(Object.entries(row));\n}\n\nfunction getErrorMessage(error: unknown): string | undefined {\n return error instanceof Error ? error.message : undefined;\n}\n\nfunction firstSqlToken(sql: string): string {\n return sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n}\n\nfunction normalizeNativeQuery(\n sql: string,\n values: any[],\n): { sql: string; args: any[] } {\n const flatArgs = Array.isArray(values[0]) ? values[0] : values;\n const parameterIndexes = Array.from(sql.matchAll(/\\$(\\d+)/g)).map((match) =>\n Number(match[1]),\n );\n\n if (parameterIndexes.length === 0) {\n return { sql, args: flatArgs };\n }\n\n const args = parameterIndexes.map((index) => flatArgs[index - 1]);\n return { sql: sql.replace(/\\$(\\d+)/g, '?'), args };\n}\n\nfunction executeNativeQuery(\n database: NativeDatabase,\n sql: string,\n args: any[] = [],\n): NativeQueryResult {\n const statement: NativeStatement = database.prepare(sql);\n const columns = statement.columns();\n\n if (columns.length > 0) {\n const rows = statement.all(...args).map(toPlainRow);\n return {\n rows,\n rowCount: rows.length,\n command: firstSqlToken(sql),\n fields: columns.map((column: { name: string }) => ({\n name: column.name,\n })),\n };\n }\n\n const result = statement.run(...args);\n return {\n rows: [],\n rowCount: Number(result?.changes ?? 0),\n command: firstSqlToken(sql),\n fields: [],\n };\n}\n\nfunction createNativeTablesFromSchemas(\n database: NativeDatabase,\n schemas: Record<string, SchemaProvider>,\n): void {\n for (const [tableName, schema] of Object.entries(schemas)) {\n try {\n const result = executeNativeQuery(\n database,\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [tableName],\n );\n\n if (result.rows.length > 0) {\n continue;\n }\n\n database.exec(schema.ddl);\n\n for (const indexSql of schema.indexes ?? []) {\n try {\n database.exec(indexSql);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create index for ${tableName}: ${formatDbError(error)}`,\n );\n }\n }\n\n for (const triggerSql of schema.triggers ?? []) {\n try {\n database.exec(triggerSql);\n } catch (error) {\n console.warn(\n `[sqlite] Failed to create trigger for ${tableName}: ${formatDbError(error)}`,\n );\n }\n }\n } catch (error) {\n throw new DatabaseError(\n `Failed to create table ${tableName} from schema`,\n {\n tableName,\n schema,\n originalError: formatDbError(error),\n },\n );\n }\n }\n}\n\nfunction quoteIdentifier(identifier: string): string {\n return `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction buildQuestionWhere(where: WhereClause, startIndex = 1) {\n const built = buildWhere(where, startIndex, 'sqlite');\n return {\n sql: built.sql.replace(/\\$(\\d+)/g, '?'),\n values: built.values,\n };\n}\n\nfunction serializeValue(value: any): any {\n if (value === null || value === undefined) {\n return value;\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === 'object') {\n return JSON.stringify(value);\n }\n return value;\n}\n\nfunction serializeRecord(record: Record<string, any>): Record<string, any> {\n const serialized: Record<string, any> = {};\n for (const [key, value] of Object.entries(record)) {\n if (value === undefined) {\n continue;\n }\n serialized[key] = serializeValue(value);\n }\n return serialized;\n}\n\nasync function withNativeNullAwareUpsertLock<T>(\n lockKey: string,\n callback: () => Promise<T>,\n): Promise<T> {\n const previous = nativeNullAwareUpsertLocks.get(lockKey) ?? Promise.resolve();\n let release: () => void = () => {};\n const current = new Promise<void>((resolve) => {\n release = resolve;\n });\n const next = previous.catch(() => {}).then(() => current);\n\n nativeNullAwareUpsertLocks.set(lockKey, next);\n await previous.catch(() => {});\n\n try {\n return await callback();\n } finally {\n release();\n if (nativeNullAwareUpsertLocks.get(lockKey) === next) {\n nativeNullAwareUpsertLocks.delete(lockKey);\n }\n }\n}\n\nfunction sqliteVectorDistance(metric: 'cosine' | 'l2' | 'ip'): string {\n switch (metric) {\n case 'l2':\n return 'L2';\n case 'ip':\n return 'DOT';\n default:\n return 'COSINE';\n }\n}\n\nfunction sqliteVectorQuantization(\n options: SqliteVectorCapabilityOptions,\n): string {\n const parts: string[] = [];\n\n switch (options.quantization ?? 'turbo4') {\n case 'turbo2':\n parts.push('qtype=TURBO,qbits=2');\n break;\n case 'turbo3':\n parts.push('qtype=TURBO,qbits=3');\n break;\n case 'turbo4':\n parts.push('qtype=TURBO,qbits=4');\n break;\n case 'uint8':\n parts.push('qtype=UINT8');\n break;\n case 'int8':\n parts.push('qtype=INT8');\n break;\n case '1bit':\n parts.push('qtype=1BIT');\n break;\n }\n\n if (options.maxMemory) {\n parts.push(`max_memory=${options.maxMemory}`);\n }\n\n return parts.join(',');\n}\n\nfunction createNotificationCapabilities(\n honkerDb: any,\n closeables: Set<{ close?: () => void }>,\n): NotificationCapabilities {\n return {\n async notify(channel: string, payload: any): Promise<number> {\n return Number(honkerDb.notify(channel, payload));\n },\n\n listen(channel: string, options?: NotificationListenOptions) {\n const listener = honkerDb.listen(channel, {\n fallbackPollS:\n options?.fallbackPollMs === null\n ? null\n : options?.fallbackPollMs !== undefined\n ? options.fallbackPollMs / 1000\n : undefined,\n });\n closeables.add(listener);\n return listener;\n },\n\n async waitForUpdate(options?: NotificationWaitOptions): Promise<boolean> {\n const updateEvents = honkerDb.updateEvents();\n closeables.add(updateEvents);\n let timeout: NodeJS.Timeout | undefined;\n let removeAbortListener: (() => void) | undefined;\n\n try {\n if (options?.signal?.aborted) {\n return false;\n }\n\n const updatePromise = updateEvents.next().then(\n () => true,\n () => false,\n );\n const races: Array<Promise<boolean>> = [updatePromise];\n\n if (options?.timeoutMs !== undefined) {\n const timeoutMs = options.timeoutMs;\n races.push(\n new Promise((resolve) => {\n timeout = setTimeout(\n () => resolve(false),\n Math.max(0, timeoutMs),\n );\n }),\n );\n }\n\n if (options?.signal) {\n races.push(\n new Promise((resolve) => {\n const onAbort = () => resolve(false);\n options.signal?.addEventListener('abort', onAbort, {\n once: true,\n });\n removeAbortListener = () =>\n options.signal?.removeEventListener('abort', onAbort);\n }),\n );\n }\n\n return await Promise.race(races);\n } finally {\n if (timeout) {\n clearTimeout(timeout);\n }\n removeAbortListener?.();\n closeables.delete(updateEvents);\n updateEvents.close?.();\n }\n },\n\n async prune(options?: NotificationPruneOptions): Promise<number> {\n return Number(\n honkerDb.pruneNotifications(options?.olderThanS, options?.maxKeep),\n );\n },\n };\n}\n\nfunction createVectorCapabilities(\n database: NativeDatabase,\n options: SqliteVectorCapabilityOptions,\n): VectorCapabilities {\n const initialized = new Map<\n string,\n { dimensions: number; metric: 'cosine' | 'l2' | 'ip' }\n >();\n const indexed = new Map<\n string,\n { metric: 'cosine' | 'l2' | 'ip'; options: SqliteVectorCapabilityOptions }\n >();\n\n const keyFor = (table: string, column: string) => `${table}.${column}`;\n\n const columnExists = (table: string, column: string): boolean => {\n const rows = database\n .prepare(`PRAGMA table_info(${quoteIdentifier(table)})`)\n .all()\n .map(toPlainRow);\n return rows.some((row: Record<string, any>) => row.name === column);\n };\n\n const ensureInitialized = (\n table: string,\n column: string,\n dimensions: number,\n metric: 'cosine' | 'l2' | 'ip' = 'cosine',\n ): void => {\n if (!Number.isInteger(dimensions) || dimensions <= 0) {\n throw new DatabaseError('Vector dimensions must be a positive integer', {\n dimensions,\n });\n }\n\n validateTableName(table);\n validateColumnName(column);\n\n executeNativeQuery(database, 'SELECT vector_init(?, ?, ?)', [\n table,\n column,\n `dimension=${dimensions},type=FLOAT32,distance=${sqliteVectorDistance(metric)}`,\n ]);\n initialized.set(keyFor(table, column), { dimensions, metric });\n };\n\n const quantize = (\n table: string,\n column: string,\n vectorOptions: SqliteVectorCapabilityOptions,\n ): void => {\n const quantizationOptions = sqliteVectorQuantization(vectorOptions);\n if (quantizationOptions) {\n executeNativeQuery(database, 'SELECT vector_quantize(?, ?, ?)', [\n table,\n column,\n quantizationOptions,\n ]);\n } else {\n executeNativeQuery(database, 'SELECT vector_quantize(?, ?)', [\n table,\n column,\n ]);\n }\n\n if (vectorOptions.preload) {\n executeNativeQuery(database, 'SELECT vector_quantize_preload(?, ?)', [\n table,\n column,\n ]);\n }\n };\n\n return {\n async ensureColumn(\n table: string,\n column: string,\n dimensions: number,\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n try {\n if (!columnExists(table, column)) {\n executeNativeQuery(\n database,\n `ALTER TABLE ${quoteIdentifier(table)} ADD COLUMN ${quoteIdentifier(column)} BLOB`,\n );\n }\n ensureInitialized(table, column, dimensions, 'cosine');\n } catch (error) {\n throw new DatabaseError('Failed to ensure SQLite vector column', {\n table,\n column,\n dimensions,\n originalError: formatDbError(error),\n });\n }\n },\n\n async ensureIndex(\n table: string,\n column: string,\n indexOptions?: VectorIndexOptions,\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const dimensions =\n indexOptions?.dimensions ?? initialized.get(key)?.dimensions;\n if (!dimensions) {\n throw new DatabaseError(\n 'SQLite vector index creation requires dimensions',\n {\n table,\n column,\n hint: 'Call ensureColumn(table, column, dimensions) first or pass dimensions to ensureIndex().',\n },\n );\n }\n\n const metric = indexOptions?.metric ?? 'cosine';\n\n try {\n ensureInitialized(table, column, dimensions, metric);\n const vectorOptions = { ...options };\n quantize(table, column, vectorOptions);\n indexed.set(key, { metric, options: vectorOptions });\n } catch (error) {\n throw new DatabaseError('Failed to ensure SQLite vector index', {\n table,\n column,\n originalError: formatDbError(error),\n });\n }\n },\n\n async upsertVector(\n table: string,\n where: Record<string, any>,\n column: string,\n embedding: number[],\n ): Promise<void> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const dimensions = initialized.get(key)?.dimensions ?? embedding.length;\n const { sql: whereClause, values } = buildQuestionWhere(where);\n\n if (!whereClause) {\n throw new DatabaseError(\n 'SQLite vector update requires at least one WHERE condition',\n { table, column },\n );\n }\n\n try {\n ensureInitialized(\n table,\n column,\n dimensions,\n initialized.get(key)?.metric ?? 'cosine',\n );\n executeNativeQuery(\n database,\n `UPDATE ${quoteIdentifier(table)} SET ${quoteIdentifier(column)} = vector_as_f32(?) ${whereClause}`,\n [JSON.stringify(embedding), ...values],\n );\n\n const indexState = indexed.get(key);\n if (indexState) {\n quantize(table, column, indexState.options);\n }\n } catch (error) {\n throw new DatabaseError('Failed to upsert SQLite vector', {\n table,\n column,\n where,\n originalError: formatDbError(error),\n });\n }\n },\n\n async search(\n table: string,\n column: string,\n embedding: number[],\n searchOptions?: VectorSearchOptions,\n ): Promise<VectorSearchResult[]> {\n validateTableName(table);\n validateColumnName(column);\n\n const key = keyFor(table, column);\n const metric = searchOptions?.metric ?? 'cosine';\n const limit = searchOptions?.limit ?? 10;\n const limitArg = BigInt(limit);\n const dimensions = initialized.get(key)?.dimensions ?? embedding.length;\n const indexState = indexed.get(key);\n const useQuantizedScan = indexState?.metric === metric;\n const scanFunction = useQuantizedScan\n ? 'vector_quantize_scan'\n : 'vector_full_scan';\n const convertedWhere = searchOptions?.where\n ? searchOptions.where.replace(/\\$(\\d+)/g, '?')\n : '';\n const whereParams = searchOptions?.params ?? [];\n\n try {\n ensureInitialized(table, column, dimensions, metric);\n\n const rows = convertedWhere\n ? executeNativeQuery(\n database,\n `SELECT base.*, base.rowid AS __sqlite_rowid, vec.distance\n FROM ${scanFunction}(?, ?, vector_as_f32(?)) AS vec\n JOIN ${quoteIdentifier(table)} AS base ON base.rowid = vec.rowid\n WHERE (${convertedWhere})\n ORDER BY vec.distance\n LIMIT ?`,\n [\n table,\n column,\n JSON.stringify(embedding),\n ...whereParams,\n limitArg,\n ],\n ).rows\n : executeNativeQuery(\n database,\n `SELECT base.*, base.rowid AS __sqlite_rowid, vec.distance\n FROM ${scanFunction}(?, ?, vector_as_f32(?), ?) AS vec\n JOIN ${quoteIdentifier(table)} AS base ON base.rowid = vec.rowid\n ORDER BY vec.distance`,\n [table, column, JSON.stringify(embedding), limitArg],\n ).rows;\n\n return rows.map((row) => {\n const { __sqlite_rowid: rowid, ...result } = row;\n return {\n ...result,\n id: String(result.id ?? rowid),\n distance: Number(row.distance),\n };\n });\n } catch (error) {\n throw new DatabaseError('Failed to execute SQLite vector search', {\n table,\n column,\n metric,\n limit,\n originalError: formatDbError(error),\n });\n }\n },\n };\n}\n\nexport async function getNativeSqliteDatabase(\n options: SqliteOptions = {},\n): Promise<DatabaseInterface> {\n const url = options.url || ':memory:';\n\n return getCachedSqliteDatabase('native', options, () =>\n createNativeSqliteDatabase(options, url),\n );\n}\n\nasync function createNativeSqliteDatabase(\n options: SqliteOptions,\n url: string,\n): Promise<DatabaseInterface> {\n const location = resolveNativeLocation(url);\n const notificationsOptions = getCapabilityOptions(\n options.capabilities?.notifications,\n );\n const vectorOptions = getCapabilityOptions(options.capabilities?.vector);\n const wantsNotifications = Boolean(options.capabilities?.notifications);\n const wantsVector = Boolean(options.capabilities?.vector);\n\n if (wantsNotifications && location.isMemory) {\n throw new DatabaseError(\n 'SQLite notification capabilities require a file-backed database',\n {\n url,\n hint: 'Use a temporary file-backed .db for development and tests that need notifications.',\n },\n );\n }\n\n const { DatabaseSync } = await loadNodeSqlite();\n const database = new DatabaseSync(location.location, {\n allowExtension: wantsVector,\n });\n\n // One lock per connection. `getNativeSqliteDatabase` hands cached callers\n // this same closure, so \"created here\" and \"per connection\" are the same\n // scope.\n const connectionLock = createTransactionLock(\n 'sqlite (native)',\n options.transactionQueueTimeout,\n );\n let closeResources: (() => Promise<void>) | undefined;\n\n try {\n if (wantsVector) {\n await loadSqliteVectorExtension(database);\n }\n\n const honkerDb =\n wantsNotifications && location.filePath\n ? await openHonkerDatabase(location.filePath, notificationsOptions)\n : null;\n const notificationCloseables = new Set<{ close?: () => void }>();\n\n const vectorCapabilities = wantsVector\n ? createVectorCapabilities(database, vectorOptions)\n : undefined;\n const notificationCapabilities = honkerDb\n ? createNotificationCapabilities(honkerDb, notificationCloseables)\n : undefined;\n\n const query = async (\n sql: string,\n ...values: any[]\n ): Promise<{ rows: Record<string, any>[]; rowCount: number }> => {\n const normalized = normalizeNativeQuery(sql, values);\n try {\n const result = executeNativeQuery(\n database,\n normalized.sql,\n normalized.args,\n );\n return { rows: result.rows, rowCount: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to execute raw query', {\n sql: normalized.sql,\n values: normalized.args,\n originalError: formatDbError(error),\n });\n }\n };\n\n const parseTemplate = (strings: TemplateStringsArray, ...vars: any[]) => {\n let sql = strings[0];\n const values = [];\n for (let i = 0; i < vars.length; i++) {\n values.push(vars[i]);\n sql += `?${strings[i + 1]}`;\n }\n return { sql, values };\n };\n\n const insert = async (\n table: string,\n data: Record<string, any> | Record<string, any>[],\n ): Promise<QueryResult> => {\n validateTableName(table);\n const records = (Array.isArray(data) ? data : [data]).map(\n serializeRecord,\n );\n if (records.length === 0) {\n return { operation: 'insert', affected: 0 };\n }\n const keys = resolveInsertColumns(table, records);\n validateColumnNames(keys);\n const placeholders = records\n .map(() => `(${keys.map(() => '?').join(', ')})`)\n .join(', ');\n const values = records.flatMap((record) =>\n keys.map((key) => record[key]),\n );\n const sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES ${placeholders}`;\n\n try {\n const result = executeNativeQuery(database, sql, values);\n return { operation: 'insert', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to insert records into table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const get = async (\n table: string,\n where: WhereClause,\n ): Promise<Record<string, any> | null> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n if (!whereClause) {\n throw new DatabaseError(\n 'GET requires at least one WHERE condition to prevent returning an arbitrary record',\n { table },\n );\n }\n\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n const result = executeNativeQuery(database, sql, values);\n return result.rows[0] || null;\n } catch (error) {\n throw new DatabaseError('Failed to retrieve record from table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const list = async (\n table: string,\n where: WhereClause,\n ): Promise<Record<string, any>[]> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n const sql = `SELECT * FROM ${table} ${whereClause}`;\n try {\n return executeNativeQuery(database, sql, values).rows;\n } catch (error) {\n throw new DatabaseError('Failed to list records from table', {\n table,\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const update = async (\n table: string,\n where: WhereClause,\n data: Record<string, any>,\n ): Promise<QueryResult> => {\n validateTableName(table);\n const serializedData = serializeRecord(data);\n const keys = Object.keys(serializedData);\n validateColumnNames(keys);\n const values = Object.values(serializedData);\n const setClause = keys.map((key) => `${key} = ?`).join(', ');\n const { sql: whereClause, values: whereValues } =\n buildQuestionWhere(where);\n\n if (!whereClause) {\n throw new DatabaseError(\n 'UPDATE requires at least one WHERE condition to prevent accidental update of all records',\n { table },\n );\n }\n\n const sql = `UPDATE ${table} SET ${setClause} ${whereClause}`;\n try {\n const result = executeNativeQuery(database, sql, [\n ...values,\n ...whereValues,\n ]);\n return { operation: 'update', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to update records in table', {\n table,\n sql,\n values: [...values, ...whereValues],\n originalError: formatDbError(error),\n });\n }\n };\n\n const hasNullConflictValue = (\n conflictColumns: string[],\n data: Record<string, any>,\n ): boolean => conflictColumns.some((column) => data[column] === null);\n\n const validateUpsertConflictColumns = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): void => {\n const missingColumns = conflictColumns.filter(\n (column) => !(column in serializedData),\n );\n\n if (missingColumns.length > 0) {\n throw new DatabaseError('Conflict columns missing from data', {\n table,\n conflictColumns,\n missingColumns,\n availableColumns: Object.keys(serializedData),\n hint: 'All columns specified in ON CONFLICT must be present in the data being inserted. Undefined values are filtered out during serialization - consider using null or an empty string instead.',\n });\n }\n };\n\n const executeStandardUpsert = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): QueryResult => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const placeholders = keys.map(() => '?').join(', ');\n const updateSet = keys\n .map((key) => `${key} = excluded.${key}`)\n .join(', ');\n const conflict = conflictColumns.join(', ');\n const sql = `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders}) ON CONFLICT(${conflict}) DO UPDATE SET ${updateSet}`;\n const result = executeNativeQuery(database, sql, values);\n return { operation: 'upsert', affected: result.rowCount };\n };\n\n const executeNullAwareUpsertAttempt = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): QueryResult => {\n const keys = Object.keys(serializedData);\n const values = Object.values(serializedData);\n const updateSet = keys.map((key) => `${key} = ?`).join(', ');\n const whereClause = conflictColumns\n .map((column) => `${column} IS ?`)\n .join(' AND ');\n const whereValues = conflictColumns.map(\n (column) => serializedData[column],\n );\n\n const updateResult = executeNativeQuery(\n database,\n `UPDATE ${table} SET ${updateSet} WHERE ${whereClause}`,\n [...values, ...whereValues],\n );\n\n if (updateResult.rowCount > 0) {\n return { operation: 'upsert', affected: updateResult.rowCount };\n }\n\n const placeholders = keys.map(() => '?').join(', ');\n const insertResult = executeNativeQuery(\n database,\n `INSERT INTO ${table} (${keys.join(', ')}) VALUES (${placeholders})`,\n values,\n );\n\n return { operation: 'upsert', affected: insertResult.rowCount };\n };\n\n /**\n * Rejects hostile table/column identifiers before an upsert builds SQL.\n *\n * Called from the public entry points rather than from `executeUpsert`, so\n * the failure reaches the caller naming the bad identifier instead of being\n * flattened into the wrapper's generic \"Failed to upsert record into table\".\n *\n * Validates the keys of the already-serialized snapshot — the exact array\n * the executors interpolate — not a fresh enumeration of the caller's\n * `data`. Enumerating `data` again could disagree with the snapshot if it is\n * a Proxy whose `ownKeys` trap returns different keys on successive reads.\n */\n const validateUpsertIdentifiers = (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n ): void => {\n validateTableName(table);\n validateColumnNames([...conflictColumns, ...Object.keys(serializedData)]);\n };\n\n const executeUpsert = async (\n table: string,\n conflictColumns: string[],\n serializedData: Record<string, any>,\n upsertOptions: UpsertOptions | undefined,\n acquireTransaction: boolean,\n ): Promise<QueryResult> => {\n validateUpsertConflictColumns(table, conflictColumns, serializedData);\n\n if (\n upsertOptions?.nullsDistinct ||\n !hasNullConflictValue(conflictColumns, serializedData)\n ) {\n return executeStandardUpsert(table, conflictColumns, serializedData);\n }\n\n if (!acquireTransaction) {\n return executeNullAwareUpsertAttempt(\n table,\n conflictColumns,\n serializedData,\n );\n }\n\n const lockKey = `${location.location}:${table}:${conflictColumns\n .map((column) => `${column}:${JSON.stringify(serializedData[column])}`)\n .join('|')}`;\n\n return withNativeNullAwareUpsertLock(lockKey, async () => {\n // This opens a transaction on the shared connection like any other, so\n // it takes the same lock — otherwise a null-aware upsert overlapping a\n // `transaction()` call reintroduces exactly the corruption the lock\n // exists to prevent.\n const releaseConnection = await connectionLock.acquire();\n try {\n database.exec('BEGIN IMMEDIATE');\n const result = executeNullAwareUpsertAttempt(\n table,\n conflictColumns,\n serializedData,\n );\n database.exec('COMMIT');\n return result;\n } catch (error) {\n try {\n database.exec('ROLLBACK');\n } catch (_rollbackError) {\n // Preserve the original failure.\n }\n throw error;\n } finally {\n releaseConnection();\n }\n });\n };\n\n const upsert = async (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n upsertOptions?: UpsertOptions,\n ): Promise<QueryResult> => {\n // Snapshot the record and the conflict columns once and thread both\n // through validation and SQL, so neither a hostile record nor a hostile\n // conflict-column array can present different identifiers to each.\n const serializedData = serializeRecord(data);\n const conflictCols = [...conflictColumns];\n validateUpsertIdentifiers(table, conflictCols, serializedData);\n try {\n return await executeUpsert(\n table,\n conflictCols,\n serializedData,\n upsertOptions,\n true,\n );\n } catch (error) {\n if (getErrorMessage(error) === 'Conflict columns missing from data') {\n throw error;\n }\n throw new DatabaseError('Failed to upsert record into table', {\n table,\n values: data,\n conflictColumns,\n originalError: formatDbError(error),\n });\n }\n };\n\n const upsertInCurrentTransaction = (\n table: string,\n conflictColumns: string[],\n data: Record<string, any>,\n upsertOptions?: UpsertOptions,\n ): Promise<QueryResult> => {\n // Snapshot the record and the conflict columns once and thread both\n // through validation and SQL, so neither a hostile record nor a hostile\n // conflict-column array can present different identifiers to each.\n const serializedData = serializeRecord(data);\n const conflictCols = [...conflictColumns];\n validateUpsertIdentifiers(table, conflictCols, serializedData);\n return executeUpsert(\n table,\n conflictCols,\n serializedData,\n upsertOptions,\n false,\n );\n };\n\n const getOrInsert = async (\n table: string,\n where: WhereClause,\n data: Record<string, any>,\n ): Promise<Record<string, any>> => {\n const existing = await get(table, where);\n if (existing) return existing;\n await insert(table, data);\n const inserted = await get(table, where);\n if (!inserted) {\n throw new DatabaseError('Failed to insert and retrieve record', {\n table,\n where,\n data,\n });\n }\n return inserted;\n };\n\n const deleteRecords = async (\n table: string,\n where: WhereClause,\n ): Promise<QueryResult> => {\n validateTableName(table);\n const { sql: whereClause, values } = buildQuestionWhere(where);\n if (!whereClause) {\n throw new DatabaseError(\n 'DELETE requires at least one WHERE condition to prevent accidental deletion of all records',\n { table },\n );\n }\n\n try {\n const result = executeNativeQuery(\n database,\n `DELETE FROM ${table} ${whereClause}`,\n values,\n );\n return { operation: 'delete', affected: result.rowCount };\n } catch (error) {\n throw new DatabaseError('Failed to delete records from table', {\n table,\n where,\n originalError: formatDbError(error),\n });\n }\n };\n\n const count = async (\n table: string,\n where?: WhereClause,\n ): Promise<number> => {\n validateTableName(table);\n if (\n !where ||\n (typeof where === 'object' && Object.keys(where).length === 0)\n ) {\n const result = executeNativeQuery(\n database,\n `SELECT COUNT(*) as count FROM ${table}`,\n );\n return Number(result.rows[0]?.count) || 0;\n }\n\n const { sql: whereClause, values } = buildQuestionWhere(where);\n const result = executeNativeQuery(\n database,\n `SELECT COUNT(*) as count FROM ${table} ${whereClause}`,\n values,\n );\n return Number(result.rows[0]?.count) || 0;\n };\n\n const tableExists = async (tableName: string): Promise<boolean> => {\n const result = executeNativeQuery(\n database,\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [tableName],\n );\n return result.rows.length > 0;\n };\n\n const syncSchema = async (schema: string): Promise<void> => {\n const commands = schema\n .replace(/\\s+/g, ' ')\n .replace(/DEFAULT CAST\\(([^)]+)\\s+AS\\s+\\w+\\)/gi, 'DEFAULT $1')\n .trim()\n .split(';')\n .map((command) => command.trim())\n .filter((command) => command.length > 0);\n\n for (const command of commands) {\n database.exec(command);\n }\n };\n\n const many = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any>[]> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n return executeNativeQuery(database, sql, values).rows;\n } catch (error) {\n throw new DatabaseError('Failed to execute many query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const single = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<Record<string, any> | null> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n return executeNativeQuery(database, sql, values).rows[0] || null;\n } catch (error) {\n throw new DatabaseError('Failed to execute single query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const pluck = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<any> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n const row = executeNativeQuery(database, sql, values).rows[0];\n return row ? Object.values(row)[0] : null;\n } catch (error) {\n throw new DatabaseError('Failed to execute pluck query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const execute = async (\n strings: TemplateStringsArray,\n ...vars: any[]\n ): Promise<void> => {\n const { sql, values } = parseTemplate(strings, ...vars);\n try {\n if (values.length === 0) {\n database.exec(sql);\n return;\n }\n executeNativeQuery(database, sql, values);\n } catch (error) {\n throw new DatabaseError('Failed to execute query', {\n sql,\n values,\n originalError: formatDbError(error),\n });\n }\n };\n\n const oo = many;\n const oO = single;\n const ox = pluck;\n const xx = execute;\n\n const table = (tableName: string): TableInterface => ({\n insert: (data) => insert(tableName, data),\n get: (where) => get(tableName, where),\n list: (where) => list(tableName, where),\n });\n\n // Names nested savepoints. Only ever appended to an identifier the adapter\n // generates, never caller input.\n let savepointSequence = 0;\n\n /**\n * Re-enters the transaction already in progress under a savepoint.\n *\n * This is what the transaction-scoped interface exposes as `transaction`.\n * Handing it the top-level `transaction` instead — the previous behaviour —\n * sent a nested call into a second BEGIN on the one shared connection; that\n * throws, and the nested call's own ROLLBACK then discarded the *enclosing*\n * transaction's work while later writes committed in autocommit.\n */\n const nestedTransaction = async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> => {\n savepointSequence += 1;\n const name = `hv_sp_${savepointSequence}`;\n database.exec(`SAVEPOINT ${name}`);\n try {\n const result = await callback(\n buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n ),\n );\n database.exec(`RELEASE SAVEPOINT ${name}`);\n return result;\n } catch (error) {\n try {\n // ROLLBACK TO leaves the savepoint defined, so release it too or it\n // accumulates for the life of the transaction.\n database.exec(`ROLLBACK TO SAVEPOINT ${name}`);\n database.exec(`RELEASE SAVEPOINT ${name}`);\n } catch {\n // The enclosing transaction is already unwinding; its teardown owns\n // the connection from here.\n }\n throw error;\n }\n };\n\n const transaction = async <T>(\n callback: (tx: DatabaseInterface) => Promise<T>,\n ): Promise<T> =>\n // Held across the whole BEGIN … COMMIT/ROLLBACK span. Without it two\n // overlapping calls raced on the one connection: the second BEGIN threw,\n // its catch ran ROLLBACK, and that rollback ended the *first*\n // transaction — half of its writes durable, half lost, and its promise\n // rejected, so the caller was told nothing had happened.\n connectionLock.run(async () => {\n try {\n database.exec('BEGIN TRANSACTION');\n const txDb = buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n );\n const result = await callback(txDb);\n database.exec('COMMIT');\n return result;\n } catch (error) {\n // A failing ROLLBACK must not replace the caller's error: SQLite\n // reports \"cannot rollback - no transaction is active\" whenever the\n // transaction is already gone, which says nothing about what failed.\n try {\n database.exec('ROLLBACK');\n } catch {\n // Nothing left to roll back.\n }\n throw error;\n }\n });\n\n /**\n * The `beginTransaction` exposed on a transaction-scoped interface.\n *\n * Unlike `transaction`, a manual handle has no savepoint equivalent: it\n * would have to own the connection until the caller ends it, which the\n * enclosing transaction already does. Issuing a second BEGIN threw before\n * transactions were serialized; now it would instead wait on a lock its own\n * caller holds, so refuse explicitly rather than deadlock.\n */\n const refuseNestedBeginTransaction = async (): Promise<never> => {\n throw new NestedTransactionError(\n 'beginTransaction() cannot be called inside a transaction. Use the transaction you already have, or nest with transaction() to re-enter it under a savepoint.',\n { adapter: 'sqlite-native' },\n );\n };\n\n const beginTransaction = async (): Promise<TransactionHandle> => {\n // The handle owns the connection until the caller ends it, so the lock is\n // held across the gap rather than around a single call. A handle that is\n // never committed or rolled back therefore blocks every later transaction\n // until the queue timeout reports it.\n const releaseConnection = await connectionLock.acquire();\n try {\n database.exec('BEGIN TRANSACTION');\n } catch (error) {\n // No transaction was opened, so there is no handle to end it and\n // nothing left to release the connection.\n releaseConnection();\n throw error;\n }\n\n let active = true;\n\n // COMMIT and ROLLBACK can both throw, and the transaction is over either\n // way, so the connection goes back before the error is rethrown.\n const end = (command: 'COMMIT' | 'ROLLBACK'): void => {\n if (!active) {\n throw new DatabaseError('Transaction already ended', {});\n }\n try {\n database.exec(command);\n } catch (error) {\n // COMMIT can fail and leave the transaction *open* — SQLite documents\n // exactly that for SQLITE_BUSY. Releasing the connection then would hand\n // the next queued caller a connection still inside a transaction: its\n // BEGIN would throw, and its catch would ROLLBACK, discarding this\n // transaction's work. So normalize before releasing, the way the pooled\n // adapter's discardTxClient does.\n try {\n database.exec('ROLLBACK');\n } catch {\n // Already gone; nothing left to normalize.\n }\n throw error;\n } finally {\n active = false;\n releaseConnection();\n }\n };\n\n const commit = async (): Promise<void> => end('COMMIT');\n\n const rollback = async (): Promise<void> => end('ROLLBACK');\n\n return {\n // The handle is inside a transaction for the same reason a callback\n // scope is, so it re-enters via savepoint too.\n ...buildDatabaseInterface(\n upsertInCurrentTransaction,\n nestedTransaction,\n refuseNestedBeginTransaction,\n ),\n commit,\n rollback,\n isActive: () => active,\n };\n };\n\n const initializeSchemas = async (\n schemaOptions: SchemaInitializationOptions,\n ): Promise<void> => {\n const schemaManager = new DatabaseSchemaManager();\n await schemaManager.initializeSchemas(\n buildDatabaseInterface(upsert),\n schemaOptions,\n );\n };\n\n const getTableSchema = async (\n tableName: string,\n ): Promise<TableSchemaInfo | null> => {\n validateTableName(tableName);\n\n if (!(await tableExists(tableName))) {\n return null;\n }\n\n const columnRows = database\n .prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n const columns: Record<string, ColumnDefinition> = {};\n for (const row of columnRows) {\n columns[row.name as string] = {\n type: row.type as string,\n primaryKey: row.pk === 1,\n notNull: row.notnull === 1,\n defaultValue: row.dflt_value,\n };\n }\n\n const indexRows = executeNativeQuery(\n database,\n `SELECT name FROM sqlite_master\n WHERE type = 'index'\n AND tbl_name = ?\n AND name NOT LIKE 'sqlite_%'`,\n [tableName],\n ).rows;\n\n const indexes: IndexDefinition[] = [];\n for (const row of indexRows) {\n const indexName = row.name as string;\n const indexInfoRows = database\n .prepare(`PRAGMA index_info(${quoteIdentifier(indexName)})`)\n .all()\n .map(toPlainRow);\n const indexListRows = database\n .prepare(`PRAGMA index_list(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n indexes.push({\n name: indexName,\n columns: indexInfoRows.map((info: Record<string, any>) => info.name),\n unique: indexListRows.some(\n (info: Record<string, any>) =>\n info.name === indexName && info.unique === 1,\n ),\n });\n }\n\n const fkRows = database\n .prepare(`PRAGMA foreign_key_list(${quoteIdentifier(tableName)})`)\n .all()\n .map(toPlainRow);\n\n return {\n tableName,\n columns,\n indexes,\n foreignKeys: fkRows.map((row: Record<string, any>) => ({\n column: row.from as string,\n referencesTable: row.table as string,\n referencesColumn: row.to as string,\n onDelete: row.on_delete as string | undefined,\n onUpdate: row.on_update as string | undefined,\n })),\n };\n };\n\n const alterTable = {\n addColumn: async (\n tableName: string,\n column: ColumnDefinitionWithName,\n ): Promise<void> => {\n validateTableName(tableName);\n validateColumnName(column.name);\n const sql = generateAddColumnStatement(tableName, column, 'sqlite');\n executeNativeQuery(database, sql);\n },\n\n addIndex: async (\n tableName: string,\n index: IndexDefinition,\n ): Promise<void> => {\n validateTableName(tableName);\n validateIndexName(index.name);\n for (const column of index.columns) {\n validateColumnName(column);\n }\n const sql = generateCreateIndexStatement(tableName, index);\n executeNativeQuery(database, sql);\n },\n };\n\n const close = createNativeSqliteCloser(\n notificationCloseables,\n honkerDb,\n database,\n );\n closeResources = close;\n\n function buildDatabaseInterface(\n upsertImpl: DatabaseInterface['upsert'],\n transactionImpl: DatabaseInterface['transaction'] = transaction,\n beginTransactionImpl: DatabaseInterface['beginTransaction'] = beginTransaction,\n ): DatabaseInterface {\n return {\n url,\n client: database,\n query,\n insert,\n update,\n upsert: upsertImpl,\n get,\n list,\n getOrInsert,\n delete: deleteRecords,\n count,\n table,\n tableExists,\n many,\n single,\n pluck,\n execute,\n oo,\n oO,\n ox,\n xx,\n syncSchema,\n initializeSchemas,\n transaction: transactionImpl,\n beginTransaction: beginTransactionImpl,\n getTableSchema,\n alterTable,\n close,\n ...(vectorCapabilities ? { vector: vectorCapabilities } : {}),\n ...(notificationCapabilities\n ? { notifications: notificationCapabilities }\n : {}),\n };\n }\n\n const db = buildDatabaseInterface(upsert);\n const resolvedSchemas = resolveSchemas(options.schemas);\n if (resolvedSchemas && Object.keys(resolvedSchemas).length > 0) {\n createNativeTablesFromSchemas(database, resolvedSchemas);\n }\n\n return db;\n } catch (error) {\n try {\n if (closeResources) await closeResources();\n else database.close();\n } catch (cleanupError) {\n throw new AggregateError(\n [error, cleanupError],\n 'Native SQLite initialization and cleanup failed',\n );\n }\n throw error;\n }\n}\n"],"mappings":"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"}