npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-opfs

v1.0.0

Published

Node.js implementation of the Origin Private File System (OPFS) API

Readme

node-opfs

A Node.js implementation of the Origin Private File System (OPFS) API that provides a drop-in replacement for the browser's File System Access API.

Features

  • API Compatible: Implements the same API as browser OPFS for easy code sharing
  • Full TypeScript Support: Complete type definitions included
  • Async/Await: Modern async/await based API
  • File Operations: Read, write, create, and delete files and directories
  • Directory Navigation: Navigate and manage directory structures
  • Stream Support: Efficient file writing with streams

Installation

npm install node-opfs

Quick Start

import { navigator } from 'node-opfs';

// Get the root directory
const root = await navigator.storage.getDirectory();

// Create a file
const fileHandle = await root.getFileHandle('hello.txt', { create: true });

// Write to the file
const writable = await fileHandle.createWritable();
await writable.write('Hello, World!');
await writable.close();

// Read from the file
const file = await fileHandle.getFile();
const text = await file.text();
console.log(text); // 'Hello, World!'

API Documentation

StorageManager

The main entry point for accessing the file system.

import { navigator, storage, StorageManager } from 'node-opfs';

// Using the global navigator object
const root = await navigator.storage.getDirectory();

// Using the storage object directly
const root = await storage.getDirectory();

// Using a custom base directory
const customStorage = new StorageManager('/path/to/custom/directory');
const root = await customStorage.getDirectory();

FileSystemDirectoryHandle

Represents a directory in the file system.

Methods

getFileHandle(name, options?)

Get a handle to a file in the directory.

// Get existing file
const fileHandle = await dirHandle.getFileHandle('file.txt');

// Create new file if it doesn't exist
const fileHandle = await dirHandle.getFileHandle('file.txt', { create: true });
getDirectoryHandle(name, options?)

Get a handle to a subdirectory.

// Get existing directory
const subDir = await dirHandle.getDirectoryHandle('subdir');

// Create new directory if it doesn't exist
const subDir = await dirHandle.getDirectoryHandle('subdir', { create: true });
removeEntry(name, options?)

Remove a file or directory.

// Remove a file
await dirHandle.removeEntry('file.txt');

// Remove a directory recursively
await dirHandle.removeEntry('subdir', { recursive: true });
resolve(possibleDescendant)

Get the path from this directory to a descendant.

const subDir = await root.getDirectoryHandle('subdir', { create: true });
const fileHandle = await subDir.getFileHandle('file.txt', { create: true });

const path = await root.resolve(fileHandle);
console.log(path); // ['subdir', 'file.txt']
Iteration Methods
// Iterate over entry names
for await (const name of dirHandle.keys()) {
  console.log(name);
}

// Iterate over handles
for await (const handle of dirHandle.values()) {
  console.log(handle.name, handle.kind);
}

// Iterate over entries (name-handle pairs)
for await (const [name, handle] of dirHandle.entries()) {
  console.log(name, handle.kind);
}

// Using async iteration directly
for await (const [name, handle] of dirHandle) {
  console.log(name, handle.kind);
}

FileSystemFileHandle

Represents a file in the file system.

Methods

getFile()

Get a File object representing the current state of the file.

const file = await fileHandle.getFile();
const text = await file.text();
const buffer = await file.arrayBuffer();
createWritable(options?)

Create a writable stream for the file.

// Truncate file and write
const writable = await fileHandle.createWritable();
await writable.write('New content');
await writable.close();

// Keep existing data
const writable = await fileHandle.createWritable({ keepExistingData: true });
await writable.write('Appended content');
await writable.close();
createSyncAccessHandle()

Create a synchronous access handle (primarily for compatibility).

const accessHandle = await fileHandle.createSyncAccessHandle();
await accessHandle.truncate(100);
const size = await accessHandle.getSize();
await accessHandle.flush();
await accessHandle.close();

Synchronous read/write example (use in worker contexts to avoid blocking the main thread):

const accessHandle = await fileHandle.createSyncAccessHandle();

// Write synchronously
const writeBuf = new TextEncoder().encode('Hello');
accessHandle.write(writeBuf, { at: 0 });

// Read synchronously
const readBuf = new Uint8Array(5);
accessHandle.read(readBuf, { at: 0 });
console.log(new TextDecoder().decode(readBuf)); // 'Hello'

