opfs-worker 0.3.3 → 0.4.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,9 +1,9 @@
1
- import { expose as p } 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 D, j as C, e as d, f as y, k as M, r as T, n as u, l as b } from "./helpers-DxFcNkZe.js";
3
- class A {
1
+ import { expose as v } from "comlink";
2
+ import { d as S, n as f, O as n, b as m, s as u, P as E, k as I, p as C, F as g, w as p, j as b, e as D, f as F, l as T, r as x, i as d, m as A } from "./helpers-BgMlBRWa.js";
3
+ class $ {
4
4
  /** Root directory handle for the file system */
5
5
  root = null;
6
- /** Map of watched paths to their last known state */
6
+ /** Map of watched paths to their last known state and options */
7
7
  watchers = /* @__PURE__ */ new Map();
8
8
  /** Interval handle for polling watched paths */
9
9
  watchTimer = null;
@@ -15,6 +15,7 @@ class A {
15
15
  broadcastChannel = null;
16
16
  /** Configuration options */
17
17
  options = {
18
+ root: "/",
18
19
  watchInterval: 1e3,
19
20
  maxFileSize: 50 * 1024 * 1024,
20
21
  hashAlgorithm: null,
@@ -43,7 +44,7 @@ class A {
43
44
  try {
44
45
  this.broadcastChannel || (this.broadcastChannel = new BroadcastChannel(this.options.broadcastChannel));
45
46
  const i = {
46
- root: this.root.name,
47
+ root: this.options.root,
47
48
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48
49
  ...t,
49
50
  ...e && { hash: e }
@@ -57,13 +58,14 @@ class A {
57
58
  * Creates a new OPFSFileSystem instance
58
59
  *
59
60
  * @param options - Optional configuration options
61
+ * @param options.root - Root path for the file system (default: '/')
60
62
  * @param options.watchInterval - Polling interval in milliseconds for file watching
61
63
  * @param options.hashAlgorithm - Hash algorithm for file hashing
62
64
  * @param options.maxFileSize - Maximum file size for hashing in bytes (default: 50MB)
63
65
  * @throws {OPFSError} If OPFS is not supported in the current browser
64
66
  */
65
67
  constructor(t) {
66
- S(), t && this.setOptions(t), this.mount("/");
68
+ S(), t && this.setOptions(t);
67
69
  }
68
70
  /**
69
71
  * Initialize the file system within a given directory
@@ -86,8 +88,8 @@ class A {
86
88
  * await fs.mount('/my-app');
87
89
  * ```
88
90
  */
89
- async mount(t = "/") {
90
- return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, i) => {
91
+ async mount(t = this.options.root) {
92
+ return this.mountingPromise && await this.mountingPromise, t = f(t), this.mountingPromise = new Promise(async (e, i) => {
91
93
  this.root = null;
92
94
  try {
93
95
  const a = await navigator.storage.getDirectory();
@@ -103,31 +105,14 @@ class A {
103
105
  * Update configuration options
104
106
  *
105
107
  * @param options - Configuration options to update
108
+ * @param options.root - Root path for the file system
106
109
  * @param options.watchInterval - Polling interval in milliseconds for file watching
107
110
  * @param options.hashAlgorithm - Hash algorithm for file hashing
108
111
  * @param options.maxFileSize - Maximum file size for hashing in bytes
109
112
  * @param options.broadcastChannel - Custom name for the broadcast channel
110
113
  */
111
- 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), t.broadcastChannel !== void 0 && (this.broadcastChannel && this.options.broadcastChannel !== t.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.options.broadcastChannel = t.broadcastChannel);
113
- }
114
- /**
115
- * Automatically mount the OPFS root if not already mounted
116
- *
117
- * This method is called internally when file operations are performed
118
- * without explicitly mounting first.
119
- *
120
- * @returns Promise that resolves when auto-mount is complete
121
- * @throws {OPFSError} If auto-mount fails
122
- */
123
- async ensureMounted() {
124
- if (!this.root) {
125
- if (this.mountingPromise) {
126
- await this.mountingPromise;
127
- return;
128
- }
129
- throw new n("OPFS not mounted", "NOT_MOUNTED");
130
- }
114
+ async setOptions(t) {
115
+ 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), t.root !== void 0 && (this.options.root = t.root, await this.mount(this.options.root));
131
116
  }
132
117
  /**
133
118
  * Get a directory handle from a path
@@ -149,8 +134,8 @@ class A {
149
134
  */
150
135
  async getDirectoryHandle(t, e = !1, i = this.root) {
151
136
  if (!i)
152
- throw new l();
153
- const a = Array.isArray(t) ? t : w(t);
137
+ throw new m();
138
+ const a = Array.isArray(t) ? t : u(t);
154
139
  let r = i;
155
140
  for (const s of a)
156
141
  r = await r.getDirectoryHandle(s, { create: e });
@@ -177,10 +162,10 @@ class A {
177
162
  */
178
163
  async getFileHandle(t, e = !1, i = this.root) {
179
164
  if (!i)
180
- throw new l();
181
- const a = w(t);
165
+ throw new m();
166
+ const a = u(t);
182
167
  if (a.length === 0)
183
- throw new g("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
168
+ throw new E("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
184
169
  const r = a.pop();
185
170
  return (await this.getDirectoryHandle(a, e, i)).getFileHandle(r, { create: e });
186
171
  }
@@ -226,12 +211,12 @@ class A {
226
211
  }), await e("/"), t;
227
212
  }
228
213
  async readFile(t, e = "utf-8") {
229
- await this.ensureMounted();
214
+ await this.mount();
230
215
  try {
231
- const i = await this.getFileHandle(t, !1), a = await v(i);
232
- return e === "binary" ? a : I(a, e);
216
+ const i = await this.getFileHandle(t, !1), a = await I(i);
217
+ return e === "binary" ? a : C(a, e);
233
218
  } catch (i) {
234
- throw console.error(i), new f(t);
219
+ throw console.error(i), new g(t);
235
220
  }
236
221
  }
237
222
  /**
@@ -260,9 +245,9 @@ class A {
260
245
  * ```
261
246
  */
262
247
  async writeFile(t, e, i) {
263
- await this.ensureMounted();
248
+ await this.mount();
264
249
  const a = await this.getFileHandle(t, !0);
265
- await D(a, e, i, { truncate: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
250
+ await p(a, e, i, { truncate: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
266
251
  }
267
252
  /**
268
253
  * Append data to a file
@@ -287,9 +272,9 @@ class A {
287
272
  * ```
288
273
  */
289
274
  async appendFile(t, e, i) {
290
- await this.ensureMounted();
275
+ await this.mount();
291
276
  const a = await this.getFileHandle(t, !0);
292
- await D(a, e, i, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
277
+ await p(a, e, i, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
293
278
  }
294
279
  /**
295
280
  * Create a directory
@@ -313,9 +298,9 @@ class A {
313
298
  * ```
314
299
  */
315
300
  async mkdir(t, e) {
316
- if (await this.ensureMounted(), !this.root)
317
- throw new l();
318
- const i = e?.recursive ?? !1, a = w(t);
301
+ if (await this.mount(), !this.root)
302
+ throw new m();
303
+ const i = e?.recursive ?? !1, a = u(t);
319
304
  let r = this.root;
320
305
  for (let s = 0; s < a.length; s++) {
321
306
  const o = a[s];
@@ -323,7 +308,7 @@ class A {
323
308
  r = await r.getDirectoryHandle(o, { create: i || s === a.length - 1 });
324
309
  } catch (c) {
325
310
  throw c.name === "NotFoundError" ? new n(
326
- `Parent directory does not exist: ${C(a.slice(0, s + 1))}`,
311
+ `Parent directory does not exist: ${b(a.slice(0, s + 1))}`,
327
312
  "ENOENT"
328
313
  ) : c.name === "TypeMismatchError" ? new n(`Path segment is not a directory: ${o}`, "ENOTDIR") : new n("Failed to create directory", "MKDIR_FAILED");
329
314
  }
@@ -353,7 +338,7 @@ class A {
353
338
  * ```
354
339
  */
355
340
  async stat(t) {
356
- if (await this.ensureMounted(), t === "/")
341
+ if (await this.mount(), t === "/")
357
342
  return {
358
343
  kind: "directory",
359
344
  size: 0,
@@ -362,7 +347,7 @@ class A {
362
347
  isFile: !1,
363
348
  isDirectory: !0
364
349
  };
365
- const e = d(t), i = await this.getDirectoryHandle(y(t), !1), a = this.options.hashAlgorithm !== null;
350
+ const e = D(t), i = await this.getDirectoryHandle(F(t), !1), a = this.options.hashAlgorithm !== null;
366
351
  try {
367
352
  const s = await (await i.getFileHandle(e, { create: !1 })).getFile(), o = {
368
353
  kind: "file",
@@ -374,7 +359,7 @@ class A {
374
359
  };
375
360
  if (a && this.options.hashAlgorithm)
376
361
  try {
377
- const c = await M(s, this.options.hashAlgorithm, this.options.maxFileSize);
362
+ const c = await T(s, this.options.hashAlgorithm, this.options.maxFileSize);
378
363
  o.hash = c;
379
364
  } catch (c) {
380
365
  console.warn(`Failed to calculate hash for ${t}:`, c);
@@ -416,7 +401,7 @@ class A {
416
401
  * ```
417
402
  */
418
403
  async readDir(t) {
419
- await this.ensureMounted();
404
+ await this.mount();
420
405
  const e = await this.getDirectoryHandle(t, !1), i = [];
421
406
  for await (const [a, r] of e.entries()) {
422
407
  const s = r.kind === "file";
@@ -444,12 +429,12 @@ class A {
444
429
  * ```
445
430
  */
446
431
  async exists(t) {
447
- if (await this.ensureMounted(), t === "/")
432
+ if (await this.mount(), t === "/")
448
433
  return !0;
449
- const e = d(t);
434
+ const e = D(t);
450
435
  let i = null;
451
436
  try {
452
- i = await this.getDirectoryHandle(y(t), !1);
437
+ i = await this.getDirectoryHandle(F(t), !1);
453
438
  } catch (a) {
454
439
  throw (a.name === "NotFoundError" || a.name === "TypeMismatchError") && (i = null), a;
455
440
  }
@@ -489,7 +474,7 @@ class A {
489
474
  * ```
490
475
  */
491
476
  async clear(t = "/") {
492
- await this.ensureMounted();
477
+ await this.mount();
493
478
  try {
494
479
  const e = await this.readDir(t);
495
480
  for (const i of e) {
@@ -526,14 +511,14 @@ class A {
526
511
  * ```
527
512
  */
528
513
  async remove(t, e) {
529
- await this.ensureMounted();
514
+ await this.mount();
530
515
  const i = e?.recursive ?? !1, a = e?.force ?? !1;
531
516
  if (t === "/")
532
517
  throw new n("Cannot remove root directory", "EROOT");
533
- const r = d(t);
518
+ const r = D(t);
534
519
  if (!r)
535
- throw new g("Invalid path", t);
536
- const s = await this.getDirectoryHandle(y(t), !1);
520
+ throw new E("Invalid path", t);
521
+ const s = await this.getDirectoryHandle(F(t), !1);
537
522
  try {
538
523
  await s.removeEntry(r, { recursive: i });
539
524
  } catch (o) {
@@ -563,11 +548,11 @@ class A {
563
548
  * ```
564
549
  */
565
550
  async realpath(t) {
566
- await this.ensureMounted();
551
+ await this.mount();
567
552
  try {
568
- const e = T(t);
553
+ const e = x(t);
569
554
  if (!await this.exists(e))
570
- throw new f(e);
555
+ throw new g(e);
571
556
  return e;
572
557
  } catch (e) {
573
558
  throw e instanceof n ? e : new n(`Failed to resolve path: ${t}`, "REALPATH_FAILED");
@@ -590,10 +575,10 @@ class A {
590
575
  * ```
591
576
  */
592
577
  async rename(t, e) {
593
- await this.ensureMounted();
578
+ await this.mount();
594
579
  try {
595
580
  if (!await this.exists(t))
596
- throw new f(t);
581
+ throw new g(t);
597
582
  await this.copy(t, e, { recursive: !0 }), await this.remove(t, { recursive: !0 }), await this.notifyChange({ path: t, type: "removed", isDirectory: !1 }), await this.notifyChange({ path: e, type: "added", isDirectory: !1 });
598
583
  } catch (i) {
599
584
  throw i instanceof n ? i : new n(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED");
@@ -625,7 +610,7 @@ class A {
625
610
  * ```
626
611
  */
627
612
  async copy(t, e, i) {
628
- await this.ensureMounted();
613
+ await this.mount();
629
614
  try {
630
615
  const a = i?.recursive ?? !1, r = i?.force ?? !0;
631
616
  if (!await this.exists(t))
@@ -640,9 +625,9 @@ class A {
640
625
  throw new n(`Cannot copy directory without recursive option: ${t}`, "EISDIR");
641
626
  await this.mkdir(e, { recursive: !0 });
642
627
  const h = await this.readDir(t);
643
- for (const m of h) {
644
- const F = `${t}/${m.name}`, E = `${e}/${m.name}`;
645
- await this.copy(F, E, { recursive: !0, force: r });
628
+ for (const w of h) {
629
+ const y = `${t}/${w.name}`, l = `${e}/${w.name}`;
630
+ await this.copy(y, l, { recursive: !0, force: r });
646
631
  }
647
632
  }
648
633
  await this.notifyChange({ path: e, type: "added", isDirectory: !1 });
@@ -652,11 +637,29 @@ class A {
652
637
  }
653
638
  /**
654
639
  * Start watching a file or directory for changes
640
+ *
641
+ * @param path - The path to watch
642
+ * @param options - Watch options
643
+ * @param options.recursive - Whether to watch recursively (default: true)
644
+ * @param options.excludes - Glob pattern(s) to exclude (minimatch).
645
+ * @returns Promise that resolves when watching starts
646
+ *
647
+ * @example
648
+ * ```typescript
649
+ * // Watch entire directory tree recursively (default)
650
+ * await fs.watch('/data');
651
+ *
652
+ * // Watch only immediate children (shallow)
653
+ * await fs.watch('/data', { recursive: false });
654
+ *
655
+ * // Watch a single file
656
+ * await fs.watch('/config.json', { recursive: false });
657
+ * ```
655
658
  */
656
- async watch(t) {
657
- await this.ensureMounted();
658
- const e = u(t), i = await this.buildSnapshot(e);
659
- this.watchers.set(e, i), this.watchTimer || (this.watchTimer = setInterval(() => {
659
+ async watch(t, e) {
660
+ await this.mount();
661
+ const i = f(t), a = { recursive: !0, ...e }, r = await this.buildSnapshot(i, a);
662
+ this.watchers.set(i, { snapshot: r, options: a }), this.watchTimer || (this.watchTimer = setInterval(() => {
660
663
  this.scanWatches();
661
664
  }, this.options.watchInterval));
662
665
  }
@@ -664,7 +667,7 @@ class A {
664
667
  * Stop watching a previously watched path
665
668
  */
666
669
  unwatch(t) {
667
- const e = u(t);
670
+ const e = f(t);
668
671
  this.watchers.delete(e), this.watchers.size === 0 && this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null);
669
672
  }
670
673
  /**
@@ -676,41 +679,45 @@ class A {
676
679
  dispose() {
677
680
  this.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null), this.watchers.clear();
678
681
  }
679
- async buildSnapshot(t) {
680
- const e = /* @__PURE__ */ new Map(), i = async (a) => {
681
- const r = await this.stat(a);
682
- if (e.set(a, r), r.isDirectory) {
683
- const s = await this.readDir(a);
684
- for (const o of s) {
685
- const c = `${a === "/" ? "" : a}/${o.name}`;
686
- await i(c);
682
+ async buildSnapshot(t, e) {
683
+ const i = /* @__PURE__ */ new Map(), a = e?.recursive ?? !0, r = e?.excludes, s = async (c) => {
684
+ const h = await this.stat(c);
685
+ return i.set(c, h), h;
686
+ }, o = async (c) => {
687
+ if (d(c, r))
688
+ return;
689
+ if ((await s(c)).isDirectory) {
690
+ const w = await this.readDir(c);
691
+ for (const y of w) {
692
+ const l = `${c === "/" ? "" : c}/${y.name}`;
693
+ a ? await o(l) : d(l, r) || await s(l);
687
694
  }
688
695
  }
689
696
  };
690
- return await i(t), e;
697
+ return await o(t), i;
691
698
  }
692
699
  async scanWatches() {
693
700
  if (!this.scanning) {
694
701
  this.scanning = !0;
695
702
  try {
696
703
  await Promise.all(
697
- [...this.watchers.entries()].map(async ([t, e]) => {
698
- let i;
704
+ [...this.watchers.entries()].map(async ([t, { snapshot: e, options: i }]) => {
705
+ let a;
699
706
  try {
700
- i = await this.buildSnapshot(t);
707
+ a = await this.buildSnapshot(t, i);
701
708
  } catch {
702
- i = /* @__PURE__ */ new Map();
709
+ a = /* @__PURE__ */ new Map();
703
710
  }
704
- for (const [a, r] of i) {
705
- const s = e.get(a);
706
- 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 });
711
+ for (const [r, s] of a) {
712
+ const o = e.get(r);
713
+ d(r, i.excludes) || (o ? (o.mtime !== s.mtime || o.size !== s.size) && await this.notifyChange({ path: r, type: "changed", isDirectory: s.isDirectory }) : await this.notifyChange({ path: r, type: "added", isDirectory: s.isDirectory }));
707
714
  }
708
- for (const a of e.keys())
709
- if (!i.has(a)) {
710
- const r = e.get(a);
711
- await this.notifyChange({ path: a, type: "removed", isDirectory: r?.isDirectory ?? !1 });
715
+ for (const r of e.keys())
716
+ if (!d(r, i.excludes) && !a.has(r)) {
717
+ const s = e.get(r);
718
+ await this.notifyChange({ path: r, type: "removed", isDirectory: s?.isDirectory ?? !1 });
712
719
  }
713
- this.watchers.set(t, i);
720
+ this.watchers.set(t, { snapshot: a, options: i });
714
721
  })
715
722
  );
716
723
  } finally {
@@ -747,13 +754,13 @@ class A {
747
754
  * ```
748
755
  */
749
756
  async sync(t, e) {
750
- await this.ensureMounted();
757
+ await this.mount();
751
758
  try {
752
759
  (e?.cleanBefore ?? !1) && await this.clear("/");
753
760
  for (const [a, r] of t) {
754
- const s = u(a);
761
+ const s = f(a);
755
762
  let o;
756
- r instanceof Blob ? o = await b(r) : o = r, await this.writeFile(s, o);
763
+ r instanceof Blob ? o = await A(r) : o = r, await this.writeFile(s, o);
757
764
  }
758
765
  await this.notifyChange({ path: "/", type: "changed", isDirectory: !0 });
759
766
  } catch (i) {
@@ -761,8 +768,8 @@ class A {
761
768
  }
762
769
  }
763
770
  }
764
- typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && p(new A());
771
+ typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && v(new $());
765
772
  export {
766
- A as OPFSWorker
773
+ $ as OPFSWorker
767
774
  };
768
775
  //# sourceMappingURL=raw.js.map