@ai-ecoverse/slicc-kernel 1.26.6 → 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.
- package/README.md +62 -4
- package/dist/driver-worker.js +1 -1
- package/dist/driver-worker.js.map +1 -1
- package/dist/index.js +221 -12
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +634 -51
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +1 -1
- package/dist/node-driver-worker.js.map +1 -1
- package/dist/node-process-worker.js +89 -20
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +547 -49
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +89 -20
- package/dist/process-worker.js.map +4 -4
- package/dist/testing.js +223 -0
- package/dist/testing.js.map +7 -0
- package/package.json +4 -3
package/dist/kernel-worker.js
CHANGED
|
@@ -883,7 +883,7 @@ function portCdpHook(ask, gone) {
|
|
|
883
883
|
}
|
|
884
884
|
|
|
885
885
|
// src/client/protocol.ts
|
|
886
|
-
var PROTOCOL = [1,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
1493
|
-
this.
|
|
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
|
|
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(
|
|
1892
|
+
const chunk2 = await socket.read(CHUNK2);
|
|
1641
1893
|
if (chunk2.length === 0) break;
|
|
1642
1894
|
credit--;
|
|
1643
1895
|
send(chunk2, [chunk2.buffer]);
|
|
@@ -2086,6 +2338,7 @@ function serveClient(port, host) {
|
|
|
2086
2338
|
|
|
2087
2339
|
// src/kernel/net/loopback-names.ts
|
|
2088
2340
|
var HOST_LOOPBACK = "host.slicc.internal";
|
|
2341
|
+
var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
|
|
2089
2342
|
var PAGE_LOOPBACK = "kernel.localhost";
|
|
2090
2343
|
var PAGE_LOOPBACK_URL = `http://{port}.${PAGE_LOOPBACK}`;
|
|
2091
2344
|
function toHostLoopback(href) {
|
|
@@ -3569,6 +3822,178 @@ var RemoteTransport = class {
|
|
|
3569
3822
|
}
|
|
3570
3823
|
};
|
|
3571
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
|
+
|
|
3914
|
+
// src/kernel/net/uplink.ts
|
|
3915
|
+
function answerOf(result) {
|
|
3916
|
+
const answer = Array.isArray(result) ? { addresses: result } : result;
|
|
3917
|
+
const addresses = Array.isArray(answer?.addresses) ? answer.addresses.filter((a) => typeof a === "string") : [];
|
|
3918
|
+
return {
|
|
3919
|
+
addresses,
|
|
3920
|
+
...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3923
|
+
var NEVER = new Routes(true);
|
|
3924
|
+
var RemoteUplink = class {
|
|
3925
|
+
traits;
|
|
3926
|
+
routes;
|
|
3927
|
+
port;
|
|
3928
|
+
waiting = /* @__PURE__ */ new Map();
|
|
3929
|
+
nextId = 0;
|
|
3930
|
+
constructor(port, traits, routes) {
|
|
3931
|
+
this.port = port;
|
|
3932
|
+
this.traits = traits;
|
|
3933
|
+
this.routes = routes;
|
|
3934
|
+
}
|
|
3935
|
+
receive(reply) {
|
|
3936
|
+
const waiter = this.waiting.get(reply.uid);
|
|
3937
|
+
if (!waiter) return;
|
|
3938
|
+
this.waiting.delete(reply.uid);
|
|
3939
|
+
if (reply.uplink === "error") {
|
|
3940
|
+
waiter.reject(Object.assign(new Error(reply.message), { code: reply.code }));
|
|
3941
|
+
} else waiter.resolve(reply);
|
|
3942
|
+
}
|
|
3943
|
+
ask(call, signal, transfer) {
|
|
3944
|
+
const waiter = Promise.withResolvers();
|
|
3945
|
+
this.waiting.set(call.uid, waiter);
|
|
3946
|
+
const abort = () => {
|
|
3947
|
+
this.waiting.delete(call.uid);
|
|
3948
|
+
this.port.postMessage({ uplink: "cancel", uid: call.uid });
|
|
3949
|
+
waiter.reject(signal?.reason);
|
|
3950
|
+
};
|
|
3951
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3952
|
+
const settled = () => signal?.removeEventListener("abort", abort);
|
|
3953
|
+
waiter.promise.then(settled, settled);
|
|
3954
|
+
this.port.postMessage(call, transfer);
|
|
3955
|
+
return waiter.promise;
|
|
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
|
+
}
|
|
3995
|
+
};
|
|
3996
|
+
|
|
3572
3997
|
// src/cdp/types.ts
|
|
3573
3998
|
var CDP_PORT = 9222;
|
|
3574
3999
|
var NO_CDP_HOST = "slicc-kernel: no CDP host is attached (createKernel({ cdp }) or client.serveCdp)";
|
|
@@ -4701,9 +5126,9 @@ function parseSize(text) {
|
|
|
4701
5126
|
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
4702
5127
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
4703
5128
|
}
|
|
4704
|
-
var
|
|
5129
|
+
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
4705
5130
|
function heldUnder(held, target) {
|
|
4706
|
-
for (const paths of held) for (const path of paths.keys()) if (
|
|
5131
|
+
for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
|
|
4707
5132
|
return false;
|
|
4708
5133
|
}
|
|
4709
5134
|
var HeldPaths = class extends Map {
|
|
@@ -4717,11 +5142,11 @@ var HeldPaths = class extends Map {
|
|
|
4717
5142
|
}
|
|
4718
5143
|
}
|
|
4719
5144
|
revoke(prefix) {
|
|
4720
|
-
for (const path of this.keys()) if (
|
|
5145
|
+
for (const path of this.keys()) if (within3(path, prefix)) this.revoked.add(path);
|
|
4721
5146
|
}
|
|
4722
5147
|
renamed(from, to) {
|
|
4723
5148
|
for (const [path, count] of [...this]) {
|
|
4724
|
-
if (!
|
|
5149
|
+
if (!within3(path, from)) continue;
|
|
4725
5150
|
const moved2 = to + path.slice(from.length);
|
|
4726
5151
|
this.delete(path);
|
|
4727
5152
|
this.set(moved2, (this.get(moved2) ?? 0) + count);
|
|
@@ -4819,7 +5244,7 @@ var MountTable = class {
|
|
|
4819
5244
|
locate(path) {
|
|
4820
5245
|
let best;
|
|
4821
5246
|
for (const mount of this.mounts.values()) {
|
|
4822
|
-
if (
|
|
5247
|
+
if (within3(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
4823
5248
|
best = mount;
|
|
4824
5249
|
}
|
|
4825
5250
|
if (!best) return void 0;
|
|
@@ -4931,10 +5356,10 @@ var MountTable = class {
|
|
|
4931
5356
|
this.deps.changed?.(...full);
|
|
4932
5357
|
}
|
|
4933
5358
|
forget(mount, path) {
|
|
4934
|
-
for (const key of [...mount.attrs.keys()]) if (
|
|
5359
|
+
for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
|
|
4935
5360
|
mount.lists.delete(path);
|
|
4936
5361
|
mount.lists.delete(parentOf4(path));
|
|
4937
|
-
for (const key of [...mount.lists.keys()]) if (
|
|
5362
|
+
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
4938
5363
|
}
|
|
4939
5364
|
async getattr(mount, path, rel2) {
|
|
4940
5365
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
@@ -5897,6 +6322,115 @@ var HttpHandles = class {
|
|
|
5897
6322
|
}
|
|
5898
6323
|
};
|
|
5899
6324
|
|
|
6325
|
+
// src/kernel/net/resolver.ts
|
|
6326
|
+
var RESOLVE_TTL_MAX = 60;
|
|
6327
|
+
var RESOLVE_TIMEOUT_MS = 1e4;
|
|
6328
|
+
var RESOLVE_CACHE_MAX = 256;
|
|
6329
|
+
var LOOPBACK_NAMES2 = /* @__PURE__ */ new Set(["localhost", "localhost.localdomain", "ip6-localhost", "::1"]);
|
|
6330
|
+
function hostsEntry(name2) {
|
|
6331
|
+
if (LOOPBACK_NAMES2.has(name2) || name2.endsWith(".localhost")) return "127.0.0.1";
|
|
6332
|
+
if (name2 === HOST_LOOPBACK) return HOST_LOOPBACK_ADDRESS.join(".");
|
|
6333
|
+
return ipv4Bytes(name2) ? name2 : void 0;
|
|
6334
|
+
}
|
|
6335
|
+
var Resolver = class {
|
|
6336
|
+
uplink;
|
|
6337
|
+
routes;
|
|
6338
|
+
now;
|
|
6339
|
+
timeoutMs;
|
|
6340
|
+
log;
|
|
6341
|
+
cache = /* @__PURE__ */ new Map();
|
|
6342
|
+
pending = /* @__PURE__ */ new Map();
|
|
6343
|
+
constructor(options) {
|
|
6344
|
+
this.uplink = options.uplink;
|
|
6345
|
+
this.routes = options.routes;
|
|
6346
|
+
this.now = options.now ?? Date.now;
|
|
6347
|
+
this.timeoutMs = options.timeoutMs ?? RESOLVE_TIMEOUT_MS;
|
|
6348
|
+
this.log = options.log ?? ((message2) => console.warn(message2));
|
|
6349
|
+
}
|
|
6350
|
+
async resolve(name2, family, blocking) {
|
|
6351
|
+
const key = name2.toLowerCase().replace(/\.$/, "");
|
|
6352
|
+
const local = hostsEntry(key);
|
|
6353
|
+
if (ipv6Groups(key)) {
|
|
6354
|
+
const v4 = family !== 6 && local ? [local] : [];
|
|
6355
|
+
return family === 4 ? v4 : [...v4, key];
|
|
6356
|
+
}
|
|
6357
|
+
if (local) {
|
|
6358
|
+
const six = LOOPBACK_NAMES2.has(key) || key.endsWith(".localhost") ? ["::1"] : [];
|
|
6359
|
+
return family === 6 ? six : family === 4 ? [local] : [local, ...six];
|
|
6360
|
+
}
|
|
6361
|
+
if (!this.uplink || key === "") return [];
|
|
6362
|
+
const asked = this.uplink.traits.ipv6 ? family : 4;
|
|
6363
|
+
if (family === 6 && asked !== 6) return [];
|
|
6364
|
+
const id = `${asked}:${key}`;
|
|
6365
|
+
const hit = this.cache.get(id);
|
|
6366
|
+
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
6367
|
+
const signal = blocking?.();
|
|
6368
|
+
if (signal?.aborted) throw new KernelError("EINTR");
|
|
6369
|
+
let lookup = this.pending.get(id);
|
|
6370
|
+
if (!lookup) {
|
|
6371
|
+
const abort = new AbortController();
|
|
6372
|
+
lookup = { work: this.lookup(key, asked, id, abort.signal), abort, waiters: 0 };
|
|
6373
|
+
this.pending.set(id, lookup);
|
|
6374
|
+
void lookup.work.then(() => this.pending.delete(id));
|
|
6375
|
+
}
|
|
6376
|
+
return waitFor(lookup, signal);
|
|
6377
|
+
}
|
|
6378
|
+
async lookup(name2, family, id, abandoned) {
|
|
6379
|
+
const uplink = this.uplink;
|
|
6380
|
+
const timeout = AbortSignal.any([AbortSignal.timeout(this.timeoutMs), abandoned]);
|
|
6381
|
+
let answer;
|
|
6382
|
+
try {
|
|
6383
|
+
answer = answerOf(
|
|
6384
|
+
await Promise.race([
|
|
6385
|
+
uplink.resolve(name2, family, timeout),
|
|
6386
|
+
new Promise(
|
|
6387
|
+
(_, reject) => timeout.addEventListener("abort", () => reject(timeout.reason), { once: true })
|
|
6388
|
+
)
|
|
6389
|
+
])
|
|
6390
|
+
);
|
|
6391
|
+
} catch {
|
|
6392
|
+
return [];
|
|
6393
|
+
}
|
|
6394
|
+
const addresses = answer.addresses.filter((a) => this.usable(name2, a, family));
|
|
6395
|
+
if (addresses.length > 0) this.remember(id, addresses, answer.ttl);
|
|
6396
|
+
return addresses;
|
|
6397
|
+
}
|
|
6398
|
+
usable(name2, address, family) {
|
|
6399
|
+
const v4 = ipv4Bytes(address) !== void 0;
|
|
6400
|
+
if (!v4 && (family === 4 || !ipv6Groups(address))) return false;
|
|
6401
|
+
if (v4 && family === 6) return false;
|
|
6402
|
+
const kind = this.routes.classify(address);
|
|
6403
|
+
if (kind !== "loopback" && kind !== "host" && kind !== "reserved") return true;
|
|
6404
|
+
this.log(`slicc-kernel: dropped the uplink's answer ${address} for ${name2}: it is ${kind}`);
|
|
6405
|
+
return false;
|
|
6406
|
+
}
|
|
6407
|
+
remember(id, addresses, ttl) {
|
|
6408
|
+
const seconds = Math.min(ttl ?? RESOLVE_TTL_MAX, RESOLVE_TTL_MAX);
|
|
6409
|
+
if (seconds <= 0) return;
|
|
6410
|
+
this.cache.delete(id);
|
|
6411
|
+
this.cache.set(id, { addresses, expires: this.now() + seconds * 1e3 });
|
|
6412
|
+
if (this.cache.size > RESOLVE_CACHE_MAX) {
|
|
6413
|
+
this.cache.delete(this.cache.keys().next().value);
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
};
|
|
6417
|
+
function waitFor(lookup, signal) {
|
|
6418
|
+
lookup.waiters++;
|
|
6419
|
+
return new Promise((resolve, reject) => {
|
|
6420
|
+
const abort = () => {
|
|
6421
|
+
if (--lookup.waiters === 0) lookup.abort.abort(new KernelError("EINTR"));
|
|
6422
|
+
reject(new KernelError("EINTR"));
|
|
6423
|
+
};
|
|
6424
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
6425
|
+
void lookup.work.then((value) => {
|
|
6426
|
+
if (signal?.aborted) return;
|
|
6427
|
+
lookup.waiters--;
|
|
6428
|
+
signal?.removeEventListener("abort", abort);
|
|
6429
|
+
resolve(value);
|
|
6430
|
+
});
|
|
6431
|
+
});
|
|
6432
|
+
}
|
|
6433
|
+
|
|
5900
6434
|
// src/kernel/tty.ts
|
|
5901
6435
|
var ICRNL = 256;
|
|
5902
6436
|
var IUTF8 = 16384;
|
|
@@ -6400,7 +6934,8 @@ var SOCKET_OPS = [
|
|
|
6400
6934
|
"sock-shutdown",
|
|
6401
6935
|
"sock-name",
|
|
6402
6936
|
"sock-getopt",
|
|
6403
|
-
"sock-setopt"
|
|
6937
|
+
"sock-setopt",
|
|
6938
|
+
"sock-resolve"
|
|
6404
6939
|
];
|
|
6405
6940
|
function socketAt(fds, fd) {
|
|
6406
6941
|
const file = fds.get(fd).file;
|
|
@@ -6431,10 +6966,15 @@ async function socketSyscall(req, proc) {
|
|
|
6431
6966
|
return ok();
|
|
6432
6967
|
case "sock-accept":
|
|
6433
6968
|
return ok(await accept(proc, req.fd, req.nonblock));
|
|
6434
|
-
case "sock-connect":
|
|
6435
|
-
socketAt(fds, req.fd).connect(req.addr);
|
|
6436
|
-
if (req.nonblock)
|
|
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());
|
|
6437
6976
|
return ok();
|
|
6977
|
+
}
|
|
6438
6978
|
case "sock-shutdown":
|
|
6439
6979
|
socketAt(fds, req.fd).shutdown(req.how);
|
|
6440
6980
|
return ok();
|
|
@@ -6449,8 +6989,20 @@ async function socketSyscall(req, proc) {
|
|
|
6449
6989
|
case "sock-setopt":
|
|
6450
6990
|
socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
|
|
6451
6991
|
return ok();
|
|
6992
|
+
case "sock-resolve": {
|
|
6993
|
+
const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
|
|
6994
|
+
if (addresses.length === 0) throw new KernelError("ENOENT");
|
|
6995
|
+
return ok(addresses);
|
|
6996
|
+
}
|
|
6452
6997
|
}
|
|
6453
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
|
+
}
|
|
6454
7006
|
function unnamed(domain) {
|
|
6455
7007
|
return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
|
|
6456
7008
|
}
|
|
@@ -6700,7 +7252,7 @@ function isMissing(err) {
|
|
|
6700
7252
|
if (err?.code === "ENOENT") return true;
|
|
6701
7253
|
return err instanceof Error && err.message.startsWith("ENOENT");
|
|
6702
7254
|
}
|
|
6703
|
-
function
|
|
7255
|
+
function within4(path, root) {
|
|
6704
7256
|
return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
|
|
6705
7257
|
}
|
|
6706
7258
|
var VfsNode = class {
|
|
@@ -6908,7 +7460,7 @@ var VfsNodes = class {
|
|
|
6908
7460
|
return node;
|
|
6909
7461
|
}
|
|
6910
7462
|
holds(prefix) {
|
|
6911
|
-
for (const path of this.byPath.keys()) if (
|
|
7463
|
+
for (const path of this.byPath.keys()) if (within4(path, prefix)) return true;
|
|
6912
7464
|
return false;
|
|
6913
7465
|
}
|
|
6914
7466
|
closed(node) {
|
|
@@ -6916,24 +7468,24 @@ var VfsNodes = class {
|
|
|
6916
7468
|
if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
|
|
6917
7469
|
}
|
|
6918
7470
|
async flush(path) {
|
|
6919
|
-
for (const [p, node] of this.byPath) if (
|
|
7471
|
+
for (const [p, node] of this.byPath) if (within4(p, path)) await node.serial(() => node.flush());
|
|
6920
7472
|
}
|
|
6921
7473
|
async unlinking(path) {
|
|
6922
7474
|
for (const [held, node] of [...this.byPath]) {
|
|
6923
|
-
if (!
|
|
7475
|
+
if (!within4(held, path)) continue;
|
|
6924
7476
|
await node.serial(async () => void await Promise.all([node.keep(), node.limit()]));
|
|
6925
7477
|
}
|
|
6926
7478
|
}
|
|
6927
7479
|
revoke(prefix) {
|
|
6928
7480
|
for (const [path, node] of this.byPath) {
|
|
6929
|
-
if (!
|
|
7481
|
+
if (!within4(path, prefix)) continue;
|
|
6930
7482
|
node.revoke();
|
|
6931
7483
|
this.byPath.delete(path);
|
|
6932
7484
|
}
|
|
6933
7485
|
}
|
|
6934
7486
|
unlinked(path) {
|
|
6935
7487
|
for (const [held, node] of [...this.byPath]) {
|
|
6936
|
-
if (!
|
|
7488
|
+
if (!within4(held, path)) continue;
|
|
6937
7489
|
node.orphaned = true;
|
|
6938
7490
|
this.byPath.delete(held);
|
|
6939
7491
|
}
|
|
@@ -6942,8 +7494,8 @@ var VfsNodes = class {
|
|
|
6942
7494
|
if (from === to) return;
|
|
6943
7495
|
const moved2 = [];
|
|
6944
7496
|
for (const [p, node] of this.byPath) {
|
|
6945
|
-
if (
|
|
6946
|
-
else if (
|
|
7497
|
+
if (within4(p, from)) moved2.push(node);
|
|
7498
|
+
else if (within4(p, to)) {
|
|
6947
7499
|
node.orphaned = true;
|
|
6948
7500
|
this.byPath.delete(p);
|
|
6949
7501
|
}
|
|
@@ -7197,6 +7749,7 @@ function isWasmSyscall(req) {
|
|
|
7197
7749
|
return typeof op === "string" && SYSCALL_OPS.has(op);
|
|
7198
7750
|
}
|
|
7199
7751
|
var MAX_READ = 1024 * 1024;
|
|
7752
|
+
var HOSTS_ONLY = new Resolver({ routes: new Routes() });
|
|
7200
7753
|
var WasmProcess = class {
|
|
7201
7754
|
exited = false;
|
|
7202
7755
|
children;
|
|
@@ -7588,10 +8141,12 @@ var WasmProcess = class {
|
|
|
7588
8141
|
}
|
|
7589
8142
|
socketSyscall(req) {
|
|
7590
8143
|
this.net ??= this.options.net ?? new LoopbackNet();
|
|
8144
|
+
const resolver = this.options.resolver ?? HOSTS_ONLY;
|
|
7591
8145
|
return socketSyscall(req, {
|
|
7592
8146
|
fds: this.fds,
|
|
7593
8147
|
net: this.net,
|
|
7594
|
-
blocking: () => this.blockingSignal()
|
|
8148
|
+
blocking: () => this.blockingSignal(),
|
|
8149
|
+
resolve: (name2, family, blocking) => resolver.resolve(name2, family, blocking)
|
|
7595
8150
|
});
|
|
7596
8151
|
}
|
|
7597
8152
|
tty(fd) {
|
|
@@ -7786,6 +8341,7 @@ function spawnWasmProcess(opts) {
|
|
|
7786
8341
|
http: opts.http,
|
|
7787
8342
|
locks: opts.locks,
|
|
7788
8343
|
onReap: opts.onReap,
|
|
8344
|
+
resolver: opts.resolver,
|
|
7789
8345
|
onPending: (sig) => void Atomics.or(header2, SAB_I_SIGNALS, sigbit(sig)),
|
|
7790
8346
|
onTimer: (which) => void Atomics.or(header2, SAB_I_TIMERS, 1 << which),
|
|
7791
8347
|
hasPending: () => Atomics.load(header2, SAB_I_SIGNALS) !== 0 || Atomics.load(header2, SAB_I_TIMERS) !== 0,
|
|
@@ -8867,7 +9423,7 @@ async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
|
8867
9423
|
}
|
|
8868
9424
|
|
|
8869
9425
|
// src/mount/chunked.ts
|
|
8870
|
-
var
|
|
9426
|
+
var CHUNK3 = 1024 * 1024;
|
|
8871
9427
|
var ChunkedBytes = class {
|
|
8872
9428
|
chunks = [];
|
|
8873
9429
|
size = 0;
|
|
@@ -8876,9 +9432,9 @@ var ChunkedBytes = class {
|
|
|
8876
9432
|
if (end <= offset) return new Uint8Array(0);
|
|
8877
9433
|
const out = new Uint8Array(end - offset);
|
|
8878
9434
|
for (let at = offset; at < end; ) {
|
|
8879
|
-
const index = Math.floor(at /
|
|
8880
|
-
const from = at - index *
|
|
8881
|
-
const n = Math.min(
|
|
9435
|
+
const index = Math.floor(at / CHUNK3);
|
|
9436
|
+
const from = at - index * CHUNK3;
|
|
9437
|
+
const n = Math.min(CHUNK3 - from, end - at);
|
|
8882
9438
|
const chunk2 = this.chunks[index];
|
|
8883
9439
|
if (chunk2) out.set(chunk2.subarray(from, Math.min(from + n, chunk2.length)), at - offset);
|
|
8884
9440
|
at += n;
|
|
@@ -8889,10 +9445,10 @@ var ChunkedBytes = class {
|
|
|
8889
9445
|
const whole = owned && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength;
|
|
8890
9446
|
for (let done4 = 0; done4 < bytes.length; ) {
|
|
8891
9447
|
const at = offset + done4;
|
|
8892
|
-
const index = Math.floor(at /
|
|
8893
|
-
const from = at - index *
|
|
8894
|
-
const n = Math.min(
|
|
8895
|
-
if (whole && n ===
|
|
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;
|
|
8896
9452
|
else this.room(index, from + n).set(bytes.subarray(done4, done4 + n), from);
|
|
8897
9453
|
done4 += n;
|
|
8898
9454
|
}
|
|
@@ -8900,10 +9456,10 @@ var ChunkedBytes = class {
|
|
|
8900
9456
|
}
|
|
8901
9457
|
truncate(size) {
|
|
8902
9458
|
if (size < this.size) {
|
|
8903
|
-
const keep = Math.ceil(size /
|
|
9459
|
+
const keep = Math.ceil(size / CHUNK3);
|
|
8904
9460
|
this.chunks.length = Math.min(this.chunks.length, keep);
|
|
8905
9461
|
const tail = this.chunks[keep - 1];
|
|
8906
|
-
const from = size - (keep - 1) *
|
|
9462
|
+
const from = size - (keep - 1) * CHUNK3;
|
|
8907
9463
|
if (tail && from < tail.length) tail.fill(0, from);
|
|
8908
9464
|
}
|
|
8909
9465
|
this.size = size;
|
|
@@ -8911,7 +9467,7 @@ var ChunkedBytes = class {
|
|
|
8911
9467
|
room(index, need) {
|
|
8912
9468
|
const chunk2 = this.chunks[index];
|
|
8913
9469
|
if (chunk2 && chunk2.length >= need) return chunk2;
|
|
8914
|
-
const grown = new Uint8Array(Math.min(
|
|
9470
|
+
const grown = new Uint8Array(Math.min(CHUNK3, Math.max(need, (chunk2?.length ?? 0) * 2)));
|
|
8915
9471
|
if (chunk2) grown.set(chunk2);
|
|
8916
9472
|
this.chunks[index] = grown;
|
|
8917
9473
|
return grown;
|
|
@@ -9325,6 +9881,9 @@ var Launcher = class {
|
|
|
9325
9881
|
cdp;
|
|
9326
9882
|
ca;
|
|
9327
9883
|
transport;
|
|
9884
|
+
uplink;
|
|
9885
|
+
routes;
|
|
9886
|
+
resolver;
|
|
9328
9887
|
watchers = new FsWatchers();
|
|
9329
9888
|
pnpmHome;
|
|
9330
9889
|
mounts;
|
|
@@ -9364,6 +9923,11 @@ var Launcher = class {
|
|
|
9364
9923
|
this.env = { ...networkEnv(), SLICC_CDP_URL: this.cdp.url, ...options.env };
|
|
9365
9924
|
this.ca = kernelCa(options.caStore ?? memoryCaStore());
|
|
9366
9925
|
this.transport = options.transport ?? missingTransport();
|
|
9926
|
+
this.uplink = options.uplink;
|
|
9927
|
+
this.routes = new Routes(options.uplink?.traits.ipv6 === true);
|
|
9928
|
+
if (options.uplink?.routes) this.routes.set(options.uplink.routes);
|
|
9929
|
+
this.resolver = new Resolver({ uplink: options.uplink, routes: this.routes });
|
|
9930
|
+
if (options.uplink) this.net.useUplink({ uplink: options.uplink, routes: this.routes });
|
|
9367
9931
|
enableNetwork(this.net, {
|
|
9368
9932
|
transport: this.transport,
|
|
9369
9933
|
engine: kernelTlsEngine(packageTlsEngine(options.fs, this.modulesDir)),
|
|
@@ -9378,6 +9942,9 @@ var Launcher = class {
|
|
|
9378
9942
|
this.catalog = void 0;
|
|
9379
9943
|
});
|
|
9380
9944
|
}
|
|
9945
|
+
setRoutes(table) {
|
|
9946
|
+
this.routes.set(table);
|
|
9947
|
+
}
|
|
9381
9948
|
commands() {
|
|
9382
9949
|
this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
|
|
9383
9950
|
return this.catalog;
|
|
@@ -9557,6 +10124,7 @@ var Launcher = class {
|
|
|
9557
10124
|
net: this.net,
|
|
9558
10125
|
http: new HttpHandles(this.transport),
|
|
9559
10126
|
locks: this.locks,
|
|
10127
|
+
resolver: this.resolver,
|
|
9560
10128
|
onReap: (child) => this.reaped(child),
|
|
9561
10129
|
...req.fork ? { fork: req.fork } : {},
|
|
9562
10130
|
...this.shownIds(req)
|
|
@@ -9959,9 +10527,22 @@ function cdpOption(req, port) {
|
|
|
9959
10527
|
)
|
|
9960
10528
|
};
|
|
9961
10529
|
}
|
|
10530
|
+
function remotes(port, req) {
|
|
10531
|
+
return {
|
|
10532
|
+
...req.transport ? { transport: new RemoteTransport(port, req.transport) } : {},
|
|
10533
|
+
...req.uplink ? { uplink: new RemoteUplink(port, req.uplink.traits, req.uplink.routes) } : {}
|
|
10534
|
+
};
|
|
10535
|
+
}
|
|
10536
|
+
function answered(data, remote, uplink) {
|
|
10537
|
+
if (data.net !== void 0) remote?.receive(data);
|
|
10538
|
+
else if (typeof data.uplink === "string") uplink?.receive(data);
|
|
10539
|
+
else return false;
|
|
10540
|
+
return true;
|
|
10541
|
+
}
|
|
9962
10542
|
function serveKernel(port, deps) {
|
|
9963
10543
|
let launcher;
|
|
9964
10544
|
let remote;
|
|
10545
|
+
let uplink;
|
|
9965
10546
|
const terminals = /* @__PURE__ */ new Map();
|
|
9966
10547
|
const reply = (id, body, transfer) => port.postMessage({ id, ...body }, transfer);
|
|
9967
10548
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -10017,7 +10598,8 @@ function serveKernel(port, deps) {
|
|
|
10017
10598
|
launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(async (root) => {
|
|
10018
10599
|
const name2 = req.metadata ?? META_DB;
|
|
10019
10600
|
const { meta, media } = await stores(deps, name2, req.media);
|
|
10020
|
-
|
|
10601
|
+
const net = remotes(port, req);
|
|
10602
|
+
({ transport: remote, uplink } = net);
|
|
10021
10603
|
const ranged2 = await inOpfs(deps, root);
|
|
10022
10604
|
const fs = new OpfsFs(root, meta, dirsChannel(name2), { ranged: ranged2 });
|
|
10023
10605
|
await fs.reconcile();
|
|
@@ -10027,7 +10609,7 @@ function serveKernel(port, deps) {
|
|
|
10027
10609
|
...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
|
|
10028
10610
|
...req.modules ? { modules: req.modules } : {},
|
|
10029
10611
|
...req.env ? { env: req.env } : {},
|
|
10030
|
-
...
|
|
10612
|
+
...net,
|
|
10031
10613
|
...media ? { media } : {},
|
|
10032
10614
|
onMountPending: (medium) => port.postMessage({ medium }),
|
|
10033
10615
|
...req.hostfs ? { hostfs: askGrant } : {},
|
|
@@ -10052,6 +10634,10 @@ function serveKernel(port, deps) {
|
|
|
10052
10634
|
if (req.op === "init") return init(req);
|
|
10053
10635
|
if (req.op === "open-terminal") return terminal(req);
|
|
10054
10636
|
if (req.op === "connect") return connect();
|
|
10637
|
+
if (req.op === "routes") {
|
|
10638
|
+
(await ready2()).setRoutes(req.routes);
|
|
10639
|
+
return true;
|
|
10640
|
+
}
|
|
10055
10641
|
if (req.op === "dial") return dial(req);
|
|
10056
10642
|
if (req.op === "mount" || req.op === "umount" || req.op === "mounts" || req.op === "insert")
|
|
10057
10643
|
return mountOp(req);
|
|
@@ -10100,10 +10686,7 @@ function serveKernel(port, deps) {
|
|
|
10100
10686
|
policies.delete(answer.id);
|
|
10101
10687
|
return;
|
|
10102
10688
|
}
|
|
10103
|
-
if (event.data
|
|
10104
|
-
remote?.receive(event.data);
|
|
10105
|
-
return;
|
|
10106
|
-
}
|
|
10689
|
+
if (answered(event.data, remote, uplink)) return;
|
|
10107
10690
|
const req = event.data;
|
|
10108
10691
|
handle(req).then(
|
|
10109
10692
|
(result) => reply(req.id, { result }, result instanceof MessagePort ? [result] : void 0),
|