@b9g/filesystem 0.1.7 → 0.1.8

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 CHANGED
@@ -251,28 +251,28 @@ router.post('/upload', async (request) => {
251
251
  - `ShovelFileHandle` - FileSystemFileHandle implementation
252
252
  - `ShovelDirectoryHandle` - FileSystemDirectoryHandle implementation
253
253
  - `ShovelHandle` - Base handle class
254
- - `CustomBucketStorage` - Bucket storage with custom backend factories
254
+ - `CustomDirectoryStorage` - Directory storage with custom backend factories
255
255
 
256
256
  ### Types
257
257
 
258
- - `Bucket` - Alias for FileSystemDirectoryHandle
259
- - `BucketStorage` - Interface for bucket storage (`open(name): Promise<FileSystemDirectoryHandle>`)
260
- - `BucketFactory` - Factory function type for creating bucket backends
258
+ - `Directory` - Alias for FileSystemDirectoryHandle
259
+ - `DirectoryStorage` - Interface for directory storage (`open(name): Promise<FileSystemDirectoryHandle>`)
260
+ - `DirectoryFactory` - Factory function type for creating directory backends
261
261
  - `FileSystemConfig` - Configuration for filesystem backends
262
262
  - `FileSystemPermissionDescriptor` - Permission descriptor type
263
263
  - `FileSystemBackend` - Backend interface for filesystem implementations
264
264
 
265
265
  ## API Reference
266
266
 
267
- ### BucketStorage
267
+ ### DirectoryStorage
268
268
 
269
269
  ```typescript
270
- interface BucketStorage {
270
+ interface DirectoryStorage {
271
271
  open(name: string): Promise<FileSystemDirectoryHandle>;
272
272
  }
273
273
 
274
- class CustomBucketStorage implements BucketStorage {
275
- register(name: string, factory: BucketFactory): void;
274
+ class CustomDirectoryStorage implements DirectoryStorage {
275
+ register(name: string, factory: DirectoryFactory): void;
276
276
  open(name: string): Promise<FileSystemDirectoryHandle>;
277
277
  }
278
278
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/filesystem",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Universal File System Access API implementations for all platforms",
5
5
  "license": "MIT",
6
6
  "dependencies": {
@@ -50,13 +50,13 @@
50
50
  "types": "./src/memory.d.ts",
51
51
  "import": "./src/memory.js"
52
52
  },
53
- "./node": {
54
- "types": "./src/node.d.ts",
55
- "import": "./src/node.js"
53
+ "./node-fs": {
54
+ "types": "./src/node-fs.d.ts",
55
+ "import": "./src/node-fs.js"
56
56
  },
57
- "./node.js": {
58
- "types": "./src/node.d.ts",
59
- "import": "./src/node.js"
57
+ "./node-fs.js": {
58
+ "types": "./src/node-fs.d.ts",
59
+ "import": "./src/node-fs.js"
60
60
  },
61
61
  "./package.json": "./package.json"
62
62
  }
package/src/bun-s3.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Bun S3 filesystem implementation
3
3
  *
4
- * Provides S3Bucket (root) and S3FileSystemBackend for storage operations
4
+ * Provides S3Directory (root) and S3FileSystemBackend for storage operations
5
5
  * using Bun's native S3 client.
6
6
  */
7
7
  import { type FileSystemBackend } from "./index.js";
@@ -27,16 +27,16 @@ export declare class S3FileSystemBackend implements FileSystemBackend {
27
27
  remove(path: string, recursive?: boolean): Promise<void>;
28
28
  }
29
29
  /**
30
- * S3 bucket - root entry point for S3 filesystem using Bun's S3 client
30
+ * S3 directory - root entry point for S3 filesystem using Bun's S3 client
31
31
  * Implements FileSystemDirectoryHandle for S3 object storage
32
32
  *
33
33
  * Example usage with namespacing:
34
34
  * ```typescript
35
35
  * const s3 = new S3Client({ ... });
36
36
  *
37
- * // Use in CustomBucketStorage factory for multi-tenancy
38
- * const buckets = new CustomBucketStorage(async (name) => {
39
- * return new S3Bucket(
37
+ * // Use in CustomDirectoryStorage factory for multi-tenancy
38
+ * const directories = new CustomDirectoryStorage(async (name) => {
39
+ * return new S3Directory(
40
40
  * s3,
41
41
  * "my-company-bucket",
42
42
  * `my-app/production/${name}` // Prefix for isolation
@@ -44,7 +44,7 @@ export declare class S3FileSystemBackend implements FileSystemBackend {
44
44
  * });
45
45
  * ```
46
46
  */
47
- export declare class S3Bucket implements FileSystemDirectoryHandle {
47
+ export declare class S3Directory implements FileSystemDirectoryHandle {
48
48
  #private;
49
49
  readonly kind: "directory";
50
50
  readonly name: string;
package/src/bun-s3.js CHANGED
@@ -154,8 +154,7 @@ var S3FileSystemBackend = class {
154
154
  }
155
155
  }
156
156
  } catch (error) {
157
- if (error instanceof DOMException)
158
- throw error;
157
+ if (error instanceof DOMException) throw error;
159
158
  throw new DOMException("Entry not found", "NotFoundError");
160
159
  }
161
160
  }
@@ -179,7 +178,7 @@ var S3FileSystemBackend = class {
179
178
  return this.#prefix ? `${this.#prefix}/${cleanPath}` : cleanPath;
180
179
  }
181
180
  };
182
- var S3Bucket = class _S3Bucket {
181
+ var S3Directory = class _S3Directory {
183
182
  kind;
184
183
  name;
185
184
  #backend;
@@ -257,9 +256,8 @@ var S3Bucket = class _S3Bucket {
257
256
  return this.entries();
258
257
  }
259
258
  async isSameEntry(other) {
260
- if (other.kind !== "directory")
261
- return false;
262
- return other instanceof _S3Bucket && other.name === this.name;
259
+ if (other.kind !== "directory") return false;
260
+ return other instanceof _S3Directory && other.name === this.name;
263
261
  }
264
262
  async queryPermission() {
265
263
  return "granted";
@@ -269,6 +267,6 @@ var S3Bucket = class _S3Bucket {
269
267
  }
270
268
  };
271
269
  export {
272
- S3Bucket,
270
+ S3Directory,
273
271
  S3FileSystemBackend
274
272
  };
package/src/index.d.ts CHANGED
@@ -7,11 +7,6 @@ export interface FileSystemConfig {
7
7
  /** Platform-specific configuration */
8
8
  [key: string]: any;
9
9
  }
10
- /**
11
- * Bucket is a semantic alias for FileSystemDirectoryHandle
12
- * Represents a named storage bucket that provides direct filesystem access
13
- */
14
- export type Bucket = FileSystemDirectoryHandle;
15
10
  /**
16
11
  * Permission descriptor for File System Access API
17
12
  */
@@ -84,11 +79,12 @@ export declare abstract class ShovelHandle implements FileSystemHandle {
84
79
  get name(): string;
85
80
  get backend(): FileSystemBackend;
86
81
  isSameEntry(other: FileSystemHandle): Promise<boolean>;
87
- queryPermission(descriptor?: FileSystemPermissionDescriptor): Promise<PermissionState>;
88
- requestPermission(descriptor?: FileSystemPermissionDescriptor): Promise<PermissionState>;
82
+ queryPermission(_descriptor?: FileSystemPermissionDescriptor): Promise<PermissionState>;
83
+ requestPermission(_descriptor?: FileSystemPermissionDescriptor): Promise<PermissionState>;
89
84
  /**
90
85
  * Validates that a name is actually a name and not a path
91
86
  * The File System Access API only accepts names, not paths
87
+ * WPT expects TypeError for invalid names
92
88
  */
93
89
  validateName(name: string): void;
94
90
  }
@@ -134,82 +130,81 @@ export declare class ShovelDirectoryHandle extends ShovelHandle implements FileS
134
130
  ]>;
135
131
  }
136
132
  /**
137
- * Bucket storage interface - parallels CacheStorage for filesystem access
138
- * This could become a future web standard
133
+ * Directory storage interface - parallels CacheStorage for filesystem access
139
134
  */
140
- export interface BucketStorage {
135
+ export interface DirectoryStorage {
141
136
  /**
142
- * Open a named bucket - returns FileSystemDirectoryHandle (root of that bucket)
137
+ * Open a named directory - returns FileSystemDirectoryHandle (root)
143
138
  * Well-known names: 'static', 'tmp'
144
139
  */
145
140
  open(name: string): Promise<FileSystemDirectoryHandle>;
146
141
  /**
147
- * Check if a named bucket exists
142
+ * Check if a named directory exists
148
143
  */
149
144
  has(name: string): Promise<boolean>;
150
145
  /**
151
- * Delete a named bucket and all its contents
146
+ * Delete a named directory and all its contents
152
147
  */
153
148
  delete(name: string): Promise<boolean>;
154
149
  /**
155
- * List all available bucket names
150
+ * List all available directory names
156
151
  */
157
152
  keys(): Promise<string[]>;
158
153
  }
159
154
  /**
160
- * Factory function type for creating buckets
161
- * @param name Bucket name to create
162
- * @returns FileSystemDirectoryHandle (Bucket) instance
155
+ * Factory function type for creating directories
156
+ * @param name Directory name to create
157
+ * @returns FileSystemDirectoryHandle instance
163
158
  */
164
- export type BucketFactory = (name: string) => FileSystemDirectoryHandle | Promise<FileSystemDirectoryHandle>;
159
+ export type DirectoryFactory = (name: string) => FileSystemDirectoryHandle | Promise<FileSystemDirectoryHandle>;
165
160
  /**
166
- * Custom bucket storage with factory-based bucket creation
161
+ * Custom directory storage with factory-based directory creation
167
162
  *
168
- * Provides a registry of named buckets (FileSystemDirectoryHandle instances)
169
- * with lazy instantiation and singleton behavior per bucket name.
163
+ * Provides a registry of named directories (FileSystemDirectoryHandle instances)
164
+ * with lazy instantiation and singleton behavior per directory name.
170
165
  *
171
166
  * Mirrors the CustomCacheStorage pattern for consistency across the platform.
172
167
  */
173
- export declare class CustomBucketStorage {
168
+ export declare class CustomDirectoryStorage {
174
169
  #private;
175
170
  /**
176
- * @param factory Function that creates bucket instances by name
171
+ * @param factory Function that creates directory instances by name
177
172
  */
178
- constructor(factory: BucketFactory);
173
+ constructor(factory: DirectoryFactory);
179
174
  /**
180
- * Open a named bucket - creates if it doesn't exist
175
+ * Open a named directory - creates if it doesn't exist
181
176
  *
182
- * @param name Bucket name (e.g., 'tmp', 'dist', 'uploads')
183
- * @returns FileSystemDirectoryHandle for the bucket
177
+ * @param name Directory name (e.g., 'tmp', 'dist', 'uploads')
178
+ * @returns FileSystemDirectoryHandle for the directory
184
179
  */
185
180
  open(name: string): Promise<FileSystemDirectoryHandle>;
186
181
  /**
187
- * Check if a named bucket exists
182
+ * Check if a named directory exists
188
183
  *
189
- * @param name Bucket name to check
190
- * @returns true if bucket has been opened
184
+ * @param name Directory name to check
185
+ * @returns true if directory has been opened
191
186
  */
192
187
  has(name: string): Promise<boolean>;
193
188
  /**
194
- * Delete a named bucket
189
+ * Delete a named directory
195
190
  *
196
- * @param name Bucket name to delete
197
- * @returns true if bucket was deleted, false if it didn't exist
191
+ * @param name Directory name to delete
192
+ * @returns true if directory was deleted, false if it didn't exist
198
193
  */
199
194
  delete(name: string): Promise<boolean>;
200
195
  /**
201
- * List all opened bucket names
196
+ * List all opened directory names
202
197
  *
203
- * @returns Array of bucket names
198
+ * @returns Array of directory names
204
199
  */
205
200
  keys(): Promise<string[]>;
206
201
  /**
207
- * Get statistics about opened buckets (non-standard utility method)
202
+ * Get statistics about opened directories (non-standard utility method)
208
203
  *
209
- * @returns Object with bucket statistics
204
+ * @returns Object with directory statistics
210
205
  */
211
206
  getStats(): {
212
207
  openInstances: number;
213
- bucketNames: string[];
208
+ directoryNames: string[];
214
209
  };
215
210
  }
package/src/index.js CHANGED
@@ -41,11 +41,29 @@ var ShovelWritableFileStream = class extends WritableStream {
41
41
  try {
42
42
  if (typeof data === "string") {
43
43
  await writer.write(new TextEncoder().encode(data));
44
- } else if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
45
- const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
46
- await writer.write(bytes);
44
+ } else if (data instanceof Uint8Array) {
45
+ await writer.write(data);
46
+ } else if (data instanceof ArrayBuffer) {
47
+ await writer.write(new Uint8Array(data));
48
+ } else if (data instanceof Blob) {
49
+ const buffer = await data.arrayBuffer();
50
+ await writer.write(new Uint8Array(buffer));
51
+ } else if (ArrayBuffer.isView(data)) {
52
+ await writer.write(
53
+ new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
54
+ );
55
+ } else if (typeof data === "object" && data !== null && "type" in data) {
56
+ const params = data;
57
+ if (params.type === "write" && params.data !== void 0) {
58
+ await this.write(params.data);
59
+ return;
60
+ }
61
+ throw new DOMException("Invalid write params", "NotSupportedError");
47
62
  } else {
48
- await writer.write(new TextEncoder().encode(String(data)));
63
+ throw new DOMException(
64
+ "Invalid data type for write",
65
+ "NotSupportedError"
66
+ );
49
67
  }
50
68
  } finally {
51
69
  writer.releaseLock();
@@ -78,36 +96,30 @@ var ShovelHandle = class _ShovelHandle {
78
96
  return this.#backend;
79
97
  }
80
98
  async isSameEntry(other) {
81
- if (other.kind !== this.kind)
82
- return false;
83
- if (!(other instanceof _ShovelHandle))
84
- return false;
99
+ if (other.kind !== this.kind) return false;
100
+ if (!(other instanceof _ShovelHandle)) return false;
85
101
  return this.path === other.path;
86
102
  }
87
- async queryPermission(descriptor) {
88
- const _mode = descriptor?.mode || "read";
103
+ async queryPermission(_descriptor) {
89
104
  return "granted";
90
105
  }
91
- async requestPermission(descriptor) {
92
- const _mode = descriptor?.mode || "read";
106
+ async requestPermission(_descriptor) {
93
107
  return "granted";
94
108
  }
95
109
  /**
96
110
  * Validates that a name is actually a name and not a path
97
111
  * The File System Access API only accepts names, not paths
112
+ * WPT expects TypeError for invalid names
98
113
  */
99
114
  validateName(name) {
100
115
  if (!name || name.trim() === "") {
101
- throw new DOMException("Name cannot be empty", "NotAllowedError");
116
+ throw new TypeError("Name cannot be empty");
102
117
  }
103
118
  if (name.includes("/") || name.includes("\\")) {
104
- throw new DOMException(
105
- "Name cannot contain path separators",
106
- "NotAllowedError"
107
- );
119
+ throw new TypeError("Name cannot contain path separators");
108
120
  }
109
121
  if (name === "." || name === "..") {
110
- throw new DOMException("Name cannot be '.' or '..'", "NotAllowedError");
122
+ throw new TypeError("Name cannot be '.' or '..'");
111
123
  }
112
124
  }
113
125
  };
@@ -249,45 +261,45 @@ var ShovelDirectoryHandle = class _ShovelDirectoryHandle extends ShovelHandle {
249
261
  return `${base}/${name}`;
250
262
  }
251
263
  };
252
- var CustomBucketStorage = class {
264
+ var CustomDirectoryStorage = class {
253
265
  #instances;
254
266
  #factory;
255
267
  /**
256
- * @param factory Function that creates bucket instances by name
268
+ * @param factory Function that creates directory instances by name
257
269
  */
258
270
  constructor(factory) {
259
271
  this.#instances = /* @__PURE__ */ new Map();
260
272
  this.#factory = factory;
261
273
  }
262
274
  /**
263
- * Open a named bucket - creates if it doesn't exist
275
+ * Open a named directory - creates if it doesn't exist
264
276
  *
265
- * @param name Bucket name (e.g., 'tmp', 'dist', 'uploads')
266
- * @returns FileSystemDirectoryHandle for the bucket
277
+ * @param name Directory name (e.g., 'tmp', 'dist', 'uploads')
278
+ * @returns FileSystemDirectoryHandle for the directory
267
279
  */
268
280
  async open(name) {
269
281
  const existing = this.#instances.get(name);
270
282
  if (existing) {
271
283
  return existing;
272
284
  }
273
- const bucket = await this.#factory(name);
274
- this.#instances.set(name, bucket);
275
- return bucket;
285
+ const dir = await this.#factory(name);
286
+ this.#instances.set(name, dir);
287
+ return dir;
276
288
  }
277
289
  /**
278
- * Check if a named bucket exists
290
+ * Check if a named directory exists
279
291
  *
280
- * @param name Bucket name to check
281
- * @returns true if bucket has been opened
292
+ * @param name Directory name to check
293
+ * @returns true if directory has been opened
282
294
  */
283
295
  async has(name) {
284
296
  return this.#instances.has(name);
285
297
  }
286
298
  /**
287
- * Delete a named bucket
299
+ * Delete a named directory
288
300
  *
289
- * @param name Bucket name to delete
290
- * @returns true if bucket was deleted, false if it didn't exist
301
+ * @param name Directory name to delete
302
+ * @returns true if directory was deleted, false if it didn't exist
291
303
  */
292
304
  async delete(name) {
293
305
  const instance = this.#instances.get(name);
@@ -298,27 +310,27 @@ var CustomBucketStorage = class {
298
310
  return false;
299
311
  }
300
312
  /**
301
- * List all opened bucket names
313
+ * List all opened directory names
302
314
  *
303
- * @returns Array of bucket names
315
+ * @returns Array of directory names
304
316
  */
305
317
  async keys() {
306
318
  return Array.from(this.#instances.keys());
307
319
  }
308
320
  /**
309
- * Get statistics about opened buckets (non-standard utility method)
321
+ * Get statistics about opened directories (non-standard utility method)
310
322
  *
311
- * @returns Object with bucket statistics
323
+ * @returns Object with directory statistics
312
324
  */
313
325
  getStats() {
314
326
  return {
315
327
  openInstances: this.#instances.size,
316
- bucketNames: Array.from(this.#instances.keys())
328
+ directoryNames: Array.from(this.#instances.keys())
317
329
  };
318
330
  }
319
331
  };
320
332
  export {
321
- CustomBucketStorage,
333
+ CustomDirectoryStorage,
322
334
  ShovelDirectoryHandle,
323
335
  ShovelFileHandle,
324
336
  ShovelHandle
package/src/memory.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * In-memory filesystem implementation
3
3
  *
4
- * Provides MemoryBucket (root) and MemoryFileSystemBackend for storage operations
4
+ * Provides MemoryDirectory (root) and MemoryFileSystemBackend for storage operations
5
5
  * using in-memory data structures.
6
6
  */
7
7
  import { type FileSystemBackend } from "./index.js";
@@ -15,19 +15,19 @@ interface MemoryFile {
15
15
  type: string;
16
16
  }
17
17
  /**
18
- * In-memory directory data
18
+ * In-memory directory data (internal structure)
19
19
  */
20
- interface MemoryDirectory {
20
+ interface MemoryDirectoryData {
21
21
  name: string;
22
22
  files: Map<string, MemoryFile>;
23
- directories: Map<string, MemoryDirectory>;
23
+ directories: Map<string, MemoryDirectoryData>;
24
24
  }
25
25
  /**
26
26
  * In-memory storage backend that implements FileSystemBackend
27
27
  */
28
28
  export declare class MemoryFileSystemBackend implements FileSystemBackend {
29
29
  #private;
30
- constructor(root: MemoryDirectory);
30
+ constructor(root: MemoryDirectoryData);
31
31
  stat(path: string): Promise<{
32
32
  kind: "file" | "directory";
33
33
  } | null>;
@@ -44,10 +44,10 @@ export declare class MemoryFileSystemBackend implements FileSystemBackend {
44
44
  remove(path: string, recursive?: boolean): Promise<void>;
45
45
  }
46
46
  /**
47
- * Memory bucket - root entry point for in-memory filesystem
47
+ * Memory directory - root entry point for in-memory filesystem
48
48
  * Implements FileSystemDirectoryHandle and owns the root data structure
49
49
  */
50
- export declare class MemoryBucket implements FileSystemDirectoryHandle {
50
+ export declare class MemoryDirectory implements FileSystemDirectoryHandle {
51
51
  #private;
52
52
  readonly kind: "directory";
53
53
  readonly name: string;
package/src/memory.js CHANGED
@@ -11,8 +11,7 @@ var MemoryFileSystemBackend = class {
11
11
  }
12
12
  async stat(path) {
13
13
  const entry = this.#resolvePath(path);
14
- if (!entry)
15
- return null;
14
+ if (!entry) return null;
16
15
  if ("content" in entry) {
17
16
  return { kind: "file" };
18
17
  } else {
@@ -115,17 +114,14 @@ var MemoryFileSystemBackend = class {
115
114
  let current = this.#root;
116
115
  for (let i = 0; i < parts.length - 1; i++) {
117
116
  const nextDir = current.directories.get(parts[i]);
118
- if (!nextDir)
119
- return null;
117
+ if (!nextDir) return null;
120
118
  current = nextDir;
121
119
  }
122
120
  const finalName = parts[parts.length - 1];
123
121
  const file = current.files.get(finalName);
124
- if (file)
125
- return file;
122
+ if (file) return file;
126
123
  const dir = current.directories.get(finalName);
127
- if (dir)
128
- return dir;
124
+ if (dir) return dir;
129
125
  return null;
130
126
  }
131
127
  #resolveParent(path) {
@@ -145,7 +141,7 @@ var MemoryFileSystemBackend = class {
145
141
  return { parentDir: current, name };
146
142
  }
147
143
  };
148
- var MemoryBucket = class _MemoryBucket {
144
+ var MemoryDirectory = class _MemoryDirectory {
149
145
  kind;
150
146
  name;
151
147
  #backend;
@@ -160,6 +156,15 @@ var MemoryBucket = class _MemoryBucket {
160
156
  this.#backend = new MemoryFileSystemBackend(root);
161
157
  }
162
158
  async getFileHandle(name, options) {
159
+ if (!name || name.trim() === "") {
160
+ throw new TypeError("Name cannot be empty");
161
+ }
162
+ if (name.includes("/") || name.includes("\\")) {
163
+ throw new TypeError("Name cannot contain path separators");
164
+ }
165
+ if (name === "." || name === "..") {
166
+ throw new TypeError("Name cannot be '.' or '..'");
167
+ }
163
168
  const filePath = `/${name}`;
164
169
  const stat = await this.#backend.stat(filePath);
165
170
  if (!stat && options?.create) {
@@ -175,6 +180,15 @@ var MemoryBucket = class _MemoryBucket {
175
180
  return new ShovelFileHandle(this.#backend, filePath);
176
181
  }
177
182
  async getDirectoryHandle(name, options) {
183
+ if (!name || name.trim() === "") {
184
+ throw new TypeError("Name cannot be empty");
185
+ }
186
+ if (name.includes("/") || name.includes("\\")) {
187
+ throw new TypeError("Name cannot contain path separators");
188
+ }
189
+ if (name === "." || name === "..") {
190
+ throw new TypeError("Name cannot be '.' or '..'");
191
+ }
178
192
  const dirPath = `/${name}`;
179
193
  const stat = await this.#backend.stat(dirPath);
180
194
  if (!stat && options?.create) {
@@ -194,6 +208,12 @@ var MemoryBucket = class _MemoryBucket {
194
208
  await this.#backend.remove(entryPath, options?.recursive);
195
209
  }
196
210
  async resolve(possibleDescendant) {
211
+ if (possibleDescendant instanceof _MemoryDirectory) {
212
+ if (await this.isSameEntry(possibleDescendant)) {
213
+ return [];
214
+ }
215
+ return null;
216
+ }
197
217
  if (!(possibleDescendant instanceof ShovelDirectoryHandle || possibleDescendant instanceof ShovelFileHandle)) {
198
218
  return null;
199
219
  }
@@ -228,9 +248,8 @@ var MemoryBucket = class _MemoryBucket {
228
248
  return this.entries();
229
249
  }
230
250
  async isSameEntry(other) {
231
- if (other.kind !== "directory")
232
- return false;
233
- return other instanceof _MemoryBucket && other.name === this.name;
251
+ if (other.kind !== "directory") return false;
252
+ return other instanceof _MemoryDirectory && other.name === this.name;
234
253
  }
235
254
  async queryPermission() {
236
255
  return "granted";
@@ -240,6 +259,6 @@ var MemoryBucket = class _MemoryBucket {
240
259
  }
241
260
  };
242
261
  export {
243
- MemoryBucket,
262
+ MemoryDirectory,
244
263
  MemoryFileSystemBackend
245
264
  };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Node.js filesystem implementation using node:fs
3
+ *
4
+ * Provides NodeFSDirectory (root) and NodeFSBackend for storage operations
5
+ * using Node.js fs module. Works in both Node.js and Bun.
6
+ */
7
+ import { type FileSystemBackend, ShovelDirectoryHandle } from "./index.js";
8
+ /**
9
+ * Node.js storage backend using node:fs
10
+ */
11
+ export declare class NodeFSBackend implements FileSystemBackend {
12
+ #private;
13
+ constructor(rootPath: string);
14
+ stat(filePath: string): Promise<{
15
+ kind: "file" | "directory";
16
+ } | null>;
17
+ readFile(filePath: string): Promise<{
18
+ content: Uint8Array;
19
+ lastModified?: number;
20
+ }>;
21
+ writeFile(filePath: string, data: Uint8Array): Promise<void>;
22
+ listDir(dirPath: string): Promise<Array<{
23
+ name: string;
24
+ kind: "file" | "directory";
25
+ }>>;
26
+ createDir(dirPath: string): Promise<void>;
27
+ remove(entryPath: string, recursive?: boolean): Promise<void>;
28
+ }
29
+ /**
30
+ * Node.js directory using node:fs - root entry point for local filesystem
31
+ * Extends ShovelDirectoryHandle with "/" as root path
32
+ */
33
+ export declare class NodeFSDirectory extends ShovelDirectoryHandle {
34
+ #private;
35
+ /**
36
+ * Create a NodeFSDirectory
37
+ * @param name - Directory name (used for display)
38
+ * @param options - Options object containing the filesystem path
39
+ * @param options.path - The actual filesystem path to use as root
40
+ */
41
+ constructor(name: string, options?: {
42
+ path?: string;
43
+ });
44
+ get name(): string;
45
+ }
@@ -1,9 +1,12 @@
1
- /// <reference types="./node.d.ts" />
2
- // src/node.ts
1
+ /// <reference types="./node-fs.d.ts" />
2
+ // src/node-fs.ts
3
3
  import { ShovelDirectoryHandle } from "./index.js";
4
4
  import * as FS from "fs/promises";
5
5
  import * as Path from "path";
6
- var NodeFileSystemBackend = class {
6
+ function isErrnoException(error) {
7
+ return error instanceof Error && "code" in error;
8
+ }
9
+ var NodeFSBackend = class {
7
10
  #rootPath;
8
11
  constructor(rootPath) {
9
12
  this.#rootPath = rootPath;
@@ -20,7 +23,7 @@ var NodeFileSystemBackend = class {
20
23
  return null;
21
24
  }
22
25
  } catch (error) {
23
- if (error.code === "ENOENT") {
26
+ if (isErrnoException(error) && error.code === "ENOENT") {
24
27
  return null;
25
28
  }
26
29
  throw error;
@@ -38,7 +41,7 @@ var NodeFileSystemBackend = class {
38
41
  lastModified: stats.mtimeMs
39
42
  };
40
43
  } catch (error) {
41
- if (error.code === "ENOENT") {
44
+ if (isErrnoException(error) && error.code === "ENOENT") {
42
45
  throw new DOMException("File not found", "NotFoundError");
43
46
  }
44
47
  throw error;
@@ -70,7 +73,7 @@ var NodeFileSystemBackend = class {
70
73
  }
71
74
  return results;
72
75
  } catch (error) {
73
- if (error.code === "ENOENT") {
76
+ if (isErrnoException(error) && error.code === "ENOENT") {
74
77
  throw new DOMException("Directory not found", "NotFoundError");
75
78
  }
76
79
  throw error;
@@ -108,7 +111,7 @@ var NodeFileSystemBackend = class {
108
111
  }
109
112
  }
110
113
  } catch (error) {
111
- if (error.code === "ENOENT") {
114
+ if (isErrnoException(error) && error.code === "ENOENT") {
112
115
  throw new DOMException("Entry not found", "NotFoundError");
113
116
  }
114
117
  throw error;
@@ -135,10 +138,17 @@ var NodeFileSystemBackend = class {
135
138
  return resolvedPath;
136
139
  }
137
140
  };
138
- var NodeBucket = class extends ShovelDirectoryHandle {
141
+ var NodeFSDirectory = class extends ShovelDirectoryHandle {
139
142
  #rootPath;
140
- constructor(rootPath) {
141
- super(new NodeFileSystemBackend(rootPath), "/");
143
+ /**
144
+ * Create a NodeFSDirectory
145
+ * @param name - Directory name (used for display)
146
+ * @param options - Options object containing the filesystem path
147
+ * @param options.path - The actual filesystem path to use as root
148
+ */
149
+ constructor(name, options) {
150
+ const rootPath = options?.path ?? name;
151
+ super(new NodeFSBackend(rootPath), "/");
142
152
  this.#rootPath = rootPath;
143
153
  }
144
154
  // Override name to use the directory basename instead of "/"
@@ -147,6 +157,6 @@ var NodeBucket = class extends ShovelDirectoryHandle {
147
157
  }
148
158
  };
149
159
  export {
150
- NodeBucket,
151
- NodeFileSystemBackend
160
+ NodeFSBackend,
161
+ NodeFSDirectory
152
162
  };
package/src/node.d.ts DELETED
@@ -1,37 +0,0 @@
1
- /**
2
- * Node.js filesystem implementation
3
- *
4
- * Provides NodeBucket (root) and NodeFileSystemBackend for storage operations
5
- * using Node.js fs module.
6
- */
7
- import { type FileSystemBackend, ShovelDirectoryHandle } from "./index.js";
8
- /**
9
- * Node.js storage backend that implements FileSystemBackend
10
- */
11
- export declare class NodeFileSystemBackend implements FileSystemBackend {
12
- #private;
13
- constructor(rootPath: string);
14
- stat(filePath: string): Promise<{
15
- kind: "file" | "directory";
16
- } | null>;
17
- readFile(filePath: string): Promise<{
18
- content: Uint8Array;
19
- lastModified?: number;
20
- }>;
21
- writeFile(filePath: string, data: Uint8Array): Promise<void>;
22
- listDir(dirPath: string): Promise<Array<{
23
- name: string;
24
- kind: "file" | "directory";
25
- }>>;
26
- createDir(dirPath: string): Promise<void>;
27
- remove(entryPath: string, recursive?: boolean): Promise<void>;
28
- }
29
- /**
30
- * Node bucket - root entry point for Node.js filesystem
31
- * Extends ShovelDirectoryHandle with "/" as root path
32
- */
33
- export declare class NodeBucket extends ShovelDirectoryHandle {
34
- #private;
35
- constructor(rootPath: string);
36
- get name(): string;
37
- }