opfs-worker 1.0.1 → 1.2.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 x, transfer as u } from "comlink";
2
+ import { O as o, o as m, g as A, n as g, s as F, P as C, w as d, F as D, j as b, h as E, k as p, u as H, y as $, r as v, l as T, C as O, A as S, z as I, B as N, f, x as P } from "./helpers-CkNHswLp.js";
3
+ class _ {
4
4
  /** Root directory handle for the file system */
5
5
  root;
6
6
  /** Map of watched paths and options */
@@ -14,9 +14,23 @@ class T {
14
14
  root: "/",
15
15
  namespace: "",
16
16
  maxFileSize: 50 * 1024 * 1024,
17
- hashAlgorithm: null,
17
+ hashAlgorithm: !1,
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,7 +44,7 @@ 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) => m(e, r.pattern) && r.include.some((s) => s && m(e, s)) && !r.exclude.some((s) => s && m(e, s))))
34
48
  return;
35
49
  let i;
36
50
  if (this.options.hashAlgorithm)
@@ -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
+ A(), t && this.setOptions(t);
66
80
  }
67
81
  /**
68
82
  * Initialize the file system within a given directory
@@ -109,7 +123,7 @@ class T {
109
123
  * @param options.broadcastChannel - Custom name for the broadcast channel
110
124
  */
111
125
  async setOptions(t) {
112
- 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.namespace && (this.options.namespace = t.namespace), t.root !== void 0 && (this.options.root = u(t.root), this.options.namespace || (this.options.namespace = `opfs-worker:${this.options.root}`), await this.mount());
126
+ 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.namespace && (this.options.namespace = t.namespace), t.root !== void 0 && (this.options.root = g(t.root), this.options.namespace || (this.options.namespace = `opfs-worker:${this.options.root}`), await this.mount());
113
127
  }
114
128
  /**
115
129
  * Get a directory handle from a path
@@ -130,7 +144,7 @@ class T {
130
144
  * ```
131
145
  */
132
146
  async getDirectoryHandle(t, e = !1, a = this.root) {
133
- const i = Array.isArray(t) ? t : w(t);
147
+ const i = Array.isArray(t) ? t : F(t);
134
148
  let r = a;
135
149
  for (const s of i)
136
150
  r = await r.getDirectoryHandle(s, { create: e });
@@ -156,9 +170,9 @@ class T {
156
170
  * ```
157
171
  */
158
172
  async getFileHandle(t, e = !1, a = this.root) {
159
- const i = w(t);
173
+ const i = F(t);
160
174
  if (i.length === 0)
161
- throw new S("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
175
+ throw new C("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
162
176
  const r = i.pop();
163
177
  return (await this.getDirectoryHandle(i, e, a)).getFileHandle(r, { create: e });
164
178
  }
@@ -203,71 +217,111 @@ class T {
203
217
  isDirectory: !0
204
218
  }), await e("/"), t;
205
219
  }
206
- async readFile(t, e) {
207
- await this.mount(), e || (e = d(t) ? "binary" : "utf-8");
220
+ /**
221
+ * Read a file from the file system
222
+ *
223
+ * Reads the contents of a file and returns it as binary data.
224
+ *
225
+ * @param path - The path to the file to read
226
+ * @returns Promise that resolves to the file contents as Uint8Array
227
+ * @throws {FileNotFoundError} If the file does not exist
228
+ * @throws {OPFSError} If reading the file fails
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * // Read as binary data
233
+ * const content = await fs.readFile('/config/settings.json');
234
+ *
235
+ * // Read binary file
236
+ * const binaryData = await fs.readFile('/images/logo.png');
237
+ * ```
238
+ */
239
+ async readFile(t) {
240
+ await this.mount();
208
241
  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);
242
+ return await d(t, "shared", async () => {
243
+ const e = await this.open(t);
244
+ try {
245
+ const { size: a } = await this.fstat(e), i = new Uint8Array(a);
246
+ return a > 0 && await this.read(e, i, 0, a, 0), u(i, [i.buffer]);
247
+ } finally {
248
+ await this.close(e);
249
+ }
250
+ });
251
+ } catch (e) {
252
+ throw new D(t, e);
213
253
  }
214
254
  }
