worker-fs-mount 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,360 @@
1
+ # worker-fs-mount
2
+
3
+ Mount WorkerEntrypoints as virtual filesystems in Cloudflare Workers. This package provides a drop-in replacement for `node:fs/promises` that intercepts filesystem calls and redirects them to your WorkerEntrypoint implementations via jsrpc.
4
+
5
+ ## Features
6
+
7
+ - **Simple setup** - Just add an alias to `wrangler.toml` and your existing `node:fs/promises` code works
8
+ - **Multiple mount sources** - Works with `ctx.exports`, service bindings, and Durable Objects
9
+ - **Full fs coverage** - Supports 20+ filesystem operations (read, write, stat, readdir, mkdir, rm, rename, etc.)
10
+ - **TypeScript-first** - Full type definitions with strict types
11
+ - **Cross-mount safety** - Properly handles operations across mount boundaries
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install worker-fs-mount
17
+ ```
18
+
19
+ ## Setup
20
+
21
+ Add the following alias to your `wrangler.toml`:
22
+
23
+ ```toml
24
+ [alias]
25
+ "node:fs/promises" = "worker-fs-mount/fs"
26
+ ```
27
+
28
+ This replaces `node:fs/promises` imports with our mount-aware implementation at build time.
29
+
30
+ ## Quick Start
31
+
32
+ ```typescript
33
+ import { env } from 'cloudflare:workers';
34
+ import { mount } from 'worker-fs-mount';
35
+ import fs from 'node:fs/promises';
36
+
37
+ // Mount at module level using importable env
38
+ mount('/mnt/storage', env.STORAGE_SERVICE);
39
+
40
+ export default {
41
+ async fetch(request) {
42
+ // Standard fs operations are automatically intercepted
43
+ await fs.writeFile('/mnt/storage/data.json', JSON.stringify({ hello: 'world' }));
44
+ const content = await fs.readFile('/mnt/storage/data.json', 'utf8');
45
+
46
+ // Non-mounted paths work normally
47
+ await fs.readFile('/tmp/local.txt');
48
+
49
+ return new Response(content);
50
+ }
51
+ };
52
+ ```
53
+
54
+ ## How It Works
55
+
56
+ With the wrangler alias configured, every `node:fs/promises` import is replaced with our implementation. Each filesystem call checks if the path falls under a mounted location:
57
+
58
+ ```
59
+ fs.readFile('/mnt/storage/file.txt')
60
+
61
+ Is '/mnt/storage' mounted? → YES → Call stub.readFile('/file.txt') via jsrpc
62
+
63
+ Is '/tmp/file.txt' mounted? → NO → Use native node:fs/promises
64
+ ```
65
+
66
+ Both import styles work:
67
+ ```typescript
68
+ import fs from 'node:fs/promises';
69
+ import { readFile, writeFile } from 'node:fs/promises';
70
+
71
+ // Both are intercepted for mounted paths
72
+ await fs.readFile('/mnt/storage/file.txt');
73
+ await readFile('/mnt/storage/file.txt');
74
+ ```
75
+
76
+ ## Mount Sources
77
+
78
+ ### Service Bindings
79
+
80
+ ```typescript
81
+ // wrangler.toml
82
+ // [[services]]
83
+ // binding = "STORAGE"
84
+ // service = "storage-worker"
85
+
86
+ mount('/mnt/storage', env.STORAGE);
87
+ ```
88
+
89
+ ### Same-Worker Entrypoints
90
+
91
+ ```typescript
92
+ export class MyFilesystem extends WorkerEntrypoint {
93
+ async readFile(path) { /* ... */ }
94
+ // ...
95
+ }
96
+
97
+ export default class extends WorkerEntrypoint {
98
+ async fetch() {
99
+ mount('/mnt/local', this.ctx.exports.MyFilesystem);
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Durable Objects
105
+
106
+ Access via `ctx.exports` (recommended) - run `wrangler types` to generate types:
107
+
108
+ ```typescript
109
+ export class StorageDO extends DurableObject implements WorkerFilesystem {
110
+ // ... implement filesystem methods
111
+ }
112
+
113
+ export default class extends WorkerEntrypoint<Env> {
114
+ async fetch() {
115
+ // ctx.exports provides typed access to your exported Durable Objects
116
+ const id = this.ctx.exports.StorageDO.idFromName('user-123');
117
+ const stub = this.ctx.exports.StorageDO.get(id);
118
+ mount('/mnt/user', stub);
119
+ }
120
+ }
121
+ ```
122
+
123
+ ## Implementing a WorkerFilesystem
124
+
125
+ Your entrypoint must implement the `WorkerFilesystem` interface. The interface is stream-first - you implement 6 core methods and higher-level operations like `readFile`/`writeFile` are automatically derived.
126
+
127
+ Here's a minimal in-memory example:
128
+
129
+ ```typescript
130
+ import { WorkerEntrypoint } from 'cloudflare:workers';
131
+ import type { WorkerFilesystem, Stat, DirEntry } from 'worker-fs-mount';
132
+
133
+ export class MemoryFS extends WorkerEntrypoint implements WorkerFilesystem {
134
+ #files = new Map<string, Uint8Array>();
135
+ #dirs = new Set<string>(['/']);
136
+
137
+ async stat(path: string): Promise<Stat | null> {
138
+ if (this.#dirs.has(path)) {
139
+ return { type: 'directory', size: 0 };
140
+ }
141
+ const file = this.#files.get(path);
142
+ if (!file) return null;
143
+ return { type: 'file', size: file.length };
144
+ }
145
+
146
+ async createReadStream(path: string, options?: { start?: number; end?: number }): Promise<ReadableStream<Uint8Array>> {
147
+ const file = this.#files.get(path);
148
+ if (!file) throw new Error(`ENOENT: ${path}`);
149
+ const start = options?.start ?? 0;
150
+ const end = options?.end !== undefined ? options.end + 1 : file.length;
151
+ const chunk = file.slice(start, end);
152
+ return new ReadableStream({
153
+ start(controller) {
154
+ controller.enqueue(chunk);
155
+ controller.close();
156
+ },
157
+ });
158
+ }
159
+
160
+ async createWriteStream(path: string, options?: { start?: number; flags?: 'w' | 'a' | 'r+' }): Promise<WritableStream<Uint8Array>> {
161
+ const self = this;
162
+ let offset = options?.start ?? 0;
163
+ let content = options?.flags === 'a' || options?.flags === 'r+'
164
+ ? (this.#files.get(path) ?? new Uint8Array(0))
165
+ : new Uint8Array(0);
166
+ if (options?.flags === 'a') offset = content.length;
167
+
168
+ return new WritableStream({
169
+ write(chunk) {
170
+ const newLength = Math.max(content.length, offset + chunk.length);
171
+ const newContent = new Uint8Array(newLength);
172
+ newContent.set(content, 0);
173
+ newContent.set(chunk, offset);
174
+ content = newContent;
175
+ offset += chunk.length;
176
+ self.#files.set(path, content);
177
+ },
178
+ });
179
+ }
180
+
181
+ async readdir(path: string): Promise<DirEntry[]> {
182
+ const prefix = path === '/' ? '/' : path + '/';
183
+ const entries: DirEntry[] = [];
184
+ const seen = new Set<string>();
185
+ for (const [filePath] of this.#files) {
186
+ if (filePath.startsWith(prefix)) {
187
+ const name = filePath.slice(prefix.length).split('/')[0];
188
+ if (name && !seen.has(name)) {
189
+ seen.add(name);
190
+ entries.push({ name, type: 'file' });
191
+ }
192
+ }
193
+ }
194
+ return entries;
195
+ }
196
+
197
+ async mkdir(path: string, options?: { recursive?: boolean }): Promise<string | undefined> {
198
+ if (this.#dirs.has(path)) return undefined;
199
+ this.#dirs.add(path);
200
+ return path;
201
+ }
202
+
203
+ async rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
204
+ if (!this.#files.delete(path) && !this.#dirs.delete(path)) {
205
+ if (!options?.force) throw new Error(`ENOENT: ${path}`);
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ For production implementations, see the `r2-fs`, `durable-object-fs`, and `memory-fs` packages.
212
+
213
+ ## API Reference
214
+
215
+ ### `mount(path, stub): void`
216
+
217
+ Mount a WorkerFilesystem at the specified path.
218
+
219
+ | Parameter | Type | Description |
220
+ |-----------|------|-------------|
221
+ | `path` | `string` | Mount point (must be absolute, start with `/`) |
222
+ | `stub` | `WorkerFilesystem` | WorkerEntrypoint stub |
223
+
224
+ ### `unmount(path): boolean`
225
+
226
+ Unmount a filesystem at the specified path.
227
+
228
+ | Parameter | Type | Description |
229
+ |-----------|------|-------------|
230
+ | `path` | `string` | Mount point to unmount |
231
+
232
+ Returns `true` if a mount was removed, `false` if nothing was mounted at that path.
233
+
234
+ ### `withMounts(fn): Promise<T>`
235
+
236
+ Run a function with request-scoped mount isolation. Required for Durable Objects (getting a DO stub is IO). Use when different requests need different mounts (e.g., per-user DOs).
237
+
238
+ ```typescript
239
+ // Durable Objects require request scope - use withMounts for isolation
240
+ return withMounts(async () => {
241
+ const userId = getUserId(request);
242
+ const id = ctx.exports.UserStorage.idFromName(userId);
243
+ mount('/user', ctx.exports.UserStorage.get(id));
244
+ // Each request gets its own isolated mount
245
+ });
246
+ ```
247
+
248
+ For R2, KV, service bindings, and same-worker entrypoints, prefer mounting at module level using `import { env, exports } from 'cloudflare:workers'`.
249
+
250
+ ### `isMounted(path): boolean`
251
+
252
+ Check if a path is under any mount.
253
+
254
+ ### `isInMountContext(): boolean`
255
+
256
+ Check if code is running inside a `withMounts` callback.
257
+
258
+ ## WorkerFilesystem Interface
259
+
260
+ The interface is stream-first with minimal required methods. Higher-level operations like `readFile`, `writeFile`, `truncate`, `rename`, `cp`, and `unlink` are automatically derived from these core methods.
261
+
262
+ ### Required Methods (6)
263
+
264
+ | Method | Description |
265
+ |--------|-------------|
266
+ | `stat(path, options?)` | Get file/directory metadata |
267
+ | `createReadStream(path, options?)` | Create readable stream for a file |
268
+ | `createWriteStream(path, options?)` | Create writable stream for a file |
269
+ | `readdir(path, options?)` | List directory contents |
270
+ | `mkdir(path, options?)` | Create directory |
271
+ | `rm(path, options?)` | Remove file or directory |
272
+
273
+ ### Optional Methods (2)
274
+
275
+ | Method | Description |
276
+ |--------|-------------|
277
+ | `symlink(linkPath, targetPath)` | Create symlink |
278
+ | `readlink(path)` | Read symlink target |
279
+
280
+ ### Automatically Derived Operations
281
+
282
+ These `node:fs/promises` methods are automatically implemented using the core streaming methods:
283
+
284
+ | Method | Derived From |
285
+ |--------|--------------|
286
+ | `readFile` | `createReadStream` |
287
+ | `writeFile` | `createWriteStream` |
288
+ | `appendFile` | `createWriteStream` with append flag |
289
+ | `truncate` | `createReadStream` + `createWriteStream` |
290
+ | `unlink` | `stat` + `rm` |
291
+ | `copyFile`, `cp` | `createReadStream` + `createWriteStream` |
292
+ | `rename` | streams + `rm` |
293
+ | `access` | `stat` |
294
+
295
+ ## Supported fs Operations
296
+
297
+ The following `node:fs/promises` methods are intercepted:
298
+
299
+ - `readFile`, `writeFile`, `appendFile`
300
+ - `stat`, `lstat`
301
+ - `readdir`
302
+ - `mkdir`, `rmdir`, `rm`
303
+ - `unlink`
304
+ - `rename`
305
+ - `copyFile`, `cp`
306
+ - `access`
307
+ - `truncate`
308
+ - `symlink`, `readlink`
309
+ - `realpath`
310
+ - `utimes`
311
+
312
+ ## Constraints
313
+
314
+ ### Async Only
315
+
316
+ Only `node:fs/promises` is supported. Synchronous operations (`readFileSync`, etc.) are not intercepted and will use the native filesystem.
317
+
318
+ ### No File Descriptors
319
+
320
+ The fd-based API (`open`/`read`/`write`/`close`) is not supported. Use the high-level methods instead.
321
+
322
+ ### Same-Mount Operations
323
+
324
+ `rename` only works within the same mount. Cross-mount rename throws `EXDEV`. For cross-mount moves, use `copyFile` + `unlink`.
325
+
326
+ ### Reserved Paths
327
+
328
+ Cannot mount over `/bundle`, `/tmp`, or `/dev`.
329
+
330
+ ### No Nested Mounts
331
+
332
+ Cannot mount `/mnt/a/b` if `/mnt/a` is already mounted, or vice versa.
333
+
334
+ ## Error Handling
335
+
336
+ Errors follow Node.js conventions with `.code` property:
337
+
338
+ | Code | Meaning |
339
+ |------|---------|
340
+ | `ENOENT` | File or directory not found |
341
+ | `EEXIST` | File already exists |
342
+ | `ENOTDIR` | Expected directory but found file |
343
+ | `EISDIR` | Expected file but found directory |
344
+ | `ENOSYS` | Operation not supported by filesystem |
345
+ | `EXDEV` | Cross-mount operation not supported |
346
+ | `EACCES` | Permission denied |
347
+
348
+ ## Concurrency
349
+
350
+ The mounted WorkerFilesystem is responsible for handling concurrent access. For consistent state, use Durable Objects which provide single-threaded execution:
351
+
352
+ ```typescript
353
+ export class StorageDO extends DurableObject implements WorkerFilesystem {
354
+ // All methods automatically serialized by DO runtime
355
+ }
356
+ ```
357
+
358
+ ## License
359
+
360
+ MIT
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Replacement module for node:fs/promises that routes mounted paths
3
+ * to their WorkerFilesystem implementations.
4
+ *
5
+ * Users should alias this in their wrangler.toml:
6
+ *
7
+ * [alias]
8
+ * "node:fs/promises" = "worker-fs-mount/fs"
9
+ *
10
+ * This module implements derived operations (readFile, writeFile, truncate,
11
+ * cp, access, rename) on top of the core streaming interface.
12
+ */
13
+ import { Buffer } from 'node:buffer';
14
+ import type { BigIntStats, Dirent, Stats } from 'node:fs';
15
+ import * as nodeFs from 'node:fs';
16
+ declare const realFs: typeof nodeFs.promises;
17
+ export declare function readFile(path: Parameters<typeof realFs.readFile>[0], options?: Parameters<typeof realFs.readFile>[1]): Promise<Buffer | string>;
18
+ export declare function writeFile(path: Parameters<typeof realFs.writeFile>[0], data: Parameters<typeof realFs.writeFile>[1], options?: Parameters<typeof realFs.writeFile>[2]): Promise<void>;
19
+ export declare function appendFile(path: Parameters<typeof realFs.appendFile>[0], data: Parameters<typeof realFs.appendFile>[1], options?: Parameters<typeof realFs.appendFile>[2]): Promise<void>;
20
+ export declare function stat(path: Parameters<typeof realFs.stat>[0], options?: Parameters<typeof realFs.stat>[1]): Promise<Stats | BigIntStats>;
21
+ export declare function lstat(path: Parameters<typeof realFs.lstat>[0], options?: Parameters<typeof realFs.lstat>[1]): Promise<Stats | BigIntStats>;
22
+ export declare function readdir(path: Parameters<typeof realFs.readdir>[0], options?: Parameters<typeof realFs.readdir>[1]): Promise<string[] | Buffer[] | Dirent[]>;
23
+ export declare function mkdir(path: Parameters<typeof realFs.mkdir>[0], options?: Parameters<typeof realFs.mkdir>[1]): Promise<string | undefined>;
24
+ export declare function rm(path: Parameters<typeof realFs.rm>[0], options?: Parameters<typeof realFs.rm>[1]): Promise<void>;
25
+ export declare function rmdir(path: Parameters<typeof realFs.rmdir>[0], options?: {
26
+ maxRetries?: number;
27
+ retryDelay?: number;
28
+ }): Promise<void>;
29
+ export declare function unlink(path: Parameters<typeof realFs.unlink>[0]): Promise<void>;
30
+ export declare function rename(oldPath: Parameters<typeof realFs.rename>[0], newPath: Parameters<typeof realFs.rename>[1]): Promise<void>;
31
+ export declare function copyFile(src: Parameters<typeof realFs.copyFile>[0], dest: Parameters<typeof realFs.copyFile>[1], mode?: Parameters<typeof realFs.copyFile>[2]): Promise<void>;
32
+ export declare function cp(src: Parameters<typeof realFs.cp>[0], dest: Parameters<typeof realFs.cp>[1], options?: Parameters<typeof realFs.cp>[2]): Promise<void>;
33
+ export declare function access(path: Parameters<typeof realFs.access>[0], mode?: Parameters<typeof realFs.access>[1]): Promise<void>;
34
+ export declare function truncate(path: Parameters<typeof realFs.truncate>[0], len?: Parameters<typeof realFs.truncate>[1]): Promise<void>;
35
+ export declare function symlink(target: Parameters<typeof realFs.symlink>[0], path: Parameters<typeof realFs.symlink>[1], type?: Parameters<typeof realFs.symlink>[2]): Promise<void>;
36
+ export declare function readlink(path: Parameters<typeof realFs.readlink>[0], options?: Parameters<typeof realFs.readlink>[1]): Promise<string | Buffer>;
37
+ export declare function realpath(path: Parameters<typeof realFs.realpath>[0], options?: Parameters<typeof realFs.realpath>[1]): Promise<string | Buffer>;
38
+ export declare function utimes(path: Parameters<typeof realFs.utimes>[0], atime: Parameters<typeof realFs.utimes>[1], mtime: Parameters<typeof realFs.utimes>[2]): Promise<void>;
39
+ export declare const chmod: typeof nodeFs.promises.chmod;
40
+ export declare const chown: typeof nodeFs.promises.chown;
41
+ export declare const lchmod: typeof nodeFs.promises.lchmod;
42
+ export declare const lchown: typeof nodeFs.promises.lchown;
43
+ export declare const lutimes: typeof nodeFs.promises.lutimes;
44
+ export declare const link: typeof nodeFs.promises.link;
45
+ export declare const open: typeof nodeFs.promises.open;
46
+ export declare const opendir: typeof nodeFs.promises.opendir;
47
+ export declare const mkdtemp: typeof nodeFs.promises.mkdtemp;
48
+ export declare const watch: typeof nodeFs.promises.watch;
49
+ export declare const constants: typeof nodeFs.constants;
50
+ declare const _default: {
51
+ readFile: typeof readFile;
52
+ writeFile: typeof writeFile;
53
+ appendFile: typeof appendFile;
54
+ stat: typeof stat;
55
+ lstat: typeof lstat;
56
+ readdir: typeof readdir;
57
+ mkdir: typeof mkdir;
58
+ rm: typeof rm;
59
+ rmdir: typeof rmdir;
60
+ unlink: typeof unlink;
61
+ rename: typeof rename;
62
+ copyFile: typeof copyFile;
63
+ cp: typeof cp;
64
+ access: typeof access;
65
+ truncate: typeof truncate;
66
+ symlink: typeof symlink;
67
+ readlink: typeof readlink;
68
+ realpath: typeof realpath;
69
+ utimes: typeof utimes;
70
+ chmod: typeof nodeFs.promises.chmod;
71
+ chown: typeof nodeFs.promises.chown;
72
+ lchmod: typeof nodeFs.promises.lchmod;
73
+ lchown: typeof nodeFs.promises.lchown;
74
+ lutimes: typeof nodeFs.promises.lutimes;
75
+ link: typeof nodeFs.promises.link;
76
+ open: typeof nodeFs.promises.open;
77
+ opendir: typeof nodeFs.promises.opendir;
78
+ mkdtemp: typeof nodeFs.promises.mkdtemp;
79
+ watch: typeof nodeFs.promises.watch;
80
+ constants: typeof nodeFs.constants;
81
+ };
82
+ export default _default;
83
+ //# sourceMappingURL=fs-promises.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-promises.d.ts","sourceRoot":"","sources":["../src/fs-promises.ts"],"names":[],"mappings":"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"}