@statewalker/cellb 0.5.2 → 0.6.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.
@@ -21,16 +21,38 @@ export function installLtxVfs(sqlite3, channel, opts = {}) {
21
21
  const ctl = new Int32Array(channel.sab, 0, 2);
22
22
  const payload = new Uint8Array(channel.sab, 8);
23
23
  const stats = { reads: 0, cacheHits: 0, storeReads: 0, commits: 0, blockedMs: 0 };
24
+ /**
25
+ * Each request's number: the store tags its answer with it (state word = seq << 2 | state),
26
+ * so the late answer to a request this worker gave up on is not taken for the next one's.
27
+ */
28
+ let seq = 0;
24
29
  /** A request answered synchronously: blocks this worker until the store notifies. */
25
30
  const call = (msg) => {
31
+ seq = (seq % 0x1fffffff) + 1;
26
32
  Atomics.store(ctl, 0, 0);
27
33
  const t = performance.now();
28
- channel.post(msg);
34
+ // `until` (wall clock, which both threads share): a commit the store could only finish
35
+ // near it is not kept, since this worker may already have given up on it.
36
+ channel.post({ ...msg, seq, until: Date.now() + 30_000 });
29
37
  // A store that never answers (a worker that failed to load) must not freeze this worker.
30
- if (Atomics.wait(ctl, 0, 0, 30_000) === "timed-out")
31
- throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);
38
+ const deadline = t + 30_000;
39
+ let word = 0;
40
+ for (;;) {
41
+ const left = deadline - performance.now();
42
+ if (left > 0)
43
+ Atomics.wait(ctl, 0, 0, left);
44
+ word = Atomics.load(ctl, 0);
45
+ // Out of time: unless this request's answer is there now, it is abandoned (the store
46
+ // does not keep a commit past `until`, nor serve a request once a newer one came).
47
+ if (performance.now() >= deadline && word >>> 2 !== seq)
48
+ throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);
49
+ if (word >>> 2 === seq)
50
+ break;
51
+ // An answer to an earlier request (given up on): not this one's; wait on.
52
+ Atomics.compareExchange(ctl, 0, word, 0);
53
+ }
32
54
  stats.blockedMs += performance.now() - t;
33
- if (ctl[0] === 2)
55
+ if ((word & 3) === 2)
34
56
  throw new Error(new TextDecoder().decode(payload.slice(0, ctl[1])));
35
57
  return payload.slice(0, ctl[1]);
