CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yarnpkg--fslib

A TypeScript library abstracting the Node filesystem APIs with virtual filesystems and cross-platform path handling

Overview
Eval results
Files

filesystem-implementations.mddocs/

Filesystem Implementations

Concrete filesystem implementations that provide different behaviors and capabilities built on the @yarnpkg/fslib abstractions.

Overview

The package provides several filesystem implementations for different use cases:

  • NodeFS: Direct access to the real filesystem
  • VirtualFS: In-memory filesystem for testing and isolation
  • JailFS: Security-restricted filesystem
  • CwdFS: Working directory-relative filesystem
  • MountFS: Multi-filesystem composition
  • ProxiedFS: Base for filesystem decorators
  • Specialized implementations: AliasFS, LazyFS, PosixFS, NoFS

Real Filesystem Access

NodeFS

Direct interface to the Node.js filesystem with portable path support.

import { NodeFS, type PortablePath } from '@yarnpkg/fslib';

// Create a NodeFS instance
const nodeFs = new NodeFS();

// Standard filesystem operations
const content = await nodeFs.readFilePromise('/path/to/file.txt' as PortablePath, 'utf8');
await nodeFs.writeFilePromise('/path/to/output.txt' as PortablePath, content);

// Directory operations
await nodeFs.mkdirPromise('/new/directory' as PortablePath, { recursive: true });
const files = await nodeFs.readdirPromise('/directory' as PortablePath);

// Metadata operations
const stats = await nodeFs.statPromise('/file.txt' as PortablePath);
console.log(`File size: ${stats.size}, Modified: ${stats.mtime}`);

NodePathFS

Compatibility wrapper that adds support for file URLs and Buffers to any filesystem. Primarily used internally for Node.js compatibility.

import { NodePathFS, NodeFS, type NativePath } from '@yarnpkg/fslib';

/**
 * Adds support for file URLs and Buffers to the wrapped baseFs
 * Only exists for compatibility with Node's behavior - avoid direct use
 */
class NodePathFS extends ProxiedFS<NativePath, NativePath> {
  constructor(baseFs: FakeFS<NativePath>);
}

// Usage (typically internal)
const nodeFs = new NodeFS();
const pathFs = new NodePathFS(nodeFs);

// Now supports file URLs and Buffers (compatibility only)
const fileUrl = new URL('file:///path/to/file.txt');
const content = await pathFs.readFilePromise(fileUrl, 'utf8');

// Also supports Buffer paths (compatibility only)
const bufferPath = Buffer.from('/path/to/file.txt');
const data = await pathFs.readFilePromise(bufferPath, 'utf8');

Virtual and Memory-Based Filesystems

VirtualFS

In-memory filesystem implementation for testing and isolated operations.

import { VirtualFS, ppath, type PortablePath } from '@yarnpkg/fslib';

// Create a virtual filesystem
const vfs = new VirtualFS();

// Create directory structure
await vfs.mkdirPromise('/project' as PortablePath, { recursive: true });
await vfs.mkdirPromise('/project/src' as PortablePath);

// Write virtual files
await vfs.writeFilePromise('/project/package.json' as PortablePath, JSON.stringify({
  name: 'my-project',
  version: '1.0.0'
}, null, 2));

await vfs.writeFilePromise('/project/src/index.ts' as PortablePath, 'console.log("Hello World");');

// Read from virtual filesystem
const packageContent = await vfs.readFilePromise('/project/package.json' as PortablePath, 'utf8');
const packageData = JSON.parse(packageContent);
console.log(`Project: ${packageData.name} v${packageData.version}`);

// List virtual directory contents
const sourceFiles = await vfs.readdirPromise('/project/src' as PortablePath);
console.log('Source files:', sourceFiles);

Security and Isolation Filesystems

JailFS

Filesystem that restricts access to within a specific directory tree.

import { JailFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';

// Create a jailed filesystem
const baseFs = new NodeFS();
const jailPath = '/safe/sandbox' as PortablePath;
const jailedFs = new JailFS(jailPath, { baseFs });

// All operations are restricted to the jail directory
await jailedFs.writeFilePromise('/file.txt' as PortablePath, 'content');
// Actually writes to /safe/sandbox/file.txt

await jailedFs.mkdirPromise('/subdir' as PortablePath);
// Actually creates /safe/sandbox/subdir

// Attempts to access outside the jail will fail
try {
  await jailedFs.readFilePromise('/../../../etc/passwd' as PortablePath);
  // This will throw an error or be blocked
} catch (error) {
  console.log('Access denied outside jail');
}

// Safe operations within the jail
const files = await jailedFs.readdirPromise('/' as PortablePath);
console.log('Files in jail:', files);

JailFS Options

import { JailFS, type PortablePath } from '@yarnpkg/fslib';

interface JailFSOptions {
  baseFs?: FakeFS<PortablePath>;
}

// Custom configuration
const jailedFs = new JailFS('/restricted/area' as PortablePath, {
  baseFs: customFileSystem
});

Working Directory Filesystems

CwdFS

Filesystem that operates relative to a specific working directory.

import { CwdFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';

// Create a filesystem rooted at a specific directory
const baseFs = new NodeFS();
const projectRoot = '/path/to/project' as PortablePath;
const cwdFs = new CwdFS(projectRoot, { baseFs });

// All relative paths are resolved relative to the project root
await cwdFs.writeFilePromise('config.json' as PortablePath, '{}');
// Actually writes to /path/to/project/config.json

await cwdFs.mkdirPromise('src/components' as PortablePath, { recursive: true });
// Creates /path/to/project/src/components

// Read files relative to the working directory
const configContent = await cwdFs.readFilePromise('config.json' as PortablePath, 'utf8');

// Absolute paths still work but are resolved from the working directory
const absoluteInCwd = await cwdFs.readFilePromise('/README.md' as PortablePath, 'utf8');
// Reads /path/to/project/README.md

CwdFS Options

import { CwdFS, type PortablePath } from '@yarnpkg/fslib';

interface CwdFSOptions {
  baseFs?: FakeFS<PortablePath>;
}

// Custom configuration
const cwdFs = new CwdFS('/working/directory' as PortablePath, {
  baseFs: virtualFileSystem
});

Composite and Mounting Filesystems

MountFS

Filesystem that can mount other filesystems at specific paths.

import { MountFS, NodeFS, VirtualFS, type PortablePath } from '@yarnpkg/fslib';

// Create mount filesystem
const mountFs = new MountFS({ baseFs: new NodeFS() });

// Mount virtual filesystem at /virtual
const virtualFs = new VirtualFS();
await virtualFs.writeFilePromise('/data.txt' as PortablePath, 'virtual content');
mountFs.mount('/virtual' as PortablePath, virtualFs);

// Mount real directory at /real  
const realFs = new NodeFS();
mountFs.mount('/real' as PortablePath, realFs);

// Access mounted filesystems
const virtualData = await mountFs.readFilePromise('/virtual/data.txt' as PortablePath, 'utf8');
console.log('Virtual content:', virtualData);

const realFiles = await mountFs.readdirPromise('/real' as PortablePath);
console.log('Real files:', realFiles);

// Unmount when done
mountFs.unmount('/virtual' as PortablePath);

MountFS Types and Options

import { type MountableFS, type GetMountPointFn, type MountFSOptions } from '@yarnpkg/fslib';

// Mountable filesystem interface
interface MountableFS extends FakeFS<PortablePath> {
  // Additional requirements for mountable filesystems
}

// Function to determine mount points
type GetMountPointFn<T extends MountableFS> = (
  fs: T, 
  path: PortablePath
) => PortablePath | null;

// Mount filesystem options
interface MountFSOptions<T extends MountableFS> {
  baseFs?: FakeFS<PortablePath>;
  getMountPoint?: GetMountPointFn<T>;
}

Specialized Filesystem Implementations

AliasFS

Filesystem that redirects specific paths to other locations.

import { AliasFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';

// Create filesystem with path aliasing
const baseFs = new NodeFS();
const aliasMap = new Map<PortablePath, PortablePath>();
aliasMap.set('/alias/target' as PortablePath, '/real/location' as PortablePath);

const aliasFs = new AliasFS('/base/path' as PortablePath, aliasMap, { baseFs });

// Access through alias
const content = await aliasFs.readFilePromise('/alias/target/file.txt' as PortablePath, 'utf8');
// Actually reads from /real/location/file.txt

LazyFS

Filesystem with deferred initialization.

import { LazyFS, NodeFS, type PortablePath } from '@yarnpkg/fslib';

// Create lazy filesystem that initializes on first use
const lazyFs = new LazyFS<PortablePath>(() => {
  console.log('Initializing filesystem...');
  return new NodeFS();
}, { baseFs: undefined });

// Filesystem is not initialized until first operation
const content = await lazyFs.readFilePromise('/file.txt' as PortablePath, 'utf8');
// Now the underlying NodeFS is created and used

PosixFS

Filesystem that handles conversion between native and portable paths.

import { PosixFS, type NativePath, type PortablePath } from '@yarnpkg/fslib';

// Create POSIX filesystem for cross-platform compatibility
const posixFs = new PosixFS();

// Works with native paths, converts internally
const nativePath = process.platform === 'win32' ? 'C:\\Windows\\System32' : '/usr/bin';
const files = await posixFs.readdirPromise(nativePath as NativePath);
console.log('System files:', files);

NoFS

Filesystem that denies all operations.

import { NoFS, type PortablePath } from '@yarnpkg/fslib';

// Create filesystem that throws errors for all operations
const noFs = new NoFS();

try {
  await noFs.readFilePromise('/any/file.txt' as PortablePath, 'utf8');
} catch (error) {
  console.log('Operation denied:', error.message);
}

// Useful as a placeholder or for security

Base Classes for Custom Implementations

ProxiedFS

Abstract base class for filesystems that proxy to another filesystem.

import { ProxiedFS, type PortablePath, type NativePath } from '@yarnpkg/fslib';

// Create a custom proxied filesystem
class LoggingFS extends ProxiedFS<PortablePath, PortablePath> {
  constructor(baseFs: FakeFS<PortablePath>) {
    super(baseFs);
  }
  
  async readFilePromise(p: PortablePath, encoding?: BufferEncoding): Promise<Buffer | string> {
    console.log(`Reading file: ${p}`);
    const result = await this.baseFs.readFilePromise(p, encoding);
    console.log(`Read ${Buffer.isBuffer(result) ? result.length : result.length} bytes/characters`);
    return result;
  }
  
  async writeFilePromise(p: PortablePath, content: string | Buffer): Promise<void> {
    console.log(`Writing file: ${p}`);
    await this.baseFs.writeFilePromise(p, content);
    console.log(`Write completed`);
  }
  
  // Other methods automatically proxy to baseFs
}

// Usage
const baseFs = new NodeFS();
const loggingFs = new LoggingFS(baseFs);

await loggingFs.writeFilePromise('/log/file.txt' as PortablePath, 'content');
// Logs: Writing file: /log/file.txt
// Logs: Write completed

Advanced Usage Patterns

Filesystem Composition

import { 
  MountFS, JailFS, VirtualFS, NodeFS,
  type PortablePath 
} from '@yarnpkg/fslib';

// Create a complex filesystem setup
async function createDevelopmentEnvironment(): Promise<MountFS> {
  const mountFs = new MountFS({ baseFs: new NodeFS() });
  
  // Mount real project directory at /project (jailed for security)
  const projectFs = new JailFS('/real/project/path' as PortablePath, { 
    baseFs: new NodeFS() 
  });
  mountFs.mount('/project' as PortablePath, projectFs);
  
  // Mount virtual filesystem for temporary files at /tmp
  const tmpFs = new VirtualFS();
  mountFs.mount('/tmp' as PortablePath, tmpFs);
  
  // Mount system tools (read-only jail) at /tools
  const toolsFs = new JailFS('/usr/local/bin' as PortablePath, {
    baseFs: new NodeFS()
  });
  mountFs.mount('/tools' as PortablePath, toolsFs);
  
  return mountFs;
}

// Usage
const devEnv = await createDevelopmentEnvironment();
await devEnv.writeFilePromise('/project/src/main.ts' as PortablePath, 'code');
await devEnv.writeFilePromise('/tmp/cache.json' as PortablePath, '{}');
const tools = await devEnv.readdirPromise('/tools' as PortablePath);

Testing with Virtual Filesystems

import { VirtualFS, type PortablePath } from '@yarnpkg/fslib';

// Set up test filesystem
async function setupTestEnvironment(): Promise<VirtualFS> {
  const testFs = new VirtualFS();
  
  // Create test project structure
  await testFs.mkdirPromise('/test-project' as PortablePath, { recursive: true });
  await testFs.mkdirPromise('/test-project/src' as PortablePath);
  await testFs.mkdirPromise('/test-project/tests' as PortablePath);
  
  // Add test files
  await testFs.writeFilePromise(
    '/test-project/package.json' as PortablePath,
    JSON.stringify({ name: 'test-pkg', version: '1.0.0' }, null, 2)
  );
  
  await testFs.writeFilePromise(
    '/test-project/src/index.ts' as PortablePath,
    'export function hello() { return "world"; }'
  );
  
  await testFs.writeFilePromise(
    '/test-project/tests/index.test.ts' as PortablePath,
    'import { hello } from "../src"; console.assert(hello() === "world");'
  );
  
  return testFs;
}

// Use in tests
describe('File operations', () => {
  let testFs: VirtualFS;
  
  beforeEach(async () => {
    testFs = await setupTestEnvironment();
  });
  
  it('should read package.json', async () => {
    const content = await testFs.readFilePromise('/test-project/package.json' as PortablePath, 'utf8');
    const pkg = JSON.parse(content);
    expect(pkg.name).toBe('test-pkg');
  });
});

Migration Between Filesystems

import { 
  VirtualFS, NodeFS, 
  type FakeFS, type PortablePath 
} from '@yarnpkg/fslib';

// Copy directory tree between filesystems
async function copyDirectoryTree(
  sourceFs: FakeFS<PortablePath>,
  targetFs: FakeFS<PortablePath>,
  sourcePath: PortablePath,
  targetPath: PortablePath
): Promise<void> {
  const entries = await sourceFs.readdirPromise(sourcePath, { withFileTypes: true });
  
  await targetFs.mkdirPromise(targetPath, { recursive: true });
  
  for (const entry of entries) {
    const srcPath = ppath.join(sourcePath, entry.name);
    const dstPath = ppath.join(targetPath, entry.name);
    
    if (entry.isDirectory()) {
      await copyDirectoryTree(sourceFs, targetFs, srcPath, dstPath);
    } else if (entry.isFile()) {
      const content = await sourceFs.readFilePromise(srcPath);
      await targetFs.writeFilePromise(dstPath, content);
    }
  }
}

// Usage: migrate from virtual to real filesystem
const virtualFs = new VirtualFS();
const nodeFs = new NodeFS();

// ... populate virtualFs ...

await copyDirectoryTree(
  virtualFs, 
  nodeFs,
  '/virtual/project' as PortablePath,
  '/real/backup' as PortablePath
);

Related Documentation

  • Filesystem Abstractions - Base classes and interfaces these implementations extend
  • Path Handling - Path utilities used by filesystem operations
  • Constants and Utilities - Error handling and statistics utilities
  • Advanced Features - Advanced operations and filesystem patching

Install with Tessl CLI

npx tessl i tessl/npm-yarnpkg--fslib

docs

advanced-features.md

constants-and-utilities.md

filesystem-abstractions.md

filesystem-implementations.md

index.md

path-handling.md

tile.json