opfs-worker 1.3.2 → 2.1.0

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 (149) hide show
  1. package/README.md +64 -458
  2. package/dist/BaseOPFS-BPw9AIQ-.cjs +4 -0
  3. package/dist/BaseOPFS-BPw9AIQ-.cjs.map +1 -0
  4. package/dist/BaseOPFS-Cylz6ijk.js +2036 -0
  5. package/dist/BaseOPFS-Cylz6ijk.js.map +1 -0
  6. package/dist/OPFSAsync-BKSAJJeh.js +124 -0
  7. package/dist/OPFSAsync-BKSAJJeh.js.map +1 -0
  8. package/dist/OPFSAsync-s2P7y3sw.cjs +2 -0
  9. package/dist/OPFSAsync-s2P7y3sw.cjs.map +1 -0
  10. package/dist/OPFSFacade-DePnNRgt.cjs +2 -0
  11. package/dist/OPFSFacade-DePnNRgt.cjs.map +1 -0
  12. package/dist/OPFSFacade-XohWlHuu.js +281 -0
  13. package/dist/OPFSFacade-XohWlHuu.js.map +1 -0
  14. package/dist/OPFSSync-C7KBepe_.cjs +2 -0
  15. package/dist/OPFSSync-C7KBepe_.cjs.map +1 -0
  16. package/dist/OPFSSync-CsSQkpk5.js +208 -0
  17. package/dist/OPFSSync-CsSQkpk5.js.map +1 -0
  18. package/dist/assets/dedicated.worker-nY3tK4zi.js.map +1 -0
  19. package/dist/{worker.d.ts → core/BaseOPFS.d.ts} +34 -300
  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-BwKDmHsy.cjs +2 -0
  26. package/dist/createOPFSAsync-BwKDmHsy.cjs.map +1 -0
  27. package/dist/createOPFSAsync-Y4Mg6qte.js +10 -0
  28. package/dist/createOPFSAsync-Y4Mg6qte.js.map +1 -0
  29. package/dist/createOPFSDedicated-CsnV6MLP.js +2333 -0
  30. package/dist/createOPFSDedicated-CsnV6MLP.js.map +1 -0
  31. package/dist/createOPFSDedicated-pKTRWLdz.cjs +2283 -0
  32. package/dist/createOPFSDedicated-pKTRWLdz.cjs.map +1 -0
  33. package/dist/createOPFSShared-CGdd__PH.js +23 -0
  34. package/dist/createOPFSShared-CGdd__PH.js.map +1 -0
  35. package/dist/createOPFSShared-CskROt7U.cjs +2 -0
  36. package/dist/createOPFSShared-CskROt7U.cjs.map +1 -0
  37. package/dist/dedicated.worker.js +2281 -0
  38. package/dist/dedicated.worker.js.map +1 -0
  39. package/dist/{facade.d.ts → facade/OPFSFacade.d.ts} +35 -10
  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 -2466
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.ts +15 -7
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +61 -2799
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.pure.cjs +2 -0
  60. package/dist/index.pure.cjs.map +1 -0
  61. package/dist/index.pure.d.ts +8 -0
  62. package/dist/index.pure.d.ts.map +1 -0
  63. package/dist/index.pure.js +53 -0
  64. package/dist/index.pure.js.map +1 -0
  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 +2166 -0
  78. package/dist/shared.worker.js.map +1 -0
  79. package/dist/types.d.ts +9 -4
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/utils/helpers.d.ts +11 -1
  82. package/dist/utils/helpers.d.ts.map +1 -1
  83. package/dist/worker/createDedicatedWorker.d.ts +30 -0
  84. package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
  85. package/dist/worker/createSharedWorker.d.ts +37 -0
  86. package/dist/worker/createSharedWorker.d.ts.map +1 -0
  87. package/dist/worker/dedicated.worker.d.ts +2 -0
  88. package/dist/worker/dedicated.worker.d.ts.map +1 -0
  89. package/dist/worker/shared.worker.d.ts +2 -0
  90. package/dist/worker/shared.worker.d.ts.map +1 -0
  91. package/docs/README.md +30 -0
  92. package/docs/api/backend.md +36 -0
  93. package/docs/api/create.md +82 -0
  94. package/docs/api/facade.md +52 -0
  95. package/docs/choosing-a-mode.md +41 -0
  96. package/docs/file-descriptors.md +695 -0
  97. package/docs/guides/async.md +36 -0
  98. package/docs/guides/dedicated.md +76 -0
  99. package/docs/guides/hashing.md +34 -0
  100. package/docs/guides/pure.md +46 -0
  101. package/docs/guides/sharedworker.md +57 -0
  102. package/docs/guides/streaming.md +51 -0
  103. package/docs/guides/watching.md +35 -0
  104. package/docs/migration.md +41 -0
  105. package/docs/types.md +246 -0
  106. package/package.json +55 -10
  107. package/src/core/BaseOPFS.ts +951 -0
  108. package/src/core/OPFSAsync.ts +248 -0
  109. package/src/core/OPFSSync.ts +460 -0
  110. package/src/facade/OPFSFacade.ts +489 -0
  111. package/src/facade/createOPFSAsync.ts +22 -0
  112. package/src/facade/createOPFSDedicated.ts +23 -0
  113. package/src/facade/createOPFSShared.ts +30 -0
  114. package/src/index.async.ts +11 -0
  115. package/src/index.pure.ts +8 -0
  116. package/src/index.sharedworker.ts +12 -0
  117. package/src/index.sync.ts +12 -0
  118. package/src/index.ts +44 -0
  119. package/src/types.ts +103 -0
  120. package/src/utils/encoder.ts +205 -0
  121. package/src/utils/errors.ts +297 -0
  122. package/src/utils/helpers.ts +465 -0
  123. package/src/worker/createDedicatedWorker.ts +74 -0
  124. package/src/worker/createSharedWorker.ts +67 -0
  125. package/src/worker/dedicated.worker.ts +13 -0
  126. package/src/worker/shared.worker.ts +17 -0
  127. package/dist/assets/worker-1Wh1cr7P.js.map +0 -1
  128. package/dist/assets/worker-BeJaVyBV.js.map +0 -1
  129. package/dist/assets/worker-DYYLzR1Y.js.map +0 -1
  130. package/dist/facade.d.ts.map +0 -1
  131. package/dist/helpers-BuGfPAg0.js +0 -1439
  132. package/dist/helpers-BuGfPAg0.js.map +0 -1
  133. package/dist/helpers-CF7A2WQG.cjs +0 -4
  134. package/dist/helpers-CF7A2WQG.cjs.map +0 -1
  135. package/dist/helpers-CIiblZ8d.cjs +0 -4
  136. package/dist/helpers-CIiblZ8d.cjs.map +0 -1
  137. package/dist/helpers-CKOebsbw.js +0 -1423
  138. package/dist/helpers-CKOebsbw.js.map +0 -1
  139. package/dist/helpers-Ca2c767K.js +0 -1427
  140. package/dist/helpers-Ca2c767K.js.map +0 -1
  141. package/dist/helpers-Dwc92hv9.cjs +0 -4
  142. package/dist/helpers-Dwc92hv9.cjs.map +0 -1
  143. package/dist/raw.cjs +0 -2
  144. package/dist/raw.cjs.map +0 -1
  145. package/dist/raw.d.ts +0 -13
  146. package/dist/raw.d.ts.map +0 -1
  147. package/dist/raw.js +0 -1001
  148. package/dist/raw.js.map +0 -1
  149. package/dist/worker.d.ts.map +0 -1
