@korajs/store 0.3.2 → 0.5.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.
Files changed (73) hide show
  1. package/dist/adapters/better-sqlite3.cjs +1 -1
  2. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  3. package/dist/adapters/better-sqlite3.d.cts +1 -1
  4. package/dist/adapters/better-sqlite3.d.ts +1 -1
  5. package/dist/adapters/better-sqlite3.js +1 -1
  6. package/dist/adapters/better-sqlite3.js.map +1 -1
  7. package/dist/adapters/indexeddb.cjs +413 -18
  8. package/dist/adapters/indexeddb.cjs.map +1 -1
  9. package/dist/adapters/indexeddb.d.cts +23 -5
  10. package/dist/adapters/indexeddb.d.ts +23 -5
  11. package/dist/adapters/indexeddb.js +166 -19
  12. package/dist/adapters/indexeddb.js.map +1 -1
  13. package/dist/adapters/sqlite-wasm-shared-host.cjs +68 -0
  14. package/dist/adapters/sqlite-wasm-shared-host.cjs.map +1 -0
  15. package/dist/adapters/sqlite-wasm-shared-host.d.cts +2 -0
  16. package/dist/adapters/sqlite-wasm-shared-host.d.ts +2 -0
  17. package/dist/adapters/sqlite-wasm-shared-host.js +66 -0
  18. package/dist/adapters/sqlite-wasm-shared-host.js.map +1 -0
  19. package/dist/adapters/sqlite-wasm-worker.cjs +34 -5
  20. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  21. package/dist/adapters/sqlite-wasm-worker.js +34 -5
  22. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  23. package/dist/adapters/sqlite-wasm.cjs +249 -2
  24. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  25. package/dist/adapters/sqlite-wasm.d.cts +15 -3
  26. package/dist/adapters/sqlite-wasm.d.ts +15 -3
  27. package/dist/adapters/sqlite-wasm.js +2 -2
  28. package/dist/audit-trace-store-AM4EWQNZ.js +15 -0
  29. package/dist/backup-JN3LB4YT.js +14 -0
  30. package/dist/backup-JN3LB4YT.js.map +1 -0
  31. package/dist/chunk-56LL4XP6.js +205 -0
  32. package/dist/chunk-56LL4XP6.js.map +1 -0
  33. package/dist/chunk-6AHYGQQT.js +425 -0
  34. package/dist/chunk-6AHYGQQT.js.map +1 -0
  35. package/dist/chunk-7BPETY5Y.js +570 -0
  36. package/dist/chunk-7BPETY5Y.js.map +1 -0
  37. package/dist/chunk-AGN3CIPB.js +188 -0
  38. package/dist/chunk-AGN3CIPB.js.map +1 -0
  39. package/dist/chunk-EZSTD2XC.js +380 -0
  40. package/dist/chunk-EZSTD2XC.js.map +1 -0
  41. package/dist/chunk-MYSJN3DF.js +117 -0
  42. package/dist/chunk-MYSJN3DF.js.map +1 -0
  43. package/dist/{chunk-DXKLAQ6P.js → chunk-XO4LMPAH.js} +1 -1
  44. package/dist/chunk-XO4LMPAH.js.map +1 -0
  45. package/dist/export-audit-5GV6VRDZ.js +15 -0
  46. package/dist/export-audit-5GV6VRDZ.js.map +1 -0
  47. package/dist/index.cjs +3351 -510
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +822 -50
  50. package/dist/index.d.ts +822 -50
  51. package/dist/index.js +1976 -558
  52. package/dist/index.js.map +1 -1
  53. package/dist/internal.cjs +789 -0
  54. package/dist/internal.cjs.map +1 -0
  55. package/dist/internal.d.cts +154 -0
  56. package/dist/internal.d.ts +154 -0
  57. package/dist/internal.js +48 -0
  58. package/dist/internal.js.map +1 -0
  59. package/dist/{sqlite-wasm-channel-46AOWNPM.js → sqlite-wasm-channel-HDDW6UBH.js} +2 -2
  60. package/dist/sqlite-wasm-channel-HDDW6UBH.js.map +1 -0
  61. package/dist/{sqlite-wasm-channel-Lakjuk2E.d.cts → sqlite-wasm-channel-I9y_jObg.d.cts} +1 -0
  62. package/dist/{sqlite-wasm-channel-Lakjuk2E.d.ts → sqlite-wasm-channel-I9y_jObg.d.ts} +1 -0
  63. package/dist/types-BMyHmwvn.d.ts +163 -0
  64. package/dist/types-CTr00g_d.d.cts +163 -0
  65. package/dist/types-DepnRDk_.d.cts +212 -0
  66. package/dist/types-DepnRDk_.d.ts +212 -0
  67. package/package.json +23 -2
  68. package/dist/chunk-DXKLAQ6P.js.map +0 -1
  69. package/dist/chunk-ZP5AXQ3Z.js +0 -179
  70. package/dist/chunk-ZP5AXQ3Z.js.map +0 -1
  71. package/dist/types-DF-KDSK1.d.cts +0 -106
  72. package/dist/types-DF-KDSK1.d.ts +0 -106
  73. /package/dist/{sqlite-wasm-channel-46AOWNPM.js.map → audit-trace-store-AM4EWQNZ.js.map} +0 -0
