opfs-worker 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +64 -428
  2. package/dist/BaseOPFS-BusBkyJX.js +2114 -0
  3. package/dist/BaseOPFS-BusBkyJX.js.map +1 -0
  4. package/dist/BaseOPFS-CYwrD7-q.cjs +4 -0
  5. package/dist/BaseOPFS-CYwrD7-q.cjs.map +1 -0
  6. package/dist/OPFSAsync-B2dR-2VR.cjs +2 -0
  7. package/dist/OPFSAsync-B2dR-2VR.cjs.map +1 -0
  8. package/dist/OPFSAsync-CO44W23e.js +124 -0
  9. package/dist/OPFSAsync-CO44W23e.js.map +1 -0
  10. package/dist/OPFSFacade-CEaSqTut.js +308 -0
  11. package/dist/OPFSFacade-CEaSqTut.js.map +1 -0
  12. package/dist/OPFSFacade-hrNXCn6w.cjs +2 -0
  13. package/dist/OPFSFacade-hrNXCn6w.cjs.map +1 -0
  14. package/dist/OPFSSync-BPrNtvFS.cjs +2 -0
  15. package/dist/OPFSSync-BPrNtvFS.cjs.map +1 -0
  16. package/dist/OPFSSync-D7obTsl3.js +208 -0
  17. package/dist/OPFSSync-D7obTsl3.js.map +1 -0
  18. package/dist/assets/dedicated.worker-DW6UqFo0.js.map +1 -0
  19. package/dist/{OPFSWorker.d.ts → core/BaseOPFS.d.ts} +53 -292
  20. package/dist/core/BaseOPFS.d.ts.map +1 -0
  21. package/dist/core/OPFSAsync.d.ts +55 -0
  22. package/dist/core/OPFSAsync.d.ts.map +1 -0
  23. package/dist/core/OPFSSync.d.ts +54 -0
  24. package/dist/core/OPFSSync.d.ts.map +1 -0
  25. package/dist/createOPFSAsync-DdhJYoC0.js +10 -0
  26. package/dist/createOPFSAsync-DdhJYoC0.js.map +1 -0
  27. package/dist/createOPFSAsync-DyWpvmL-.cjs +2 -0
  28. package/dist/createOPFSAsync-DyWpvmL-.cjs.map +1 -0
  29. package/dist/createOPFSDedicated-BwTc0NV5.js +2407 -0
  30. package/dist/createOPFSDedicated-BwTc0NV5.js.map +1 -0
  31. package/dist/createOPFSDedicated-CEeRexom.cjs +2357 -0
  32. package/dist/createOPFSDedicated-CEeRexom.cjs.map +1 -0
  33. package/dist/createOPFSShared-BSFWsoLe.js +23 -0
  34. package/dist/createOPFSShared-BSFWsoLe.js.map +1 -0
  35. package/dist/createOPFSShared-BoCft_BS.cjs +2 -0
  36. package/dist/createOPFSShared-BoCft_BS.cjs.map +1 -0
  37. package/dist/dedicated.worker.js +2355 -0
  38. package/dist/dedicated.worker.js.map +1 -0
  39. package/dist/{OPFSFacade.d.ts → facade/OPFSFacade.d.ts} +51 -25
  40. package/dist/facade/OPFSFacade.d.ts.map +1 -0
  41. package/dist/facade/createOPFSAsync.d.ts +16 -0
  42. package/dist/facade/createOPFSAsync.d.ts.map +1 -0
  43. package/dist/facade/createOPFSDedicated.d.ts +17 -0
  44. package/dist/facade/createOPFSDedicated.d.ts.map +1 -0
  45. package/dist/facade/createOPFSShared.d.ts +24 -0
  46. package/dist/facade/createOPFSShared.d.ts.map +1 -0
  47. package/dist/index.async.cjs +2 -0
  48. package/dist/index.async.cjs.map +1 -0
  49. package/dist/index.async.d.ts +10 -0
  50. package/dist/index.async.d.ts.map +1 -0
  51. package/dist/index.async.js +55 -0
  52. package/dist/index.async.js.map +1 -0
  53. package/dist/index.cjs +1 -2477
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.ts +17 -3
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +61 -2828
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.pure.cjs +1 -1
  60. package/dist/index.pure.cjs.map +1 -1
  61. package/dist/index.pure.d.ts +3 -1
  62. package/dist/index.pure.d.ts.map +1 -1
  63. package/dist/index.pure.js +50 -1053
  64. package/dist/index.pure.js.map +1 -1
  65. package/dist/index.sharedworker.cjs +2 -0
  66. package/dist/index.sharedworker.cjs.map +1 -0
  67. package/dist/index.sharedworker.d.ts +11 -0
  68. package/dist/index.sharedworker.d.ts.map +1 -0
  69. package/dist/index.sharedworker.js +55 -0
  70. package/dist/index.sharedworker.js.map +1 -0
  71. package/dist/index.sync.cjs +2 -0
  72. package/dist/index.sync.cjs.map +1 -0
  73. package/dist/index.sync.d.ts +11 -0
  74. package/dist/index.sync.d.ts.map +1 -0
  75. package/dist/index.sync.js +55 -0
  76. package/dist/index.sync.js.map +1 -0
  77. package/dist/shared.worker.js +2240 -0
  78. package/dist/shared.worker.js.map +1 -0
  79. package/dist/types.d.ts +42 -4
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/worker/createDedicatedWorker.d.ts +30 -0
  82. package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
  83. package/dist/worker/createSharedWorker.d.ts +37 -0
  84. package/dist/worker/createSharedWorker.d.ts.map +1 -0
  85. package/dist/worker/dedicated.worker.d.ts +2 -0
  86. package/dist/worker/dedicated.worker.d.ts.map +1 -0
  87. package/dist/worker/shared.worker.d.ts +2 -0
  88. package/dist/worker/shared.worker.d.ts.map +1 -0
  89. package/docs/api/backend.md +36 -0
  90. package/docs/api/create.md +82 -0
  91. package/docs/api/facade.md +53 -0
  92. package/docs/choosing-a-mode.md +41 -0
  93. package/docs/file-descriptors.md +46 -605
  94. package/docs/guides/async.md +36 -0
  95. package/docs/guides/dedicated.md +76 -0
  96. package/docs/guides/hashing.md +34 -0
  97. package/docs/guides/pure.md +46 -0
  98. package/docs/guides/sharedworker.md +57 -0
  99. package/docs/guides/streaming.md +185 -0
  100. package/docs/guides/watching.md +35 -0
  101. package/docs/migration.md +41 -0
  102. package/docs/types.md +105 -153
  103. package/package.json +53 -4
  104. package/src/{OPFSWorker.ts → core/BaseOPFS.ts} +159 -629
  105. package/src/core/OPFSAsync.ts +248 -0
  106. package/src/core/OPFSSync.ts +460 -0
  107. package/src/{OPFSFacade.ts → facade/OPFSFacade.ts} +134 -58
  108. package/src/facade/createOPFSAsync.ts +22 -0
  109. package/src/facade/createOPFSDedicated.ts +23 -0
  110. package/src/facade/createOPFSShared.ts +30 -0
  111. package/src/index.async.ts +11 -0
  112. package/src/index.pure.ts +3 -2
  113. package/src/index.sharedworker.ts +12 -0
  114. package/src/index.sync.ts +12 -0
  115. package/src/index.ts +37 -8
  116. package/src/types.ts +45 -4
  117. package/src/worker/createDedicatedWorker.ts +74 -0
  118. package/src/worker/createSharedWorker.ts +67 -0
  119. package/src/worker/dedicated.worker.ts +13 -0
  120. package/src/worker/shared.worker.ts +17 -0
  121. package/dist/OPFSFacade.d.ts.map +0 -1
  122. package/dist/OPFSWorker.d.ts.map +0 -1
  123. package/dist/assets/worker.entry-DUlEoroc.js.map +0 -1
  124. package/dist/createOPFSWorker.d.ts +0 -17
  125. package/dist/createOPFSWorker.d.ts.map +0 -1
  126. package/dist/helpers-DNj8ZoMu.cjs +0 -4
  127. package/dist/helpers-DNj8ZoMu.cjs.map +0 -1
  128. package/dist/helpers-WY2jfbOT.js +0 -1427
  129. package/dist/helpers-WY2jfbOT.js.map +0 -1
  130. package/dist/worker.entry.d.ts +0 -2
  131. package/dist/worker.entry.d.ts.map +0 -1
  132. package/docs/api-reference.md +0 -815
  133. package/src/createOPFSWorker.ts +0 -57
  134. package/src/worker.entry.ts +0 -6
