@ai-ecoverse/slicc-kernel 1.64.2 → 1.65.1

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.
@@ -1,18 +1,99 @@
1
+ // src/fs/path-map.ts
2
+ function parentOf(path) {
3
+ if (path === "/" || path === "") return void 0;
4
+ const at = path.lastIndexOf("/");
5
+ return at <= 0 ? "/" : path.slice(0, at);
6
+ }
7
+ var PathMap = class {
8
+ values = /* @__PURE__ */ new Map();
9
+ children = /* @__PURE__ */ new Map();
10
+ get size() {
11
+ return this.values.size;
12
+ }
13
+ has(path) {
14
+ return this.values.has(path);
15
+ }
16
+ get(path) {
17
+ return this.values.get(path);
18
+ }
19
+ keys() {
20
+ return this.values.keys();
21
+ }
22
+ set(path, value) {
23
+ if (!this.values.has(path)) this.link(path);
24
+ this.values.set(path, value);
25
+ return this;
26
+ }
27
+ delete(path) {
28
+ if (!this.values.delete(path)) return false;
29
+ this.prune(path);
30
+ return true;
31
+ }
32
+ subtree(root) {
33
+ const out = [];
34
+ const stack = [root];
35
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
36
+ if (this.values.has(path)) out.push(path);
37
+ const below = this.children.get(path);
38
+ if (below) for (const child of below) stack.push(child);
39
+ }
40
+ return out;
41
+ }
42
+ entries() {
43
+ return this.values.entries();
44
+ }
45
+ deleteTree(root) {
46
+ const stack = [root];
47
+ for (let path = stack.pop(); path !== void 0; path = stack.pop()) {
48
+ this.values.delete(path);
49
+ const below = this.children.get(path);
50
+ if (below) for (const child of below) stack.push(child);
51
+ this.children.delete(path);
52
+ }
53
+ this.prune(root);
54
+ }
55
+ clear() {
56
+ this.values.clear();
57
+ this.children.clear();
58
+ }
59
+ link(path) {
60
+ for (let child = path, parent = parentOf(path); parent !== void 0; ) {
61
+ const set2 = this.children.get(parent);
62
+ if (set2?.has(child)) return;
63
+ if (set2) set2.add(child);
64
+ else this.children.set(parent, /* @__PURE__ */ new Set([child]));
65
+ child = parent;
66
+ parent = parentOf(parent);
67
+ }
68
+ }
69
+ prune(path) {
70
+ for (let child = path, parent = parentOf(path); parent !== void 0; ) {
71
+ if (this.values.has(child) || this.children.has(child)) return;
72
+ const set2 = this.children.get(parent);
73
+ set2?.delete(child);
74
+ if (set2 && set2.size > 0) return;
75
+ this.children.delete(parent);
76
+ child = parent;
77
+ parent = parentOf(parent);
78
+ }
79
+ }
80
+ };
81
+
1
82
  // src/fs/meta.ts
2
83
  var META_DB = "slicc-kernel";
3
84
  var META_VERSION = 1;
4
85
  var META_STORE = "entries";
5
- function parentOf(path) {
86
+ function parentOf2(path) {
6
87
  return path.slice(0, path.lastIndexOf("/")) || "/";
7
88
  }
8
89
  function moved(entry, path) {
9
- return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf(path) };
90
+ return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
10
91
  }
11
92
  function under(path, root) {
12
93
  return path === root || path.startsWith(`${root}/`);
13
94
  }