@@ -0,0 +1,212 @@
1
+ import { SchemaDefinition, Operation, KoraEventEmitter } from '@korajs/core';
2
+
3
+ /**
4
+ * Transaction interface for executing multiple operations atomically.
5
+ */
6
+ interface Transaction {
7
+ execute(sql: string, params?: unknown[]): Promise<void>;
8
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
9
+ }
10
+ /**
11
+ * Migration plan containing SQL statements and optional data transforms.
12
+ */
13
+ interface MigrationPlan {
14
+ statements: string[];
15
+ transforms?: Array<(row: Record<string, unknown>) => Record<string, unknown>>;
16
+ }
17
+ /**
18
+ * Storage adapter interface. All storage backends must implement this.
19
+ * Operations are async to support both sync (better-sqlite3) and async (IndexedDB, WASM) backends.
20
+ */
21
+ interface StorageAdapter {
22
+ /** Open or create the database */
23
+ open(schema: SchemaDefinition): Promise<void>;
24
+ /** Close the database and release resources */
25
+ close(): Promise<void>;
26
+ /** Execute a write query (INSERT, UPDATE, DELETE) within a transaction */
27
+ execute(sql: string, params?: unknown[]): Promise<void>;
28
+ /** Execute a read query (SELECT) */
29
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
30
+ /** Execute multiple operations atomically */
31
+ transaction(fn: (tx: Transaction) => Promise<void>): Promise<void>;
32
+ /** Apply a schema migration */
33
+ migrate(from: number, to: number, migration: MigrationPlan): Promise<void>;
34
+ }
35
+ /**
36
+ * Buffered SQL + operation produced during a transaction (before commit).
37
+ */
38
+ interface TransactionBufferedEntry {
39
+ operation: Operation;
40
+ commands: Array<{
41
+ sql: string;
42
+ params: unknown[];
43
+ }>;
44
+ collection: string;
45
+ }
46
+ /**
47
+ * Batch passed to {@link LocalMutationHandler.commitTransaction} on commit.
48
+ */
49
+ interface TransactionCommitBatch {
50
+ entries: TransactionBufferedEntry[];
51
+ transactionId: string;
52
+ mutationName?: string;
53
+ }
54
+ interface TransactionCommitResult {
55
+ operations: Operation[];
56
+ affectedCollections: Set<string>;
57
+ }
58
+ interface LocalMutationHandler {
59
+ insert(collection: string, data: Record<string, unknown>): Promise<CollectionRecord>;
60
+ update(collection: string, id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
61
+ delete(collection: string, id: string): Promise<void>;
62
+ /**
63
+ * Commit a buffered transaction through the unified apply pipeline.
64
+ * When omitted, the store uses the built-in SQL commit path.
65
+ */
66
+ commitTransaction?(batch: TransactionCommitBatch): Promise<TransactionCommitResult>;
67
+ }
68
+ type StoreIsolation = 'shared' | 'per-tab';
69
+ interface StoreConfig {
70
+ schema: SchemaDefinition;
71
+ adapter: StorageAdapter;
72
+ /** Database name used for per-tab node id keys. Defaults to 'kora-db'. */
73
+ dbName?: string;
74
+ /**
75
+ * `shared` (default): one node id per database in `_kora_meta`.
76
+ * `per-tab`: unique node id per browser tab via sessionStorage.
77
+ */
78
+ isolation?: StoreIsolation;
79
+ /** Optional node ID. If omitted, one is generated or loaded from the database. */
80
+ nodeId?: string;
81
+ /** Optional event emitter. When provided, local mutations emit 'operation:created' events. */
82
+ emitter?: KoraEventEmitter;
83
+ /** Routes local mutations through a unified apply pipeline when provided. */
84
+ localMutationHandler?: LocalMutationHandler;
85
+ /** Called when a reactive query subscription is registered (for sync query subsets). */
86
+ onQuerySubscribed?: (descriptor: QueryDescriptor) => () => void;
87
+ }
88
+ /**
89
+ * Operators for where clause conditions.
90
+ */
91
+ interface WhereOperators {
92
+ $eq?: unknown;
93
+ $ne?: unknown;
94
+ $gt?: number | string;
95
+ $gte?: number | string;
96
+ $lt?: number | string;
97
+ $lte?: number | string;
98
+ $in?: unknown[];
99
+ }
100
+ /**
101
+ * Where clause: field name to value (shorthand for $eq) or WhereOperators.
102
+ */
103
+ type WhereClause = Record<string, unknown | WhereOperators>;
104
+ /**
105
+ * Order direction for sorting query results.
106
+ */
107
+ type OrderByDirection = 'asc' | 'desc';
108
+ /**
109
+ * Order-by clause: field name and optional direction.
110
+ */
111
+ interface OrderByClause {
112
+ field: string;
113
+ direction: OrderByDirection;
114
+ }
115
+ /**
116
+ * Internal representation of a query to be compiled to SQL.
117
+ */
118
+ interface QueryDescriptor {
119
+ collection: string;
120
+ where: WhereClause;
121
+ orderBy: OrderByClause[];
122
+ limit?: number;
123
+ offset?: number;
124
+ /** Relation names to include in results (for relational queries). */
125
+ include?: string[];
126
+ /** Resolved collection names for included relations (for subscription tracking). */
127
+ includeCollections?: string[];
128
+ }
129
+ /**
130
+ * Callback for reactive subscriptions. Receives the current result set.
131
+ */
132
+ type SubscriptionCallback<T> = (results: T[]) => void;
133
+ /**
134
+ * Serialized row in the operations log table.
135
+ */
136
+ interface OperationRow {
137
+ id: string;
138
+ node_id: string;
139
+ type: string;
140
+ record_id: string;
141
+ data: string | null;
142
+ previous_data: string | null;
143
+ timestamp: string;
144
+ sequence_number: number;
145
+ causal_deps: string;
146
+ schema_version: number;
147
+ }
148
+ /**
149
+ * Record type returned from collection queries.
150
+ * Includes the id and mapped metadata fields.
151
+ */
152
+ interface CollectionRecord {
153
+ id: string;
154
+ createdAt: number;
155
+ updatedAt: number;
156
+ [key: string]: unknown;
157
+ }
158
+
159
+ /**
160
+ * Internal row shape returned from SQL queries on collection tables.
161
+ */
162
+ interface RawCollectionRow {
163
+ id: string;
164
+ _created_at: number;
165
+ _updated_at: number;
166
+ _version?: string;
167
+ _deleted: number;
168
+ [key: string]: unknown;
169
+ }
170
+ /**
171
+ * A registered subscription tracked by the SubscriptionManager.
172
+ */
173
+ interface Subscription<T = CollectionRecord> {
174
+ id: string;
175
+ descriptor: QueryDescriptor;
176
+ callback: SubscriptionCallback<T>;
177
+ executeFn: () => Promise<T[]>;
178
+ lastResults: T[];
179
+ }
180
+
181
+ /**
182
+ * Options for applying a remote operation to materialized storage.
183
+ */
184
+ interface ApplyRemoteOptions {
185
+ /** When true, a winning remote update clears soft-delete on the row. */
186
+ reactivateIfDeleted?: boolean;
187
+ }
188
+ /**
189
+ * Snapshot of a materialized row, including soft-deleted records.
190
+ */
191
+ interface MaterializedRowSnapshot {
192
+ /** Deserialized application record (excludes tombstone metadata). */
193
+ record: CollectionRecord;
194
+ /** Whether the row is soft-deleted (_deleted = 1). */
195
+ deleted: boolean;
196
+ }
197
+ /**
198
+ * Metadata row from _kora_meta table.
199
+ */
200
+ interface MetaRow {
201
+ key: string;
202
+ value: string;
203
+ }
204
+ /**
205
+ * Version vector row from _kora_version_vector table.
206
+ */
207
+ interface VersionVectorRow {
208
+ node_id: string;
209
+ sequence_number: number;
210
+ }
211
+
212
+ export type { ApplyRemoteOptions as A, CollectionRecord as C, LocalMutationHandler as L, MigrationPlan as M, OperationRow as O, QueryDescriptor as Q, RawCollectionRow as R, StorageAdapter as S, Transaction as T, VersionVectorRow as V, WhereClause as W, MetaRow as a, Subscription as b, SubscriptionCallback as c, OrderByDirection as d, StoreConfig as e, MaterializedRowSnapshot as f, OrderByClause as g, StoreIsolation as h, TransactionBufferedEntry as i, TransactionCommitBatch as j, TransactionCommitResult as k, WhereOperators as l };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korajs/store",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
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",
@@ -47,6 +47,16 @@
47
47
  "default": "./dist/adapters/sqlite-wasm-worker.cjs"
48
48
  }
