@ai-ecoverse/slicc-kernel 1.27.0 → 1.29.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 +41 -6
- package/dist/driver-worker.js +1 -1
- package/dist/driver-worker.js.map +1 -1
- package/dist/index.js +171 -16
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +442 -133
- 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 +7 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +313 -41
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +7 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/testing.js +196 -0
- package/dist/testing.js.map +3 -3
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -282,6 +282,101 @@ 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
|
+
|
|
285
380
|
// src/kernel/net/uplink.ts
|
|
286
381
|
function answerOf(result) {
|
|
287
382
|
const answer = Array.isArray(result) ? { addresses: result } : result;
|
|
@@ -300,30 +395,90 @@ function failure(uid, e) {
|
|
|
300
395
|
...typeof code === "string" ? { code } : {}
|
|
301
396
|
};
|
|
302
397
|
}
|
|
398
|
+
var NEVER = new Routes(true);
|
|
303
399
|
function serveUplink(peer, uplink) {
|
|
400
|
+
const conns = /* @__PURE__ */ new Map();
|
|
304
401
|
const pending = /* @__PURE__ */ new Map();
|
|
305
|
-
|
|
306
|
-
|
|
402
|
+
let nextCid = 0;
|
|
403
|
+
let closed = false;
|
|
404
|
+
const run = (uid, work) => {
|
|
307
405
|
const abort = new AbortController();
|
|
308
|
-
pending.set(
|
|
309
|
-
void (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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();
|
|
319
439
|
};
|
|
320
440
|
return {
|
|
321
|
-
answer
|
|
322
|
-
|
|
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;
|
|
323
473
|
for (const abort of pending.values()) abort.abort();
|
|
474
|
+
for (const cid of [...conns.keys()]) drop(cid);
|
|
324
475
|
}
|
|
325
476
|
};
|
|
326
477
|
}
|
|
478
|
+
function transferOf(reply) {
|
|
479
|
+
if (reply.uplink !== "data" || !reply.bytes) return void 0;
|
|
480
|
+
return [reply.bytes.buffer];
|
|
481
|
+
}
|
|
327
482
|
|
|
328
483
|
// src/body-idle.ts
|
|
329
484
|
var BODY_IDLE_MS = 5 * 60 * 1e3;
|
|
@@ -604,7 +759,7 @@ var RemoteTransport = class {
|
|
|
604
759
|
};
|
|
605
760
|
|
|
606
761
|
// src/client/protocol.ts
|
|
607
|
-
var PROTOCOL = [1,
|
|
762
|
+
var PROTOCOL = [1, 7];
|
|
608
763
|
function locksOf(scope = globalThis) {
|
|
609
764
|
const locks = scope.navigator?.locks;
|
|
610
765
|
return typeof locks?.request === "function" ? locks : void 0;
|