opfs-worker 1.3.2 → 2.0.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 (65) hide show
  1. package/README.md +89 -106
  2. package/dist/{facade.d.ts → OPFSFacade.d.ts} +22 -5
  3. package/dist/OPFSFacade.d.ts.map +1 -0
  4. package/dist/{worker.d.ts → OPFSWorker.d.ts} +10 -6
  5. package/dist/OPFSWorker.d.ts.map +1 -0
  6. package/dist/assets/worker.entry-DUlEoroc.js.map +1 -0
  7. package/dist/createOPFSWorker.d.ts +17 -0
  8. package/dist/createOPFSWorker.d.ts.map +1 -0
  9. package/dist/helpers-DNj8ZoMu.cjs +4 -0
  10. package/dist/helpers-DNj8ZoMu.cjs.map +1 -0
  11. package/dist/{helpers-Ca2c767K.js → helpers-WY2jfbOT.js} +221 -221
  12. package/dist/helpers-WY2jfbOT.js.map +1 -0
  13. package/dist/index.cjs +296 -285
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +3 -9
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +369 -340
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.pure.cjs +2 -0
  20. package/dist/index.pure.cjs.map +1 -0
  21. package/dist/index.pure.d.ts +6 -0
  22. package/dist/index.pure.d.ts.map +1 -0
  23. package/dist/{raw.js → index.pure.js} +241 -186
  24. package/dist/index.pure.js.map +1 -0
  25. package/dist/types.d.ts +1 -1
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/utils/helpers.d.ts +11 -1
  28. package/dist/utils/helpers.d.ts.map +1 -1
  29. package/dist/worker.entry.d.ts +2 -0
  30. package/dist/worker.entry.d.ts.map +1 -0
  31. package/docs/api-reference.md +815 -0
  32. package/docs/file-descriptors.md +696 -0
  33. package/docs/types.md +232 -0
  34. package/package.json +14 -8
  35. package/src/OPFSFacade.ts +460 -0
  36. package/src/OPFSWorker.ts +1544 -0
  37. package/src/createOPFSWorker.ts +57 -0
  38. package/src/index.pure.ts +7 -0
  39. package/src/index.ts +15 -0
  40. package/src/types.ts +99 -0
  41. package/src/utils/encoder.ts +205 -0
  42. package/src/utils/errors.ts +297 -0
  43. package/src/utils/helpers.ts +465 -0
  44. package/src/worker.entry.ts +6 -0
  45. package/dist/assets/worker-1Wh1cr7P.js.map +0 -1
  46. package/dist/assets/worker-BeJaVyBV.js.map +0 -1
  47. package/dist/assets/worker-DYYLzR1Y.js.map +0 -1
  48. package/dist/facade.d.ts.map +0 -1
  49. package/dist/helpers-BuGfPAg0.js +0 -1439
  50. package/dist/helpers-BuGfPAg0.js.map +0 -1
  51. package/dist/helpers-CF7A2WQG.cjs +0 -4
  52. package/dist/helpers-CF7A2WQG.cjs.map +0 -1
  53. package/dist/helpers-CIiblZ8d.cjs +0 -4
  54. package/dist/helpers-CIiblZ8d.cjs.map +0 -1
  55. package/dist/helpers-CKOebsbw.js +0 -1423
  56. package/dist/helpers-CKOebsbw.js.map +0 -1
  57. package/dist/helpers-Ca2c767K.js.map +0 -1
  58. package/dist/helpers-Dwc92hv9.cjs +0 -4
  59. package/dist/helpers-Dwc92hv9.cjs.map +0 -1
  60. package/dist/raw.cjs +0 -2
  61. package/dist/raw.cjs.map +0 -1
  62. package/dist/raw.d.ts +0 -13
  63. package/dist/raw.d.ts.map +0 -1
  64. package/dist/raw.js.map +0 -1
  65. package/dist/worker.d.ts.map +0 -1
