opfs-worker 0.2.1 → 0.2.3

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
@@ -57,22 +57,34 @@ async function example() {
57
57
  // Create a file system instance
58
58
  const fs = await createWorker();
59
59
 
60
- // Mount the file system (required, even for '/')
61
- await fs.mount('/my-app');
60
+ // Mount is optional - OPFS root is used by default
61
+ // await fs.mount('/my-app'); // Uses custom subdirectory
62
62
 
63
- // Write a file
63
+ // Write a file (auto-mounts if not mounted)
64
64
  await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
65
65
 
66
66
  // Read the file back
67
67
  const config = await fs.readFile('/config.json');
68
68
  console.log(JSON.parse(config));
69
+
70
+ // Handle binary files
71
+ const imageData = new Uint8Array([/* binary data */]);
72
+ await fs.writeFile('/image.png', imageData);
73
+ const binaryData = await fs.readFile('/image.png', 'binary');
74
+ }
75
+
76
+ // With watch callbacks
77
+ async function exampleWithWatch() {
78
+ const fs = await createWorker(
79
+ (event) => console.log('File changed:', event),
80
+ { watchInterval: 500 }
81
+ );
82
+
83
+ await fs.watch('/docs');
69
84
  }
70
85
  ```
71
86
 
72
- > **Note:** The `mount` call defines the directory that becomes the root of your
73
- > file system. This call is mandatory before any file operations. For example,
74
- > after `fs.mount('/dir')`, calling `fs.readFile('/text.txt')` accesses the file
75
- > located at `/dir/text.txt` inside OPFS.
87
+ > **Note:** The `mount` call is now optional. If not called, the OPFS root directory is used automatically. For custom subdirectories, call `fs.mount('/my-app')` to create a subdirectory in OPFS. All file operations will auto-mount if needed.
76
88
 
77
89
  ### Manual Worker Setup
78
90
 
@@ -86,18 +98,32 @@ async function example() {
86
98
  // Create and wrap the worker
87
99
  const worker = wrap(new OPFSWorker());
88
100
 
89
- // Mount the file system (required, even for '/')
90
- await worker.mount('/my-app');
101
+ // Mount is optional - OPFS root is used by default
102
+ // await worker.mount(); // Uses OPFS root directory
103
+ // await worker.mount('/my-app'); // Uses custom subdirectory
91
104
 
92
- // Use the file system
105
+ // Use the file system (auto-mounts if not mounted)
93
106
  await worker.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
94
107
  const config = await worker.readFile('/config.json');
95
108
  console.log(JSON.parse(config));
96
109
  }
110
+
111
+ // With watch callbacks
112
+ async function exampleWithWatch() {
113
+ const worker = wrap(new OPFSWorker(
114
+ (event) => console.log('File changed:', event),
115
+ { watchInterval: 500 }
116
+ ));
117
+
118
+ await worker.mount('/my-app');
119
+ await worker.watch('/docs');
120
+ }
97
121
  ```
98
122
 
99
123
  **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.
100
124
 
125
+ **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.
126
+
101
127
  ### Advanced Usage
102
128
 
103
129
  ```typescript
@@ -105,7 +131,8 @@ import { createWorker } from 'opfs-worker';
105
131
 
106
132
  async function advancedExample() {
107
133
  const fs = await createWorker();
108
- await fs.mount('/my-app');
134
+ // Mount is optional - auto-mounts to OPFS root if not called
135
+ // await fs.mount('/my-app'); // For custom subdirectory
109
136
 
110
137
  // Create directories
111
138
  await fs.mkdir('/data/logs', { recursive: true });
@@ -114,6 +141,11 @@ async function advancedExample() {
114
141
  await fs.writeFile('/data/config.json', JSON.stringify({ version: '1.0' }));
115
142
  await fs.writeFile('/data/logs/app.log', 'Application started\n');
116
143
 
144
+ // Handle binary files
145
+ const imageData = new Uint8Array([/* binary data */]);
146
+ await fs.writeFile('/data/image.png', imageData);
147
+ const binaryData = await fs.readFile('/data/image.png', 'binary');
148
+
117
149
  // Append to a file
118
150
  await fs.appendFile('/data/logs/app.log', `${new Date().toISOString()}: User logged in\n`);
119
151
 
@@ -129,6 +161,8 @@ async function advancedExample() {
129
161
  files.forEach(item => {
130
162
  console.log(`${item.name} - ${item.isFile ? 'file' : 'directory'}`);
131
163
  });
164
+
165
+
132
166
  }
133
167
  ```
