Cloudflare R2 Adapter
Cloudflare R2 blob storage adapter for MemoFS remote-blob memory stores.
The @memofs/adapter-r2 adapter provides serverless raw byte storage for MemoFS using Cloudflare R2 object storage buckets (R2Bucket).
It implements core's provider-neutral BlobClient interface, allowing MemoFS's RemoteBlobMemoryStore to persist canonical .memofs/ files across distributed edge runtimes.
Installation
npm install @memofs/adapter-r2Requires Node.js >= 22 or the Cloudflare Workers runtime.
Usage in Cloudflare Workers
In your Cloudflare Worker, bind an R2 bucket (e.g. env.BLOBS), create a BlobClient with createR2BlobClient(), and pair it with a metadata store in RemoteBlobMemoryStore:
import { MemoFS, RemoteBlobMemoryStore } from "@memofs/core";
import { createR2BlobClient } from "@memofs/adapter-r2";
import { createTursoMetadataStore } from "@memofs/adapter-turso";
import { createClient } from "@libsql/client";
export interface Env {
BLOBS: R2Bucket; // R2 Bucket binding from wrangler.jsonc / wrangler.toml
TURSO_DATABASE_URL: string;
TURSO_AUTH_TOKEN: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const projectId = "team-workspace";
// 1. Initialize R2 Blob Client (implements BlobClient)
const blobClient = createR2BlobClient({
binding: env.BLOBS,
});
// 2. Initialize Turso Metadata Store (implements MetadataStore)
const metadata = createTursoMetadataStore({
client: createClient({
url: env.TURSO_DATABASE_URL,
authToken: env.TURSO_AUTH_TOKEN,
}),
projectId,
});
// 3. Compose the provider-neutral RemoteBlobMemoryStore
const store = new RemoteBlobMemoryStore({
blobClient,
metadata,
rootKey: projectId,
});
// 4. Initialize MemoFS
const memo = new MemoFS({
store,
projectId,
mode: "local",
});
const coreMemory = await memo.core.read();
return new Response(coreMemory, {
headers: { "Content-Type": "text/markdown" },
});
},
};Content Addressing & Storage Layout
@memofs/adapter-r2 adheres to MemoFS's content-addressed storage architecture:
- SHA-256 Keying: The runtime computes the 64-character lowercase hexadecimal SHA-256 hash of the canonical file content and passes it as the blob key (
blobKey === sha256). - Exact Cloud Replica Parity: Matches the cloud sync engine's file replica format (
r2_key === sha256) exactly. Identical file contents across multiple projects or file paths share the same underlying blob storage without duplication. - Idempotent Operations: Because keys are derived strictly from file content, writing the same file repeatedly is fully idempotent. Deleting non-existent keys succeeds safely without errors.
Configuration API (CreateR2BlobClientOptions)
The createR2BlobClient(options) factory accepts CreateR2BlobClientOptions:
| Option | Type | Required | Description |
|---|---|---|---|
binding | R2Bucket | Yes | The Cloudflare R2 bucket binding object from env. |
Method Reference (BlobClient)
The returned BlobClient exposes three provider-neutral methods:
interface BlobClient {
/** Reads raw bytes from R2; returns null if key is absent. */
get(key: string): Promise<ArrayBuffer | null>;
/** Writes bytes or streams to R2 under the content-derived key. */
put(key: string, body: BufferSource | ReadableStream<Uint8Array>): Promise<void>;
/** Deletes a blob from R2; idempotent. */
delete(key: string): Promise<void>;
}