package/README.md CHANGED
@@ -1,111 +1,33 @@
1
1
  # OPFS Worker
2
2
 
3
- [npm version](https://www.npmjs.com/package/opfs-worker)
3
+ <a href="https://www.npmjs.com/package/opfs-worker" target="_blank" rel="noreferrer"><img src="https://img.shields.io/npm/v/opfs-worker.svg" alt="npm"></a>
4
+ <a href="https://kachurun.github.io/opfs-worker/" target="_blank" rel="noreferrer"><img src="https://img.shields.io/badge/demo-live-brightgreen" alt="demo"></a>
4
5
 
5
- A robust TypeScript library for working with Origin Private File System (OPFS) through Web Workers, providing a Node.js-like file system API for browser environments.
6
+ ## What is OPFS?
6
7
 
7
- ## Table of Contents
8
+ OPFS ([Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)) is a browser filesystem scoped to your origin. It sits under the usual site quota; clearing site data wipes it. The browser does not show a file picker or ask for access.
8
9
 
9
- - [Features](#features)
10
- - [Installation](#installation)
11
- - [Quick Start](#quick-start)
12
- - [Demo](#demo)
13
- - [API Reference](#api-reference)
14
- - [Types](#types)
15
- - [Browser Support](#browser-support)
16
- - [Development](#development)
17
- - [License](#license)
10
+ This package puts a Node-like API on top of it.
18
11
 
19
- ## Features
12
+ **File API** — `readFile`, `writeFile`, `mkdir`, `stat`, `rename`, `copy`, `readDir`; encodings, extension-based detection, `string | URL` paths. [Docs](docs/api/facade.md)
20
13
 
21
- ### 🚀 **Performance & Architecture**
14
+ **File descriptors** `open` / `read` / `write` at an offset, plus `ftruncate` and `fsync`. [Docs](docs/file-descriptors.md)
22
15
 
23
- - **Web Worker-based**: Runs in a separate thread, keeping your main thread responsive
24
- - **Zero-copy data transfer**: Efficient binary data handling with Comlink transfer
25
- - **Faster than IndexedDB hacks**: Direct OPFS access without the overhead of database abstractions
26
- - **Efficient file watching**: Real-time change detection with minimatch patterns, no polling delays
16
+ **Concurrent access** operations on the same path are serialized, so parallel reads and writes do not fail because another sync access handle is open.
27
17
 
28
- ### 🛠️ **Developer Experience**
18
+ **Dedicated worker (default)** — `createOPFS()` sets up the worker for you. Fastest path, supports FDs, writes work in older Safari too. [Docs](docs/guides/dedicated.md)
29
19
 
30
- - **Better DX vs File System Access API**: Familiar Node.js-like API instead of complex browser APIs
31
- - **Type-safe**: Full TypeScript support with comprehensive type definitions
32
- - **Comlink-powered**: Seamless RPC communication between main thread and worker
20
+ **Async** same API on the main thread, no worker. No FDs. Writes need Safari 26+ (Safari 18 and older can only read). [Docs](docs/guides/async.md)
33
21
 
34
- ### 🌐 **Compatibility & Standards**
22
+ **SharedWorker** one instance shared across tabs. Same limits as async (no FDs, Safari 26+ for writes). [Docs](docs/guides/sharedworker.md)
35
23
 
36
- - **Cross-browser compatible**: Works in all modern browsers including Safari, Firefox, Chrome, and Edge
37
- - **OPFS-native**: Built directly on Origin Private File System standards
38
- - **No polyfills needed**: Uses native browser capabilities
24
+ **Bring your own worker** — use it directly in a worker you already run, or load a prebuilt worker script. [Docs](docs/choosing-a-mode.md)
39
25
 
40
- ### 📁 **File System Operations**
26
+ **Large files & uploads** — stream a `ReadableStream`, `Blob`, or `File` in chunks with progress, or bulk-import whole folders from a file picker / drag-and-drop via `importFiles`. [Docs](docs/guides/streaming.md)
41
27
 
42
- - **Complete API**: `readFile`, `writeFile`, `mkdir`, `remove`, `copy`, `rename`, and more
43
- - **Binary file support**: Handle images, documents, and any binary data seamlessly
44
- - **Hash support**: Cheap `etag` by default (`mtime-size`), plus optional content hashes (SHA-1 / SHA-256 / SHA-384 / SHA-512)
45
- - **File indexing**: Complete file system indexing with metadata and search
28
+ **Watch** change events over `BroadcastChannel` across tabs / workers. [Docs](docs/guides/watching.md)
46
29
 
47
- ### 🔄 **Advanced Features**
48
-
49
- - **Sync operations**: Bulk file synchronization from external data sources
50
- - **Event broadcasting**: Real-time file change notifications via BroadcastChannel
51
- - **Include/exclude filters**: Fine-grained control over file watching patterns
52
- - **Comprehensive error handling**: Detailed error types for better debugging
53
-
54
- ## When to Use
55
-
56
- **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.
57
-
58
- Here are the key use cases:
59
-
60
- ### 🚀 **Offline-First Applications**
61
-
62
- - **Progressive Web Apps (PWAs)**: Store user data, cached resources, and app state locally
63
- - **Media applications**: Cache images, videos, and audio files for offline playback
64
- - **Data synchronization**: Local-first data with background sync to remote servers
65
-
66
- ### 💾 **Performance & Caching**
67
-
68
- - **Asset caching**: Store and serve static files (CSS, JS, images) from local storage
69
- - **Database caching**: Cache API responses and database queries locally
70
- - **Session persistence**: Maintain user sessions and preferences across browser restarts
71
- - **Resource preloading**: Preload critical resources for instant access
72
-
73
- ### 🎨 **Editor & IDE Features**
74
-
75
- - **Code editors**: File tree navigation, syntax highlighting, and project management
76
- - **Design tools**: Canvas state persistence, project files, and asset management
77
- - **Document editors**: Rich text, markdown, and collaborative editing with local storage
78
- - **Build tools**: Local development servers, build caches, and project configurations
79
-
80
- ### 🔄 **State Management**
81
-
82
- - **Application state**: Persist complex application state and user preferences
83
- - **Form data**: Auto-save forms and restore user input on page refresh
84
- - **Game state**: Save game progress, levels, and user achievements
85
- - **User preferences**: Settings, themes, and personalized configurations
86
-
87
- ### 📱 **Mobile & Cross-Platform**
88
-
89
- - **Mobile web apps**: Native-like file management on mobile devices
90
- - **Cross-tab synchronization**: Share data between multiple browser tabs
91
- - **Worker-based processing**: Offload file operations to background threads
92
- - **Progressive Web Apps**: Full offline capabilities with file system access
93
-
94
- ### ❌ **When NOT to Use**
95
-
96
- - **Server-side storage**: This is a client-side solution, not a replacement for server storage
97
- - **Cross-origin access**: Files are isolated to the specific domain/origin
98
- - **Large datasets**: OPFS has browser-specific storage limitations:
99
- - **Chrome/Edge**: Up to ~50% of free disk space (check via `navigator.storage.estimate()`)
100
- - **Safari**: Usually 1-2 GB, sometimes less
101
- - **Firefox**: Shared limit with IndexedDB, similar to Chromium
102
-
103
- ### ⚡ **Performance Notes**
104
-
105
- - **Faster than IndexedDB**: Direct file system access without database overhead
106
- - **Slower than native FS**: Browser APIs have performance limitations compared to desktop file systems
107
- - **Worker-based**: File operations run in background threads, keeping main thread responsive
108
- - **Memory efficient**: Files are stored in browser's native file system, not in memory
30
+ **Hashing** — `stat()` can include an etag or a SHA hash (SHA skipped above a size cap). [Docs](docs/guides/hashing.md)
109
31
 
110
32
  ## Installation
111
33
 
@@ -113,362 +35,76 @@ Here are the key use cases:
113
35
  npm install opfs-worker
114
36
  ```
115
37
 
116
- **Dependencies:**
117
-
118
- - Modes 1 and 2 depend on `comlink` (worker communication is handled for you)
119
- - Mode 3 does not use Comlink — you run the class inside your own worker
120
- - Modes 1 and 2 ship an inlined worker, so your bundler does not need a `?worker` setup
121
-
122
- ## Quick Start
123
-
124
- OPFS sync I/O only works inside a dedicated Web Worker. The library covers three common setups:
125
-
126
- | | Import | Best when |
127
- | ---------- | --------------------------------------- | ----------------------------------------------------------------- |
128
- | **Mode 1** | `createOPFS()` from `opfs-worker` | You want a Node-like `fs` API on the main thread |
129
- | **Mode 2** | `createOPFSWorker()` from `opfs-worker` | You want the low-level worker API, but still from the main thread |
130
- | **Mode 3** | `OPFSWorker` from `opfs-worker/pure` | Your code already runs inside a worker |
131
-
132
- Modes 1 and 2 both pull Comlink. Mode 3 does not.
133
-
134
- ### Mode 1: Node-like facade (recommended)
135
-
136
- The usual path. `createOPFS()` starts an inlined worker and returns a facade with a Node-like API — strings, encodings, auto-detection. Comlink stays under the hood.
137
-
138
- > 1.x alias: `createWorker` still works and returns the same facade — prefer `createOPFS`.
38
+ ## Quick start
139
39
 
140
40
  ```typescript
141
41
  import { createOPFS } from 'opfs-worker';
142
42
 
143
- async function basicExample() {
144
- const fs = createOPFS();
145
-
146
- await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
147
- const config = await fs.readFile('/config.json');
148
- console.log(JSON.parse(config as string));
149
- }
150
-
151
- async function extendedExample() {
152
- const broadcastChannel = new BroadcastChannel('my-app-events');
153
-
154
- const fs = createOPFS({
155
- root: '/my-app',
156
- namespace: 'my-app:fs',
157
- hashAlgorithm: 'SHA-256',
158
- maxFileSize: 100 * 1024 * 1024, // 100MB
159
- broadcastChannel
160
- });
161
-
162
- const imageData = new Uint8Array([58 /* binary data */]);
163
- await fs.writeFile('/image.png', imageData);
164
- const binaryData = await fs.readFile('/image.png', 'binary');
165
-
166
- await fs.writeFile('/config.txt', 'Hello, World!');
167
- const textContent = await fs.readFile('/config.txt'); // string
168
-
169
- await fs.watch('/', {
170
- recursive: true,
171
- include: ['*.json'],
172
- exclude: ['dist/**']
173
- });
174
-
175
- broadcastChannel.onmessage = (event) => {
176
- console.log('File changed:', event.data);
177
- };
178
-
179
- const stats = await fs.stat('/image.png');
180
- console.log(`File size: ${stats.size} bytes`, `Hash: ${stats.hash}`);
181
- }
182
- ```
183
-
184
- > Watch events go over `BroadcastChannel` (default name `opfs-worker`). Pass `broadcastChannel` to customize. Only watched paths emit events.
185
-
186
- ### Mode 2: Raw worker API from the main thread
187
-
188
- Same package entry, same inlined worker but without the facade. You get a Comlink proxy to `OPFSWorker`: bytes in, bytes out. Useful if you want to wrap it yourself or prefer the lower-level API.
189
-
190
- ```typescript
191
- import { createOPFSWorker } from 'opfs-worker';
192
-
193
- const { fs, dispose } = createOPFSWorker({
194
- root: '/my-app',
195
- namespace: 'my-app:fs',
196
- hashAlgorithm: 'SHA-256',
197
- maxFileSize: 100 * 1024 * 1024,
198
- broadcastChannel: 'my-app-events',
199
- });
200
-
201
- await fs.writeFile('/config.json', new TextEncoder().encode(JSON.stringify({ theme: 'dark' })));
202
- const bytes = await fs.readFile('/config.json');
203
- console.log(JSON.parse(new TextDecoder().decode(bytes)));
204
-
205
- dispose(); // worker.dispose() + terminate()
206
- ```
207
-
208
- > Want your own facade? Our `OPFSFacade` is built on `createOPFSWorker` — copy [`src/OPFSFacade.ts`](src/OPFSFacade.ts), swap the imports to `opfs-worker` and change what you need.
209
-
210
- ### Mode 3: `OPFSWorker` inside your own worker
211
-
212
- If you already have a worker (or want full control over it), import the class and use it directly. No nested worker, no Comlink. Bundling that worker and talking to the main thread — if you need that at all — is up to you.
213
-
214
- ```typescript
215
- // my-app.worker.ts (this file IS the worker)
216
- import { OPFSWorker } from 'opfs-worker/pure';
217
-
218
- const fs = new OPFSWorker({
219
- root: '/my-app',
220
- namespace: 'my-app:fs',
221
- hashAlgorithm: 'SHA-256',
222
- broadcastChannel: 'my-app-events', // name only — BroadcastChannel instances can't cross the wire
43
+ // Starts a dedicated worker and returns a Node-like fs API
44
+ const fs = createOPFS({
45
+ root: '/my-app',
46
+ hashAlgorithm: 'SHA-256'
223
47
  });
224
48
 
225
- await fs.writeFile('/config.json', new TextEncoder().encode(JSON.stringify({ theme: 'dark' })));
226
- const bytes = await fs.readFile('/config.json');
227
- console.log(JSON.parse(new TextDecoder().decode(bytes)));
228
- ```
229
-
230
- `opfs-worker/pure` does not call Comlink `expose()`, so it will not take over your worker's message port.
231
-
232
- ### Hash Algorithm Configuration
233
-
234
- Set once via options / `setOptions()` — used by `stat()`, `index()`, and watch events.
235
-
236
- Default is `'etag'`: a cheap weak tag from `File.lastModified` + size (no content read). Switch to a SHA algorithm when you need a real content hash.
237
-
238
- ```typescript
239
- import { createOPFS } from 'opfs-worker';
240
-
241
- async function hashExample() {
242
- // Default: etag (fast, no content read)
243
- const fs = createOPFS();
244
- await fs.writeFile('/data.txt', 'Hello World');
245
-
246
- const etagStat = await fs.stat('/data.txt');
247
- console.log(`ETag: ${etagStat.hash}`); // e.g. "m1abc-c" (mtime36-size36)
248
-
249
- // Opt into a cryptographic content hash
250
- await fs.setOptions({ hashAlgorithm: 'SHA-256' });
251
- const shaStat = await fs.stat('/data.txt');
252
- console.log(`SHA-256: ${shaStat.hash}`);
253
-
254
- // Index / watch events also include hash when enabled
255
- const index = await fs.index();
256
- for (const [path, stat] of index) {
257
- if (stat.isFile && stat.hash) {
258
- console.log(`${path}: ${stat.hash}`);
259
- }
260
- }
261
-
262
- const channel = new BroadcastChannel('opfs-worker');
263
- channel.onmessage = (event) => {
264
- if (event.data.hash) {
265
- console.log(`File ${event.data.path} changed, hash: ${event.data.hash}`);
266
- }
267
- };
268
- }
269
-
270
- // maxFileSize only applies to SHA-* (content) hashing — etag ignores it
271
- async function maxFileSizeExample() {
272
- const fs = createOPFS({
273
- hashAlgorithm: 'SHA-256',
274
- maxFileSize: 100 * 1024 * 1024 // 100MB (default 50MB)
275
- });
276
-
277
- const stats = await fs.stat('/large-file.dat');
278
- if (stats.hash) {
279
- console.log(`Hash: ${stats.hash}`);
280
- } else {
281
- console.log('File too large for content hashing');
282
- }
283
- }
284
- ```
285
-
286
- **Supported Hash Algorithms:**
287
-
288
- - `'etag'` — **default**. Weak tag from mtime + size; free, no content read
289
- - `'SHA-1'` / `'SHA-256'` / `'SHA-384'` / `'SHA-512'` — content hashes via Web Crypto (bounded by `maxFileSize`)
290
- - `null` / `false` — disable hashing
291
-
292
- **Note:** Prefer `'etag'` for watch / change-detection. Use SHA only when you need integrity of file bytes. Set to `null`/`false` if you don't need `hash` at all.
293
-
294
- ### Root Path Configuration
49
+ await fs.mkdir('/project');
50
+ await fs.writeFile('/project/hello.txt', 'Hello, OPFS!');
51
+ await fs.rename('/project/hello.txt', '/project/readme.txt');
295
52
 
296
- 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.
53
+ const files = await fs.readDir('/project');
54
+ const text = await fs.readFile('/project/readme.txt'); // 'Hello, OPFS!'
297
55
 
298
- ```typescript
299
- import { createOPFS } from 'opfs-worker';
300
-
301
- async function rootPathExample() {
302
- // Use default root path '/'
303
- const fsDefault = createOPFS();
304
- await fsDefault.writeFile('/config.json', '{}');
305
- // This creates /config.json in OPFS root
306
-
307
- // Use custom root path
308
- const fsCustom = createOPFS({ root: '/my-app' });
309
- await fsCustom.writeFile('/config.json', '{}');
310
- // This creates /my-app/config.json in OPFS root
311
-
312
- // Change root path dynamically
313
- await fsCustom.setOptions({ root: '/new-app' });
314
- await fsCustom.writeFile('/config.json', '{}');
315
- // This creates /new-app/config.json in OPFS root
316
- }
56
+ // Tear down watches, close the backend, and terminate the worker
57
+ fs.dispose();
317
58
  ```
318
59
 
319
- **Root Path Behavior:**
320
-
321
- - **Default**: Uses `/` (OPFS root directory)
322
- - **Custom**: Creates a subdirectory within OPFS for isolation
323
- - **Dynamic**: Can be changed at runtime via `setOptions()`
324
- - **Auto-mount**: Automatically mounts to the specified root when needed
325
- - **Path Resolution**: All API paths are relative to the configured root
326
-
327
- ## Demo
328
-
329
- Check out the live demo powered by Vite and hosted on GitHub Pages.
330
-
331
- [Live Demo](https://kachurun.github.io/opfs-worker/)
332
-
333
- ## API Reference
334
-
335
- The complete API reference is available in the [docs/api-reference.md](docs/api-reference.md) file.
336
-
337
- **Additional Documentation:**
338
-
339
- - [File Descriptors Guide](docs/file-descriptors.md) - Comprehensive guide to low-level file I/O operations
340
- - [Types Reference](docs/types.md) - Complete TypeScript type definitions
341
-
342
- ### Quick API Overview
343
-
344
- **Entry Points:**
345
-
346
- - `createOPFS(options?)` — Mode 1: Node-like facade on the main thread
347
- - `createOPFSWorker(options?)` — Mode 2: raw `OPFSWorker` proxy on the main thread
348
- - `OPFSWorker` from `opfs-worker/pure` — Mode 3: use the class inside your own worker
349
- - `OPFSFacade` — Mode 1 class returned by `createOPFS`
350
-
351
- **Core File Operations:**
352
-
353
- - `readFile(path, encoding?)` - Read files as text or binary with auto-detection
354
- - `writeFile(path, data, encoding?)` - Write text or binary data with auto-detection
355
- - `readText(path, encoding?)` - Read files as text with specified encoding
356
- - `writeText(path, text, encoding?)` - Write text with specified encoding
357
- - `appendText(path, text, encoding?)` - Append text with specified encoding
358
-
359
- **Common Operations:**
360
-
361
- - `mkdir(path, options?)` - Create directories
362
- - `readDir(path)` - List directory contents
363
- - `stat(path)` - Get file/directory statistics
364
- - `remove(path, options?)` - Remove files/directories
365
- - `copy(source, destination, options?)` - Copy files/directories
366
- - `rename(oldPath, newPath)` - Rename files/directories
367
-
368
- **File Descriptors (Low-level I/O):**
369
-
370
- - `open(path, options?)` - Open file and return descriptor
371
- - `read(fd, buffer, offset, length, position?)` - Read from descriptor (returns `{bytesRead, buffer}`)
372
- - `write(fd, buffer, offset?, length?, position?)` - Write to descriptor
373
- - `fstat(fd)` - Get stats by descriptor
374
- - `ftruncate(fd, size?)` - Truncate file by descriptor
375
- - `fsync(fd)` - Sync file data to storage
376
- - `close(fd)` - Close file descriptor
377
-
378
- **Note**: The `read()` method uses `Comlink.transfer()` for efficient buffer handling. **From the main window**, you must transfer buffer ownership to the worker, and **from the worker**, the buffer is transferred back to you. See [File Descriptors Guide](docs/file-descriptors.md) for complete usage examples.
379
-
380
- _For detailed file descriptor documentation, see [File Descriptors Guide](docs/file-descriptors.md)_
381
-
382
- **Advanced Features:**
383
-
384
- - `watch(path, options?)` - Watch for file changes with minimatch patterns
385
- - `index()` - Get complete file system index
386
- - `sync(entries, options?)` - Bulk synchronization
387
- - `setOptions(options)` - Update configuration
388
-
389
- **Binary File Support:**
390
-
391
- - Full support for images, documents, and any binary data
392
- - Automatic conversion between Uint8Array, ArrayBuffer, and Blob
393
- - Multiple text encodings (UTF-8, UTF-16, ASCII, Base64, etc.)
394
-
395
- **Utility Functions:**
396
-
397
- - Path manipulation (`basename`, `dirname`, `normalizePath`, etc.)
398
- - Data conversion helpers
399
- - OPFS support detection
400
-
401
- For detailed API documentation with examples, see [docs/api-reference.md](docs/api-reference.md).
402
-
403
- ## Types
404
-
405
- Full TypeScript types are provided — see [docs/types.md](docs/types.md) for complete type definitions including `FileStat`, `DirentData`, `WatchOptions`, `OPFSOptions`, and more.
406
-
407
- ## Browser Support
408
-
409
- This library works in **all modern browsers**, including Safari, Firefox, Chrome, and Edge.
410
-
411
- **Requirements:**
412
-
413
- - Web Workers support (available in all modern browsers)
414
- - File System Access API (for OPFS functionality)
415
-
416
- **Browser Compatibility:**
417
-
418
- - ✅ Chrome 86+
419
- - ✅ Edge 86+
420
- - ✅ Firefox 111+
421
- - ✅ Safari 15.2+
422
- - ✅ Opera 72+
60
+ ## Choose a mode
61
+
62
+ | What you want | Use |
63
+ | ---------------------------------------------------------------- | ---------------------------------------------------- |
64
+ | Node-like `fs` via dedicated worker (default, works everywhere) | `createOPFS()` |
65
+ | Node-like `fs` on the main thread (modern browsers / Safari 26+) | `createOPFSAsync()` from `opfs-worker/async` |
66
+ | Node-like `fs` via SharedWorker (modern browsers / Safari 26+) | `createOPFSShared()` from `opfs-worker/sharedworker` |
67
+ | OPFS inside a worker you already run (classes only) | `OPFSSync` / `OPFSAsync` from `opfs-worker/pure` |
68
+
69
+ Trade-offs (FDs, Safari, size, CSP): [Choosing a mode](docs/choosing-a-mode.md).
70
+
71
+ ## Docs
72
+
73
+ - [Choosing a mode](docs/choosing-a-mode.md)
74
+ - API
75
+ - [Create helpers & options](docs/api/create.md)
76
+ - [Facade](docs/api/facade.md)
77
+ - [Backend](docs/api/backend.md)
78
+ - [File descriptors](docs/file-descriptors.md)
79
+ - [Types](docs/types.md)
80
+ - Guides
81
+ - [Dedicated worker](docs/guides/dedicated.md)
82
+ - [Async](docs/guides/async.md)
83
+ - [SharedWorker](docs/guides/sharedworker.md)
84
+ - [Pure classes](docs/guides/pure.md)
85
+ - [Streaming](docs/guides/streaming.md)
86
+ - [Watching](docs/guides/watching.md)
87
+ - [Hashing](docs/guides/hashing.md)
88
+ - [Migration from 1.x](docs/migration.md)
423
89
 
424
90
  ## Development
425
91
 
426
- ### Building
427
-
428
92
  ```bash
93
+ npm install
429
94
  npm run build
430
- ```
431
-
432
- ### Development Server
433
-
434
- ```bash
435
- npm run dev
436
- ```
437
-
438
- ### Testing
439
-
440
- ```bash
441
- npm test
442
- npm run test:coverage
443
- ```
444
-
445
- ### Linting
446
-
447
- ```bash
95
+ npm run test
448
96
  npm run lint
97
+ npm run type-check
449
98
  ```
450
99
 
100
+ Demo: `npm run dev:demo` / `npm run build:demo` / `npm run preview`.
101
+
451
102
  ## License
452
103
 
453
104
  MIT
454
105
 
455
- ## Contributing
456
-
457
- Contributions are welcome!
458
-
459
- **How to contribute:**
460
-
461
- - 🐛 **Report bugs** or suggest features via [GitHub Issues](https://github.com/kachurun/opfs-worker/issues)
462
- - 💡 **Submit ideas** for improvements or new features
463
- - 🔧 **Send PRs** for bug fixes, documentation, or enhancements
464
- - 📚 **Improve docs** - help make the library more accessible
465
-
466
- **Getting started:**
106
+ ## Maintainer
467
107
 
468
- 1. Fork the repository
469
- 2. Create a feature branch
470
- 3. Make your changes
471
- 4. Add tests if applicable
472
- 5. Submit a Pull Request
108
+ <img src="https://github.com/kachurun.png" width="100" height="100" alt="@kachurun's avatar">
473
109
 
474
- _We welcome all contributions, big and small!_
110
+ Maintained with ❤️ by [@kachurun](https://github.com/kachurun)