134
168
 
@@ -160,6 +194,8 @@ Check out the live demo powered by Vite and hosted on GitHub Pages.
160
194
  - [Watch](#watchpath-string-promisevoid)
161
195
  - [Unwatch](#unwatchpath-string-void)
162
196
  - [Real Path](#realpathpath-string-promisestring)
197
+ - [Binary File Handling](#binary-file-handling)
198
+ - [Utility Functions](#utility-functions)
163
199
 
164
200
  ### Entry Points
165
201
 
@@ -196,11 +232,9 @@ const worker = wrap(new OPFSWorker());
196
232
 
197
233
  ### Mount
198
234
 
199
- #### `mount(root?: string, watch?: (event: WatchEvent) => void, options?: { watchInterval?: number }): Promise<boolean>`
235
+ #### `mount(root?: string): Promise<boolean>`
200
236
 
201
- Initialize the file system within a given directory and optionally register a
202
- watch callback. Calling `mount` is required before performing any file
203
- operations, even if you're using the root directory (`'/'`).
237
+ Initialize the file system within a given directory. **Mount is now optional** - if not called, the OPFS root directory is used automatically.
204
238
 
205
239
  The `root` parameter defines where in OPFS the file system's root will be
206
240
  created. All file paths passed to the API are relative to this mount point. For
@@ -208,26 +242,30 @@ example, after `fs.mount('/dir')`, calling `fs.readFile('/text.txt')` accesses
208
242
  `/dir/text.txt` in OPFS.
209
243
 
210
244
  ```typescript
211
- await fs.mount('/my-app', event => console.log(event), { watchInterval: 500 });
245
+ // Use OPFS root (default behavior)
246
+ await fs.mount();
247
+
248
+ // Use custom subdirectory
249
+ await fs.mount('/my-app');
212
250
  ```
213
251
 
214
252
  **Parameters:**
215
253
 
216
254
  - `root` (optional): The root path for the file system (default: '/')
217
- - `watch` (optional): Callback invoked with `WatchEvent` objects when watched
218
- paths change
219
- - `options.watchInterval` (optional): Polling interval in milliseconds
220
- (default: `1000`)
221
255
 
222
256
  **Returns:** `Promise<boolean>` - True if initialization was successful
223
257
 
224
258
  **Throws:** `OPFSError` if initialization fails
225
259
 
260
+ **Note:** All file operations will automatically mount the OPFS root if no explicit mount has been performed.
261
+
262
+ **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.
263
+
226
264
  ### Read File
227
265
 
228
266
  #### `readFile(path: string, encoding?: BufferEncoding | 'binary'): Promise<string | Uint8Array>`
229
267
 
230
- Read a file from the file system.
268
+ Read a file from the file system. Supports both text and binary files.
231
269
 
232
270
  ```typescript
233
271
  // Read as text (default)
@@ -238,22 +276,32 @@ const binaryData = await fs.readFile('/images/logo.png', 'binary');
238
276
 
239
277
  // Read with specific encoding
240
278
  const utf8Content = await fs.readFile('/data/utf8.txt', 'utf-8');
279
+
280
+ // Handle binary data
281
+ const imageBuffer = await fs.readFile('/image.png', 'binary');
282
+ const blob = new Blob([imageBuffer], { type: 'image/png' });
283
+ const url = URL.createObjectURL(blob);
241
284
  ```
242
285
 
243
286
  **Parameters:**
244
287
 
245
288
  - `path`: The path to the file to read
246
- - `encoding` (optional): The encoding to use ('utf-8', 'binary', etc.)
289
+ - `encoding` (optional): The encoding to use ('utf-8', 'binary', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
247
290
 
248
- **Returns:** `Promise<string | Uint8Array>` - File contents
291
+ **Returns:** `Promise<string | Uint8Array>` - File contents as string or binary data
249
292
 
250
293
  **Throws:** `FileNotFoundError` if the file doesn't exist
251
294
 
295
+ **Binary File Handling:**
296
+ - Use `'binary'` encoding to read files as `Uint8Array`
297
+ - Binary data can be converted to `Blob` for use with `URL.createObjectURL()`
298
+ - Supports various encodings for text files
299
+
252
300
  ### Write File
253
301
 
254
302
  #### `writeFile(path: string, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Promise<void>`
255
303
 
256
- Write data to a file, creating or overwriting it.
304
+ Write data to a file, creating or overwriting it. Supports both text and binary data.
257
305
 
258
306
  ```typescript
259
307
  // Write text data
@@ -265,13 +313,31 @@ await fs.writeFile('/data/binary.dat', binaryData);
265
313
 
266
314
  // Write with specific encoding
267
315
  await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
316
+
317
+ // Write binary data from file input
318
+ const fileInput = document.getElementById('file') as HTMLInputElement;
319
+ const file = fileInput.files?.[0];
320
+ if (file) {
321
+ const arrayBuffer = await file.arrayBuffer();
322
+ await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
323
+ }
324
+
325
+ // Write binary data from fetch
326
+ const response = await fetch('/api/image.png');
327
+ const arrayBuffer = await response.arrayBuffer();
328
+ await fs.writeFile('/downloaded-image.png', new Uint8Array(arrayBuffer));
268
329
  ```
269
330
 
270
331
  **Parameters:**
271
332
 
272
333
  - `path`: The path to the file to write
273
334
  - `data`: The data to write (string, Uint8Array, or ArrayBuffer)
274
- - `encoding` (optional): The encoding to use when writing string data
335
+ - `encoding` (optional): The encoding to use when writing string data ('utf-8', 'utf-16le', 'ascii', 'latin1', 'base64', 'hex')
336
+
337
+ **Binary File Handling:**
338
+ - Pass `Uint8Array` or `ArrayBuffer` directly for binary data
339
+ - Use with file uploads, image processing, or any binary content
340
+ - Supports various text encodings for string data
275
341
 
276
342
  ### Append File
277
343
 
@@ -520,8 +586,8 @@ await fs.sync(entries, { cleanBefore: true });
520
586
  #### `watch(path: string): Promise<void>`
521
587
 
522
588
  Start watching a file or directory for changes. Detected changes are sent to the
523
- callback provided to `mount`. The polling interval is configured globally when
524
- calling `mount`.
589
+ callback provided to the constructor. The polling interval is configured globally when
590
+ creating the worker instance.
525
591
 
526
592
  ```typescript
527
593
  await fs.watch('/docs');
@@ -531,6 +597,8 @@ await fs.watch('/docs');
531
597
 
532
598
  - `path`: File or directory to watch
533
599
 
600
+ **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.
601
+
534
602
  ### Unwatch
535
603
 
536
604
  #### `unwatch(path: string): void`
@@ -557,6 +625,171 @@ console.log(absolute); // '/data/file.txt'
557
625
 
558
626
  **Throws:** `FileNotFoundError` if the path doesn't exist
559
627
 
628
+ ## Binary File Handling
629
+
630
+ This library provides comprehensive support for binary files, making it easy to work with images, documents, and other binary data.
631
+
632
+ ### Reading Binary Files
633
+
634
+ ```typescript
635
+ // Read as binary data
636
+ const imageData = await fs.readFile('/image.png', 'binary');
637
+ const documentData = await fs.readFile('/document.pdf', 'binary');
638
+
639
+ // Convert to Blob for use with URLs
640
+ const imageBuffer = await fs.readFile('/image.png', 'binary');
641
+ const blob = new Blob([imageBuffer], { type: 'image/png' });
642
+ const url = URL.createObjectURL(blob);
643
+
644
+ // Display image
645
+ const img = document.createElement('img');
646
+ img.src = url;
647
+ document.body.appendChild(img);
648
+ ```
649
+
650
+ ### Writing Binary Files
651
+
652
+ ```typescript
653
+ // Write binary data directly
654
+ const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
655
+ await fs.writeFile('/data.bin', binaryData);
656
+
657
+ // From file input
658
+ const fileInput = document.getElementById('file') as HTMLInputElement;
659
+ const file = fileInput.files?.[0];
660
+ if (file) {
661
+ const arrayBuffer = await file.arrayBuffer();
662
+ await fs.writeFile('/uploaded-file', new Uint8Array(arrayBuffer));
663
+ }
664
+
665
+ // From fetch response
666
+ const response = await fetch('/api/download/file.pdf');
667
+ const arrayBuffer = await response.arrayBuffer();
668
+ await fs.writeFile('/downloaded-file.pdf', new Uint8Array(arrayBuffer));
669
+
670
+ // From canvas
671
+ const canvas = document.createElement('canvas');
672
+ const ctx = canvas.getContext('2d');
673
+ // ... draw something ...
674
+ canvas.toBlob(async (blob) => {
675
+ if (blob) {
676
+ const arrayBuffer = await blob.arrayBuffer();
677
+ await fs.writeFile('/canvas-image.png', new Uint8Array(arrayBuffer));
678
+ }
679
+ });
680
+ ```
681
+
682
+ ### Working with Different Data Types
683
+
684
+ ```typescript
685
+ // Convert between different binary formats
686
+ const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
687
+ const arrayBuffer = uint8Array.buffer;
688
+
689
+ // All of these work the same way
690
+ await fs.writeFile('/data1.bin', uint8Array);
691
+ await fs.writeFile('/data2.bin', arrayBuffer);
692
+ await fs.writeFile('/data3.bin', new Uint8Array(arrayBuffer));
693
+
694
+ // Read back as binary
695
+ const data = await fs.readFile('/data1.bin', 'binary');
696
+ console.log(data); // Uint8Array
697
+ ```
698
+
699
+ ### File Upload and Download
700
+
701
+ ```typescript
702
+ // Handle file uploads
703
+ const handleFileUpload = async (file: File) => {
704
+ const arrayBuffer = await file.arrayBuffer();
705
+ await fs.writeFile(`/uploads/${file.name}`, new Uint8Array(arrayBuffer));
706
+ };
707
+
708
+ // Create downloadable files
709
+ const createDownloadLink = async (filePath: string, fileName: string) => {
710
+ const data = await fs.readFile(filePath, 'binary');
711
+ const blob = new Blob([data]);
712
+ const url = URL.createObjectURL(blob);
713
+
714
+ const a = document.createElement('a');
715
+ a.href = url;
716
+ a.download = fileName;
717
+ a.click();
718
+
719
+ URL.revokeObjectURL(url);
720
+ };
721
+ ```
722
+
723
+ ### Supported Encodings
724
+
725
+ The library supports various encodings for text files:
726
+
727
+ - `'utf-8'` (default) - UTF-8 encoding
728
+ - `'utf-16le'` - UTF-16 little-endian
729
+ - `'ascii'` - ASCII encoding
730
+ - `'latin1'` - Latin-1 encoding
731
+ - `'base64'` - Base64 encoding
732
+ - `'hex'` - Hexadecimal encoding
733
+ - `'binary'` - Raw binary data (returns Uint8Array)
734
+
735
+ ## Utility Functions
736
+
737
+ The library exports several utility functions that can be used independently:
738
+
739
+ ### Path Utilities
740
+
741
+ ```typescript
742
+ import { basename, dirname, normalizePath, resolvePath, extname } from 'opfs-worker';
743
+
744
+ // Extract filename from path
745
+ basename('/path/to/file.txt'); // 'file.txt'
746
+ basename('/path/to/directory/'); // ''
747
+
748
+ // Extract directory path
749
+ dirname('/path/to/file.txt'); // '/path/to'
750
+ dirname('file.txt'); // '/'
751
+
752
+ // Normalize path to start with '/'
753
+ normalizePath('path/to/file'); // '/path/to/file'
754
+ normalizePath('/path/to/file'); // '/path/to/file'
755
+
756
+ // Resolve relative paths
757
+ resolvePath('./config/../data/file.txt'); // '/data/file.txt'
758
+ resolvePath('/path/to/../file.txt'); // '/path/file.txt'
759
+
760
+ // Get file extension
761
+ extname('/path/to/file.txt'); // '.txt'
762
+ extname('/path/to/file'); // ''
763
+ extname('/path/to/file.name.ext'); // '.ext'
764
+ ```
765
+
766
+ ### Data Conversion
767
+
768
+ ```typescript
769
+ import { convertBlobToUint8Array } from 'opfs-worker';
770
+
771
+ // Convert Blob to Uint8Array
772
+ const fileInput = document.getElementById('file') as HTMLInputElement;
773
+ const file = fileInput.files?.[0];
774
+ if (file) {
775
+ const data = await convertBlobToUint8Array(file);
776
+ await fs.writeFile('/uploaded-file', data);
777
+ }
778
+ ```
779
+
780
+ ### File System Utilities
781
+
782
+ ```typescript
783
+ import { checkOPFSSupport, splitPath, joinPath } from 'opfs-worker';
784
+
785
+ // Check if browser supports OPFS
786
+ checkOPFSSupport(); // Throws error if not supported
787
+
788
+ // Path manipulation
789
+ splitPath('/path/to/file'); // ['path', 'to', 'file']
790
+ joinPath(['path', 'to', 'file']); // '/path/to/file'
791
+ ```
792
+
560
793
  ## Types
561
794
 
562
795
  ### `FileStat`