@ai-ecoverse/slicc-kernel 1.171.0 → 1.172.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
@@ -598,6 +598,54 @@ function isHostname(name) {
598
598
  return name.split(".").every((l) => LABEL.test(l));
599
599
  }
600
600
 
601
+ // src/body-idle.ts
602
+ var BODY_IDLE_MS = 5 * 60 * 1e3;
603
+ function idleFailure(ms) {
604
+ return Object.assign(new Error(`the response body sent no data for ${ms} ms`), {
605
+ code: "ETIMEDOUT"
606
+ });
607
+ }
608
+ async function readWithin(reader, ms) {
609
+ if (!(ms > 0 && ms < Number.POSITIVE_INFINITY)) return reader.read();
610
+ let timer;
611
+ const idle = new Promise((_, reject) => {
612
+ timer = setTimeout(() => reject(idleFailure(ms)), ms);
613
+ });
614
+ try {
615
+ return await Promise.race([reader.read(), idle]);
616
+ } finally {
617
+ clearTimeout(timer);
618
+ }
619
+ }
620
+
621
+ // src/kernel/net/sealed-fetch.ts
622
+ var SEALED_FETCH_VERSION = 1;
623
+ var UNSAFE_FIELD = /[\r\n\0]/;
624
+ function sealedHooks(option) {
625
+ if (!option) return {};
626
+ if (typeof option === "function") return { request: option };
627
+ const { request, fetch } = option;
628
+ if (typeof request !== "function" || fetch !== void 0 && typeof fetch !== "function") {
629
+ throw new TypeError("sealed takes a function or { request, fetch? }");
630
+ }
631
+ return { request, ...fetch ? { fetch } : {} };
632
+ }
633
+ function headerList(headers) {
634
+ return Array.isArray(headers) && headers.every(
635
+ (field) => Array.isArray(field) && field.length === 2 && field.every((text) => typeof text === "string" && !UNSAFE_FIELD.test(text))
636
+ );
637
+ }
638
+ function sealedReplyValid(reply) {
639
+ if (typeof reply !== "object" || reply === null) return false;
640
+ const { redirects, redirected, status, statusText, headers, url, body, cancel } = reply;
641
+ return redirects === void 0 && redirected !== true && Number.isInteger(status) && status >= 100 && status <= 599 && typeof statusText === "string" && !UNSAFE_FIELD.test(statusText) && headerList(headers) && (url === void 0 || typeof url === "string") && typeof body?.[Symbol.asyncIterator] === "function" && typeof cancel === "function";
642
+ }
643
+ function dropSealedReply(reply) {
644
+ const { cancel } = reply ?? {};
645
+ if (typeof cancel !== "function") return;
646
+ void Promise.resolve().then(() => cancel.call(reply)).catch(() => void 0);
647
+ }
648
+
601
649
  // src/kernel/net/routes.ts
602
650
  var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
