@statewalker/cellb 0.8.6 → 0.8.8
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.
|
@@ -22,18 +22,35 @@ export class NodeSqliteDriver {
|
|
|
22
22
|
this.#temporary = !!opts.temporary && path !== ":memory:";
|
|
23
23
|
this.#exclusive = !!opts.exclusive;
|
|
24
24
|
this.#pageCapture = !!opts.pageCapture && path !== ":memory:";
|
|
25
|
+
this.#db = this.#connect();
|
|
26
|
+
this.#walSetup();
|
|
27
|
+
}
|
|
28
|
+
/** Extensions loaded so far, by name: a new connection (restore()) loads them again. */
|
|
29
|
+
#extensions = [];
|
|
30
|
+
/** The directory restore() made for an in-memory database's file. */
|
|
31
|
+
#restoredDir;
|
|
32
|
+
/** A connection to `#path`, set up as every connection of this driver is. */
|
|
33
|
+
#connect() {
|
|
25
34
|
// As celld and workerd: a double-quoted name that is no column is a string literal.
|
|
26
|
-
|
|
35
|
+
const db = new DatabaseSync(this.#path, {
|
|
27
36
|
allowExtension: true,
|
|
28
37
|
enableDoubleQuotedStringLiterals: true,
|
|
29
38
|
});
|
|
30
|
-
|
|
39
|
+
db.enableLoadExtension(false); // only loadExtension() below may load, by name
|
|
31
40
|
// EXCLUSIVE locking: the first write takes an OS lock on the file that is
|
|
32
41
|
// kept until close (or process death) — a second process can neither read
|
|
33
42
|
// nor write the cell. Storage-level fencing on one host, no lease needed.
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
if (this.#exclusive)
|
|
44
|
+
db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
|
|
45
|
+
try {
|
|
46
|
+
for (const name of this.#extensions)
|
|
47
|
+
this.#load(db, name);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
db.close(); // not left open behind the error
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
return db;
|
|
37
54
|
}
|
|
38
55
|
// ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----
|
|
39
56
|
// In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame
|
|
@@ -171,6 +188,11 @@ export class NodeSqliteDriver {
|
|
|
171
188
|
}
|
|
172
189
|
close() {
|
|
173
190
|
this.#db.close();
|
|
191
|
+
if (this.#restoredDir) {
|
|
192
|
+
rmSync(this.#restoredDir, { recursive: true, force: true });
|
|
193
|
+
temporaryDirs.delete(this.#restoredDir);
|
|
194
|
+
this.#restoredDir = undefined;
|
|
195
|
+
}
|
|
174
196
|
if (!this.#temporary)
|
|
175
197
|
return;
|
|
176
198
|
for (const suffix of ["", "-wal", "-shm", "-journal"])
|
|
@@ -215,15 +237,20 @@ export class NodeSqliteDriver {
|
|
|
215
237
|
}
|
|
216
238
|
/** Known extensions only: "sqlite_vec" → the sqlite-vec npm package's loadable library. */
|
|
217
239
|
loadExtension(name) {
|
|
240
|
+
this.#load(this.#db, name);
|
|
241
|
+
if (!this.#extensions.includes(name))
|
|
242
|
+
this.#extensions.push(name);
|
|
243
|
+
}
|
|
244
|
+
#load(db, name) {
|
|
218
245
|
const path = KNOWN_EXTENSIONS[name]?.();
|
|
219
246
|
if (!path)
|
|
220
247
|
throw new Error(`Unknown SQLite extension "${name}"`);
|
|
221
|
-
|
|
248
|
+
db.enableLoadExtension(true);
|
|
222
249
|
try {
|
|
223
|
-
|
|
250
|
+
db.loadExtension(path);
|
|
224
251
|
}
|
|
225
252
|
finally {
|
|
226
|
-
|
|
253
|
+
db.enableLoadExtension(false);
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
/**
|
|
@@ -256,14 +283,16 @@ export class NodeSqliteDriver {
|
|
|
256
283
|
/** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */
|
|
257
284
|
restore(image) {
|
|
258
285
|
this.#db.close();
|
|
259
|
-
if (this.#path === ":memory:")
|
|
260
|
-
|
|
286
|
+
if (this.#path === ":memory:") {
|
|
287
|
+
// A directory of its own, removed with the driver (close() or process exit).
|
|
288
|
+
this.#restoredDir = mkdtempSync(join(tmpdir(), "cell-mem-"));
|
|
289
|
+
removeAtExit(this.#restoredDir);
|
|
290
|
+
this.#path = join(this.#restoredDir, "db.sqlite");
|
|
291
|
+
}
|
|
261
292
|
for (const suffix of ["-wal", "-shm", "-journal"])
|
|
262
293
|
rmSync(this.#path + suffix, { force: true });
|
|
263
294
|
writeFileSync(this.#path, image);
|
|
264
|
-
this.#db =
|
|
265
|
-
if (this.#exclusive)
|
|
266
|
-
this.#db.exec("PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;");
|
|
295
|
+
this.#db = this.#connect(); // with the extensions loaded before
|
|
267
296
|
this.#walSetup(); // a new file: a new WAL
|
|
268
297
|
}
|
|
269
298
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-sqlite.js","sourceRoot":"","sources":["../../src/adapters/node-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n closeSync,\n existsSync,\n fstatSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n rmdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { DatabaseSync as DatabaseSyncT } from \"node:sqlite\";\n\nimport { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { requireNodeSqlite } from \"./node-sqlite-module.js\";\n\nconst { DatabaseSync } = requireNodeSqlite();\n\nconst requireCjs = createRequire(import.meta.url);\nconst KNOWN_EXTENSIONS: Record<string, () => string> = {\n sqlite_vec: () => (requireCjs(\"sqlite-vec\") as { getLoadablePath(): string }).getLoadablePath(),\n};\n\n/** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */\nexport class NodeSqliteDriver implements SyncSqlDriver {\n #db: DatabaseSyncT;\n #path: string;\n readonly #exclusive: boolean;\n readonly #pageCapture: boolean;\n /** A file of its own that close() removes (a cell \"in memory\" that needs a WAL). */\n readonly #temporary: boolean;\n constructor(\n path = \":memory:\",\n opts: { exclusive?: boolean; pageCapture?: boolean; temporary?: boolean } = {},\n ) {\n this.#path = path;\n this.#temporary = !!opts.temporary && path !== \":memory:\";\n this.#exclusive = !!opts.exclusive;\n this.#pageCapture = !!opts.pageCapture && path !== \":memory:\";\n // As celld and workerd: a double-quoted name that is no column is a string literal.\n this.#db = new DatabaseSync(path, {\n allowExtension: true,\n enableDoubleQuotedStringLiterals: true,\n });\n this.#db.enableLoadExtension(false); // only loadExtension() below may load, by name\n // EXCLUSIVE locking: the first write takes an OS lock on the file that is\n // kept until close (or process death) — a second process can neither read\n // nor write the cell. Storage-level fencing on one host, no lease needed.\n if (opts.exclusive) this.#db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n this.#walSetup();\n }\n\n // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----\n // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame\n // header + page; a commit frame carries the database size in pages). Reading the frames\n // appended since the last read gives exactly the committed transaction's pages, O(changed),\n // instead of reading the whole file. Automatic checkpoints are off; take() checkpoints\n // (TRUNCATE) itself once the WAL grows, and restarts from its start.\n #walOffset = 0;\n /** Whether anyone reads the captured pages; until then exec() keeps the WAL bounded itself. */\n #taken = false;\n #writes = 0;\n #walSetup(): void {\n if (!this.#pageCapture) return;\n this.#db.exec(\"PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0;\");\n // A temporary file is only the WAL's carrier (the bucket keeps the cell): no fsync per commit.\n if (this.#temporary) this.#db.exec(\"PRAGMA synchronous = OFF\");\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n /** The pages of the transactions committed since the previous call (WAL frames). */\n takeDirtyPages():\n | { pageSize: number; pageCount: number; pages: Map<number, Uint8Array> }\n | undefined {\n if (!this.#pageCapture) return undefined;\n this.#taken = true;\n const walPath = `${this.#path}-wal`;\n let fd: number;\n try {\n fd = openSync(walPath, \"r\");\n } catch {\n return undefined;\n }\n try {\n const size = fstatSync(fd).size;\n if (size < 32) return undefined;\n const header = Buffer.alloc(32);\n readSync(fd, header, 0, 32, 0);\n const pageSize = header.readUInt32BE(8) === 1 ? 65536 : header.readUInt32BE(8);\n const [salt1, salt2] = [header.readUInt32BE(16), header.readUInt32BE(20)];\n const frameSize = 24 + pageSize;\n let offset = Math.max(32, this.#walOffset);\n const pending = new Map<number, Uint8Array>();\n const pages = new Map<number, Uint8Array>();\n let pageCount = 0;\n let committedTo = offset;\n const frame = Buffer.alloc(frameSize);\n while (offset + frameSize <= size) {\n readSync(fd, frame, 0, frameSize, offset);\n if (frame.readUInt32BE(8) !== salt1 || frame.readUInt32BE(12) !== salt2) break; // stale frames\n pending.set(frame.readUInt32BE(0), new Uint8Array(frame.subarray(24)));\n offset += frameSize;\n const commit = frame.readUInt32BE(4);\n if (commit) {\n for (const [p, d] of pending) pages.set(p, d);\n pending.clear();\n pageCount = commit;\n committedTo = offset;\n }\n }\n this.#walOffset = committedTo;\n if (!pageCount)\n return {\n pageSize,\n pageCount: Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]),\n pages,\n };\n for (const p of [...pages.keys()]) if (p > pageCount) pages.delete(p);\n // keep the WAL bounded: fold it into the database and start over\n if (committedTo > 4 * 1024 * 1024) {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n return { pageSize, pageCount, pages };\n } finally {\n closeSync(fd);\n }\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n if (this.#pageCapture && !this.#taken && ++this.#writes % 64 === 0) this.#boundWal();\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const stmt = this.#db.prepare(sql);\n const columnNames = stmt.columns().map((c) => c.name);\n const args = params.map(toSqlite) as any[];\n if (columnNames.length === 0) {\n const r = stmt.run(...args);\n return { columnNames, rows: [], rowsWritten: Number(r.changes) };\n }\n stmt.setReturnArrays(true);\n const rows = (stmt.all(...args) as unknown as unknown[][]).map((row) => row.map(fromSqlite));\n return { columnNames, rows, rowsWritten: 0 };\n }\n /**\n * A database whose pages nobody takes (the runtime's own tables, a storage that logs no pages)\n * has no one to checkpoint its WAL (automatic checkpoints are off): past 4 MiB, fold it in.\n */\n #boundWal(): void {\n if (this.inTransaction()) return;\n try {\n if (statSync(`${this.#path}-wal`).size <= 4 * 1024 * 1024) return;\n } catch {\n return;\n }\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n inTransaction(): boolean {\n return (this.#db as unknown as { isTransaction: boolean }).isTransaction;\n }\n databaseSize(): number {\n const [pc] = this.exec(\"PRAGMA page_count\", []).rows[0] as [number];\n const [ps] = this.exec(\"PRAGMA page_size\", []).rows[0] as [number];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n if (!this.#temporary) return;\n for (const suffix of [\"\", \"-wal\", \"-shm\", \"-journal\"])\n rmSync(this.#path + suffix, { force: true });\n try {\n rmdirSync(dirname(this.#path)); // the opener's directory, once its last cell is closed\n temporaryDirs.delete(dirname(this.#path));\n } catch {}\n }\n\n // ---- change capture (session extension) for transaction logs -------------------------\n #session: { changeset(): Uint8Array; close(): void } | undefined;\n /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */\n readonly captureCoversRowidTables = false;\n captureStart(): void {\n this.#session?.close();\n this.#session = (\n this.#db as unknown as { createSession(): { changeset(): Uint8Array; close(): void } }\n ).createSession();\n }\n captureTake(): Uint8Array {\n const s = this.#session;\n this.#session = undefined;\n if (!s) return new Uint8Array();\n try {\n return s.changeset();\n } finally {\n s.close();\n }\n }\n captureDiscard(): void {\n this.#session?.close();\n this.#session = undefined;\n }\n applyChangeset(changeset: Uint8Array): void {\n if (!changeset.byteLength) return;\n const SQLITE_CHANGESET_REPLACE = 1;\n (this.#db as unknown as { applyChangeset(c: Uint8Array, o: unknown): boolean }).applyChangeset(\n changeset,\n {\n onConflict: () => SQLITE_CHANGESET_REPLACE,\n },\n );\n }\n\n /** Known extensions only: \"sqlite_vec\" → the sqlite-vec npm package's loadable library. */\n loadExtension(name: string): void {\n const path = KNOWN_EXTENSIONS[name]?.();\n if (!path) throw new Error(`Unknown SQLite extension \"${name}\"`);\n this.#db.enableLoadExtension(true);\n try {\n this.#db.loadExtension(path);\n } finally {\n this.#db.enableLoadExtension(false);\n }\n }\n\n /**\n * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory\n * database falls back to image(). Valid between transactions (rollback journal mode).\n */\n pageImage(): Uint8Array {\n if (this.#path === \":memory:\") return this.image();\n // A database restored from celld's files is in WAL mode (litestream needs it): its last\n // commits are in the -wal file until a checkpoint moves them into the main file.\n const mode = String(this.#db.prepare(\"PRAGMA journal_mode\").get()?.journal_mode ?? \"\");\n if (mode.toLowerCase() === \"wal\") {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0; // the WAL starts over\n }\n return new Uint8Array(readFileSync(this.#path));\n }\n\n /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */\n image(): Uint8Array {\n const tmp = join(mkdtempSync(join(tmpdir(), \"cell-snap-\")), \"db.sqlite\");\n try {\n this.#db.exec(`VACUUM INTO '${tmp.replace(/'/g, \"''\")}'`);\n return new Uint8Array(readFileSync(tmp));\n } finally {\n rmSync(dirname(tmp), { recursive: true, force: true });\n }\n }\n\n /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */\n restore(image: Uint8Array): void {\n this.#db.close();\n if (this.#path === \":memory:\")\n this.#path = join(mkdtempSync(join(tmpdir(), \"cell-mem-\")), \"db.sqlite\");\n for (const suffix of [\"-wal\", \"-shm\", \"-journal\"]) rmSync(this.#path + suffix, { force: true });\n writeFileSync(this.#path, image);\n this.#db = new DatabaseSync(this.#path, { enableDoubleQuotedStringLiterals: true });\n if (this.#exclusive) this.#db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n this.#walSetup(); // a new file: a new WAL\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\n/** Workers return BLOBs as ArrayBuffer. */\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** Temporary cell directories still present: removed if the process exits without closing them. */\nconst temporaryDirs = new Set<string>();\nlet exitHook = false;\nfunction removeAtExit(dir: string): void {\n temporaryDirs.add(dir);\n if (exitHook) return;\n exitHook = true;\n process.once(\"exit\", () => {\n for (const d of temporaryDirs) rmSync(d, { recursive: true, force: true });\n });\n}\n\n/**\n * One file per cell in `dir`; without `dir`, each cell in memory.\n *\n * `pageCapture`: WAL mode, and each commit's changed pages read from the WAL (takeDirtyPages),\n * so LTX logs (a celld bucket) cost O(changed pages) on Node as with SQLite WASM, instead of an\n * image of the whole database (VACUUM INTO) and a page-by-page diff per commit. A WAL needs a\n * file: without `dir`, each cell opened gets a temporary file (in a `cellb-cells-…` directory\n * under the OS temp directory), removed when the cell closes, so it still starts empty each\n * time as in memory (a bucket restores it).\n */\nexport function nodeSqliteOpener(\n dir?: string,\n opts: { exclusive?: boolean; pageCapture?: boolean } = {},\n): OpenCellSql {\n let temp: string | undefined;\n let opened = 0;\n return (cellKey) => {\n const name = cellKey.replace(/[^\\w.-]/g, \"_\");\n // The runtime's own databases (alarms index, cron state) log no pages: never captured.\n const capture = !!opts.pageCapture && !cellKey.startsWith(\"__cellb/\");\n const o = { ...opts, pageCapture: capture };\n if (dir) return new NodeSqliteDriver(`${dir}/${name}.sqlite`, o);\n if (!capture) return new NodeSqliteDriver(\":memory:\", o);\n if (!temp || !existsSync(temp)) {\n temp = mkdtempSync(join(tmpdir(), \"cellb-cells-\"));\n removeAtExit(temp);\n }\n // A file per opening: a cell reopened while its previous database is still closing gets\n // a fresh one, as in memory.\n return new NodeSqliteDriver(join(temp, `${name}-${++opened}.sqlite`), {\n ...o,\n temporary: true,\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"node-sqlite.js","sourceRoot":"","sources":["../../src/adapters/node-sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n closeSync,\n existsSync,\n fstatSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n rmdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { DatabaseSync as DatabaseSyncT } from \"node:sqlite\";\n\nimport { splitSqlStatements } from \"../core/sql-split.js\";\nimport type { OpenCellSql, SqlResult, SyncSqlDriver } from \"../ports.js\";\nimport { requireNodeSqlite } from \"./node-sqlite-module.js\";\n\nconst { DatabaseSync } = requireNodeSqlite();\n\nconst requireCjs = createRequire(import.meta.url);\nconst KNOWN_EXTENSIONS: Record<string, () => string> = {\n sqlite_vec: () => (requireCjs(\"sqlite-vec\") as { getLoadablePath(): string }).getLoadablePath(),\n};\n\n/** Server-side SyncSqlDriver over node:sqlite (Node >= 22.13). */\nexport class NodeSqliteDriver implements SyncSqlDriver {\n #db: DatabaseSyncT;\n #path: string;\n readonly #exclusive: boolean;\n readonly #pageCapture: boolean;\n /** A file of its own that close() removes (a cell \"in memory\" that needs a WAL). */\n readonly #temporary: boolean;\n constructor(\n path = \":memory:\",\n opts: { exclusive?: boolean; pageCapture?: boolean; temporary?: boolean } = {},\n ) {\n this.#path = path;\n this.#temporary = !!opts.temporary && path !== \":memory:\";\n this.#exclusive = !!opts.exclusive;\n this.#pageCapture = !!opts.pageCapture && path !== \":memory:\";\n this.#db = this.#connect();\n this.#walSetup();\n }\n\n /** Extensions loaded so far, by name: a new connection (restore()) loads them again. */\n #extensions: string[] = [];\n /** The directory restore() made for an in-memory database's file. */\n #restoredDir: string | undefined;\n\n /** A connection to `#path`, set up as every connection of this driver is. */\n #connect(): DatabaseSyncT {\n // As celld and workerd: a double-quoted name that is no column is a string literal.\n const db = new DatabaseSync(this.#path, {\n allowExtension: true,\n enableDoubleQuotedStringLiterals: true,\n });\n db.enableLoadExtension(false); // only loadExtension() below may load, by name\n // EXCLUSIVE locking: the first write takes an OS lock on the file that is\n // kept until close (or process death) — a second process can neither read\n // nor write the cell. Storage-level fencing on one host, no lease needed.\n if (this.#exclusive) db.exec(\"PRAGMA locking_mode = EXCLUSIVE; PRAGMA busy_timeout = 0;\");\n try {\n for (const name of this.#extensions) this.#load(db, name);\n } catch (e) {\n db.close(); // not left open behind the error\n throw e;\n }\n return db;\n }\n\n // ---- page capture through the WAL (what the WASM driver does with its xWrite hook) ----\n // In WAL mode every commit appends its changed pages to `<db>-wal` as frames (24-byte frame\n // header + page; a commit frame carries the database size in pages). Reading the frames\n // appended since the last read gives exactly the committed transaction's pages, O(changed),\n // instead of reading the whole file. Automatic checkpoints are off; take() checkpoints\n // (TRUNCATE) itself once the WAL grows, and restarts from its start.\n #walOffset = 0;\n /** Whether anyone reads the captured pages; until then exec() keeps the WAL bounded itself. */\n #taken = false;\n #writes = 0;\n #walSetup(): void {\n if (!this.#pageCapture) return;\n this.#db.exec(\"PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0;\");\n // A temporary file is only the WAL's carrier (the bucket keeps the cell): no fsync per commit.\n if (this.#temporary) this.#db.exec(\"PRAGMA synchronous = OFF\");\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n /** The pages of the transactions committed since the previous call (WAL frames). */\n takeDirtyPages():\n | { pageSize: number; pageCount: number; pages: Map<number, Uint8Array> }\n | undefined {\n if (!this.#pageCapture) return undefined;\n this.#taken = true;\n const walPath = `${this.#path}-wal`;\n let fd: number;\n try {\n fd = openSync(walPath, \"r\");\n } catch {\n return undefined;\n }\n try {\n const size = fstatSync(fd).size;\n if (size < 32) return undefined;\n const header = Buffer.alloc(32);\n readSync(fd, header, 0, 32, 0);\n const pageSize = header.readUInt32BE(8) === 1 ? 65536 : header.readUInt32BE(8);\n const [salt1, salt2] = [header.readUInt32BE(16), header.readUInt32BE(20)];\n const frameSize = 24 + pageSize;\n let offset = Math.max(32, this.#walOffset);\n const pending = new Map<number, Uint8Array>();\n const pages = new Map<number, Uint8Array>();\n let pageCount = 0;\n let committedTo = offset;\n const frame = Buffer.alloc(frameSize);\n while (offset + frameSize <= size) {\n readSync(fd, frame, 0, frameSize, offset);\n if (frame.readUInt32BE(8) !== salt1 || frame.readUInt32BE(12) !== salt2) break; // stale frames\n pending.set(frame.readUInt32BE(0), new Uint8Array(frame.subarray(24)));\n offset += frameSize;\n const commit = frame.readUInt32BE(4);\n if (commit) {\n for (const [p, d] of pending) pages.set(p, d);\n pending.clear();\n pageCount = commit;\n committedTo = offset;\n }\n }\n this.#walOffset = committedTo;\n if (!pageCount)\n return {\n pageSize,\n pageCount: Number(this.exec(\"PRAGMA page_count\", []).rows[0][0]),\n pages,\n };\n for (const p of [...pages.keys()]) if (p > pageCount) pages.delete(p);\n // keep the WAL bounded: fold it into the database and start over\n if (committedTo > 4 * 1024 * 1024) {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n return { pageSize, pageCount, pages };\n } finally {\n closeSync(fd);\n }\n }\n exec(sql: string, params: unknown[]): SqlResult {\n const statements = params.length === 0 ? splitSqlStatements(sql) : [sql];\n // Several statements: the result of the last one, with the rows written by all of them.\n let last: SqlResult = { columnNames: [], rows: [], rowsWritten: 0 };\n let written = 0;\n for (const text of statements) {\n last = this.#one(text, params);\n written += last.rowsWritten;\n }\n if (this.#pageCapture && !this.#taken && ++this.#writes % 64 === 0) this.#boundWal();\n return { ...last, rowsWritten: written };\n }\n #one(sql: string, params: unknown[]): SqlResult {\n const stmt = this.#db.prepare(sql);\n const columnNames = stmt.columns().map((c) => c.name);\n const args = params.map(toSqlite) as any[];\n if (columnNames.length === 0) {\n const r = stmt.run(...args);\n return { columnNames, rows: [], rowsWritten: Number(r.changes) };\n }\n stmt.setReturnArrays(true);\n const rows = (stmt.all(...args) as unknown as unknown[][]).map((row) => row.map(fromSqlite));\n return { columnNames, rows, rowsWritten: 0 };\n }\n /**\n * A database whose pages nobody takes (the runtime's own tables, a storage that logs no pages)\n * has no one to checkpoint its WAL (automatic checkpoints are off): past 4 MiB, fold it in.\n */\n #boundWal(): void {\n if (this.inTransaction()) return;\n try {\n if (statSync(`${this.#path}-wal`).size <= 4 * 1024 * 1024) return;\n } catch {\n return;\n }\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0;\n }\n inTransaction(): boolean {\n return (this.#db as unknown as { isTransaction: boolean }).isTransaction;\n }\n databaseSize(): number {\n const [pc] = this.exec(\"PRAGMA page_count\", []).rows[0] as [number];\n const [ps] = this.exec(\"PRAGMA page_size\", []).rows[0] as [number];\n return Number(pc) * Number(ps);\n }\n close(): void {\n this.#db.close();\n if (this.#restoredDir) {\n rmSync(this.#restoredDir, { recursive: true, force: true });\n temporaryDirs.delete(this.#restoredDir);\n this.#restoredDir = undefined;\n }\n if (!this.#temporary) return;\n for (const suffix of [\"\", \"-wal\", \"-shm\", \"-journal\"])\n rmSync(this.#path + suffix, { force: true });\n try {\n rmdirSync(dirname(this.#path)); // the opener's directory, once its last cell is closed\n temporaryDirs.delete(dirname(this.#path));\n } catch {}\n }\n\n // ---- change capture (session extension) for transaction logs -------------------------\n #session: { changeset(): Uint8Array; close(): void } | undefined;\n /** node:sqlite exposes no ROWID session option: PK-less tables are logged as statements. */\n readonly captureCoversRowidTables = false;\n captureStart(): void {\n this.#session?.close();\n this.#session = (\n this.#db as unknown as { createSession(): { changeset(): Uint8Array; close(): void } }\n ).createSession();\n }\n captureTake(): Uint8Array {\n const s = this.#session;\n this.#session = undefined;\n if (!s) return new Uint8Array();\n try {\n return s.changeset();\n } finally {\n s.close();\n }\n }\n captureDiscard(): void {\n this.#session?.close();\n this.#session = undefined;\n }\n applyChangeset(changeset: Uint8Array): void {\n if (!changeset.byteLength) return;\n const SQLITE_CHANGESET_REPLACE = 1;\n (this.#db as unknown as { applyChangeset(c: Uint8Array, o: unknown): boolean }).applyChangeset(\n changeset,\n {\n onConflict: () => SQLITE_CHANGESET_REPLACE,\n },\n );\n }\n\n /** Known extensions only: \"sqlite_vec\" → the sqlite-vec npm package's loadable library. */\n loadExtension(name: string): void {\n this.#load(this.#db, name);\n if (!this.#extensions.includes(name)) this.#extensions.push(name);\n }\n #load(db: DatabaseSyncT, name: string): void {\n const path = KNOWN_EXTENSIONS[name]?.();\n if (!path) throw new Error(`Unknown SQLite extension \"${name}\"`);\n db.enableLoadExtension(true);\n try {\n db.loadExtension(path);\n } finally {\n db.enableLoadExtension(false);\n }\n }\n\n /**\n * The database file as SQLite wrote it (page for page), for page-level logs; an in-memory\n * database falls back to image(). Valid between transactions (rollback journal mode).\n */\n pageImage(): Uint8Array {\n if (this.#path === \":memory:\") return this.image();\n // A database restored from celld's files is in WAL mode (litestream needs it): its last\n // commits are in the -wal file until a checkpoint moves them into the main file.\n const mode = String(this.#db.prepare(\"PRAGMA journal_mode\").get()?.journal_mode ?? \"\");\n if (mode.toLowerCase() === \"wal\") {\n this.#db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n this.#walOffset = 0; // the WAL starts over\n }\n return new Uint8Array(readFileSync(this.#path));\n }\n\n /** `VACUUM INTO` a temp file: a consistent, compact image (no open transaction allowed). */\n image(): Uint8Array {\n const tmp = join(mkdtempSync(join(tmpdir(), \"cell-snap-\")), \"db.sqlite\");\n try {\n this.#db.exec(`VACUUM INTO '${tmp.replace(/'/g, \"''\")}'`);\n return new Uint8Array(readFileSync(tmp));\n } finally {\n rmSync(dirname(tmp), { recursive: true, force: true });\n }\n }\n\n /** Replaces the database file with `image` and reopens it (an in-memory DB becomes file-backed). */\n restore(image: Uint8Array): void {\n this.#db.close();\n if (this.#path === \":memory:\") {\n // A directory of its own, removed with the driver (close() or process exit).\n this.#restoredDir = mkdtempSync(join(tmpdir(), \"cell-mem-\"));\n removeAtExit(this.#restoredDir);\n this.#path = join(this.#restoredDir, \"db.sqlite\");\n }\n for (const suffix of [\"-wal\", \"-shm\", \"-journal\"]) rmSync(this.#path + suffix, { force: true });\n writeFileSync(this.#path, image);\n this.#db = this.#connect(); // with the extensions loaded before\n this.#walSetup(); // a new file: a new WAL\n }\n}\n\nfunction toSqlite(v: unknown): unknown {\n if (v instanceof ArrayBuffer) return new Uint8Array(v);\n if (typeof v === \"boolean\") return v ? 1 : 0;\n return v === undefined ? null : v;\n}\n/** Workers return BLOBs as ArrayBuffer. */\nfunction fromSqlite(v: unknown): unknown {\n if (v instanceof Uint8Array) return v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength);\n return v;\n}\n\n/** Temporary cell directories still present: removed if the process exits without closing them. */\nconst temporaryDirs = new Set<string>();\nlet exitHook = false;\nfunction removeAtExit(dir: string): void {\n temporaryDirs.add(dir);\n if (exitHook) return;\n exitHook = true;\n process.once(\"exit\", () => {\n for (const d of temporaryDirs) rmSync(d, { recursive: true, force: true });\n });\n}\n\n/**\n * One file per cell in `dir`; without `dir`, each cell in memory.\n *\n * `pageCapture`: WAL mode, and each commit's changed pages read from the WAL (takeDirtyPages),\n * so LTX logs (a celld bucket) cost O(changed pages) on Node as with SQLite WASM, instead of an\n * image of the whole database (VACUUM INTO) and a page-by-page diff per commit. A WAL needs a\n * file: without `dir`, each cell opened gets a temporary file (in a `cellb-cells-…` directory\n * under the OS temp directory), removed when the cell closes, so it still starts empty each\n * time as in memory (a bucket restores it).\n */\nexport function nodeSqliteOpener(\n dir?: string,\n opts: { exclusive?: boolean; pageCapture?: boolean } = {},\n): OpenCellSql {\n let temp: string | undefined;\n let opened = 0;\n return (cellKey) => {\n const name = cellKey.replace(/[^\\w.-]/g, \"_\");\n // The runtime's own databases (alarms index, cron state) log no pages: never captured.\n const capture = !!opts.pageCapture && !cellKey.startsWith(\"__cellb/\");\n const o = { ...opts, pageCapture: capture };\n if (dir) return new NodeSqliteDriver(`${dir}/${name}.sqlite`, o);\n if (!capture) return new NodeSqliteDriver(\":memory:\", o);\n if (!temp || !existsSync(temp)) {\n temp = mkdtempSync(join(tmpdir(), \"cellb-cells-\"));\n removeAtExit(temp);\n }\n // A file per opening: a cell reopened while its previous database is still closing gets\n // a fresh one, as in memory.\n return new NodeSqliteDriver(join(temp, `${name}-${++opened}.sqlite`), {\n ...o,\n temporary: true,\n });\n };\n}\n"]}
|
package/package.json
CHANGED