opfs-worker 2.1.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 +40 -27
- package/dist/{BaseOPFS-Cylz6ijk.js → BaseOPFS-BusBkyJX.js} +436 -358
- package/dist/BaseOPFS-BusBkyJX.js.map +1 -0
- package/dist/{BaseOPFS-BPw9AIQ-.cjs → BaseOPFS-CYwrD7-q.cjs} +4 -4
- package/dist/BaseOPFS-CYwrD7-q.cjs.map +1 -0
- package/dist/{OPFSAsync-s2P7y3sw.cjs → OPFSAsync-B2dR-2VR.cjs} +2 -2
- package/dist/{OPFSAsync-s2P7y3sw.cjs.map → OPFSAsync-B2dR-2VR.cjs.map} +1 -1
- package/dist/{OPFSAsync-BKSAJJeh.js → OPFSAsync-CO44W23e.js} +2 -2
- package/dist/{OPFSAsync-BKSAJJeh.js.map → OPFSAsync-CO44W23e.js.map} +1 -1
- 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-C7KBepe_.cjs → OPFSSync-BPrNtvFS.cjs} +2 -2
- package/dist/{OPFSSync-C7KBepe_.cjs.map → OPFSSync-BPrNtvFS.cjs.map} +1 -1
- package/dist/{OPFSSync-CsSQkpk5.js → OPFSSync-D7obTsl3.js} +2 -2
- package/dist/{OPFSSync-CsSQkpk5.js.map → OPFSSync-D7obTsl3.js.map} +1 -1
- package/dist/assets/dedicated.worker-DW6UqFo0.js.map +1 -0
- package/dist/core/BaseOPFS.d.ts +38 -7
- package/dist/core/BaseOPFS.d.ts.map +1 -1
- package/dist/createOPFSAsync-DdhJYoC0.js +10 -0
- package/dist/{createOPFSAsync-Y4Mg6qte.js.map → createOPFSAsync-DdhJYoC0.js.map} +1 -1
- package/dist/createOPFSAsync-DyWpvmL-.cjs +2 -0
- package/dist/{createOPFSAsync-BwKDmHsy.cjs.map → createOPFSAsync-DyWpvmL-.cjs.map} +1 -1
- package/dist/{createOPFSDedicated-CsnV6MLP.js → createOPFSDedicated-BwTc0NV5.js} +289 -215
- package/dist/{createOPFSDedicated-CsnV6MLP.js.map → createOPFSDedicated-BwTc0NV5.js.map} +1 -1
- package/dist/{createOPFSDedicated-pKTRWLdz.cjs → createOPFSDedicated-CEeRexom.cjs} +286 -212
- package/dist/{createOPFSDedicated-pKTRWLdz.cjs.map → createOPFSDedicated-CEeRexom.cjs.map} +1 -1
- package/dist/{createOPFSShared-CGdd__PH.js → createOPFSShared-BSFWsoLe.js} +2 -2
- package/dist/{createOPFSShared-CGdd__PH.js.map → createOPFSShared-BSFWsoLe.js.map} +1 -1
- package/dist/{createOPFSShared-CskROt7U.cjs → createOPFSShared-BoCft_BS.cjs} +2 -2
- package/dist/{createOPFSShared-CskROt7U.cjs.map → createOPFSShared-BoCft_BS.cjs.map} +1 -1
- package/dist/dedicated.worker.js +282 -208
- package/dist/dedicated.worker.js.map +1 -1
- package/dist/facade/OPFSFacade.d.ts +21 -3
- package/dist/facade/OPFSFacade.d.ts.map +1 -1
- package/dist/index.async.cjs +1 -1
- package/dist/index.async.js +4 -4
- package/dist/index.cjs +1 -1
- package/dist/index.js +7 -7
- package/dist/index.pure.cjs +1 -1
- package/dist/index.pure.js +3 -3
- package/dist/index.sharedworker.cjs +1 -1
- package/dist/index.sharedworker.js +4 -4
- package/dist/index.sync.cjs +1 -1
- package/dist/index.sync.js +4 -4
- package/dist/shared.worker.js +430 -356
- package/dist/shared.worker.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/api/backend.md +3 -3
- package/docs/api/create.md +1 -1
- package/docs/api/facade.md +28 -27
- package/docs/choosing-a-mode.md +6 -6
- package/docs/file-descriptors.md +46 -604
- package/docs/guides/async.md +2 -2
- package/docs/guides/dedicated.md +5 -5
- package/docs/guides/hashing.md +2 -2
- package/docs/guides/pure.md +3 -3
- package/docs/guides/sharedworker.md +1 -1
- package/docs/guides/streaming.md +145 -11
- package/docs/guides/watching.md +3 -3
- package/docs/migration.md +3 -3
- package/docs/types.md +92 -154
- package/package.json +12 -2
- package/src/core/BaseOPFS.ts +148 -25
- package/src/facade/OPFSFacade.ts +51 -4
- package/src/types.ts +37 -0
- package/dist/BaseOPFS-BPw9AIQ-.cjs.map +0 -1
- package/dist/BaseOPFS-Cylz6ijk.js.map +0 -1
- package/dist/OPFSFacade-DePnNRgt.cjs +0 -2
- package/dist/OPFSFacade-DePnNRgt.cjs.map +0 -1
- package/dist/OPFSFacade-XohWlHuu.js +0 -281
- package/dist/OPFSFacade-XohWlHuu.js.map +0 -1
- package/dist/assets/dedicated.worker-nY3tK4zi.js.map +0 -1
- package/dist/createOPFSAsync-BwKDmHsy.cjs +0 -2
- package/dist/createOPFSAsync-Y4Mg6qte.js +0 -10
- package/docs/README.md +0 -30
package/docs/guides/async.md
CHANGED
|
@@ -16,7 +16,7 @@ await fs.appendText('/note.txt', '!');
|
|
|
16
16
|
const text = await fs.readFile('/note.txt', 'utf-8');
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Import from `opfs-worker/async`
|
|
19
|
+
Import from `opfs-worker/async` to keep the dedicated-worker code out of the bundle.
|
|
20
20
|
|
|
21
21
|
## Raw class
|
|
22
22
|
|
|
@@ -31,6 +31,6 @@ await fs.writeFile('/x.bin', new Uint8Array([1, 2, 3]));
|
|
|
31
31
|
|
|
32
32
|
- **Writes** need `createWritable()` — Chrome, Firefox, Safari **26+**. Reads work anywhere OPFS does.
|
|
33
33
|
- **No file descriptors** — `open` / `read` / `write` / … throw `ENOTSUP`.
|
|
34
|
-
- Each write goes through a swap file and commits on close — fine for documents,
|
|
34
|
+
- Each write goes through a swap file and commits on close — fine for documents, slow for many tiny random writes.
|
|
35
35
|
|
|
36
36
|
Streaming still works: [streaming](./streaming.md). For one fs across tabs: [SharedWorker](./sharedworker.md).
|
package/docs/guides/dedicated.md
CHANGED
|
@@ -22,7 +22,7 @@ The worker is inlined by default, so you don’t need a special bundler worker s
|
|
|
22
22
|
|
|
23
23
|
## Raw backend
|
|
24
24
|
|
|
25
|
-
Bytes in, bytes out — no encoding helpers. Same facade,
|
|
25
|
+
Bytes in, bytes out — no encoding helpers. Same facade, via `backend`:
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
28
|
const fs = createOPFSDedicated({ root: '/my-app' });
|
|
@@ -30,7 +30,7 @@ const fs = createOPFSDedicated({ root: '/my-app' });
|
|
|
30
30
|
await fs.backend.writeFile('/config.json', new TextEncoder().encode('{}'));
|
|
31
31
|
const bytes = await fs.backend.readFile('/config.json');
|
|
32
32
|
|
|
33
|
-
// Browser Worker
|
|
33
|
+
// Browser Worker:
|
|
34
34
|
fs.worker; // Worker
|
|
35
35
|
|
|
36
36
|
fs.dispose(); // dispose + terminate
|
|
@@ -68,9 +68,9 @@ const fs = new OPFSFacade({
|
|
|
68
68
|
});
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
##
|
|
71
|
+
## Notes
|
|
72
72
|
|
|
73
73
|
- Each call gets its own worker. Tabs don’t share state — use [SharedWorker](./sharedworker.md) for that.
|
|
74
|
-
- Sync access handles
|
|
74
|
+
- Sync access handles only work in a dedicated worker.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Also: [file descriptors](../file-descriptors.md), [streaming](./streaming.md), [create helpers](../api/create.md).
|
package/docs/guides/hashing.md
CHANGED
|
@@ -4,7 +4,7 @@ Set once via `hashAlgorithm` / `maxFileSize` in options or `setOptions()`. Shows
|
|
|
4
4
|
|
|
5
5
|
| Value | What you get |
|
|
6
6
|
| ----- | ------------ |
|
|
7
|
-
| `'etag'` (default) |
|
|
7
|
+
| `'etag'` (default) | Tag from mtime + size — no content read |
|
|
8
8
|
| `'SHA-1'` … `'SHA-512'` | Real content hash (Web Crypto) |
|
|
9
9
|
| `null` / `false` | No `hash` field |
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ import { createOPFS } from 'opfs-worker';
|
|
|
16
16
|
const fs = createOPFS();
|
|
17
17
|
|
|
18
18
|
await fs.writeFile('/data.txt', 'Hello');
|
|
19
|
-
console.log((await fs.stat('/data.txt')).hash); //
|
|
19
|
+
console.log((await fs.stat('/data.txt')).hash); // e.g. "m1abc-c"
|
|
20
20
|
|
|
21
21
|
await fs.setOptions({ hashAlgorithm: 'SHA-256' });
|
|
22
22
|
console.log((await fs.stat('/data.txt')).hash); // hex digest
|
package/docs/guides/pure.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pure classes
|
|
2
2
|
|
|
3
|
-
Use the classes
|
|
3
|
+
Use the classes inside a worker (or thread) you already own. This package does not spawn a nested worker or call Comlink `expose()` — main-thread wiring is yours.
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
6
|
import { OPFSSync, OPFSAsync } from 'opfs-worker/pure';
|
|
@@ -26,7 +26,7 @@ const fs = new OPFSSync({
|
|
|
26
26
|
await fs.writeFile('/config.json', new TextEncoder().encode('{}'));
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
`/pure`
|
|
29
|
+
`/pure` does not take over your worker’s message port.
|
|
30
30
|
|
|
31
31
|
## OPFSAsync in a SharedWorker you write
|
|
32
32
|
|
|
@@ -41,6 +41,6 @@ onconnect = (e) => {
|
|
|
41
41
|
};
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Or
|
|
44
|
+
Or use the [SharedWorker guide](./sharedworker.md).
|
|
45
45
|
|
|
46
46
|
Also: [backend API](../api/backend.md), [dedicated](./dedicated.md), [async](./async.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SharedWorker
|
|
2
2
|
|
|
3
|
-
One `OPFSAsync` for every tab. Locks live in that
|
|
3
|
+
One `OPFSAsync` for every tab. Locks live in that instance, so writes across tabs don’t collide. Watch events use `BroadcastChannel` as usual.
|
|
4
4
|
|
|
5
5
|
Same limits as [async](./async.md): Safari 26+ for writes, no FDs.
|
|
6
6
|
|
package/docs/guides/streaming.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Streaming
|
|
2
2
|
|
|
3
|
-
Write
|
|
3
|
+
Write large files without buffering the whole payload.
|
|
4
4
|
|
|
5
|
-
|
|
|
6
|
-
|
|
|
7
|
-
| Facade
|
|
8
|
-
| Backend / raw | `writeStream(path, stream, onProgress?)` |
|
|
5
|
+
| | Method |
|
|
6
|
+
| ------------- | ------------------------------------------------------------------------------ |
|
|
7
|
+
| Facade | `importStream(path, source, options?)` · `importFiles(entries, options?)` |
|
|
8
|
+
| Backend / raw | `writeStream(path, stream, onProgress?)` · `importFiles(entries, onProgress?)` |
|
|
9
9
|
|
|
10
10
|
Both create or overwrite the file, return bytes written, and fire a watch event when done.
|
|
11
11
|
|
|
@@ -27,7 +27,120 @@ const res = await fetch('/large.bin');
|
|
|
27
27
|
await fs.importStream('/cache/large.bin', res.body!);
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
With a worker backend the facade transfers the stream and proxies `onProgress
|
|
30
|
+
With a worker backend the facade transfers the stream and proxies `onProgress`.
|
|
31
|
+
|
|
32
|
+
## `importFiles` (bulk)
|
|
33
|
+
|
|
34
|
+
Same streaming path for many entries — strings, bytes, Blobs, or Files.
|
|
35
|
+
|
|
36
|
+
First argument is any iterable of `[path, data]` pairs: an array of tuples, a `Map`, etc.
|
|
37
|
+
|
|
38
|
+
`onProgress` receives an object (not a bare number):
|
|
39
|
+
|
|
40
|
+
| Field | Meaning |
|
|
41
|
+
| ---------------------------------- | ----------------------------------------- |
|
|
42
|
+
| `path` | File currently being written |
|
|
43
|
+
| `index` / `count` | 0-based index and total number of entries |
|
|
44
|
+
| `bytesWritten` / `bytesTotal` | Progress within the current file |
|
|
45
|
+
| `totalBytesWritten` / `totalBytes` | Progress across the whole import |
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
await fs.importFiles(
|
|
49
|
+
[
|
|
50
|
+
['/a.txt', 'hello'],
|
|
51
|
+
['/upload.bin', fileFromInput],
|
|
52
|
+
],
|
|
53
|
+
{
|
|
54
|
+
onProgress: ({ path, bytesWritten, bytesTotal, totalBytesWritten, totalBytes }) => {
|
|
55
|
+
console.log(`${path}: ${bytesWritten}/${bytesTotal} (all ${totalBytesWritten}/${totalBytes})`);
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
// → { paths: ['/a.txt', '/upload.bin'], count: 2, bytesWritten: … }
|
|
60
|
+
|
|
61
|
+
// Map works too
|
|
62
|
+
await fs.importFiles(new Map([
|
|
63
|
+
['/a.txt', 'hello'],
|
|
64
|
+
['/b.txt', 'world'],
|
|
65
|
+
]));
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`createIndex` is kept as a deprecated alias.
|
|
69
|
+
|
|
70
|
+
## Uploading files and folders from disk
|
|
71
|
+
|
|
72
|
+
Getting `File` objects out of the browser is the DOM's job; once you have them, `importFiles` does the rest.
|
|
73
|
+
|
|
74
|
+
### File / folder picker
|
|
75
|
+
|
|
76
|
+
A plain `<input type="file">` gives you files. Add `webkitdirectory` and the picker selects a whole folder — each file then carries its relative path in `webkitRelativePath`:
|
|
77
|
+
|
|
78
|
+
```html
|
|
79
|
+
<input type="file" id="files" multiple />
|
|
80
|
+
<input type="file" id="folder" webkitdirectory multiple />
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
input.addEventListener('change', async () => {
|
|
85
|
+
const entries = [...input.files].map((file) => [
|
|
86
|
+
`/uploads/${file.webkitRelativePath || file.name}`,
|
|
87
|
+
file,
|
|
88
|
+
] as [string, File]);
|
|
89
|
+
|
|
90
|
+
await fs.importFiles(entries, {
|
|
91
|
+
onProgress: ({ path, totalBytesWritten, totalBytes }) => {
|
|
92
|
+
console.log(`${path}: ${totalBytesWritten}/${totalBytes}`);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Drag and drop
|
|
99
|
+
|
|
100
|
+
Dropped folders are only reachable through the non-standard (but universally supported) `webkitGetAsEntry` API, which has to be walked recursively:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
async function collect(entry: FileSystemEntry, prefix = ''): Promise<[string, File][]> {
|
|
104
|
+
if (entry.isFile) {
|
|
105
|
+
const file = await new Promise<File>((res, rej) => (entry as FileSystemFileEntry).file(res, rej));
|
|
106
|
+
|
|
107
|
+
return [[`${prefix}${entry.name}`, file]];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const reader = (entry as FileSystemDirectoryEntry).createReader();
|
|
111
|
+
const children: FileSystemEntry[] = [];
|
|
112
|
+
|
|
113
|
+
// readEntries returns partial batches — keep calling until it's empty
|
|
114
|
+
for (;;) {
|
|
115
|
+
const batch = await new Promise<FileSystemEntry[]>((res, rej) => reader.readEntries(res, rej));
|
|
116
|
+
|
|
117
|
+
if (batch.length === 0) break;
|
|
118
|
+
children.push(...batch);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const nested = await Promise.all(children.map((c) => collect(c, `${prefix}${entry.name}/`)));
|
|
122
|
+
|
|
123
|
+
return nested.flat();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
dropZone.addEventListener('drop', async (e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
|
|
129
|
+
const entries = [...e.dataTransfer!.items]
|
|
130
|
+
.map((item) => item.webkitGetAsEntry())
|
|
131
|
+
.filter((entry): entry is FileSystemEntry => entry !== null);
|
|
132
|
+
|
|
133
|
+
const files = (await Promise.all(entries.map((entry) => collect(entry, '/uploads/')))).flat();
|
|
134
|
+
|
|
135
|
+
await fs.importFiles(files, {
|
|
136
|
+
onProgress: ({ path, index, count, bytesWritten, bytesTotal }) => {
|
|
137
|
+
console.log(`[${index + 1}/${count}] ${path}: ${bytesWritten}/${bytesTotal}`);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The [demo](https://kachurun.github.io/opfs-worker/) implements exactly this flow — see `demo/FileBrowser.tsx`.
|
|
31
144
|
|
|
32
145
|
## `writeStream` (raw)
|
|
33
146
|
|
|
@@ -38,13 +151,34 @@ const fs = createOPFS();
|
|
|
38
151
|
await fs.backend.writeStream('/data.bin', someBlob.stream(), (n) => console.log(n));
|
|
39
152
|
```
|
|
40
153
|
|
|
154
|
+
## Reading back without buffering: `readBlob`
|
|
155
|
+
|
|
156
|
+
`readFile` copies the whole file into a `Uint8Array`, which is wasteful for media. `readBlob` returns the disk-backed `Blob` that OPFS already has, so nothing is read until something asks for bytes:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const blob = await fs.readBlob('/media/clip.mp4');
|
|
160
|
+
|
|
161
|
+
video.src = URL.createObjectURL(blob);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The browser then seeks and buffers only the ranges it plays — a 2 GB video costs no memory up front. The same `Blob` works for `<img>`, `<audio>`, `<iframe>` (PDF), `fetch()` bodies, and `showSaveFilePicker` downloads.
|
|
165
|
+
|
|
166
|
+
Reading a small header stays cheap too, since `slice()` does not touch the rest of the file:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const magic = new Uint8Array(await blob.slice(0, 4).arrayBuffer());
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
When the file system runs in a worker, the `Blob` crosses the boundary by reference — structured clone does not copy its contents.
|
|
173
|
+
|
|
41
174
|
## When to use what
|
|
42
175
|
|
|
43
|
-
| API
|
|
44
|
-
|
|
|
45
|
-
| `writeFile`
|
|
46
|
-
| `importStream` / `writeStream` |
|
|
47
|
-
| `
|
|
176
|
+
| API | Good for |
|
|
177
|
+
| ------------------------------ | ------------------------------------------------------------ |
|
|
178
|
+
| `writeFile` | Data already in memory |
|
|
179
|
+
| `importStream` / `writeStream` | One large `File` / `Blob` / network body |
|
|
180
|
+
| `importFiles` | Many files / folder uploads (streamed, with total progress) |
|
|
181
|
+
| `readBlob` | Media previews, downloads, anything you can hand a `Blob` to |
|
|
48
182
|
|
|
49
183
|
Dedicated path chunks through FDs; async path uses `createWritable()` (Safari 26+ for writes).
|
|
50
184
|
|
package/docs/guides/watching.md
CHANGED
|
@@ -28,8 +28,8 @@ stop(); // or fs.unwatch('/')
|
|
|
28
28
|
|
|
29
29
|
`watch` returns an unsubscribe function. Options: [`WatchOptions`](../types.md#watchoptions). Payload: [`WatchEvent`](../types.md#watchevent).
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## Notes
|
|
32
32
|
|
|
33
|
-
-
|
|
33
|
+
- Pass a **channel name** (string) into a worker — a `BroadcastChannel` instance can’t cross the wire. On the main thread the facade accepts either.
|
|
34
34
|
- SharedWorker: one backend + one channel → every listening tab sees the same events.
|
|
35
|
-
- Whether events include `hash` depends on [hashing](./hashing.md)
|
|
35
|
+
- Whether events include `hash` depends on [hashing](./hashing.md).
|
package/docs/migration.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| `OPFSWorker` | `OPFSSync` |
|
|
10
10
|
| `opfs-worker/raw` | `opfs-worker/pure` |
|
|
11
11
|
| `opfs-worker/raw?worker` | `createOPFSDedicated()` + `fs.backend` / `fs.worker`, or `dedicated.worker.js` |
|
|
12
|
-
| `fs.sync(entries)` | `fs.
|
|
12
|
+
| `fs.sync(entries)` | `fs.importFiles(entries)` (`createIndex` alias kept, deprecated) |
|
|
13
13
|
|
|
14
14
|
```diff
|
|
15
15
|
- import { createWorker, OPFSFileSystem } from 'opfs-worker';
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
- SharedWorker: `createOPFSShared` / `opfs-worker/sharedworker`
|
|
30
30
|
- Prebuilt scripts: `dedicated.worker.js`, `shared.worker.js`
|
|
31
31
|
- Streaming: `importStream` / `writeStream`
|
|
32
|
-
-
|
|
33
|
-
-
|
|
32
|
+
- `fs.backend` / `fs.worker` on every facade
|
|
33
|
+
- `createOPFSDedicated`, `createOPFSAsync`, `createOPFSShared`
|
|
34
34
|
|
|
35
35
|
## Behaviour
|
|
36
36
|
|
package/docs/types.md
CHANGED
|
@@ -1,38 +1,22 @@
|
|
|
1
1
|
# Types
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Core Types
|
|
6
|
-
|
|
7
|
-
### `FileStat`
|
|
8
|
-
|
|
9
|
-
File or directory statistics.
|
|
3
|
+
## `FileStat`
|
|
10
4
|
|
|
11
5
|
```typescript
|
|
12
6
|
interface FileStat {
|
|
13
7
|
kind: 'file' | 'directory';
|
|
14
8
|
size: number;
|
|
15
|
-
mtime: string; // ISO
|
|
16
|
-
ctime: string; // ISO
|
|
9
|
+
mtime: string; // ISO
|
|
10
|
+
ctime: string; // ISO
|
|
17
11
|
isFile: boolean;
|
|
18
12
|
isDirectory: boolean;
|
|
19
|
-
hash?: string; //
|
|
13
|
+
hash?: string; // files only, when hashing is enabled
|
|
20
14
|
}
|
|
21
15
|
```
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- `kind`: Type of the file system entry
|
|
26
|
-
- `size`: Size in bytes (0 for directories)
|
|
27
|
-
- `mtime`: Last modification time as ISO string
|
|
28
|
-
- `ctime`: Creation time as ISO string
|
|
29
|
-
- `isFile`: True if this is a file
|
|
30
|
-
- `isDirectory`: True if this is a directory
|
|
31
|
-
- `hash`: Optional hash of file content (only present for files when hashing is enabled)
|
|
17
|
+
`size` is `0` for directories.
|
|
32
18
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Directory entry information.
|
|
19
|
+
## `DirentData`
|
|
36
20
|
|
|
37
21
|
```typescript
|
|
38
22
|
interface DirentData {
|
|
@@ -43,46 +27,61 @@ interface DirentData {
|
|
|
43
27
|
}
|
|
44
28
|
```
|
|
45
29
|
|
|
46
|
-
|
|
30
|
+
## `WatchOptions`
|
|
47
31
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
32
|
+
```typescript
|
|
33
|
+
interface WatchOptions {
|
|
34
|
+
recursive?: boolean; // default: true
|
|
35
|
+
include?: string | string[]; // minimatch, default: ['**']
|
|
36
|
+
exclude?: string | string[]; // minimatch, default: []
|
|
37
|
+
}
|
|
38
|
+
```
|
|
52
39
|
|
|
53
|
-
|
|
40
|
+
## `ImportFilesProgress`
|
|
54
41
|
|
|
55
|
-
|
|
42
|
+
Fired by `importFiles` / `onProgress` for each chunk written:
|
|
56
43
|
|
|
57
44
|
```typescript
|
|
58
|
-
interface
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
interface ImportFilesProgress {
|
|
46
|
+
path: string; // file currently being written
|
|
47
|
+
index: number; // 0-based
|
|
48
|
+
count: number; // total entries
|
|
49
|
+
bytesWritten: number; // within current file
|
|
50
|
+
bytesTotal: number; // size of current file
|
|
51
|
+
totalBytesWritten: number;
|
|
52
|
+
totalBytes: number; // sum of all entry sizes
|
|
62
53
|
}
|
|
63
54
|
```
|
|
64
55
|
|
|
65
|
-
|
|
56
|
+
## `ImportFilesResult`
|
|
57
|
+
|
|
58
|
+
Returned when `importFiles` finishes:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
interface ImportFilesResult {
|
|
62
|
+
paths: string[]; // written paths, in order
|
|
63
|
+
count: number; // paths.length
|
|
64
|
+
bytesWritten: number; // total bytes across all files
|
|
65
|
+
}
|
|
66
|
+
```
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
- `include`: Glob patterns to include in watching (default: all files)
|
|
69
|
-
- `exclude`: Glob patterns to exclude from watching (default: none)
|
|
68
|
+
Entries accept `Iterable<[path, string | Uint8Array | Blob]>` or `Map<string, …>` — see [streaming](./guides/streaming.md).
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
## `OPFSApi`
|
|
72
71
|
|
|
73
|
-
Promise-based
|
|
72
|
+
Promise-based surface shared by all backends — a Comlink proxy to `OPFSSync`, or an in-process `OPFSAsync`. What `OPFSFacade` talks to (see `OPFSBackend` in [Create helpers](./api/create.md)).
|
|
74
73
|
|
|
75
74
|
```typescript
|
|
76
75
|
type OPFSApi = { [K in keyof OPFSSync]: OPFSSync[K] };
|
|
77
76
|
```
|
|
78
77
|
|
|
79
|
-
|
|
78
|
+
## `PathLike`
|
|
80
79
|
|
|
81
80
|
```typescript
|
|
82
81
|
type PathLike = string | URL;
|
|
83
82
|
```
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
## `FileOpenOptions`
|
|
86
85
|
|
|
87
86
|
```typescript
|
|
88
87
|
interface FileOpenOptions {
|
|
@@ -92,40 +91,26 @@ interface FileOpenOptions {
|
|
|
92
91
|
}
|
|
93
92
|
```
|
|
94
93
|
|
|
95
|
-
##
|
|
96
|
-
|
|
97
|
-
### `OPFSOptions`
|
|
94
|
+
## `OPFSOptions`
|
|
98
95
|
|
|
99
|
-
|
|
96
|
+
Table: [Create helpers → Options](./api/create.md#options).
|
|
100
97
|
|
|
101
98
|
```typescript
|
|
102
99
|
interface OPFSOptions {
|
|
103
|
-
/** Root path
|
|
100
|
+
/** Root path (default: '/') */
|
|
104
101
|
root?: string;
|
|
105
|
-
/**
|
|
102
|
+
/** Event namespace (default: 'opfs-worker:${root}') */
|
|
106
103
|
namespace?: string;
|
|
107
|
-
/** Hash algorithm
|
|
104
|
+
/** Hash algorithm, or false/null to disable (default: 'etag') */
|
|
108
105
|
hashAlgorithm?: null | false | 'etag' | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';
|
|
109
|
-
/**
|
|
106
|
+
/** Max bytes for SHA-* hashing (default: 50MB); ignored by 'etag' */
|
|
110
107
|
maxFileSize?: number;
|
|
111
|
-
/**
|
|
108
|
+
/** Broadcast channel name or instance (default: 'opfs-worker') */
|
|
112
109
|
broadcastChannel?: string | BroadcastChannel | null;
|
|
113
110
|
}
|
|
114
111
|
```
|
|
115
112
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
- `root`: Root path for the file system
|
|
119
|
-
- `namespace`: Namespace for events and isolation
|
|
120
|
-
- `hashAlgorithm`: `'etag'` (default, mtime+size), `'SHA-*'`, or `null`/`false` to disable
|
|
121
|
-
- `maxFileSize`: Max bytes for SHA-* content hashing (etag ignores this)
|
|
122
|
-
- `broadcastChannel`: Custom broadcast channel name or instance
|
|
123
|
-
|
|
124
|
-
## Event Types
|
|
125
|
-
|
|
126
|
-
### `WatchEventType`
|
|
127
|
-
|
|
128
|
-
Enumeration of file system change event types.
|
|
113
|
+
## `WatchEventType`
|
|
129
114
|
|
|
130
115
|
```typescript
|
|
131
116
|
enum WatchEventType {
|
|
@@ -135,15 +120,7 @@ enum WatchEventType {
|
|
|
135
120
|
}
|
|
136
121
|
```
|
|
137
122
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- `Added`: File or directory was created
|
|
141
|
-
- `Changed`: File or directory was modified
|
|
142
|
-
- `Removed`: File or directory was deleted
|
|
143
|
-
|
|
144
|
-
### `WatchEvent`
|
|
145
|
-
|
|
146
|
-
File system change event sent via BroadcastChannel.
|
|
123
|
+
## `WatchEvent`
|
|
147
124
|
|
|
148
125
|
```typescript
|
|
149
126
|
interface WatchEvent {
|
|
@@ -156,91 +133,52 @@ interface WatchEvent {
|
|
|
156
133
|
}
|
|
157
134
|
```
|
|
158
135
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
- `namespace`: Event namespace for isolation
|
|
162
|
-
- `path`: Path of the changed file/directory
|
|
163
|
-
- `type`: Type of change (added, changed, removed)
|
|
164
|
-
- `isDirectory`: Whether the changed item is a directory
|
|
165
|
-
- `timestamp`: ISO timestamp of the change
|
|
166
|
-
- `hash`: Optional hash of the file content (if hashing is enabled)
|
|
167
|
-
|
|
168
|
-
## Utility Types
|
|
169
|
-
|
|
170
|
-
### `Kind`
|
|
171
|
-
|
|
172
|
-
File system entry type.
|
|
136
|
+
## Utility types
|
|
173
137
|
|
|
174
138
|
```typescript
|
|
175
139
|
type Kind = 'file' | 'directory';
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### `Encoding`
|
|
179
|
-
|
|
180
|
-
Supported text encodings.
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
140
|
type Encoding = 'utf-8' | 'utf-16le' | 'ascii' | 'latin1' | 'base64' | 'hex' | 'binary';
|
|
184
141
|
```
|
|
185
142
|
|
|
186
|
-
##
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
The library uses standard POSIX error codes with numeric errno mapping:
|
|
230
|
-
|
|
231
|
-
- `ENOENT` (-2) - No such file or directory
|
|
232
|
-
- `EISDIR` (-21) - Is a directory
|
|
233
|
-
- `ENOTDIR` (-20) - Not a directory
|
|
234
|
-
- `EACCES` (-13) - Permission denied
|
|
235
|
-
- `EEXIST` (-17) - File exists
|
|
236
|
-
- `ENOTEMPTY` (-39) - Directory not empty
|
|
237
|
-
- `EINVAL` (-22) - Invalid argument
|
|
238
|
-
- `EIO` (-5) - I/O error
|
|
239
|
-
- `ENOSPC` (-28) - No space left on device
|
|
240
|
-
- `EBUSY` (-16) - Device or resource busy
|
|
241
|
-
- `EINTR` (-4) - Interrupted system call
|
|
242
|
-
- `ENOTSUP` (-95) - Operation not supported
|
|
243
|
-
- `ERANGE` (-34) - Result too large
|
|
244
|
-
- `EBADF` (-9) - Bad file descriptor
|
|
245
|
-
|
|
246
|
-
Each error type extends the base `OPFSError` class and provides specific error codes and context information for better debugging.
|
|
143
|
+
## Errors
|
|
144
|
+
|
|
145
|
+
Node.js SystemError-compatible. Base class: `OPFSError` (`errno`, `syscall?`, `path?`, `cause?`).
|
|
146
|
+
|
|
147
|
+
| Class | |
|
|
148
|
+
| ---------------------------- | ------------------------------------------------------------------- |
|
|
149
|
+
| `OPFSNotSupportedError` | OPFS missing in the browser |
|
|
150
|
+
| `PathError` | Bad path / traversal |
|
|
151
|
+
| `ExistenceError` | Missing entry — `new ExistenceError(msg, 'ENOENT', path)` |
|
|
152
|
+
| `PermissionError` | Access denied |
|
|
153
|
+
| `StorageError` | Quota / storage full |
|
|
154
|
+
| `TimeoutError` | Timed out |
|
|
155
|
+
| `FileBusyError` | Locked / busy |
|
|
156
|
+
| `FileTypeError` | File vs directory mismatch — `new FileTypeError('directory', path)` |
|
|
157
|
+
| `ValidationError` | Bad args — `new ValidationError(msg, 'EINVAL', path)` |
|
|
158
|
+
| `OperationAbortedError` | Aborted |
|
|
159
|
+
| `IOError` | I/O failure |
|
|
160
|
+
| `OperationNotSupportedError` | Not supported (e.g. FDs on async) |
|
|
161
|
+
| `DirectoryOperationError` | Dir op failed — `new DirectoryOperationError('RM_FAILED', path)` |
|
|
162
|
+
| `InitializationFailedError` | Init failed |
|
|
163
|
+
| `FileSystemOperationError` | Generic FS failure |
|
|
164
|
+
| `PathResolutionFailedError` | Path resolve failed |
|
|
165
|
+
| `AlreadyExistsError` | Already exists |
|
|
166
|
+
|
|
167
|
+
### errno
|
|
168
|
+
|
|
169
|
+
| Code | errno | |
|
|
170
|
+
| ----------- | ----- | ------------------------- |
|
|
171
|
+
| `ENOENT` | -2 | No such file or directory |
|
|
172
|
+
| `EISDIR` | -21 | Is a directory |
|
|
173
|
+
| `ENOTDIR` | -20 | Not a directory |
|
|
174
|
+
| `EACCES` | -13 | Permission denied |
|
|
175
|
+
| `EEXIST` | -17 | File exists |
|
|
176
|
+
| `ENOTEMPTY` | -39 | Directory not empty |
|
|
177
|
+
| `EINVAL` | -22 | Invalid argument |
|
|
178
|
+
| `EIO` | -5 | I/O error |
|
|
179
|
+
| `ENOSPC` | -28 | No space left |
|
|
180
|
+
| `EBUSY` | -16 | Busy |
|
|
181
|
+
| `EINTR` | -4 | Interrupted |
|
|
182
|
+
| `ENOTSUP` | -95 | Not supported |
|
|
183
|
+
| `ERANGE` | -34 | Result too large |
|
|
184
|
+
| `EBADF` | -9 | Bad file descriptor |
|