14
95
  var MemoryMeta = class {
15
- entries = /* @__PURE__ */ new Map();
96
+ entries = new PathMap();
16
97
  async get(paths) {
17
98
  return paths.map((path) => this.entries.get(path));
18
99
  }
@@ -25,7 +106,7 @@ var MemoryMeta = class {
25
106
  }
26
107
  async move(from, to, root) {
27
108
  await this.remove([to]);
28
- const carried = [...this.entries.values()].filter((entry) => under(entry.path, from));
109
+ const carried = this.entries.subtree(from).map((path) => this.entries.get(path));
29
110
  for (const entry of carried) this.entries.delete(entry.path);
30
111
  for (const entry of carried) {
31
112
  const path = to + entry.path.slice(from.length);
@@ -34,18 +115,16 @@ var MemoryMeta = class {
34
115
  this.entries.set(to, moved(root(carried.find((entry) => entry.path === from)), to));
35
116
  }
36
117
  async remove(paths) {
37
- for (const key of [...this.entries.keys()]) {
38
- if (paths.some((path) => under(key, path))) this.entries.delete(key);
39
- }
118
+ for (const path of paths) this.entries.deleteTree(path);
40
119
  }
41
120
  async links(dir) {
42
- return [...this.entries.values()].filter((entry) => entry.dir === dir);
121
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
43
122
  }
44
123
  async under(path) {
45
- return [...this.entries.values()].filter((entry) => under(entry.path, path));
124
+ return [...this.entries.entries()].map(([, e]) => e).filter((entry) => under(entry.path, path));
46
125
  }
47
126
  async all() {
48
- return [...this.entries.values()];
127
+ return [...this.entries.entries()].map(([, e]) => e);
49
128
  }
50
129
  };
51
130
  function descendants(path) {
@@ -59,7 +138,7 @@ function settle(transaction, value) {
59
138
  }
60
139
  var IndexedDbMeta = class {
61
140
  db;
62
- cache = /* @__PURE__ */ new Map();
141
+ cache = new PathMap();
63
142
  channel;
64
143
  generation = 0;
65
144
  constructor(db) {
@@ -72,9 +151,7 @@ var IndexedDbMeta = class {
72
151
  drop({ paths = [], trees = [] }) {
73
152
  this.generation++;
74
153
  for (const path of paths) this.cache.delete(path);
75
- for (const key of [...this.cache.keys()]) {
76
- if (trees.some((tree2) => under(key, tree2))) this.cache.delete(key);
77
- }
154
+ for (const tree2 of trees) this.cache.deleteTree(tree2);
78
155
  }
79
156
  changed(invalidation2) {
80
157
  this.drop(invalidation2);
@@ -327,7 +404,7 @@ function missing(err) {
327
404
  function split(path) {
328
405
  return path.split("/").filter(Boolean);
329
406
  }
330
- function parentOf2(path) {
407
+ function parentOf3(path) {
331
408
  return path.slice(0, path.lastIndexOf("/")) || "/";
332
409
  }
333
410
  function baseOf(path) {
@@ -400,7 +477,7 @@ function isHandle(child) {
400
477
  var OpfsFs = class {
401
478
  root;
402
479
  meta;
403
- dirs = /* @__PURE__ */ new Map();
480
+ dirs = new PathMap();
404
481
  checked = /* @__PURE__ */ new WeakMap();
405
482
  channel;
406
483
  ranged;
@@ -449,7 +526,7 @@ var OpfsFs = class {
449
526
  async ownerAt(path, entry) {
450
527
  const own = ownOf(entry);
451
528
  if (own) return own;
452
- return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf2(path));
529
+ return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
453
530
  }
454
531
  async owned(stat, path, entry) {
455
532
  return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
@@ -479,7 +556,7 @@ var OpfsFs = class {
479
556
  async chown(path, change) {
480
557
  if (!this.owners) return;
481
558
  if (change.kind === "new") {
482
- const at = resolvePath((await this.locate(parentOf2(path), true)).path, baseOf(path));
559
+ const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
483
560
  const { uid = 0, gid = 0, mode } = change;
484
561
  return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
485
562
  }
@@ -543,7 +620,7 @@ var OpfsFs = class {
543
620
  return true;
544
621
  }
545
622
  forget(path) {
546
- for (const key of [...this.dirs.keys()]) if (within(key, path)) this.dirs.delete(key);
623
+ this.dirs.deleteTree(path);
547
624
  }
548
625
  async inDir(path, op) {
549
626
  const dir = await this.dir(path);
@@ -590,7 +667,7 @@ var OpfsFs = class {
590
667
  async linkAlong(path) {
591
668
  for (const entry of await this.meta.get(prefixes(path))) {
592
669
  if (entry?.link === void 0) continue;
593
- if (!await this.real(entry.path) && await this.real(parentOf2(entry.path))) return entry;
670
+ if (!await this.real(entry.path) && await this.real(parentOf3(entry.path))) return entry;
594
671
  }
595
672
  return void 0;
596
673
  }
@@ -605,7 +682,7 @@ var OpfsFs = class {
605
682
  if (!link2) throw err;
606
683
  if (link2.path === current && !follow2) return { path: current, link: link2 };
607
684
  const target = `${link2.link}${current.slice(link2.path.length)}`;
608
- current = target.split("/").includes("..") ? await this.physical(parentOf2(link2.path), target, hops + 1) : resolvePath(parentOf2(link2.path), target);
685
+ current = target.split("/").includes("..") ? await this.physical(parentOf3(link2.path), target, hops + 1) : resolvePath(parentOf3(link2.path), target);
609
686
  }
610
687
  }
611
688
  throw fsError("ELOOP", path);
@@ -795,8 +872,8 @@ var OpfsFs = class {
795
872
  if (!missing(err)) throw err;
796
873
  }
797
874
  const [entry] = await this.meta.get([path]);
798
- const wanted = entry?.link === void 0 ? path : resolvePath(parentOf2(path), entry.link);
799
- return [resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted)), false];
875
+ const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
876
+ return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
800
877
  }
801
878
  async fileFor(path) {
802
879
  const [target, existed] = await this.writeTarget(path);
@@ -806,7 +883,7 @@ var OpfsFs = class {
806
883
  dir,
807
884
  (parent) => parent.getFileHandle(name2, { create: true })
808
885
  );
809
- if (!existed) this.touch(parentOf2(target));
886
+ if (!existed) this.touch(parentOf3(target));
810
887
  return [handle, existed, target];
811
888
  } catch (err) {
812
889
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
@@ -876,9 +953,9 @@ var OpfsFs = class {
876
953
  gid: owner.gid,
877
954
  ...mode ? { mode, parents: mode } : {}
878
955
  };
879
- await this.mkdir(parentOf2(path), { recursive: true, ...above ? { owner: above } : {} });
956
+ await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
880
957
  }
881
- const parent = await this.locate(parentOf2(path), true);
958
+ const parent = await this.locate(parentOf3(path), true);
882
959
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
883
960
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
884
961
  const made2 = `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`;
@@ -907,7 +984,7 @@ var OpfsFs = class {
907
984
  this.changed(found.path);
908
985
  }
909
986
  await this.meta.remove([found.path]);
910
- this.touch(parentOf2(found.path));
987
+ this.touch(parentOf3(found.path));
911
988
  }
912
989
  links() {
913
990
  return false;
@@ -919,7 +996,7 @@ var OpfsFs = class {
919
996
  if (from === to) return;
920
997
  const source = await this.locate(from, false);
921
998
  this.forget(source.path);
922
- const parent = await this.locate(parentOf2(to), true);
999
+ const parent = await this.locate(parentOf3(to), true);
923
1000
  const destination = resolvePath(parent.path, baseOf(to));
924
1001
  const sourceStat = await this.describe(source);
925
1002
  if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
@@ -947,7 +1024,7 @@ var OpfsFs = class {
947
1024
  if (source.link) {
948
1025
  if (target?.handle) await this.rm(destination);
949
1026
  await this.meta.move(source.path, destination, carry);
950
- this.touch(parentOf2(source.path), parent.path);
1027
+ this.touch(parentOf3(source.path), parent.path);
951
1028
  return;
952
1029
  }
953
1030
  const dir = parent.handle;
@@ -969,7 +1046,7 @@ var OpfsFs = class {
969
1046
  }
970
1047
  if (copied) await this.restamp(destination, timed2);
971
1048
  this.changed(source.path, destination);
972
- this.touch(parentOf2(source.path), parent.path);
1049
+ this.touch(parentOf3(source.path), parent.path);
973
1050
  }
974
1051
  async modified(path) {
975
1052
  const handle = await this.handle(path);
@@ -1015,7 +1092,7 @@ var OpfsFs = class {
1015
1092
  for await (const child of handle.values()) await this.copy(child, target, child.name);
1016
1093
  }
1017
1094
  async symlink(target, path, owner) {
1018
- const parent = await this.locate(parentOf2(path), true);
1095
+ const parent = await this.locate(parentOf3(path), true);
1019
1096
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
1020
1097
  const at = resolvePath(parent.path, baseOf(path));
1021
1098
  if (await this.locate(at, false).then(
@@ -1091,7 +1168,7 @@ var OpfsFs = class {
1091
1168
  let removed = 0;
1092
1169
  for (const entry of await this.meta.all()) {
1093
1170
  const real = await this.real(entry.path);
1094
- const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf2(entry.path));
1171
+ const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf3(entry.path));
1095
1172
  if (!stale) continue;
1096
1173
  await this.meta.update(entry.path, () => void 0);
1097
1174
  removed++;
@@ -1262,7 +1339,7 @@ function versionError(theirs) {
1262
1339
 
1263
1340
  // src/fs/walk.ts
1264
1341
  var MAX_LINKS2 = 40;
1265
- function parentOf3(path) {
1342
+ function parentOf4(path) {
1266
1343
  return path.slice(0, path.lastIndexOf("/")) || "/";
1267
1344
  }
1268
1345
  async function mustBeDir(fs, path) {
@@ -1280,7 +1357,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
1280
1357
  const part = queue.shift();
1281
1358
  if (part === ".") continue;
1282
1359
  if (part === "..") {
1283
- at = parentOf3(at);
1360
+ at = parentOf4(at);
1284
1361
  continue;
1285
1362
  }
1286
1363
  const next2 = at === "/" ? `/${part}` : `${at}/${part}`;
@@ -1302,7 +1379,7 @@ function joinName(dir, name2) {
1302
1379
  }
1303
1380
  async function realTarget(fs, path) {
1304
1381
  const budget = { hops: 0 };
1305
- let dir = await realDir(fs, parentOf3(path), budget);
1382
+ let dir = await realDir(fs, parentOf4(path), budget);
1306
1383
  let name2 = path.slice(path.lastIndexOf("/") + 1);
1307
1384
  for (; ; ) {
1308
1385
  const link2 = await linkOf(fs, joinName(dir, name2));
@@ -1310,7 +1387,7 @@ async function realTarget(fs, path) {
1310
1387
  if (budget.hops >= MAX_LINKS2) throw fsError("ELOOP", path);
1311
1388
  budget.hops++;
1312
1389
  const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
1313
- dir = await realDir(fs, parentOf3(target), budget);
1390
+ dir = await realDir(fs, parentOf4(target), budget);
1314
1391
  name2 = target.slice(target.lastIndexOf("/") + 1);
1315
1392
  }
1316
1393
  return joinName(dir, name2);
@@ -1384,7 +1461,7 @@ async function walk(fs, cred, path, follow2) {
1384
1461
  if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
1385
1462
  if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
1386
1463
  if (part === "..") {
1387
- at = parentOf3(at);
1464
+ at = parentOf4(at);
1388
1465
  continue;
1389
1466
  }
1390
1467
  const next2 = joinName(at, part);
@@ -1506,17 +1583,17 @@ async function stickyEntry(g, op, path, dir) {
1506
1583
  }
1507
1584
  async function inParent(g, op, path, old = false) {
1508
1585
  if (old === "replaced") {
1509
- const dir2 = await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1586
+ const dir2 = await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
1510
1587
  return dir2.denied === void 0 ? stickyEntry(g, op, path, dir2) : void 0;
1511
1588
  }
1512
- if (!old) return void await need(g, op, parentOf3(path), W_OK | X_OK, DIR, path);
1513
- const dir = await access(g.fs, g.cred, parentOf3(path), W_OK | X_OK, DIR);
1589
+ if (!old) return void await need(g, op, parentOf4(path), W_OK | X_OK, DIR, path);
1590
+ const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, DIR);
1514
1591
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
1515
1592
  const lookup = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
1516
1593
  if (!lookup || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
1517
1594
  }
1518
1595
  async function makeIn(g, op, path) {
1519
- for (let dir = parentOf3(path); ; dir = parentOf3(dir)) {
1596
+ for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
1520
1597
  const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, DIR);
1521
1598
  if (walked.denied !== void 0) {
1522
1599
  if (!await present(g.fs, path, true)) deny(g, op, path);
@@ -1544,7 +1621,7 @@ async function renamable(g, from, to) {
1544
1621
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
1545
1622
  await inParent(g, "rename", from, true);
1546
1623
  await inParent(g, "rename", to, "replaced");
1547
- if (g.cred.euid === 0 || parentOf3(from) === parentOf3(to)) return;
1624
+ if (g.cred.euid === 0 || parentOf4(from) === parentOf4(to)) return;
1548
1625
  const st = await g.fs.lstat(from).catch(() => void 0);
1549
1626
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
1550
1627
  }
@@ -1598,7 +1675,7 @@ async function writable(g, op, path) {
1598
1675
  }
1599
1676
  async function creatable(g, op, path) {
1600
1677
  const target = await need(g, op, path, 0);
1601
- if (target.denied === void 0) await need(g, op, parentOf3(target.path), W_OK | X_OK, DIR, path);
1678
+ if (target.denied === void 0) await need(g, op, parentOf4(target.path), W_OK | X_OK, DIR, path);
1602
1679
  }
1603
1680
  async function openable(g, op, path, want, create) {
1604
1681
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
@@ -3516,7 +3593,7 @@ function cdpRegistry(send, gone) {
3516
3593
  function visibleTo(g, notify) {
3517
3594
  if (!g?.dac.enforcing) return notify;
3518
3595
  let queue = Promise.resolve();
3519
- const seen = async (path) => (await access(g.fs, g.cred, parentOf3(path), R_OK | X_OK, { directory: true })).denied === void 0;
3596
+ const seen = async (path) => (await access(g.fs, g.cred, parentOf4(path), R_OK | X_OK, { directory: true })).denied === void 0;
3520
3597
  return (change) => {
3521
3598
  queue = queue.then(async () => {
3522
3599
  if (!("paths" in change)) return notify(change);
@@ -6504,7 +6581,7 @@ function webLocks() {
6504
6581
  return globalThis.navigator?.locks;
6505
6582
  }
6506
6583
  var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6507
- var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6584
+ var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6508
6585
  var lockName = (instance) => `slicc-unlinked:${instance}`;
6509
6586
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
6510
6587
  var PathGate = class {
@@ -6637,7 +6714,7 @@ var UnlinkedKeeper = class {
6637
6714
  await this.serial(async () => {
6638
6715
  await Promise.allSettled([this.fs.rename(file.hidden, path)]);
6639
6716
  this.unlinkedName(file.hidden);
6640
- await this.tidy(parentOf4(file.hidden));
6717
+ await this.tidy(parentOf5(file.hidden));
6641
6718
  });
6642
6719
  }
6643
6720
  done4();
@@ -6678,7 +6755,7 @@ var UnlinkedKeeper = class {
6678
6755
  }
6679
6756
  await this.fs.rm(hidden).catch(() => void 0);
6680
6757
  this.unlinkedName(hidden);
6681
- await this.tidy(parentOf4(hidden));
6758
+ await this.tidy(parentOf5(hidden));
6682
6759
  }
6683
6760
  async linkedName(hidden) {
6684
6761
  if (!mayLink(this.fs, hidden)) return;
@@ -6723,10 +6800,10 @@ var UnlinkedKeeper = class {
6723
6800
  // src/fs/watch.ts
6724
6801
  var WATCH_BATCH_LIMIT = 1e3;
6725
6802
  var under2 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
6726
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6803
+ var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
6727
6804
  function touches(watcher, changed) {
6728
6805
  return watcher.paths.some(
6729
- (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf5(changed) === watched)
6806
+ (watched) => changed === watched || under2(watched, changed) || (watcher.recursive ? under2(changed, watched) : parentOf6(changed) === watched)
6730
6807
  );
6731
6808
  }
6732
6809
  var FsWatchers = class {
@@ -6839,6 +6916,23 @@ var CONTINUED_STATUS = 65535;
6839
6916
  function waitStatus(code, termsig) {
6840
6917
  return termsig ? termsig & 127 : (code & 255) << 8;
6841
6918
  }
6919
+ function capturing(chunks, max = Number.POSITIVE_INFINITY, overflow) {
6920
+ let size = 0;
6921
+ let over = false;
6922
+ return (bytes) => {
6923
+ if (over) return;
6924
+ const room = max - size;
6925
+ if (bytes.length > room) {
6926
+ over = true;
6927
+ if (room > 0) chunks.push(bytes.slice(0, room));
6928
+ size = max;
6929
+ overflow?.();
6930
+ return;
6931
+ }
6932
+ chunks.push(bytes);
6933
+ size += bytes.length;
6934
+ };
6935
+ }
6842
6936
  function concat3(chunks) {
6843
6937
  const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
6844
6938
  let offset = 0;
@@ -6872,15 +6966,15 @@ var ChildTable = class {
6872
6966
  const fds = new FdTable();
6873
6967
  const captured = /* @__PURE__ */ new Map();
6874
6968
  try {
6875
- for (const [n, slot] of stdio.entries()) fds.installAt(n, this.openSlot(slot, n, captured));
6876
- for (const slot of inherit2) {
6877
- if (slot.fd <= 2) continue;
6878
- if ("device" in slot) {
6879
- fds.installAt(slot.fd, deviceFile(slot.device, slot.access));
6969
+ for (const [n, slot2] of stdio.entries()) fds.installAt(n, this.openSlot(slot2, n, captured));
6970
+ for (const slot2 of inherit2) {
6971
+ if (slot2.fd <= 2) continue;
6972
+ if ("device" in slot2) {
6973
+ fds.installAt(slot2.fd, deviceFile(slot2.device, slot2.access));
6880
6974
  continue;
6881
6975
  }
6882
- fds.installAt(slot.fd, this.parentFds.get(slot.kernel).retain());
6883
- if (slot.flags !== void 0) fds.setStatusFlags(slot.fd, slot.flags);
6976
+ fds.installAt(slot2.fd, this.parentFds.get(slot2.kernel).retain());
6977
+ if (slot2.flags !== void 0) fds.setStatusFlags(slot2.fd, slot2.flags);
6884
6978
  }
6885
6979
  } catch (e) {
6886
6980
  await fds.closeAll();
@@ -6921,15 +7015,15 @@ var ChildTable = class {
6921
7015
  this.watchers.set(pid, listener);
6922
7016
  if (child.stoppedBy !== void 0) listener("stopped", child.stoppedBy);
6923
7017
  }
6924
- openSlot(slot, n, captured) {
6925
- if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
6926
- if ("input" in slot) return bytesSource(slot.input);
6927
- if ("device" in slot) return deviceFile(slot.device, slot.access);
6928
- if ("dir" in slot) return heldFile({ dir: slot.dir });
6929
- if ("capture" in slot) {
7018
+ openSlot(slot2, n, captured) {
7019
+ if ("fd" in slot2) return this.parentFds.get(slot2.fd).retain();
7020
+ if ("input" in slot2) return bytesSource(slot2.input);
7021
+ if ("device" in slot2) return deviceFile(slot2.device, slot2.access);
7022
+ if ("dir" in slot2) return heldFile({ dir: slot2.dir });
7023
+ if ("capture" in slot2) {
6930
7024
  const chunks = [];
6931
7025
  captured.set(n, chunks);
6932
- return sinkFile((bytes) => chunks.push(bytes));
7026
+ return sinkFile(capturing(chunks, slot2.max, slot2.overflow));
6933
7027
  }
6934
7028
  return deviceFile("null");
6935
7029
  }
@@ -6996,11 +7090,11 @@ var ChildTable = class {
6996
7090
  pids() {
6997
7091
  return [...this.children.keys()];
6998
7092
  }
6999
- captured(pid, slot) {
7093
+ captured(pid, slot2) {
7000
7094
  const slots = this.leftovers.get(pid);
7001
- const chunks = slots?.get(slot);
7095
+ const chunks = slots?.get(slot2);
7002
7096
  if (!slots || !chunks) return new Uint8Array(0);
7003
- slots.delete(slot);
7097
+ slots.delete(slot2);
7004
7098
  if (slots.size === 0) this.leftovers.delete(pid);
7005
7099
  return concat3(chunks);
7006
7100
  }
@@ -7286,7 +7380,7 @@ var HeldPaths = class extends Map {
7286
7380
  return this.revoked.has(path);
7287
7381
  }
7288
7382
  };
7289
- var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7383
+ var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
7290
7384
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
7291
7385
  function inodeless(mount, attr2) {
7292
7386
  if (attr2.ino !== void 0) return false;
@@ -7566,7 +7660,7 @@ var MountTable = class {
7566
7660
  mount.epoch++;
7567
7661
  for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
7568
7662
  mount.lists.delete(path);
7569
- mount.lists.delete(parentOf6(path));
7663
+ mount.lists.delete(parentOf7(path));
7570
7664
  for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
7571
7665
  }
7572
7666
  async getattr(mount, path, rel2, settled = false) {
@@ -7642,7 +7736,7 @@ var MountFs = class {
7642
7736
  if (attr2.kind !== "symlink") return real;
7643
7737
  if (hops >= MAX_LINKS6) throw errnoError("ELOOP", path);
7644
7738
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
7645
- return this.follow(this.base.resolvePath(parentOf6(real), target), hops + 1, create);
7739
+ return this.follow(this.base.resolvePath(parentOf7(real), target), hops + 1, create);
7646
7740
  }
7647
7741
  async open(mount, rel2, write, owner) {
7648
7742
  return await mount.conn.call({
@@ -7658,8 +7752,8 @@ var MountFs = class {
7658
7752
  async driverOwner(found, real, owner, directory) {
7659
7753
  const { mount, rel: rel2 } = found;
7660
7754
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
7661
- const dir = parentOf6(real);
7662
- const above = statOf(await this.table.getattr(mount, dir, parentOf6(rel2)), dir, mount);
7755
+ const dir = parentOf7(real);
7756
+ const above = statOf(await this.table.getattr(mount, dir, parentOf7(rel2)), dir, mount);
7663
7757
  const setgid = (above.mode & S_ISGID4) !== 0;
7664
7758
  let mode = owner.mode;
7665
7759
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
@@ -7998,7 +8092,7 @@ var MountFs = class {
7998
8092
  }
7999
8093
  async named(path) {
8000
8094
  const at = normalizePath(path);
8001
- return joinName(await realDir(this, parentOf6(at)), at.slice(at.lastIndexOf("/") + 1));
8095
+ return joinName(await realDir(this, parentOf7(at)), at.slice(at.lastIndexOf("/") + 1));
8002
8096
  }
8003
8097
  async link(source, target, follow2 = false) {
8004
8098
  const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
@@ -9234,6 +9328,7 @@ var HOST_OPS = [
9234
9328
  "async-resolve",
9235
9329
  "async-hold",
9236
9330
  "async-cancel",
9331
+ "async-settle",
9237
9332
  "async-close"
9238
9333
  ];
9239
9334
  var json3 = (value) => ({ ok: true, kind: "json", json: value });
@@ -9265,12 +9360,16 @@ var LockTable = class {
9265
9360
  for (const path of [...this.held.keys()]) this.unlock(pid, path);
9266
9361
  }
9267
9362
  };
9363
+ var KEPT_OPS = /* @__PURE__ */ new Set(["fd-write"]);
9364
+ var unknownSettled = false;
9268
9365
  var AsyncOps = class {
9269
9366
  next = 1;
9270
9367
  results = /* @__PURE__ */ new Map();
9271
9368
  ready = [];
9272
9369
  running = /* @__PURE__ */ new Map();
9273
9370
  waiters = /* @__PURE__ */ new Set();
9371
+ taken = /* @__PURE__ */ new Map();
9372
+ kept = /* @__PURE__ */ new Set();
9274
9373
  closed = false;
9275
9374
  run;
9276
9375
  onReady;
@@ -9280,6 +9379,7 @@ var AsyncOps = class {
9280
9379
  }
9281
9380
  submit(req) {
9282
9381
  const id2 = this.hold();
9382
+ if (KEPT_OPS.has(req.op)) this.kept.add(id2);
9283
9383
  const running = new AbortController();
9284
9384
  this.running.set(id2, running);
9285
9385
  this.run(req, running.signal).then(
@@ -9303,9 +9403,26 @@ var AsyncOps = class {
9303
9403
  const result = this.results.get(id2);
9304
9404
  if (!result) throw new KernelError("EAGAIN");
9305
9405
  this.forget(id2);
9406
+ if (this.kept.delete(id2)) this.taken.set(id2, result);
9306
9407
  return result;
9307
9408
  }
9409
+ ack(ids) {
9410
+ for (const id2 of ids) this.taken.delete(id2);
9411
+ }
9412
+ async settle(ids) {
9413
+ const never = new AbortController().signal;
9414
+ while (ids.some((id2) => this.running.has(id2))) await this.sleep(void 0, never);
9415
+ return ids.map((id2) => {
9416
+ const result = this.results.get(id2) ?? this.taken.get(id2) ?? null;
9417
+ if (!result && !unknownSettled) {
9418
+ unknownSettled = true;
9419
+ console.warn(`async-settle: no result for op ${id2}; its write counts as made`);
9420
+ }
9421
+ return result;
9422
+ });
9423
+ }
9308
9424
  cancel(id2) {
9425
+ this.kept.delete(id2);
9309
9426
  this.running.get(id2)?.abort();
9310
9427
  this.forget(id2);
9311
9428
  }
@@ -9381,6 +9498,7 @@ async function hostSyscall(req, ctx) {
9381
9498
  return done2;
9382
9499
  case "async-submit":
9383
9500
  if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
9501
+ if (req.ack) ctx.ops.ack(req.ack);
9384
9502
  return json3(ctx.ops.submit(req.req));
9385
9503
  case "async-wait":
9386
9504
  return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
@@ -9393,6 +9511,8 @@ async function hostSyscall(req, ctx) {
9393
9511
  case "async-cancel":
9394
9512
  ctx.ops.cancel(req.id);
9395
9513
  return done2;
9514
+ case "async-settle":
9515
+ return json3(await ctx.ops.settle(req.ids));
9396
9516
  case "async-close":
9397
9517
  ctx.ops.close();
9398
9518
  return done2;
@@ -9900,8 +10020,8 @@ var KernelTty = class {
9900
10020
  get canonical() {
9901
10021
  return (this.termios.c_lflag & ICANON) !== 0;
9902
10022
  }
9903
- cc(slot) {
9904
- return this.termios.c_cc[slot] ?? 0;
10023
+ cc(slot2) {
10024
+ return this.termios.c_cc[slot2] ?? 0;
9905
10025
  }
9906
10026
  key(input) {
9907
10027
  const { c_iflag, c_lflag } = this.termios;
@@ -10256,6 +10376,84 @@ function ptySyscall(req, ctx) {
10256
10376
  }
10257
10377
  }
10258
10378
 
10379
+ // src/kernel/run-child.ts
10380
+ var RUN_CAPTURE_CAP = 64 * 1024 * 1024;
10381
+ var RUN_MAX_BUFFER = 1024 * 1024;
10382
+ function capLimit(maxBuffer) {
10383
+ const asked = maxBuffer === void 0 ? RUN_MAX_BUFFER : maxBuffer;
10384
+ return Math.max(0, Math.min(asked, RUN_CAPTURE_CAP));
10385
+ }
10386
+ function slot(how, n, extra) {
10387
+ if (how === "inherit") return { fd: n };
10388
+ if (how === "ignore") return { none: true };
10389
+ return extra;
10390
+ }
10391
+ function frame2(outcome, out, err) {
10392
+ const head = new TextEncoder().encode(JSON.stringify(outcome));
10393
+ const bytes = new Uint8Array(4 + head.length + out.length + err.length);
10394
+ new DataView(bytes.buffer).setUint32(0, head.length, true);
10395
+ bytes.set(head, 4);
10396
+ bytes.set(out, 4 + head.length);
10397
+ bytes.set(err, 4 + head.length + out.length);
10398
+ return bytes;
10399
+ }
10400
+ async function reaped(deps, pid) {
10401
+ for (; ; ) {
10402
+ try {
10403
+ return (await deps.children.wait(pid, false, deps.interrupt()))[1];
10404
+ } catch (e) {
10405
+ if (!(e instanceof KernelError && e.code === "EINTR") || deps.dying()) throw e;
10406
+ }
10407
+ }
10408
+ }
10409
+ async function runChild(req, deps) {
10410
+ const max = capLimit(req.maxBuffer);
10411
+ let failure2;
10412
+ let pid = 0;
10413
+ const kill = () => void Promise.allSettled([deps.kill(pid, req.killSignal)]);
10414
+ const stop = (code) => {
10415
+ if (failure2 !== void 0) return;
10416
+ failure2 = code;
10417
+ if (pid !== 0) kill();
10418
+ };
10419
+ const capture = () => ({ capture: true, max, overflow: () => stop("ENOBUFS") });
10420
+ const stdio = [
10421
+ slot(req.stdin, 0, { input: req.input ?? new Uint8Array(0) }),
10422
+ slot(req.stdout, 1, capture()),
10423
+ slot(req.stderr, 2, capture())
10424
+ ];
10425
+ try {
10426
+ pid = await deps.children.spawn(
10427
+ { file: req.file, argv: req.argv, env: req.env, cwd: req.cwd },
10428
+ stdio
10429
+ );
10430
+ } catch (e) {
10431
+ if (!(e instanceof SpawnError)) throw e;
10432
+ const outcome2 = { pid: 0, status: null, error: { code: e.code }, out: 0, err: 0 };
10433
+ return frame2(outcome2, new Uint8Array(0), new Uint8Array(0));
10434
+ }
10435
+ if (failure2 !== void 0) kill();
10436
+ const timer = req.timeoutMs !== void 0 && req.timeoutMs > 0 ? setTimeout(() => stop("ETIMEDOUT"), req.timeoutMs) : void 0;
10437
+ let status;
10438
+ try {
10439
+ status = await reaped(deps, pid);
10440
+ } finally {
10441
+ clearTimeout(timer);
10442
+ }
10443
+ const out = deps.children.captured(pid, 1);
10444
+ const err = deps.children.captured(pid, 2);
10445
+ const termsig = status & 127;
10446
+ const outcome = {
10447
+ pid,
10448
+ status: termsig ? null : status >> 8 & 255,
10449
+ ...termsig ? { signal: termsig } : {},
10450
+ ...failure2 !== void 0 ? { error: { code: failure2 } } : {},
10451
+ out: out.length,
10452
+ err: err.length
10453
+ };
10454
+ return frame2(outcome, out, err);
10455
+ }
10456
+
10259
10457
  // src/kernel/select.ts
10260
10458
  function ready(fds, read, write) {
10261
10459
  const hangup = /* @__PURE__ */ new Set();
@@ -10632,7 +10830,7 @@ var VfsNodes = class {
10632
10830
  return run2;
10633
10831
  }
10634
10832
  async entryKey(path) {
10635
- const parent = parentOf3(path);
10833
+ const parent = parentOf4(path);
10636
10834
  const dir = await this.real(parent, true, () => realDir(this.fs, parent));
10637
10835
  return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
10638
10836
  }
@@ -10834,7 +11032,7 @@ function errCode(err) {
10834
11032
  return err?.code;
10835
11033
  }
10836
11034
  async function targetDir(fs, path) {
10837
- return parentOf3(await realTarget(fs, path).catch(() => path));
11035
+ return parentOf4(await realTarget(fs, path).catch(() => path));
10838
11036
  }
10839
11037
  async function refuseReadonly(fs, path, flags, opts = {}) {
10840
11038
  const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -11052,6 +11250,8 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11052
11250
  "proc-spawn",
11053
11251
  "proc-wait",
11054
11252
  "proc-captured",
11253
+ "proc-run",
11254
+ "proc-exit",
11055
11255
  "proc-fork",
11056
11256
  "proc-kill",
11057
11257
  "thread-kill",
@@ -11172,6 +11372,14 @@ var WasmProcess = class {
11172
11372
  else this.adoptExec(pid);
11173
11373
  return pid;
11174
11374
  }
11375
+ run(req) {
11376
+ return runChild(req, {
11377
+ children: this.children,
11378
+ kill: (pid, sig) => this.options.kill?.(pid, sig),
11379
+ interrupt: () => this.interrupt.signal,
11380
+ dying: () => this.dying
11381
+ });
11382
+ }
11175
11383
  adoptExec(pid) {
11176
11384
  this.execChild = pid;
11177
11385
  this.children.watch(pid, (state, sig) => state === "stopped" ? this.stop(sig) : this.cont());
@@ -11720,6 +11928,12 @@ var WasmProcess = class {
11720
11928
  return { ok: true, kind: "json", json: await this.spawn(req) };
11721
11929
  case "proc-wait":
11722
11930
  return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
11931
+ case "proc-run":
11932
+ return { ok: true, kind: "bytes", bytes: await this.run(req) };
11933
+ case "proc-exit":
11934
+ if (!this.options.exitNow) throw new KernelError("ENOSYS");
11935
+ this.options.exitNow(req.status & 255);
11936
+ return DYING;
11723
11937
  case "proc-identity":
11724
11938
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
11725
11939
  case "proc-exec": {
@@ -12126,6 +12340,7 @@ function processOptions(opts, header3, hooks, created3) {
12126
12340
  hasPending: () => Atomics.load(header3, SAB_I_SIGNALS) !== 0 || Atomics.load(header3, SAB_I_TIMERS) !== 0,
12127
12341
  raise: hooks.raise,
12128
12342
  threadKill: hooks.threadKill,
12343
+ ...hooks.exitNow ? { exitNow: hooks.exitNow } : {},
12129
12344
  ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header3, SAB_I_SIGNALS) } : {}
12130
12345
  };
12131
12346
  }
@@ -12142,7 +12357,8 @@ function spawnWasmProcess(opts) {
12142
12357
  header3,
12143
12358
  {
12144
12359
  raise: (sig) => signal(sig),
12145
- threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig)
12360
+ threadKill: (tid, sig) => killThread(threadSignals.get(tid), process.signalMasks().caught, sig),
12361
+ ...opts.program.abi === "js" ? { exitNow: (code) => exitNow(code) } : {}
12146
12362
  },
12147
12363
  created3
12148
12364
  )
@@ -12183,6 +12399,9 @@ function spawnWasmProcess(opts) {
12183
12399
  () => settle3(code)
12184
12400
  );
12185
12401
  };
12402
+ const exitNow = (code) => {
12403
+ queueMicrotask(() => finish(...dying.status(code)));
12404
+ };
12186
12405
  const handle = (data) => {
12187
12406
  if (data?.type === WASM_PROCESS_EXIT) {
12188
12407
  finish(...dying.status(data.code));
@@ -12918,15 +13137,15 @@ function granted(handle) {
12918
13137
  );
12919
13138
  }
12920
13139
  function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
12921
- const slot = mediumSlot(
13140
+ const slot2 = mediumSlot(
12922
13141
  async (err, handle) => denied(err) && !await granted(handle),
12923
13142
  onLost
12924
13143
  );
12925
13144
  return {
12926
- handlers: slot.handlers,
12927
- present: slot.present,
12928
- insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
12929
- eject: slot.eject
13145
+ handlers: slot2.handlers,
13146
+ present: slot2.present,
13147
+ insert: (handle) => slot2.insert(handle, kernelFsHandlers(open(handle))),
13148
+ eject: slot2.eject
12930
13149
  };
12931
13150
  }
12932
13151
 
@@ -13710,7 +13929,7 @@ function grantConnect(hook, fetch2, liveness = {}) {
13710
13929
  async function openHostfs(spec, connect, timing = {}) {
13711
13930
  const readonly2 = spec.options?.ro !== void 0;
13712
13931
  const { port1, port2 } = new MessageChannel();
13713
- const slot = mediumSlot(
13932
+ const slot2 = mediumSlot(
13714
13933
  async (err) => unreachable(err),
13715
13934
  () => watching.kick()
13716
13935
  );
@@ -13723,7 +13942,7 @@ async function openHostfs(spec, connect, timing = {}) {
13723
13942
  });
13724
13943
  const served = serveFilesystem(
13725
13944
  port2,
13726
- { ...slot.handlers, mount: ranged2 },
13945
+ { ...slot2.handlers, mount: ranged2 },
13727
13946
  {
13728
13947
  symlinks: true,
13729
13948
  chmod: true,
@@ -13737,12 +13956,12 @@ async function openHostfs(spec, connect, timing = {}) {
13737
13956
  connect: () => connect(spec.source, { readonly: readonly2 }),
13738
13957
  up(client) {
13739
13958
  active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
13740
- slot.insert(client, handlers);
13959
+ slot2.insert(client, handlers);
13741
13960
  served.invalidate(true);
13742
13961
  },
13743
13962
  event: (event) => served.invalidate(invalidation(event)),
13744
13963
  down() {
13745
- slot.eject();
13964
+ slot2.eject();
13746
13965
  served.invalidate(true);
13747
13966
  },
13748
13967
  ...timing
@@ -13752,7 +13971,7 @@ async function openHostfs(spec, connect, timing = {}) {
13752
13971
  await watching.ready;
13753
13972
  return {
13754
13973
  port: port1,
13755
- present: slot.present,
13974
+ present: slot2.present,
13756
13975
  dispose() {
13757
13976
  stop.abort();
13758
13977
  port1.close();
@@ -15025,7 +15244,7 @@ var Launcher = class {
15025
15244
  async realLayoutPath(path) {
15026
15245
  if (!await this.base.exists(path)) return void 0;
15027
15246
  if (path === "/") return path;
15028
- return joinName(await realDir(this.base, parentOf3(path)), baseName(path));
15247
+ return joinName(await realDir(this.base, parentOf4(path)), baseName(path));
15029
15248
  }
15030
15249
  async provisionPnpmHome() {
15031
15250
  await this.base.mkdir(this.pnpmHome, { recursive: true }).catch(() => void 0);
@@ -15046,16 +15265,16 @@ var Launcher = class {
15046
15265
  }
15047
15266
  async insert(target, handle, source) {
15048
15267
  const at = normalizePath(target);
15049
- const slot = this.removable.get(at);
15050
- if (!slot) throw fsError("EINVAL", `${at} is not a removable mount`);
15051
- if (source !== void 0 && source !== `fsa:${slot.id}`) {
15268
+ const slot2 = this.removable.get(at);
15269
+ if (!slot2) throw fsError("EINVAL", `${at} is not a removable mount`);
15270
+ if (source !== void 0 && source !== `fsa:${slot2.id}`) {
15052
15271
  throw fsError("EINVAL", `${at} is no longer the drive ${source}`);
15053
15272
  }
15054
15273
  if (!await granted(handle)) throw fsError("EACCES", `${at}: no permission for this folder`);
15055
- this.inserted.set(slot.id, handle);
15056
- await this.media.put(slot.id, handle);
15057
- slot.medium.insert(handle);
15058
- slot.served.invalidate(true);
15274
+ this.inserted.set(slot2.id, handle);
15275
+ await this.media.put(slot2.id, handle);
15276
+ slot2.medium.insert(handle);
15277
+ slot2.served.invalidate(true);
15059
15278
  void this.unlinked.sweep(at);
15060
15279
  }
15061
15280
  umount(target, detach = false) {