UNPKG

convex

Version:

Client for the Convex Cloud

475 lines (474 loc) 14.5 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); var fs_exports = {}; __export(fs_exports, { Observations: () => Observations, RecordingFs: () => RecordingFs, nodeFs: () => nodeFs, stMatches: () => stMatches, withTmpDir: () => withTmpDir }); module.exports = __toCommonJS(fs_exports); var import_chalk = __toESM(require("chalk"), 1); var import_fs = __toESM(require("fs"), 1); var fsPromises = __toESM(require("fs/promises"), 1); var import_os = __toESM(require("os"), 1); var import_path = __toESM(require("path"), 1); var import_crypto = __toESM(require("crypto"), 1); const tmpDirOverrideVar = "CONVEX_TMPDIR"; function tmpDirPath() { const envTmpDir = process.env[tmpDirOverrideVar]; return envTmpDir ?? import_os.default.tmpdir(); } const tmpDirRoot = tmpDirPath(); let warned = false; function warnCrossFilesystem(dstPath) { const dstDir = import_path.default.dirname(dstPath); if (!warned) { console.warn( import_chalk.default.yellow( `Temporary directory '${tmpDirRoot}' and project directory '${dstDir}' are on different filesystems.` ) ); console.warn( import_chalk.default.gray( ` If you're running into errors with other tools watching the project directory, override the temporary directory location with the ${import_chalk.default.bold( tmpDirOverrideVar )} environment variable.` ) ); console.warn( import_chalk.default.gray( ` Be sure to pick a temporary directory that's on the same filesystem as your project.` ) ); warned = true; } } async function withTmpDir(callback) { const tmpPath = import_fs.default.mkdtempSync(import_path.default.join(tmpDirRoot, "convex")); const tmpDir = { writeUtf8File(contents) { const filePath = import_path.default.join(tmpPath, import_crypto.default.randomUUID()); nodeFs.writeUtf8File(filePath, contents); return filePath; } }; try { await callback(tmpDir); } finally { import_fs.default.rmSync(tmpPath, { force: true, recursive: true }); } } class NodeFs { listDir(dirPath) { return import_fs.default.readdirSync(dirPath, { withFileTypes: true }); } exists(path2) { try { import_fs.default.statSync(path2); return true; } catch (e) { if (e.code === "ENOENT") { return false; } throw e; } } stat(path2) { return import_fs.default.statSync(path2); } readUtf8File(path2) { return import_fs.default.readFileSync(path2, { encoding: "utf-8" }); } createReadStream(path2, options) { return import_fs.default.createReadStream(path2, options); } // To avoid issues with filesystem events triggering for our own streamed file // writes, writeFileStream is intentionally not on the Filesystem interface // and not implemented by RecordingFs. async writeFileStream(path2, stream) { const fileHandle = await fsPromises.open(path2, "wx", 420); try { for await (const chunk of stream) { await fileHandle.write(chunk); } } finally { await fileHandle.close(); } } access(path2) { return import_fs.default.accessSync(path2); } writeUtf8File(path2, contents, mode) { const fd = import_fs.default.openSync(path2, "w", mode); try { import_fs.default.writeFileSync(fd, contents, { encoding: "utf-8" }); import_fs.default.fsyncSync(fd); } finally { import_fs.default.closeSync(fd); } } mkdir(dirPath, options) { try { import_fs.default.mkdirSync(dirPath, { recursive: options?.recursive }); } catch (e) { if (options?.allowExisting && e.code === "EEXIST") { return; } throw e; } } rmdir(path2) { import_fs.default.rmdirSync(path2); } unlink(path2) { return import_fs.default.unlinkSync(path2); } swapTmpFile(fromPath, toPath) { try { return import_fs.default.renameSync(fromPath, toPath); } catch (e) { if (e.code === "EXDEV") { warnCrossFilesystem(toPath); import_fs.default.copyFileSync(fromPath, toPath); return; } throw e; } } registerPath(_path, _st) { } invalidate() { } } const nodeFs = new NodeFs(); class RecordingFs { constructor(traceEvents) { // Absolute path -> Set of observed child names __publicField(this, "observedDirectories", /* @__PURE__ */ new Map()); // Absolute path -> observed stat (or null if observed nonexistent) __publicField(this, "observedFiles", /* @__PURE__ */ new Map()); // Have we noticed that files have changed while recording? __publicField(this, "invalidated", false); __publicField(this, "traceEvents"); this.traceEvents = traceEvents; } listDir(dirPath) { const absDirPath = import_path.default.resolve(dirPath); const dirSt = nodeFs.stat(absDirPath); this.registerNormalized(absDirPath, dirSt); const entries = nodeFs.listDir(dirPath); for (const entry of entries) { const childPath = import_path.default.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)) { if (this.traceEvents) { console.log( "Invalidating due to directory children mismatch", 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; } } createReadStream(path2, options) { try { const st = nodeFs.stat(path2); this.registerPath(path2, st); return nodeFs.createReadStream(path2, options); } 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 = import_path.default.resolve(filePath); nodeFs.writeUtf8File(filePath, contents, mode); this.updateOnWrite(absPath); } mkdir(dirPath, options) { const absPath = import_path.default.resolve(dirPath); try { import_fs.default.mkdirSync(absPath, { recursive: options?.recursive }); } catch (e) { if (options?.allowExisting && e.code === "EEXIST") { const st = nodeFs.stat(absPath); this.registerNormalized(absPath, st); return; } throw e; } this.updateOnWrite(absPath); } rmdir(dirPath) { const absPath = import_path.default.resolve(dirPath); import_fs.default.rmdirSync(absPath); this.updateOnDelete(absPath); } unlink(filePath) { const absPath = import_path.default.resolve(filePath); import_fs.default.unlinkSync(absPath); this.updateOnDelete(absPath); } swapTmpFile(fromPath, toPath) { const absToPath = import_path.default.resolve(toPath); nodeFs.swapTmpFile(fromPath, absToPath); this.updateOnWrite(absToPath); } updateOnWrite(absPath) { const newSt = nodeFs.stat(absPath); this.observedFiles.set(absPath, newSt); const parentPath = import_path.default.resolve(import_path.default.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.add(import_path.default.basename(absPath)); } } updateOnDelete(absPath) { this.observedFiles.set(absPath, null); const parentPath = import_path.default.resolve(import_path.default.dirname(absPath)); const observedParent = this.observedDirectories.get(parentPath); if (observedParent !== void 0) { observedParent.delete(import_path.default.basename(absPath)); } } registerPath(p, st) { const absPath = import_path.default.resolve(p); this.registerNormalized(absPath, st); } invalidate() { this.invalidated = true; } registerNormalized(absPath, observed) { const existing = this.observedFiles.get(absPath); if (existing !== void 0) { const stMatch = stMatches(observed, existing); if (!stMatch.matches) { if (this.traceEvents) { console.log( "Invalidating due to st mismatch", absPath, observed, existing, stMatch.reason ); } this.invalidated = true; } } this.observedFiles.set(absPath, observed); } finalize() { if (this.invalidated) { return "invalidated"; } return new Observations(this.observedDirectories, this.observedFiles); } } class Observations { constructor(directories, files) { __publicField(this, "directories"); __publicField(this, "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 = import_path.default.resolve(import_path.default.dirname(absPath)); const observedParent = this.directories.get(parentPath); if (observedParent !== void 0) { const filename = import_path.default.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; } 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