opfs-worker 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
- - 🌐 **Cross-browser compatible**: Works in all modern browsers including Safari, Firefox, Chrome, and Edge
24
- - 🚀 **Web Worker-based**: Runs in a separate thread for better performance
25
- - 📁 **Node.js-like API**: Familiar file system operations (`readFile`, `writeFile`, `mkdir`, etc.)
26
- - 🔒 **Type-safe**: Full TypeScript support with comprehensive type definitions
27
- - 🎯 **Comlink-powered**: Seamless communication between main thread and worker
28
- - 🔐 **Hash support**: Built-in file hash calculation (SHA-1, SHA-256, SHA-384, SHA-512)
29
- - 📊 **File indexing**: Complete file system indexing with metadata
30
- - 🔄 **Sync operations**: Bulk file synchronization from external data
31
- - 👀 **File watching**: Polling-based change detection for files and directories
32
- - 📡 **Event broadcasting**: Broadcast file system changes to other contexts (tabs, workers, etc.)
33
- - 🛡️ **Error handling**: Comprehensive error types and handling
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 example() {
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
- // With custom root and broadcast channel
78
- async function exampleWithBroadcastChannel() {
79
- const worker = wrap(new OPFSWorker({
80
- root: '/my-app',
81
- watchInterval: 500,
82
- hashAlgorithm: 'SHA-1',
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
- // Listen for file change events via BroadcastChannel
87
- const channel = new BroadcastChannel('my-app-events');
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
- await worker.watch('/docs');
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 example() {
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
- // With custom root and broadcast channel
120
- async function exampleWithBroadcastChannel() {
121
- const worker = wrap(new OPFSWorker({
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
- watchInterval: 500,
124
- hashAlgorithm: 'SHA-1',
125
- broadcastChannel: 'my-app-events'
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
- // Listen for file change events via BroadcastChannel
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 now 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.
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
- // Now files up to 100MB will be hashed
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
- - [OPFS Worker](#opfs-worker)
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
- **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 `createWorker()` function automatically handles the BroadcastChannel setup.
348
+ ### Quick API Overview
774
349
 
775
- ### Unwatch
350
+ **Entry Points:**
776
351
 
777
- #### `unwatch(path: string): void`
352
+ - `createWorker(options?)` - Create file system instance with inline worker
353
+ - `OPFSWorker` - Direct worker class for manual setup
778
354
 
779
- Stop watching a previously watched path.
780
-
781
- ```typescript
782
- fs.unwatch('/docs');
783
- ```
355
+ **Core File Operations:**
784
356
 
785
- ### Dispose
786
-
787
- #### `dispose(): void`
788
-
789
- 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.
790
-
791
- ```typescript
792
- // Clean up resources when done
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
- **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.
366
+ **Advanced Features:**
797
367
 
798
- ### Configuration
799
-
800
- #### `setOptions(options: { root?: string; watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): Promise<void>`
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
- **Parameters:**
373
+ **Binary File Support:**
830
374
 
831
- - `options.root` (optional): Root path for the file system
832
- - `options.watchInterval` (optional): Polling interval in milliseconds for file watching
833
- - `options.hashAlgorithm` (optional): Hash algorithm to use, or `null` to disable hashing
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
- **Note:** When the `root` option is changed, the file system will automatically remount to the new location. All other options are updated immediately without affecting the current mount.
379
+ **Utility Functions:**
837
380
 
838
- ### Resolve Path
381
+ - Path manipulation (`basename`, `dirname`, `normalizePath`, etc.)
382
+ - Data conversion helpers
383
+ - OPFS support detection
839
384
 
840
- #### `realpath(path: string): Promise<string>`
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
- ### `FileStat`
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! Please feel free to submit a Pull Request.
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!_