opfs-worker 0.1.1

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.
@@ -0,0 +1,997 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("comlink"),a=`/**
2
+ * @license
3
+ * Copyright 2019 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ const C = Symbol("Comlink.proxy"), v = Symbol("Comlink.endpoint"), _ = Symbol("Comlink.releaseProxy"), D = Symbol("Comlink.finalizer"), p = Symbol("Comlink.thrown"), M = (e) => typeof e == "object" && e !== null || typeof e == "function", R = {
7
+ canHandle: (e) => M(e) && e[C],
8
+ serialize(e) {
9
+ const { port1: t, port2: r } = new MessageChannel();
10
+ return O(e, t), [r, [r]];
11
+ },
12
+ deserialize(e) {
13
+ return e.start(), V(e);
14
+ }
15
+ }, z = {
16
+ canHandle: (e) => M(e) && p in e,
17
+ serialize({ value: e }) {
18
+ let t;
19
+ return e instanceof Error ? t = {
20
+ isError: !0,
21
+ value: {
22
+ message: e.message,
23
+ name: e.name,
24
+ stack: e.stack
25
+ }
26
+ } : t = { isError: !1, value: e }, [t, []];
27
+ },
28
+ deserialize(e) {
29
+ throw e.isError ? Object.assign(new Error(e.value.message), e.value) : e.value;
30
+ }
31
+ }, k = /* @__PURE__ */ new Map([
32
+ ["proxy", R],
33
+ ["throw", z]
34
+ ]);
35
+ function B(e, t) {
36
+ for (const r of e)
37
+ if (t === r || r === "*" || r instanceof RegExp && r.test(t))
38
+ return !0;
39
+ return !1;
40
+ }
41
+ function O(e, t = globalThis, r = ["*"]) {
42
+ t.addEventListener("message", function a(n) {
43
+ if (!n || !n.data)
44
+ return;
45
+ if (!B(r, n.origin)) {
46
+ console.warn(\`Invalid origin '\${n.origin}' for comlink proxy\`);
47
+ return;
48
+ }
49
+ const { id: o, type: s, path: c } = Object.assign({ path: [] }, n.data), i = (n.data.argumentList || []).map(d);
50
+ let u;
51
+ try {
52
+ const f = c.slice(0, -1).reduce((h, g) => h[g], e), w = c.reduce((h, g) => h[g], e);
53
+ switch (s) {
54
+ case "GET":
55
+ u = w;
56
+ break;
57
+ case "SET":
58
+ f[c.slice(-1)[0]] = d(n.data.value), u = !0;
59
+ break;
60
+ case "APPLY":
61
+ u = w.apply(f, i);
62
+ break;
63
+ case "CONSTRUCT":
64
+ {
65
+ const h = new w(...i);
66
+ u = q(h);
67
+ }
68
+ break;
69
+ case "ENDPOINT":
70
+ {
71
+ const { port1: h, port2: g } = new MessageChannel();
72
+ O(e, g), u = X(h, [h]);
73
+ }
74
+ break;
75
+ case "RELEASE":
76
+ u = void 0;
77
+ break;
78
+ default:
79
+ return;
80
+ }
81
+ } catch (f) {
82
+ u = { value: f, [p]: 0 };
83
+ }
84
+ Promise.resolve(u).catch((f) => ({ value: f, [p]: 0 })).then((f) => {
85
+ const [w, h] = S(f);
86
+ t.postMessage(Object.assign(Object.assign({}, w), { id: o }), h), s === "RELEASE" && (t.removeEventListener("message", a), $(t), D in e && typeof e[D] == "function" && e[D]());
87
+ }).catch((f) => {
88
+ const [w, h] = S({
89
+ value: new TypeError("Unserializable return value"),
90
+ [p]: 0
91
+ });
92
+ t.postMessage(Object.assign(Object.assign({}, w), { id: o }), h);
93
+ });
94
+ }), t.start && t.start();
95
+ }
96
+ function j(e) {
97
+ return e.constructor.name === "MessagePort";
98
+ }
99
+ function $(e) {
100
+ j(e) && e.close();
101
+ }
102
+ function V(e, t) {
103
+ const r = /* @__PURE__ */ new Map();
104
+ return e.addEventListener("message", function(n) {
105
+ const { data: o } = n;
106
+ if (!o || !o.id)
107
+ return;
108
+ const s = r.get(o.id);
109
+ if (s)
110
+ try {
111
+ s(o);
112
+ } finally {
113
+ r.delete(o.id);
114
+ }
115
+ }), b(e, r, [], t);
116
+ }
117
+ function E(e) {
118
+ if (e)
119
+ throw new Error("Proxy has been released and is not useable");
120
+ }
121
+ function L(e) {
122
+ return m(e, /* @__PURE__ */ new Map(), {
123
+ type: "RELEASE"
124
+ }).then(() => {
125
+ $(e);
126
+ });
127
+ }
128
+ const F = /* @__PURE__ */ new WeakMap(), A = "FinalizationRegistry" in globalThis && new FinalizationRegistry((e) => {
129
+ const t = (F.get(e) || 0) - 1;
130
+ F.set(e, t), t === 0 && L(e);
131
+ });
132
+ function W(e, t) {
133
+ const r = (F.get(t) || 0) + 1;
134
+ F.set(t, r), A && A.register(e, t, e);
135
+ }
136
+ function Y(e) {
137
+ A && A.unregister(e);
138
+ }
139
+ function b(e, t, r = [], a = function() {
140
+ }) {
141
+ let n = !1;
142
+ const o = new Proxy(a, {
143
+ get(s, c) {
144
+ if (E(n), c === _)
145
+ return () => {
146
+ Y(o), L(e), t.clear(), n = !0;
147
+ };
148
+ if (c === "then") {
149
+ if (r.length === 0)
150
+ return { then: () => o };
151
+ const i = m(e, t, {
152
+ type: "GET",
153
+ path: r.map((u) => u.toString())
154
+ }).then(d);
155
+ return i.then.bind(i);
156
+ }
157
+ return b(e, t, [...r, c]);
158
+ },
159
+ set(s, c, i) {
160
+ E(n);
161
+ const [u, f] = S(i);
162
+ return m(e, t, {
163
+ type: "SET",
164
+ path: [...r, c].map((w) => w.toString()),
165
+ value: u
166
+ }, f).then(d);
167
+ },
168
+ apply(s, c, i) {
169
+ E(n);
170
+ const u = r[r.length - 1];
171
+ if (u === v)
172
+ return m(e, t, {
173
+ type: "ENDPOINT"
174
+ }).then(d);
175
+ if (u === "bind")
176
+ return b(e, t, r.slice(0, -1));
177
+ const [f, w] = I(i);
178
+ return m(e, t, {
179
+ type: "APPLY",
180
+ path: r.map((h) => h.toString()),
181
+ argumentList: f
182
+ }, w).then(d);
183
+ },
184
+ construct(s, c) {
185
+ E(n);
186
+ const [i, u] = I(c);
187
+ return m(e, t, {
188
+ type: "CONSTRUCT",
189
+ path: r.map((f) => f.toString()),
190
+ argumentList: i
191
+ }, u).then(d);
192
+ }
193
+ });
194
+ return W(o, e), o;
195
+ }
196
+ function G(e) {
197
+ return Array.prototype.concat.apply([], e);
198
+ }
199
+ function I(e) {
200
+ const t = e.map(S);
201
+ return [t.map((r) => r[0]), G(t.map((r) => r[1]))];
202
+ }
203
+ const U = /* @__PURE__ */ new WeakMap();
204
+ function X(e, t) {
205
+ return U.set(e, t), e;
206
+ }
207
+ function q(e) {
208
+ return Object.assign(e, { [C]: !0 });
209
+ }
210
+ function S(e) {
211
+ for (const [t, r] of k)
212
+ if (r.canHandle(e)) {
213
+ const [a, n] = r.serialize(e);
214
+ return [
215
+ {
216
+ type: "HANDLER",
217
+ name: t,
218
+ value: a
219
+ },
220
+ n
221
+ ];
222
+ }
223
+ return [
224
+ {
225
+ type: "RAW",
226
+ value: e
227
+ },
228
+ U.get(e) || []
229
+ ];
230
+ }
231
+ function d(e) {
232
+ switch (e.type) {
233
+ case "HANDLER":
234
+ return k.get(e.name).deserialize(e.value);
235
+ case "RAW":
236
+ return e.value;
237
+ }
238
+ }
239
+ function m(e, t, r, a) {
240
+ return new Promise((n) => {
241
+ const o = K();
242
+ t.set(o, n), e.start && e.start(), e.postMessage(Object.assign({ id: o }, r), a);
243
+ });
244
+ }
245
+ function K() {
246
+ return new Array(4).fill(0).map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)).join("-");
247
+ }
248
+ class l extends Error {
249
+ constructor(t, r, a) {
250
+ super(t), this.code = r, this.path = a, this.name = "OPFSError";
251
+ }
252
+ }
253
+ class J extends l {
254
+ constructor() {
255
+ super("OPFS is not supported in this browser", "OPFS_NOT_SUPPORTED");
256
+ }
257
+ }
258
+ class T extends l {
259
+ constructor() {
260
+ super("OPFS is not mounted", "OPFS_NOT_MOUNTED");
261
+ }
262
+ }
263
+ class N extends l {
264
+ constructor(t, r) {
265
+ super(t, "INVALID_PATH", r);
266
+ }
267
+ }
268
+ class x extends l {
269
+ constructor(t) {
270
+ super(\`File not found: \${t}\`, "FILE_NOT_FOUND", t);
271
+ }
272
+ }
273
+ function Q(e, t = "utf-8") {
274
+ switch (t) {
275
+ case "utf8":
276
+ case "utf-8":
277
+ return new TextEncoder().encode(e);
278
+ case "utf16le":
279
+ case "ucs2":
280
+ case "ucs-2":
281
+ return ee(e);
282
+ case "ascii":
283
+ return ne(e);
284
+ case "latin1":
285
+ return re(e);
286
+ case "binary":
287
+ return Uint8Array.from(e, (r) => r.charCodeAt(0));
288
+ case "base64":
289
+ return Uint8Array.from(atob(e), (r) => r.charCodeAt(0));
290
+ case "hex":
291
+ if (!/^[\\da-f]+$/i.test(e) || e.length % 2 !== 0)
292
+ throw new l("Invalid hex string", "INVALID_HEX_FORMAT");
293
+ return Uint8Array.from(e.match(/.{1,2}/g).map((r) => parseInt(r, 16)));
294
+ default:
295
+ return console.warn("Encoding not supported, falling back to UTF-8"), new TextEncoder().encode(e);
296
+ }
297
+ }
298
+ function Z(e, t = "utf-8") {
299
+ switch (t) {
300
+ case "utf8":
301
+ case "utf-8":
302
+ return new TextDecoder().decode(e);
303
+ case "utf16le":
304
+ case "ucs2":
305
+ case "ucs-2":
306
+ return te(e);
307
+ case "latin1":
308
+ return String.fromCharCode(...e);
309
+ case "binary":
310
+ return String.fromCharCode(...e);
311
+ case "ascii":
312
+ return String.fromCharCode(...e.map((r) => r & 127));
313
+ case "base64":
314
+ return btoa(String.fromCharCode(...e));
315
+ case "hex":
316
+ return Array.from(e).map((r) => r.toString(16).padStart(2, "0")).join("");
317
+ default:
318
+ return console.warn("Unsupported encoding, falling back to UTF-8"), new TextDecoder().decode(e);
319
+ }
320
+ }
321
+ function ee(e) {
322
+ const t = new Uint8Array(e.length * 2);
323
+ for (let r = 0; r < e.length; r++) {
324
+ const a = e.charCodeAt(r);
325
+ t[r * 2] = a & 255, t[r * 2 + 1] = a >> 8;
326
+ }
327
+ return t;
328
+ }
329
+ function te(e) {
330
+ e.length % 2 !== 0 && (console.warn("Invalid UTF-16LE buffer length, truncating last byte"), e = e.slice(0, e.length - 1));
331
+ const t = new Uint16Array(e.buffer, e.byteOffset, e.byteLength / 2);
332
+ return String.fromCharCode(...t);
333
+ }
334
+ function re(e) {
335
+ const t = new Uint8Array(e.length);
336
+ for (let r = 0; r < e.length; r++)
337
+ t[r] = e.charCodeAt(r) & 255;
338
+ return t;
339
+ }
340
+ function ne(e) {
341
+ const t = new Uint8Array(e.length);
342
+ for (let r = 0; r < e.length; r++)
343
+ t[r] = e.charCodeAt(r) & 127;
344
+ return t;
345
+ }
346
+ function ae() {
347
+ if (!("storage" in navigator) || !("getDirectory" in navigator.storage))
348
+ throw new J();
349
+ }
350
+ function y(e) {
351
+ return Array.isArray(e) ? e : e.split("/").filter(Boolean);
352
+ }
353
+ function P(e) {
354
+ return typeof e == "string" ? e ?? "/" : \`/\${e.join("/")}\`;
355
+ }
356
+ function oe(e, t = "utf-8") {
357
+ return typeof e == "string" ? Q(e, t) : e instanceof Uint8Array ? e : new Uint8Array(e);
358
+ }
359
+ async function se(e) {
360
+ const t = await e.createSyncAccessHandle();
361
+ try {
362
+ const r = t.getSize(), a = new Uint8Array(r);
363
+ return t.read(a, { at: 0 }), a;
364
+ } finally {
365
+ t.close();
366
+ }
367
+ }
368
+ async function H(e, t, r, a = {}) {
369
+ let n = null;
370
+ try {
371
+ n = await e.createSyncAccessHandle();
372
+ const o = oe(t, r), s = a.append ? n.getSize() : 0;
373
+ n.write(o, { at: s }), a.truncate && !a.append && n.truncate(o.byteLength), n.flush();
374
+ } catch (o) {
375
+ console.error(o);
376
+ const s = a.append ? "append" : "write";
377
+ throw new l(\`Failed to \${s} file\`, \`\${s.toUpperCase()}_FAILED\`);
378
+ } finally {
379
+ if (n)
380
+ try {
381
+ n.close();
382
+ } catch {
383
+ }
384
+ }
385
+ }
386
+ async function ie(e, t = "SHA-1") {
387
+ try {
388
+ const r = new Uint8Array(e), a = await crypto.subtle.digest(t, r);
389
+ return Array.from(new Uint8Array(a)).map((o) => o.toString(16).padStart(2, "0")).join("");
390
+ } catch (r) {
391
+ throw console.warn(\`Failed to calculate \${t} hash:\`, r), r;
392
+ }
393
+ }
394
+ class ce {
395
+ /** Root directory handle for the file system */
396
+ root = null;
397
+ /**
398
+ * Creates a new OPFSFileSystem instance
399
+ *
400
+ * @throws {OPFSError} If OPFS is not supported in the current browser
401
+ */
402
+ constructor() {
403
+ ae();
404
+ }
405
+ /**
406
+ * Initialize the file system within a given directory
407
+ *
408
+ * This method sets up the root directory for all subsequent operations.
409
+ * It must be called before any other file system operations.
410
+ *
411
+ * @param root - The root path for the file system (default: '/')
412
+ * @returns Promise that resolves to true if initialization was successful
413
+ * @throws {OPFSError} If initialization fails
414
+ *
415
+ * @example
416
+ * \`\`\`typescript
417
+ * const fs = new OPFSFileSystem();
418
+ * const success = await fs.init('/my-app');
419
+ * \`\`\`
420
+ */
421
+ async mount(t = "/") {
422
+ try {
423
+ const r = await navigator.storage.getDirectory();
424
+ return this.root = await this.getDirectoryHandle(t, !0, r), !0;
425
+ } catch (r) {
426
+ throw console.error(r), new l("Failed to initialize OPFS", "INIT_FAILED");
427
+ }
428
+ }
429
+ /**
430
+ * Get a directory handle from a path
431
+ *
432
+ * Navigates through the directory structure to find or create a directory
433
+ * at the specified path.
434
+ *
435
+ * @param path - The path to the directory (string or array of segments)
436
+ * @param create - Whether to create the directory if it doesn't exist (default: false)
437
+ * @param from - The directory to start from (default: root directory)
438
+ * @returns Promise that resolves to the directory handle
439
+ * @throws {OPFSError} If the directory cannot be accessed or created
440
+ *
441
+ * @example
442
+ * \`\`\`typescript
443
+ * const docsDir = await fs.getDirectoryHandle('/users/john/documents', true);
444
+ * const docsDir2 = await fs.getDirectoryHandle(['users', 'john', 'documents'], true);
445
+ * \`\`\`
446
+ */
447
+ async getDirectoryHandle(t, r = !1, a = this.root) {
448
+ if (!a)
449
+ throw new T();
450
+ const n = Array.isArray(t) ? t : y(t);
451
+ let o = a;
452
+ for (const s of n)
453
+ o = await o.getDirectoryHandle(s, { create: r });
454
+ return o;
455
+ }
456
+ /**
457
+ * Get a file handle from a path
458
+ *
459
+ * Navigates to the parent directory and retrieves or creates a file handle
460
+ * for the specified file path.
461
+ *
462
+ * @param path - The path to the file (string or array of segments)
463
+ * @param create - Whether to create the file if it doesn't exist (default: false)
464
+ * @param from - The directory to start from (default: root directory)
465
+ * @returns Promise that resolves to the file handle
466
+ * @throws {PathError} If the path is empty
467
+ * @throws {OPFSError} If the file cannot be accessed or created
468
+ *
469
+ * @example
470
+ * \`\`\`typescript
471
+ * const fileHandle = await fs.getFileHandle('/config/settings.json', true);
472
+ * const fileHandle2 = await fs.getFileHandle(['config', 'settings.json'], true);
473
+ * \`\`\`
474
+ */
475
+ async getFileHandle(t, r = !1, a = this.root) {
476
+ if (!a)
477
+ throw new T();
478
+ const n = y(t);
479
+ if (n.length === 0)
480
+ throw new N("Path must not be empty", Array.isArray(t) ? t.join("/") : t);
481
+ const o = n.pop();
482
+ return (await this.getDirectoryHandle(n, r, a)).getFileHandle(o, { create: r });
483
+ }
484
+ /**
485
+ * Recursively list all files and directories with their stats
486
+ *
487
+ * Traverses the entire file system starting from the root and returns
488
+ * a Map containing all paths and their corresponding file statistics.
489
+ *
490
+ * @param options - Options for indexing
491
+ * @param options.includeHash - Whether to calculate file hash (default: false)
492
+ * @param options.hashAlgorithm - Hash algorithm to use (default: 'SHA-1', fastest)
493
+ * @returns Promise that resolves to a Map of path => FileStat
494
+ * @throws {OPFSError} If the indexing operation fails
495
+ *
496
+ * @example
497
+ * \`\`\`typescript
498
+ * // Basic index without hash
499
+ * const index = await fs.index();
500
+ *
501
+ * // Index with file hash
502
+ * const indexWithHash = await fs.index({
503
+ * includeHash: true,
504
+ * hashAlgorithm: 'SHA-1'
505
+ * });
506
+ *
507
+ * // Iterate through all files and directories
508
+ * for (const [path, stat] of index) {
509
+ * console.log(\`\${path}: \${stat.isFile ? 'file' : 'directory'} (\${stat.size} bytes)\`);
510
+ * if (stat.hash) console.log(\` Hash: \${stat.hash}\`);
511
+ * }
512
+ *
513
+ * // Get specific file stats
514
+ * const fileStats = index.get('/data/config.json');
515
+ * if (fileStats) {
516
+ * console.log(\`File size: \${fileStats.size} bytes\`);
517
+ * if (fileStats.hash) console.log(\`Hash: \${fileStats.hash}\`);
518
+ * }
519
+ * \`\`\`
520
+ */
521
+ async index(t) {
522
+ const r = /* @__PURE__ */ new Map(), a = async (n) => {
523
+ const o = await this.readdir(n, { withFileTypes: !0 });
524
+ for (const s of o) {
525
+ const c = \`\${n === "/" ? "" : n}/\${s.name}\`;
526
+ try {
527
+ const i = await this.stat(c, t);
528
+ r.set(c, i), i.isDirectory && await a(c);
529
+ } catch (i) {
530
+ console.warn(\`Skipping broken entry: \${c}\`, i);
531
+ }
532
+ }
533
+ };
534
+ return r.set("/", {
535
+ kind: "directory",
536
+ size: 0,
537
+ mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
538
+ ctime: (/* @__PURE__ */ new Date(0)).toISOString(),
539
+ isFile: !1,
540
+ isDirectory: !0
541
+ }), await a("/"), r;
542
+ }
543
+ async readFile(t, r = "utf-8") {
544
+ try {
545
+ const a = await this.getFileHandle(t, !1), n = await se(a);
546
+ return r === "binary" ? n : Z(n, r);
547
+ } catch (a) {
548
+ throw console.error(a), new x(t);
549
+ }
550
+ }
551
+ /**
552
+ * Write data to a file
553
+ *
554
+ * Creates or overwrites a file with the specified data. If the file already
555
+ * exists, it will be truncated before writing.
556
+ *
557
+ * @param path - The path to the file to write
558
+ * @param data - The data to write to the file (string, Uint8Array, or ArrayBuffer)
559
+ * @param encoding - The encoding to use when writing string data (default: 'utf-8')
560
+ * @returns Promise that resolves when the write operation is complete
561
+ * @throws {OPFSError} If writing the file fails
562
+ *
563
+ * @example
564
+ * \`\`\`typescript
565
+ * // Write text data
566
+ * await fs.writeFile('/config/settings.json', JSON.stringify({ theme: 'dark' }));
567
+ *
568
+ * // Write binary data
569
+ * const binaryData = new Uint8Array([1, 2, 3, 4, 5]);
570
+ * await fs.writeFile('/data/binary.dat', binaryData);
571
+ *
572
+ * // Write with specific encoding
573
+ * await fs.writeFile('/data/utf16.txt', 'Hello World', 'utf-16le');
574
+ * \`\`\`
575
+ */
576
+ async writeFile(t, r, a) {
577
+ const n = await this.getFileHandle(t, !0);
578
+ await H(n, r, a, { truncate: !0 });
579
+ }
580
+ /**
581
+ * Append data to a file
582
+ *
583
+ * Adds data to the end of an existing file. If the file doesn't exist,
584
+ * it will be created.
585
+ *
586
+ * @param path - The path to the file to append to
587
+ * @param data - The data to append to the file (string, Uint8Array, or ArrayBuffer)
588
+ * @param encoding - The encoding to use when appending string data (default: 'utf-8')
589
+ * @returns Promise that resolves when the append operation is complete
590
+ * @throws {OPFSError} If appending to the file fails
591
+ *
592
+ * @example
593
+ * \`\`\`typescript
594
+ * // Append text to a log file
595
+ * await fs.appendFile('/logs/app.log', \`[\${new Date().toISOString()}] User logged in\\n\`);
596
+ *
597
+ * // Append binary data
598
+ * const additionalData = new Uint8Array([6, 7, 8]);
599
+ * await fs.appendFile('/data/binary.dat', additionalData);
600
+ * \`\`\`
601
+ */
602
+ async appendFile(t, r, a) {
603
+ const n = await this.getFileHandle(t, !0);
604
+ await H(n, r, a, { append: !0 });
605
+ }
606
+ /**
607
+ * Create a directory
608
+ *
609
+ * Creates a new directory at the specified path. If the recursive option
610
+ * is enabled, parent directories will be created as needed.
611
+ *
612
+ * @param path - The path where the directory should be created
613
+ * @param options - Options for directory creation
614
+ * @param options.recursive - Whether to create parent directories if they don't exist (default: false)
615
+ * @returns Promise that resolves when the directory is created
616
+ * @throws {OPFSError} If the directory cannot be created
617
+ *
618
+ * @example
619
+ * \`\`\`typescript
620
+ * // Create a single directory
621
+ * await fs.mkdir('/users/john');
622
+ *
623
+ * // Create nested directories
624
+ * await fs.mkdir('/users/john/documents/projects', { recursive: true });
625
+ * \`\`\`
626
+ */
627
+ async mkdir(t, r) {
628
+ if (!this.root)
629
+ throw new T();
630
+ const a = r?.recursive ?? !1, n = y(t);
631
+ let o = this.root;
632
+ for (let s = 0; s < n.length; s++) {
633
+ const c = n[s];
634
+ try {
635
+ o = await o.getDirectoryHandle(c, { create: a || s === n.length - 1 });
636
+ } catch (i) {
637
+ throw i.name === "NotFoundError" ? new l(
638
+ \`Parent directory does not exist: \${P(n.slice(0, s + 1))}\`,
639
+ "ENOENT"
640
+ ) : i.name === "TypeMismatchError" ? new l(\`Path segment is not a directory: \${c}\`, "ENOTDIR") : new l("Failed to create directory", "MKDIR_FAILED");
641
+ }
642
+ }
643
+ }
644
+ /**
645
+ * Get file or directory stats
646
+ *
647
+ * Retrieves metadata about a file or directory, including size, modification time,
648
+ * type information, and optionally file hashes.
649
+ *
650
+ * @param path - The path to the file or directory
651
+ * @param options - Options for stat operation
652
+ * @param options.includeHash - Whether to calculate file hash (default: false, only for files)
653
+ * @param options.hashAlgorithm - Hash algorithm to use (default: 'SHA-1', fastest)
654
+ * @returns Promise that resolves to file/directory statistics
655
+ * @throws {OPFSError} If the file or directory does not exist or cannot be accessed
656
+ *
657
+ * @example
658
+ * \`\`\`typescript
659
+ * // Basic stats
660
+ * const stats = await fs.stat('/config/settings.json');
661
+ * console.log(\`File size: \${stats.size} bytes\`);
662
+ * console.log(\`Is file: \${stats.isFile}\`);
663
+ * console.log(\`Modified: \${stats.mtime}\`);
664
+ *
665
+ * // Stats with hash (SHA-1 is fastest)
666
+ * const statsWithHash = await fs.stat('/config/settings.json', {
667
+ * includeHash: true,
668
+ * hashAlgorithm: 'SHA-1'
669
+ * });
670
+ * console.log(\`Hash: \${statsWithHash.hash}\`);
671
+ * \`\`\`
672
+ */
673
+ async stat(t, r) {
674
+ const a = y(t), n = a.pop(), o = await this.getDirectoryHandle(a, !1), s = r?.includeHash ?? !1, c = r?.hashAlgorithm ?? "SHA-1";
675
+ try {
676
+ const u = await (await o.getFileHandle(n, { create: !1 })).getFile(), f = {
677
+ kind: "file",
678
+ size: u.size,
679
+ mtime: new Date(u.lastModified).toISOString(),
680
+ ctime: new Date(u.lastModified).toISOString(),
681
+ isFile: !0,
682
+ isDirectory: !1
683
+ };
684
+ if (s)
685
+ try {
686
+ const w = new Uint8Array(await u.arrayBuffer()), h = await ie(w, c);
687
+ f.hash = h;
688
+ } catch (w) {
689
+ console.warn(\`Failed to calculate hash for \${t}:\`, w);
690
+ }
691
+ return f;
692
+ } catch (i) {
693
+ if (i.name !== "TypeMismatchError" && i.name !== "NotFoundError")
694
+ throw new l("Failed to stat (file)", "STAT_FAILED");
695
+ }
696
+ try {
697
+ return await o.getDirectoryHandle(n, { create: !1 }), {
698
+ kind: "directory",
699
+ size: 0,
700
+ mtime: (/* @__PURE__ */ new Date(0)).toISOString(),
701
+ ctime: (/* @__PURE__ */ new Date(0)).toISOString(),
702
+ isFile: !1,
703
+ isDirectory: !0
704
+ // Directories don't have hashes
705
+ };
706
+ } catch (i) {
707
+ throw i.name === "NotFoundError" ? new l(\`No such file or directory: \${t}\`, "ENOENT") : new l("Failed to stat (directory)", "STAT_FAILED");
708
+ }
709
+ }
710
+ async readdir(t, r) {
711
+ const a = r?.withFileTypes ?? !1, n = await this.getDirectoryHandle(t, !1);
712
+ if (a) {
713
+ const o = [];
714
+ for await (const [s, c] of n.entries()) {
715
+ const i = c.kind === "file";
716
+ o.push({
717
+ name: s,
718
+ kind: c.kind,
719
+ isFile: i,
720
+ isDirectory: !i
721
+ });
722
+ }
723
+ return o;
724
+ } else {
725
+ const o = [];
726
+ for await (const [s] of n.entries())
727
+ o.push(s);
728
+ return o;
729
+ }
730
+ }
731
+ /**
732
+ * Check if a file or directory exists
733
+ *
734
+ * Verifies if a file or directory exists at the specified path.
735
+ *
736
+ * @param path - The path to check
737
+ * @returns Promise that resolves to true if the file or directory exists, false otherwise
738
+ *
739
+ * @example
740
+ * \`\`\`typescript
741
+ * const exists = await fs.exists('/config/settings.json');
742
+ * console.log(\`File exists: \${exists}\`);
743
+ * \`\`\`
744
+ */
745
+ async exists(t) {
746
+ const r = y(t), a = r.pop();
747
+ let n = null;
748
+ try {
749
+ n = await this.getDirectoryHandle(r, !1);
750
+ } catch (o) {
751
+ throw (o.name === "NotFoundError" || o.name === "TypeMismatchError") && (n = null), o;
752
+ }
753
+ if (!n || !a)
754
+ return !1;
755
+ try {
756
+ return await n.getFileHandle(a, { create: !1 }), !0;
757
+ } catch (o) {
758
+ if (o.name !== "NotFoundError" && o.name !== "TypeMismatchError")
759
+ throw o;
760
+ }
761
+ try {
762
+ return await n.getDirectoryHandle(a, { create: !1 }), !0;
763
+ } catch (o) {
764
+ if (o.name !== "NotFoundError" && o.name !== "TypeMismatchError")
765
+ throw o;
766
+ }
767
+ return !1;
768
+ }
769
+ /**
770
+ * Clear all contents of a directory without removing the directory itself
771
+ *
772
+ * Removes all files and subdirectories within the specified directory,
773
+ * but keeps the directory itself.
774
+ *
775
+ * @param path - The path to the directory to clear (default: '/')
776
+ * @returns Promise that resolves when all contents are removed
777
+ * @throws {OPFSError} If the operation fails
778
+ *
779
+ * @example
780
+ * \`\`\`typescript
781
+ * // Clear root directory contents
782
+ * await fs.clear('/');
783
+ *
784
+ * // Clear specific directory contents
785
+ * await fs.clear('/data');
786
+ * \`\`\`
787
+ */
788
+ async clear(t = "/") {
789
+ try {
790
+ const r = await this.readdir(t, { withFileTypes: !0 });
791
+ for (const a of r) {
792
+ const n = \`\${t === "/" ? "" : t}/\${a.name}\`;
793
+ await this.remove(n, { recursive: !0 });
794
+ }
795
+ } catch (r) {
796
+ throw r instanceof l ? r : new l(\`Failed to clear directory: \${t}\`, "CLEAR_FAILED");
797
+ }
798
+ }
799
+ /**
800
+ * Remove files and directories
801
+ *
802
+ * Removes files and directories. Similar to Node.js fs.rm().
803
+ *
804
+ * @param path - The path to remove
805
+ * @param options - Options for removal
806
+ * @param options.recursive - Whether to remove directories and their contents recursively (default: false)
807
+ * @param options.force - Whether to ignore errors if the path doesn't exist (default: false)
808
+ * @returns Promise that resolves when the removal is complete
809
+ * @throws {OPFSError} If the removal fails
810
+ *
811
+ * @example
812
+ * \`\`\`typescript
813
+ * // Remove a file
814
+ * await fs.rm('/path/to/file.txt');
815
+ *
816
+ * // Remove a directory and all its contents
817
+ * await fs.rm('/path/to/directory', { recursive: true });
818
+ *
819
+ * // Remove with force (ignore if doesn't exist)
820
+ * await fs.rm('/maybe/exists', { force: true });
821
+ * \`\`\`
822
+ */
823
+ async remove(t, r) {
824
+ const a = r?.recursive ?? !1, n = r?.force ?? !1, o = y(t), s = o.pop();
825
+ if (!s)
826
+ throw new N("Invalid path", t);
827
+ const c = await this.getDirectoryHandle(o, !1);
828
+ try {
829
+ await c.removeEntry(s, { recursive: a });
830
+ } catch (i) {
831
+ if (i.name === "NotFoundError") {
832
+ if (!n)
833
+ throw new l(\`No such file or directory: \${t}\`, "ENOENT");
834
+ } else throw i.name === "InvalidModificationError" ? new l(\`Directory not empty: \${t}. Use recursive option to force removal.\`, "ENOTEMPTY") : i.name === "TypeMismatchError" && !a ? new l(\`Cannot remove directory without recursive option: \${t}\`, "EISDIR") : new l(\`Failed to remove path: \${t}\`, "RM_FAILED");
835
+ }
836
+ }
837
+ /**
838
+ * Resolve a path to an absolute path
839
+ *
840
+ * Resolves relative paths and normalizes path segments (like '..' and '.').
841
+ * Similar to Node.js fs.realpath() but without symlink resolution since OPFS doesn't support symlinks.
842
+ *
843
+ * @param path - The path to resolve
844
+ * @returns Promise that resolves to the absolute normalized path
845
+ * @throws {FileNotFoundError} If the path does not exist
846
+ * @throws {OPFSError} If path resolution fails
847
+ *
848
+ * @example
849
+ * \`\`\`typescript
850
+ * // Resolve relative path
851
+ * const absolute = await fs.realpath('./config/../data/file.txt');
852
+ * console.log(absolute); // '/data/file.txt'
853
+ * \`\`\`
854
+ */
855
+ async realpath(t) {
856
+ try {
857
+ const r = y(t), a = [];
858
+ for (const s of r)
859
+ if (!(s === "." || s === ""))
860
+ if (s === "..") {
861
+ if (a.length === 0)
862
+ throw new l("Path escapes root", "EINVAL");
863
+ a.length > 0 && a.pop();
864
+ } else
865
+ a.push(s);
866
+ const n = P(a);
867
+ if (!await this.exists(n))
868
+ throw new x(n);
869
+ return n;
870
+ } catch (r) {
871
+ throw r instanceof l ? r : new l(\`Failed to resolve path: \${t}\`, "REALPATH_FAILED");
872
+ }
873
+ }
874
+ /**
875
+ * Rename a file or directory
876
+ *
877
+ * Changes the name of a file or directory. If the target path already exists,
878
+ * it will be replaced.
879
+ *
880
+ * @param oldPath - The current path of the file or directory
881
+ * @param newPath - The new path for the file or directory
882
+ * @returns Promise that resolves when the rename operation is complete
883
+ * @throws {OPFSError} If the rename operation fails
884
+ *
885
+ * @example
886
+ * \`\`\`typescript
887
+ * await fs.rename('/old/path/file.txt', '/new/path/renamed.txt');
888
+ * \`\`\`
889
+ */
890
+ async rename(t, r) {
891
+ try {
892
+ if (!await this.exists(t))
893
+ throw new x(t);
894
+ await this.copy(t, r, { recursive: !0 }), await this.remove(t, { recursive: !0 });
895
+ } catch (a) {
896
+ throw a instanceof l ? a : new l(\`Failed to rename from \${t} to \${r}\`, "RENAME_FAILED");
897
+ }
898
+ }
899
+ /**
900
+ * Copy files and directories
901
+ *
902
+ * Copies files and directories. Similar to Node.js fs.cp().
903
+ *
904
+ * @param source - The source path to copy from
905
+ * @param destination - The destination path to copy to
906
+ * @param options - Options for copying
907
+ * @param options.recursive - Whether to copy directories recursively (default: false)
908
+ * @param options.force - Whether to overwrite existing files (default: true)
909
+ * @returns Promise that resolves when the copy operation is complete
910
+ * @throws {OPFSError} If the copy operation fails
911
+ *
912
+ * @example
913
+ * \`\`\`typescript
914
+ * // Copy a file
915
+ * await fs.cp('/source/file.txt', '/dest/file.txt');
916
+ *
917
+ * // Copy a directory and all its contents
918
+ * await fs.cp('/source/dir', '/dest/dir', { recursive: true });
919
+ *
920
+ * // Copy without overwriting existing files
921
+ * await fs.cp('/source', '/dest', { recursive: true, force: false });
922
+ * \`\`\`
923
+ */
924
+ async copy(t, r, a) {
925
+ try {
926
+ const n = a?.recursive ?? !1, o = a?.force ?? !0;
927
+ if (!await this.exists(t))
928
+ throw new l(\`Source does not exist: \${t}\`, "ENOENT");
929
+ if (await this.exists(r) && !o)
930
+ throw new l(\`Destination already exists: \${r}\`, "EEXIST");
931
+ if ((await this.stat(t)).isFile) {
932
+ const u = await this.readFile(t, "binary");
933
+ await this.writeFile(r, u);
934
+ } else {
935
+ if (!n)
936
+ throw new l(\`Cannot copy directory without recursive option: \${t}\`, "EISDIR");
937
+ await this.mkdir(r, { recursive: !0 });
938
+ const u = await this.readdir(t, { withFileTypes: !0 });
939
+ for (const f of u) {
940
+ const w = \`\${t}/\${f.name}\`, h = \`\${r}/\${f.name}\`;
941
+ await this.copy(w, h, { recursive: !0, force: o });
942
+ }
943
+ }
944
+ } catch (n) {
945
+ throw n instanceof l ? n : new l(\`Failed to copy from \${t} to \${r}\`, "CP_FAILED");
946
+ }
947
+ }
948
+ /**
949
+ * Synchronize the file system with external data
950
+ *
951
+ * Syncs the file system with an array of entries containing paths and data.
952
+ * This is useful for importing data from external sources or syncing with remote data.
953
+ *
954
+ * @param entries - Array of [path, data] tuples to sync
955
+ * @param options - Options for synchronization
956
+ * @param options.cleanBefore - Whether to clear the file system before syncing (default: false)
957
+ * @returns Promise that resolves when synchronization is complete
958
+ * @throws {OPFSError} If the synchronization fails
959
+ *
960
+ * @example
961
+ * \`\`\`typescript
962
+ * // Sync with external data
963
+ * const entries: [string, string | Uint8Array | Blob][] = [
964
+ * ['/config.json', JSON.stringify({ theme: 'dark' })],
965
+ * ['/data/binary.dat', new Uint8Array([1, 2, 3, 4])],
966
+ * ['/upload.txt', new Blob(['file content'], { type: 'text/plain' })]
967
+ * ];
968
+ *
969
+ * // Sync without clearing existing files
970
+ * await fs.sync(entries);
971
+ *
972
+ * // Clean file system and then sync
973
+ * await fs.sync(entries, { cleanBefore: true });
974
+ * \`\`\`
975
+ */
976
+ async sync(t, r) {
977
+ try {
978
+ (r?.cleanBefore ?? !1) && await this.clear("/");
979
+ for (const [n, o] of t) {
980
+ const s = n.startsWith("/") ? n : \`/\${n}\`;
981
+ let c;
982
+ if (o instanceof Blob) {
983
+ const i = await o.arrayBuffer();
984
+ c = new Uint8Array(i);
985
+ } else
986
+ c = o;
987
+ await this.writeFile(s, c);
988
+ }
989
+ } catch (a) {
990
+ throw a instanceof l ? a : new l("Failed to sync file system", "SYNC_FAILED");
991
+ }
992
+ }
993
+ }
994
+ O(new ce());
995
+ //# sourceMappingURL=opfs.worker-BiWuxhcz.js.map
996
+ `,r=typeof self<"u"&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",a],{type:"text/javascript;charset=utf-8"});function i(t){let n;try{if(n=r&&(self.URL||self.webkitURL).createObjectURL(r),!n)throw"";const e=new Worker(n,{type:"module",name:t?.name});return e.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(n)}),e}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(a),{type:"module",name:t?.name})}}async function s(){return o.wrap(new i)}exports.createWorker=s;
997
+ //# sourceMappingURL=inline.cjs.map