603
651
  function ipv4Bytes(text) {
@@ -917,8 +965,129 @@ var KERNEL_SIGINFO = Object.freeze({
917
965
  });
918
966
 
919
967
  // src/sealed-relay.ts
920
- function sealedAnswerer(port, hook) {
968
+ function textOf(err) {
969
+ return err instanceof Error ? err.message : String(err);
970
+ }
971
+ function sendBytes(port, bytes) {
972
+ const copy = fresh(bytes);
973
+ port.postMessage({ t: "chunk", bytes: copy }, [copy.buffer]);
974
+ }
975
+ function uploadOf(far) {
976
+ let waiting;
977
+ let failure2;
978
+ const next = () => {
979
+ if (failure2) return Promise.reject(failure2);
980
+ waiting = Promise.withResolvers();
981
+ far.postMessage({ t: "pull" });
982
+ return waiting.promise;
983
+ };
984
+ return {
985
+ stream: {
986
+ [Symbol.asyncIterator]: () => ({
987
+ next,
988
+ return: async () => {
989
+ far.postMessage({ t: "stop" });
990
+ return { value: void 0, done: true };
991
+ }
992
+ })
993
+ },
994
+ answer(message) {
995
+ if (message.t === "chunk") waiting?.resolve({ value: message.bytes, done: false });
996
+ else if (message.t === "end") waiting?.resolve({ value: void 0, done: true });
997
+ else {
998
+ failure2 = new Error(message.message);
999
+ waiting?.reject(failure2);
1000
+ }
1001
+ waiting = void 0;
1002
+ },
1003
+ fail(reason) {
1004
+ failure2 = reason;
1005
+ waiting?.reject(reason);
1006
+ waiting = void 0;
1007
+ }
1008
+ };
1009
+ }
1010
+ function answerFetch(hook, { req, port: far }, live) {
1011
+ const control = new AbortController();
1012
+ const { streamed, ...fields } = req;
1013
+ const upload = uploadOf(far);
1014
+ let source;
1015
+ let reply;
1016
+ let closed = false;
1017
+ const settle = () => {
1018
+ closed = true;
1019
+ live.delete(answer);
1020
+ upload.fail(new Error("the request ended"));
1021
+ };
1022
+ const answer = {
1023
+ close(reason) {
1024
+ if (closed) return;
1025
+ settle();
1026
+ control.abort(reason);
1027
+ dropSealedReply(reply);
1028
+ far.close();
1029
+ }
1030
+ };
1031
+ const finish = (message) => {
1032
+ if (closed) return;
1033
+ settle();
1034
+ far.postMessage(message);
1035
+ };
1036
+ const failWith = (err) => {
1037
+ dropSealedReply(reply);
1038
+ finish({ t: "error", message: textOf(err) });
1039
+ };
1040
+ const read = async () => {
1041
+ try {
1042
+ const next = await source.next();
1043
+ if (closed) return;
1044
+ if (next.done) finish({ t: "end" });
1045
+ else if (next.value instanceof Uint8Array) sendBytes(far, next.value);
1046
+ else failWith("a body chunk is not bytes");
1047
+ } catch (err) {
1048
+ failWith(err);
1049
+ }
1050
+ };
1051
+ const ask = async () => {
1052
+ try {
1053
+ if (!hook) throw new Error("no vault is connected");
1054
+ const request = {
1055
+ ...fields,
1056
+ ...streamed ? { bodyStream: upload.stream } : {}
1057
+ };
1058
+ const answered = await hook(request, { signal: control.signal });
1059
+ if (!sealedReplyValid(answered)) {
1060
+ dropSealedReply(answered);
1061
+ throw new Error("the reply is not a scrubbed response");
1062
+ }
1063
+ if (closed) return dropSealedReply(answered);
1064
+ reply = answered;
1065
+ source = answered.body[Symbol.asyncIterator]();
1066
+ const { status, statusText, headers, url } = answered;
1067
+ far.postMessage({
1068
+ t: "head",
1069
+ status,
1070
+ statusText,
1071
+ headers,
1072
+ ...url !== void 0 ? { url } : {}
1073
+ });
1074
+ } catch (err) {
1075
+ failWith(err);
1076
+ }
1077
+ };
1078
+ far.addEventListener("message", ({ data }) => {
1079
+ if (data.t === "read") void read();
1080
+ else if (data.t === "cancel") answer.close(data.reason);
1081
+ else upload.answer(data);
1082
+ });
1083
+ far.addEventListener("close", () => answer.close("withdrawn"));
1084
+ far.start();
1085
+ live.add(answer);
1086
+ void ask();
1087
+ }
1088
+ function sealedAnswerer(port, hook, fetchHook) {
921
1089
  const asked = /* @__PURE__ */ new Map();
1090
+ const fetches = /* @__PURE__ */ new Set();
922
1091
  const ask = async ({ id, req }) => {
923
1092
  const control = new AbortController();
924
1093
  asked.set(id, control);
@@ -942,36 +1111,18 @@ function sealedAnswerer(port, hook) {
942
1111
  const message = data;
943
1112
  if (message?.sealedAsk) void ask(message.sealedAsk);
944
1113
  else if (message?.sealedAbort) abort(message.sealedAbort);
1114
+ else if (message?.sealedFetch) answerFetch(fetchHook, message.sealedFetch, fetches);
945
1115
  else return false;
946
1116
  return true;
947
1117
  },
948
1118
  close() {
949
1119
  for (const control of asked.values()) control.abort("withdrawn");
950
1120
  asked.clear();
1121
+ for (const answer of [...fetches]) answer.close("withdrawn");
951
1122
  }
952
1123
  };
953
1124
  }
954
1125
 
955
- // src/body-idle.ts
956
- var BODY_IDLE_MS = 5 * 60 * 1e3;
957
- function idleFailure(ms) {
958
- return Object.assign(new Error(`the response body sent no data for ${ms} ms`), {
959
- code: "ETIMEDOUT"
960
- });
961
- }
962
- async function readWithin(reader, ms) {
963
- if (!(ms > 0 && ms < Number.POSITIVE_INFINITY)) return reader.read();
964
- let timer;
965
- const idle = new Promise((_, reject) => {
966
- timer = setTimeout(() => reject(idleFailure(ms)), ms);
967
- });
968
- try {
969
- return await Promise.race([reader.read(), idle]);
970
- } finally {
971
- clearTimeout(timer);
972
- }
973
- }
974
-
975
1126
  // src/kernel/net/ws-queue.ts
976
1127
  var INBOUND_LIMIT = 16 * 1024 * 1024;
977
1128
  var MESSAGE_LIMIT = 64 * 1024 * 1024;
@@ -1282,7 +1433,21 @@ function headOf(nid, response) {
1282
1433
  headers: response.headers
1283
1434
  };
1284
1435
  }
1285
- function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
1436
+ function requestOf(call, signal, uploads) {
1437
+ const { url, method, headers, streamed, body, redirect, nid } = call;
1438
+ return {
1439
+ url,
1440
+ method,
1441
+ headers,
1442
+ ...streamed ? { bodyStream: uploads.of(nid, signal) } : body ? { body } : {},
1443
+ ...redirect !== void 0 ? { redirect } : {},
1444
+ signal
1445
+ };
1446
+ }
1447
+ function serveTransport(peer, transport, {
1448
+ closeWaitMs = CLOSE_WAIT_MS,
1449
+ attributed = false
1450
+ } = {}) {
1286
1451
  const open = /* @__PURE__ */ new Map();
1287
1452
  const fail = (nid, e) => {
1288
1453
  open.delete(nid);
@@ -1302,14 +1467,11 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
1302
1467
  const entry = { abort };
1303
1468
  open.set(call.nid, entry);
1304
1469
  try {
1305
- const response = await transport.fetch({
1306
- url: call.url,
1307
- method: call.method,
1308
- headers: call.headers,
1309
- ...call.streamed ? { bodyStream: uploads.of(call.nid, abort.signal) } : call.body ? { body: call.body } : {},
1310
- ...call.redirect !== void 0 ? { redirect: call.redirect } : {},
1311
- signal: abort.signal
1312
- });
1470
+ const response = await transport.fetch(
1471
+ requestOf(call, abort.signal, uploads),
1472
+ attributed ? call.owner : void 0,
1473
+ attributed ? call.stamp : void 0
1474
+ );
1313
1475
  if (!open.has(call.nid)) {
1314
1476
  await response.cancel();
1315
1477
  return;
@@ -1564,7 +1726,7 @@ var RemoteTransport = class {
1564
1726
  waiter?.reject(new TransportError("the request was cancelled"));
1565
1727
  this.port.postMessage({ net: "cancel", nid });
1566
1728
  }
1567
- async fetch(request) {
1729
+ async fetch(request, owner, stamp) {
1568
1730
  const nid = ++this.nextId;
1569
1731
  const { signal, redirect } = request;
1570
1732
  signal.throwIfAborted();
@@ -1594,7 +1756,9 @@ var RemoteTransport = class {
1594
1756
  method: request.method,
1595
1757
  headers: request.headers,
1596
1758
  ...request.bodyStream ? { streamed: true } : request.body ? { body: request.body } : {},
1597
- ...redirect !== void 0 ? { redirect } : {}
1759
+ ...redirect !== void 0 ? { redirect } : {},
1760
+ ...owner !== void 0 ? { owner } : {},
1761
+ ...stamp ? { stamp } : {}
1598
1762
  });
1599
1763
  } catch (e) {
1600
1764
  finish();
@@ -2279,6 +2443,7 @@ function uplinkInit(uplink) {
2279
2443
  return { uplink: { traits, ...uplink.routes ? { routes: uplink.routes } : {}, ...nameserver } };
2280
2444
  }
2281
2445
  function initCall(options, transport) {
2446
+ const hooks = sealedHooks(options.sealed);
2282
2447
  return {
2283
2448
  op: "init",
2284
2449
  ...options.root ? { root: options.root } : {},
@@ -2297,7 +2462,8 @@ function initCall(options, transport) {
2297
2462
  ...options.hostname !== void 0 ? { hostname: options.hostname } : {},
2298
2463
  users: options.users === void 0 ? DEFAULT_USERS : options.users,
2299
2464
  ...options.onDenial ? { denials: true } : {},
2300
- ...options.sealed ? { sealed: true } : {},
2465
+ ...hooks.request ? { sealed: true } : {},
2466
+ ...hooks.fetch ? { sealedFetch: true } : {},
2301
2467
  ...options.clipboard ? { clipboard: true } : {},
2302
2468
  ...options.requests ? { requests: kindsOf(options.requests) } : {},
2303
2469
  ...options.approve ? { approve: true } : {},
@@ -2458,6 +2624,7 @@ function checkOptions(options) {
2458
2624
  if (options.protect) protectInit(options.protect);
2459
2625
  integrityOptions(options.protect?.integrity);
2460
2626
  facadesInit(options.facades);
2627
+ sealedHooks(options.sealed);
2461
2628
  }
2462
2629
  function connector(call, bound) {
2463
2630
  return async (connectOptions = {}) => {
@@ -2474,7 +2641,8 @@ function connector(call, bound) {
2474
2641
  };
2475
2642
  }
2476
2643
  function hookRelays(worker, options, bound) {
2477
- const sealed = sealedAnswerer(worker, options.sealed);
2644
+ const hooks = sealedHooks(options.sealed);
2645
+ const sealed = sealedAnswerer(worker, hooks.request, hooks.fetch);
2478
2646
  const clipboard = clipboardAnswerer(worker, options.clipboard);
2479
2647
  const integrity = integrityAnswerer(worker, options.protect?.integrity?.lookup);
2480
2648
  const requests = embedderAnswerer(
@@ -2509,6 +2677,17 @@ function traitsUpdater(transport, call) {
2509
2677
  return await call({ op: "transport-traits", ...traits }) === true;
2510
2678
  };
2511
2679
  }
2680
+ function transportBridge(worker, transport) {
2681
+ return transport ? serveTransport(worker, transport, { attributed: true }) : void 0;
2682
+ }
2683
+ function pageOnly(call) {
2684
+ return {
2685
+ sealed: {
2686
+ setPlaceholders: async (list) => void await call({ op: "sealed-placeholders", list })
2687
+ },
2688
+ attest: ((path, options) => call({ op: "attest", path, ...options ? { options } : {} }))
2689
+ };
2690
+ }
2512
2691
  async function createKernel(options = {}) {
2513
2692
  checkOptions(options);
2514
2693
  const url = options.worker ?? new URL("./kernel-worker.js", import.meta.url);
@@ -2539,7 +2718,7 @@ async function createKernel(options = {}) {
2539
2718
  pending.clear();
2540
2719
  };
2541
2720
  const transport = options.network?.transport;
2542
- const bridge = transport ? serveTransport(worker, transport) : void 0;
2721
+ const bridge = transportBridge(worker, transport);
2543
2722
  const uplinks = uplinkServer(worker, options.network?.uplink);
2544
2723
  async function chosen(handle) {
2545
2724
  if (handle) {
@@ -2663,7 +2842,7 @@ async function createKernel(options = {}) {
2663
2842
  umount: async (target) => void await call({ op: "umount", target }),
2664
2843
  mounts: async () => await call({ op: "mounts" }),
2665
2844
  insert: async (target, handle) => void await call({ op: "insert", target, handle }),
2666
- attest: async (path) => await call({ op: "attest", path }),
2845
+ ...pageOnly(call),
2667
2846
  watch: files.watch,
2668
2847
  terminate() {
2669
2848
  fail(new Error("slicc-kernel terminated"));
@@ -2674,6 +2853,7 @@ async function createKernel(options = {}) {
2674
2853
  export {
2675
2854
  KernelCallError,
2676
2855
  KernelGoneError,
2856
+ SEALED_FETCH_VERSION,
2677
2857
  attachKernel,
2678
2858
  createKernel,
2679
2859
  fetchTransport,