opfs-worker 0.3.3 → 0.4.2

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
@@ -55,13 +55,13 @@ The easiest way to get started - just import and use:
55
55
  import { createWorker } from 'opfs-worker';
56
56
 
57
57
  async function example() {
58
- // Create a file system instance
58
+ // Create a file system instance with default root path '/'
59
59
  const fs = await createWorker();
60
60
 
61
- // Mount is optional - OPFS root is used by default
62
- // await fs.mount('/my-app'); // Uses custom subdirectory
61
+ // Or specify a custom root path
62
+ const fsCustom = await createWorker({ root: '/my-app' });
63
63
 
64
- // Write a file (auto-mounts if not mounted)
64
+ // Write a file
65
65
  await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
66
66
 
67
67
  // Read the file back
@@ -74,20 +74,26 @@ async function example() {
74
74
  const binaryData = await fs.readFile('/image.png', 'binary');
75
75
  }
76
76
 
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
- );
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
+ }));
85
+
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);
90
+ };
85
91
 
86
- await fs.watch('/docs');
92
+ await worker.watch('/docs');
87
93
  }
88
94
  ```
89
95
 
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.
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.
91
97
 
92
98
  ### Manual Worker Setup
93
99
 
@@ -98,37 +104,40 @@ import OPFSWorker from 'opfs-worker/raw?worker';
98
104
  import { wrap } from 'comlink';
99
105
 
100
106
  async function example() {
101
- // Create and wrap the worker
107
+ // Create and wrap the worker with default root path '/'
102
108
  const worker = wrap(new OPFSWorker());
103
109
 
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
110
+ // Or specify a custom root path
111
+ const workerCustom = wrap(new OPFSWorker({ root: '/my-app' }));
107
112
 
108
- // Use the file system (auto-mounts if not mounted)
113
+ // Use the file system
109
114
  await worker.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
110
115
  const config = await worker.readFile('/config.json');
111
116
  console.log(JSON.parse(config));
112
117
  }
113
118
 
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
- ));
119
+ // With custom root and broadcast channel
120
+ async function exampleWithBroadcastChannel() {
121
+ const worker = wrap(new OPFSWorker({
122
+ root: '/my-app',
123
+ watchInterval: 500,
124
+ hashAlgorithm: 'SHA-1',
125
+ broadcastChannel: 'my-app-events'
126
+ }));
127
+
128
+ // Listen for file change events via BroadcastChannel
129
+ const channel = new BroadcastChannel('my-app-events');
130
+ channel.onmessage = (event) => {
131
+ console.log('File changed:', event.data);
132
+ };
123
133
 
124
- await worker.mount('/my-app');
125
134
  await worker.watch('/docs');
126
135
  }
127
136
  ```
128
137
 
129
138
  **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
139
 
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.
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.
132
141
 
133
142
  ### Advanced Usage
134
143
 