36
58
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ltx-vfs.js","sourceRoot":"","sources":["../../src/adapters/ltx-vfs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A synchronous SQLite VFS (SQLite WASM, in a worker) over an ASYNCHRONOUS page store in another\n * thread: the database is a cell's LTX files on a FilesApi (src/ltx/ltx-pages.ts, served by\n * ltxPageServer). Reads block on a SharedArrayBuffer (Atomics.wait) while the store reads the\n * page by range; pages are cached here (LRU). Writes stay in this worker until the commit\n * (xSync of the main file), which sends the transaction's pages to the store: one LTX file.\n * The rollback journal lives in memory (journal_mode=MEMORY): only the main file is a file.\n *\n * Needs cross-origin isolation (SharedArrayBuffer). One writer per database.\n */\nconst SQLITE_OK = 0;\nconst SQLITE_IOERR = 10;\nconst SQLITE_IOERR_SHORT_READ = 522;\nconst SQLITE_OPEN_MAIN_DB = 0x100;\n\nexport interface LtxVfsChannel {\n /** Shared control words [state, length] + payload (≥ one page). */\n sab: SharedArrayBuffer;\n /** Posts a request to the store (it answers into `sab`, then notifies). */\n post(msg: unknown): void;\n}\n\nexport interface LtxVfsStats {\n reads: number;\n cacheHits: number;\n storeReads: number;\n commits: number;\n blockedMs: number;\n}\n\nexport function installLtxVfs(\n sqlite3: any,\n channel: LtxVfsChannel,\n opts: { name?: string; cachePages?: number; readAhead?: number } = {},\n): { name: string; stats: LtxVfsStats } {\n const { capi, wasm } = sqlite3;\n const name = opts.name ?? \"ltx\";\n const cacheMax = opts.cachePages ?? 2048;\n // A miss fetches this many consecutive pages at once (SQLite scans walk pages in order).\n const readAhead = Math.max(1, opts.readAhead ?? 16);\n const ctl = new Int32Array(channel.sab, 0, 2);\n const payload = new Uint8Array(channel.sab, 8);\n const stats: LtxVfsStats = { reads: 0, cacheHits: 0, storeReads: 0, commits: 0, blockedMs: 0 };\n\n /** A request answered synchronously: blocks this worker until the store notifies. */\n const call = (msg: Record<string, unknown>): Uint8Array => {\n Atomics.store(ctl, 0, 0);\n const t = performance.now();\n channel.post(msg);\n // A store that never answers (a worker that failed to load) must not freeze this worker.\n if (Atomics.wait(ctl, 0, 0, 30_000) === \"timed-out\")\n throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);\n stats.blockedMs += performance.now() - t;\n if (ctl[0] === 2) throw new Error(new TextDecoder().decode(payload.slice(0, ctl[1])));\n return payload.slice(0, ctl[1]);\n };\n /** One open database (a cell): its size, its page cache, the current transaction's pages. */\n interface Db {\n cell: string;\n pageSize: number;\n pageCount: number;\n cache: Map<number, Uint8Array>; // LRU: insertion order\n dirty: Map<number, Uint8Array>;\n }\n const dbs = new Map<string, Db>();\n const dbOf = (cell: string): Db => {\n let d = dbs.get(cell);\n if (!d) {\n const meta = JSON.parse(new TextDecoder().decode(call({ op: \"meta\", cell }))) as {\n pageSize: number;\n pageCount: number;\n };\n d = {\n cell,\n pageSize: meta.pageSize || 4096,\n pageCount: meta.pageCount,\n cache: new Map(),\n dirty: new Map(),\n };\n dbs.set(cell, d);\n }\n return d;\n };\n const page = (d: Db, pgno: number): Uint8Array => {\n const w = d.dirty.get(pgno);\n if (w) return w;\n stats.reads++;\n const c = d.cache.get(pgno);\n if (c) {\n stats.cacheHits++;\n d.cache.delete(pgno);\n d.cache.set(pgno, c);\n return c;\n }\n stats.storeReads++;\n if (pgno > d.pageCount) return new Uint8Array(d.pageSize);\n const count = Math.min(\n readAhead,\n d.pageCount - pgno + 1,\n Math.floor((channel.sab.byteLength - 8) / d.pageSize),\n );\n const run = call({ op: \"readRun\", cell: d.cell, pgno, count });\n for (let i = 0; i * d.pageSize < run.length; i++) {\n const q = pgno + i;\n if (d.dirty.has(q)) continue;\n d.cache.set(q, run.subarray(i * d.pageSize, (i + 1) * d.pageSize));\n if (d.cache.size > cacheMax) d.cache.delete(d.cache.keys().next().value as number);\n }\n return d.cache.get(pgno) ?? new Uint8Array(d.pageSize);\n };\n\n // Journal and other non-main files: plain memory.\n const memFiles = new Map<string, Uint8Array>();\n const open = new Map<number, { main: boolean; path: string; db?: Db }>();\n\n const io = new capi.sqlite3_io_methods();\n io.$iVersion = 1;\n const vfs = new capi.sqlite3_vfs();\n vfs.$iVersion = 1;\n vfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;\n vfs.$mxPathname = 512;\n\n sqlite3.vfs.installVfs({\n io: {\n struct: io,\n methods: {\n xClose(pFile: number) {\n const d = open.get(pFile)?.db;\n if (d) dbs.delete(d.cell); // its cache goes with it\n open.delete(pFile);\n return SQLITE_OK;\n },\n xRead(pFile: number, pDest: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const heap = wasm.heap8u();\n if (!f?.main) {\n const b = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const got = b.subarray(at, at + n);\n heap.set(got, pDest);\n if (got.length < n) {\n heap.fill(0, pDest + got.length, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n }\n try {\n const d = f.db as Db;\n const { pageSize } = d;\n const size = d.pageCount * pageSize;\n let o = 0;\n while (o < n && at + o < size) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n heap.set(page(d, pgno).subarray(inPage, inPage + take), pDest + o);\n o += take;\n }\n if (o < n) {\n heap.fill(0, pDest + o, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xWrite(pFile: number, pSrc: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const src = wasm.heap8u().subarray(pSrc, pSrc + n);\n if (!f?.main) {\n const cur = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const next = cur.length >= at + n ? cur : new Uint8Array(at + n);\n if (next !== cur) next.set(cur);\n next.set(src, at);\n memFiles.set(f?.path ?? \"\", next);\n return SQLITE_OK;\n }\n const d = f.db as Db;\n if (d.pageCount === 0 && at === 0 && n >= 18) {\n const ps = (src[16] << 8) | src[17];\n d.pageSize = ps === 1 ? 65536 : ps;\n }\n const { pageSize } = d;\n for (let o = 0; o < n; ) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n let p = d.dirty.get(pgno);\n if (!p) {\n p = take === pageSize ? new Uint8Array(pageSize) : page(d, pgno).slice();\n d.dirty.set(pgno, p);\n }\n p.set(src.subarray(o, o + take), inPage);\n o += take;\n if (pgno > d.pageCount) d.pageCount = pgno;\n }\n return SQLITE_OK;\n },\n xTruncate(pFile: number, size: bigint) {\n const f = open.get(pFile);\n if (!f?.main) {\n memFiles.set(\n f?.path ?? \"\",\n (memFiles.get(f?.path ?? \"\") ?? new Uint8Array()).slice(0, Number(size)),\n );\n return SQLITE_OK;\n }\n const d = f.db as Db;\n d.pageCount = Math.ceil(Number(size) / d.pageSize);\n for (const k of [...d.dirty.keys()]) if (k > d.pageCount) d.dirty.delete(k);\n return SQLITE_OK;\n },\n xSync(pFile: number) {\n const d = open.get(pFile)?.db;\n if (!d || !d.dirty.size) return SQLITE_OK;\n // The commit: this transaction's pages become one LTX file in the store.\n try {\n const pages = [...d.dirty].sort((a, b) => a[0] - b[0]);\n call({\n op: \"commit\",\n cell: d.cell,\n pageSize: d.pageSize,\n pageCount: d.pageCount,\n pages,\n });\n stats.commits++;\n for (const [pgno, p] of pages) d.cache.set(pgno, p);\n d.dirty.clear();\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xFileSize(pFile: number, pOut: number) {\n const f = open.get(pFile);\n const size = f?.db\n ? f.db.pageCount * f.db.pageSize\n : (memFiles.get(f?.path ?? \"\")?.length ?? 0);\n wasm.poke64(pOut, BigInt(size));\n return SQLITE_OK;\n },\n xLock: () => SQLITE_OK,\n xUnlock: () => SQLITE_OK,\n xCheckReservedLock(_p: number, pOut: number) {\n wasm.poke32(pOut, 0);\n return SQLITE_OK;\n },\n xFileControl: () => 12, // SQLITE_NOTFOUND\n xSectorSize: () => 4096,\n xDeviceCharacteristics: () => 0,\n },\n },\n vfs: {\n struct: vfs,\n name,\n methods: {\n xOpen(_pVfs: number, zName: number, pFile: number, flags: number, pOutFlags: number) {\n const path = zName ? wasm.cstrToJs(zName) : `tmp-${pFile}`;\n const main = !!(flags & SQLITE_OPEN_MAIN_DB);\n try {\n open.set(pFile, { main, path, db: main ? dbOf(path) : undefined });\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n const f = new capi.sqlite3_file(pFile);\n f.$pMethods = io.pointer;\n f.dispose();\n if (pOutFlags) wasm.poke32(pOutFlags, flags);\n return SQLITE_OK;\n },\n xDelete(_p: number, zName: number) {\n memFiles.delete(wasm.cstrToJs(zName));\n return SQLITE_OK;\n },\n xAccess(_p: number, zName: number, _flags: number, pOut: number) {\n wasm.poke32(pOut, memFiles.has(wasm.cstrToJs(zName)) ? 1 : 0);\n return SQLITE_OK;\n },\n xFullPathname(_p: number, zName: number, nOut: number, pOut: number) {\n wasm.cstrncpy(pOut, zName, nOut);\n return SQLITE_OK;\n },\n xRandomness(_p: number, n: number, pOut: number) {\n crypto.getRandomValues(wasm.heap8u().subarray(pOut, pOut + n));\n return n;\n },\n xSleep: () => SQLITE_OK,\n xCurrentTime(_p: number, pOut: number) {\n wasm.poke64f(pOut, 2440587.5 + Date.now() / 86400000);\n return SQLITE_OK;\n },\n xCurrentTimeInt64(_p: number, pOut: number) {\n wasm.poke64(pOut, BigInt(Math.floor(2440587.5 * 86400000 + Date.now())));\n return SQLITE_OK;\n },\n xGetLastError: () => 0,\n },\n },\n });\n return { name, stats };\n}\n"]}
1
+ {"version":3,"file":"ltx-vfs.js","sourceRoot":"","sources":["../../src/adapters/ltx-vfs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A synchronous SQLite VFS (SQLite WASM, in a worker) over an ASYNCHRONOUS page store in another\n * thread: the database is a cell's LTX files on a FilesApi (src/ltx/ltx-pages.ts, served by\n * ltxPageServer). Reads block on a SharedArrayBuffer (Atomics.wait) while the store reads the\n * page by range; pages are cached here (LRU). Writes stay in this worker until the commit\n * (xSync of the main file), which sends the transaction's pages to the store: one LTX file.\n * The rollback journal lives in memory (journal_mode=MEMORY): only the main file is a file.\n *\n * Needs cross-origin isolation (SharedArrayBuffer). One writer per database.\n */\nconst SQLITE_OK = 0;\nconst SQLITE_IOERR = 10;\nconst SQLITE_IOERR_SHORT_READ = 522;\nconst SQLITE_OPEN_MAIN_DB = 0x100;\n\nexport interface LtxVfsChannel {\n /** Shared control words [state, length] + payload (≥ one page). */\n sab: SharedArrayBuffer;\n /** Posts a request to the store (it answers into `sab`, then notifies). */\n post(msg: unknown): void;\n}\n\nexport interface LtxVfsStats {\n reads: number;\n cacheHits: number;\n storeReads: number;\n commits: number;\n blockedMs: number;\n}\n\nexport function installLtxVfs(\n sqlite3: any,\n channel: LtxVfsChannel,\n opts: { name?: string; cachePages?: number; readAhead?: number } = {},\n): { name: string; stats: LtxVfsStats } {\n const { capi, wasm } = sqlite3;\n const name = opts.name ?? \"ltx\";\n const cacheMax = opts.cachePages ?? 2048;\n // A miss fetches this many consecutive pages at once (SQLite scans walk pages in order).\n const readAhead = Math.max(1, opts.readAhead ?? 16);\n const ctl = new Int32Array(channel.sab, 0, 2);\n const payload = new Uint8Array(channel.sab, 8);\n const stats: LtxVfsStats = { reads: 0, cacheHits: 0, storeReads: 0, commits: 0, blockedMs: 0 };\n\n /**\n * Each request's number: the store tags its answer with it (state word = seq << 2 | state),\n * so the late answer to a request this worker gave up on is not taken for the next one's.\n */\n let seq = 0;\n /** A request answered synchronously: blocks this worker until the store notifies. */\n const call = (msg: Record<string, unknown>): Uint8Array => {\n seq = (seq % 0x1fffffff) + 1;\n Atomics.store(ctl, 0, 0);\n const t = performance.now();\n // `until` (wall clock, which both threads share): a commit the store could only finish\n // near it is not kept, since this worker may already have given up on it.\n channel.post({ ...msg, seq, until: Date.now() + 30_000 });\n // A store that never answers (a worker that failed to load) must not freeze this worker.\n const deadline = t + 30_000;\n let word = 0;\n for (;;) {\n const left = deadline - performance.now();\n if (left > 0) Atomics.wait(ctl, 0, 0, left);\n word = Atomics.load(ctl, 0);\n // Out of time: unless this request's answer is there now, it is abandoned (the store\n // does not keep a commit past `until`, nor serve a request once a newer one came).\n if (performance.now() >= deadline && word >>> 2 !== seq)\n throw new Error(`ltx store: no answer to ${String(msg.op)} in 30 s`);\n if (word >>> 2 === seq) break;\n // An answer to an earlier request (given up on): not this one's; wait on.\n Atomics.compareExchange(ctl, 0, word, 0);\n }\n stats.blockedMs += performance.now() - t;\n if ((word & 3) === 2) throw new Error(new TextDecoder().decode(payload.slice(0, ctl[1])));\n return payload.slice(0, ctl[1]);\n };\n /** One open database (a cell): its size, its page cache, the current transaction's pages. */\n interface Db {\n cell: string;\n pageSize: number;\n pageCount: number;\n cache: Map<number, Uint8Array>; // LRU: insertion order\n dirty: Map<number, Uint8Array>;\n }\n const dbs = new Map<string, Db>();\n const dbOf = (cell: string): Db => {\n let d = dbs.get(cell);\n if (!d) {\n const meta = JSON.parse(new TextDecoder().decode(call({ op: \"meta\", cell }))) as {\n pageSize: number;\n pageCount: number;\n };\n d = {\n cell,\n pageSize: meta.pageSize || 4096,\n pageCount: meta.pageCount,\n cache: new Map(),\n dirty: new Map(),\n };\n dbs.set(cell, d);\n }\n return d;\n };\n const page = (d: Db, pgno: number): Uint8Array => {\n const w = d.dirty.get(pgno);\n if (w) return w;\n stats.reads++;\n const c = d.cache.get(pgno);\n if (c) {\n stats.cacheHits++;\n d.cache.delete(pgno);\n d.cache.set(pgno, c);\n return c;\n }\n stats.storeReads++;\n if (pgno > d.pageCount) return new Uint8Array(d.pageSize);\n const count = Math.min(\n readAhead,\n d.pageCount - pgno + 1,\n Math.floor((channel.sab.byteLength - 8) / d.pageSize),\n );\n const run = call({ op: \"readRun\", cell: d.cell, pgno, count });\n for (let i = 0; i * d.pageSize < run.length; i++) {\n const q = pgno + i;\n if (d.dirty.has(q)) continue;\n d.cache.set(q, run.subarray(i * d.pageSize, (i + 1) * d.pageSize));\n if (d.cache.size > cacheMax) d.cache.delete(d.cache.keys().next().value as number);\n }\n return d.cache.get(pgno) ?? new Uint8Array(d.pageSize);\n };\n\n // Journal and other non-main files: plain memory.\n const memFiles = new Map<string, Uint8Array>();\n const open = new Map<number, { main: boolean; path: string; db?: Db }>();\n\n const io = new capi.sqlite3_io_methods();\n io.$iVersion = 1;\n const vfs = new capi.sqlite3_vfs();\n vfs.$iVersion = 1;\n vfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;\n vfs.$mxPathname = 512;\n\n sqlite3.vfs.installVfs({\n io: {\n struct: io,\n methods: {\n xClose(pFile: number) {\n const d = open.get(pFile)?.db;\n if (d) dbs.delete(d.cell); // its cache goes with it\n open.delete(pFile);\n return SQLITE_OK;\n },\n xRead(pFile: number, pDest: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const heap = wasm.heap8u();\n if (!f?.main) {\n const b = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const got = b.subarray(at, at + n);\n heap.set(got, pDest);\n if (got.length < n) {\n heap.fill(0, pDest + got.length, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n }\n try {\n const d = f.db as Db;\n const { pageSize } = d;\n const size = d.pageCount * pageSize;\n let o = 0;\n while (o < n && at + o < size) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n heap.set(page(d, pgno).subarray(inPage, inPage + take), pDest + o);\n o += take;\n }\n if (o < n) {\n heap.fill(0, pDest + o, pDest + n);\n return SQLITE_IOERR_SHORT_READ;\n }\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xWrite(pFile: number, pSrc: number, n: number, off: bigint) {\n const f = open.get(pFile);\n const at = Number(off);\n const src = wasm.heap8u().subarray(pSrc, pSrc + n);\n if (!f?.main) {\n const cur = memFiles.get(f?.path ?? \"\") ?? new Uint8Array();\n const next = cur.length >= at + n ? cur : new Uint8Array(at + n);\n if (next !== cur) next.set(cur);\n next.set(src, at);\n memFiles.set(f?.path ?? \"\", next);\n return SQLITE_OK;\n }\n const d = f.db as Db;\n if (d.pageCount === 0 && at === 0 && n >= 18) {\n const ps = (src[16] << 8) | src[17];\n d.pageSize = ps === 1 ? 65536 : ps;\n }\n const { pageSize } = d;\n for (let o = 0; o < n; ) {\n const pos = at + o;\n const pgno = Math.floor(pos / pageSize) + 1;\n const inPage = pos % pageSize;\n const take = Math.min(pageSize - inPage, n - o);\n let p = d.dirty.get(pgno);\n if (!p) {\n p = take === pageSize ? new Uint8Array(pageSize) : page(d, pgno).slice();\n d.dirty.set(pgno, p);\n }\n p.set(src.subarray(o, o + take), inPage);\n o += take;\n if (pgno > d.pageCount) d.pageCount = pgno;\n }\n return SQLITE_OK;\n },\n xTruncate(pFile: number, size: bigint) {\n const f = open.get(pFile);\n if (!f?.main) {\n memFiles.set(\n f?.path ?? \"\",\n (memFiles.get(f?.path ?? \"\") ?? new Uint8Array()).slice(0, Number(size)),\n );\n return SQLITE_OK;\n }\n const d = f.db as Db;\n d.pageCount = Math.ceil(Number(size) / d.pageSize);\n for (const k of [...d.dirty.keys()]) if (k > d.pageCount) d.dirty.delete(k);\n return SQLITE_OK;\n },\n xSync(pFile: number) {\n const d = open.get(pFile)?.db;\n if (!d || !d.dirty.size) return SQLITE_OK;\n // The commit: this transaction's pages become one LTX file in the store.\n try {\n const pages = [...d.dirty].sort((a, b) => a[0] - b[0]);\n call({\n op: \"commit\",\n cell: d.cell,\n pageSize: d.pageSize,\n pageCount: d.pageCount,\n pages,\n });\n stats.commits++;\n for (const [pgno, p] of pages) d.cache.set(pgno, p);\n d.dirty.clear();\n return SQLITE_OK;\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n },\n xFileSize(pFile: number, pOut: number) {\n const f = open.get(pFile);\n const size = f?.db\n ? f.db.pageCount * f.db.pageSize\n : (memFiles.get(f?.path ?? \"\")?.length ?? 0);\n wasm.poke64(pOut, BigInt(size));\n return SQLITE_OK;\n },\n xLock: () => SQLITE_OK,\n xUnlock: () => SQLITE_OK,\n xCheckReservedLock(_p: number, pOut: number) {\n wasm.poke32(pOut, 0);\n return SQLITE_OK;\n },\n xFileControl: () => 12, // SQLITE_NOTFOUND\n xSectorSize: () => 4096,\n xDeviceCharacteristics: () => 0,\n },\n },\n vfs: {\n struct: vfs,\n name,\n methods: {\n xOpen(_pVfs: number, zName: number, pFile: number, flags: number, pOutFlags: number) {\n const path = zName ? wasm.cstrToJs(zName) : `tmp-${pFile}`;\n const main = !!(flags & SQLITE_OPEN_MAIN_DB);\n try {\n open.set(pFile, { main, path, db: main ? dbOf(path) : undefined });\n } catch (err) {\n // SQLite only sees SQLITE_IOERR (and rolls the transaction back): say why.\n console.error(`cellb ltx-vfs: ${(err as Error)?.message ?? err}`);\n return SQLITE_IOERR;\n }\n const f = new capi.sqlite3_file(pFile);\n f.$pMethods = io.pointer;\n f.dispose();\n if (pOutFlags) wasm.poke32(pOutFlags, flags);\n return SQLITE_OK;\n },\n xDelete(_p: number, zName: number) {\n memFiles.delete(wasm.cstrToJs(zName));\n return SQLITE_OK;\n },\n xAccess(_p: number, zName: number, _flags: number, pOut: number) {\n wasm.poke32(pOut, memFiles.has(wasm.cstrToJs(zName)) ? 1 : 0);\n return SQLITE_OK;\n },\n xFullPathname(_p: number, zName: number, nOut: number, pOut: number) {\n wasm.cstrncpy(pOut, zName, nOut);\n return SQLITE_OK;\n },\n xRandomness(_p: number, n: number, pOut: number) {\n crypto.getRandomValues(wasm.heap8u().subarray(pOut, pOut + n));\n return n;\n },\n xSleep: () => SQLITE_OK,\n xCurrentTime(_p: number, pOut: number) {\n wasm.poke64f(pOut, 2440587.5 + Date.now() / 86400000);\n return SQLITE_OK;\n },\n xCurrentTimeInt64(_p: number, pOut: number) {\n wasm.poke64(pOut, BigInt(Math.floor(2440587.5 * 86400000 + Date.now())));\n return SQLITE_OK;\n },\n xGetLastError: () => 0,\n },\n },\n });\n return { name, stats };\n}\n"]}
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { sahpoolOpener } from "./adapters/wasm-sqlite.js";
2
2
  export { idbObjectStore } from "./bucket/idb-object-store.js";
