@componentor/fs 3.0.21 → 3.0.23

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/dist/index.d.mts CHANGED
@@ -232,6 +232,8 @@ declare class VFSFileSystem {
232
232
  * The readyPromise will resolve once OPFS mode is ready, but init()
233
233
  * will reject with the corruption error to inform the caller. */
234
234
  private handleCorruptVFS;
235
+ /** Initialize the async-relay worker. Called after sync-relay signals ready. */
236
+ private initAsyncRelay;
235
237
  /** Start as leader — tell sync-relay to init VFS engine + OPFS handle */
236
238
  private startAsLeader;
237
239
  /** Start as follower — connect to leader via service worker port brokering */
package/dist/index.js CHANGED
@@ -1381,6 +1381,7 @@ var VFSFileSystem = class {
1381
1381
  const msg = e.data;
1382
1382
  if (msg.type === "ready") {
1383
1383
  this.isReady = true;
1384
+ this.initAsyncRelay();
1384
1385
  this.resolveReady();
1385
1386
  if (!this.isFollower) {
1386
1387
  this.initLeaderBroker();
@@ -1405,23 +1406,6 @@ var VFSFileSystem = class {
1405
1406
  }
1406
1407
  }
1407
1408
  };
1408
- if (this.hasSAB) {
1409
- this.asyncWorker.postMessage({
1410
- type: "init-leader",
1411
- asyncSab: this.asyncSab,
1412
- wakeSab: this.sab
1413
- });
1414
- } else {
1415
- const mc = new MessageChannel();
1416
- this.asyncWorker.postMessage(
1417
- { type: "init-port", port: mc.port1 },
1418
- [mc.port1]
1419
- );
1420
- this.syncWorker.postMessage(
1421
- { type: "async-port", port: mc.port2 },
1422
- [mc.port2]
1423
- );
1424
- }
1425
1409
  this.acquireLeaderLock();
1426
1410
  }
1427
1411
  /** Use Web Locks API for leader election. The tab that acquires the lock is
@@ -1516,6 +1500,26 @@ var VFSFileSystem = class {
1516
1500
  this._mode = "opfs";
1517
1501
  this.sendOPFSInit();
1518
1502
  }
1503
+ /** Initialize the async-relay worker. Called after sync-relay signals ready. */
1504
+ initAsyncRelay() {
1505
+ if (this.hasSAB) {
1506
+ this.asyncWorker.postMessage({
1507
+ type: "init-leader",
1508
+ asyncSab: this.asyncSab,
1509
+ wakeSab: this.sab
1510
+ });
1511
+ } else {
1512
+ const mc = new MessageChannel();
1513
+ this.asyncWorker.postMessage(
1514
+ { type: "init-port", port: mc.port1 },
1515
+ [mc.port1]
1516
+ );
1517
+ this.syncWorker.postMessage(
1518
+ { type: "async-port", port: mc.port2 },
1519
+ [mc.port2]
1520
+ );
1521
+ }
1522
+ }
1519
1523
  /** Start as leader — tell sync-relay to init VFS engine + OPFS handle */
1520
1524
  startAsLeader() {
1521
1525
  this.isFollower = false;
@@ -2695,6 +2699,16 @@ var VFSEngine = class {
2695
2699
  }
2696
2700
  /** Resolve symlinks in intermediate path components */
2697
2701
  resolvePathComponents(path, followLast = true, depth = 0) {
2702
+ const result = this.resolvePathFull(path, followLast, depth);
2703
+ return result?.idx;
2704
+ }
2705
+ /**
2706
+ * Resolve a path following symlinks, returning both the inode index AND the
2707
+ * fully resolved path. This is needed by readdir: when listing a symlinked
2708
+ * directory, we must search for children under the resolved target path
2709
+ * (where files actually exist in pathIndex), not under the symlink path.
2710
+ */
2711
+ resolvePathFull(path, followLast = true, depth = 0) {
2698
2712
  if (depth > MAX_SYMLINK_DEPTH) return void 0;
2699
2713
  const parts = path.split("/").filter(Boolean);
2700
2714
  let current = "/";
@@ -2708,14 +2722,16 @@ var VFSEngine = class {
2708
2722
  const target = decoder8.decode(this.readData(inode.firstBlock, inode.blockCount, inode.size));
2709
2723
  const resolved = target.startsWith("/") ? target : this.resolveRelative(current, target);
2710
2724
  if (isLast) {
2711
- return this.resolvePathComponents(resolved, true, depth + 1);
2725
+ return this.resolvePathFull(resolved, true, depth + 1);
2712
2726
  }
2713
2727
  const remaining = parts.slice(i + 1).join("/");
2714
2728
  const newPath = resolved + (remaining ? "/" + remaining : "");
2715
- return this.resolvePathComponents(newPath, followLast, depth + 1);
2729
+ return this.resolvePathFull(newPath, followLast, depth + 1);
2716
2730
  }
2717
2731
  }
2718
- return this.pathIndex.get(current);
2732
+ const finalIdx = this.pathIndex.get(current);
2733
+ if (finalIdx === void 0) return void 0;
2734
+ return { idx: finalIdx, resolvedPath: current };
2719
2735
  }
2720
2736
  resolveRelative(from, target) {
2721
2737
  const dir = from.substring(0, from.lastIndexOf("/")) || "/";
@@ -2914,10 +2930,10 @@ var VFSEngine = class {
2914
2930
  if (idx === void 0) return { status: CODE_TO_STATUS.ENOENT, data: null };
2915
2931
  return this.encodeStatResponse(idx);
2916
2932
  }
2917
- // ---- LSTAT (no symlink follow) ----
2933
+ // ---- LSTAT (no symlink follow for the FINAL component) ----
2918
2934
  lstat(path) {
2919
2935
  path = this.normalizePath(path);
2920
- const idx = this.pathIndex.get(path);
2936
+ const idx = this.resolvePathComponents(path, false);
2921
2937
  if (idx === void 0) return { status: CODE_TO_STATUS.ENOENT, data: null };
2922
2938
  return this.encodeStatResponse(idx);
2923
2939
  }
@@ -3004,12 +3020,12 @@ var VFSEngine = class {
3004
3020
  // ---- READDIR ----
3005
3021
  readdir(path, flags = 0) {
3006
3022
  path = this.normalizePath(path);
3007
- const idx = this.resolvePathComponents(path, true);
3008
- if (idx === void 0) return { status: CODE_TO_STATUS.ENOENT, data: null };
3009
- const inode = this.readInode(idx);
3023
+ const resolved = this.resolvePathFull(path, true);
3024
+ if (!resolved) return { status: CODE_TO_STATUS.ENOENT, data: null };
3025
+ const inode = this.readInode(resolved.idx);
3010
3026
  if (inode.type !== INODE_TYPE.DIRECTORY) return { status: CODE_TO_STATUS.ENOTDIR, data: null };
3011
3027
  const withFileTypes = (flags & 1) !== 0;
3012
- const children = this.getDirectChildren(path);
3028
+ const children = this.getDirectChildren(resolved.resolvedPath);
3013
3029
  if (withFileTypes) {
3014
3030
  let totalSize2 = 4;
3015
3031
  const entries = [];