@@ -136,9 +145,8 @@ async function exampleWithWatch() {
136
145
  import { createWorker } from 'opfs-worker';
137
146
 
138
147
  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
148
+ // Create with custom root path
149
+ const fs = await createWorker({ root: '/my-app' });
142
150
 
143
151
  // Create directories
144
152
  await fs.mkdir('/data/logs', { recursive: true });
@@ -240,6 +248,43 @@ async function maxFileSizeExample() {
240
248
 
241
249
  **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
250
 
251
+ ### Root Path Configuration
252
+
253
+ 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.
254
+
255
+ ```typescript
256
+ import { createWorker } from 'opfs-worker';
257
+
258
+ async function rootPathExample() {
259
+ // Use default root path '/'
260
+ const fsDefault = await createWorker();
261
+ await fsDefault.writeFile('/config.json', '{}');
262
+ // This creates /config.json in OPFS root
263
+
264
+ // Use custom root path
265
+ const fsCustom = await createWorker({ root: '/my-app' });
266
+ await fsCustom.writeFile('/config.json', '{}');
267
+ // This creates /my-app/config.json in OPFS root
268
+
269
+ // Change root path dynamically
270
+ await fsCustom.setOptions({ root: '/new-app' });
271
+ await fsCustom.writeFile('/config.json', '{}');
272
+ // This creates /new-app/config.json in OPFS root
273
+ }
274
+ ```
275
+
276
+ **Root Path Behavior:**
277
+ - **Default**: Uses `/` (OPFS root directory)
278
+ - **Custom**: Creates a subdirectory within OPFS for isolation
279
+ - **Dynamic**: Can be changed at runtime via `setOptions()`
280
+ - **Auto-mount**: Automatically mounts to the specified root when needed
281
+ - **Path Resolution**: All API paths are relative to the configured root
282
+
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
+
243
288
  ## Demo
244
289
 
245
290
  Check out the live demo powered by Vite and hosted on GitHub Pages.
@@ -257,6 +302,7 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
257
302
  - [Manual Worker Setup](#manual-worker-setup)
258
303
  - [Advanced Usage](#advanced-usage)
259
304
  - [Hash Algorithm Configuration](#hash-algorithm-configuration)
305
+ - [Root Path Configuration](#root-path-configuration)
260
306
  - [Demo](#demo)
261
307
  - [API Reference](#api-reference)
262
308
  - [Entry Points](#entry-points)
@@ -265,8 +311,6 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
265
311
  - [Mode 2: Manual Worker Setup](#mode-2-manual-worker-setup)
266
312
  - [`OPFSWorker`](#opfsworker)
267
313
  - [Core Methods](#core-methods)
268
- - [Mount](#mount)
269
- - [`mount(root?: string): Promise<boolean>`](#mountroot-string-promiseboolean)
270
314
  - [Read File](#read-file)
271
315
  - [`readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`](#readfilepath-string-encoding-bufferencoding--binary-promisestring--uint8array)
272
316
  - [Write File](#write-file)
@@ -276,7 +320,7 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
276
320
  - [Create Directory](#create-directory)
277
321
  - [`mkdir(path: string, options?: { recursive?: boolean }): Promise<void>`](#mkdirpath-string-options--recursive-boolean--promisevoid)
278
322
  - [Read Directory](#read-directory)
279
- - [`readDir(path: string): Promise<DirentData[]>`](#readdirpath-string--promisedirentdata)
323
+ - [`readDir(path: string): Promise<DirentData[]>`](#readdirpath-string-promisedirentdata)
280
324
  - [Get Stats](#get-stats)
281
325
  - [`stat(path: string): Promise<FileStat>`](#statpath-string-promisefilestat)
282
326
  - [Check Existence](#check-existence)
@@ -294,13 +338,13 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
294
338
  - [Sync File System](#sync-file-system)
295
339
  - [`sync(entries: [string, string | Uint8Array | Blob][], options?: { cleanBefore?: boolean }): Promise<void>`](#syncentries-string-string--uint8array--blob-options--cleanbefore-boolean--promisevoid)
296
340
  - [Watch](#watch)
297
- - [`watch(path: string): Promise<void>`](#watchpath-string-promisevoid)
341
+ - [`watch(path: string, options?: WatchOptions): Promise<void>`](#watchpath-string-options-watchoptions-promisevoid)
298
342
  - [Unwatch](#unwatch)
299
343
  - [`unwatch(path: string): void`](#unwatchpath-string-void)
300
344
  - [Dispose](#dispose)
301
345
  - [`dispose(): void`](#dispose-void)
302
346
  - [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)
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)
304
348
  - [Resolve Path](#resolve-path)
305
349
  - [`realpath(path: string): Promise<string>`](#realpathpath-string-promisestring)
306
350
  - [Binary File Handling](#binary-file-handling)
@@ -316,6 +360,7 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
316
360
  - [Types](#types)
317
361
  - [`FileStat`](#filestat)
318
362
  - [`DirentData`](#direntdata)
363
+ - [`WatchOptions`](#watchoptions)
319
364
  - [`RemoteOPFSWorker`](#remoteopfsworker)
320
365
  - [Error Types](#error-types)
321
366
  - [Browser Support](#browser-support)
@@ -343,6 +388,7 @@ const fs = await createWorker();
343
388
 
344
389
  // With options
345
390
  const fs = await createWorker({
391
+ root: '/my-app',
346
392
  watchInterval: 500,
347
393
  hashAlgorithm: 'SHA-256',
348
394
  broadcastChannel: 'my-app-events'
@@ -358,6 +404,7 @@ channel.onmessage = (event) => {
358
404
  **Parameters:**
359
405
 
360
406
  - `options` (optional): Configuration options
407
+ - `root` (optional): Root path for the file system (default: '/')
361
408
  - `watchInterval` (optional): Polling interval in milliseconds for file watching
362
409
  - `hashAlgorithm` (optional): Hash algorithm for file hashing
363
410
  - `broadcastChannel` (optional): Custom name for the broadcast channel (default: 'opfs-worker')
@@ -381,37 +428,6 @@ const worker = wrap(new OPFSWorker());
381
428
 
382
429
  ### Core Methods
383
430
 
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
431
  ### Read File
416
432
 
417
433
  #### `readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`
@@ -722,21 +738,39 @@ await fs.sync(entries, { cleanBefore: true });
722
738
 
723
739
  ### Watch
724
740
 
725
- #### `watch(path: string): Promise<void>`
741
+ #### `watch(path: string, options?: WatchOptions): Promise<void>`
726
742
 
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.
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.
730
745
 
731
746
  ```typescript
747
+ // Start watching a directory recursively (default behavior)
732
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
+ };
733
763
  ```
734
764
 
735
765
  **Parameters:**
736
766
 
737
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)
738
772
 
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.
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.
740
774
 
741
775
  ### Unwatch
742
776
 
@@ -763,25 +797,29 @@ fs.dispose();
763
797
 
764
798
  ### Configuration
765
799
 
766
- #### `setOptions(options: { watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): void`
800
+ #### `setOptions(options: { root?: string; watchInterval?: number; hashAlgorithm?: null | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512'; maxFileSize?: number }): Promise<void>`
767
801
 
768
- Update configuration options for the file system, including watch interval, hash algorithm, and maximum file size for hashing.
802
+ Update configuration options for the file system, including root path, watch interval, hash algorithm, and maximum file size for hashing.
769
803
 
770
804
  ```typescript
805
+ // Change root path (will automatically remount)
806
+ await fs.setOptions({ root: '/new-app' });
807
+
771
808
  // Enable SHA-256 hashing for all file operations
772
- fs.setOptions({ hashAlgorithm: 'SHA-256' });
809
+ await fs.setOptions({ hashAlgorithm: 'SHA-256' });
773
810
 
774
811
  // Change watch interval to 100ms for faster change detection
775
- fs.setOptions({ watchInterval: 100 });
812
+ await fs.setOptions({ watchInterval: 100 });
776
813
 
777
814
  // Set custom maximum file size for hashing (100MB)
778
- fs.setOptions({ maxFileSize: 100 * 1024 * 1024 });
815
+ await fs.setOptions({ maxFileSize: 100 * 1024 * 1024 });
779
816
 
780
817
  // Disable hashing
781
- fs.setOptions({ hashAlgorithm: null });
818
+ await fs.setOptions({ hashAlgorithm: null });
782
819
 
783
820
  // Update multiple options at once
784
- fs.setOptions({
821
+ await fs.setOptions({
822
+ root: '/my-app',
785
823
  watchInterval: 200,
786
824
  hashAlgorithm: 'SHA-1',
787
825
  maxFileSize: 50 * 1024 * 1024 // 50MB
@@ -790,11 +828,12 @@ fs.setOptions({
790
828
 
791
829
  **Parameters:**
792
830
 
831
+ - `options.root` (optional): Root path for the file system
793
832
  - `options.watchInterval` (optional): Polling interval in milliseconds for file watching
794
833
  - `options.hashAlgorithm` (optional): Hash algorithm to use, or `null` to disable hashing
795
834
  - `options.maxFileSize` (optional): Maximum file size in bytes for hashing (default: 50MB)
796
835
 
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.
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.
798
837
 
799
838
  ### Resolve Path
800
839
 
@@ -1008,6 +1047,16 @@ interface DirentData {
1008
1047
  }
1009
1048
  ```
1010
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
+
1011
1060
  ### `RemoteOPFSWorker`
1012
1061
 
1013
1062
  Remote file system interface type.