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 🙏

© 2025 – Pkg Stats / Ryan Hefner

dofs

v0.1.0

Published

A filesystem for Cloudflare Durable Objects.

Readme

Durable Objects File System (dofs)

A filesystem-like API for Cloudflare Durable Objects, supporting streaming reads and writes with chunked storage.

Features

  • File and directory operations (read, write, mkdir, rmdir, stat, etc.)
  • Efficient chunked storage for large files
  • Streaming read and write support via ReadableStream and WritableStream
  • Designed for use in Durable Objects (DOs)

Basic Usage

The recommended way to add dofs to your Durable Object is using the @Dofs decorator:

import { DurableObject } from 'cloudflare:workers'
import { Dofs } from 'dofs'

@Dofs({ chunkSize: 256 * 1024 })
export class MyDurableObject extends DurableObject<Env> {
  // Your custom methods here
  // Access filesystem via this.getFs()
}

The @Dofs decorator:

  • Automatically creates the fs property in your Durable Object
  • Adds a getFs() method to access the filesystem instance
  • Accepts the same configuration options as the Fs constructor
  • Works directly with classes extending DurableObject

Alternative: Using withDofs Helper

For cases where you need more control or are working with existing class hierarchies, you can use the withDofs helper:

import { DurableObject } from 'cloudflare:workers'
import { withDofs } from 'dofs'

// Create a concrete base class first
class MyDurableObjectBase extends DurableObject<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env)
  }
}

// Then extend it with dofs
export class MyDurableObject extends withDofs(MyDurableObjectBase) {
  // Your custom methods here
}

// Or with configuration options:
export class MyDurableObject extends withDofs(MyDurableObjectBase, { chunkSize: 256 * 1024 }) {
  // Your custom methods here
}

Important: Due to TypeScript declaration generation limitations, withDofs requires a concrete base class. You cannot pass the abstract DurableObject class directly to withDofs.

Both approaches provide the same functionality:

  • Automatically creates the fs property in your Durable Object
  • Adds a getFs() method to access the filesystem instance
  • Accepts the same configuration options as the Fs constructor

Note: class instances can be passed via RPC as long as they inherit from RpcTarget as Fs does.

Advanced: Manual Setup

For more control, you can manually create a dofs instance in your Durable Object:

import { DurableObject } from 'cloudflare:workers'
import { Fs } from 'dofs'

export class MyDurableObject extends DurableObject<Env> {
  private fs: Fs

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env)
    this.fs = new Fs(ctx, env)
  }

  // Expose fs
  public getDofs() {
    return this.fs
  }
}

Configuration Options

Chunk Size

By default, the chunk size is 64kb. You can configure it by passing the chunkSize option (in bytes) to the Fs constructor:

import { Fs } from 'dofs'

const fs = new Fs(ctx, env, { chunkSize: 256 * 1024 }) // 256kb chunks

How chunk size affects query frequency and cost:

  • Smaller chunk sizes mean more database queries per file read/write, which can increase Durable Object query costs and latency.
  • Larger chunk sizes reduce the number of queries (lower cost, better throughput), but may use more memory per operation and can be less efficient for small files or random access.
  • Choose a chunk size that balances your workload's cost, performance, and memory needs.

Note: Chunk size cannot be changed after the first file has been written to the filesystem. It is fixed for the lifetime of the filesystem instance.

Device Size

By default, the device size (total storage available) is 1GB (1024 * 1024 * 1024 bytes). You can change this limit using the setDeviceSize method:

fs.setDeviceSize(10 * 1024 * 1024 * 1024) // Set device size to 10GB
  • The device size must be set before writing data that would exceed the current limit.
  • If you try to write more data than the device size allows, an ENOSPC error will be thrown.
  • You can check the current device size and usage with getDeviceStats().
const stats = fs.getDeviceStats()
console.log(stats.deviceSize, stats.spaceUsed, stats.spaceAvailable)

Default: 1GB if not set.

Streaming Support

  • Read: readFile(path) returns a ReadableStream<Uint8Array> for efficient, chunked reading.
  • Write: writeFile(path, stream) accepts a ReadableStream<Uint8Array> for efficient, chunked writing.
  • You can also use writeFile(path, data) with a string or ArrayBuffer for non-streaming writes.

API Reference

Note: These are async from the CF Worker stub (RPC call), but are sync when called inside the Durable Object (direct call).

  • readFile(path: string): ReadableStream<Uint8Array>
  • writeFile(path: string, data: string | ArrayBuffer | ReadableStream<Uint8Array>): void
  • read(path: string, options): ArrayBuffer (non-streaming, offset/length)
  • write(path: string, data, options): void (non-streaming, offset)
  • mkdir(path: string, options?): void
  • rmdir(path: string, options?): void
  • listDir(path: string, options?): string[]
  • stat(path: string): Stat
  • unlink(path: string): void
  • rename(oldPath: string, newPath: string): void
  • symlink(target: string, path: string): void
  • readlink(path: string): string

Projects that work with dofs

  • dterm

Future Plans

  • In-memory block caching for improved read/write performance
  • Store small files (that fit in one block) directly in the inode table instead of the chunk table to reduce queries
  • defrag() method to allow changing chunk size and optimizing storage