@componentor/fs 3.0.8 → 3.0.9
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 +7 -0
- package/dist/index.js +101 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -509,6 +509,13 @@ Make sure `opfsSync` is enabled (it's `true` by default). Files are mirrored to
|
|
|
509
509
|
|
|
510
510
|
## Changelog
|
|
511
511
|
|
|
512
|
+
### v3.0.9 (2026)
|
|
513
|
+
|
|
514
|
+
**Improvements:**
|
|
515
|
+
- `unpackToOPFS`, `loadFromOPFS`, and `repairVFS` now accept an optional `fs` parameter (a running `VFSFileSystem` instance) so they work from any tab — leader or follower — without stopping the VFS
|
|
516
|
+
- When `fs` is not provided, falls back to direct `.vfs.bin` access via VFSEngine (requires VFS to be stopped or a Worker context)
|
|
517
|
+
- `repairVFS` with a running instance uses OPFS as source of truth: rebuilds VFS from OPFS, then syncs back for full consistency
|
|
518
|
+
|
|
512
519
|
### v3.0.8 (2026)
|
|
513
520
|
|
|
514
521
|
**Improvements:**
|
package/dist/index.js
CHANGED
|
@@ -3283,7 +3283,6 @@ var MemoryHandle = class {
|
|
|
3283
3283
|
}
|
|
3284
3284
|
close() {
|
|
3285
3285
|
}
|
|
3286
|
-
/** Get the current data as an ArrayBuffer (trimmed to actual size) */
|
|
3287
3286
|
getBuffer() {
|
|
3288
3287
|
return this.buf.buffer.slice(0, this.len);
|
|
3289
3288
|
}
|
|
@@ -3386,8 +3385,52 @@ async function readOPFSRecursive(dir, prefix, skip) {
|
|
|
3386
3385
|
}
|
|
3387
3386
|
return result;
|
|
3388
3387
|
}
|
|
3389
|
-
|
|
3388
|
+
function readVFSRecursive(fs, vfsPath) {
|
|
3389
|
+
const result = [];
|
|
3390
|
+
let entries;
|
|
3391
|
+
try {
|
|
3392
|
+
entries = fs.readdirSync(vfsPath, { withFileTypes: true });
|
|
3393
|
+
} catch {
|
|
3394
|
+
return result;
|
|
3395
|
+
}
|
|
3396
|
+
for (const entry of entries) {
|
|
3397
|
+
const fullPath = vfsPath === "/" ? `/${entry.name}` : `${vfsPath}/${entry.name}`;
|
|
3398
|
+
if (entry.isDirectory()) {
|
|
3399
|
+
result.push({ path: fullPath, type: "directory" });
|
|
3400
|
+
result.push(...readVFSRecursive(fs, fullPath));
|
|
3401
|
+
} else {
|
|
3402
|
+
try {
|
|
3403
|
+
const data = fs.readFileSync(fullPath);
|
|
3404
|
+
result.push({ path: fullPath, type: "file", data });
|
|
3405
|
+
} catch {
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
return result;
|
|
3410
|
+
}
|
|
3411
|
+
async function unpackToOPFS(root = "/", fs) {
|
|
3390
3412
|
const rootDir = await navigateToRoot(root);
|
|
3413
|
+
if (fs) {
|
|
3414
|
+
const vfsEntries = readVFSRecursive(fs, "/");
|
|
3415
|
+
let files2 = 0;
|
|
3416
|
+
let directories2 = 0;
|
|
3417
|
+
for (const entry of vfsEntries) {
|
|
3418
|
+
if (entry.type === "directory") {
|
|
3419
|
+
const name = basename2(entry.path);
|
|
3420
|
+
const parent = await ensureParentDirs(rootDir, entry.path);
|
|
3421
|
+
await parent.getDirectoryHandle(name, { create: true });
|
|
3422
|
+
directories2++;
|
|
3423
|
+
} else {
|
|
3424
|
+
try {
|
|
3425
|
+
await writeOPFSFile(rootDir, entry.path, entry.data ?? new Uint8Array(0));
|
|
3426
|
+
files2++;
|
|
3427
|
+
} catch (err) {
|
|
3428
|
+
console.warn(`[VFS] Failed to write OPFS file ${entry.path}: ${err.message}`);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
return { files: files2, directories: directories2 };
|
|
3433
|
+
}
|
|
3391
3434
|
const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin");
|
|
3392
3435
|
const { handle } = await openVFSHandle(vfsFileHandle);
|
|
3393
3436
|
let entries;
|
|
@@ -3404,24 +3447,59 @@ async function unpackToOPFS(root = "/") {
|
|
|
3404
3447
|
for (const entry of entries) {
|
|
3405
3448
|
if (entry.path === "/") continue;
|
|
3406
3449
|
if (entry.type === INODE_TYPE.DIRECTORY) {
|
|
3407
|
-
await ensureParentDirs(rootDir, entry.path + "/dummy");
|
|
3408
3450
|
const name = basename2(entry.path);
|
|
3409
3451
|
const parent = await ensureParentDirs(rootDir, entry.path);
|
|
3410
3452
|
await parent.getDirectoryHandle(name, { create: true });
|
|
3411
3453
|
directories++;
|
|
3412
|
-
} else if (entry.type === INODE_TYPE.FILE) {
|
|
3413
|
-
await writeOPFSFile(rootDir, entry.path, entry.data ?? new Uint8Array(0));
|
|
3414
|
-
files++;
|
|
3415
|
-
} else if (entry.type === INODE_TYPE.SYMLINK) {
|
|
3454
|
+
} else if (entry.type === INODE_TYPE.FILE || entry.type === INODE_TYPE.SYMLINK) {
|
|
3416
3455
|
await writeOPFSFile(rootDir, entry.path, entry.data ?? new Uint8Array(0));
|
|
3417
3456
|
files++;
|
|
3418
3457
|
}
|
|
3419
3458
|
}
|
|
3420
3459
|
return { files, directories };
|
|
3421
3460
|
}
|
|
3422
|
-
async function loadFromOPFS(root = "/") {
|
|
3461
|
+
async function loadFromOPFS(root = "/", fs) {
|
|
3423
3462
|
const rootDir = await navigateToRoot(root);
|
|
3424
3463
|
const opfsEntries = await readOPFSRecursive(rootDir, "", /* @__PURE__ */ new Set([".vfs.bin"]));
|
|
3464
|
+
if (fs) {
|
|
3465
|
+
try {
|
|
3466
|
+
const rootEntries = fs.readdirSync("/");
|
|
3467
|
+
for (const entry of rootEntries) {
|
|
3468
|
+
try {
|
|
3469
|
+
fs.rmSync(`/${entry}`, { recursive: true, force: true });
|
|
3470
|
+
} catch {
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
} catch {
|
|
3474
|
+
}
|
|
3475
|
+
const dirs = opfsEntries.filter((e) => e.type === "directory").sort((a, b) => a.path.localeCompare(b.path));
|
|
3476
|
+
let files = 0;
|
|
3477
|
+
let directories = 0;
|
|
3478
|
+
for (const dir of dirs) {
|
|
3479
|
+
try {
|
|
3480
|
+
fs.mkdirSync(dir.path, { recursive: true, mode: 493 });
|
|
3481
|
+
directories++;
|
|
3482
|
+
} catch {
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
const fileEntries = opfsEntries.filter((e) => e.type === "file");
|
|
3486
|
+
for (const file of fileEntries) {
|
|
3487
|
+
try {
|
|
3488
|
+
const parentPath = file.path.substring(0, file.path.lastIndexOf("/")) || "/";
|
|
3489
|
+
if (parentPath !== "/") {
|
|
3490
|
+
try {
|
|
3491
|
+
fs.mkdirSync(parentPath, { recursive: true, mode: 493 });
|
|
3492
|
+
} catch {
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
fs.writeFileSync(file.path, new Uint8Array(file.data));
|
|
3496
|
+
files++;
|
|
3497
|
+
} catch (err) {
|
|
3498
|
+
console.warn(`[VFS] Failed to write ${file.path}: ${err.message}`);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return { files, directories };
|
|
3502
|
+
}
|
|
3425
3503
|
try {
|
|
3426
3504
|
await rootDir.removeEntry(".vfs.bin");
|
|
3427
3505
|
} catch (_) {
|
|
@@ -3452,7 +3530,21 @@ async function loadFromOPFS(root = "/") {
|
|
|
3452
3530
|
handle.close();
|
|
3453
3531
|
}
|
|
3454
3532
|
}
|
|
3455
|
-
async function repairVFS(root = "/") {
|
|
3533
|
+
async function repairVFS(root = "/", fs) {
|
|
3534
|
+
if (fs) {
|
|
3535
|
+
const loadResult = await loadFromOPFS(root, fs);
|
|
3536
|
+
await unpackToOPFS(root, fs);
|
|
3537
|
+
const total = loadResult.files + loadResult.directories;
|
|
3538
|
+
return {
|
|
3539
|
+
recovered: total,
|
|
3540
|
+
lost: 0,
|
|
3541
|
+
entries: []
|
|
3542
|
+
// Detailed entries not available in fs-based path
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
return repairVFSRaw(root);
|
|
3546
|
+
}
|
|
3547
|
+
async function repairVFSRaw(root) {
|
|
3456
3548
|
const rootDir = await navigateToRoot(root);
|
|
3457
3549
|
const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin");
|
|
3458
3550
|
const file = await vfsFileHandle.getFile();
|