opfs-worker 1.0.1 → 1.1.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,6 +1,6 @@
1
- import { expose as v } from "comlink";
2
- import { m as h, d as p, O as o, n as u, s as w, P as S, i as d, o as b, y as C, F as f, p as g, j as x, e as F, f as m, q as A, v as I, r as H, g as $, u as O } from "./helpers-CTCvNFs1.js";
3
- class T {
1
+ import { expose as H, transfer as D } from "comlink";
2
+ import { O as o, i as y, e as C, n as u, s as m, P as b, E as g, p as A, H as $, F, q as E, j as O, f as v, g as p, t as T, x as N, r as S, h as P, w as _, B as k, z as x, y as I, A as L, d, v as z } from "./helpers-DS5dyURe.js";
3
+ class M {
4
4
  /** Root directory handle for the file system */
5
5
  root;
6
6
  /** Map of watched paths and options */
@@ -17,6 +17,20 @@ class T {
17
17
  hashAlgorithm: null,
18
18
  broadcastChannel: "opfs-worker"
19
19
  };
20
+ /** Map of open file descriptors to their metadata */
21
+ openFiles = /* @__PURE__ */ new Map();
22
+ /** Next available file descriptor number */
23
+ nextFd = 1;
24
+ /**
25
+ * Get file info by descriptor with validation
26
+ * @private
27
+ */
28
+ _getFileDescriptor(t) {
29
+ const e = this.openFiles.get(t);
30
+ if (!e)
31
+ throw new o(`Invalid file descriptor: ${t}`, "EBADF");
32
+ return e;
33
+ }
20
34
  /**
21
35
  * Notify about internal changes to the file system
22
36
  *
@@ -30,12 +44,12 @@ class T {
30
44
  if (!this.options.broadcastChannel)
31
45
  return;
32
46
  const e = t.path;
33
- if (![...this.watchers.values()].some((r) => h(e, r.pattern) && r.include.some((s) => s && h(e, s)) && !r.exclude.some((s) => s && h(e, s))))
47
+ if (![...this.watchers.values()].some((r) => y(e, r.pattern) && r.include.some((s) => s && y(e, s)) && !r.exclude.some((s) => s && y(e, s))))
34
48
  return;
35
- let i;
49
+ let a;
36
50
  if (this.options.hashAlgorithm)
37
51
  try {
38
- i = (await this.stat(e)).hash;
52
+ a = (await this.stat(e)).hash;
39
53
  } catch {
40
54
  }
41
55
  try {
@@ -44,7 +58,7 @@ class T {
44
58
  namespace: this.options.namespace,
45
59
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
46
60
  ...t,
47
- ...i && { hash: i }
61
+ ...a && { hash: a }
48
62
  };
49
63
  this.broadcastChannel.postMessage(r);
50
64
  } catch (r) {
@@ -62,7 +76,7 @@ class T {
62
76
  * @throws {OPFSError} If OPFS is not supported in the current browser
63
77
  */
64
78
  constructor(t) {
65
- p(), t && this.setOptions(t);
79
+ C(), t && this.setOptions(t);
66
80
  }
67
81
  /**
68
82
  * Initialize the file system within a given directory
@@ -87,12 +101,12 @@ class T {
87
101
  */
88
102
  async mount() {
89
103
  const t = this.options.root;
90
- return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, a) => {
104
+ return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, i) => {
91
105
  try {
92
- const i = await navigator.storage.getDirectory();
93
- this.root = t === "/" ? i : await this.getDirectoryHandle(t, !0, i), e(!0);
94
- } catch (i) {
95
- a(new o("Failed to initialize OPFS", "INIT_FAILED", t, i));
106
+ const a = await navigator.storage.getDirectory();
107
+ this.root = t === "/" ? a : await this.getDirectoryHandle(t, !0, a), e(!0);
108
+ } catch (a) {
109
+ i(new o("Failed to initialize OPFS", "INIT_FAILED", t, a));
96
110
  } finally {
97
111
  this.mountingPromise = null;
98
112
  }
@@ -129,10 +143,10 @@ class T {
129
143
  * const docsDir2 = await fs.getDirectoryHandle(['users', 'john', 'documents'], true);
130
144
  * ```
131
145
  */
132
- async getDirectoryHandle(t, e = !1, a = this.root) {
133
- const i = Array.isArray(t) ? t : w(t);
134
- let r = a;
135
- for (const s of i)
146
+ async getDirectoryHandle(t, e = !1, i = this.root) {
147
+ const a = Array.isArray(t) ? t : m(t);
148
+ let r = i;
149
+ for (const s of a)
136
150
  r = await r.getDirectoryHandle(s, { create: e });
137
151
  return r;
138
152
  }
@@ -155,12 +169,12 @@ class T {
155
169
  * const fileHandle2 = await fs.getFileHandle(['config', 'settings.json'], true);
156
170
  * ```
157
171
  */
158
- async getFileHandle(t, e = !1, a = this.root) {
159
- const i = w(t);
160
- if (i.length === 0)
161
- throw new S("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
162
- const r = i.pop();
163
- return (await this.getDirectoryHandle(i, e, a)).getFileHandle(r, { create: e });
172
+ async getFileHandle(t, e = !1, i = this.root) {
173
+ const a = m(t);
174
+ if (a.length === 0)
175
+ throw new b("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
176
+ const r = a.pop();
177
+ return (await this.getDirectoryHandle(a, e, i)).getFileHandle(r, { create: e });
164
178
  }
165
179
  /**
166
180
  * Get a complete index of all files and directories in the file system
@@ -182,10 +196,10 @@ class T {
182
196
  * ```
183
197
  */
184
198
  async index() {
185
- const t = /* @__PURE__ */ new Map(), e = async (a) => {
186
- const i = await this.readDir(a);
187
- for (const r of i) {
188
- const s = `${a === "/" ? "" : a}/${r.name}`;
199
+ const t = /* @__PURE__ */ new Map(), e = async (i) => {
200
+ const a = await this.readDir(i);
201
+ for (const r of a) {
202
+ const s = `${i === "/" ? "" : i}/${r.name}`;
189
203
  try {
190
204
  const n = await this.stat(s);
191
205
  t.set(s, n), n.isDirectory && await e(s);
@@ -204,12 +218,12 @@ class T {
204
218
  }), await e("/"), t;
205
219
  }
206
220
  async readFile(t, e) {
207
- await this.mount(), e || (e = d(t) ? "binary" : "utf-8");
221
+ await this.mount(), e || (e = g(t) ? "binary" : "utf-8");
208
222
  try {
209
- const a = await this.getFileHandle(t, !1, this.root), i = await b(a, t);
210
- return e === "binary" ? i : C(i, e);
211
- } catch (a) {
212
- throw new f(t, a);
223
+ const i = await this.getFileHandle(t, !1, this.root), a = await A(i, t);
224
+ return e === "binary" ? a : $(a, e);
225
+ } catch (i) {
226
+ throw new F(t, i);
213
227
  }
214
228
  }
215
229
  /**
@@ -237,10 +251,10 @@ class T {
237
251
  * await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
238
252
  * ```
239
253
  */
240
- async writeFile(t, e, a) {
254
+ async writeFile(t, e, i) {
241
255
  await this.mount();
242
- const i = await this.exists(t), r = await this.getFileHandle(t, !0);
243
- a || (a = typeof e != "string" || d(t) ? "binary" : "utf-8"), await g(r, e, a, t), i ? await this.notifyChange({ path: t, type: "changed", isDirectory: !1 }) : await this.notifyChange({ path: t, type: "added", isDirectory: !1 });
256
+ const a = await this.exists(t), r = await this.getFileHandle(t, !0);
257
+ i || (i = typeof e != "string" || g(t) ? "binary" : "utf-8"), await E(r, e, i, t), a ? await this.notifyChange({ path: t, type: "changed", isDirectory: !1 }) : await this.notifyChange({ path: t, type: "added", isDirectory: !1 });
244
258
  }
245
259
  /**
246
260
  * Append data to a file
@@ -264,10 +278,10 @@ class T {
264
278
  * await fs.appendFile('/data/binary.dat', additionalData);
265
279
  * ```
266
280
  */
267
- async appendFile(t, e, a) {
281
+ async appendFile(t, e, i) {
268
282
  await this.mount();
269
- const i = await this.getFileHandle(t, !0);
270
- a || (a = typeof e != "string" || d(t) ? "binary" : "utf-8"), await g(i, e, a, t, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
283
+ const a = await this.getFileHandle(t, !0);
284
+ i || (i = typeof e != "string" || g(t) ? "binary" : "utf-8"), await E(a, e, i, t, { append: !0 }), await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
271
285
  }
272
286
  /**
273
287
  * Create a directory
@@ -292,15 +306,15 @@ class T {
292
306
  */
293
307
  async mkdir(t, e) {
294
308
  await this.mount();
295
- const a = e?.recursive ?? !1, i = w(t);
309
+ const i = e?.recursive ?? !1, a = m(t);
296
310
  let r = this.root;
297
- for (let s = 0; s < i.length; s++) {
298
- const n = i[s];
311
+ for (let s = 0; s < a.length; s++) {
312
+ const n = a[s];
299
313
  try {
300
- r = await r.getDirectoryHandle(n, { create: a || s === i.length - 1 });
314
+ r = await r.getDirectoryHandle(n, { create: i || s === a.length - 1 });
301
315
  } catch (c) {
302
316
  throw c.name === "NotFoundError" ? new o(
303
- `Parent directory does not exist: ${x(i.slice(0, s + 1))}`,
317
+ `Parent directory does not exist: ${O(a.slice(0, s + 1))}`,
304
318
  "ENOENT",
305
319
  void 0,
306
320
  c
@@ -341,9 +355,9 @@ class T {
341
355
  isFile: !1,
342
356
  isDirectory: !0
343
357
  };
344
- const e = F(t), a = await this.getDirectoryHandle(m(t), !1), i = this.options.hashAlgorithm !== null;
358
+ const e = v(t), i = await this.getDirectoryHandle(p(t), !1), a = this.options.hashAlgorithm !== null;
345
359
  try {
346
- const s = await (await a.getFileHandle(e, { create: !1 })).getFile(), n = {
360
+ const s = await (await i.getFileHandle(e, { create: !1 })).getFile(), n = {
347
361
  kind: "file",
348
362
  size: s.size,
349
363
  mtime: new Date(s.lastModified).toISOString(),
@@ -351,9 +365,9 @@ class T {
351
365
  isFile: !0,
352
366
  isDirectory: !1
353
367
  };
354
- if (i && this.options.hashAlgorithm)
368
+ if (a && this.options.hashAlgorithm)
355
369
  try {
356
- const c = await A(s, this.options.hashAlgorithm, this.options.maxFileSize);
370
+ const c = await T(s, this.options.hashAlgorithm, this.options.maxFileSize);
357
371
  n.hash = c;
358
372
  } catch (c) {
359
373
  console.warn(`Failed to calculate hash for ${t}:`, c);
@@ -364,7 +378,7 @@ class T {
364
378
  throw new o("Failed to stat (file)", "STAT_FAILED", void 0, r);
365
379
  }
366
380
  try {
367
- return await a.getDirectoryHandle(e, { create: !1 }), {
381
+ return await i.getDirectoryHandle(e, { create: !1 }), {
368
382
  kind: "directory",
369
383
  size: 0,
370
384
  mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
@@ -396,17 +410,17 @@ class T {
396
410
  */
397
411
  async readDir(t) {
398
412
  await this.mount();
399
- const e = await this.getDirectoryHandle(t, !1), a = [];
400
- for await (const [i, r] of e.entries()) {
413
+ const e = await this.getDirectoryHandle(t, !1), i = [];
414
+ for await (const [a, r] of e.entries()) {
401
415
  const s = r.kind === "file";
402
- a.push({
403
- name: i,
416
+ i.push({
417
+ name: a,
404
418
  kind: r.kind,
405
419
  isFile: s,
406
420
  isDirectory: !s
407
421
  });
408
422
  }
409
- return a;
423
+ return i;
410
424
  }
411
425
  /**
412
426
  * Check if a file or directory exists
@@ -425,23 +439,23 @@ class T {
425
439
  async exists(t) {
426
440
  if (await this.mount(), t === "/")
427
441
  return !0;
428
- const e = F(t);
429
- let a = null;
442
+ const e = v(t);
443
+ let i = null;
430
444
  try {
431
- a = await this.getDirectoryHandle(m(t), !1);
432
- } catch (i) {
433
- if (a = null, i.name !== "NotFoundError" && i.name !== "TypeMismatchError")
434
- throw i;
445
+ i = await this.getDirectoryHandle(p(t), !1);
446
+ } catch (a) {
447
+ if (i = null, a.name !== "NotFoundError" && a.name !== "TypeMismatchError")
448
+ throw a;
435
449
  }
436
- if (!a || !e)
450
+ if (!i || !e)
437
451
  return !1;
438
452
  try {
439
- return await a.getFileHandle(e, { create: !1 }), !0;
440
- } catch (i) {
441
- if (i.name !== "NotFoundError" && i.name !== "TypeMismatchError")
442
- throw i;
453
+ return await i.getFileHandle(e, { create: !1 }), !0;
454
+ } catch (a) {
455
+ if (a.name !== "NotFoundError" && a.name !== "TypeMismatchError")
456
+ throw a;
443
457
  try {
444
- return await a.getDirectoryHandle(e, { create: !1 }), !0;
458
+ return await i.getDirectoryHandle(e, { create: !1 }), !0;
445
459
  } catch (r) {
446
460
  if (r.name !== "NotFoundError" && r.name !== "TypeMismatchError")
447
461
  throw r;
@@ -472,9 +486,9 @@ class T {
472
486
  await this.mount();
473
487
  try {
474
488
  const e = await this.readDir(t);
475
- for (const a of e) {
476
- const i = `${t === "/" ? "" : t}/${a.name}`;
477
- await this.remove(i, { recursive: !0 });
489
+ for (const i of e) {
490
+ const a = `${t === "/" ? "" : t}/${i.name}`;
491
+ await this.remove(a, { recursive: !0 });
478
492
  }
479
493
  await this.notifyChange({ path: t, type: "changed", isDirectory: !0 });
480
494
  } catch (e) {
@@ -508,8 +522,8 @@ class T {
508
522
  async remove(t, e) {
509
523
  if (await this.mount(), t === "/")
510
524
  throw new o("Cannot remove root directory", "EROOT");
511
- const { recursive: a = !1, force: i = !1 } = e || {}, r = await this.getDirectoryHandle(m(t), !1);
512
- await I(r, t, { recursive: a, force: i }), await this.notifyChange({ path: t, type: "removed", isDirectory: !1 });
525
+ const { recursive: i = !1, force: a = !1 } = e || {}, r = await this.getDirectoryHandle(p(t), !1);
526
+ await N(r, t, { recursive: i, force: a }), await this.notifyChange({ path: t, type: "removed", isDirectory: !1 });
513
527
  }
514
528
  /**
515
529
  * Resolve a path to an absolute path
@@ -532,9 +546,9 @@ class T {
532
546
  async realpath(t) {
533
547
  await this.mount();
534
548
  try {
535
- const e = H(t);
549
+ const e = S(t);
536
550
  if (!await this.exists(e))
537
- throw new f(e);
551
+ throw new F(e);
538
552
  return e;
539
553
  } catch (e) {
540
554
  throw e instanceof o ? e : new o(`Failed to resolve path: ${t}`, "REALPATH_FAILED", void 0, e);
@@ -562,17 +576,17 @@ class T {
562
576
  * await fs.rename('/old/path/file.txt', '/new/path/renamed.txt', { overwrite: true });
563
577
  * ```
564
578
  */
565
- async rename(t, e, a) {
579
+ async rename(t, e, i) {
566
580
  await this.mount();
567
581
  try {
568
- const i = a?.overwrite ?? !1;
582
+ const a = i?.overwrite ?? !1;
569
583
  if (!await this.exists(t))
570
- throw new f(t);
571
- if (await this.exists(e) && !i)
584
+ throw new F(t);
585
+ if (await this.exists(e) && !a)
572
586
  throw new o(`Destination already exists: ${e}`, "EEXIST", void 0);
573
- await this.copy(t, e, { recursive: !0, overwrite: i }), await this.remove(t, { recursive: !0 }), await this.notifyChange({ path: t, type: "removed", isDirectory: !1 }), await this.notifyChange({ path: e, type: "added", isDirectory: !1 });
574
- } catch (i) {
575
- throw i instanceof o ? i : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED", void 0, i);
587
+ await this.copy(t, e, { recursive: !0, overwrite: a }), await this.remove(t, { recursive: !0 }), await this.notifyChange({ path: t, type: "removed", isDirectory: !1 }), await this.notifyChange({ path: e, type: "added", isDirectory: !1 });
588
+ } catch (a) {
589
+ throw a instanceof o ? a : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED", void 0, a);
576
590
  }
577
591
  }
578
592
  /**
@@ -600,10 +614,10 @@ class T {
600
614
  * await fs.copy('/source', '/dest', { recursive: true, overwrite: false });
601
615
  * ```
602
616
  */
603
- async copy(t, e, a) {
617
+ async copy(t, e, i) {
604
618
  await this.mount();
605
619
  try {
606
- const i = a?.recursive ?? !1, r = a?.overwrite ?? !0;
620
+ const a = i?.recursive ?? !1, r = i?.overwrite ?? !0;
607
621
  if (!await this.exists(t))
608
622
  throw new o(`Source does not exist: ${t}`, "ENOENT", void 0);
609
623
  if (await this.exists(e) && !r)
@@ -612,17 +626,17 @@ class T {
612
626
  const l = await this.readFile(t, "binary");
613
627
  await this.writeFile(e, l);
614
628
  } else {
615
- if (!i)
629
+ if (!a)
616
630
  throw new o(`Cannot copy directory without recursive option: ${t}`, "EISDIR", void 0);
617
631
  await this.mkdir(e, { recursive: !0 });
618
632
  const l = await this.readDir(t);
619
- for (const y of l) {
620
- const D = `${t}/${y.name}`, E = `${e}/${y.name}`;
621
- await this.copy(D, E, { recursive: !0, overwrite: r });
633
+ for (const h of l) {
634
+ const f = `${t}/${h.name}`, w = `${e}/${h.name}`;
635
+ await this.copy(f, w, { recursive: !0, overwrite: r });
622
636
  }
623
637
  }
624
- } catch (i) {
625
- throw i instanceof o ? i : new o(`Failed to copy from ${t} to ${e}`, "CP_FAILED", void 0, i);
638
+ } catch (a) {
639
+ throw a instanceof o ? a : new o(`Failed to copy from ${t} to ${e}`, "CP_FAILED", void 0, a);
626
640
  }
627
641
  }
628
642
  /**
@@ -653,12 +667,12 @@ class T {
653
667
  async watch(t, e) {
654
668
  if (!this.options.broadcastChannel)
655
669
  throw new o("This instance is not configured to send events. Please specify options.broadcastChannel to enable watching.", "ENOENT");
656
- const a = {
657
- pattern: $(t, e?.recursive ?? !0),
670
+ const i = {
671
+ pattern: P(t, e?.recursive ?? !0),
658
672
  include: Array.isArray(e?.include) ? e.include : [e?.include ?? "**"],
659
673
  exclude: Array.isArray(e?.exclude) ? e.exclude : [e?.exclude ?? ""]
660
674
  };
661
- this.watchers.set(t, a);
675
+ this.watchers.set(t, i);
662
676
  }
663
677
  /**
664
678
  * Stop watching a previously watched path
@@ -666,6 +680,222 @@ class T {
666
680
  unwatch(t) {
667
681
  this.watchers.delete(t);
668
682
  }
683
+ /**
684
+ * Open a file and return a file descriptor
685
+ *
686
+ * @param path - The path to the file to open
687
+ * @param options - Options for opening the file
688
+ * @param options.create - Whether to create the file if it doesn't exist (default: false)
689
+ * @param options.exclusive - If true and create is true, fails if file already exists (default: false)
690
+ * Note: This is best-effort in OPFS, not fully atomic due to browser limitations
691
+ * @param options.truncate - Whether to truncate the file to zero length (default: false)
692
+ * @returns Promise that resolves to a file descriptor number
693
+ * @throws {OPFSError} If opening the file fails
694
+ *
695
+ * @example
696
+ * ```typescript
697
+ * // Open existing file for reading
698
+ * const fd = await fs.open('/data/config.json');
699
+ *
700
+ * // Create new file for writing
701
+ * const fd = await fs.open('/data/new.txt', { create: true });
702
+ *
703
+ * // Create file exclusively (fails if exists)
704
+ * const fd = await fs.open('/data/unique.txt', { create: true, exclusive: true });
705
+ *
706
+ * // Open and truncate file
707
+ * const fd = await fs.open('/data/log.txt', { create: true, truncate: true });
708
+ * ```
709
+ */
710
+ async open(t, e) {
711
+ await this.mount();
712
+ const { create: i = !1, exclusive: a = !1, truncate: r = !1 } = e || {}, s = u(S(t));
713
+ try {
714
+ return i && a ? await _(s, "exclusive", async () => {
715
+ if (await this.exists(s))
716
+ throw new o(`File already exists: ${s}`, "EEXIST", s);
717
+ return this._openFile(s, i, r);
718
+ }) : await this._openFile(s, i, r);
719
+ } catch (n) {
720
+ throw n instanceof o ? n : new o(`Failed to open file: ${s}`, "OPEN_FAILED", s, n);
721
+ }
722
+ }
723
+ /**
724
+ * Internal method to open a file (without locking)
725
+ * @private
726
+ */
727
+ async _openFile(t, e, i) {
728
+ const a = await this.getFileHandle(t, e);
729
+ try {
730
+ await a.getFile();
731
+ } catch (n) {
732
+ throw n.name === "TypeMismatchError" ? new o(`Is a directory: ${t}`, "EISDIR", t) : n;
733
+ }
734
+ const r = await k(a, t);
735
+ i && (r.truncate(0), r.flush());
736
+ const s = this.nextFd++;
737
+ return this.openFiles.set(s, {
738
+ path: t,
739
+ fileHandle: a,
740
+ syncHandle: r,
741
+ position: 0
742
+ }), s;
743
+ }
744
+ /**
745
+ * Close a file descriptor
746
+ *
747
+ * @param fd - The file descriptor to close
748
+ * @returns Promise that resolves when the file descriptor is closed
749
+ * @throws {OPFSError} If the file descriptor is invalid or closing fails
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * const fd = await fs.open('/data/file.txt');
754
+ * // ... use the file descriptor ...
755
+ * await fs.close(fd);
756
+ * ```
757
+ */
758
+ async close(t) {
759
+ const e = this._getFileDescriptor(t);
760
+ x(t, e.syncHandle, e.path), this.openFiles.delete(t);
761
+ }
762
+ /**
763
+ * Read data from a file descriptor
764
+ *
765
+ * @param fd - The file descriptor to read from
766
+ * @param buffer - The buffer to read data into
767
+ * @param offset - The offset in the buffer to start writing at
768
+ * @param length - The number of bytes to read
769
+ * @param position - The position in the file to read from (null for current position)
770
+ * @returns Promise that resolves to the number of bytes read and the modified buffer
771
+ * @throws {OPFSError} If the file descriptor is invalid or reading fails
772
+ *
773
+ * @note This method uses Comlink.transfer() to efficiently pass the buffer as a Transferable Object,
774
+ * ensuring zero-copy performance across Web Worker boundaries.
775
+ *
776
+ * @example
777
+ * ```typescript
778
+ * const fd = await fs.open('/data/file.txt');
779
+ * const buffer = new Uint8Array(1024);
780
+ * const { bytesRead, buffer: modifiedBuffer } = await fs.read(fd, buffer, 0, 1024, null);
781
+ * console.log(`Read ${bytesRead} bytes`);
782
+ * // Use modifiedBuffer which contains the actual data
783
+ * await fs.close(fd);
784
+ * ```
785
+ */
786
+ async read(t, e, i, a, r) {
787
+ const s = this._getFileDescriptor(t);
788
+ I(e.length, i, a, r);
789
+ try {
790
+ const n = r ?? s.position, c = s.syncHandle.getSize(), { isEOF: l, actualLength: h } = L(n, a, c);
791
+ if (l)
792
+ return D({ bytesRead: 0, buffer: e }, [e.buffer]);
793
+ const f = e.subarray(i, i + h), w = s.syncHandle.read(f, { at: n });
794
+ return r === null && (s.position = n + w), D({ bytesRead: w, buffer: e }, [e.buffer]);
795
+ } catch (n) {
796
+ throw d("read", t, s.path, n);
797
+ }
798
+ }
799
+ /**
800
+ * Write data to a file descriptor
801
+ *
802
+ * @param fd - The file descriptor to write to
803
+ * @param buffer - The buffer containing data to write
804
+ * @param offset - The offset in the buffer to start reading from (default: 0)
805
+ * @param length - The number of bytes to write (default: entire buffer)
806
+ * @param position - The position in the file to write to (null/undefined for current position)
807
+ * @returns Promise that resolves to the number of bytes written
808
+ * @throws {OPFSError} If the file descriptor is invalid or writing fails
809
+ *
810
+ * @example
811
+ * ```typescript
812
+ * const fd = await fs.open('/data/file.txt', { create: true });
813
+ * const data = new TextEncoder().encode('Hello, World!');
814
+ * const bytesWritten = await fs.write(fd, data, 0, data.length, null);
815
+ * console.log(`Wrote ${bytesWritten} bytes`);
816
+ * await fs.close(fd);
817
+ * ```
818
+ */
819
+ async write(t, e, i = 0, a, r) {
820
+ const s = this._getFileDescriptor(t), n = a ?? e.length - i;
821
+ I(e.length, i, n, r);
822
+ try {
823
+ const c = r ?? s.position, l = e.subarray(i, i + n), h = s.syncHandle.write(l, { at: c });
824
+ return (r == null || r === s.position) && (s.position = c + h), await this.notifyChange({ path: s.path, type: "changed", isDirectory: !1 }), h;
825
+ } catch (c) {
826
+ throw d("write", t, s.path, c);
827
+ }
828
+ }
829
+ /**
830
+ * Get file status information by file descriptor
831
+ *
832
+ * @param fd - The file descriptor
833
+ * @returns Promise that resolves to FileStat object
834
+ * @throws {OPFSError} If the file descriptor is invalid
835
+ *
836
+ * @example
837
+ * ```typescript
838
+ * const fd = await fs.open('/data/file.txt');
839
+ * const stats = await fs.fstat(fd);
840
+ * console.log(`File size: ${stats.size} bytes`);
841
+ * console.log(`Last modified: ${stats.mtime}`);
842
+ *
843
+ * // If hashing is enabled, hash will be included
844
+ * if (stats.hash) {
845
+ * console.log(`Hash: ${stats.hash}`);
846
+ * }
847
+ * ```
848
+ */
849
+ async fstat(t) {
850
+ const e = this._getFileDescriptor(t);
851
+ return this.stat(e.path);
852
+ }
853
+ /**
854
+ * Truncate file to specified size
855
+ *
856
+ * @param fd - The file descriptor
857
+ * @param size - The new size of the file (default: 0)
858
+ * @returns Promise that resolves when truncation is complete
859
+ * @throws {OPFSError} If the file descriptor is invalid or truncation fails
860
+ *
861
+ * @example
862
+ * ```typescript
863
+ * const fd = await fs.open('/data/file.txt', { create: true });
864
+ * await fs.truncate(fd, 100); // Truncate to 100 bytes
865
+ * ```
866
+ */
867
+ async ftruncate(t, e = 0) {
868
+ const i = this._getFileDescriptor(t);
869
+ if (e < 0 || !Number.isInteger(e))
870
+ throw new o("Invalid size", "EINVAL");
871
+ try {
872
+ i.syncHandle.truncate(e), i.syncHandle.flush(), i.position > e && (i.position = e), await this.notifyChange({ path: i.path, type: "changed", isDirectory: !1 });
873
+ } catch (a) {
874
+ throw d("truncate", t, i.path, a);
875
+ }
876
+ }
877
+ /**
878
+ * Synchronize file data to storage (fsync equivalent)
879
+ *
880
+ * @param fd - The file descriptor
881
+ * @returns Promise that resolves when synchronization is complete
882
+ * @throws {OPFSError} If the file descriptor is invalid or sync fails
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const fd = await fs.open('/data/file.txt', { create: true });
887
+ * await fs.write(fd, data);
888
+ * await fs.fsync(fd); // Ensure data is written to storage
889
+ * ```
890
+ */
891
+ async fsync(t) {
892
+ const e = this._getFileDescriptor(t);
893
+ try {
894
+ e.syncHandle.flush();
895
+ } catch (i) {
896
+ throw d("sync", t, e.path, i);
897
+ }
898
+ }
669
899
  /**
670
900
  * Dispose of resources and clean up the file system instance
671
901
  *
@@ -674,6 +904,9 @@ class T {
674
904
  */
675
905
  dispose() {
676
906
  this.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.watchers.clear();
907
+ for (const [t, e] of this.openFiles)
908
+ x(t, e.syncHandle, e.path);
909
+ this.openFiles.clear(), this.nextFd = 1;
677
910
  }
678
911
  /**
679
912
  * Synchronize the file system with external data
@@ -707,18 +940,18 @@ class T {
707
940
  await this.mount();
708
941
  try {
709
942
  (e?.cleanBefore ?? !1) && await this.clear("/");
710
- for (const [i, r] of t) {
711
- const s = u(i);
943
+ for (const [a, r] of t) {
944
+ const s = u(a);
712
945
  let n;
713
- r instanceof Blob ? n = await O(r) : n = r, await this.writeFile(s, n);
946
+ r instanceof Blob ? n = await z(r) : n = r, await this.writeFile(s, n);
714
947
  }
715
- } catch (a) {
716
- throw a instanceof o ? a : new o("Failed to sync file system", "SYNC_FAILED", void 0, a);
948
+ } catch (i) {
949
+ throw i instanceof o ? i : new o("Failed to sync file system", "SYNC_FAILED", void 0, i);
717
950
  }
718
951
  }
719
952
  }
720
- typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && v(new T());
953
+ typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && H(new M());
721
954
  export {
722
- T as OPFSWorker
955
+ M as OPFSWorker
723
956
  };
724
957
  //# sourceMappingURL=raw.js.map