3
+ export { type FilesRpcOptions, rpcFilesClient, serveFilesApi } from "./ltx/files-rpc.js";
4
+ export { filesApiPort, type ServeLtxStoreOptions, serveLtxStore } from "./ltx/ltx-host.js";
5
+ export type { LtxStoreFiles } from "./ltx/ltx-page-server.js";
3
6
  export { type BrowserBucket, type BrowserProjectOptions, browserProjectRuntime, } from "./project/browser-host.js";
4
7
  export { type BrowserLoadProjectOptions, loadProject } from "./project/browser-load.js";
5
8
  export { type ProjectShards, serveProjectShard, startProjectShards, } from "./project/browser-shards.js";
package/dist/browser.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export { sahpoolOpener } from "./adapters/wasm-sqlite.js";
2
2
  export { idbObjectStore } from "./bucket/idb-object-store.js";
3
+ export { rpcFilesClient, serveFilesApi } from "./ltx/files-rpc.js";
4
+ export { filesApiPort, serveLtxStore } from "./ltx/ltx-host.js";
3
5
  export { browserProjectRuntime, } from "./project/browser-host.js";
4
6
  export { loadProject } from "./project/browser-load.js";
5
7
  export { serveProjectShard, startProjectShards, } from "./project/browser-shards.js";
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAGL,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAkC,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAEL,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EAIjB,UAAU,GACX,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["export { sahpoolOpener } from \"./adapters/wasm-sqlite.js\";\nexport { idbObjectStore } from \"./bucket/idb-object-store.js\";\nexport {\n type BrowserBucket,\n type BrowserProjectOptions,\n browserProjectRuntime,\n} from \"./project/browser-host.js\";\nexport { type BrowserLoadProjectOptions, loadProject } from \"./project/browser-load.js\";\nexport {\n type ProjectShards,\n serveProjectShard,\n startProjectShards,\n} from \"./project/browser-shards.js\";\nexport {\n installProjectRouter,\n joinProjectShards,\n type ProjectTabs,\n type ProjectTabsOptions,\n type ServiceWorkerScope,\n shardOwner,\n} from \"./project/browser-tabs.js\";\nexport type {\n LoadedProject,\n LoadProjectOptions,\n ProjectSource,\n} from \"./project/built-project.js\";\nexport { serviceWorkerConnector } from \"./ws/sw-connector.js\";\n"]}
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAwB,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EAAE,YAAY,EAA6B,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAGL,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAkC,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAEL,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EAIjB,UAAU,GACX,MAAM,2BAA2B,CAAC;AAMnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["export { sahpoolOpener } from \"./adapters/wasm-sqlite.js\";\nexport { idbObjectStore } from \"./bucket/idb-object-store.js\";\nexport { type FilesRpcOptions, rpcFilesClient, serveFilesApi } from \"./ltx/files-rpc.js\";\nexport { filesApiPort, type ServeLtxStoreOptions, serveLtxStore } from \"./ltx/ltx-host.js\";\nexport type { LtxStoreFiles } from \"./ltx/ltx-page-server.js\";\nexport {\n type BrowserBucket,\n type BrowserProjectOptions,\n browserProjectRuntime,\n} from \"./project/browser-host.js\";\nexport { type BrowserLoadProjectOptions, loadProject } from \"./project/browser-load.js\";\nexport {\n type ProjectShards,\n serveProjectShard,\n startProjectShards,\n} from \"./project/browser-shards.js\";\nexport {\n installProjectRouter,\n joinProjectShards,\n type ProjectTabs,\n type ProjectTabsOptions,\n type ServiceWorkerScope,\n shardOwner,\n} from \"./project/browser-tabs.js\";\nexport type {\n LoadedProject,\n LoadProjectOptions,\n ProjectSource,\n} from \"./project/built-project.js\";\nexport { serviceWorkerConnector } from \"./ws/sw-connector.js\";\n"]}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export { BrokenObjectError, CellOwnedElsewhereError, ObjectClosedError, } from "
12
12
  export { type CellRuntime, type CellRuntimeOptions, createCellRuntime, type WorkerModule, } from "./core/runtime.js";
