opfs-worker 0.3.3 → 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 +238 -865
- package/dist/assets/worker-3SMz_DAt.js.map +1 -0
- package/dist/helpers-Bm2TWyPQ.js +1224 -0
- 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 +1428 -506
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1465 -537
- package/dist/index.js.map +1 -1
- package/dist/raw.cjs +1 -1
- package/dist/raw.cjs.map +1 -1
- package/dist/raw.js +133 -188
- package/dist/raw.js.map +1 -1
- package/dist/types.d.ts +20 -5
- 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 +34 -2
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/worker.d.ts +29 -21
- package/dist/worker.d.ts.map +1 -1
- package/package.json +5 -2
- package/dist/assets/worker-CLK22qZk.js.map +0 -1
- package/dist/helpers-B87wz5kv.cjs +0 -2
- package/dist/helpers-B87wz5kv.cjs.map +0 -1
- package/dist/helpers-DxFcNkZe.js +0 -222
- package/dist/helpers-DxFcNkZe.js.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,40 +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
|
|
58
|
-
// Create a file system instance
|
|
131
|
+
async function basicExample() {
|
|
132
|
+
// Create a file system instance with default root path '/'
|
|
59
133
|
const fs = await createWorker();
|
|
60
134
|
|
|
61
|
-
//
|
|
62
|
-
// await fs.mount('/my-app'); // Uses custom subdirectory
|
|
63
|
-
|
|
64
|
-
// Write a file (auto-mounts if not mounted)
|
|
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
|
-
|
|
84
|
-
)
|
|
160
|
+
// Use file watching with BroadcastChannel
|
|
161
|
+
await fs.watch('/', {
|
|
162
|
+
recursive: true,
|
|
163
|
+
include: ['*.json'],
|
|
164
|
+
exclude: ['dist/**']
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
broadcastChannel.onmessage = (event) => {
|
|
168
|
+
console.log(`File changed: `, event.data);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Get file statistics with hashing
|
|
172
|
+
const stats = await fs.stat('/image.png');
|
|
85
173
|
|
|
86
|
-
|
|
174
|
+
console.log(
|
|
175
|
+
`File size: ${stats.size} bytes`,
|
|
176
|
+
`Hash: ${stats.hash}`
|
|
177
|
+
);
|
|
87
178
|
}
|
|
88
179
|
```
|
|
89
180
|
|
|
90
|
-
> **Note:**
|
|
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.
|
|
91
182
|
|
|
92
183
|
### Manual Worker Setup
|
|
93
184
|
|
|
@@ -97,102 +188,72 @@ For more control over the worker lifecycle, use the worker directly with your bu
|
|
|
97
188
|
import OPFSWorker from 'opfs-worker/raw?worker';
|
|
98
189
|
import { wrap } from 'comlink';
|
|
99
190
|
|
|
100
|
-
async function
|
|
101
|
-
// Create and wrap the worker
|
|
191
|
+
async function basicExample() {
|
|
192
|
+
// Create and wrap the worker with default root path '/'
|
|
102
193
|
const worker = wrap(new OPFSWorker());
|
|
103
194
|
|
|
104
|
-
//
|
|
105
|
-
// await worker.mount(); // Uses OPFS root directory
|
|
106
|
-
// await worker.mount('/my-app'); // Uses custom subdirectory
|
|
107
|
-
|
|
108
|
-
// Use the file system (auto-mounts if not mounted)
|
|
195
|
+
// Use the file system
|
|
109
196
|
await worker.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
|
|
110
197
|
const config = await worker.readFile('/config.json');
|
|
111
198
|
console.log(JSON.parse(config));
|
|
112
199
|
}
|
|
113
200
|
|
|
114
|
-
//
|
|
115
|
-
async function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
await worker.mount('/my-app');
|
|
125
|
-
await worker.watch('/docs');
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
**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.
|
|
130
|
-
|
|
131
|
-
**Watch Callbacks:** File watching with callbacks is available with both `createWorker()` and raw worker usage. The `createWorker()` function uses Comlink.proxy to handle function serialization across worker boundaries.
|
|
201
|
+
// Extended example with all options
|
|
202
|
+
async function extendedExample() {
|
|
203
|
+
// Create with all available options
|
|
204
|
+
const worker = wrap(new OPFSWorker({
|
|
205
|
+
root: '/my-app',
|
|
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
|
|
210
|
+
}));
|
|
132
211
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const fs = await createWorker();
|
|
140
|
-
// Mount is optional - auto-mounts to OPFS root if not called
|
|
141
|
-
// await fs.mount('/my-app'); // For custom subdirectory
|
|
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 });
|
|
142
218
|
|
|
143
219
|
// Create directories
|
|
144
|
-
await
|
|
145
|
-
|
|
146
|
-
// Write multiple files
|
|
147
|
-
await fs.writeFile('/data/config.json', JSON.stringify({ version: '1.0' }));
|
|
148
|
-
await fs.writeFile('/data/logs/app.log', 'Application started\n');
|
|
149
|
-
|
|
150
|
-
// Handle binary files
|
|
151
|
-
const imageData = new Uint8Array([/* binary data */]);
|
|
152
|
-
await fs.writeFile('/data/image.png', imageData);
|
|
153
|
-
const binaryData = await fs.readFile('/data/image.png', 'binary');
|
|
154
|
-
|
|
155
|
-
// Append to a file
|
|
156
|
-
await fs.appendFile('/data/logs/app.log', `${new Date().toISOString()}: User logged in\n`);
|
|
220
|
+
await worker.mkdir('/data/logs', { recursive: true });
|
|
157
221
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
console.log(`File size: ${stats.size} bytes`);
|
|
162
|
-
if (stats.hash) {
|
|
163
|
-
console.log(`Hash: ${stats.hash}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// List directory contents
|
|
167
|
-
const files = await fs.readDir('/data');
|
|
168
|
-
files.forEach(item => {
|
|
169
|
-
console.log(`${item.name} - ${item.isFile ? 'file' : 'directory'}`);
|
|
170
|
-
});
|
|
222
|
+
// Append to log files
|
|
223
|
+
await worker.appendFile('/data/logs/app.log', `${new Date().toISOString()}: App started\n`);
|
|
171
224
|
|
|
225
|
+
// Watch for changes
|
|
226
|
+
await worker.watch('/data', { recursive: true });
|
|
172
227
|
|
|
228
|
+
const channel = new BroadcastChannel('my-app-events');
|
|
229
|
+
channel.onmessage = (event) => {
|
|
230
|
+
console.log('File changed:', event.data);
|
|
231
|
+
};
|
|
173
232
|
}
|
|
174
233
|
```
|
|
175
234
|
|
|
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.
|
|
236
|
+
|
|
176
237
|
### Hash Algorithm Configuration
|
|
177
238
|
|
|
178
|
-
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.
|
|
179
240
|
|
|
180
241
|
```typescript
|
|
181
242
|
import { createWorker } from 'opfs-worker';
|
|
182
243
|
|
|
183
244
|
async function hashExample() {
|
|
184
245
|
const fs = await createWorker();
|
|
185
|
-
|
|
246
|
+
|
|
186
247
|
// Enable SHA-256 hashing globally
|
|
187
248
|
fs.setOptions({ hashAlgorithm: 'SHA-256' });
|
|
188
|
-
|
|
249
|
+
|
|
189
250
|
// Write a file
|
|
190
251
|
await fs.writeFile('/data.txt', 'Hello World');
|
|
191
|
-
|
|
252
|
+
|
|
192
253
|
// Get stats - hash will be included automatically
|
|
193
254
|
const stats = await fs.stat('/data.txt');
|
|
194
255
|
console.log(`Hash: ${stats.hash}`); // SHA-256 hash
|
|
195
|
-
|
|
256
|
+
|
|
196
257
|
// Get file system index - all files will include hashes
|
|
197
258
|
const index = await fs.index();
|
|
198
259
|
for (const [path, stat] of index) {
|
|
@@ -200,7 +261,7 @@ async function hashExample() {
|
|
|
200
261
|
console.log(`${path}: ${stat.hash}`);
|
|
201
262
|
}
|
|
202
263
|
}
|
|
203
|
-
|
|
264
|
+
|
|
204
265
|
// Watch events will also include hash information
|
|
205
266
|
const channel = new BroadcastChannel('opfs-worker');
|
|
206
267
|
channel.onmessage = (event) => {
|
|
@@ -213,14 +274,14 @@ async function hashExample() {
|
|
|
213
274
|
// Configure maximum file size for hashing
|
|
214
275
|
async function maxFileSizeExample() {
|
|
215
276
|
const fs = await createWorker();
|
|
216
|
-
|
|
277
|
+
|
|
217
278
|
// Set custom maximum file size (100MB instead of default 50MB)
|
|
218
|
-
fs.setOptions({
|
|
279
|
+
fs.setOptions({
|
|
219
280
|
hashAlgorithm: 'SHA-256',
|
|
220
281
|
maxFileSize: 100 * 1024 * 1024 // 100MB
|
|
221
282
|
});
|
|
222
|
-
|
|
223
|
-
//
|
|
283
|
+
|
|
284
|
+
// Files up to 100MB will be hashed
|
|
224
285
|
// Files larger than this will not have hash information
|
|
225
286
|
const stats = await fs.stat('/large-file.dat');
|
|
226
287
|
if (stats.hash) {
|
|
@@ -232,6 +293,7 @@ async function maxFileSizeExample() {
|
|
|
232
293
|
```
|
|
233
294
|
|
|
234
295
|
**Supported Hash Algorithms:**
|
|
296
|
+
|
|
235
297
|
- `'SHA-1'` - Fastest, good for general use (default)
|
|
236
298
|
- `'SHA-256'` - More secure, widely supported
|
|
237
299
|
- `'SHA-384'` - Higher security
|
|
@@ -240,795 +302,91 @@ async function maxFileSizeExample() {
|
|
|
240
302
|
|
|
241
303
|
**Note:** When hashing is enabled, it affects all file operations including `stat()`, `index()`, and watch events. Set to `null` when you don't need hash information to improve performance.
|
|
242
304
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
Check out the live demo powered by Vite and hosted on GitHub Pages.
|
|
246
|
-
|
|
247
|
-
[Live Demo](https://kachurun.github.io/opfs-worker/)
|
|
248
|
-
|
|
249
|
-
## API Reference
|
|
250
|
-
|
|
251
|
-
- [OPFS Worker](#opfs-worker)
|
|
252
|
-
- [Table of Contents](#table-of-contents)
|
|
253
|
-
- [Features](#features)
|
|
254
|
-
- [Installation](#installation)
|
|
255
|
-
- [Quick Start](#quick-start)
|
|
256
|
-
- [Inline Worker (Recommended)](#inline-worker-recommended)
|
|
257
|
-
- [Manual Worker Setup](#manual-worker-setup)
|
|
258
|
-
- [Advanced Usage](#advanced-usage)
|
|
259
|
-
- [Hash Algorithm Configuration](#hash-algorithm-configuration)
|
|
260
|
-
- [Demo](#demo)
|
|
261
|
-
- [API Reference](#api-reference)
|
|
262
|
-
- [Entry Points](#entry-points)
|
|
263
|
-
- [Mode 1: Inline Worker](#mode-1-inline-worker)
|
|
264
|
-
- [`createWorker(options?: OPFSOptions)`](#createworkeroptions-opfsoptions)
|
|
265
|
-
- [Mode 2: Manual Worker Setup](#mode-2-manual-worker-setup)
|
|
266
|
-
- [`OPFSWorker`](#opfsworker)
|
|
267
|
-
- [Core Methods](#core-methods)
|
|
268
|
-
- [Mount](#mount)
|
|
269
|
-
- [`mount(root?: string): Promise<boolean>`](#mountroot-string-promiseboolean)
|
|
270
|
-
- [Read File](#read-file)
|
|
271
|
-
- [`readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`](#readfilepath-string-encoding-bufferencoding--binary-promisestring--uint8array)
|
|
272
|
-
- [Write File](#write-file)
|
|
273
|
-
- [`writeFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`](#writefilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
|
|
274
|
-
- [Append File](#append-file)
|
|
275
|
-
- [`appendFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`](#appendfilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
|
|
276
|
-
- [Create Directory](#create-directory)
|
|
277
|
-
- [`mkdir(path: string, options?: { recursive?: boolean }): Promise<void>`](#mkdirpath-string-options--recursive-boolean--promisevoid)
|
|
278
|
-
- [Read Directory](#read-directory)
|
|
279
|
-
- [`readDir(path: string): Promise<DirentData[]>`](#readdirpath-string--promisedirentdata)
|
|
280
|
-
- [Get Stats](#get-stats)
|
|
281
|
-
- [`stat(path: string): Promise<FileStat>`](#statpath-string-promisefilestat)
|
|
282
|
-
- [Check Existence](#check-existence)
|
|
283
|
-
- [`exists(path: string): Promise<boolean>`](#existspath-string-promiseboolean)
|
|
284
|
-
- [Remove Path](#remove-path)
|
|
285
|
-
- [`remove(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`](#removepath-string-options--recursive-boolean-force-boolean--promisevoid)
|
|
286
|
-
- [Copy Path](#copy-path)
|
|
287
|
-
- [`copy(source: string, destination: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`](#copysource-string-destination-string-options--recursive-boolean-force-boolean--promisevoid)
|
|
288
|
-
- [Rename Path](#rename-path)
|
|
289
|
-
- [`rename(oldPath: string, newPath: string): Promise<void>`](#renameoldpath-string-newpath-string-promisevoid)
|
|
290
|
-
- [Clear Directory](#clear-directory)
|
|
291
|
-
- [`clear(path?: string): Promise<void>`](#clearpath-string-promisevoid)
|
|
292
|
-
- [Index File System](#index-file-system)
|
|
293
|
-
- [`index(): Promise<Map<string, FileStat>>`](#index-promisemapstring-filestat)
|
|
294
|
-
- [Sync File System](#sync-file-system)
|
|
295
|
-
- [`sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`](#syncentries-string-string--uint8array--blob-options--cleanbefore-boolean--promisevoid)
|
|
296
|
-
- [Watch](#watch)
|
|
297
|
-
- [`watch(path: string): Promise<void>`](#watchpath-string-promisevoid)
|
|
298
|
-
- [Unwatch](#unwatch)
|
|
299
|
-
- [`unwatch(path: string): void`](#unwatchpath-string-void)
|
|
300
|
-
- [Dispose](#dispose)
|
|
301
|
-
- [`dispose(): void`](#dispose-void)
|
|
302
|
-
- [Configuration](#configuration)
|
|
303
|
-
- [`setOptions(options: { watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): void`](#setoptionsoptions--watchinterval-number-hashalgorithm-null--sha-1--sha-256--sha-384--sha-512-maxfilesize-number--void)
|
|
304
|
-
- [Resolve Path](#resolve-path)
|
|
305
|
-
- [`realpath(path: string): Promise<string>`](#realpathpath-string-promisestring)
|
|
306
|
-
- [Binary File Handling](#binary-file-handling)
|
|
307
|
-
- [Reading Binary Files](#reading-binary-files)
|
|
308
|
-
- [Writing Binary Files](#writing-binary-files)
|
|
309
|
-
- [Working with Different Data Types](#working-with-different-data-types)
|
|
310
|
-
- [File Upload and Download](#file-upload-and-download)
|
|
311
|
-
- [Supported Encodings](#supported-encodings)
|
|
312
|
-
- [Utility Functions](#utility-functions)
|
|
313
|
-
- [Path Utilities](#path-utilities)
|
|
314
|
-
- [Data Conversion](#data-conversion)
|
|
315
|
-
- [File System Utilities](#file-system-utilities)
|
|
316
|
-
- [Types](#types)
|
|
317
|
-
- [`FileStat`](#filestat)
|
|
318
|
-
- [`DirentData`](#direntdata)
|
|
319
|
-
- [`RemoteOPFSWorker`](#remoteopfsworker)
|
|
320
|
-
- [Error Types](#error-types)
|
|
321
|
-
- [Browser Support](#browser-support)
|
|
322
|
-
- [Development](#development)
|
|
323
|
-
- [Building](#building)
|
|
324
|
-
- [Development Server](#development-server)
|
|
325
|
-
- [Testing](#testing)
|
|
326
|
-
- [Linting](#linting)
|
|
327
|
-
- [License](#license)
|
|
328
|
-
- [Contributing](#contributing)
|
|
329
|
-
|
|
330
|
-
### Entry Points
|
|
331
|
-
|
|
332
|
-
#### Mode 1: Inline Worker
|
|
333
|
-
|
|
334
|
-
##### `createWorker(options?: OPFSOptions)`
|
|
335
|
-
|
|
336
|
-
Creates a new file system instance with an inline worker.
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
import { createWorker } from 'opfs-worker/inline';
|
|
340
|
-
|
|
341
|
-
// Basic usage
|
|
342
|
-
const fs = await createWorker();
|
|
343
|
-
|
|
344
|
-
// With options
|
|
345
|
-
const fs = await createWorker({
|
|
346
|
-
watchInterval: 500,
|
|
347
|
-
hashAlgorithm: 'SHA-256',
|
|
348
|
-
broadcastChannel: 'my-app-events'
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
// Listen for file change events via BroadcastChannel
|
|
352
|
-
const channel = new BroadcastChannel('my-app-events');
|
|
353
|
-
channel.onmessage = (event) => {
|
|
354
|
-
console.log('File changed:', event.data);
|
|
355
|
-
};
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**Parameters:**
|
|
359
|
-
|
|
360
|
-
- `options` (optional): Configuration options
|
|
361
|
-
- `watchInterval` (optional): Polling interval in milliseconds for file watching
|
|
362
|
-
- `hashAlgorithm` (optional): Hash algorithm for file hashing
|
|
363
|
-
- `broadcastChannel` (optional): Custom name for the broadcast channel (default: 'opfs-worker')
|
|
364
|
-
|
|
365
|
-
**Returns:** `Promise<RemoteOPFSWorker>` - A remote file system interface
|
|
366
|
-
|
|
367
|
-
#### Mode 2: Manual Worker Setup
|
|
368
|
-
|
|
369
|
-
##### `OPFSWorker`
|
|
370
|
-
|
|
371
|
-
The worker class that can be imported directly.
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
import OPFSWorker from 'opfs-worker/raw?worker';
|
|
375
|
-
import { wrap } from 'comlink';
|
|
376
|
-
|
|
377
|
-
const worker = wrap(new OPFSWorker());
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
**Note:** This approach requires a bundler that supports Web Workers (Vite, Webpack, Rollup, etc.) and the `comlink` package.
|
|
381
|
-
|
|
382
|
-
### Core Methods
|
|
383
|
-
|
|
384
|
-
### Mount
|
|
385
|
-
|
|
386
|
-
#### `mount(root?: string): Promise<boolean>`
|
|
387
|
-
|
|
388
|
-
Initialize the file system within a given directory. **Mount is optional** - if not called, the OPFS root directory is used automatically.
|
|
389
|
-
|
|
390
|
-
The `root` parameter defines where in OPFS the file system's root will be
|
|
391
|
-
created. All file paths passed to the API are relative to this mount point. For
|
|
392
|
-
example, after `fs.mount('/dir')`, calling `fs.readFile('/text.txt')` accesses
|
|
393
|
-
`/dir/text.txt` in OPFS.
|
|
394
|
-
|
|
395
|
-
```typescript
|
|
396
|
-
// Use OPFS root (default behavior)
|
|
397
|
-
await fs.mount();
|
|
398
|
-
|
|
399
|
-
// Use custom subdirectory
|
|
400
|
-
await fs.mount('/my-app');
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
**Parameters:**
|
|
404
|
-
|
|
405
|
-
- `root` (optional): The root path for the file system (default: '/')
|
|
406
|
-
|
|
407
|
-
**Returns:** `Promise<boolean>` - True if initialization was successful
|
|
408
|
-
|
|
409
|
-
**Throws:** `OPFSError` if initialization fails
|
|
410
|
-
|
|
411
|
-
**Note:** All file operations will automatically mount the OPFS root if no explicit mount has been performed.
|
|
412
|
-
|
|
413
|
-
**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.
|
|
414
|
-
|
|
415
|
-
### Read File
|
|
416
|
-
|
|
417
|
-
#### `readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`
|
|
418
|
-
|
|
419
|
-
Read a file from the file system. Supports both text and binary files.
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
// Read as text (default)
|
|
423
|
-
const content = await fs.readFile('/config/settings.json');
|
|
424
|
-
|
|
425
|
-
// Read as binary
|
|
426
|
-
const binaryData = await fs.readFile('/images/logo.png', 'binary');
|
|
427
|
-
|
|
428
|
-
// Read with specific encoding
|
|
429
|
-
const utf8Content = await fs.readFile('/data/utf8.txt', 'utf-8');
|
|
430
|
-
|
|
431
|
-
// Handle binary data
|
|
432
|
-
const imageBuffer = await fs.readFile('/image.png', 'binary');
|
|
433
|
-
const blob = new Blob([imageBuffer], { type: 'image/png' });
|
|
434
|
-
const url = URL.createObjectURL(blob);
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
**Parameters:**
|
|
438
|
-
|
|
439
|
-
- `path`: The path to the file to read
|
|
440
|
-
- `encoding` (optional): The encoding to use ('utf-8', 'binary', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
|
|
441
|
-
|
|
442
|
-
**Returns:** `Promise<string | Uint8Array>` - File contents as string or binary data
|
|
443
|
-
|
|
444
|
-
**Throws:** `FileNotFoundError` if the file doesn't exist
|
|
445
|
-
|
|
446
|
-
**Binary File Handling:**
|
|
447
|
-
- Use `'binary'` encoding to read files as `Uint8Array`
|
|
448
|
-
- Binary data can be converted to `Blob` for use with `URL.createObjectURL()`
|
|
449
|
-
- Supports various encodings for text files
|
|
450
|
-
|
|
451
|
-
### Write File
|
|
452
|
-
|
|
453
|
-
#### `writeFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`
|
|
454
|
-
|
|
455
|
-
Write data to a file, creating or overwriting it. Supports both text and binary data.
|
|
456
|
-
|
|
457
|
-
```typescript
|
|
458
|
-
// Write text data
|
|
459
|
-
await fs.writeFile('/config/settings.json', JSON.stringify({ theme: 'dark' }));
|
|
460
|
-
|
|
461
|
-
// Write binary data
|
|
462
|
-
const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
|
|
463
|
-
await fs.writeFile('/data/binary.dat', binaryData);
|
|
464
|
-
|
|
465
|
-
// Write with specific encoding
|
|
466
|
-
await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
|
|
467
|
-
|
|
468
|
-
// Write binary data from file input
|
|
469
|
-
const fileInput = document.getElementById('file') as HTMLInputElement;
|
|
470
|
-
const file = fileInput.files?.[0];
|
|
471
|
-
if (file) {
|
|
472
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
473
|
-
await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// Write binary data from fetch
|
|
477
|
-
const response = await fetch('/api/image.png');
|
|
478
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
479
|
-
await fs.writeFile('/downloaded-image.png', new Uint8Array(arrayBuffer));
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
**Parameters:**
|
|
483
|
-
|
|
484
|
-
- `path`: The path to the file to write
|
|
485
|
-
- `data`: The data to write (string, Uint8Array, or ArrayBuffer)
|
|
486
|
-
- `encoding` (optional): The encoding to use when writing string data ('utf-8', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
|
|
487
|
-
|
|
488
|
-
**Binary File Handling:**
|
|
489
|
-
- Pass `Uint8Array` or `ArrayBuffer` directly for binary data
|
|
490
|
-
- Use with file uploads, image processing, or any binary content
|
|
491
|
-
- Supports various text encodings for string data
|
|
492
|
-
|
|
493
|
-
### Append File
|
|
494
|
-
|
|
495
|
-
#### `appendFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`
|
|
496
|
-
|
|
497
|
-
Append data to the end of a file.
|
|
498
|
-
|
|
499
|
-
```typescript
|
|
500
|
-
// Append text to a log file
|
|
501
|
-
await fs.appendFile('/logs/app.log', `[${new Date().toISOString()}] User logged in\n`);
|
|
502
|
-
|
|
503
|
-
// Append binary data
|
|
504
|
-
const additionalData = new Uint8Array([6, 7, 8]);
|
|
505
|
-
await fs.appendFile('/data/binary.dat', additionalData);
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
### Create Directory
|
|
509
|
-
|
|
510
|
-
#### `mkdir(path: string, options?: { recursive?: boolean }): Promise<void>`
|
|
511
|
-
|
|
512
|
-
Create a directory.
|
|
513
|
-
|
|
514
|
-
```typescript
|
|
515
|
-
// Create a single directory
|
|
516
|
-
await fs.mkdir('/users/john');
|
|
517
|
-
|
|
518
|
-
// Create nested directories
|
|
519
|
-
await fs.mkdir('/users/john/documents/projects', { recursive: true });
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
**Parameters:**
|
|
523
|
-
|
|
524
|
-
- `path`: The path where the directory should be created
|
|
525
|
-
- `options.recursive` (optional): Whether to create parent directories if they don't exist
|
|
526
|
-
|
|
527
|
-
### Read Directory
|
|
528
|
-
|
|
529
|
-
#### `readDir(path: string): Promise<DirentData[]>`
|
|
530
|
-
|
|
531
|
-
Read a directory's contents.
|
|
532
|
-
|
|
533
|
-
```typescript
|
|
534
|
-
// Get simple list of names
|
|
535
|
-
const files = await fs.readDir('/users/john/documents');
|
|
536
|
-
console.log('Files:', files); // ['readme.txt', 'config.json', 'images']
|
|
537
|
-
|
|
538
|
-
// Get detailed information
|
|
539
|
-
const detailed = await fs.readDir('/users/john/documents');
|
|
540
|
-
detailed.forEach(item => {
|
|
541
|
-
console.log(`${item.name} - ${item.isFile ? 'file' : 'directory'}`);
|
|
542
|
-
});
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
**Returns:**
|
|
546
|
-
|
|
547
|
-
- `Promise<DirentData[]>` - Always returns detailed file/directory information
|
|
548
|
-
|
|
549
|
-
### Get Stats
|
|
550
|
-
|
|
551
|
-
#### `stat(path: string): Promise<FileStat>`
|
|
552
|
-
|
|
553
|
-
Get file or directory statistics. If hashing is enabled globally, file hashes will be included automatically.
|
|
305
|
+
### Root Path Configuration
|
|
554
306
|
|
|
555
|
-
|
|
556
|
-
// Basic stats
|
|
557
|
-
const stats = await fs.stat('/config/settings.json');
|
|
558
|
-
console.log(`File size: ${stats.size} bytes`);
|
|
559
|
-
console.log(`Is file: ${stats.isFile}`);
|
|
560
|
-
console.log(`Modified: ${stats.mtime}`);
|
|
561
|
-
|
|
562
|
-
// If hashing is enabled globally, hash will be included
|
|
563
|
-
if (stats.hash) {
|
|
564
|
-
console.log(`Hash: ${stats.hash}`);
|
|
565
|
-
}
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
**Parameters:**
|
|
569
|
-
|
|
570
|
-
- `path`: The path to the file or directory
|
|
571
|
-
|
|
572
|
-
**Returns:** `Promise<FileStat>` - File/directory statistics
|
|
573
|
-
|
|
574
|
-
**Note:** File hashing is controlled globally via the constructor options or `setOptions()` method. When enabled, all file stats will automatically include hash information.
|
|
575
|
-
|
|
576
|
-
### Check Existence
|
|
577
|
-
|
|
578
|
-
#### `exists(path: string): Promise<boolean>`
|
|
579
|
-
|
|
580
|
-
Check if a file or directory exists.
|
|
581
|
-
|
|
582
|
-
```typescript
|
|
583
|
-
const exists = await fs.exists('/config/settings.json');
|
|
584
|
-
console.log(`File exists: ${exists}`);
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
**Returns:** `Promise<boolean>` - True if the file or directory exists
|
|
588
|
-
|
|
589
|
-
### Remove Path
|
|
590
|
-
|
|
591
|
-
#### `remove(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`
|
|
592
|
-
|
|
593
|
-
Remove files and directories.
|
|
594
|
-
|
|
595
|
-
```typescript
|
|
596
|
-
// Remove a file
|
|
597
|
-
await fs.remove('/path/to/file.txt');
|
|
598
|
-
|
|
599
|
-
// Remove a directory and all its contents
|
|
600
|
-
await fs.remove('/path/to/directory', { recursive: true });
|
|
601
|
-
|
|
602
|
-
// Remove with force (ignore if doesn't exist)
|
|
603
|
-
await fs.remove('/maybe/exists', { force: true });
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
**Parameters:**
|
|
607
|
-
|
|
608
|
-
- `path`: The path to remove
|
|
609
|
-
- `options.recursive` (optional): Whether to remove directories recursively (default: false)
|
|
610
|
-
- `options.force` (optional): Whether to ignore errors if the path doesn't exist (default: false)
|
|
611
|
-
|
|
612
|
-
### Copy Path
|
|
613
|
-
|
|
614
|
-
#### `copy(source: string, destination: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>`
|
|
615
|
-
|
|
616
|
-
Copy files and directories.
|
|
617
|
-
|
|
618
|
-
```typescript
|
|
619
|
-
// Copy a file
|
|
620
|
-
await fs.copy('/source/file.txt', '/dest/file.txt');
|
|
621
|
-
|
|
622
|
-
// Copy a directory and all its contents
|
|
623
|
-
await fs.copy('/source/dir', '/dest/dir', { recursive: true });
|
|
624
|
-
|
|
625
|
-
// Copy without overwriting existing files
|
|
626
|
-
await fs.copy('/source', '/dest', { recursive: true, force: false });
|
|
627
|
-
```
|
|
628
|
-
|
|
629
|
-
**Parameters:**
|
|
630
|
-
|
|
631
|
-
- `source`: The source path to copy from
|
|
632
|
-
- `destination`: The destination path to copy to
|
|
633
|
-
- `options.recursive` (optional): Whether to copy directories recursively (default: false)
|
|
634
|
-
- `options.force` (optional): Whether to overwrite existing files (default: true)
|
|
635
|
-
|
|
636
|
-
### Rename Path
|
|
637
|
-
|
|
638
|
-
#### `rename(oldPath: string, newPath: string): Promise<void>`
|
|
639
|
-
|
|
640
|
-
Rename a file or directory.
|
|
641
|
-
|
|
642
|
-
```typescript
|
|
643
|
-
await fs.rename('/old/path/file.txt', '/new/path/renamed.txt');
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
**Parameters:**
|
|
647
|
-
|
|
648
|
-
- `oldPath`: The current path of the file or directory
|
|
649
|
-
- `newPath`: The new path for the file or directory
|
|
650
|
-
|
|
651
|
-
### Clear Directory
|
|
652
|
-
|
|
653
|
-
#### `clear(path?: string): Promise<void>`
|
|
654
|
-
|
|
655
|
-
Clear all contents of a directory without removing the directory itself.
|
|
656
|
-
|
|
657
|
-
```typescript
|
|
658
|
-
// Clear root directory contents
|
|
659
|
-
await fs.clear('/');
|
|
660
|
-
|
|
661
|
-
// Clear specific directory contents
|
|
662
|
-
await fs.clear('/data');
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
**Parameters:**
|
|
666
|
-
|
|
667
|
-
- `path` (optional): The path to the directory to clear (default: '/')
|
|
668
|
-
|
|
669
|
-
### Index File System
|
|
670
|
-
|
|
671
|
-
#### `index(): Promise<Map<string, FileStat>>`
|
|
672
|
-
|
|
673
|
-
Recursively list all files and directories with their stats. If hashing is enabled globally, file hashes will be included automatically.
|
|
674
|
-
|
|
675
|
-
```typescript
|
|
676
|
-
// Get complete file system index
|
|
677
|
-
const index = await fs.index();
|
|
678
|
-
|
|
679
|
-
// Iterate through all files and directories
|
|
680
|
-
for (const [path, stat] of index) {
|
|
681
|
-
console.log(`${path}: ${stat.isFile ? 'file' : 'directory'} (${stat.size} bytes)`);
|
|
682
|
-
if (stat.hash) console.log(` Hash: ${stat.hash}`);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Get specific file stats
|
|
686
|
-
const fileStats = index.get('/data/config.json');
|
|
687
|
-
if (fileStats) {
|
|
688
|
-
console.log(`File size: ${fileStats.size} bytes`);
|
|
689
|
-
if (fileStats.hash) console.log(`Hash: ${fileStats.hash}`);
|
|
690
|
-
}
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
**Returns:** `Promise<Map<string, FileStat>>` - Map of path => FileStat
|
|
694
|
-
|
|
695
|
-
**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.
|
|
696
|
-
|
|
697
|
-
### Sync File System
|
|
698
|
-
|
|
699
|
-
#### `sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`
|
|
700
|
-
|
|
701
|
-
Synchronize the file system with external data.
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
// Sync with external data
|
|
705
|
-
const entries: [string, string | Uint8Array | Blob][] = [
|
|
706
|
-
['/config.json', JSON.stringify({ theme: 'dark' })],
|
|
707
|
-
['/data/binary.dat', new Uint8Array([1, 2, 3, 4])],
|
|
708
|
-
['/upload.txt', new Blob(['file content'], { type: 'text/plain' })]
|
|
709
|
-
];
|
|
710
|
-
|
|
711
|
-
// Sync without clearing existing files
|
|
712
|
-
await fs.sync(entries);
|
|
713
|
-
|
|
714
|
-
// Clean file system and then sync
|
|
715
|
-
await fs.sync(entries, { cleanBefore: true });
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
**Parameters:**
|
|
719
|
-
|
|
720
|
-
- `entries`: Array of [path, data] tuples to sync
|
|
721
|
-
- `options.cleanBefore` (optional): Whether to clear the file system before syncing (default: false)
|
|
722
|
-
|
|
723
|
-
### Watch
|
|
724
|
-
|
|
725
|
-
#### `watch(path: string): Promise<void>`
|
|
726
|
-
|
|
727
|
-
Start watching a file or directory for changes. Detected changes are sent to the
|
|
728
|
-
callback provided to the constructor. The polling interval is configured globally when
|
|
729
|
-
creating the worker instance.
|
|
730
|
-
|
|
731
|
-
```typescript
|
|
732
|
-
await fs.watch('/docs');
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
**Parameters:**
|
|
736
|
-
|
|
737
|
-
- `path`: File or directory to watch
|
|
738
|
-
|
|
739
|
-
**Note:** Watch callbacks are available with both `createWorker()` and raw worker usage. The `createWorker()` function uses Comlink.proxy to handle function serialization across worker boundaries.
|
|
740
|
-
|
|
741
|
-
### Unwatch
|
|
742
|
-
|
|
743
|
-
#### `unwatch(path: string): void`
|
|
744
|
-
|
|
745
|
-
Stop watching a previously watched path.
|
|
307
|
+
The file system supports configuring the root path through options. The root path determines where in OPFS the file system's root will be created. All file paths passed to the API are relative to this root path.
|
|
746
308
|
|
|
747
309
|
```typescript
|
|
748
|
-
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
### Dispose
|
|
752
|
-
|
|
753
|
-
#### `dispose(): void`
|
|
754
|
-
|
|
755
|
-
Dispose of resources and clean up the file system instance. This method should be called when the file system instance is no longer needed to properly clean up resources like the broadcast channel and watch timers.
|
|
756
|
-
|
|
757
|
-
```typescript
|
|
758
|
-
// Clean up resources when done
|
|
759
|
-
fs.dispose();
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
**Note:** This method closes the broadcast channel, clears watch timers, and cleans up all watched paths. Call this when you're done with the file system instance to prevent memory leaks.
|
|
763
|
-
|
|
764
|
-
### Configuration
|
|
765
|
-
|
|
766
|
-
#### `setOptions(options: { watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): void`
|
|
767
|
-
|
|
768
|
-
Update configuration options for the file system, including watch interval, hash algorithm, and maximum file size for hashing.
|
|
769
|
-
|
|
770
|
-
```typescript
|
|
771
|
-
// Enable SHA-256 hashing for all file operations
|
|
772
|
-
fs.setOptions({ hashAlgorithm: 'SHA-256' });
|
|
773
|
-
|
|
774
|
-
// Change watch interval to 100ms for faster change detection
|
|
775
|
-
fs.setOptions({ watchInterval: 100 });
|
|
776
|
-
|
|
777
|
-
// Set custom maximum file size for hashing (100MB)
|
|
778
|
-
fs.setOptions({ maxFileSize: 100 * 1024 * 1024 });
|
|
779
|
-
|
|
780
|
-
// Disable hashing
|
|
781
|
-
fs.setOptions({ hashAlgorithm: null });
|
|
782
|
-
|
|
783
|
-
// Update multiple options at once
|
|
784
|
-
fs.setOptions({
|
|
785
|
-
watchInterval: 200,
|
|
786
|
-
hashAlgorithm: 'SHA-1',
|
|
787
|
-
maxFileSize: 50 * 1024 * 1024 // 50MB
|
|
788
|
-
});
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
**Parameters:**
|
|
792
|
-
|
|
793
|
-
- `options.watchInterval` (optional): Polling interval in milliseconds for file watching
|
|
794
|
-
- `options.hashAlgorithm` (optional): Hash algorithm to use, or `null` to disable hashing
|
|
795
|
-
- `options.maxFileSize` (optional): Maximum file size in bytes for hashing (default: 50MB)
|
|
796
|
-
|
|
797
|
-
**Note:** When a hash algorithm is set, all file operations (`stat`, `index`, watch events) will automatically include hash information for files within the size limit. Files exceeding `maxFileSize` will not have hash information. Set `hashAlgorithm` to `null` to disable hashing and improve performance.
|
|
798
|
-
|
|
799
|
-
### Resolve Path
|
|
800
|
-
|
|
801
|
-
#### `realpath(path: string): Promise<string>`
|
|
802
|
-
|
|
803
|
-
Resolve a path to an absolute path.
|
|
804
|
-
|
|
805
|
-
```typescript
|
|
806
|
-
// Resolve relative path
|
|
807
|
-
const absolute = await fs.realpath('./config/../data/file.txt');
|
|
808
|
-
console.log(absolute); // '/data/file.txt'
|
|
809
|
-
```
|
|
810
|
-
|
|
811
|
-
**Returns:** `Promise<string>` - The absolute normalized path
|
|
812
|
-
|
|
813
|
-
**Throws:** `FileNotFoundError` if the path doesn't exist
|
|
814
|
-
|
|
815
|
-
## Binary File Handling
|
|
816
|
-
|
|
817
|
-
This library provides comprehensive support for binary files, making it easy to work with images, documents, and other binary data.
|
|
818
|
-
|
|
819
|
-
### Reading Binary Files
|
|
820
|
-
|
|
821
|
-
```typescript
|
|
822
|
-
// Read as binary data
|
|
823
|
-
const imageData = await fs.readFile('/image.png', 'binary');
|
|
824
|
-
const documentData = await fs.readFile('/document.pdf', 'binary');
|
|
825
|
-
|
|
826
|
-
// Convert to Blob for use with URLs
|
|
827
|
-
const imageBuffer = await fs.readFile('/image.png', 'binary');
|
|
828
|
-
const blob = new Blob([imageBuffer], { type: 'image/png' });
|
|
829
|
-
const url = URL.createObjectURL(blob);
|
|
830
|
-
|
|
831
|
-
// Display image
|
|
832
|
-
const img = document.createElement('img');
|
|
833
|
-
img.src = url;
|
|
834
|
-
document.body.appendChild(img);
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
### Writing Binary Files
|
|
310
|
+
import { createWorker } from 'opfs-worker';
|
|
838
311
|
|
|
839
|
-
|
|
840
|
-
//
|
|
841
|
-
const
|
|
842
|
-
await
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
312
|
+
async function rootPathExample() {
|
|
313
|
+
// Use default root path '/'
|
|
314
|
+
const fsDefault = await createWorker();
|
|
315
|
+
await fsDefault.writeFile('/config.json', '{}');
|
|
316
|
+
// This creates /config.json in OPFS root
|
|
317
|
+
|
|
318
|
+
// Use custom root path
|
|
319
|
+
const fsCustom = await createWorker({ root: '/my-app' });
|
|
320
|
+
await fsCustom.writeFile('/config.json', '{}');
|
|
321
|
+
// This creates /my-app/config.json in OPFS root
|
|
322
|
+
|
|
323
|
+
// Change root path dynamically
|
|
324
|
+
await fsCustom.setOptions({ root: '/new-app' });
|
|
325
|
+
await fsCustom.writeFile('/config.json', '{}');
|
|
326
|
+
// This creates /new-app/config.json in OPFS root
|
|
850
327
|
}
|
|
851
|
-
|
|
852
|
-
// From fetch response
|
|
853
|
-
const response = await fetch('/api/download/file.pdf');
|
|
854
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
855
|
-
await fs.writeFile('/downloaded-file.pdf', new Uint8Array(arrayBuffer));
|
|
856
|
-
|
|
857
|
-
// From canvas
|
|
858
|
-
const canvas = document.createElement('canvas');
|
|
859
|
-
const ctx = canvas.getContext('2d');
|
|
860
|
-
// ... draw something ...
|
|
861
|
-
canvas.toBlob(async (blob) => {
|
|
862
|
-
if (blob) {
|
|
863
|
-
const arrayBuffer = await blob.arrayBuffer();
|
|
864
|
-
await fs.writeFile('/canvas-image.png', new Uint8Array(arrayBuffer));
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
328
|
```
|
|
868
329
|
|
|
869
|
-
|
|
330
|
+
**Root Path Behavior:**
|
|
870
331
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
// All of these work the same way
|
|
877
|
-
await fs.writeFile('/data1.bin', uint8Array);
|
|
878
|
-
await fs.writeFile('/data2.bin', arrayBuffer);
|
|
879
|
-
await fs.writeFile('/data3.bin', new Uint8Array(arrayBuffer));
|
|
880
|
-
|
|
881
|
-
// Read back as binary
|
|
882
|
-
const data = await fs.readFile('/data1.bin', 'binary');
|
|
883
|
-
console.log(data); // Uint8Array
|
|
884
|
-
```
|
|
332
|
+
- **Default**: Uses `/` (OPFS root directory)
|
|
333
|
+
- **Custom**: Creates a subdirectory within OPFS for isolation
|
|
334
|
+
- **Dynamic**: Can be changed at runtime via `setOptions()`
|
|
335
|
+
- **Auto-mount**: Automatically mounts to the specified root when needed
|
|
336
|
+
- **Path Resolution**: All API paths are relative to the configured root
|
|
885
337
|
|
|
886
|
-
|
|
338
|
+
## Demo
|
|
887
339
|
|
|
888
|
-
|
|
889
|
-
// Handle file uploads
|
|
890
|
-
const handleFileUpload = async (file: File) => {
|
|
891
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
892
|
-
await fs.writeFile(`/uploads/${file.name}`, new Uint8Array(arrayBuffer));
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
// Create downloadable files
|
|
896
|
-
const createDownloadLink = async (filePath: string, fileName: string) => {
|
|
897
|
-
const data = await fs.readFile(filePath, 'binary');
|
|
898
|
-
const blob = new Blob([data]);
|
|
899
|
-
const url = URL.createObjectURL(blob);
|
|
900
|
-
|
|
901
|
-
const a = document.createElement('a');
|
|
902
|
-
a.href = url;
|
|
903
|
-
a.download = fileName;
|
|
904
|
-
a.click();
|
|
905
|
-
|
|
906
|
-
URL.revokeObjectURL(url);
|
|
907
|
-
};
|
|
908
|
-
```
|
|
340
|
+
Check out the live demo powered by Vite and hosted on GitHub Pages.
|
|
909
341
|
|
|
910
|
-
|
|
342
|
+
[Live Demo](https://kachurun.github.io/opfs-worker/)
|
|
911
343
|
|
|
912
|
-
|
|
344
|
+
## API Reference
|
|
913
345
|
|
|
914
|
-
|
|
915
|
-
- `'utf-16le'` - UTF-16 little-endian
|
|
916
|
-
- `'ascii'` - ASCII encoding
|
|
917
|
-
- `'latin1'` - Latin-1 encoding
|
|
918
|
-
- `'base64'` - Base64 encoding
|
|
919
|
-
- `'hex'` - Hexadecimal encoding
|
|
920
|
-
- `'binary'` - Raw binary data (returns Uint8Array)
|
|
346
|
+
The complete API reference is available in the [docs/api-reference.md](docs/api-reference.md) file.
|
|
921
347
|
|
|
922
|
-
|
|
348
|
+
### Quick API Overview
|
|
923
349
|
|
|
924
|
-
|
|
350
|
+
**Entry Points:**
|
|
925
351
|
|
|
926
|
-
|
|
352
|
+
- `createWorker(options?)` - Create file system instance with inline worker
|
|
353
|
+
- `OPFSWorker` - Direct worker class for manual setup
|
|
927
354
|
|
|
928
|
-
|
|
929
|
-
import { basename, dirname, normalizePath, resolvePath, extname } from 'opfs-worker';
|
|
355
|
+
**Core File Operations:**
|
|
930
356
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
|
934
365
|
|
|
935
|
-
|
|
936
|
-
dirname('/path/to/file.txt'); // '/path/to'
|
|
937
|
-
dirname('file.txt'); // '/'
|
|
366
|
+
**Advanced Features:**
|
|
938
367
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
|
942
372
|
|
|
943
|
-
|
|
944
|
-
resolvePath('./config/../data/file.txt'); // '/data/file.txt'
|
|
945
|
-
resolvePath('/path/to/../file.txt'); // '/path/file.txt'
|
|
373
|
+
**Binary File Support:**
|
|
946
374
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
extname('/path/to/file.name.ext'); // '.ext'
|
|
951
|
-
```
|
|
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.)
|
|
952
378
|
|
|
953
|
-
|
|
379
|
+
**Utility Functions:**
|
|
954
380
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
// Convert Blob to Uint8Array
|
|
959
|
-
const fileInput = document.getElementById('file') as HTMLInputElement;
|
|
960
|
-
const file = fileInput.files?.[0];
|
|
961
|
-
if (file) {
|
|
962
|
-
const data = await convertBlobToUint8Array(file);
|
|
963
|
-
await fs.writeFile('/uploaded-file', data);
|
|
964
|
-
}
|
|
965
|
-
```
|
|
381
|
+
- Path manipulation (`basename`, `dirname`, `normalizePath`, etc.)
|
|
382
|
+
- Data conversion helpers
|
|
383
|
+
- OPFS support detection
|
|
966
384
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
```typescript
|
|
970
|
-
import { checkOPFSSupport, splitPath, joinPath } from 'opfs-worker';
|
|
971
|
-
|
|
972
|
-
// Check if browser supports OPFS
|
|
973
|
-
checkOPFSSupport(); // Throws error if not supported
|
|
974
|
-
|
|
975
|
-
// Path manipulation
|
|
976
|
-
splitPath('/path/to/file'); // ['path', 'to', 'file']
|
|
977
|
-
joinPath(['path', 'to', 'file']); // '/path/to/file'
|
|
978
|
-
```
|
|
385
|
+
For detailed API documentation with examples, see [docs/api-reference.md](docs/api-reference.md).
|
|
979
386
|
|
|
980
387
|
## Types
|
|
981
388
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
File or directory statistics.
|
|
985
|
-
|
|
986
|
-
```typescript
|
|
987
|
-
interface FileStat {
|
|
988
|
-
kind: 'file' | 'directory';
|
|
989
|
-
size: number;
|
|
990
|
-
mtime: string; // ISO string
|
|
991
|
-
ctime: string; // ISO string
|
|
992
|
-
isFile: boolean;
|
|
993
|
-
isDirectory: boolean;
|
|
994
|
-
hash?: string; // Hash of file content (only for files)
|
|
995
|
-
}
|
|
996
|
-
```
|
|
997
|
-
|
|
998
|
-
### `DirentData`
|
|
999
|
-
|
|
1000
|
-
Directory entry information.
|
|
1001
|
-
|
|
1002
|
-
```typescript
|
|
1003
|
-
interface DirentData {
|
|
1004
|
-
name: string;
|
|
1005
|
-
kind: 'file' | 'directory';
|
|
1006
|
-
isFile: boolean;
|
|
1007
|
-
isDirectory: boolean;
|
|
1008
|
-
}
|
|
1009
|
-
```
|
|
1010
|
-
|
|
1011
|
-
### `RemoteOPFSWorker`
|
|
1012
|
-
|
|
1013
|
-
Remote file system interface type.
|
|
1014
|
-
|
|
1015
|
-
```typescript
|
|
1016
|
-
type RemoteOPFSWorker = Remote<OPFSWorker>;
|
|
1017
|
-
```
|
|
1018
|
-
|
|
1019
|
-
## Error Types
|
|
1020
|
-
|
|
1021
|
-
The library provides comprehensive error handling with specific error types:
|
|
1022
|
-
|
|
1023
|
-
- `OPFSError` - Base error class for all OPFS-related errors
|
|
1024
|
-
- `OPFSNotSupportedError` - Thrown when OPFS is not supported in the browser
|
|
1025
|
-
- `OPFSNotMountedError` - Thrown when OPFS is not mounted
|
|
1026
|
-
- `PathError` - Thrown for invalid paths or path traversal attempts
|
|
1027
|
-
- `FileNotFoundError` - Thrown when a requested file doesn't exist
|
|
1028
|
-
- `DirectoryNotFoundError` - Thrown when a requested directory doesn't exist
|
|
1029
|
-
- `PermissionError` - Thrown when permission is denied for an operation
|
|
1030
|
-
- `StorageError` - Thrown when an operation fails due to insufficient storage
|
|
1031
|
-
- `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.
|
|
1032
390
|
|
|
1033
391
|
## Browser Support
|
|
1034
392
|
|
|
@@ -1047,8 +405,6 @@ This library works in **all modern browsers**, including Safari, Firefox, Chrome
|
|
|
1047
405
|
- ✅ Safari 15.2+
|
|
1048
406
|
- ✅ Opera 72+
|
|
1049
407
|
|
|
1050
|
-
**Note:** The library gracefully handles browsers that don't support OPFS by providing appropriate error messages and fallback behavior.
|
|
1051
|
-
|
|
1052
408
|
## Development
|
|
1053
409
|
|
|
1054
410
|
### Building
|
|
@@ -1083,4 +439,21 @@ MIT
|
|
|
1083
439
|
|
|
1084
440
|
## Contributing
|
|
1085
441
|
|
|
1086
|
-
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!_
|