2.8 KiB
fs-helpers
(no submodules)
Purpose
Isolates the node:fs surface the sink touches behind an injectable
FsAdapter interface and concrete nodeFsAdapter implementation. The
indirection lets tests run the sink against a real filesystem under a
temp directory (the default for this package's test suite) while
keeping a clean seam for in-memory adapters used by external consumers.
The module also exports the rotated-file enumeration helper used by the retention module — keeping the file-name conventions next to the filesystem primitives means rotation, retention, and sink all share the same view of "what counts as a rotated sibling of this base path".
Features
FsAdapter— six-method surface:appendLine,fileSize,rename,unlink,readdir,ensureWriteableDir.nodeFsAdapter— production adapter againstnode:fs/promises.listRotatedFiles(fs, basePath)— returns full paths of every rotated sibling, sorted oldest-first by embedded timestamp.rotatedFilePathAt(basePath, iso)— builds the rotated path for the supplied ISO timestamp;:is replaced with-so the name is Windows-safe and grep-friendly.
Usage: Basic
import { nodeFsAdapter } from "@suaveplan/api-audit-file/fs-helpers";
await nodeFsAdapter.ensureWriteableDir("/var/log/suaveplan");
await nodeFsAdapter.appendLine("/var/log/suaveplan/audit.log", "...\n", false);
Usage: Advanced
Provide an injectable adapter to drive the sink against an in-memory filesystem:
import type { FsAdapter } from "@suaveplan/api-audit-file/fs-helpers";
const inMemory = new Map<string, string>();
const adapter: FsAdapter = {
async appendLine(p, line) {
inMemory.set(p, (inMemory.get(p) ?? "") + line);
},
async fileSize(p) {
return Buffer.byteLength(inMemory.get(p) ?? "", "utf8");
},
async rename(from, to) {
inMemory.set(to, inMemory.get(from) ?? "");
inMemory.delete(from);
},
async unlink(p) {
inMemory.delete(p);
},
async readdir() {
return [...inMemory.keys()];
},
async ensureWriteableDir() {
/* always writeable */
},
};
API Reference
interface FsAdapter— see Features above.const nodeFsAdapter: FsAdapterlistRotatedFiles(fs: FsAdapter, basePath: string): Promise<string[]>rotatedFilePathAt(basePath: string, isoTimestamp: string): string
Implementation Notes
ensureWriteableDir calls mkdir(..., { recursive: true }) then a
W_OK access probe so a fresh boot creates the directory and a
borked permission setup throws PATH_NOT_WRITEABLE synchronously
during sink construction. fileSize swallows ENOENT and returns 0
so the rotation logic naturally treats a not-yet-created file as
empty — that simplifies the open-on-first-write code path.