215
255
  /**
216
256
  * Write data to a file
217
257
  *
218
- * Creates or overwrites a file with the specified data. If the file already
258
+ * Creates or overwrites a file with the specified binary data. If the file already
219
259
  * exists, it will be truncated before writing.
220
260
  *
221
261
  * @param path - The path to the file to write
222
- * @param data - The data to write to the file (string, Uint8Array, or ArrayBuffer)
223
- * @param encoding - The encoding to use when writing string data (default: 'utf-8')
262
+ * @param data - The binary data to write to the file (Uint8Array or ArrayBuffer)
224
263
  * @returns Promise that resolves when the write operation is complete
225
264
  * @throws {OPFSError} If writing the file fails
226
265
  *
227
266
  * @example
228
267
  * ```typescript
229
- * // Write text data
230
- * await fs.writeFile('/config/settings.json', JSON.stringify({ theme: 'dark' }));
231
- *
232
268
  * // Write binary data
233
269
  * const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
234
270
  * await fs.writeFile('/data/binary.dat', binaryData);
235
271
  *
236
- * // Write with specific encoding
237
- * await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
272
+ * // Write from ArrayBuffer
273
+ * const arrayBuffer = new ArrayBuffer(10);
274
+ * await fs.writeFile('/data/buffer.dat', arrayBuffer);
238
275
  * ```
239
276
  */
240
- async writeFile(t, e, a) {
277
+ async writeFile(t, e) {
241
278
  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 });
279
+ const a = e instanceof Uint8Array ? e : new Uint8Array(e);
280
+ await d(t, "exclusive", async () => {
281
+ const i = await this.exists(t), r = await this.open(t, { create: !0, truncate: !0 });
282
+ try {
283
+ await this.write(r, a, 0, a.length, null, !1), await this.fsync(r);
284
+ } finally {
285
+ await this.close(r);
286
+ }
287
+ await this.notifyChange({ path: t, type: i ? "changed" : "added", isDirectory: !1 });
288
+ });
244
289
  }
245
290
  /**
246
291
  * Append data to a file
247
292
  *
248
- * Adds data to the end of an existing file. If the file doesn't exist,
293
+ * Adds binary data to the end of an existing file. If the file doesn't exist,
249
294
  * it will be created.
250
295
  *
251
296
  * @param path - The path to the file to append to
252
- * @param data - The data to append to the file (string, Uint8Array, or ArrayBuffer)
253
- * @param encoding - The encoding to use when appending string data (default: 'utf-8')
297
+ * @param data - The binary data to append to the file (Uint8Array or ArrayBuffer)
254
298
  * @returns Promise that resolves when the append operation is complete
255
299
  * @throws {OPFSError} If appending to the file fails
256
300
  *
257
301
  * @example
258
302
  * ```typescript
259
- * // Append text to a log file
260
- * await fs.appendFile('/logs/app.log', `[${new Date().toISOString()}] User logged in\n`);
261
- *
262
303
  * // Append binary data
263
304
  * const additionalData = new Uint8Array([6, 7, 8]);
264
305
  * await fs.appendFile('/data/binary.dat', additionalData);
306
+ *
307
+ * // Append from ArrayBuffer
308
+ * const arrayBuffer = new ArrayBuffer(5);
309
+ * await fs.appendFile('/data/buffer.dat', arrayBuffer);
265
310
  * ```
266
311
  */
267
- async appendFile(t, e, a) {
312
+ async appendFile(t, e) {
268
313
  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 });
314
+ const a = e instanceof Uint8Array ? e : new Uint8Array(e);
315
+ await d(t, "exclusive", async () => {
316
+ const i = await this.open(t, { create: !0 });
317
+ try {
318
+ const { size: r } = await this.fstat(i);
319
+ await this.write(i, a, 0, a.length, r, !1), await this.fsync(i);
320
+ } finally {
321
+ await this.close(i);
322
+ }
323
+ await this.notifyChange({ path: t, type: "changed", isDirectory: !1 });
324
+ });
271
325
  }
272
326
  /**
273
327
  * Create a directory
@@ -292,7 +346,7 @@ class T {
292
346
  */
