firebird-wasm 0.1.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 (66) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +120 -0
  3. package/dist/.tsbuildinfo.build +1 -0
  4. package/dist/browser/db-lock.d.ts +92 -0
  5. package/dist/browser/db-lock.d.ts.map +1 -0
  6. package/dist/browser/db-lock.js +163 -0
  7. package/dist/browser/db-lock.js.map +1 -0
  8. package/dist/browser/engine-transport.d.ts +94 -0
  9. package/dist/browser/engine-transport.d.ts.map +1 -0
  10. package/dist/browser/engine-transport.js +217 -0
  11. package/dist/browser/engine-transport.js.map +1 -0
  12. package/dist/browser/field-types.d.ts +19 -0
  13. package/dist/browser/field-types.d.ts.map +1 -0
  14. package/dist/browser/field-types.js +79 -0
  15. package/dist/browser/field-types.js.map +1 -0
  16. package/dist/browser/firebird-browser.d.ts +249 -0
  17. package/dist/browser/firebird-browser.d.ts.map +1 -0
  18. package/dist/browser/firebird-browser.js +401 -0
  19. package/dist/browser/firebird-browser.js.map +1 -0
  20. package/dist/browser/index.d.ts +31 -0
  21. package/dist/browser/index.d.ts.map +1 -0
  22. package/dist/browser/index.js +39 -0
  23. package/dist/browser/index.js.map +1 -0
  24. package/dist/browser/indexeddb-vfs.d.ts +132 -0
  25. package/dist/browser/indexeddb-vfs.d.ts.map +1 -0
  26. package/dist/browser/indexeddb-vfs.js +285 -0
  27. package/dist/browser/indexeddb-vfs.js.map +1 -0
  28. package/dist/browser/params.d.ts +33 -0
  29. package/dist/browser/params.d.ts.map +1 -0
  30. package/dist/browser/params.js +92 -0
  31. package/dist/browser/params.js.map +1 -0
  32. package/dist/browser/sql-script.d.ts +27 -0
  33. package/dist/browser/sql-script.d.ts.map +1 -0
  34. package/dist/browser/sql-script.js +132 -0
  35. package/dist/browser/sql-script.js.map +1 -0
  36. package/dist/browser/worker-entry.d.ts +45 -0
  37. package/dist/browser/worker-entry.d.ts.map +1 -0
  38. package/dist/browser/worker-entry.js +79 -0
  39. package/dist/browser/worker-entry.js.map +1 -0
  40. package/dist/browser/worker-protocol.d.ts +37 -0
  41. package/dist/browser/worker-protocol.d.ts.map +1 -0
  42. package/dist/browser/worker-protocol.js +10 -0
  43. package/dist/browser/worker-protocol.js.map +1 -0
  44. package/dist/browser/worker-transport.d.ts +63 -0
  45. package/dist/browser/worker-transport.d.ts.map +1 -0
  46. package/dist/browser/worker-transport.js +133 -0
  47. package/dist/browser/worker-transport.js.map +1 -0
  48. package/dist/firebird.d.ts +73 -0
  49. package/dist/firebird.d.ts.map +1 -0
  50. package/dist/firebird.js +208 -0
  51. package/dist/firebird.js.map +1 -0
  52. package/dist/index.d.ts +22 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +28 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/types.d.ts +88 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +6 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/wasm/firebird-embedded.js +2 -0
  61. package/dist/wasm/firebird-embedded.wasm +0 -0
  62. package/dist/wasm-loader.d.ts +162 -0
  63. package/dist/wasm-loader.d.ts.map +1 -0
  64. package/dist/wasm-loader.js +103 -0
  65. package/dist/wasm-loader.js.map +1 -0
  66. package/package.json +86 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * firebird-wasm/browser – Browser entry point.
