@korajs/store 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,7 +69,18 @@ var BetterSqlite3Adapter = class {
69
69
  this.db.pragma("foreign_keys = ON");
70
70
  const statements = (0, import_core2.generateFullDDL)(schema);
71
71
  for (const sql of statements) {
72
- this.db.exec(sql);
72
+ if (sql.startsWith("--kora:safe-alter")) {
73
+ try {
74
+ this.db.exec(sql.replace("--kora:safe-alter\n", ""));
75
+ } catch (e) {
76
+ const msg = e.message || "";
77
+ if (!msg.includes("duplicate column name")) {
78
+ throw e;
79
+ }
80
+ }
81
+ } else {
82
+ this.db.exec(sql);
83
+ }
73
84
  }
74
85
  }
75
86
  async close() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/better-sqlite3.ts","../../src/adapters/better-sqlite3-adapter.ts","../../src/errors.ts"],"sourcesContent":["// Entry point for @korajs/store/better-sqlite3\nexport { BetterSqlite3Adapter } from './better-sqlite3-adapter'\n","import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport type Database from 'better-sqlite3'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\n\n/**\n * Storage adapter backed by better-sqlite3 for Node.js environments.\n * Used for testing and server-side usage.\n *\n * @example\n * ```typescript\n * import { BetterSqlite3Adapter } from '@korajs/store/better-sqlite3'\n *\n * const adapter = new BetterSqlite3Adapter(':memory:')\n * ```\n */\nexport class BetterSqlite3Adapter implements StorageAdapter {\n\tprivate db: Database.Database | null = null\n\n\t/**\n\t * @param path - Database file path, or ':memory:' for in-memory database\n\t */\n\tconstructor(private readonly path: string = ':memory:') {}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// Dynamic import so better-sqlite3 is only loaded when this adapter is used\n\t\tconst BetterSqlite3 = (await import('better-sqlite3')).default\n\t\tthis.db = new BetterSqlite3(this.path)\n\n\t\t// WAL mode for better concurrent read/write performance\n\t\tthis.db.pragma('journal_mode = WAL')\n\t\t// Enable foreign keys\n\t\tthis.db.pragma('foreign_keys = ON')\n\n\t\tconst statements = generateFullDDL(schema)\n\t\tfor (const sql of statements) {\n\t\t\tthis.db.exec(sql)\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.db) {\n\t\t\tthis.db.close()\n\t\t\tthis.db = null\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Execute failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Query failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tconst db = this.getDb()\n\n\t\t// better-sqlite3's transaction() is synchronous, but our interface is async.\n\t\t// We use BEGIN/COMMIT/ROLLBACK manually for the async callback.\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}\n\t\t\tawait fn(tx)\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tconst db = this.getDb()\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tdb.exec(sql)\n\t\t\t}\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate getDb(): Database.Database {\n\t\tif (!this.db) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn this.db\n\t}\n}\n","import { KoraError } from '@korajs/core'\n\n/**\n * Thrown when a query is invalid (bad field names, invalid operators, etc.).\n */\nexport class QueryError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'QUERY_ERROR', context)\n\t\tthis.name = 'QueryError'\n\t}\n}\n\n/**\n * Thrown when a record is not found by ID (findById, update, delete on missing record).\n */\nexport class RecordNotFoundError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(`Record \"${recordId}\" not found in collection \"${collection}\"`, 'RECORD_NOT_FOUND', {\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t})\n\t\tthis.name = 'RecordNotFoundError'\n\t}\n}\n\n/**\n * Thrown when a storage adapter operation fails.\n */\nexport class AdapterError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'ADAPTER_ERROR', context)\n\t\tthis.name = 'AdapterError'\n\t}\n}\n\n/**\n * Thrown when an operation is attempted on a store that has not been opened.\n */\nexport class StoreNotOpenError extends KoraError {\n\tconstructor() {\n\t\tsuper('Store is not open. Call store.open() before performing operations.', 'STORE_NOT_OPEN')\n\t\tthis.name = 'StoreNotOpenError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).\n */\nexport class WorkerInitError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Worker initialization failed: ${message}`, 'WORKER_INIT_ERROR', context)\n\t\tthis.name = 'WorkerInitError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker does not respond within the configured timeout.\n */\nexport class WorkerTimeoutError extends KoraError {\n\tconstructor(operation: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\t`Worker did not respond within ${timeoutMs}ms for operation \"${operation}\"`,\n\t\t\t'WORKER_TIMEOUT',\n\t\t\t{ operation, timeoutMs },\n\t\t)\n\t\tthis.name = 'WorkerTimeoutError'\n\t}\n}\n\n/**\n * Thrown when IndexedDB persistence operations fail (serialize/deserialize).\n */\nexport class PersistenceError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Persistence error: ${message}`, 'PERSISTENCE_ERROR', context)\n\t\tthis.name = 'PersistenceError'\n\t}\n}\n"],"mappings":"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","names":["import_core"]}
