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/README.md +38 -6
- package/dist/assets/worker-CLvhwwfc.js.map +1 -0
- package/dist/facade.d.ts +141 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/{helpers-CTCvNFs1.js → helpers-CkNHswLp.js} +412 -373
- package/dist/helpers-CkNHswLp.js.map +1 -0
- package/dist/helpers-TAynP0fb.cjs +4 -0
- package/dist/helpers-TAynP0fb.cjs.map +1 -0
- package/dist/index.cjs +641 -474
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +886 -522
- package/dist/index.js.map +1 -1
- package/dist/raw.cjs +1 -1
- package/dist/raw.cjs.map +1 -1
- package/dist/raw.d.ts +13 -0
- package/dist/raw.d.ts.map +1 -0
- package/dist/raw.js +331 -59
- package/dist/raw.js.map +1 -1
- package/dist/types.d.ts +8 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/encoder.d.ts +20 -2
- 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 +42 -40
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/worker.d.ts +178 -31
- package/dist/worker.d.ts.map +1 -1
- package/package.json +3 -3
- 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 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:
|
|
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) =>
|
|
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
|
-
|
|
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 =
|
|
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 :
|
|
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 =
|
|
173
|
+
const i = F(t);
|
|
160
174
|
if (i.length === 0)
|
|
161
|
-
throw new
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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 (
|
|
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
|
|
237
|
-
*
|
|
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
|
|
277
|
+
async writeFile(t, e) {
|
|
241
278
|
await this.mount();
|
|
242
|
-
const
|
|
243
|
-
|
|
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 (
|
|
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
|
|
312
|
+
async appendFile(t, e) {
|
|
268
313
|
await this.mount();
|
|
269
|
-
const
|
|
270
|
-
|
|
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 =
|
|
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: ${
|
|
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 =
|
|
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
|
|
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 =
|
|
482
|
+
const e = E(t);
|
|
429
483
|
let a = null;
|
|
430
484
|
try {
|
|
431
|
-
a = await this.getDirectoryHandle(
|
|
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(
|
|
512
|
-
await
|
|
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 =
|
|
589
|
+
const e = v(t);
|
|
536
590
|
if (!await this.exists(e))
|
|
537
|
-
throw new
|
|
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:
|
|
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
|
|
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
|
|
620
|
-
const
|
|
621
|
-
await this.copy(
|
|
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:
|
|
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 =
|
|
983
|
+
const s = g(i);
|
|
712
984
|
let n;
|
|
713
|
-
r instanceof Blob ? n = await
|
|
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" &&
|
|
992
|
+
typeof globalThis < "u" && globalThis.constructor.name === "DedicatedWorkerGlobalScope" && x(new _());
|
|
721
993
|
export {
|
|
722
|
-
|
|
994
|
+
_ as OPFSWorker
|
|
723
995
|
};
|
|
724
996
|
//# sourceMappingURL=raw.js.map
|