opfs-worker 2.1.0 → 2.1.2

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 (111) hide show
  1. package/README.md +51 -37
  2. package/dist/BaseOPFS-Bu7FZtCb.cjs +2 -0
  3. package/dist/BaseOPFS-Bu7FZtCb.cjs.map +1 -0
  4. package/dist/{BaseOPFS-Cylz6ijk.js → BaseOPFS-C3NE4BKr.js} +804 -484
  5. package/dist/BaseOPFS-C3NE4BKr.js.map +1 -0
  6. package/dist/OPFSAsync-C0RsxCX5.cjs +2 -0
  7. package/dist/OPFSAsync-C0RsxCX5.cjs.map +1 -0
  8. package/dist/{OPFSAsync-BKSAJJeh.js → OPFSAsync-aeLUdIqd.js} +19 -19
  9. package/dist/OPFSAsync-aeLUdIqd.js.map +1 -0
  10. package/dist/OPFSFacade-BJv2yuWU.cjs +2 -0
  11. package/dist/OPFSFacade-BJv2yuWU.cjs.map +1 -0
  12. package/dist/OPFSFacade-DiogtQoq.js +308 -0
  13. package/dist/OPFSFacade-DiogtQoq.js.map +1 -0
  14. package/dist/OPFSSync-3qlSBYmz.cjs +2 -0
  15. package/dist/OPFSSync-3qlSBYmz.cjs.map +1 -0
  16. package/dist/{OPFSSync-CsSQkpk5.js → OPFSSync-CvSOswhW.js} +57 -57
  17. package/dist/OPFSSync-CvSOswhW.js.map +1 -0
  18. package/dist/assets/dedicated.worker-Bqqr9UBA.js.map +1 -0
  19. package/dist/core/BaseOPFS.d.ts +60 -9
  20. package/dist/core/BaseOPFS.d.ts.map +1 -1
  21. package/dist/core/OPFSAsync.d.ts.map +1 -1
  22. package/dist/core/OPFSSync.d.ts.map +1 -1
  23. package/dist/createOPFSAsync-B611B63X.js +10 -0
  24. package/dist/{createOPFSAsync-Y4Mg6qte.js.map → createOPFSAsync-B611B63X.js.map} +1 -1
  25. package/dist/createOPFSAsync-BATjJg8k.cjs +2 -0
  26. package/dist/{createOPFSAsync-BwKDmHsy.cjs.map → createOPFSAsync-BATjJg8k.cjs.map} +1 -1
  27. package/dist/createOPFSDedicated-DgudPTB8.js +2676 -0
  28. package/dist/createOPFSDedicated-DgudPTB8.js.map +1 -0
  29. package/dist/createOPFSDedicated-EmIwluQo.cjs +2597 -0
  30. package/dist/createOPFSDedicated-EmIwluQo.cjs.map +1 -0
  31. package/dist/{createOPFSShared-CGdd__PH.js → createOPFSShared-BWYln8jJ.js} +2 -2
  32. package/dist/{createOPFSShared-CGdd__PH.js.map → createOPFSShared-BWYln8jJ.js.map} +1 -1
  33. package/dist/{createOPFSShared-CskROt7U.cjs → createOPFSShared-DPJpCd7z.cjs} +2 -2
  34. package/dist/{createOPFSShared-CskROt7U.cjs.map → createOPFSShared-DPJpCd7z.cjs.map} +1 -1
  35. package/dist/dedicated.worker.js +1104 -790
  36. package/dist/dedicated.worker.js.map +1 -1
  37. package/dist/facade/OPFSFacade.d.ts +21 -3
  38. package/dist/facade/OPFSFacade.d.ts.map +1 -1
  39. package/dist/facade/createOPFSDedicated.d.ts +4 -2
  40. package/dist/facade/createOPFSDedicated.d.ts.map +1 -1
  41. package/dist/index.async.cjs +1 -1
  42. package/dist/index.async.js +33 -32
  43. package/dist/index.cjs +1 -1
  44. package/dist/index.d.ts +1 -5
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +44 -43
  47. package/dist/index.pure.cjs +1 -1
  48. package/dist/index.pure.js +38 -37
  49. package/dist/index.sharedworker.cjs +1 -1
  50. package/dist/index.sharedworker.js +34 -33
  51. package/dist/index.sync.cjs +1 -1
  52. package/dist/index.sync.d.ts +2 -0
  53. package/dist/index.sync.d.ts.map +1 -1
  54. package/dist/index.sync.js +34 -32
  55. package/dist/shared.worker.js +1101 -791
  56. package/dist/shared.worker.js.map +1 -1
  57. package/dist/types.d.ts +38 -5
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/utils/helpers.d.ts +13 -3
  60. package/dist/utils/helpers.d.ts.map +1 -1
  61. package/dist/worker/createDedicatedWorker.d.ts +8 -3
  62. package/dist/worker/createDedicatedWorker.d.ts.map +1 -1
  63. package/docs/README.md +13 -25
  64. package/docs/api/README.md +221 -0
  65. package/docs/api/file-descriptors.md +137 -0
  66. package/docs/guides/async.md +4 -4
  67. package/docs/guides/dedicated.md +10 -8
  68. package/docs/guides/hashing.md +3 -3
  69. package/docs/guides/pure.md +4 -4
  70. package/docs/guides/sharedworker.md +2 -2
  71. package/docs/guides/streaming.md +146 -12
  72. package/docs/guides/watching.md +3 -3
  73. package/docs/migration.md +10 -11
  74. package/docs/types.md +93 -155
  75. package/package.json +20 -9
  76. package/src/core/BaseOPFS.ts +194 -29
  77. package/src/core/OPFSAsync.ts +4 -5
  78. package/src/core/OPFSSync.ts +6 -7
  79. package/src/facade/OPFSFacade.ts +51 -4
  80. package/src/facade/createOPFSDedicated.ts +4 -2
  81. package/src/index.sync.ts +2 -0
  82. package/src/index.ts +1 -5
  83. package/src/types.ts +42 -3
  84. package/src/utils/helpers.ts +32 -6
  85. package/src/worker/createDedicatedWorker.ts +85 -7
  86. package/src/worker/dedicated.worker.ts +12 -1
  87. package/dist/BaseOPFS-BPw9AIQ-.cjs +0 -4
  88. package/dist/BaseOPFS-BPw9AIQ-.cjs.map +0 -1
  89. package/dist/BaseOPFS-Cylz6ijk.js.map +0 -1
  90. package/dist/OPFSAsync-BKSAJJeh.js.map +0 -1
  91. package/dist/OPFSAsync-s2P7y3sw.cjs +0 -2
  92. package/dist/OPFSAsync-s2P7y3sw.cjs.map +0 -1
  93. package/dist/OPFSFacade-DePnNRgt.cjs +0 -2
  94. package/dist/OPFSFacade-DePnNRgt.cjs.map +0 -1
  95. package/dist/OPFSFacade-XohWlHuu.js +0 -281
  96. package/dist/OPFSFacade-XohWlHuu.js.map +0 -1
  97. package/dist/OPFSSync-C7KBepe_.cjs +0 -2
  98. package/dist/OPFSSync-C7KBepe_.cjs.map +0 -1
  99. package/dist/OPFSSync-CsSQkpk5.js.map +0 -1
  100. package/dist/assets/dedicated.worker-nY3tK4zi.js.map +0 -1
  101. package/dist/createOPFSAsync-BwKDmHsy.cjs +0 -2
  102. package/dist/createOPFSAsync-Y4Mg6qte.js +0 -10
  103. package/dist/createOPFSDedicated-CsnV6MLP.js +0 -2333
  104. package/dist/createOPFSDedicated-CsnV6MLP.js.map +0 -1
  105. package/dist/createOPFSDedicated-pKTRWLdz.cjs +0 -2283
  106. package/dist/createOPFSDedicated-pKTRWLdz.cjs.map +0 -1
  107. package/docs/api/backend.md +0 -36
  108. package/docs/api/create.md +0 -82
  109. package/docs/api/facade.md +0 -52
  110. package/docs/choosing-a-mode.md +0 -41
  111. package/docs/file-descriptors.md +0 -695
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { BaseOPFS } from './core/BaseOPFS';
1
2
  import type { OPFSSync } from './core/OPFSSync';
