opfs-worker 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +64 -428
  2. package/dist/BaseOPFS-BusBkyJX.js +2114 -0
  3. package/dist/BaseOPFS-BusBkyJX.js.map +1 -0
  4. package/dist/BaseOPFS-CYwrD7-q.cjs +4 -0
  5. package/dist/BaseOPFS-CYwrD7-q.cjs.map +1 -0
  6. package/dist/OPFSAsync-B2dR-2VR.cjs +2 -0
  7. package/dist/OPFSAsync-B2dR-2VR.cjs.map +1 -0
  8. package/dist/OPFSAsync-CO44W23e.js +124 -0
  9. package/dist/OPFSAsync-CO44W23e.js.map +1 -0
  10. package/dist/OPFSFacade-CEaSqTut.js +308 -0
  11. package/dist/OPFSFacade-CEaSqTut.js.map +1 -0
  12. package/dist/OPFSFacade-hrNXCn6w.cjs +2 -0
  13. package/dist/OPFSFacade-hrNXCn6w.cjs.map +1 -0
  14. package/dist/OPFSSync-BPrNtvFS.cjs +2 -0
  15. package/dist/OPFSSync-BPrNtvFS.cjs.map +1 -0
  16. package/dist/OPFSSync-D7obTsl3.js +208 -0
  17. package/dist/OPFSSync-D7obTsl3.js.map +1 -0
  18. package/dist/assets/dedicated.worker-DW6UqFo0.js.map +1 -0
  19. package/dist/{OPFSWorker.d.ts → core/BaseOPFS.d.ts} +53 -292
  20. package/dist/core/BaseOPFS.d.ts.map +1 -0
  21. package/dist/core/OPFSAsync.d.ts +55 -0
  22. package/dist/core/OPFSAsync.d.ts.map +1 -0
  23. package/dist/core/OPFSSync.d.ts +54 -0
  24. package/dist/core/OPFSSync.d.ts.map +1 -0
  25. package/dist/createOPFSAsync-DdhJYoC0.js +10 -0
  26. package/dist/createOPFSAsync-DdhJYoC0.js.map +1 -0
  27. package/dist/createOPFSAsync-DyWpvmL-.cjs +2 -0
  28. package/dist/createOPFSAsync-DyWpvmL-.cjs.map +1 -0
  29. package/dist/createOPFSDedicated-BwTc0NV5.js +2407 -0
  30. package/dist/createOPFSDedicated-BwTc0NV5.js.map +1 -0
  31. package/dist/createOPFSDedicated-CEeRexom.cjs +2357 -0
  32. package/dist/createOPFSDedicated-CEeRexom.cjs.map +1 -0
  33. package/dist/createOPFSShared-BSFWsoLe.js +23 -0
  34. package/dist/createOPFSShared-BSFWsoLe.js.map +1 -0
  35. package/dist/createOPFSShared-BoCft_BS.cjs +2 -0
  36. package/dist/createOPFSShared-BoCft_BS.cjs.map +1 -0
  37. package/dist/dedicated.worker.js +2355 -0
  38. package/dist/dedicated.worker.js.map +1 -0
  39. package/dist/{OPFSFacade.d.ts → facade/OPFSFacade.d.ts} +51 -25
  40. package/dist/facade/OPFSFacade.d.ts.map +1 -0
  41. package/dist/facade/createOPFSAsync.d.ts +16 -0
  42. package/dist/facade/createOPFSAsync.d.ts.map +1 -0
  43. package/dist/facade/createOPFSDedicated.d.ts +17 -0
  44. package/dist/facade/createOPFSDedicated.d.ts.map +1 -0
  45. package/dist/facade/createOPFSShared.d.ts +24 -0
  46. package/dist/facade/createOPFSShared.d.ts.map +1 -0
  47. package/dist/index.async.cjs +2 -0
  48. package/dist/index.async.cjs.map +1 -0
  49. package/dist/index.async.d.ts +10 -0
  50. package/dist/index.async.d.ts.map +1 -0
  51. package/dist/index.async.js +55 -0
  52. package/dist/index.async.js.map +1 -0
  53. package/dist/index.cjs +1 -2477
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.ts +17 -3
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +61 -2828
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.pure.cjs +1 -1
  60. package/dist/index.pure.cjs.map +1 -1
  61. package/dist/index.pure.d.ts +3 -1
  62. package/dist/index.pure.d.ts.map +1 -1
  63. package/dist/index.pure.js +50 -1053
  64. package/dist/index.pure.js.map +1 -1
  65. package/dist/index.sharedworker.cjs +2 -0
  66. package/dist/index.sharedworker.cjs.map +1 -0
  67. package/dist/index.sharedworker.d.ts +11 -0
  68. package/dist/index.sharedworker.d.ts.map +1 -0
  69. package/dist/index.sharedworker.js +55 -0
  70. package/dist/index.sharedworker.js.map +1 -0
  71. package/dist/index.sync.cjs +2 -0
  72. package/dist/index.sync.cjs.map +1 -0
  73. package/dist/index.sync.d.ts +11 -0
  74. package/dist/index.sync.d.ts.map +1 -0
  75. package/dist/index.sync.js +55 -0
  76. package/dist/index.sync.js.map +1 -0
  77. package/dist/shared.worker.js +2240 -0
  78. package/dist/shared.worker.js.map +1 -0
  79. package/dist/types.d.ts +42 -4
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/worker/createDedicatedWorker.d.ts +30 -0
  82. package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
  83. package/dist/worker/createSharedWorker.d.ts +37 -0
  84. package/dist/worker/createSharedWorker.d.ts.map +1 -0
  85. package/dist/worker/dedicated.worker.d.ts +2 -0
  86. package/dist/worker/dedicated.worker.d.ts.map +1 -0
  87. package/dist/worker/shared.worker.d.ts +2 -0
  88. package/dist/worker/shared.worker.d.ts.map +1 -0
  89. package/docs/api/backend.md +36 -0
  90. package/docs/api/create.md +82 -0
  91. package/docs/api/facade.md +53 -0
  92. package/docs/choosing-a-mode.md +41 -0
  93. package/docs/file-descriptors.md +46 -605
  94. package/docs/guides/async.md +36 -0
  95. package/docs/guides/dedicated.md +76 -0
  96. package/docs/guides/hashing.md +34 -0
  97. package/docs/guides/pure.md +46 -0
  98. package/docs/guides/sharedworker.md +57 -0
  99. package/docs/guides/streaming.md +185 -0
  100. package/docs/guides/watching.md +35 -0
  101. package/docs/migration.md +41 -0
  102. package/docs/types.md +105 -153
  103. package/package.json +53 -4
  104. package/src/{OPFSWorker.ts → core/BaseOPFS.ts} +159 -629
  105. package/src/core/OPFSAsync.ts +248 -0
  106. package/src/core/OPFSSync.ts +460 -0
  107. package/src/{OPFSFacade.ts → facade/OPFSFacade.ts} +134 -58
  108. package/src/facade/createOPFSAsync.ts +22 -0
  109. package/src/facade/createOPFSDedicated.ts +23 -0
  110. package/src/facade/createOPFSShared.ts +30 -0
  111. package/src/index.async.ts +11 -0
  112. package/src/index.pure.ts +3 -2
  113. package/src/index.sharedworker.ts +12 -0
  114. package/src/index.sync.ts +12 -0
  115. package/src/index.ts +37 -8
  116. package/src/types.ts +45 -4
  117. package/src/worker/createDedicatedWorker.ts +74 -0
  118. package/src/worker/createSharedWorker.ts +67 -0
  119. package/src/worker/dedicated.worker.ts +13 -0
  120. package/src/worker/shared.worker.ts +17 -0
  121. package/dist/OPFSFacade.d.ts.map +0 -1
  122. package/dist/OPFSWorker.d.ts.map +0 -1
  123. package/dist/assets/worker.entry-DUlEoroc.js.map +0 -1
  124. package/dist/createOPFSWorker.d.ts +0 -17
  125. package/dist/createOPFSWorker.d.ts.map +0 -1
  126. package/dist/helpers-DNj8ZoMu.cjs +0 -4
  127. package/dist/helpers-DNj8ZoMu.cjs.map +0 -1
  128. package/dist/helpers-WY2jfbOT.js +0 -1427
  129. package/dist/helpers-WY2jfbOT.js.map +0 -1
  130. package/dist/worker.entry.d.ts +0 -2
  131. package/dist/worker.entry.d.ts.map +0 -1
  132. package/docs/api-reference.md +0 -815
  133. package/src/createOPFSWorker.ts +0 -57
  134. package/src/worker.entry.ts +0 -6
