@ai-ecoverse/slicc-kernel 1.57.0 → 1.58.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.
@@ -533,10 +533,26 @@ function pathInode(path) {
533
533
  }
534
534
  return h;
535
535
  }
536
- function cachingBridge(bridge, now = () => performance.now()) {
537
- const stats = /* @__PURE__ */ new Map();
538
- const lstats = /* @__PURE__ */ new Map();
536
+ function inoKey(st) {
537
+ return "ino" in st && typeof st.ino === "number" ? st.ino % 4294967296 | 0 : 0;
538
+ }
539
+ function pathHash(path) {
540
+ let h = 2166136261;
541
+ for (let i = 0; i < path.length; i++) h = Math.imul(h ^ path.charCodeAt(i), 16777619);
542
+ return h === 0 ? 1 : h;
543
+ }
544
+ function index(map, key, path) {
545
+ const paths = map.get(key) ?? /* @__PURE__ */ new Set();
546
+ paths.add(path);
547
+ map.set(key, paths);
548
+ }
549
+ function cachingBridge(bridge, now = () => performance.now(), shared) {
550
+ const blind = shared !== void 0 && shared.length * 4 < SHARED_IDS_BYTES;
551
+ const cache = new StatCache(blind ? void 0 : shared);
552
+ const { stats, lstats } = cache;
539
553
  const cached = (map, path, get) => {
554
+ if (blind) return get();
555
+ cache.fresh();
540
556
  let hit = map.get(path);
541
557
  if (hit === void 0 || "until" in hit && hit.until <= now()) {
542
558
  try {
@@ -544,22 +560,29 @@ function cachingBridge(bridge, now = () => performance.now()) {
544
560
  } catch (e) {
545
561
  hit = { error: e, until: now() + MISS_TTL_MS };
546
562
  }
547
- map.set(path, hit);
563
+ cache.put(map, path, hit);
548
564
  }
549
565
  if ("until" in hit) throw hit.error;
550
566
  return hit;
551
567
  };
552
- const invalidate = () => {
553
- stats.clear();
554
- lstats.clear();
568
+ const invalidate = () => cache.mutated([]);
569
+ const touched = (path, ino) => cache.changed(path, ino);
570
+ const mutating = (op, ...paths) => {
571
+ try {
572
+ return op();
573
+ } finally {
574
+ cache.mutated(paths);
575
+ }
555
576
  };
556
- const mutating = (op) => {
577
+ const rewriting = (p, op, ...also) => {
578
+ const ino = cache.inoOf(p, () => bridge.stat(p));
557
579
  try {
558
580
  return op();
559
581
  } finally {
560
- invalidate();
582
+ cache.mutated([p, ...also], ino);
561
583
  }
562
584
  };
585
+ const entry = (p) => [p, p.slice(0, p.lastIndexOf("/")) || "/"];
563
586
  return {
564
587
  readFile: (p) => bridge.readFile(p),
565
588
  readdir: (p) => bridge.readdir(p),
@@ -567,9 +590,9 @@ function cachingBridge(bridge, now = () => performance.now()) {
567
590
  const list = bridge.readdirStat(p);
568
591
  const base = p === "/" ? "" : p;
569
592
  for (const [name2, st] of list) {
570
- if (!st) continue;
571
- lstats.set(`${base}/${name2}`, st);
572
- if (!st.isSymbolicLink) stats.set(`${base}/${name2}`, st);
593
+ if (!st || blind) continue;
594
+ cache.put(lstats, `${base}/${name2}`, st);
595
+ if (!st.isSymbolicLink) cache.put(stats, `${base}/${name2}`, st);
573
596
  }
574
597
  return list;
575
598
  },
@@ -584,40 +607,42 @@ function cachingBridge(bridge, now = () => performance.now()) {
584
607
  return false;
585
608
  }
586
609
  },
587
- writeFile: (p, bytes, open) => mutating(() => bridge.writeFile(p, bytes, open)),
588
- mkdir: (p) => mutating(() => bridge.mkdir(p)),
610
+ writeFile: (p, bytes, open) => rewriting(p, () => bridge.writeFile(p, bytes, open), ...entry(p).slice(1)),
611
+ mkdir: (p) => mutating(() => bridge.mkdir(p), ...entry(p)),
589
612
  rm: (p) => mutating(() => bridge.rm(p)),
590
613
  rename: (from, to) => mutating(() => bridge.rename(from, to)),
614
+ link: (from, to, follow2) => mutating(() => bridge.link(from, to, follow2)),
591
615
  unlink: (p) => mutating(() => bridge.unlink(p)),
592
616
  rmdir: (p) => mutating(() => bridge.rmdir(p)),
593
- symlink: (target, link) => mutating(() => bridge.symlink(target, link)),
594
- chmod: (p, mode) => mutating(() => bridge.chmod(p, mode)),
595
- utimes: (p, a, m) => mutating(() => bridge.utimes(p, a, m)),
596
- lutimes: (p, a, m) => mutating(() => bridge.lutimes(p, a, m)),
617
+ symlink: (target, link) => mutating(() => bridge.symlink(target, link), ...entry(link)),
618
+ chmod: (p, mode) => rewriting(p, () => bridge.chmod(p, mode)),
619
+ utimes: (p, a, m) => rewriting(p, () => bridge.utimes(p, a, m)),
620
+ lutimes: (p, a, m) => mutating(() => bridge.lutimes(p, a, m), p),
597
621
  ...bridge.hold ? { hold: bridge.hold.bind(bridge) } : {},
598
622
  ...bridge.kept ? { kept: bridge.kept.bind(bridge) } : {},
599
- ...bridge.pinned ? { pinned: (handle) => pinnedIo(bridge, handle, mutating) } : {},
600
- ...bridge.create ? { create: (p) => mutating(() => bridge.create?.(p)) } : {},
623
+ ...bridge.pinned ? { pinned: (handle) => pinnedIo(bridge, handle, rewriting) } : {},
624
+ ...bridge.create ? { create: (p) => mutating(() => bridge.create?.(p), ...entry(p)) } : {},
601
625
  ...bridge.pread && bridge.pwrite && bridge.truncate ? {
602
626
  pread: bridge.pread.bind(bridge),
603
- pwrite: (p, at, bytes, transfer) => mutating(() => bridge.pwrite?.(p, at, bytes, transfer)),
604
- truncate: (p, size) => mutating(() => bridge.truncate?.(p, size))
627
+ pwrite: (p, at, bytes, transfer) => rewriting(p, () => bridge.pwrite?.(p, at, bytes, transfer)),
628
+ truncate: (p, size) => rewriting(p, () => bridge.truncate?.(p, size))
605
629
  } : {},
606
- invalidate
630
+ invalidate,
631
+ touched
607
632
  };
608
633
  }
609
- function pinnedIo(bridge, handle, mutating) {
634
+ function pinnedIo(bridge, handle, rewriting) {
610
635
  const io = bridge.pinned(handle);
611
636
  const { pread, pwrite, truncate } = io;
612
637
  return {
613
638
  readFile: (p) => io.readFile(p),
614
- writeFile: (p, bytes, open) => mutating(() => io.writeFile(p, bytes, open)),
639
+ writeFile: (p, bytes, open) => rewriting(p, () => io.writeFile(p, bytes, open)),
615
640
  pread: (p, at, n, version) => pread(p, at, n, version),
616
- pwrite: (p, at, bytes, transfer) => mutating(() => pwrite(p, at, bytes, transfer)),
617
- truncate: (p, size) => mutating(() => truncate(p, size))
641
+ pwrite: (p, at, bytes, transfer) => rewriting(p, () => pwrite(p, at, bytes, transfer)),
642
+ truncate: (p, size) => rewriting(p, () => truncate(p, size))
618
643
  };
619
644
  }
620
- var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS;
645
+ var WasiError, FileBuffer, LocalFile, MAX_LINKS, MISS_TTL_MS, FS_GEN, RING, RING_SIZE, SLOT, SHARED_IDS_BYTES, StatCache;
621
646
  var init_wasi_files = __esm({
622
647
  "src/process/wasi/wasi-files.ts"() {
623
648
  "use strict";
@@ -807,6 +832,110 @@ var init_wasi_files = __esm({
807
832
  };
808
833
  MAX_LINKS = 40;
809
834
  MISS_TTL_MS = 20;
835
+ FS_GEN = 4;
836
+ RING = 8;
837
+ RING_SIZE = 64;
838
+ SLOT = 3;
839
+ SHARED_IDS_BYTES = (RING + RING_SIZE * SLOT) * 4;
840
+ StatCache = class {
841
+ stats = /* @__PURE__ */ new Map();
842
+ lstats = /* @__PURE__ */ new Map();
843
+ byHash = /* @__PURE__ */ new Map();
844
+ byIno = /* @__PURE__ */ new Map();
845
+ shared;
846
+ seen;
847
+ constructor(shared) {
848
+ this.shared = shared;
849
+ this.seen = shared ? Atomics.load(shared, FS_GEN) : 0;
850
+ }
851
+ put(map, path, entry) {
852
+ map.set(path, entry);
853
+ if (!this.shared) return;
854
+ index(this.byHash, pathHash(path), path);
855
+ const ino = inoKey(entry);
856
+ if (ino !== 0) index(this.byIno, ino, path);
857
+ }
858
+ fresh() {
859
+ if (!this.shared) return;
860
+ const now = Atomics.load(this.shared, FS_GEN);
861
+ if (now === this.seen) return;
862
+ this.replay(now);
863
+ this.seen = now;
864
+ }
865
+ changed(path, ino = 0) {
866
+ const key = ino % 4294967296 | 0;
867
+ if (this.shared) {
868
+ const g = this.publish(this.shared, path === void 0 ? 0 : pathHash(path), key);
869
+ this.replay(g - 1);
870
+ this.seen = g;
871
+ if (path === void 0) this.clear();
872
+ else this.drop(pathHash(path), key);
873
+ }
874
+ if (path === void 0) return;
875
+ this.stats.delete(path);
876
+ this.lstats.delete(path);
877
+ }
878
+ mutated(paths, ino = 0) {
879
+ this.clear();
880
+ if (!this.shared) return;
881
+ const key = ino % 4294967296 | 0;
882
+ for (const [i, hash] of (paths.length > 0 ? paths.map(pathHash) : [0]).entries()) {
883
+ this.seen = this.publish(this.shared, hash, i === 0 ? key : 0);
884
+ }
885
+ }
886
+ inoOf(path, look) {
887
+ if (!this.shared) return 0;
888
+ this.fresh();
889
+ const hit = this.stats.get(path);
890
+ if (hit && "ino" in hit && typeof hit.ino === "number") return hit.ino;
891
+ try {
892
+ return look().ino ?? 0;
893
+ } catch {
894
+ return 0;
895
+ }
896
+ }
897
+ publish(shared, hash, ino) {
898
+ const g = Atomics.add(shared, FS_GEN, 1) + 1;
899
+ const at = RING + g % RING_SIZE * SLOT;
900
+ Atomics.store(shared, at, 0);
901
+ Atomics.store(shared, at + 1, hash);
902
+ Atomics.store(shared, at + 2, ino);
903
+ Atomics.store(shared, at, g);
904
+ return g;
905
+ }
906
+ replay(upTo) {
907
+ const shared = this.shared;
908
+ const missed = [];
909
+ for (let g = this.seen + 1; g <= upTo && upTo - this.seen <= RING_SIZE; g++) {
910
+ const at = RING + g % RING_SIZE * SLOT;
911
+ const tag = Atomics.load(shared, at);
912
+ const hash = Atomics.load(shared, at + 1);
913
+ const ino = Atomics.load(shared, at + 2);
914
+ if (tag !== g || hash === 0 || Atomics.load(shared, at) !== g) break;
915
+ missed.push([hash, ino]);
916
+ }
917
+ if (missed.length < upTo - this.seen) this.clear();
918
+ else for (const [hash, ino] of missed) this.drop(hash, ino);
919
+ }
920
+ drop(hash, ino) {
921
+ const paths = [
922
+ ...this.byHash.get(hash) ?? [],
923
+ ...ino === 0 ? [] : this.byIno.get(ino) ?? []
924
+ ];
925
+ for (const path of paths) {
926
+ this.stats.delete(path);
927
+ this.lstats.delete(path);
928
+ }
929
+ this.byHash.delete(hash);
930
+ if (ino !== 0) this.byIno.delete(ino);
931
+ }
932
+ clear() {
933
+ this.stats.clear();
934
+ this.lstats.clear();
935
+ this.byHash.clear();
936
+ this.byIno.clear();
937
+ }
938
+ };
810
939
  }
811
940
  });
812
941
 
@@ -836,6 +965,7 @@ var init_wasi_threads = __esm({
836
965
  "src/process/wasi/wasi-threads.ts"() {
837
966
  "use strict";
838
967
  init_protocol();
968
+ init_wasi_files();
839
969
  LAST_TID = 0;
840
970
  RUNNING = 1;
841
971
  MAIN_TID = 1;
@@ -858,7 +988,7 @@ var init_wasi_threads = __esm({
858
988
  this.memory = memory;
859
989
  this.cap = cap;
860
990
  this.tid = tid;
861
- this.ids = new Int32Array(ids ?? new SharedArrayBuffer(16));
991
+ this.ids = new Int32Array(ids ?? new SharedArrayBuffer(SHARED_IDS_BYTES));
862
992
  if (!ids) Atomics.store(this.ids, LAST_TID, MAIN_TID);
863
993
  }
864
994
  spawn(arg) {
@@ -1089,6 +1219,8 @@ var init_wasi_fds = __esm({
1089
1219
  WasiFds = class {
1090
1220
  table = /* @__PURE__ */ new Map();
1091
1221
  buffers = /* @__PURE__ */ new Map();
1222
+ byId = /* @__PURE__ */ new Map();
1223
+ ids = /* @__PURE__ */ new Map();
1092
1224
  kernel;
1093
1225
  fs;
1094
1226
  constructor(kernel, fs) {
@@ -1327,11 +1459,18 @@ var init_wasi_fds = __esm({
1327
1459
  handed.add(e.file.buffer);
1328
1460
  promoted.set(e.file, fd);
1329
1461
  }
1330
- this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append: e.file.append });
1462
+ this.table.set(fd, {
1463
+ type: "kernel",
1464
+ kind: "file",
1465
+ nonblock: false,
1466
+ append: e.file.append,
1467
+ path: e.file.path,
1468
+ ino: this.inoOf(e.file.path)
1469
+ });
1331
1470
  }
1332
1471
  for (const file of promoted.keys()) {
1333
1472
  if (--file.buffer.opens <= 0) {
1334
- this.buffers.delete(file.path);
1473
+ this.forget(file.buffer);
1335
1474
  this.letGo(file.buffer);
1336
1475
  }
1337
1476
  }
@@ -1402,9 +1541,48 @@ var init_wasi_fds = __esm({
1402
1541
  const { buffer } = e.file;
1403
1542
  buffer.flush();
1404
1543
  if (--buffer.opens > 0) return;
1405
- if (this.buffers.get(buffer.path) === buffer) this.buffers.delete(buffer.path);
1544
+ this.forget(buffer);
1406
1545
  this.letGo(buffer);
1407
1546
  }
1547
+ forget(buffer) {
1548
+ for (const [p, b] of [...this.buffers]) if (b === buffer) this.buffers.delete(p);
1549
+ const id = this.ids.get(buffer);
1550
+ if (id !== void 0 && this.byId.get(id) === buffer) this.byId.delete(id);
1551
+ this.ids.delete(buffer);
1552
+ }
1553
+ know(buffer, id) {
1554
+ if (this.ids.has(buffer)) return;
1555
+ this.ids.set(buffer, id);
1556
+ if (!this.byId.has(id)) this.byId.set(id, buffer);
1557
+ }
1558
+ identity(st) {
1559
+ if (!st || st.ino === void 0 || (st.nlink ?? 1) < 2) return void 0;
1560
+ return `${st.dev ?? 0}:${st.ino}`;
1561
+ }
1562
+ sharing(path, st) {
1563
+ if (st?.ino === void 0) return void 0;
1564
+ const id = `${st.dev ?? 0}:${st.ino}`;
1565
+ if (!this.byId.has(id) && this.identity(st) !== void 0) this.learn();
1566
+ const buffer = this.byId.get(id);
1567
+ if (!buffer) return void 0;
1568
+ this.buffers.set(path, buffer);
1569
+ return buffer;
1570
+ }
1571
+ learn() {
1572
+ for (const [p, buffer] of this.buffers) {
1573
+ if (this.ids.has(buffer) || buffer.isOrphan()) continue;
1574
+ const known = this.identity(this.statOrMissing(p));
1575
+ if (known !== void 0) this.know(buffer, known);
1576
+ }
1577
+ }
1578
+ linked(from, to) {
1579
+ const buffer = this.buffers.get(from);
1580
+ if (!buffer || buffer.isOrphan()) return;
1581
+ const id = this.identity(this.statOrMissing(to));
1582
+ if (id === void 0) return;
1583
+ this.know(buffer, id);
1584
+ if (!this.buffers.has(to)) this.buffers.set(to, buffer);
1585
+ }
1408
1586
  letGo(buffer) {
1409
1587
  this.fs.hold?.(buffer.path, false, true, buffer.handle);
1410
1588
  }
@@ -1430,32 +1608,63 @@ var init_wasi_fds = __esm({
1430
1608
  for (const [p, buffer] of this.buffers) if (within(p, path)) buffer.flush();
1431
1609
  if (this.shared) this.kernel.call({ op: "fd-path-flush", path });
1432
1610
  }
1433
- unlinking(path) {
1434
- this.buffers.get(path)?.keep();
1611
+ holdsUnder(path) {
1612
+ for (const p of this.buffers.keys()) if (within(p, path)) return true;
1613
+ return false;
1614
+ }
1615
+ unlinking(path, linked) {
1616
+ const buffer = this.buffers.get(path);
1617
+ if (buffer && this.survives(buffer, linked)) buffer.flush();
1618
+ else buffer?.keep();
1435
1619
  if (this.shared) this.kernel.call({ op: "fd-path-unlinking", path });
1436
1620
  }
1437
- unlinked(path) {
1621
+ unlinked(path, linked) {
1438
1622
  if (this.shared) this.kernel.call({ op: "fd-path-unlinked", path });
1439
1623
  const buffer = this.buffers.get(path);
1440
1624
  if (!buffer) return;
1441
- buffer.orphan();
1625
+ this.detach(buffer, linked);
1442
1626
  this.buffers.delete(path);
1443
1627
  }
1444
- renamed(from, to) {
1628
+ inoOf(path) {
1629
+ try {
1630
+ return this.fs.stat(path).ino;
1631
+ } catch {
1632
+ return void 0;
1633
+ }
1634
+ }
1635
+ survives(buffer, linked) {
1636
+ return linked !== void 0 && buffer.handle !== void 0;
1637
+ }
1638
+ detach(buffer, linked) {
1639
+ if (this.survives(buffer, linked)) {
1640
+ const id = this.identity(linked);
1641
+ if (id !== void 0) this.know(buffer, id);
1642
+ return;
1643
+ }
1644
+ buffer.orphan();
1645
+ this.forget(buffer);
1646
+ }
1647
+ renamed(from, to, replacedLinked) {
1445
1648
  if (from === to) return;
1446
1649
  if (this.shared) this.kernel.call({ op: "fd-path-renamed", from, to });
1650
+ for (const e of this.table.values()) {
1651
+ if (e.type === "kernel" && e.path !== void 0 && within(e.path, from)) {
1652
+ e.path = to + e.path.slice(from.length);
1653
+ }
1654
+ }
1447
1655
  const moved = [];
1448
1656
  for (const [p, buffer] of this.buffers) {
1449
1657
  if (within(p, from)) moved.push([p, buffer]);
1450
1658
  else if (within(p, to)) {
1451
- buffer.orphan();
1659
+ this.detach(buffer, replacedLinked);
1452
1660
  this.buffers.delete(p);
1453
1661
  }
1454
1662
  }
1663
+ for (const [p] of moved) this.buffers.delete(p);
1455
1664
  for (const [p, buffer] of moved) {
1456
- this.buffers.delete(p);
1457
- buffer.path = to + p.slice(from.length);
1458
- this.buffers.set(buffer.path, buffer);
1665
+ const next2 = to + p.slice(from.length);
1666
+ if (buffer.path === p) buffer.path = next2;
1667
+ this.buffers.set(next2, buffer);
1459
1668
  }
1460
1669
  }
1461
1670
  resolve(dirfd, path) {
@@ -1498,6 +1707,7 @@ var init_wasi_fds = __esm({
1498
1707
  this.buffers.delete(path);
1499
1708
  buffer = void 0;
1500
1709
  }
1710
+ buffer ??= this.sharing(path, existing);
1501
1711
  if (buffer) {
1502
1712
  if (oflags & OFLAGS.TRUNC) buffer.truncate(0);
1503
1713
  } else {
@@ -1511,6 +1721,8 @@ var init_wasi_fds = __esm({
1511
1721
  buffer = new FileBuffer(io, path, empty, st.maxFile, ranged, st.version);
1512
1722
  buffer.handle = handle;
1513
1723
  this.buffers.set(path, buffer);
1724
+ const id = this.identity(st);
1725
+ if (id !== void 0) this.know(buffer, id);
1514
1726
  }
1515
1727
  buffer.opens++;
1516
1728
  return new LocalFile(buffer, readable2, writable2, (fdflags & FDFLAGS.APPEND) !== 0);
@@ -1522,7 +1734,10 @@ var init_wasi_fds = __esm({
1522
1734
  const made = !existing && this.createNew(path, oflags);
1523
1735
  const truncate = made || (oflags & OFLAGS.TRUNC) !== 0;
1524
1736
  const fd = this.kernel.sys.openVfs(path, flags, 0, truncate ? { truncate } : {});
1525
- this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append });
1737
+ const was = existing && this.inoOf(path);
1738
+ if (truncate) this.fs.touched?.(path, was);
1739
+ const ino = existing ? was : this.inoOf(path);
1740
+ this.table.set(fd, { type: "kernel", kind: "file", nonblock: false, append, path, ino });
1526
1741
  if (append) this.publishFlags(fd, { nonblock: false, append });
1527
1742
  this.bump();
1528
1743
  return fd;
@@ -1841,6 +2056,16 @@ var init_wasi_termios = __esm({
1841
2056
  });
1842
2057
 
1843
2058
  // src/process/wasi/wasi-host.ts
2059
+ function linkedOf(st) {
2060
+ return (st.nlink ?? 1) > 1 ? st : void 0;
2061
+ }
2062
+ function linkedElsewhere(fs, path) {
2063
+ try {
2064
+ return linkedOf(fs.lstat(path));
2065
+ } catch {
2066
+ return void 0;
2067
+ }
2068
+ }
1844
2069
  function filetypeOf(s) {
1845
2070
  if (!s) return FILETYPE.UNKNOWN;
1846
2071
  if (s.isSymbolicLink) return FILETYPE.SYMBOLIC_LINK;
@@ -1852,6 +2077,7 @@ function filestatOf(path, s) {
1852
2077
  filetype: filetypeOf(s),
1853
2078
  size: BigInt(s.size),
1854
2079
  ino: s.ino !== void 0 ? BigInt(s.ino) : pathInode(path),
2080
+ ...s.nlink !== void 0 ? { nlink: BigInt(s.nlink) } : {},
1855
2081
  mtimeNs: BigInt(Math.round(s.mtimeMs ?? 0)) * NS_PER_MS
1856
2082
  };
1857
2083
  }
@@ -1902,6 +2128,7 @@ var WasiExit, IDS, NS_PER_MS, AT_SYMLINK_NOFOLLOW, S_IFIFO, DEFAULT_UMASK, MONOT
1902
2128
  var init_wasi_host = __esm({
1903
2129
  "src/process/wasi/wasi-host.ts"() {
1904
2130
  "use strict";
2131
+ init_physical_path();
1905
2132
  init_cred();
1906
2133
  init_wasi_abi();
1907
2134
  init_wasi_fds();
@@ -2211,17 +2438,20 @@ var init_wasi_host = __esm({
2211
2438
  path_remove_directory: (dirfd, p, l) => void o.fs.rmdir(at(dirfd, p, l)),
2212
2439
  path_unlink_file: (dirfd, p, l) => {
2213
2440
  const path = at(dirfd, p, l);
2214
- if (o.fs.lstat(path).isDirectory) throw new WasiError("EISDIR");
2215
- fds.unlinking(path);
2441
+ const st = o.fs.lstat(path);
2442
+ if (st.isDirectory) throw new WasiError("EISDIR");
2443
+ const linked = linkedOf(st);
2444
+ fds.unlinking(path, linked);
2216
2445
  o.fs.unlink(path);
2217
- fds.unlinked(path);
2446
+ fds.unlinked(path, linked);
2218
2447
  },
2219
2448
  path_rename: (fd, p, l, fd2, p2, l2) => {
2220
2449
  const from = at(fd, p, l);
2221
2450
  const to = at(fd2, p2, l2);
2222
2451
  fds.flushPath(from);
2452
+ const replaced = fds.holdsUnder(to) ? linkedElsewhere(o.fs, to) : void 0;
2223
2453
  o.fs.rename(from, to);
2224
- fds.renamed(from, to);
2454
+ fds.renamed(from, to, replaced);
2225
2455
  },
2226
2456
  path_symlink: (tp, tl, dirfd, p, l) => void o.fs.symlink(mem.string(tp, tl), at(dirfd, p, l)),
2227
2457
  path_readlink: (dirfd, p, l, buf, len, used) => {
@@ -2230,7 +2460,17 @@ var init_wasi_host = __esm({
2230
2460
  mem.bytes(buf, n).set(target.subarray(0, n));
2231
2461
  mem.view().setUint32(used, n, true);
2232
2462
  },
2233
- path_link: () => E.NOTSUP
2463
+ path_link: (fd, lookup, p, l, fd2, p2, l2) => {
2464
+ const from = at(fd, p, l);
2465
+ const to = at(fd2, p2, l2);
2466
+ const follow2 = (lookup & LOOKUP_SYMLINK_FOLLOW) !== 0;
2467
+ const source = follow2 ? physicalPath(o.fs, from) : from;
2468
+ fds.flushPath(from);
2469
+ if (source !== from) fds.flushPath(source);
2470
+ o.fs.link(from, to, follow2);
2471
+ fds.linked(from, to);
2472
+ if (source !== from) fds.linked(source, to);
2473
+ }
2234
2474
  };
2235
2475
  }
2236
2476
  socket(fd) {
@@ -2316,11 +2556,29 @@ var init_wasi_host = __esm({
2316
2556
  if (!kernel.sys.pread) throw new WasiError("ESPIPE");
2317
2557
  return kernel.sys.pread(fd, Math.min(max, MAX_READ), at);
2318
2558
  },
2319
- pwrite: (body, at) => kernel.call({ op: "fd-pwrite", fd, offset: at, body }),
2559
+ pwrite: (body, at) => this.changed(fd, e, kernel.call({ op: "fd-pwrite", fd, offset: at, body })),
2320
2560
  size: () => this.kernelStat(fd).size,
2321
- truncate: (size) => void kernel.call({ op: "fd-resize", fd, size })
2561
+ truncate: (size) => this.changed(fd, e, void kernel.call({ op: "fd-resize", fd, size }))
2322
2562
  };
2323
2563
  }
2564
+ unnamed = /* @__PURE__ */ new WeakSet();
2565
+ changed(fd, e, result) {
2566
+ if (e.path === void 0 && !this.unnamed.has(e)) this.learnPath(fd, e);
2567
+ this.o.fs.touched?.(e.path, e.ino);
2568
+ return result;
2569
+ }
2570
+ learnPath(fd, e) {
2571
+ this.unnamed.add(e);
2572
+ let path;
2573
+ try {
2574
+ const st = this.kernelStat(fd);
2575
+ path = st.orphan ? void 0 : st.path;
2576
+ if (path !== void 0) e.ino = this.o.fs.stat(path).ino;
2577
+ } catch {
2578
+ return;
2579
+ }
2580
+ e.path = path;
2581
+ }
2324
2582
  kernelStat(fd) {
2325
2583
  return this.o.kernel.call({ op: "fd-vfs-stat", fd });
2326
2584
  }
@@ -2329,7 +2587,8 @@ var init_wasi_host = __esm({
2329
2587
  const e = this.fds.get(fd);
2330
2588
  if (e.type === "kernel") {
2331
2589
  try {
2332
- return this.o.kernel.sys.write(fd, data, { nonblock: e.nonblock });
2590
+ const n = this.o.kernel.sys.write(fd, data, { nonblock: e.nonblock });
2591
+ return this.fds.kind(fd, e) === "file" ? this.changed(fd, e, n) : n;
2333
2592
  } catch (err) {
2334
2593
  const broken = err.code === "EPIPE";
2335
2594
  if (broken && this.fds.kind(fd, e) !== "socket") throw new WasiExit(SIGPIPE_EXIT);
@@ -2415,7 +2674,7 @@ var init_wasi_host = __esm({
2415
2674
  v.setBigUint64(ptr, 1n, true);
2416
2675
  v.setBigUint64(ptr + 8, f.ino, true);
2417
2676
  v.setUint8(ptr + 16, f.filetype);
2418
- v.setBigUint64(ptr + 24, 1n, true);
2677
+ v.setBigUint64(ptr + 24, f.nlink ?? 1n, true);
2419
2678
  v.setBigUint64(ptr + 32, f.size, true);
2420
2679
  v.setBigUint64(ptr + 40, f.mtimeNs, true);
2421
2680
  v.setBigUint64(ptr + 48, f.mtimeNs, true);
@@ -3025,8 +3284,8 @@ var init_sync_sab_wire = __esm({
3025
3284
  });
3026
3285
 
3027
3286
  // src/process/js/async-sab.ts
3028
- async function changed(header, index2, seen, waitAsync = defaultWaitAsync) {
3029
- const waited = waitAsync(header, index2, seen);
3287
+ async function changed(header, index3, seen, waitAsync = defaultWaitAsync) {
3288
+ const waited = waitAsync(header, index3, seen);
3030
3289
  if (waited.async) await waited.value;
3031
3290
  }
3032
3291
  function createAsyncSabTransport(sab, port3, waitAsync = defaultWaitAsync) {
@@ -3180,10 +3439,12 @@ function parseSyncFsStat(json) {
3180
3439
  ...typeof s.atimeMs === "number" ? { atimeMs: s.atimeMs } : {},
3181
3440
  ...typeof s.ctimeMs === "number" ? { ctimeMs: s.ctimeMs } : {},
3182
3441
  ...typeof s.ino === "number" ? { ino: s.ino } : {},
3442
+ ...typeof s.nlink === "number" ? { nlink: s.nlink } : {},
3183
3443
  ...typeof s.dev === "number" ? { dev: s.dev } : {},
3184
3444
  ...s.readonly === true ? { readonly: true } : {},
3185
3445
  ...typeof s.maxFile === "number" ? { maxFile: s.maxFile } : {},
3186
3446
  ...s.ranged === true ? { ranged: true } : {},
3447
+ ...s.linkable === true ? { linkable: true } : {},
3187
3448
  ...typeof s.version === "string" ? { version: s.version } : {},
3188
3449
  ...typeof s.uid === "number" ? { uid: s.uid } : {},
3189
3450
  ...typeof s.gid === "number" ? { gid: s.gid } : {}
@@ -3204,6 +3465,7 @@ var init_sync_fs_wire = __esm({
3204
3465
  "mkdir",
3205
3466
  "rm",
3206
3467
  "rename",
3468
+ "link",
3207
3469
  "unlink",
3208
3470
  "rmdir",
3209
3471
  "symlink",
@@ -3920,7 +4182,7 @@ function openRequest(abs, o) {
3920
4182
  ...o.truncate ? { truncate: true } : {}
3921
4183
  };
3922
4184
  }
3923
- function index(n, what) {
4185
+ function index2(n, what) {
3924
4186
  if (!Number.isSafeInteger(n) || n < 0) throw new JsCallError("EINVAL", what);
3925
4187
  }
3926
4188
  var CHUNK2, MAX_IO, O_WRONLY3, O_RDWR3, O_APPEND3;
@@ -4220,6 +4482,9 @@ function createSyncFsSabBridge(transport, opts = {}) {
4220
4482
  rename: (from, to) => {
4221
4483
  run({ op: "rename", path: from, arg2: to }, from);
4222
4484
  },
4485
+ link: (from, to, follow2) => {
4486
+ run({ op: "link", path: from, arg2: to, ...follow2 ? { mode: 1 } : {} }, from);
4487
+ },
4223
4488
  unlink: (path) => {
4224
4489
  run({ op: "unlink", path }, path);
4225
4490
  },
@@ -4284,7 +4549,7 @@ function syncOps(kernel, shared) {
4284
4549
  return got;
4285
4550
  };
4286
4551
  const read2 = (fd, max = CHUNK2) => {
4287
- index(max, "read");
4552
+ index2(max, "read");
4288
4553
  const emulated = readDevice(info(fd), max, shared.random);
4289
4554
  if (emulated) return emulated;
4290
4555
  const r = kernel.blocking({ op: "fd-read", fd, max: Math.min(max, MAX_IO) });
@@ -4362,8 +4627,8 @@ function syncFile(kernel, fd, path, append, forget) {
4362
4627
  return kernel.json({ op: "fd-vfs-stat", fd }).size;
4363
4628
  },
4364
4629
  read(position, length) {
4365
- index(position, "read");
4366
- index(length, "read");
4630
+ index2(position, "read");
4631
+ index2(length, "read");
4367
4632
  const parts = [];
4368
4633
  let got = 0;
4369
4634
  do {
@@ -4378,7 +4643,7 @@ function syncFile(kernel, fd, path, append, forget) {
4378
4643
  return joined(parts, got);
4379
4644
  },
4380
4645
  write(position, data) {
4381
- index(position, "write");
4646
+ index2(position, "write");
4382
4647
  let at = 0;
4383
4648
  do {
4384
4649
  live();
@@ -4392,7 +4657,7 @@ function syncFile(kernel, fd, path, append, forget) {
4392
4657
  },
4393
4658
  truncate(size) {
4394
4659
  live();
4395
- index(size, "truncate");
4660
+ index2(size, "truncate");
4396
4661
  kernel.call({ op: "fd-resize", fd, size });
4397
4662
  },
4398
4663
  sync() {
@@ -4423,6 +4688,7 @@ function syncPathOps(kernel, resolve, statOf, open) {
4423
4688
  rm: (path) => void kernel.call({ op: "rm", path: resolve(path) }),
4424
4689
  unlink: (path) => void kernel.call({ op: "unlink", path: resolve(path) }),
4425
4690
  rename: (from, to) => void kernel.call({ op: "rename", path: resolve(from), arg2: resolve(to) }),
4691
+ link: (from, to) => void kernel.call({ op: "link", path: resolve(from), arg2: resolve(to) }),
4426
4692
  symlink: (target, path) => void kernel.call({ op: "symlink", path: resolve(path), arg2: target }),
4427
4693
  readlink: (path) => String(kernel.json({ op: "readlink", path: resolve(path) })),
4428
4694
  readFile(path) {
@@ -4788,7 +5054,7 @@ function createContext(o) {
4788
5054
  return got;
4789
5055
  };
4790
5056
  const read2 = async (fd, max = CHUNK2, signal) => {
4791
- index(max, "read");
5057
+ index2(max, "read");
4792
5058
  const emulated = readDevice(await info(fd), max, random);
4793
5059
  if (emulated) return emulated;
4794
5060
  return readPipe(kernel, fd, Math.min(max, MAX_IO), signal);
@@ -4935,8 +5201,8 @@ function fileHandle(kernel, fd, path, append, forget) {
4935
5201
  return s.size;
4936
5202
  },
4937
5203
  async read(position, length) {
4938
- index(position, "read");
4939
- index(length, "read");
5204
+ index2(position, "read");
5205
+ index2(length, "read");
4940
5206
  const parts = [];
4941
5207
  let got = 0;
4942
5208
  do {
@@ -4951,7 +5217,7 @@ function fileHandle(kernel, fd, path, append, forget) {
4951
5217
  return joined(parts, got);
4952
5218
  },
4953
5219
  async write(position, data) {
4954
- index(position, "write");
5220
+ index2(position, "write");
4955
5221
  let at = 0;
4956
5222
  do {
4957
5223
  live();
@@ -4965,7 +5231,7 @@ function fileHandle(kernel, fd, path, append, forget) {
4965
5231
  },
4966
5232
  async truncate(size) {
4967
5233
  live();
4968
- index(size, "truncate");
5234
+ index2(size, "truncate");
4969
5235
  await kernel.call({ op: "fd-resize", fd, size });
4970
5236
  },
4971
5237
  async sync() {
@@ -5019,6 +5285,7 @@ function pathOps(kernel, resolve, statOf, open) {
5019
5285
  rm: async (path) => void await kernel.call({ op: "rm", path: resolve(path) }),
5020
5286
  unlink: async (path) => void await kernel.call({ op: "unlink", path: resolve(path) }),
5021
5287
  rename: async (from, to) => void await kernel.call({ op: "rename", path: resolve(from), arg2: resolve(to) }),
5288
+ link: async (from, to) => void await kernel.call({ op: "link", path: resolve(from), arg2: resolve(to) }),
5022
5289
  symlink: async (target, path) => void await kernel.call({ op: "symlink", path: resolve(path), arg2: target }),
5023
5290
  readlink: async (path) => String(await kernel.json({ op: "readlink", path: resolve(path) })),
5024
5291
  async readFile(path) {
@@ -5270,11 +5537,54 @@ function toMs(v) {
5270
5537
  if (v === void 0 || v === null) return void 0;
5271
5538
  return v instanceof Date ? v.getTime() : v;
5272
5539
  }
5540
+ function linkedStates(bridgeOf) {
5541
+ const opens = /* @__PURE__ */ new Map();
5542
+ const byId = /* @__PURE__ */ new Map();
5543
+ const idOf2 = (st) => st?.ino === void 0 || (st.nlink ?? 1) < 2 ? void 0 : `${st.dev}:${st.ino}`;
5544
+ function know(s, id) {
5545
+ s.id = id;
5546
+ if (!byId.has(id)) byId.set(id, s);
5547
+ }
5548
+ function opening(node, on) {
5549
+ const s = node.live;
5550
+ if (!on) {
5551
+ opens.delete(s);
5552
+ if (s.id !== void 0 && byId.get(s.id) === s) byId.delete(s.id);
5553
+ s.id = void 0;
5554
+ return;
5555
+ }
5556
+ opens.set(s, node);
5557
+ const id = idOf2(s.stat);
5558
+ if (id !== void 0) know(s, id);
5559
+ }
5560
+ function sharedWith(node) {
5561
+ const st = node.live.stat;
5562
+ if (st?.ino === void 0) return void 0;
5563
+ const id = `${st.dev}:${st.ino}`;
5564
+ if (!byId.has(id) && idOf2(st) !== void 0) {
5565
+ for (const [s2, other] of opens) {
5566
+ if (s2.id !== void 0 || s2.orphan) continue;
5567
+ let st2;
5568
+ try {
5569
+ st2 = bridgeOf(other).lstat(liveNodePath(other));
5570
+ } catch {
5571
+ }
5572
+ const known = idOf2(st2);
5573
+ if (known !== void 0) know(s2, known);
5574
+ }
5575
+ }
5576
+ const s = byId.get(id);
5577
+ return s && s !== node.live && !stale(s) ? s : void 0;
5578
+ }
5579
+ const holder = (node) => opens.get(node.live) ?? node;
5580
+ return { opening, sharedWith, know, idOf: idOf2, holder };
5581
+ }
5273
5582
  function createHelpers(Fs, ops) {
5274
5583
  const bridgeOf = (node) => node.mount.opts.bridge;
5275
5584
  const fileIo = (node) => pinnedIo2(bridgeOf(node), node.live.handle);
5276
5585
  const dirty = /* @__PURE__ */ new Set();
5277
5586
  const handled = /* @__PURE__ */ new Set();
5587
+ const linked = linkedStates(bridgeOf);
5278
5588
  function markDirty(node) {
5279
5589
  node.live.dirty = true;
5280
5590
  dirty.add(node);
@@ -5282,7 +5592,7 @@ function createHelpers(Fs, ops) {
5282
5592
  function flushDirty() {
5283
5593
  for (const node of [...dirty]) {
5284
5594
  const s = node.live;
5285
- if (!s.dirty || s.orphan) {
5595
+ if (!s.dirty || stale(s)) {
5286
5596
  dirty.delete(node);
5287
5597
  continue;
5288
5598
  }
@@ -5324,7 +5634,7 @@ function createHelpers(Fs, ops) {
5324
5634
  return { ...node.live.stat, size };
5325
5635
  }
5326
5636
  if (!node.live.stat) {
5327
- const st = call2(() => bridgeOf(node).lstat(liveNodePath(node)));
5637
+ const st = call2(() => bridgeOf(node).lstat(liveNodePath(linked.holder(node))));
5328
5638
  node.live.stat = st;
5329
5639
  node.mode = modeFromStat(st);
5330
5640
  }
@@ -5337,7 +5647,7 @@ function createHelpers(Fs, ops) {
5337
5647
  function ensureLoaded(node) {
5338
5648
  const s = node.live;
5339
5649
  if (s.loaded) return;
5340
- const bytes = call2(() => fileIo(node).readFile(liveNodePath(node)));
5650
+ const bytes = call2(() => fileIo(node).readFile(liveNodePath(linked.holder(node))));
5341
5651
  s.data = bytes;
5342
5652
  s.len = bytes.length;
5343
5653
  s.loaded = true;
@@ -5346,7 +5656,7 @@ function createHelpers(Fs, ops) {
5346
5656
  const s = node.live;
5347
5657
  if (s.ranged || !s.ranges) return s.ranged;
5348
5658
  const bridge = bridgeOf(node);
5349
- const path = () => liveNodePath(node);
5659
+ const path = () => liveNodePath(linked.holder(node));
5350
5660
  const file = () => fileIo(node);
5351
5661
  const io = {
5352
5662
  pread: (_, at, n, version) => file().pread(path(), at, n, version),
@@ -5368,11 +5678,11 @@ function createHelpers(Fs, ops) {
5368
5678
  }
5369
5679
  function flushNode(node) {
5370
5680
  const s = node.live;
5371
- if (!s.dirty || s.orphan || !(s.data || s.ranged)) return;
5681
+ if (!s.dirty || stale(s) || !(s.data || s.ranged)) return;
5372
5682
  if (s.ranged) call2(() => s.ranged?.flush());
5373
5683
  else {
5374
5684
  const bytes = s.data.slice(0, s.len);
5375
- call2(() => fileIo(node).writeFile(liveNodePath(node), bytes, true));
5685
+ call2(() => fileIo(node).writeFile(liveNodePath(linked.holder(node)), bytes, true));
5376
5686
  }
5377
5687
  s.dirty = false;
5378
5688
  s.stat = void 0;
@@ -5420,6 +5730,7 @@ function createHelpers(Fs, ops) {
5420
5730
  Fs,
5421
5731
  bridgeOf,
5422
5732
  handled,
5733
+ linked,
5423
5734
  call: call2,
5424
5735
  metadataCall,
5425
5736
  makeNode,
@@ -5434,6 +5745,33 @@ function createHelpers(Fs, ops) {
5434
5745
  truncate
5435
5746
  };
5436
5747
  }
5748
+ function freshStat(h, node) {
5749
+ const { statOf } = h;
5750
+ const st = statOf(node);
5751
+ if (!st.linkable) return st;
5752
+ node.live.stat = void 0;
5753
+ return statOf(node);
5754
+ }
5755
+ function orphaned(h, node, st) {
5756
+ const { bridgeOf } = h;
5757
+ const s = node.live;
5758
+ const nlink = st.nlink ?? 1;
5759
+ let kept = false;
5760
+ try {
5761
+ kept = nlink > 1 && s.handle !== void 0 && bridgeOf(node).kept?.(s.handle) === true;
5762
+ } catch {
5763
+ }
5764
+ if (nlink > 1 && !kept && h.linked.holder(node) !== node) {
5765
+ s.stat = void 0;
5766
+ return;
5767
+ }
5768
+ s.orphan = true;
5769
+ if (!kept) return;
5770
+ s.kept = true;
5771
+ s.stat = { ...st, nlink: nlink - 1 };
5772
+ const id = h.linked.idOf(st);
5773
+ if (id !== void 0) h.linked.know(s, id);
5774
+ }
5437
5775
  function createNodeOps(h) {
5438
5776
  const { Fs, bridgeOf, call: call2, metadataCall, makeNode, statOf, childPath, flushNode, truncate } = h;
5439
5777
  const { ensureLoaded } = h;
@@ -5464,7 +5802,7 @@ function createNodeOps(h) {
5464
5802
  dev: st.dev ?? 1,
5465
5803
  ino: st.ino ?? inodeOf(liveNodePath(node)),
5466
5804
  mode: node.mode,
5467
- nlink: 1,
5805
+ nlink: st.nlink ?? 1,
5468
5806
  uid: owned ? st.uid ?? 0 : 0,
5469
5807
  gid: owned ? st.gid ?? 0 : 0,
5470
5808
  rdev: 0,
@@ -5559,14 +5897,14 @@ function createNodeOps(h) {
5559
5897
  if (node && node.live?.openCount > 0 && Fs.isFile(node.mode)) open = node;
5560
5898
  } catch {
5561
5899
  }
5900
+ const st = open && freshStat(h, open);
5562
5901
  if (open) {
5563
- statOf(open);
5564
5902
  const ranged = h.rangedOf(open);
5565
5903
  if (ranged) ranged.pin();
5566
5904
  else ensureLoaded(open);
5567
5905
  }
5568
5906
  call2(() => bridgeOf(parent).unlink(childPath(parent, name2)));
5569
- if (open) open.live.orphan = true;
5907
+ if (open && st) orphaned(h, open, st);
5570
5908
  },
5571
5909
  rmdir(parent, name2) {
5572
5910
  parent.live.listed = void 0;
@@ -5614,6 +5952,7 @@ function createStreamOps(h) {
5614
5952
  try {
5615
5953
  const handle = bridgeOf(node).hold?.(liveNodePath(node), on, true, s.handle);
5616
5954
  s.handle = on ? handle : void 0;
5955
+ h.linked.opening(node, on);
5617
5956
  if (s.handle === void 0) h.handled.delete(node);
5618
5957
  else h.handled.add(node);
5619
5958
  } catch {
@@ -5626,7 +5965,9 @@ function createStreamOps(h) {
5626
5965
  if (s.openCount === 0 && !s.orphan) {
5627
5966
  const bridge = bridgeOf(stream.node);
5628
5967
  s.ranges = !!(bridge.pread && bridge.pwrite && bridge.truncate && statOf(stream.node).ranged);
5629
- rangedOf(stream.node);
5968
+ const shared = h.linked.sharedWith(stream.node);
5969
+ if (shared) stream.node.live = shared;
5970
+ else rangedOf(stream.node);
5630
5971
  }
5631
5972
  if ((stream.flags & 3) !== 0) {
5632
5973
  try {
@@ -5948,7 +6289,7 @@ function useLiveChown(Fs) {
5948
6289
  Fs.chown(target, uid, gid);
5949
6290
  };
5950
6291
  }
5951
- var S_IFDIR, S_IFREG, S_IFLNK, PERM_MASK, DEFAULT_DIR_PERM, UMASK, SEEK_CUR, SEEK_END, EARLY_WHOLE_FILE;
6292
+ var S_IFDIR, S_IFREG, S_IFLNK, PERM_MASK, DEFAULT_DIR_PERM, UMASK, SEEK_CUR, SEEK_END, EARLY_WHOLE_FILE, stale;
5952
6293
  var init_live_vfs_fs = __esm({
5953
6294
  "src/realm/live-vfs-fs.ts"() {
5954
6295
  "use strict";
@@ -5964,6 +6305,7 @@ var init_live_vfs_fs = __esm({
5964
6305
  SEEK_CUR = 1;
5965
6306
  SEEK_END = 2;
5966
6307
  EARLY_WHOLE_FILE = 64 * 1024;
6308
+ stale = (s) => !!s.orphan && !s.kept;
5967
6309
  }
5968
6310
  });
5969
6311
 
@@ -8965,6 +9307,13 @@ function procfsBridge(fs, tree) {
8965
9307
  () => fs.rename(from, to)
8966
9308
  ),
8967
9309
  symlink: (target, link) => readOnly([[link, false]], () => fs.symlink(target, link)),
9310
+ link: (from, to, follow2) => readOnly(
9311
+ [
9312
+ [from, follow2 === true],
9313
+ [to, false]
9314
+ ],
9315
+ () => fs.link(from, to, follow2)
9316
+ ),
8968
9317
  hold: (p, held, open, handle) => {
8969
9318
  if (handle !== void 0 && snapshots.delete(handle)) return void 0;
8970
9319
  if (!held) return fs.hold?.(p, held, open, handle);
@@ -9737,8 +10086,8 @@ function readExports(r, s) {
9737
10086
  for (let i = r.u32(); i > 0; i--) {
9738
10087
  const name2 = r.name();
9739
10088
  const kind = r.byte();
9740
- const index2 = r.u32();
9741
- if (kind === 0) s.info.exports[name2] = index2;
10089
+ const index3 = r.u32();
10090
+ if (kind === 0) s.info.exports[name2] = index3;
9742
10091
  }
9743
10092
  }
9744
10093
  function readElems(r, s) {
@@ -10041,15 +10390,15 @@ var init_wasix_linker = __esm({
10041
10390
  return out;
10042
10391
  }
10043
10392
  allocateClosure() {
10044
- const index2 = this.indices.allocate(this.table.length);
10045
- growTable(this.table, index2 + 1);
10046
- this.live.add(index2);
10047
- return index2;
10393
+ const index3 = this.indices.allocate(this.table.length);
10394
+ growTable(this.table, index3 + 1);
10395
+ this.live.add(index3);
10396
+ return index3;
10048
10397
  }
10049
- freeClosure(index2) {
10050
- this.indices?.free(index2);
10051
- this.live.delete(index2);
10052
- this.table.set(index2, null);
10398
+ freeClosure(index3) {
10399
+ this.indices?.free(index3);
10400
+ this.live.delete(index3);
10401
+ this.table.set(index3, null);
10053
10402
  }
10054
10403
  live = /* @__PURE__ */ new Set();
10055
10404
  prepareClosure(record) {
@@ -10282,12 +10631,12 @@ var init_wasix_linker = __esm({
10282
10631
  this.indexTable();
10283
10632
  const known = this.slots.get(fn);
10284
10633
  if (known !== void 0) return known;
10285
- const index2 = this.reserve(1);
10286
- growTable(this.table, index2 + 1);
10287
- this.table.set(index2, fn);
10288
- this.slots.set(fn, index2);
10289
- this.publisher?.({ kind: "slot", index: index2, handle, name: name2 });
10290
- return index2;
10634
+ const index3 = this.reserve(1);
10635
+ growTable(this.table, index3 + 1);
10636
+ this.table.set(index3, fn);
10637
+ this.slots.set(fn, index3);
10638
+ this.publisher?.({ kind: "slot", index: index3, handle, name: name2 });
10639
+ return index3;
10291
10640
  }
10292
10641
  allocate(size, alignment) {
10293
10642
  if (size === 0) return 0;
@@ -10572,11 +10921,11 @@ var init_wasix_sockets = __esm({
10572
10921
  });
10573
10922
 
10574
10923
  // src/process/wasi/wasix-host.ts
10575
- function futexWait(view, index2, expected, ms, interrupted) {
10924
+ function futexWait(view, index3, expected, ms, interrupted) {
10576
10925
  const deadline = performance.now() + ms;
10577
10926
  for (; ; ) {
10578
10927
  const slice = interrupted ? Math.min(FUTEX_SLICE_MS, ms) : ms;
10579
- if (Atomics.wait(view, index2, expected, slice) !== "timed-out") return true;
10928
+ if (Atomics.wait(view, index3, expected, slice) !== "timed-out") return true;
10580
10929
  if (interrupted?.()) return true;
10581
10930
  ms = deadline - performance.now();
10582
10931
  if (ms <= 0) return false;
@@ -10705,21 +11054,21 @@ var init_wasix_host = __esm({
10705
11054
  if (!this.linker) throw new WasiError("ENOTSUP");
10706
11055
  this.mem.view().setUint32(out, this.linker.allocateClosure(), true);
10707
11056
  },
10708
- closure_prepare: (backing, index2, argTypes, argCount, resultTypes, resultCount, userData) => {
11057
+ closure_prepare: (backing, index3, argTypes, argCount, resultTypes, resultCount, userData) => {
10709
11058
  if (!this.linker) throw new WasiError("ENOTSUP");
10710
11059
  const params = valueTypes(this.mem.bytes(argTypes, argCount));
10711
11060
  const results = valueTypes(this.mem.bytes(resultTypes, resultCount));
10712
11061
  if (!params || !results) throw new WasiError("EINVAL");
10713
11062
  try {
10714
- this.linker.prepareClosure({ index: index2, backing, userData, params, results });
11063
+ this.linker.prepareClosure({ index: index3, backing, userData, params, results });
10715
11064
  } catch (e) {
10716
11065
  if (e instanceof DlError) throw new WasiError("EINVAL");
10717
11066
  throw e;
10718
11067
  }
10719
11068
  },
10720
- closure_free: (index2) => {
11069
+ closure_free: (index3) => {
10721
11070
  if (!this.linker) throw new WasiError("ENOTSUP");
10722
- this.linker.freeClosure(index2);
11071
+ this.linker.freeClosure(index3);
10723
11072
  },
10724
11073
  call_dynamic: (...a) => void callDynamic(
10725
11074
  { mem: this.mem, table: () => this.table, types: this.types },
@@ -11024,10 +11373,10 @@ function parseFunctionNames(payload) {
11024
11373
  if (end > payload.length) throw new RangeError("truncated name section");
11025
11374
  if (id === 1) {
11026
11375
  for (let count = leb(); count > 0; count--) {
11027
- const index2 = leb();
11376
+ const index3 = leb();
11028
11377
  const length = leb();
11029
11378
  if (at + length > end) throw new RangeError("truncated name section");
11030
- names.set(index2, decoder3.decode(payload.subarray(at, at + length)));
11379
+ names.set(index3, decoder3.decode(payload.subarray(at, at + length)));
11031
11380
  at += length;
11032
11381
  }
11033
11382
  }
@@ -11051,7 +11400,7 @@ function nameFrame(line, module, name2) {
11051
11400
  }
11052
11401
  function sidecarNames(read2, path) {
11053
11402
  let names;
11054
- return (index2) => {
11403
+ return (index3) => {
11055
11404
  if (!names) {
11056
11405
  try {
11057
11406
  names = parseFunctionNames(read2(path));
@@ -11059,7 +11408,7 @@ function sidecarNames(read2, path) {
11059
11408
  names = /* @__PURE__ */ new Map();
11060
11409
  }
11061
11410
  }
11062
- return names.get(index2);
11411
+ return names.get(index3);
11063
11412
  };
11064
11413
  }
11065
11414
  var UNNAMED_FRAME, WASM_MODULE;
@@ -11420,7 +11769,7 @@ async function runWasiProcess(init, port3) {
11420
11769
  parent: () => identify(transport, init).ppid,
11421
11770
  kernel: { sys: traced(stats, "kernel", sys), call: call2 },
11422
11771
  fs: procfsBridge(
11423
- cachingBridge(traced(stats, "fs", createSyncFsSabBridge(transport))),
11772
+ cachingBridge(traced(stats, "fs", createSyncFsSabBridge(transport)), void 0, threads?.ids),
11424
11773
  procTree(call2, id.pid)
11425
11774
  ),
11426
11775
  ...descriptors(init, fork, threads)
@@ -11513,7 +11862,10 @@ async function runWasiThread(init, port3) {
11513
11862
  ppid: id.ppid,
11514
11863
  parent: () => identify(transport, init).ppid,
11515
11864
  kernel: { sys, call: call2 },
11516
- fs: procfsBridge(cachingBridge(createSyncFsSabBridge(transport)), procTree(call2, id.pid)),
11865
+ fs: procfsBridge(
11866
+ cachingBridge(createSyncFsSabBridge(transport), void 0, threads.ids),
11867
+ procTree(call2, id.pid)
11868
+ ),
11517
11869
  shared: threads.ids
11518
11870
  });
11519
11871
  const program = await programImports(init.program, host, transport, thread.tid);
@@ -11625,13 +11977,13 @@ var init_wasi_runtime = __esm({
11625
11977
  },
11626
11978
  unlock: () => this.call({ op: "dl-unlock", owner: this.owner }),
11627
11979
  allocate: (floor) => {
11628
- const index2 = this.call({ op: "dl-closure", floor });
11980
+ const index3 = this.call({ op: "dl-closure", floor });
11629
11981
  this.catchUp();
11630
11982
  this.bump();
11631
- return index2;
11983
+ return index3;
11632
11984
  },
11633
- free: (index2) => {
11634
- this.call({ op: "dl-closure-free", index: index2 });
11985
+ free: (index3) => {
11986
+ this.call({ op: "dl-closure-free", index: index3 });
11635
11987
  this.bump();
11636
11988
  }
11637
11989
  };