3
+ *
4
+ * Re-exports the browser-specific FirebirdBrowser class together with the
5
+ * shared types so that browser consumers can import from a single path:
6
+ *
7
+ * ```ts
8
+ * import { FirebirdBrowser } from 'firebird-wasm/browser';
9
+ * ```
10
+ */
11
+ export { FirebirdBrowser, FirebirdBrowserTransaction } from './firebird-browser';
12
+ export type { FirebirdBrowserOptions, ExecResult } from './firebird-browser';
13
+ export { encodeParams, encodeParamValue } from './params';
14
+ export { firebirdTypeName } from './field-types';
15
+ export { acquireDatabaseLock, databaseLockName, DatabaseLockedError, } from './db-lock';
16
+ export type { DatabaseLock, DatabaseLockOptions } from './db-lock';
17
+ export { splitStatements } from './sql-script';
18
+ export type { ScriptStatement } from './sql-script';
19
+ export { DirectTransport } from './engine-transport';
20
+ export type { EngineTransport, EngineHandle, DirectTransportOptions, } from './engine-transport';
21
+ export { WorkerTransport } from './worker-transport';
22
+ export type { WorkerTransportOptions } from './worker-transport';
23
+ export { serveEngine } from './worker-entry';
24
+ export type { EngineWorkerScope } from './worker-entry';
25
+ export type { EngineOp, EngineRequest, EngineResponse, } from './worker-protocol';
26
+ export { IndexedDBVFS } from './indexeddb-vfs';
27
+ export type { IndexedDBVFSOptions, VFSMetadata } from './indexeddb-vfs';
28
+ export { loadFirebirdWasm, allocString } from '../wasm-loader';
29
+ export type { FirebirdWasmModule, WasmLoadOptions, EmscriptenFS, FbHandle, } from '../wasm-loader';
30
+ export type { QueryResult, QueryParams, Row, FieldInfo, FirebirdTypeName, TransactionOptions, IsolationLevel, } from '../types';
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EACV,eAAe,EACf,YAAY,EACZ,sBAAsB,GACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC/D,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,QAAQ,GACT,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACV,WAAW,EACX,WAAW,EACX,GAAG,EACH,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACf,MAAM,UAAU,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * firebird-wasm/browser – Browser entry point.
4
+ *
5
+ * Re-exports the browser-specific FirebirdBrowser class together with the
6
+ * shared types so that browser consumers can import from a single path:
7
+ *
8
+ * ```ts
9
+ * import { FirebirdBrowser } from 'firebird-wasm/browser';
10
+ * ```
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.allocString = exports.loadFirebirdWasm = exports.IndexedDBVFS = exports.serveEngine = exports.WorkerTransport = exports.DirectTransport = exports.splitStatements = exports.DatabaseLockedError = exports.databaseLockName = exports.acquireDatabaseLock = exports.firebirdTypeName = exports.encodeParamValue = exports.encodeParams = exports.FirebirdBrowserTransaction = exports.FirebirdBrowser = void 0;
14
+ var firebird_browser_1 = require("./firebird-browser");
15
+ Object.defineProperty(exports, "FirebirdBrowser", { enumerable: true, get: function () { return firebird_browser_1.FirebirdBrowser; } });
16
+ Object.defineProperty(exports, "FirebirdBrowserTransaction", { enumerable: true, get: function () { return firebird_browser_1.FirebirdBrowserTransaction; } });
17
+ var params_1 = require("./params");
18
+ Object.defineProperty(exports, "encodeParams", { enumerable: true, get: function () { return params_1.encodeParams; } });
19
+ Object.defineProperty(exports, "encodeParamValue", { enumerable: true, get: function () { return params_1.encodeParamValue; } });
20
+ var field_types_1 = require("./field-types");
21
+ Object.defineProperty(exports, "firebirdTypeName", { enumerable: true, get: function () { return field_types_1.firebirdTypeName; } });
22
+ var db_lock_1 = require("./db-lock");
23
+ Object.defineProperty(exports, "acquireDatabaseLock", { enumerable: true, get: function () { return db_lock_1.acquireDatabaseLock; } });
24
+ Object.defineProperty(exports, "databaseLockName", { enumerable: true, get: function () { return db_lock_1.databaseLockName; } });
25
+ Object.defineProperty(exports, "DatabaseLockedError", { enumerable: true, get: function () { return db_lock_1.DatabaseLockedError; } });
26
+ var sql_script_1 = require("./sql-script");
27
+ Object.defineProperty(exports, "splitStatements", { enumerable: true, get: function () { return sql_script_1.splitStatements; } });
28
+ var engine_transport_1 = require("./engine-transport");
29
+ Object.defineProperty(exports, "DirectTransport", { enumerable: true, get: function () { return engine_transport_1.DirectTransport; } });
30
+ var worker_transport_1 = require("./worker-transport");
31
+ Object.defineProperty(exports, "WorkerTransport", { enumerable: true, get: function () { return worker_transport_1.WorkerTransport; } });
32
+ var worker_entry_1 = require("./worker-entry");
33
+ Object.defineProperty(exports, "serveEngine", { enumerable: true, get: function () { return worker_entry_1.serveEngine; } });
34
+ var indexeddb_vfs_1 = require("./indexeddb-vfs");
35
+ Object.defineProperty(exports, "IndexedDBVFS", { enumerable: true, get: function () { return indexeddb_vfs_1.IndexedDBVFS; } });
36
+ var wasm_loader_1 = require("../wasm-loader");
37
+ Object.defineProperty(exports, "loadFirebirdWasm", { enumerable: true, get: function () { return wasm_loader_1.loadFirebirdWasm; } });
38
+ Object.defineProperty(exports, "allocString", { enumerable: true, get: function () { return wasm_loader_1.allocString; } });
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,uDAAiF;AAAxE,mHAAA,eAAe,OAAA;AAAE,8HAAA,0BAA0B,OAAA;AAGpD,mCAA0D;AAAjD,sGAAA,YAAY,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AAEvC,6CAAiD;AAAxC,+GAAA,gBAAgB,OAAA;AAEzB,qCAImB;AAHjB,8GAAA,mBAAmB,OAAA;AACnB,2GAAA,gBAAgB,OAAA;AAChB,8GAAA,mBAAmB,OAAA;AAIrB,2CAA+C;AAAtC,6GAAA,eAAe,OAAA;AAGxB,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AAOxB,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AAGxB,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AAQpB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAGrB,8CAA+D;AAAtD,+GAAA,gBAAgB,OAAA;AAAE,0GAAA,WAAW,OAAA"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * indexeddb-vfs.ts – IndexedDB-backed virtual filesystem for Firebird WASM.
3
+ *
4
+ * Provides page-level persistence so that a Firebird database running inside
5
+ * the browser WASM sandbox can survive page reloads. The API mirrors the
6
+ * operations that Emscripten's IDBFS performs, but is designed to be used
7
+ * directly as a persistence layer plugged into the Firebird WASM wrapper.
8
+ *
9
+ * ## Storage model
10
+ *
11
+ * Each database is stored in its own IndexedDB database and a single object
12
+ * store named `pages`. Every record stores a fixed-size page of the database
13
+ * file, keyed by zero-based page number.
14
+ *
15
+ * key (pageNumber: number) → value ({ data: ArrayBuffer })
16
+ *
17
+ * A special metadata record with key `__meta__` stores the page size and the
18
+ * total number of pages.
19
+ */
20
+ /** Metadata record persisted alongside the page data. */
21
+ export interface VFSMetadata {
22
+ /** Page size in bytes (Firebird default is usually 8 192). */
23
+ pageSize: number;
24
+ /** Total number of pages currently stored. */
25
+ pageCount: number;
26
+ }
27
+ /** Options for creating an {@link IndexedDBVFS} instance. */
28
+ export interface IndexedDBVFSOptions {
29
+ /**
30
+ * Page size in bytes. Must match the Firebird database page size.
31
+ * @default 8192
32
+ */
33
+ pageSize?: number;
34
+ /**
35
+ * IndexedDB database name prefix. The actual database name is
36
+ * `${prefix}${dbName}`.
37
+ * @default 'firebird_'
38
+ */
39
+ prefix?: string;
40
+ }
41
+ /**
42
+ * IndexedDB-backed virtual filesystem for a single Firebird database.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const vfs = new IndexedDBVFS({ pageSize: 8192 });
47
+ * await vfs.open('mydb');
48
+ *
49
+ * // Write the first page
50
+ * await vfs.writePage(0, someArrayBuffer);
51
+ *
52
+ * // Read it back
53
+ * const page = await vfs.readPage(0);
54
+ *
55
+ * // Persist all pending writes
56
+ * await vfs.sync();
57
+ *
58
+ * await vfs.close();
59
+ * ```
60
+ */
61
+ export declare class IndexedDBVFS {
62
+ private readonly pageSize;
63
+ private readonly prefix;
64
+ private db;
65
+ private dbName;
66
+ constructor(options?: IndexedDBVFSOptions);
67
+ /**
68
+ * Open (or create) the IndexedDB store for `dbName`.
69
+ * Must be called before any read/write operations.
70
+ */
71
+ open(dbName: string): Promise<void>;
72
+ /** Close the underlying IndexedDB connection. */
73
+ close(): Promise<void>;
74
+ /**
75
+ * Read a page by its zero-based number.
76
+ * Returns a zero-filled buffer if the page has not been written yet.
77
+ */
78
+ readPage(pageNumber: number): Promise<ArrayBuffer>;
79
+ /** Write a page (full page overwrite). */
80
+ writePage(pageNumber: number, data: ArrayBuffer): Promise<void>;
81
+ /** Read the stored metadata, or return defaults if none exists. */
82
+ getMetadata(): Promise<VFSMetadata>;
83
+ /**
84
+ * Read the full database as a contiguous `Uint8Array`.
85
+ * Useful for exporting/downloading a database snapshot.
86
+ */
87
+ exportDatabase(): Promise<Uint8Array>;
88
+ /**
89
+ * Import a full database image, replacing any existing content.
90
+ * The `data` length must be a multiple of `pageSize`.
91
+ *
92
+ * The whole import runs in **one** IndexedDB transaction, and only pages
93
+ * whose contents actually changed are written.
94
+ *
95
+ * Both matter. A transaction per page — which is what this used to do — is
96
+ * not atomic: a tab closed midway through leaves some pages new and some
97
+ * old, which is a corrupt database with no way back. IndexedDB guarantees a
98
+ * transaction is all-or-nothing, so an interrupted persist now leaves the
99
+ * previous image intact. Skipping unchanged pages then turns the common
100
+ * case (a few pages dirty in a large database) from O(database) into
101
+ * O(changes).
102
+ *
103
+ * Pages are compared against what is already stored rather than against an
104
+ * in-memory copy: the comparison is exact, and it costs no extra memory
105
+ * proportional to the database.
106
+ */
107
+ importDatabase(data: Uint8Array): Promise<void>;
108
+ /**
109
+ * Flush – a no-op for IndexedDB since each `put` is already durable once
110
+ * the transaction completes. Provided for API symmetry.
111
+ */
112
+ sync(): Promise<void>;
113
+ /** Delete all stored pages and metadata for this database. */
114
+ clear(): Promise<void>;
115
+ /** Delete the entire IndexedDB database. */
116
+ destroy(): Promise<void>;
117
+ /**
118
+ * Synchronise pages between Emscripten's in-memory FS and IndexedDB.
119
+ *
120
+ * @param direction - `'persist'` writes MEMFS → IndexedDB,
121
+ * `'populate'` reads IndexedDB → MEMFS.
122
+ * @param readFile - Callback to read a file from Emscripten's FS.
123
+ * @param writeFile - Callback to write a file into Emscripten's FS.
124
+ * @param dbPath - The virtual path of the database in Emscripten's FS.
125
+ */
126
+ syncWithEmscriptenFS(direction: 'persist' | 'populate', readFile: (path: string) => Uint8Array, writeFile: (path: string, data: Uint8Array) => void, dbPath: string): Promise<void>;
127
+ private requireDb;
128
+ private idbGet;
129
+ private idbPut;
130
+ private updateMeta;
131
+ }
132
+ //# sourceMappingURL=indexeddb-vfs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb-vfs.d.ts","sourceRoot":"","sources":["../../src/browser/indexeddb-vfs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA4BD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,MAAM,CAAM;gBAER,OAAO,GAAE,mBAAwB;IAO7C;;;OAGG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBzC,iDAAiD;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B;;;OAGG;IACG,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAMxD,0CAA0C;IACpC,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE,mEAAmE;IAC7D,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAKzC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC;IAa3C;;;;;;;;;;;;;;;;;;OAkBG;IACG,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDrD;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,8DAA8D;IACxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,4CAA4C;IACtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B;;;;;;;;OAQG;IACG,oBAAoB,CACxB,SAAS,EAAE,SAAS,GAAG,UAAU,EACjC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,EACtC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EACnD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAchB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,MAAM;YAWA,UAAU;CAOzB"}
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ /**
3
+ * indexeddb-vfs.ts – IndexedDB-backed virtual filesystem for Firebird WASM.
4
+ *
5
+ * Provides page-level persistence so that a Firebird database running inside
6
+ * the browser WASM sandbox can survive page reloads. The API mirrors the
7
+ * operations that Emscripten's IDBFS performs, but is designed to be used
8
+ * directly as a persistence layer plugged into the Firebird WASM wrapper.
9
+ *
10
+ * ## Storage model
11
+ *
12
+ * Each database is stored in its own IndexedDB database and a single object
13
+ * store named `pages`. Every record stores a fixed-size page of the database
14
+ * file, keyed by zero-based page number.
15
+ *
16
+ * key (pageNumber: number) → value ({ data: ArrayBuffer })
17
+ *
18
+ * A special metadata record with key `__meta__` stores the page size and the
19
+ * total number of pages.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.IndexedDBVFS = void 0;
23
+ // ---------------------------------------------------------------------------
24
+ // Constants
25
+ // ---------------------------------------------------------------------------
26
+ const DEFAULT_PAGE_SIZE = 8192;
27
+ const DEFAULT_PREFIX = 'firebird_';
28
+ const STORE_NAME = 'pages';
29
+ const META_KEY = '__meta__';
30
+ // ---------------------------------------------------------------------------
31
+ // Helpers
32
+ // ---------------------------------------------------------------------------
33
+ /** Exact byte equality. Used to decide whether a page needs rewriting. */
34
+ function sameBytes(a, b) {
35
+ if (a.length !== b.length)
36
+ return false;
37
+ for (let i = 0; i < a.length; i++) {
38
+ if (a[i] !== b[i])
39
+ return false;
40
+ }
41
+ return true;
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Implementation
45
+ // ---------------------------------------------------------------------------
46
+ /**
47
+ * IndexedDB-backed virtual filesystem for a single Firebird database.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const vfs = new IndexedDBVFS({ pageSize: 8192 });
52
+ * await vfs.open('mydb');
53
+ *
54
+ * // Write the first page
55
+ * await vfs.writePage(0, someArrayBuffer);
56
+ *
57
+ * // Read it back
58
+ * const page = await vfs.readPage(0);
59
+ *
60
+ * // Persist all pending writes
61
+ * await vfs.sync();
62
+ *
63
+ * await vfs.close();
64
+ * ```
65
+ */
66
+ class IndexedDBVFS {
67
+ constructor(options = {}) {
68
+ this.db = null;
69
+ this.dbName = '';
70
+ this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
71
+ this.prefix = options.prefix ?? DEFAULT_PREFIX;
72
+ }
73
+ // ── Lifecycle ─────────────────────────────────────────────────────────
74
+ /**
75
+ * Open (or create) the IndexedDB store for `dbName`.
76
+ * Must be called before any read/write operations.
77
+ */
78
+ async open(dbName) {
79
+ if (this.db) {
80
+ throw new Error(`IndexedDBVFS already open for "${this.dbName}"`);
81
+ }
82
+ this.dbName = dbName;
83
+ const idbName = `${this.prefix}${dbName}`;
84
+ this.db = await new Promise((resolve, reject) => {
85
+ const request = indexedDB.open(idbName, 1);
86
+ request.onupgradeneeded = () => {
87
+ const db = request.result;
88
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
89
+ db.createObjectStore(STORE_NAME);
90
+ }
91
+ };
92
+ request.onsuccess = () => resolve(request.result);
93
+ request.onerror = () => reject(request.error);
94
+ });
95
+ }
96
+ /** Close the underlying IndexedDB connection. */
97
+ async close() {
98
+ if (this.db) {
99
+ this.db.close();
100
+ this.db = null;
101
+ }
102
+ }
103
+ // ── Page I/O ──────────────────────────────────────────────────────────
104
+ /**
105
+ * Read a page by its zero-based number.
106
+ * Returns a zero-filled buffer if the page has not been written yet.
107
+ */
108
+ async readPage(pageNumber) {
109
+ const record = await this.idbGet(pageNumber);
110
+ if (record)
111
+ return record.data;
112
+ return new ArrayBuffer(this.pageSize);
113
+ }
114
+ /** Write a page (full page overwrite). */
115
+ async writePage(pageNumber, data) {
116
+ if (data.byteLength !== this.pageSize) {
117
+ throw new RangeError(`Page data must be exactly ${this.pageSize} bytes (got ${data.byteLength})`);
118
+ }
119
+ await this.idbPut(pageNumber, { data });
120
+ await this.updateMeta(pageNumber);
121
+ }
122
+ /** Read the stored metadata, or return defaults if none exists. */
123
+ async getMetadata() {
124
+ const meta = await this.idbGet(META_KEY);
125
+ return meta ?? { pageSize: this.pageSize, pageCount: 0 };
126
+ }
127
+ /**
128
+ * Read the full database as a contiguous `Uint8Array`.
129
+ * Useful for exporting/downloading a database snapshot.
130
+ */
131
+ async exportDatabase() {
132
+ const meta = await this.getMetadata();
133
+ const totalBytes = meta.pageCount * meta.pageSize;
134
+ const output = new Uint8Array(totalBytes);
135
+ for (let i = 0; i < meta.pageCount; i++) {
136
+ const page = await this.readPage(i);
137
+ output.set(new Uint8Array(page), i * meta.pageSize);
138
+ }
139
+ return output;
140
+ }
141
+ /**
142
+ * Import a full database image, replacing any existing content.
143
+ * The `data` length must be a multiple of `pageSize`.
144
+ *
145
+ * The whole import runs in **one** IndexedDB transaction, and only pages
146
+ * whose contents actually changed are written.
147
+ *
148
+ * Both matter. A transaction per page — which is what this used to do — is
149
+ * not atomic: a tab closed midway through leaves some pages new and some
150
+ * old, which is a corrupt database with no way back. IndexedDB guarantees a
151
+ * transaction is all-or-nothing, so an interrupted persist now leaves the
152
+ * previous image intact. Skipping unchanged pages then turns the common
153
+ * case (a few pages dirty in a large database) from O(database) into
154
+ * O(changes).
155
+ *
156
+ * Pages are compared against what is already stored rather than against an
157
+ * in-memory copy: the comparison is exact, and it costs no extra memory
158
+ * proportional to the database.
159
+ */
160
+ async importDatabase(data) {
161
+ if (data.byteLength % this.pageSize !== 0) {
162
+ throw new RangeError(`Database size (${data.byteLength}) is not a multiple of page size (${this.pageSize})`);
163
+ }
164
+ const pageCount = data.byteLength / this.pageSize;
165
+ const db = this.requireDb();
166
+ await new Promise((resolve, reject) => {
167
+ const tx = db.transaction(STORE_NAME, 'readwrite');
168
+ const store = tx.objectStore(STORE_NAME);
169
+ // Resolve on the transaction, not on the last request: only completion
170
+ // means the changes are durable.
171
+ tx.oncomplete = () => resolve();
172
+ tx.onerror = () => reject(tx.error);
173
+ tx.onabort = () => reject(tx.error ?? new Error('IndexedDB transaction aborted'));
174
+ // Previous size, so pages past the new end can be removed.
175
+ const metaRequest = store.get(META_KEY);
176
+ metaRequest.onsuccess = () => {
177
+ const previous = metaRequest.result?.pageCount ?? 0;
178
+ for (let i = pageCount; i < previous; i++) {
179
+ store.delete(i);
180
+ }
181
+ };
182
+ for (let i = 0; i < pageCount; i++) {
183
+ const offset = i * this.pageSize;
184
+ const page = data.subarray(offset, offset + this.pageSize);
185
+ const existing = store.get(i);
186
+ existing.onsuccess = () => {
187
+ const stored = existing.result?.data;
188
+ if (stored && sameBytes(new Uint8Array(stored), page)) {
189
+ return; // unchanged
190
+ }
191
+ // slice() copies: `page` is a view onto the caller's buffer, which
192
+ // must not be captured by the store.
193
+ store.put({ data: page.slice().buffer }, i);
194
+ };
195
+ }
196
+ store.put({ pageSize: this.pageSize, pageCount }, META_KEY);
197
+ });
198
+ }
199
+ /**
200
+ * Flush – a no-op for IndexedDB since each `put` is already durable once
201
+ * the transaction completes. Provided for API symmetry.
202
+ */
203
+ async sync() {
204
+ // IndexedDB writes are durable after the transaction completes.
205
+ }
206
+ /** Delete all stored pages and metadata for this database. */
207
+ async clear() {
208
+ const db = this.requireDb();
209
+ await new Promise((resolve, reject) => {
210
+ const tx = db.transaction(STORE_NAME, 'readwrite');
211
+ const store = tx.objectStore(STORE_NAME);
212
+ const request = store.clear();
213
+ request.onsuccess = () => resolve();
214
+ request.onerror = () => reject(request.error);
215
+ });
216
+ }
217
+ /** Delete the entire IndexedDB database. */
218
+ async destroy() {
219
+ const idbName = `${this.prefix}${this.dbName}`;
220
+ await this.close();
221
+ await new Promise((resolve, reject) => {
222
+ const request = indexedDB.deleteDatabase(idbName);
223
+ request.onsuccess = () => resolve();
224
+ request.onerror = () => reject(request.error);
225
+ });
226
+ }
227
+ // ── Emscripten FS integration ─────────────────────────────────────────
228
+ /**
229
+ * Synchronise pages between Emscripten's in-memory FS and IndexedDB.
230
+ *
231
+ * @param direction - `'persist'` writes MEMFS → IndexedDB,
232
+ * `'populate'` reads IndexedDB → MEMFS.
233
+ * @param readFile - Callback to read a file from Emscripten's FS.
234
+ * @param writeFile - Callback to write a file into Emscripten's FS.
235
+ * @param dbPath - The virtual path of the database in Emscripten's FS.
236
+ */
237
+ async syncWithEmscriptenFS(direction, readFile, writeFile, dbPath) {
238
+ if (direction === 'persist') {
239
+ const data = readFile(dbPath);
240
+ await this.importDatabase(data);
241
+ }
242
+ else {
243
+ const data = await this.exportDatabase();
244
+ if (data.byteLength > 0) {
245
+ writeFile(dbPath, data);
246
+ }
247
+ }
248
+ }
249
+ // ── Private helpers ───────────────────────────────────────────────────
250
+ requireDb() {
251
+ if (!this.db) {
252
+ throw new Error('IndexedDBVFS is not open. Call open() first.');
253
+ }
254
+ return this.db;
255
+ }
256
+ idbGet(key) {
257
+ const db = this.requireDb();
258
+ return new Promise((resolve, reject) => {
259
+ const tx = db.transaction(STORE_NAME, 'readonly');
260
+ const store = tx.objectStore(STORE_NAME);
261
+ const request = store.get(key);
262
+ request.onsuccess = () => resolve(request.result);
263
+ request.onerror = () => reject(request.error);
264
+ });
265
+ }
266
+ idbPut(key, value) {
267
+ const db = this.requireDb();
268
+ return new Promise((resolve, reject) => {
269
+ const tx = db.transaction(STORE_NAME, 'readwrite');
270
+ const store = tx.objectStore(STORE_NAME);
271
+ const request = store.put(value, key);
272
+ request.onsuccess = () => resolve();
273
+ request.onerror = () => reject(request.error);
274
+ });
275
+ }
276
+ async updateMeta(latestPageNumber) {
277
+ const meta = await this.getMetadata();
278
+ const newCount = Math.max(meta.pageCount, latestPageNumber + 1);
279
+ if (newCount !== meta.pageCount) {
280
+ await this.idbPut(META_KEY, { pageSize: this.pageSize, pageCount: newCount });
281
+ }
282
+ }
283
+ }
284
+ exports.IndexedDBVFS = IndexedDBVFS;
285
+ //# sourceMappingURL=indexeddb-vfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexeddb-vfs.js","sourceRoot":"","sources":["../../src/browser/indexeddb-vfs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * params.ts – encode query parameters for the WASM engine.
3
+ *
4
+ * Parameters cross the boundary as one packed buffer rather than JSON, so the
5
+ * C side needs no parser and there is no escaping to get wrong:
6
+ *
7
+ * u32 count
8
+ * per parameter:
9
+ * u8 isNull
10
+ * u32 byteLength (absent when isNull)
11
+ * ... UTF-8 bytes (absent when isNull)
12
+ *
13
+ * Every value is sent as text. The engine describes the input message as
14
+ * VARCHAR UTF-8 and converts to each column's actual type, which is why one
15
+ * path handles integers, dates and strings alike — the alternative is
16
+ * reimplementing Firebird's conversion rules here and getting them subtly
17
+ * wrong.
18
+ */
19
+ import type { QueryParams } from '../types';
20
+ /**
21
+ * Render one parameter the way Firebird's string-to-type conversion expects.
22
+ *
23
+ * @throws if the value has no unambiguous SQL text form.
24
+ */
25
+ export declare function encodeParamValue(value: unknown, index: number): string;
26
+ /**
27
+ * Pack parameters into the buffer the C API expects.
28
+ *
29
+ * `null` and `undefined` both become SQL NULL — JavaScript uses them
30
+ * interchangeably for "absent", and rejecting one would be a trap.
31
+ */
32
+ export declare function encodeParams(params: QueryParams): Uint8Array;
33
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../src/browser/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA6CtE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,CAkC5D"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * params.ts – encode query parameters for the WASM engine.
4
+ *
5
+ * Parameters cross the boundary as one packed buffer rather than JSON, so the
6
+ * C side needs no parser and there is no escaping to get wrong:
7
+ *
8
+ * u32 count
9
+ * per parameter:
10
+ * u8 isNull
11
+ * u32 byteLength (absent when isNull)
12
+ * ... UTF-8 bytes (absent when isNull)
13
+ *
14
+ * Every value is sent as text. The engine describes the input message as
15
+ * VARCHAR UTF-8 and converts to each column's actual type, which is why one
16
+ * path handles integers, dates and strings alike — the alternative is
17
+ * reimplementing Firebird's conversion rules here and getting them subtly
18
+ * wrong.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.encodeParamValue = encodeParamValue;
22
+ exports.encodeParams = encodeParams;
23
+ /**
24
+ * Render one parameter the way Firebird's string-to-type conversion expects.
25
+ *
26
+ * @throws if the value has no unambiguous SQL text form.
27
+ */
28
+ function encodeParamValue(value, index) {
29
+ switch (typeof value) {
30
+ case 'string':
31
+ return value;
32
+ case 'number':
33
+ if (!Number.isFinite(value)) {
34
+ throw new RangeError(`Parameter ${index} is ${value}, which SQL has no representation for`);
35
+ }
36
+ return String(value);
37
+ case 'bigint':
38
+ return value.toString();
39
+ case 'boolean':
40
+ // Firebird's BOOLEAN literals.
41
+ return value ? 'TRUE' : 'FALSE';
42
+ default:
43
+ break;
44
+ }
45
+ if (value instanceof Date) {
46
+ if (Number.isNaN(value.getTime())) {
47
+ throw new RangeError(`Parameter ${index} is an invalid Date`);
48
+ }
49
+ // Firebird's TIMESTAMP literal format. toISOString() is always UTC, and
50
+ // its 'T'/'Z' are not accepted, so the pieces are reassembled.
51
+ return value.toISOString().replace('T', ' ').replace('Z', '');
52
+ }
53
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
54
+ throw new TypeError(`Parameter ${index} is binary, which the WASM build cannot bind yet — ` +
55
+ 'the parameter encoding is text-based (see docs/roadmap.md, M2)');
56
+ }
57
+ throw new TypeError(`Parameter ${index} has unsupported type ${value === null ? 'null' : typeof value}; pass a string, number, bigint, boolean, Date, null or undefined`);
58
+ }
59
+ /**
60
+ * Pack parameters into the buffer the C API expects.
61
+ *
62
+ * `null` and `undefined` both become SQL NULL — JavaScript uses them
63
+ * interchangeably for "absent", and rejecting one would be a trap.
64
+ */
65
+ function encodeParams(params) {
66
+ const encoder = new TextEncoder();
67
+ const encoded = params.map((value, index) => value === null || value === undefined
68
+ ? null
69
+ : encoder.encode(encodeParamValue(value, index)));
70
+ let size = 4;
71
+ for (const bytes of encoded) {
72
+ size += 1 + (bytes ? 4 + bytes.length : 0);
73
+ }
74
+ const out = new Uint8Array(size);
75
+ const view = new DataView(out.buffer);
76
+ let pos = 0;
77
+ view.setUint32(pos, encoded.length, true);
78
+ pos += 4;
79
+ for (const bytes of encoded) {
80
+ if (!bytes) {
81
+ out[pos++] = 1; // isNull
82
+ continue;
83
+ }
84
+ out[pos++] = 0;
85
+ view.setUint32(pos, bytes.length, true);
86
+ pos += 4;
87
+ out.set(bytes, pos);
88
+ pos += bytes.length;
89
+ }
90
+ return out;
91
+ }
92
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.js","sourceRoot":"","sources":["../../src/browser/params.ts"],"names":[],"mappings":"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"}