opfs-worker 0.4.2 → 0.5.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 +204 -880
- package/dist/assets/worker-3SMz_DAt.js.map +1 -0
- package/dist/{helpers-BgMlBRWa.js → helpers-Bm2TWyPQ.js} +290 -251
- package/dist/helpers-Bm2TWyPQ.js.map +1 -0
- package/dist/helpers-BoLdVbGk.cjs +4 -0
- package/dist/helpers-BoLdVbGk.cjs.map +1 -0
- package/dist/index.cjs +532 -579
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +560 -602
- package/dist/index.js.map +1 -1
- package/dist/raw.cjs +1 -1
- package/dist/raw.cjs.map +1 -1
- package/dist/raw.js +97 -159
- package/dist/raw.js.map +1 -1
- package/dist/types.d.ts +14 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/errors.d.ts +9 -9
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/helpers.d.ts +26 -2
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/worker.d.ts +7 -9
- package/dist/worker.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/assets/worker-Bra0Mewp.js.map +0 -1
- package/dist/helpers-BgMlBRWa.js.map +0 -1
- package/dist/helpers-DuJbWewc.cjs +0 -4
- package/dist/helpers-DuJbWewc.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -12,25 +12,99 @@ A robust TypeScript library for working with Origin Private File System (OPFS) t
|
|
|
12
12
|
- [Demo](#demo)
|
|
13
13
|
- [API Reference](#api-reference)
|
|
14
14
|
- [Types](#types)
|
|
15
|
-
- [Error Types](#error-types)
|
|
16
15
|
- [Browser Support](#browser-support)
|
|
17
16
|
- [Development](#development)
|
|
18
17
|
- [License](#license)
|
|
19
|
-
- [Contributing](#contributing)
|
|
20
18
|
|
|
21
19
|
## Features
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
21
|
+
### 🚀 **Performance & Architecture**
|
|
22
|
+
|
|
23
|
+
- **Web Worker-based**: Runs in a separate thread, keeping your main thread responsive
|
|
24
|
+
- **Faster than IndexedDB hacks**: Direct OPFS access without the overhead of database abstractions
|
|
25
|
+
- **Efficient file watching**: Real-time change detection with minimatch patterns, no polling delays
|
|
26
|
+
|
|
27
|
+
### 🛠️ **Developer Experience**
|
|
28
|
+
|
|
29
|
+
- **Better DX vs File System Access API**: Familiar Node.js-like API instead of complex browser APIs
|
|
30
|
+
- **Type-safe**: Full TypeScript support with comprehensive type definitions
|
|
31
|
+
- **Comlink-powered**: Seamless RPC communication between main thread and worker
|
|
32
|
+
|
|
33
|
+
### 🌐 **Compatibility & Standards**
|
|
34
|
+
|
|
35
|
+
- **Cross-browser compatible**: Works in all modern browsers including Safari, Firefox, Chrome, and Edge
|
|
36
|
+
- **OPFS-native**: Built directly on Origin Private File System standards
|
|
37
|
+
- **No polyfills needed**: Uses native browser capabilities
|
|
38
|
+
|
|
39
|
+
### 📁 **File System Operations**
|
|
40
|
+
|
|
41
|
+
- **Complete API**: `readFile`, `writeFile`, `mkdir`, `remove`, `copy`, `rename`, and more
|
|
42
|
+
- **Binary file support**: Handle images, documents, and any binary data seamlessly
|
|
43
|
+
- **Hash support**: Built-in file hash calculation (SHA-1, SHA-256, SHA-384, SHA-512)
|
|
44
|
+
- **File indexing**: Complete file system indexing with metadata and search
|
|
45
|
+
|
|
46
|
+
### 🔄 **Advanced Features**
|
|
47
|
+
|
|
48
|
+
- **Sync operations**: Bulk file synchronization from external data sources
|
|
49
|
+
- **Event broadcasting**: Real-time file change notifications via BroadcastChannel
|
|
50
|
+
- **Include/exclude filters**: Fine-grained control over file watching patterns
|
|
51
|
+
- **Comprehensive error handling**: Detailed error types for better debugging
|
|
52
|
+
|
|
53
|
+
## When to Use
|
|
54
|
+
|
|
55
|
+
**Use OPFS Worker when you need a persistent file system directly in the browser with an API almost like Node.js `fs`.** It's faster than IndexedDB for file operations and provides better DX than raw File System Access API.
|
|
56
|
+
|
|
57
|
+
Here are the key use cases:
|
|
58
|
+
|
|
59
|
+
### 🚀 **Offline-First Applications**
|
|
60
|
+
|
|
61
|
+
- **Progressive Web Apps (PWAs)**: Store user data, cached resources, and app state locally
|
|
62
|
+
- **Media applications**: Cache images, videos, and audio files for offline playback
|
|
63
|
+
- **Data synchronization**: Local-first data with background sync to remote servers
|
|
64
|
+
|
|
65
|
+
### 💾 **Performance & Caching**
|
|
66
|
+
|
|
67
|
+
- **Asset caching**: Store and serve static files (CSS, JS, images) from local storage
|
|
68
|
+
- **Database caching**: Cache API responses and database queries locally
|
|
69
|
+
- **Session persistence**: Maintain user sessions and preferences across browser restarts
|
|
70
|
+
- **Resource preloading**: Preload critical resources for instant access
|
|
71
|
+
|
|
72
|
+
### 🎨 **Editor & IDE Features**
|
|
73
|
+
|
|
74
|
+
- **Code editors**: File tree navigation, syntax highlighting, and project management
|
|
75
|
+
- **Design tools**: Canvas state persistence, project files, and asset management
|
|
76
|
+
- **Document editors**: Rich text, markdown, and collaborative editing with local storage
|
|
77
|
+
- **Build tools**: Local development servers, build caches, and project configurations
|
|
78
|
+
|
|
79
|
+
### 🔄 **State Management**
|
|
80
|
+
|
|
81
|
+
- **Application state**: Persist complex application state and user preferences
|
|
82
|
+
- **Form data**: Auto-save forms and restore user input on page refresh
|
|
83
|
+
- **Game state**: Save game progress, levels, and user achievements
|
|
84
|
+
- **User preferences**: Settings, themes, and personalized configurations
|
|
85
|
+
|
|
86
|
+
### 📱 **Mobile & Cross-Platform**
|
|
87
|
+
|
|
88
|
+
- **Mobile web apps**: Native-like file management on mobile devices
|
|
89
|
+
- **Cross-tab synchronization**: Share data between multiple browser tabs
|
|
90
|
+
- **Worker-based processing**: Offload file operations to background threads
|
|
91
|
+
- **Progressive Web Apps**: Full offline capabilities with file system access
|
|
92
|
+
|
|
93
|
+
### ❌ **When NOT to Use**
|
|
94
|
+
|
|
95
|
+
- **Server-side storage**: This is a client-side solution, not a replacement for server storage
|
|
96
|
+
- **Cross-origin access**: Files are isolated to the specific domain/origin
|
|
97
|
+
- **Large datasets**: OPFS has browser-specific storage limitations:
|
|
98
|
+
- **Chrome/Edge**: Up to ~50% of free disk space (check via `navigator.storage.estimate()`)
|
|
99
|
+
- **Safari**: Usually 1-2 GB, sometimes less
|
|
100
|
+
- **Firefox**: Shared limit with IndexedDB, similar to Chromium
|
|
101
|
+
|
|
102
|
+
### ⚡ **Performance Notes**
|
|
103
|
+
|
|
104
|
+
- **Faster than IndexedDB**: Direct file system access without database overhead
|
|
105
|
+
- **Slower than native FS**: Browser APIs have performance limitations compared to desktop file systems
|
|
106
|
+
- **Worker-based**: File operations run in background threads, keeping main thread responsive
|
|
107
|
+
- **Memory efficient**: Files are stored in browser's native file system, not in memory
|
|
34
108
|
|
|
35
109
|
## Installation
|
|
36
110
|
|
|
@@ -54,46 +128,57 @@ The easiest way to get started - just import and use:
|
|
|
54
128
|
```typescript
|
|
55
129
|
import { createWorker } from 'opfs-worker';
|
|
56
130
|
|
|
57
|
-
async function
|
|
131
|
+
async function basicExample() {
|
|
58
132
|
// Create a file system instance with default root path '/'
|
|
59
133
|
const fs = await createWorker();
|
|
60
134
|
|
|
61
|
-
// Or specify a custom root path
|
|
62
|
-
const fsCustom = await createWorker({ root: '/my-app' });
|
|
63
|
-
|
|
64
135
|
// Write a file
|
|
65
136
|
await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
|
|
66
137
|
|
|
67
138
|
// Read the file back
|
|
68
139
|
const config = await fs.readFile('/config.json');
|
|
69
140
|
console.log(JSON.parse(config));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Extended example with all options
|
|
144
|
+
async function extendedExample() {
|
|
145
|
+
const broadcastChannel = new BroadcastChannel('my-app-events');
|
|
146
|
+
|
|
147
|
+
const fs = await createWorker({
|
|
148
|
+
root: '/my-app',
|
|
149
|
+
namespace: 'my-app:fs',
|
|
150
|
+
hashAlgorithm: 'SHA-256',
|
|
151
|
+
maxFileSize: 100 * 1024 * 1024, // 100MB
|
|
152
|
+
broadcastChannel
|
|
153
|
+
});
|
|
70
154
|
|
|
71
155
|
// Handle binary files
|
|
72
|
-
const imageData = new Uint8Array([/* binary data */]);
|
|
156
|
+
const imageData = new Uint8Array([58, /* binary data */]);
|
|
73
157
|
await fs.writeFile('/image.png', imageData);
|
|
74
158
|
const binaryData = await fs.readFile('/image.png', 'binary');
|
|
75
|
-
}
|
|
76
159
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
broadcastChannel: 'my-app-events'
|
|
84
|
-
}));
|
|
160
|
+
// Use file watching with BroadcastChannel
|
|
161
|
+
await fs.watch('/', {
|
|
162
|
+
recursive: true,
|
|
163
|
+
include: ['*.json'],
|
|
164
|
+
exclude: ['dist/**']
|
|
165
|
+
});
|
|
85
166
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
channel.onmessage = (event) => {
|
|
89
|
-
console.log('File changed:', event.data);
|
|
167
|
+
broadcastChannel.onmessage = (event) => {
|
|
168
|
+
console.log(`File changed: `, event.data);
|
|
90
169
|
};
|
|
91
170
|
|
|
92
|
-
|
|
171
|
+
// Get file statistics with hashing
|
|
172
|
+
const stats = await fs.stat('/image.png');
|
|
173
|
+
|
|
174
|
+
console.log(
|
|
175
|
+
`File size: ${stats.size} bytes`,
|
|
176
|
+
`Hash: ${stats.hash}`
|
|
177
|
+
);
|
|
93
178
|
}
|
|
94
179
|
```
|
|
95
180
|
|
|
96
|
-
> **Note:** File change events are sent via BroadcastChannel. Set the `broadcastChannel` option to customize the channel name, or use the default 'opfs-worker' channel.
|
|
181
|
+
> **Note:** File change events are sent via BroadcastChannel. Set the `broadcastChannel` option to customize the channel name, or use the default 'opfs-worker' channel. The watch system only sends events for watched paths, making it efficient and focused.
|
|
97
182
|
|
|
98
183
|
### Manual Worker Setup
|
|
99
184
|
|
|
@@ -103,104 +188,72 @@ For more control over the worker lifecycle, use the worker directly with your bu
|
|
|
103
188
|
import OPFSWorker from 'opfs-worker/raw?worker';
|
|
104
189
|
import { wrap } from 'comlink';
|
|
105
190
|
|
|
106
|
-
async function
|
|
191
|
+
async function basicExample() {
|
|
107
192
|
// Create and wrap the worker with default root path '/'
|
|
108
193
|
const worker = wrap(new OPFSWorker());
|
|
109
194
|
|
|
110
|
-
// Or specify a custom root path
|
|
111
|
-
const workerCustom = wrap(new OPFSWorker({ root: '/my-app' }));
|
|
112
|
-
|
|
113
195
|
// Use the file system
|
|
114
196
|
await worker.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
|
|
115
197
|
const config = await worker.readFile('/config.json');
|
|
116
198
|
console.log(JSON.parse(config));
|
|
117
199
|
}
|
|
118
200
|
|
|
119
|
-
//
|
|
120
|
-
async function
|
|
121
|
-
|
|
201
|
+
// Extended example with all options
|
|
202
|
+
async function extendedExample() {
|
|
203
|
+
// Create with all available options
|
|
204
|
+
const worker = wrap(new OPFSWorker({
|
|
122
205
|
root: '/my-app',
|
|
123
|
-
|
|
124
|
-
hashAlgorithm: 'SHA-
|
|
125
|
-
|
|
206
|
+
namespace: 'my-app:fs',
|
|
207
|
+
hashAlgorithm: 'SHA-256',
|
|
208
|
+
maxFileSize: 100 * 1024 * 1024, // 100MB
|
|
209
|
+
broadcastChannel: 'my-app-events' // You can't pass a BroadcastChannel instance to the worker, so you must use a broadcast channel name here
|
|
126
210
|
}));
|
|
127
211
|
|
|
128
|
-
//
|
|
212
|
+
// Bulk sync from external data
|
|
213
|
+
const entries = [
|
|
214
|
+
['/data/config.json', JSON.stringify({ version: '1.0' })],
|
|
215
|
+
['/data/users.json', JSON.stringify([{ id: 1, name: 'John' }])]
|
|
216
|
+
];
|
|
217
|
+
await worker.sync(entries, { cleanBefore: true });
|
|
218
|
+
|
|
219
|
+
// Create directories
|
|
220
|
+
await worker.mkdir('/data/logs', { recursive: true });
|
|
221
|
+
|
|
222
|
+
// Append to log files
|
|
223
|
+
await worker.appendFile('/data/logs/app.log', `${new Date().toISOString()}: App started\n`);
|
|
224
|
+
|
|
225
|
+
// Watch for changes
|
|
226
|
+
await worker.watch('/data', { recursive: true });
|
|
227
|
+
|
|
129
228
|
const channel = new BroadcastChannel('my-app-events');
|
|
130
229
|
channel.onmessage = (event) => {
|
|
131
230
|
console.log('File changed:', event.data);
|
|
132
231
|
};
|
|
133
|
-
|
|
134
|
-
await worker.watch('/docs');
|
|
135
232
|
}
|
|
136
233
|
```
|
|
137
234
|
|
|
138
235
|
**Note:** Manual worker setup requires a bundler that supports Web Workers (like Vite, Webpack, Rollup, etc.) and the `comlink` package for communication between the main thread and worker.
|
|
139
236
|
|
|
140
|
-
**File Change Events:** File change events are sent via BroadcastChannel. Set the `broadcastChannel` option to customize the channel name, or use the default 'opfs-worker' channel.
|
|
141
|
-
|
|
142
|
-
### Advanced Usage
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
import { createWorker } from 'opfs-worker';
|
|
146
|
-
|
|
147
|
-
async function advancedExample() {
|
|
148
|
-
// Create with custom root path
|
|
149
|
-
const fs = await createWorker({ root: '/my-app' });
|
|
150
|
-
|
|
151
|
-
// Create directories
|
|
152
|
-
await fs.mkdir('/data/logs', { recursive: true });
|
|
153
|
-
|
|
154
|
-
// Write multiple files
|
|
155
|
-
await fs.writeFile('/data/config.json', JSON.stringify({ version: '1.0' }));
|
|
156
|
-
await fs.writeFile('/data/logs/app.log', 'Application started\n');
|
|
157
|
-
|
|
158
|
-
// Handle binary files
|
|
159
|
-
const imageData = new Uint8Array([/* binary data */]);
|
|
160
|
-
await fs.writeFile('/data/image.png', imageData);
|
|
161
|
-
const binaryData = await fs.readFile('/data/image.png', 'binary');
|
|
162
|
-
|
|
163
|
-
// Append to a file
|
|
164
|
-
await fs.appendFile('/data/logs/app.log', `${new Date().toISOString()}: User logged in\n`);
|
|
165
|
-
|
|
166
|
-
// Get file statistics with hash
|
|
167
|
-
const stats = await fs.stat('/data/config.json');
|
|
168
|
-
|
|
169
|
-
console.log(`File size: ${stats.size} bytes`);
|
|
170
|
-
if (stats.hash) {
|
|
171
|
-
console.log(`Hash: ${stats.hash}`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// List directory contents
|
|
175
|
-
const files = await fs.readDir('/data');
|
|
176
|
-
files.forEach(item => {
|
|
177
|
-
console.log(`${item.name} - ${item.isFile ? 'file' : 'directory'}`);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
237
|
### Hash Algorithm Configuration
|
|
185
238
|
|
|
186
|
-
The file system
|
|
239
|
+
The file system supports global hash algorithm configuration. Instead of passing hash options to individual methods, you can set the hash algorithm once and it will be used for all file operations that support hashing.
|
|
187
240
|
|
|
188
241
|
```typescript
|
|
189
242
|
import { createWorker } from 'opfs-worker';
|
|
190
243
|
|
|
191
244
|
async function hashExample() {
|
|
192
245
|
const fs = await createWorker();
|
|
193
|
-
|
|
246
|
+
|
|
194
247
|
// Enable SHA-256 hashing globally
|
|
195
248
|
fs.setOptions({ hashAlgorithm: 'SHA-256' });
|
|
196
|
-
|
|
249
|
+
|
|
197
250
|
// Write a file
|
|
198
251
|
await fs.writeFile('/data.txt', 'Hello World');
|
|
199
|
-
|
|
252
|
+
|
|
200
253
|
// Get stats - hash will be included automatically
|
|
201
254
|
const stats = await fs.stat('/data.txt');
|
|
202
255
|
console.log(`Hash: ${stats.hash}`); // SHA-256 hash
|
|
203
|
-
|
|
256
|
+
|
|
204
257
|
// Get file system index - all files will include hashes
|
|
205
258
|
const index = await fs.index();
|
|
206
259
|
for (const [path, stat] of index) {
|
|
@@ -208,7 +261,7 @@ async function hashExample() {
|
|
|
208
261
|
console.log(`${path}: ${stat.hash}`);
|
|
209
262
|
}
|
|
210
263
|
}
|
|
211
|
-
|
|
264
|
+
|
|
212
265
|
// Watch events will also include hash information
|
|
213
266
|
const channel = new BroadcastChannel('opfs-worker');
|
|
214
267
|
channel.onmessage = (event) => {
|
|
@@ -221,14 +274,14 @@ async function hashExample() {
|
|
|
221
274
|
// Configure maximum file size for hashing
|
|
222
275
|
async function maxFileSizeExample() {
|
|
223
276
|
const fs = await createWorker();
|
|
224
|
-
|
|
277
|
+
|
|
225
278
|
// Set custom maximum file size (100MB instead of default 50MB)
|
|
226
|
-
fs.setOptions({
|
|
279
|
+
fs.setOptions({
|
|
227
280
|
hashAlgorithm: 'SHA-256',
|
|
228
281
|
maxFileSize: 100 * 1024 * 1024 // 100MB
|
|
229
282
|
});
|
|
230
|
-
|
|
231
|
-
//
|
|
283
|
+
|
|
284
|
+
// Files up to 100MB will be hashed
|
|
232
285
|
// Files larger than this will not have hash information
|
|
233
286
|
const stats = await fs.stat('/large-file.dat');
|
|
234
287
|
if (stats.hash) {
|
|
@@ -240,6 +293,7 @@ async function maxFileSizeExample() {
|
|
|
240
293
|
```
|
|
241
294
|
|
|
242
295
|
**Supported Hash Algorithms:**
|
|
296
|
+
|
|
243
297
|
- `'SHA-1'` - Fastest, good for general use (default)
|
|
244
298
|
- `'SHA-256'` - More secure, widely supported
|
|
245
299
|
- `'SHA-384'` - Higher security
|
|
@@ -260,12 +314,12 @@ async function rootPathExample() {
|
|
|
260
314
|
const fsDefault = await createWorker();
|
|
261
315
|
await fsDefault.writeFile('/config.json', '{}');
|
|
262
316
|
// This creates /config.json in OPFS root
|
|
263
|
-
|
|
317
|
+
|
|
264
318
|
// Use custom root path
|
|
265
319
|
const fsCustom = await createWorker({ root: '/my-app' });
|
|
266
320
|
await fsCustom.writeFile('/config.json', '{}');
|
|
267
321
|
// This creates /my-app/config.json in OPFS root
|
|
268
|
-
|
|
322
|
+
|
|
269
323
|
// Change root path dynamically
|
|
270
324
|
await fsCustom.setOptions({ root: '/new-app' });
|
|
271
325
|
await fsCustom.writeFile('/config.json', '{}');
|
|
@@ -274,17 +328,13 @@ async function rootPathExample() {
|
|
|
274
328
|
```
|
|
275
329
|
|
|
276
330
|
**Root Path Behavior:**
|
|
331
|
+
|
|
277
332
|
- **Default**: Uses `/` (OPFS root directory)
|
|
278
333
|
- **Custom**: Creates a subdirectory within OPFS for isolation
|
|
279
334
|
- **Dynamic**: Can be changed at runtime via `setOptions()`
|
|
280
335
|
- **Auto-mount**: Automatically mounts to the specified root when needed
|
|
281
336
|
- **Path Resolution**: All API paths are relative to the configured root
|
|
282
337
|
|
|
283
|
-
**Use Cases:**
|
|
284
|
-
- **Isolation**: Keep different apps' data separate (`root: '/app1'`, `root: '/app2'`)
|
|
285
|
-
- **Versioning**: Use different roots for different versions (`root: '/v1'`, `root: '/v2'`)
|
|
286
|
-
- **User Separation**: Separate data by user (`root: '/user/john'`, `root: '/user/jane'`)
|
|
287
|
-
|
|
288
338
|
## Demo
|
|
289
339
|
|
|
290
340
|
Check out the live demo powered by Vite and hosted on GitHub Pages.
|
|
@@ -293,791 +343,50 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
|
|
|
293
343
|
|
|
294
344
|
## API Reference
|
|
295
345
|
|
|
296
|
-
|
|
297
|
-
- [Table of Contents](#table-of-contents)
|
|
298
|
-
- [Features](#features)
|
|
299
|
-
- [Installation](#installation)
|
|
300
|
-
- [Quick Start](#quick-start)
|
|
301
|
-
- [Inline Worker (Recommended)](#inline-worker-recommended)
|
|
302
|
-
- [Manual Worker Setup](#manual-worker-setup)
|
|
303
|
-
- [Advanced Usage](#advanced-usage)
|
|
304
|
-
- [Hash Algorithm Configuration](#hash-algorithm-configuration)
|
|
305
|
-
- [Root Path Configuration](#root-path-configuration)
|
|
306
|
-
- [Demo](#demo)
|
|
307
|
-
- [API Reference](#api-reference)
|
|
308
|
-
- [Entry Points](#entry-points)
|
|
309
|
-
- [Mode 1: Inline Worker](#mode-1-inline-worker)
|
|
310
|
-
- [`createWorker(options?: OPFSOptions)`](#createworkeroptions-opfsoptions)
|
|
311
|
-
- [Mode 2: Manual Worker Setup](#mode-2-manual-worker-setup)
|
|
312
|
-
- [`OPFSWorker`](#opfsworker)
|
|
313
|
-
- [Core Methods](#core-methods)
|
|
314
|
-
- [Read File](#read-file)
|
|
315
|
-
- [`readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`](#readfilepath-string-encoding-bufferencoding--binary-promisestring--uint8array)
|
|
316
|
-
- [Write File](#write-file)
|
|
317
|
-
- [`writeFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`](#writefilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
|
|
318
|
-
- [Append File](#append-file)
|
|
319
|
-
- [`appendFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`](#appendfilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
|
|
320
|
-
- [Create Directory](#create-directory)
|
|
321
|
-
- [`mkdir(path: string, options?: { recursive?: boolean }): Promise<void>`](#mkdirpath-string-options--recursive-boolean--promisevoid)
|
|
322
|
-
- [Read Directory](#read-directory)
|
|
323
|
-
- [`readDir(path: string): Promise<DirentData[]>`](#readdirpath-string-promisedirentdata)
|
|
324
|
-
- [Get Stats](#get-stats)
|
|
325
|
-
- [`stat(path: string): Promise<FileStat>`](#statpath-string-promisefilestat)
|
|
326
|
-
- [Check Existence](#check-existence)
|
|
327
|
-
- [`exists(path: string): Promise<boolean>`](#existspath-string-promiseboolean)
|
|
328
|
-
- [Remove Path](#remove-path)
|
|
329
|
-
- [`remove(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`](#removepath-string-options--recursive-boolean-force-boolean--promisevoid)
|
|
330
|
-
- [Copy Path](#copy-path)
|
|
331
|
-
- [`copy(source: string, destination: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`](#copysource-string-destination-string-options--recursive-boolean-force-boolean--promisevoid)
|
|
332
|
-
- [Rename Path](#rename-path)
|
|
333
|
-
- [`rename(oldPath: string, newPath: string): Promise<void>`](#renameoldpath-string-newpath-string-promisevoid)
|
|
334
|
-
- [Clear Directory](#clear-directory)
|
|
335
|
-
- [`clear(path?: string): Promise<void>`](#clearpath-string-promisevoid)
|
|
336
|
-
- [Index File System](#index-file-system)
|
|
337
|
-
- [`index(): Promise<Map<string, FileStat>>`](#index-promisemapstring-filestat)
|
|
338
|
-
- [Sync File System](#sync-file-system)
|
|
339
|
-
- [`sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`](#syncentries-string-string--uint8array--blob-options--cleanbefore-boolean--promisevoid)
|
|
340
|
-
- [Watch](#watch)
|
|
341
|
-
- [`watch(path: string, options?: WatchOptions): Promise<void>`](#watchpath-string-options-watchoptions-promisevoid)
|
|
342
|
-
- [Unwatch](#unwatch)
|
|
343
|
-
- [`unwatch(path: string): void`](#unwatchpath-string-void)
|
|
344
|
-
- [Dispose](#dispose)
|
|
345
|
-
- [`dispose(): void`](#dispose-void)
|
|
346
|
-
- [Configuration](#configuration)
|
|
347
|
-
- [`setOptions(options: { root?: string; watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): Promise<void>`](#setoptionsoptions--root-string-watchinterval-number-hashalgorithm-null--sha-1--sha-256--sha-384--sha-512-maxfilesize-number--promisevoid)
|
|
348
|
-
- [Resolve Path](#resolve-path)
|
|
349
|
-
- [`realpath(path: string): Promise<string>`](#realpathpath-string-promisestring)
|
|
350
|
-
- [Binary File Handling](#binary-file-handling)
|
|
351
|
-
- [Reading Binary Files](#reading-binary-files)
|
|
352
|
-
- [Writing Binary Files](#writing-binary-files)
|
|
353
|
-
- [Working with Different Data Types](#working-with-different-data-types)
|
|
354
|
-
- [File Upload and Download](#file-upload-and-download)
|
|
355
|
-
- [Supported Encodings](#supported-encodings)
|
|
356
|
-
- [Utility Functions](#utility-functions)
|
|
357
|
-
- [Path Utilities](#path-utilities)
|
|
358
|
-
- [Data Conversion](#data-conversion)
|
|
359
|
-
- [File System Utilities](#file-system-utilities)
|
|
360
|
-
- [Types](#types)
|
|
361
|
-
- [`FileStat`](#filestat)
|
|
362
|
-
- [`DirentData`](#direntdata)
|
|
363
|
-
- [`WatchOptions`](#watchoptions)
|
|
364
|
-
- [`RemoteOPFSWorker`](#remoteopfsworker)
|
|
365
|
-
- [Error Types](#error-types)
|
|
366
|
-
- [Browser Support](#browser-support)
|
|
367
|
-
- [Development](#development)
|
|
368
|
-
- [Building](#building)
|
|
369
|
-
- [Development Server](#development-server)
|
|
370
|
-
- [Testing](#testing)
|
|
371
|
-
- [Linting](#linting)
|
|
372
|
-
- [License](#license)
|
|
373
|
-
- [Contributing](#contributing)
|
|
374
|
-
|
|
375
|
-
### Entry Points
|
|
376
|
-
|
|
377
|
-
#### Mode 1: Inline Worker
|
|
378
|
-
|
|
379
|
-
##### `createWorker(options?: OPFSOptions)`
|
|
380
|
-
|
|
381
|
-
Creates a new file system instance with an inline worker.
|
|
382
|
-
|
|
383
|
-
```typescript
|
|
384
|
-
import { createWorker } from 'opfs-worker/inline';
|
|
385
|
-
|
|
386
|
-
// Basic usage
|
|
387
|
-
const fs = await createWorker();
|
|
388
|
-
|
|
389
|
-
// With options
|
|
390
|
-
const fs = await createWorker({
|
|
391
|
-
root: '/my-app',
|
|
392
|
-
watchInterval: 500,
|
|
393
|
-
hashAlgorithm: 'SHA-256',
|
|
394
|
-
broadcastChannel: 'my-app-events'
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Listen for file change events via BroadcastChannel
|
|
398
|
-
const channel = new BroadcastChannel('my-app-events');
|
|
399
|
-
channel.onmessage = (event) => {
|
|
400
|
-
console.log('File changed:', event.data);
|
|
401
|
-
};
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
**Parameters:**
|
|
405
|
-
|
|
406
|
-
- `options` (optional): Configuration options
|
|
407
|
-
- `root` (optional): Root path for the file system (default: '/')
|
|
408
|
-
- `watchInterval` (optional): Polling interval in milliseconds for file watching
|
|
409
|
-
- `hashAlgorithm` (optional): Hash algorithm for file hashing
|
|
410
|
-
- `broadcastChannel` (optional): Custom name for the broadcast channel (default: 'opfs-worker')
|
|
411
|
-
|
|
412
|
-
**Returns:** `Promise<RemoteOPFSWorker>` - A remote file system interface
|
|
413
|
-
|
|
414
|
-
#### Mode 2: Manual Worker Setup
|
|
415
|
-
|
|
416
|
-
##### `OPFSWorker`
|
|
417
|
-
|
|
418
|
-
The worker class that can be imported directly.
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
import OPFSWorker from 'opfs-worker/raw?worker';
|
|
422
|
-
import { wrap } from 'comlink';
|
|
423
|
-
|
|
424
|
-
const worker = wrap(new OPFSWorker());
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
**Note:** This approach requires a bundler that supports Web Workers (Vite, Webpack, Rollup, etc.) and the `comlink` package.
|
|
428
|
-
|
|
429
|
-
### Core Methods
|
|
430
|
-
|
|
431
|
-
### Read File
|
|
432
|
-
|
|
433
|
-
#### `readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`
|
|
434
|
-
|
|
435
|
-
Read a file from the file system. Supports both text and binary files.
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
// Read as text (default)
|
|
439
|
-
const content = await fs.readFile('/config/settings.json');
|
|
440
|
-
|
|
441
|
-
// Read as binary
|
|
442
|
-
const binaryData = await fs.readFile('/images/logo.png', 'binary');
|
|
443
|
-
|
|
444
|
-
// Read with specific encoding
|
|
445
|
-
const utf8Content = await fs.readFile('/data/utf8.txt', 'utf-8');
|
|
446
|
-
|
|
447
|
-
// Handle binary data
|
|
448
|
-
const imageBuffer = await fs.readFile('/image.png', 'binary');
|
|
449
|
-
const blob = new Blob([imageBuffer], { type: 'image/png' });
|
|
450
|
-
const url = URL.createObjectURL(blob);
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
**Parameters:**
|
|
454
|
-
|
|
455
|
-
- `path`: The path to the file to read
|
|
456
|
-
- `encoding` (optional): The encoding to use ('utf-8', 'binary', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
|
|
457
|
-
|
|
458
|
-
**Returns:** `Promise<string | Uint8Array>` - File contents as string or binary data
|
|
459
|
-
|
|
460
|
-
**Throws:** `FileNotFoundError` if the file doesn't exist
|
|
461
|
-
|
|
462
|
-
**Binary File Handling:**
|
|
463
|
-
- Use `'binary'` encoding to read files as `Uint8Array`
|
|
464
|
-
- Binary data can be converted to `Blob` for use with `URL.createObjectURL()`
|
|
465
|
-
- Supports various encodings for text files
|
|
466
|
-
|
|
467
|
-
### Write File
|
|
468
|
-
|
|
469
|
-
#### `writeFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`
|
|
470
|
-
|
|
471
|
-
Write data to a file, creating or overwriting it. Supports both text and binary data.
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
// Write text data
|
|
475
|
-
await fs.writeFile('/config/settings.json', JSON.stringify({ theme: 'dark' }));
|
|
476
|
-
|
|
477
|
-
// Write binary data
|
|
478
|
-
const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
|
|
479
|
-
await fs.writeFile('/data/binary.dat', binaryData);
|
|
480
|
-
|
|
481
|
-
// Write with specific encoding
|
|
482
|
-
await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
|
|
483
|
-
|
|
484
|
-
// Write binary data from file input
|
|
485
|
-
const fileInput = document.getElementById('file') as HTMLInputElement;
|
|
486
|
-
const file = fileInput.files?.[0];
|
|
487
|
-
if (file) {
|
|
488
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
489
|
-
await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Write binary data from fetch
|
|
493
|
-
const response = await fetch('/api/image.png');
|
|
494
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
495
|
-
await fs.writeFile('/downloaded-image.png', new Uint8Array(arrayBuffer));
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
**Parameters:**
|
|
499
|
-
|
|
500
|
-
- `path`: The path to the file to write
|
|
501
|
-
- `data`: The data to write (string, Uint8Array, or ArrayBuffer)
|
|
502
|
-
- `encoding` (optional): The encoding to use when writing string data ('utf-8', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
|
|
503
|
-
|
|
504
|
-
**Binary File Handling:**
|
|
505
|
-
- Pass `Uint8Array` or `ArrayBuffer` directly for binary data
|
|
506
|
-
- Use with file uploads, image processing, or any binary content
|
|
507
|
-
- Supports various text encodings for string data
|
|
508
|
-
|
|
509
|
-
### Append File
|
|
510
|
-
|
|
511
|
-
#### `appendFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`
|
|
512
|
-
|
|
513
|
-
Append data to the end of a file.
|
|
514
|
-
|
|
515
|
-
```typescript
|
|
516
|
-
// Append text to a log file
|
|
517
|
-
await fs.appendFile('/logs/app.log', `[${new Date().toISOString()}] User logged in\n`);
|
|
518
|
-
|
|
519
|
-
// Append binary data
|
|
520
|
-
const additionalData = new Uint8Array([6, 7, 8]);
|
|
521
|
-
await fs.appendFile('/data/binary.dat', additionalData);
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### Create Directory
|
|
525
|
-
|
|
526
|
-
#### `mkdir(path: string, options?: { recursive?: boolean }): Promise<void>`
|
|
527
|
-
|
|
528
|
-
Create a directory.
|
|
529
|
-
|
|
530
|
-
```typescript
|
|
531
|
-
// Create a single directory
|
|
532
|
-
await fs.mkdir('/users/john');
|
|
533
|
-
|
|
534
|
-
// Create nested directories
|
|
535
|
-
await fs.mkdir('/users/john/documents/projects', { recursive: true });
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
**Parameters:**
|
|
539
|
-
|
|
540
|
-
- `path`: The path where the directory should be created
|
|
541
|
-
- `options.recursive` (optional): Whether to create parent directories if they don't exist
|
|
542
|
-
|
|
543
|
-
### Read Directory
|
|
544
|
-
|
|
545
|
-
#### `readDir(path: string): Promise<DirentData[]>`
|
|
546
|
-
|
|
547
|
-
Read a directory's contents.
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
// Get simple list of names
|
|
551
|
-
const files = await fs.readDir('/users/john/documents');
|
|
552
|
-
console.log('Files:', files); // ['readme.txt', 'config.json', 'images']
|
|
553
|
-
|
|
554
|
-
// Get detailed information
|
|
555
|
-
const detailed = await fs.readDir('/users/john/documents');
|
|
556
|
-
detailed.forEach(item => {
|
|
557
|
-
console.log(`${item.name} - ${item.isFile ? 'file' : 'directory'}`);
|
|
558
|
-
});
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
**Returns:**
|
|
562
|
-
|
|
563
|
-
- `Promise<DirentData[]>` - Always returns detailed file/directory information
|
|
564
|
-
|
|
565
|
-
### Get Stats
|
|
566
|
-
|
|
567
|
-
#### `stat(path: string): Promise<FileStat>`
|
|
568
|
-
|
|
569
|
-
Get file or directory statistics. If hashing is enabled globally, file hashes will be included automatically.
|
|
570
|
-
|
|
571
|
-
```typescript
|
|
572
|
-
// Basic stats
|
|
573
|
-
const stats = await fs.stat('/config/settings.json');
|
|
574
|
-
console.log(`File size: ${stats.size} bytes`);
|
|
575
|
-
console.log(`Is file: ${stats.isFile}`);
|
|
576
|
-
console.log(`Modified: ${stats.mtime}`);
|
|
577
|
-
|
|
578
|
-
// If hashing is enabled globally, hash will be included
|
|
579
|
-
if (stats.hash) {
|
|
580
|
-
console.log(`Hash: ${stats.hash}`);
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
**Parameters:**
|
|
585
|
-
|
|
586
|
-
- `path`: The path to the file or directory
|
|
587
|
-
|
|
588
|
-
**Returns:** `Promise<FileStat>` - File/directory statistics
|
|
589
|
-
|
|
590
|
-
**Note:** File hashing is controlled globally via the constructor options or `setOptions()` method. When enabled, all file stats will automatically include hash information.
|
|
591
|
-
|
|
592
|
-
### Check Existence
|
|
593
|
-
|
|
594
|
-
#### `exists(path: string): Promise<boolean>`
|
|
595
|
-
|
|
596
|
-
Check if a file or directory exists.
|
|
597
|
-
|
|
598
|
-
```typescript
|
|
599
|
-
const exists = await fs.exists('/config/settings.json');
|
|
600
|
-
console.log(`File exists: ${exists}`);
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
**Returns:** `Promise<boolean>` - True if the file or directory exists
|
|
604
|
-
|
|
605
|
-
### Remove Path
|
|
606
|
-
|
|
607
|
-
#### `remove(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`
|
|
608
|
-
|
|
609
|
-
Remove files and directories.
|
|
610
|
-
|
|
611
|
-
```typescript
|
|
612
|
-
// Remove a file
|
|
613
|
-
await fs.remove('/path/to/file.txt');
|
|
614
|
-
|
|
615
|
-
// Remove a directory and all its contents
|
|
616
|
-
await fs.remove('/path/to/directory', { recursive: true });
|
|
617
|
-
|
|
618
|
-
// Remove with force (ignore if doesn't exist)
|
|
619
|
-
await fs.remove('/maybe/exists', { force: true });
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
**Parameters:**
|
|
623
|
-
|
|
624
|
-
- `path`: The path to remove
|
|
625
|
-
- `options.recursive` (optional): Whether to remove directories recursively (default: false)
|
|
626
|
-
- `options.force` (optional): Whether to ignore errors if the path doesn't exist (default: false)
|
|
627
|
-
|
|
628
|
-
### Copy Path
|
|
629
|
-
|
|
630
|
-
#### `copy(source: string, destination: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`
|
|
631
|
-
|
|
632
|
-
Copy files and directories.
|
|
633
|
-
|
|
634
|
-
```typescript
|
|
635
|
-
// Copy a file
|
|
636
|
-
await fs.copy('/source/file.txt', '/dest/file.txt');
|
|
637
|
-
|
|
638
|
-
// Copy a directory and all its contents
|
|
639
|
-
await fs.copy('/source/dir', '/dest/dir', { recursive: true });
|
|
640
|
-
|
|
641
|
-
// Copy without overwriting existing files
|
|
642
|
-
await fs.copy('/source', '/dest', { recursive: true, force: false });
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
**Parameters:**
|
|
646
|
-
|
|
647
|
-
- `source`: The source path to copy from
|
|
648
|
-
- `destination`: The destination path to copy to
|
|
649
|
-
- `options.recursive` (optional): Whether to copy directories recursively (default: false)
|
|
650
|
-
- `options.force` (optional): Whether to overwrite existing files (default: true)
|
|
651
|
-
|
|
652
|
-
### Rename Path
|
|
653
|
-
|
|
654
|
-
#### `rename(oldPath: string, newPath: string): Promise<void>`
|
|
655
|
-
|
|
656
|
-
Rename a file or directory.
|
|
657
|
-
|
|
658
|
-
```typescript
|
|
659
|
-
await fs.rename('/old/path/file.txt', '/new/path/renamed.txt');
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
**Parameters:**
|
|
663
|
-
|
|
664
|
-
- `oldPath`: The current path of the file or directory
|
|
665
|
-
- `newPath`: The new path for the file or directory
|
|
666
|
-
|
|
667
|
-
### Clear Directory
|
|
668
|
-
|
|
669
|
-
#### `clear(path?: string): Promise<void>`
|
|
670
|
-
|
|
671
|
-
Clear all contents of a directory without removing the directory itself.
|
|
672
|
-
|
|
673
|
-
```typescript
|
|
674
|
-
// Clear root directory contents
|
|
675
|
-
await fs.clear('/');
|
|
676
|
-
|
|
677
|
-
// Clear specific directory contents
|
|
678
|
-
await fs.clear('/data');
|
|
679
|
-
```
|
|
680
|
-
|
|
681
|
-
**Parameters:**
|
|
682
|
-
|
|
683
|
-
- `path` (optional): The path to the directory to clear (default: '/')
|
|
684
|
-
|
|
685
|
-
### Index File System
|
|
686
|
-
|
|
687
|
-
#### `index(): Promise<Map<string, FileStat>>`
|
|
688
|
-
|
|
689
|
-
Recursively list all files and directories with their stats. If hashing is enabled globally, file hashes will be included automatically.
|
|
690
|
-
|
|
691
|
-
```typescript
|
|
692
|
-
// Get complete file system index
|
|
693
|
-
const index = await fs.index();
|
|
694
|
-
|
|
695
|
-
// Iterate through all files and directories
|
|
696
|
-
for (const [path, stat] of index) {
|
|
697
|
-
console.log(`${path}: ${stat.isFile ? 'file' : 'directory'} (${stat.size} bytes)`);
|
|
698
|
-
if (stat.hash) console.log(` Hash: ${stat.hash}`);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Get specific file stats
|
|
702
|
-
const fileStats = index.get('/data/config.json');
|
|
703
|
-
if (fileStats) {
|
|
704
|
-
console.log(`File size: ${fileStats.size} bytes`);
|
|
705
|
-
if (fileStats.hash) console.log(`Hash: ${fileStats.hash}`);
|
|
706
|
-
}
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
**Returns:** `Promise<Map<string, FileStat>>` - Map of path => FileStat
|
|
710
|
-
|
|
711
|
-
**Note:** File hashing is controlled globally via the constructor options or `setOptions()` method. When enabled, all file stats in the index will automatically include hash information.
|
|
712
|
-
|
|
713
|
-
### Sync File System
|
|
714
|
-
|
|
715
|
-
#### `sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`
|
|
716
|
-
|
|
717
|
-
Synchronize the file system with external data.
|
|
718
|
-
|
|
719
|
-
```typescript
|
|
720
|
-
// Sync with external data
|
|
721
|
-
const entries: [string, string | Uint8Array | Blob][] = [
|
|
722
|
-
['/config.json', JSON.stringify({ theme: 'dark' })],
|
|
723
|
-
['/data/binary.dat', new Uint8Array([1, 2, 3, 4])],
|
|
724
|
-
['/upload.txt', new Blob(['file content'], { type: 'text/plain' })]
|
|
725
|
-
];
|
|
726
|
-
|
|
727
|
-
// Sync without clearing existing files
|
|
728
|
-
await fs.sync(entries);
|
|
729
|
-
|
|
730
|
-
// Clean file system and then sync
|
|
731
|
-
await fs.sync(entries, { cleanBefore: true });
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
**Parameters:**
|
|
735
|
-
|
|
736
|
-
- `entries`: Array of [path, data] tuples to sync
|
|
737
|
-
- `options.cleanBefore` (optional): Whether to clear the file system before syncing (default: false)
|
|
738
|
-
|
|
739
|
-
### Watch
|
|
740
|
-
|
|
741
|
-
#### `watch(path: string, options?: WatchOptions): Promise<void>`
|
|
742
|
-
|
|
743
|
-
Start watching a file or directory for changes. Detected changes are sent via BroadcastChannel
|
|
744
|
-
using the channel name specified in the `broadcastChannel` option.
|
|
745
|
-
|
|
746
|
-
```typescript
|
|
747
|
-
// Start watching a directory recursively (default behavior)
|
|
748
|
-
await fs.watch('/docs');
|
|
749
|
-
|
|
750
|
-
// Start watching a directory shallow (only immediate children)
|
|
751
|
-
await fs.watch('/docs', { recursive: false });
|
|
752
|
-
|
|
753
|
-
// Watch a single file (non-recursive)
|
|
754
|
-
await fs.watch('/config.json', { recursive: false });
|
|
755
|
-
|
|
756
|
-
// Listen for changes via BroadcastChannel
|
|
757
|
-
const channel = new BroadcastChannel('opfs-worker'); // or your custom channel name
|
|
758
|
-
channel.onmessage = (event) => {
|
|
759
|
-
const { path, type, isDirectory, timestamp, hash } = event.data;
|
|
760
|
-
console.log(`File ${path} was ${type} at ${timestamp}`);
|
|
761
|
-
if (hash) console.log(`Hash: ${hash}`);
|
|
762
|
-
};
|
|
763
|
-
```
|
|
764
|
-
|
|
765
|
-
**Parameters:**
|
|
766
|
-
|
|
767
|
-
- `path`: File or directory to watch
|
|
768
|
-
- `options` (optional): Watch options
|
|
769
|
-
- `options.recursive` (optional): Whether to watch recursively (default: `true`)
|
|
770
|
-
- `true`: Watch the entire directory tree (current behavior)
|
|
771
|
-
- `false`: Only watch the specified path and its immediate children (shallow watching)
|
|
346
|
+
The complete API reference is available in the [docs/api-reference.md](docs/api-reference.md) file.
|
|
772
347
|
|
|
773
|
-
|
|
348
|
+
### Quick API Overview
|
|
774
349
|
|
|
775
|
-
|
|
350
|
+
**Entry Points:**
|
|
776
351
|
|
|
777
|
-
|
|
352
|
+
- `createWorker(options?)` - Create file system instance with inline worker
|
|
353
|
+
- `OPFSWorker` - Direct worker class for manual setup
|
|
778
354
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
```typescript
|
|
782
|
-
fs.unwatch('/docs');
|
|
783
|
-
```
|
|
355
|
+
**Core File Operations:**
|
|
784
356
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
fs.dispose();
|
|
794
|
-
```
|
|
357
|
+
- `readFile(path, encoding?)` - Read files as text or binary
|
|
358
|
+
- `writeFile(path, data, encoding?)` - Write text or binary data
|
|
359
|
+
- `mkdir(path, options?)` - Create directories
|
|
360
|
+
- `readDir(path)` - List directory contents
|
|
361
|
+
- `stat(path)` - Get file/directory statistics
|
|
362
|
+
- `remove(path, options?)` - Remove files/directories
|
|
363
|
+
- `copy(source, destination, options?)` - Copy files/directories
|
|
364
|
+
- `rename(oldPath, newPath)` - Rename files/directories
|
|
795
365
|
|
|
796
|
-
**
|
|
366
|
+
**Advanced Features:**
|
|
797
367
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
Update configuration options for the file system, including root path, watch interval, hash algorithm, and maximum file size for hashing.
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
// Change root path (will automatically remount)
|
|
806
|
-
await fs.setOptions({ root: '/new-app' });
|
|
807
|
-
|
|
808
|
-
// Enable SHA-256 hashing for all file operations
|
|
809
|
-
await fs.setOptions({ hashAlgorithm: 'SHA-256' });
|
|
810
|
-
|
|
811
|
-
// Change watch interval to 100ms for faster change detection
|
|
812
|
-
await fs.setOptions({ watchInterval: 100 });
|
|
813
|
-
|
|
814
|
-
// Set custom maximum file size for hashing (100MB)
|
|
815
|
-
await fs.setOptions({ maxFileSize: 100 * 1024 * 1024 });
|
|
816
|
-
|
|
817
|
-
// Disable hashing
|
|
818
|
-
await fs.setOptions({ hashAlgorithm: null });
|
|
819
|
-
|
|
820
|
-
// Update multiple options at once
|
|
821
|
-
await fs.setOptions({
|
|
822
|
-
root: '/my-app',
|
|
823
|
-
watchInterval: 200,
|
|
824
|
-
hashAlgorithm: 'SHA-1',
|
|
825
|
-
maxFileSize: 50 * 1024 * 1024 // 50MB
|
|
826
|
-
});
|
|
827
|
-
```
|
|
368
|
+
- `watch(path, options?)` - Watch for file changes with minimatch patterns
|
|
369
|
+
- `index()` - Get complete file system index
|
|
370
|
+
- `sync(entries, options?)` - Bulk synchronization
|
|
371
|
+
- `setOptions(options)` - Update configuration
|
|
828
372
|
|
|
829
|
-
**
|
|
373
|
+
**Binary File Support:**
|
|
830
374
|
|
|
831
|
-
-
|
|
832
|
-
-
|
|
833
|
-
-
|
|
834
|
-
- `options.maxFileSize` (optional): Maximum file size in bytes for hashing (default: 50MB)
|
|
375
|
+
- Full support for images, documents, and any binary data
|
|
376
|
+
- Automatic conversion between Uint8Array, ArrayBuffer, and Blob
|
|
377
|
+
- Multiple text encodings (UTF-8, UTF-16, ASCII, Base64, etc.)
|
|
835
378
|
|
|
836
|
-
**
|
|
379
|
+
**Utility Functions:**
|
|
837
380
|
|
|
838
|
-
|
|
381
|
+
- Path manipulation (`basename`, `dirname`, `normalizePath`, etc.)
|
|
382
|
+
- Data conversion helpers
|
|
383
|
+
- OPFS support detection
|
|
839
384
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
Resolve a path to an absolute path.
|
|
843
|
-
|
|
844
|
-
```typescript
|
|
845
|
-
// Resolve relative path
|
|
846
|
-
const absolute = await fs.realpath('./config/../data/file.txt');
|
|
847
|
-
console.log(absolute); // '/data/file.txt'
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
**Returns:** `Promise<string>` - The absolute normalized path
|
|
851
|
-
|
|
852
|
-
**Throws:** `FileNotFoundError` if the path doesn't exist
|
|
853
|
-
|
|
854
|
-
## Binary File Handling
|
|
855
|
-
|
|
856
|
-
This library provides comprehensive support for binary files, making it easy to work with images, documents, and other binary data.
|
|
857
|
-
|
|
858
|
-
### Reading Binary Files
|
|
859
|
-
|
|
860
|
-
```typescript
|
|
861
|
-
// Read as binary data
|
|
862
|
-
const imageData = await fs.readFile('/image.png', 'binary');
|
|
863
|
-
const documentData = await fs.readFile('/document.pdf', 'binary');
|
|
864
|
-
|
|
865
|
-
// Convert to Blob for use with URLs
|
|
866
|
-
const imageBuffer = await fs.readFile('/image.png', 'binary');
|
|
867
|
-
const blob = new Blob([imageBuffer], { type: 'image/png' });
|
|
868
|
-
const url = URL.createObjectURL(blob);
|
|
869
|
-
|
|
870
|
-
// Display image
|
|
871
|
-
const img = document.createElement('img');
|
|
872
|
-
img.src = url;
|
|
873
|
-
document.body.appendChild(img);
|
|
874
|
-
```
|
|
875
|
-
|
|
876
|
-
### Writing Binary Files
|
|
877
|
-
|
|
878
|
-
```typescript
|
|
879
|
-
// Write binary data directly
|
|
880
|
-
const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
|
|
881
|
-
await fs.writeFile('/data.bin', binaryData);
|
|
882
|
-
|
|
883
|
-
// From file input
|
|
884
|
-
const fileInput = document.getElementById('file') as HTMLInputElement;
|
|
885
|
-
const file = fileInput.files?.[0];
|
|
886
|
-
if (file) {
|
|
887
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
888
|
-
await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// From fetch response
|
|
892
|
-
const response = await fetch('/api/download/file.pdf');
|
|
893
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
894
|
-
await fs.writeFile('/downloaded-file.pdf', new Uint8Array(arrayBuffer));
|
|
895
|
-
|
|
896
|
-
// From canvas
|
|
897
|
-
const canvas = document.createElement('canvas');
|
|
898
|
-
const ctx = canvas.getContext('2d');
|
|
899
|
-
// ... draw something ...
|
|
900
|
-
canvas.toBlob(async (blob) => {
|
|
901
|
-
if (blob) {
|
|
902
|
-
const arrayBuffer = await blob.arrayBuffer();
|
|
903
|
-
await fs.writeFile('/canvas-image.png', new Uint8Array(arrayBuffer));
|
|
904
|
-
}
|
|
905
|
-
});
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
### Working with Different Data Types
|
|
909
|
-
|
|
910
|
-
```typescript
|
|
911
|
-
// Convert between different binary formats
|
|
912
|
-
const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
|
|
913
|
-
const arrayBuffer = uint8Array.buffer;
|
|
914
|
-
|
|
915
|
-
// All of these work the same way
|
|
916
|
-
await fs.writeFile('/data1.bin', uint8Array);
|
|
917
|
-
await fs.writeFile('/data2.bin', arrayBuffer);
|
|
918
|
-
await fs.writeFile('/data3.bin', new Uint8Array(arrayBuffer));
|
|
919
|
-
|
|
920
|
-
// Read back as binary
|
|
921
|
-
const data = await fs.readFile('/data1.bin', 'binary');
|
|
922
|
-
console.log(data); // Uint8Array
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
### File Upload and Download
|
|
926
|
-
|
|
927
|
-
```typescript
|
|
928
|
-
// Handle file uploads
|
|
929
|
-
const handleFileUpload = async (file: File) => {
|
|
930
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
931
|
-
await fs.writeFile(`/uploads/${file.name}`, new Uint8Array(arrayBuffer));
|
|
932
|
-
};
|
|
933
|
-
|
|
934
|
-
// Create downloadable files
|
|
935
|
-
const createDownloadLink = async (filePath: string, fileName: string) => {
|
|
936
|
-
const data = await fs.readFile(filePath, 'binary');
|
|
937
|
-
const blob = new Blob([data]);
|
|
938
|
-
const url = URL.createObjectURL(blob);
|
|
939
|
-
|
|
940
|
-
const a = document.createElement('a');
|
|
941
|
-
a.href = url;
|
|
942
|
-
a.download = fileName;
|
|
943
|
-
a.click();
|
|
944
|
-
|
|
945
|
-
URL.revokeObjectURL(url);
|
|
946
|
-
};
|
|
947
|
-
```
|
|
948
|
-
|
|
949
|
-
### Supported Encodings
|
|
950
|
-
|
|
951
|
-
The library supports various encodings for text files:
|
|
952
|
-
|
|
953
|
-
- `'utf-8'` (default) - UTF-8 encoding
|
|
954
|
-
- `'utf-16le'` - UTF-16 little-endian
|
|
955
|
-
- `'ascii'` - ASCII encoding
|
|
956
|
-
- `'latin1'` - Latin-1 encoding
|
|
957
|
-
- `'base64'` - Base64 encoding
|
|
958
|
-
- `'hex'` - Hexadecimal encoding
|
|
959
|
-
- `'binary'` - Raw binary data (returns Uint8Array)
|
|
960
|
-
|
|
961
|
-
## Utility Functions
|
|
962
|
-
|
|
963
|
-
The library exports several utility functions that can be used independently:
|
|
964
|
-
|
|
965
|
-
### Path Utilities
|
|
966
|
-
|
|
967
|
-
```typescript
|
|
968
|
-
import { basename, dirname, normalizePath, resolvePath, extname } from 'opfs-worker';
|
|
969
|
-
|
|
970
|
-
// Extract filename from path
|
|
971
|
-
basename('/path/to/file.txt'); // 'file.txt'
|
|
972
|
-
basename('/path/to/directory/'); // ''
|
|
973
|
-
|
|
974
|
-
// Extract directory path
|
|
975
|
-
dirname('/path/to/file.txt'); // '/path/to'
|
|
976
|
-
dirname('file.txt'); // '/'
|
|
977
|
-
|
|
978
|
-
// Normalize path to start with '/'
|
|
979
|
-
normalizePath('path/to/file'); // '/path/to/file'
|
|
980
|
-
normalizePath('/path/to/file'); // '/path/to/file'
|
|
981
|
-
|
|
982
|
-
// Resolve relative paths
|
|
983
|
-
resolvePath('./config/../data/file.txt'); // '/data/file.txt'
|
|
984
|
-
resolvePath('/path/to/../file.txt'); // '/path/file.txt'
|
|
985
|
-
|
|
986
|
-
// Get file extension
|
|
987
|
-
extname('/path/to/file.txt'); // '.txt'
|
|
988
|
-
extname('/path/to/file'); // ''
|
|
989
|
-
extname('/path/to/file.name.ext'); // '.ext'
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
### Data Conversion
|
|
993
|
-
|
|
994
|
-
```typescript
|
|
995
|
-
import { convertBlobToUint8Array } from 'opfs-worker';
|
|
996
|
-
|
|
997
|
-
// Convert Blob to Uint8Array
|
|
998
|
-
const fileInput = document.getElementById('file') as HTMLInputElement;
|
|
999
|
-
const file = fileInput.files?.[0];
|
|
1000
|
-
if (file) {
|
|
1001
|
-
const data = await convertBlobToUint8Array(file);
|
|
1002
|
-
await fs.writeFile('/uploaded-file', data);
|
|
1003
|
-
}
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
### File System Utilities
|
|
1007
|
-
|
|
1008
|
-
```typescript
|
|
1009
|
-
import { checkOPFSSupport, splitPath, joinPath } from 'opfs-worker';
|
|
1010
|
-
|
|
1011
|
-
// Check if browser supports OPFS
|
|
1012
|
-
checkOPFSSupport(); // Throws error if not supported
|
|
1013
|
-
|
|
1014
|
-
// Path manipulation
|
|
1015
|
-
splitPath('/path/to/file'); // ['path', 'to', 'file']
|
|
1016
|
-
joinPath(['path', 'to', 'file']); // '/path/to/file'
|
|
1017
|
-
```
|
|
385
|
+
For detailed API documentation with examples, see [docs/api-reference.md](docs/api-reference.md).
|
|
1018
386
|
|
|
1019
387
|
## Types
|
|
1020
388
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
File or directory statistics.
|
|
1024
|
-
|
|
1025
|
-
```typescript
|
|
1026
|
-
interface FileStat {
|
|
1027
|
-
kind: 'file' | 'directory';
|
|
1028
|
-
size: number;
|
|
1029
|
-
mtime: string; // ISO string
|
|
1030
|
-
ctime: string; // ISO string
|
|
1031
|
-
isFile: boolean;
|
|
1032
|
-
isDirectory: boolean;
|
|
1033
|
-
hash?: string; // Hash of file content (only for files)
|
|
1034
|
-
}
|
|
1035
|
-
```
|
|
1036
|
-
|
|
1037
|
-
### `DirentData`
|
|
1038
|
-
|
|
1039
|
-
Directory entry information.
|
|
1040
|
-
|
|
1041
|
-
```typescript
|
|
1042
|
-
interface DirentData {
|
|
1043
|
-
name: string;
|
|
1044
|
-
kind: 'file' | 'directory';
|
|
1045
|
-
isFile: boolean;
|
|
1046
|
-
isDirectory: boolean;
|
|
1047
|
-
}
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
### `WatchOptions`
|
|
1051
|
-
|
|
1052
|
-
Options for file watching operations.
|
|
1053
|
-
|
|
1054
|
-
```typescript
|
|
1055
|
-
interface WatchOptions {
|
|
1056
|
-
recursive?: boolean; // Whether to watch recursively (default: true)
|
|
1057
|
-
}
|
|
1058
|
-
```
|
|
1059
|
-
|
|
1060
|
-
### `RemoteOPFSWorker`
|
|
1061
|
-
|
|
1062
|
-
Remote file system interface type.
|
|
1063
|
-
|
|
1064
|
-
```typescript
|
|
1065
|
-
type RemoteOPFSWorker = Remote<OPFSWorker>;
|
|
1066
|
-
```
|
|
1067
|
-
|
|
1068
|
-
## Error Types
|
|
1069
|
-
|
|
1070
|
-
The library provides comprehensive error handling with specific error types:
|
|
1071
|
-
|
|
1072
|
-
- `OPFSError` - Base error class for all OPFS-related errors
|
|
1073
|
-
- `OPFSNotSupportedError` - Thrown when OPFS is not supported in the browser
|
|
1074
|
-
- `OPFSNotMountedError` - Thrown when OPFS is not mounted
|
|
1075
|
-
- `PathError` - Thrown for invalid paths or path traversal attempts
|
|
1076
|
-
- `FileNotFoundError` - Thrown when a requested file doesn't exist
|
|
1077
|
-
- `DirectoryNotFoundError` - Thrown when a requested directory doesn't exist
|
|
1078
|
-
- `PermissionError` - Thrown when permission is denied for an operation
|
|
1079
|
-
- `StorageError` - Thrown when an operation fails due to insufficient storage
|
|
1080
|
-
- `TimeoutError` - Thrown when an operation times out
|
|
389
|
+
Full TypeScript types are provided — see [docs/types.md](docs/types.md) for complete type definitions including `FileStat`, `DirentData`, `WatchOptions`, `OPFSOptions`, and more.
|
|
1081
390
|
|
|
1082
391
|
## Browser Support
|
|
1083
392
|
|
|
@@ -1096,8 +405,6 @@ This library works in **all modern browsers**, including Safari, Firefox, Chrome
|
|
|
1096
405
|
- ✅ Safari 15.2+
|
|
1097
406
|
- ✅ Opera 72+
|
|
1098
407
|
|
|
1099
|
-
**Note:** The library gracefully handles browsers that don't support OPFS by providing appropriate error messages and fallback behavior.
|
|
1100
|
-
|
|
1101
408
|
## Development
|
|
1102
409
|
|
|
1103
410
|
### Building
|
|
@@ -1132,4 +439,21 @@ MIT
|
|
|
1132
439
|
|
|
1133
440
|
## Contributing
|
|
1134
441
|
|
|
1135
|
-
Contributions are welcome!
|
|
442
|
+
Contributions are welcome!
|
|
443
|
+
|
|
444
|
+
**How to contribute:**
|
|
445
|
+
|
|
446
|
+
- 🐛 **Report bugs** or suggest features via [GitHub Issues](https://github.com/kachurun/opfs-worker/issues)
|
|
447
|
+
- 💡 **Submit ideas** for improvements or new features
|
|
448
|
+
- 🔧 **Send PRs** for bug fixes, documentation, or enhancements
|
|
449
|
+
- 📚 **Improve docs** - help make the library more accessible
|
|
450
|
+
|
|
451
|
+
**Getting started:**
|
|
452
|
+
|
|
453
|
+
1. Fork the repository
|
|
454
|
+
2. Create a feature branch
|
|
455
|
+
3. Make your changes
|
|
456
|
+
4. Add tests if applicable
|
|
457
|
+
5. Submit a Pull Request
|
|
458
|
+
|
|
459
|
+
_We welcome all contributions, big and small!_
|