Platform-agnostic file storage abstraction with in-memory and Node.js implementations.
Without @auto-engineer/file-store, you would have to write platform-specific file operations, handle path normalization across operating systems, and maintain separate test doubles for every component that touches the filesystem.
This package defines a single IFileStore interface that both InMemoryFileStore and NodeFileStore implement. Code that depends on IFileStore can run against the in-memory store in tests and the Node store in production with no changes. All paths are normalized to POSIX format, and all reads return null for missing files instead of throwing.
- Binary-first API -- Core operations use
Uint8Arrayfor cross-platform compatibility. The Node store adds text convenience methods viaIExtendedFileStore. - Null over exceptions --
read()andreadText()returnnullfor missing files rather than throwing. - POSIX normalization -- All paths use forward slashes, even on Windows.
- Auto directory creation --
write()andwriteText()create parent directories automatically. - Two entry points -- The main entry point (
".") is platform-agnostic; the"./node"entry point requires Node.js built-ins.
pnpm add @auto-engineer/file-storeimport { InMemoryFileStore } from '@auto-engineer/file-store';
const store = new InMemoryFileStore();
await store.write('/data/file.txt', new TextEncoder().encode('content'));
const data = await store.read('/data/file.txt');
console.log(new TextDecoder().decode(data!));
// → "content"import { InMemoryFileStore } from '@auto-engineer/file-store';
const store = new InMemoryFileStore();
await store.write('/input.txt', new TextEncoder().encode('data'));
const exists = await store.exists('/input.txt');
const tree = await store.listTree('/');import { NodeFileStore } from '@auto-engineer/file-store/node';
const store = new NodeFileStore();
await store.writeText('config.json', JSON.stringify({ key: 'value' }));
const text = await store.readText('config.json');import { NodeFileStore } from '@auto-engineer/file-store/node';
const store = new NodeFileStore();
const tree = await store.listTree('/project', {
pruneDirRegex: /node_modules|\.git/,
includeSizes: true,
});
for (const entry of tree) {
console.log(`${entry.type} ${entry.path} (${entry.size} bytes)`);
}Accept IFileStore in your constructors so callers can swap implementations:
import type { IFileStore } from '@auto-engineer/file-store';
class DocumentManager {
constructor(private store: IFileStore) {}
async save(id: string, content: string): Promise<void> {
await this.store.write(`/docs/${id}.json`, new TextEncoder().encode(content));
}
}| Entry Point | Import Path | Exports |
|---|---|---|
Main (.) |
@auto-engineer/file-store |
InMemoryFileStore, IFileStore, IExtendedFileStore |
Node (./node) |
@auto-engineer/file-store/node |
NodeFileStore, IFileStore, IExtendedFileStore |
// Platform-agnostic (no Node.js dependency)
import { InMemoryFileStore, type IFileStore, type IExtendedFileStore } from '@auto-engineer/file-store';
// Node.js only
import { NodeFileStore } from '@auto-engineer/file-store/node';The base interface implemented by both stores.
| Method | Signature | Description |
|---|---|---|
write |
(path: string, data: Uint8Array) => Promise<void> |
Write binary data. Creates parent directories. |
read |
(path: string) => Promise<Uint8Array | null> |
Read binary data. Returns null if missing. |
exists |
(path: string) => Promise<boolean> |
Check whether a file or directory exists. |
listTree |
(root?: string, opts?: ListTreeOpts) => Promise<TreeEntry[]> |
Recursively list files and directories. |
remove |
(path: string) => Promise<void> |
Delete a file. |
| Option | Type | Default | Description |
|---|---|---|---|
followSymlinkDirs |
boolean |
true |
Traverse symlinked directories |
includeSizes |
boolean |
true |
Include file sizes in results |
pruneDirRegex |
RegExp |
-- | Skip directories whose path matches |
Array<{ path: string; type: 'file' | 'dir'; size: number }>Extends IFileStore with convenience methods. Implemented by NodeFileStore.
| Method | Signature | Description |
|---|---|---|
ensureDir |
(path: string) => Promise<void> |
Create a directory recursively (like mkdir -p). |
readdir |
(path: string) => Promise<Array<{ name: string; type: 'file' | 'dir' }>> |
List immediate children of a directory. |
readText |
(path: string) => Promise<string | null> |
Read a file as UTF-8 text. Returns null if missing. |
writeText |
(path: string, text: string) => Promise<void> |
Write a UTF-8 text file. Creates parent directories. |
join |
(...parts: string[]) => string |
Join path segments, returning a POSIX path. Handles file:// URLs. |
dirname |
(p: string) => string |
Return the directory portion of a path as a POSIX path. |
fromHere |
(relative: string, base?: string) => string |
Resolve a relative path from a base directory (defaults to __dirname). |
Implements IFileStore. Stores files in a Map<string, Uint8Array>. Paths are normalized to start with /. The remove method deletes the path and any files beneath it (prefix match), so it works for both files and directories.
Implements IExtendedFileStore. Delegates to node:fs/promises. Paths can be absolute, relative (resolved against process.cwd()), or file:// URLs. All returned paths use POSIX separators.
src/
├── index.ts # Main entry: exports InMemoryFileStore + types
├── node.ts # Node entry: exports NodeFileStore + types
├── types.ts # IFileStore and IExtendedFileStore interfaces
├── path.ts # toPosix() helper
├── InMemoryFileStore.ts # In-memory implementation (Map-backed)
└── NodeFileStore.ts # Node.js fs implementation
classDiagram
class IFileStore {
<<interface>>
+write(path, data) Promise~void~
+read(path) Promise~Uint8Array | null~
+exists(path) Promise~boolean~
+listTree(root, opts) Promise~TreeEntry[]~
+remove(path) Promise~void~
}
class IExtendedFileStore {
<<interface>>
+ensureDir(path) Promise~void~
+readdir(path) Promise~DirEntry[]~
+readText(path) Promise~string | null~
+writeText(path, text) Promise~void~
+join(parts) string
+dirname(p) string
+fromHere(relative, base) string
}
IExtendedFileStore --|> IFileStore
InMemoryFileStore ..|> IFileStore
NodeFileStore ..|> IExtendedFileStore
class InMemoryFileStore {
-files Map~string, Uint8Array~
}
class NodeFileStore {
}
This package has zero external dependencies. The Node entry point uses only Node.js built-in modules (node:fs/promises, node:path, node:url).