Skip to content

Using ZenFS

This is the full usage guide for ZenFS. If you just want to get something running, start with the quick start.

A single InMemory backend is created by default and mounted on /, so fs works with no setup:

import { fs } from '@zenfs/core'; // You can also use the default export
fs.writeFileSync('/test.txt', 'You can do this anywhere, including browsers!');
const contents = fs.readFileSync('/test.txt', 'utf-8');
console.log(contents);

You can configure ZenFS to use a different backend and mount multiple backends. It is strongly recommended to do so using the configure function.

You can use multiple backends by passing an object to configure which maps paths to file systems. The following example mounts a zip file to /mnt/zip, in-memory storage to /tmp, and IndexedDB to /home. / has the default in-memory backend.

import { configure, InMemory } from '@zenfs/core';
import { IndexedDB } from '@zenfs/dom';
import { Zip } from '@zenfs/archives';
const res = await fetch('mydata.zip');
await configure({
mounts: {
'/mnt/zip': { backend: Zip, data: await res.arrayBuffer() },
'/tmp': InMemory,
'/home': IndexedDB,
},
});

You aren’t required to use absolute paths for the keys of mounts, but it is a good practice to do so.

If you only need a single backend mounted at /, configureSingle is more direct. Here is an example that mounts the WebStorage backend from @zenfs/dom:

import { configureSingle, fs } from '@zenfs/core';
import { WebStorage } from '@zenfs/dom';
await configureSingle({ backend: WebStorage });
if (!fs.existsSync('/test.txt')) {
fs.writeFileSync('/test.txt', 'This will persist across reloads!');
}
const contents = fs.readFileSync('/test.txt', 'utf-8');
console.log(contents);

For everything configure accepts (including permissions), see Configuration.

The FS promises API is exposed as promises.

import { configureSingle } from '@zenfs/core';
import { exists, writeFile } from '@zenfs/core/promises';
import { IndexedDB } from '@zenfs/dom';
await configureSingle({ backend: IndexedDB });
if (!(await exists('/myfile.txt'))) {
await writeFile('/myfile.txt', 'Lots of persistent data');
}

Mounting and unmounting, creating backends

Section titled “Mounting and unmounting, creating backends”

If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling resolveMountConfig with it.

You can then mount and unmount the backend instance by using mount and umount.

import { configure, resolveMountConfig, InMemory, fs } from '@zenfs/core';
import { IndexedDB } from '@zenfs/dom';
import { Zip } from '@zenfs/archives';
await configure({
mounts: {
'/tmp': InMemory,
'/home': IndexedDB,
},
});
fs.mkdirSync('/mnt/zip', { recursive: true });
const res = await fetch('mydata.zip');
const zipfs = await resolveMountConfig({ backend: Zip, data: await res.arrayBuffer() });
fs.mount('/mnt/zip', zipfs);
// do stuff with the mounted zip
fs.umount('/mnt/zip'); // finished using the zip

ZenFS also includes emulation of some other node: modules for various reasons, importable from @zenfs/core/<name>:

  • node:path
  • node:readline

For example:

import * as path from '@zenfs/core/path';

ZenFS exports a drop-in for Node’s fs module, so you can use it for your bundler of preference using the default export.