@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/dist/index.js CHANGED
@@ -282,6 +282,204 @@ async function loopbackFetch(dial, input, options) {
282
282
  }
283
283
  }
284
284
 
285
+ // src/kernel/net/loopback-names.ts
286
+ var HOST_LOOPBACK = "host.slicc.internal";
287
+ var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
288
+ var PAGE_LOOPBACK = "kernel.localhost";
289
+ var PAGE_LOOPBACK_URL = `http://{port}.${PAGE_LOOPBACK}`;
290
+
291
+ // src/kernel/net/routes.ts
292
+ var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
293
+ function ipv4Bytes(text) {
294
+ const parts = text.split(".");
295
+ if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
296
+ const bytes = parts.map(Number);
297
+ return bytes.every((b) => b <= 255) ? bytes : void 0;
298
+ }
299
+ function ipv6Groups(text) {
300
+ if (!text.includes(":")) return void 0;
301
+ let canonical;
302
+ try {
303
+ canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
304
+ } catch {
305
+ return void 0;
306
+ }
307
+ const [head, tail] = canonical.split("::");
308
+ const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
309
+ const front = part(head);
310
+ const back = part(tail);
311
+ return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
312
+ }
313
+ function toBits(values, width) {
314
+ return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
315
+ }
316
+ function addressBits(host) {
317
+ const v4 = ipv4Bytes(host);
318
+ if (v4) return { v6: false, bits: toBits(v4, 8) };
319
+ const v6 = ipv6Groups(host);
320
+ return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
321
+ }
322
+ function parsePrefix(text) {
323
+ const parts = text.trim().split("/");
324
+ if (parts.length > 2) return void 0;
325
+ const [address = "", size] = parts;
326
+ const parsed = addressBits(address);
327
+ if (!parsed) return void 0;
328
+ const max = parsed.v6 ? 128 : 32;
329
+ const length = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
330
+ if (length < 0 || length > max) return void 0;
331
+ return { ...parsed, length };
332
+ }
333
+ function within(prefix, addr) {
334
+ if (prefix.v6 !== addr.v6) return false;
335
+ const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
336
+ return addr.bits >> shift === prefix.bits >> shift;
337
+ }
338
+ var RESERVED = [
339
+ "0.0.0.0/8",
340
+ "127.0.0.0/8",
341
+ "169.254.0.0/16",
342
+ "224.0.0.0/4",
343
+ "240.0.0.0/4",
344
+ "::/96",
345
+ "::ffff:0:0/96",
346
+ "fe80::/10",
347
+ "ff00::/8"
348
+ ].map((p) => parsePrefix(p));
349
+ var Routes = class {
350
+ table = [];
351
+ exit = false;
352
+ ipv6;
353
+ constructor(ipv6 = false) {
354
+ this.ipv6 = ipv6;
355
+ }
356
+ set(table) {
357
+ const parsed = (table.prefixes ?? []).map(parsePrefix);
358
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed[i]);
359
+ if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
360
+ this.table = parsed;
361
+ this.exit = table.exit === true;
362
+ }
363
+ classify(host) {
364
+ const name = host.toLowerCase().replace(/\.$/, "");
365
+ if (name === "localhost" || name.endsWith(".localhost") || name === "0.0.0.0") {
366
+ return "loopback";
367
+ }
368
+ if (name === HOST_LOOPBACK || name === HOST_ADDRESS) return "host";
369
+ const addr = addressBits(name);
370
+ if (!addr) return "unreachable";
371
+ if (addr.v6 && addr.bits === 1n) return "loopback";
372
+ if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
373
+ if (RESERVED.some((p) => within(p, addr))) return "reserved";
374
+ if (addr.v6 && !this.ipv6) return "unreachable";
375
+ if (this.exit || this.table.some((p) => within(p, addr))) return "uplink";
376
+ return "unreachable";
377
+ }
378
+ };
379
+
380
+ // src/kernel/net/uplink.ts
381
+ function answerOf(result) {
382
+ const answer = Array.isArray(result) ? { addresses: result } : result;
383
+ const addresses = Array.isArray(answer?.addresses) ? answer.addresses.filter((a) => typeof a === "string") : [];
384
+ return {
385
+ addresses,
386
+ ...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
387
+ };
388
+ }
389
+ function failure(uid, e) {
390
+ const code = e?.code;
391
+ return {
392
+ uplink: "error",
393
+ uid,
394
+ message: e instanceof Error ? e.message : String(e),
395
+ ...typeof code === "string" ? { code } : {}
396
+ };
397
+ }
398
+ var NEVER = new Routes(true);
399
+ function serveUplink(peer, uplink) {
400
+ const conns = /* @__PURE__ */ new Map();
401
+ const pending = /* @__PURE__ */ new Map();
402
+ let nextCid = 0;
403
+ let closed = false;
404
+ const run = (uid, work) => {
405
+ const abort = new AbortController();
406
+ pending.set(uid, abort);
407
+ void work(abort.signal).then(
408
+ (reply) => reply && peer.postMessage(reply, transferOf(reply)),
409
+ (e) => peer.postMessage(failure(uid, e))
410
+ ).finally(() => pending.delete(uid));
411
+ };
412
+ const conn = (cid) => {
413
+ const found = conns.get(cid);
414
+ if (!found) throw Object.assign(new Error("no such uplink connection"), { code: "EBADF" });
415
+ return found;
416
+ };
417
+ const dial = async (call, signal) => {
418
+ const kind = NEVER.classify(call.host);
419
+ if (kind === "loopback" || kind === "host" || kind === "reserved") {
420
+ throw Object.assign(new Error(`the uplink does not carry ${call.host}`), {
421
+ code: "ENETUNREACH"
422
+ });
423
+ }
424
+ const { host, port } = call;
425
+ const opened = await uplink.dial({ network: "tcp", host, port, signal });
426
+ if (signal.aborted || closed) {
427
+ opened.close();
428
+ return void 0;
429
+ }
430
+ const cid = ++nextCid;
431
+ conns.set(cid, opened);
432
+ const { localAddr, remoteAddr } = opened;
433
+ return { uplink: "dialled", uid: call.uid, cid, localAddr, remoteAddr };
434
+ };
435
+ const drop = (cid) => {
436
+ const found = conns.get(cid);
437
+ conns.delete(cid);
438
+ found?.close();
439
+ };
440
+ return {
441
+ answer(call) {
442
+ switch (call.uplink) {
443
+ case "resolve":
444
+ return run(call.uid, async (signal) => ({
445
+ uplink: "resolved",
446
+ uid: call.uid,
447
+ ...answerOf(await uplink.resolve(call.name, call.family, signal))
448
+ }));
449
+ case "dial":
450
+ return run(call.uid, (signal) => dial(call, signal));
451
+ case "cancel":
452
+ return pending.get(call.uid)?.abort();
453
+ case "read":
454
+ return run(call.uid, async () => ({
455
+ uplink: "data",
456
+ uid: call.uid,
457
+ bytes: (await conn(call.cid).read())?.slice() ?? null
458
+ }));
459
+ case "write":
460
+ return run(call.uid, async () => ({
461
+ uplink: "written",
462
+ uid: call.uid,
463
+ n: await conn(call.cid).write(call.bytes)
464
+ }));
465
+ case "close-write":
466
+ return conns.get(call.cid)?.closeWrite();
467
+ default:
468
+ return drop(call.cid);
469
+ }
470
+ },
471
+ close() {
472
+ closed = true;
473
+ for (const abort of pending.values()) abort.abort();
474
+ for (const cid of [...conns.keys()]) drop(cid);
475
+ }
476
+ };
477
+ }
478
+ function transferOf(reply) {
479
+ if (reply.uplink !== "data" || !reply.bytes) return void 0;
480
+ return [reply.bytes.buffer];
481
+ }
482
+
285
483
  // src/body-idle.ts