@@ -0,0 +1,36 @@
1
+ # Async (no worker)
2
+
3
+ `OPFSAsync` on the current thread — `getFile()` / `createWritable()`, no Comlink, no worker in the bundle.
4
+
5
+ Same limits apply as SharedWorker (it’s the same backend).
6
+
7
+ ## Facade
8
+
9
+ ```typescript
10
+ import { createOPFSAsync } from 'opfs-worker/async';
11
+
12
+ const fs = createOPFSAsync({ root: '/my-app' });
13
+
14
+ await fs.writeFile('/note.txt', 'hello');
15
+ await fs.appendText('/note.txt', '!');
16
+ const text = await fs.readFile('/note.txt', 'utf-8');
17
+ ```
18
+
19
+ Import from `opfs-worker/async` if you care about bundle size — the main entry also carries the dedicated-worker path.
20
+
21
+ ## Raw class
22
+
23
+ ```typescript
24
+ import { OPFSAsync } from 'opfs-worker/async';
25
+
26
+ const fs = new OPFSAsync({ root: '/my-app' });
27
+ await fs.writeFile('/x.bin', new Uint8Array([1, 2, 3]));
28
+ ```
29
+
30
+ ## Limits
31
+
32
+ - **Writes** need `createWritable()` — Chrome, Firefox, Safari **26+**. Reads work anywhere OPFS does.
33
+ - **No file descriptors** — `open` / `read` / `write` / … throw `ENOTSUP`.
34
+ - Each write goes through a swap file and commits on close — fine for documents, awkward for tons of tiny random writes.
35
+
36
+ Streaming still works: [streaming](./streaming.md). For one fs across tabs: [SharedWorker](./sharedworker.md).
@@ -0,0 +1,76 @@
1
+ # Dedicated worker
2
+
3
+ The default path: `OPFSSync` inside a dedicated Web Worker. You get file descriptors and the fastest write path the browser allows for OPFS.
4
+
5
+ See also [Choosing a mode](../choosing-a-mode.md).
6
+
7
+ ## Facade
8
+
9
+ ```typescript
10
+ import { createOPFSDedicated } from 'opfs-worker';
11
+ // or from 'opfs-worker/sync'
12
+
13
+ const fs = createOPFSDedicated({ root: '/my-app' });
14
+
15
+ await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
16
+ const config = await fs.readFile('/config.json'); // string — auto-detected
17
+ ```
18
+
19
+ `createOPFS` is the same thing with a shorter name.
20
+
21
+ The worker is inlined by default, so you don’t need a special bundler worker setup.
22
+
23
+ ## Raw backend
24
+
25
+ Bytes in, bytes out — no encoding helpers. Same facade, just talk to `backend`:
26
+
27
+ ```typescript
28
+ const fs = createOPFSDedicated({ root: '/my-app' });
29
+
30
+ await fs.backend.writeFile('/config.json', new TextEncoder().encode('{}'));
31
+ const bytes = await fs.backend.readFile('/config.json');
32
+
33
+ // Browser Worker, if you ever need it:
34
+ fs.worker; // Worker
35
+
36
+ fs.dispose(); // dispose + terminate
37
+ ```
38
+
39
+ ## Loading a real worker file
40
+
41
+ Pass `url` or `worker` when you can’t use the inline blob (strict CSP, custom hosting):
42
+
43
+ ```typescript
44
+ import workerUrl from 'opfs-worker/dedicated.worker.js?url'; // Vite
45
+
46
+ const fs = createOPFSDedicated({ root: '/my-app', url: workerUrl });
47
+ ```
48
+
49
+ ## DIY prebuilt script
50
+
51
+ `opfs-worker/dedicated.worker.js` is a self-contained Worker (Comlink + `OPFSSync` already inside):
52
+
53
+ ```typescript
54
+ import { wrap } from 'comlink';
55
+ import { OPFSFacade } from 'opfs-worker';
56
+ import workerUrl from 'opfs-worker/dedicated.worker.js?url';
57
+
58
+ const worker = new Worker(workerUrl, { type: 'module' });
59
+ const remote = wrap(worker);
60
+
61
+ const fs = new OPFSFacade({
62
+ fs: remote,
63
+ worker,
64
+ dispose: () => {
65
+ void remote.dispose();
66
+ worker.terminate();
67
+ },
68
+ });
69
+ ```
70
+
71
+ ## Heads-up
72
+
73
+ - Each call gets its own worker. Tabs don’t share state — use [SharedWorker](./sharedworker.md) for that.
74
+ - Sync access handles are a dedicated-worker-only browser API.
75
+
76
+ More: [file descriptors](../file-descriptors.md), [streaming](./streaming.md), [create helpers](../api/create.md).
@@ -0,0 +1,34 @@
1
+ # Hashing
2
+
3
+ Set once via `hashAlgorithm` / `maxFileSize` in options or `setOptions()`. Shows up on `stat()`, `index()`, and [watch](./watching.md) events (files only).
4
+
5
+ | Value | What you get |
6
+ | ----- | ------------ |
7
+ | `'etag'` (default) | Cheap tag from mtime + size — no content read |
8
+ | `'SHA-1'` … `'SHA-512'` | Real content hash (Web Crypto) |
9
+ | `null` / `false` | No `hash` field |
10
+
11
+ `maxFileSize` (default 50MB) only limits SHA-\* hashing. Oversized files skip the hash.
12
+
13
+ ```typescript
14
+ import { createOPFS } from 'opfs-worker';
15
+
16
+ const fs = createOPFS();
17
+
18
+ await fs.writeFile('/data.txt', 'Hello');
19
+ console.log((await fs.stat('/data.txt')).hash); // etag-ish, e.g. "m1abc-c"
20
+
21
+ await fs.setOptions({ hashAlgorithm: 'SHA-256' });
22
+ console.log((await fs.stat('/data.txt')).hash); // hex digest
23
+ ```
24
+
25
+ `root` scopes you under a subdirectory of OPFS. `namespace` is stamped on every watch event:
26
+
27
+ ```typescript
28
+ const fs = createOPFS({
29
+ root: '/my-app',
30
+ namespace: 'my-app:fs',
31
+ });
32
+ ```
33
+
34
+ Full options table: [`create` helpers](../api/create.md#options).
@@ -0,0 +1,46 @@
1
+ # Pure classes
2
+
3
+ Use the classes directly inside a worker (or thread) you already own. No nested worker, no Comlink `expose()` from this package — wiring to the main thread is on you.
4
+
5
+ ```typescript
6
+ import { OPFSSync, OPFSAsync } from 'opfs-worker/pure';
7
+ ```
8
+
9
+ | Class | Runs where | FD |
10
+ | ----- | ---------- | -- |
11
+ | `OPFSSync` | Dedicated worker only | yes |
12
+ | `OPFSAsync` | Main thread, dedicated, or SharedWorker | no |
13
+ | `BaseOPFS` | Shared base — usually you don’t instantiate it | — |
14
+
15
+ ## OPFSSync in your dedicated worker
16
+
17
+ ```typescript
18
+ // my-app.worker.ts
19
+ import { OPFSSync } from 'opfs-worker/pure';
20
+
21
+ const fs = new OPFSSync({
22
+ root: '/my-app',
23
+ broadcastChannel: 'my-app-events', // name only — instances can’t cross the wire
24
+ });
25
+
26
+ await fs.writeFile('/config.json', new TextEncoder().encode('{}'));
27
+ ```
28
+
29
+ `/pure` won’t hijack your worker’s message port.
30
+
31
+ ## OPFSAsync in a SharedWorker you write
32
+
33
+ ```typescript
34
+ import { expose } from 'comlink';
35
+ import { OPFSAsync } from 'opfs-worker/pure';
36
+
37
+ const fs = new OPFSAsync({ root: '/my-app' });
38
+
39
+ onconnect = (e) => {
40
+ expose(fs, e.ports[0]);
41
+ };
42
+ ```
43
+
44
+ Or skip the boilerplate and use the [SharedWorker guide](./sharedworker.md).
45
+
46
+ Also: [backend API](../api/backend.md), [dedicated](./dedicated.md), [async](./async.md).
@@ -0,0 +1,57 @@
1
+ # SharedWorker
2
+
3
+ One `OPFSAsync` for every tab. Locks live in that single instance, so writes don’t stomp each other across tabs. Watch events go out on `BroadcastChannel` like everywhere else.
4
+
5
+ Same limits as [async](./async.md): Safari 26+ for writes, no FDs.
6
+
7
+ SharedWorkers are keyed by **script URL**, so we can’t inline the worker. Use the shipped file: `opfs-worker/shared.worker.js`.
8
+
9
+ ## With a bundler (Vite example)
10
+
11
+ ```typescript
12
+ import workerUrl from 'opfs-worker/shared.worker.js?url';
13
+ import { createOPFSShared } from 'opfs-worker/sharedworker';
14
+
15
+ const fs = createOPFSShared({ root: '/my-app', url: workerUrl });
16
+
17
+ await fs.writeText('/shared-config.json', '{}');
18
+ ```
19
+
20
+ ## Without a bundler
21
+
22
+ Default URL looks for `./shared.worker.js` next to the package files:
23
+
24
+ ```typescript
25
+ import { createOPFSShared } from 'opfs-worker/sharedworker';
26
+
27
+ const fs = createOPFSShared({ root: '/my-app' });
28
+ ```
29
+
30
+ Or pass your own instance:
31
+
32
+ ```typescript
33
+ const worker = new SharedWorker(
34
+ new URL('opfs-worker/shared.worker.js', import.meta.url),
35
+ { type: 'module', name: 'opfs-worker' }
36
+ );
37
+ const fs = createOPFSShared({ root: '/my-app', worker });
38
+ ```
39
+
40
+ ## Raw backend
41
+
42
+ ```typescript
43
+ const fs = createOPFSShared({ root: '/my-app', url: workerUrl });
44
+
45
+ await fs.backend.writeFile('/a.bin', bytes);
46
+ fs.worker; // SharedWorker
47
+
48
+ fs.dispose(); // closes this tab’s port only
49
+ ```
50
+
51
+ ## Notes
52
+
53
+ - `dispose()` does **not** kill the worker for other tabs — only your port.
54
+ - `setOptions` hits the shared instance, so keep options consistent across tabs.
55
+ - Same script URL + `name` (default `'opfs-worker'`) → same instance.
56
+
57
+ Also: [async](./async.md), [watching](./watching.md), [create helpers](../api/create.md).
@@ -0,0 +1,51 @@
1
+ # Streaming
2
+
3
+ Write big files without holding the whole thing in memory.
4
+
5
+ | | Method |
6
+ | --- | --- |
7
+ | Facade | `importStream(path, source, options?)` |
8
+ | Backend / raw | `writeStream(path, stream, onProgress?)` |
9
+
10
+ Both create or overwrite the file, return bytes written, and fire a watch event when done.
11
+
12
+ ## `importStream` (facade)
13
+
14
+ Takes a `ReadableStream<Uint8Array>`, or a `Blob` / `File` (we call `.stream()` for you):
15
+
16
+ ```typescript
17
+ import { createOPFS } from 'opfs-worker';
18
+
19
+ const fs = createOPFS({ root: '/my-app' });
20
+
21
+ const file = input.files[0];
22
+ await fs.importStream(`/uploads/${file.name}`, file, {
23
+ onProgress: (n) => console.log(`wrote ${n} bytes`),
24
+ });
25
+
26
+ const res = await fetch('/large.bin');
27
+ await fs.importStream('/cache/large.bin', res.body!);
28
+ ```
29
+
30
+ With a worker backend the facade transfers the stream and proxies `onProgress` — you don’t need Comlink `transfer` yourself.
31
+
32
+ ## `writeStream` (raw)
33
+
34
+ ```typescript
35
+ import { createOPFS } from 'opfs-worker';
36
+
37
+ const fs = createOPFS();
38
+ await fs.backend.writeStream('/data.bin', someBlob.stream(), (n) => console.log(n));
39
+ ```
40
+
41
+ ## When to use what
42
+
43
+ | API | Good for |
44
+ | --- | -------- |
45
+ | `writeFile` | Stuff already in memory |
46
+ | `importStream` / `writeStream` | Large `File` / `Blob` / network bodies |
47
+ | `createIndex` | Many small entries (Blobs here are still fully buffered) |
48
+
49
+ Dedicated path chunks through FDs; async path uses `createWritable()` (Safari 26+ for writes).
50
+
51
+ Also: [facade](../api/facade.md), [backend](../api/backend.md).
@@ -0,0 +1,35 @@
1
+ # Watching
2
+
3
+ Changes go out on a `BroadcastChannel` (default name `opfs-worker`). Nothing is emitted unless you `watch` a path.
4
+
5
+ ```typescript
6
+ import { createOPFS } from 'opfs-worker';
7
+
8
+ const channel = new BroadcastChannel('opfs-worker');
9
+ channel.onmessage = (e) => console.log(e.data);
10
+
11
+ const fs = createOPFS({
12
+ root: '/my-app',
13
+ namespace: 'my-app:fs',
14
+ broadcastChannel: channel, // or just the string 'opfs-worker'
15
+ });
16
+
17
+ const stop = fs.watch('/', {
18
+ recursive: true,
19
+ include: ['**/*.json'],
20
+ exclude: ['tmp/**'],
21
+ });
22
+
23
+ await fs.writeFile('/config.json', '{}');
24
+ // → { namespace, path, type: 'added' | 'changed' | 'removed', isDirectory, timestamp, hash? }
25
+
26
+ stop(); // or fs.unwatch('/')
27
+ ```
28
+
29
+ `watch` returns an unsubscribe function. Options: [`WatchOptions`](../types.md#watchoptions). Payload: [`WatchEvent`](../types.md#watchevent).
30
+
31
+ ## Gotchas
32
+
33
+ - Into a worker, pass a **channel name** (string). You can’t post a `BroadcastChannel` instance across the wire. On the main thread the facade accepts either.
34
+ - SharedWorker: one backend + one channel → every listening tab sees the same events.
35
+ - Whether events include `hash` depends on [hashing](./hashing.md) settings.
@@ -0,0 +1,41 @@
1
+ # Migration (1.x → 2.x)
2
+
3
+ ## Renames
4
+
5
+ | Was | Now |
6
+ | ------------------------ | ------------------------------------------------------------------------------------ |
7
+ | `createWorker` | `createOPFSDedicated` (`createOPFS` alias; deprecated `createWorker` still exported) |
8
+ | `OPFSFileSystem` | `OPFSFacade` (deprecated alias kept) |
9
+ | `OPFSWorker` | `OPFSSync` |
10
+ | `opfs-worker/raw` | `opfs-worker/pure` |
11
+ | `opfs-worker/raw?worker` | `createOPFSDedicated()` + `fs.backend` / `fs.worker`, or `dedicated.worker.js` |
12
+ | `fs.sync(entries)` | `fs.createIndex(entries)` |
13
+
14
+ ```diff
15
+ - import { createWorker, OPFSFileSystem } from 'opfs-worker';
16
+ - const fs: OPFSFileSystem = createWorker({ root: '/my-app' });
17
+ + import { createOPFS, OPFSFacade } from 'opfs-worker';
18
+ + const fs: OPFSFacade = createOPFS({ root: '/my-app' });
19
+ ```
20
+
21
+ ```diff
22
+ - import { OPFSWorker } from 'opfs-worker/raw';
23
+ + import { OPFSSync } from 'opfs-worker/pure';
24
+ ```
25
+
26
+ ## New in 2.x
27
+
28
+ - Async: `createOPFSAsync` / `opfs-worker/async`
29
+ - SharedWorker: `createOPFSShared` / `opfs-worker/sharedworker`
30
+ - Prebuilt scripts: `dedicated.worker.js`, `shared.worker.js`
31
+ - Streaming: `importStream` / `writeStream`
32
+ - Escape hatch on every facade: `fs.backend` / `fs.worker`
33
+ - Clearer names: `createOPFSDedicated`, `createOPFSAsync`, `createOPFSShared`
34
+
35
+ ## Behaviour
36
+
37
+ - Concurrent `readFile` on one path takes the same exclusive lock as writes
38
+ - Multiple FDs on one path share one sync handle, each with its own cursor
39
+ - Dedicated `dispose()` terminates the browser `Worker`
40
+
41
+ Full entry map: [Choosing a mode](./choosing-a-mode.md).
package/docs/types.md ADDED
@@ -0,0 +1,246 @@
1
+ # Types
2
+
3
+ TypeScript types shipped with the package.
4
+
5
+ ## Core Types
6
+
7
+ ### `FileStat`
8
+
9
+ File or directory statistics.
10
+
11
+ ```typescript
12
+ interface FileStat {
13
+ kind: 'file' | 'directory';
14
+ size: number;
15
+ mtime: string; // ISO string
16
+ ctime: string; // ISO string
17
+ isFile: boolean;
18
+ isDirectory: boolean;
19
+ hash?: string; // Hash of file content (only for files)
20
+ }
21
+ ```
22
+
23
+ **Properties:**
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)
32
+
33
+ ### `DirentData`
34
+
35
+ Directory entry information.
36
+
37
+ ```typescript
38
+ interface DirentData {
39
+ name: string;
40
+ kind: 'file' | 'directory';
41
+ isFile: boolean;
42
+ isDirectory: boolean;
43
+ }
44
+ ```
45
+
46
+ **Properties:**
47
+
48
+ - `name`: Name of the file or directory
49
+ - `kind`: Type of the entry
50
+ - `isFile`: True if this is a file
51
+ - `isDirectory`: True if this is a directory
52
+
53
+ ### `WatchOptions`
54
+
55
+ Options for file watching operations.
56
+
57
+ ```typescript
58
+ interface WatchOptions {
59
+ recursive?: boolean; // Whether to watch recursively (default: true)
60
+ include?: string | string[]; // Glob patterns to include in watching (minimatch syntax, default: ['**'])
61
+ exclude?: string | string[]; // Glob patterns to exclude from watching (minimatch syntax, default: [])
62
+ }
63
+ ```
64
+
65
+ **Properties:**
66
+
67
+ - `recursive`: Whether to watch the entire directory tree (default: true)
68
+ - `include`: Glob patterns to include in watching (default: all files)
69
+ - `exclude`: Glob patterns to exclude from watching (default: none)
70
+
71
+ ### `OPFSApi`
72
+
73
+ Promise-based fs API surface shared by all backends — a Comlink proxy to `OPFSSync` in a worker, or an in-process `OPFSAsync` instance. This is what `OPFSFacade` talks to (see `OPFSBackend` in [Create helpers](./api/create.md)).
74
+
75
+ ```typescript
76
+ type OPFSApi = { [K in keyof OPFSSync]: OPFSSync[K] };
77
+ ```
78
+
79
+ ### `PathLike`
80
+
81
+ ```typescript
82
+ type PathLike = string | URL;
83
+ ```
84
+
85
+ ### `FileOpenOptions`
86
+
87
+ ```typescript
88
+ interface FileOpenOptions {
89
+ create?: boolean;
90
+ exclusive?: boolean;
91
+ truncate?: boolean;
92
+ }
93
+ ```
94
+
95
+ ## Configuration Types
96
+
97
+ ### `OPFSOptions`
98
+
99
+ Configuration for all backends. Human-readable table: [Create helpers → Options](./api/create.md#options).
100
+
101
+ ```typescript
102
+ interface OPFSOptions {
103
+ /** Root path for the file system (default: '/') */
104
+ root?: string;
105
+ /** Namespace for the events (default: 'opfs-worker:${root}') */
106
+ namespace?: string;
107
+ /** Hash algorithm for file hashing, or false/null to disable (default: 'etag') */
108
+ hashAlgorithm?: null | false | 'etag' | 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';
109
+ /** Maximum file size in bytes for SHA-* hashing (default: 50MB); ignored by 'etag' */
110
+ maxFileSize?: number;
111
+ /** Custom name for the broadcast channel (default: 'opfs-worker') */
112
+ broadcastChannel?: string | BroadcastChannel | null;
113
+ }
114
+ ```
115
+
116
+ **Properties:**
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.
129
+
130
+ ```typescript
131
+ enum WatchEventType {
132
+ Added = 'added',
133
+ Changed = 'changed',
134
+ Removed = 'removed'
135
+ }
136
+ ```
137
+
138
+ **Values:**
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.
147
+
148
+ ```typescript
149
+ interface WatchEvent {
150
+ namespace: string;
151
+ path: string;
152
+ type: WatchEventType;
153
+ isDirectory: boolean;
154
+ timestamp: string;
155
+ hash?: string;
156
+ }
157
+ ```
158
+
159
+ **Properties:**
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.
173
+
174
+ ```typescript
175
+ type Kind = 'file' | 'directory';
176
+ ```
177
+
178
+ ### `Encoding`
179
+
180
+ Supported text encodings.
181
+
182
+ ```typescript
183
+ type Encoding = 'utf-8' | 'utf-16le' | 'ascii' | 'latin1' | 'base64' | 'hex' | 'binary';
184
+ ```
185
+
186
+ ## Error Types
187
+
188
+ The library provides comprehensive error handling with specific error types that are Node.js SystemError compatible:
189
+
190
+ ### Base Error Class
191
+
192
+ - `OPFSError` - Base error class for all OPFS-related errors (Node.js SystemError compatible)
193
+
194
+ **Properties:**
195
+
196
+ - `errno: number` - Numeric error code (e.g., -2 for ENOENT)
197
+ - `syscall?: string` - System call name (e.g., 'open', 'read', 'write')
198
+ - `path?: string` - File path when applicable
199
+ - `name: string` - Error class name ('OPFSError')
200
+ - `message: string` - Human-readable error message
201
+ - `cause?: any` - Underlying error (Node.js 16+ feature)
202
+
203
+ ### Specialized Error Classes
204
+
205
+ - `OPFSNotSupportedError` - Thrown when OPFS is not supported in the browser
206
+ - `PathError` - Thrown for invalid paths or path traversal attempts
207
+ - `ExistenceError` - Thrown when files or directories don't exist
208
+ - Usage: `new ExistenceError('File not found: /path/to/file', 'ENOENT', '/path/to/file')`
209
+ - `PermissionError` - Thrown when permission is denied for an operation
210
+ - `StorageError` - Thrown when an operation fails due to insufficient storage
211
+ - `TimeoutError` - Thrown when an operation times out
212
+ - `FileBusyError` - Thrown when a file is busy (locked by another operation)
213
+ - `FileTypeError` - Thrown when file/directory type expectations don't match
214
+ - Usage: `new FileTypeError('directory', '/path/to/dir')`
215
+ - `ValidationError` - Thrown for validation failures (invalid arguments, formats, etc.)
216
+ - Usage: `new ValidationError('Invalid size', 'EINVAL', '/path/to/file')`
217
+ - `OperationAbortedError` - Thrown when an operation is aborted
218
+ - `IOError` - Thrown for I/O operation failures
219
+ - `OperationNotSupportedError` - Thrown when an operation is not supported
220
+ - `DirectoryOperationError` - Thrown when directory operations fail
221
+ - Usage: `new DirectoryOperationError('RM_FAILED', '/path/to/dir')`
222
+ - `InitializationFailedError` - Thrown when OPFS initialization fails
223
+ - `FileSystemOperationError` - Thrown when file system operations fail
224
+ - `PathResolutionFailedError` - Thrown when path resolution fails
225
+ - `AlreadyExistsError` - Thrown when a file or directory already exists
226
+
227
+ ### Error Code Mapping
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.