293
347
  async mkdir(t, e) {
294
348
  await this.mount();
295
- const a = e?.recursive ?? !1, i = w(t);
349
+ const a = e?.recursive ?? !1, i = F(t);
296
350
  let r = this.root;
297
351
  for (let s = 0; s < i.length; s++) {
298
352
  const n = i[s];
@@ -300,7 +354,7 @@ class T {
300
354
  r = await r.getDirectoryHandle(n, { create: a || s === i.length - 1 });
301
355
  } catch (c) {
302
356
  throw c.name === "NotFoundError" ? new o(
303
- `Parent directory does not exist: ${x(i.slice(0, s + 1))}`,
357
+ `Parent directory does not exist: ${b(i.slice(0, s + 1))}`,
304
358
  "ENOENT",
305
359
  void 0,
306
360
  c
@@ -341,7 +395,7 @@ class T {
341
395
  isFile: !1,
342
396
  isDirectory: !0
343
397
  };
344
- const e = F(t), a = await this.getDirectoryHandle(m(t), !1), i = this.options.hashAlgorithm !== null;
398
+ const e = E(t), a = await this.getDirectoryHandle(p(t), !1), i = this.options.hashAlgorithm !== null;
345
399
  try {
346
400
  const s = await (await a.getFileHandle(e, { create: !1 })).getFile(), n = {
347
401
  kind: "file",
@@ -353,7 +407,7 @@ class T {
353
407
  };
354
408
  if (i && this.options.hashAlgorithm)
355
409
  try {
356
- const c = await A(s, this.options.hashAlgorithm, this.options.maxFileSize);
410
+ const c = await H(s, this.options.hashAlgorithm, this.options.maxFileSize);
357
411
  n.hash = c;
358
412
  } catch (c) {
359
413
  console.warn(`Failed to calculate hash for ${t}:`, c);
@@ -425,10 +479,10 @@ class T {
425
479
  async exists(t) {
426
480
  if (await this.mount(), t === "/")
427
481
  return !0;
428
- const e = F(t);
482
+ const e = E(t);
429
483
  let a = null;
430
484
  try {
431
- a = await this.getDirectoryHandle(m(t), !1);
485
+ a = await this.getDirectoryHandle(p(t), !1);
432
486
  } catch (i) {
433
487
  if (a = null, i.name !== "NotFoundError" && i.name !== "TypeMismatchError")
434
488
  throw i;
@@ -508,8 +562,8 @@ class T {
508
562
  async remove(t, e) {
509
563
  if (await this.mount(), t === "/")
510
564
  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 });
565
+ const { recursive: a = !1, force: i = !1 } = e || {}, r = await this.getDirectoryHandle(p(t), !1), s = await this.stat(t);
566
+ await $(r, t, { recursive: a, force: i }), await this.notifyChange({ path: t, type: "removed", isDirectory: s.isDirectory });
513
567
  }
514
568
  /**
515
569
  * Resolve a path to an absolute path
@@ -532,9 +586,9 @@ class T {
532
586
  async realpath(t) {
533
587
  await this.mount();
534
588
  try {
535
- const e = H(t);
589
+ const e = v(t);
536
590
  if (!await this.exists(e))
537
- throw new f(e);
591
+ throw new D(e);
538
592
  return e;
539
593
  } catch (e) {
540
594
  throw e instanceof o ? e : new o(`Failed to resolve path: ${t}`, "REALPATH_FAILED", void 0, e);
@@ -565,12 +619,10 @@ class T {
565
619
  async rename(t, e, a) {
566
620
  await this.mount();
567
621
  try {
568
- const i = a?.overwrite ?? !1;
569
- if (!await this.exists(t))
570
- throw new f(t);
622
+ const i = a?.overwrite ?? !1, r = await this.stat(t);
571
623
  if (await this.exists(e) && !i)
572
624
  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 });
625
+ await this.copy(t, e, { recursive: !0, overwrite: i }), await this.remove(t, { recursive: !0 }), await this.notifyChange({ path: t, type: "removed", isDirectory: r.isDirectory }), await this.notifyChange({ path: e, type: "added", isDirectory: r.isDirectory });
574
626
  } catch (i) {
575
627
  throw i instanceof o ? i : new o(`Failed to rename from ${t} to ${e}`, "RENAME_FAILED", void 0, i);
576
628
  }
@@ -609,16 +661,16 @@ class T {
609
661
  if (await this.exists(e) && !r)
610
662
  throw new o(`Destination already exists: ${e}`, "EEXIST", void 0);
611
663
  if ((await this.stat(t)).isFile) {
612
- const l = await this.readFile(t, "binary");
664
+ const l = await this.readFile(t);
613
665
  await this.writeFile(e, l);
614
666
  } else {
615
667
  if (!i)
616
668
  throw new o(`Cannot copy directory without recursive option: ${t}`, "EISDIR", void 0);
617
669
  await this.mkdir(e, { recursive: !0 });
618
670
  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 });
671
+ for (const h of l) {
672
+ const w = `${t}/${h.name}`, y = `${e}/${h.name}`;
673
+ await this.copy(w, y, { recursive: !0, overwrite: r });
622
674
  }
623
675
  }
624
676
  } catch (i) {
@@ -654,7 +706,7 @@ class T {
654
706
  if (!this.options.broadcastChannel)
655
707
  throw new o("This instance is not configured to send events. Please specify options.broadcastChannel to enable watching.", "ENOENT");
656
708
  const a = {
657
- pattern: $(t, e?.recursive ?? !0),
709
+ pattern: T(t, e?.recursive ?? !0),
658
710
  include: Array.isArray(e?.include) ? e.include : [e?.include ?? "**"],
659
711
  exclude: Array.isArray(e?.exclude) ? e.exclude : [e?.exclude ?? ""]
660
712
  };
@@ -666,6 +718,223 @@ class T {
666
718
  unwatch(t) {
667
719
  this.watchers.delete(t);
668
720
  }
721
+ /**
722
+ * Open a file and return a file descriptor
723
+ *
724
+ * @param path - The path to the file to open
725
+ * @param options - Options for opening the file
726
+ * @param options.create - Whether to create the file if it doesn't exist (default: false)
727
+ * @param options.exclusive - If true and create is true, fails if file already exists (default: false)
728
+ * Note: This is best-effort in OPFS, not fully atomic due to browser limitations
729
+ * @param options.truncate - Whether to truncate the file to zero length (default: false)
730
+ * @returns Promise that resolves to a file descriptor number
731
+ * @throws {OPFSError} If opening the file fails
732
+ *
733
+ * @example
734
+ * ```typescript
735
+ * // Open existing file for reading
736
+ * const fd = await fs.open('/data/config.json');
737
+ *
738
+ * // Create new file for writing
739
+ * const fd = await fs.open('/data/new.txt', { create: true });
740
+ *
741
+ * // Create file exclusively (fails if exists)
742
+ * const fd = await fs.open('/data/unique.txt', { create: true, exclusive: true });
743
+ *
744
+ * // Open and truncate file
745
+ * const fd = await fs.open('/data/log.txt', { create: true, truncate: true });
746
+ * ```
747
+ */
748
+ async open(t, e) {
749
+ await this.mount();
750
+ const { create: a = !1, exclusive: i = !1, truncate: r = !1 } = e || {}, s = g(v(t));
751
+ try {
752
+ return a && i ? await d(s, "exclusive", async () => {
753
+ if (await this.exists(s))
754
+ throw new o(`File already exists: ${s}`, "EEXIST", s);
755
+ return this._openFile(s, a, r);
756
+ }) : await this._openFile(s, a, r);
757
+ } catch (n) {
758
+ throw n instanceof o ? n : new o(`Failed to open file: ${s}`, "OPEN_FAILED", s, n);
759
+ }
760
+ }
761
+ /**
762
+ * Internal method to open a file (without locking)
763
+ * @private
764
+ */
765
+ async _openFile(t, e, a) {
766
+ const i = await this.getFileHandle(t, e);
767
+ try {
768
+ await i.getFile();
769
+ } catch (n) {
770
+ throw n.name === "TypeMismatchError" ? new o(`Is a directory: ${t}`, "EISDIR", t) : n;
771
+ }
772
+ const r = await O(i, t);
773
+ a && (r.truncate(0), r.flush());
774
+ const s = this.nextFd++;
775
+ return this.openFiles.set(s, {
776
+ path: t,
777
+ fileHandle: i,
778
+ syncHandle: r,
779
+ position: 0
780
+ }), s;
781
+ }
782
+ /**
783
+ * Close a file descriptor
784
+ *
785
+ * @param fd - The file descriptor to close
786
+ * @returns Promise that resolves when the file descriptor is closed
787
+ * @throws {OPFSError} If the file descriptor is invalid or closing fails
788
+ *
789
+ * @example
790
+ * ```typescript
791
+ * const fd = await fs.open('/data/file.txt');
792
+ * // ... use the file descriptor ...
793
+ * await fs.close(fd);
794
+ * ```
795
+ */
796
+ async close(t) {
797
+ const e = this._getFileDescriptor(t);
798
+ S(t, e.syncHandle, e.path), this.openFiles.delete(t);
799
+ }
800
+ /**
801
+ * Read data from a file descriptor
802
+ *
803
+ * @param fd - The file descriptor to read from
804
+ * @param buffer - The buffer to read data into
805
+ * @param offset - The offset in the buffer to start writing at
806
+ * @param length - The number of bytes to read
807
+ * @param position - The position in the file to read from (null for current position)
808
+ * @returns Promise that resolves to the number of bytes read and the modified buffer
809
+ * @throws {OPFSError} If the file descriptor is invalid or reading fails
810
+ *
811
+ * @note This method uses Comlink.transfer() to efficiently pass the buffer as a Transferable Object,
812
+ * ensuring zero-copy performance across Web Worker boundaries.
813
+ *
814
+ * @example
815
+ * ```typescript
816
+ * const fd = await fs.open('/data/file.txt');
817
+ * const buffer = new Uint8Array(1024);
818
+ * const { bytesRead, buffer: modifiedBuffer } = await fs.read(fd, buffer, 0, 1024, null);
819
+ * console.log(`Read ${bytesRead} bytes`);
820
+ * // Use modifiedBuffer which contains the actual data
821
+ * await fs.close(fd);
822
+ * ```
823
+ */
824
+ async read(t, e, a, i, r) {
825
+ const s = this._getFileDescriptor(t);
826
+ I(e.length, a, i, r);
827
+ try {
828
+ const n = r ?? s.position, c = s.syncHandle.getSize(), { isEOF: l, actualLength: h } = N(n, i, c);
829
+ if (l)
830
+ return u({ bytesRead: 0, buffer: e }, [e.buffer]);
831
+ const w = e.subarray(a, a + h), y = s.syncHandle.read(w, { at: n });
832
+ return r == null && (s.position = n + y), u({ bytesRead: y, buffer: e }, [e.buffer]);
833
+ } catch (n) {
834
+ throw f("read", t, s.path, n);
835
+ }
836
+ }
837
+ /**
838
+ * Write data to a file descriptor
839
+ *
840
+ * @param fd - The file descriptor to write to
841
+ * @param buffer - The buffer containing data to write
842
+ * @param offset - The offset in the buffer to start reading from (default: 0)
843
+ * @param length - The number of bytes to write (default: entire buffer)
844
+ * @param position - The position in the file to write to (null/undefined for current position)
845
+ * @param emitEvent - Whether to emit a change event (default: true)
846
+ * @returns Promise that resolves to the number of bytes written
847
+ * @throws {OPFSError} If the file descriptor is invalid or writing fails
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * const fd = await fs.open('/data/file.txt', { create: true });
852
+ * const data = new TextEncoder().encode('Hello, World!');
853
+ * const bytesWritten = await fs.write(fd, data, 0, data.length, null);
854
+ * console.log(`Wrote ${bytesWritten} bytes`);
855
+ * await fs.close(fd);
856
+ * ```
857
+ */
858
+ async write(t, e, a = 0, i, r, s = !0) {
859
+ const n = this._getFileDescriptor(t), c = i ?? e.length - a;
860
+ I(e.length, a, c, r);
861
+ try {
862
+ const l = r ?? n.position, h = e.subarray(a, a + c), w = n.syncHandle.write(h, { at: l });
863
+ return (r == null || r === n.position) && (n.position = l + w), s && await this.notifyChange({ path: n.path, type: "changed", isDirectory: !1 }), w;
864
+ } catch (l) {
865
+ throw f("write", t, n.path, l);
866
+ }
867
+ }
868
+ /**
869
+ * Get file status information by file descriptor
870
+ *
871
+ * @param fd - The file descriptor
872
+ * @returns Promise that resolves to FileStat object
873
+ * @throws {OPFSError} If the file descriptor is invalid
874
+ *
875
+ * @example
876
+ * ```typescript
877
+ * const fd = await fs.open('/data/file.txt');
878
+ * const stats = await fs.fstat(fd);
879
+ * console.log(`File size: ${stats.size} bytes`);
880
+ * console.log(`Last modified: ${stats.mtime}`);
881
+ *
882
+ * // If hashing is enabled, hash will be included
883
+ * if (stats.hash) {
884
+ * console.log(`Hash: ${stats.hash}`);
885
+ * }
886
+ * ```
887
+ */
888
+ async fstat(t) {
889
+ const e = this._getFileDescriptor(t);
890
+ return this.stat(e.path);
891
+ }
892
+ /**
893
+ * Truncate file to specified size
894
+ *
895
+ * @param fd - The file descriptor
896
+ * @param size - The new size of the file (default: 0)
897
+ * @returns Promise that resolves when truncation is complete
898
+ * @throws {OPFSError} If the file descriptor is invalid or truncation fails
899
+ *
900
+ * @example
901
+ * ```typescript
902
+ * const fd = await fs.open('/data/file.txt', { create: true });
903
+ * await fs.truncate(fd, 100); // Truncate to 100 bytes
904
+ * ```
905
+ */
906
+ async ftruncate(t, e = 0) {
907
+ const a = this._getFileDescriptor(t);
908
+ if (e < 0 || !Number.isInteger(e))
909
+ throw new o("Invalid size", "EINVAL");
910
+ try {
911
+ a.syncHandle.truncate(e), a.syncHandle.flush(), a.position > e && (a.position = e), await this.notifyChange({ path: a.path, type: "changed", isDirectory: !1 });
912
+ } catch (i) {
913
+ throw f("truncate", t, a.path, i);
914
+ }
915
+ }
916
+ /**
917
+ * Synchronize file data to storage (fsync equivalent)
918
+ *
919
+ * @param fd - The file descriptor
920
+ * @returns Promise that resolves when synchronization is complete
921
+ * @throws {OPFSError} If the file descriptor is invalid or sync fails
922
+ *
923
+ * @example
924
+ * ```typescript
925
+ * const fd = await fs.open('/data/file.txt', { create: true });
926
+ * await fs.write(fd, data);
927
+ * await fs.fsync(fd); // Ensure data is written to storage
928
+ * ```
929
+ */
930
+ async fsync(t) {
931
+ const e = this._getFileDescriptor(t);
932
+ try {
933
+ e.syncHandle.flush();
934
+ } catch (a) {
935
+ throw f("sync", t, e.path, a);
936
+ }
937
+ }
669
938
  /**
670
939
  * Dispose of resources and clean up the file system instance
671
940
  *
@@ -674,6 +943,9 @@ class T {
674
943
  */
675
944
  dispose() {
676
945
  this.broadcastChannel && (this.broadcastChannel.close(), this.broadcastChannel = null), this.watchers.clear();
946
+ for (const [t, e] of this.openFiles)
947
+ S(t, e.syncHandle, e.path);
948
+ this.openFiles.clear(), this.nextFd = 1;
677
949
  }
678
950
  /**
679
951
  * Synchronize the file system with external data
@@ -708,17 +980,17 @@ class T {
708
980
  try {
709
981
  (e?.cleanBefore ?? !1) && await this.clear("/");
710
982
  for (const [i, r] of t) {
711
- const s = u(i);
983
+ const s = g(i);
712
984
  let n;
713
- r instanceof Blob ? n = await O(r) : n = r, await this.writeFile(s, n);
985
+ r instanceof Blob ? n = await P(r) : typeof r == "string" ? n = new TextEncoder().encode(r) : n = r, await this.writeFile(s, n);
714
986
  }
715
987
  } catch (a) {
716
988
  throw a instanceof o ? a : new o("Failed to sync file system", "SYNC_FAILED", void 0, a);
717
989
  }
718
990
  }
719
991
  }
720
- typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && v(new T());
992
+ typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && x(new _());
721
993
  export {
722
- T as OPFSWorker
994
+ _ as OPFSWorker
723
995
  };
724
996
  //# sourceMappingURL=raw.js.map