@b9g/filesystem 0.1.1 → 0.1.4
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 +339 -0
- package/package.json +18 -4
- package/src/bun-s3.d.ts +4 -4
- package/src/bun-s3.js +5 -5
- package/src/directory-storage.d.ts +50 -0
- package/src/directory-storage.js +74 -0
- package/src/index.d.ts +6 -5
- package/src/index.js +11 -7
- package/src/memory.d.ts +4 -4
- package/src/memory.js +7 -6
- package/src/node.d.ts +4 -4
- package/src/node.js +6 -6
- package/src/registry.d.ts +11 -2
- package/src/registry.js +23 -7
- package/src/types.d.ts +7 -7
package/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# @b9g/filesystem
|
|
2
|
+
|
|
3
|
+
Universal filesystem abstraction with multiple backend implementations for different runtimes and storage systems.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Universal API**: Same interface across Node.js, Bun, browsers, and edge platforms
|
|
8
|
+
- **Multiple Backends**: Memory, Node.js fs, Bun, S3, R2, and more
|
|
9
|
+
- **Registry Pattern**: Register filesystem implementations by name
|
|
10
|
+
- **Async/Await**: Promise-based API throughout
|
|
11
|
+
- **Directory Handles**: File System Access API compatible
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @b9g/filesystem
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import { FilesystemRegistry, MemoryFilesystem } from '@b9g/filesystem';
|
|
23
|
+
|
|
24
|
+
// Create registry
|
|
25
|
+
const registry = new FilesystemRegistry();
|
|
26
|
+
|
|
27
|
+
// Register backends
|
|
28
|
+
registry.register('memory', () => new MemoryFilesystem());
|
|
29
|
+
registry.register('temp', () => new NodeFilesystem('/tmp'));
|
|
30
|
+
|
|
31
|
+
// Get filesystem
|
|
32
|
+
const fs = await registry.get('memory');
|
|
33
|
+
|
|
34
|
+
// Create directory
|
|
35
|
+
const dir = await fs.getDirectoryHandle('projects', { create: true });
|
|
36
|
+
|
|
37
|
+
// Create file
|
|
38
|
+
const file = await dir.getFileHandle('readme.txt', { create: true });
|
|
39
|
+
|
|
40
|
+
// Write content
|
|
41
|
+
const writable = await file.createWritable();
|
|
42
|
+
await writable.write('Hello World!');
|
|
43
|
+
await writable.close();
|
|
44
|
+
|
|
45
|
+
// Read content
|
|
46
|
+
const fileData = await file.getFile();
|
|
47
|
+
const text = await fileData.text();
|
|
48
|
+
console.log(text); // 'Hello World!'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Filesystem Implementations
|
|
52
|
+
|
|
53
|
+
### MemoryFilesystem
|
|
54
|
+
|
|
55
|
+
In-memory filesystem for testing and temporary storage:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import { MemoryFilesystem } from '@b9g/filesystem';
|
|
59
|
+
|
|
60
|
+
const fs = new MemoryFilesystem();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### NodeFilesystem
|
|
64
|
+
|
|
65
|
+
Node.js filesystem backend:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
import { NodeFilesystem } from '@b9g/filesystem';
|
|
69
|
+
|
|
70
|
+
const fs = new NodeFilesystem('/app/data');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### BunS3Filesystem
|
|
74
|
+
|
|
75
|
+
Bun with S3-compatible storage:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import { BunS3Filesystem } from '@b9g/filesystem';
|
|
79
|
+
|
|
80
|
+
const fs = new BunS3Filesystem({
|
|
81
|
+
bucket: 'my-bucket',
|
|
82
|
+
region: 'us-east-1',
|
|
83
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
84
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Registry Usage
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
import {
|
|
92
|
+
FilesystemRegistry,
|
|
93
|
+
MemoryFilesystem,
|
|
94
|
+
NodeFilesystem
|
|
95
|
+
} from '@b9g/filesystem';
|
|
96
|
+
|
|
97
|
+
const registry = new FilesystemRegistry();
|
|
98
|
+
|
|
99
|
+
// Register implementations
|
|
100
|
+
registry.register('memory', () => new MemoryFilesystem());
|
|
101
|
+
registry.register('local', () => new NodeFilesystem('./data'));
|
|
102
|
+
registry.register('temp', () => new NodeFilesystem('/tmp'));
|
|
103
|
+
|
|
104
|
+
// Use by name
|
|
105
|
+
const memFs = await registry.get('memory');
|
|
106
|
+
const localFs = await registry.get('local');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## File System Access API
|
|
110
|
+
|
|
111
|
+
Compatible with the File System Access API:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
// Get directory handle
|
|
115
|
+
const dirHandle = await fs.getDirectoryHandle('uploads', { create: true });
|
|
116
|
+
|
|
117
|
+
// List directory contents
|
|
118
|
+
for await (const [name, handle] of dirHandle.entries()) {
|
|
119
|
+
if (handle.kind === 'file') {
|
|
120
|
+
console.log(`File: ${name}`);
|
|
121
|
+
} else {
|
|
122
|
+
console.log(`Directory: ${name}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Get file handle
|
|
127
|
+
const fileHandle = await dirHandle.getFileHandle('data.json', { create: true });
|
|
128
|
+
|
|
129
|
+
// Check if exists
|
|
130
|
+
try {
|
|
131
|
+
await dirHandle.getFileHandle('missing.txt');
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.log('File does not exist');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Remove file
|
|
137
|
+
await dirHandle.removeEntry('old-file.txt');
|
|
138
|
+
|
|
139
|
+
// Remove directory
|
|
140
|
+
await dirHandle.removeEntry('old-dir', { recursive: true });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## File Operations
|
|
144
|
+
|
|
145
|
+
### Writing Files
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Get file handle
|
|
149
|
+
const file = await dir.getFileHandle('config.json', { create: true });
|
|
150
|
+
|
|
151
|
+
// Create writable stream
|
|
152
|
+
const writable = await file.createWritable();
|
|
153
|
+
|
|
154
|
+
// Write data
|
|
155
|
+
await writable.write(JSON.stringify({ setting: 'value' }));
|
|
156
|
+
await writable.close();
|
|
157
|
+
|
|
158
|
+
// Or write all at once
|
|
159
|
+
await writable.write(new Blob(['Hello World']));
|
|
160
|
+
await writable.close();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Reading Files
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// Get file handle
|
|
167
|
+
const file = await dir.getFileHandle('config.json');
|
|
168
|
+
|
|
169
|
+
// Get file object
|
|
170
|
+
const fileData = await file.getFile();
|
|
171
|
+
|
|
172
|
+
// Read as text
|
|
173
|
+
const text = await fileData.text();
|
|
174
|
+
|
|
175
|
+
// Read as JSON
|
|
176
|
+
const json = JSON.parse(text);
|
|
177
|
+
|
|
178
|
+
// Read as ArrayBuffer
|
|
179
|
+
const buffer = await fileData.arrayBuffer();
|
|
180
|
+
|
|
181
|
+
// Read as stream
|
|
182
|
+
const stream = fileData.stream();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Integration Examples
|
|
186
|
+
|
|
187
|
+
### Cache Storage
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
import { FilesystemRegistry, NodeFilesystem } from '@b9g/filesystem';
|
|
191
|
+
|
|
192
|
+
const registry = new FilesystemRegistry();
|
|
193
|
+
registry.register('cache', () => new NodeFilesystem('./cache'));
|
|
194
|
+
|
|
195
|
+
// Use with cache
|
|
196
|
+
const fs = await registry.get('cache');
|
|
197
|
+
const cacheDir = await fs.getDirectoryHandle('pages', { create: true });
|
|
198
|
+
|
|
199
|
+
// Store cached response
|
|
200
|
+
const file = await cacheDir.getFileHandle('index.html', { create: true });
|
|
201
|
+
const writable = await file.createWritable();
|
|
202
|
+
await writable.write('<html>...</html>');
|
|
203
|
+
await writable.close();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Asset Pipeline
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Static assets filesystem
|
|
210
|
+
registry.register('assets', () => new NodeFilesystem('./dist/assets'));
|
|
211
|
+
|
|
212
|
+
const assets = await registry.get('assets');
|
|
213
|
+
const staticDir = await assets.getDirectoryHandle('static', { create: true });
|
|
214
|
+
|
|
215
|
+
// Copy build assets
|
|
216
|
+
for (const asset of buildAssets) {
|
|
217
|
+
const file = await staticDir.getFileHandle(asset.name, { create: true });
|
|
218
|
+
const writable = await file.createWritable();
|
|
219
|
+
await writable.write(asset.content);
|
|
220
|
+
await writable.close();
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Upload Handling
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
router.post('/upload', async (request) => {
|
|
228
|
+
const formData = await request.formData();
|
|
229
|
+
const file = formData.get('file');
|
|
230
|
+
|
|
231
|
+
if (file) {
|
|
232
|
+
const uploads = await registry.get('uploads');
|
|
233
|
+
const uploadsDir = await uploads.getDirectoryHandle('files', { create: true });
|
|
234
|
+
|
|
235
|
+
const fileHandle = await uploadsDir.getFileHandle(file.name, { create: true });
|
|
236
|
+
const writable = await fileHandle.createWritable();
|
|
237
|
+
await writable.write(await file.arrayBuffer());
|
|
238
|
+
await writable.close();
|
|
239
|
+
|
|
240
|
+
return Response.json({ success: true, filename: file.name });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return BadRequest('No file provided');
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## API Reference
|
|
248
|
+
|
|
249
|
+
### FilesystemRegistry
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
class FilesystemRegistry {
|
|
253
|
+
register(name: string, factory: () => Filesystem): void;
|
|
254
|
+
get(name: string): Promise<Filesystem>;
|
|
255
|
+
has(name: string): boolean;
|
|
256
|
+
delete(name: string): boolean;
|
|
257
|
+
keys(): string[];
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Filesystem Interface
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
interface Filesystem {
|
|
265
|
+
getDirectoryHandle(name: string, options?: GetDirectoryOptions): Promise<DirectoryHandle>;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
interface DirectoryHandle {
|
|
269
|
+
kind: 'directory';
|
|
270
|
+
name: string;
|
|
271
|
+
|
|
272
|
+
getDirectoryHandle(name: string, options?: GetDirectoryOptions): Promise<DirectoryHandle>;
|
|
273
|
+
getFileHandle(name: string, options?: GetFileOptions): Promise<FileHandle>;
|
|
274
|
+
removeEntry(name: string, options?: RemoveOptions): Promise<void>;
|
|
275
|
+
entries(): AsyncIterableIterator<[string, DirectoryHandle | FileHandle]>;
|
|
276
|
+
keys(): AsyncIterableIterator<string>;
|
|
277
|
+
values(): AsyncIterableIterator<DirectoryHandle | FileHandle>;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface FileHandle {
|
|
281
|
+
kind: 'file';
|
|
282
|
+
name: string;
|
|
283
|
+
|
|
284
|
+
getFile(): Promise<File>;
|
|
285
|
+
createWritable(options?: CreateWritableOptions): Promise<WritableStream>;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Backend-Specific Options
|
|
290
|
+
|
|
291
|
+
### S3 Configuration
|
|
292
|
+
|
|
293
|
+
```javascript
|
|
294
|
+
import { BunS3Filesystem } from '@b9g/filesystem';
|
|
295
|
+
|
|
296
|
+
const s3fs = new BunS3Filesystem({
|
|
297
|
+
bucket: 'my-bucket',
|
|
298
|
+
region: 'us-west-2',
|
|
299
|
+
endpoint: 'https://s3.us-west-2.amazonaws.com',
|
|
300
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
301
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
302
|
+
pathStyle: false,
|
|
303
|
+
prefix: 'app-data/'
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Memory Options
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
import { MemoryFilesystem } from '@b9g/filesystem';
|
|
311
|
+
|
|
312
|
+
const memfs = new MemoryFilesystem({
|
|
313
|
+
maxSize: 100 * 1024 * 1024, // 100MB limit
|
|
314
|
+
caseSensitive: true
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Error Handling
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
try {
|
|
322
|
+
const file = await dir.getFileHandle('missing.txt');
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (error.name === 'NotFoundError') {
|
|
325
|
+
console.log('File not found');
|
|
326
|
+
} else {
|
|
327
|
+
console.error('Unexpected error:', error);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check before accessing
|
|
332
|
+
const exists = await dir.getFileHandle('data.txt')
|
|
333
|
+
.then(() => true)
|
|
334
|
+
.catch(() => false);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/filesystem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Universal File System Access API implementations for all platforms",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@b9g/libuild": "^0.1.
|
|
7
|
+
"@b9g/libuild": "^0.1.11",
|
|
8
8
|
"@types/bun": "^1.2.2"
|
|
9
9
|
},
|
|
10
10
|
"peerDependencies": {
|
|
11
|
-
"@types/wicg-file-system-access": "^2023.10.7"
|
|
11
|
+
"@types/wicg-file-system-access": "^2023.10.7",
|
|
12
|
+
"@types/node": "^18.0.0"
|
|
13
|
+
},
|
|
14
|
+
"peerDependenciesMeta": {
|
|
15
|
+
"@types/node": {
|
|
16
|
+
"optional": true
|
|
17
|
+
}
|
|
12
18
|
},
|
|
13
19
|
"type": "module",
|
|
14
20
|
"types": "src/index.d.ts",
|
|
@@ -66,6 +72,14 @@
|
|
|
66
72
|
"types": "./src/types.d.ts",
|
|
67
73
|
"import": "./src/types.js"
|
|
68
74
|
},
|
|
69
|
-
"./package.json": "./package.json"
|
|
75
|
+
"./package.json": "./package.json",
|
|
76
|
+
"./directory-storage": {
|
|
77
|
+
"types": "./src/directory-storage.d.ts",
|
|
78
|
+
"import": "./src/directory-storage.js"
|
|
79
|
+
},
|
|
80
|
+
"./directory-storage.js": {
|
|
81
|
+
"types": "./src/directory-storage.d.ts",
|
|
82
|
+
"import": "./src/directory-storage.js"
|
|
83
|
+
}
|
|
70
84
|
}
|
|
71
85
|
}
|
package/src/bun-s3.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Leverages Bun's native S3Client and S3File for high-performance
|
|
5
5
|
* cloud storage operations with File System Access API compatibility.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { Bucket, FileSystemConfig } from "./types.js";
|
|
8
8
|
/**
|
|
9
9
|
* Bun S3 implementation of FileSystemWritableFileStream
|
|
10
10
|
*/
|
|
@@ -59,13 +59,13 @@ export declare class BunS3FileSystemDirectoryHandle implements FileSystemDirecto
|
|
|
59
59
|
requestPermission(): Promise<PermissionState>;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* S3 bucket using Bun's native S3Client
|
|
63
63
|
*/
|
|
64
|
-
export declare class
|
|
64
|
+
export declare class S3Bucket implements Bucket {
|
|
65
65
|
private config;
|
|
66
66
|
private s3Client;
|
|
67
67
|
constructor(s3Client: any, config?: FileSystemConfig);
|
|
68
|
-
|
|
68
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
69
69
|
getConfig(): FileSystemConfig;
|
|
70
70
|
dispose(): Promise<void>;
|
|
71
71
|
}
|
package/src/bun-s3.js
CHANGED
|
@@ -216,7 +216,7 @@ var BunS3FileSystemDirectoryHandle = class _BunS3FileSystemDirectoryHandle {
|
|
|
216
216
|
return "granted";
|
|
217
217
|
}
|
|
218
218
|
};
|
|
219
|
-
var
|
|
219
|
+
var S3Bucket = class {
|
|
220
220
|
config;
|
|
221
221
|
s3Client;
|
|
222
222
|
constructor(s3Client, config = {}) {
|
|
@@ -226,8 +226,8 @@ var BunS3FileSystemAdapter = class {
|
|
|
226
226
|
};
|
|
227
227
|
this.s3Client = s3Client;
|
|
228
228
|
}
|
|
229
|
-
async
|
|
230
|
-
const prefix = `filesystems/${name}
|
|
229
|
+
async getDirectoryHandle(name) {
|
|
230
|
+
const prefix = name ? `filesystems/${name}` : "filesystems/root";
|
|
231
231
|
return new BunS3FileSystemDirectoryHandle(this.s3Client, prefix);
|
|
232
232
|
}
|
|
233
233
|
getConfig() {
|
|
@@ -237,8 +237,8 @@ var BunS3FileSystemAdapter = class {
|
|
|
237
237
|
}
|
|
238
238
|
};
|
|
239
239
|
export {
|
|
240
|
-
BunS3FileSystemAdapter,
|
|
241
240
|
BunS3FileSystemDirectoryHandle,
|
|
242
241
|
BunS3FileSystemFileHandle,
|
|
243
|
-
BunS3FileSystemWritableFileStream
|
|
242
|
+
BunS3FileSystemWritableFileStream,
|
|
243
|
+
S3Bucket
|
|
244
244
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Bucket } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BucketStorage implements bucket storage with a configurable factory
|
|
4
|
+
* The factory function receives the bucket name and can return different filesystem types
|
|
5
|
+
* This mirrors the CustomCacheStorage pattern for consistency
|
|
6
|
+
*
|
|
7
|
+
* Example usage:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const buckets = new BucketStorage((name) => {
|
|
10
|
+
* if (name === 'uploads') return new S3Bucket('my-bucket');
|
|
11
|
+
* if (name === 'temp') return new LocalBucket('/tmp');
|
|
12
|
+
* return new LocalBucket('./dist'); // Default to dist
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare class BucketStorage {
|
|
17
|
+
private factory;
|
|
18
|
+
private instances;
|
|
19
|
+
constructor(factory: (name: string) => Bucket | Promise<Bucket>);
|
|
20
|
+
/**
|
|
21
|
+
* Opens a bucket with the given name
|
|
22
|
+
* Returns existing instance if already opened, otherwise creates a new one
|
|
23
|
+
*/
|
|
24
|
+
open(name: string): Promise<FileSystemDirectoryHandle>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns true if a bucket with the given name exists (has been opened)
|
|
27
|
+
*/
|
|
28
|
+
has(name: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Deletes a bucket with the given name
|
|
31
|
+
* Disposes of the instance if it exists
|
|
32
|
+
*/
|
|
33
|
+
delete(name: string): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns a list of all opened bucket names
|
|
36
|
+
*/
|
|
37
|
+
keys(): Promise<string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Get statistics about the bucket storage
|
|
40
|
+
*/
|
|
41
|
+
getStats(): {
|
|
42
|
+
openInstances: number;
|
|
43
|
+
bucketNames: string[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Dispose of all open adapter instances
|
|
47
|
+
* Useful for cleanup during shutdown
|
|
48
|
+
*/
|
|
49
|
+
dispose(): Promise<void>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="./directory-storage.d.ts" />
|
|
2
|
+
// src/directory-storage.ts
|
|
3
|
+
var BucketStorage = class {
|
|
4
|
+
constructor(factory) {
|
|
5
|
+
this.factory = factory;
|
|
6
|
+
}
|
|
7
|
+
instances = /* @__PURE__ */ new Map();
|
|
8
|
+
/**
|
|
9
|
+
* Opens a bucket with the given name
|
|
10
|
+
* Returns existing instance if already opened, otherwise creates a new one
|
|
11
|
+
*/
|
|
12
|
+
async open(name) {
|
|
13
|
+
const existingInstance = this.instances.get(name);
|
|
14
|
+
if (existingInstance) {
|
|
15
|
+
return await existingInstance.getDirectoryHandle("");
|
|
16
|
+
}
|
|
17
|
+
const adapter = await this.factory(name);
|
|
18
|
+
this.instances.set(name, adapter);
|
|
19
|
+
return await adapter.getDirectoryHandle("");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns true if a bucket with the given name exists (has been opened)
|
|
23
|
+
*/
|
|
24
|
+
async has(name) {
|
|
25
|
+
return this.instances.has(name);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Deletes a bucket with the given name
|
|
29
|
+
* Disposes of the instance if it exists
|
|
30
|
+
*/
|
|
31
|
+
async delete(name) {
|
|
32
|
+
const instance = this.instances.get(name);
|
|
33
|
+
if (instance) {
|
|
34
|
+
if (instance.dispose) {
|
|
35
|
+
await instance.dispose();
|
|
36
|
+
}
|
|
37
|
+
this.instances.delete(name);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns a list of all opened bucket names
|
|
44
|
+
*/
|
|
45
|
+
async keys() {
|
|
46
|
+
return Array.from(this.instances.keys());
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get statistics about the bucket storage
|
|
50
|
+
*/
|
|
51
|
+
getStats() {
|
|
52
|
+
return {
|
|
53
|
+
openInstances: this.instances.size,
|
|
54
|
+
bucketNames: Array.from(this.instances.keys())
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Dispose of all open adapter instances
|
|
59
|
+
* Useful for cleanup during shutdown
|
|
60
|
+
*/
|
|
61
|
+
async dispose() {
|
|
62
|
+
const disposePromises = [];
|
|
63
|
+
for (const [_name, instance] of this.instances) {
|
|
64
|
+
if (instance.dispose) {
|
|
65
|
+
disposePromises.push(instance.dispose());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await Promise.all(disposePromises);
|
|
69
|
+
this.instances.clear();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
export {
|
|
73
|
+
BucketStorage
|
|
74
|
+
};
|
package/src/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
1
|
+
export type { Bucket, FileSystemConfig } from "./types.js";
|
|
2
|
+
export { MemoryBucket } from "./memory.js";
|
|
3
|
+
export { LocalBucket, NodeFileSystemDirectoryHandle, NodeFileSystemFileHandle } from "./node.js";
|
|
4
|
+
export { S3Bucket, BunS3FileSystemDirectoryHandle, BunS3FileSystemFileHandle } from "./bun-s3.js";
|
|
5
|
+
export { BucketStorage } from "./directory-storage.js";
|
|
6
|
+
export { FileSystemRegistry, getDirectoryHandle, getBucket, getFileSystemRoot } from "./registry.js";
|
package/src/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
2
|
// src/index.ts
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { MemoryBucket } from "./memory.js";
|
|
4
|
+
import { LocalBucket, NodeFileSystemDirectoryHandle, NodeFileSystemFileHandle } from "./node.js";
|
|
5
|
+
import { S3Bucket, BunS3FileSystemDirectoryHandle, BunS3FileSystemFileHandle } from "./bun-s3.js";
|
|
6
|
+
import { BucketStorage } from "./directory-storage.js";
|
|
7
|
+
import { FileSystemRegistry, getDirectoryHandle, getBucket, getFileSystemRoot } from "./registry.js";
|
|
7
8
|
export {
|
|
8
|
-
|
|
9
|
+
BucketStorage,
|
|
9
10
|
BunS3FileSystemDirectoryHandle,
|
|
10
11
|
BunS3FileSystemFileHandle,
|
|
11
12
|
FileSystemRegistry,
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
LocalBucket,
|
|
14
|
+
MemoryBucket,
|
|
14
15
|
NodeFileSystemDirectoryHandle,
|
|
15
16
|
NodeFileSystemFileHandle,
|
|
17
|
+
S3Bucket,
|
|
18
|
+
getBucket,
|
|
19
|
+
getDirectoryHandle,
|
|
16
20
|
getFileSystemRoot
|
|
17
21
|
};
|
package/src/memory.d.ts
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
* Provides a complete filesystem interface using in-memory data structures.
|
|
5
5
|
* Useful for testing, development, and temporary storage scenarios.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { Bucket, FileSystemConfig } from "./types.js";
|
|
8
8
|
/**
|
|
9
|
-
* Memory
|
|
9
|
+
* Memory bucket
|
|
10
10
|
*/
|
|
11
|
-
export declare class
|
|
11
|
+
export declare class MemoryBucket implements Bucket {
|
|
12
12
|
private config;
|
|
13
13
|
private filesystems;
|
|
14
14
|
constructor(config?: FileSystemConfig);
|
|
15
|
-
|
|
15
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
16
16
|
getConfig(): FileSystemConfig;
|
|
17
17
|
dispose(): Promise<void>;
|
|
18
18
|
}
|
package/src/memory.js
CHANGED
|
@@ -190,7 +190,7 @@ var MemoryFileSystemDirectoryHandle = class _MemoryFileSystemDirectoryHandle {
|
|
|
190
190
|
return "granted";
|
|
191
191
|
}
|
|
192
192
|
};
|
|
193
|
-
var
|
|
193
|
+
var MemoryBucket = class {
|
|
194
194
|
config;
|
|
195
195
|
filesystems = /* @__PURE__ */ new Map();
|
|
196
196
|
constructor(config = {}) {
|
|
@@ -199,16 +199,17 @@ var MemoryFileSystemAdapter = class {
|
|
|
199
199
|
...config
|
|
200
200
|
};
|
|
201
201
|
}
|
|
202
|
-
async
|
|
203
|
-
|
|
202
|
+
async getDirectoryHandle(name) {
|
|
203
|
+
const dirName = name || "root";
|
|
204
|
+
if (!this.filesystems.has(dirName)) {
|
|
204
205
|
const root2 = {
|
|
205
206
|
name: "root",
|
|
206
207
|
files: /* @__PURE__ */ new Map(),
|
|
207
208
|
directories: /* @__PURE__ */ new Map()
|
|
208
209
|
};
|
|
209
|
-
this.filesystems.set(
|
|
210
|
+
this.filesystems.set(dirName, root2);
|
|
210
211
|
}
|
|
211
|
-
const root = this.filesystems.get(
|
|
212
|
+
const root = this.filesystems.get(dirName);
|
|
212
213
|
return new MemoryFileSystemDirectoryHandle(root);
|
|
213
214
|
}
|
|
214
215
|
getConfig() {
|
|
@@ -219,5 +220,5 @@ var MemoryFileSystemAdapter = class {
|
|
|
219
220
|
}
|
|
220
221
|
};
|
|
221
222
|
export {
|
|
222
|
-
|
|
223
|
+
MemoryBucket
|
|
223
224
|
};
|
package/src/node.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements FileSystemDirectoryHandle and FileSystemFileHandle using fs/promises
|
|
5
5
|
* to provide universal File System Access API on Node.js platforms.
|
|
6
6
|
*/
|
|
7
|
-
import type {
|
|
7
|
+
import type { Bucket, FileSystemConfig } from "./types.js";
|
|
8
8
|
/**
|
|
9
9
|
* Node.js implementation of FileSystemWritableFileStream
|
|
10
10
|
*/
|
|
@@ -56,12 +56,12 @@ export declare class NodeFileSystemDirectoryHandle implements FileSystemDirector
|
|
|
56
56
|
requestPermission(): Promise<PermissionState>;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
|
-
* Node.js
|
|
59
|
+
* Local filesystem bucket using Node.js fs
|
|
60
60
|
*/
|
|
61
|
-
export declare class
|
|
61
|
+
export declare class LocalBucket implements Bucket {
|
|
62
62
|
private config;
|
|
63
63
|
private rootPath;
|
|
64
64
|
constructor(config?: FileSystemConfig);
|
|
65
|
-
|
|
65
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
66
66
|
getConfig(): FileSystemConfig;
|
|
67
67
|
}
|
package/src/node.js
CHANGED
|
@@ -236,7 +236,7 @@ var NodeFileSystemDirectoryHandle = class _NodeFileSystemDirectoryHandle {
|
|
|
236
236
|
return "granted";
|
|
237
237
|
}
|
|
238
238
|
};
|
|
239
|
-
var
|
|
239
|
+
var LocalBucket = class {
|
|
240
240
|
config;
|
|
241
241
|
rootPath;
|
|
242
242
|
constructor(config = {}) {
|
|
@@ -246,20 +246,20 @@ var NodeFileSystemAdapter = class {
|
|
|
246
246
|
};
|
|
247
247
|
this.rootPath = config.rootPath || path.join(process.cwd(), "dist");
|
|
248
248
|
}
|
|
249
|
-
async
|
|
250
|
-
const
|
|
249
|
+
async getDirectoryHandle(name) {
|
|
250
|
+
const dirPath = name ? path.join(this.rootPath, name) : this.rootPath;
|
|
251
251
|
try {
|
|
252
|
-
await fs.mkdir(
|
|
252
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
253
253
|
} catch (error) {
|
|
254
254
|
}
|
|
255
|
-
return new NodeFileSystemDirectoryHandle(
|
|
255
|
+
return new NodeFileSystemDirectoryHandle(dirPath);
|
|
256
256
|
}
|
|
257
257
|
getConfig() {
|
|
258
258
|
return { ...this.config };
|
|
259
259
|
}
|
|
260
260
|
};
|
|
261
261
|
export {
|
|
262
|
-
|
|
262
|
+
LocalBucket,
|
|
263
263
|
NodeFileSystemDirectoryHandle,
|
|
264
264
|
NodeFileSystemFileHandle,
|
|
265
265
|
NodeFileSystemWritableFileStream
|
package/src/registry.d.ts
CHANGED
|
@@ -36,8 +36,17 @@ declare class Registry {
|
|
|
36
36
|
*/
|
|
37
37
|
export declare const FileSystemRegistry: Registry;
|
|
38
38
|
/**
|
|
39
|
-
* Get a file system
|
|
40
|
-
* @param name
|
|
39
|
+
* Get a file system directory handle using the registered adapters
|
|
40
|
+
* @param name Directory name. Use "" for root directory
|
|
41
|
+
* @param adapterName Optional adapter name (uses default if not specified)
|
|
42
|
+
*/
|
|
43
|
+
export declare function getDirectoryHandle(name: string, adapterName?: string): Promise<FileSystemDirectoryHandle>;
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Use getDirectoryHandle() instead
|
|
46
|
+
*/
|
|
47
|
+
export declare function getBucket(name?: string, adapterName?: string): Promise<FileSystemDirectoryHandle>;
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Use getDirectoryHandle() instead
|
|
41
50
|
*/
|
|
42
51
|
export declare function getFileSystemRoot(name?: string): Promise<FileSystemDirectoryHandle>;
|
|
43
52
|
export {};
|
package/src/registry.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/// <reference types="./registry.d.ts" />
|
|
2
2
|
// src/registry.ts
|
|
3
|
-
import {
|
|
3
|
+
import { MemoryBucket } from "./memory.js";
|
|
4
4
|
var Registry = class {
|
|
5
5
|
adapters = /* @__PURE__ */ new Map();
|
|
6
6
|
defaultAdapter;
|
|
7
7
|
constructor() {
|
|
8
|
-
this.defaultAdapter = new
|
|
8
|
+
this.defaultAdapter = new MemoryBucket();
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Register a filesystem adapter with a name
|
|
@@ -46,18 +46,34 @@ var Registry = class {
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
var FileSystemRegistry = new Registry();
|
|
49
|
-
async function
|
|
50
|
-
const adapter = FileSystemRegistry.get(
|
|
49
|
+
async function getDirectoryHandle(name, adapterName) {
|
|
50
|
+
const adapter = FileSystemRegistry.get(adapterName);
|
|
51
51
|
if (!adapter) {
|
|
52
|
-
if (
|
|
53
|
-
throw new Error(`No filesystem adapter registered with name: ${
|
|
52
|
+
if (adapterName) {
|
|
53
|
+
throw new Error(`No filesystem adapter registered with name: ${adapterName}`);
|
|
54
54
|
} else {
|
|
55
55
|
throw new Error("No default filesystem adapter registered");
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
return await adapter.
|
|
58
|
+
return await adapter.getDirectoryHandle(name);
|
|
59
|
+
}
|
|
60
|
+
async function getBucket(name, adapterName) {
|
|
61
|
+
const adapter = FileSystemRegistry.get(adapterName);
|
|
62
|
+
if (!adapter) {
|
|
63
|
+
throw new Error("No default filesystem adapter registered");
|
|
64
|
+
}
|
|
65
|
+
return await adapter.getDirectoryHandle(name || "");
|
|
66
|
+
}
|
|
67
|
+
async function getFileSystemRoot(name) {
|
|
68
|
+
const adapter = FileSystemRegistry.get();
|
|
69
|
+
if (!adapter) {
|
|
70
|
+
throw new Error("No default filesystem adapter registered");
|
|
71
|
+
}
|
|
72
|
+
return await adapter.getDirectoryHandle(name || "");
|
|
59
73
|
}
|
|
60
74
|
export {
|
|
61
75
|
FileSystemRegistry,
|
|
76
|
+
getBucket,
|
|
77
|
+
getDirectoryHandle,
|
|
62
78
|
getFileSystemRoot
|
|
63
79
|
};
|
package/src/types.d.ts
CHANGED
|
@@ -11,21 +11,21 @@ export interface FileSystemConfig {
|
|
|
11
11
|
[key: string]: any;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Core interface that all
|
|
14
|
+
* Core interface that all buckets must implement
|
|
15
15
|
* Provides File System Access API compatibility across all platforms
|
|
16
16
|
*/
|
|
17
|
-
export interface
|
|
17
|
+
export interface Bucket {
|
|
18
18
|
/**
|
|
19
|
-
* Get a
|
|
20
|
-
* @param name
|
|
19
|
+
* Get a directory handle for the bucket
|
|
20
|
+
* @param name Directory name. Use "" for root directory
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
getDirectoryHandle(name: string): Promise<FileSystemDirectoryHandle>;
|
|
23
23
|
/**
|
|
24
|
-
* Get configuration for this
|
|
24
|
+
* Get configuration for this bucket
|
|
25
25
|
*/
|
|
26
26
|
getConfig(): FileSystemConfig;
|
|
27
27
|
/**
|
|
28
|
-
* Cleanup resources when
|
|
28
|
+
* Cleanup resources when bucket is no longer needed
|
|
29
29
|
*/
|
|
30
30
|
dispose?(): Promise<void>;
|
|
31
31
|
}
|