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,489 @@
1
+ import { proxy, transfer } from 'comlink';
2
+
3
+ import { decodeBuffer, encodeString, isBinaryFileExtension } from '../utils/encoder';
4
+
5
+ import type {
6
+ BinaryEncoding,
7
+ DirentData,
8
+ Encoding,
9
+ FileOpenOptions,
10
+ FileStat,
11
+ OPFSApi,
12
+ OPFSOptions,
13
+ PathLike,
14
+ RenameOptions,
15
+ StringEncoding,
16
+ WatchOptions
17
+ } from '../types';
18
+
19
+ /**
20
+ * Utility function to convert a PathLike to a string path
21
+ * If it's a URI, extracts the pathname; otherwise returns the string as-is
22
+ */
23
+ function normalizePath(path: PathLike): string {
24
+ if (path instanceof URL) {
25
+ return path.pathname;
26
+ }
27
+
28
+ return path;
29
+ }
30
+
31
+ /** Backend the facade talks to: an fs implementation plus its cleanup. */
32
+ export interface OPFSBackend {
33
+ fs: OPFSApi;
34
+ /** Underlying Worker / SharedWorker when the backend lives off-thread */
35
+ worker?: Worker | SharedWorker;
36
+ dispose: () => void;
37
+ }
38
+
39
+ /**
40
+ * Node-like facade with encoding helpers and string/binary auto-detection.
41
+ *
42
+ * Transport-agnostic: works over any {@link OPFSBackend} — a Comlink proxy to
43
+ * a worker (`createOPFSDedicated`) or an in-process instance (`createOPFSAsync`).
44
+ *
45
+ * Escape hatch: {@link backend} is the raw bytes API; {@link worker} is the
46
+ * browser Worker / SharedWorker when one was created (undefined for async).
47
+ */
48
+ export class OPFSFacade {
49
+ #fs: OPFSApi;
50
+ #dispose: () => void;
51
+ /** Raw backend (`OPFSApi`) — bytes in / bytes out, no encoding helpers */
52
+ readonly backend: OPFSApi;
53
+ /** Dedicated Worker or SharedWorker, if this facade was created with one */
54
+ readonly worker: Worker | SharedWorker | undefined;
55
+ promises: OPFSFacade = this;
56
+
57
+ constructor(backend: OPFSBackend) {
58
+ this.#fs = backend.fs;
59
+ this.backend = backend.fs;
60
+ this.worker = backend.worker;
61
+ this.#dispose = backend.dispose;
62
+ }
63
+
64
+ /**
65
+ * Start watching a file or directory for changes
66
+ */
67
+ watch(path: PathLike, options?: WatchOptions): () => void {
68
+ const normalizedPath = normalizePath(path);
69
+
70
+ void this.#fs.watch(normalizedPath, options);
71
+
72
+ return () => this.unwatch(normalizedPath);
73
+ }
74
+
75
+ /**
76
+ * Stop watching a previously watched path
77
+ */
78
+ unwatch(path: PathLike) {
79
+ const normalizedPath = normalizePath(path);
80
+
81
+ void this.#fs.unwatch(normalizedPath);
82
+ }
83
+
84
+ /**
85
+ * Update configuration options
86
+ */
87
+ async setOptions(options: OPFSOptions) {
88
+ return this.#fs.setOptions(options);
89
+ }
90
+
91
+ /**
92
+ * Get a complete index of all files and directories in the file system
93
+ */
94
+ async index(): Promise<Map<string, FileStat>> {
95
+ return this.#fs.index();
96
+ }
97
+
98
+ /**
99
+ * Read a file from the file system
100
+ */
101
+ // Overload for explicit string encoding - returns string
102
+ async readFile(path: PathLike, encoding: StringEncoding): Promise<string>;
103
+ // Overload for explicit binary encoding - returns Uint8Array
104
+ async readFile(path: PathLike, encoding: BinaryEncoding): Promise<Uint8Array>;
105
+ // Overload for options object with string encoding - returns string
106
+ async readFile(path: PathLike, options: { encoding: StringEncoding }): Promise<string>;
107
+ // Overload for options object with binary encoding - returns Uint8Array
108
+ async readFile(path: PathLike, options: { encoding: BinaryEncoding }): Promise<Uint8Array>;
109
+ // Overload for no encoding (auto-detected) - returns string | Uint8Array based on file extension
110
+ async readFile(path: PathLike): Promise<string | Uint8Array>;
111
+ // Implementation
112
+ async readFile(
113
+ path: PathLike,
114
+ optionsOrEncoding?: Encoding | { encoding?: Encoding }
115
+ ): Promise<string | Uint8Array> {
116
+ const normalizedPath = normalizePath(path);
117
+
118
+ // Handle both options object and direct encoding parameter for backward compatibility
119
+ let encoding: Encoding | undefined;
120
+
121
+ if (typeof optionsOrEncoding === 'string') {
122
+ encoding = optionsOrEncoding;
123
+ }
124
+ else if (optionsOrEncoding && typeof optionsOrEncoding === 'object') {
125
+ encoding = optionsOrEncoding.encoding;
126
+ }
127
+
128
+ // Same-path reads are serialized by the worker's exclusive path lock
129
+ const buffer = await this.#fs.readFile(normalizedPath);
130
+
131
+ // If no encoding specified, auto-detect based on file extension
132
+ if (!encoding) {
133
+ encoding = isBinaryFileExtension(normalizedPath) ? 'binary' : 'utf-8';
134
+ }
135
+
136
+ return (encoding === 'binary') ? buffer : decodeBuffer(buffer, encoding);
137
+ }
138
+
139
+ /**
140
+ * Normalize writable data into a Uint8Array.
141
+ *
142
+ * Accepts strings (encoded via `encoding`, auto-detected from the file
143
+ * extension when omitted), raw binary (`Uint8Array` / `ArrayBuffer`), and
144
+ * `Blob`/`File` sources (read via `arrayBuffer()`).
145
+ */
146
+ async #toBuffer(
147
+ path: string,
148
+ data: string | Uint8Array | ArrayBuffer | Blob,
149
+ encoding?: Encoding
150
+ ): Promise<Uint8Array> {
151
+ if (data instanceof Blob) {
152
+ return new Uint8Array(await data.arrayBuffer());
153
+ }
154
+
155
+ // If no encoding specified, auto-detect based on file extension
156
+ if (!encoding) {
157
+ encoding = (typeof data !== 'string' || isBinaryFileExtension(path)) ? 'binary' : 'utf-8';
158
+ }
159
+
160
+ if (typeof data === 'string') {
161
+ return encodeString(data, encoding);
162
+ }
163
+
164
+ return data instanceof Uint8Array ? data : new Uint8Array(data);
165
+ }
166
+
167
+ /**
168
+ * Write data to a file
169
+ */
170
+ async writeFile(
171
+ path: PathLike,
172
+ data: string | Uint8Array | ArrayBuffer | Blob,
173
+ options?: { encoding?: Encoding } | Encoding
174
+ ): Promise<void> {
175
+ const normalizedPath = normalizePath(path);
176
+
177
+ let encoding: Encoding | undefined;
178
+
179
+ if (typeof options === 'string') {
180
+ encoding = options;
181
+ }
182
+ else if (options && typeof options === 'object') {
183
+ encoding = options.encoding;
184
+ }
185
+
186
+ const buffer = await this.#toBuffer(normalizedPath, data, encoding);
187
+
188
+ return this.#fs.writeFile(normalizedPath, buffer);
189
+ }
190
+
191
+ /**
192
+ * Append data to a file
193
+ */
194
+ async appendFile(
195
+ path: PathLike,
196
+ data: string | Uint8Array | ArrayBuffer | Blob,
197
+ encoding?: Encoding
198
+ ): Promise<void> {
199
+ const normalizedPath = normalizePath(path);
200
+
201
+ const buffer = await this.#toBuffer(normalizedPath, data, encoding);
202
+
203
+ return this.#fs.appendFile(normalizedPath, buffer);
204
+ }
205
+
206
+ /**
207
+ * Create or overwrite a file from a byte stream without buffering the
208
+ * complete source in memory.
209
+ *
210
+ * Returns the total number of bytes written.
211
+ */
212
+ async importStream(
213
+ path: PathLike,
214
+ source: ReadableStream<Uint8Array> | Blob,
215
+ options?: { onProgress?: (bytesWritten: number) => void }
216
+ ): Promise<number> {
217
+ const normalizedPath = normalizePath(path);
218
+ const stream = source instanceof Blob ? source.stream() : source;
219
+ const transferredStream = transfer(stream, [stream]) as unknown as ReadableStream<Uint8Array>;
220
+ const onProgress = options?.onProgress
221
+ ? proxy((bytesWritten: number) => options.onProgress!(bytesWritten))
222
+ : undefined;
223
+
224
+ return this.#fs.writeStream(normalizedPath, transferredStream, onProgress);
225
+ }
226
+
227
+ /**
228
+ * Create a directory
229
+ */
230
+ async mkdir(path: PathLike, mode?: number | { recursive?: boolean }): Promise<void> {
231
+ const normalizedPath = normalizePath(path);
232
+
233
+ let options: { recursive?: boolean } | undefined;
234
+
235
+ // OPFS doesn't support file modes, so we ignore the mode parameter
236
+ if (typeof mode === 'number') {
237
+ options = { recursive: false };
238
+ }
239
+ else {
240
+ options = mode;
241
+ }
242
+
243
+ return this.#fs.mkdir(normalizedPath, options);
244
+ }
245
+
246
+ /**
247
+ * Get file or directory statistics
248
+ */
249
+ async stat(path: PathLike): Promise<FileStat> {
250
+ const normalizedPath = normalizePath(path);
251
+
252
+ return this.#fs.stat(normalizedPath);
253
+ }
254
+
255
+ /**
256
+ * Read a directory's contents
257
+ */
258
+ async readDir(path: PathLike): Promise<DirentData[]> {
259
+ const normalizedPath = normalizePath(path);
260
+
261
+ return this.#fs.readDir(normalizedPath);
262
+ }
263
+
264
+ /**
265
+ * Check if a file or directory exists
266
+ */
267
+ async exists(path: PathLike): Promise<boolean> {
268
+ const normalizedPath = normalizePath(path);
269
+
270
+ return this.#fs.exists(normalizedPath);
271
+ }
272
+
273
+ /**
274
+ * Clear all contents of a directory without removing the directory itself
275
+ */
276
+ async clear(path?: PathLike): Promise<void> {
277
+ const normalizedPath = path ? normalizePath(path) : undefined;
278
+
279
+ return this.#fs.clear(normalizedPath);
280
+ }
281
+
282
+ /**
283
+ * Remove files and directories
284
+ */
285
+ async remove(path: PathLike, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
286
+ const normalizedPath = normalizePath(path);
287
+
288
+ return this.#fs.remove(normalizedPath, options);
289
+ }
290
+
291
+ /**
292
+ * Alias for remove() for NodeJS like API compatibility
293
+ */
294
+ async unlink(path: PathLike): Promise<void> {
295
+ return this.remove(path);
296
+ }
297
+
298
+ /**
299
+ * Alias for remove() for NodeJS like API compatibility
300
+ */
301
+ async rm(path: PathLike, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
302
+ return this.remove(path, options);
303
+ }
304
+
305
+ /**
306
+ * Alias for remove() for NodeJS like API compatibility
307
+ */
308
+ async rmdir(path: PathLike): Promise<void> {
309
+ return this.remove(path);
310
+ }
311
+
312
+ /**
313
+ * Alias for readDir() for NodeJS like API compatibility
314
+ */
315
+ async readdir(path: PathLike, _options?: unknown): Promise<DirentData[]> {
316
+ return this.readDir(path);
317
+ }
318
+
319
+ /**
320
+ * Alias for stat() for NodeJS like API compatibility
321
+ */
322
+ async lstat(path: PathLike): Promise<FileStat> {
323
+ return this.stat(path);
324
+ }
325
+
326
+ /**
327
+ * Note: OPFS doesn't support file modes, so this is a no-op and exists only for compatibility with tools like isomorphic-git
328
+ */
329
+ async chmod(_path: PathLike, _mode: number): Promise<void> {
330
+ return Promise.resolve();
331
+ }
332
+
333
+ /**
334
+ * Resolve a path to an absolute path
335
+ */
336
+ async realpath(path: PathLike): Promise<string> {
337
+ const normalizedPath = normalizePath(path);
338
+
339
+ return this.#fs.realpath(normalizedPath);
340
+ }
341
+
342
+ /**
343
+ * Rename a file or directory
344
+ */
345
+ async rename(oldPath: PathLike, newPath: PathLike, options?: RenameOptions): Promise<void> {
346
+ const normalizedOldPath = normalizePath(oldPath);
347
+ const normalizedNewPath = normalizePath(newPath);
348
+
349
+ return this.#fs.rename(normalizedOldPath, normalizedNewPath, options);
350
+ }
351
+
352
+ /**
353
+ * Copy files and directories
354
+ */
355
+ async copy(source: PathLike, destination: PathLike, options?: { recursive?: boolean; overwrite?: boolean }): Promise<void> {
356
+ const normalizedSource = normalizePath(source);
357
+ const normalizedDestination = normalizePath(destination);
358
+
359
+ return this.#fs.copy(normalizedSource, normalizedDestination, options);
360
+ }
361
+
362
+ /**
363
+ * Open a file and return a file descriptor
364
+ */
365
+ async open(path: PathLike, options?: FileOpenOptions): Promise<number> {
366
+ const normalizedPath = normalizePath(path);
367
+
368
+ return this.#fs.open(normalizedPath, options);
369
+ }
370
+
371
+ /**
372
+ * Close a file descriptor
373
+ */
374
+ async close(fd: number): Promise<void> {
375
+ return this.#fs.close(fd);
376
+ }
377
+
378
+ /**
379
+ * Read data from a file descriptor
380
+ *
381
+ * This method requires special handling due to Comlink transfer requirements.
382
+ * The buffer is transferred to the worker and back, so the original buffer
383
+ * becomes unusable after the call.
384
+ */
385
+ async read(
386
+ fd: number,
387
+ buffer: Uint8Array,
388
+ offset: number,
389
+ length: number,
390
+ position?: number | null | undefined
391
+ ): Promise<{ bytesRead: number; buffer: Uint8Array }> {
392
+ const { bytesRead, buffer: transferred } = await this.#fs.read(
393
+ fd,
394
+ // Temp buffer to preserve the original buffer
395
+ new Uint8Array(length),
396
+ 0,
397
+ length,
398
+ position
399
+ );
400
+
401
+ // Copy the data from the transferred buffer to the original buffer
402
+ if (bytesRead > 0) {
403
+ buffer.set(transferred.subarray(0, bytesRead), offset);
404
+ }
405
+
406
+ return { bytesRead, buffer };
407
+ }
408
+
409
+ /**
410
+ * Write data to a file descriptor
411
+ */
412
+ async write(
413
+ fd: number,
414
+ buffer: Uint8Array,
415
+ offset?: number,
416
+ length?: number,
417
+ position?: number | null | undefined,
418
+ emitEvent?: boolean
419
+ ): Promise<number> {
420
+ return this.#fs.write(fd, buffer, offset, length, position, emitEvent);
421
+ }
422
+
423
+ /**
424
+ * Get file status information by file descriptor
425
+ */
426
+ async fstat(fd: number): Promise<FileStat> {
427
+ return this.#fs.fstat(fd);
428
+ }
429
+
430
+ /**
431
+ * Truncate file to specified size
432
+ */
433
+ async ftruncate(fd: number, size?: number): Promise<void> {
434
+ return this.#fs.ftruncate(fd, size);
435
+ }
436
+
437
+ /**
438
+ * Synchronize file data to storage (fsync equivalent)
439
+ */
440
+ async fsync(fd: number): Promise<void> {
441
+ return this.#fs.fsync(fd);
442
+ }
443
+
444
+ /**
445
+ * Synchronize the file system with external data
446
+ */
447
+ async createIndex(entries: [PathLike, string | Uint8Array | Blob][]): Promise<void> {
448
+ const normalizedEntries = entries.map(([path, data]) => [normalizePath(path), data] as [string, string | Uint8Array | Blob]);
449
+
450
+ return this.#fs.createIndex(normalizedEntries);
451
+ }
452
+
453
+ /**
454
+ * Read a file as text with automatic encoding detection
455
+ */
456
+ async readText(path: PathLike, encoding: Encoding = 'utf-8'): Promise<string> {
457
+ const normalizedPath = normalizePath(path);
458
+ const buffer = await this.#fs.readFile(normalizedPath);
459
+
460
+ return decodeBuffer(buffer, encoding);
461
+ }
462
+
463
+ /**
464
+ * Write text to a file with specified encoding
465
+ */
466
+ async writeText(path: PathLike, text: string, encoding: Encoding = 'utf-8'): Promise<void> {
467
+ const normalizedPath = normalizePath(path);
468
+ const buffer = encodeString(text, encoding);
469
+
470
+ return this.#fs.writeFile(normalizedPath, buffer);
471
+ }
472
+
473
+ /**
474
+ * Append text to a file with specified encoding
475
+ */
476
+ async appendText(path: PathLike, text: string, encoding: Encoding = 'utf-8'): Promise<void> {
477
+ const normalizedPath = normalizePath(path);
478
+ const buffer = encodeString(text, encoding);
479
+
480
+ return this.#fs.appendFile(normalizedPath, buffer);
481
+ }
482
+
483
+ /**
484
+ * Dispose of resources, detach the backend and clean up the file system instance
485
+ */
486
+ dispose() {
487
+ this.#dispose();
488
+ }
489
+ }
@@ -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';
@@ -0,0 +1,8 @@
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 { 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 ADDED
@@ -0,0 +1,44 @@
1
+ export * from './types';
2
+ export * from './utils/errors';
3
+ export * from './utils/helpers';
4
+ export * from './utils/encoder';
5
+
6
+ export {
7
+ BaseOPFS,
8
+ OPFSFacade,
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';