@rivetkit/sqlite-vfs 2.1.2-rc.1 → 2.1.2
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.
- package/dist/tsup/index.cjs +2 -1
- package/dist/tsup/index.cjs.map +1 -1
- package/dist/tsup/index.js +2 -1
- package/dist/tsup/index.js.map +1 -1
- package/package.json +1 -1
- package/src/vfs.ts +5 -1
package/dist/tsup/index.cjs
CHANGED
|
@@ -129,7 +129,8 @@ function isSQLiteModule(value) {
|
|
|
129
129
|
return typeof candidate.UTF8ToString === "function" && candidate.HEAPU8 instanceof Uint8Array;
|
|
130
130
|
}
|
|
131
131
|
async function loadSqliteRuntime() {
|
|
132
|
-
const
|
|
132
|
+
const specifier = ["@rivetkit/sqlite", "dist", "wa-sqlite-async.mjs"].join("/");
|
|
133
|
+
const sqliteModule = await Promise.resolve().then(() => _interopRequireWildcard(require(specifier)));
|
|
133
134
|
if (!isSqliteEsmFactory(sqliteModule.default)) {
|
|
134
135
|
throw new Error("Invalid SQLite ESM factory export");
|
|
135
136
|
}
|
package/dist/tsup/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/rivet/rivet/rivetkit-typescript/packages/sqlite-vfs/dist/tsup/index.cjs","../../../../../node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.53.2_@types+node@22.19.10__@swc+core@1.15.11_@swc_c97c1b0c6eca1589eb738d8730776c3e/node_modules/tsup/assets/cjs_shims.js","../../src/vfs.ts","../../src/kv.ts","../../schemas/file-meta/versioned.ts","../schemas/file-meta/v1.ts"],"names":["SQLITE_OPEN_CREATE"],"mappings":"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","file":"/home/runner/work/rivet/rivet/rivetkit-typescript/packages/sqlite-vfs/dist/tsup/index.cjs","sourcesContent":[null,"// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * SQLite raw database with KV storage backend\n *\n * This module provides a SQLite API that uses a KV-backed VFS\n * for storage. Each SqliteVfs instance is independent and can be\n * used concurrently with other instances.\n *\n * Keep this VFS on direct VFS.Base callbacks for minimal wrapper overhead.\n * Use @rivetkit/sqlite/src/FacadeVFS.js as the reference implementation for\n * callback ABI and pointer/data conversion behavior.\n * This implementation is optimized for single-writer semantics because each\n * actor owns one SQLite database.\n * SQLite invokes this VFS with byte-range file operations. This VFS maps those\n * ranges onto fixed-size KV chunks keyed by file tag and chunk index.\n * We intentionally rely on SQLite's pager cache for hot page reuse and do not\n * add a second cache in this VFS. This avoids duplicate cache invalidation\n * logic and keeps memory usage predictable for each actor.\n */\n\nimport * as VFS from \"@rivetkit/sqlite/src/VFS.js\";\nimport {\n\tFactory,\n\tSQLITE_OPEN_CREATE,\n\tSQLITE_OPEN_READWRITE,\n\tSQLITE_ROW,\n} from \"@rivetkit/sqlite\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport {\n\tCHUNK_SIZE,\n\tFILE_TAG_JOURNAL,\n\tFILE_TAG_MAIN,\n\tFILE_TAG_SHM,\n\tFILE_TAG_WAL,\n\tgetChunkKey,\n\tgetMetaKey,\n\ttype SqliteFileTag,\n} from \"./kv\";\nimport {\n\tFILE_META_VERSIONED,\n\tCURRENT_VERSION,\n} from \"../schemas/file-meta/versioned\";\nimport type { FileMeta } from \"../schemas/file-meta/mod\";\nimport type { KvVfsOptions } from \"./types\";\n\ntype SqliteEsmFactory = (config?: { wasmBinary?: ArrayBuffer | Uint8Array }) => Promise<unknown>;\ntype SQLite3Api = ReturnType<typeof Factory>;\ntype SqliteBindings = Parameters<SQLite3Api[\"bind_collection\"]>[1];\ntype SqliteVfsRegistration = Parameters<SQLite3Api[\"vfs_register\"]>[0];\n\ninterface SQLiteModule {\n\tUTF8ToString: (ptr: number) => string;\n\tHEAPU8: Uint8Array;\n}\n\nconst TEXT_ENCODER = new TextEncoder();\nconst TEXT_DECODER = new TextDecoder();\nconst SQLITE_MAX_PATHNAME_BYTES = 64;\n\n// Chunk keys encode the chunk index in 32 bits, so a file can span at most\n// 2^32 chunks. At 4 KiB/chunk this yields a hard limit of 16 TiB.\nconst UINT32_SIZE = 0x100000000;\nconst MAX_CHUNK_INDEX = 0xffffffff;\nconst MAX_FILE_SIZE_BYTES = (MAX_CHUNK_INDEX + 1) * CHUNK_SIZE;\nconst MAX_FILE_SIZE_HI32 = Math.floor(MAX_FILE_SIZE_BYTES / UINT32_SIZE);\nconst MAX_FILE_SIZE_LO32 = MAX_FILE_SIZE_BYTES % UINT32_SIZE;\n\n// libvfs captures this async/sync mask at registration time. Any VFS callback\n// that returns a Promise must be listed here so SQLite uses async relays.\nconst SQLITE_ASYNC_METHODS = new Set([\n\t\"xOpen\",\n\t\"xClose\",\n\t\"xRead\",\n\t\"xWrite\",\n\t\"xTruncate\",\n\t\"xSync\",\n\t\"xFileSize\",\n\t\"xDelete\",\n\t\"xAccess\",\n]);\n\ninterface LoadedSqliteRuntime {\n\tsqlite3: SQLite3Api;\n\tmodule: SQLiteModule;\n}\n\nfunction isSqliteEsmFactory(value: unknown): value is SqliteEsmFactory {\n\treturn typeof value === \"function\";\n}\n\nfunction isSQLiteModule(value: unknown): value is SQLiteModule {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as {\n\t\tUTF8ToString?: unknown;\n\t\tHEAPU8?: unknown;\n\t};\n\treturn (\n\t\ttypeof candidate.UTF8ToString === \"function\" &&\n\t\tcandidate.HEAPU8 instanceof Uint8Array\n\t);\n}\n\n\n/**\n * Lazily load and instantiate the async SQLite module for this VFS instance.\n * We do this on first open so actors that do not use SQLite do not pay module\n * parse and wasm initialization cost at startup, and we pass wasmBinary\n * explicitly so this works consistently in both ESM and CJS bundles.\n */\nasync function loadSqliteRuntime(): Promise<LoadedSqliteRuntime> {\n\t// Keep the module specifier assembled at runtime so TypeScript declaration\n\t// generation does not try to typecheck this deep dist import path.\n\tconst sqliteModule = await import(\"@rivetkit/sqlite/dist/\" + \"wa-sqlite-async.mjs\");\n\tif (!isSqliteEsmFactory(sqliteModule.default)) {\n\t\tthrow new Error(\"Invalid SQLite ESM factory export\");\n\t}\n\tconst sqliteEsmFactory = sqliteModule.default;\n\tconst require = createRequire(import.meta.url);\n\tconst wasmPath = require.resolve(\"@rivetkit/sqlite/dist/wa-sqlite-async.wasm\");\n\tconst wasmBinary = readFileSync(wasmPath);\n\tconst module = await sqliteEsmFactory({ wasmBinary });\n\tif (!isSQLiteModule(module)) {\n\t\tthrow new Error(\"Invalid SQLite runtime module\");\n\t}\n\treturn {\n\t\tsqlite3: Factory(module),\n\t\tmodule,\n\t};\n}\n\n/**\n * Represents an open file\n */\ninterface OpenFile {\n\t/** File path */\n\tpath: string;\n\t/** File kind tag used by compact key layout */\n\tfileTag: SqliteFileTag;\n\t/** Precomputed metadata key */\n\tmetaKey: Uint8Array;\n\t/** File size in bytes */\n\tsize: number;\n\t/** True when in-memory size has not been persisted yet */\n\tmetaDirty: boolean;\n\t/** Open flags */\n\tflags: number;\n\t/** KV options for this file */\n\toptions: KvVfsOptions;\n}\n\ninterface ResolvedFile {\n\toptions: KvVfsOptions;\n\tfileTag: SqliteFileTag;\n}\n\n/**\n * Encodes file metadata to a Uint8Array using BARE schema\n */\nfunction encodeFileMeta(size: number): Uint8Array {\n\tconst meta: FileMeta = { size: BigInt(size) };\n\treturn FILE_META_VERSIONED.serializeWithEmbeddedVersion(\n\t\tmeta,\n\t\tCURRENT_VERSION,\n\t);\n}\n\n/**\n * Decodes file metadata from a Uint8Array using BARE schema\n */\nfunction decodeFileMeta(data: Uint8Array): number {\n\tconst meta = FILE_META_VERSIONED.deserializeWithEmbeddedVersion(data);\n\treturn Number(meta.size);\n}\n\nfunction isValidFileSize(size: number): boolean {\n\treturn Number.isSafeInteger(size) && size >= 0 && size <= MAX_FILE_SIZE_BYTES;\n}\n\n/**\n * Simple async mutex for serializing database operations\n * @rivetkit/sqlite calls are not safe to run concurrently on one module instance\n */\nclass AsyncMutex {\n\t#locked = false;\n\t#waiting: (() => void)[] = [];\n\n\tasync acquire(): Promise<void> {\n\t\twhile (this.#locked) {\n\t\t\tawait new Promise<void>((resolve) => this.#waiting.push(resolve));\n\t\t}\n\t\tthis.#locked = true;\n\t}\n\n\trelease(): void {\n\t\tthis.#locked = false;\n\t\tconst next = this.#waiting.shift();\n\t\tif (next) {\n\t\t\tnext();\n\t\t}\n\t}\n\n\tasync run<T>(fn: () => Promise<T>): Promise<T> {\n\t\tawait this.acquire();\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tthis.release();\n\t\t}\n\t}\n}\n\n/**\n * Database wrapper that provides a simplified SQLite API\n */\nexport class Database {\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #handle: number;\n\treadonly #fileName: string;\n\treadonly #onClose: () => void;\n\treadonly #sqliteMutex: AsyncMutex;\n\n\tconstructor(\n\t\tsqlite3: SQLite3Api,\n\t\thandle: number,\n\t\tfileName: string,\n\t\tonClose: () => void,\n\t\tsqliteMutex: AsyncMutex,\n\t) {\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#handle = handle;\n\t\tthis.#fileName = fileName;\n\t\tthis.#onClose = onClose;\n\t\tthis.#sqliteMutex = sqliteMutex;\n\t}\n\n\t/**\n\t * Execute SQL with optional row callback\n\t * @param sql - SQL statement to execute\n\t * @param callback - Called for each result row with (row, columns)\n\t */\n\tasync exec(sql: string, callback?: (row: unknown[], columns: string[]) => void): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.exec(this.#handle, sql, callback);\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL statement (no result rows)\n\t * @param sql - SQL statement with ? placeholders\n\t * @param params - Parameter values to bind\n\t */\n\tasync run(sql: string, params?: SqliteBindings): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\t// Consume rows for statements that return results.\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL query and return results\n\t * @param sql - SQL query with ? placeholders\n\t * @param params - Parameter values to bind\n\t * @returns Object with rows (array of arrays) and columns (column names)\n\t */\n\tasync query(sql: string, params?: SqliteBindings): Promise<{ rows: unknown[][]; columns: string[] }> {\n\t\treturn this.#sqliteMutex.run(async () => {\n\t\t\tconst rows: unknown[][] = [];\n\t\t\tlet columns: string[] = [];\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\tif (columns.length === 0) {\n\t\t\t\t\t\tcolumns = this.#sqlite3.column_names(stmt);\n\t\t\t\t\t}\n\t\t\t\t\trows.push(this.#sqlite3.row(stmt));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { rows, columns };\n\t\t});\n\t}\n\n\t/**\n\t * Close the database\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.close(this.#handle);\n\t\t});\n\t\tthis.#onClose();\n\t}\n\n\t/**\n\t * Get the raw @rivetkit/sqlite API (for advanced usage)\n\t */\n\tget sqlite3(): SQLite3Api {\n\t\treturn this.#sqlite3;\n\t}\n\n\t/**\n\t * Get the raw database handle (for advanced usage)\n\t */\n\tget handle(): number {\n\t\treturn this.#handle;\n\t}\n}\n\n/**\n * SQLite VFS backed by KV storage.\n *\n * Each instance is independent and has its own @rivetkit/sqlite WASM module.\n * This allows multiple instances to operate concurrently without interference.\n */\nexport class SqliteVfs {\n\t#sqlite3: SQLite3Api | null = null;\n\t#sqliteSystem: SqliteSystem | null = null;\n\t#initPromise: Promise<void> | null = null;\n\t#openMutex = new AsyncMutex();\n\t#sqliteMutex = new AsyncMutex();\n\t#instanceId: string;\n\t#destroyed = false;\n\n\tconstructor() {\n\t\t// Generate unique instance ID for VFS name\n\t\tthis.#instanceId = crypto.randomUUID().replace(/-/g, '').slice(0, 8);\n\t}\n\n\t/**\n\t * Initialize @rivetkit/sqlite and VFS (called once per instance)\n\t */\n\tasync #ensureInitialized(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Fast path: already initialized\n\t\tif (this.#sqlite3 && this.#sqliteSystem) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Synchronously create the promise if not started\n\t\tif (!this.#initPromise) {\n\t\t\tthis.#initPromise = (async () => {\n\t\t\t\tconst { sqlite3, module } = await loadSqliteRuntime();\n\t\t\t\tif (this.#destroyed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#sqlite3 = sqlite3;\n\t\t\t\tthis.#sqliteSystem = new SqliteSystem(\n\t\t\t\t\tsqlite3,\n\t\t\t\t\tmodule,\n\t\t\t\t\t`kv-vfs-${this.#instanceId}`,\n\t\t\t\t);\n\t\t\t\tthis.#sqliteSystem.register();\n\t\t\t})();\n\t\t}\n\n\t\t// Wait for initialization\n\t\ttry {\n\t\t\tawait this.#initPromise;\n\t\t} catch (error) {\n\t\t\tthis.#initPromise = null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Open a SQLite database using KV storage backend\n\t *\n\t * @param fileName - The database file name (typically the actor ID)\n\t * @param options - KV storage operations for this database\n\t * @returns A Database instance\n\t */\n\tasync open(\n\t\tfileName: string,\n\t\toptions: KvVfsOptions,\n\t): Promise<Database> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Serialize all open operations within this instance\n\t\tawait this.#openMutex.acquire();\n\t\ttry {\n\t\t\t// Initialize @rivetkit/sqlite and SqliteSystem on first call\n\t\t\tawait this.#ensureInitialized();\n\n\t\t\tif (!this.#sqlite3 || !this.#sqliteSystem) {\n\t\t\t\tthrow new Error(\"Failed to initialize SQLite\");\n\t\t\t}\n\t\t\tconst sqlite3 = this.#sqlite3;\n\t\t\tconst sqliteSystem = this.#sqliteSystem;\n\n\t\t\t// Register this filename with its KV options\n\t\t\tsqliteSystem.registerFile(fileName, options);\n\n\t\t\t// Open database\n\t\t\tconst db = await this.#sqliteMutex.run(async () =>\n\t\t\t\tsqlite3.open_v2(\n\t\t\t\t\tfileName,\n\t\t\t\t\tSQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,\n\t\t\t\t\tsqliteSystem.name,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// TODO: Benchmark PRAGMA tuning for KV-backed SQLite after open.\n\t\t\t// Start with journal_mode=PERSIST and journal_size_limit to reduce\n\t\t\t// journal churn on high-latency KV without introducing WAL.\n\n\t\t\t// Create cleanup callback\n\t\t\tconst onClose = () => {\n\t\t\t\tsqliteSystem.unregisterFile(fileName);\n\t\t\t};\n\n\t\t\treturn new Database(\n\t\t\t\tsqlite3,\n\t\t\t\tdb,\n\t\t\t\tfileName,\n\t\t\t\tonClose,\n\t\t\t\tthis.#sqliteMutex,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.#openMutex.release();\n\t\t}\n\t}\n\n\t/**\n\t * Tears down this VFS instance and releases internal references.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#destroyed = true;\n\n\t\tconst initPromise = this.#initPromise;\n\t\tif (initPromise) {\n\t\t\ttry {\n\t\t\t\tawait initPromise;\n\t\t\t} catch {\n\t\t\t\t// Initialization failure already surfaced to caller.\n\t\t\t}\n\t\t}\n\n\t\tif (this.#sqliteSystem) {\n\t\t\tawait this.#sqliteSystem.close();\n\t\t}\n\n\t\tthis.#sqliteSystem = null;\n\t\tthis.#sqlite3 = null;\n\t\tthis.#initPromise = null;\n\t}\n\n\t/**\n\t * Alias for destroy to align with DB-style lifecycle naming.\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.destroy();\n\t}\n}\n\n/**\n * Internal VFS implementation\n */\nclass SqliteSystem implements SqliteVfsRegistration {\n\treadonly name: string;\n\treadonly mxPathName = SQLITE_MAX_PATHNAME_BYTES;\n\treadonly mxPathname = SQLITE_MAX_PATHNAME_BYTES;\n\t#mainFileName: string | null = null;\n\t#mainFileOptions: KvVfsOptions | null = null;\n\treadonly #openFiles: Map<number, OpenFile> = new Map();\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #module: SQLiteModule;\n\t#heapDataView: DataView;\n\t#heapDataViewBuffer: ArrayBufferLike;\n\n\tconstructor(sqlite3: SQLite3Api, module: SQLiteModule, name: string) {\n\t\tthis.name = name;\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#module = module;\n\t\tthis.#heapDataViewBuffer = module.HEAPU8.buffer;\n\t\tthis.#heapDataView = new DataView(this.#heapDataViewBuffer);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#openFiles.clear();\n\t\tthis.#mainFileName = null;\n\t\tthis.#mainFileOptions = null;\n\t}\n\n\tisReady(): boolean {\n\t\treturn true;\n\t}\n\n\thasAsyncMethod(methodName: string): boolean {\n\t\treturn SQLITE_ASYNC_METHODS.has(methodName);\n\t}\n\n\t/**\n\t * Registers the VFS with SQLite\n\t */\n\tregister(): void {\n\t\tthis.#sqlite3.vfs_register(this, false);\n\t}\n\n\t/**\n\t * Registers a file with its KV options (before opening)\n\t */\n\tregisterFile(fileName: string, options: KvVfsOptions): void {\n\t\tif (!this.#mainFileName) {\n\t\t\tthis.#mainFileName = fileName;\n\t\t\tthis.#mainFileOptions = options;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#mainFileName !== fileName) {\n\t\t\tthrow new Error(\n\t\t\t\t`SqliteSystem is actor-scoped and expects one main file. Got ${fileName}, expected ${this.#mainFileName}.`,\n\t\t\t);\n\t\t}\n\n\t\tthis.#mainFileOptions = options;\n\t}\n\n\t/**\n\t * Unregisters a file's KV options (after closing)\n\t */\n\tunregisterFile(fileName: string): void {\n\t\tif (this.#mainFileName === fileName) {\n\t\t\tthis.#mainFileName = null;\n\t\t\tthis.#mainFileOptions = null;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve file path to the actor's main DB file or known SQLite sidecars.\n\t */\n\t#resolveFile(path: string): ResolvedFile | null {\n\t\tif (!this.#mainFileName || !this.#mainFileOptions) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (path === this.#mainFileName) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_MAIN };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-journal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_JOURNAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-wal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_WAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-shm`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_SHM };\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t#resolveFileOrThrow(path: string): ResolvedFile {\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (resolved) {\n\t\t\treturn resolved;\n\t\t}\n\n\t\tif (!this.#mainFileName) {\n\t\t\tthrow new Error(`No KV options registered for file: ${path}`);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Unsupported SQLite file path ${path}. Expected one of ${this.#mainFileName}, ${this.#mainFileName}-journal, ${this.#mainFileName}-wal, ${this.#mainFileName}-shm.`,\n\t\t);\n\t}\n\n\t#chunkKey(file: OpenFile, chunkIndex: number): Uint8Array {\n\t\treturn getChunkKey(file.fileTag, chunkIndex);\n\t}\n\n\tasync xOpen(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\tfileId: number,\n\t\tflags: number,\n\t\tpOutFlags: number,\n\t): Promise<number> {\n\t\tconst path = this.#decodeFilename(zName, flags);\n\t\tif (!path) {\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Get the registered KV options for this file\n\t\t// For journal/wal files, use the main database's options\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get existing file size if the file exists\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tlet size: number;\n\n\t\tif (sizeData) {\n\t\t\t// File exists, use existing size\n\t\t\tsize = decodeFileMeta(sizeData);\n\t\t\tif (!isValidFileSize(size)) {\n\t\t\t\treturn VFS.SQLITE_IOERR;\n\t\t\t}\n\t\t} else if (flags & VFS.SQLITE_OPEN_CREATE) {\n\t\t\t// File doesn't exist, create it\n\t\t\tsize = 0;\n\t\t\tawait options.put(metaKey, encodeFileMeta(size));\n\t\t} else {\n\t\t\t// File doesn't exist and we're not creating it\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Store open file info with options\n\t\tthis.#openFiles.set(fileId, {\n\t\t\tpath,\n\t\t\tfileTag,\n\t\t\tmetaKey,\n\t\t\tsize,\n\t\t\tmetaDirty: false,\n\t\t\tflags,\n\t\t\toptions,\n\t\t});\n\n\t\t// Set output flags to the actual flags used.\n\t\tthis.#writeInt32(pOutFlags, flags);\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xClose(fileId: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Delete-on-close files should skip metadata flush because the file will\n\t\t// be removed immediately.\n\t\tif (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {\n\t\t\tawait this.#delete(file.path);\n\t\t\tthis.#openFiles.delete(fileId);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tif (file.metaDirty) {\n\t\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\tthis.#openFiles.delete(fileId);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xRead(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst options = file.options;\n\t\tconst requestedLength = iAmt;\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\t\tconst fileSize = file.size;\n\n\t\t// If offset is beyond file size, return short read with zeroed buffer\n\t\tif (iOffset >= fileSize) {\n\t\t\tdata.fill(0);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\t// Calculate which chunks we need to read\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + requestedLength - 1) / CHUNK_SIZE);\n\n\t\t// Fetch all needed chunks\n\t\tconst chunkKeys: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tchunkKeys.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tconst chunks = await options.getBatch(chunkKeys);\n\n\t\t// Copy data from chunks to output buffer\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkData = chunks[i - startChunk];\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\n\t\t\t// Calculate the range within this chunk\n\t\t\tconst readStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst readEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + requestedLength - chunkOffset,\n\t\t\t);\n\n\t\t\tif (chunkData) {\n\t\t\t\t// Copy available data\n\t\t\t\tconst sourceStart = readStart;\n\t\t\t\tconst sourceEnd = Math.min(readEnd, chunkData.length);\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\n\t\t\t\tif (sourceEnd > sourceStart) {\n\t\t\t\t\tdata.set(chunkData.subarray(sourceStart, sourceEnd), destStart);\n\t\t\t\t}\n\n\t\t\t\t// Zero-fill if chunk is smaller than expected\n\t\t\t\tif (sourceEnd < readEnd) {\n\t\t\t\t\tconst zeroStart = destStart + (sourceEnd - sourceStart);\n\t\t\t\t\tconst zeroEnd = destStart + (readEnd - readStart);\n\t\t\t\t\tdata.fill(0, zeroStart, zeroEnd);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Chunk doesn't exist, zero-fill\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\t\t\t\tconst destEnd = destStart + (readEnd - readStart);\n\t\t\t\tdata.fill(0, destStart, destEnd);\n\t\t\t}\n\t\t}\n\n\t\t// If we read less than requested (past EOF), return short read\n\t\tconst actualBytes = Math.min(requestedLength, fileSize - iOffset);\n\t\tif (actualBytes < requestedLength) {\n\t\t\tdata.fill(0, actualBytes);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xWrite(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\t\tconst options = file.options;\n\t\tconst writeLength = iAmt;\n\t\tconst writeEndOffset = iOffset + writeLength;\n\t\tif (writeEndOffset > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\t// Calculate which chunks we need to modify\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + writeLength - 1) / CHUNK_SIZE);\n\n\t\tinterface WritePlan {\n\t\t\tchunkKey: Uint8Array;\n\t\t\tchunkOffset: number;\n\t\t\twriteStart: number;\n\t\t\twriteEnd: number;\n\t\t\texistingChunkIndex: number;\n\t\t}\n\n\t\t// Only fetch chunks where we must preserve existing prefix/suffix bytes.\n\t\tconst plans: WritePlan[] = [];\n\t\tconst chunkKeysToFetch: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\t\t\tconst writeStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst writeEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + writeLength - chunkOffset,\n\t\t\t);\n\t\t\tconst existingBytesInChunk = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(CHUNK_SIZE, file.size - chunkOffset),\n\t\t\t);\n\t\t\tconst needsExisting = writeStart > 0 || existingBytesInChunk > writeEnd;\n\t\t\tconst chunkKey = this.#chunkKey(file, i);\n\t\t\tlet existingChunkIndex = -1;\n\t\t\tif (needsExisting) {\n\t\t\t\texistingChunkIndex = chunkKeysToFetch.length;\n\t\t\t\tchunkKeysToFetch.push(chunkKey);\n\t\t\t}\n\t\t\tplans.push({\n\t\t\t\tchunkKey,\n\t\t\t\tchunkOffset,\n\t\t\t\twriteStart,\n\t\t\t\twriteEnd,\n\t\t\t\texistingChunkIndex,\n\t\t\t});\n\t\t}\n\n\t\tconst existingChunks = chunkKeysToFetch.length > 0\n\t\t\t? await options.getBatch(chunkKeysToFetch)\n\t\t\t: [];\n\n\t\t// Prepare new chunk data\n\t\tconst entriesToWrite: [Uint8Array, Uint8Array][] = [];\n\n\t\tfor (const plan of plans) {\n\t\t\tconst existingChunk =\n\t\t\t\tplan.existingChunkIndex >= 0\n\t\t\t\t\t? existingChunks[plan.existingChunkIndex]\n\t\t\t\t\t: null;\n\t\t\t// Create new chunk data\n\t\t\tlet newChunk: Uint8Array;\n\t\t\tif (existingChunk) {\n\t\t\t\tnewChunk = new Uint8Array(Math.max(existingChunk.length, plan.writeEnd));\n\t\t\t\tnewChunk.set(existingChunk);\n\t\t\t} else {\n\t\t\t\tnewChunk = new Uint8Array(plan.writeEnd);\n\t\t\t}\n\n\t\t\t// Copy data from input buffer to chunk\n\t\t\tconst sourceStart = plan.chunkOffset + plan.writeStart - iOffset;\n\t\t\tconst sourceEnd = sourceStart + (plan.writeEnd - plan.writeStart);\n\t\t\tnewChunk.set(data.subarray(sourceStart, sourceEnd), plan.writeStart);\n\n\t\t\tentriesToWrite.push([plan.chunkKey, newChunk]);\n\t\t}\n\n\t\t// Update file size if we wrote past the end\n\t\tconst previousSize = file.size;\n\t\tconst newSize = Math.max(file.size, writeEndOffset);\n\t\tif (newSize !== previousSize) {\n\t\t\tfile.size = newSize;\n\t\t\tfile.metaDirty = true;\n\t\t}\n\t\tif (file.metaDirty) {\n\t\t\tentriesToWrite.push([file.metaKey, encodeFileMeta(file.size)]);\n\t\t}\n\n\t\t// Write all chunks and metadata\n\t\tawait options.putBatch(entriesToWrite);\n\t\tif (file.metaDirty) {\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xTruncate(\n\t\tfileId: number,\n\t\tsizeLo: number,\n\t\tsizeHi: number,\n\t): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\n\t\tconst size = delegalize(sizeLo, sizeHi);\n\t\tif (size < 0 || size > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\t\tconst options = file.options;\n\n\t\t// If truncating to larger size, just update metadata\n\t\tif (size >= file.size) {\n\t\t\tif (size > file.size) {\n\t\t\t\tfile.size = size;\n\t\t\t\tfile.metaDirty = true;\n\t\t\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\t\tfile.metaDirty = false;\n\t\t\t}\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Calculate which chunks to delete\n\t\t// Note: When size=0, lastChunkToKeep = floor(-1/4096) = -1, which means\n\t\t// all chunks (starting from index 0) will be deleted in the loop below.\n\t\tconst lastChunkToKeep = Math.floor((size - 1) / CHUNK_SIZE);\n\t\tconst lastExistingChunk = Math.floor((file.size - 1) / CHUNK_SIZE);\n\n\t\t// Delete chunks beyond the new size\n\t\tconst keysToDelete: Uint8Array[] = [];\n\t\tfor (let i = lastChunkToKeep + 1; i <= lastExistingChunk; i++) {\n\t\t\tkeysToDelete.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tif (keysToDelete.length > 0) {\n\t\t\tawait options.deleteBatch(keysToDelete);\n\t\t}\n\n\t\t// Truncate the last kept chunk if needed\n\t\tif (size > 0 && size % CHUNK_SIZE !== 0) {\n\t\t\tconst lastChunkKey = this.#chunkKey(file, lastChunkToKeep);\n\t\t\tconst lastChunkData = await options.get(lastChunkKey);\n\n\t\t\tif (lastChunkData && lastChunkData.length > size % CHUNK_SIZE) {\n\t\t\t\tconst truncatedChunk = lastChunkData.subarray(0, size % CHUNK_SIZE);\n\t\t\t\tawait options.put(lastChunkKey, truncatedChunk);\n\t\t\t}\n\t\t}\n\n\t\t// Update file size\n\t\tfile.size = size;\n\t\tfile.metaDirty = true;\n\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xSync(fileId: number, _flags: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file || !file.metaDirty) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xFileSize(fileId: number, pSize: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_FSTAT;\n\t\t}\n\n\t\t// Set size as 64-bit integer.\n\t\tthis.#writeBigInt64(pSize, BigInt(file.size));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xDelete(_pVfs: number, zName: number, _syncDir: number): Promise<number> {\n\t\tawait this.#delete(this.#module.UTF8ToString(zName));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t/**\n\t * Internal delete implementation\n\t */\n\tasync #delete(path: string): Promise<void> {\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get file size to find out how many chunks to delete\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tif (!sizeData) {\n\t\t\t// File doesn't exist, that's OK\n\t\t\treturn;\n\t\t}\n\n\t\tconst size = decodeFileMeta(sizeData);\n\n\t\t// Delete all chunks\n\t\tconst keysToDelete: Uint8Array[] = [metaKey];\n\t\tconst numChunks = Math.ceil(size / CHUNK_SIZE);\n\t\tfor (let i = 0; i < numChunks; i++) {\n\t\t\tkeysToDelete.push(getChunkKey(fileTag, i));\n\t\t}\n\n\t\tawait options.deleteBatch(keysToDelete);\n\t}\n\n\tasync xAccess(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\t_flags: number,\n\t\tpResOut: number,\n\t): Promise<number> {\n\t\t// TODO: Measure how often xAccess runs during open and whether these\n\t\t// existence checks add meaningful KV round-trip overhead. If they do,\n\t\t// consider serving file existence from in-memory state.\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (!resolved) {\n\t\t\t// File not registered, doesn't exist\n\t\t\tthis.#writeInt32(pResOut, 0);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst compactMetaKey = getMetaKey(resolved.fileTag);\n\t\tconst metaData = await resolved.options.get(compactMetaKey);\n\n\t\t// Set result: 1 if file exists, 0 otherwise\n\t\tthis.#writeInt32(pResOut, metaData ? 1 : 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txCheckReservedLock(_fileId: number, pResOut: number): number {\n\t\t// This VFS is actor-scoped with one writer, so there is no external\n\t\t// reserved lock state to report.\n\t\tthis.#writeInt32(pResOut, 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txLock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txUnlock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txFileControl(_fileId: number, _flags: number, _pArg: number): number {\n\t\treturn VFS.SQLITE_NOTFOUND;\n\t}\n\n\txDeviceCharacteristics(_fileId: number): number {\n\t\treturn 0;\n\t}\n\n\txFullPathname(_pVfs: number, zName: number, nOut: number, zOut: number): number {\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst bytes = TEXT_ENCODER.encode(path);\n\t\tconst out = this.#module.HEAPU8.subarray(zOut, zOut + nOut);\n\t\tif (bytes.length >= out.length) {\n\t\t\treturn VFS.SQLITE_IOERR;\n\t\t}\n\t\tout.set(bytes, 0);\n\t\tout[bytes.length] = 0;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t#decodeFilename(zName: number, flags: number): string | null {\n\t\tif (!zName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (flags & VFS.SQLITE_OPEN_URI) {\n\t\t\t// Decode SQLite URI filename layout: path\\0key\\0value\\0...\\0\n\t\t\tlet pName = zName;\n\t\t\tlet state: 1 | 2 | 3 | null = 1;\n\t\t\tconst charCodes: number[] = [];\n\t\t\twhile (state) {\n\t\t\t\tconst charCode = this.#module.HEAPU8[pName++];\n\t\t\t\tif (charCode) {\n\t\t\t\t\tcharCodes.push(charCode);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!this.#module.HEAPU8[pName]) {\n\t\t\t\t\tstate = null;\n\t\t\t\t}\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tcharCodes.push(\"?\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tcharCodes.push(\"=\".charCodeAt(0));\n\t\t\t\t\t\tstate = 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tcharCodes.push(\"&\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn TEXT_DECODER.decode(new Uint8Array(charCodes));\n\t\t}\n\n\t\treturn this.#module.UTF8ToString(zName);\n\t}\n\n\t#heapView(): DataView {\n\t\tconst heapBuffer = this.#module.HEAPU8.buffer;\n\t\tif (heapBuffer !== this.#heapDataViewBuffer) {\n\t\t\tthis.#heapDataViewBuffer = heapBuffer;\n\t\t\tthis.#heapDataView = new DataView(heapBuffer);\n\t\t}\n\t\treturn this.#heapDataView;\n\t}\n\n\t#writeInt32(pointer: number, value: number): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setInt32(heapByteOffset, value, true);\n\t}\n\n\t#writeBigInt64(pointer: number, value: bigint): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setBigInt64(heapByteOffset, value, true);\n\t}\n}\n\n/**\n * Rebuild an i64 from Emscripten's legalized (lo32, hi32) pair.\n * SQLite passes file offsets and sizes this way. We decode into unsigned words\n * and reject values above the VFS max file size.\n */\nfunction delegalize(lo32: number, hi32: number): number {\n\tconst hi = hi32 >>> 0;\n\tconst lo = lo32 >>> 0;\n\tif (hi > MAX_FILE_SIZE_HI32) {\n\t\treturn -1;\n\t}\n\tif (hi === MAX_FILE_SIZE_HI32 && lo > MAX_FILE_SIZE_LO32) {\n\t\treturn -1;\n\t}\n\treturn (hi * UINT32_SIZE) + lo;\n}\n","/**\n * Key management for SQLite VFS storage\n *\n * This module contains constants and utilities for building keys used in the\n * key-value store for SQLite file storage.\n */\n\n/**\n * Size of each file chunk stored in KV.\n *\n * SQLite calls the VFS with byte ranges, but KV stores whole values by key.\n * The VFS maps each byte range to one or more fixed-size chunks, then uses\n * chunk keys to read or write those values in KV.\n */\nexport const CHUNK_SIZE = 4096;\n\n/** Top-level SQLite prefix (must match SQLITE_PREFIX in actor KV system) */\nexport const SQLITE_PREFIX = 8;\n\n/** Schema version namespace byte after SQLITE_PREFIX */\nexport const SQLITE_SCHEMA_VERSION = 1;\n\n/** Key prefix byte for file metadata (after SQLITE_PREFIX + version) */\nexport const META_PREFIX = 0;\n\n/** Key prefix byte for file chunks (after SQLITE_PREFIX + version) */\nexport const CHUNK_PREFIX = 1;\n\n/** File kind tag for the actor's main database file */\nexport const FILE_TAG_MAIN = 0;\n\n/** File kind tag for the actor's rollback journal sidecar */\nexport const FILE_TAG_JOURNAL = 1;\n\n/** File kind tag for the actor's WAL sidecar */\nexport const FILE_TAG_WAL = 2;\n\n/** File kind tag for the actor's SHM sidecar */\nexport const FILE_TAG_SHM = 3;\n\nexport type SqliteFileTag =\n\t| typeof FILE_TAG_MAIN\n\t| typeof FILE_TAG_JOURNAL\n\t| typeof FILE_TAG_WAL\n\t| typeof FILE_TAG_SHM;\n\n/**\n * Gets the key for file metadata\n * Format: [SQLITE_PREFIX (1 byte), version (1 byte), META_PREFIX (1 byte), file tag (1 byte)]\n */\nexport function getMetaKey(fileTag: SqliteFileTag): Uint8Array {\n\tconst key = new Uint8Array(4);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = META_PREFIX;\n\tkey[3] = fileTag;\n\treturn key;\n}\n\n/**\n * Gets the key for one chunk of file data.\n * Format: [SQLITE_PREFIX, CHUNK_PREFIX, file tag, chunk index (u32 big-endian)]\n *\n * The chunk index is derived from byte offset as floor(offset / CHUNK_SIZE),\n * which is how SQLite byte ranges map onto KV keys.\n */\nexport function getChunkKey(\n\tfileTag: SqliteFileTag,\n\tchunkIndex: number,\n): Uint8Array {\n\tconst key = new Uint8Array(8);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = CHUNK_PREFIX;\n\tkey[3] = fileTag;\n\tkey[4] = (chunkIndex >>> 24) & 0xff;\n\tkey[5] = (chunkIndex >>> 16) & 0xff;\n\tkey[6] = (chunkIndex >>> 8) & 0xff;\n\tkey[7] = chunkIndex & 0xff;\n\treturn key;\n}\n","import { createVersionedDataHandler } from \"vbare\";\nimport * as v1 from \"../../dist/schemas/file-meta/v1\";\n\nexport const CURRENT_VERSION = 1;\n\nexport const FILE_META_VERSIONED = createVersionedDataHandler<v1.FileMeta>({\n\tdeserializeVersion: (bytes, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.decodeFileMeta(bytes);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tserializeVersion: (data, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.encodeFileMeta(data as v1.FileMeta);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tdeserializeConverters: () => [],\n\tserializeConverters: () => [],\n});\n","// @generated - post-processed by compile-bare.ts\nimport * as bare from \"@rivetkit/bare-ts\"\n\nconst config = /* @__PURE__ */ bare.Config({})\n\nexport type u64 = bigint\n\nexport type FileMeta = {\n readonly size: u64,\n}\n\nexport function readFileMeta(bc: bare.ByteCursor): FileMeta {\n return {\n size: bare.readU64(bc),\n }\n}\n\nexport function writeFileMeta(bc: bare.ByteCursor, x: FileMeta): void {\n bare.writeU64(bc, x.size)\n}\n\nexport function encodeFileMeta(x: FileMeta): Uint8Array {\n const bc = new bare.ByteCursor(\n new Uint8Array(config.initialBufferLength),\n config\n )\n writeFileMeta(bc, x)\n return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset)\n}\n\nexport function decodeFileMeta(bytes: Uint8Array): FileMeta {\n const bc = new bare.ByteCursor(bytes, config)\n const result = readFileMeta(bc)\n if (bc.offset < bc.view.byteLength) {\n throw new bare.BareError(bc.offset, \"remaining bytes\")\n }\n return result\n}\n\n\nfunction assert(condition: boolean, message?: string): asserts condition {\n if (!condition) throw new Error(message ?? \"Assertion failed\")\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/rivet/rivet/rivetkit-typescript/packages/sqlite-vfs/dist/tsup/index.cjs","../../../../../node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.53.2_@types+node@22.19.10__@swc+core@1.15.11_@swc_c97c1b0c6eca1589eb738d8730776c3e/node_modules/tsup/assets/cjs_shims.js","../../src/vfs.ts","../../src/kv.ts","../../schemas/file-meta/versioned.ts","../schemas/file-meta/v1.ts"],"names":["SQLITE_OPEN_CREATE"],"mappings":"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","file":"/home/runner/work/rivet/rivet/rivetkit-typescript/packages/sqlite-vfs/dist/tsup/index.cjs","sourcesContent":[null,"// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\n * SQLite raw database with KV storage backend\n *\n * This module provides a SQLite API that uses a KV-backed VFS\n * for storage. Each SqliteVfs instance is independent and can be\n * used concurrently with other instances.\n *\n * Keep this VFS on direct VFS.Base callbacks for minimal wrapper overhead.\n * Use @rivetkit/sqlite/src/FacadeVFS.js as the reference implementation for\n * callback ABI and pointer/data conversion behavior.\n * This implementation is optimized for single-writer semantics because each\n * actor owns one SQLite database.\n * SQLite invokes this VFS with byte-range file operations. This VFS maps those\n * ranges onto fixed-size KV chunks keyed by file tag and chunk index.\n * We intentionally rely on SQLite's pager cache for hot page reuse and do not\n * add a second cache in this VFS. This avoids duplicate cache invalidation\n * logic and keeps memory usage predictable for each actor.\n */\n\nimport * as VFS from \"@rivetkit/sqlite/src/VFS.js\";\nimport {\n\tFactory,\n\tSQLITE_OPEN_CREATE,\n\tSQLITE_OPEN_READWRITE,\n\tSQLITE_ROW,\n} from \"@rivetkit/sqlite\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport {\n\tCHUNK_SIZE,\n\tFILE_TAG_JOURNAL,\n\tFILE_TAG_MAIN,\n\tFILE_TAG_SHM,\n\tFILE_TAG_WAL,\n\tgetChunkKey,\n\tgetMetaKey,\n\ttype SqliteFileTag,\n} from \"./kv\";\nimport {\n\tFILE_META_VERSIONED,\n\tCURRENT_VERSION,\n} from \"../schemas/file-meta/versioned\";\nimport type { FileMeta } from \"../schemas/file-meta/mod\";\nimport type { KvVfsOptions } from \"./types\";\n\ntype SqliteEsmFactory = (config?: { wasmBinary?: ArrayBuffer | Uint8Array }) => Promise<unknown>;\ntype SQLite3Api = ReturnType<typeof Factory>;\ntype SqliteBindings = Parameters<SQLite3Api[\"bind_collection\"]>[1];\ntype SqliteVfsRegistration = Parameters<SQLite3Api[\"vfs_register\"]>[0];\n\ninterface SQLiteModule {\n\tUTF8ToString: (ptr: number) => string;\n\tHEAPU8: Uint8Array;\n}\n\nconst TEXT_ENCODER = new TextEncoder();\nconst TEXT_DECODER = new TextDecoder();\nconst SQLITE_MAX_PATHNAME_BYTES = 64;\n\n// Chunk keys encode the chunk index in 32 bits, so a file can span at most\n// 2^32 chunks. At 4 KiB/chunk this yields a hard limit of 16 TiB.\nconst UINT32_SIZE = 0x100000000;\nconst MAX_CHUNK_INDEX = 0xffffffff;\nconst MAX_FILE_SIZE_BYTES = (MAX_CHUNK_INDEX + 1) * CHUNK_SIZE;\nconst MAX_FILE_SIZE_HI32 = Math.floor(MAX_FILE_SIZE_BYTES / UINT32_SIZE);\nconst MAX_FILE_SIZE_LO32 = MAX_FILE_SIZE_BYTES % UINT32_SIZE;\n\n// libvfs captures this async/sync mask at registration time. Any VFS callback\n// that returns a Promise must be listed here so SQLite uses async relays.\nconst SQLITE_ASYNC_METHODS = new Set([\n\t\"xOpen\",\n\t\"xClose\",\n\t\"xRead\",\n\t\"xWrite\",\n\t\"xTruncate\",\n\t\"xSync\",\n\t\"xFileSize\",\n\t\"xDelete\",\n\t\"xAccess\",\n]);\n\ninterface LoadedSqliteRuntime {\n\tsqlite3: SQLite3Api;\n\tmodule: SQLiteModule;\n}\n\nfunction isSqliteEsmFactory(value: unknown): value is SqliteEsmFactory {\n\treturn typeof value === \"function\";\n}\n\nfunction isSQLiteModule(value: unknown): value is SQLiteModule {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as {\n\t\tUTF8ToString?: unknown;\n\t\tHEAPU8?: unknown;\n\t};\n\treturn (\n\t\ttypeof candidate.UTF8ToString === \"function\" &&\n\t\tcandidate.HEAPU8 instanceof Uint8Array\n\t);\n}\n\n\n/**\n * Lazily load and instantiate the async SQLite module for this VFS instance.\n * We do this on first open so actors that do not use SQLite do not pay module\n * parse and wasm initialization cost at startup, and we pass wasmBinary\n * explicitly so this works consistently in both ESM and CJS bundles.\n */\nasync function loadSqliteRuntime(): Promise<LoadedSqliteRuntime> {\n\t// Keep the module specifier assembled at runtime so TypeScript declaration\n\t// generation does not try to typecheck this deep dist import path.\n\t// Uses Array.join() instead of string concatenation to prevent esbuild/tsup\n\t// from constant-folding the expression at build time, which would allow\n\t// Turbopack to trace into the WASM package.\n\tconst specifier = [\"@rivetkit/sqlite\", \"dist\", \"wa-sqlite-async.mjs\"].join(\"/\");\n\tconst sqliteModule = await import(specifier);\n\tif (!isSqliteEsmFactory(sqliteModule.default)) {\n\t\tthrow new Error(\"Invalid SQLite ESM factory export\");\n\t}\n\tconst sqliteEsmFactory = sqliteModule.default;\n\tconst require = createRequire(import.meta.url);\n\tconst wasmPath = require.resolve(\"@rivetkit/sqlite/dist/wa-sqlite-async.wasm\");\n\tconst wasmBinary = readFileSync(wasmPath);\n\tconst module = await sqliteEsmFactory({ wasmBinary });\n\tif (!isSQLiteModule(module)) {\n\t\tthrow new Error(\"Invalid SQLite runtime module\");\n\t}\n\treturn {\n\t\tsqlite3: Factory(module),\n\t\tmodule,\n\t};\n}\n\n/**\n * Represents an open file\n */\ninterface OpenFile {\n\t/** File path */\n\tpath: string;\n\t/** File kind tag used by compact key layout */\n\tfileTag: SqliteFileTag;\n\t/** Precomputed metadata key */\n\tmetaKey: Uint8Array;\n\t/** File size in bytes */\n\tsize: number;\n\t/** True when in-memory size has not been persisted yet */\n\tmetaDirty: boolean;\n\t/** Open flags */\n\tflags: number;\n\t/** KV options for this file */\n\toptions: KvVfsOptions;\n}\n\ninterface ResolvedFile {\n\toptions: KvVfsOptions;\n\tfileTag: SqliteFileTag;\n}\n\n/**\n * Encodes file metadata to a Uint8Array using BARE schema\n */\nfunction encodeFileMeta(size: number): Uint8Array {\n\tconst meta: FileMeta = { size: BigInt(size) };\n\treturn FILE_META_VERSIONED.serializeWithEmbeddedVersion(\n\t\tmeta,\n\t\tCURRENT_VERSION,\n\t);\n}\n\n/**\n * Decodes file metadata from a Uint8Array using BARE schema\n */\nfunction decodeFileMeta(data: Uint8Array): number {\n\tconst meta = FILE_META_VERSIONED.deserializeWithEmbeddedVersion(data);\n\treturn Number(meta.size);\n}\n\nfunction isValidFileSize(size: number): boolean {\n\treturn Number.isSafeInteger(size) && size >= 0 && size <= MAX_FILE_SIZE_BYTES;\n}\n\n/**\n * Simple async mutex for serializing database operations\n * @rivetkit/sqlite calls are not safe to run concurrently on one module instance\n */\nclass AsyncMutex {\n\t#locked = false;\n\t#waiting: (() => void)[] = [];\n\n\tasync acquire(): Promise<void> {\n\t\twhile (this.#locked) {\n\t\t\tawait new Promise<void>((resolve) => this.#waiting.push(resolve));\n\t\t}\n\t\tthis.#locked = true;\n\t}\n\n\trelease(): void {\n\t\tthis.#locked = false;\n\t\tconst next = this.#waiting.shift();\n\t\tif (next) {\n\t\t\tnext();\n\t\t}\n\t}\n\n\tasync run<T>(fn: () => Promise<T>): Promise<T> {\n\t\tawait this.acquire();\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tthis.release();\n\t\t}\n\t}\n}\n\n/**\n * Database wrapper that provides a simplified SQLite API\n */\nexport class Database {\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #handle: number;\n\treadonly #fileName: string;\n\treadonly #onClose: () => void;\n\treadonly #sqliteMutex: AsyncMutex;\n\n\tconstructor(\n\t\tsqlite3: SQLite3Api,\n\t\thandle: number,\n\t\tfileName: string,\n\t\tonClose: () => void,\n\t\tsqliteMutex: AsyncMutex,\n\t) {\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#handle = handle;\n\t\tthis.#fileName = fileName;\n\t\tthis.#onClose = onClose;\n\t\tthis.#sqliteMutex = sqliteMutex;\n\t}\n\n\t/**\n\t * Execute SQL with optional row callback\n\t * @param sql - SQL statement to execute\n\t * @param callback - Called for each result row with (row, columns)\n\t */\n\tasync exec(sql: string, callback?: (row: unknown[], columns: string[]) => void): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.exec(this.#handle, sql, callback);\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL statement (no result rows)\n\t * @param sql - SQL statement with ? placeholders\n\t * @param params - Parameter values to bind\n\t */\n\tasync run(sql: string, params?: SqliteBindings): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\t// Consume rows for statements that return results.\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL query and return results\n\t * @param sql - SQL query with ? placeholders\n\t * @param params - Parameter values to bind\n\t * @returns Object with rows (array of arrays) and columns (column names)\n\t */\n\tasync query(sql: string, params?: SqliteBindings): Promise<{ rows: unknown[][]; columns: string[] }> {\n\t\treturn this.#sqliteMutex.run(async () => {\n\t\t\tconst rows: unknown[][] = [];\n\t\t\tlet columns: string[] = [];\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\tif (columns.length === 0) {\n\t\t\t\t\t\tcolumns = this.#sqlite3.column_names(stmt);\n\t\t\t\t\t}\n\t\t\t\t\trows.push(this.#sqlite3.row(stmt));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { rows, columns };\n\t\t});\n\t}\n\n\t/**\n\t * Close the database\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.close(this.#handle);\n\t\t});\n\t\tthis.#onClose();\n\t}\n\n\t/**\n\t * Get the raw @rivetkit/sqlite API (for advanced usage)\n\t */\n\tget sqlite3(): SQLite3Api {\n\t\treturn this.#sqlite3;\n\t}\n\n\t/**\n\t * Get the raw database handle (for advanced usage)\n\t */\n\tget handle(): number {\n\t\treturn this.#handle;\n\t}\n}\n\n/**\n * SQLite VFS backed by KV storage.\n *\n * Each instance is independent and has its own @rivetkit/sqlite WASM module.\n * This allows multiple instances to operate concurrently without interference.\n */\nexport class SqliteVfs {\n\t#sqlite3: SQLite3Api | null = null;\n\t#sqliteSystem: SqliteSystem | null = null;\n\t#initPromise: Promise<void> | null = null;\n\t#openMutex = new AsyncMutex();\n\t#sqliteMutex = new AsyncMutex();\n\t#instanceId: string;\n\t#destroyed = false;\n\n\tconstructor() {\n\t\t// Generate unique instance ID for VFS name\n\t\tthis.#instanceId = crypto.randomUUID().replace(/-/g, '').slice(0, 8);\n\t}\n\n\t/**\n\t * Initialize @rivetkit/sqlite and VFS (called once per instance)\n\t */\n\tasync #ensureInitialized(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Fast path: already initialized\n\t\tif (this.#sqlite3 && this.#sqliteSystem) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Synchronously create the promise if not started\n\t\tif (!this.#initPromise) {\n\t\t\tthis.#initPromise = (async () => {\n\t\t\t\tconst { sqlite3, module } = await loadSqliteRuntime();\n\t\t\t\tif (this.#destroyed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#sqlite3 = sqlite3;\n\t\t\t\tthis.#sqliteSystem = new SqliteSystem(\n\t\t\t\t\tsqlite3,\n\t\t\t\t\tmodule,\n\t\t\t\t\t`kv-vfs-${this.#instanceId}`,\n\t\t\t\t);\n\t\t\t\tthis.#sqliteSystem.register();\n\t\t\t})();\n\t\t}\n\n\t\t// Wait for initialization\n\t\ttry {\n\t\t\tawait this.#initPromise;\n\t\t} catch (error) {\n\t\t\tthis.#initPromise = null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Open a SQLite database using KV storage backend\n\t *\n\t * @param fileName - The database file name (typically the actor ID)\n\t * @param options - KV storage operations for this database\n\t * @returns A Database instance\n\t */\n\tasync open(\n\t\tfileName: string,\n\t\toptions: KvVfsOptions,\n\t): Promise<Database> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Serialize all open operations within this instance\n\t\tawait this.#openMutex.acquire();\n\t\ttry {\n\t\t\t// Initialize @rivetkit/sqlite and SqliteSystem on first call\n\t\t\tawait this.#ensureInitialized();\n\n\t\t\tif (!this.#sqlite3 || !this.#sqliteSystem) {\n\t\t\t\tthrow new Error(\"Failed to initialize SQLite\");\n\t\t\t}\n\t\t\tconst sqlite3 = this.#sqlite3;\n\t\t\tconst sqliteSystem = this.#sqliteSystem;\n\n\t\t\t// Register this filename with its KV options\n\t\t\tsqliteSystem.registerFile(fileName, options);\n\n\t\t\t// Open database\n\t\t\tconst db = await this.#sqliteMutex.run(async () =>\n\t\t\t\tsqlite3.open_v2(\n\t\t\t\t\tfileName,\n\t\t\t\t\tSQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,\n\t\t\t\t\tsqliteSystem.name,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// TODO: Benchmark PRAGMA tuning for KV-backed SQLite after open.\n\t\t\t// Start with journal_mode=PERSIST and journal_size_limit to reduce\n\t\t\t// journal churn on high-latency KV without introducing WAL.\n\n\t\t\t// Create cleanup callback\n\t\t\tconst onClose = () => {\n\t\t\t\tsqliteSystem.unregisterFile(fileName);\n\t\t\t};\n\n\t\t\treturn new Database(\n\t\t\t\tsqlite3,\n\t\t\t\tdb,\n\t\t\t\tfileName,\n\t\t\t\tonClose,\n\t\t\t\tthis.#sqliteMutex,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.#openMutex.release();\n\t\t}\n\t}\n\n\t/**\n\t * Tears down this VFS instance and releases internal references.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#destroyed = true;\n\n\t\tconst initPromise = this.#initPromise;\n\t\tif (initPromise) {\n\t\t\ttry {\n\t\t\t\tawait initPromise;\n\t\t\t} catch {\n\t\t\t\t// Initialization failure already surfaced to caller.\n\t\t\t}\n\t\t}\n\n\t\tif (this.#sqliteSystem) {\n\t\t\tawait this.#sqliteSystem.close();\n\t\t}\n\n\t\tthis.#sqliteSystem = null;\n\t\tthis.#sqlite3 = null;\n\t\tthis.#initPromise = null;\n\t}\n\n\t/**\n\t * Alias for destroy to align with DB-style lifecycle naming.\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.destroy();\n\t}\n}\n\n/**\n * Internal VFS implementation\n */\nclass SqliteSystem implements SqliteVfsRegistration {\n\treadonly name: string;\n\treadonly mxPathName = SQLITE_MAX_PATHNAME_BYTES;\n\treadonly mxPathname = SQLITE_MAX_PATHNAME_BYTES;\n\t#mainFileName: string | null = null;\n\t#mainFileOptions: KvVfsOptions | null = null;\n\treadonly #openFiles: Map<number, OpenFile> = new Map();\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #module: SQLiteModule;\n\t#heapDataView: DataView;\n\t#heapDataViewBuffer: ArrayBufferLike;\n\n\tconstructor(sqlite3: SQLite3Api, module: SQLiteModule, name: string) {\n\t\tthis.name = name;\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#module = module;\n\t\tthis.#heapDataViewBuffer = module.HEAPU8.buffer;\n\t\tthis.#heapDataView = new DataView(this.#heapDataViewBuffer);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#openFiles.clear();\n\t\tthis.#mainFileName = null;\n\t\tthis.#mainFileOptions = null;\n\t}\n\n\tisReady(): boolean {\n\t\treturn true;\n\t}\n\n\thasAsyncMethod(methodName: string): boolean {\n\t\treturn SQLITE_ASYNC_METHODS.has(methodName);\n\t}\n\n\t/**\n\t * Registers the VFS with SQLite\n\t */\n\tregister(): void {\n\t\tthis.#sqlite3.vfs_register(this, false);\n\t}\n\n\t/**\n\t * Registers a file with its KV options (before opening)\n\t */\n\tregisterFile(fileName: string, options: KvVfsOptions): void {\n\t\tif (!this.#mainFileName) {\n\t\t\tthis.#mainFileName = fileName;\n\t\t\tthis.#mainFileOptions = options;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#mainFileName !== fileName) {\n\t\t\tthrow new Error(\n\t\t\t\t`SqliteSystem is actor-scoped and expects one main file. Got ${fileName}, expected ${this.#mainFileName}.`,\n\t\t\t);\n\t\t}\n\n\t\tthis.#mainFileOptions = options;\n\t}\n\n\t/**\n\t * Unregisters a file's KV options (after closing)\n\t */\n\tunregisterFile(fileName: string): void {\n\t\tif (this.#mainFileName === fileName) {\n\t\t\tthis.#mainFileName = null;\n\t\t\tthis.#mainFileOptions = null;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve file path to the actor's main DB file or known SQLite sidecars.\n\t */\n\t#resolveFile(path: string): ResolvedFile | null {\n\t\tif (!this.#mainFileName || !this.#mainFileOptions) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (path === this.#mainFileName) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_MAIN };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-journal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_JOURNAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-wal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_WAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-shm`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_SHM };\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t#resolveFileOrThrow(path: string): ResolvedFile {\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (resolved) {\n\t\t\treturn resolved;\n\t\t}\n\n\t\tif (!this.#mainFileName) {\n\t\t\tthrow new Error(`No KV options registered for file: ${path}`);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Unsupported SQLite file path ${path}. Expected one of ${this.#mainFileName}, ${this.#mainFileName}-journal, ${this.#mainFileName}-wal, ${this.#mainFileName}-shm.`,\n\t\t);\n\t}\n\n\t#chunkKey(file: OpenFile, chunkIndex: number): Uint8Array {\n\t\treturn getChunkKey(file.fileTag, chunkIndex);\n\t}\n\n\tasync xOpen(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\tfileId: number,\n\t\tflags: number,\n\t\tpOutFlags: number,\n\t): Promise<number> {\n\t\tconst path = this.#decodeFilename(zName, flags);\n\t\tif (!path) {\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Get the registered KV options for this file\n\t\t// For journal/wal files, use the main database's options\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get existing file size if the file exists\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tlet size: number;\n\n\t\tif (sizeData) {\n\t\t\t// File exists, use existing size\n\t\t\tsize = decodeFileMeta(sizeData);\n\t\t\tif (!isValidFileSize(size)) {\n\t\t\t\treturn VFS.SQLITE_IOERR;\n\t\t\t}\n\t\t} else if (flags & VFS.SQLITE_OPEN_CREATE) {\n\t\t\t// File doesn't exist, create it\n\t\t\tsize = 0;\n\t\t\tawait options.put(metaKey, encodeFileMeta(size));\n\t\t} else {\n\t\t\t// File doesn't exist and we're not creating it\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Store open file info with options\n\t\tthis.#openFiles.set(fileId, {\n\t\t\tpath,\n\t\t\tfileTag,\n\t\t\tmetaKey,\n\t\t\tsize,\n\t\t\tmetaDirty: false,\n\t\t\tflags,\n\t\t\toptions,\n\t\t});\n\n\t\t// Set output flags to the actual flags used.\n\t\tthis.#writeInt32(pOutFlags, flags);\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xClose(fileId: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Delete-on-close files should skip metadata flush because the file will\n\t\t// be removed immediately.\n\t\tif (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {\n\t\t\tawait this.#delete(file.path);\n\t\t\tthis.#openFiles.delete(fileId);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tif (file.metaDirty) {\n\t\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\tthis.#openFiles.delete(fileId);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xRead(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst options = file.options;\n\t\tconst requestedLength = iAmt;\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\t\tconst fileSize = file.size;\n\n\t\t// If offset is beyond file size, return short read with zeroed buffer\n\t\tif (iOffset >= fileSize) {\n\t\t\tdata.fill(0);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\t// Calculate which chunks we need to read\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + requestedLength - 1) / CHUNK_SIZE);\n\n\t\t// Fetch all needed chunks\n\t\tconst chunkKeys: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tchunkKeys.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tconst chunks = await options.getBatch(chunkKeys);\n\n\t\t// Copy data from chunks to output buffer\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkData = chunks[i - startChunk];\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\n\t\t\t// Calculate the range within this chunk\n\t\t\tconst readStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst readEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + requestedLength - chunkOffset,\n\t\t\t);\n\n\t\t\tif (chunkData) {\n\t\t\t\t// Copy available data\n\t\t\t\tconst sourceStart = readStart;\n\t\t\t\tconst sourceEnd = Math.min(readEnd, chunkData.length);\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\n\t\t\t\tif (sourceEnd > sourceStart) {\n\t\t\t\t\tdata.set(chunkData.subarray(sourceStart, sourceEnd), destStart);\n\t\t\t\t}\n\n\t\t\t\t// Zero-fill if chunk is smaller than expected\n\t\t\t\tif (sourceEnd < readEnd) {\n\t\t\t\t\tconst zeroStart = destStart + (sourceEnd - sourceStart);\n\t\t\t\t\tconst zeroEnd = destStart + (readEnd - readStart);\n\t\t\t\t\tdata.fill(0, zeroStart, zeroEnd);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Chunk doesn't exist, zero-fill\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\t\t\t\tconst destEnd = destStart + (readEnd - readStart);\n\t\t\t\tdata.fill(0, destStart, destEnd);\n\t\t\t}\n\t\t}\n\n\t\t// If we read less than requested (past EOF), return short read\n\t\tconst actualBytes = Math.min(requestedLength, fileSize - iOffset);\n\t\tif (actualBytes < requestedLength) {\n\t\t\tdata.fill(0, actualBytes);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xWrite(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\t\tconst options = file.options;\n\t\tconst writeLength = iAmt;\n\t\tconst writeEndOffset = iOffset + writeLength;\n\t\tif (writeEndOffset > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\t// Calculate which chunks we need to modify\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + writeLength - 1) / CHUNK_SIZE);\n\n\t\tinterface WritePlan {\n\t\t\tchunkKey: Uint8Array;\n\t\t\tchunkOffset: number;\n\t\t\twriteStart: number;\n\t\t\twriteEnd: number;\n\t\t\texistingChunkIndex: number;\n\t\t}\n\n\t\t// Only fetch chunks where we must preserve existing prefix/suffix bytes.\n\t\tconst plans: WritePlan[] = [];\n\t\tconst chunkKeysToFetch: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\t\t\tconst writeStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst writeEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + writeLength - chunkOffset,\n\t\t\t);\n\t\t\tconst existingBytesInChunk = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(CHUNK_SIZE, file.size - chunkOffset),\n\t\t\t);\n\t\t\tconst needsExisting = writeStart > 0 || existingBytesInChunk > writeEnd;\n\t\t\tconst chunkKey = this.#chunkKey(file, i);\n\t\t\tlet existingChunkIndex = -1;\n\t\t\tif (needsExisting) {\n\t\t\t\texistingChunkIndex = chunkKeysToFetch.length;\n\t\t\t\tchunkKeysToFetch.push(chunkKey);\n\t\t\t}\n\t\t\tplans.push({\n\t\t\t\tchunkKey,\n\t\t\t\tchunkOffset,\n\t\t\t\twriteStart,\n\t\t\t\twriteEnd,\n\t\t\t\texistingChunkIndex,\n\t\t\t});\n\t\t}\n\n\t\tconst existingChunks = chunkKeysToFetch.length > 0\n\t\t\t? await options.getBatch(chunkKeysToFetch)\n\t\t\t: [];\n\n\t\t// Prepare new chunk data\n\t\tconst entriesToWrite: [Uint8Array, Uint8Array][] = [];\n\n\t\tfor (const plan of plans) {\n\t\t\tconst existingChunk =\n\t\t\t\tplan.existingChunkIndex >= 0\n\t\t\t\t\t? existingChunks[plan.existingChunkIndex]\n\t\t\t\t\t: null;\n\t\t\t// Create new chunk data\n\t\t\tlet newChunk: Uint8Array;\n\t\t\tif (existingChunk) {\n\t\t\t\tnewChunk = new Uint8Array(Math.max(existingChunk.length, plan.writeEnd));\n\t\t\t\tnewChunk.set(existingChunk);\n\t\t\t} else {\n\t\t\t\tnewChunk = new Uint8Array(plan.writeEnd);\n\t\t\t}\n\n\t\t\t// Copy data from input buffer to chunk\n\t\t\tconst sourceStart = plan.chunkOffset + plan.writeStart - iOffset;\n\t\t\tconst sourceEnd = sourceStart + (plan.writeEnd - plan.writeStart);\n\t\t\tnewChunk.set(data.subarray(sourceStart, sourceEnd), plan.writeStart);\n\n\t\t\tentriesToWrite.push([plan.chunkKey, newChunk]);\n\t\t}\n\n\t\t// Update file size if we wrote past the end\n\t\tconst previousSize = file.size;\n\t\tconst newSize = Math.max(file.size, writeEndOffset);\n\t\tif (newSize !== previousSize) {\n\t\t\tfile.size = newSize;\n\t\t\tfile.metaDirty = true;\n\t\t}\n\t\tif (file.metaDirty) {\n\t\t\tentriesToWrite.push([file.metaKey, encodeFileMeta(file.size)]);\n\t\t}\n\n\t\t// Write all chunks and metadata\n\t\tawait options.putBatch(entriesToWrite);\n\t\tif (file.metaDirty) {\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xTruncate(\n\t\tfileId: number,\n\t\tsizeLo: number,\n\t\tsizeHi: number,\n\t): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\n\t\tconst size = delegalize(sizeLo, sizeHi);\n\t\tif (size < 0 || size > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\t\tconst options = file.options;\n\n\t\t// If truncating to larger size, just update metadata\n\t\tif (size >= file.size) {\n\t\t\tif (size > file.size) {\n\t\t\t\tfile.size = size;\n\t\t\t\tfile.metaDirty = true;\n\t\t\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\t\tfile.metaDirty = false;\n\t\t\t}\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Calculate which chunks to delete\n\t\t// Note: When size=0, lastChunkToKeep = floor(-1/4096) = -1, which means\n\t\t// all chunks (starting from index 0) will be deleted in the loop below.\n\t\tconst lastChunkToKeep = Math.floor((size - 1) / CHUNK_SIZE);\n\t\tconst lastExistingChunk = Math.floor((file.size - 1) / CHUNK_SIZE);\n\n\t\t// Delete chunks beyond the new size\n\t\tconst keysToDelete: Uint8Array[] = [];\n\t\tfor (let i = lastChunkToKeep + 1; i <= lastExistingChunk; i++) {\n\t\t\tkeysToDelete.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tif (keysToDelete.length > 0) {\n\t\t\tawait options.deleteBatch(keysToDelete);\n\t\t}\n\n\t\t// Truncate the last kept chunk if needed\n\t\tif (size > 0 && size % CHUNK_SIZE !== 0) {\n\t\t\tconst lastChunkKey = this.#chunkKey(file, lastChunkToKeep);\n\t\t\tconst lastChunkData = await options.get(lastChunkKey);\n\n\t\t\tif (lastChunkData && lastChunkData.length > size % CHUNK_SIZE) {\n\t\t\t\tconst truncatedChunk = lastChunkData.subarray(0, size % CHUNK_SIZE);\n\t\t\t\tawait options.put(lastChunkKey, truncatedChunk);\n\t\t\t}\n\t\t}\n\n\t\t// Update file size\n\t\tfile.size = size;\n\t\tfile.metaDirty = true;\n\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xSync(fileId: number, _flags: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file || !file.metaDirty) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xFileSize(fileId: number, pSize: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_FSTAT;\n\t\t}\n\n\t\t// Set size as 64-bit integer.\n\t\tthis.#writeBigInt64(pSize, BigInt(file.size));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xDelete(_pVfs: number, zName: number, _syncDir: number): Promise<number> {\n\t\tawait this.#delete(this.#module.UTF8ToString(zName));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t/**\n\t * Internal delete implementation\n\t */\n\tasync #delete(path: string): Promise<void> {\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get file size to find out how many chunks to delete\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tif (!sizeData) {\n\t\t\t// File doesn't exist, that's OK\n\t\t\treturn;\n\t\t}\n\n\t\tconst size = decodeFileMeta(sizeData);\n\n\t\t// Delete all chunks\n\t\tconst keysToDelete: Uint8Array[] = [metaKey];\n\t\tconst numChunks = Math.ceil(size / CHUNK_SIZE);\n\t\tfor (let i = 0; i < numChunks; i++) {\n\t\t\tkeysToDelete.push(getChunkKey(fileTag, i));\n\t\t}\n\n\t\tawait options.deleteBatch(keysToDelete);\n\t}\n\n\tasync xAccess(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\t_flags: number,\n\t\tpResOut: number,\n\t): Promise<number> {\n\t\t// TODO: Measure how often xAccess runs during open and whether these\n\t\t// existence checks add meaningful KV round-trip overhead. If they do,\n\t\t// consider serving file existence from in-memory state.\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (!resolved) {\n\t\t\t// File not registered, doesn't exist\n\t\t\tthis.#writeInt32(pResOut, 0);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst compactMetaKey = getMetaKey(resolved.fileTag);\n\t\tconst metaData = await resolved.options.get(compactMetaKey);\n\n\t\t// Set result: 1 if file exists, 0 otherwise\n\t\tthis.#writeInt32(pResOut, metaData ? 1 : 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txCheckReservedLock(_fileId: number, pResOut: number): number {\n\t\t// This VFS is actor-scoped with one writer, so there is no external\n\t\t// reserved lock state to report.\n\t\tthis.#writeInt32(pResOut, 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txLock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txUnlock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txFileControl(_fileId: number, _flags: number, _pArg: number): number {\n\t\treturn VFS.SQLITE_NOTFOUND;\n\t}\n\n\txDeviceCharacteristics(_fileId: number): number {\n\t\treturn 0;\n\t}\n\n\txFullPathname(_pVfs: number, zName: number, nOut: number, zOut: number): number {\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst bytes = TEXT_ENCODER.encode(path);\n\t\tconst out = this.#module.HEAPU8.subarray(zOut, zOut + nOut);\n\t\tif (bytes.length >= out.length) {\n\t\t\treturn VFS.SQLITE_IOERR;\n\t\t}\n\t\tout.set(bytes, 0);\n\t\tout[bytes.length] = 0;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t#decodeFilename(zName: number, flags: number): string | null {\n\t\tif (!zName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (flags & VFS.SQLITE_OPEN_URI) {\n\t\t\t// Decode SQLite URI filename layout: path\\0key\\0value\\0...\\0\n\t\t\tlet pName = zName;\n\t\t\tlet state: 1 | 2 | 3 | null = 1;\n\t\t\tconst charCodes: number[] = [];\n\t\t\twhile (state) {\n\t\t\t\tconst charCode = this.#module.HEAPU8[pName++];\n\t\t\t\tif (charCode) {\n\t\t\t\t\tcharCodes.push(charCode);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!this.#module.HEAPU8[pName]) {\n\t\t\t\t\tstate = null;\n\t\t\t\t}\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tcharCodes.push(\"?\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tcharCodes.push(\"=\".charCodeAt(0));\n\t\t\t\t\t\tstate = 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tcharCodes.push(\"&\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn TEXT_DECODER.decode(new Uint8Array(charCodes));\n\t\t}\n\n\t\treturn this.#module.UTF8ToString(zName);\n\t}\n\n\t#heapView(): DataView {\n\t\tconst heapBuffer = this.#module.HEAPU8.buffer;\n\t\tif (heapBuffer !== this.#heapDataViewBuffer) {\n\t\t\tthis.#heapDataViewBuffer = heapBuffer;\n\t\t\tthis.#heapDataView = new DataView(heapBuffer);\n\t\t}\n\t\treturn this.#heapDataView;\n\t}\n\n\t#writeInt32(pointer: number, value: number): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setInt32(heapByteOffset, value, true);\n\t}\n\n\t#writeBigInt64(pointer: number, value: bigint): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setBigInt64(heapByteOffset, value, true);\n\t}\n}\n\n/**\n * Rebuild an i64 from Emscripten's legalized (lo32, hi32) pair.\n * SQLite passes file offsets and sizes this way. We decode into unsigned words\n * and reject values above the VFS max file size.\n */\nfunction delegalize(lo32: number, hi32: number): number {\n\tconst hi = hi32 >>> 0;\n\tconst lo = lo32 >>> 0;\n\tif (hi > MAX_FILE_SIZE_HI32) {\n\t\treturn -1;\n\t}\n\tif (hi === MAX_FILE_SIZE_HI32 && lo > MAX_FILE_SIZE_LO32) {\n\t\treturn -1;\n\t}\n\treturn (hi * UINT32_SIZE) + lo;\n}\n","/**\n * Key management for SQLite VFS storage\n *\n * This module contains constants and utilities for building keys used in the\n * key-value store for SQLite file storage.\n */\n\n/**\n * Size of each file chunk stored in KV.\n *\n * SQLite calls the VFS with byte ranges, but KV stores whole values by key.\n * The VFS maps each byte range to one or more fixed-size chunks, then uses\n * chunk keys to read or write those values in KV.\n */\nexport const CHUNK_SIZE = 4096;\n\n/** Top-level SQLite prefix (must match SQLITE_PREFIX in actor KV system) */\nexport const SQLITE_PREFIX = 8;\n\n/** Schema version namespace byte after SQLITE_PREFIX */\nexport const SQLITE_SCHEMA_VERSION = 1;\n\n/** Key prefix byte for file metadata (after SQLITE_PREFIX + version) */\nexport const META_PREFIX = 0;\n\n/** Key prefix byte for file chunks (after SQLITE_PREFIX + version) */\nexport const CHUNK_PREFIX = 1;\n\n/** File kind tag for the actor's main database file */\nexport const FILE_TAG_MAIN = 0;\n\n/** File kind tag for the actor's rollback journal sidecar */\nexport const FILE_TAG_JOURNAL = 1;\n\n/** File kind tag for the actor's WAL sidecar */\nexport const FILE_TAG_WAL = 2;\n\n/** File kind tag for the actor's SHM sidecar */\nexport const FILE_TAG_SHM = 3;\n\nexport type SqliteFileTag =\n\t| typeof FILE_TAG_MAIN\n\t| typeof FILE_TAG_JOURNAL\n\t| typeof FILE_TAG_WAL\n\t| typeof FILE_TAG_SHM;\n\n/**\n * Gets the key for file metadata\n * Format: [SQLITE_PREFIX (1 byte), version (1 byte), META_PREFIX (1 byte), file tag (1 byte)]\n */\nexport function getMetaKey(fileTag: SqliteFileTag): Uint8Array {\n\tconst key = new Uint8Array(4);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = META_PREFIX;\n\tkey[3] = fileTag;\n\treturn key;\n}\n\n/**\n * Gets the key for one chunk of file data.\n * Format: [SQLITE_PREFIX, CHUNK_PREFIX, file tag, chunk index (u32 big-endian)]\n *\n * The chunk index is derived from byte offset as floor(offset / CHUNK_SIZE),\n * which is how SQLite byte ranges map onto KV keys.\n */\nexport function getChunkKey(\n\tfileTag: SqliteFileTag,\n\tchunkIndex: number,\n): Uint8Array {\n\tconst key = new Uint8Array(8);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = CHUNK_PREFIX;\n\tkey[3] = fileTag;\n\tkey[4] = (chunkIndex >>> 24) & 0xff;\n\tkey[5] = (chunkIndex >>> 16) & 0xff;\n\tkey[6] = (chunkIndex >>> 8) & 0xff;\n\tkey[7] = chunkIndex & 0xff;\n\treturn key;\n}\n","import { createVersionedDataHandler } from \"vbare\";\nimport * as v1 from \"../../dist/schemas/file-meta/v1\";\n\nexport const CURRENT_VERSION = 1;\n\nexport const FILE_META_VERSIONED = createVersionedDataHandler<v1.FileMeta>({\n\tdeserializeVersion: (bytes, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.decodeFileMeta(bytes);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tserializeVersion: (data, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.encodeFileMeta(data as v1.FileMeta);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tdeserializeConverters: () => [],\n\tserializeConverters: () => [],\n});\n","// @generated - post-processed by compile-bare.ts\nimport * as bare from \"@rivetkit/bare-ts\"\n\nconst config = /* @__PURE__ */ bare.Config({})\n\nexport type u64 = bigint\n\nexport type FileMeta = {\n readonly size: u64,\n}\n\nexport function readFileMeta(bc: bare.ByteCursor): FileMeta {\n return {\n size: bare.readU64(bc),\n }\n}\n\nexport function writeFileMeta(bc: bare.ByteCursor, x: FileMeta): void {\n bare.writeU64(bc, x.size)\n}\n\nexport function encodeFileMeta(x: FileMeta): Uint8Array {\n const bc = new bare.ByteCursor(\n new Uint8Array(config.initialBufferLength),\n config\n )\n writeFileMeta(bc, x)\n return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset)\n}\n\nexport function decodeFileMeta(bytes: Uint8Array): FileMeta {\n const bc = new bare.ByteCursor(bytes, config)\n const result = readFileMeta(bc)\n if (bc.offset < bc.view.byteLength) {\n throw new bare.BareError(bc.offset, \"remaining bytes\")\n }\n return result\n}\n\n\nfunction assert(condition: boolean, message?: string): asserts condition {\n if (!condition) throw new Error(message ?? \"Assertion failed\")\n}\n"]}
|
package/dist/tsup/index.js
CHANGED
|
@@ -125,7 +125,8 @@ function isSQLiteModule(value) {
|
|
|
125
125
|
return typeof candidate.UTF8ToString === "function" && candidate.HEAPU8 instanceof Uint8Array;
|
|
126
126
|
}
|
|
127
127
|
async function loadSqliteRuntime() {
|
|
128
|
-
const
|
|
128
|
+
const specifier = ["@rivetkit/sqlite", "dist", "wa-sqlite-async.mjs"].join("/");
|
|
129
|
+
const sqliteModule = await import(specifier);
|
|
129
130
|
if (!isSqliteEsmFactory(sqliteModule.default)) {
|
|
130
131
|
throw new Error("Invalid SQLite ESM factory export");
|
|
131
132
|
}
|
package/dist/tsup/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vfs.ts","../../src/kv.ts","../../schemas/file-meta/versioned.ts","../schemas/file-meta/v1.ts"],"sourcesContent":["/**\n * SQLite raw database with KV storage backend\n *\n * This module provides a SQLite API that uses a KV-backed VFS\n * for storage. Each SqliteVfs instance is independent and can be\n * used concurrently with other instances.\n *\n * Keep this VFS on direct VFS.Base callbacks for minimal wrapper overhead.\n * Use @rivetkit/sqlite/src/FacadeVFS.js as the reference implementation for\n * callback ABI and pointer/data conversion behavior.\n * This implementation is optimized for single-writer semantics because each\n * actor owns one SQLite database.\n * SQLite invokes this VFS with byte-range file operations. This VFS maps those\n * ranges onto fixed-size KV chunks keyed by file tag and chunk index.\n * We intentionally rely on SQLite's pager cache for hot page reuse and do not\n * add a second cache in this VFS. This avoids duplicate cache invalidation\n * logic and keeps memory usage predictable for each actor.\n */\n\nimport * as VFS from \"@rivetkit/sqlite/src/VFS.js\";\nimport {\n\tFactory,\n\tSQLITE_OPEN_CREATE,\n\tSQLITE_OPEN_READWRITE,\n\tSQLITE_ROW,\n} from \"@rivetkit/sqlite\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport {\n\tCHUNK_SIZE,\n\tFILE_TAG_JOURNAL,\n\tFILE_TAG_MAIN,\n\tFILE_TAG_SHM,\n\tFILE_TAG_WAL,\n\tgetChunkKey,\n\tgetMetaKey,\n\ttype SqliteFileTag,\n} from \"./kv\";\nimport {\n\tFILE_META_VERSIONED,\n\tCURRENT_VERSION,\n} from \"../schemas/file-meta/versioned\";\nimport type { FileMeta } from \"../schemas/file-meta/mod\";\nimport type { KvVfsOptions } from \"./types\";\n\ntype SqliteEsmFactory = (config?: { wasmBinary?: ArrayBuffer | Uint8Array }) => Promise<unknown>;\ntype SQLite3Api = ReturnType<typeof Factory>;\ntype SqliteBindings = Parameters<SQLite3Api[\"bind_collection\"]>[1];\ntype SqliteVfsRegistration = Parameters<SQLite3Api[\"vfs_register\"]>[0];\n\ninterface SQLiteModule {\n\tUTF8ToString: (ptr: number) => string;\n\tHEAPU8: Uint8Array;\n}\n\nconst TEXT_ENCODER = new TextEncoder();\nconst TEXT_DECODER = new TextDecoder();\nconst SQLITE_MAX_PATHNAME_BYTES = 64;\n\n// Chunk keys encode the chunk index in 32 bits, so a file can span at most\n// 2^32 chunks. At 4 KiB/chunk this yields a hard limit of 16 TiB.\nconst UINT32_SIZE = 0x100000000;\nconst MAX_CHUNK_INDEX = 0xffffffff;\nconst MAX_FILE_SIZE_BYTES = (MAX_CHUNK_INDEX + 1) * CHUNK_SIZE;\nconst MAX_FILE_SIZE_HI32 = Math.floor(MAX_FILE_SIZE_BYTES / UINT32_SIZE);\nconst MAX_FILE_SIZE_LO32 = MAX_FILE_SIZE_BYTES % UINT32_SIZE;\n\n// libvfs captures this async/sync mask at registration time. Any VFS callback\n// that returns a Promise must be listed here so SQLite uses async relays.\nconst SQLITE_ASYNC_METHODS = new Set([\n\t\"xOpen\",\n\t\"xClose\",\n\t\"xRead\",\n\t\"xWrite\",\n\t\"xTruncate\",\n\t\"xSync\",\n\t\"xFileSize\",\n\t\"xDelete\",\n\t\"xAccess\",\n]);\n\ninterface LoadedSqliteRuntime {\n\tsqlite3: SQLite3Api;\n\tmodule: SQLiteModule;\n}\n\nfunction isSqliteEsmFactory(value: unknown): value is SqliteEsmFactory {\n\treturn typeof value === \"function\";\n}\n\nfunction isSQLiteModule(value: unknown): value is SQLiteModule {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as {\n\t\tUTF8ToString?: unknown;\n\t\tHEAPU8?: unknown;\n\t};\n\treturn (\n\t\ttypeof candidate.UTF8ToString === \"function\" &&\n\t\tcandidate.HEAPU8 instanceof Uint8Array\n\t);\n}\n\n\n/**\n * Lazily load and instantiate the async SQLite module for this VFS instance.\n * We do this on first open so actors that do not use SQLite do not pay module\n * parse and wasm initialization cost at startup, and we pass wasmBinary\n * explicitly so this works consistently in both ESM and CJS bundles.\n */\nasync function loadSqliteRuntime(): Promise<LoadedSqliteRuntime> {\n\t// Keep the module specifier assembled at runtime so TypeScript declaration\n\t// generation does not try to typecheck this deep dist import path.\n\tconst sqliteModule = await import(\"@rivetkit/sqlite/dist/\" + \"wa-sqlite-async.mjs\");\n\tif (!isSqliteEsmFactory(sqliteModule.default)) {\n\t\tthrow new Error(\"Invalid SQLite ESM factory export\");\n\t}\n\tconst sqliteEsmFactory = sqliteModule.default;\n\tconst require = createRequire(import.meta.url);\n\tconst wasmPath = require.resolve(\"@rivetkit/sqlite/dist/wa-sqlite-async.wasm\");\n\tconst wasmBinary = readFileSync(wasmPath);\n\tconst module = await sqliteEsmFactory({ wasmBinary });\n\tif (!isSQLiteModule(module)) {\n\t\tthrow new Error(\"Invalid SQLite runtime module\");\n\t}\n\treturn {\n\t\tsqlite3: Factory(module),\n\t\tmodule,\n\t};\n}\n\n/**\n * Represents an open file\n */\ninterface OpenFile {\n\t/** File path */\n\tpath: string;\n\t/** File kind tag used by compact key layout */\n\tfileTag: SqliteFileTag;\n\t/** Precomputed metadata key */\n\tmetaKey: Uint8Array;\n\t/** File size in bytes */\n\tsize: number;\n\t/** True when in-memory size has not been persisted yet */\n\tmetaDirty: boolean;\n\t/** Open flags */\n\tflags: number;\n\t/** KV options for this file */\n\toptions: KvVfsOptions;\n}\n\ninterface ResolvedFile {\n\toptions: KvVfsOptions;\n\tfileTag: SqliteFileTag;\n}\n\n/**\n * Encodes file metadata to a Uint8Array using BARE schema\n */\nfunction encodeFileMeta(size: number): Uint8Array {\n\tconst meta: FileMeta = { size: BigInt(size) };\n\treturn FILE_META_VERSIONED.serializeWithEmbeddedVersion(\n\t\tmeta,\n\t\tCURRENT_VERSION,\n\t);\n}\n\n/**\n * Decodes file metadata from a Uint8Array using BARE schema\n */\nfunction decodeFileMeta(data: Uint8Array): number {\n\tconst meta = FILE_META_VERSIONED.deserializeWithEmbeddedVersion(data);\n\treturn Number(meta.size);\n}\n\nfunction isValidFileSize(size: number): boolean {\n\treturn Number.isSafeInteger(size) && size >= 0 && size <= MAX_FILE_SIZE_BYTES;\n}\n\n/**\n * Simple async mutex for serializing database operations\n * @rivetkit/sqlite calls are not safe to run concurrently on one module instance\n */\nclass AsyncMutex {\n\t#locked = false;\n\t#waiting: (() => void)[] = [];\n\n\tasync acquire(): Promise<void> {\n\t\twhile (this.#locked) {\n\t\t\tawait new Promise<void>((resolve) => this.#waiting.push(resolve));\n\t\t}\n\t\tthis.#locked = true;\n\t}\n\n\trelease(): void {\n\t\tthis.#locked = false;\n\t\tconst next = this.#waiting.shift();\n\t\tif (next) {\n\t\t\tnext();\n\t\t}\n\t}\n\n\tasync run<T>(fn: () => Promise<T>): Promise<T> {\n\t\tawait this.acquire();\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tthis.release();\n\t\t}\n\t}\n}\n\n/**\n * Database wrapper that provides a simplified SQLite API\n */\nexport class Database {\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #handle: number;\n\treadonly #fileName: string;\n\treadonly #onClose: () => void;\n\treadonly #sqliteMutex: AsyncMutex;\n\n\tconstructor(\n\t\tsqlite3: SQLite3Api,\n\t\thandle: number,\n\t\tfileName: string,\n\t\tonClose: () => void,\n\t\tsqliteMutex: AsyncMutex,\n\t) {\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#handle = handle;\n\t\tthis.#fileName = fileName;\n\t\tthis.#onClose = onClose;\n\t\tthis.#sqliteMutex = sqliteMutex;\n\t}\n\n\t/**\n\t * Execute SQL with optional row callback\n\t * @param sql - SQL statement to execute\n\t * @param callback - Called for each result row with (row, columns)\n\t */\n\tasync exec(sql: string, callback?: (row: unknown[], columns: string[]) => void): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.exec(this.#handle, sql, callback);\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL statement (no result rows)\n\t * @param sql - SQL statement with ? placeholders\n\t * @param params - Parameter values to bind\n\t */\n\tasync run(sql: string, params?: SqliteBindings): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\t// Consume rows for statements that return results.\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL query and return results\n\t * @param sql - SQL query with ? placeholders\n\t * @param params - Parameter values to bind\n\t * @returns Object with rows (array of arrays) and columns (column names)\n\t */\n\tasync query(sql: string, params?: SqliteBindings): Promise<{ rows: unknown[][]; columns: string[] }> {\n\t\treturn this.#sqliteMutex.run(async () => {\n\t\t\tconst rows: unknown[][] = [];\n\t\t\tlet columns: string[] = [];\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\tif (columns.length === 0) {\n\t\t\t\t\t\tcolumns = this.#sqlite3.column_names(stmt);\n\t\t\t\t\t}\n\t\t\t\t\trows.push(this.#sqlite3.row(stmt));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { rows, columns };\n\t\t});\n\t}\n\n\t/**\n\t * Close the database\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.close(this.#handle);\n\t\t});\n\t\tthis.#onClose();\n\t}\n\n\t/**\n\t * Get the raw @rivetkit/sqlite API (for advanced usage)\n\t */\n\tget sqlite3(): SQLite3Api {\n\t\treturn this.#sqlite3;\n\t}\n\n\t/**\n\t * Get the raw database handle (for advanced usage)\n\t */\n\tget handle(): number {\n\t\treturn this.#handle;\n\t}\n}\n\n/**\n * SQLite VFS backed by KV storage.\n *\n * Each instance is independent and has its own @rivetkit/sqlite WASM module.\n * This allows multiple instances to operate concurrently without interference.\n */\nexport class SqliteVfs {\n\t#sqlite3: SQLite3Api | null = null;\n\t#sqliteSystem: SqliteSystem | null = null;\n\t#initPromise: Promise<void> | null = null;\n\t#openMutex = new AsyncMutex();\n\t#sqliteMutex = new AsyncMutex();\n\t#instanceId: string;\n\t#destroyed = false;\n\n\tconstructor() {\n\t\t// Generate unique instance ID for VFS name\n\t\tthis.#instanceId = crypto.randomUUID().replace(/-/g, '').slice(0, 8);\n\t}\n\n\t/**\n\t * Initialize @rivetkit/sqlite and VFS (called once per instance)\n\t */\n\tasync #ensureInitialized(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Fast path: already initialized\n\t\tif (this.#sqlite3 && this.#sqliteSystem) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Synchronously create the promise if not started\n\t\tif (!this.#initPromise) {\n\t\t\tthis.#initPromise = (async () => {\n\t\t\t\tconst { sqlite3, module } = await loadSqliteRuntime();\n\t\t\t\tif (this.#destroyed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#sqlite3 = sqlite3;\n\t\t\t\tthis.#sqliteSystem = new SqliteSystem(\n\t\t\t\t\tsqlite3,\n\t\t\t\t\tmodule,\n\t\t\t\t\t`kv-vfs-${this.#instanceId}`,\n\t\t\t\t);\n\t\t\t\tthis.#sqliteSystem.register();\n\t\t\t})();\n\t\t}\n\n\t\t// Wait for initialization\n\t\ttry {\n\t\t\tawait this.#initPromise;\n\t\t} catch (error) {\n\t\t\tthis.#initPromise = null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Open a SQLite database using KV storage backend\n\t *\n\t * @param fileName - The database file name (typically the actor ID)\n\t * @param options - KV storage operations for this database\n\t * @returns A Database instance\n\t */\n\tasync open(\n\t\tfileName: string,\n\t\toptions: KvVfsOptions,\n\t): Promise<Database> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Serialize all open operations within this instance\n\t\tawait this.#openMutex.acquire();\n\t\ttry {\n\t\t\t// Initialize @rivetkit/sqlite and SqliteSystem on first call\n\t\t\tawait this.#ensureInitialized();\n\n\t\t\tif (!this.#sqlite3 || !this.#sqliteSystem) {\n\t\t\t\tthrow new Error(\"Failed to initialize SQLite\");\n\t\t\t}\n\t\t\tconst sqlite3 = this.#sqlite3;\n\t\t\tconst sqliteSystem = this.#sqliteSystem;\n\n\t\t\t// Register this filename with its KV options\n\t\t\tsqliteSystem.registerFile(fileName, options);\n\n\t\t\t// Open database\n\t\t\tconst db = await this.#sqliteMutex.run(async () =>\n\t\t\t\tsqlite3.open_v2(\n\t\t\t\t\tfileName,\n\t\t\t\t\tSQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,\n\t\t\t\t\tsqliteSystem.name,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// TODO: Benchmark PRAGMA tuning for KV-backed SQLite after open.\n\t\t\t// Start with journal_mode=PERSIST and journal_size_limit to reduce\n\t\t\t// journal churn on high-latency KV without introducing WAL.\n\n\t\t\t// Create cleanup callback\n\t\t\tconst onClose = () => {\n\t\t\t\tsqliteSystem.unregisterFile(fileName);\n\t\t\t};\n\n\t\t\treturn new Database(\n\t\t\t\tsqlite3,\n\t\t\t\tdb,\n\t\t\t\tfileName,\n\t\t\t\tonClose,\n\t\t\t\tthis.#sqliteMutex,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.#openMutex.release();\n\t\t}\n\t}\n\n\t/**\n\t * Tears down this VFS instance and releases internal references.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#destroyed = true;\n\n\t\tconst initPromise = this.#initPromise;\n\t\tif (initPromise) {\n\t\t\ttry {\n\t\t\t\tawait initPromise;\n\t\t\t} catch {\n\t\t\t\t// Initialization failure already surfaced to caller.\n\t\t\t}\n\t\t}\n\n\t\tif (this.#sqliteSystem) {\n\t\t\tawait this.#sqliteSystem.close();\n\t\t}\n\n\t\tthis.#sqliteSystem = null;\n\t\tthis.#sqlite3 = null;\n\t\tthis.#initPromise = null;\n\t}\n\n\t/**\n\t * Alias for destroy to align with DB-style lifecycle naming.\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.destroy();\n\t}\n}\n\n/**\n * Internal VFS implementation\n */\nclass SqliteSystem implements SqliteVfsRegistration {\n\treadonly name: string;\n\treadonly mxPathName = SQLITE_MAX_PATHNAME_BYTES;\n\treadonly mxPathname = SQLITE_MAX_PATHNAME_BYTES;\n\t#mainFileName: string | null = null;\n\t#mainFileOptions: KvVfsOptions | null = null;\n\treadonly #openFiles: Map<number, OpenFile> = new Map();\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #module: SQLiteModule;\n\t#heapDataView: DataView;\n\t#heapDataViewBuffer: ArrayBufferLike;\n\n\tconstructor(sqlite3: SQLite3Api, module: SQLiteModule, name: string) {\n\t\tthis.name = name;\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#module = module;\n\t\tthis.#heapDataViewBuffer = module.HEAPU8.buffer;\n\t\tthis.#heapDataView = new DataView(this.#heapDataViewBuffer);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#openFiles.clear();\n\t\tthis.#mainFileName = null;\n\t\tthis.#mainFileOptions = null;\n\t}\n\n\tisReady(): boolean {\n\t\treturn true;\n\t}\n\n\thasAsyncMethod(methodName: string): boolean {\n\t\treturn SQLITE_ASYNC_METHODS.has(methodName);\n\t}\n\n\t/**\n\t * Registers the VFS with SQLite\n\t */\n\tregister(): void {\n\t\tthis.#sqlite3.vfs_register(this, false);\n\t}\n\n\t/**\n\t * Registers a file with its KV options (before opening)\n\t */\n\tregisterFile(fileName: string, options: KvVfsOptions): void {\n\t\tif (!this.#mainFileName) {\n\t\t\tthis.#mainFileName = fileName;\n\t\t\tthis.#mainFileOptions = options;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#mainFileName !== fileName) {\n\t\t\tthrow new Error(\n\t\t\t\t`SqliteSystem is actor-scoped and expects one main file. Got ${fileName}, expected ${this.#mainFileName}.`,\n\t\t\t);\n\t\t}\n\n\t\tthis.#mainFileOptions = options;\n\t}\n\n\t/**\n\t * Unregisters a file's KV options (after closing)\n\t */\n\tunregisterFile(fileName: string): void {\n\t\tif (this.#mainFileName === fileName) {\n\t\t\tthis.#mainFileName = null;\n\t\t\tthis.#mainFileOptions = null;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve file path to the actor's main DB file or known SQLite sidecars.\n\t */\n\t#resolveFile(path: string): ResolvedFile | null {\n\t\tif (!this.#mainFileName || !this.#mainFileOptions) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (path === this.#mainFileName) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_MAIN };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-journal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_JOURNAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-wal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_WAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-shm`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_SHM };\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t#resolveFileOrThrow(path: string): ResolvedFile {\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (resolved) {\n\t\t\treturn resolved;\n\t\t}\n\n\t\tif (!this.#mainFileName) {\n\t\t\tthrow new Error(`No KV options registered for file: ${path}`);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Unsupported SQLite file path ${path}. Expected one of ${this.#mainFileName}, ${this.#mainFileName}-journal, ${this.#mainFileName}-wal, ${this.#mainFileName}-shm.`,\n\t\t);\n\t}\n\n\t#chunkKey(file: OpenFile, chunkIndex: number): Uint8Array {\n\t\treturn getChunkKey(file.fileTag, chunkIndex);\n\t}\n\n\tasync xOpen(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\tfileId: number,\n\t\tflags: number,\n\t\tpOutFlags: number,\n\t): Promise<number> {\n\t\tconst path = this.#decodeFilename(zName, flags);\n\t\tif (!path) {\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Get the registered KV options for this file\n\t\t// For journal/wal files, use the main database's options\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get existing file size if the file exists\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tlet size: number;\n\n\t\tif (sizeData) {\n\t\t\t// File exists, use existing size\n\t\t\tsize = decodeFileMeta(sizeData);\n\t\t\tif (!isValidFileSize(size)) {\n\t\t\t\treturn VFS.SQLITE_IOERR;\n\t\t\t}\n\t\t} else if (flags & VFS.SQLITE_OPEN_CREATE) {\n\t\t\t// File doesn't exist, create it\n\t\t\tsize = 0;\n\t\t\tawait options.put(metaKey, encodeFileMeta(size));\n\t\t} else {\n\t\t\t// File doesn't exist and we're not creating it\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Store open file info with options\n\t\tthis.#openFiles.set(fileId, {\n\t\t\tpath,\n\t\t\tfileTag,\n\t\t\tmetaKey,\n\t\t\tsize,\n\t\t\tmetaDirty: false,\n\t\t\tflags,\n\t\t\toptions,\n\t\t});\n\n\t\t// Set output flags to the actual flags used.\n\t\tthis.#writeInt32(pOutFlags, flags);\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xClose(fileId: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Delete-on-close files should skip metadata flush because the file will\n\t\t// be removed immediately.\n\t\tif (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {\n\t\t\tawait this.#delete(file.path);\n\t\t\tthis.#openFiles.delete(fileId);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tif (file.metaDirty) {\n\t\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\tthis.#openFiles.delete(fileId);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xRead(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst options = file.options;\n\t\tconst requestedLength = iAmt;\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\t\tconst fileSize = file.size;\n\n\t\t// If offset is beyond file size, return short read with zeroed buffer\n\t\tif (iOffset >= fileSize) {\n\t\t\tdata.fill(0);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\t// Calculate which chunks we need to read\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + requestedLength - 1) / CHUNK_SIZE);\n\n\t\t// Fetch all needed chunks\n\t\tconst chunkKeys: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tchunkKeys.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tconst chunks = await options.getBatch(chunkKeys);\n\n\t\t// Copy data from chunks to output buffer\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkData = chunks[i - startChunk];\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\n\t\t\t// Calculate the range within this chunk\n\t\t\tconst readStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst readEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + requestedLength - chunkOffset,\n\t\t\t);\n\n\t\t\tif (chunkData) {\n\t\t\t\t// Copy available data\n\t\t\t\tconst sourceStart = readStart;\n\t\t\t\tconst sourceEnd = Math.min(readEnd, chunkData.length);\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\n\t\t\t\tif (sourceEnd > sourceStart) {\n\t\t\t\t\tdata.set(chunkData.subarray(sourceStart, sourceEnd), destStart);\n\t\t\t\t}\n\n\t\t\t\t// Zero-fill if chunk is smaller than expected\n\t\t\t\tif (sourceEnd < readEnd) {\n\t\t\t\t\tconst zeroStart = destStart + (sourceEnd - sourceStart);\n\t\t\t\t\tconst zeroEnd = destStart + (readEnd - readStart);\n\t\t\t\t\tdata.fill(0, zeroStart, zeroEnd);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Chunk doesn't exist, zero-fill\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\t\t\t\tconst destEnd = destStart + (readEnd - readStart);\n\t\t\t\tdata.fill(0, destStart, destEnd);\n\t\t\t}\n\t\t}\n\n\t\t// If we read less than requested (past EOF), return short read\n\t\tconst actualBytes = Math.min(requestedLength, fileSize - iOffset);\n\t\tif (actualBytes < requestedLength) {\n\t\t\tdata.fill(0, actualBytes);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xWrite(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\t\tconst options = file.options;\n\t\tconst writeLength = iAmt;\n\t\tconst writeEndOffset = iOffset + writeLength;\n\t\tif (writeEndOffset > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\t// Calculate which chunks we need to modify\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + writeLength - 1) / CHUNK_SIZE);\n\n\t\tinterface WritePlan {\n\t\t\tchunkKey: Uint8Array;\n\t\t\tchunkOffset: number;\n\t\t\twriteStart: number;\n\t\t\twriteEnd: number;\n\t\t\texistingChunkIndex: number;\n\t\t}\n\n\t\t// Only fetch chunks where we must preserve existing prefix/suffix bytes.\n\t\tconst plans: WritePlan[] = [];\n\t\tconst chunkKeysToFetch: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\t\t\tconst writeStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst writeEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + writeLength - chunkOffset,\n\t\t\t);\n\t\t\tconst existingBytesInChunk = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(CHUNK_SIZE, file.size - chunkOffset),\n\t\t\t);\n\t\t\tconst needsExisting = writeStart > 0 || existingBytesInChunk > writeEnd;\n\t\t\tconst chunkKey = this.#chunkKey(file, i);\n\t\t\tlet existingChunkIndex = -1;\n\t\t\tif (needsExisting) {\n\t\t\t\texistingChunkIndex = chunkKeysToFetch.length;\n\t\t\t\tchunkKeysToFetch.push(chunkKey);\n\t\t\t}\n\t\t\tplans.push({\n\t\t\t\tchunkKey,\n\t\t\t\tchunkOffset,\n\t\t\t\twriteStart,\n\t\t\t\twriteEnd,\n\t\t\t\texistingChunkIndex,\n\t\t\t});\n\t\t}\n\n\t\tconst existingChunks = chunkKeysToFetch.length > 0\n\t\t\t? await options.getBatch(chunkKeysToFetch)\n\t\t\t: [];\n\n\t\t// Prepare new chunk data\n\t\tconst entriesToWrite: [Uint8Array, Uint8Array][] = [];\n\n\t\tfor (const plan of plans) {\n\t\t\tconst existingChunk =\n\t\t\t\tplan.existingChunkIndex >= 0\n\t\t\t\t\t? existingChunks[plan.existingChunkIndex]\n\t\t\t\t\t: null;\n\t\t\t// Create new chunk data\n\t\t\tlet newChunk: Uint8Array;\n\t\t\tif (existingChunk) {\n\t\t\t\tnewChunk = new Uint8Array(Math.max(existingChunk.length, plan.writeEnd));\n\t\t\t\tnewChunk.set(existingChunk);\n\t\t\t} else {\n\t\t\t\tnewChunk = new Uint8Array(plan.writeEnd);\n\t\t\t}\n\n\t\t\t// Copy data from input buffer to chunk\n\t\t\tconst sourceStart = plan.chunkOffset + plan.writeStart - iOffset;\n\t\t\tconst sourceEnd = sourceStart + (plan.writeEnd - plan.writeStart);\n\t\t\tnewChunk.set(data.subarray(sourceStart, sourceEnd), plan.writeStart);\n\n\t\t\tentriesToWrite.push([plan.chunkKey, newChunk]);\n\t\t}\n\n\t\t// Update file size if we wrote past the end\n\t\tconst previousSize = file.size;\n\t\tconst newSize = Math.max(file.size, writeEndOffset);\n\t\tif (newSize !== previousSize) {\n\t\t\tfile.size = newSize;\n\t\t\tfile.metaDirty = true;\n\t\t}\n\t\tif (file.metaDirty) {\n\t\t\tentriesToWrite.push([file.metaKey, encodeFileMeta(file.size)]);\n\t\t}\n\n\t\t// Write all chunks and metadata\n\t\tawait options.putBatch(entriesToWrite);\n\t\tif (file.metaDirty) {\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xTruncate(\n\t\tfileId: number,\n\t\tsizeLo: number,\n\t\tsizeHi: number,\n\t): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\n\t\tconst size = delegalize(sizeLo, sizeHi);\n\t\tif (size < 0 || size > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\t\tconst options = file.options;\n\n\t\t// If truncating to larger size, just update metadata\n\t\tif (size >= file.size) {\n\t\t\tif (size > file.size) {\n\t\t\t\tfile.size = size;\n\t\t\t\tfile.metaDirty = true;\n\t\t\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\t\tfile.metaDirty = false;\n\t\t\t}\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Calculate which chunks to delete\n\t\t// Note: When size=0, lastChunkToKeep = floor(-1/4096) = -1, which means\n\t\t// all chunks (starting from index 0) will be deleted in the loop below.\n\t\tconst lastChunkToKeep = Math.floor((size - 1) / CHUNK_SIZE);\n\t\tconst lastExistingChunk = Math.floor((file.size - 1) / CHUNK_SIZE);\n\n\t\t// Delete chunks beyond the new size\n\t\tconst keysToDelete: Uint8Array[] = [];\n\t\tfor (let i = lastChunkToKeep + 1; i <= lastExistingChunk; i++) {\n\t\t\tkeysToDelete.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tif (keysToDelete.length > 0) {\n\t\t\tawait options.deleteBatch(keysToDelete);\n\t\t}\n\n\t\t// Truncate the last kept chunk if needed\n\t\tif (size > 0 && size % CHUNK_SIZE !== 0) {\n\t\t\tconst lastChunkKey = this.#chunkKey(file, lastChunkToKeep);\n\t\t\tconst lastChunkData = await options.get(lastChunkKey);\n\n\t\t\tif (lastChunkData && lastChunkData.length > size % CHUNK_SIZE) {\n\t\t\t\tconst truncatedChunk = lastChunkData.subarray(0, size % CHUNK_SIZE);\n\t\t\t\tawait options.put(lastChunkKey, truncatedChunk);\n\t\t\t}\n\t\t}\n\n\t\t// Update file size\n\t\tfile.size = size;\n\t\tfile.metaDirty = true;\n\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xSync(fileId: number, _flags: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file || !file.metaDirty) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xFileSize(fileId: number, pSize: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_FSTAT;\n\t\t}\n\n\t\t// Set size as 64-bit integer.\n\t\tthis.#writeBigInt64(pSize, BigInt(file.size));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xDelete(_pVfs: number, zName: number, _syncDir: number): Promise<number> {\n\t\tawait this.#delete(this.#module.UTF8ToString(zName));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t/**\n\t * Internal delete implementation\n\t */\n\tasync #delete(path: string): Promise<void> {\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get file size to find out how many chunks to delete\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tif (!sizeData) {\n\t\t\t// File doesn't exist, that's OK\n\t\t\treturn;\n\t\t}\n\n\t\tconst size = decodeFileMeta(sizeData);\n\n\t\t// Delete all chunks\n\t\tconst keysToDelete: Uint8Array[] = [metaKey];\n\t\tconst numChunks = Math.ceil(size / CHUNK_SIZE);\n\t\tfor (let i = 0; i < numChunks; i++) {\n\t\t\tkeysToDelete.push(getChunkKey(fileTag, i));\n\t\t}\n\n\t\tawait options.deleteBatch(keysToDelete);\n\t}\n\n\tasync xAccess(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\t_flags: number,\n\t\tpResOut: number,\n\t): Promise<number> {\n\t\t// TODO: Measure how often xAccess runs during open and whether these\n\t\t// existence checks add meaningful KV round-trip overhead. If they do,\n\t\t// consider serving file existence from in-memory state.\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (!resolved) {\n\t\t\t// File not registered, doesn't exist\n\t\t\tthis.#writeInt32(pResOut, 0);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst compactMetaKey = getMetaKey(resolved.fileTag);\n\t\tconst metaData = await resolved.options.get(compactMetaKey);\n\n\t\t// Set result: 1 if file exists, 0 otherwise\n\t\tthis.#writeInt32(pResOut, metaData ? 1 : 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txCheckReservedLock(_fileId: number, pResOut: number): number {\n\t\t// This VFS is actor-scoped with one writer, so there is no external\n\t\t// reserved lock state to report.\n\t\tthis.#writeInt32(pResOut, 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txLock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txUnlock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txFileControl(_fileId: number, _flags: number, _pArg: number): number {\n\t\treturn VFS.SQLITE_NOTFOUND;\n\t}\n\n\txDeviceCharacteristics(_fileId: number): number {\n\t\treturn 0;\n\t}\n\n\txFullPathname(_pVfs: number, zName: number, nOut: number, zOut: number): number {\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst bytes = TEXT_ENCODER.encode(path);\n\t\tconst out = this.#module.HEAPU8.subarray(zOut, zOut + nOut);\n\t\tif (bytes.length >= out.length) {\n\t\t\treturn VFS.SQLITE_IOERR;\n\t\t}\n\t\tout.set(bytes, 0);\n\t\tout[bytes.length] = 0;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t#decodeFilename(zName: number, flags: number): string | null {\n\t\tif (!zName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (flags & VFS.SQLITE_OPEN_URI) {\n\t\t\t// Decode SQLite URI filename layout: path\\0key\\0value\\0...\\0\n\t\t\tlet pName = zName;\n\t\t\tlet state: 1 | 2 | 3 | null = 1;\n\t\t\tconst charCodes: number[] = [];\n\t\t\twhile (state) {\n\t\t\t\tconst charCode = this.#module.HEAPU8[pName++];\n\t\t\t\tif (charCode) {\n\t\t\t\t\tcharCodes.push(charCode);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!this.#module.HEAPU8[pName]) {\n\t\t\t\t\tstate = null;\n\t\t\t\t}\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tcharCodes.push(\"?\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tcharCodes.push(\"=\".charCodeAt(0));\n\t\t\t\t\t\tstate = 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tcharCodes.push(\"&\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn TEXT_DECODER.decode(new Uint8Array(charCodes));\n\t\t}\n\n\t\treturn this.#module.UTF8ToString(zName);\n\t}\n\n\t#heapView(): DataView {\n\t\tconst heapBuffer = this.#module.HEAPU8.buffer;\n\t\tif (heapBuffer !== this.#heapDataViewBuffer) {\n\t\t\tthis.#heapDataViewBuffer = heapBuffer;\n\t\t\tthis.#heapDataView = new DataView(heapBuffer);\n\t\t}\n\t\treturn this.#heapDataView;\n\t}\n\n\t#writeInt32(pointer: number, value: number): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setInt32(heapByteOffset, value, true);\n\t}\n\n\t#writeBigInt64(pointer: number, value: bigint): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setBigInt64(heapByteOffset, value, true);\n\t}\n}\n\n/**\n * Rebuild an i64 from Emscripten's legalized (lo32, hi32) pair.\n * SQLite passes file offsets and sizes this way. We decode into unsigned words\n * and reject values above the VFS max file size.\n */\nfunction delegalize(lo32: number, hi32: number): number {\n\tconst hi = hi32 >>> 0;\n\tconst lo = lo32 >>> 0;\n\tif (hi > MAX_FILE_SIZE_HI32) {\n\t\treturn -1;\n\t}\n\tif (hi === MAX_FILE_SIZE_HI32 && lo > MAX_FILE_SIZE_LO32) {\n\t\treturn -1;\n\t}\n\treturn (hi * UINT32_SIZE) + lo;\n}\n","/**\n * Key management for SQLite VFS storage\n *\n * This module contains constants and utilities for building keys used in the\n * key-value store for SQLite file storage.\n */\n\n/**\n * Size of each file chunk stored in KV.\n *\n * SQLite calls the VFS with byte ranges, but KV stores whole values by key.\n * The VFS maps each byte range to one or more fixed-size chunks, then uses\n * chunk keys to read or write those values in KV.\n */\nexport const CHUNK_SIZE = 4096;\n\n/** Top-level SQLite prefix (must match SQLITE_PREFIX in actor KV system) */\nexport const SQLITE_PREFIX = 8;\n\n/** Schema version namespace byte after SQLITE_PREFIX */\nexport const SQLITE_SCHEMA_VERSION = 1;\n\n/** Key prefix byte for file metadata (after SQLITE_PREFIX + version) */\nexport const META_PREFIX = 0;\n\n/** Key prefix byte for file chunks (after SQLITE_PREFIX + version) */\nexport const CHUNK_PREFIX = 1;\n\n/** File kind tag for the actor's main database file */\nexport const FILE_TAG_MAIN = 0;\n\n/** File kind tag for the actor's rollback journal sidecar */\nexport const FILE_TAG_JOURNAL = 1;\n\n/** File kind tag for the actor's WAL sidecar */\nexport const FILE_TAG_WAL = 2;\n\n/** File kind tag for the actor's SHM sidecar */\nexport const FILE_TAG_SHM = 3;\n\nexport type SqliteFileTag =\n\t| typeof FILE_TAG_MAIN\n\t| typeof FILE_TAG_JOURNAL\n\t| typeof FILE_TAG_WAL\n\t| typeof FILE_TAG_SHM;\n\n/**\n * Gets the key for file metadata\n * Format: [SQLITE_PREFIX (1 byte), version (1 byte), META_PREFIX (1 byte), file tag (1 byte)]\n */\nexport function getMetaKey(fileTag: SqliteFileTag): Uint8Array {\n\tconst key = new Uint8Array(4);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = META_PREFIX;\n\tkey[3] = fileTag;\n\treturn key;\n}\n\n/**\n * Gets the key for one chunk of file data.\n * Format: [SQLITE_PREFIX, CHUNK_PREFIX, file tag, chunk index (u32 big-endian)]\n *\n * The chunk index is derived from byte offset as floor(offset / CHUNK_SIZE),\n * which is how SQLite byte ranges map onto KV keys.\n */\nexport function getChunkKey(\n\tfileTag: SqliteFileTag,\n\tchunkIndex: number,\n): Uint8Array {\n\tconst key = new Uint8Array(8);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = CHUNK_PREFIX;\n\tkey[3] = fileTag;\n\tkey[4] = (chunkIndex >>> 24) & 0xff;\n\tkey[5] = (chunkIndex >>> 16) & 0xff;\n\tkey[6] = (chunkIndex >>> 8) & 0xff;\n\tkey[7] = chunkIndex & 0xff;\n\treturn key;\n}\n","import { createVersionedDataHandler } from \"vbare\";\nimport * as v1 from \"../../dist/schemas/file-meta/v1\";\n\nexport const CURRENT_VERSION = 1;\n\nexport const FILE_META_VERSIONED = createVersionedDataHandler<v1.FileMeta>({\n\tdeserializeVersion: (bytes, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.decodeFileMeta(bytes);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tserializeVersion: (data, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.encodeFileMeta(data as v1.FileMeta);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tdeserializeConverters: () => [],\n\tserializeConverters: () => [],\n});\n","// @generated - post-processed by compile-bare.ts\nimport * as bare from \"@rivetkit/bare-ts\"\n\nconst config = /* @__PURE__ */ bare.Config({})\n\nexport type u64 = bigint\n\nexport type FileMeta = {\n readonly size: u64,\n}\n\nexport function readFileMeta(bc: bare.ByteCursor): FileMeta {\n return {\n size: bare.readU64(bc),\n }\n}\n\nexport function writeFileMeta(bc: bare.ByteCursor, x: FileMeta): void {\n bare.writeU64(bc, x.size)\n}\n\nexport function encodeFileMeta(x: FileMeta): Uint8Array {\n const bc = new bare.ByteCursor(\n new Uint8Array(config.initialBufferLength),\n config\n )\n writeFileMeta(bc, x)\n return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset)\n}\n\nexport function decodeFileMeta(bytes: Uint8Array): FileMeta {\n const bc = new bare.ByteCursor(bytes, config)\n const result = readFileMeta(bc)\n if (bc.offset < bc.view.byteLength) {\n throw new bare.BareError(bc.offset, \"remaining bytes\")\n }\n return result\n}\n\n\nfunction assert(condition: boolean, message?: string): asserts condition {\n if (!condition) throw new Error(message ?? \"Assertion failed\")\n}\n"],"mappings":"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","names":["SQLITE_OPEN_CREATE","require","encodeFileMeta","decodeFileMeta","SQLITE_OPEN_CREATE"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vfs.ts","../../src/kv.ts","../../schemas/file-meta/versioned.ts","../schemas/file-meta/v1.ts"],"sourcesContent":["/**\n * SQLite raw database with KV storage backend\n *\n * This module provides a SQLite API that uses a KV-backed VFS\n * for storage. Each SqliteVfs instance is independent and can be\n * used concurrently with other instances.\n *\n * Keep this VFS on direct VFS.Base callbacks for minimal wrapper overhead.\n * Use @rivetkit/sqlite/src/FacadeVFS.js as the reference implementation for\n * callback ABI and pointer/data conversion behavior.\n * This implementation is optimized for single-writer semantics because each\n * actor owns one SQLite database.\n * SQLite invokes this VFS with byte-range file operations. This VFS maps those\n * ranges onto fixed-size KV chunks keyed by file tag and chunk index.\n * We intentionally rely on SQLite's pager cache for hot page reuse and do not\n * add a second cache in this VFS. This avoids duplicate cache invalidation\n * logic and keeps memory usage predictable for each actor.\n */\n\nimport * as VFS from \"@rivetkit/sqlite/src/VFS.js\";\nimport {\n\tFactory,\n\tSQLITE_OPEN_CREATE,\n\tSQLITE_OPEN_READWRITE,\n\tSQLITE_ROW,\n} from \"@rivetkit/sqlite\";\nimport { readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport {\n\tCHUNK_SIZE,\n\tFILE_TAG_JOURNAL,\n\tFILE_TAG_MAIN,\n\tFILE_TAG_SHM,\n\tFILE_TAG_WAL,\n\tgetChunkKey,\n\tgetMetaKey,\n\ttype SqliteFileTag,\n} from \"./kv\";\nimport {\n\tFILE_META_VERSIONED,\n\tCURRENT_VERSION,\n} from \"../schemas/file-meta/versioned\";\nimport type { FileMeta } from \"../schemas/file-meta/mod\";\nimport type { KvVfsOptions } from \"./types\";\n\ntype SqliteEsmFactory = (config?: { wasmBinary?: ArrayBuffer | Uint8Array }) => Promise<unknown>;\ntype SQLite3Api = ReturnType<typeof Factory>;\ntype SqliteBindings = Parameters<SQLite3Api[\"bind_collection\"]>[1];\ntype SqliteVfsRegistration = Parameters<SQLite3Api[\"vfs_register\"]>[0];\n\ninterface SQLiteModule {\n\tUTF8ToString: (ptr: number) => string;\n\tHEAPU8: Uint8Array;\n}\n\nconst TEXT_ENCODER = new TextEncoder();\nconst TEXT_DECODER = new TextDecoder();\nconst SQLITE_MAX_PATHNAME_BYTES = 64;\n\n// Chunk keys encode the chunk index in 32 bits, so a file can span at most\n// 2^32 chunks. At 4 KiB/chunk this yields a hard limit of 16 TiB.\nconst UINT32_SIZE = 0x100000000;\nconst MAX_CHUNK_INDEX = 0xffffffff;\nconst MAX_FILE_SIZE_BYTES = (MAX_CHUNK_INDEX + 1) * CHUNK_SIZE;\nconst MAX_FILE_SIZE_HI32 = Math.floor(MAX_FILE_SIZE_BYTES / UINT32_SIZE);\nconst MAX_FILE_SIZE_LO32 = MAX_FILE_SIZE_BYTES % UINT32_SIZE;\n\n// libvfs captures this async/sync mask at registration time. Any VFS callback\n// that returns a Promise must be listed here so SQLite uses async relays.\nconst SQLITE_ASYNC_METHODS = new Set([\n\t\"xOpen\",\n\t\"xClose\",\n\t\"xRead\",\n\t\"xWrite\",\n\t\"xTruncate\",\n\t\"xSync\",\n\t\"xFileSize\",\n\t\"xDelete\",\n\t\"xAccess\",\n]);\n\ninterface LoadedSqliteRuntime {\n\tsqlite3: SQLite3Api;\n\tmodule: SQLiteModule;\n}\n\nfunction isSqliteEsmFactory(value: unknown): value is SqliteEsmFactory {\n\treturn typeof value === \"function\";\n}\n\nfunction isSQLiteModule(value: unknown): value is SQLiteModule {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as {\n\t\tUTF8ToString?: unknown;\n\t\tHEAPU8?: unknown;\n\t};\n\treturn (\n\t\ttypeof candidate.UTF8ToString === \"function\" &&\n\t\tcandidate.HEAPU8 instanceof Uint8Array\n\t);\n}\n\n\n/**\n * Lazily load and instantiate the async SQLite module for this VFS instance.\n * We do this on first open so actors that do not use SQLite do not pay module\n * parse and wasm initialization cost at startup, and we pass wasmBinary\n * explicitly so this works consistently in both ESM and CJS bundles.\n */\nasync function loadSqliteRuntime(): Promise<LoadedSqliteRuntime> {\n\t// Keep the module specifier assembled at runtime so TypeScript declaration\n\t// generation does not try to typecheck this deep dist import path.\n\t// Uses Array.join() instead of string concatenation to prevent esbuild/tsup\n\t// from constant-folding the expression at build time, which would allow\n\t// Turbopack to trace into the WASM package.\n\tconst specifier = [\"@rivetkit/sqlite\", \"dist\", \"wa-sqlite-async.mjs\"].join(\"/\");\n\tconst sqliteModule = await import(specifier);\n\tif (!isSqliteEsmFactory(sqliteModule.default)) {\n\t\tthrow new Error(\"Invalid SQLite ESM factory export\");\n\t}\n\tconst sqliteEsmFactory = sqliteModule.default;\n\tconst require = createRequire(import.meta.url);\n\tconst wasmPath = require.resolve(\"@rivetkit/sqlite/dist/wa-sqlite-async.wasm\");\n\tconst wasmBinary = readFileSync(wasmPath);\n\tconst module = await sqliteEsmFactory({ wasmBinary });\n\tif (!isSQLiteModule(module)) {\n\t\tthrow new Error(\"Invalid SQLite runtime module\");\n\t}\n\treturn {\n\t\tsqlite3: Factory(module),\n\t\tmodule,\n\t};\n}\n\n/**\n * Represents an open file\n */\ninterface OpenFile {\n\t/** File path */\n\tpath: string;\n\t/** File kind tag used by compact key layout */\n\tfileTag: SqliteFileTag;\n\t/** Precomputed metadata key */\n\tmetaKey: Uint8Array;\n\t/** File size in bytes */\n\tsize: number;\n\t/** True when in-memory size has not been persisted yet */\n\tmetaDirty: boolean;\n\t/** Open flags */\n\tflags: number;\n\t/** KV options for this file */\n\toptions: KvVfsOptions;\n}\n\ninterface ResolvedFile {\n\toptions: KvVfsOptions;\n\tfileTag: SqliteFileTag;\n}\n\n/**\n * Encodes file metadata to a Uint8Array using BARE schema\n */\nfunction encodeFileMeta(size: number): Uint8Array {\n\tconst meta: FileMeta = { size: BigInt(size) };\n\treturn FILE_META_VERSIONED.serializeWithEmbeddedVersion(\n\t\tmeta,\n\t\tCURRENT_VERSION,\n\t);\n}\n\n/**\n * Decodes file metadata from a Uint8Array using BARE schema\n */\nfunction decodeFileMeta(data: Uint8Array): number {\n\tconst meta = FILE_META_VERSIONED.deserializeWithEmbeddedVersion(data);\n\treturn Number(meta.size);\n}\n\nfunction isValidFileSize(size: number): boolean {\n\treturn Number.isSafeInteger(size) && size >= 0 && size <= MAX_FILE_SIZE_BYTES;\n}\n\n/**\n * Simple async mutex for serializing database operations\n * @rivetkit/sqlite calls are not safe to run concurrently on one module instance\n */\nclass AsyncMutex {\n\t#locked = false;\n\t#waiting: (() => void)[] = [];\n\n\tasync acquire(): Promise<void> {\n\t\twhile (this.#locked) {\n\t\t\tawait new Promise<void>((resolve) => this.#waiting.push(resolve));\n\t\t}\n\t\tthis.#locked = true;\n\t}\n\n\trelease(): void {\n\t\tthis.#locked = false;\n\t\tconst next = this.#waiting.shift();\n\t\tif (next) {\n\t\t\tnext();\n\t\t}\n\t}\n\n\tasync run<T>(fn: () => Promise<T>): Promise<T> {\n\t\tawait this.acquire();\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tthis.release();\n\t\t}\n\t}\n}\n\n/**\n * Database wrapper that provides a simplified SQLite API\n */\nexport class Database {\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #handle: number;\n\treadonly #fileName: string;\n\treadonly #onClose: () => void;\n\treadonly #sqliteMutex: AsyncMutex;\n\n\tconstructor(\n\t\tsqlite3: SQLite3Api,\n\t\thandle: number,\n\t\tfileName: string,\n\t\tonClose: () => void,\n\t\tsqliteMutex: AsyncMutex,\n\t) {\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#handle = handle;\n\t\tthis.#fileName = fileName;\n\t\tthis.#onClose = onClose;\n\t\tthis.#sqliteMutex = sqliteMutex;\n\t}\n\n\t/**\n\t * Execute SQL with optional row callback\n\t * @param sql - SQL statement to execute\n\t * @param callback - Called for each result row with (row, columns)\n\t */\n\tasync exec(sql: string, callback?: (row: unknown[], columns: string[]) => void): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.exec(this.#handle, sql, callback);\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL statement (no result rows)\n\t * @param sql - SQL statement with ? placeholders\n\t * @param params - Parameter values to bind\n\t */\n\tasync run(sql: string, params?: SqliteBindings): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\t// Consume rows for statements that return results.\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Execute a parameterized SQL query and return results\n\t * @param sql - SQL query with ? placeholders\n\t * @param params - Parameter values to bind\n\t * @returns Object with rows (array of arrays) and columns (column names)\n\t */\n\tasync query(sql: string, params?: SqliteBindings): Promise<{ rows: unknown[][]; columns: string[] }> {\n\t\treturn this.#sqliteMutex.run(async () => {\n\t\t\tconst rows: unknown[][] = [];\n\t\t\tlet columns: string[] = [];\n\t\t\tfor await (const stmt of this.#sqlite3.statements(this.#handle, sql)) {\n\t\t\t\tif (params) {\n\t\t\t\t\tthis.#sqlite3.bind_collection(stmt, params);\n\t\t\t\t}\n\n\t\t\t\twhile ((await this.#sqlite3.step(stmt)) === SQLITE_ROW) {\n\t\t\t\t\tif (columns.length === 0) {\n\t\t\t\t\t\tcolumns = this.#sqlite3.column_names(stmt);\n\t\t\t\t\t}\n\t\t\t\t\trows.push(this.#sqlite3.row(stmt));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { rows, columns };\n\t\t});\n\t}\n\n\t/**\n\t * Close the database\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.#sqliteMutex.run(async () => {\n\t\t\tawait this.#sqlite3.close(this.#handle);\n\t\t});\n\t\tthis.#onClose();\n\t}\n\n\t/**\n\t * Get the raw @rivetkit/sqlite API (for advanced usage)\n\t */\n\tget sqlite3(): SQLite3Api {\n\t\treturn this.#sqlite3;\n\t}\n\n\t/**\n\t * Get the raw database handle (for advanced usage)\n\t */\n\tget handle(): number {\n\t\treturn this.#handle;\n\t}\n}\n\n/**\n * SQLite VFS backed by KV storage.\n *\n * Each instance is independent and has its own @rivetkit/sqlite WASM module.\n * This allows multiple instances to operate concurrently without interference.\n */\nexport class SqliteVfs {\n\t#sqlite3: SQLite3Api | null = null;\n\t#sqliteSystem: SqliteSystem | null = null;\n\t#initPromise: Promise<void> | null = null;\n\t#openMutex = new AsyncMutex();\n\t#sqliteMutex = new AsyncMutex();\n\t#instanceId: string;\n\t#destroyed = false;\n\n\tconstructor() {\n\t\t// Generate unique instance ID for VFS name\n\t\tthis.#instanceId = crypto.randomUUID().replace(/-/g, '').slice(0, 8);\n\t}\n\n\t/**\n\t * Initialize @rivetkit/sqlite and VFS (called once per instance)\n\t */\n\tasync #ensureInitialized(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Fast path: already initialized\n\t\tif (this.#sqlite3 && this.#sqliteSystem) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Synchronously create the promise if not started\n\t\tif (!this.#initPromise) {\n\t\t\tthis.#initPromise = (async () => {\n\t\t\t\tconst { sqlite3, module } = await loadSqliteRuntime();\n\t\t\t\tif (this.#destroyed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#sqlite3 = sqlite3;\n\t\t\t\tthis.#sqliteSystem = new SqliteSystem(\n\t\t\t\t\tsqlite3,\n\t\t\t\t\tmodule,\n\t\t\t\t\t`kv-vfs-${this.#instanceId}`,\n\t\t\t\t);\n\t\t\t\tthis.#sqliteSystem.register();\n\t\t\t})();\n\t\t}\n\n\t\t// Wait for initialization\n\t\ttry {\n\t\t\tawait this.#initPromise;\n\t\t} catch (error) {\n\t\t\tthis.#initPromise = null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Open a SQLite database using KV storage backend\n\t *\n\t * @param fileName - The database file name (typically the actor ID)\n\t * @param options - KV storage operations for this database\n\t * @returns A Database instance\n\t */\n\tasync open(\n\t\tfileName: string,\n\t\toptions: KvVfsOptions,\n\t): Promise<Database> {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new Error(\"SqliteVfs is closed\");\n\t\t}\n\n\t\t// Serialize all open operations within this instance\n\t\tawait this.#openMutex.acquire();\n\t\ttry {\n\t\t\t// Initialize @rivetkit/sqlite and SqliteSystem on first call\n\t\t\tawait this.#ensureInitialized();\n\n\t\t\tif (!this.#sqlite3 || !this.#sqliteSystem) {\n\t\t\t\tthrow new Error(\"Failed to initialize SQLite\");\n\t\t\t}\n\t\t\tconst sqlite3 = this.#sqlite3;\n\t\t\tconst sqliteSystem = this.#sqliteSystem;\n\n\t\t\t// Register this filename with its KV options\n\t\t\tsqliteSystem.registerFile(fileName, options);\n\n\t\t\t// Open database\n\t\t\tconst db = await this.#sqliteMutex.run(async () =>\n\t\t\t\tsqlite3.open_v2(\n\t\t\t\t\tfileName,\n\t\t\t\t\tSQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,\n\t\t\t\t\tsqliteSystem.name,\n\t\t\t\t),\n\t\t\t);\n\t\t\t// TODO: Benchmark PRAGMA tuning for KV-backed SQLite after open.\n\t\t\t// Start with journal_mode=PERSIST and journal_size_limit to reduce\n\t\t\t// journal churn on high-latency KV without introducing WAL.\n\n\t\t\t// Create cleanup callback\n\t\t\tconst onClose = () => {\n\t\t\t\tsqliteSystem.unregisterFile(fileName);\n\t\t\t};\n\n\t\t\treturn new Database(\n\t\t\t\tsqlite3,\n\t\t\t\tdb,\n\t\t\t\tfileName,\n\t\t\t\tonClose,\n\t\t\t\tthis.#sqliteMutex,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.#openMutex.release();\n\t\t}\n\t}\n\n\t/**\n\t * Tears down this VFS instance and releases internal references.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tif (this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#destroyed = true;\n\n\t\tconst initPromise = this.#initPromise;\n\t\tif (initPromise) {\n\t\t\ttry {\n\t\t\t\tawait initPromise;\n\t\t\t} catch {\n\t\t\t\t// Initialization failure already surfaced to caller.\n\t\t\t}\n\t\t}\n\n\t\tif (this.#sqliteSystem) {\n\t\t\tawait this.#sqliteSystem.close();\n\t\t}\n\n\t\tthis.#sqliteSystem = null;\n\t\tthis.#sqlite3 = null;\n\t\tthis.#initPromise = null;\n\t}\n\n\t/**\n\t * Alias for destroy to align with DB-style lifecycle naming.\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this.destroy();\n\t}\n}\n\n/**\n * Internal VFS implementation\n */\nclass SqliteSystem implements SqliteVfsRegistration {\n\treadonly name: string;\n\treadonly mxPathName = SQLITE_MAX_PATHNAME_BYTES;\n\treadonly mxPathname = SQLITE_MAX_PATHNAME_BYTES;\n\t#mainFileName: string | null = null;\n\t#mainFileOptions: KvVfsOptions | null = null;\n\treadonly #openFiles: Map<number, OpenFile> = new Map();\n\treadonly #sqlite3: SQLite3Api;\n\treadonly #module: SQLiteModule;\n\t#heapDataView: DataView;\n\t#heapDataViewBuffer: ArrayBufferLike;\n\n\tconstructor(sqlite3: SQLite3Api, module: SQLiteModule, name: string) {\n\t\tthis.name = name;\n\t\tthis.#sqlite3 = sqlite3;\n\t\tthis.#module = module;\n\t\tthis.#heapDataViewBuffer = module.HEAPU8.buffer;\n\t\tthis.#heapDataView = new DataView(this.#heapDataViewBuffer);\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.#openFiles.clear();\n\t\tthis.#mainFileName = null;\n\t\tthis.#mainFileOptions = null;\n\t}\n\n\tisReady(): boolean {\n\t\treturn true;\n\t}\n\n\thasAsyncMethod(methodName: string): boolean {\n\t\treturn SQLITE_ASYNC_METHODS.has(methodName);\n\t}\n\n\t/**\n\t * Registers the VFS with SQLite\n\t */\n\tregister(): void {\n\t\tthis.#sqlite3.vfs_register(this, false);\n\t}\n\n\t/**\n\t * Registers a file with its KV options (before opening)\n\t */\n\tregisterFile(fileName: string, options: KvVfsOptions): void {\n\t\tif (!this.#mainFileName) {\n\t\t\tthis.#mainFileName = fileName;\n\t\t\tthis.#mainFileOptions = options;\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.#mainFileName !== fileName) {\n\t\t\tthrow new Error(\n\t\t\t\t`SqliteSystem is actor-scoped and expects one main file. Got ${fileName}, expected ${this.#mainFileName}.`,\n\t\t\t);\n\t\t}\n\n\t\tthis.#mainFileOptions = options;\n\t}\n\n\t/**\n\t * Unregisters a file's KV options (after closing)\n\t */\n\tunregisterFile(fileName: string): void {\n\t\tif (this.#mainFileName === fileName) {\n\t\t\tthis.#mainFileName = null;\n\t\t\tthis.#mainFileOptions = null;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve file path to the actor's main DB file or known SQLite sidecars.\n\t */\n\t#resolveFile(path: string): ResolvedFile | null {\n\t\tif (!this.#mainFileName || !this.#mainFileOptions) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (path === this.#mainFileName) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_MAIN };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-journal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_JOURNAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-wal`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_WAL };\n\t\t}\n\t\tif (path === `${this.#mainFileName}-shm`) {\n\t\t\treturn { options: this.#mainFileOptions, fileTag: FILE_TAG_SHM };\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t#resolveFileOrThrow(path: string): ResolvedFile {\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (resolved) {\n\t\t\treturn resolved;\n\t\t}\n\n\t\tif (!this.#mainFileName) {\n\t\t\tthrow new Error(`No KV options registered for file: ${path}`);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Unsupported SQLite file path ${path}. Expected one of ${this.#mainFileName}, ${this.#mainFileName}-journal, ${this.#mainFileName}-wal, ${this.#mainFileName}-shm.`,\n\t\t);\n\t}\n\n\t#chunkKey(file: OpenFile, chunkIndex: number): Uint8Array {\n\t\treturn getChunkKey(file.fileTag, chunkIndex);\n\t}\n\n\tasync xOpen(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\tfileId: number,\n\t\tflags: number,\n\t\tpOutFlags: number,\n\t): Promise<number> {\n\t\tconst path = this.#decodeFilename(zName, flags);\n\t\tif (!path) {\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Get the registered KV options for this file\n\t\t// For journal/wal files, use the main database's options\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get existing file size if the file exists\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tlet size: number;\n\n\t\tif (sizeData) {\n\t\t\t// File exists, use existing size\n\t\t\tsize = decodeFileMeta(sizeData);\n\t\t\tif (!isValidFileSize(size)) {\n\t\t\t\treturn VFS.SQLITE_IOERR;\n\t\t\t}\n\t\t} else if (flags & VFS.SQLITE_OPEN_CREATE) {\n\t\t\t// File doesn't exist, create it\n\t\t\tsize = 0;\n\t\t\tawait options.put(metaKey, encodeFileMeta(size));\n\t\t} else {\n\t\t\t// File doesn't exist and we're not creating it\n\t\t\treturn VFS.SQLITE_CANTOPEN;\n\t\t}\n\n\t\t// Store open file info with options\n\t\tthis.#openFiles.set(fileId, {\n\t\t\tpath,\n\t\t\tfileTag,\n\t\t\tmetaKey,\n\t\t\tsize,\n\t\t\tmetaDirty: false,\n\t\t\tflags,\n\t\t\toptions,\n\t\t});\n\n\t\t// Set output flags to the actual flags used.\n\t\tthis.#writeInt32(pOutFlags, flags);\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xClose(fileId: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Delete-on-close files should skip metadata flush because the file will\n\t\t// be removed immediately.\n\t\tif (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {\n\t\t\tawait this.#delete(file.path);\n\t\t\tthis.#openFiles.delete(fileId);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tif (file.metaDirty) {\n\t\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\tthis.#openFiles.delete(fileId);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xRead(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst options = file.options;\n\t\tconst requestedLength = iAmt;\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_READ;\n\t\t}\n\t\tconst fileSize = file.size;\n\n\t\t// If offset is beyond file size, return short read with zeroed buffer\n\t\tif (iOffset >= fileSize) {\n\t\t\tdata.fill(0);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\t// Calculate which chunks we need to read\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + requestedLength - 1) / CHUNK_SIZE);\n\n\t\t// Fetch all needed chunks\n\t\tconst chunkKeys: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tchunkKeys.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tconst chunks = await options.getBatch(chunkKeys);\n\n\t\t// Copy data from chunks to output buffer\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkData = chunks[i - startChunk];\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\n\t\t\t// Calculate the range within this chunk\n\t\t\tconst readStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst readEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + requestedLength - chunkOffset,\n\t\t\t);\n\n\t\t\tif (chunkData) {\n\t\t\t\t// Copy available data\n\t\t\t\tconst sourceStart = readStart;\n\t\t\t\tconst sourceEnd = Math.min(readEnd, chunkData.length);\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\n\t\t\t\tif (sourceEnd > sourceStart) {\n\t\t\t\t\tdata.set(chunkData.subarray(sourceStart, sourceEnd), destStart);\n\t\t\t\t}\n\n\t\t\t\t// Zero-fill if chunk is smaller than expected\n\t\t\t\tif (sourceEnd < readEnd) {\n\t\t\t\t\tconst zeroStart = destStart + (sourceEnd - sourceStart);\n\t\t\t\t\tconst zeroEnd = destStart + (readEnd - readStart);\n\t\t\t\t\tdata.fill(0, zeroStart, zeroEnd);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Chunk doesn't exist, zero-fill\n\t\t\t\tconst destStart = chunkOffset + readStart - iOffset;\n\t\t\t\tconst destEnd = destStart + (readEnd - readStart);\n\t\t\t\tdata.fill(0, destStart, destEnd);\n\t\t\t}\n\t\t}\n\n\t\t// If we read less than requested (past EOF), return short read\n\t\tconst actualBytes = Math.min(requestedLength, fileSize - iOffset);\n\t\tif (actualBytes < requestedLength) {\n\t\t\tdata.fill(0, actualBytes);\n\t\t\treturn VFS.SQLITE_IOERR_SHORT_READ;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xWrite(\n\t\tfileId: number,\n\t\tpData: number,\n\t\tiAmt: number,\n\t\tiOffsetLo: number,\n\t\tiOffsetHi: number,\n\t): Promise<number> {\n\t\tif (iAmt === 0) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\tconst data = this.#module.HEAPU8.subarray(pData, pData + iAmt);\n\t\tconst iOffset = delegalize(iOffsetLo, iOffsetHi);\n\t\tif (iOffset < 0) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\t\tconst options = file.options;\n\t\tconst writeLength = iAmt;\n\t\tconst writeEndOffset = iOffset + writeLength;\n\t\tif (writeEndOffset > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_WRITE;\n\t\t}\n\n\t\t// Calculate which chunks we need to modify\n\t\tconst startChunk = Math.floor(iOffset / CHUNK_SIZE);\n\t\tconst endChunk = Math.floor((iOffset + writeLength - 1) / CHUNK_SIZE);\n\n\t\tinterface WritePlan {\n\t\t\tchunkKey: Uint8Array;\n\t\t\tchunkOffset: number;\n\t\t\twriteStart: number;\n\t\t\twriteEnd: number;\n\t\t\texistingChunkIndex: number;\n\t\t}\n\n\t\t// Only fetch chunks where we must preserve existing prefix/suffix bytes.\n\t\tconst plans: WritePlan[] = [];\n\t\tconst chunkKeysToFetch: Uint8Array[] = [];\n\t\tfor (let i = startChunk; i <= endChunk; i++) {\n\t\t\tconst chunkOffset = i * CHUNK_SIZE;\n\t\t\tconst writeStart = Math.max(0, iOffset - chunkOffset);\n\t\t\tconst writeEnd = Math.min(\n\t\t\t\tCHUNK_SIZE,\n\t\t\t\tiOffset + writeLength - chunkOffset,\n\t\t\t);\n\t\t\tconst existingBytesInChunk = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(CHUNK_SIZE, file.size - chunkOffset),\n\t\t\t);\n\t\t\tconst needsExisting = writeStart > 0 || existingBytesInChunk > writeEnd;\n\t\t\tconst chunkKey = this.#chunkKey(file, i);\n\t\t\tlet existingChunkIndex = -1;\n\t\t\tif (needsExisting) {\n\t\t\t\texistingChunkIndex = chunkKeysToFetch.length;\n\t\t\t\tchunkKeysToFetch.push(chunkKey);\n\t\t\t}\n\t\t\tplans.push({\n\t\t\t\tchunkKey,\n\t\t\t\tchunkOffset,\n\t\t\t\twriteStart,\n\t\t\t\twriteEnd,\n\t\t\t\texistingChunkIndex,\n\t\t\t});\n\t\t}\n\n\t\tconst existingChunks = chunkKeysToFetch.length > 0\n\t\t\t? await options.getBatch(chunkKeysToFetch)\n\t\t\t: [];\n\n\t\t// Prepare new chunk data\n\t\tconst entriesToWrite: [Uint8Array, Uint8Array][] = [];\n\n\t\tfor (const plan of plans) {\n\t\t\tconst existingChunk =\n\t\t\t\tplan.existingChunkIndex >= 0\n\t\t\t\t\t? existingChunks[plan.existingChunkIndex]\n\t\t\t\t\t: null;\n\t\t\t// Create new chunk data\n\t\t\tlet newChunk: Uint8Array;\n\t\t\tif (existingChunk) {\n\t\t\t\tnewChunk = new Uint8Array(Math.max(existingChunk.length, plan.writeEnd));\n\t\t\t\tnewChunk.set(existingChunk);\n\t\t\t} else {\n\t\t\t\tnewChunk = new Uint8Array(plan.writeEnd);\n\t\t\t}\n\n\t\t\t// Copy data from input buffer to chunk\n\t\t\tconst sourceStart = plan.chunkOffset + plan.writeStart - iOffset;\n\t\t\tconst sourceEnd = sourceStart + (plan.writeEnd - plan.writeStart);\n\t\t\tnewChunk.set(data.subarray(sourceStart, sourceEnd), plan.writeStart);\n\n\t\t\tentriesToWrite.push([plan.chunkKey, newChunk]);\n\t\t}\n\n\t\t// Update file size if we wrote past the end\n\t\tconst previousSize = file.size;\n\t\tconst newSize = Math.max(file.size, writeEndOffset);\n\t\tif (newSize !== previousSize) {\n\t\t\tfile.size = newSize;\n\t\t\tfile.metaDirty = true;\n\t\t}\n\t\tif (file.metaDirty) {\n\t\t\tentriesToWrite.push([file.metaKey, encodeFileMeta(file.size)]);\n\t\t}\n\n\t\t// Write all chunks and metadata\n\t\tawait options.putBatch(entriesToWrite);\n\t\tif (file.metaDirty) {\n\t\t\tfile.metaDirty = false;\n\t\t}\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xTruncate(\n\t\tfileId: number,\n\t\tsizeLo: number,\n\t\tsizeHi: number,\n\t): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\n\t\tconst size = delegalize(sizeLo, sizeHi);\n\t\tif (size < 0 || size > MAX_FILE_SIZE_BYTES) {\n\t\t\treturn VFS.SQLITE_IOERR_TRUNCATE;\n\t\t}\n\t\tconst options = file.options;\n\n\t\t// If truncating to larger size, just update metadata\n\t\tif (size >= file.size) {\n\t\t\tif (size > file.size) {\n\t\t\t\tfile.size = size;\n\t\t\t\tfile.metaDirty = true;\n\t\t\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\t\t\tfile.metaDirty = false;\n\t\t\t}\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\t// Calculate which chunks to delete\n\t\t// Note: When size=0, lastChunkToKeep = floor(-1/4096) = -1, which means\n\t\t// all chunks (starting from index 0) will be deleted in the loop below.\n\t\tconst lastChunkToKeep = Math.floor((size - 1) / CHUNK_SIZE);\n\t\tconst lastExistingChunk = Math.floor((file.size - 1) / CHUNK_SIZE);\n\n\t\t// Delete chunks beyond the new size\n\t\tconst keysToDelete: Uint8Array[] = [];\n\t\tfor (let i = lastChunkToKeep + 1; i <= lastExistingChunk; i++) {\n\t\t\tkeysToDelete.push(this.#chunkKey(file, i));\n\t\t}\n\n\t\tif (keysToDelete.length > 0) {\n\t\t\tawait options.deleteBatch(keysToDelete);\n\t\t}\n\n\t\t// Truncate the last kept chunk if needed\n\t\tif (size > 0 && size % CHUNK_SIZE !== 0) {\n\t\t\tconst lastChunkKey = this.#chunkKey(file, lastChunkToKeep);\n\t\t\tconst lastChunkData = await options.get(lastChunkKey);\n\n\t\t\tif (lastChunkData && lastChunkData.length > size % CHUNK_SIZE) {\n\t\t\t\tconst truncatedChunk = lastChunkData.subarray(0, size % CHUNK_SIZE);\n\t\t\t\tawait options.put(lastChunkKey, truncatedChunk);\n\t\t\t}\n\t\t}\n\n\t\t// Update file size\n\t\tfile.size = size;\n\t\tfile.metaDirty = true;\n\t\tawait options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xSync(fileId: number, _flags: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file || !file.metaDirty) {\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tawait file.options.put(file.metaKey, encodeFileMeta(file.size));\n\t\tfile.metaDirty = false;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xFileSize(fileId: number, pSize: number): Promise<number> {\n\t\tconst file = this.#openFiles.get(fileId);\n\t\tif (!file) {\n\t\t\treturn VFS.SQLITE_IOERR_FSTAT;\n\t\t}\n\n\t\t// Set size as 64-bit integer.\n\t\tthis.#writeBigInt64(pSize, BigInt(file.size));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\tasync xDelete(_pVfs: number, zName: number, _syncDir: number): Promise<number> {\n\t\tawait this.#delete(this.#module.UTF8ToString(zName));\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t/**\n\t * Internal delete implementation\n\t */\n\tasync #delete(path: string): Promise<void> {\n\t\tconst { options, fileTag } = this.#resolveFileOrThrow(path);\n\t\tconst metaKey = getMetaKey(fileTag);\n\n\t\t// Get file size to find out how many chunks to delete\n\t\tconst sizeData = await options.get(metaKey);\n\n\t\tif (!sizeData) {\n\t\t\t// File doesn't exist, that's OK\n\t\t\treturn;\n\t\t}\n\n\t\tconst size = decodeFileMeta(sizeData);\n\n\t\t// Delete all chunks\n\t\tconst keysToDelete: Uint8Array[] = [metaKey];\n\t\tconst numChunks = Math.ceil(size / CHUNK_SIZE);\n\t\tfor (let i = 0; i < numChunks; i++) {\n\t\t\tkeysToDelete.push(getChunkKey(fileTag, i));\n\t\t}\n\n\t\tawait options.deleteBatch(keysToDelete);\n\t}\n\n\tasync xAccess(\n\t\t_pVfs: number,\n\t\tzName: number,\n\t\t_flags: number,\n\t\tpResOut: number,\n\t): Promise<number> {\n\t\t// TODO: Measure how often xAccess runs during open and whether these\n\t\t// existence checks add meaningful KV round-trip overhead. If they do,\n\t\t// consider serving file existence from in-memory state.\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst resolved = this.#resolveFile(path);\n\t\tif (!resolved) {\n\t\t\t// File not registered, doesn't exist\n\t\t\tthis.#writeInt32(pResOut, 0);\n\t\t\treturn VFS.SQLITE_OK;\n\t\t}\n\n\t\tconst compactMetaKey = getMetaKey(resolved.fileTag);\n\t\tconst metaData = await resolved.options.get(compactMetaKey);\n\n\t\t// Set result: 1 if file exists, 0 otherwise\n\t\tthis.#writeInt32(pResOut, metaData ? 1 : 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txCheckReservedLock(_fileId: number, pResOut: number): number {\n\t\t// This VFS is actor-scoped with one writer, so there is no external\n\t\t// reserved lock state to report.\n\t\tthis.#writeInt32(pResOut, 0);\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txLock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txUnlock(_fileId: number, _flags: number): number {\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\txFileControl(_fileId: number, _flags: number, _pArg: number): number {\n\t\treturn VFS.SQLITE_NOTFOUND;\n\t}\n\n\txDeviceCharacteristics(_fileId: number): number {\n\t\treturn 0;\n\t}\n\n\txFullPathname(_pVfs: number, zName: number, nOut: number, zOut: number): number {\n\t\tconst path = this.#module.UTF8ToString(zName);\n\t\tconst bytes = TEXT_ENCODER.encode(path);\n\t\tconst out = this.#module.HEAPU8.subarray(zOut, zOut + nOut);\n\t\tif (bytes.length >= out.length) {\n\t\t\treturn VFS.SQLITE_IOERR;\n\t\t}\n\t\tout.set(bytes, 0);\n\t\tout[bytes.length] = 0;\n\t\treturn VFS.SQLITE_OK;\n\t}\n\n\t#decodeFilename(zName: number, flags: number): string | null {\n\t\tif (!zName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (flags & VFS.SQLITE_OPEN_URI) {\n\t\t\t// Decode SQLite URI filename layout: path\\0key\\0value\\0...\\0\n\t\t\tlet pName = zName;\n\t\t\tlet state: 1 | 2 | 3 | null = 1;\n\t\t\tconst charCodes: number[] = [];\n\t\t\twhile (state) {\n\t\t\t\tconst charCode = this.#module.HEAPU8[pName++];\n\t\t\t\tif (charCode) {\n\t\t\t\t\tcharCodes.push(charCode);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!this.#module.HEAPU8[pName]) {\n\t\t\t\t\tstate = null;\n\t\t\t\t}\n\t\t\t\tswitch (state) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tcharCodes.push(\"?\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tcharCodes.push(\"=\".charCodeAt(0));\n\t\t\t\t\t\tstate = 3;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tcharCodes.push(\"&\".charCodeAt(0));\n\t\t\t\t\t\tstate = 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn TEXT_DECODER.decode(new Uint8Array(charCodes));\n\t\t}\n\n\t\treturn this.#module.UTF8ToString(zName);\n\t}\n\n\t#heapView(): DataView {\n\t\tconst heapBuffer = this.#module.HEAPU8.buffer;\n\t\tif (heapBuffer !== this.#heapDataViewBuffer) {\n\t\t\tthis.#heapDataViewBuffer = heapBuffer;\n\t\t\tthis.#heapDataView = new DataView(heapBuffer);\n\t\t}\n\t\treturn this.#heapDataView;\n\t}\n\n\t#writeInt32(pointer: number, value: number): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setInt32(heapByteOffset, value, true);\n\t}\n\n\t#writeBigInt64(pointer: number, value: bigint): void {\n\t\tconst heapByteOffset = this.#module.HEAPU8.byteOffset + pointer;\n\t\tthis.#heapView().setBigInt64(heapByteOffset, value, true);\n\t}\n}\n\n/**\n * Rebuild an i64 from Emscripten's legalized (lo32, hi32) pair.\n * SQLite passes file offsets and sizes this way. We decode into unsigned words\n * and reject values above the VFS max file size.\n */\nfunction delegalize(lo32: number, hi32: number): number {\n\tconst hi = hi32 >>> 0;\n\tconst lo = lo32 >>> 0;\n\tif (hi > MAX_FILE_SIZE_HI32) {\n\t\treturn -1;\n\t}\n\tif (hi === MAX_FILE_SIZE_HI32 && lo > MAX_FILE_SIZE_LO32) {\n\t\treturn -1;\n\t}\n\treturn (hi * UINT32_SIZE) + lo;\n}\n","/**\n * Key management for SQLite VFS storage\n *\n * This module contains constants and utilities for building keys used in the\n * key-value store for SQLite file storage.\n */\n\n/**\n * Size of each file chunk stored in KV.\n *\n * SQLite calls the VFS with byte ranges, but KV stores whole values by key.\n * The VFS maps each byte range to one or more fixed-size chunks, then uses\n * chunk keys to read or write those values in KV.\n */\nexport const CHUNK_SIZE = 4096;\n\n/** Top-level SQLite prefix (must match SQLITE_PREFIX in actor KV system) */\nexport const SQLITE_PREFIX = 8;\n\n/** Schema version namespace byte after SQLITE_PREFIX */\nexport const SQLITE_SCHEMA_VERSION = 1;\n\n/** Key prefix byte for file metadata (after SQLITE_PREFIX + version) */\nexport const META_PREFIX = 0;\n\n/** Key prefix byte for file chunks (after SQLITE_PREFIX + version) */\nexport const CHUNK_PREFIX = 1;\n\n/** File kind tag for the actor's main database file */\nexport const FILE_TAG_MAIN = 0;\n\n/** File kind tag for the actor's rollback journal sidecar */\nexport const FILE_TAG_JOURNAL = 1;\n\n/** File kind tag for the actor's WAL sidecar */\nexport const FILE_TAG_WAL = 2;\n\n/** File kind tag for the actor's SHM sidecar */\nexport const FILE_TAG_SHM = 3;\n\nexport type SqliteFileTag =\n\t| typeof FILE_TAG_MAIN\n\t| typeof FILE_TAG_JOURNAL\n\t| typeof FILE_TAG_WAL\n\t| typeof FILE_TAG_SHM;\n\n/**\n * Gets the key for file metadata\n * Format: [SQLITE_PREFIX (1 byte), version (1 byte), META_PREFIX (1 byte), file tag (1 byte)]\n */\nexport function getMetaKey(fileTag: SqliteFileTag): Uint8Array {\n\tconst key = new Uint8Array(4);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = META_PREFIX;\n\tkey[3] = fileTag;\n\treturn key;\n}\n\n/**\n * Gets the key for one chunk of file data.\n * Format: [SQLITE_PREFIX, CHUNK_PREFIX, file tag, chunk index (u32 big-endian)]\n *\n * The chunk index is derived from byte offset as floor(offset / CHUNK_SIZE),\n * which is how SQLite byte ranges map onto KV keys.\n */\nexport function getChunkKey(\n\tfileTag: SqliteFileTag,\n\tchunkIndex: number,\n): Uint8Array {\n\tconst key = new Uint8Array(8);\n\tkey[0] = SQLITE_PREFIX;\n\tkey[1] = SQLITE_SCHEMA_VERSION;\n\tkey[2] = CHUNK_PREFIX;\n\tkey[3] = fileTag;\n\tkey[4] = (chunkIndex >>> 24) & 0xff;\n\tkey[5] = (chunkIndex >>> 16) & 0xff;\n\tkey[6] = (chunkIndex >>> 8) & 0xff;\n\tkey[7] = chunkIndex & 0xff;\n\treturn key;\n}\n","import { createVersionedDataHandler } from \"vbare\";\nimport * as v1 from \"../../dist/schemas/file-meta/v1\";\n\nexport const CURRENT_VERSION = 1;\n\nexport const FILE_META_VERSIONED = createVersionedDataHandler<v1.FileMeta>({\n\tdeserializeVersion: (bytes, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.decodeFileMeta(bytes);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tserializeVersion: (data, version) => {\n\t\tswitch (version) {\n\t\t\tcase 1:\n\t\t\t\treturn v1.encodeFileMeta(data as v1.FileMeta);\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown version ${version}`);\n\t\t}\n\t},\n\tdeserializeConverters: () => [],\n\tserializeConverters: () => [],\n});\n","// @generated - post-processed by compile-bare.ts\nimport * as bare from \"@rivetkit/bare-ts\"\n\nconst config = /* @__PURE__ */ bare.Config({})\n\nexport type u64 = bigint\n\nexport type FileMeta = {\n readonly size: u64,\n}\n\nexport function readFileMeta(bc: bare.ByteCursor): FileMeta {\n return {\n size: bare.readU64(bc),\n }\n}\n\nexport function writeFileMeta(bc: bare.ByteCursor, x: FileMeta): void {\n bare.writeU64(bc, x.size)\n}\n\nexport function encodeFileMeta(x: FileMeta): Uint8Array {\n const bc = new bare.ByteCursor(\n new Uint8Array(config.initialBufferLength),\n config\n )\n writeFileMeta(bc, x)\n return new Uint8Array(bc.view.buffer, bc.view.byteOffset, bc.offset)\n}\n\nexport function decodeFileMeta(bytes: Uint8Array): FileMeta {\n const bc = new bare.ByteCursor(bytes, config)\n const result = readFileMeta(bc)\n if (bc.offset < bc.view.byteLength) {\n throw new bare.BareError(bc.offset, \"remaining bytes\")\n }\n return result\n}\n\n\nfunction assert(condition: boolean, message?: string): asserts condition {\n if (!condition) throw new Error(message ?? \"Assertion failed\")\n}\n"],"mappings":"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","names":["SQLITE_OPEN_CREATE","require","encodeFileMeta","decodeFileMeta","SQLITE_OPEN_CREATE"]}
|
package/package.json
CHANGED
package/src/vfs.ts
CHANGED
|
@@ -112,7 +112,11 @@ function isSQLiteModule(value: unknown): value is SQLiteModule {
|
|
|
112
112
|
async function loadSqliteRuntime(): Promise<LoadedSqliteRuntime> {
|
|
113
113
|
// Keep the module specifier assembled at runtime so TypeScript declaration
|
|
114
114
|
// generation does not try to typecheck this deep dist import path.
|
|
115
|
-
|
|
115
|
+
// Uses Array.join() instead of string concatenation to prevent esbuild/tsup
|
|
116
|
+
// from constant-folding the expression at build time, which would allow
|
|
117
|
+
// Turbopack to trace into the WASM package.
|
|
118
|
+
const specifier = ["@rivetkit/sqlite", "dist", "wa-sqlite-async.mjs"].join("/");
|
|
119
|
+
const sqliteModule = await import(specifier);
|
|
116
120
|
if (!isSqliteEsmFactory(sqliteModule.default)) {
|
|
117
121
|
throw new Error("Invalid SQLite ESM factory export");
|
|
118
122
|
}
|