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.
- package/README.md +64 -428
- package/dist/BaseOPFS-BusBkyJX.js +2114 -0
- package/dist/BaseOPFS-BusBkyJX.js.map +1 -0
- package/dist/BaseOPFS-CYwrD7-q.cjs +4 -0
- package/dist/BaseOPFS-CYwrD7-q.cjs.map +1 -0
- package/dist/OPFSAsync-B2dR-2VR.cjs +2 -0
- package/dist/OPFSAsync-B2dR-2VR.cjs.map +1 -0
- package/dist/OPFSAsync-CO44W23e.js +124 -0
- package/dist/OPFSAsync-CO44W23e.js.map +1 -0
- package/dist/OPFSFacade-CEaSqTut.js +308 -0
- package/dist/OPFSFacade-CEaSqTut.js.map +1 -0
- package/dist/OPFSFacade-hrNXCn6w.cjs +2 -0
- package/dist/OPFSFacade-hrNXCn6w.cjs.map +1 -0
- package/dist/OPFSSync-BPrNtvFS.cjs +2 -0
- package/dist/OPFSSync-BPrNtvFS.cjs.map +1 -0
- package/dist/OPFSSync-D7obTsl3.js +208 -0
- package/dist/OPFSSync-D7obTsl3.js.map +1 -0
- package/dist/assets/dedicated.worker-DW6UqFo0.js.map +1 -0
- package/dist/{OPFSWorker.d.ts → core/BaseOPFS.d.ts} +53 -292
- package/dist/core/BaseOPFS.d.ts.map +1 -0
- package/dist/core/OPFSAsync.d.ts +55 -0
- package/dist/core/OPFSAsync.d.ts.map +1 -0
- package/dist/core/OPFSSync.d.ts +54 -0
- package/dist/core/OPFSSync.d.ts.map +1 -0
- package/dist/createOPFSAsync-DdhJYoC0.js +10 -0
- package/dist/createOPFSAsync-DdhJYoC0.js.map +1 -0
- package/dist/createOPFSAsync-DyWpvmL-.cjs +2 -0
- package/dist/createOPFSAsync-DyWpvmL-.cjs.map +1 -0
- package/dist/createOPFSDedicated-BwTc0NV5.js +2407 -0
- package/dist/createOPFSDedicated-BwTc0NV5.js.map +1 -0
- package/dist/createOPFSDedicated-CEeRexom.cjs +2357 -0
- package/dist/createOPFSDedicated-CEeRexom.cjs.map +1 -0
- package/dist/createOPFSShared-BSFWsoLe.js +23 -0
- package/dist/createOPFSShared-BSFWsoLe.js.map +1 -0
- package/dist/createOPFSShared-BoCft_BS.cjs +2 -0
- package/dist/createOPFSShared-BoCft_BS.cjs.map +1 -0
- package/dist/dedicated.worker.js +2355 -0
- package/dist/dedicated.worker.js.map +1 -0
- package/dist/{OPFSFacade.d.ts → facade/OPFSFacade.d.ts} +51 -25
- package/dist/facade/OPFSFacade.d.ts.map +1 -0
- package/dist/facade/createOPFSAsync.d.ts +16 -0
- package/dist/facade/createOPFSAsync.d.ts.map +1 -0
- package/dist/facade/createOPFSDedicated.d.ts +17 -0
- package/dist/facade/createOPFSDedicated.d.ts.map +1 -0
- package/dist/facade/createOPFSShared.d.ts +24 -0
- package/dist/facade/createOPFSShared.d.ts.map +1 -0
- package/dist/index.async.cjs +2 -0
- package/dist/index.async.cjs.map +1 -0
- package/dist/index.async.d.ts +10 -0
- package/dist/index.async.d.ts.map +1 -0
- package/dist/index.async.js +55 -0
- package/dist/index.async.js.map +1 -0
- package/dist/index.cjs +1 -2477
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -2828
- package/dist/index.js.map +1 -1
- package/dist/index.pure.cjs +1 -1
- package/dist/index.pure.cjs.map +1 -1
- package/dist/index.pure.d.ts +3 -1
- package/dist/index.pure.d.ts.map +1 -1
- package/dist/index.pure.js +50 -1053
- package/dist/index.pure.js.map +1 -1
- package/dist/index.sharedworker.cjs +2 -0
- package/dist/index.sharedworker.cjs.map +1 -0
- package/dist/index.sharedworker.d.ts +11 -0
- package/dist/index.sharedworker.d.ts.map +1 -0
- package/dist/index.sharedworker.js +55 -0
- package/dist/index.sharedworker.js.map +1 -0
- package/dist/index.sync.cjs +2 -0
- package/dist/index.sync.cjs.map +1 -0
- package/dist/index.sync.d.ts +11 -0
- package/dist/index.sync.d.ts.map +1 -0
- package/dist/index.sync.js +55 -0
- package/dist/index.sync.js.map +1 -0
- package/dist/shared.worker.js +2240 -0
- package/dist/shared.worker.js.map +1 -0
- package/dist/types.d.ts +42 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/worker/createDedicatedWorker.d.ts +30 -0
- package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
- package/dist/worker/createSharedWorker.d.ts +37 -0
- package/dist/worker/createSharedWorker.d.ts.map +1 -0
- package/dist/worker/dedicated.worker.d.ts +2 -0
- package/dist/worker/dedicated.worker.d.ts.map +1 -0
- package/dist/worker/shared.worker.d.ts +2 -0
- package/dist/worker/shared.worker.d.ts.map +1 -0
- package/docs/api/backend.md +36 -0
- package/docs/api/create.md +82 -0
- package/docs/api/facade.md +53 -0
- package/docs/choosing-a-mode.md +41 -0
- package/docs/file-descriptors.md +46 -605
- package/docs/guides/async.md +36 -0
- package/docs/guides/dedicated.md +76 -0
- package/docs/guides/hashing.md +34 -0
- package/docs/guides/pure.md +46 -0
- package/docs/guides/sharedworker.md +57 -0
- package/docs/guides/streaming.md +185 -0
- package/docs/guides/watching.md +35 -0
- package/docs/migration.md +41 -0
- package/docs/types.md +105 -153
- package/package.json +53 -4
- package/src/{OPFSWorker.ts → core/BaseOPFS.ts} +159 -629
- package/src/core/OPFSAsync.ts +248 -0
- package/src/core/OPFSSync.ts +460 -0
- package/src/{OPFSFacade.ts → facade/OPFSFacade.ts} +134 -58
- package/src/facade/createOPFSAsync.ts +22 -0
- package/src/facade/createOPFSDedicated.ts +23 -0
- package/src/facade/createOPFSShared.ts +30 -0
- package/src/index.async.ts +11 -0
- package/src/index.pure.ts +3 -2
- package/src/index.sharedworker.ts +12 -0
- package/src/index.sync.ts +12 -0
- package/src/index.ts +37 -8
- package/src/types.ts +45 -4
- package/src/worker/createDedicatedWorker.ts +74 -0
- package/src/worker/createSharedWorker.ts +67 -0
- package/src/worker/dedicated.worker.ts +13 -0
- package/src/worker/shared.worker.ts +17 -0
- package/dist/OPFSFacade.d.ts.map +0 -1
- package/dist/OPFSWorker.d.ts.map +0 -1
- package/dist/assets/worker.entry-DUlEoroc.js.map +0 -1
- package/dist/createOPFSWorker.d.ts +0 -17
- package/dist/createOPFSWorker.d.ts.map +0 -1
- package/dist/helpers-DNj8ZoMu.cjs +0 -4
- package/dist/helpers-DNj8ZoMu.cjs.map +0 -1
- package/dist/helpers-WY2jfbOT.js +0 -1427
- package/dist/helpers-WY2jfbOT.js.map +0 -1
- package/dist/worker.entry.d.ts +0 -2
- package/dist/worker.entry.d.ts.map +0 -1
- package/docs/api-reference.md +0 -815
- package/src/createOPFSWorker.ts +0 -57
- package/src/worker.entry.ts +0 -6
package/README.md
CHANGED
|
@@ -1,111 +1,33 @@
|
|
|
1
1
|
# OPFS Worker
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
6
|
+
## What is OPFS?
|
|
6
7
|
|
|
7
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
+
→ **File descriptors** — `open` / `read` / `write` at an offset, plus `ftruncate` and `fsync`. [Docs](docs/file-descriptors.md)
|
|
22
15
|
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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.
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
53
|
+
const files = await fs.readDir('/project');
|
|
54
|
+
const text = await fs.readFile('/project/readme.txt'); // 'Hello, OPFS!'
|
|
297
55
|
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
-
|
|
340
|
-
- [
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
110
|
+
Maintained with ❤️ by [@kachurun](https://github.com/kachurun)
|