13
13
  export { shardOf } from "./core/shard.js";
14
14
  export { DurableObjectState } from "./core/state.js";
15
+ export { type FilesRpcOptions, rpcFilesClient, serveFilesApi } from "./ltx/files-rpc.js";
15
16
  export * from "./ports.js";
16
17
  export { parseJsonc } from "./project/jsonc.js";
17
18
  export { createProjectRuntime, type ProjectHost, type ProjectRuntime, type WranglerConfig, } from "./project/wrangler.js";
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export { BrokenObjectError, CellOwnedElsewhereError, ObjectClosedError, } from "
12
12
  export { createCellRuntime, } from "./core/runtime.js";
13
13
  export { shardOf } from "./core/shard.js";
14
14
  export { DurableObjectState } from "./core/state.js";
15
+ export { rpcFilesClient, serveFilesApi } from "./ltx/files-rpc.js";
15
16
  export * from "./ports.js";
16
17
  export { parseJsonc } from "./project/jsonc.js";
17
18
  export { createProjectRuntime, } from "./project/wrangler.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAyB,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAA8B,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,OAAO,EAGP,SAAS,GACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EAEjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAyB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,EACL,sBAAsB,EAEtB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,oBAAoB,GAIrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAA0B,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,EACL,iBAAiB,EAGjB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,oBAAoB,GAErB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { type FilesApiBackupOptions, filesApiBackup } from \"./adapters/files-backup.js\";\nexport { type ChangeLogOptions, filesApiChangeLog } from \"./adapters/files-changelog.js\";\nexport { type FilesApiLtxLogOptions, filesApiLtxLog } from \"./adapters/files-ltx.js\";\nexport { wasmMemoryOpener } from \"./adapters/wasm-sqlite.js\";\nexport {\n AiError,\n type AiRunOptions,\n type WorkersAiOptions,\n workersAi,\n} from \"./bindings/workers-ai.js\";\nexport { type CelldBucketOptions, celldBucket } from \"./bucket/celld-cells.js\";\nexport {\n filesApiObjectStore,\n memoryObjectStore,\n type ObjectStore,\n PreconditionFailedError,\n} from \"./bucket/object-store.js\";\nexport { type S3Options, s3ObjectStore } from \"./bucket/s3-object-store.js\";\nexport { DurableObject, type ExecutionContext, WorkerEntrypoint } from \"./core/durable-object.js\";\nexport {\n DurableObjectNamespace,\n type DurableObjectStub,\n runInDurableObject,\n} from \"./core/namespace.js\";\nexport {\n BrokenObjectError,\n CellOwnedElsewhereError,\n ObjectClosedError,\n} from \"./core/object-host.js\";\nexport {\n type CellRuntime,\n type CellRuntimeOptions,\n createCellRuntime,\n type WorkerModule,\n} from \"./core/runtime.js\";\nexport { shardOf } from \"./core/shard.js\";\nexport { DurableObjectState } from \"./core/state.js\";\nexport * from \"./ports.js\";\nexport { parseJsonc } from \"./project/jsonc.js\";\nexport {\n createProjectRuntime,\n type ProjectHost,\n type ProjectRuntime,\n type WranglerConfig,\n} from \"./project/wrangler.js\";\nexport { CellRouter, type CellRouterOptions, ShardOwnerLostError } from \"./router/cell-router.js\";\nexport {\n createShardWorker,\n type ShardWorker,\n type ShardWorkerOptions,\n shardRouting,\n} from \"./router/shard-worker.js\";\nexport {\n type CellsHttpOptions,\n cellsHttpHandler,\n type RemoteRuntime,\n remoteRuntime,\n} from \"./transport/cells-http.js\";\nexport {\n type CellPort,\n connectRuntime,\n linkOverPort,\n serveHandlerOverPort,\n serveRuntime,\n} from \"./transport/port.js\";\nexport {\n type CellsWebSocketClass,\n createWebSocketClass,\n type WebSocketConnector,\n} from \"./ws/client.js\";\nexport { CellsResponse, takeClientPort, WebSocketPair } from \"./ws/server.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAyB,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,EAA8B,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,OAAO,EAGP,SAAS,GACV,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAA2B,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EAEjB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAkB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAyB,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAClG,OAAO,EACL,sBAAsB,EAEtB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAwB,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzF,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,oBAAoB,GAIrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAA0B,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAClG,OAAO,EACL,iBAAiB,EAGjB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,gBAAgB,EAEhB,aAAa,GACd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,oBAAoB,GAErB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { type FilesApiBackupOptions, filesApiBackup } from \"./adapters/files-backup.js\";\nexport { type ChangeLogOptions, filesApiChangeLog } from \"./adapters/files-changelog.js\";\nexport { type FilesApiLtxLogOptions, filesApiLtxLog } from \"./adapters/files-ltx.js\";\nexport { wasmMemoryOpener } from \"./adapters/wasm-sqlite.js\";\nexport {\n AiError,\n type AiRunOptions,\n type WorkersAiOptions,\n workersAi,\n} from \"./bindings/workers-ai.js\";\nexport { type CelldBucketOptions, celldBucket } from \"./bucket/celld-cells.js\";\nexport {\n filesApiObjectStore,\n memoryObjectStore,\n type ObjectStore,\n PreconditionFailedError,\n} from \"./bucket/object-store.js\";\nexport { type S3Options, s3ObjectStore } from \"./bucket/s3-object-store.js\";\nexport { DurableObject, type ExecutionContext, WorkerEntrypoint } from \"./core/durable-object.js\";\nexport {\n DurableObjectNamespace,\n type DurableObjectStub,\n runInDurableObject,\n} from \"./core/namespace.js\";\nexport {\n BrokenObjectError,\n CellOwnedElsewhereError,\n ObjectClosedError,\n} from \"./core/object-host.js\";\nexport {\n type CellRuntime,\n type CellRuntimeOptions,\n createCellRuntime,\n type WorkerModule,\n} from \"./core/runtime.js\";\nexport { shardOf } from \"./core/shard.js\";\nexport { DurableObjectState } from \"./core/state.js\";\nexport { type FilesRpcOptions, rpcFilesClient, serveFilesApi } from \"./ltx/files-rpc.js\";\nexport * from \"./ports.js\";\nexport { parseJsonc } from \"./project/jsonc.js\";\nexport {\n createProjectRuntime,\n type ProjectHost,\n type ProjectRuntime,\n type WranglerConfig,\n} from \"./project/wrangler.js\";\nexport { CellRouter, type CellRouterOptions, ShardOwnerLostError } from \"./router/cell-router.js\";\nexport {\n createShardWorker,\n type ShardWorker,\n type ShardWorkerOptions,\n shardRouting,\n} from \"./router/shard-worker.js\";\nexport {\n type CellsHttpOptions,\n cellsHttpHandler,\n type RemoteRuntime,\n remoteRuntime,\n} from \"./transport/cells-http.js\";\nexport {\n type CellPort,\n connectRuntime,\n linkOverPort,\n serveHandlerOverPort,\n serveRuntime,\n} from \"./transport/port.js\";\nexport {\n type CellsWebSocketClass,\n createWebSocketClass,\n type WebSocketConnector,\n} from \"./ws/client.js\";\nexport { CellsResponse, takeClientPort, WebSocketPair } from \"./ws/server.js\";\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A FilesApi across threads: `serveFilesApi(port, files)` answers on one end of a MessagePort