1
+ {"version":3,"sources":["../../src/adapters/better-sqlite3.ts","../../src/adapters/better-sqlite3-adapter.ts","../../src/errors.ts"],"sourcesContent":["// Entry point for @korajs/store/better-sqlite3\nexport { BetterSqlite3Adapter } from './better-sqlite3-adapter'\n","import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport type Database from 'better-sqlite3'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\n\n/**\n * Storage adapter backed by better-sqlite3 for Node.js environments.\n * Used for testing and server-side usage.\n *\n * @example\n * ```typescript\n * import { BetterSqlite3Adapter } from '@korajs/store/better-sqlite3'\n *\n * const adapter = new BetterSqlite3Adapter(':memory:')\n * ```\n */\nexport class BetterSqlite3Adapter implements StorageAdapter {\n\tprivate db: Database.Database | null = null\n\n\t/**\n\t * @param path - Database file path, or ':memory:' for in-memory database\n\t */\n\tconstructor(private readonly path: string = ':memory:') {}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// Dynamic import so better-sqlite3 is only loaded when this adapter is used\n\t\tconst BetterSqlite3 = (await import('better-sqlite3')).default\n\t\tthis.db = new BetterSqlite3(this.path)\n\n\t\t// WAL mode for better concurrent read/write performance\n\t\tthis.db.pragma('journal_mode = WAL')\n\t\t// Enable foreign keys\n\t\tthis.db.pragma('foreign_keys = ON')\n\n\t\tconst statements = generateFullDDL(schema)\n\t\tfor (const sql of statements) {\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\t// Safe ALTER TABLE — ignore \"duplicate column name\" errors for existing columns\n\t\t\t\ttry {\n\t\t\t\t\tthis.db.exec(sql.replace('--kora:safe-alter\\n', ''))\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst msg = (e as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.db.exec(sql)\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.db) {\n\t\t\tthis.db.close()\n\t\t\tthis.db = null\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Execute failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Query failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tconst db = this.getDb()\n\n\t\t// better-sqlite3's transaction() is synchronous, but our interface is async.\n\t\t// We use BEGIN/COMMIT/ROLLBACK manually for the async callback.\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}\n\t\t\tawait fn(tx)\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tconst db = this.getDb()\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tdb.exec(sql)\n\t\t\t}\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate getDb(): Database.Database {\n\t\tif (!this.db) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn this.db\n\t}\n}\n","import { KoraError } from '@korajs/core'\n\n/**\n * Thrown when a query is invalid (bad field names, invalid operators, etc.).\n */\nexport class QueryError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'QUERY_ERROR', context)\n\t\tthis.name = 'QueryError'\n\t}\n}\n\n/**\n * Thrown when a record is not found by ID (findById, update, delete on missing record).\n */\nexport class RecordNotFoundError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(`Record \"${recordId}\" not found in collection \"${collection}\"`, 'RECORD_NOT_FOUND', {\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t})\n\t\tthis.name = 'RecordNotFoundError'\n\t}\n}\n\n/**\n * Thrown when a storage adapter operation fails.\n */\nexport class AdapterError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'ADAPTER_ERROR', context)\n\t\tthis.name = 'AdapterError'\n\t}\n}\n\n/**\n * Thrown when an operation is attempted on a store that has not been opened.\n */\nexport class StoreNotOpenError extends KoraError {\n\tconstructor() {\n\t\tsuper('Store is not open. Call store.open() before performing operations.', 'STORE_NOT_OPEN')\n\t\tthis.name = 'StoreNotOpenError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).\n */\nexport class WorkerInitError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Worker initialization failed: ${message}`, 'WORKER_INIT_ERROR', context)\n\t\tthis.name = 'WorkerInitError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker does not respond within the configured timeout.\n */\nexport class WorkerTimeoutError extends KoraError {\n\tconstructor(operation: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\t`Worker did not respond within ${timeoutMs}ms for operation \"${operation}\"`,\n\t\t\t'WORKER_TIMEOUT',\n\t\t\t{ operation, timeoutMs },\n\t\t)\n\t\tthis.name = 'WorkerTimeoutError'\n\t}\n}\n\n/**\n * Thrown when IndexedDB persistence operations fail (serialize/deserialize).\n */\nexport class PersistenceError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Persistence error: ${message}`, 'PERSISTENCE_ERROR', context)\n\t\tthis.name = 'PersistenceError'\n\t}\n}\n"],"mappings":"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","names":["import_core"]}
@@ -21,7 +21,18 @@ var BetterSqlite3Adapter = class {
21
21
  this.db.pragma("foreign_keys = ON");
22
22
  const statements = generateFullDDL(schema);
23
23
  for (const sql of statements) {
24
- this.db.exec(sql);
24
+ if (sql.startsWith("--kora:safe-alter")) {
25
+ try {
26
+ this.db.exec(sql.replace("--kora:safe-alter\n", ""));
27
+ } catch (e) {
28
+ const msg = e.message || "";
29
+ if (!msg.includes("duplicate column name")) {
30
+ throw e;
31
+ }
32
+ }
33
+ } else {
34
+ this.db.exec(sql);
35
+ }
25
36
  }
26
37
  }
