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/README.md +19 -0
- package/dist/assets/worker-CvILLJKw.js.map +1 -0
- package/dist/helpers-CKqaiMjI.cjs +4 -0
- package/dist/helpers-CKqaiMjI.cjs.map +1 -0
- package/dist/{helpers-CTCvNFs1.js → helpers-DS5dyURe.js} +575 -510
- package/dist/helpers-DS5dyURe.js.map +1 -0
- package/dist/index.cjs +1073 -781
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1108 -810
- package/dist/index.js.map +1 -1
- package/dist/raw.cjs +1 -1
- package/dist/raw.cjs.map +1 -1
- package/dist/raw.js +330 -97
- package/dist/raw.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/encoder.d.ts +19 -0
- package/dist/utils/encoder.d.ts.map +1 -1
- package/dist/utils/errors.d.ts +23 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/helpers.d.ts +40 -18
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/worker.d.ts +158 -1
- package/dist/worker.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/assets/worker-CccfxYdr.js.map +0 -1
- package/dist/helpers-CTCvNFs1.js.map +0 -1
- package/dist/helpers-Cvjm0f_r.cjs +0 -4
- package/dist/helpers-Cvjm0f_r.cjs.map +0 -1
package/dist/raw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { expose as
|
|
2
|
-
import {
|
|
3
|
-
class
|
|
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) =>
|
|
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
|
|
49
|
+
let a;
|
|
36
50
|
if (this.options.hashAlgorithm)
|
|
37
51
|
try {
|
|
38
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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,
|
|
104
|
+
return this.mountingPromise && await this.mountingPromise, this.mountingPromise = new Promise(async (e, i) => {
|
|
91
105
|
try {
|
|
92
|
-
const
|
|
93
|
-
this.root = t === "/" ?
|
|
94
|
-
} catch (
|
|
95
|
-
|
|
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,
|
|
133
|
-
const
|
|
134
|
-
let r =
|
|
135
|
-
for (const s of
|
|
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,
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
throw new
|
|
162
|
-
const r =
|
|
163
|
-
return (await this.getDirectoryHandle(
|
|
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 (
|
|
186
|
-
const
|
|
187
|
-
for (const r of
|
|
188
|
-
const s = `${
|
|
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 =
|
|
221
|
+
await this.mount(), e || (e = g(t) ? "binary" : "utf-8");
|
|
208
222
|
try {
|
|
209
|
-
const
|
|
210
|
-
return e === "binary" ?
|
|
211
|
-
} catch (
|
|
212
|
-
throw new
|
|
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,
|
|
254
|
+
async writeFile(t, e, i) {
|
|
241
255
|
await this.mount();
|
|
242
|
-
const
|
|
243
|
-
|
|
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,
|
|
281
|
+
async appendFile(t, e, i) {
|
|
268
282
|
await this.mount();
|
|
269
|
-
const
|
|
270
|
-
|
|
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
|
|
309
|
+
const i = e?.recursive ?? !1, a = m(t);
|
|
296
310
|
let r = this.root;
|
|
297
|
-
for (let s = 0; s <
|
|
298
|
-
const n =
|
|
311
|
+
for (let s = 0; s < a.length; s++) {
|
|
312
|
+
const n = a[s];
|
|
299
313
|
try {
|
|
300
|
-
r = await r.getDirectoryHandle(n, { create:
|
|
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: ${
|
|
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 =
|
|
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
|
|
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 (
|
|
368
|
+
if (a && this.options.hashAlgorithm)
|
|
355
369
|
try {
|
|
356
|
-
const c = await
|
|
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
|
|
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),
|
|
400
|
-
for await (const [
|
|
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
|
-
|
|
403
|
-
name:
|
|
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
|
|
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 =
|
|
429
|
-
let
|
|
442
|
+
const e = v(t);
|
|
443
|
+
let i = null;
|
|
430
444
|
try {
|
|
431
|
-
|
|
432
|
-
} catch (
|
|
433
|
-
if (
|
|
434
|
-
throw
|
|
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 (!
|
|
450
|
+
if (!i || !e)
|
|
437
451
|
return !1;
|
|
438
452
|
try {
|
|
439
|
-
return await
|
|
440
|
-
} catch (
|
|
441
|
-
if (
|
|
442
|
-
throw
|
|
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
|
|
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
|
|
476
|
-
const
|
|
477
|
-
await this.remove(
|
|
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:
|
|
512
|
-
await
|
|
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 =
|
|
549
|
+
const e = S(t);
|
|
536
550
|
if (!await this.exists(e))
|
|
537
|
-
throw new
|
|
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,
|
|
579
|
+
async rename(t, e, i) {
|
|
566
580
|
await this.mount();
|
|
567
581
|
try {
|
|
568
|
-
const
|
|
582
|
+
const a = i?.overwrite ?? !1;
|
|
569
583
|
if (!await this.exists(t))
|
|
570
|
-
throw new
|
|
571
|
-
if (await this.exists(e) && !
|
|
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:
|
|
574
|
-
} catch (
|
|
575
|
-
throw
|
|
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,
|
|
617
|
+
async copy(t, e, i) {
|
|
604
618
|
await this.mount();
|
|
605
619
|
try {
|
|
606
|
-
const
|
|
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 (!
|
|
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
|
|
620
|
-
const
|
|
621
|
-
await this.copy(
|
|
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 (
|
|
625
|
-
throw
|
|
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
|
|
657
|
-
pattern:
|
|
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,
|
|
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 [
|
|
711
|
-
const s = u(
|
|
943
|
+
for (const [a, r] of t) {
|
|
944
|
+
const s = u(a);
|
|
712
945
|
let n;
|
|
713
|
-
r instanceof Blob ? n = await
|
|
946
|
+
r instanceof Blob ? n = await z(r) : n = r, await this.writeFile(s, n);
|
|
714
947
|
}
|
|
715
|
-
} catch (
|
|
716
|
-
throw
|
|
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" &&
|
|
953
|
+
typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && H(new M());
|
|
721
954
|
export {
|
|
722
|
-
|
|
955
|
+
M as OPFSWorker
|
|
723
956
|
};
|
|
724
957
|
//# sourceMappingURL=raw.js.map
|