@@ -1,5 +1,6 @@
1
- import { createOPFSWorker } from './createOPFSWorker';
2
- import { decodeBuffer, encodeString, isBinaryFileExtension } from './utils/encoder';
1
+ import { proxy, transfer } from 'comlink';
2
+
3
+ import { decodeBuffer, encodeString, isBinaryFileExtension } from '../utils/encoder';
3
4
 
4
5
  import type {
5
6
  BinaryEncoding,
@@ -7,13 +8,16 @@ import type {
7
8
  Encoding,
8
9
  FileOpenOptions,
9
10
  FileStat,
11
+ ImportFileData,
12
+ ImportFilesProgress,
13
+ ImportFilesResult,
14
+ OPFSApi,
10
15
  OPFSOptions,
11
16
  PathLike,
12
- RemoteOPFSWorker,
13
17
  RenameOptions,
14
18
  StringEncoding,
15
19
  WatchOptions
16
- } from './types';
20
+ } from '../types';
17
21
 
18
22
  /**
19
23
  * Utility function to convert a PathLike to a string path
@@ -27,22 +31,48 @@ function normalizePath(path: PathLike): string {
27
31
  return path;
28
32
  }
29
33
 
34
+ let createIndexWarned = false;
35
+
36
+ function warnCreateIndexDeprecated(): void {
37
+ if (createIndexWarned) {
38
+ return;
39
+ }
40
+
41
+ createIndexWarned = true;
42
+ console.warn('[opfs-worker] createIndex() is deprecated; use importFiles() instead');
43
+ }
44
+
45
+ /** Backend the facade talks to: an fs implementation plus its cleanup. */
46
+ export interface OPFSBackend {
47
+ fs: OPFSApi;
48
+ /** Underlying Worker / SharedWorker when the backend lives off-thread */
49
+ worker?: Worker | SharedWorker;
50
+ dispose: () => void;
51
+ }
52
+
30
53
  /**
31
- * Mode 1: Node-like facade with encoding helpers and string/binary auto-detection,
32
- * built on top of the raw worker API from {@link createOPFSWorker}.
54
+ * Node-like facade with encoding helpers and string/binary auto-detection.
33
55
  *
34
- * Use {@link createOPFS} to create one.
56
+ * Transport-agnostic: works over any {@link OPFSBackend} a Comlink proxy to
57
+ * a worker (`createOPFSDedicated`) or an in-process instance (`createOPFSAsync`).
58
+ *
59
+ * Escape hatch: {@link backend} is the raw bytes API; {@link worker} is the
60
+ * browser Worker / SharedWorker when one was created (undefined for async).
35
61
  */
