@ai-ecoverse/slicc-kernel 1.27.0 → 1.28.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.
@@ -883,7 +883,7 @@ function portCdpHook(ask, gone) {
883
883
  }
884
884
 
885
885
  // src/client/protocol.ts
886
- var PROTOCOL = [1, 6];
886
+ var PROTOCOL = [1, 7];
887
887
  var FS_METHODS = [
888
888
  "readFile",
889
889
  "writeFile",
@@ -1253,6 +1253,170 @@ var FdTable = class _FdTable {
1253
1253
  }
1254
1254
  };
1255
1255
 
1256
+ // src/process/wasi/wasi-abi.ts
1257
+ var E = {
1258
+ SUCCESS: 0,
1259
+ "2BIG": 1,
1260
+ ACCES: 2,
1261
+ ADDRINUSE: 3,
1262
+ ADDRNOTAVAIL: 4,
1263
+ AFNOSUPPORT: 5,
1264
+ AGAIN: 6,
1265
+ ALREADY: 7,
1266
+ BADF: 8,
1267
+ BADMSG: 9,
1268
+ BUSY: 10,
1269
+ CANCELED: 11,
1270
+ CHILD: 12,
1271
+ CONNABORTED: 13,
1272
+ CONNREFUSED: 14,
1273
+ CONNRESET: 15,
1274
+ DEADLK: 16,
1275
+ DESTADDRREQ: 17,
1276
+ DOM: 18,
1277
+ DQUOT: 19,
1278
+ EXIST: 20,
1279
+ FAULT: 21,
1280
+ FBIG: 22,
1281
+ HOSTUNREACH: 23,
1282
+ IDRM: 24,
1283
+ ILSEQ: 25,
1284
+ INPROGRESS: 26,
1285
+ INTR: 27,
1286
+ INVAL: 28,
1287
+ IO: 29,
1288
+ ISCONN: 30,
1289
+ ISDIR: 31,
1290
+ LOOP: 32,
1291
+ MFILE: 33,
1292
+ MLINK: 34,
1293
+ MSGSIZE: 35,
1294
+ MULTIHOP: 36,
1295
+ NAMETOOLONG: 37,
1296
+ NETDOWN: 38,
1297
+ NETRESET: 39,
1298
+ NETUNREACH: 40,
1299
+ NFILE: 41,
1300
+ NOBUFS: 42,
1301
+ NODEV: 43,
1302
+ NOENT: 44,
1303
+ NOEXEC: 45,
1304
+ NOLCK: 46,
1305
+ NOLINK: 47,
1306
+ NOMEM: 48,
1307
+ NOMSG: 49,
1308
+ NOPROTOOPT: 50,
1309
+ NOSPC: 51,
1310
+ NOSYS: 52,
1311
+ NOTCONN: 53,
1312
+ NOTDIR: 54,
1313
+ NOTEMPTY: 55,
1314
+ NOTRECOVERABLE: 56,
1315
+ NOTSOCK: 57,
1316
+ NOTSUP: 58,
1317
+ NOTTY: 59,
1318
+ NXIO: 60,
1319
+ OVERFLOW: 61,
1320
+ OWNERDEAD: 62,
1321
+ PERM: 63,
1322
+ PIPE: 64,
1323
+ PROTO: 65,
1324
+ PROTONOSUPPORT: 66,
1325
+ PROTOTYPE: 67,
1326
+ RANGE: 68,
1327
+ ROFS: 69,
1328
+ SPIPE: 70,
1329
+ SRCH: 71,
1330
+ STALE: 72,
1331
+ TIMEDOUT: 73,
1332
+ TXTBSY: 74,
1333
+ XDEV: 75,
1334
+ NOTCAPABLE: 76
1335
+ };
1336
+ var RIGHTS = {
1337
+ FD_DATASYNC: 1n << 0n,
1338
+ FD_READ: 1n << 1n,
1339
+ FD_SEEK: 1n << 2n,
1340
+ FD_FDSTAT_SET_FLAGS: 1n << 3n,
1341
+ FD_SYNC: 1n << 4n,
1342
+ FD_TELL: 1n << 5n,
1343
+ FD_WRITE: 1n << 6n,
1344
+ ALL: (1n << 30n) - 1n
1345
+ };
1346
+ var WASI_SIGNAL_TO_POSIX = Object.fromEntries(
1347
+ Array.from({ length: 30 }, (_, i) => [i + 1, i + 1 < 16 ? i + 1 : i + 2])
1348
+ );
1349
+
1350
+ // src/process/wasi-errno.ts
1351
+ var WASI_ERRNO = {
1352
+ ...Object.fromEntries(Object.entries(E).map(([name2, errno]) => [`E${name2}`, errno])),
1353
+ EWOULDBLOCK: E.AGAIN,
1354
+ ENOTSUP: 138,
1355
+ EOPNOTSUPP: 138,
1356
+ ENOMEDIUM: 148
1357
+ };
1358
+ function wasiErrno(code) {
1359
+ return WASI_ERRNO[code] ?? E.IO;
1360
+ }
1361
+
1362
+ // src/kernel/net/uplink-socket.ts
1363
+ var CHUNK = 64 * 1024;
1364
+ function hostPort(text) {
1365
+ const match = /^\[?([^\]]*?)\]?:(\d{1,5})$/.exec(text);
1366
+ const port = Number(match?.[2]);
1367
+ const host = match?.[1];
1368
+ if (!host || port > 65535) return void 0;
1369
+ return { host, port };
1370
+ }
1371
+ async function inbound(conn, rx) {
1372
+ for (; ; ) {
1373
+ while (!rx.writeReady) await rx.changed();
1374
+ if (rx.readersGone) return;
1375
+ const bytes = await conn.read();
1376
+ if (!bytes) return;
1377
+ await rx.write(bytes);
1378
+ }
1379
+ }
1380
+ async function outbound(conn, tx) {
1381
+ for (; ; ) {
1382
+ const bytes = await tx.read(CHUNK);
1383
+ if (bytes.length === 0) {
1384
+ conn.closeWrite();
1385
+ return;
1386
+ }
1387
+ for (let at = 0; at < bytes.length; ) {
1388
+ const n = await conn.write(bytes.subarray(at));
1389
+ if (!(n > 0)) throw new Error("the uplink wrote nothing");
1390
+ at += n;
1391
+ }
1392
+ }
1393
+ }
1394
+ async function released(rx, tx) {
1395
+ while (!(rx.readersGone && tx.writersGone)) await Promise.race([rx.changed(), tx.changed()]);
1396
+ }
1397
+ function uplinkLink(conn, onReset) {
1398
+ const rx = new KernelPipe();
1399
+ const tx = new KernelPipe();
1400
+ for (const pipe of [rx, tx]) {
1401
+ pipe.openRead();
1402
+ pipe.openWrite();
1403
+ }
1404
+ let open = true;
1405
+ const close = () => {
1406
+ if (!open) return;
1407
+ open = false;
1408
+ conn.close();
1409
+ };
1410
+ void inbound(conn, rx).catch(() => {
1411
+ onReset();
1412
+ close();
1413
+ tx.closeRead();
1414
+ }).finally(() => rx.closeWrite());
1415
+ void outbound(conn, tx).catch(close).finally(() => tx.closeRead());
1416
+ void released(rx, tx).then(close);
1417
+ return { rx, tx };
1418
+ }
1419
+
1256
1420
  // src/kernel/socket.ts