49
49
  },
50
+ "./sqlite-wasm/shared-host": {
51
+ "import": {
52
+ "types": "./dist/adapters/sqlite-wasm-shared-host.d.ts",
53
+ "default": "./dist/adapters/sqlite-wasm-shared-host.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/adapters/sqlite-wasm-shared-host.d.cts",
57
+ "default": "./dist/adapters/sqlite-wasm-shared-host.cjs"
58
+ }
59
+ },
50
60
  "./indexeddb": {
51
61
  "import": {
52
62
  "types": "./dist/adapters/indexeddb.d.ts",
@@ -56,6 +66,16 @@
56
66
  "types": "./dist/adapters/indexeddb.d.cts",
57
67
  "default": "./dist/adapters/indexeddb.cjs"
58
68
  }
69
+ },
70
+ "./internal": {
71
+ "import": {
72
+ "types": "./dist/internal.d.ts",
73
+ "default": "./dist/internal.js"
74
+ },
75
+ "require": {
76
+ "types": "./dist/internal.d.cts",
77
+ "default": "./dist/internal.cjs"
78
+ }
59
79
  }
60
80
  },
61
81
  "files": [
@@ -63,7 +83,7 @@
63
83
  ],
64
84
  "dependencies": {
65
85
  "yjs": "^13.6.30",
66
- "@korajs/core": "0.3.2"
86
+ "@korajs/core": "0.5.0"
67
87
  },