36
62
  export class OPFSFacade {
37
- #fs: RemoteOPFSWorker;
63
+ #fs: OPFSApi;
38
64
  #dispose: () => void;
65
+ /** Raw backend (`OPFSApi`) — bytes in / bytes out, no encoding helpers */
66
+ readonly backend: OPFSApi;
67
+ /** Dedicated Worker or SharedWorker, if this facade was created with one */
68
+ readonly worker: Worker | SharedWorker | undefined;
39
69
  promises: OPFSFacade = this;
40
70
 
41
- constructor(options?: OPFSOptions) {
42
- const raw = createOPFSWorker(options);
43
-
44
- this.#fs = raw.fs;
45
- this.#dispose = raw.dispose;
71
+ constructor(backend: OPFSBackend) {
72
+ this.#fs = backend.fs;
73
+ this.backend = backend.fs;
74
+ this.worker = backend.worker;
75
+ this.#dispose = backend.dispose;
46
76
  }
47
77
 
48
78
  /**
@@ -120,12 +150,40 @@ export class OPFSFacade {
120
150
  return (encoding === 'binary') ? buffer : decodeBuffer(buffer, encoding);
121
151
  }
122
152
 
153
+ /**
154
+ * Normalize writable data into a Uint8Array.
155
+ *
156
+ * Accepts strings (encoded via `encoding`, auto-detected from the file
157
+ * extension when omitted), raw binary (`Uint8Array` / `ArrayBuffer`), and
158
+ * `Blob`/`File` sources (read via `arrayBuffer()`).
159
+ */
160
+ async #toBuffer(
161
+ path: string,
162
+ data: string | Uint8Array | ArrayBuffer | Blob,
163
+ encoding?: Encoding
164
+ ): Promise<Uint8Array> {
165
+ if (data instanceof Blob) {
166
+ return new Uint8Array(await data.arrayBuffer());
167
+ }
168
+
169
+ // If no encoding specified, auto-detect based on file extension
170
+ if (!encoding) {
171
+ encoding = (typeof data !== 'string' || isBinaryFileExtension(path)) ? 'binary' : 'utf-8';
172
+ }
173
+
174
+ if (typeof data === 'string') {
175
+ return encodeString(data, encoding);
176
+ }
177
+
178
+ return data instanceof Uint8Array ? data : new Uint8Array(data);
179
+ }
180
+
123
181
  /**
124
182
  * Write data to a file
125
183
  */
126
184
  async writeFile(
127
185
  path: PathLike,
128
- data: string | Uint8Array | ArrayBuffer,
186
+ data: string | Uint8Array | ArrayBuffer | Blob,
129
187
  options?: { encoding?: Encoding } | Encoding
130
188
  ): Promise<void> {
131
189
  const normalizedPath = normalizePath(path);
@@ -139,15 +197,7 @@ export class OPFSFacade {
139
197
  encoding = options.encoding;
140
198
  }
141
199
 
142
- // If no encoding specified, auto-detect based on file extension
143
- if (!encoding) {
144
- encoding = (typeof data !== 'string' || isBinaryFileExtension(normalizedPath)) ? 'binary' : 'utf-8';
145
- }
146
-
147
- // Convert data to Uint8Array
148
- const buffer = typeof data === 'string'
149
- ? encodeString(data, encoding)
150
- : (data instanceof Uint8Array ? data : new Uint8Array(data));
200
+ const buffer = await this.#toBuffer(normalizedPath, data, encoding);
151
201
 
152
202
  return this.#fs.writeFile(normalizedPath, buffer);
153
203
  }
@@ -157,24 +207,37 @@ export class OPFSFacade {
157
207
  */
158
208
  async appendFile(
159
209
  path: PathLike,
160
- data: string | Uint8Array | ArrayBuffer,
210
+ data: string | Uint8Array | ArrayBuffer | Blob,
161
211
  encoding?: Encoding
162
212
  ): Promise<void> {
163
213
  const normalizedPath = normalizePath(path);
164
214
 
165
- // If no encoding specified, auto-detect based on file extension
166
- if (!encoding) {
167
- encoding = (typeof data !== 'string' || isBinaryFileExtension(normalizedPath)) ? 'binary' : 'utf-8';
168
- }
169
-
170
- // Convert data to Uint8Array
171
- const buffer = typeof data === 'string'
172
- ? encodeString(data, encoding)
173
- : (data instanceof Uint8Array ? data : new Uint8Array(data));
215
+ const buffer = await this.#toBuffer(normalizedPath, data, encoding);
174
216
 
175
217
  return this.#fs.appendFile(normalizedPath, buffer);
176
218
  }
177
219
 
220
+ /**
221
+ * Create or overwrite a file from a byte stream without buffering the
222
+ * complete source in memory.
223
+ *
224
+ * Returns the total number of bytes written.
225
+ */
226
+ async importStream(
227
+ path: PathLike,
228
+ source: ReadableStream<Uint8Array> | Blob,
229
+ options?: { onProgress?: (bytesWritten: number) => void }
230
+ ): Promise<number> {
231
+ const normalizedPath = normalizePath(path);
232
+ const stream = source instanceof Blob ? source.stream() : source;
233
+ const transferredStream = transfer(stream, [stream]) as unknown as ReadableStream<Uint8Array>;
234
+ const onProgress = options?.onProgress
235
+ ? proxy((bytesWritten: number) => options.onProgress!(bytesWritten))
236
+ : undefined;
237
+
238
+ return this.#fs.writeStream(normalizedPath, transferredStream, onProgress);
239
+ }
240
+
178
241
  /**
179
242
  * Create a directory
180
243
  */
@@ -393,12 +456,45 @@ export class OPFSFacade {
393
456
  }
394
457
 
395
458
  /**
396
- * Synchronize the file system with external data
459
+ * Read a file as a lazy, disk-backed `Blob` without copying the data to memory.
460
+ *
461
+ * Ideal for `URL.createObjectURL()`: the browser streams `<video>` / `<audio>`
462
+ * on demand instead of loading the whole file first.
397
463
  */
398
- async createIndex(entries: [PathLike, string | Uint8Array | Blob][]): Promise<void> {
399
- const normalizedEntries = entries.map(([path, data]) => [normalizePath(path), data] as [string, string | Uint8Array | Blob]);
464
+ async readBlob(path: PathLike): Promise<Blob> {
465
+ return this.#fs.readBlob(normalizePath(path));
466
+ }
400
467
 
401
- return this.#fs.createIndex(normalizedEntries);
468
+ /**
469
+ * Bulk-import files from `[path, data]` entries (strings, bytes, Blobs, or Files).
470
+ * Each entry is streamed via {@link importStream} / `writeStream` so large
471
+ * Blobs/Files are not fully buffered in memory.
472
+ *
473
+ * Accepts an array of tuples, a `Map`, or any iterable of `[path, data]` pairs.
474
+ * Returns written paths, count, and total bytes.
475
+ */
476
+ async importFiles(
477
+ entries: Iterable<[PathLike, ImportFileData]> | Map<string, ImportFileData>,
478
+ options?: { onProgress?: (progress: ImportFilesProgress) => void }
479
+ ): Promise<ImportFilesResult> {
480
+ const normalizedEntries = [...entries].map(([path, data]) => [
481
+ normalizePath(path),
482
+ data,
483
+ ] as [string, ImportFileData]);
484
+
485
+ const onProgress = options?.onProgress
486
+ ? proxy((progress: ImportFilesProgress) => options.onProgress!(progress))
487
+ : undefined;
488
+
489
+ return this.#fs.importFiles(normalizedEntries, onProgress);
490
+ }
491
+
492
+ /**
493
+ * @deprecated Use {@link importFiles} instead.
494
+ */
495
+ async createIndex(entries: Iterable<[PathLike, ImportFileData]> | Map<string, ImportFileData>): Promise<void> {
496
+ warnCreateIndexDeprecated();
497
+ await this.importFiles(entries);
402
498
  }
403
499
 
404
500
  /**
@@ -432,29 +528,9 @@ export class OPFSFacade {
432
528
  }
433
529
 
434
530
  /**
435
- * Dispose of resources, detach the worker and clean up the file system instance
531
+ * Dispose of resources, detach the backend and clean up the file system instance
436
532
  */
437
533
  dispose() {
438
534
  this.#dispose();
439
535
  }
440
536
  }
441
-
442
- /**
443
- * Mode 1: start an inlined worker and get a Node-like `fs` API on top of it.
444
- *
445
- * For the raw worker API without the facade, use {@link createOPFSWorker}.
446
- * To use the worker class inside your own worker, see `opfs-worker/pure`.
447
- */
448
- export function createOPFS(options?: OPFSOptions): OPFSFacade {
449
- return new OPFSFacade(options);
450
- }
451
-
452
- /**
453
- * @deprecated Use {@link createOPFS}. Kept for 1.x compatibility — still returns the facade.
454
- */
455
- export const createWorker = createOPFS;
456
-
457
- /**
458
- * @deprecated Use {@link OPFSFacade}.
459
- */
460
- export { OPFSFacade as OPFSFileSystem };
@@ -0,0 +1,22 @@
1
+ import { OPFSFacade } from './OPFSFacade';
2
+ import { OPFSAsync } from '../core/OPFSAsync';
3
+
4
+ import type { OPFSOptions } from '../types';
5
+
6
+ /**
7
+ * Async mode: Node-like `fs` API over the promise-based File System API,
8
+ * without any worker.
9
+ *
10
+ * Prefer importing from `opfs-worker/async` when you care about bundle size
11
+ * (avoids the inlined dedicated worker). Importing from `opfs-worker` works too.
12
+ *
13
+ * Writing requires `FileSystemFileHandle.createWritable()` — Chrome, Firefox,
14
+ * Safari 26+. File descriptors (`open`/`read`/`write`/...) are not supported
15
+ * and throw `OperationNotSupportedError` — use `createOPFSDedicated` from `opfs-worker`
16
+ * or `opfs-worker/sync` for positional I/O.
17
+ */
18
+ export function createOPFSAsync(options?: OPFSOptions): OPFSFacade {
19
+ const fs = new OPFSAsync(options);
20
+
21
+ return new OPFSFacade({ fs, dispose: () => fs.dispose() });
22
+ }
@@ -0,0 +1,23 @@
1
+ import { OPFSFacade } from './OPFSFacade';
2
+ import { createDedicatedWorker } from '../worker/createDedicatedWorker';
3
+
4
+ import type { DedicatedWorkerOptions } from '../worker/createDedicatedWorker';
5
+
6
+ /**
7
+ * Dedicated mode: start a dedicated worker and get a Node-like `fs` API
8
+ * backed by `OPFSSync`.
9
+ *
10
+ * Prefer importing from `opfs-worker/sync` when you only need the worker backend
11
+ * (avoids pulling `OPFSAsync` into the graph). Importing from `opfs-worker` works too.
12
+ *
13
+ * By default uses an inlined worker. Pass `url` / `worker` to load
14
+ * `opfs-worker/dedicated.worker.js` instead (see {@link DedicatedWorkerOptions}).
15
+ *
16
+ * Need the raw bytes API or the Worker instance? Use `fs.backend` / `fs.worker`.
17
+ * For the workerless async backend, use {@link createOPFSAsync}.
18
+ */
19
+ export function createOPFSDedicated(options?: DedicatedWorkerOptions): OPFSFacade {
20
+ const { fs, worker, dispose } = createDedicatedWorker(options);
21
+
22
+ return new OPFSFacade({ fs, worker, dispose });
23
+ }
@@ -0,0 +1,30 @@
1
+ import { OPFSFacade } from './OPFSFacade';
2
+ import { createSharedWorker } from '../worker/createSharedWorker';
3
+
4
+ import type { SharedWorkerOptions } from '../worker/createSharedWorker';
5
+
6
+ /**
7
+ * Shared mode: Node-like `fs` API over a SharedWorker running one `OPFSAsync`
8
+ * instance for every tab — writes are serialized across tabs by per-path locks,
9
+ * watch events reach all tabs via `BroadcastChannel`.
10
+ *
11
+ * Uses the async backend (sync access handles are unavailable in SharedWorker):
12
+ * writing requires `createWritable()` (Chrome, Firefox, Safari 26+), and file
13
+ * descriptors throw `OperationNotSupportedError`.
14
+ *
15
+ * Need the raw bytes API or the SharedWorker? Use `fs.backend` / `fs.worker`.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Vite: pass the worker url explicitly
20
+ * import workerUrl from 'opfs-worker/shared.worker.js?url';
21
+ * import { createOPFSShared } from 'opfs-worker/sharedworker';
22
+ *
23
+ * const fs = createOPFSShared({ root: '/my-app', url: workerUrl });
24
+ * ```
25
+ */
26
+ export function createOPFSShared(options?: SharedWorkerOptions): OPFSFacade {
27
+ const { fs, worker, dispose } = createSharedWorker(options);
28
+
29
+ return new OPFSFacade({ fs, worker, dispose });
30
+ }
@@ -0,0 +1,11 @@
1
+ export * from './types';
2
+ export * from './utils/errors';
3
+ export * from './utils/helpers';
4
+ export * from './utils/encoder';
5
+
6
+ export { BaseOPFS } from './core/BaseOPFS';
7
+ export { OPFSAsync } from './core/OPFSAsync';
8
+ export { OPFSFacade } from './facade/OPFSFacade';
9
+ export type { OPFSBackend } from './facade/OPFSFacade';
10
+
11
+ export { createOPFSAsync } from './facade/createOPFSAsync';
package/src/index.pure.ts CHANGED
@@ -3,5 +3,6 @@ export * from './utils/errors';
3
3
  export * from './utils/helpers';
4
4
  export * from './utils/encoder';
5
5
 
6
- // Explicit re-export shadows the type-only `OPFSWorker` coming from './types'
7
- export { OPFSWorker } from './OPFSWorker';
6
+ export { BaseOPFS } from './core/BaseOPFS';
7
+ export { OPFSSync } from './core/OPFSSync';
8
+ export { OPFSAsync } from './core/OPFSAsync';
@@ -0,0 +1,12 @@
1
+ export * from './types';
2
+ export * from './utils/errors';
3
+ export * from './utils/helpers';
4
+ export * from './utils/encoder';
5
+
6
+ export { BaseOPFS } from './core/BaseOPFS';
7
+ export { OPFSAsync } from './core/OPFSAsync';
8
+ export { OPFSFacade } from './facade/OPFSFacade';
9
+ export type { OPFSBackend } from './facade/OPFSFacade';
10
+
11
+ export { createOPFSShared } from './facade/createOPFSShared';
12
+ export type { SharedWorkerOptions } from './worker/createSharedWorker';
@@ -0,0 +1,12 @@
1
+ export * from './types';
2
+ export * from './utils/errors';
3
+ export * from './utils/helpers';
4
+ export * from './utils/encoder';
5
+
6
+ export { BaseOPFS } from './core/BaseOPFS';
7
+ export { OPFSSync } from './core/OPFSSync';
8
+ export { OPFSFacade } from './facade/OPFSFacade';
9
+ export type { OPFSBackend } from './facade/OPFSFacade';
10
+
11
+ export { createOPFSDedicated } from './facade/createOPFSDedicated';
12
+ export type { DedicatedWorkerOptions } from './worker/createDedicatedWorker';
package/src/index.ts CHANGED
@@ -4,12 +4,41 @@ export * from './utils/helpers';
4
4
  export * from './utils/encoder';
5
5
 
6
6
  export {
7
+ BaseOPFS,
7
8
  OPFSFacade,
8
- OPFSFileSystem,
9
- createOPFS,
10
- // 1.x aliases
11
- createWorker,
12
- } from './OPFSFacade';
13
-
14
- export { createOPFSWorker } from './createOPFSWorker';
15
- export type { RawWorker } from './createOPFSWorker';
9
+ OPFSSync,
10
+ createOPFSDedicated,
11
+ } from './index.sync';
12
+
13
+ export type {
14
+ DedicatedWorkerOptions,
15
+ OPFSBackend,
16
+ } from './index.sync';
17
+
18
+ export {
19
+ OPFSAsync,
20
+ createOPFSAsync,
21
+ } from './index.async';
22
+
23
+ export {
24
+ createOPFSShared,
25
+ } from './index.sharedworker';
26
+
27
+ export type {
28
+ SharedWorkerOptions,
29
+ } from './index.sharedworker';
30
+
31
+ /**
32
+ * Kept as a short alias for a default Sync backend
33
+ */
34
+ export { createOPFSDedicated as createOPFS } from './index.sync';
35
+
36
+ /**
37
+ * @deprecated Use {@link createOPFSDedicated}. Kept for 1.x → 2.x migration.
38
+ */
39
+ export { createOPFSDedicated as createWorker } from './index.sync';
40
+
41
+ /**
42
+ * @deprecated Use {@link OPFSFacade}. Kept for 1.x → 2.x migration.
43
+ */
44
+ export { OPFSFacade as OPFSFileSystem } from './index.sync';
package/src/types.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { OPFSWorker } from './OPFSWorker';
2
- import type { Remote } from 'comlink';
1
+ import type { OPFSSync } from './core/OPFSSync';
3
2
 
4
3
  /**
5
4
  * Type for paths that can be either a string or URI
@@ -56,8 +55,13 @@ export interface WatchEvent {
56
55
  hash?: string;
57
56
  }
58
57
 
59
- export type { OPFSWorker };
60
- export type RemoteOPFSWorker = Remote<OPFSWorker>;
58
+ export type { OPFSSync };
59
+
60
+ /**
61
+ * Promise-based fs API surface shared by all backends: a Comlink proxy to
62
+ * `OPFSSync` in a worker, or an in-process `OPFSAsync` instance.
63
+ */
64
+ export type OPFSApi = { [K in keyof OPFSSync]: OPFSSync[K] };
61
65
 
62
66
  export interface OPFSOptions {
63
67
  /** Root path for the file system (default: '/') */
@@ -72,6 +76,43 @@ export interface OPFSOptions {
72
76
  broadcastChannel?: string | BroadcastChannel | null;
73
77
  }
74
78
 
79
+ /** Payload accepted by {@link importFiles} for a single entry. */
80
+ export type ImportFileData = string | Uint8Array | Blob;
81
+
82
+ /**
83
+ * Entries for {@link importFiles}: an array of `[path, data]` tuples, a `Map`,
84
+ * or any iterable that yields the same pairs.
85
+ */
86
+ export type ImportFilesEntries = Iterable<[string, ImportFileData]> | Map<string, ImportFileData>;
87
+
88
+ /** Progress event fired while {@link importFiles} writes entries. */
89
+ export interface ImportFilesProgress {
90
+ /** Path of the file currently being written */
91
+ path: string;
92
+ /** 0-based index of the current file */
93
+ index: number;
94
+ /** Total number of entries in this import */
95
+ count: number;
96
+ /** Bytes written for the current file so far */
97
+ bytesWritten: number;
98
+ /** Size of the current file in bytes */
99
+ bytesTotal: number;
100
+ /** Bytes written across all files so far */
101
+ totalBytesWritten: number;
102
+ /** Sum of all entry sizes in bytes */
103
+ totalBytes: number;
104
+ }
105
+
106
+ /** Result of a finished {@link importFiles} call. */
107
+ export interface ImportFilesResult {
108
+ /** Paths written, in import order */
109
+ paths: string[];
110
+ /** Number of files imported (`paths.length`) */
111
+ count: number;
112
+ /** Total bytes written across all files */
113
+ bytesWritten: number;
114
+ }
115
+
75
116
  export interface RenameOptions {
76
117
  /** Whether to overwrite existing files (default: false) */
77
118
  overwrite?: boolean;
@@ -0,0 +1,74 @@
1
+ import { wrap } from 'comlink';
2
+
3
+ import WorkerCtor from './dedicated.worker?worker&inline';
4
+
5
+ import type { OPFSOptions, OPFSSync } from '../types';
6
+ import type { Remote } from 'comlink';
7
+
8
+ const InlineWorker = WorkerCtor as new (options?: WorkerOptions) => Worker;
9
+
10
+ type RemoteOPFSSync = Remote<OPFSSync>;
11
+
12
+ export interface DedicatedWorkerOptions extends OPFSOptions {
13
+ /**
14
+ * URL of the worker script, e.g. `import url from 'opfs-worker/dedicated.worker.js?url'` (Vite).
15
+ *
16
+ * Defaults to the inlined worker (no URL needed). Pass an explicit url when you
17
+ * prefer the self-contained file (strict CSP without `blob:`, or DIY hosting).
18
+ */
19
+ url?: string | URL;
20
+ /** Bring your own Worker instance (overrides `url`) */
21
+ worker?: Worker;
22
+ }
23
+
24
+ export interface RawWorker {
25
+ /** Comlink proxy to `OPFSSync` (bytes in / bytes out) */
26
+ fs: RemoteOPFSSync;
27
+ /** Underlying browser Worker */
28
+ worker: Worker;
29
+ /** Calls worker `dispose()` then `terminate()` */
30
+ dispose: () => void;
31
+ }
32
+
33
+ /** A `BroadcastChannel` instance can't cross the wire — send its name instead. */
34
+ function applyWorkerOptions(fs: Pick<RemoteOPFSSync, 'setOptions'>, options?: OPFSOptions): void {
35
+ if (!options) {
36
+ return;
37
+ }
38
+
39
+ if (options.broadcastChannel instanceof BroadcastChannel) {
40
+ options.broadcastChannel = options.broadcastChannel.name;
41
+ }
42
+
43
+ void fs.setOptions(options);
44
+ }
45
+
46
+ /**
47
+ * Internal helper: spawn a dedicated worker and wrap `OPFSSync` with Comlink.
48
+ * Prefer {@link createOPFSDedicated}; access the proxy / Worker via
49
+ * `facade.backend` / `facade.worker`.
50
+ */
51
+ export function createDedicatedWorker(options: DedicatedWorkerOptions = {}): RawWorker {
52
+ const { url, worker: providedWorker, ...fsOptions } = options;
53
+
54
+ const worker = providedWorker
55
+ ?? (url ? new Worker(url, { type: 'module' }) : new InlineWorker());
56
+ const fs = wrap<RemoteOPFSSync>(worker);
57
+
58
+ applyWorkerOptions(fs, fsOptions);
59
+
60
+ return {
61
+ fs,
62
+ worker,
63
+ dispose() {
64
+ void (async() => {
65
+ try {
66
+ await fs.dispose();
67
+ }
68
+ finally {
69
+ worker.terminate();
70
+ }
71
+ })();
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,67 @@
1
+ import { wrap } from 'comlink';
2
+
3
+ import type { OPFSAsync } from '../core/OPFSAsync';
4
+ import type { OPFSOptions } from '../types';
5
+ import type { Remote } from 'comlink';
6
+
7
+ type RemoteOPFSAsync = Remote<OPFSAsync>;
8
+
9
+ export interface SharedWorkerOptions extends OPFSOptions {
10
+ /**
11
+ * URL of the worker script, e.g. `import url from 'opfs-worker/shared.worker.js?url'` (Vite).
12
+ *
13
+ * Defaults to `new URL('./shared.worker.js', import.meta.url)`, which works when
14
+ * `opfs-worker` is served as-is (CDN, unbundled deps). When your bundler inlines
15
+ * the package into app chunks, pass the url explicitly.
16
+ */
17
+ url?: string | URL;
18
+ /** Bring your own SharedWorker instance (overrides `url`) */
19
+ worker?: SharedWorker;
20
+ /** Worker name — tabs with the same script URL + name share one instance (default: 'opfs-worker') */
21
+ name?: string;
22
+ }
23
+
24
+ export interface RawSharedWorker {
25
+ /** Comlink proxy to the `OPFSAsync` instance shared by all connected tabs */
26
+ fs: RemoteOPFSAsync;
27
+ /** Underlying browser SharedWorker */
28
+ worker: SharedWorker;
29
+ /** Closes this tab's port; the worker keeps running for other tabs */
30
+ dispose: () => void;
31
+ }
32
+
33
+ /**
34
+ * Internal helper: connect to the SharedWorker and wrap `OPFSAsync` with Comlink.
35
+ * Prefer {@link createOPFSShared}; access the proxy / SharedWorker via
36
+ * `facade.backend` / `facade.worker`.
37
+ *
38
+ * Note: `OPFSOptions` are applied via `setOptions()` on the shared instance, so
39
+ * they affect every connected tab — use the same options in all tabs.
40
+ */
41
+ export function createSharedWorker(options: SharedWorkerOptions = {}): RawSharedWorker {
42
+ const { url, worker: providedWorker, name = 'opfs-worker', ...fsOptions } = options;
43
+
44
+ const worker = providedWorker ?? new SharedWorker(
45
+ url ?? new URL('./shared.worker.js', import.meta.url),
46
+ { type: 'module', name }
47
+ );
48
+
49
+ const fs = wrap<RemoteOPFSAsync>(worker.port);
50
+
51
+ if (Object.keys(fsOptions).length > 0) {
52
+ // A BroadcastChannel instance can't cross the wire — send its name instead
53
+ if (fsOptions.broadcastChannel instanceof BroadcastChannel) {
54
+ fsOptions.broadcastChannel = fsOptions.broadcastChannel.name;
55
+ }
56
+
57
+ void fs.setOptions(fsOptions);
58
+ }
59
+
60
+ return {
61
+ fs,
62
+ worker,
63
+ dispose() {
64
+ worker.port.close();
65
+ },
66
+ };
67
+ }
@@ -0,0 +1,13 @@
1
+ import { expose } from 'comlink';
2
+
3
+ import { OPFSSync } from '../core/OPFSSync';
4
+
5
+ /**
6
+ * Dedicated worker entry.
7
+ *
8
+ * Built two ways:
9
+ * - Inlined into `createOPFSDedicated` via `?worker&inline` (default path)
10
+ * - Self-contained `dist/dedicated.worker.js` → `opfs-worker/dedicated.worker.js`
11
+ * for DIY `new Worker(url, { type: 'module' })` + Comlink wrap
12
+ */
13
+ expose(new OPFSSync());