@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 +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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 {
|
|
50
|
+
export type { AtomicNodeFsLogger };
|
|
51
|
+
export {};
|
|
50
52
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/atomic-node-fs.ts"],"mappings":"
|
|
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
|
-
|
|
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)
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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.
|
|
31
|
-
"tsx": "4.
|
|
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": "
|
|
36
|
+
"lint": "cd ../.. && oxlint packages/pglite-fs",
|
|
37
37
|
"build": "tsdown",
|
|
38
38
|
"test": "vitest --run"
|
|
39
39
|
}
|