68
88
  "peerDependencies": {
69
89
  "@sqlite.org/sqlite-wasm": ">=3.51.0-build1"
@@ -87,6 +107,7 @@
87
107
  "build": "tsup",
88
108
  "dev": "tsup --watch",
89
109
  "test": "vitest run",
110
+ "test:benchmarks": "vitest run --config vitest.benchmark.config.ts",
90
111
  "test:watch": "vitest",
91
112
  "typecheck": "tsc --noEmit",
92
113
  "lint": "biome check .",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/sqlite-wasm-channel.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\nimport { WorkerTimeoutError } from '../errors'\n\n// === Message Protocol ===\n\n/**\n * Request message sent from the main thread to the SQLite WASM worker.\n * Each request carries a unique `id` for response correlation.\n */\nexport type WorkerRequest =\n\t| { id: number; type: 'open'; ddlStatements: string[] }\n\t| { id: number; type: 'close' }\n\t| { id: number; type: 'execute'; sql: string; params?: unknown[] }\n\t| { id: number; type: 'query'; sql: string; params?: unknown[] }\n\t| { id: number; type: 'begin' }\n\t| { id: number; type: 'commit' }\n\t| { id: number; type: 'rollback' }\n\t| { id: number; type: 'migrate'; from: number; to: number; statements: string[] }\n\t| { id: number; type: 'export' }\n\t| { id: number; type: 'import'; data: Uint8Array }\n\n/**\n * Response message sent from the worker back to the main thread.\n * Matches the request `id` for correlation.\n */\nexport type WorkerResponse =\n\t| { id: number; type: 'success'; data?: unknown }\n\t| { id: number; type: 'error'; message: string; code: string; context?: Record<string, unknown> }\n\n// === WorkerBridge Interface ===\n\n/**\n * Abstraction over the communication channel with the SQLite WASM worker.\n * In browsers, this is backed by a real Web Worker via MessagePort.\n * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.\n */\nexport interface WorkerBridge {\n\t/** Send a request to the worker and wait for a response. */\n\tsend(request: WorkerRequest): Promise<WorkerResponse>\n\n\t/** Terminate the worker. Safe to call multiple times. */\n\tterminate(): void\n}\n\n// === Mutex ===\n\n/**\n * Async mutex for serializing transaction access across the async worker boundary.\n * Only one transaction may be active at a time.\n */\nexport class Mutex {\n\tprivate locked = false\n\tprivate waiters: Array<() => void> = []\n\n\t/**\n\t * Acquire the mutex. Returns a release function.\n\t * If the mutex is already held, the caller waits until it's released.\n\t */\n\tasync acquire(): Promise<() => void> {\n\t\tif (!this.locked) {\n\t\t\tthis.locked = true\n\t\t\treturn this.createRelease()\n\t\t}\n\n\t\treturn new Promise<() => void>((resolve) => {\n\t\t\tthis.waiters.push(() => {\n\t\t\t\tresolve(this.createRelease())\n\t\t\t})\n\t\t})\n\t}\n\n\tprivate createRelease(): () => void {\n\t\tlet released = false\n\t\treturn () => {\n\t\t\tif (released) return\n\t\t\treleased = true\n\t\t\tconst next = this.waiters.shift()\n\t\t\tif (next) {\n\t\t\t\tnext()\n\t\t\t} else {\n\t\t\t\tthis.locked = false\n\t\t\t}\n\t\t}\n\t}\n}\n\n// === WebWorkerBridge ===\n\n/**\n * WorkerBridge implementation for browser environments.\n * Communicates with an actual Web Worker running SQLite WASM.\n */\nexport class WebWorkerBridge implements WorkerBridge {\n\tprivate worker: Worker\n\tprivate pending = new Map<\n\t\tnumber,\n\t\t{ resolve: (r: WorkerResponse) => void; reject: (e: Error) => void }\n\t>()\n\tprivate nextId = 1\n\tprivate terminated = false\n\tprivate timeoutMs: number\n\n\t/**\n\t * @param workerUrl - URL to the sqlite-wasm-worker script\n\t * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)\n\t */\n\tconstructor(workerUrl: string | URL, timeoutMs = 30000) {\n\t\tthis.timeoutMs = timeoutMs\n\t\tthis.worker = new Worker(workerUrl, { type: 'module' })\n\t\tthis.worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\n\t\t\tconst response = event.data\n\t\t\tconst entry = this.pending.get(response.id)\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(response.id)\n\t\t\t\tentry.resolve(response)\n\t\t\t}\n\t\t}\n\t\tthis.worker.onerror = (event) => {\n\t\t\t// Reject all pending requests on worker error\n\t\t\tconst error = new Error(`Worker error: ${event.message}`)\n\t\t\tfor (const [id, entry] of this.pending) {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\tentry.reject(error)\n\t\t\t}\n\t\t}\n\t}\n\n\tasync send(request: WorkerRequest): Promise<WorkerResponse> {\n\t\tif (this.terminated) {\n\t\t\treturn {\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: 'Worker has been terminated',\n\t\t\t\tcode: 'WORKER_TERMINATED',\n\t\t\t}\n\t\t}\n\n\t\tconst id = this.nextId++\n\t\tconst req = { ...request, id }\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\treject(new WorkerTimeoutError(req.type, this.timeoutMs))\n\t\t\t}, this.timeoutMs)\n\n\t\t\tthis.pending.set(id, {\n\t\t\t\tresolve: (response) => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tresolve(response)\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(error)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tthis.worker.postMessage(req)\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) return\n\t\tthis.terminated = true\n\t\tthis.worker.terminate()\n\t\t// Reject any pending requests\n\t\tfor (const [id, entry] of this.pending) {\n\t\t\tthis.pending.delete(id)\n\t\t\tentry.reject(new Error('Worker terminated'))\n\t\t}\n\t}\n}\n"],"mappings":";;;;;AAkDO,IAAM,QAAN,MAAY;AAAA,EACV,SAAS;AAAA,EACT,UAA6B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtC,MAAM,UAA+B;AACpC,QAAI,CAAC,KAAK,QAAQ;AACjB,WAAK,SAAS;AACd,aAAO,KAAK,cAAc;AAAA,IAC3B;AAEA,WAAO,IAAI,QAAoB,CAAC,YAAY;AAC3C,WAAK,QAAQ,KAAK,MAAM;AACvB,gBAAQ,KAAK,cAAc,CAAC;AAAA,MAC7B,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEQ,gBAA4B;AACnC,QAAI,WAAW;AACf,WAAO,MAAM;AACZ,UAAI,SAAU;AACd,iBAAW;AACX,YAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,UAAI,MAAM;AACT,aAAK;AAAA,MACN,OAAO;AACN,aAAK,SAAS;AAAA,MACf;AAAA,IACD;AAAA,EACD;AACD;AAQO,IAAM,kBAAN,MAA8C;AAAA,EAC5C;AAAA,EACA,UAAU,oBAAI,IAGpB;AAAA,EACM,SAAS;AAAA,EACT,aAAa;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,YAAY,WAAyB,YAAY,KAAO;AACvD,SAAK,YAAY;AACjB,SAAK,SAAS,IAAI,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC;AACtD,SAAK,OAAO,YAAY,CAAC,UAAwC;AAChE,YAAM,WAAW,MAAM;AACvB,YAAM,QAAQ,KAAK,QAAQ,IAAI,SAAS,EAAE;AAC1C,UAAI,OAAO;AACV,aAAK,QAAQ,OAAO,SAAS,EAAE;AAC/B,cAAM,QAAQ,QAAQ;AAAA,MACvB;AAAA,IACD;AACA,SAAK,OAAO,UAAU,CAAC,UAAU;AAEhC,YAAM,QAAQ,IAAI,MAAM,iBAAiB,MAAM,OAAO,EAAE;AACxD,iBAAW,CAAC,IAAI,KAAK,KAAK,KAAK,SAAS;AACvC,aAAK,QAAQ,OAAO,EAAE;AACtB,cAAM,OAAO,KAAK;AAAA,MACnB;AAAA,IACD;AAAA,EACD;AAAA,EAEA,MAAM,KAAK,SAAiD;AAC3D,QAAI,KAAK,YAAY;AACpB,aAAO;AAAA,QACN,IAAI,QAAQ;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,MACP;AAAA,IACD;AAEA,UAAM,KAAK,KAAK;AAChB,UAAM,MAAM,EAAE,GAAG,SAAS,GAAG;AAE7B,WAAO,IAAI,QAAwB,CAAC,SAAS,WAAW;AACvD,YAAM,QAAQ,WAAW,MAAM;AAC9B,aAAK,QAAQ,OAAO,EAAE;AACtB,eAAO,IAAI,mBAAmB,IAAI,MAAM,KAAK,SAAS,CAAC;AAAA,MACxD,GAAG,KAAK,SAAS;AAEjB,WAAK,QAAQ,IAAI,IAAI;AAAA,QACpB,SAAS,CAAC,aAAa;AACtB,uBAAa,KAAK;AAClB,kBAAQ,QAAQ;AAAA,QACjB;AAAA,QACA,QAAQ,CAAC,UAAU;AAClB,uBAAa,KAAK;AAClB,iBAAO,KAAK;AAAA,QACb;AAAA,MACD,CAAC;AAED,WAAK,OAAO,YAAY,GAAG;AAAA,IAC5B,CAAC;AAAA,EACF;AAAA,EAEA,YAAkB;AACjB,QAAI,KAAK,WAAY;AACrB,SAAK,aAAa;AAClB,SAAK,OAAO,UAAU;AAEtB,eAAW,CAAC,IAAI,KAAK,KAAK,KAAK,SAAS;AACvC,WAAK,QAAQ,OAAO,EAAE;AACtB,YAAM,OAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,IAC5C;AAAA,EACD;AACD;","names":[]}
@@ -1,179 +0,0 @@
1
- import {
2
- Mutex
3
- } from "./chunk-DXKLAQ6P.js";
4
- import {
5
- AdapterError,
6
- StoreNotOpenError
7
- } from "./chunk-LAWV6CFH.js";
8
-
9
- // src/adapters/sqlite-wasm-adapter.ts
10
- import { generateFullDDL } from "@korajs/core";
11
- var SqliteWasmAdapter = class {
12
- bridge = null;
13
- opened = false;
14
- mutex = new Mutex();
15
- injectedBridge;
16
- workerUrl;
17
- dbName;
18
- constructor(options = {}) {
19
- this.injectedBridge = options.bridge;
20
- this.workerUrl = options.workerUrl;
21
- this.dbName = options.dbName ?? "kora-db";
22
- }
23
- async open(schema) {
24
- if (this.opened) return;
25
- if (this.injectedBridge) {
26
- this.bridge = this.injectedBridge;
27
- } else if (this.workerUrl) {
28
- const { WebWorkerBridge } = await import("./sqlite-wasm-channel-46AOWNPM.js");
29
- this.bridge = new WebWorkerBridge(this.workerUrl);
30
- } else {
31
- throw new AdapterError(
32
- 'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: "/worker.js" } for browsers.'
33
- );
34
- }
35
- const ddlStatements = generateFullDDL(schema);
36
- const response = await this.sendRequest({ id: 0, type: "open", ddlStatements });
37
- if (response.type === "error") {
38
- throw new AdapterError(`Failed to open database: ${response.message}`, {
39
- code: response.code,
40
- dbName: this.dbName
41
- });
42
- }
43
- this.opened = true;
44
- }
45
- async close() {
46
- if (!this.bridge) return;
47
- try {
48
- await this.sendRequest({ id: 0, type: "close" });
49
- } finally {
50
- this.bridge.terminate();
51
- this.bridge = null;
52
- this.opened = false;
53
- }
54
- }
55
- async execute(sql, params) {
56
- this.guardOpen();
57
- const response = await this.sendRequest({ id: 0, type: "execute", sql, params });
58
- if (response.type === "error") {
59
- throw new AdapterError(`Execute failed: ${response.message}`, { sql, params });
60
- }
61
- }
62
- async query(sql, params) {
63
- this.guardOpen();
64
- const response = await this.sendRequest({ id: 0, type: "query", sql, params });
65
- if (response.type === "error") {
66
- throw new AdapterError(`Query failed: ${response.message}`, { sql, params });
67
- }
68
- return response.data ?? [];
69
- }
70
- async transaction(fn) {
71
- this.guardOpen();
72
- const release = await this.mutex.acquire();
73
- try {
74
- await this.sendChecked({ id: 0, type: "begin" }, "BEGIN transaction");
75
- const tx = {
76
- execute: async (sql, params) => {
77
- const response = await this.sendRequest({ id: 0, type: "execute", sql, params });
78
- if (response.type === "error") {
79
- throw new AdapterError(`Transaction execute failed: ${response.message}`, {
80
- sql,
81
- params
82
- });
83
- }
84
- },
85
- query: async (sql, params) => {
86
- const response = await this.sendRequest({ id: 0, type: "query", sql, params });
87
- if (response.type === "error") {
88
- throw new AdapterError(`Transaction query failed: ${response.message}`, { sql, params });
89
- }
90
- return response.data ?? [];
91
- }
92
- };
93
- await fn(tx);
94
- await this.sendChecked({ id: 0, type: "commit" }, "COMMIT transaction");
95
- } catch (error) {
96
- try {
97
- await this.sendRequest({ id: 0, type: "rollback" });
98
- } catch {
99
- }
100
- throw error;
101
- } finally {
102
- release();
103
- }
104
- }
105
- async migrate(from, to, migration) {
106
- this.guardOpen();
107
- const release = await this.mutex.acquire();
108
- try {
109
- await this.sendChecked({ id: 0, type: "begin" }, "BEGIN migration");
110
- for (const sql of migration.statements) {
111
- const response = await this.sendRequest({ id: 0, type: "execute", sql });
112
- if (response.type === "error") {
113
- throw new AdapterError(`Migration from v${from} to v${to} failed: ${response.message}`, {
114
- from,
115
- to
116
- });
117
- }
118
- }
119
- await this.sendChecked({ id: 0, type: "commit" }, "COMMIT migration");
120
- } catch (error) {
121
- try {
122
- await this.sendRequest({ id: 0, type: "rollback" });
123
- } catch {
124
- }
125
- if (error instanceof AdapterError) throw error;
126
- throw new AdapterError(
127
- `Migration from v${from} to v${to} failed: ${error.message}`,
128
- { from, to }
129
- );
130
- } finally {
131
- release();
132
- }
133
- }
134
- /**
135
- * Export the database as a Uint8Array (for IndexedDB persistence).
136
- * Only available when the database is open.
137
- */
138
- async exportDatabase() {
139
- this.guardOpen();
140
- const response = await this.sendRequest({ id: 0, type: "export" });
141
- if (response.type === "error") {
142
- throw new AdapterError(`Export failed: ${response.message}`);
143
- }
144
- return response.data;
145
- }
146
- /**
147
- * Import a serialized database snapshot.
148
- */
149
- async importDatabase(data) {
150
- this.guardOpen();
151
- const response = await this.sendRequest({ id: 0, type: "import", data });
152
- if (response.type === "error") {
153
- throw new AdapterError(`Import failed: ${response.message}`);
154
- }
155
- }
156
- guardOpen() {
157
- if (!this.opened || !this.bridge) {
158
- throw new StoreNotOpenError();
159
- }
160
- }
161
- async sendRequest(request) {
162
- const bridge = this.bridge;
163
- if (!bridge) {
164
- throw new StoreNotOpenError();
165
- }
166
- return bridge.send(request);
167
- }
168
- async sendChecked(request, description) {
169
- const response = await this.sendRequest(request);
170
- if (response.type === "error") {
171
- throw new AdapterError(`${description} failed: ${response.message}`);
172
- }
173
- }
174
- };
175
-
176
- export {
177
- SqliteWasmAdapter
178
- };
179
- //# sourceMappingURL=chunk-ZP5AXQ3Z.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/adapters/sqlite-wasm-adapter.ts"],"sourcesContent":["import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\nimport { Mutex } from './sqlite-wasm-channel'\nimport type { WorkerBridge, WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\n/**\n * Options for creating a SqliteWasmAdapter.\n */\nexport interface SqliteWasmAdapterOptions {\n\t/**\n\t * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created\n\t * in browser environments.\n\t */\n\tbridge?: WorkerBridge\n\n\t/**\n\t * Database name for persistence. Used as the OPFS file name or IDB key.\n\t */\n\tdbName?: string\n\n\t/**\n\t * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.\n\t */\n\tworkerUrl?: string | URL\n}\n\n/**\n * SQLite WASM adapter that communicates with a SQLite instance through a WorkerBridge.\n *\n * In browsers, the bridge is backed by a Web Worker running SQLite WASM with OPFS persistence.\n * In Node.js tests, the bridge is backed by MockWorkerBridge wrapping better-sqlite3.\n *\n * @example\n * ```typescript\n * // Browser usage\n * const adapter = new SqliteWasmAdapter({ workerUrl: '/sqlite-wasm-worker.js' })\n *\n * // Test usage with MockWorkerBridge\n * import { MockWorkerBridge } from './sqlite-wasm-mock-bridge'\n * const adapter = new SqliteWasmAdapter({ bridge: new MockWorkerBridge() })\n * ```\n */\nexport class SqliteWasmAdapter implements StorageAdapter {\n\tprivate bridge: WorkerBridge | null = null\n\tprivate opened = false\n\tprivate readonly mutex = new Mutex()\n\tprivate readonly injectedBridge: WorkerBridge | undefined\n\tprivate readonly workerUrl: string | URL | undefined\n\tprivate readonly dbName: string\n\n\tconstructor(options: SqliteWasmAdapterOptions = {}) {\n\t\tthis.injectedBridge = options.bridge\n\t\tthis.workerUrl = options.workerUrl\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\tif (this.opened) return\n\n\t\tif (this.injectedBridge) {\n\t\t\tthis.bridge = this.injectedBridge\n\t\t} else if (this.workerUrl) {\n\t\t\t// Dynamic import to avoid loading WebWorkerBridge in Node.js\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tthis.bridge = new WebWorkerBridge(this.workerUrl)\n\t\t} else {\n\t\t\tthrow new AdapterError(\n\t\t\t\t'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). ' +\n\t\t\t\t\t'Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: \"/worker.js\" } for browsers.',\n\t\t\t)\n\t\t}\n\n\t\tconst ddlStatements = generateFullDDL(schema)\n\t\tconst response = await this.sendRequest({ id: 0, type: 'open', ddlStatements })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Failed to open database: ${response.message}`, {\n\t\t\t\tcode: response.code,\n\t\t\t\tdbName: this.dbName,\n\t\t\t})\n\t\t}\n\t\tthis.opened = true\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (!this.bridge) return\n\n\t\ttry {\n\t\t\tawait this.sendRequest({ id: 0, type: 'close' })\n\t\t} finally {\n\t\t\tthis.bridge.terminate()\n\t\t\tthis.bridge = null\n\t\t\tthis.opened = false\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql, params })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Execute failed: ${response.message}`, { sql, params })\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'query', sql, params })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Query failed: ${response.message}`, { sql, params })\n\t\t}\n\t\treturn (response.data as T[]) ?? []\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tthis.guardOpen()\n\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tawait this.sendChecked({ id: 0, type: 'begin' }, 'BEGIN transaction')\n\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${response.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\tconst response = await this.sendRequest({ id: 0, type: 'query', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${response.message}`, { sql, params })\n\t\t\t\t\t}\n\t\t\t\t\treturn (response.data as T[]) ?? []\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tawait fn(tx)\n\t\t\tawait this.sendChecked({ id: 0, type: 'commit' }, 'COMMIT transaction')\n\t\t} catch (error) {\n\t\t\t// Attempt rollback, but don't mask the original error\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest({ id: 0, type: 'rollback' })\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary to the original error\n\t\t\t}\n\t\t\tthrow error\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tthis.guardOpen()\n\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tawait this.sendChecked({ id: 0, type: 'begin' }, 'BEGIN migration')\n\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql })\n\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\tthrow new AdapterError(`Migration from v${from} to v${to} failed: ${response.message}`, {\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this.sendChecked({ id: 0, type: 'commit' }, 'COMMIT migration')\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest({ id: 0, type: 'rollback' })\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary\n\t\t\t}\n\t\t\tif (error instanceof AdapterError) throw error\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{ from, to },\n\t\t\t)\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Export the database as a Uint8Array (for IndexedDB persistence).\n\t * Only available when the database is open.\n\t */\n\tasync exportDatabase(): Promise<Uint8Array> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'export' })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Export failed: ${response.message}`)\n\t\t}\n\t\treturn response.data as Uint8Array\n\t}\n\n\t/**\n\t * Import a serialized database snapshot.\n\t */\n\tasync importDatabase(data: Uint8Array): Promise<void> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'import', data })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Import failed: ${response.message}`)\n\t\t}\n\t}\n\n\tprivate guardOpen(): void {\n\t\tif (!this.opened || !this.bridge) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t}\n\n\tprivate async sendRequest(request: WorkerRequest): Promise<WorkerResponse> {\n\t\t// guardOpen() is always called before sendRequest, so bridge is guaranteed non-null\n\t\tconst bridge = this.bridge\n\t\tif (!bridge) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn bridge.send(request)\n\t}\n\n\tprivate async sendChecked(request: WorkerRequest, description: string): Promise<void> {\n\t\tconst response = await this.sendRequest(request)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`${description} failed: ${response.message}`)\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,106 +0,0 @@
1
- import { SchemaDefinition, KoraEventEmitter } from '@korajs/core';
2
-
3
- /**
4
- * Transaction interface for executing multiple operations atomically.
5
- */
6
- interface Transaction {
7
- execute(sql: string, params?: unknown[]): Promise<void>;
8
- query<T>(sql: string, params?: unknown[]): Promise<T[]>;
9
- }
10
- /**
11
- * Migration plan containing SQL statements and optional data transforms.
12
- */
13
- interface MigrationPlan {
14
- statements: string[];
15
- transforms?: Array<(row: Record<string, unknown>) => Record<string, unknown>>;
16
- }
17
- /**
18
- * Storage adapter interface. All storage backends must implement this.
19
- * Operations are async to support both sync (better-sqlite3) and async (IndexedDB, WASM) backends.
20
- */
21
- interface StorageAdapter {
22
- /** Open or create the database */
23
- open(schema: SchemaDefinition): Promise<void>;
24
- /** Close the database and release resources */
25
- close(): Promise<void>;
26
- /** Execute a write query (INSERT, UPDATE, DELETE) within a transaction */
27
- execute(sql: string, params?: unknown[]): Promise<void>;
28
- /** Execute a read query (SELECT) */
29
- query<T>(sql: string, params?: unknown[]): Promise<T[]>;
30
- /** Execute multiple operations atomically */
31
- transaction(fn: (tx: Transaction) => Promise<void>): Promise<void>;
32
- /** Apply a schema migration */
33
- migrate(from: number, to: number, migration: MigrationPlan): Promise<void>;
34
- }
35
- /**
36
- * Configuration for creating a Store instance.
37
- */
38
- interface StoreConfig {
39
- schema: SchemaDefinition;
40
- adapter: StorageAdapter;
41
- /** Optional node ID. If omitted, one is generated or loaded from the database. */
42
- nodeId?: string;
43
- /** Optional event emitter. When provided, local mutations emit 'operation:created' events. */
44
- emitter?: KoraEventEmitter;
45
- }
46
- /**
47
- * Operators for where clause conditions.
48
- */
49
- interface WhereOperators {
50
- $eq?: unknown;
51
- $ne?: unknown;
52
- $gt?: number | string;
53
- $gte?: number | string;
54
- $lt?: number | string;
55
- $lte?: number | string;
56
- $in?: unknown[];
57
- }
58
- /**
59
- * Where clause: field name to value (shorthand for $eq) or WhereOperators.
60
- */
61
- type WhereClause = Record<string, unknown | WhereOperators>;
62
- /**
63
- * Order direction for sorting query results.
64
- */
65
- type OrderByDirection = 'asc' | 'desc';
66
- /**
67
- * Order-by clause: field name and optional direction.
68
- */
69
- interface OrderByClause {
70
- field: string;
71
- direction: OrderByDirection;
72
- }
73
- /**
74
- * Internal representation of a query to be compiled to SQL.
75
- */
76
- interface QueryDescriptor {
77
- collection: string;
78
- where: WhereClause;
79
- orderBy: OrderByClause[];
80
- limit?: number;
81
- offset?: number;
82
- /** Relation names to include in results (for relational queries). */
83
- include?: string[];
84
- /** Resolved collection names for included relations (for subscription tracking). */
85
- includeCollections?: string[];
86
- }
87
- /**
88
- * Callback for reactive subscriptions. Receives the current result set.
89
- */
90
- type SubscriptionCallback<T> = (results: T[]) => void;
91
- /**
92
- * Record type returned from collection queries.
93
- * Includes the id and mapped metadata fields.
94
- */
95
- interface CollectionRecord {
96
- id: string;
97
- createdAt: number;
98
- updatedAt: number;
99
- [key: string]: unknown;
100
- }
101
- /**
102
- * Result of a remote operation application.
103
- */
104
- type ApplyResult = 'applied' | 'duplicate' | 'skipped';
105
-
106
- export type { ApplyResult as A, CollectionRecord as C, MigrationPlan as M, OrderByDirection as O, QueryDescriptor as Q, StorageAdapter as S, Transaction as T, WhereClause as W, SubscriptionCallback as a, StoreConfig as b, OrderByClause as c, WhereOperators as d };