/** * Internal file-IO helpers for @peac/cli command handlers. * * Package-private; not exported from any package barrel and not part of * the public CLI API. Mirrors the syscall-style conventions already in * use in `output-preflight.ts`: named imports from `node:fs`, * fd-bound reads, errno discrimination via `NodeJS.ErrnoException`, and * `closeSync` always wrapped in `try`/`finally` with secondary errors * swallowed so they do not mask the primary error. * * The helpers exist to remove TOCTOU patterns where an existence probe * (`existsSync` / `accessSync` / `statSync`) is followed by a file * operation (`readFileSync` / `writeFileSync` / `openSync` / `unlinkSync`) * on the same path. The atomic alternative is a single syscall that * discriminates outcome via errno: success means the operation worked; * `ENOENT` means the file is missing; `EACCES` means the file is * unreadable / unwritable; `EISDIR` / `ENOTDIR` means the path is the * wrong type. * * Each helper takes a path string. Internal use of file descriptors is * hidden from the caller. Callers receive plain return values on success * and `NodeJS.ErrnoException`-shaped errors on failure that they can * inspect via `e.code === 'ENOENT' | 'EACCES' | 'EEXIST' | 'EISDIR' | * 'ENOTDIR'` and translate into user-facing wording. */ /** * Read the entire file at `path` as a UTF-8 string via a single * fd-bound `open`/`fstat`/`read`/`close` sequence. * * Throws an `ErrnoException`-shaped error on failure. Callers * discriminate via `e.code`: * - `ENOENT`: file does not exist * - `EISDIR`: path resolves to a directory * - `EACCES`: not readable */ export declare function readFileUtf8Snapshot(path: string): string; /** * Read the entire file at `path` as a `Buffer` via a single fd-bound * `open`/`fstat`/`read`/`close` sequence. Optionally enforces a maximum * byte size. The size limit is checked twice: once against the `fstat` * size before reading (cheap fast-fail) and once against the actual * read length (defends against the file growing between `fstat` and the * subsequent `readFileSync`). * * Throws on failure: * - `ENOENT`: file does not exist * - `EISDIR`: path resolves to a directory * - `EACCES`: not readable * - `E_PEAC_FILE_TOO_LARGE`: file exceeds `options.maxBytes` */ export declare function readFileBufferSnapshot(path: string, options?: { maxBytes?: number; }): Buffer; /** * Write `data` to `path` atomically without overwriting an existing * file. Uses POSIX `O_CREAT | O_EXCL | O_WRONLY` semantics via Node's * `'wx'` flag. The path either gets created with the supplied content * or the call throws `EEXIST`. * * Callers translate `EEXIST` into user-facing wording (e.g. "use --force * to overwrite"). */ export declare function writeFileNoOverwrite(path: string, data: string | Buffer, options?: { encoding?: BufferEncoding; mode?: number; }): void; /** * Rewrite a JSON config file atomically. If the file does not exist * (ENOENT), returns silently: there is no config to rewrite. Otherwise * the existing content is parsed, `mutate(config)` is invoked, and the * mutated config is written via a sibling temp file plus * `renameSync`. The final target path is never partially written. * * Existing file permissions on the target are preserved across the * rename. If the existing mode cannot be observed, the temp file is * created with a restrictive `0o600` fallback so a config rewrite never * accidentally widens the permission set. * * Cleans up the temp file if rename fails. Read failures other than * `ENOENT` propagate to the caller. */ export declare function rewriteJsonFileAtomic(path: string, mutate: (config: Record) => void): void; /** * Remove the file at `path` if it exists. Swallows `ENOENT`. All * other errors propagate to the caller. */ export declare function unlinkIfExists(path: string): void; //# sourceMappingURL=safe-file.d.ts.map