opfs-worker 0.2.6 → 0.3.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.
package/dist/raw.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import { expose as E } from "comlink";
2
- import { d as S, O as n, b as l, s as w, P as g, i as v, o as I, F as f, w as F, j as T, e as y, f as u, k as M, r as A, n as d, l as $ } from "./helpers-C0nyU6hv.js";
3
- class O {
2
+ import { d as S, O as n, b as l, s as w, P as g, i as v, o as I, F as f, w as F, j as C, e as d, f as y, k as T, r as M, n as u, l as b } from "./helpers-C0nyU6hv.js";
3
+ class A {
4
4
  /** Root directory handle for the file system */
5
5
  root = null;
6
- /** Watch event callback */
7
- watchCallback = null;
8
6
  /** Map of watched paths to their last known state */
9
7
  watchers = /* @__PURE__ */ new Map();
10
8
  /** Interval handle for polling watched paths */
@@ -13,11 +11,14 @@ class O {
13
11
  scanning = !1;
14
12
  /** Promise to prevent concurrent mount operations */
15
13
  mountingPromise = null;
14
+ /** BroadcastChannel instance for sending events */
15
+ broadcastChannel = null;
16
16
  /** Configuration options */
17
17
  options = {
18
18
  watchInterval: 1e3,
19
19
  maxFileSize: 50 * 1024 * 1024,
20
- hashAlgorithm: null
20
+ hashAlgorithm: null,
21
+ broadcastChannel: "opfs-worker"
21
22
  };
22
23
  /**
23
24
  * Notify about internal changes to the file system
@@ -29,7 +30,7 @@ class O {
29
30
  * @param type - The type of change (create, change, delete)
30
31
  */
31
32
  async notifyChange(t) {
32
- if (!this.watchCallback)
33
+ if (!this.options.broadcastChannel)
33
34
  return;
34
35
  let e;
35
36
  if (this.options.hashAlgorithm && !t.isDirectory && t.type !== "removed")
@@ -39,24 +40,29 @@ class O {
39
40
  } catch (i) {
40
41
  console.warn(`Failed to calculate hash for ${t.path}:`, i);
41
42
  }
42
- this.watchCallback({
43
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
44
- ...t,
45
- ...e && { hash: e }
46
- });
43
+ try {
44
+ this.broadcastChannel || (this.broadcastChannel = new BroadcastChannel(this.options.broadcastChannel));
45
+ const i = {
46
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
47
+ ...t,
48
+ ...e && { hash: e }
49
+ };
50
+ this.broadcastChannel.postMessage(i);
51
+ } catch (i) {
52
+ console.warn("Failed to send event via BroadcastChannel:", i);
53
+ }
47
54
  }
48
55
  /**
49
56
  * Creates a new OPFSFileSystem instance
50
57
  *
51
- * @param watchCallback - Optional callback for file change events
52
58
  * @param options - Optional configuration options
53
59
  * @param options.watchInterval - Polling interval in milliseconds for file watching
54
60
  * @param options.hashAlgorithm - Hash algorithm for file hashing
55
61
  * @param options.maxFileSize - Maximum file size for hashing in bytes (default: 50MB)
56
62
  * @throws {OPFSError} If OPFS is not supported in the current browser
57
63
  */
58
- constructor(t, e) {
59
- S(), t && (this.watchCallback = t), e && this.setOptions(e), this.mount("/");
64
+ constructor(t) {
65
+ S(), t && this.setOptions(t), this.mount("/");
60
66
  }
61
67
  /**
62
68
  * Initialize the file system within a given directory
@@ -83,23 +89,15 @@ class O {
83
89
  return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, i) => {
84
90
  this.root = null;
85
91
  try {
86
- const r = await navigator.storage.getDirectory();
87
- t === "/" ? this.root = r : this.root = await this.getDirectoryHandle(t, !0, r), e(!0);
88
- } catch (r) {
89
- console.error(r), i(new n("Failed to initialize OPFS", "INIT_FAILED"));
92
+ const a = await navigator.storage.getDirectory();
93
+ t === "/" ? this.root = a : this.root = await this.getDirectoryHandle(t, !0, a), e(!0);
94
+ } catch (a) {
95
+ console.error(a), i(new n("Failed to initialize OPFS", "INIT_FAILED"));
90
96
  } finally {
91
97
  this.mountingPromise = null;
92
98
  }
93
99
  }), this.mountingPromise;
94
100
  }
95
- /**
96
- * Set the watch callback for file change events
97
- *
98
- * @param callback - The callback function to invoke when files change
99
- */
100
- setWatchCallback(t) {
101
- this.watchCallback = t;
102
- }
103
101
  /**
104
102
  * Update configuration options
105
103
  *
@@ -107,9 +105,10 @@ class O {
107
105
  * @param options.watchInterval - Polling interval in milliseconds for file watching
108
106
  * @param options.hashAlgorithm - Hash algorithm for file hashing
109
107
  * @param options.maxFileSize - Maximum file size for hashing in bytes
108
+ * @param options.broadcastChannel - Custom name for the broadcast channel
110
109
  */
111
110
  setOptions(t) {
112
- t.watchInterval !== void 0 && (this.options.watchInterval = t.watchInterval), t.hashAlgorithm !== void 0 && (this.options.hashAlgorithm = t.hashAlgorithm), t.maxFileSize !== void 0 && (this.options.maxFileSize = t.maxFileSize);
111
+ t.watchInterval !== void 0 && (this.options.watchInterval = t.watchInterval), t.hashAlgorithm !== void 0 && (this.options.hashAlgorithm = t.hashAlgorithm), t.maxFileSize !== void 0 && (this.options.maxFileSize = t.maxFileSize), t.broadcastChannel !== void 0 && (this.broadcastChannel && this.options.broadcastChannel !== t.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.options.broadcastChannel = t.broadcastChannel);
113
112
  }
114
113
  /**
115
114
  * Automatically mount the OPFS root if not already mounted
@@ -150,11 +149,11 @@ class O {
150
149
  async getDirectoryHandle(t, e = !1, i = this.root) {
151
150
  if (!i)
152
151
  throw new l();
153
- const r = Array.isArray(t) ? t : w(t);
154
- let a = i;
155
- for (const s of r)
156
- a = await a.getDirectoryHandle(s, { create: e });
157
- return a;
152
+ const a = Array.isArray(t) ? t : w(t);
153
+ let r = i;
154
+ for (const s of a)
155
+ r = await r.getDirectoryHandle(s, { create: e });
156
+ return r;
158
157
  }
159
158
  /**
160
159
  * Get a file handle from a path
@@ -178,11 +177,11 @@ class O {
178
177
  async getFileHandle(t, e = !1, i = this.root) {
179
178
  if (!i)
180
179
  throw new l();
181
- const r = w(t);
182
- if (r.length === 0)
180
+ const a = w(t);
181
+ if (a.length === 0)
183
182
  throw new g("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
184
- const a = r.pop();
185
- return (await this.getDirectoryHandle(r, e, i)).getFileHandle(a, { create: e });
183
+ const r = a.pop();
184
+ return (await this.getDirectoryHandle(a, e, i)).getFileHandle(r, { create: e });
186
185
  }
187
186
  /**
188
187
  * Get a complete index of all files and directories in the file system
@@ -205,9 +204,9 @@ class O {
205
204
  */
206
205
  async index() {
207
206
  const t = /* @__PURE__ */ new Map(), e = async (i) => {
208
- const r = await this.readdir(i, { withFileTypes: !0 });
209
- for (const a of r) {
210
- const s = `${i === "/" ? "" : i}/${a.name}`;
207
+ const a = await this.readdir(i, { withFileTypes: !0 });
208
+ for (const r of a) {
209
+ const s = `${i === "/" ? "" : i}/${r.name}`;
211
210
  try {
212
211
  const o = await this.stat(s);
213
212
  t.set(s, o), o.isDirectory && await e(s);
@@ -228,8 +227,8 @@ class O {
228
227
  async readFile(t, e = "utf-8") {
229
228
  await this.ensureMounted();
230
229
  try {
231
- const i = await this.getFileHandle(t, !1), r = await v(i);
232
- return e === "binary" ? r : I(r, e);
230
+ const i = await this.getFileHandle(t, !1), a = await v(i);
231
+ return e === "binary" ? a : I(a, e);
233
232
  } catch (i) {
234
233
  throw console.error(i), new f(t);
235
234
  }
@@ -261,8 +260,8 @@ class O {
261
260
  */
262
261
  async writeFile(t, e, i) {
263
262
  await this.ensureMounted();
264
- const r = await this.getFileHandle(t, !0);
265
- await F(r, e, i, { truncate: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
263
+ const a = await this.getFileHandle(t, !0);
264
+ await F(a, e, i, { truncate: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
266
265
  }
267
266
  /**
268
267
  * Append data to a file
@@ -288,8 +287,8 @@ class O {
288
287
  */
289
288
  async appendFile(t, e, i) {
290
289
  await this.ensureMounted();
291
- const r = await this.getFileHandle(t, !0);
292
- await F(r, e, i, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
290
+ const a = await this.getFileHandle(t, !0);
291
+ await F(a, e, i, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
293
292
  }
294
293
  /**
295
294
  * Create a directory
@@ -315,15 +314,15 @@ class O {
315
314
  async mkdir(t, e) {
316
315
  if (await this.ensureMounted(), !this.root)
317
316
  throw new l();
318
- const i = e?.recursive ?? !1, r = w(t);
319
- let a = this.root;
320
- for (let s = 0; s < r.length; s++) {
321
- const o = r[s];
317
+ const i = e?.recursive ?? !1, a = w(t);
318
+ let r = this.root;
319
+ for (let s = 0; s < a.length; s++) {
320
+ const o = a[s];
322
321
  try {
323
- a = await a.getDirectoryHandle(o, { create: i || s === r.length - 1 });
322
+ r = await r.getDirectoryHandle(o, { create: i || s === a.length - 1 });
324
323
  } catch (c) {
325
324
  throw c.name === "NotFoundError" ? new n(
326
- `Parent directory does not exist: ${T(r.slice(0, s + 1))}`,
325
+ `Parent directory does not exist: ${C(a.slice(0, s + 1))}`,
327
326
  "ENOENT"
328
327
  ) : c.name === "TypeMismatchError" ? new n(`Path segment is not a directory: ${o}`, "ENOTDIR") : new n("Failed to create directory", "MKDIR_FAILED");
329
328
  }
@@ -362,7 +361,7 @@ class O {
362
361
  isFile: !1,
363
362
  isDirectory: !0
364
363
  };
365
- const e = y(t), i = await this.getDirectoryHandle(u(t), !1), r = this.options.hashAlgorithm !== null;
364
+ const e = d(t), i = await this.getDirectoryHandle(y(t), !1), a = this.options.hashAlgorithm !== null;
366
365
  try {
367
366
  const s = await (await i.getFileHandle(e, { create: !1 })).getFile(), o = {
368
367
  kind: "file",
@@ -372,16 +371,16 @@ class O {
372
371
  isFile: !0,
373
372
  isDirectory: !1
374
373
  };
375
- if (r && this.options.hashAlgorithm)
374
+ if (a && this.options.hashAlgorithm)
376
375
  try {
377
- const c = await M(s, this.options.hashAlgorithm, this.options.maxFileSize);
376
+ const c = await T(s, this.options.hashAlgorithm, this.options.maxFileSize);
378
377
  o.hash = c;
379
378
  } catch (c) {
380
379
  console.warn(`Failed to calculate hash for ${t}:`, c);
381
380
  }
382
381
  return o;
383
- } catch (a) {
384
- if (a.name !== "TypeMismatchError" && a.name !== "NotFoundError")
382
+ } catch (r) {
383
+ if (r.name !== "TypeMismatchError" && r.name !== "NotFoundError")
385
384
  throw new n("Failed to stat (file)", "STAT_FAILED");
386
385
  }
387
386
  try {
@@ -393,30 +392,30 @@ class O {
393
392
  isFile: !1,
394
393
  isDirectory: !0
395
394
  };
396
- } catch (a) {
397
- throw a.name === "NotFoundError" ? new n(`No such file or directory: ${t}`, "ENOENT") : new n("Failed to stat (directory)", "STAT_FAILED");
395
+ } catch (r) {
396
+ throw r.name === "NotFoundError" ? new n(`No such file or directory: ${t}`, "ENOENT") : new n("Failed to stat (directory)", "STAT_FAILED");
398
397
  }
399
398
  }
400
399
  async readdir(t, e) {
401
400
  await this.ensureMounted();
402
- const i = e?.withFileTypes ?? !1, r = await this.getDirectoryHandle(t, !1);
401
+ const i = e?.withFileTypes ?? !1, a = await this.getDirectoryHandle(t, !1);
403
402
  if (i) {
404
- const a = [];
405
- for await (const [s, o] of r.entries()) {
403
+ const r = [];
404
+ for await (const [s, o] of a.entries()) {
406
405
  const c = o.kind === "file";
407
- a.push({
406
+ r.push({
408
407
  name: s,
409
408
  kind: o.kind,
410
409
  isFile: c,
411
410
  isDirectory: !c
412
411
  });
413
412
  }
414
- return a;
413
+ return r;
415
414
  } else {
416
- const a = [];
417
- for await (const [s] of r.entries())
418
- a.push(s);
419
- return a;
415
+ const r = [];
416
+ for await (const [s] of a.entries())
417
+ r.push(s);
418
+ return r;
420
419
  }
421
420
  }
422
421
  /**
@@ -436,26 +435,26 @@ class O {
436
435
  async exists(t) {
437
436
  if (await this.ensureMounted(), t === "/")
438
437
  return !0;
439
- const e = y(t);
438
+ const e = d(t);
440
439
  let i = null;
441
440
  try {
442
- i = await this.getDirectoryHandle(u(t), !1);
443
- } catch (r) {
444
- throw (r.name === "NotFoundError" || r.name === "TypeMismatchError") && (i = null), r;
441
+ i = await this.getDirectoryHandle(y(t), !1);
442
+ } catch (a) {
443
+ throw (a.name === "NotFoundError" || a.name === "TypeMismatchError") && (i = null), a;
445
444
  }
446
445
  if (!i || !e)
447
446
  return !1;
448
447
  try {
449
448
  return await i.getFileHandle(e, { create: !1 }), !0;
450
- } catch (r) {
451
- if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
452
- throw r;
449
+ } catch (a) {
450
+ if (a.name !== "NotFoundError" && a.name !== "TypeMismatchError")
451
+ throw a;
453
452
  }
454
453
  try {
455
454
  return await i.getDirectoryHandle(e, { create: !1 }), !0;
456
- } catch (r) {
457
- if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
458
- throw r;
455
+ } catch (a) {
456
+ if (a.name !== "NotFoundError" && a.name !== "TypeMismatchError")
457
+ throw a;
459
458
  }
460
459
  return !1;
461
460
  }
@@ -483,8 +482,8 @@ class O {
483
482
  try {
484
483
  const e = await this.readdir(t, { withFileTypes: !0 });
485
484
  for (const i of e) {
486
- const r = `${t === "/" ? "" : t}/${i.name}`;
487
- await this.remove(r, { recursive: !0 });
485
+ const a = `${t === "/" ? "" : t}/${i.name}`;
486
+ await this.remove(a, { recursive: !0 });
488
487
  }
489
488
  await this.notifyChange({ path: t, type: "changed", isDirectory: !0 });
490
489
  } catch (e) {
@@ -517,18 +516,18 @@ class O {
517
516
  */
518
517
  async remove(t, e) {
519
518
  await this.ensureMounted();
520
- const i = e?.recursive ?? !1, r = e?.force ?? !1;
519
+ const i = e?.recursive ?? !1, a = e?.force ?? !1;
521
520
  if (t === "/")
522
521
  throw new n("Cannot remove root directory", "EROOT");
523
- const a = y(t);
524
- if (!a)
522
+ const r = d(t);
523
+ if (!r)
525
524
  throw new g("Invalid path", t);
526
- const s = await this.getDirectoryHandle(u(t), !1);
525
+ const s = await this.getDirectoryHandle(y(t), !1);
527
526
  try {
528
- await s.removeEntry(a, { recursive: i });
527
+ await s.removeEntry(r, { recursive: i });
529
528
  } catch (o) {
530
529
  if (o.name === "NotFoundError") {
531
- if (!r)
530
+ if (!a)
532
531
  throw new n(`No such file or directory: ${t}`, "ENOENT");
533
532
  } else throw o.name === "InvalidModificationError" ? new n(`Directory not empty: ${t}. Use recursive option to force removal.`, "ENOTEMPTY") : o.name === "TypeMismatchError" && !i ? new n(`Cannot remove directory without recursive option: ${t}`, "EISDIR") : new n(`Failed to remove path: ${t}`, "RM_FAILED");
534
533
  }
@@ -555,7 +554,7 @@ class O {
555
554
  async realpath(t) {
556
555
  await this.ensureMounted();
557
556
  try {
558
- const e = A(t);
557
+ const e = M(t);
559
558
  if (!await this.exists(e))
560
559
  throw new f(e);
561
560
  return e;
@@ -617,27 +616,27 @@ class O {
617
616
  async copy(t, e, i) {
618
617
  await this.ensureMounted();
619
618
  try {
620
- const r = i?.recursive ?? !1, a = i?.force ?? !0;
619
+ const a = i?.recursive ?? !1, r = i?.force ?? !0;
621
620
  if (!await this.exists(t))
622
621
  throw new n(`Source does not exist: ${t}`, "ENOENT");
623
- if (await this.exists(e) && !a)
622
+ if (await this.exists(e) && !r)
624
623
  throw new n(`Destination already exists: ${e}`, "EEXIST");
625
624
  if ((await this.stat(t)).isFile) {
626
625
  const h = await this.readFile(t, "binary");
627
626
  await this.writeFile(e, h);
628
627
  } else {
629
- if (!r)
628
+ if (!a)
630
629
  throw new n(`Cannot copy directory without recursive option: ${t}`, "EISDIR");
631
630
  await this.mkdir(e, { recursive: !0 });
632
631
  const h = await this.readdir(t, { withFileTypes: !0 });
633
632
  for (const m of h) {
634
633
  const D = `${t}/${m.name}`, p = `${e}/${m.name}`;
635
- await this.copy(D, p, { recursive: !0, force: a });
634
+ await this.copy(D, p, { recursive: !0, force: r });
636
635
  }
637
636
  }
638
637
  await this.notifyChange({ path: e, type: "added", isDirectory: !1 });
639
- } catch (r) {
640
- throw r instanceof n ? r : new n(`Failed to copy from ${t} to ${e}`, "CP_FAILED");
638
+ } catch (a) {
639
+ throw a instanceof n ? a : new n(`Failed to copy from ${t} to ${e}`, "CP_FAILED");
641
640
  }
642
641
  }
643
642
  /**
@@ -645,7 +644,7 @@ class O {
645
644
  */
646
645
  async watch(t) {
647
646
  await this.ensureMounted();
648
- const e = d(t), i = await this.buildSnapshot(e);
647
+ const e = u(t), i = await this.buildSnapshot(e);
649
648
  this.watchers.set(e, i), this.watchTimer || (this.watchTimer = setInterval(() => {
650
649
  this.scanWatches();
651
650
  }, this.options.watchInterval));
@@ -654,16 +653,25 @@ class O {
654
653
  * Stop watching a previously watched path
655
654
  */
656
655
  unwatch(t) {
657
- const e = d(t);
656
+ const e = u(t);
658
657
  this.watchers.delete(e), this.watchers.size === 0 && this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null);
659
658
  }
659
+ /**
660
+ * Dispose of resources and clean up the file system instance
661
+ *
662
+ * This method should be called when the file system instance is no longer needed
663
+ * to properly clean up resources like the broadcast channel and watch timers.
664
+ */
665
+ dispose() {
666
+ this.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null), this.watchers.clear();
667
+ }
660
668
  async buildSnapshot(t) {
661
- const e = /* @__PURE__ */ new Map(), i = async (r) => {
662
- const a = await this.stat(r);
663
- if (e.set(r, a), a.isDirectory) {
664
- const s = await this.readdir(r, { withFileTypes: !0 });
669
+ const e = /* @__PURE__ */ new Map(), i = async (a) => {
670
+ const r = await this.stat(a);
671
+ if (e.set(a, r), r.isDirectory) {
672
+ const s = await this.readdir(a, { withFileTypes: !0 });
665
673
  for (const o of s) {
666
- const c = `${r === "/" ? "" : r}/${o.name}`;
674
+ const c = `${a === "/" ? "" : a}/${o.name}`;
667
675
  await i(c);
668
676
  }
669
677
  }
@@ -682,12 +690,15 @@ class O {
682
690
  } catch {
683
691
  i = /* @__PURE__ */ new Map();
684
692
  }
685
- for (const [r, a] of i) {
686
- const s = e.get(r);
687
- s ? (s.mtime !== a.mtime || s.size !== a.size) && await this.notifyChange({ path: r, type: "changed", isDirectory: !1 }) : await this.notifyChange({ path: r, type: "added", isDirectory: !1 });
693
+ for (const [a, r] of i) {
694
+ const s = e.get(a);
695
+ s ? (s.mtime !== r.mtime || s.size !== r.size) && await this.notifyChange({ path: a, type: "changed", isDirectory: r.isDirectory }) : await this.notifyChange({ path: a, type: "added", isDirectory: r.isDirectory });
688
696
  }
689
- for (const r of e.keys())
690
- i.has(r) || await this.notifyChange({ path: r, type: "removed", isDirectory: !1 });
697
+ for (const a of e.keys())
698
+ if (!i.has(a)) {
699
+ const r = e.get(a);
700
+ await this.notifyChange({ path: a, type: "removed", isDirectory: r?.isDirectory ?? !1 });
701
+ }
691
702
  this.watchers.set(t, i);
692
703
  })
693
704
  );
@@ -728,10 +739,10 @@ class O {
728
739
  await this.ensureMounted();
729
740
  try {
730
741
  (e?.cleanBefore ?? !1) && await this.clear("/");
731
- for (const [r, a] of t) {
732
- const s = d(r);
742
+ for (const [a, r] of t) {
743
+ const s = u(a);
733
744
  let o;
734
- a instanceof Blob ? o = await $(a) : o = a, await this.writeFile(s, o);
745
+ r instanceof Blob ? o = await b(r) : o = r, await this.writeFile(s, o);
735
746
  }
736
747
  await this.notifyChange({ path: "/", type: "changed", isDirectory: !0 });
737
748
  } catch (i) {
@@ -739,8 +750,8 @@ class O {
739
750
  }
740
751
  }
741
752
  }
742
- typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && E(new O());
753
+ typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && E(new A());
743
754
  export {
744
- O as OPFSWorker
755
+ A as OPFSWorker
745
756
  };
746
757
  //# sourceMappingURL=raw.js.map