opfs-worker 0.2.1 → 0.2.3

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
- 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 {
1
+ import { expose as v } from "comlink";
2
+ import { d as I, O as o, b as f, s as u, P as g, i as p, o as S, F as d, w as E, 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
+ I(), 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
- 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 });
140
+ throw new g("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
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 p(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 E(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 E(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,18 @@ 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
+ if (await this.ensureMounted(), t === "/")
338
+ return {
339
+ kind: "directory",
340
+ size: 0,
341
+ mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
342
+ ctime: (/* @__PURE__ */ new Date(0)).toISOString(),
343
+ isFile: !1,
344
+ isDirectory: !0
345
+ };
346
+ const i = m(t), r = await this.getDirectoryHandle(y(t), !1), a = e?.includeHash ?? !1, n = e?.hashAlgorithm ?? "SHA-1";
294
347
  try {
295
- const c = await (await r.getFileHandle(a, { create: !1 })).getFile(), l = {
348
+ const c = await (await r.getFileHandle(i, { create: !1 })).getFile(), l = {
296
349
  kind: "file",
297
350
  size: c.size,
298
351
  mtime: new Date(c.lastModified).toISOString(),
@@ -300,9 +353,9 @@ class N {
300
353
  isFile: !0,
301
354
  isDirectory: !1
302
355
  };
303
- if (i)
356
+ if (a)
304
357
  try {
305
- const h = new Uint8Array(await c.arrayBuffer()), w = await H(h, n);
358
+ const h = new Uint8Array(await c.arrayBuffer()), w = await M(h, n);
306
359
  l.hash = w;
307
360
  } catch (h) {
308
361
  console.warn(`Failed to calculate hash for ${t}:`, h);
@@ -313,7 +366,7 @@ class N {
313
366
  throw new o("Failed to stat (file)", "STAT_FAILED");
314
367
  }
315
368
  try {
316
- return await r.getDirectoryHandle(a, { create: !1 }), {
369
+ return await r.getDirectoryHandle(i, { create: !1 }), {
317
370
  kind: "directory",
318
371
  size: 0,
319
372
  mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
@@ -326,24 +379,25 @@ class N {
326
379
  }
327
380
  }
328
381
  async readdir(t, e) {
329
- const a = e?.withFileTypes ?? !1, r = await this.getDirectoryHandle(t, !1);
330
- if (a) {
331
- const i = [];
382
+ await this.ensureMounted();
383
+ const i = e?.withFileTypes ?? !1, r = await this.getDirectoryHandle(t, !1);
384
+ if (i) {
385
+ const a = [];
332
386
  for await (const [n, s] of r.entries()) {
333
387
  const c = s.kind === "file";
334
- i.push({
388
+ a.push({
335
389
  name: n,
336
390
  kind: s.kind,
337
391
  isFile: c,
338
392
  isDirectory: !c
339
393
  });
340
394
  }
341
- return i;
395
+ return a;
342
396
  } else {
343
- const i = [];
397
+ const a = [];
344
398
  for await (const [n] of r.entries())
345
- i.push(n);
346
- return i;
399
+ a.push(n);
400
+ return a;
347
401
  }
348
402
  }
349
403
  /**
@@ -361,23 +415,25 @@ class N {
361
415
  * ```
362
416
  */
363
417
  async exists(t) {
364
- const e = u(t);
365
- let a = null;
418
+ if (await this.ensureMounted(), t === "/")
419
+ return !0;
420
+ const e = m(t);
421
+ let i = null;
366
422
  try {
367
- a = await this.getDirectoryHandle(m(t), !1);
423
+ i = await this.getDirectoryHandle(y(t), !1);
368
424
  } catch (r) {
369
- throw (r.name === "NotFoundError" || r.name === "TypeMismatchError") && (a = null), r;
425
+ throw (r.name === "NotFoundError" || r.name === "TypeMismatchError") && (i = null), r;
370
426
  }
371
- if (!a || !e)
427
+ if (!i || !e)
372
428
  return !1;
373
429
  try {
374
- return await a.getFileHandle(e, { create: !1 }), !0;
430
+ return await i.getFileHandle(e, { create: !1 }), !0;
375
431
  } catch (r) {
376
432
  if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
377
433
  throw r;
378
434
  }
379
435
  try {
380
- return await a.getDirectoryHandle(e, { create: !1 }), !0;
436
+ return await i.getDirectoryHandle(e, { create: !1 }), !0;
381
437
  } catch (r) {
382
438
  if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
383
439
  throw r;
@@ -404,10 +460,11 @@ class N {
404
460
  * ```
405
461
  */
406
462
  async clear(t = "/") {
463
+ await this.ensureMounted();
407
464
  try {
408
465
  const e = await this.readdir(t, { withFileTypes: !0 });
409
- for (const a of e) {
410
- const r = `${t === "/" ? "" : t}/${a.name}`;
466
+ for (const i of e) {
467
+ const r = `${t === "/" ? "" : t}/${i.name}`;
411
468
  await this.remove(r, { recursive: !0 });
412
469
  }
413
470
  } catch (e) {
@@ -439,17 +496,21 @@ class N {
439
496
  * ```
440
497
  */
441
498
  async remove(t, e) {
442
- const a = e?.recursive ?? !1, r = e?.force ?? !1, i = u(t);
443
- if (!i)
444
- throw new E("Invalid path", t);
445
- const n = await this.getDirectoryHandle(m(t), !1);
499
+ await this.ensureMounted();
500
+ const i = e?.recursive ?? !1, r = e?.force ?? !1;
501
+ if (t === "/")
502
+ throw new o("Cannot remove root directory", "EROOT");
503
+ const a = m(t);
504
+ if (!a)
505
+ throw new g("Invalid path", t);
506
+ const n = await this.getDirectoryHandle(y(t), !1);
446
507
  try {
447
- await n.removeEntry(i, { recursive: a });
508
+ await n.removeEntry(a, { recursive: i });
448
509
  } catch (s) {
449
510
  if (s.name === "NotFoundError") {
450
511
  if (!r)
451
512
  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");
513
+ } 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
514
  }
454
515
  }
455
516
  /**
@@ -471,8 +532,9 @@ class N {
471
532
  * ```
472
533
  */
473
534
  async realpath(t) {
535
+ await this.ensureMounted();
474
536
  try {
475
- const e = $(t);
537
+ const e = H(t);
476
538
  if (!await this.exists(e))
477
539
  throw new d(e);
478
540
  return e;
@@ -497,12 +559,13 @@ class N {
497
559
  * ```
498
560
  */
499
561
  async rename(t, e) {
562
+ await this.ensureMounted();
500
563
  try {
501
564
  if (!await this.exists(t))
502
565
  throw new d(t);
503
566
  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");
567
+ } catch (i) {
568
+ throw i instanceof o ? i : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED");
506
569
  }
507
570
  }
508
571
  /**
@@ -521,21 +584,22 @@ class N {
521
584
  * @example
522
585
  * ```typescript
523
586
  * // Copy a file
524
- * await fs.cp('/source/file.txt', '/dest/file.txt');
587
+ * await fs.copy('/source/file.txt', '/dest/file.txt');
525
588
  *
526
589
  * // Copy a directory and all its contents
527
- * await fs.cp('/source/dir', '/dest/dir', { recursive: true });
590
+ * await fs.copy('/source/dir', '/dest/dir', { recursive: true });
528
591
  *
529
592
  * // Copy without overwriting existing files
530
- * await fs.cp('/source', '/dest', { recursive: true, force: false });
593
+ * await fs.copy('/source', '/dest', { recursive: true, force: false });
531
594
  * ```
532
595
  */
533
- async copy(t, e, a) {
596
+ async copy(t, e, i) {
597
+ await this.ensureMounted();
534
598
  try {
535
- const r = a?.recursive ?? !1, i = a?.force ?? !0;
599
+ const r = i?.recursive ?? !1, a = i?.force ?? !0;
536
600
  if (!await this.exists(t))
537
601
  throw new o(`Source does not exist: ${t}`, "ENOENT");
538
- if (await this.exists(e) && !i)
602
+ if (await this.exists(e) && !a)
539
603
  throw new o(`Destination already exists: ${e}`, "EEXIST");
540
604
  if ((await this.stat(t)).isFile) {
541
605
  const l = await this.readFile(t, "binary");
@@ -547,7 +611,7 @@ class N {
547
611
  const l = await this.readdir(t, { withFileTypes: !0 });
548
612
  for (const h of l) {
549
613
  const w = `${t}/${h.name}`, D = `${e}/${h.name}`;
550
- await this.copy(w, D, { recursive: !0, force: i });
614
+ await this.copy(w, D, { recursive: !0, force: a });
551
615
  }
552
616
  }
553
617
  } catch (r) {
@@ -558,8 +622,9 @@ class N {
558
622
  * Start watching a file or directory for changes
559
623
  */
560
624
  async watch(t) {
561
- const e = F(t), a = await this.buildSnapshot(e);
562
- this.watchers.set(e, a), this.watchTimer || (this.watchTimer = setInterval(() => {
625
+ await this.ensureMounted();
626
+ const e = F(t), i = await this.buildSnapshot(e);
627
+ this.watchers.set(e, i), this.watchTimer || (this.watchTimer = setInterval(() => {
563
628
  this.scanWatches();
564
629
  }, this.watchInterval));
565
630
  }
@@ -571,17 +636,17 @@ class N {
571
636
  this.watchers.delete(e), this.watchers.size === 0 && this.watchTimer && (clearInterval(this.watchTimer), this.watchTimer = null);
572
637
  }
573
638
  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) {
639
+ const e = /* @__PURE__ */ new Map(), i = async (r) => {
640
+ const a = await this.stat(r);
641
+ if (e.set(r, a), a.isDirectory) {
577
642
  const n = await this.readdir(r, { withFileTypes: !0 });
578
643
  for (const s of n) {
579
644
  const c = `${r === "/" ? "" : r}/${s.name}`;
580
- await a(c);
645
+ await i(c);
581
646
  }
582
647
  }
583
648
  };
584
- return await a(t), e;
649
+ return await i(t), e;
585
650
  }
586
651
  async scanWatches() {
587
652
  if (!this.scanning) {
@@ -589,23 +654,23 @@ class N {
589
654
  try {
590
655
  await Promise.all(
591
656
  [...this.watchers.entries()].map(async ([t, e]) => {
592
- let a;
657
+ let i;
593
658
  try {
594
- a = await this.buildSnapshot(t);
659
+ i = await this.buildSnapshot(t);
595
660
  } catch {
596
- a = /* @__PURE__ */ new Map();
661
+ i = /* @__PURE__ */ new Map();
597
662
  }
598
663
  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" });
664
+ for (const [a, n] of i) {
665
+ const s = e.get(a);
666
+ s ? (s.mtime !== n.mtime || s.size !== n.size) && r.push({ path: a, type: "change" }) : r.push({ path: a, type: "create" });
602
667
  }
603
- for (const i of e.keys())
604
- a.has(i) || r.push({ path: i, type: "delete" });
668
+ for (const a of e.keys())
669
+ i.has(a) || r.push({ path: a, type: "delete" });
605
670
  if (r.length && this.watchCallback)
606
- for (const i of r)
607
- this.watchCallback(i);
608
- this.watchers.set(t, a);
671
+ for (const a of r)
672
+ this.watchCallback(a);
673
+ this.watchers.set(t, i);
609
674
  })
610
675
  );
611
676
  } finally {
@@ -642,20 +707,21 @@ class N {
642
707
  * ```
643
708
  */
644
709
  async sync(t, e) {
710
+ await this.ensureMounted();
645
711
  try {
646
712
  (e?.cleanBefore ?? !1) && await this.clear("/");
647
- for (const [r, i] of t) {
713
+ for (const [r, a] of t) {
648
714
  const n = F(r);
649
715
  let s;
650
- i instanceof Blob ? s = await A(i) : s = i, await this.writeFile(n, s);
716
+ a instanceof Blob ? s = await P(a) : s = a, await this.writeFile(n, s);
651
717
  }
652
- } catch (a) {
653
- throw a instanceof o ? a : new o("Failed to sync file system", "SYNC_FAILED");
718
+ } catch (i) {
719
+ throw i instanceof o ? i : new o("Failed to sync file system", "SYNC_FAILED");
654
720
  }
655
721
  }
656
722
  }
657
- typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && p(new N());
723
+ typeof self < "u" && self.constructor.name === "DedicatedWorkerGlobalScope" && v(new $());
658
724
  export {
659
- N as OPFSWorker
725
+ $ as OPFSWorker
660
726
  };
661
727
  //# sourceMappingURL=raw.js.map