UNPKG

convex

Version:

Client for the Convex Cloud

341 lines (340 loc) 9.11 kB
"use strict"; import stdFs from "fs"; import path from "path"; class NodeFs { listDir(dirPath) { return stdFs.readdirSync(dirPath, { withFileTypes: true }); } exists(path2) { try { stdFs.statSync(path2); return true; } catch (e) { if (e.code === "ENOENT") { return false; } throw e; } } stat(path2) { return stdFs.statSync(path2); } readUtf8File(path2) { return stdFs.readFileSync(path2, { encoding: "utf-8" }); } access(path2) { return stdFs.accessSync(path2); } writeUtf8File(path2, contents, mode) { const fd = stdFs.openSync(path2, "w", mode); try { stdFs.writeFileSync(fd, contents, { encoding: "utf-8" }); stdFs.fsyncSync(fd); } finally { stdFs.closeSync(fd); } } mkdir(path2, options) { try { stdFs.mkdirSync(path2); } catch (e) { if (options?.allowExisting && e.code == "EEXIST") { return; } throw e; } } rm(path2, options) { stdFs.rmSync(path2, options); } unlink(path2) { return stdFs.unlinkSync(path2); } registerPath(_path, _st) { } invalidate() { } } export const nodeFs = new NodeFs(); export class RecordingFs { constructor(traceEvents) { this.observedDirectories = /* @__PURE__ */ new Map(); this.observedFiles = /* @__PURE__ */ new Map(); this.invalidated = false; this.traceEvents = traceEvents; } listDir(dirPath) { const absDirPath = path.resolve(dirPath); const dirSt = nodeFs.stat(absDirPath); this.registerNormalized(absDirPath, dirSt); const entries = nodeFs.listDir(dirPath); for (const entry of entries) { const childPath = path.join(absDirPath, entry.name); const childSt = nodeFs.stat(childPath); this.registerPath(childPath, childSt); } const observedNames = new Set(entries.map((e) => e.name)); const existingNames = this.observedDirectories.get(absDirPath); if (existingNames) { if (!setsEqual(observedNames, existingNames)) { this.invalidated = true; } } this.observedDirectories.set(absDirPath, observedNames); return entries; } exists(path2) { try { const st = nodeFs.stat(path2); this.registerPath(path2, st); return true; } catch (err) { if (err.code === "ENOENT") { this.registerPath(path2, null); return false; } throw err; } } stat(path2) { try { const st = nodeFs.stat(path2); this.registerPath(path2, st); return st; } catch (err) { if (err.code === "ENOENT") { this.registerPath(path2, null); } throw err; } } readUtf8File(path2) { try { const st = nodeFs.stat(path2); this.registerPath(path2, st); return nodeFs.readUtf8File(path2); } catch (err) { if (err.code === "ENOENT") { this.registerPath(path2, null); } throw err; } } access(path2) { try { const st = nodeFs.stat(path2); this.registerPath(path2, st); return nodeFs.access(path2); } catch (err) { if (err.code === "ENOENT") { this.registerPath(path2, null); } throw err; } } writeUtf8File(filePath, contents, mode) { const absPath = path.resolve(filePath); nodeFs.writeUtf8File(filePath, contents, mode); const newSt = nodeFs.stat(absPath); this.observedFiles.set(absPath, newSt); const parentPath = path.resolve(path.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.add(path.basename(absPath)); } } mkdir(dirPath, options) { const absPath = path.resolve(dirPath); try { stdFs.mkdirSync(absPath); } catch (e) { if (options?.allowExisting && e.code === "EEXIST") { const st = nodeFs.stat(absPath); this.registerNormalized(absPath, st); return; } throw e; } const newSt = nodeFs.stat(absPath); this.observedFiles.set(absPath, newSt); const parentPath = path.resolve(path.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.add(path.basename(absPath)); } } rm(entityPath, options) { const absPath = path.resolve(entityPath); if (options?.recursive && this.exists(absPath) && this.stat(absPath).isDirectory()) { const entries = this.listDir(entityPath); for (const entry of entries) { this.rm(path.join(absPath, entry.name), options); } } stdFs.rmSync(absPath, options); this.observedFiles.set(absPath, null); const parentPath = path.resolve(path.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.delete(path.basename(absPath)); } } unlink(filePath) { const absPath = path.resolve(filePath); stdFs.unlinkSync(absPath); this.observedFiles.set(absPath, null); const parentPath = path.resolve(path.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.delete(path.basename(absPath)); } } registerPath(p, st) { const absPath = path.resolve(p); this.registerNormalized(absPath, st); } invalidate() { this.invalidated = true; } registerNormalized(p, observed) { const existing = this.observedFiles.get(p); if (existing !== void 0) { const stMatch = stMatches(observed, existing); if (!stMatch.matches) { if (this.traceEvents) { console.log( "Invalidating due to st mismatch", p, observed, existing, stMatch.reason ); } this.invalidated = true; } } this.observedFiles.set(p, observed); } finalize() { if (this.invalidated) { return "invalidated"; } return new Observations(this.observedDirectories, this.observedFiles); } } export class Observations { constructor(directories, files) { this.directories = directories; this.files = files; } paths() { const out = []; for (const path2 of this.directories.keys()) { out.push(path2); } for (const path2 of this.files.keys()) { out.push(path2); } return out; } overlaps({ absPath }) { let currentSt; try { currentSt = nodeFs.stat(absPath); } catch (e) { if (e.code === "ENOENT") { currentSt = null; } else { throw e; } } const observedSt = this.files.get(absPath); if (observedSt !== void 0) { const stMatch = stMatches(observedSt, currentSt); if (!stMatch.matches) { const reason = `modified (${stMatch.reason})`; return { overlaps: true, reason }; } } const parentPath = path.resolve(path.dirname(absPath)); const observedParent = this.directories.get(parentPath); if (observedParent !== void 0) { const filename = path.basename(absPath); if (currentSt === null && observedParent.has(filename)) { return { overlaps: true, reason: "deleted" }; } if (currentSt !== null && !observedParent.has(filename)) { return { overlaps: true, reason: "added" }; } } return { overlaps: false }; } } function setsEqual(a, b) { if (a.size !== b.size) { return false; } for (const elem of a.keys()) { if (!b.has(elem)) { return false; } } return true; } export function stMatches(a, b) { if (a === null && b === null) { return { matches: true }; } if (a !== null && b !== null) { if (a.dev !== b.dev) { return { matches: false, reason: "device boundary" }; } if (a.isFile() || b.isFile()) { if (!a.isFile() || !b.isFile()) { return { matches: false, reason: "file type" }; } if (a.ino !== b.ino) { return { matches: false, reason: `file inode (${a.ino} vs. ${b.ino})` }; } if (a.size !== b.size) { return { matches: false, reason: `file size (${a.size} vs. ${b.size})` }; } if (a.mtimeMs !== b.mtimeMs) { return { matches: false, reason: `file mtime (${a.mtimeMs} vs. ${b.mtimeMs})` }; } return { matches: true }; } if (a.isDirectory() || b.isDirectory()) { if (!b.isDirectory() || !b.isDirectory()) { return { matches: false, reason: "dir file type" }; } if (a.ino !== b.ino) { return { matches: false, reason: `dir inode (${a.ino} vs. ${b.ino})` }; } return { matches: true }; } if (a.ino !== b.ino) { return { matches: false, reason: `special inode (${a.ino} vs. ${b.ino})` }; } return { matches: true }; } return { matches: false, reason: "deleted mismatch" }; } //# sourceMappingURL=fs.js.map