await accessHandle.close();

FileSystemWritableFileStream

A writable stream for file operations.

Methods

write(data)

Write data to the file.

// Write string
await writable.write('Hello');

// Write buffer
const buffer = new TextEncoder().encode('Hello');
await writable.write(buffer);

// Write at specific position
await writable.write({ type: 'write', position: 10, data: 'Hello' });

// Seek to position
await writable.write({ type: 'seek', position: 5 });

// Truncate file
await writable.write({ type: 'truncate', size: 100 });
seek(position)

Move the write position.

await writable.seek(10);
await writable.write('At position 10');
truncate(size)

Truncate the file to the specified size.

await writable.truncate(100);
close()

Close the stream and flush all data.

await writable.close();

Examples

Working with Directories

import { navigator } from 'node-opfs';

const root = await navigator.storage.getDirectory();

// Create nested directories
const docs = await root.getDirectoryHandle('documents', { create: true });
const projects = await docs.getDirectoryHandle('projects', { create: true });

// Create a file in the nested directory
const fileHandle = await projects.getFileHandle('readme.md', { create: true });
const writable = await fileHandle.createWritable();
await writable.write('# My Project\n\nProject documentation...');
await writable.close();

// List all files in a directory
for await (const [name, handle] of docs) {
  console.log(`${name}: ${handle.kind}`);
}

Advanced File Writing

import { navigator } from 'node-opfs';

const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.txt', { create: true });
const writable = await fileHandle.createWritable();

// Write at different positions
await writable.write('Hello');
await writable.write({ type: 'seek', position: 0 });
await writable.write('Goodbye');

// Result: "Goodbye"
await writable.close();

Copying Files

async function copyFile(source, dest) {
  const sourceFile = await source.getFile();
  const buffer = await sourceFile.arrayBuffer();
  
  const writable = await dest.createWritable();
  await writable.write(buffer);
  await writable.close();
}

const sourceHandle = await root.getFileHandle('source.txt');
const destHandle = await root.getFileHandle('dest.txt', { create: true });
await copyFile(sourceHandle, destHandle);

Custom Storage Location

import { StorageManager } from 'node-opfs';

// Use a custom directory for storage
const storage = new StorageManager('/path/to/my/storage');
const root = await storage.getDirectory();

// Now all operations use the custom directory
const fileHandle = await root.getFileHandle('test.txt', { create: true });

Default Storage Location

By default, files are stored in ~/.node-opfs (in the user's home directory). You can change this by creating a custom StorageManager instance with a different base directory.

Browser Compatibility

This library implements the same API as the browser's File System Access API (OPFS), making it easy to share code between Node.js and browser environments. Simply swap the import when running in different environments.

Worker Threads (Node)

Use Node's worker_threads for best-practice synchronous I/O (mirrors browser Workers). Minimal ESM example:

// main.mjs
import { Worker } from 'node:worker_threads';

const worker = new Worker(new URL('./opfs-worker.mjs', import.meta.url), { type: 'module' });

worker.on('message', (msg) => {
  console.log('Worker says:', msg);
});

worker.on('error', (err) => {
  console.error('Worker error:', err);
});

worker.on('exit', (code) => {
  console.log('Worker exited with code', code);
});
// opfs-worker.mjs
import { parentPort } from 'node:worker_threads';
import { navigator } from 'node-opfs';

const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('worker-sync.txt', { create: true });
const accessHandle = await fileHandle.createSyncAccessHandle();

// Write synchronously
const writeBuf = new TextEncoder().encode('Hello from worker');
accessHandle.write(writeBuf, { at: 0 });

// Read synchronously
const readBuf = new Uint8Array(writeBuf.length);
accessHandle.read(readBuf, { at: 0 });

await accessHandle.close();

parentPort.postMessage(new TextDecoder().decode(readBuf));

Known Limitations

  • FileSystemSyncAccessHandle: The synchronous read() and write() methods are implemented via Node's fs.readSync/fs.writeSync on the file descriptor and will block the calling thread. For heavy I/O, prefer using them inside worker_threads (similar to browser Workers). Async methods (FileSystemWritableFileStream) remain the recommended default for non-worker contexts.

License

BSD 2-Clause License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.