opfs-worker 0.2.1 → 0.2.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.
package/dist/raw.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { expose as p } from "comlink";
2
- import { d as v, O as o, b as f, s as y, P as E, i as I, o as S, F as d, w as g, j as T, e as u, f as m, k as H, r as $, n as F, l as A } from "./helpers-Co-qCBmA.js";
3
- class N {
2
+ import { d as v, O as o, b as f, s as u, P as E, i as I, o as S, F as d, w as g, j as T, e as m, f as y, k as M, r as H, n as F, l as P } from "./helpers-Co-qCBmA.js";
3
+ class $ {
4
4
  /** Root directory handle for the file system */
5
5
  root = null;
6
6
  /** Watch event callback */
@@ -13,19 +13,23 @@ class N {
13
13
  watchInterval = 1e3;
14
14
  /** Flag to avoid concurrent scans */
15
15
  scanning = !1;
16
+ /** Promise to prevent concurrent mount operations */
17
+ mountingPromise = null;
16
18
  /**
17
19
  * Creates a new OPFSFileSystem instance
18
20
  *
21
+ * @param watchCallback - Optional callback for file change events
22
+ * @param watchOptions - Optional configuration for watching
19
23
  * @throws {OPFSError} If OPFS is not supported in the current browser
20
24
  */
21
- constructor() {
22
- v();
25
+ constructor(t, e) {
26
+ v(), t && (this.watchCallback = t, e?.watchInterval && (this.watchInterval = e.watchInterval)), this.mount("/");
23
27
  }
24
28
  /**
25
29
  * Initialize the file system within a given directory
26
30
  *
27
31
  * This method sets up the root directory for all subsequent operations.
28
- * It must be called before any other file system operations.
32
+ * If no root is specified, it will use the OPFS root directory.
29
33
  *
30
34
  * @param root - The root path for the file system (default: '/')
31
35
  * @returns Promise that resolves to true if initialization was successful
@@ -34,15 +38,52 @@ class N {
34
38
  * @example
35
39
  * ```typescript
36
40
  * const fs = new OPFSFileSystem();
37
- * const success = await fs.init('/my-app');
41
+ *
42
+ * // Use OPFS root (default)
43
+ * await fs.mount();
44
+ *
45
+ * // Use custom directory
46
+ * await fs.mount('/my-app');
38
47
  * ```
39
48
  */
40
- async mount(t = "/", e, a) {
41
- try {
42
- const r = await navigator.storage.getDirectory();
43
- return this.root = await this.getDirectoryHandle(t, !0, r), e && (this.watchCallback = e, a?.watchInterval && (this.watchInterval = a.watchInterval)), !0;
44
- } catch (r) {
45
- throw console.error(r), new o("Failed to initialize OPFS", "INIT_FAILED");
49
+ async mount(t = "/") {
50
+ return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, i) => {
51
+ this.root = null;
52
+ try {
53
+ const r = await navigator.storage.getDirectory();
54
+ t === "/" ? this.root = r : this.root = await this.getDirectoryHandle(t, !0, r), e(!0);
55
+ } catch (r) {
56
+ console.error(r), i(new o("Failed to initialize OPFS", "INIT_FAILED"));
57
+ } finally {
58
+ this.mountingPromise = null;
59
+ }
60
+ }), this.mountingPromise;
61
+ }
62
+ /**
63
+ * Set the watch callback for file change events
64
+ *
65
+ * @param callback - The callback function to invoke when files change
66
+ * @param options - Optional configuration for watching
67
+ */
68
+ setWatchCallback(t, e) {
69
+ this.watchCallback = t, e?.watchInterval && (this.watchInterval = e.watchInterval);
70
+ }
71
+ /**
72
+ * Automatically mount the OPFS root if not already mounted
73
+ *
74
+ * This method is called internally when file operations are performed
75
+ * without explicitly mounting first.
76
+ *
77
+ * @returns Promise that resolves when auto-mount is complete
78
+ * @throws {OPFSError} If auto-mount fails
79
+ */
80
+ async ensureMounted() {
81
+ if (!this.root) {
82
+ if (this.mountingPromise) {
83
+ await this.mountingPromise;
84
+ return;
85
+ }
86
+ throw new o("OPFS not mounted", "NOT_MOUNTED");
46
87
  }
47
88
  }
48
89
  /**
@@ -63,14 +104,14 @@ class N {
63
104
  * const docsDir2 = await fs.getDirectoryHandle(['users', 'john', 'documents'], true);
64
105
  * ```
65
106
  */
66
- async getDirectoryHandle(t, e = !1, a = this.root) {
67
- if (!a)
107
+ async getDirectoryHandle(t, e = !1, i = this.root) {
108
+ if (!i)
68
109
  throw new f();
69
- const r = Array.isArray(t) ? t : y(t);
70
- let i = a;
110
+ const r = Array.isArray(t) ? t : u(t);
111
+ let a = i;
71
112
  for (const n of r)
72
- i = await i.getDirectoryHandle(n, { create: e });
73
- return i;
113
+ a = await a.getDirectoryHandle(n, { create: e });
114
+ return a;
74
115
  }
75
116
  /**
76
117
  * Get a file handle from a path
@@ -91,14 +132,14 @@ class N {
91
132
  * const fileHandle2 = await fs.getFileHandle(['config', 'settings.json'], true);
92
133
  * ```
93
134
  */
94
- async getFileHandle(t, e = !1, a = this.root) {
95
- if (!a)
135
+ async getFileHandle(t, e = !1, i = this.root) {
136
+ if (!i)
96
137
  throw new f();
97
- const r = y(t);
138
+ const r = u(t);
98
139
  if (r.length === 0)
99
140
  throw new E("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
100
- const i = r.pop();
101
- return (await this.getDirectoryHandle(r, e, a)).getFileHandle(i, { create: e });
141
+ const a = r.pop();
142
+ return (await this.getDirectoryHandle(r, e, i)).getFileHandle(a, { create: e });
102
143
  }
103
144
  /**
104
145
  * Recursively list all files and directories with their stats
@@ -138,13 +179,13 @@ class N {
138
179
  * ```
139
180
  */
140
181
  async index(t) {
141
- const e = /* @__PURE__ */ new Map(), a = async (r) => {
142
- const i = await this.readdir(r, { withFileTypes: !0 });
143
- for (const n of i) {
182
+ const e = /* @__PURE__ */ new Map(), i = async (r) => {
183
+ const a = await this.readdir(r, { withFileTypes: !0 });
184
+ for (const n of a) {
144
185
  const s = `${r === "/" ? "" : r}/${n.name}`;
145
186
  try {
146
187
  const c = await this.stat(s, t);
147
- e.set(s, c), c.isDirectory && await a(s);
188
+ e.set(s, c), c.isDirectory && await i(s);
148
189
  } catch (c) {
149
190
  console.warn(`Skipping broken entry: ${s}`, c);
150
191
  }
@@ -157,14 +198,15 @@ class N {
157
198
  ctime: (/* @__PURE__ */ new Date(0)).toISOString(),
158
199
  isFile: !1,
159
200
  isDirectory: !0
160
- }), await a("/"), e;
201
+ }), await i("/"), e;
161
202
  }
162
203
  async readFile(t, e = "utf-8") {
204
+ await this.ensureMounted();
163
205
  try {
164
- const a = await this.getFileHandle(t, !1), r = await I(a);
206
+ const i = await this.getFileHandle(t, !1), r = await I(i);
165
207
  return e === "binary" ? r : S(r, e);
166
- } catch (a) {
167
- throw console.error(a), new d(t);
208
+ } catch (i) {
209
+ throw console.error(i), new d(t);
168
210
  }
169
211
  }
170
212
  /**
@@ -192,9 +234,10 @@ class N {
192
234
  * await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
193
235
  * ```
194
236
  */
195
- async writeFile(t, e, a) {
237
+ async writeFile(t, e, i) {
238
+ await this.ensureMounted();
196
239
  const r = await this.getFileHandle(t, !0);
197
- await g(r, e, a, { truncate: !0 });
240
+ await g(r, e, i, { truncate: !0 });
198
241
  }
199
242
  /**
200
243
  * Append data to a file
@@ -218,9 +261,10 @@ class N {
218
261
  * await fs.appendFile('/data/binary.dat', additionalData);
219
262
  * ```
220
263
  */
221
- async appendFile(t, e, a) {
264
+ async appendFile(t, e, i) {
265
+ await this.ensureMounted();
222
266
  const r = await this.getFileHandle(t, !0);
223
- await g(r, e, a, { append: !0 });
267
+ await g(r, e, i, { append: !0 });
224
268
  }
225
269
  /**
226
270
  * Create a directory
@@ -244,14 +288,14 @@ class N {
244
288
  * ```
245
289
  */
246
290
  async mkdir(t, e) {
247
- if (!this.root)
291
+ if (await this.ensureMounted(), !this.root)
248
292
  throw new f();
249
- const a = e?.recursive ?? !1, r = y(t);
250
- let i = this.root;
293
+ const i = e?.recursive ?? !1, r = u(t);
294
+ let a = this.root;
251
295
  for (let n = 0; n < r.length; n++) {
252
296
  const s = r[n];
253
297
  try {
254
- i = await i.getDirectoryHandle(s, { create: a || n === r.length - 1 });
298
+ a = await a.getDirectoryHandle(s, { create: i || n === r.length - 1 });
255
299
  } catch (c) {
256
300
  throw c.name === "NotFoundError" ? new o(
257
301
  `Parent directory does not exist: ${T(r.slice(0, n + 1))}`,
@@ -290,9 +334,10 @@ class N {
290
334
  * ```
291
335
  */
292
336
  async stat(t, e) {
293
- const a = u(t), r = await this.getDirectoryHandle(m(t), !1), i = e?.includeHash ?? !1, n = e?.hashAlgorithm ?? "SHA-1";
337
+ await this.ensureMounted();
338
+ const i = m(t), r = await this.getDirectoryHandle(y(t), !1), a = e?.includeHash ?? !1, n = e?.hashAlgorithm ?? "SHA-1";
294
339
  try {
295
- const c = await (await r.getFileHandle(a, { create: !1 })).getFile(), l = {
340
+ const c = await (await r.getFileHandle(i, { create: !1 })).getFile(), l = {
296
341
  kind: "file",
297
342
  size: c.size,
298
343
  mtime: new Date(c.lastModified).toISOString(),
@@ -300,9 +345,9 @@ class N {
300
345
  isFile: !0,
301
346
  isDirectory: !1
302
347
  };
303
- if (i)
348
+ if (a)
304
349
  try {
305
- const h = new Uint8Array(await c.arrayBuffer()), w = await H(h, n);
350
+ const h = new Uint8Array(await c.arrayBuffer()), w = await M(h, n);
306
351
  l.hash = w;
307
352
  } catch (h) {
308
353
  console.warn(`Failed to calculate hash for ${t}:`, h);
@@ -313,7 +358,7 @@ class N {
313
358
  throw new o("Failed to stat (file)", "STAT_FAILED");
314
359
  }
315
360
  try {
316
- return await r.getDirectoryHandle(a, { create: !1 }), {
361
+ return await r.getDirectoryHandle(i, { create: !1 }), {
317
362
  kind: "directory",
318
363
  size: 0,
319
364
  mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
@@ -326,24 +371,25 @@ class N {
326
371
  }
327
372
  }
328
373
  async readdir(t, e) {
329
- const a = e?.withFileTypes ?? !1, r = await this.getDirectoryHandle(t, !1);
330
- if (a) {
331
- const i = [];
374
+ await this.ensureMounted();
375
+ const i = e?.withFileTypes ?? !1, r = await this.getDirectoryHandle(t, !1);
376
+ if (i) {
377
+ const a = [];
332
378
  for await (const [n, s] of r.entries()) {
333
379
  const c = s.kind === "file";
334
- i.push({
380
+ a.push({
335
381
  name: n,
336
382
  kind: s.kind,
337
383
  isFile: c,
338
384
  isDirectory: !c
339
385
  });
340
386
  }
341
- return i;
387
+ return a;
342
388
  } else {
343
- const i = [];
389
+ const a = [];
344
390
  for await (const [n] of r.entries())
345
- i.push(n);
346
- return i;
391
+ a.push(n);
392
+ return a;
347
393
  }
348
394
  }
349
395
  /**
@@ -361,23 +407,24 @@ class N {
361
407
  * ```
362
408
  */
363
409
  async exists(t) {
364
- const e = u(t);
365
- let a = null;
410
+ await this.ensureMounted();
411
+ const e = m(t);
412
+ let i = null;
366
413
  try {
367
- a = await this.getDirectoryHandle(m(t), !1);
414
+ i = await this.getDirectoryHandle(y(t), !1);
368
415
  } catch (r) {
369
- throw (r.name === "NotFoundError" || r.name === "TypeMismatchError") && (a = null), r;
416
+ throw (r.name === "NotFoundError" || r.name === "TypeMismatchError") && (i = null), r;
370
417
  }
371
- if (!a || !e)
418
+ if (!i || !e)
372
419
  return !1;
373
420
  try {
374
- return await a.getFileHandle(e, { create: !1 }), !0;
421
+ return await i.getFileHandle(e, { create: !1 }), !0;
375
422
  } catch (r) {
376
423
  if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
377
424
  throw r;
378
425
  }
379
426
  try {
380
- return await a.getDirectoryHandle(e, { create: !1 }), !0;
427
+ return await i.getDirectoryHandle(e, { create: !1 }), !0;
381
428
  } catch (r) {
382
429
  if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
383
430
  throw r;
@@ -404,10 +451,11 @@ class N {
404
451
  * ```
405
452
  */
406
453
  async clear(t = "/") {
454
+ await this.ensureMounted();
407
455
  try {
408
456
  const e = await this.readdir(t, { withFileTypes: !0 });
409
- for (const a of e) {
410
- const r = `${t === "/" ? "" : t}/${a.name}`;
457
+ for (const i of e) {
458
+ const r = `${t === "/" ? "" : t}/${i.name}`;
411
459
  await this.remove(r, { recursive: !0 });
412
460
  }
413
461
  } catch (e) {
@@ -439,17 +487,18 @@ class N {
439
487
  * ```
440
488
  */
441
489
  async remove(t, e) {
442
- const a = e?.recursive ?? !1, r = e?.force ?? !1, i = u(t);
443
- if (!i)
490
+ await this.ensureMounted();
491
+ const i = e?.recursive ?? !1, r = e?.force ?? !1, a = m(t);
492
+ if (!a)
444
493
  throw new E("Invalid path", t);
445
- const n = await this.getDirectoryHandle(m(t), !1);
494
+ const n = await this.getDirectoryHandle(y(t), !1);
446
495
  try {
447
- await n.removeEntry(i, { recursive: a });
496
+ await n.removeEntry(a, { recursive: i });
448
497
  } catch (s) {
449
498
  if (s.name === "NotFoundError") {
450
499
  if (!r)
451
500
  throw new o(`No such file or directory: ${t}`, "ENOENT");
452
- } else throw s.name === "InvalidModificationError" ? new o(`Directory not empty: ${t}. Use recursive option to force removal.`, "ENOTEMPTY") : s.name === "TypeMismatchError" && !a ? new o(`Cannot remove directory without recursive option: ${t}`, "EISDIR") : new o(`Failed to remove path: ${t}`, "RM_FAILED");
501
+ } else throw s.name === "InvalidModificationError" ? new o(`Directory not empty: ${t}. Use recursive option to force removal.`, "ENOTEMPTY") : s.name === "TypeMismatchError" && !i ? new o(`Cannot remove directory without recursive option: ${t}`, "EISDIR") : new o(`Failed to remove path: ${t}`, "RM_FAILED");
453
502
  }
454
503
  }
455
504
  /**
@@ -471,8 +520,9 @@ class N {
471
520
  * ```
472
521
  */
473
522
  async realpath(t) {
523
+ await this.ensureMounted();
474
524
  try {
475
- const e = $(t);
525
+ const e = H(t);
476
526
  if (!await this.exists(e))
477
527
  throw new d(e);
478
528
  return e;
@@ -497,12 +547,13 @@ class N {
497
547
  * ```
498
548
  */
499
549
  async rename(t, e) {
550
+ await this.ensureMounted();
500
551
  try {
501
552
  if (!await this.exists(t))
502
553
  throw new d(t);
503
554
  await this.copy(t, e, { recursive: !0 }), await this.remove(t, { recursive: !0 });
504
- } catch (a) {
505
- throw a instanceof o ? a : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED");
555
+ } catch (i) {
556
+ throw i instanceof o ? i : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED");
506
557
  }
507
558
  }
508
559
  /**
@@ -521,21 +572,22 @@ class N {
521
572
  * @example
522
573
  * ```typescript
523
574
  * // Copy a file
524
- * await fs.cp('/source/file.txt', '/dest/file.txt');
575
+ * await fs.copy('/source/file.txt', '/dest/file.txt');
525
576
  *
526
577
  * // Copy a directory and all its contents
527
- * await fs.cp('/source/dir', '/dest/dir', { recursive: true });
578
+ * await fs.copy('/source/dir', '/dest/dir', { recursive: true });
528
579
  *
529
580
  * // Copy without overwriting existing files
530
- * await fs.cp('/source', '/dest', { recursive: true, force: false });
581
+ * await fs.copy('/source', '/dest', { recursive: true, force: false });
531
582
  * ```
532
583
  */
533
- async copy(t, e, a) {
584
+ async copy(t, e, i) {
585
+ await this.ensureMounted();
534
586
  try {
535
- const r = a?.recursive ?? !1, i = a?.force ?? !0;
587
+ const r = i?.recursive ?? !1, a = i?.force ?? !0;
536
588
  if (!await this.exists(t))
537
589
  throw new o(`Source does not exist: ${t}`, "ENOENT");
538
- if (await this.exists(e) && !i)
590
+ if (await this.exists(e) && !a)
539
591
  throw new o(`Destination already exists: ${e}`, "EEXIST");
540
592
  if ((await this.stat(t)).isFile) {
541
593
  const l = await this.readFile(t, "binary");
@@ -547,7 +599,7 @@ class N {
547
599
  const l = await this.readdir(t, { withFileTypes: !0 });
548
600
  for (const h of l) {
549
601
  const w = `${t}/${h.name}`, D = `${e}/${h.name}`;
550
- await this.copy(w, D, { recursive: !0, force: i });
602
+ await this.copy(w, D, { recursive: !0, force: a });
551
603
  }
552
604
  }
553
605
  } catch (r) {
@@ -558,8 +610,9 @@ class N {
558
610
  * Start watching a file or directory for changes
559
611
  */
560
612
  async watch(t) {
561
- const e = F(t), a = await this.buildSnapshot(e);
562
- this.watchers.set(e, a), this.watchTimer || (this.watchTimer = setInterval(() => {
613
+ await this.ensureMounted();
614
+ const e = F(t), i = await this.buildSnapshot(e);
615
+ this.watchers.set(e, i), this.watchTimer || (this.watchTimer = setInterval(() => {
563
616
  this.scanWatches();
564
617
  }, this.watchInterval));
565
618
  }
@@ -571,17 +624,17 @@ class N {
571
624
  this.watchers.delete(e), this.watchers.size === 0 && this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null);
572
625
  }
573
626
  async buildSnapshot(t) {
574
- const e = /* @__PURE__ */ new Map(), a = async (r) => {
575
- const i = await this.stat(r);
576
- if (e.set(r, i), i.isDirectory) {
627
+ const e = /* @__PURE__ */ new Map(), i = async (r) => {
628
+ const a = await this.stat(r);
629
+ if (e.set(r, a), a.isDirectory) {
577
630
  const n = await this.readdir(r, { withFileTypes: !0 });
578
631
  for (const s of n) {
579
632
  const c = `${r === "/" ? "" : r}/${s.name}`;
580
- await a(c);
633
+ await i(c);
581
634
  }
582
635
  }
583
636
  };
584
- return await a(t), e;
637
+ return await i(t), e;
585
638
  }
586
639
  async scanWatches() {
587
640
  if (!this.scanning) {
@@ -589,23 +642,23 @@ class N {
589
642
  try {
590
643
  await Promise.all(
591
644
  [...this.watchers.entries()].map(async ([t, e]) => {
592
- let a;
645
+ let i;
593
646
  try {
594
- a = await this.buildSnapshot(t);
647
+ i = await this.buildSnapshot(t);
595
648
  } catch {
596
- a = /* @__PURE__ */ new Map();
649
+ i = /* @__PURE__ */ new Map();
597
650
  }
598
651
  const r = [];
599
- for (const [i, n] of a) {
600
- const s = e.get(i);
601
- s ? (s.mtime !== n.mtime || s.size !== n.size) && r.push({ path: i, type: "change" }) : r.push({ path: i, type: "create" });
652
+ for (const [a, n] of i) {
653
+ const s = e.get(a);
654
+ s ? (s.mtime !== n.mtime || s.size !== n.size) && r.push({ path: a, type: "change" }) : r.push({ path: a, type: "create" });
602
655
  }
603
- for (const i of e.keys())
604
- a.has(i) || r.push({ path: i, type: "delete" });
656
+ for (const a of e.keys())
657
+ i.has(a) || r.push({ path: a, type: "delete" });
605
658
  if (r.length && this.watchCallback)
606
- for (const i of r)
607
- this.watchCallback(i);
608
- this.watchers.set(t, a);
659
+ for (const a of r)
660
+ this.watchCallback(a);
661
+ this.watchers.set(t, i);
609
662
  })
610
663
  );
611
664
  } finally {
@@ -642,20 +695,21 @@ class N {
642
695
  * ```
643
696
  */
644
697
  async sync(t, e) {
698
+ await this.ensureMounted();
645
699
  try {
646
700
  (e?.cleanBefore ?? !1) && await this.clear("/");
647
- for (const [r, i] of t) {
701
+ for (const [r, a] of t) {
648
702
  const n = F(r);
649
703
  let s;
650
- i instanceof Blob ? s = await A(i) : s = i, await this.writeFile(n, s);
704
+ a instanceof Blob ? s = await P(a) : s = a, await this.writeFile(n, s);
651
705
  }
652
- } catch (a) {
653
- throw a instanceof o ? a : new o("Failed to sync file system", "SYNC_FAILED");
706
+ } catch (i) {
707
+ throw i instanceof o ? i : new o("Failed to sync file system", "SYNC_FAILED");
654
708
  }
655
709
  }
656
710
  }
657
- typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && p(new N());
711
+ typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && p(new $());
658
712
  export {
659
- N as OPFSWorker
713
+ $ as OPFSWorker
660
714
  };
661
715
  //# sourceMappingURL=raw.js.map