2
3
  /**
3
4
  * Type for paths that can be either a string or URI
@@ -38,12 +39,11 @@ export interface WatchEvent {
38
39
  }
39
40
  export type { OPFSSync };
40
41
  /**
41
- * Promise-based fs API surface shared by all backends: a Comlink proxy to
42
- * `OPFSSync` in a worker, or an in-process `OPFSAsync` instance.
42
+ * Public bytes API shared by all backends (`OPFSSync`, `OPFSAsync`, or a
43
+ * Comlink proxy to one of them). Declared on {@link BaseOPFS}; subclasses
44
+ * implement the abstract I/O and FD methods.
43
45
  */
44
- export type OPFSApi = {
45
- [K in keyof OPFSSync]: OPFSSync[K];
46
- };
46
+ export type OPFSApi = Pick<BaseOPFS, keyof BaseOPFS>;
47
47
  export interface OPFSOptions {
48
48
  /** Root path for the file system (default: '/') */
49
49
  root?: string;
@@ -56,6 +56,39 @@ export interface OPFSOptions {
56
56
  /** Custom name for the broadcast channel (default: 'opfs-worker') */
57
57
  broadcastChannel?: string | BroadcastChannel | null;
58
58
  }
59
+ /** Payload accepted by {@link importFiles} for a single entry. */
60
+ export type ImportFileData = string | Uint8Array | Blob;
61
+ /**
62
+ * Entries for {@link importFiles}: an array of `[path, data]` tuples, a `Map`,
63
+ * or any iterable that yields the same pairs.
64
+ */
65
+ export type ImportFilesEntries = Iterable<[string, ImportFileData]> | Map<string, ImportFileData>;
66
+ /** Progress event fired while {@link importFiles} writes entries. */
67
+ export interface ImportFilesProgress {
68
+ /** Path of the file currently being written */
69
+ path: string;
70
+ /** 0-based index of the current file */
71
+ index: number;
72
+ /** Total number of entries in this import */
73
+ count: number;
74
+ /** Bytes written for the current file so far */
75
+ bytesWritten: number;
76
+ /** Size of the current file in bytes */
77
+ bytesTotal: number;
78
+ /** Bytes written across all files so far */
79
+ totalBytesWritten: number;
80
+ /** Sum of all entry sizes in bytes */
81
+ totalBytes: number;
82
+ }
83
+ /** Result of a finished {@link importFiles} call. */
84
+ export interface ImportFilesResult {
85
+ /** Paths written, in import order */
86
+ paths: string[];
87
+ /** Number of files imported (`paths.length`) */
88
+ count: number;
89
+ /** Total bytes written across all files */
90
+ bytesWritten: number;
91
+ }
59
92
  export interface RenameOptions {
60
93
  /** Whether to overwrite existing files (default: false) */
61
94
  overwrite?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,GAAG,CAAC;AAEpC,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,CAAC;AAExC,MAAM,MAAM,cAAc,GAAG,OAAO,GAChC,MAAM,GACN,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,KAAK,CAAC;AAEV,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEtC,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,cAAc,CAAC;AAEvD,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACxB;AAED,oBAAY,cAAc;IACtB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,OAAO,YAAY;CACtB;AAED,MAAM,WAAW,UAAU;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;KAAG,CAAC,IAAI,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC;CAAE,CAAC;AAE7D,MAAM,WAAW,WAAW;IACxB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACpF,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,aAAa;IAC1B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IACzB,mDAAmD;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -5,17 +5,26 @@ import type { Encoding } from '../types';
5
5
  * @throws {OPFSNotSupportedError} If the browser does not support the OPFS API
6
6
  */
7
7
  export declare function checkOPFSSupport(): void;
8
+ /**
9
+ * Join a mount `root` with an API path into an absolute OPFS path.
10
+ *
11
+ * Lock keys and cross-instance coordination use this so `/x.txt` under
12
+ * `root: '/app'` and `/app/x.txt` under `root: '/'` resolve to the same file.
13
+ */
14
+ export declare function absoluteOpfsPath(root: string, path: string): string;
8
15
  /**
9
16
  * Run a callback while holding an exclusive lock on a path
10
17
  *
11
18
  * Locks are always exclusive: OPFS permits a single sync access handle per file,
12
- * so readers can't share access either.
19
+ * so readers can't share access either. The lock key is the absolute OPFS path
20
+ * (`root` + API `path`) so different mounts of the same file serialize correctly.
13
21
  *
14
- * @param path - The path to lock
22
+ * @param path - The API path to lock (relative to `root`)
15
23
  * @param fn - The callback to run while holding the lock
24
+ * @param root - Mount root (default: `/`)
16
25
  * @returns The value returned by the callback
17
26
  */
18
- export declare function withLock<T>(path: string, fn: () => Promise<T>): Promise<T>;
27
+ export declare function withLock<T>(path: string, fn: () => Promise<T>, root?: string): Promise<T>;
19
28
  /**
20
29
  * Split a path into an array of segments
21
30
  *
@@ -174,6 +183,7 @@ export declare function removeEntry(parentHandle: FileSystemDirectoryHandle, pat
174
183
  recursive?: boolean;
175
184
  force?: boolean;
176
185
  useTrash?: boolean;
186
+ root?: string;
177
187
  }): Promise<void>;
178
188
  /**
179
189
  * Validate read/write arguments for file descriptor operations
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ export interface DedicatedWorkerOptions extends OPFSOptions {
9
9
  * prefer the self-contained file (strict CSP without `blob:`, or DIY hosting).
10
10
  */
11
11
  url?: string | URL;
12
- /** Bring your own Worker instance (overrides `url`) */
12
+ /** Bring your own Worker instance (overrides `url`) — not pooled */
13
13
  worker?: Worker;
14
14
  }
15
15
  export interface RawWorker {
@@ -17,13 +17,18 @@ export interface RawWorker {
17
17
  fs: RemoteOPFSSync;
18
18
  /** Underlying browser Worker */
19
19
  worker: Worker;
20
- /** Calls worker `dispose()` then `terminate()` */
20
+ /** Drops this facade's port; terminates the Worker when the last user of this pool entry disposes */
21
21
  dispose: () => void;
22
22
  }
23
23
  /**
24
- * Internal helper: spawn a dedicated worker and wrap `OPFSSync` with Comlink.
24
+ * Internal helper: spawn (or reuse) a dedicated worker and wrap `OPFSSync` with Comlink.
25
25
  * Prefer {@link createOPFSDedicated}; access the proxy / Worker via
26
26
  * `facade.backend` / `facade.worker`.
27
+ *
28
+ * Calls with the same `root` (and same `url`, if any) share one Worker on this page.
29
+ * Different roots get different Workers. Passing `worker` bypasses the pool.
30
+ *
31
+ * `setOptions` hits the shared instance — keep options consistent for the same root.
27
32
  */
28
33
  export declare function createDedicatedWorker(options?: DedicatedWorkerOptions): RawWorker;
29
34
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"createDedicatedWorker.d.ts","sourceRoot":"","sources":["../../src/worker/createDedicatedWorker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,KAAK,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEvC,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACvD;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACtB,yDAAyD;IACzD,EAAE,EAAE,cAAc,CAAC;IACnB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAeD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,SAAS,CAuBrF"}
1
+ {"version":3,"file":"createDedicatedWorker.d.ts","sourceRoot":"","sources":["../../src/worker/createDedicatedWorker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,KAAK,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEvC,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACvD;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACtB,yDAAyD;IACzD,EAAE,EAAE,cAAc,CAAC;IACnB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAmCD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,SAAS,CA2ErF"}
package/docs/README.md CHANGED
@@ -1,30 +1,18 @@
1
1
  # Docs
2
2
 
3
- New here? Start with [Choosing a mode](./choosing-a-mode.md).
3
+ - [API overview](./api/README.md) — entries, facade methods, options, trade-offs
4
+ - [File descriptors](./api/file-descriptors.md) — positional `open` / `read` / `write`
5
+ - [Types](./types.md)
6
+ - [Migration from 1.x](./migration.md)
4
7
 
5
8
  ## Guides
6
9
 
7
- | Guide | About |
8
- | ----------------------------------------- | -------------------------------------------------- |
9
- | [Choosing a mode](./choosing-a-mode.md) | Which entry / backend to pick |
10
- | [Dedicated worker](./guides/dedicated.md) | Default path + DIY worker script |
11
- | [Async (no worker)](./guides/async.md) | `createOPFSAsync` on the current thread |
12
- | [SharedWorker](./guides/sharedworker.md) | One fs shared by all tabs |
13
- | [Pure classes](./guides/pure.md) | Drop `OPFSSync` / `OPFSAsync` into your own worker |
14
- | [Streaming](./guides/streaming.md) | Large files with `importStream` |
15
- | [Watching](./guides/watching.md) | Change events over `BroadcastChannel` |
16
- | [Hashing](./guides/hashing.md) | etag vs SHA |
17
-
18
- ## API
19
-
20
- | Page | About |
21
- | ----------------------------------------- | ------------------------------------- |
22
- | [Create helpers](./api/create.md) | `createOPFS*` + **options table** |
23
- | [Facade](./api/facade.md) | Node-like methods and aliases |
24
- | [Backend](./api/backend.md) | `BaseOPFS` / `OPFSSync` / `OPFSAsync` |
25
- | [File descriptors](./file-descriptors.md) | `open` / `read` / `write` / … |
26
- | [Types](./types.md) | Types and errors |
27
-
28
- ## Migration
29
-
30
- [1.x → 2.x](./migration.md)
10
+ | Guide | About |
11
+ | ----------------------------------------- | --------------------------------------------------------- |
12
+ | [Dedicated worker](./guides/dedicated.md) | How to use OPFS via a dedicated worker (the default path) |
13
+ | [Async](./guides/async.md) | How to use OPFS on the main thread, without a worker |
14
+ | [SharedWorker](./guides/sharedworker.md) | How to share one filesystem across all tabs |
15
+ | [Pure classes](./guides/pure.md) | How to drop OPFS into a worker you already run |
16
+ | [Streaming](./guides/streaming.md) | How to handle large files and bulk uploads |
17
+ | [Watching](./guides/watching.md) | How to listen for file changes across tabs |
18
+ | [Hashing](./guides/hashing.md) | How file hashes / etags work on `stat` and watch events |
@@ -0,0 +1,221 @@
1
+ # API
2
+
3
+ The sync backend works in every browser that has OPFS, but the browser only allows it inside a dedicated worker — `createOPFS()` / `createOPFSDedicated()` set that up for you. The async backend can run on the main thread or in a SharedWorker, but needs a modern browser — writes will not work in Safari before 26.
4
+
5
+ ## Package entries
6
+
7
+ | Import from | Worker | Use it when |
8
+ | -------------------------- | ------------------------------------------ | --------------------------------------------------------------------- |
9
+ | `opfs-worker` | Starts a dedicated worker | Default; fine if bundle size doesn’t matter or tree-shaking is set up |
10
+ | `opfs-worker/sync` | Starts a dedicated worker | Same as the default, without async / shared code in the bundle |
11
+ | `opfs-worker/async` | Runs directly in the current thread | Main thread or your own worker; Safari prior to 26 can’t write |
12
+ | `opfs-worker/sharedworker` | Starts one SharedWorker shared by all tabs | One shared fs process for every tab; Safari prior to 26 can’t write |
13
+ | `opfs-worker/pure` | None | Low-level classes to build your own custom worker |
14
+
15
+ ## Facade
16
+
17
+ Every helper returns an `OPFSFacade` — the same Node-like API in all modes. The facade hides the actual work: depending on which helper you call, it either creates a dedicated worker and talks to it, works with OPFS directly in the current thread, or connects to a SharedWorker.
18
+
19
+ Each takes optional `[options](#options)`.
20
+
21
+ | Function | From | Under the hood |
22
+ | ------------------ | ------------------------------------------- | ------------------------------------------------ |
23
+ | `createOPFS` | `opfs-worker` or `opfs-worker/sync` | Dedicated worker + `OPFSSync` |
24
+ | `createOPFSAsync` | `opfs-worker` or `opfs-worker/async` | OPFS directly, `OPFSAsync` in the current thread |
25
+ | `createOPFSShared` | `opfs-worker` or `opfs-worker/sharedworker` | SharedWorker + `OPFSAsync` |
26
+
27
+ ```typescript
28
+ import { createOPFS, createOPFSAsync, createOPFSShared } from 'opfs-worker';
29
+
30
+ const fs = createOPFS(); // or createOPFSAsync(), createOPFSShared({ url: '...' })
31
+ ```
32
+
33
+ ### File I/O
34
+
35
+ Paths are `string | URL` everywhere below.
36
+
37
+ | Method | Parameters | Returns |
38
+ | -------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------- | --------------- |
39
+ | `readFile` | `path`, optional `encoding` or `{ encoding }` | `Promise<string | Uint8Array>` — auto by extension if omitted (`.txt`→ string,`.bin` → bytes) |
40
+ | `writeFile` | `path`, `data` (`string | Uint8Array | ArrayBuffer | Blob`), optional encoding | `Promise<void>` |
41
+ | `appendFile` | `path`, `data` (`string | Uint8Array | ArrayBuffer | Blob`), optional encoding | `Promise<void>` |
42
+ | `readText` | `path`, `encoding?` (default `'utf-8'`) | `Promise<string>` |
43
+ | `writeText` | `path`, `text`, `encoding?` (default `'utf-8'`) | `Promise<void>` |
44
+ | `appendText` | `path`, `text`, `encoding?` (default `'utf-8'`) | `Promise<void>` |
45
+ | `readBlob` | `path` | `Promise<Blob>` — disk-backed, not copied into memory. See [streaming](../guides/streaming.md) |
46
+ | `importStream` | `path`, `ReadableStream | Blob | File`, optional `{ onProgress }` | `Promise<number>` — bytes written. See [streaming](../guides/streaming.md) |
47
+ | `importFiles` | `[path, data][]` / `Map` / iterable, optional `{ onProgress }` | `Promise<ImportFilesResult>` — paths, count, bytes. See [streaming](../guides/streaming.md) |
48
+
49
+ For Node compatibility, `fs.promises` is the same instance (`fs.promises === fs`).
50
+
51
+ ### Directories & metadata
52
+
53
+ | Method | Parameters | Returns |
54
+ | ---------- | -------------------------------------------------------------------- | ------------------------------------------------------------------- |
55
+ | `mkdir` | `path`, optional `{ recursive }` (numeric mode accepted and ignored) | `Promise<void>` |
56
+ | `readDir` | `path` | `Promise<DirentData[]>` |
57
+ | `stat` | `path` | `Promise<FileStat>` |
58
+ | `exists` | `path` | `Promise<boolean>` |
59
+ | `realpath` | `path` | `Promise<string>` |
60
+ | `remove` | `path`, optional `{ recursive, force }` | `Promise<void>` |
61
+ | `rename` | `oldPath`, `newPath`, optional `{ overwrite }` | `Promise<void>` |
62
+ | `copy` | `source`, `destination`, optional `{ recursive, overwrite }` | `Promise<void>` |
63
+ | `clear` | `path?` (default `/`) | `Promise<void>` — empties the directory, keeps the directory itself |
64
+ | `index` | — | `Promise<Map<string, FileStat>>` |
65
+
66
+ ### Watch & lifecycle
67
+
68
+ | Method | Parameters | Returns |
69
+ | ------------ | --------------------------------------------------------------------- | ------------------------------------------------ |
70
+ | `watch` | `path`, optional `{ recursive, include, exclude }` | `() => void` unsubscribe |
71
+ | `unwatch` | `path` | `void` |
72
+ | `setOptions` | `options` — see [Options](#options) / [hashing](../guides/hashing.md) | `Promise<void>` |
73
+ | `dispose` | — | `void` — tears down watches, backend, and worker |
74
+
75
+ ### File descriptors
76
+
77
+ Dedicated / `OPFSSync` only — async throws `ENOTSUP`. Details: [file descriptors](./file-descriptors.md).
78
+
79
+ | Method | Parameters | Returns |
80
+ | ----------- | -------------------------------------------------- | ------------------------------------------- |
81
+ | `open` | `path`, optional `{ create, exclusive, truncate }` | `Promise<number>` — file descriptor |
82
+ | `read` | `fd`, `buffer`, `offset`, `length`, `position?` | `Promise<{ bytesRead, buffer }>` |
83
+ | `write` | `fd`, `buffer`, `offset?`, `length?`, `position?` | `Promise<number>` — bytes written |
84
+ | `close` | `fd` | `Promise<void>` |
85
+ | `fstat` | `fd` | `Promise<FileStat>` |
86
+ | `ftruncate` | `fd`, `size?` (default `0`) | `Promise<void>` |
87
+ | `fsync` | `fd` | `Promise<void>` — best-effort flush in OPFS |
88
+
89
+ ### Node aliases
90
+
91
+ | Alias | Parameters | Returns |
92
+ | --------- | --------------------------------------- | ----------------------------------------------- |
93
+ | `unlink` | `path` | same as `remove(path)` |
94
+ | `rm` | `path`, optional `{ recursive, force }` | same as `remove` |
95
+ | `rmdir` | `path` | same as `remove(path)` |
96
+ | `readdir` | `path` | same as `readDir` |
97
+ | `lstat` | `path` | same as `stat` |
98
+ | `chmod` | `path`, `mode` | `Promise<void>` — no-op (no Unix modes in OPFS) |
99
+
100
+ ### Backend access
101
+
102
+ `fs.backend` is the raw bytes API the facade wraps — a Comlink proxy to the worker for dedicated / SharedWorker, or the in-process `OPFSAsync` instance for async. Same methods as the facade, but without encoding helpers (you pass / get `Uint8Array`).
103
+
104
+ | Field | Type | Notes |
105
+ | ------------ | -------------------------------- | -------------------- | ---------- | ---------------------------------------------------- |
106
+ | `fs.backend` | `[OPFSApi](../types.md#opfsapi)` | bytes in / bytes out |
107
+ | `fs.worker` | `Worker | SharedWorker | undefined` | set when a worker was created; `undefined` for async |
108
+
109
+ ## Options
110
+
111
+ Passed to any `createOPFS*` (and to `setOptions()` later).
112
+
113
+ | Option | Default | What it does |
114
+ | ------------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
115
+ | `root` | `'/'` | Scope all paths under this OPFS subdirectory |
116
+ | `namespace` | `'opfs-worker:${root}'` | Stamp on every [watch](../guides/watching.md) event |
117
+ | `hashAlgorithm` | `'etag'` | File hash on `stat` / `index` / watch — `'etag'`, `'SHA-*'`, or `null`/`false` to disable. See [hashing](../guides/hashing.md) |
118
+ | `maxFileSize` | `50MB` | Skip SHA- hashing above this size (`etag` ignores it) |
119
+ | `broadcastChannel` | `'opfs-worker'` | Channel name, a `BroadcastChannel` instance, or `null` to disable |
120
+
121
+ ```typescript
122
+ const fs = createOPFS({
123
+ root: '/my-app',
124
+ hashAlgorithm: 'SHA-256',
125
+ maxFileSize: 10 * 1024 * 1024,
126
+ });
127
+ ```
128
+
129
+ ### Dedicated worker only
130
+
131
+ | Option | Type | Default | What it does |
132
+ | -------- | -------- | ------- | ------------------------------------------- | ---------------------------------------------------------------------------------- |
133
+ | `url` | `string | URL` | inlined worker | Script URL for `opfs-worker/dedicated.worker.js` (or your own) instead of the blob |
134
+ | `worker` | `Worker` | — | Pass an existing `Worker` (overrides `url`) |
135
+
136
+ Leave these unset unless you need them — the default inlined worker is enough.
137
+
138
+ ```typescript
139
+ const fs = createOPFS({ root: '/my-app' });
140
+
141
+ // Escape hatches only:
142
+ // createOPFS({ root: '/my-app', url: workerUrl }) // CSP without blob:
143
+ // createOPFS({ root: '/my-app', worker: existingWorker }) // bring your own Worker
144
+ ```
145
+
146
+ ### SharedWorker only
147
+
148
+ | Option | Type | Default | What it does |
149
+ | -------- | -------------- | --------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
150
+ | `url` | `string | URL` | next to the package | Script URL for `opfs-worker/shared.worker.js` — with Vite: `import url from '…/shared.worker.js?url'` |
151
+ | `worker` | `SharedWorker` | — | Pass an existing `SharedWorker` (overrides `url`) |
152
+ | `name` | `string` | `'opfs-worker'` | Tabs with the same script URL + name share one instance |
153
+
154
+ ```typescript
155
+ import workerUrl from 'opfs-worker/shared.worker.js?url'; // Vite
156
+ import { createOPFSShared } from 'opfs-worker/sharedworker';
157
+
158
+ const fs = createOPFSShared({
159
+ root: '/my-app',
160
+ url: workerUrl,
161
+ name: 'opfs-worker',
162
+ });
163
+ ```
164
+
165
+ ## Ready-made worker files
166
+
167
+ These files already contain a backend (`OPFSSync` or `OPFSAsync`) wrapped in Comlink expose.
168
+
169
+ | Export | Inside | Start with |
170
+ | --------------------------------- | --------------------- | -------------------------------------------------------- |
171
+ | `opfs-worker/dedicated.worker.js` | Comlink + `OPFSSync` | `new Worker(url, { type: 'module' })` or `{ url }` |
172
+ | `opfs-worker/shared.worker.js` | Comlink + `OPFSAsync` | `new SharedWorker(url, { type: 'module' })` or `{ url }` |
173
+
174
+ ### Use the facade with a worker URL
175
+
176
+ This is required for SharedWorker. For dedicated workers, use it only when the default inlined worker is blocked by CSP or you want to host the script yourself.
177
+
178
+ ```typescript
179
+ import workerUrl from 'opfs-worker/dedicated.worker.js?url';
180
+
181
+ const fs = createOPFS({ root: '/my-app', url: workerUrl });
182
+ ```
183
+
184
+ You can also create the `Worker` yourself and pass it to the facade. The facade still handles Comlink, options, encoding helpers, buffer transfers, and cleanup.
185
+
186
+ ```typescript
187
+ const worker = new Worker(workerUrl, { type: 'module' });
188
+ const fs = createOPFS({ root: '/my-app', worker });
189
+ ```
190
+
191
+ ### Use the worker without the facade
192
+
193
+ Wrap it with Comlink and configure the backend manually. This exposes the bytes API directly: no encoding helpers, automatic FD buffer transfers, or automatic worker termination.
194
+
195
+ ```typescript
196
+ import { wrap } from 'comlink';
197
+ import type { OPFSApi } from 'opfs-worker';
198
+ import workerUrl from 'opfs-worker/dedicated.worker.js?url';
199
+
200
+ const worker = new Worker(workerUrl, { type: 'module' });
201
+ const backend = wrap<OPFSApi>(worker);
202
+
203
+ await backend.setOptions({ root: '/my-app' });
204
+ await backend.writeFile('/hello.txt', new TextEncoder().encode('hello'));
205
+
206
+ await backend.dispose();
207
+ worker.terminate();
208
+ ```
209
+
210
+ Guides: [Dedicated worker](../guides/dedicated.md), [SharedWorker](../guides/sharedworker.md).
211
+
212
+ ## Trade-offs
213
+
214
+ | | Dedicated (`OPFSSync`) | Async (`OPFSAsync`) |
215
+ | ----------------------------------- | ------------------------------------------------ | ---------------------------- |
216
+ | File descriptors | yes | throw `ENOTSUP` |
217
+ | Browser support | every browser that has OPFS | Safari before 26 can’t write |
218
+ | SharedWorker | no | yes |
219
+ | One instance for all tabs | no | yes, with `createOPFSShared` |
220
+ | Bundle | ~80 KB inlined worker, or ready-made worker file | small via `/async` |
221
+ | Works under strict CSP (no `blob:`) | pass a worker `url` instead of the inlined blob | n/a |
@@ -0,0 +1,137 @@
1
+ # File Descriptors
2
+
3
+ Positional I/O on the **dedicated / sync** backend (`createOPFSDedicated` / `createOPFS`, or `OPFSSync` from `/pure`). Async and SharedWorker throw `ENOTSUP`.
4
+
5
+ If you call FD `read`/`write` on **`fs.backend`** from the main thread, transfer buffers with Comlink. The [facade](./README.md#facade) does that for you.
6
+
7
+ ```typescript
8
+ import { transfer } from 'comlink';
9
+ ```
10
+
11
+ ## `open(path, options?): Promise<number>`
12
+
13
+ ```typescript
14
+ const fd = await fs.open('/data/config.json');
15
+ const fd = await fs.open('/data/new.txt', { create: true });
16
+ const fd = await fs.open('/data/unique.txt', { create: true, exclusive: true });
17
+ const fd = await fs.open('/data/log.txt', { create: true, truncate: true });
18
+ ```
19
+
20
+ | Option | Default | |
21
+ | ----------- | ------- | ---------------------------------------------- |
22
+ | `create` | `false` | Create the file if missing |
23
+ | `exclusive` | `false` | With `create`, fail if the file already exists |
24
+ | `truncate` | `false` | Truncate to zero length |
25
+
26
+ Throws `AlreadyExistsError` (`exclusive`), `FileTypeError` (path is a directory), or `FileSystemOperationError`.
27
+
28
+ `exclusive` is best-effort only — two workers can still race. OPFS has no real file locking.
29
+
30
+ ## `read(fd, buffer, offset, length, position?): Promise<{ bytesRead, buffer }>`
31
+
32
+ ```typescript
33
+ const buffer = new Uint8Array(1024);
34
+ const { bytesRead, buffer: out } = await fs.read(fd, buffer, 0, 1024, null);
35
+ ```
36
+
37
+ - `position` `null` / `undefined` — current cursor, then advance
38
+ - `position` number — that offset, cursor unchanged
39
+ - Returns `bytesRead === 0` at EOF
40
+
41
+ With a Comlink proxy, buffers are transferred both ways. Use the **returned** buffer; the original is detached after transfer.
42
+
43
+ ```typescript
44
+ import { transfer } from 'comlink';
45
+
46
+ const buffer = new Uint8Array(64);
47
+ const { bytesRead, buffer: out } = await fs.read(
48
+ fd,
49
+ transfer(buffer, [buffer.buffer]),
50
+ 0,
51
+ buffer.length,
52
+ null
53
+ );
54
+ ```
55
+
56
+ Inside a worker with `OPFSSync` directly, plain buffers are fine — no `transfer`.
57
+
58
+ Chunked read from the main thread:
59
+
60
+ ```typescript
61
+ const fd = await fs.open('/data/large-file.txt');
62
+ const chunkSize = 1024;
63
+ let buffer = new Uint8Array(chunkSize);
64
+
65
+ try {
66
+ while (true) {
67
+ const result = await fs.read(
68
+ fd,
69
+ transfer(buffer, [buffer.buffer]),
70
+ 0,
71
+ chunkSize,
72
+ null
73
+ );
74
+ if (result.bytesRead === 0) break;
75
+ processChunk(result.buffer.subarray(0, result.bytesRead));
76
+ buffer = new Uint8Array(chunkSize);
77
+ }
78
+ } finally {
79
+ await fs.close(fd);
80
+ }
81
+ ```
82
+
83
+ Prefer small chunks over allocating a buffer the size of the whole file.
84
+
85
+ ## `write(fd, buffer, offset?, length?, position?): Promise<number>`
86
+
87
+ ```typescript
88
+ const data = new TextEncoder().encode('Hello, World!');
89
+ const bytesWritten = await fs.write(fd, data);
90
+ const bytesWritten2 = await fs.write(fd, data2, 0, 10, 100);
91
+ ```
92
+
93
+ Same `position` rules as `read`. Extends the file if you write past EOF. Triggers watch events.
94
+
95
+ ## Positioning
96
+
97
+ ```typescript
98
+ await fs.read(fd, buf, 0, 10, null); // cursor 0 → 10
99
+ await fs.read(fd, buf, 0, 10, null); // cursor 10 → 20
100
+ await fs.read(fd, buf, 0, 10, 0); // reads at 0, cursor stays 20
101
+ ```
102
+
103
+ ## `fstat` / `ftruncate` / `fsync`
104
+
105
+ ```typescript
106
+ const stats = await fs.fstat(fd);
107
+ await fs.ftruncate(fd, 5);
108
+ await fs.fsync(fd);
109
+ ```
110
+
111
+ `ftruncate` clamps the cursor if it sits past the new size, emits a watch event, and flushes.
112
+
113
+ `fsync` is best-effort. Unlike POSIX, the browser does not guarantee durability on power loss.
114
+
115
+ ## `close(fd)`
116
+
117
+ Always close in a `finally`. Pending writes are flushed; open FDs are also closed when the worker is disposed.
118
+
119
+ ```typescript
120
+ const fd = await fs.open('/data/file.txt');
121
+ try {
122
+ // …
123
+ } finally {
124
+ await fs.close(fd);
125
+ }
126
+ ```
127
+
128
+ ## Errors
129
+
130
+ | Error | When |
131
+ | -------------------------------------- | --------------------------------- |
132
+ | `AlreadyExistsError` | `exclusive: true` and file exists |
133
+ | `ExistenceError` | Missing path |
134
+ | `FileTypeError` | File vs directory mismatch |
135
+ | `ValidationError` | Bad offset / length / args |
136
+ | `FileSystemOperationError` / `IOError` | Underlying FS / I/O failure |
137
+ | `PermissionError` | Access denied |
@@ -1,6 +1,6 @@
1
- # Async (no worker)
1
+ # Async
2
2
 
3
- `OPFSAsync` on the current thread `getFile()` / `createWritable()`, no Comlink, no worker in the bundle.
3
+ `OPFSAsync` on the **current** thread (main or a worker you already run). Does not create a worker. Uses `getFile()` / `createWritable()`, no Comlink.
4
4
 
5
5
  Same limits apply as SharedWorker (it’s the same backend).
6
6
 
@@ -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` if you care about bundle size — the main entry also carries the dedicated-worker path.
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, awkward for tons of tiny random writes.
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).