1257
1421
  var SOMAXCONN = 4096;
1258
1422
  var EPHEMERAL_FIRST = 32768;
@@ -1270,6 +1434,21 @@ var AF_INET = 2;
1270
1434
  var SHUT_RD = 0;
1271
1435
  var SHUT_WR = 1;
1272
1436
  var SHUT_RDWR = 2;
1437
+ var DIAL_TIMEOUT_MS = 3e4;
1438
+ var DIAL_ERRORS = [
1439
+ "ECONNREFUSED",
1440
+ "ENETUNREACH",
1441
+ "EHOSTUNREACH",
1442
+ "ETIMEDOUT",
1443
+ "ECONNRESET"
1444
+ ];
1445
+ function dialError(e, timedOut, closed) {
1446
+ if (timedOut) return new KernelError("ETIMEDOUT");
1447
+ if (closed) return new KernelError("ECONNABORTED");
1448
+ const code = e?.code;
1449
+ const known = DIAL_ERRORS.find((errno) => errno === code);
1450
+ return new KernelError(known ?? "EHOSTUNREACH");
1451
+ }
1273
1452
  function isLoopback(host) {
1274
1453
  return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
1275
1454
  }
@@ -1318,6 +1497,9 @@ var KernelSocket = class _KernelSocket {
1318
1497
  net;
1319
1498
  domain;
1320
1499
  state = "open";
1500
+ dialing;
1501
+ failed;
1502
+ reset = false;
1321
1503
  local;
1322
1504
  peer;
1323
1505
  link;
@@ -1325,7 +1507,7 @@ var KernelSocket = class _KernelSocket {
1325
1507
  writeShut = false;
1326
1508
  queue = [];
1327
1509
  backlog = 0;
1328
- bound = false;
1510
+ boundAt;
1329
1511
  changes = new Changes();
1330
1512
  options = /* @__PURE__ */ new Map();
1331
1513
  constructor(net, domain) {
@@ -1347,12 +1529,16 @@ var KernelSocket = class _KernelSocket {
1347
1529
  async read(max, signal) {
1348
1530
  const { rx } = this.connection();
1349
1531
  if (this.readShut || max <= 0) return new Uint8Array(0);
1350
- return pipeCall(() => rx.read(max, signal));
1532
+ const bytes = await pipeCall(() => rx.read(max, signal));
1533
+ if (bytes.length === 0 && this.reset) throw new KernelError("ECONNRESET");
1534
+ return bytes;
1351
1535
  }
1352
1536
  async peek(max, signal) {
1353
1537
  const { rx } = this.connection();
1354
1538
  if (this.readShut || max <= 0) return new Uint8Array(0);
1355
- return pipeCall(() => rx.peek(max, signal));
1539
+ const bytes = await pipeCall(() => rx.peek(max, signal));
1540
+ if (bytes.length === 0 && this.reset) throw new KernelError("ECONNRESET");
1541
+ return bytes;
1356
1542
  }
1357
1543
  async write(bytes, signal) {
1358
1544
  const { tx } = this.connection();
@@ -1363,6 +1549,8 @@ var KernelSocket = class _KernelSocket {
1363
1549
  if (this.state === "listening") {
1364
1550
  return { readable: this.queue.length > 0, writable: false, hangup: false };
1365
1551
  }
1552
+ if (this.state === "connecting") return { readable: false, writable: false, hangup: false };
1553
+ if (this.failed) return { readable: true, writable: true, hangup: true };
1366
1554
  if (this.state !== "connected" || !this.link) {
1367
1555
  return { readable: false, writable: false, hangup: true };
1368
1556
  }
@@ -1384,7 +1572,7 @@ var KernelSocket = class _KernelSocket {
1384
1572
  if (this.state !== "open" || this.local) throw new KernelError("EINVAL");
1385
1573
  if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
1386
1574
  this.local = this.net.bind(this, addr);
1387
- this.bound = true;
1575
+ this.boundAt = this.local;
1388
1576
  }
1389
1577
  listen(backlog) {
1390
1578
  if (this.state === "connected") throw new KernelError("EISCONN");
@@ -1411,15 +1599,19 @@ var KernelSocket = class _KernelSocket {
1411
1599
  this.changes.wake();
1412
1600
  return true;
1413
1601
  }
1414
- connect(addr) {
1602
+ connect(addr, viaUplink = true) {
1415
1603
  if (this.state === "connected") throw new KernelError("EISCONN");
1604
+ if (this.state === "connecting") throw new KernelError("EALREADY");
1416
1605
  if (this.state !== "open") throw new KernelError("EINVAL");
1417
1606
  if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
1607
+ this.failed = void 0;
1608
+ const route = viaUplink && addr.family === "inet" ? this.net.uplinkFor(addr.host) : void 0;
1609
+ if (route) return this.dial(addr, route);
1418
1610
  const target = this.net.target(addr);
1419
1611
  if (!this.local) {
1420
1612
  if (addr.family === "inet") {
1421
1613
  this.local = this.net.bind(this, { family: "inet", host: "127.0.0.1", port: 0 });
1422
- this.bound = true;
1614
+ this.boundAt = this.local;
1423
1615
  } else this.local = { family: "unix", path: "" };
1424
1616
  }
1425
1617
  const server = new _KernelSocket(this.net, this.domain);
@@ -1432,6 +1624,52 @@ var KernelSocket = class _KernelSocket {
1432
1624
  server.close();
1433
1625
  throw new KernelError("ECONNREFUSED");
1434
1626
  }
1627
+ return void 0;
1628
+ }
1629
+ dial(addr, route) {
1630
+ this.state = "connecting";
1631
+ const abort = new AbortController();
1632
+ this.dialing = abort;
1633
+ let timedOut = false;
1634
+ const timer = setTimeout(() => {
1635
+ timedOut = true;
1636
+ abort.abort(new KernelError("ETIMEDOUT"));
1637
+ }, route.timeoutMs ?? DIAL_TIMEOUT_MS);
1638
+ const abandoned = new Promise((_, reject) => {
1639
+ abort.signal.addEventListener("abort", () => reject(abort.signal.reason), { once: true });
1640
+ });
1641
+ const { host, port } = addr;
1642
+ const dialled = new Promise(
1643
+ (resolve) => resolve(route.uplink.dial({ network: "tcp", host, port, signal: abort.signal }))
1644
+ );
1645
+ dialled.then(
1646
+ (conn) => abort.signal.aborted && conn.close(),
1647
+ () => void 0
1648
+ );
1649
+ return Promise.race([dialled, abandoned]).then(
1650
+ (conn) => {
1651
+ if (abort.signal.aborted) throw dialError(abort.signal.reason, timedOut, true);
1652
+ this.local = { family: "inet", host: "0.0.0.0", port: 0, ...hostPort(conn.localAddr) };
1653
+ this.peer = { ...addr, ...hostPort(conn.remoteAddr) };
1654
+ this.dialing = void 0;
1655
+ this.attach(
1656
+ uplinkLink(conn, () => {
1657
+ this.reset = true;
1658
+ this.changes.wake();
1659
+ })
1660
+ );
1661
+ },
1662
+ (e) => {
1663
+ const error = dialError(e, timedOut, abort.signal.aborted);
1664
+ if (this.dialing === abort) {
1665
+ this.dialing = void 0;
1666
+ this.failed = error.code;
1667
+ this.state = "open";
1668
+ this.changes.wake();
1669
+ }
1670
+ throw error;
1671
+ }
1672
+ ).finally(() => clearTimeout(timer));
1435
1673
  }
1436
1674
  unlink() {
1437
1675
  const link2 = this.link;
@@ -1461,8 +1699,11 @@ var KernelSocket = class _KernelSocket {
1461
1699
  switch (name2) {
1462
1700
  case SO_TYPE:
1463
1701
  return SOCK_STREAM;
1464
- case SO_ERROR:
1465
- return 0;
1702
+ case SO_ERROR: {
1703
+ const failed = this.failed;
1704
+ this.failed = void 0;
1705
+ return failed ? wasiErrno(failed) : 0;
1706
+ }
1466
1707
  case SO_ACCEPTCONN:
1467
1708
  return this.state === "listening" ? 1 : 0;
1468
1709
  case SO_DOMAIN:
@@ -1480,6 +1721,8 @@ var KernelSocket = class _KernelSocket {
1480
1721
  close() {
1481
1722
  const was = this.state;
1482
1723
  this.state = "closed";
1724
+ this.dialing?.abort(new KernelError("ECONNABORTED"));
1725
+ this.dialing = void 0;
1483
1726
  if (was === "listening") {
1484
1727
  for (const pending of this.queue.splice(0)) pending.close();
1485
1728
  }
@@ -1489,8 +1732,8 @@ var KernelSocket = class _KernelSocket {
1489
1732
  this.readShut = true;
1490
1733
  this.writeShut = true;
1491
1734
  }
1492
- if (this.bound && this.local) this.net.release(this.local, this);
1493
- this.bound = false;
1735
+ if (this.boundAt) this.net.release(this.boundAt, this);
1736
+ this.boundAt = void 0;
1494
1737
  this.changes.wake();
1495
1738
  }
1496
1739
  static pair(net, domain) {
@@ -1513,6 +1756,15 @@ var LoopbackNet = class {
1513
1756
  activators = /* @__PURE__ */ new Map();
1514
1757
  internal = /* @__PURE__ */ new Set();
1515
1758
  nextEphemeral = EPHEMERAL_FIRST;
1759
+ route;
1760
+ useUplink(route) {
1761
+ this.route = route;
1762
+ }
1763
+ uplinkFor(host) {
1764
+ const route = this.route;
1765
+ if (!route) return void 0;
1766
+ return route.routes.classify(host) === "uplink" ? route : void 0;
1767
+ }
1516
1768
  kernelOnly(port, reserve = false) {
1517
1769
  if (reserve) this.internal.add(port);
1518
1770
  return this.internal.has(port);
@@ -1531,7 +1783,7 @@ var LoopbackNet = class {
1531
1783
  }
1532
1784
  connect(addr) {
1533
1785
  const socket = this.socket(addr.family);
1534
- socket.connect(addr);
1786
+ void socket.connect(addr, false);
1535
1787
  return socket;
1536
1788
  }
1537
1789
  bind(socket, addr) {
@@ -1572,7 +1824,7 @@ var LoopbackNet = class {
1572
1824
  };
1573
1825
 
1574
1826
  // src/kernel/dial.ts
1575
- var CHUNK = 64 * 1024;
1827
+ var CHUNK2 = 64 * 1024;
1576
1828
  var LOOPBACK_NAMES = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
1577
1829
  function dialSocket(net, port, host = "127.0.0.1") {
1578
1830
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new KernelError("EINVAL");
@@ -1637,7 +1889,7 @@ function serveSocket(socket, port, onClose) {
1637
1889
  for (; ; ) {
1638
1890
  await turn();
1639
1891
  if (closed) return;
1640
- const chunk2 = await socket.read(CHUNK);
1892
+ const chunk2 = await socket.read(CHUNK2);
1641
1893
  if (chunk2.length === 0) break;
1642
1894
  credit--;
1643
1895
  send(chunk2, [chunk2.buffer]);
@@ -3570,6 +3822,95 @@ var RemoteTransport = class {
3570
3822
  }
3571
3823
  };
3572
3824
 
3825
+ // src/kernel/net/routes.ts
3826
+ var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
3827
+ function ipv4Bytes(text) {
3828
+ const parts = text.split(".");
3829
+ if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
3830
+ const bytes = parts.map(Number);
3831
+ return bytes.every((b) => b <= 255) ? bytes : void 0;
3832
+ }
3833
+ function ipv6Groups(text) {
3834
+ if (!text.includes(":")) return void 0;
3835
+ let canonical;
3836
+ try {
3837
+ canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
3838
+ } catch {
3839
+ return void 0;
3840
+ }
3841
+ const [head, tail] = canonical.split("::");
3842
+ const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
3843
+ const front = part(head);
3844
+ const back = part(tail);
3845
+ return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
3846
+ }
3847
+ function toBits(values, width) {
3848
+ return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
3849
+ }
3850
+ function addressBits(host) {
3851
+ const v4 = ipv4Bytes(host);
3852
+ if (v4) return { v6: false, bits: toBits(v4, 8) };
3853
+ const v6 = ipv6Groups(host);
3854
+ return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
3855
+ }
3856
+ function parsePrefix(text) {
3857
+ const parts = text.trim().split("/");
3858
+ if (parts.length > 2) return void 0;
3859
+ const [address = "", size] = parts;
3860
+ const parsed = addressBits(address);
3861
+ if (!parsed) return void 0;
3862
+ const max = parsed.v6 ? 128 : 32;
3863
+ const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
3864
+ if (length2 < 0 || length2 > max) return void 0;
3865
+ return { ...parsed, length: length2 };
3866
+ }
3867
+ function within2(prefix, addr) {
3868
+ if (prefix.v6 !== addr.v6) return false;
3869
+ const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
3870
+ return addr.bits >> shift === prefix.bits >> shift;
3871
+ }
3872
+ var RESERVED = [
3873
+ "0.0.0.0/8",
3874
+ "127.0.0.0/8",
3875
+ "169.254.0.0/16",
3876
+ "224.0.0.0/4",
3877
+ "240.0.0.0/4",
3878
+ "::/96",
3879
+ "::ffff:0:0/96",
3880
+ "fe80::/10",
3881
+ "ff00::/8"
3882
+ ].map((p) => parsePrefix(p));
3883
+ var Routes = class {
3884
+ table = [];
3885
+ exit = false;
3886
+ ipv6;
3887
+ constructor(ipv62 = false) {
3888
+ this.ipv6 = ipv62;
3889
+ }
3890
+ set(table) {
3891
+ const parsed = (table.prefixes ?? []).map(parsePrefix);
3892
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed[i]);
3893
+ if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
3894
+ this.table = parsed;
3895
+ this.exit = table.exit === true;
3896
+ }
3897
+ classify(host) {
3898
+ const name2 = host.toLowerCase().replace(/\.$/, "");
3899
+ if (name2 === "localhost" || name2.endsWith(".localhost") || name2 === "0.0.0.0") {
3900
+ return "loopback";
3901
+ }
3902
+ if (name2 === HOST_LOOPBACK || name2 === HOST_ADDRESS) return "host";
3903
+ const addr = addressBits(name2);
3904
+ if (!addr) return "unreachable";
3905
+ if (addr.v6 && addr.bits === 1n) return "loopback";
3906
+ if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
3907
+ if (RESERVED.some((p) => within2(p, addr))) return "reserved";
3908
+ if (addr.v6 && !this.ipv6) return "unreachable";
3909
+ if (this.exit || this.table.some((p) => within2(p, addr))) return "uplink";
3910
+ return "unreachable";
3911
+ }
3912
+ };
3913
+
3573
3914
  // src/kernel/net/uplink.ts
3574
3915
  function answerOf(result) {
3575
3916
  const answer = Array.isArray(result) ? { addresses: result } : result;
@@ -3579,6 +3920,7 @@ function answerOf(result) {
3579
3920
  ...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
3580
3921
  };
3581
3922
  }
3923
+ var NEVER = new Routes(true);
3582
3924
  var RemoteUplink = class {
3583
3925
  traits;
3584
3926
  routes;
@@ -3596,23 +3938,60 @@ var RemoteUplink = class {
3596
3938
  this.waiting.delete(reply.uid);
3597
3939
  if (reply.uplink === "error") {
3598
3940
  waiter.reject(Object.assign(new Error(reply.message), { code: reply.code }));
3599
- } else waiter.resolve(answerOf(reply));
3941
+ } else waiter.resolve(reply);
3600
3942
  }
3601
- resolve(name2, family, signal) {
3602
- const uid = ++this.nextId;
3943
+ ask(call, signal, transfer) {
3603
3944
  const waiter = Promise.withResolvers();
3604
- this.waiting.set(uid, waiter);
3945
+ this.waiting.set(call.uid, waiter);
3605
3946
  const abort = () => {
3606
- this.waiting.delete(uid);
3607
- this.port.postMessage({ uplink: "cancel", uid });
3947
+ this.waiting.delete(call.uid);
3948
+ this.port.postMessage({ uplink: "cancel", uid: call.uid });
3608
3949
  waiter.reject(signal?.reason);
3609
3950
  };
3610
3951
  signal?.addEventListener("abort", abort, { once: true });
3611
3952
  const settled = () => signal?.removeEventListener("abort", abort);
3612
3953
  waiter.promise.then(settled, settled);
3613
- this.port.postMessage({ uplink: "resolve", uid, name: name2, family });
3954
+ this.port.postMessage(call, transfer);
3614
3955
  return waiter.promise;
3615
3956
  }
3957
+ async resolve(name2, family, signal) {
3958
+ const reply = await this.ask(
3959
+ { uplink: "resolve", uid: ++this.nextId, name: name2, family },
3960
+ signal
3961
+ );
3962
+ return answerOf(reply);
3963
+ }
3964
+ async dial(req) {
3965
+ req.signal.throwIfAborted();
3966
+ const { host, port } = req;
3967
+ const opened2 = await this.ask(
3968
+ { uplink: "dial", uid: ++this.nextId, host, port },
3969
+ req.signal
3970
+ );
3971
+ const { cid, localAddr, remoteAddr } = opened2;
3972
+ return {
3973
+ localAddr,
3974
+ remoteAddr,
3975
+ read: async () => {
3976
+ const got = await this.ask({
3977
+ uplink: "read",
3978
+ uid: ++this.nextId,
3979
+ cid
3980
+ });
3981
+ return got.bytes;
3982
+ },
3983
+ write: async (bytes) => {
3984
+ const copy = bytes.slice();
3985
+ const call = { uplink: "write", uid: ++this.nextId, cid, bytes: copy };
3986
+ const done4 = await this.ask(call, void 0, [
3987
+ copy.buffer
3988
+ ]);
3989
+ return done4.n;
3990
+ },
3991
+ closeWrite: () => this.port.postMessage({ uplink: "close-write", cid }),
3992
+ close: () => this.port.postMessage({ uplink: "close", cid })
3993
+ };
3994
+ }
3616
3995
  };
3617
3996
 
3618
3997
  // src/cdp/types.ts
@@ -4747,9 +5126,9 @@ function parseSize(text) {
4747
5126
  if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
4748
5127
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
4749
5128
  }
4750
- var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
5129
+ var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
4751
5130
  function heldUnder(held, target) {
4752
- for (const paths of held) for (const path of paths.keys()) if (within2(path, target)) return true;
5131
+ for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
4753
5132
  return false;
4754
5133
  }
4755
5134
  var HeldPaths = class extends Map {
@@ -4763,11 +5142,11 @@ var HeldPaths = class extends Map {
4763
5142
  }
4764
5143
  }
4765
5144
  revoke(prefix) {
4766
- for (const path of this.keys()) if (within2(path, prefix)) this.revoked.add(path);
5145
+ for (const path of this.keys()) if (within3(path, prefix)) this.revoked.add(path);
4767
5146
  }
4768
5147
  renamed(from, to) {
4769
5148
  for (const [path, count] of [...this]) {
4770
- if (!within2(path, from)) continue;
5149
+ if (!within3(path, from)) continue;
4771
5150
  const moved2 = to + path.slice(from.length);
4772
5151
  this.delete(path);
4773
5152
  this.set(moved2, (this.get(moved2) ?? 0) + count);
@@ -4865,7 +5244,7 @@ var MountTable = class {
4865
5244
  locate(path) {
4866
5245
  let best;
4867
5246
  for (const mount of this.mounts.values()) {
4868
- if (within2(path, mount.target) && (!best || mount.target.length > best.target.length))
5247
+ if (within3(path, mount.target) && (!best || mount.target.length > best.target.length))
4869
5248
  best = mount;
4870
5249
  }
4871
5250
  if (!best) return void 0;
@@ -4977,10 +5356,10 @@ var MountTable = class {
4977
5356
  this.deps.changed?.(...full);
4978
5357
  }
4979
5358
  forget(mount, path) {
4980
- for (const key of [...mount.attrs.keys()]) if (within2(key, path)) mount.attrs.delete(key);
5359
+ for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
4981
5360
  mount.lists.delete(path);
4982
5361
  mount.lists.delete(parentOf4(path));
4983
- for (const key of [...mount.lists.keys()]) if (within2(key, path)) mount.lists.delete(key);
5362
+ for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
4984
5363
  }
4985
5364
  async getattr(mount, path, rel2) {
4986
5365
  if (mount.conn.failure) throw mount.conn.failure;
@@ -5943,95 +6322,6 @@ var HttpHandles = class {
5943
6322
  }
5944
6323
  };
5945
6324
 
5946
- // src/kernel/net/routes.ts
5947
- var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
5948
- function ipv4Bytes(text) {
5949
- const parts = text.split(".");
5950
- if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
5951
- const bytes = parts.map(Number);
5952
- return bytes.every((b) => b <= 255) ? bytes : void 0;
5953
- }
5954
- function ipv6Groups(text) {
5955
- if (!text.includes(":")) return void 0;
5956
- let canonical;
5957
- try {
5958
- canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
5959
- } catch {
5960
- return void 0;
5961
- }
5962
- const [head, tail] = canonical.split("::");
5963
- const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
5964
- const front = part(head);
5965
- const back = part(tail);
5966
- return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
5967
- }
5968
- function toBits(values, width) {
5969
- return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
5970
- }
5971
- function addressBits(host) {
5972
- const v4 = ipv4Bytes(host);
5973
- if (v4) return { v6: false, bits: toBits(v4, 8) };
5974
- const v6 = ipv6Groups(host);
5975
- return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
5976
- }
5977
- function parsePrefix(text) {
5978
- const parts = text.trim().split("/");
5979
- if (parts.length > 2) return void 0;
5980
- const [address = "", size] = parts;
5981
- const parsed = addressBits(address);
5982
- if (!parsed) return void 0;
5983
- const max = parsed.v6 ? 128 : 32;
5984
- const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
5985
- if (length2 < 0 || length2 > max) return void 0;
5986
- return { ...parsed, length: length2 };
5987
- }
5988
- function within3(prefix, addr) {
5989
- if (prefix.v6 !== addr.v6) return false;
5990
- const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
5991
- return addr.bits >> shift === prefix.bits >> shift;
5992
- }
5993
- var RESERVED = [
5994
- "0.0.0.0/8",
5995
- "127.0.0.0/8",
5996
- "169.254.0.0/16",
5997
- "224.0.0.0/4",
5998
- "240.0.0.0/4",
5999
- "::/96",
6000
- "::ffff:0:0/96",
6001
- "fe80::/10",
6002
- "ff00::/8"
6003
- ].map((p) => parsePrefix(p));
6004
- var Routes = class {
6005
- table = [];
6006
- exit = false;
6007
- ipv6;
6008
- constructor(ipv62 = false) {
6009
- this.ipv6 = ipv62;
6010
- }
6011
- set(table) {
6012
- const parsed = (table.prefixes ?? []).map(parsePrefix);
6013
- const bad = (table.prefixes ?? []).find((_, i) => !parsed[i]);
6014
- if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
6015
- this.table = parsed;
6016
- this.exit = table.exit === true;
6017
- }
6018
- classify(host) {
6019
- const name2 = host.toLowerCase().replace(/\.$/, "");
6020
- if (name2 === "localhost" || name2.endsWith(".localhost") || name2 === "0.0.0.0") {
6021
- return "loopback";
6022
- }
6023
- if (name2 === HOST_LOOPBACK || name2 === HOST_ADDRESS) return "host";
6024
- const addr = addressBits(name2);
6025
- if (!addr) return "unreachable";
6026
- if (addr.v6 && addr.bits === 1n) return "loopback";
6027
- if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
6028
- if (RESERVED.some((p) => within3(p, addr))) return "reserved";
6029
- if (addr.v6 && !this.ipv6) return "unreachable";
6030
- if (this.exit || this.table.some((p) => within3(p, addr))) return "uplink";
6031
- return "unreachable";
6032
- }
6033
- };
6034
-
6035
6325
  // src/kernel/net/resolver.ts
6036
6326
  var RESOLVE_TTL_MAX = 60;
6037
6327
  var RESOLVE_TIMEOUT_MS = 1e4;
@@ -6676,10 +6966,15 @@ async function socketSyscall(req, proc) {
6676
6966
  return ok();
6677
6967
  case "sock-accept":
6678
6968
  return ok(await accept(proc, req.fd, req.nonblock));
6679
- case "sock-connect":
6680
- socketAt(fds, req.fd).connect(req.addr);
6681
- if (req.nonblock) throw new KernelError("EINPROGRESS");
6969
+ case "sock-connect": {
6970
+ const dialing = socketAt(fds, req.fd).connect(req.addr);
6971
+ if (req.nonblock) {
6972
+ dialing?.catch(() => void 0);
6973
+ throw new KernelError("EINPROGRESS");
6974
+ }
6975
+ if (dialing !== void 0) await interruptible(dialing, proc.blocking());
6682
6976
  return ok();
6977
+ }
6683
6978
  case "sock-shutdown":
6684
6979
  socketAt(fds, req.fd).shutdown(req.how);
6685
6980
  return ok();
@@ -6701,6 +6996,13 @@ async function socketSyscall(req, proc) {
6701
6996
  }
6702
6997
  }
6703
6998
  }
6999
+ function interruptible(dialing, signal) {
7000
+ return new Promise((resolve, reject) => {
7001
+ const abort = () => reject(new KernelError("EINTR"));
7002
+ signal.addEventListener("abort", abort, { once: true });
7003
+ dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
7004
+ });
7005
+ }
6704
7006
  function unnamed(domain) {
6705
7007
  return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
6706
7008
  }
@@ -9121,7 +9423,7 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
9121
9423
  }
9122
9424
 
9123
9425
  // src/mount/chunked.ts
9124
- var CHUNK2 = 1024 * 1024;
9426
+ var CHUNK3 = 1024 * 1024;
9125
9427
  var ChunkedBytes = class {
9126
9428
  chunks = [];
9127
9429
  size = 0;
@@ -9130,9 +9432,9 @@ var ChunkedBytes = class {
9130
9432
  if (end <= offset) return new Uint8Array(0);
9131
9433
  const out = new Uint8Array(end - offset);
9132
9434
  for (let at = offset; at < end; ) {
9133
- const index = Math.floor(at / CHUNK2);
9134
- const from = at - index * CHUNK2;
9135
- const n = Math.min(CHUNK2 - from, end - at);
9435
+ const index = Math.floor(at / CHUNK3);
9436
+ const from = at - index * CHUNK3;
9437
+ const n = Math.min(CHUNK3 - from, end - at);
9136
9438
  const chunk2 = this.chunks[index];
9137
9439
  if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
9138
9440
  at += n;
@@ -9143,10 +9445,10 @@ var ChunkedBytes = class {
9143
9445
  const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
9144
9446
  for (let done4 = 0; done4 < bytes.length; ) {
9145
9447
  const at = offset + done4;
9146
- const index = Math.floor(at / CHUNK2);
9147
- const from = at - index * CHUNK2;
9148
- const n = Math.min(CHUNK2 - from, bytes.length - done4);
9149
- if (whole && n === CHUNK2 && bytes.length === CHUNK2) this.chunks[index] = bytes;
9448
+ const index = Math.floor(at / CHUNK3);
9449
+ const from = at - index * CHUNK3;
9450
+ const n = Math.min(CHUNK3 - from, bytes.length - done4);
9451
+ if (whole && n === CHUNK3 && bytes.length === CHUNK3) this.chunks[index] = bytes;
9150
9452
  else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
9151
9453
  done4 += n;
9152
9454
  }
@@ -9154,10 +9456,10 @@ var ChunkedBytes = class {
9154
9456
  }
9155
9457
  truncate(size) {
9156
9458
  if (size < this.size) {
9157
- const keep = Math.ceil(size / CHUNK2);
9459
+ const keep = Math.ceil(size / CHUNK3);
9158
9460
  this.chunks.length = Math.min(this.chunks.length, keep);
9159
9461
  const tail = this.chunks[keep - 1];
9160
- const from = size - (keep - 1) * CHUNK2;
9462
+ const from = size - (keep - 1) * CHUNK3;
9161
9463
  if (tail && from < tail.length) tail.fill(0, from);
9162
9464
  }
9163
9465
  this.size = size;
@@ -9165,7 +9467,7 @@ var ChunkedBytes = class {
9165
9467
  room(index, need) {
9166
9468
  const chunk2 = this.chunks[index];
9167
9469
  if (chunk2 && chunk2.length >= need) return chunk2;
9168
- const grown = new Uint8Array(Math.min(CHUNK2, Math.max(need, (chunk2?.length ?? 0) * 2)));
9470
+ const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need, (chunk2?.length ?? 0) * 2)));
9169
9471
  if (chunk2) grown.set(chunk2);
9170
9472
  this.chunks[index] = grown;
9171
9473
  return grown;
@@ -9625,6 +9927,7 @@ var Launcher = class {
9625
9927
  this.routes = new Routes(options.uplink?.traits.ipv6 === true);
9626
9928
  if (options.uplink?.routes) this.routes.set(options.uplink.routes);
9627
9929
  this.resolver = new Resolver({ uplink: options.uplink, routes: this.routes });
9930
+ if (options.uplink) this.net.useUplink({ uplink: options.uplink, routes: this.routes });
9628
9931
  enableNetwork(this.net, {
9629
9932
  transport: this.transport,
9630
9933
  engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),