@powerhousedao/pglite-fs 6.2.3-dev.9 → 6.2.3

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/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { MemoryFS } from "@electric-sql/pglite";
2
-
3
2
  //#region src/atomic-node-fs.d.ts
4
3
  interface AtomicNodeFsLogger {
5
4
  warn(message: string): void;
@@ -12,9 +11,11 @@ interface AtomicNodeFsOptions {
12
11
  * non-transactional query (including each BEGIN/INSERT/COMMIT emitted by
13
12
  * Kysely), so a synchronous full-tree snapshot per call caps write
14
13
  * throughput at one query per snapshot duration. Deferred mode trades
15
- * worst-case crash loss of up to `flushIntervalMs` of writes for sustained
16
- * throughput. `closeFs` always drains pending writes durably before
17
- * returning.
14
+ * crash durability for sustained throughput: a crash loses every write
15
+ * since the last completed snapshot, which is up to `flushIntervalMs` plus
16
+ * the duration of the snapshot in flight (a full-tree write, so it grows
17
+ * with the database). `closeFs` always drains pending writes durably before
18
+ * returning; a process that exits without calling it gets no final flush.
18
19
  *
19
20
  * Default `0` preserves the original per-call synchronous behavior.
20
21
  */
@@ -30,7 +31,7 @@ interface AtomicNodeFsOptions {
30
31
  * won't scale to production write rates. For write-heavy workloads, pass
31
32
  * `flushIntervalMs` to coalesce multiple PGLite syncs into one disk write.
32
33
  */
33
- declare class AtomicNodeFs extends MemoryFS {
34
+ export declare class AtomicNodeFs extends MemoryFS {
34
35
  private readonly hostDir;
35
36
  private readonly logger?;
36
37
  private readonly flushIntervalMs;
@@ -46,5 +47,6 @@ declare class AtomicNodeFs extends MemoryFS {
46
47
  private writeSnapshot;
47
48
  }
48
49
  //#endregion
49
- export { AtomicNodeFs, type AtomicNodeFsLogger };
50
+ export type { AtomicNodeFsLogger };
51
+ export {};
50
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/atomic-node-fs.ts"],"mappings":";;;UA2BiB,kBAAA;EACf,IAAA,CAAK,OAAA;AAAA;AAAA,UAGU,mBAAA;EACf,MAAA,GAAS,kBAAA;EAJT;;AAGF;;;;;;;;;AA2BA;EAbE,eAAA;AAAA;;;;;;;;;;;cAaW,YAAA,SAAqB,QAAA;EAAA,iBACf,OAAA;EAAA,iBACA,MAAA;EAAA,iBACA,eAAA;EAAA,QAET,KAAA;EAAA,QACA,UAAA;EAAA,QACA,aAAA;cAGN,OAAA,UACA,eAAA,GAAkB,mBAAA,GAAsB,kBAAA;EASpC,aAAA,CAAA,GAAiB,OAAA;EA0BjB,QAAA,CAAS,iBAAA,aAA8B,OAAA;EASvC,OAAA,CAAA,GAAW,OAAA;EAAA,QAaT,qBAAA;EAAA,QAmBM,UAAA;EAAA,QAOA,aAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/atomic-node-fs.ts"],"mappings":";;UA2BiB;EACf,KAAK;;UAGU;EACf,SAAS;;;;;;;;;;;;;;;EAeT;;;;;;;;;;;;qBAaW,qBAAqB;mBACf;mBACA;mBACA;UAET;UACA;UACA;EAER,YACE,iBACA,kBAAkB,sBAAsB;EASpC,iBAAiB;EA0BjB,SAAS,8BAA8B;EASvC,WAAW;UAaT;UAmBM;UAOA"}
package/dist/index.js CHANGED
@@ -43,7 +43,8 @@ var AtomicNodeFs = class extends MemoryFS {
43
43
  await promises.rm(tmpPath, { force: true });
44
44
  const memFs = this.pg.Module.FS;
45
45
  if (await fileExists(snapPath)) {
46
- restoreMemfs(memFs, PGDATA, await promises.readFile(snapPath));
46
+ const bytes = await promises.readFile(snapPath);
47
+ restoreMemfs(memFs, PGDATA, bytes);
47
48
  return;
48
49
  }
49
50
  if (await fileExists(path.join(this.hostDir, "PG_VERSION"))) {
@@ -224,9 +225,10 @@ function restoreMemfs(FS, root, bytes) {
224
225
  const dataLen = view.getUint32(off, true);
225
226
  off += 4;
226
227
  const full = root + "/" + relPath;
227
- if (type === 0) if (!FS.analyzePath(full).exists) FS.mkdir(full, dirMode(mode));
228
- else FS.chmod(full, dirMode(mode));
229
- else {
228
+ if (type === 0) {
229
+ if (!FS.analyzePath(full).exists) FS.mkdir(full, dirMode(mode));
230
+ else FS.chmod(full, dirMode(mode));
231
+ } else {
230
232
  const data = bytes.subarray(off, off + dataLen);
231
233
  FS.writeFile(full, data);
232
234
  FS.chmod(full, mode);
@@ -249,7 +251,7 @@ function dirMode(mode) {
249
251
  }
250
252
  async function loadLegacyIntoMemfs(FS, root, hostDir) {
251
253
  ensureDir(FS, root);
252
- const skip = new Set([
254
+ const skip = /* @__PURE__ */ new Set([
253
255
  SNAPSHOT_NAME,
254
256
  SNAPSHOT_TMP,
255
257
  "postmaster.pid"
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fs"],"sources":["../src/atomic-node-fs.ts"],"sourcesContent":["import { MemoryFS } from \"@electric-sql/pglite\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\n// PGDATA in PGLite 0.3.x's MEMFS layout. The compiled bundle uses\n// `/tmp/pglite/base` (PG install prefix `/tmp/pglite`, data dir `base/` inside\n// it). Not exported as a runtime constant, so we hardcode.\nconst PGDATA = \"/tmp/pglite/base\";\nconst SNAPSHOT_NAME = \"snapshot.bin\";\nconst SNAPSHOT_TMP = \"snapshot.bin.tmp\";\nconst MAGIC = new Uint8Array([0x50, 0x47, 0x4c, 0x41]); // \"PGLA\"\nconst FORMAT_VERSION = 1;\n\ntype EntryType = 0 | 1; // 0=dir, 1=file\n\ninterface MemFs {\n readdir(path: string): string[];\n stat(path: string): { mode: number; size: number };\n readFile(path: string, opts?: { encoding: \"binary\" }): Uint8Array;\n writeFile(path: string, data: Uint8Array): void;\n mkdir(path: string, mode?: number): void;\n chmod(path: string, mode: number): void;\n analyzePath(path: string): { exists: boolean };\n isDir(mode: number): boolean;\n isFile(mode: number): boolean;\n}\n\nexport interface AtomicNodeFsLogger {\n warn(message: string): void;\n}\n\nexport interface AtomicNodeFsOptions {\n logger?: AtomicNodeFsLogger;\n /**\n * Coalesce `syncToFs` calls into a trailing-edge disk write at most every\n * `flushIntervalMs` milliseconds. PGLite calls `syncToFs` after every\n * non-transactional query (including each BEGIN/INSERT/COMMIT emitted by\n * Kysely), so a synchronous full-tree snapshot per call caps write\n * throughput at one query per snapshot duration. Deferred mode trades\n * worst-case crash loss of up to `flushIntervalMs` of writes for sustained\n * throughput. `closeFs` always drains pending writes durably before\n * returning.\n *\n * Default `0` preserves the original per-call synchronous behavior.\n */\n flushIntervalMs?: number;\n}\n\n/**\n * PGLite Filesystem that holds the working data dir in Emscripten MEMFS and\n * atomically swaps a single-file on-disk snapshot on `syncToFs`. A SIGKILL\n * mid-write leaves the previous snapshot intact, so the next startup loads\n * cleanly rather than aborting on torn WAL.\n *\n * Intended for local dev use — full-tree snapshots are fine at dev volume but\n * won't scale to production write rates. For write-heavy workloads, pass\n * `flushIntervalMs` to coalesce multiple PGLite syncs into one disk write.\n */\nexport class AtomicNodeFs extends MemoryFS {\n private readonly hostDir: string;\n private readonly logger?: AtomicNodeFsLogger;\n private readonly flushIntervalMs: number;\n\n private dirty = false;\n private flushTimer?: ReturnType<typeof setTimeout>;\n private flushInFlight?: Promise<void>;\n\n constructor(\n hostDir: string,\n optionsOrLogger?: AtomicNodeFsOptions | AtomicNodeFsLogger,\n ) {\n super();\n this.hostDir = path.resolve(hostDir);\n const options = normalizeOptions(optionsOrLogger);\n this.logger = options.logger;\n this.flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 0);\n }\n\n async initialSyncFs(): Promise<void> {\n await fs.mkdir(this.hostDir, { recursive: true });\n const snapPath = path.join(this.hostDir, SNAPSHOT_NAME);\n const tmpPath = path.join(this.hostDir, SNAPSHOT_TMP);\n\n // Drop any leftover staging file from a prior crashed write.\n await fs.rm(tmpPath, { force: true });\n\n const memFs = this.pg!.Module.FS as MemFs;\n\n if (await fileExists(snapPath)) {\n const bytes = await fs.readFile(snapPath);\n restoreMemfs(memFs, PGDATA, bytes);\n return;\n }\n\n const legacyMarker = path.join(this.hostDir, \"PG_VERSION\");\n if (await fileExists(legacyMarker)) {\n this.logger?.warn(\n `Migrating legacy PGLite data dir at ${this.hostDir} to atomic snapshot. Original files retained alongside snapshot.bin as a backup; remove them once the new snapshot is verified.`,\n );\n await loadLegacyIntoMemfs(memFs, PGDATA, this.hostDir);\n return;\n }\n }\n\n async syncToFs(relaxedDurability?: boolean): Promise<void> {\n if (this.flushIntervalMs === 0) {\n await this.writeSnapshot(relaxedDurability ?? false);\n return;\n }\n this.dirty = true;\n this.scheduleDeferredFlush(relaxedDurability ?? false);\n }\n\n async closeFs(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n while (this.flushInFlight) {\n await this.flushInFlight;\n }\n this.dirty = false;\n await this.writeSnapshot(false);\n await super.closeFs();\n }\n\n private scheduleDeferredFlush(relaxedDurability: boolean): void {\n if (this.flushTimer || this.flushInFlight) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined;\n this.flushInFlight = this.drainDirty(relaxedDurability)\n .catch((err) => {\n this.logger?.warn(\n `AtomicNodeFs deferred flush failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n })\n .finally(() => {\n this.flushInFlight = undefined;\n });\n }, this.flushIntervalMs);\n // Don't keep the event loop alive solely for a pending flush; closeFs is\n // responsible for draining before shutdown.\n this.flushTimer.unref?.();\n }\n\n private async drainDirty(relaxedDurability: boolean): Promise<void> {\n while (this.dirty) {\n this.dirty = false;\n await this.writeSnapshot(relaxedDurability);\n }\n }\n\n private async writeSnapshot(relaxedDurability: boolean): Promise<void> {\n const memFs = this.pg!.Module.FS as MemFs;\n const bytes = serializeMemfs(memFs, PGDATA);\n const snapPath = path.join(this.hostDir, SNAPSHOT_NAME);\n const tmpPath = path.join(this.hostDir, SNAPSHOT_TMP);\n\n const fh = await fs.open(tmpPath, \"w\");\n try {\n await fh.write(bytes);\n if (!relaxedDurability) await fh.sync();\n } finally {\n await fh.close();\n }\n\n await fs.rename(tmpPath, snapPath);\n\n if (!relaxedDurability) {\n try {\n const dirFh = await fs.open(this.hostDir, \"r\");\n try {\n await dirFh.sync();\n } finally {\n await dirFh.close();\n }\n } catch {\n // Some platforms reject fsync on a directory fd. The rename itself is\n // still atomic at the inode level; durability of the directory entry\n // is best-effort.\n }\n }\n }\n}\n\nfunction normalizeOptions(\n optionsOrLogger: AtomicNodeFsOptions | AtomicNodeFsLogger | undefined,\n): AtomicNodeFsOptions {\n if (!optionsOrLogger) return {};\n if (isLogger(optionsOrLogger)) {\n return { logger: optionsOrLogger };\n }\n return optionsOrLogger;\n}\n\nfunction isLogger(\n value: AtomicNodeFsOptions | AtomicNodeFsLogger,\n): value is AtomicNodeFsLogger {\n return (\n \"warn\" in value && typeof (value as AtomicNodeFsLogger).warn === \"function\"\n );\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction ensureDir(FS: MemFs, dir: string): void {\n const parts = dir.split(\"/\").filter(Boolean);\n let cur = \"\";\n for (const p of parts) {\n cur += \"/\" + p;\n if (!FS.analyzePath(cur).exists) FS.mkdir(cur);\n }\n}\n\ninterface Entry {\n type: EntryType;\n mode: number;\n relPath: string;\n data?: Uint8Array;\n}\n\nfunction serializeMemfs(FS: MemFs, root: string): Uint8Array {\n const entries: Entry[] = [];\n\n const walk = (dir: string, rel: string) => {\n const names = FS.readdir(dir);\n for (const name of names) {\n if (name === \".\" || name === \"..\") continue;\n const full = dir + \"/\" + name;\n const r = rel === \"\" ? name : rel + \"/\" + name;\n const stat = FS.stat(full);\n if (FS.isDir(stat.mode)) {\n entries.push({ type: 0, mode: stat.mode & 0o7777, relPath: r });\n walk(full, r);\n } else if (FS.isFile(stat.mode)) {\n const data = FS.readFile(full, { encoding: \"binary\" });\n entries.push({\n type: 1,\n mode: stat.mode & 0o7777,\n relPath: r,\n data,\n });\n }\n // skip symlinks, sockets, etc. — PGLite doesn't create them in PGDATA.\n }\n };\n walk(root, \"\");\n\n const encoder = new TextEncoder();\n const encodedPaths = entries.map((e) => encoder.encode(e.relPath));\n\n let size = 4 + 4 + 4; // magic + version + count\n for (let i = 0; i < entries.length; i++) {\n size += 1 + 4 + 4 + encodedPaths[i].byteLength + 4;\n size += entries[i].data?.byteLength ?? 0;\n }\n\n const out = new Uint8Array(size);\n const view = new DataView(out.buffer);\n let off = 0;\n\n out.set(MAGIC, off);\n off += 4;\n view.setUint32(off, FORMAT_VERSION, true);\n off += 4;\n view.setUint32(off, entries.length, true);\n off += 4;\n\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i];\n const pathBytes = encodedPaths[i];\n view.setUint8(off, e.type);\n off += 1;\n view.setUint32(off, e.mode, true);\n off += 4;\n view.setUint32(off, pathBytes.byteLength, true);\n off += 4;\n out.set(pathBytes, off);\n off += pathBytes.byteLength;\n const dataLen = e.data?.byteLength ?? 0;\n view.setUint32(off, dataLen, true);\n off += 4;\n if (dataLen > 0 && e.data) {\n out.set(e.data, off);\n off += dataLen;\n }\n }\n\n return out;\n}\n\nfunction restoreMemfs(FS: MemFs, root: string, bytes: Uint8Array): void {\n ensureDir(FS, root);\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let off = 0;\n\n for (let i = 0; i < 4; i++) {\n if (bytes[off + i] !== MAGIC[i]) {\n throw new Error(\"AtomicNodeFs: invalid snapshot magic\");\n }\n }\n off += 4;\n\n const version = view.getUint32(off, true);\n off += 4;\n if (version !== FORMAT_VERSION) {\n throw new Error(`AtomicNodeFs: unsupported snapshot version ${version}`);\n }\n const count = view.getUint32(off, true);\n off += 4;\n\n const decoder = new TextDecoder();\n\n for (let i = 0; i < count; i++) {\n const type = view.getUint8(off);\n off += 1;\n const mode = view.getUint32(off, true);\n off += 4;\n const pathLen = view.getUint32(off, true);\n off += 4;\n const relPath = decoder.decode(bytes.subarray(off, off + pathLen));\n off += pathLen;\n const dataLen = view.getUint32(off, true);\n off += 4;\n const full = root + \"/\" + relPath;\n\n if (type === 0) {\n // dirMode: a snapshot written right after a Windows migration can carry\n // execute-less directory modes, which would make it unopenable.\n if (!FS.analyzePath(full).exists) FS.mkdir(full, dirMode(mode));\n else FS.chmod(full, dirMode(mode));\n } else {\n const data = bytes.subarray(off, off + dataLen);\n FS.writeFile(full, data);\n FS.chmod(full, mode);\n }\n off += dataLen;\n }\n}\n\n/**\n * Permission bits for a directory created in MEMFS.\n *\n * Windows has no execute bit, so `fs.stat` reports every directory as 0o40666.\n * Copying that mode verbatim produces a MEMFS directory that cannot be\n * traversed, and every lookup beneath it fails with ENOENT -- which surfaces\n * as PGLite throwing a bare `ErrnoError { errno: 2 }` on the first query after\n * a legacy migration. Force the traverse bit on. MEMFS is process-local and\n * rebuilt from disk on every open, so host permissions carry no meaning here.\n */\nfunction dirMode(mode: number): number {\n return (mode & 0o7777) | 0o111;\n}\n\nasync function loadLegacyIntoMemfs(\n FS: MemFs,\n root: string,\n hostDir: string,\n): Promise<void> {\n ensureDir(FS, root);\n\n const skip = new Set([SNAPSHOT_NAME, SNAPSHOT_TMP, \"postmaster.pid\"]);\n\n const walk = async (diskPath: string, memPath: string) => {\n const ents = await fs.readdir(diskPath, { withFileTypes: true });\n for (const ent of ents) {\n if (skip.has(ent.name)) continue;\n const diskFull = path.join(diskPath, ent.name);\n const memFull = memPath + \"/\" + ent.name;\n const stat = await fs.stat(diskFull);\n if (ent.isDirectory()) {\n if (!FS.analyzePath(memFull).exists) {\n FS.mkdir(memFull, dirMode(stat.mode));\n }\n await walk(diskFull, memFull);\n } else if (ent.isFile()) {\n const data = await fs.readFile(diskFull);\n FS.writeFile(memFull, data);\n FS.chmod(memFull, stat.mode & 0o7777);\n }\n }\n };\n\n await walk(hostDir, root);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["fs"],"sources":["../src/atomic-node-fs.ts"],"sourcesContent":["import { MemoryFS } from \"@electric-sql/pglite\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\n// PGDATA in PGLite 0.3.x's MEMFS layout. The compiled bundle uses\n// `/tmp/pglite/base` (PG install prefix `/tmp/pglite`, data dir `base/` inside\n// it). Not exported as a runtime constant, so we hardcode.\nconst PGDATA = \"/tmp/pglite/base\";\nconst SNAPSHOT_NAME = \"snapshot.bin\";\nconst SNAPSHOT_TMP = \"snapshot.bin.tmp\";\nconst MAGIC = new Uint8Array([0x50, 0x47, 0x4c, 0x41]); // \"PGLA\"\nconst FORMAT_VERSION = 1;\n\ntype EntryType = 0 | 1; // 0=dir, 1=file\n\ninterface MemFs {\n readdir(path: string): string[];\n stat(path: string): { mode: number; size: number };\n readFile(path: string, opts?: { encoding: \"binary\" }): Uint8Array;\n writeFile(path: string, data: Uint8Array): void;\n mkdir(path: string, mode?: number): void;\n chmod(path: string, mode: number): void;\n analyzePath(path: string): { exists: boolean };\n isDir(mode: number): boolean;\n isFile(mode: number): boolean;\n}\n\nexport interface AtomicNodeFsLogger {\n warn(message: string): void;\n}\n\nexport interface AtomicNodeFsOptions {\n logger?: AtomicNodeFsLogger;\n /**\n * Coalesce `syncToFs` calls into a trailing-edge disk write at most every\n * `flushIntervalMs` milliseconds. PGLite calls `syncToFs` after every\n * non-transactional query (including each BEGIN/INSERT/COMMIT emitted by\n * Kysely), so a synchronous full-tree snapshot per call caps write\n * throughput at one query per snapshot duration. Deferred mode trades\n * crash durability for sustained throughput: a crash loses every write\n * since the last completed snapshot, which is up to `flushIntervalMs` plus\n * the duration of the snapshot in flight (a full-tree write, so it grows\n * with the database). `closeFs` always drains pending writes durably before\n * returning; a process that exits without calling it gets no final flush.\n *\n * Default `0` preserves the original per-call synchronous behavior.\n */\n flushIntervalMs?: number;\n}\n\n/**\n * PGLite Filesystem that holds the working data dir in Emscripten MEMFS and\n * atomically swaps a single-file on-disk snapshot on `syncToFs`. A SIGKILL\n * mid-write leaves the previous snapshot intact, so the next startup loads\n * cleanly rather than aborting on torn WAL.\n *\n * Intended for local dev use — full-tree snapshots are fine at dev volume but\n * won't scale to production write rates. For write-heavy workloads, pass\n * `flushIntervalMs` to coalesce multiple PGLite syncs into one disk write.\n */\nexport class AtomicNodeFs extends MemoryFS {\n private readonly hostDir: string;\n private readonly logger?: AtomicNodeFsLogger;\n private readonly flushIntervalMs: number;\n\n private dirty = false;\n private flushTimer?: ReturnType<typeof setTimeout>;\n private flushInFlight?: Promise<void>;\n\n constructor(\n hostDir: string,\n optionsOrLogger?: AtomicNodeFsOptions | AtomicNodeFsLogger,\n ) {\n super();\n this.hostDir = path.resolve(hostDir);\n const options = normalizeOptions(optionsOrLogger);\n this.logger = options.logger;\n this.flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 0);\n }\n\n async initialSyncFs(): Promise<void> {\n await fs.mkdir(this.hostDir, { recursive: true });\n const snapPath = path.join(this.hostDir, SNAPSHOT_NAME);\n const tmpPath = path.join(this.hostDir, SNAPSHOT_TMP);\n\n // Drop any leftover staging file from a prior crashed write.\n await fs.rm(tmpPath, { force: true });\n\n const memFs = this.pg!.Module.FS as MemFs;\n\n if (await fileExists(snapPath)) {\n const bytes = await fs.readFile(snapPath);\n restoreMemfs(memFs, PGDATA, bytes);\n return;\n }\n\n const legacyMarker = path.join(this.hostDir, \"PG_VERSION\");\n if (await fileExists(legacyMarker)) {\n this.logger?.warn(\n `Migrating legacy PGLite data dir at ${this.hostDir} to atomic snapshot. Original files retained alongside snapshot.bin as a backup; remove them once the new snapshot is verified.`,\n );\n await loadLegacyIntoMemfs(memFs, PGDATA, this.hostDir);\n return;\n }\n }\n\n async syncToFs(relaxedDurability?: boolean): Promise<void> {\n if (this.flushIntervalMs === 0) {\n await this.writeSnapshot(relaxedDurability ?? false);\n return;\n }\n this.dirty = true;\n this.scheduleDeferredFlush(relaxedDurability ?? false);\n }\n\n async closeFs(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = undefined;\n }\n while (this.flushInFlight) {\n await this.flushInFlight;\n }\n this.dirty = false;\n await this.writeSnapshot(false);\n await super.closeFs();\n }\n\n private scheduleDeferredFlush(relaxedDurability: boolean): void {\n if (this.flushTimer || this.flushInFlight) return;\n this.flushTimer = setTimeout(() => {\n this.flushTimer = undefined;\n this.flushInFlight = this.drainDirty(relaxedDurability)\n .catch((err) => {\n this.logger?.warn(\n `AtomicNodeFs deferred flush failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n })\n .finally(() => {\n this.flushInFlight = undefined;\n });\n }, this.flushIntervalMs);\n // Don't keep the event loop alive solely for a pending flush; closeFs is\n // responsible for draining before shutdown.\n this.flushTimer.unref?.();\n }\n\n private async drainDirty(relaxedDurability: boolean): Promise<void> {\n while (this.dirty) {\n this.dirty = false;\n await this.writeSnapshot(relaxedDurability);\n }\n }\n\n private async writeSnapshot(relaxedDurability: boolean): Promise<void> {\n const memFs = this.pg!.Module.FS as MemFs;\n const bytes = serializeMemfs(memFs, PGDATA);\n const snapPath = path.join(this.hostDir, SNAPSHOT_NAME);\n const tmpPath = path.join(this.hostDir, SNAPSHOT_TMP);\n\n const fh = await fs.open(tmpPath, \"w\");\n try {\n await fh.write(bytes);\n if (!relaxedDurability) await fh.sync();\n } finally {\n await fh.close();\n }\n\n await fs.rename(tmpPath, snapPath);\n\n if (!relaxedDurability) {\n try {\n const dirFh = await fs.open(this.hostDir, \"r\");\n try {\n await dirFh.sync();\n } finally {\n await dirFh.close();\n }\n } catch {\n // Some platforms reject fsync on a directory fd. The rename itself is\n // still atomic at the inode level; durability of the directory entry\n // is best-effort.\n }\n }\n }\n}\n\nfunction normalizeOptions(\n optionsOrLogger: AtomicNodeFsOptions | AtomicNodeFsLogger | undefined,\n): AtomicNodeFsOptions {\n if (!optionsOrLogger) return {};\n if (isLogger(optionsOrLogger)) {\n return { logger: optionsOrLogger };\n }\n return optionsOrLogger;\n}\n\nfunction isLogger(\n value: AtomicNodeFsOptions | AtomicNodeFsLogger,\n): value is AtomicNodeFsLogger {\n return (\n \"warn\" in value && typeof (value as AtomicNodeFsLogger).warn === \"function\"\n );\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction ensureDir(FS: MemFs, dir: string): void {\n const parts = dir.split(\"/\").filter(Boolean);\n let cur = \"\";\n for (const p of parts) {\n cur += \"/\" + p;\n if (!FS.analyzePath(cur).exists) FS.mkdir(cur);\n }\n}\n\ninterface Entry {\n type: EntryType;\n mode: number;\n relPath: string;\n data?: Uint8Array;\n}\n\nfunction serializeMemfs(FS: MemFs, root: string): Uint8Array {\n const entries: Entry[] = [];\n\n const walk = (dir: string, rel: string) => {\n const names = FS.readdir(dir);\n for (const name of names) {\n if (name === \".\" || name === \"..\") continue;\n const full = dir + \"/\" + name;\n const r = rel === \"\" ? name : rel + \"/\" + name;\n const stat = FS.stat(full);\n if (FS.isDir(stat.mode)) {\n entries.push({ type: 0, mode: stat.mode & 0o7777, relPath: r });\n walk(full, r);\n } else if (FS.isFile(stat.mode)) {\n const data = FS.readFile(full, { encoding: \"binary\" });\n entries.push({\n type: 1,\n mode: stat.mode & 0o7777,\n relPath: r,\n data,\n });\n }\n // skip symlinks, sockets, etc. — PGLite doesn't create them in PGDATA.\n }\n };\n walk(root, \"\");\n\n const encoder = new TextEncoder();\n const encodedPaths = entries.map((e) => encoder.encode(e.relPath));\n\n let size = 4 + 4 + 4; // magic + version + count\n for (let i = 0; i < entries.length; i++) {\n size += 1 + 4 + 4 + encodedPaths[i].byteLength + 4;\n size += entries[i].data?.byteLength ?? 0;\n }\n\n const out = new Uint8Array(size);\n const view = new DataView(out.buffer);\n let off = 0;\n\n out.set(MAGIC, off);\n off += 4;\n view.setUint32(off, FORMAT_VERSION, true);\n off += 4;\n view.setUint32(off, entries.length, true);\n off += 4;\n\n for (let i = 0; i < entries.length; i++) {\n const e = entries[i];\n const pathBytes = encodedPaths[i];\n view.setUint8(off, e.type);\n off += 1;\n view.setUint32(off, e.mode, true);\n off += 4;\n view.setUint32(off, pathBytes.byteLength, true);\n off += 4;\n out.set(pathBytes, off);\n off += pathBytes.byteLength;\n const dataLen = e.data?.byteLength ?? 0;\n view.setUint32(off, dataLen, true);\n off += 4;\n if (dataLen > 0 && e.data) {\n out.set(e.data, off);\n off += dataLen;\n }\n }\n\n return out;\n}\n\nfunction restoreMemfs(FS: MemFs, root: string, bytes: Uint8Array): void {\n ensureDir(FS, root);\n\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n let off = 0;\n\n for (let i = 0; i < 4; i++) {\n if (bytes[off + i] !== MAGIC[i]) {\n throw new Error(\"AtomicNodeFs: invalid snapshot magic\");\n }\n }\n off += 4;\n\n const version = view.getUint32(off, true);\n off += 4;\n if (version !== FORMAT_VERSION) {\n throw new Error(`AtomicNodeFs: unsupported snapshot version ${version}`);\n }\n const count = view.getUint32(off, true);\n off += 4;\n\n const decoder = new TextDecoder();\n\n for (let i = 0; i < count; i++) {\n const type = view.getUint8(off);\n off += 1;\n const mode = view.getUint32(off, true);\n off += 4;\n const pathLen = view.getUint32(off, true);\n off += 4;\n const relPath = decoder.decode(bytes.subarray(off, off + pathLen));\n off += pathLen;\n const dataLen = view.getUint32(off, true);\n off += 4;\n const full = root + \"/\" + relPath;\n\n if (type === 0) {\n // dirMode: a snapshot written right after a Windows migration can carry\n // execute-less directory modes, which would make it unopenable.\n if (!FS.analyzePath(full).exists) FS.mkdir(full, dirMode(mode));\n else FS.chmod(full, dirMode(mode));\n } else {\n const data = bytes.subarray(off, off + dataLen);\n FS.writeFile(full, data);\n FS.chmod(full, mode);\n }\n off += dataLen;\n }\n}\n\n/**\n * Permission bits for a directory created in MEMFS.\n *\n * Windows has no execute bit, so `fs.stat` reports every directory as 0o40666.\n * Copying that mode verbatim produces a MEMFS directory that cannot be\n * traversed, and every lookup beneath it fails with ENOENT -- which surfaces\n * as PGLite throwing a bare `ErrnoError { errno: 2 }` on the first query after\n * a legacy migration. Force the traverse bit on. MEMFS is process-local and\n * rebuilt from disk on every open, so host permissions carry no meaning here.\n */\nfunction dirMode(mode: number): number {\n return (mode & 0o7777) | 0o111;\n}\n\nasync function loadLegacyIntoMemfs(\n FS: MemFs,\n root: string,\n hostDir: string,\n): Promise<void> {\n ensureDir(FS, root);\n\n const skip = new Set([SNAPSHOT_NAME, SNAPSHOT_TMP, \"postmaster.pid\"]);\n\n const walk = async (diskPath: string, memPath: string) => {\n const ents = await fs.readdir(diskPath, { withFileTypes: true });\n for (const ent of ents) {\n if (skip.has(ent.name)) continue;\n const diskFull = path.join(diskPath, ent.name);\n const memFull = memPath + \"/\" + ent.name;\n const stat = await fs.stat(diskFull);\n if (ent.isDirectory()) {\n if (!FS.analyzePath(memFull).exists) {\n FS.mkdir(memFull, dirMode(stat.mode));\n }\n await walk(diskFull, memFull);\n } else if (ent.isFile()) {\n const data = await fs.readFile(diskFull);\n FS.writeFile(memFull, data);\n FS.chmod(memFull, stat.mode & 0o7777);\n }\n }\n };\n\n await walk(hostDir, root);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/pglite-fs",
3
- "version": "6.2.3-dev.9",
3
+ "version": "6.2.3",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "repository": {
@@ -27,13 +27,13 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@electric-sql/pglite": "0.3.15",
30
- "tsdown": "0.21.1",
31
- "tsx": "4.21.0",
30
+ "tsdown": "0.23.0",
31
+ "tsx": "4.23.13",
32
32
  "vitest": "4.1.1"
33
33
  },
34
34
  "scripts": {
35
35
  "tsc": "tsc",
36
- "lint": "eslint",
36
+ "lint": "cd ../.. && oxlint packages/pglite-fs",
37
37
  "build": "tsdown",
38
38
  "test": "vitest --run"
39
39
  }