Layer

The Layer class is the primary interface for interacting with the virtual filesystem. It provides POSIX-like methods for reading, writing, and managing files, with all mutations captured in an in-memory overlay.

Import

A Layer is created by calling createLayer:

import { createLayer } from "@catmint-fs/core";

const layer = createLayer({ root: "/path/to/project" });

Reading Methods

readFile

Read the entire contents of a file.

readFile(path: string): Promise<Uint8Array>
ParameterTypeDescription
pathstringPath to the file (relative to root)

Returns the file contents as a Uint8Array. Throws if the file does not exist.

const data = await layer.readFile("/src/index.ts");
const text = new TextDecoder().decode(data);

createReadStream

Create a readable stream for a file.

createReadStream(path: string): ReadableStream<Uint8Array>
ParameterTypeDescription
pathstringPath to the file (relative to root)

Returns a Web ReadableStream<Uint8Array>. Useful for large files that should not be loaded entirely into memory.

const stream = await layer.createReadStream("/large-file.bin");
const reader = stream.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(value);
}

readdir

List the contents of a directory.

readdir(path: string): Promise<DirentEntry[]>
ParameterTypeDescription
pathstringPath to the directory (relative to root)

Returns an array of DirentEntry objects, each with name and type fields.

const entries = await layer.readdir("/src");
for (const entry of entries) {
  console.log(entry.name, entry.type); // "index.ts" "file"
}

stat

Get metadata about a file, directory, or symlink target. Follows symlinks.

stat(path: string): Promise<StatResult>
ParameterTypeDescription
pathstringPath to stat (relative to root)
const info = await layer.stat("/package.json");
console.log(info.type); // "file"
console.log(info.size); // 1234

lstat

Get metadata about a file, directory, or symlink. Does not follow symlinks.

lstat(path: string): Promise<StatResult>
ParameterTypeDescription
pathstringPath to stat (relative to root)
const info = await layer.lstat("/my-link");
console.log(info.type); // "symlink"

readlink

Read the target path of a symbolic link.

readlink(path: string): Promise<string>
ParameterTypeDescription
pathstringPath to the symlink (relative to root)
const target = await layer.readlink("/my-link");
console.log(target); // "/data/file.txt"

exists

Check whether a path exists.

exists(path: string): Promise<boolean>
ParameterTypeDescription
pathstringPath to check (relative to root)
const fileExists = await layer.exists("/README.md");

Writing Methods

writeFile

Write data to a file. Creates the file if it does not exist, or overwrites it if it does.

writeFile(path: string, data: Uint8Array, options?: WriteOptions): Promise<void>
ParameterTypeRequiredDescription
pathstringYesPath to the file (relative to root)
dataUint8ArrayYesFile contents
optionsWriteOptionsNoWrite options (e.g., mode)
const encoder = new TextEncoder();
await layer.writeFile("/src/app.ts", encoder.encode("export default {};"));

// With permissions
await layer.writeFile("/script.sh", encoder.encode("#!/bin/sh\necho hi"), {
  mode: 0o755,
});

mkdir

Create a directory.

mkdir(path: string, options?: MkdirOptions): Promise<void>
ParameterTypeRequiredDescription
pathstringYesPath to the directory (relative to root)
optionsMkdirOptionsNoOptions (e.g., { recursive: true })
await layer.mkdir("/src/utils");

// Create nested directories
await layer.mkdir("/src/components/ui/buttons", { recursive: true });

rm

Remove a file or directory.

rm(path: string, options?: RmOptions): Promise<void>
ParameterTypeRequiredDescription
pathstringYesPath to remove (relative to root)
optionsRmOptionsNoOptions (e.g., { recursive: true })
await layer.rm("/old-file.ts");

// Remove directory and all contents
await layer.rm("/deprecated", { recursive: true });

rmdir

Remove an empty directory.

rmdir(path: string): Promise<void>
ParameterTypeDescription
pathstringPath to the empty directory (relative to root)
await layer.rmdir("/empty-dir");

rename

Rename or move a file or directory.

rename(from: string, to: string): Promise<void>
ParameterTypeDescription
fromstringCurrent path (relative to root)
tostringNew path (relative to root)
await layer.rename("/src/old-name.ts", "/src/new-name.ts");

symlink

Create a symbolic link.

symlink(target: string, path: string): Promise<void>
ParameterTypeDescription
targetstringThe path the symlink points to
pathstringThe path where the symlink is created
await layer.symlink("/data/config.json", "/app/config.json");

Permission Methods

chmod

Change file permissions.

chmod(path: string, mode: number): Promise<void>
ParameterTypeDescription
pathstringPath to the file (relative to root)
modenumberPermission mode (e.g., 0o755)
await layer.chmod("/script.sh", 0o755);

chown

Change file ownership. Follows symlinks.

chown(path: string, uid: number, gid: number): Promise<void>
ParameterTypeDescription
pathstringPath to the file (relative to root)
uidnumberUser ID
gidnumberGroup ID
await layer.chown("/data/file.txt", 1000, 1000);

lchown

Change ownership of a symlink itself (does not follow the link).

lchown(path: string, uid: number, gid: number): Promise<void>
ParameterTypeDescription
pathstringPath to the symlink (relative to root)
uidnumberUser ID
gidnumberGroup ID
await layer.lchown("/my-link", 1000, 1000);

getOwner

Get the owner of a file.

getOwner(path: string): Promise<{ uid: number; gid: number }>
ParameterTypeDescription
pathstringPath to the file (relative to root)
const owner = await layer.getOwner("/data/file.txt");
console.log(owner.uid, owner.gid);

Change Tracking Methods

getChanges

Get all pending changes.

getChanges(): ChangeEntry[]

Returns an array of ChangeEntry objects describing all mutations since the last apply() or reset().

const changes = layer.getChanges();
for (const change of changes) {
  console.log(change.type, change.path, change.entryType);
}

getChangeDetail

Get detailed information about a specific pending change.

getChangeDetail(path: string): ChangeDetail | null
ParameterTypeDescription
pathstringPath to query (relative to root)

Returns a ChangeDetail object if the path has a pending change, or null if it does not.

const detail = layer.getChangeDetail("/src/new-file.ts");
if (detail) {
  console.log(detail.type, detail.entryType);
}

Lifecycle Methods

apply

Apply all pending changes to the backing filesystem.

apply(options?: { transaction?: boolean }): Promise<ApplyResult>
ParameterTypeRequiredDescription
options.transactionbooleanNoEnable transactional mode (default: false)

In best-effort mode (default), returns an ApplyResult with the count of applied changes and any errors. In transactional mode, throws a TransactionError if any change fails.

// Best-effort
const result = await layer.apply();
console.log(result.applied, result.errors);

// Transactional
await layer.apply({ transaction: true });

reset

Discard all pending changes.

reset(): void
layer.reset();

dispose

Release all resources held by the layer.

dispose(): void
layer.dispose();

See Also