@@ -0,0 +1,460 @@
1
+ import { createOPFSWorker } from './createOPFSWorker';
2
+ import { decodeBuffer, encodeString, isBinaryFileExtension } from './utils/encoder';
3
+
4
+ import type {
5
+ BinaryEncoding,
6
+ DirentData,
7
+ Encoding,
8
+ FileOpenOptions,
9
+ FileStat,
10
+ OPFSOptions,
11
+ PathLike,
12
+ RemoteOPFSWorker,
13
+ RenameOptions,
14
+ StringEncoding,
15
+ WatchOptions
16
+ } from './types';
17
+
18
+ /**
19
+ * Utility function to convert a PathLike to a string path
20
+ * If it's a URI, extracts the pathname; otherwise returns the string as-is
21
+ */
22
+ function normalizePath(path: PathLike): string {
23
+ if (path instanceof URL) {
24
+ return path.pathname;
25
+ }
26
+
27
+ return path;
28
+ }
29
+
30
+ /**
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}.
33
+ *
34
+ * Use {@link createOPFS} to create one.
35
+ */
36
+ export class OPFSFacade {
37
+ #fs: RemoteOPFSWorker;
38
+ #dispose: () => void;
39
+ promises: OPFSFacade = this;
40
+
41
+ constructor(options?: OPFSOptions) {
42
+ const raw = createOPFSWorker(options);
43
+
44
+ this.#fs = raw.fs;
45
+ this.#dispose = raw.dispose;
46
+ }
47
+
48
+ /**
49
+ * Start watching a file or directory for changes
50
+ */
51
+ watch(path: PathLike, options?: WatchOptions): () => void {
52
+ const normalizedPath = normalizePath(path);
53
+
54
+ void this.#fs.watch(normalizedPath, options);
55
+
56
+ return () => this.unwatch(normalizedPath);
57
+ }
58
+
59
+ /**
60
+ * Stop watching a previously watched path
61
+ */
62
+ unwatch(path: PathLike) {
63
+ const normalizedPath = normalizePath(path);
64
+
65
+ void this.#fs.unwatch(normalizedPath);
66
+ }
67
+
68
+ /**
69
+ * Update configuration options
70
+ */
71
+ async setOptions(options: OPFSOptions) {
72
+ return this.#fs.setOptions(options);
73
+ }
74
+
75
+ /**
76
+ * Get a complete index of all files and directories in the file system
77
+ */
78
+ async index(): Promise<Map<string, FileStat>> {
79
+ return this.#fs.index();
80
+ }
81
+
82
+ /**
83
+ * Read a file from the file system
84
+ */
85
+ // Overload for explicit string encoding - returns string
86
+ async readFile(path: PathLike, encoding: StringEncoding): Promise<string>;
87
+ // Overload for explicit binary encoding - returns Uint8Array
88
+ async readFile(path: PathLike, encoding: BinaryEncoding): Promise<Uint8Array>;
89
+ // Overload for options object with string encoding - returns string
90
+ async readFile(path: PathLike, options: { encoding: StringEncoding }): Promise<string>;
91
+ // Overload for options object with binary encoding - returns Uint8Array
92
+ async readFile(path: PathLike, options: { encoding: BinaryEncoding }): Promise<Uint8Array>;
93
+ // Overload for no encoding (auto-detected) - returns string | Uint8Array based on file extension
94
+ async readFile(path: PathLike): Promise<string | Uint8Array>;
95
+ // Implementation
96
+ async readFile(
97
+ path: PathLike,
98
+ optionsOrEncoding?: Encoding | { encoding?: Encoding }
99
+ ): Promise<string | Uint8Array> {
100
+ const normalizedPath = normalizePath(path);
101
+
102
+ // Handle both options object and direct encoding parameter for backward compatibility
103
+ let encoding: Encoding | undefined;
104
+
105
+ if (typeof optionsOrEncoding === 'string') {
106
+ encoding = optionsOrEncoding;
107
+ }
108
+ else if (optionsOrEncoding && typeof optionsOrEncoding === 'object') {
109
+ encoding = optionsOrEncoding.encoding;
110
+ }
111
+
112
+ // Same-path reads are serialized by the worker's exclusive path lock
113
+ const buffer = await this.#fs.readFile(normalizedPath);
114
+
115
+ // If no encoding specified, auto-detect based on file extension
116
+ if (!encoding) {
117
+ encoding = isBinaryFileExtension(normalizedPath) ? 'binary' : 'utf-8';
118
+ }
119
+
120
+ return (encoding === 'binary') ? buffer : decodeBuffer(buffer, encoding);
121
+ }
122
+
123
+ /**
124
+ * Write data to a file
125
+ */
126
+ async writeFile(
127
+ path: PathLike,
128
+ data: string | Uint8Array | ArrayBuffer,
129
+ options?: { encoding?: Encoding } | Encoding
130
+ ): Promise<void> {
131
+ const normalizedPath = normalizePath(path);
132
+
133
+ let encoding: Encoding | undefined;
134
+
135
+ if (typeof options === 'string') {
136
+ encoding = options;
137
+ }
138
+ else if (options && typeof options === 'object') {
139
+ encoding = options.encoding;
140
+ }
141
+
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));
151
+
152
+ return this.#fs.writeFile(normalizedPath, buffer);
153
+ }
154
+
155
+ /**
156
+ * Append data to a file
157
+ */
158
+ async appendFile(
159
+ path: PathLike,
160
+ data: string | Uint8Array | ArrayBuffer,
161
+ encoding?: Encoding
162
+ ): Promise<void> {
163
+ const normalizedPath = normalizePath(path);
164
+
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));
174
+
175
+ return this.#fs.appendFile(normalizedPath, buffer);
176
+ }
177
+
178
+ /**
179
+ * Create a directory
180
+ */
181
+ async mkdir(path: PathLike, mode?: number | { recursive?: boolean }): Promise<void> {
182
+ const normalizedPath = normalizePath(path);
183
+
184
+ let options: { recursive?: boolean } | undefined;
185
+
186
+ // OPFS doesn't support file modes, so we ignore the mode parameter
187
+ if (typeof mode === 'number') {
188
+ options = { recursive: false };
189
+ }
190
+ else {
191
+ options = mode;
192
+ }
193
+
194
+ return this.#fs.mkdir(normalizedPath, options);
195
+ }
196
+
197
+ /**
198
+ * Get file or directory statistics
199
+ */
200
+ async stat(path: PathLike): Promise<FileStat> {
201
+ const normalizedPath = normalizePath(path);
202
+
203
+ return this.#fs.stat(normalizedPath);
204
+ }
205
+
206
+ /**
207
+ * Read a directory's contents
208
+ */
209
+ async readDir(path: PathLike): Promise<DirentData[]> {
210
+ const normalizedPath = normalizePath(path);
211
+
212
+ return this.#fs.readDir(normalizedPath);
213
+ }
214
+
215
+ /**
216
+ * Check if a file or directory exists
217
+ */
218
+ async exists(path: PathLike): Promise<boolean> {
219
+ const normalizedPath = normalizePath(path);
220
+
221
+ return this.#fs.exists(normalizedPath);
222
+ }
223
+
224
+ /**
225
+ * Clear all contents of a directory without removing the directory itself
226
+ */
227
+ async clear(path?: PathLike): Promise<void> {
228
+ const normalizedPath = path ? normalizePath(path) : undefined;
229
+
230
+ return this.#fs.clear(normalizedPath);
231
+ }
232
+
233
+ /**
234
+ * Remove files and directories
235
+ */
236
+ async remove(path: PathLike, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
237
+ const normalizedPath = normalizePath(path);
238
+
239
+ return this.#fs.remove(normalizedPath, options);
240
+ }
241
+
242
+ /**
243
+ * Alias for remove() for NodeJS like API compatibility
244
+ */
245
+ async unlink(path: PathLike): Promise<void> {
246
+ return this.remove(path);
247
+ }
248
+
249
+ /**
250
+ * Alias for remove() for NodeJS like API compatibility
251
+ */
252
+ async rm(path: PathLike, options?: { recursive?: boolean; force?: boolean }): Promise<void> {
253
+ return this.remove(path, options);
254
+ }
255
+
256
+ /**
257
+ * Alias for remove() for NodeJS like API compatibility
258
+ */
259
+ async rmdir(path: PathLike): Promise<void> {
260
+ return this.remove(path);
261
+ }
262
+
263
+ /**
264
+ * Alias for readDir() for NodeJS like API compatibility
265
+ */
266
+ async readdir(path: PathLike, _options?: unknown): Promise<DirentData[]> {
267
+ return this.readDir(path);
268
+ }
269
+
270
+ /**
271
+ * Alias for stat() for NodeJS like API compatibility
272
+ */
273
+ async lstat(path: PathLike): Promise<FileStat> {
274
+ return this.stat(path);
275
+ }
276
+
277
+ /**
278
+ * Note: OPFS doesn't support file modes, so this is a no-op and exists only for compatibility with tools like isomorphic-git
279
+ */
280
+ async chmod(_path: PathLike, _mode: number): Promise<void> {
281
+ return Promise.resolve();
282
+ }
283
+
284
+ /**
285
+ * Resolve a path to an absolute path
286
+ */
287
+ async realpath(path: PathLike): Promise<string> {
288
+ const normalizedPath = normalizePath(path);
289
+
290
+ return this.#fs.realpath(normalizedPath);
291
+ }
292
+
293
+ /**
294
+ * Rename a file or directory
295
+ */
296
+ async rename(oldPath: PathLike, newPath: PathLike, options?: RenameOptions): Promise<void> {
297
+ const normalizedOldPath = normalizePath(oldPath);
298
+ const normalizedNewPath = normalizePath(newPath);
299
+
300
+ return this.#fs.rename(normalizedOldPath, normalizedNewPath, options);
301
+ }
302
+
303
+ /**
304
+ * Copy files and directories
305
+ */
306
+ async copy(source: PathLike, destination: PathLike, options?: { recursive?: boolean; overwrite?: boolean }): Promise<void> {
307
+ const normalizedSource = normalizePath(source);
308
+ const normalizedDestination = normalizePath(destination);
309
+
310
+ return this.#fs.copy(normalizedSource, normalizedDestination, options);
311
+ }
312
+
313
+ /**
314
+ * Open a file and return a file descriptor
315
+ */
316
+ async open(path: PathLike, options?: FileOpenOptions): Promise<number> {
317
+ const normalizedPath = normalizePath(path);
318
+
319
+ return this.#fs.open(normalizedPath, options);
320
+ }
321
+
322
+ /**
323
+ * Close a file descriptor
324
+ */
325
+ async close(fd: number): Promise<void> {
326
+ return this.#fs.close(fd);
327
+ }
328
+
329
+ /**
330
+ * Read data from a file descriptor
331
+ *
332
+ * This method requires special handling due to Comlink transfer requirements.
333
+ * The buffer is transferred to the worker and back, so the original buffer
334
+ * becomes unusable after the call.
335
+ */
336
+ async read(
337
+ fd: number,
338
+ buffer: Uint8Array,
339
+ offset: number,
340
+ length: number,
341
+ position?: number | null | undefined
342
+ ): Promise<{ bytesRead: number; buffer: Uint8Array }> {
343
+ const { bytesRead, buffer: transferred } = await this.#fs.read(
344
+ fd,
345
+ // Temp buffer to preserve the original buffer
346
+ new Uint8Array(length),
347
+ 0,
348
+ length,
349
+ position
350
+ );
351
+
352
+ // Copy the data from the transferred buffer to the original buffer
353
+ if (bytesRead > 0) {
354
+ buffer.set(transferred.subarray(0, bytesRead), offset);
355
+ }
356
+
357
+ return { bytesRead, buffer };
358
+ }
359
+
360
+ /**
361
+ * Write data to a file descriptor
362
+ */
363
+ async write(
364
+ fd: number,
365
+ buffer: Uint8Array,
366
+ offset?: number,
367
+ length?: number,
368
+ position?: number | null | undefined,
369
+ emitEvent?: boolean
370
+ ): Promise<number> {
371
+ return this.#fs.write(fd, buffer, offset, length, position, emitEvent);
372
+ }
373
+
374
+ /**
375
+ * Get file status information by file descriptor
376
+ */
377
+ async fstat(fd: number): Promise<FileStat> {
378
+ return this.#fs.fstat(fd);
379
+ }
380
+
381
+ /**
382
+ * Truncate file to specified size
383
+ */
384
+ async ftruncate(fd: number, size?: number): Promise<void> {
385
+ return this.#fs.ftruncate(fd, size);
386
+ }
387
+
388
+ /**
389
+ * Synchronize file data to storage (fsync equivalent)
390
+ */
391
+ async fsync(fd: number): Promise<void> {
392
+ return this.#fs.fsync(fd);
393
+ }
394
+
395
+ /**
396
+ * Synchronize the file system with external data
397
+ */
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]);
400
+
401
+ return this.#fs.createIndex(normalizedEntries);
402
+ }
403
+
404
+ /**
405
+ * Read a file as text with automatic encoding detection
406
+ */
407
+ async readText(path: PathLike, encoding: Encoding = 'utf-8'): Promise<string> {
408
+ const normalizedPath = normalizePath(path);
409
+ const buffer = await this.#fs.readFile(normalizedPath);
410
+
411
+ return decodeBuffer(buffer, encoding);
412
+ }
413
+
414
+ /**
415
+ * Write text to a file with specified encoding
416
+ */
417
+ async writeText(path: PathLike, text: string, encoding: Encoding = 'utf-8'): Promise<void> {
418
+ const normalizedPath = normalizePath(path);
419
+ const buffer = encodeString(text, encoding);
420
+
421
+ return this.#fs.writeFile(normalizedPath, buffer);
422
+ }
423
+
424
+ /**
425
+ * Append text to a file with specified encoding
426
+ */
427
+ async appendText(path: PathLike, text: string, encoding: Encoding = 'utf-8'): Promise<void> {
428
+ const normalizedPath = normalizePath(path);
429
+ const buffer = encodeString(text, encoding);
430
+
431
+ return this.#fs.appendFile(normalizedPath, buffer);
432
+ }
433
+
434
+ /**
435
+ * Dispose of resources, detach the worker and clean up the file system instance
436
+ */
437
+ dispose() {
438
+ this.#dispose();
439
+ }
440
+ }
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 };