3
+ * for a FilesApi that lives in this thread (the user's own: File System Access, memory, OPFS,
4
+ * a key/value store, S3…), and `rpcFilesClient(port)` is that FilesApi in another thread (the
5
+ * LTX store worker, say). An instance cannot be posted to a worker; a port can.
6
+ *
7
+ * On @statewalker/webrun-rpc: the unary methods are one `callPort` each; a ranged read (the
8
+ * LTX store's page reads) is one `callPort` answered with the bytes; `read` without a length,
9
+ * `list` and `write` stream with `callBidi`. A write is sent in batches of up to `batchBytes`
10
+ * (the LTX encoder yields one small block per page).
11
+ *
12
+ * The client must not live in a thread that blocks on `Atomics.wait` (the SQLite VFS worker):
13
+ * a blocked thread receives no answers.
14
+ */
15
+ import type { FilesApi } from "@statewalker/webrun-files";
16
+ import { type MessageTarget } from "@statewalker/webrun-rpc";
17
+ export interface FilesRpcOptions {
18
+ /** How long one call may take, in ms (a disk, a network store); 60 s by default. */
19
+ timeout?: number;
20
+ }
21
+ /**
22
+ * Answers `rpcFilesClient` calls on `port` with `files`. Several ports may serve one FilesApi
23
+ * (one per worker). Returns the teardown.
24
+ */
25
+ export declare function serveFilesApi(port: MessageTarget, files: FilesApi): () => void;
26
+ /** A FilesApi whose calls go over `port` to a `serveFilesApi`. */
27
+ export declare function rpcFilesClient(port: MessageTarget, opts?: FilesRpcOptions & {
28
+ /** Bytes of a write sent per message; 1 MiB by default. */
29
+ batchBytes?: number;
30
+ }): FilesApi;
@@ -0,0 +1,129 @@
1
+ import { callBidi, callPort, listenBidi, listenPort, } from "@statewalker/webrun-rpc";
2
+ const UNARY = "webrun-files:unary";
3
+ const UNARY_METHODS = ["stats", "exists", "mkdir", "remove", "move", "copy", "readRange"];
4
+ const start = (port) => port.start?.();
5
+ /**
6
+ * The bytes of `c` in a buffer of their own: posting a view clones its WHOLE buffer (a page
7
+ * read as a subarray of a 10 MB file would cross the thread as 10 MB).
8
+ */
9
+ const own = (c) => c.byteOffset === 0 && c.byteLength === c.buffer.byteLength ? c : c.slice();
10
+ async function* owned(chunks) {
11
+ for await (const c of chunks)
12
+ yield own(c);
13
+ }
14
+ async function concat(chunks) {
15
+ const parts = [];
16
+ let n = 0;
17
+ for await (const c of chunks)
18
+ parts.push(c), (n += c.length);
19
+ if (parts.length === 1)
20
+ return own(parts[0]);
21
+ const out = new Uint8Array(n);
22
+ let o = 0;
23
+ for (const p of parts)
24
+ out.set(p, o), (o += p.length);
25
+ return out;
26
+ }
27
+ /**
28
+ * Answers `rpcFilesClient` calls on `port` with `files`. Several ports may serve one FilesApi
29
+ * (one per worker). Returns the teardown.
30
+ */
31
+ export function serveFilesApi(port, files) {
32
+ const offUnary = listenPort(port, async ({ m, a }) => {
33
+ if (!UNARY_METHODS.includes(m))
34
+ throw new Error(`files-rpc: unknown method ${String(m)}`);
35
+ if (m === "readRange") {
36
+ const [path, start, length] = a;
37
+ return concat(files.read(path, { start, length }));
38
+ }
39
+ return files[m].apply(files, a);
40
+ }, { channelName: UNARY });
41
+ const offStream = listenBidi(port, async (input, params) => {
42
+ const { fs, path, opts } = params;
43
+ if (fs === "read")
44
+ return owned(files.read(path, opts));
45
+ if (fs === "list")
46
+ return files.list(path, opts);
47
+ await files.write(path, input);
48
+ return (async function* () { })();
49
+ }, (params) => typeof params.fs === "string");
50
+ start(port);
51
+ return () => {
52
+ offUnary();
53
+ offStream();
54
+ };
55
+ }
56
+ /** A FilesApi whose calls go over `port` to a `serveFilesApi`. */
57
+ export function rpcFilesClient(port, opts = {}) {
58
+ start(port);
59
+ const timeout = opts.timeout ?? 60_000;
60
+ const batchBytes = opts.batchBytes ?? 1 << 20;
61
+ const call = (m, a, signal) => callPort(port, { m, a }, { channelName: UNARY, timeout, signal });
62
+ const unary = (m) => (...a) => call(m, a);
63
+ const stream = (req, input = [], signal) => callBidi(port, input, {
64
+ ...req,
65
+ // Read by the serving side too: its `send` waits this long for each chunk's
66
+ // acknowledgement, which comes when this side's consumer asks for the next one (without
67
+ // it, a consumer pausing over 1 s between chunks failed the stream).
68
+ timeout,
69
+ options: { timeout, bidiTimeout: 2147483647, signal },
70
+ });
71
+ /** Chunks gathered into messages of about `batchBytes`. */
72
+ async function* batched(content) {
73
+ let parts = [];
74
+ let n = 0;
75
+ const flush = () => {
76
+ const out = parts.length === 1 ? own(parts[0]) : new Uint8Array(n);
77
+ if (parts.length > 1) {
78
+ let o = 0;
79
+ for (const p of parts)
80
+ out.set(p, o), (o += p.length);
81
+ }
82
+ parts = [];
83
+ n = 0;
84
+ return out;
85
+ };
86
+ for await (const c of content) {
87
+ if (!c.length)
88
+ continue;
89
+ parts.push(c);
90
+ n += c.length;
91
+ if (n >= batchBytes)
92
+ yield flush();
93
+ }
94
+ if (n)
95
+ yield flush();
96
+ }
97
+ const api = {
98
+ read(path, o) {
99
+ const { start, length, signal } = o ?? {};
100
+ // A ranged read (the LTX store's page reads): one call, one answer.
101
+ if (length !== undefined)
102
+ return (async function* () {
103
+ const bytes = await call("readRange", [path, start ?? 0, length], signal);
104
+ if (bytes.length)
105
+ yield bytes;
106
+ })();
107
+ return stream({ fs: "read", path, opts: { start } }, [], signal);
108
+ },
109
+ list(path, o) {
110
+ return stream({
111
+ fs: "list",
112
+ path,
113
+ opts: { recursive: o?.recursive, after: o?.after },
114
+ });
115
+ },
116
+ async write(path, content) {
117
+ for await (const _ of stream({ fs: "write", path }, batched(content)))
118
+ void _;
119
+ },
120
+ stats: unary("stats"),
121
+ exists: unary("exists"),
122
+ mkdir: unary("mkdir"),
123
+ remove: unary("remove"),
124
+ move: unary("move"),
125
+ copy: unary("copy"),
126
+ };
127
+ return api;
128
+ }
129
+ //# sourceMappingURL=files-rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files-rpc.js","sourceRoot":"","sources":["../../src/ltx/files-rpc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A FilesApi across threads: `serveFilesApi(port, files)` answers on one end of a MessagePort\n * for a FilesApi that lives in this thread (the user's own: File System Access, memory, OPFS,\n * a key/value store, S3…), and `rpcFilesClient(port)` is that FilesApi in another thread (the\n * LTX store worker, say). An instance cannot be posted to a worker; a port can.\n *\n * On @statewalker/webrun-rpc: the unary methods are one `callPort` each; a ranged read (the\n * LTX store's page reads) is one `callPort` answered with the bytes; `read` without a length,\n * `list` and `write` stream with `callBidi`. A write is sent in batches of up to `batchBytes`\n * (the LTX encoder yields one small block per page).\n *\n * The client must not live in a thread that blocks on `Atomics.wait` (the SQLite VFS worker):\n * a blocked thread receives no answers.\n */\nimport type {\n FileInfo,\n FileStats,\n FilesApi,\n ListOptions,\n ReadOptions,\n} from \"@statewalker/webrun-files\";\nimport {\n callBidi,\n callPort,\n listenBidi,\n listenPort,\n type MessageTarget,\n} from \"@statewalker/webrun-rpc\";\n\nconst UNARY = \"webrun-files:unary\";\nconst UNARY_METHODS = [\"stats\", \"exists\", \"mkdir\", \"remove\", \"move\", \"copy\", \"readRange\"] as const;\ntype UnaryMethod = (typeof UNARY_METHODS)[number];\n\ninterface UnaryRequest {\n m: UnaryMethod;\n a: unknown[];\n}\ninterface StreamRequest {\n fs: \"read\" | \"list\" | \"write\";\n path: string;\n opts?: { start?: number; length?: number; recursive?: boolean; after?: string };\n}\n\nexport interface FilesRpcOptions {\n /** How long one call may take, in ms (a disk, a network store); 60 s by default. */\n timeout?: number;\n}\n\nconst start = (port: MessageTarget) => (port as { start?: () => void }).start?.();\n\n/**\n * The bytes of `c` in a buffer of their own: posting a view clones its WHOLE buffer (a page\n * read as a subarray of a 10 MB file would cross the thread as 10 MB).\n */\nconst own = (c: Uint8Array) =>\n c.byteOffset === 0 && c.byteLength === c.buffer.byteLength ? c : c.slice();\n\nasync function* owned(chunks: AsyncIterable<Uint8Array>) {\n for await (const c of chunks) yield own(c);\n}\n\nasync function concat(chunks: AsyncIterable<Uint8Array>): Promise<Uint8Array> {\n const parts: Uint8Array[] = [];\n let n = 0;\n for await (const c of chunks) parts.push(c), (n += c.length);\n if (parts.length === 1) return own(parts[0]);\n const out = new Uint8Array(n);\n let o = 0;\n for (const p of parts) out.set(p, o), (o += p.length);\n return out;\n}\n\n/**\n * Answers `rpcFilesClient` calls on `port` with `files`. Several ports may serve one FilesApi\n * (one per worker). Returns the teardown.\n */\nexport function serveFilesApi(port: MessageTarget, files: FilesApi): () => void {\n const offUnary = listenPort<UnaryRequest, unknown>(\n port,\n async ({ m, a }) => {\n if (!UNARY_METHODS.includes(m)) throw new Error(`files-rpc: unknown method ${String(m)}`);\n if (m === \"readRange\") {\n const [path, start, length] = a as [string, number, number];\n return concat(files.read(path, { start, length }));\n }\n return (files[m] as (...args: unknown[]) => Promise<unknown>).apply(files, a);\n },\n { channelName: UNARY },\n );\n const offStream = listenBidi<Uint8Array, unknown>(\n port,\n async (input, params) => {\n const { fs, path, opts } = params as unknown as StreamRequest;\n if (fs === \"read\") return owned(files.read(path, opts as ReadOptions));\n if (fs === \"list\") return files.list(path, opts as ListOptions);\n await files.write(path, input);\n return (async function* () {})();\n },\n (params) => typeof (params as { fs?: unknown }).fs === \"string\",\n );\n start(port);\n return () => {\n offUnary();\n offStream();\n };\n}\n\n/** A FilesApi whose calls go over `port` to a `serveFilesApi`. */\nexport function rpcFilesClient(\n port: MessageTarget,\n opts: FilesRpcOptions & {\n /** Bytes of a write sent per message; 1 MiB by default. */\n batchBytes?: number;\n } = {},\n): FilesApi {\n start(port);\n const timeout = opts.timeout ?? 60_000;\n const batchBytes = opts.batchBytes ?? 1 << 20;\n const call = <T>(m: UnaryMethod, a: unknown[], signal?: AbortSignal) =>\n callPort<T, UnaryRequest>(port, { m, a }, { channelName: UNARY, timeout, signal });\n const unary =\n <T>(m: UnaryMethod) =>\n (...a: unknown[]) =>\n call<T>(m, a);\n const stream = <T>(\n req: StreamRequest,\n input: AsyncIterable<Uint8Array> | Uint8Array[] = [],\n signal?: AbortSignal,\n ) =>\n callBidi<Uint8Array, T>(port, input, {\n ...req,\n // Read by the serving side too: its `send` waits this long for each chunk's\n // acknowledgement, which comes when this side's consumer asks for the next one (without\n // it, a consumer pausing over 1 s between chunks failed the stream).\n timeout,\n options: { timeout, bidiTimeout: 2147483647, signal },\n });\n /** Chunks gathered into messages of about `batchBytes`. */\n async function* batched(content: Iterable<Uint8Array> | AsyncIterable<Uint8Array>) {\n let parts: Uint8Array[] = [];\n let n = 0;\n const flush = () => {\n const out = parts.length === 1 ? own(parts[0]) : new Uint8Array(n);\n if (parts.length > 1) {\n let o = 0;\n for (const p of parts) out.set(p, o), (o += p.length);\n }\n parts = [];\n n = 0;\n return out;\n };\n for await (const c of content as AsyncIterable<Uint8Array>) {\n if (!c.length) continue;\n parts.push(c);\n n += c.length;\n if (n >= batchBytes) yield flush();\n }\n if (n) yield flush();\n }\n const api: FilesApi = {\n read(path: string, o?: ReadOptions): AsyncIterable<Uint8Array> {\n const { start, length, signal } = o ?? {};\n // A ranged read (the LTX store's page reads): one call, one answer.\n if (length !== undefined)\n return (async function* () {\n const bytes = await call<Uint8Array>(\"readRange\", [path, start ?? 0, length], signal);\n if (bytes.length) yield bytes;\n })();\n return stream<Uint8Array>({ fs: \"read\", path, opts: { start } }, [], signal);\n },\n list(path: string, o?: ListOptions): AsyncIterable<FileInfo> {\n return stream<FileInfo>({\n fs: \"list\",\n path,\n opts: { recursive: o?.recursive, after: o?.after },\n });\n },\n async write(path, content) {\n for await (const _ of stream<never>({ fs: \"write\", path }, batched(content))) void _;\n },\n stats: unary<FileStats | undefined>(\"stats\") as FilesApi[\"stats\"],\n exists: unary<boolean>(\"exists\") as FilesApi[\"exists\"],\n mkdir: unary<void>(\"mkdir\") as FilesApi[\"mkdir\"],\n remove: unary<boolean>(\"remove\") as FilesApi[\"remove\"],\n move: unary<boolean>(\"move\") as FilesApi[\"move\"],\n copy: unary<boolean>(\"copy\") as FilesApi[\"copy\"],\n };\n return api;\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Cells' storage chosen by the app: it opens a FilesApi (File System Access, memory, OPFS, a
3
+ * key/value store, S3…) BEFORE starting cellb's workers, then hands it to them in one of two
4
+ * ways (a FilesApi instance cannot be posted to a worker; a port can):
5
+ *
6
+ * - `filesApiPort(files)`: the FilesApi stays in this thread and is served over a port; the
7
+ * shard worker passes the port to its LTX store worker (`bucket: { ltx, files: { port } }`),
8
+ * which runs the store (LZ4, compaction) and calls the FilesApi by message.
9
+ * - `serveLtxStore(files)`: the LTX store itself runs in this thread on the instance (no
10
+ * FilesApi messages at all); the shard worker's VFS sends it its page reads and commits
11
+ * (`bucket: { ltx, ltxStore: { sab, port } }`). The store's work then shares this thread.
12
+ *
13
+ * Either way, one call per shard worker: a port answers one worker, a store one VFS.
14
+ */
15
+ import type { FilesApi } from "@statewalker/webrun-files";
16
+ /** A port to `files` for a worker (transfer it); `close()` stops serving it. */
17
+ export declare function filesApiPort(files: FilesApi): {
18
+ port: MessagePort;
19
+ close(): void;
20
+ };
21
+ export interface ServeLtxStoreOptions {
22
+ /** Names the storage: two stores with one scope never write the same cell at once. */
23
+ lockScope: string;
24
+ /** Files per cell before it is folded into one snapshot (default 64). */
25
+ compactAfter?: number;
26
+ /** Free bytes where the files are (see ltxCellStore); unknown by default. */
27
+ room?: () => Promise<number | undefined>;
28
+ /** Bytes of the shared buffer's payload: a read-ahead run (default 1 MiB, at least 64 KiB). */
29
+ payloadBytes?: number;
30
+ }
31
+ /**
32
+ * The LTX store on `files` in this thread, for ONE shard worker's VFS: give the worker
33
+ * `{ sab, port }` (transfer the port). This thread must not block (a page, or any worker that
34
+ * does not run SQLite); it answers into `sab`.
35
+ */
36
+ export declare function serveLtxStore(files: FilesApi, opts: ServeLtxStoreOptions): {
37
+ sab: SharedArrayBuffer;
38
+ port: MessagePort;
39
+ store: {
40
+ handle(msg: import("./ltx-page-server.js").LtxStoreMessage & {
41
+ seq?: number;
42
+ until?: number;
43
+ }): void;
44
+ compact: (cell: string) => Promise<number>;
45
+ stats: {
46
+ commits: number;
47
+ writeMs: number;
48
+ appendMs: number;
49
+ compactions: number;
50
+ compactMs: number;
51
+ reads: number;
52
+ readMs: number;
53
+ };
54
+ close(): Promise<void>;
55
+ files: (cell: string) => Promise<string[]>;
56
+ };
57
+ /**
58
+ * Stops serving: call it when the shard worker ends, so a store for its successor can
59
+ * take the cells' Web Locks (it waits 20 s for them, then fails). Resolves once the
60
+ * store's work under way has ended and its locks are released.
61
+ */
62
+ close(): Promise<void>;
63
+ };
@@ -0,0 +1,46 @@
1
+ import { serveFilesApi } from "./files-rpc.js";
2
+ import { ltxCellStore } from "./ltx-page-server.js";
3
+ /** A port to `files` for a worker (transfer it); `close()` stops serving it. */
4
+ export function filesApiPort(files) {
5
+ const ch = new MessageChannel();
6
+ const stop = serveFilesApi(ch.port1, files);
7
+ return {
8
+ port: ch.port2,
9
+ close() {
10
+ stop();
11
+ ch.port1.close();
12
+ },
13
+ };
14
+ }
15
+ /**
16
+ * The LTX store on `files` in this thread, for ONE shard worker's VFS: give the worker
17
+ * `{ sab, port }` (transfer the port). This thread must not block (a page, or any worker that
18
+ * does not run SQLite); it answers into `sab`.
19
+ */
20
+ export function serveLtxStore(files, opts) {
21
+ // At least one page of SQLite's largest size: a smaller buffer would answer reads with no
22
+ // pages at all.
23
+ const sab = new SharedArrayBuffer(8 + Math.max(65536, opts.payloadBytes ?? 16 * 65536));
24
+ const store = ltxCellStore(files, sab, {
25
+ lockScope: opts.lockScope,
26
+ compactAfter: opts.compactAfter,
27
+ room: opts.room ?? (async () => undefined),
28
+ });
29
+ const ch = new MessageChannel();
30
+ ch.port1.onmessage = (e) => store.handle(e.data);
31
+ return {
32
+ sab,
33
+ port: ch.port2,
34
+ store,
35
+ /**
36
+ * Stops serving: call it when the shard worker ends, so a store for its successor can
37
+ * take the cells' Web Locks (it waits 20 s for them, then fails). Resolves once the
38
+ * store's work under way has ended and its locks are released.
39
+ */
40
+ close() {
41
+ ch.port1.close();
42
+ return store.close();
43
+ },
44
+ };
45
+ }
46
+ //# sourceMappingURL=ltx-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ltx-host.js","sourceRoot":"","sources":["../../src/ltx/ltx-host.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,KAAe;IAC1C,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO;QACL,IAAI,EAAE,EAAE,CAAC,KAAK;QACd,KAAK;YACH,IAAI,EAAE,CAAC;YACP,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAe,EAAE,IAA0B;IACvE,0FAA0F;IAC1F,gBAAgB;IAChB,MAAM,GAAG,GAAG,IAAI,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE;QACrC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC;KAC3C,CAAC,CAAC;IACH,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO;QACL,GAAG;QACH,IAAI,EAAE,EAAE,CAAC,KAAK;QACd,KAAK;QACL;;;;WAIG;QACH,KAAK;YACH,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACjB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Cells' storage chosen by the app: it opens a FilesApi (File System Access, memory, OPFS, a\n * key/value store, S3…) BEFORE starting cellb's workers, then hands it to them in one of two\n * ways (a FilesApi instance cannot be posted to a worker; a port can):\n *\n * - `filesApiPort(files)`: the FilesApi stays in this thread and is served over a port; the\n * shard worker passes the port to its LTX store worker (`bucket: { ltx, files: { port } }`),\n * which runs the store (LZ4, compaction) and calls the FilesApi by message.\n * - `serveLtxStore(files)`: the LTX store itself runs in this thread on the instance (no\n * FilesApi messages at all); the shard worker's VFS sends it its page reads and commits\n * (`bucket: { ltx, ltxStore: { sab, port } }`). The store's work then shares this thread.\n *\n * Either way, one call per shard worker: a port answers one worker, a store one VFS.\n */\nimport type { FilesApi } from \"@statewalker/webrun-files\";\nimport { serveFilesApi } from \"./files-rpc.js\";\nimport { ltxCellStore } from \"./ltx-page-server.js\";\n\n/** A port to `files` for a worker (transfer it); `close()` stops serving it. */\nexport function filesApiPort(files: FilesApi): { port: MessagePort; close(): void } {\n const ch = new MessageChannel();\n const stop = serveFilesApi(ch.port1, files);\n return {\n port: ch.port2,\n close() {\n stop();\n ch.port1.close();\n },\n };\n}\n\nexport interface ServeLtxStoreOptions {\n /** Names the storage: two stores with one scope never write the same cell at once. */\n lockScope: string;\n /** Files per cell before it is folded into one snapshot (default 64). */\n compactAfter?: number;\n /** Free bytes where the files are (see ltxCellStore); unknown by default. */\n room?: () => Promise<number | undefined>;\n /** Bytes of the shared buffer's payload: a read-ahead run (default 1 MiB, at least 64 KiB). */\n payloadBytes?: number;\n}\n\n/**\n * The LTX store on `files` in this thread, for ONE shard worker's VFS: give the worker\n * `{ sab, port }` (transfer the port). This thread must not block (a page, or any worker that\n * does not run SQLite); it answers into `sab`.\n */\nexport function serveLtxStore(files: FilesApi, opts: ServeLtxStoreOptions) {\n // At least one page of SQLite's largest size: a smaller buffer would answer reads with no\n // pages at all.\n const sab = new SharedArrayBuffer(8 + Math.max(65536, opts.payloadBytes ?? 16 * 65536));\n const store = ltxCellStore(files, sab, {\n lockScope: opts.lockScope,\n compactAfter: opts.compactAfter,\n room: opts.room ?? (async () => undefined),\n });\n const ch = new MessageChannel();\n ch.port1.onmessage = (e) => store.handle(e.data);\n return {\n sab,\n port: ch.port2,\n store,\n /**\n * Stops serving: call it when the shard worker ends, so a store for its successor can\n * take the cells' Web Locks (it waits 20 s for them, then fails). Resolves once the\n * store's work under way has ended and its locks are released.\n */\n close() {\n ch.port1.close();\n return store.close();\n },\n };\n}\n"]}
@@ -6,6 +6,32 @@
6
6
  * shared buffer. A cell whose files pass `compactAfter` is folded into one snapshot (streamed).
