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 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,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 example() {
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
- // Mount is optional - OPFS root is used by default
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
- // With watch callbacks
78
- async function exampleWithWatch() {
79
- const fs = await createWorker(
80
- (event) => {
81
- console.log('File changed:', event);
82
- },
83
- { watchInterval: 500 }
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
- await fs.watch('/docs');
174
+ console.log(
175
+ `File size: ${stats.size} bytes`,
176
+ `Hash: ${stats.hash}`
177
+ );
87
178
  }
88
179
  ```
89
180
 
90
- > **Note:** The onChange callback will be called for all internal file changes (made by the worker itself), not just for watched paths. You only need to call `fs.watch` to start watching for changes from external sources.
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 example() {
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
- // Mount is optional - OPFS root is used by default
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
- // With watch callbacks
115
- async function exampleWithWatch() {
116
- const worker = wrap(new OPFSWorker(
117
- (event) => console.log('File changed:', event),
118
- {
119
- watchInterval: 500,
120
- hashAlgorithm: 'SHA-1'
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
- ### Advanced Usage
134
-
135
- ```typescript
136
- import { createWorker } from 'opfs-worker';
137
-
138
- async function advancedExample() {
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 fs.mkdir('/data/logs', { recursive: true });
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
- // Get file statistics with hash
159
- const stats = await fs.stat('/data/config.json');
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 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.
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
- // Now files up to 100MB will be hashed
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
- ## Demo
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
- ```typescript
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
- fs.unwatch('/docs');
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
- ```typescript
840
- // Write binary data directly
841
- const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
842
- await fs.writeFile('/data.bin', binaryData);
843
-
844
- // From file input
845
- const fileInput = document.getElementById('file') as HTMLInputElement;
846
- const file = fileInput.files?.[0];
847
- if (file) {
848
- const arrayBuffer = await file.arrayBuffer();
849
- await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
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
- ### Working with Different Data Types
330
+ **Root Path Behavior:**
870
331
 
871
- ```typescript
872
- // Convert between different binary formats
873
- const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
874
- const arrayBuffer = uint8Array.buffer;
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
- ### File Upload and Download
338
+ ## Demo
887
339
 
888
- ```typescript
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
- ### Supported Encodings
342
+ [Live Demo](https://kachurun.github.io/opfs-worker/)
911
343
 
912
- The library supports various encodings for text files:
344
+ ## API Reference
913
345
 
914
- - `'utf-8'` (default) - UTF-8 encoding
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
- ## Utility Functions
348
+ ### Quick API Overview
923
349
 
924
- The library exports several utility functions that can be used independently:
350
+ **Entry Points:**
925
351
 
926
- ### Path Utilities
352
+ - `createWorker(options?)` - Create file system instance with inline worker
353
+ - `OPFSWorker` - Direct worker class for manual setup
927
354
 
928
- ```typescript
929
- import { basename, dirname, normalizePath, resolvePath, extname } from 'opfs-worker';
355
+ **Core File Operations:**
930
356
 
931
- // Extract filename from path
932
- basename('/path/to/file.txt'); // 'file.txt'
933
- basename('/path/to/directory/'); // ''
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
- // Extract directory path
936
- dirname('/path/to/file.txt'); // '/path/to'
937
- dirname('file.txt'); // '/'
366
+ **Advanced Features:**
938
367
 
939
- // Normalize path to start with '/'
940
- normalizePath('path/to/file'); // '/path/to/file'
941
- normalizePath('/path/to/file'); // '/path/to/file'
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
- // Resolve relative paths
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
- // Get file extension
948
- extname('/path/to/file.txt'); // '.txt'
949
- extname('/path/to/file'); // ''
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
- ### Data Conversion
379
+ **Utility Functions:**
954
380
 
955
- ```typescript
956
- import { convertBlobToUint8Array } from 'opfs-worker';
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
- ### File System Utilities
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
- ### `FileStat`
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! 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!_