27
38
  async close() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/better-sqlite3-adapter.ts"],"sourcesContent":["import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport type Database from 'better-sqlite3'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\n\n/**\n * Storage adapter backed by better-sqlite3 for Node.js environments.\n * Used for testing and server-side usage.\n *\n * @example\n * ```typescript\n * import { BetterSqlite3Adapter } from '@korajs/store/better-sqlite3'\n *\n * const adapter = new BetterSqlite3Adapter(':memory:')\n * ```\n */\nexport class BetterSqlite3Adapter implements StorageAdapter {\n\tprivate db: Database.Database | null = null\n\n\t/**\n\t * @param path - Database file path, or ':memory:' for in-memory database\n\t */\n\tconstructor(private readonly path: string = ':memory:') {}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// Dynamic import so better-sqlite3 is only loaded when this adapter is used\n\t\tconst BetterSqlite3 = (await import('better-sqlite3')).default\n\t\tthis.db = new BetterSqlite3(this.path)\n\n\t\t// WAL mode for better concurrent read/write performance\n\t\tthis.db.pragma('journal_mode = WAL')\n\t\t// Enable foreign keys\n\t\tthis.db.pragma('foreign_keys = ON')\n\n\t\tconst statements = generateFullDDL(schema)\n\t\tfor (const sql of statements) {\n\t\t\tthis.db.exec(sql)\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.db) {\n\t\t\tthis.db.close()\n\t\t\tthis.db = null\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Execute failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Query failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tconst db = this.getDb()\n\n\t\t// better-sqlite3's transaction() is synchronous, but our interface is async.\n\t\t// We use BEGIN/COMMIT/ROLLBACK manually for the async callback.\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}\n\t\t\tawait fn(tx)\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tconst db = this.getDb()\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tdb.exec(sql)\n\t\t\t}\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate getDb(): Database.Database {\n\t\tif (!this.db) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn this.db\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/adapters/better-sqlite3-adapter.ts"],"sourcesContent":["import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport type Database from 'better-sqlite3'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\n\n/**\n * Storage adapter backed by better-sqlite3 for Node.js environments.\n * Used for testing and server-side usage.\n *\n * @example\n * ```typescript\n * import { BetterSqlite3Adapter } from '@korajs/store/better-sqlite3'\n *\n * const adapter = new BetterSqlite3Adapter(':memory:')\n * ```\n */\nexport class BetterSqlite3Adapter implements StorageAdapter {\n\tprivate db: Database.Database | null = null\n\n\t/**\n\t * @param path - Database file path, or ':memory:' for in-memory database\n\t */\n\tconstructor(private readonly path: string = ':memory:') {}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// Dynamic import so better-sqlite3 is only loaded when this adapter is used\n\t\tconst BetterSqlite3 = (await import('better-sqlite3')).default\n\t\tthis.db = new BetterSqlite3(this.path)\n\n\t\t// WAL mode for better concurrent read/write performance\n\t\tthis.db.pragma('journal_mode = WAL')\n\t\t// Enable foreign keys\n\t\tthis.db.pragma('foreign_keys = ON')\n\n\t\tconst statements = generateFullDDL(schema)\n\t\tfor (const sql of statements) {\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\t// Safe ALTER TABLE — ignore \"duplicate column name\" errors for existing columns\n\t\t\t\ttry {\n\t\t\t\t\tthis.db.exec(sql.replace('--kora:safe-alter\\n', ''))\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst msg = (e as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.db.exec(sql)\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.db) {\n\t\t\tthis.db.close()\n\t\t\tthis.db = null\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Execute failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tconst db = this.getDb()\n\t\ttry {\n\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t} catch (error) {\n\t\t\tthrow new AdapterError(`Query failed: ${(error as Error).message}`, {\n\t\t\t\tsql,\n\t\t\t\tparams,\n\t\t\t})\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tconst db = this.getDb()\n\n\t\t// better-sqlite3's transaction() is synchronous, but our interface is async.\n\t\t// We use BEGIN/COMMIT/ROLLBACK manually for the async callback.\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdb.prepare(sql).run(...(params ?? []))\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn db.prepare(sql).all(...(params ?? [])) as T[]\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${(error as Error).message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}\n\t\t\tawait fn(tx)\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tconst db = this.getDb()\n\t\tdb.exec('BEGIN')\n\t\ttry {\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tdb.exec(sql)\n\t\t\t}\n\t\t\tdb.exec('COMMIT')\n\t\t} catch (error) {\n\t\t\tdb.exec('ROLLBACK')\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{\n\t\t\t\t\tfrom,\n\t\t\t\t\tto,\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate getDb(): Database.Database {\n\t\tif (!this.db) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn this.db\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -63,7 +63,15 @@ function handleQuery(id, sql, params) {
63
63
  async function handleOpen(id, ddlStatements) {
64
64
  try {
65
65
  const sqlite3InitModule = (await import("@sqlite.org/sqlite-wasm")).default;
66
- const sqlite3 = await sqlite3InitModule();
66
+ const wasmUrl = globalThis.__KORA_SQLITE_WASM_URL;
67
+ const initOptions = wasmUrl ? {
68
+ locateFile: (file) => {
69
+ if (file.endsWith(".wasm")) return wasmUrl;
70
+ return file;
71
+ }
72
+ } : void 0;
73
+ const initFn = sqlite3InitModule;
74
+ const sqlite3 = await initFn(initOptions);
67
75
  sqlite3Api = sqlite3;
68
76
  let useOpfs = false;
69
77
  if (sqlite3.installOpfsSAHPoolVfs) {
@@ -81,7 +89,18 @@ async function handleOpen(id, ddlStatements) {
81
89
  db?.exec({ sql: "PRAGMA journal_mode = WAL" });
82
90
  db?.exec({ sql: "PRAGMA foreign_keys = ON" });
83
91
  for (const sql of ddlStatements) {
84
- db?.exec({ sql });
92
+ if (sql.startsWith("--kora:safe-alter")) {
93
+ try {
94
+ db?.exec({ sql: sql.replace("--kora:safe-alter\n", "") });
95
+ } catch (e) {
96
+ const msg = e.message || "";
97
+ if (!msg.includes("duplicate column name")) {
98
+ throw e;
99
+ }
100
+ }
101
+ } else {
102
+ db?.exec({ sql });
103
+ }
85
104
  }
86
105
  sendResponse({ id, type: "success" });
87
106
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM.\n *\n * This file is intended to run inside a Web Worker in browsers.\n * It loads @sqlite.org/sqlite-wasm, initializes SQLite with OPFS persistence\n * (falling back to in-memory if unavailable), and processes messages from\n * the main thread via the WorkerRequest/WorkerResponse protocol.\n *\n * This script cannot be tested in Node.js — it is validated in E2E browser tests.\n */\n\nimport type { WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\ninterface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): void\n\tclose(): void\n\tdeserialize?: (data: Uint8Array) => void\n}\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nlet db: SqliteDb | null = null\nlet sqlite3Api: unknown = null\n\nfunction sendResponse(response: WorkerResponse): void {\n\tself.postMessage(response)\n}\n\nfunction handleExecute(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tdb.exec({ sql, bind: params })\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' })\n\t}\n}\n\nfunction handleQuery(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tconst rows: Record<string, unknown>[] = []\n\t\tdb.exec({\n\t\t\tsql,\n\t\t\tbind: params,\n\t\t\trowMode: 'object',\n\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\trows.push({ ...row })\n\t\t\t},\n\t\t})\n\t\tsendResponse({ id, type: 'success', data: rows })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' })\n\t}\n}\n\nasync function handleOpen(id: number, ddlStatements: string[]): Promise<void> {\n\ttry {\n\t\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\t\tconst sqlite3 = await sqlite3InitModule()\n\t\tsqlite3Api = sqlite3\n\n\t\t// Try OPFS persistence first\n\t\tlet useOpfs = false\n\t\tif (sqlite3.installOpfsSAHPoolVfs) {\n\t\t\ttry {\n\t\t\t\tconst pool = await sqlite3.installOpfsSAHPoolVfs({ name: 'kora-opfs' })\n\t\t\t\tdb = new pool.OpfsSAHPoolDb('kora.db')\n\t\t\t\tuseOpfs = true\n\t\t\t} catch {\n\t\t\t\t// OPFS unavailable, fall back to in-memory\n\t\t\t\tconsole.warn('[kora] OPFS unavailable, falling back to in-memory SQLite')\n\t\t\t}\n\t\t}\n\n\t\tif (!useOpfs) {\n\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t}\n\n\t\t// Set pragmas\n\t\tdb?.exec({ sql: 'PRAGMA journal_mode = WAL' })\n\t\tdb?.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\n\t\t// Execute DDL statements\n\t\tfor (const sql of ddlStatements) {\n\t\t\tdb?.exec({ sql })\n\t\t}\n\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'INIT_ERROR',\n\t\t})\n\t}\n}\n\nfunction handleClose(id: number): void {\n\tif (db) {\n\t\tdb.close()\n\t\tdb = null\n\t}\n\tsendResponse({ id, type: 'success' })\n}\n\nfunction handleImport(id: number, data: Uint8Array): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\n\tconst dbWithDeserialize = db as SqliteDb & { deserialize?: (bytes: Uint8Array) => void }\n\tif (typeof dbWithDeserialize.deserialize === 'function') {\n\t\ttry {\n\t\t\tdbWithDeserialize.deserialize(data)\n\t\t\tsendResponse({ id, type: 'success' })\n\t\t\treturn\n\t\t} catch (error) {\n\t\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' })\n\t\t\treturn\n\t\t}\n\t}\n\n\tconst sqlite3 = sqlite3Api as\n\t\t| {\n\t\t\t\too1?: { DB?: new (...args: unknown[]) => SqliteDb }\n\t\t\t\tcapi?: { sqlite3_deserialize?: unknown }\n\t\t\t}\n\t\t| null\n\n\tif (!sqlite3 || typeof sqlite3.capi?.sqlite3_deserialize === 'undefined') {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t})\n\t\treturn\n\t}\n\n\tsendResponse({\n\t\tid,\n\t\ttype: 'error',\n\t\tmessage: 'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t})\n}\n\nfunction handleMessage(request: WorkerRequest): void {\n\ttry {\n\t\tswitch (request.type) {\n\t\t\tcase 'open':\n\t\t\t\t// open is async due to WASM loading\n\t\t\t\thandleOpen(request.id, request.ddlStatements)\n\t\t\t\treturn\n\t\t\tcase 'close':\n\t\t\t\thandleClose(request.id)\n\t\t\t\treturn\n\t\t\tcase 'execute':\n\t\t\t\thandleExecute(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'query':\n\t\t\t\thandleQuery(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'begin':\n\t\t\t\thandleExecute(request.id, 'BEGIN')\n\t\t\t\treturn\n\t\t\tcase 'commit':\n\t\t\t\thandleExecute(request.id, 'COMMIT')\n\t\t\t\treturn\n\t\t\tcase 'rollback':\n\t\t\t\thandleExecute(request.id, 'ROLLBACK')\n\t\t\t\treturn\n\t\t\tcase 'migrate':\n\t\t\t\tif (!db) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Database is not open',\n\t\t\t\t\t\tcode: 'DB_NOT_OPEN',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tfor (const sql of request.statements) {\n\t\t\t\t\t\tdb.exec({ sql })\n\t\t\t\t\t}\n\t\t\t\t\tsendResponse({ id: request.id, type: 'success' })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: (error as Error).message,\n\t\t\t\t\t\tcode: 'MIGRATE_ERROR',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\tcase 'export':\n\t\t\t\t// Export is not trivially supported via the oo1 API in the browser.\n\t\t\t\t// In a real implementation, we'd use the C API's sqlite3_serialize.\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'import':\n\t\t\t\thandleImport(request.id, request.data)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid: request.id,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'WORKER_ERROR',\n\t\t})\n\t}\n}\n\n// Listen for messages from the main thread\nself.onmessage = (event: MessageEvent<WorkerRequest>) => {\n\thandleMessage(event.data)\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM.\n *\n * This file is intended to run inside a Web Worker in browsers.\n * It loads @sqlite.org/sqlite-wasm, initializes SQLite with OPFS persistence\n * (falling back to in-memory if unavailable), and processes messages from\n * the main thread via the WorkerRequest/WorkerResponse protocol.\n *\n * This script cannot be tested in Node.js — it is validated in E2E browser tests.\n */\n\nimport type { WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\ninterface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): void\n\tclose(): void\n\tdeserialize?: (data: Uint8Array) => void\n}\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nlet db: SqliteDb | null = null\nlet sqlite3Api: unknown = null\n\nfunction sendResponse(response: WorkerResponse): void {\n\tself.postMessage(response)\n}\n\nfunction handleExecute(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tdb.exec({ sql, bind: params })\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' })\n\t}\n}\n\nfunction handleQuery(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tconst rows: Record<string, unknown>[] = []\n\t\tdb.exec({\n\t\t\tsql,\n\t\t\tbind: params,\n\t\t\trowMode: 'object',\n\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\trows.push({ ...row })\n\t\t\t},\n\t\t})\n\t\tsendResponse({ id, type: 'success', data: rows })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' })\n\t}\n}\n\nasync function handleOpen(id: number, ddlStatements: string[]): Promise<void> {\n\ttry {\n\t\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\n\t\t// In production builds, Vite hashes asset filenames (e.g. sqlite3-[hash].wasm).\n\t\t// The sqlite3 module's default locateFile resolves the unhashed name, causing a 404.\n\t\t// Templates set __KORA_SQLITE_WASM_URL via `import wasmUrl from '...sqlite3.wasm?url'`\n\t\t// so we can override locateFile with the correct hashed URL.\n\t\tconst wasmUrl = (globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL as\n\t\t\t| string\n\t\t\t| undefined\n\t\tconst initOptions = wasmUrl\n\t\t\t? {\n\t\t\t\t\tlocateFile: (file: string) => {\n\t\t\t\t\t\tif (file.endsWith('.wasm')) return wasmUrl\n\t\t\t\t\t\treturn file\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: undefined\n\n\t\t// sqlite3InitModule accepts Emscripten Module overrides but the types don't reflect this\n\t\tconst initFn = sqlite3InitModule as unknown as (opts?: Record<string, unknown>) => Promise<unknown>\n\t\tconst sqlite3 = (await initFn(initOptions)) as Awaited<ReturnType<typeof sqlite3InitModule>>\n\t\tsqlite3Api = sqlite3\n\n\t\t// Try OPFS persistence first\n\t\tlet useOpfs = false\n\t\tif (sqlite3.installOpfsSAHPoolVfs) {\n\t\t\ttry {\n\t\t\t\tconst pool = await sqlite3.installOpfsSAHPoolVfs({ name: 'kora-opfs' })\n\t\t\t\tdb = new pool.OpfsSAHPoolDb('kora.db')\n\t\t\t\tuseOpfs = true\n\t\t\t} catch {\n\t\t\t\t// OPFS unavailable, fall back to in-memory\n\t\t\t\tconsole.warn('[kora] OPFS unavailable, falling back to in-memory SQLite')\n\t\t\t}\n\t\t}\n\n\t\tif (!useOpfs) {\n\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t}\n\n\t\t// Set pragmas\n\t\tdb?.exec({ sql: 'PRAGMA journal_mode = WAL' })\n\t\tdb?.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\n\t\t// Execute DDL statements\n\t\tfor (const sql of ddlStatements) {\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\t// Safe ALTER TABLE — ignore \"duplicate column name\" errors for existing columns\n\t\t\t\ttry {\n\t\t\t\t\tdb?.exec({ sql: sql.replace('--kora:safe-alter\\n', '') })\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst msg = (e as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdb?.exec({ sql })\n\t\t\t}\n\t\t}\n\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'INIT_ERROR',\n\t\t})\n\t}\n}\n\nfunction handleClose(id: number): void {\n\tif (db) {\n\t\tdb.close()\n\t\tdb = null\n\t}\n\tsendResponse({ id, type: 'success' })\n}\n\nfunction handleImport(id: number, data: Uint8Array): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\n\tconst dbWithDeserialize = db as SqliteDb & { deserialize?: (bytes: Uint8Array) => void }\n\tif (typeof dbWithDeserialize.deserialize === 'function') {\n\t\ttry {\n\t\t\tdbWithDeserialize.deserialize(data)\n\t\t\tsendResponse({ id, type: 'success' })\n\t\t\treturn\n\t\t} catch (error) {\n\t\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' })\n\t\t\treturn\n\t\t}\n\t}\n\n\tconst sqlite3 = sqlite3Api as\n\t\t| {\n\t\t\t\too1?: { DB?: new (...args: unknown[]) => SqliteDb }\n\t\t\t\tcapi?: { sqlite3_deserialize?: unknown }\n\t\t\t}\n\t\t| null\n\n\tif (!sqlite3 || typeof sqlite3.capi?.sqlite3_deserialize === 'undefined') {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t})\n\t\treturn\n\t}\n\n\tsendResponse({\n\t\tid,\n\t\ttype: 'error',\n\t\tmessage: 'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t})\n}\n\nfunction handleMessage(request: WorkerRequest): void {\n\ttry {\n\t\tswitch (request.type) {\n\t\t\tcase 'open':\n\t\t\t\t// open is async due to WASM loading\n\t\t\t\thandleOpen(request.id, request.ddlStatements)\n\t\t\t\treturn\n\t\t\tcase 'close':\n\t\t\t\thandleClose(request.id)\n\t\t\t\treturn\n\t\t\tcase 'execute':\n\t\t\t\thandleExecute(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'query':\n\t\t\t\thandleQuery(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'begin':\n\t\t\t\thandleExecute(request.id, 'BEGIN')\n\t\t\t\treturn\n\t\t\tcase 'commit':\n\t\t\t\thandleExecute(request.id, 'COMMIT')\n\t\t\t\treturn\n\t\t\tcase 'rollback':\n\t\t\t\thandleExecute(request.id, 'ROLLBACK')\n\t\t\t\treturn\n\t\t\tcase 'migrate':\n\t\t\t\tif (!db) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Database is not open',\n\t\t\t\t\t\tcode: 'DB_NOT_OPEN',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tfor (const sql of request.statements) {\n\t\t\t\t\t\tdb.exec({ sql })\n\t\t\t\t\t}\n\t\t\t\t\tsendResponse({ id: request.id, type: 'success' })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: (error as Error).message,\n\t\t\t\t\t\tcode: 'MIGRATE_ERROR',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\tcase 'export':\n\t\t\t\t// Export is not trivially supported via the oo1 API in the browser.\n\t\t\t\t// In a real implementation, we'd use the C API's sqlite3_serialize.\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'import':\n\t\t\t\thandleImport(request.id, request.data)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid: request.id,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'WORKER_ERROR',\n\t\t})\n\t}\n}\n\n// Listen for messages from the main thread\nself.onmessage = (event: MessageEvent<WorkerRequest>) => {\n\thandleMessage(event.data)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,IAAI,KAAsB;AAC1B,IAAI,aAAsB;AAE1B,SAAS,aAAa,UAAgC;AACrD,OAAK,YAAY,QAAQ;AAC1B;AAEA,SAAS,cAAc,IAAY,KAAa,QAA0B;AACzE,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AACA,MAAI;AACH,OAAG,KAAK,EAAE,KAAK,MAAM,OAAO,CAAC;AAC7B,iBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,EACrC,SAAS,OAAO;AACf,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,aAAa,CAAC;AAAA,EAC1F;AACD;AAEA,SAAS,YAAY,IAAY,KAAa,QAA0B;AACvE,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AACA,MAAI;AACH,UAAM,OAAkC,CAAC;AACzC,OAAG,KAAK;AAAA,MACP;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU,CAAC,QAAiC;AAC3C,aAAK,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,MACrB;AAAA,IACD,CAAC;AACD,iBAAa,EAAE,IAAI,MAAM,WAAW,MAAM,KAAK,CAAC;AAAA,EACjD,SAAS,OAAO;AACf,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,cAAc,CAAC;AAAA,EAC3F;AACD;AAEA,eAAe,WAAW,IAAY,eAAwC;AAC7E,MAAI;AACH,UAAM,qBAAqB,MAAM,OAAO,yBAAyB,GAAG;AAMpE,UAAM,UAAW,WAAuC;AAGxD,UAAM,cAAc,UACjB;AAAA,MACA,YAAY,CAAC,SAAiB;AAC7B,YAAI,KAAK,SAAS,OAAO,EAAG,QAAO;AACnC,eAAO;AAAA,MACR;AAAA,IACD,IACC;AAGH,UAAM,SAAS;AACf,UAAM,UAAW,MAAM,OAAO,WAAW;AACzC,iBAAa;AAGb,QAAI,UAAU;AACd,QAAI,QAAQ,uBAAuB;AAClC,UAAI;AACH,cAAM,OAAO,MAAM,QAAQ,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACtE,aAAK,IAAI,KAAK,cAAc,SAAS;AACrC,kBAAU;AAAA,MACX,QAAQ;AAEP,gBAAQ,KAAK,2DAA2D;AAAA,MACzE;AAAA,IACD;AAEA,QAAI,CAAC,SAAS;AACb,WAAK,IAAI,QAAQ,IAAI,GAAG,EAAE,UAAU,WAAW,CAAC;AAAA,IACjD;AAGA,QAAI,KAAK,EAAE,KAAK,4BAA4B,CAAC;AAC7C,QAAI,KAAK,EAAE,KAAK,2BAA2B,CAAC;AAG5C,eAAW,OAAO,eAAe;AAChC,UAAI,IAAI,WAAW,mBAAmB,GAAG;AAExC,YAAI;AACH,cAAI,KAAK,EAAE,KAAK,IAAI,QAAQ,uBAAuB,EAAE,EAAE,CAAC;AAAA,QACzD,SAAS,GAAG;AACX,gBAAM,MAAO,EAAY,WAAW;AACpC,cAAI,CAAC,IAAI,SAAS,uBAAuB,GAAG;AAC3C,kBAAM;AAAA,UACP;AAAA,QACD;AAAA,MACD,OAAO;AACN,YAAI,KAAK,EAAE,IAAI,CAAC;AAAA,MACjB;AAAA,IACD;AAEA,iBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,EACrC,SAAS,OAAO;AACf,iBAAa;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,SAAU,MAAgB;AAAA,MAC1B,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AACD;AAEA,SAAS,YAAY,IAAkB;AACtC,MAAI,IAAI;AACP,OAAG,MAAM;AACT,SAAK;AAAA,EACN;AACA,eAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AACrC;AAEA,SAAS,aAAa,IAAY,MAAwB;AACzD,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AAEA,QAAM,oBAAoB;AAC1B,MAAI,OAAO,kBAAkB,gBAAgB,YAAY;AACxD,QAAI;AACH,wBAAkB,YAAY,IAAI;AAClC,mBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AACpC;AAAA,IACD,SAAS,OAAO;AACf,mBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,eAAe,CAAC;AAC3F;AAAA,IACD;AAAA,EACD;AAEA,QAAM,UAAU;AAOhB,MAAI,CAAC,WAAW,OAAO,QAAQ,MAAM,wBAAwB,aAAa;AACzE,iBAAa;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACP,CAAC;AACD;AAAA,EACD;AAEA,eAAa;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,EACP,CAAC;AACF;AAEA,SAAS,cAAc,SAA8B;AACpD,MAAI;AACH,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AAEJ,mBAAW,QAAQ,IAAI,QAAQ,aAAa;AAC5C;AAAA,MACD,KAAK;AACJ,oBAAY,QAAQ,EAAE;AACtB;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,QAAQ,KAAK,QAAQ,MAAM;AACrD;AAAA,MACD,KAAK;AACJ,oBAAY,QAAQ,IAAI,QAAQ,KAAK,QAAQ,MAAM;AACnD;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,OAAO;AACjC;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,QAAQ;AAClC;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,UAAU;AACpC;AAAA,MACD,KAAK;AACJ,YAAI,CAAC,IAAI;AACR,uBAAa;AAAA,YACZ,IAAI,QAAQ;AAAA,YACZ,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM;AAAA,UACP,CAAC;AACD;AAAA,QACD;AACA,YAAI;AACH,qBAAW,OAAO,QAAQ,YAAY;AACrC,eAAG,KAAK,EAAE,IAAI,CAAC;AAAA,UAChB;AACA,uBAAa,EAAE,IAAI,QAAQ,IAAI,MAAM,UAAU,CAAC;AAAA,QACjD,SAAS,OAAO;AACf,uBAAa;AAAA,YACZ,IAAI,QAAQ;AAAA,YACZ,MAAM;AAAA,YACN,SAAU,MAAgB;AAAA,YAC1B,MAAM;AAAA,UACP,CAAC;AAAA,QACF;AACA;AAAA,MACD,KAAK;AAGJ,qBAAa;AAAA,UACZ,IAAI,QAAQ;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QACP,CAAC;AACD;AAAA,MACD,KAAK;AACJ,qBAAa,QAAQ,IAAI,QAAQ,IAAI;AACrC;AAAA,MACD;AACC,qBAAa;AAAA,UACZ,IAAK,QAA0B;AAAA,UAC/B,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QACP,CAAC;AAAA,IACH;AAAA,EACD,SAAS,OAAO;AACf,iBAAa;AAAA,MACZ,IAAI,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,SAAU,MAAgB;AAAA,MAC1B,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AACD;AAGA,KAAK,YAAY,CAAC,UAAuC;AACxD,gBAAc,MAAM,IAAI;AACzB;","names":[]}
@@ -39,7 +39,15 @@ function handleQuery(id, sql, params) {
39
39
  async function handleOpen(id, ddlStatements) {
40
40
  try {
41
41
  const sqlite3InitModule = (await import("@sqlite.org/sqlite-wasm")).default;
42
- const sqlite3 = await sqlite3InitModule();
42
+ const wasmUrl = globalThis.__KORA_SQLITE_WASM_URL;
43
+ const initOptions = wasmUrl ? {
44
+ locateFile: (file) => {
45
+ if (file.endsWith(".wasm")) return wasmUrl;
46
+ return file;
47
+ }
48
+ } : void 0;
49
+ const initFn = sqlite3InitModule;
50
+ const sqlite3 = await initFn(initOptions);
43
51
  sqlite3Api = sqlite3;
44
52
  let useOpfs = false;
45
53
  if (sqlite3.installOpfsSAHPoolVfs) {
@@ -57,7 +65,18 @@ async function handleOpen(id, ddlStatements) {
57
65
  db?.exec({ sql: "PRAGMA journal_mode = WAL" });
58
66
  db?.exec({ sql: "PRAGMA foreign_keys = ON" });
59
67
  for (const sql of ddlStatements) {
60
- db?.exec({ sql });
68
+ if (sql.startsWith("--kora:safe-alter")) {
69
+ try {
70
+ db?.exec({ sql: sql.replace("--kora:safe-alter\n", "") });
71
+ } catch (e) {
72
+ const msg = e.message || "";
73
+ if (!msg.includes("duplicate column name")) {
74
+ throw e;
75
+ }
76
+ }
77
+ } else {
78
+ db?.exec({ sql });
79
+ }
61
80
  }
62
81
  sendResponse({ id, type: "success" });
63
82
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM.\n *\n * This file is intended to run inside a Web Worker in browsers.\n * It loads @sqlite.org/sqlite-wasm, initializes SQLite with OPFS persistence\n * (falling back to in-memory if unavailable), and processes messages from\n * the main thread via the WorkerRequest/WorkerResponse protocol.\n *\n * This script cannot be tested in Node.js — it is validated in E2E browser tests.\n */\n\nimport type { WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\ninterface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): void\n\tclose(): void\n\tdeserialize?: (data: Uint8Array) => void\n}\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nlet db: SqliteDb | null = null\nlet sqlite3Api: unknown = null\n\nfunction sendResponse(response: WorkerResponse): void {\n\tself.postMessage(response)\n}\n\nfunction handleExecute(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tdb.exec({ sql, bind: params })\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' })\n\t}\n}\n\nfunction handleQuery(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tconst rows: Record<string, unknown>[] = []\n\t\tdb.exec({\n\t\t\tsql,\n\t\t\tbind: params,\n\t\t\trowMode: 'object',\n\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\trows.push({ ...row })\n\t\t\t},\n\t\t})\n\t\tsendResponse({ id, type: 'success', data: rows })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' })\n\t}\n}\n\nasync function handleOpen(id: number, ddlStatements: string[]): Promise<void> {\n\ttry {\n\t\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\t\tconst sqlite3 = await sqlite3InitModule()\n\t\tsqlite3Api = sqlite3\n\n\t\t// Try OPFS persistence first\n\t\tlet useOpfs = false\n\t\tif (sqlite3.installOpfsSAHPoolVfs) {\n\t\t\ttry {\n\t\t\t\tconst pool = await sqlite3.installOpfsSAHPoolVfs({ name: 'kora-opfs' })\n\t\t\t\tdb = new pool.OpfsSAHPoolDb('kora.db')\n\t\t\t\tuseOpfs = true\n\t\t\t} catch {\n\t\t\t\t// OPFS unavailable, fall back to in-memory\n\t\t\t\tconsole.warn('[kora] OPFS unavailable, falling back to in-memory SQLite')\n\t\t\t}\n\t\t}\n\n\t\tif (!useOpfs) {\n\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t}\n\n\t\t// Set pragmas\n\t\tdb?.exec({ sql: 'PRAGMA journal_mode = WAL' })\n\t\tdb?.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\n\t\t// Execute DDL statements\n\t\tfor (const sql of ddlStatements) {\n\t\t\tdb?.exec({ sql })\n\t\t}\n\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'INIT_ERROR',\n\t\t})\n\t}\n}\n\nfunction handleClose(id: number): void {\n\tif (db) {\n\t\tdb.close()\n\t\tdb = null\n\t}\n\tsendResponse({ id, type: 'success' })\n}\n\nfunction handleImport(id: number, data: Uint8Array): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\n\tconst dbWithDeserialize = db as SqliteDb & { deserialize?: (bytes: Uint8Array) => void }\n\tif (typeof dbWithDeserialize.deserialize === 'function') {\n\t\ttry {\n\t\t\tdbWithDeserialize.deserialize(data)\n\t\t\tsendResponse({ id, type: 'success' })\n\t\t\treturn\n\t\t} catch (error) {\n\t\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' })\n\t\t\treturn\n\t\t}\n\t}\n\n\tconst sqlite3 = sqlite3Api as\n\t\t| {\n\t\t\t\too1?: { DB?: new (...args: unknown[]) => SqliteDb }\n\t\t\t\tcapi?: { sqlite3_deserialize?: unknown }\n\t\t\t}\n\t\t| null\n\n\tif (!sqlite3 || typeof sqlite3.capi?.sqlite3_deserialize === 'undefined') {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t})\n\t\treturn\n\t}\n\n\tsendResponse({\n\t\tid,\n\t\ttype: 'error',\n\t\tmessage: 'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t})\n}\n\nfunction handleMessage(request: WorkerRequest): void {\n\ttry {\n\t\tswitch (request.type) {\n\t\t\tcase 'open':\n\t\t\t\t// open is async due to WASM loading\n\t\t\t\thandleOpen(request.id, request.ddlStatements)\n\t\t\t\treturn\n\t\t\tcase 'close':\n\t\t\t\thandleClose(request.id)\n\t\t\t\treturn\n\t\t\tcase 'execute':\n\t\t\t\thandleExecute(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'query':\n\t\t\t\thandleQuery(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'begin':\n\t\t\t\thandleExecute(request.id, 'BEGIN')\n\t\t\t\treturn\n\t\t\tcase 'commit':\n\t\t\t\thandleExecute(request.id, 'COMMIT')\n\t\t\t\treturn\n\t\t\tcase 'rollback':\n\t\t\t\thandleExecute(request.id, 'ROLLBACK')\n\t\t\t\treturn\n\t\t\tcase 'migrate':\n\t\t\t\tif (!db) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Database is not open',\n\t\t\t\t\t\tcode: 'DB_NOT_OPEN',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tfor (const sql of request.statements) {\n\t\t\t\t\t\tdb.exec({ sql })\n\t\t\t\t\t}\n\t\t\t\t\tsendResponse({ id: request.id, type: 'success' })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: (error as Error).message,\n\t\t\t\t\t\tcode: 'MIGRATE_ERROR',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\tcase 'export':\n\t\t\t\t// Export is not trivially supported via the oo1 API in the browser.\n\t\t\t\t// In a real implementation, we'd use the C API's sqlite3_serialize.\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'import':\n\t\t\t\thandleImport(request.id, request.data)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid: request.id,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'WORKER_ERROR',\n\t\t})\n\t}\n}\n\n// Listen for messages from the main thread\nself.onmessage = (event: MessageEvent<WorkerRequest>) => {\n\thandleMessage(event.data)\n}\n"],"mappings":";AA4BA,IAAI,KAAsB;AAC1B,IAAI,aAAsB;AAE1B,SAAS,aAAa,UAAgC;AACrD,OAAK,YAAY,QAAQ;AAC1B;AAEA,SAAS,cAAc,IAAY,KAAa,QAA0B;AACzE,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AACA,MAAI;AACH,OAAG,KAAK,EAAE,KAAK,MAAM,OAAO,CAAC;AAC7B,iBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,EACrC,SAAS,OAAO;AACf,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,aAAa,CAAC;AAAA,EAC1F;AACD;AAEA,SAAS,YAAY,IAAY,KAAa,QAA0B;AACvE,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AACA,MAAI;AACH,UAAM,OAAkC,CAAC;AACzC,OAAG,KAAK;AAAA,MACP;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU,CAAC,QAAiC;AAC3C,aAAK,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,MACrB;AAAA,IACD,CAAC;AACD,iBAAa,EAAE,IAAI,MAAM,WAAW,MAAM,KAAK,CAAC;AAAA,EACjD,SAAS,OAAO;AACf,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,cAAc,CAAC;AAAA,EAC3F;AACD;AAEA,eAAe,WAAW,IAAY,eAAwC;AAC7E,MAAI;AACH,UAAM,qBAAqB,MAAM,OAAO,yBAAyB,GAAG;AACpE,UAAM,UAAU,MAAM,kBAAkB;AACxC,iBAAa;AAGb,QAAI,UAAU;AACd,QAAI,QAAQ,uBAAuB;AAClC,UAAI;AACH,cAAM,OAAO,MAAM,QAAQ,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACtE,aAAK,IAAI,KAAK,cAAc,SAAS;AACrC,kBAAU;AAAA,MACX,QAAQ;AAEP,gBAAQ,KAAK,2DAA2D;AAAA,MACzE;AAAA,IACD;AAEA,QAAI,CAAC,SAAS;AACb,WAAK,IAAI,QAAQ,IAAI,GAAG,EAAE,UAAU,WAAW,CAAC;AAAA,IACjD;AAGA,QAAI,KAAK,EAAE,KAAK,4BAA4B,CAAC;AAC7C,QAAI,KAAK,EAAE,KAAK,2BAA2B,CAAC;AAG5C,eAAW,OAAO,eAAe;AAChC,UAAI,KAAK,EAAE,IAAI,CAAC;AAAA,IACjB;AAEA,iBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AAAA,EACrC,SAAS,OAAO;AACf,iBAAa;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,SAAU,MAAgB;AAAA,MAC1B,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AACD;AAEA,SAAS,YAAY,IAAkB;AACtC,MAAI,IAAI;AACP,OAAG,MAAM;AACT,SAAK;AAAA,EACN;AACA,eAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AACrC;AAEA,SAAS,aAAa,IAAY,MAAwB;AACzD,MAAI,CAAC,IAAI;AACR,iBAAa,EAAE,IAAI,MAAM,SAAS,SAAS,wBAAwB,MAAM,cAAc,CAAC;AACxF;AAAA,EACD;AAEA,QAAM,oBAAoB;AAC1B,MAAI,OAAO,kBAAkB,gBAAgB,YAAY;AACxD,QAAI;AACH,wBAAkB,YAAY,IAAI;AAClC,mBAAa,EAAE,IAAI,MAAM,UAAU,CAAC;AACpC;AAAA,IACD,SAAS,OAAO;AACf,mBAAa,EAAE,IAAI,MAAM,SAAS,SAAU,MAAgB,SAAS,MAAM,eAAe,CAAC;AAC3F;AAAA,IACD;AAAA,EACD;AAEA,QAAM,UAAU;AAOhB,MAAI,CAAC,WAAW,OAAO,QAAQ,MAAM,wBAAwB,aAAa;AACzE,iBAAa;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACP,CAAC;AACD;AAAA,EACD;AAEA,eAAa;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,EACP,CAAC;AACF;AAEA,SAAS,cAAc,SAA8B;AACpD,MAAI;AACH,YAAQ,QAAQ,MAAM;AAAA,MACrB,KAAK;AAEJ,mBAAW,QAAQ,IAAI,QAAQ,aAAa;AAC5C;AAAA,MACD,KAAK;AACJ,oBAAY,QAAQ,EAAE;AACtB;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,QAAQ,KAAK,QAAQ,MAAM;AACrD;AAAA,MACD,KAAK;AACJ,oBAAY,QAAQ,IAAI,QAAQ,KAAK,QAAQ,MAAM;AACnD;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,OAAO;AACjC;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,QAAQ;AAClC;AAAA,MACD,KAAK;AACJ,sBAAc,QAAQ,IAAI,UAAU;AACpC;AAAA,MACD,KAAK;AACJ,YAAI,CAAC,IAAI;AACR,uBAAa;AAAA,YACZ,IAAI,QAAQ;AAAA,YACZ,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM;AAAA,UACP,CAAC;AACD;AAAA,QACD;AACA,YAAI;AACH,qBAAW,OAAO,QAAQ,YAAY;AACrC,eAAG,KAAK,EAAE,IAAI,CAAC;AAAA,UAChB;AACA,uBAAa,EAAE,IAAI,QAAQ,IAAI,MAAM,UAAU,CAAC;AAAA,QACjD,SAAS,OAAO;AACf,uBAAa;AAAA,YACZ,IAAI,QAAQ;AAAA,YACZ,MAAM;AAAA,YACN,SAAU,MAAgB;AAAA,YAC1B,MAAM;AAAA,UACP,CAAC;AAAA,QACF;AACA;AAAA,MACD,KAAK;AAGJ,qBAAa;AAAA,UACZ,IAAI,QAAQ;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QACP,CAAC;AACD;AAAA,MACD,KAAK;AACJ,qBAAa,QAAQ,IAAI,QAAQ,IAAI;AACrC;AAAA,MACD;AACC,qBAAa;AAAA,UACZ,IAAK,QAA0B;AAAA,UAC/B,MAAM;AAAA,UACN,SAAS;AAAA,UACT,MAAM;AAAA,QACP,CAAC;AAAA,IACH;AAAA,EACD,SAAS,OAAO;AACf,iBAAa;AAAA,MACZ,IAAI,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,SAAU,MAAgB;AAAA,MAC1B,MAAM;AAAA,IACP,CAAC;AAAA,EACF;AACD;AAGA,KAAK,YAAY,CAAC,UAAuC;AACxD,gBAAc,MAAM,IAAI;AACzB;","names":[]}
1
+ {"version":3,"sources":["../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM.\n *\n * This file is intended to run inside a Web Worker in browsers.\n * It loads @sqlite.org/sqlite-wasm, initializes SQLite with OPFS persistence\n * (falling back to in-memory if unavailable), and processes messages from\n * the main thread via the WorkerRequest/WorkerResponse protocol.\n *\n * This script cannot be tested in Node.js — it is validated in E2E browser tests.\n */\n\nimport type { WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\ninterface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): void\n\tclose(): void\n\tdeserialize?: (data: Uint8Array) => void\n}\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nlet db: SqliteDb | null = null\nlet sqlite3Api: unknown = null\n\nfunction sendResponse(response: WorkerResponse): void {\n\tself.postMessage(response)\n}\n\nfunction handleExecute(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tdb.exec({ sql, bind: params })\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' })\n\t}\n}\n\nfunction handleQuery(id: number, sql: string, params?: unknown[]): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\ttry {\n\t\tconst rows: Record<string, unknown>[] = []\n\t\tdb.exec({\n\t\t\tsql,\n\t\t\tbind: params,\n\t\t\trowMode: 'object',\n\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\trows.push({ ...row })\n\t\t\t},\n\t\t})\n\t\tsendResponse({ id, type: 'success', data: rows })\n\t} catch (error) {\n\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' })\n\t}\n}\n\nasync function handleOpen(id: number, ddlStatements: string[]): Promise<void> {\n\ttry {\n\t\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\n\t\t// In production builds, Vite hashes asset filenames (e.g. sqlite3-[hash].wasm).\n\t\t// The sqlite3 module's default locateFile resolves the unhashed name, causing a 404.\n\t\t// Templates set __KORA_SQLITE_WASM_URL via `import wasmUrl from '...sqlite3.wasm?url'`\n\t\t// so we can override locateFile with the correct hashed URL.\n\t\tconst wasmUrl = (globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL as\n\t\t\t| string\n\t\t\t| undefined\n\t\tconst initOptions = wasmUrl\n\t\t\t? {\n\t\t\t\t\tlocateFile: (file: string) => {\n\t\t\t\t\t\tif (file.endsWith('.wasm')) return wasmUrl\n\t\t\t\t\t\treturn file\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: undefined\n\n\t\t// sqlite3InitModule accepts Emscripten Module overrides but the types don't reflect this\n\t\tconst initFn = sqlite3InitModule as unknown as (opts?: Record<string, unknown>) => Promise<unknown>\n\t\tconst sqlite3 = (await initFn(initOptions)) as Awaited<ReturnType<typeof sqlite3InitModule>>\n\t\tsqlite3Api = sqlite3\n\n\t\t// Try OPFS persistence first\n\t\tlet useOpfs = false\n\t\tif (sqlite3.installOpfsSAHPoolVfs) {\n\t\t\ttry {\n\t\t\t\tconst pool = await sqlite3.installOpfsSAHPoolVfs({ name: 'kora-opfs' })\n\t\t\t\tdb = new pool.OpfsSAHPoolDb('kora.db')\n\t\t\t\tuseOpfs = true\n\t\t\t} catch {\n\t\t\t\t// OPFS unavailable, fall back to in-memory\n\t\t\t\tconsole.warn('[kora] OPFS unavailable, falling back to in-memory SQLite')\n\t\t\t}\n\t\t}\n\n\t\tif (!useOpfs) {\n\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t}\n\n\t\t// Set pragmas\n\t\tdb?.exec({ sql: 'PRAGMA journal_mode = WAL' })\n\t\tdb?.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\n\t\t// Execute DDL statements\n\t\tfor (const sql of ddlStatements) {\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\t// Safe ALTER TABLE — ignore \"duplicate column name\" errors for existing columns\n\t\t\t\ttry {\n\t\t\t\t\tdb?.exec({ sql: sql.replace('--kora:safe-alter\\n', '') })\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst msg = (e as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow e\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdb?.exec({ sql })\n\t\t\t}\n\t\t}\n\n\t\tsendResponse({ id, type: 'success' })\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'INIT_ERROR',\n\t\t})\n\t}\n}\n\nfunction handleClose(id: number): void {\n\tif (db) {\n\t\tdb.close()\n\t\tdb = null\n\t}\n\tsendResponse({ id, type: 'success' })\n}\n\nfunction handleImport(id: number, data: Uint8Array): void {\n\tif (!db) {\n\t\tsendResponse({ id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' })\n\t\treturn\n\t}\n\n\tconst dbWithDeserialize = db as SqliteDb & { deserialize?: (bytes: Uint8Array) => void }\n\tif (typeof dbWithDeserialize.deserialize === 'function') {\n\t\ttry {\n\t\t\tdbWithDeserialize.deserialize(data)\n\t\t\tsendResponse({ id, type: 'success' })\n\t\t\treturn\n\t\t} catch (error) {\n\t\t\tsendResponse({ id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' })\n\t\t\treturn\n\t\t}\n\t}\n\n\tconst sqlite3 = sqlite3Api as\n\t\t| {\n\t\t\t\too1?: { DB?: new (...args: unknown[]) => SqliteDb }\n\t\t\t\tcapi?: { sqlite3_deserialize?: unknown }\n\t\t\t}\n\t\t| null\n\n\tif (!sqlite3 || typeof sqlite3.capi?.sqlite3_deserialize === 'undefined') {\n\t\tsendResponse({\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t})\n\t\treturn\n\t}\n\n\tsendResponse({\n\t\tid,\n\t\ttype: 'error',\n\t\tmessage: 'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t})\n}\n\nfunction handleMessage(request: WorkerRequest): void {\n\ttry {\n\t\tswitch (request.type) {\n\t\t\tcase 'open':\n\t\t\t\t// open is async due to WASM loading\n\t\t\t\thandleOpen(request.id, request.ddlStatements)\n\t\t\t\treturn\n\t\t\tcase 'close':\n\t\t\t\thandleClose(request.id)\n\t\t\t\treturn\n\t\t\tcase 'execute':\n\t\t\t\thandleExecute(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'query':\n\t\t\t\thandleQuery(request.id, request.sql, request.params)\n\t\t\t\treturn\n\t\t\tcase 'begin':\n\t\t\t\thandleExecute(request.id, 'BEGIN')\n\t\t\t\treturn\n\t\t\tcase 'commit':\n\t\t\t\thandleExecute(request.id, 'COMMIT')\n\t\t\t\treturn\n\t\t\tcase 'rollback':\n\t\t\t\thandleExecute(request.id, 'ROLLBACK')\n\t\t\t\treturn\n\t\t\tcase 'migrate':\n\t\t\t\tif (!db) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Database is not open',\n\t\t\t\t\t\tcode: 'DB_NOT_OPEN',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tfor (const sql of request.statements) {\n\t\t\t\t\t\tdb.exec({ sql })\n\t\t\t\t\t}\n\t\t\t\t\tsendResponse({ id: request.id, type: 'success' })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tsendResponse({\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: (error as Error).message,\n\t\t\t\t\t\tcode: 'MIGRATE_ERROR',\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\tcase 'export':\n\t\t\t\t// Export is not trivially supported via the oo1 API in the browser.\n\t\t\t\t// In a real implementation, we'd use the C API's sqlite3_serialize.\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'import':\n\t\t\t\thandleImport(request.id, request.data)\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tsendResponse({\n\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t})\n\t\t}\n\t} catch (error) {\n\t\tsendResponse({\n\t\t\tid: request.id,\n\t\t\ttype: 'error',\n\t\t\tmessage: (error as Error).message,\n\t\t\tcode: 'WORKER_ERROR',\n\t\t})\n\t}\n}\n\n// Listen for messages from the main thread\nself.onmessage = (event: MessageEvent<WorkerRequest>) => {\n\thandleMessage(event.data)\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@korajs/store",
3
- "version": "0.1.2",
4
- "description": "Kora.js store package",
3
+ "version": "0.3.0",
4
+ "description": "Local storage engine for Kora.js with SQLite WASM, IndexedDB, and reactive queries",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -63,7 +63,7 @@
63
63
  ],
64
64
  "dependencies": {
65
65
  "yjs": "^13.6.30",
66
- "@korajs/core": "0.1.2"
66
+ "@korajs/core": "0.3.0"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@sqlite.org/sqlite-wasm": ">=3.51.0-build1"