opfs-worker 0.2.5 → 0.3.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
@@ -29,6 +29,7 @@ A robust TypeScript library for working with Origin Private File System (OPFS) t
29
29
  - 📊 **File indexing**: Complete file system indexing with metadata
30
30
  - 🔄 **Sync operations**: Bulk file synchronization from external data
31
31
  - 👀 **File watching**: Polling-based change detection for files and directories
32
+ - 📡 **Event broadcasting**: Broadcast file system changes to other contexts (tabs, workers, etc.)
32
33
  - 🛡️ **Error handling**: Comprehensive error types and handling
33
34
 
34
35
  ## Installation
@@ -114,7 +115,10 @@ async function example() {
114
115
  async function exampleWithWatch() {
115
116
  const worker = wrap(new OPFSWorker(
116
117
  (event) => console.log('File changed:', event),
117
- { watchInterval: 500 }
118
+ {
119
+ watchInterval: 500,
120
+ hashAlgorithm: 'SHA-1'
121
+ }
118
122
  ));
119
123
 
120
124
  await worker.mount('/my-app');
@@ -122,7 +126,7 @@ async function exampleWithWatch() {
122
126
  }
123
127
  ```
124
128
 
125
- **Note:** Manual worker setup requires a bundler that supports Web Workers (like Vite, Webpack, or Rollup) and the `comlink` package for communication between the main thread and worker.
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.
126
130
 
127
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.
128
132
 
@@ -152,11 +156,12 @@ async function advancedExample() {
152
156
  await fs.appendFile('/data/logs/app.log', `${new Date().toISOString()}: User logged in\n`);
153
157
 
154
158
  // Get file statistics with hash
155
- const stats = await fs.stat('/data/config.json', {
156
- includeHash: true,
157
- hashAlgorithm: 'SHA-1'
158
- });
159
- console.log(`File size: ${stats.size} bytes, Hash: ${stats.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
+ }
160
165
 
161
166
  // List directory contents
162
167
  const files = await fs.readdir('/data', { withFileTypes: true });
@@ -168,6 +173,73 @@ async function advancedExample() {
168
173
  }
169
174
  ```
170
175
 
176
+ ### Hash Algorithm Configuration
177
+
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.
179
+
180
+ ```typescript
181
+ import { createWorker } from 'opfs-worker';
182
+
183
+ async function hashExample() {
184
+ const fs = await createWorker();
185
+
186
+ // Enable SHA-256 hashing globally
187
+ fs.setOptions({ hashAlgorithm: 'SHA-256' });
188
+
189
+ // Write a file
190
+ await fs.writeFile('/data.txt', 'Hello World');
191
+
192
+ // Get stats - hash will be included automatically
193
+ const stats = await fs.stat('/data.txt');
194
+ console.log(`Hash: ${stats.hash}`); // SHA-256 hash
195
+
196
+ // Get file system index - all files will include hashes
197
+ const index = await fs.index();
198
+ for (const [path, stat] of index) {
199
+ if (stat.isFile && stat.hash) {
200
+ console.log(`${path}: ${stat.hash}`);
201
+ }
202
+ }
203
+
204
+ // Watch events will also include hash information
205
+ const channel = new BroadcastChannel('opfs-worker');
206
+ channel.onmessage = (event) => {
207
+ if (event.data.hash) {
208
+ console.log(`File ${event.data.path} changed, hash: ${event.data.hash}`);
209
+ }
210
+ };
211
+ }
212
+
213
+ // Configure maximum file size for hashing
214
+ async function maxFileSizeExample() {
215
+ const fs = await createWorker();
216
+
217
+ // Set custom maximum file size (100MB instead of default 50MB)
218
+ fs.setOptions({
219
+ hashAlgorithm: 'SHA-256',
220
+ maxFileSize: 100 * 1024 * 1024 // 100MB
221
+ });
222
+
223
+ // Now files up to 100MB will be hashed
224
+ // Files larger than this will not have hash information
225
+ const stats = await fs.stat('/large-file.dat');
226
+ if (stats.hash) {
227
+ console.log(`Hash: ${stats.hash}`);
228
+ } else {
229
+ console.log('File too large for hashing');
230
+ }
231
+ }
232
+ ```
233
+
234
+ **Supported Hash Algorithms:**
235
+ - `'SHA-1'` - Fastest, good for general use (default)
236
+ - `'SHA-256'` - More secure, widely supported
237
+ - `'SHA-384'` - Higher security
238
+ - `'SHA-512'` - Highest security
239
+ - `null` - Disable hashing for maximum performance
240
+
241
+ **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
+
171
243
  ## Demo
172
244
 
173
245
  Check out the live demo powered by Vite and hosted on GitHub Pages.
@@ -176,43 +248,120 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
176
248
 
177
249
  ## API Reference
178
250
 
179
- - [Entry Points](#entry-points)
180
- - [`createWorker()`](#createworker)
181
- - [Core Methods](#core-methods)
182
- - [Mount](#mountroot-string-watch-event-watch-event--void-options--watchinterval-number--promiseboolean)
183
- - [Read File](#readfilepath-string-encoding-bufferencoding--binary-promisestring--uint8array)
184
- - [Write File](#writefilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
185
- - [Append File](#appendfilepath-string-data-string--uint8array--arraybuffer-encoding-bufferencoding-promisevoid)
186
- - [Make Directory](#mkdirpath-string-options--recursive-boolean--promisevoid)
187
- - [Read Directory](#readdirpath-string-options--withfiletypes-boolean--promisestring--direntdata)
188
- - [File Stat](#statpath-string-options--includehash-boolean-hashalgorithm-string--promisefilestat)
189
- - [Exists](#existspath-string-promiseboolean)
190
- - [Remove](#removepath-string-options--recursive-boolean-force-boolean--promisevoid)
191
- - [Copy](#copysource-string-destination-string-options--recursive-boolean-force-boolean--promisevoid)
192
- - [Rename](#renameoldpath-string-newpath-string-promisevoid)
193
- - [Clear](#clearpath-string-promisevoid)
194
- - [Index](#indexoptions--includehash-boolean-hashalgorithm-string--promisemapstring-filestat)
195
- - [Sync](#syncentries-string-string--uint8array--blob-options--cleanbefore-boolean--promisevoid)
196
- - [Watch](#watchpath-string-promisevoid)
197
- - [Unwatch](#unwatchpath-string-void)
198
- - [Real Path](#realpathpath-string-promisestring)
199
- - [Binary File Handling](#binary-file-handling)
200
- - [Utility Functions](#utility-functions)
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, options?: { withFileTypes?: boolean }): Promise<string[] | DirentData[]>`](#readdirpath-string-options--withfiletypes-boolean--promisestring--direntdata)
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)
201
329
 
202
330
  ### Entry Points
203
331
 
204
332
  #### Mode 1: Inline Worker
205
333
 
206
- ##### `createWorker()`
334
+ ##### `createWorker(options?: OPFSOptions)`
207
335
 
208
336
  Creates a new file system instance with an inline worker.
209
337
 
210
338
  ```typescript
211
339
  import { createWorker } from 'opfs-worker/inline';
212
340
 
341
+ // Basic usage
213
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
+ };
214
356
  ```
215
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
+
216
365
  **Returns:** `Promise<RemoteOPFSWorker>` - A remote file system interface
217
366
 
218
367
  #### Mode 2: Manual Worker Setup
@@ -261,7 +410,7 @@ await fs.mount('/my-app');
261
410
 
262
411
  **Note:** All file operations will automatically mount the OPFS root if no explicit mount has been performed.
263
412
 
264
- **Watch Callbacks:** File watching with callbacks is only available when using the raw worker directly. The `createWorker()` function does not support watch callbacks due to Comlink limitations with function serialization.
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.
265
414
 
266
415
  ### Read File
267
416
 
@@ -400,9 +549,9 @@ detailed.forEach(item => {
400
549
 
401
550
  ### Get Stats
402
551
 
403
- #### `stat(path: string, options?: { includeHash?: boolean; hashAlgorithm?: string }): Promise<FileStat>`
552
+ #### `stat(path: string): Promise<FileStat>`
404
553
 
405
- Get file or directory statistics.
554
+ Get file or directory statistics. If hashing is enabled globally, file hashes will be included automatically.
406
555
 
407
556
  ```typescript
408
557
  // Basic stats
@@ -411,22 +560,20 @@ console.log(`File size: ${stats.size} bytes`);
411
560
  console.log(`Is file: ${stats.isFile}`);
412
561
  console.log(`Modified: ${stats.mtime}`);
413
562
 
414
- // Stats with hash
415
- const statsWithHash = await fs.stat('/config/settings.json', {
416
- includeHash: true,
417
- hashAlgorithm: 'SHA-1'
418
- });
419
- console.log(`Hash: ${statsWithHash.hash}`);
563
+ // If hashing is enabled globally, hash will be included
564
+ if (stats.hash) {
565
+ console.log(`Hash: ${stats.hash}`);
566
+ }
420
567
  ```
421
568
 
422
569
  **Parameters:**
423
570
 
424
571
  - `path`: The path to the file or directory
425
- - `options.includeHash` (optional): Whether to calculate file hash (default: false)
426
- - `options.hashAlgorithm` (optional): Hash algorithm to use ('SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', default: 'SHA-1')
427
572
 
428
573
  **Returns:** `Promise<FileStat>` - File/directory statistics
429
574
 
575
+ **Note:** File hashing is controlled globally via the constructor options or `setOptions()` method. When enabled, all file stats will automatically include hash information.
576
+
430
577
  ### Check Existence
431
578
 
432
579
  #### `exists(path: string): Promise<boolean>`
@@ -522,20 +669,14 @@ await fs.clear('/data');
522
669
 
523
670
  ### Index File System
524
671
 
525
- #### `index(options?: { includeHash?: boolean; hashAlgorithm?: string }): Promise<Map<string, FileStat>>`
672
+ #### `index(): Promise<Map<string, FileStat>>`
526
673
 
527
- Recursively list all files and directories with their stats.
674
+ Recursively list all files and directories with their stats. If hashing is enabled globally, file hashes will be included automatically.
528
675
 
529
676
  ```typescript
530
- // Basic index without hash
677
+ // Get complete file system index
531
678
  const index = await fs.index();
532
679
 
533
- // Index with file hash
534
- const indexWithHash = await fs.index({
535
- includeHash: true,
536
- hashAlgorithm: 'SHA-1'
537
- });
538
-
539
680
  // Iterate through all files and directories
540
681
  for (const [path, stat] of index) {
541
682
  console.log(`${path}: ${stat.isFile ? 'file' : 'directory'} (${stat.size} bytes)`);
@@ -550,13 +691,10 @@ if (fileStats) {
550
691
  }
551
692
  ```
552
693
 
553
- **Parameters:**
554
-
555
- - `options.includeHash` (optional): Whether to calculate file hash (default: false)
556
- - `options.hashAlgorithm` (optional): Hash algorithm to use (default: 'SHA-1')
557
-
558
694
  **Returns:** `Promise<Map<string, FileStat>>` - Map of path => FileStat
559
695
 
696
+ **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.
697
+
560
698
  ### Sync File System
561
699
 
562
700
  #### `sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`
@@ -611,6 +749,54 @@ Stop watching a previously watched path.
611
749
  fs.unwatch('/docs');
612
750
  ```
613
751
 
752
+ ### Dispose
753
+
754
+ #### `dispose(): void`
755
+
756
+ 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.
757
+
758
+ ```typescript
759
+ // Clean up resources when done
760
+ fs.dispose();
761
+ ```
762
+
763
+ **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.
764
+
765
+ ### Configuration
766
+
767
+ #### `setOptions(options: { watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): void`
768
+
769
+ Update configuration options for the file system, including watch interval, hash algorithm, and maximum file size for hashing.
770
+
771
+ ```typescript
772
+ // Enable SHA-256 hashing for all file operations
773
+ fs.setOptions({ hashAlgorithm: 'SHA-256' });
774
+
775
+ // Change watch interval to 100ms for faster change detection
776
+ fs.setOptions({ watchInterval: 100 });
777
+
778
+ // Set custom maximum file size for hashing (100MB)
779
+ fs.setOptions({ maxFileSize: 100 * 1024 * 1024 });
780
+
781
+ // Disable hashing
782
+ fs.setOptions({ hashAlgorithm: null });
783
+
784
+ // Update multiple options at once
785
+ fs.setOptions({
786
+ watchInterval: 200,
787
+ hashAlgorithm: 'SHA-1',
788
+ maxFileSize: 50 * 1024 * 1024 // 50MB
789
+ });
790
+ ```
791
+
792
+ **Parameters:**
793
+
794
+ - `options.watchInterval` (optional): Polling interval in milliseconds for file watching
795
+ - `options.hashAlgorithm` (optional): Hash algorithm to use, or `null` to disable hashing
796
+ - `options.maxFileSize` (optional): Maximum file size in bytes for hashing (default: 50MB)
797
+
798
+ **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.
799
+
614
800
  ### Resolve Path
615
801
 
616
802
  #### `realpath(path: string): Promise<string>`
@@ -898,4 +1084,4 @@ MIT
898
1084
 
899
1085
  ## Contributing
900
1086
 
901
- Contributions are welcome! Please feel free to submit a Pull Request.
1087
+ Contributions are welcome! Please feel free to submit a Pull Request.