7
7
  */
8
8
  import type { FilesApi } from "@statewalker/webrun-files";
9
+ /**
10
+ * Where the store's FilesApi comes from. An instance cannot cross to this worker, so it is:
11
+ * - a preset by name, on OPFS directory `dir`: "opfs-sync" (default: synchronous handles; each
12
+ * file written under a temporary name and moved into place, so a file being written is never
13
+ * read), "opfs" (OPFS's asynchronous API), "memory" (nothing kept);
14
+ * - `{ port }`: a FilesApi served elsewhere (`serveFilesApi`, on the page or in a worker of
15
+ * the app's own): File System Access, IndexedDB, S3, anything;
16
+ * - `{ module, args }`: a module this worker imports, whose default export (or
17
+ * `createFilesApi`) builds the FilesApi here from `args` (structured-cloneable: a
18
+ * FileSystemDirectoryHandle, S3 settings…), and whose optional `room(args)` says the free
19
+ * bytes there.
20
+ */
21
+ export type LtxStoreFiles = "opfs" | "opfs-sync" | "memory" | {
22
+ port: MessagePort;
23
+ } | {
24
+ module: string;
25
+ args?: unknown;
26
+ };
27
+ export interface LtxStoreInit {
28
+ op: "init";
29
+ sab: SharedArrayBuffer;
30
+ /** The OPFS directory of a preset; else the name of the storage (the Web Locks' scope). */
31
+ dir: string;
32
+ compactAfter?: number;
33
+ files?: LtxStoreFiles;
34
+ }
9
35
  export interface LtxStoreMessage {
10
36
  op: "meta" | "read" | "readRun" | "commit" | "stats";
11
37
  cell: string;
@@ -24,9 +50,19 @@ export declare function ltxCellStore(files: FilesApi, sab: SharedArrayBuffer, op
24
50
  * stores of two directories (two projects in one tab) do not take each other's locks.
25
51
  */
26
52
  lockScope?: string;
53
+ /**
54
+ * Free bytes where the files are, checked before a compaction writes its snapshot beside
55
+ * the files it replaces; undefined: unknown, compact anyway. Defaults to this origin's
56
+ * storage estimate, which is right for OPFS only: give another FilesApi its own (or
57
+ * `async () => undefined`).
58
+ */
59
+ room?: () => Promise<number | undefined>;
27
60
  }): {
28
61
  /** Answers one request of a VFS (requests are served in order). */
29
- handle(msg: LtxStoreMessage): void;
62
+ handle(msg: LtxStoreMessage & {
63
+ seq?: number;
64
+ until?: number;
65
+ }): void;
30
66
  /** Folds a cell's files into one snapshot; answers its file count after. */
31
67
  /** Folds a cell's files into one snapshot (not through the request queue: it takes its
32
68
  * own turn there to swap the files); answers the cell's file count after. */
@@ -40,6 +76,11 @@ export declare function ltxCellStore(files: FilesApi, sab: SharedArrayBuffer, op
40
76
  reads: number;
41
77
  readMs: number;
42
78
  };
79
+ /**
80
+ * Stops the store: its Web Locks released (another store may open its cells), its open
81
+ * cells forgotten. A store in a worker of its own ends with the worker instead.
82
+ */
83
+ close(): Promise<void>;
43
84
  /** The paths of a cell's current files. */
44
85
  files: (cell: string) => Promise<string[]>;
45
86
  };