@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 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
- async function unpackToOPFS(root = "/") {
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();