286
484
  var BODY_IDLE_MS = 5 * 60 * 1e3;
287
485
  function idleFailure(ms) {
@@ -561,7 +759,7 @@ var RemoteTransport = class {
561
759
  };
562
760
 
563
761
  // src/client/protocol.ts
564
- var PROTOCOL = [1, 5];
762
+ var PROTOCOL = [1, 7];
565
763
  function locksOf(scope = globalThis) {
566
764
  const locks = scope.navigator?.locks;
567
765
  return typeof locks?.request === "function" ? locks : void 0;
@@ -657,8 +855,8 @@ var Channel = class {
657
855
  }
658
856
  request(call, handlers = {}) {
659
857
  const id = ++this.nextId;
660
- const { failure: failure2 } = this;
661
- if (failure2) return { id, done: Promise.reject(failure2) };
858
+ const { failure: failure3 } = this;
859
+ if (failure3) return { id, done: Promise.reject(failure3) };
662
860
  const done = new Promise((resolve, reject) => {
663
861
  this.pending.set(id, { resolve, reject, ...handlers });
664
862
  this.port.postMessage({ ...call, id });
@@ -938,7 +1136,7 @@ var MAX_HEAD = 1024 * 1024;
938
1136
  var MAX_REQUEST_BODY2 = 64 * 1024 * 1024;
939
1137
  var PERMISSIONS = ["loopback-network", "local-network-access"];
940
1138
  var LOOPBACK_PAGE = /^(127\.\d+\.\d+\.\d+|localhost|\[::1\])$/;
941
- var failure = (message, status = 502) => Object.assign(new Error(`local proxy: ${message}`), { status });
1139
+ var failure2 = (message, status = 502) => Object.assign(new Error(`local proxy: ${message}`), { status });
942
1140
  function encodeHead(url, method, headers) {
943
1141
  return JSON.stringify({ url, method, headers }).replace(
944
1142
  /[\u007f-￿]/g,
@@ -961,7 +1159,7 @@ async function refusalMessage(response) {
961
1159
  return text || response.statusText || `answered ${response.status}`;
962
1160
  }
963
1161
  async function refusal(response) {
964
- return failure(await refusalMessage(response), response.ok ? 502 : response.status);
1162
+ return failure2(await refusalMessage(response), response.ok ? 502 : response.status);
965
1163
  }
966
1164
  async function permission(permissions) {
967
1165
  if (!permissions) return "unknown";
@@ -990,19 +1188,19 @@ async function readHead(reader, idleMs) {
990
1188
  for (; ; ) {
991
1189
  if (buffer.byteLength >= 4) {
992
1190
  const length = new DataView(buffer.buffer, buffer.byteOffset, 4).getUint32(0);
993
- if (length > MAX_HEAD) throw failure(`response head of ${length} bytes`);
1191
+ if (length > MAX_HEAD) throw failure2(`response head of ${length} bytes`);
994
1192
  if (buffer.byteLength >= 4 + length) {
995
1193
  let head;
996
1194
  try {
997
1195
  head = JSON.parse(new TextDecoder().decode(buffer.subarray(4, 4 + length)));
998
1196
  } catch {
999
1197
  }
1000
- if (!isHead(head)) throw failure("malformed response head");
1198
+ if (!isHead(head)) throw failure2("malformed response head");
1001
1199
  return { head, rest: buffer.subarray(4 + length) };
1002
1200
  }
1003
1201
  }
1004
1202
  const next = await readWithin(reader, idleMs);
1005
- if (next.done) throw failure("closed before the response head");
1203
+ if (next.done) throw failure2("closed before the response head");
1006
1204
  buffer = concat2(buffer, next.value);
1007
1205
  }
1008
1206
  }
@@ -1082,7 +1280,7 @@ function localProxyTransport(options) {
1082
1280
  });
1083
1281
  } catch (e) {
1084
1282
  if (request.signal.aborted) throw e;
1085
- throw failure(`${endpoint.origin} is unreachable or refuses this origin`);
1283
+ throw failure2(`${endpoint.origin} is unreachable or refuses this origin`);
1086
1284
  }
1087
1285
  const type = response.headers.get("content-type") ?? "";
1088
1286
  if (response.status !== 200 || !type.startsWith(RAW_CONTENT_TYPE) || !response.body) {
@@ -1136,6 +1334,11 @@ function dialer(call) {
1136
1334
  };
1137
1335
  return { dial, reset };
1138
1336
  }
1337
+ function uplinkInit(uplink) {
1338
+ if (!uplink) return {};
1339
+ const traits = { tcp: true, udp: false, ipv6: uplink.traits.ipv6 === true };
1340
+ return { uplink: { traits, ...uplink.routes ? { routes: uplink.routes } : {} } };
1341
+ }
1139
1342
  function initCall(options, transport) {
1140
1343
  return {
1141
1344
  op: "init",
@@ -1146,6 +1349,7 @@ function initCall(options, transport) {
1146
1349
  ...options.media !== void 0 ? { media: options.media } : {},
1147
1350
  ...options.ca !== void 0 ? { ca: options.ca } : {},
1148
1351
  ...transport ? { transport: transport.traits } : {},
1352
+ ...uplinkInit(options.network?.uplink),
1149
1353
  ...options.hostfs ? { hostfs: true } : {},
1150
1354
  ...options.cdp ? { cdp: true } : {},
1151
1355
  ...options.processMounts !== void 0 ? {
@@ -1177,19 +1381,22 @@ async function createKernel(options = {}) {
1177
1381
  const worker = new Worker(url, { type: "module", name: "slicc-kernel" });
1178
1382
  const pending = /* @__PURE__ */ new Map();
1179
1383
  let nextId = 0;
1180
- let failure2;
1384
+ let failure3;
1181
1385
  const dials = dialer((req) => call(req));
1182
1386
  const cdp = cdpBridge(options.cdp);
1183
1387
  const fail = (error) => {
1184
1388
  dials.reset();
1185
1389
  cdp.close();
1186
- failure2 = error;
1390
+ failure3 = error;
1187
1391
  bridge?.close();
1392
+ uplinks?.close();
1188
1393
  for (const call2 of pending.values()) call2.reject(error);
1189
1394
  pending.clear();
1190
1395
  };
1191
1396
  const transport = options.network?.transport;
1192
1397
  const bridge = transport ? serveTransport(worker, transport) : void 0;
1398
+ const uplink = options.network?.uplink;
1399
+ const uplinks = uplink ? serveUplink(worker, uplink) : void 0;
1193
1400
  async function chosen(handle) {
1194
1401
  if (handle) {
1195
1402
  const state = await handle.requestPermission?.({ mode: "readwrite" });
@@ -1228,6 +1435,7 @@ async function createKernel(options = {}) {
1228
1435
  data
1229
1436
  }) => {
1230
1437
  if ("net" in data) return bridge?.answer(data);
1438
+ if ("uplink" in data) return uplinks?.answer(data);
1231
1439
  if ("cdpOpen" in data) return cdp.open(data);
1232
1440
  if ("mountPolicy" in data) return void policy(data.mountPolicy);
1233
1441
  if ("medium" in data) return pendingMedium(data.medium);
@@ -1247,7 +1455,7 @@ async function createKernel(options = {}) {
1247
1455
  });
1248
1456
  function request(req, handlers = {}) {
1249
1457
  const id = ++nextId;
1250
- if (failure2) return { id, done: Promise.reject(failure2) };
1458
+ if (failure3) return { id, done: Promise.reject(failure3) };
1251
1459
  const done = new Promise((resolve, reject) => {
1252
1460
  pending.set(id, { resolve, reject, ...handlers });
1253
1461
  worker.postMessage({ ...req, id });
@@ -1309,6 +1517,7 @@ async function createKernel(options = {}) {
1309
1517
  },
1310
1518
  openTerminal,
1311
1519
  connect: async () => await call({ op: "connect" }),
1520
+ setRoutes: async (routes) => void await call({ op: "routes", routes }),
1312
1521
  dial,
1313
1522
  loopbackFetch: (input, fetchOptions) => loopbackFetch(dial, input, fetchOptions),
1314
1523
  mount: async (spec) => await call({ op: "mount", spec }),