@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.
@@ -3118,7 +3118,7 @@ var init_version = __esm({
3118
3118
  "src/version.ts"() {
3119
3119
  "use strict";
3120
3120
  init_captured_globals();
3121
- KERNEL_VERSION = true ? "1.171.0" : "0.0.0-dev";
3121
+ KERNEL_VERSION = true ? "1.172.0" : "0.0.0-dev";
3122
3122
  OS_RELEASE = "6.0.0-slicc";
3123
3123
  }
3124
3124
  });
package/dist/testing.js CHANGED
@@ -126,11 +126,11 @@ function fakeCronClock(start = Date.UTC(2026, 0, 1), utc = true) {
126
126
  get sleepers() {
127
127
  return sleepers.size;
128
128
  },
129
- sleep(until, signal) {
129
+ sleep(until2, signal) {
130
130
  if (signal.aborted) return Promise.resolve();
131
131
  return new Promise((resolve) => {
132
132
  const sleeper = {
133
- until,
133
+ until: until2,
134
134
  wake: () => {
135
135
  sleepers.delete(sleeper);
136
136
  signal.removeEventListener("abort", sleeper.wake);
@@ -145,7 +145,7 @@ function fakeCronClock(start = Date.UTC(2026, 0, 1), utc = true) {
145
145
  awake = Math.max(0, awake - 1);
146
146
  signal.addEventListener("abort", sleeper.wake, { once: true });
147
147
  sleepers.add(sleeper);
148
- if (until <= now) queueMicrotask(sleeper.ring);
148
+ if (until2 <= now) queueMicrotask(sleeper.ring);
149
149
  else check();
150
150
  });
151
151
  },
@@ -166,6 +166,223 @@ function fakeCronClock(start = Date.UTC(2026, 0, 1), utc = true) {
166
166
  };
167
167
  }
168
168
 
169
+ // src/kernel/net/placeholder-floor.ts
170
+ var PLACEHOLDER = /fnox_proxy_[0-9a-f]{24}/;
171
+ var ESCAPE = /%([0-9a-f]{2})/gi;
172
+ var BASIC = /\bbasic\b(.*)/is;
173
+ var PERCENT_LAYERS = 8;
174
+ var BASE64_GROUP = 4;
175
+ var PLACEHOLDER_REFUSED = "refused: this request has a sealed placeholder in its URL, a header or a Basic credential, and nothing fills it, so it was not sent";
176
+ var PlaceholderRefused = class extends Error {
177
+ status = 403;
178
+ code = "EACCES";
179
+ constructor(reason = PLACEHOLDER_REFUSED) {
180
+ super(reason);
181
+ }
182
+ };
183
+ function holds(text, known) {
184
+ return PLACEHOLDER.test(text) || known.some((placeholder) => text.includes(placeholder));
185
+ }
186
+ function carries(text, known) {
187
+ let layer = text;
188
+ for (let i = 0; i < PERCENT_LAYERS; i++) {
189
+ if (holds(layer, known)) return true;
190
+ const next = layer.replace(
191
+ ESCAPE,
192
+ (_, hex) => String.fromCharCode(Number.parseInt(hex, 16))
193
+ );
194
+ if (next === layer) return false;
195
+ layer = next;
196
+ }
197
+ return holds(layer, known);
198
+ }
199
+ function basicCarries(value, known) {
200
+ const credential = BASIC.exec(value)?.[1];
201
+ if (credential === void 0) return false;
202
+ const letters = credential.replace(/[^A-Za-z0-9+/_-]/g, "").replaceAll("-", "+").replaceAll("_", "/");
203
+ for (let shift = 0; shift < BASE64_GROUP; shift++) {
204
+ const part = letters.slice(shift);
205
+ const whole = part.length % BASE64_GROUP === 1 ? part.slice(0, -1) : part;
206
+ if (carries(atob(whole), known)) return true;
207
+ }
208
+ return false;
209
+ }
210
+ function headersCarry(headers, known) {
211
+ return headers.some(
212
+ ([name, value]) => carries(name, known) || carries(name.toLowerCase(), known) || carries(value, known) || basicCarries(value, known)
213
+ );
214
+ }
215
+ function urlCarries(url, known) {
216
+ return carries(url, known) || URL.canParse(url) && carries(new URL(url).href, known);
217
+ }
218
+ function detected(request, known) {
219
+ return urlCarries(request.url, known) || headersCarry(request.headers, known);
220
+ }
221
+ function refuseSocket(request, known) {
222
+ if (detected(request, known) || request.protocols.some((p) => carries(p, known))) {
223
+ throw new PlaceholderRefused();
224
+ }
225
+ }
226
+ function placeholderFloor(inner, { known = () => [], fill } = {}) {
227
+ const open = inner.websocket?.bind(inner);
228
+ return {
229
+ get traits() {
230
+ return inner.traits;
231
+ },
232
+ ...open ? {
233
+ websocket: async (request) => {
234
+ refuseSocket(request, known());
235
+ return open(request);
236
+ }
237
+ } : {},
238
+ async fetch(request, owner, stamp) {
239
+ if (!detected(request, known())) return inner.fetch(request, owner, stamp);
240
+ if (!fill || !stamp) throw new PlaceholderRefused();
241
+ return fill(request, stamp);
242
+ }
243
+ };
244
+ }
245
+
246
+ // src/body-idle.ts
247
+ var BODY_IDLE_MS = 5 * 60 * 1e3;
248
+
249
+ // src/kernel/net/sealed-fetch.ts
250
+ var UNSAFE_FIELD = /[\r\n\0]/;
251
+ function headerList(headers) {
252
+ return Array.isArray(headers) && headers.every(
253
+ (field) => Array.isArray(field) && field.length === 2 && field.every((text) => typeof text === "string" && !UNSAFE_FIELD.test(text))
254
+ );
255
+ }
256
+ function sealedReplyValid(reply) {
257
+ if (typeof reply !== "object" || reply === null) return false;
258
+ const { redirects, redirected, status, statusText, headers, url, body, cancel } = reply;
259
+ 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";
260
+ }
261
+ function dropSealedReply(reply) {
262
+ const { cancel } = reply ?? {};
263
+ if (typeof cancel !== "function") return;
264
+ void Promise.resolve().then(() => cancel.call(reply)).catch(() => void 0);
265
+ }
266
+ function failed(reason) {
267
+ const text = reason instanceof Error ? reason.message : String(reason);
268
+ return new PlaceholderRefused(
269
+ `refused: the request with a sealed placeholder was not completed: ${text}`
270
+ );
271
+ }
272
+ var Stall = class {
273
+ idle = new AbortController();
274
+ ms;
275
+ timer;
276
+ signal;
277
+ constructor(signals, ms) {
278
+ this.ms = ms;
279
+ this.signal = AbortSignal.any([...signals, this.idle.signal]);
280
+ }
281
+ arm() {
282
+ clearTimeout(this.timer);
283
+ this.timer = setTimeout(() => this.idle.abort("timeout"), this.ms);
284
+ this.timer.unref?.();
285
+ }
286
+ touch() {
287
+ if (this.timer !== void 0) this.arm();
288
+ }
289
+ rest() {
290
+ clearTimeout(this.timer);
291
+ this.timer = void 0;
292
+ }
293
+ };
294
+ async function until(work, signal) {
295
+ const stopped = Promise.withResolvers();
296
+ const stop = () => stopped.reject(signal.reason);
297
+ if (signal.aborted) stop();
298
+ else signal.addEventListener("abort", stop, { once: true });
299
+ try {
300
+ return await Promise.race([work, stopped.promise]);
301
+ } finally {
302
+ signal.removeEventListener("abort", stop);
303
+ }
304
+ }
305
+ async function* ticking(stream, stall) {
306
+ for await (const chunk of stream) {
307
+ stall.touch();
308
+ yield chunk;
309
+ }
310
+ }
311
+ function sealedRequest(request, stamp, stall) {
312
+ const { method, url, headers, body, bodyStream } = request;
313
+ return {
314
+ v: 1,
315
+ caller: structuredClone(stamp),
316
+ method,
317
+ url,
318
+ headers: headers.map(([name, value]) => [name, value]),
319
+ redirect: "manual",
320
+ ...bodyStream ? { bodyStream: ticking(bodyStream, stall) } : body ? { body } : {}
321
+ };
322
+ }
323
+ function guarded(reply, stall) {
324
+ let finished = false;
325
+ const cancel = async () => {
326
+ stall.signal.removeEventListener("abort", dropped);
327
+ stall.rest();
328
+ if (finished) return;
329
+ finished = true;
330
+ await reply.cancel().catch(() => void 0);
331
+ };
332
+ const dropped = () => void cancel();
333
+ stall.signal.addEventListener("abort", dropped, { once: true });
334
+ const source = reply.body[Symbol.asyncIterator]();
335
+ const body = (async function* () {
336
+ try {
337
+ for (; ; ) {
338
+ stall.arm();
339
+ const next = await until(source.next(), stall.signal);
340
+ stall.rest();
341
+ if (next.done) {
342
+ finished = true;
343
+ return;
344
+ }
345
+ if (!(next.value instanceof Uint8Array)) throw failed("a body chunk is not bytes");
346
+ if (next.value.byteLength > 0) yield next.value;
347
+ }
348
+ } finally {
349
+ await cancel();
350
+ }
351
+ })();
352
+ return {
353
+ ...reply.url !== void 0 ? { url: reply.url } : {},
354
+ status: reply.status,
355
+ statusText: reply.statusText,
356
+ headers: reply.headers,
357
+ body,
358
+ cancel
359
+ };
360
+ }
361
+ function sealedFetcher(hook, stop, idleMs = BODY_IDLE_MS) {
362
+ return async (request, stamp) => {
363
+ const stall = new Stall([request.signal, stop], idleMs);
364
+ const { signal } = stall;
365
+ if (signal.aborted) throw failed(signal.reason);
366
+ stall.arm();
367
+ const asked = Promise.resolve().then(() => {
368
+ signal.throwIfAborted();
369
+ return hook(sealedRequest(request, stamp, stall), { signal });
370
+ });
371
+ let reply;
372
+ try {
373
+ reply = await until(asked, signal);
374
+ } catch (e) {
375
+ void asked.then(dropSealedReply, () => void 0);
376
+ throw failed(e);
377
+ } finally {
378
+ stall.rest();
379
+ }
380
+ if (sealedReplyValid(reply)) return guarded(reply, stall);
381
+ dropSealedReply(reply);
382
+ throw failed("the reply is not a scrubbed response");
383
+ };
384
+ }
385
+
169
386
  // src/kernel/test-hooks.ts
170
387
  var SYSCALL_HOOK = /* @__PURE__ */ Symbol.for("slicc-kernel.test.syscall");
171
388
  function setTestSyscallHook(hook) {
@@ -174,6 +391,280 @@ function setTestSyscallHook(hook) {
174
391
  else delete holder[SYSCALL_HOOK];
175
392
  }
176
393
 
394
+ // src/bytes.ts
395
+ function fresh(bytes, start = 0, end = bytes.length) {
396
+ return new Uint8Array(bytes.subarray(start, end));
397
+ }
398
+
399
+ // src/sealed-relay.ts
400
+ function textOf(err) {
401
+ return err instanceof Error ? err.message : String(err);
402
+ }
403
+ function sendBytes(port, bytes) {
404
+ const copy = fresh(bytes);
405
+ port.postMessage({ t: "chunk", bytes: copy }, [copy.buffer]);
406
+ }
407
+ function askFetch(port, req, signal) {
408
+ signal.throwIfAborted();
409
+ const { bodyStream, ...wire } = req;
410
+ const { port1: near, port2: far } = new MessageChannel();
411
+ const upload = bodyStream?.[Symbol.asyncIterator]();
412
+ const head = Promise.withResolvers();
413
+ let reading;
414
+ let ended = false;
415
+ const end = (failure2) => {
416
+ if (ended) return;
417
+ ended = true;
418
+ signal.removeEventListener("abort", abort);
419
+ near.close();
420
+ void upload?.return?.().catch(() => void 0);
421
+ head.reject(failure2);
422
+ reading?.reject(failure2);
423
+ reading = void 0;
424
+ };
425
+ const abort = () => {
426
+ near.postMessage({ t: "cancel", reason: String(signal.reason) });
427
+ end(new Error(String(signal.reason)));
428
+ };
429
+ const pull = async () => {
430
+ try {
431
+ const next = await upload.next();
432
+ if (ended) return;
433
+ if (next.done) near.postMessage({ t: "end" });
434
+ else sendBytes(near, next.value);
435
+ } catch (err) {
436
+ if (!ended) near.postMessage({ t: "error", message: textOf(err) });
437
+ }
438
+ };
439
+ const respond = (message) => {
440
+ const { status, statusText, headers, url } = message;
441
+ const body = {
442
+ [Symbol.asyncIterator]: () => ({
443
+ next: () => {
444
+ if (ended) return Promise.resolve({ value: void 0, done: true });
445
+ reading = Promise.withResolvers();
446
+ near.postMessage({ t: "read" });
447
+ return reading.promise;
448
+ },
449
+ return: async () => {
450
+ await cancel();
451
+ return { value: void 0, done: true };
452
+ }
453
+ })
454
+ };
455
+ const cancel = async () => {
456
+ if (ended) return;
457
+ near.postMessage({ t: "cancel", reason: "cancelled" });
458
+ end(new Error("the request was cancelled"));
459
+ };
460
+ head.resolve({
461
+ ...url !== void 0 ? { url } : {},
462
+ status,
463
+ statusText,
464
+ headers,
465
+ body,
466
+ cancel
467
+ });
468
+ };
469
+ near.addEventListener("message", ({ data }) => {
470
+ if (data.t === "pull") void pull();
471
+ else if (data.t === "stop") void upload?.return?.().catch(() => void 0);
472
+ else if (data.t === "head") respond(data);
473
+ else if (data.t === "chunk") {
474
+ reading?.resolve({ value: data.bytes, done: false });
475
+ reading = void 0;
476
+ } else if (data.t === "end") {
477
+ reading?.resolve({ value: void 0, done: true });
478
+ reading = void 0;
479
+ end(new Error("the response ended"));
480
+ } else end(new Error(data.message));
481
+ });
482
+ near.addEventListener("close", () => end(new Error("the sealed fetch relay closed")));
483
+ near.start();
484
+ signal.addEventListener("abort", abort, { once: true });
485
+ const ask = {
486
+ req: { ...wire, ...upload ? { streamed: true } : {} },
487
+ port: far
488
+ };
489
+ port.postMessage({ sealedFetch: ask }, [far]);
490
+ return head.promise;
491
+ }
492
+ function uploadOf(far) {
493
+ let waiting;
494
+ let failure2;
495
+ const next = () => {
496
+ if (failure2) return Promise.reject(failure2);
497
+ waiting = Promise.withResolvers();
498
+ far.postMessage({ t: "pull" });
499
+ return waiting.promise;
500
+ };
501
+ return {
502
+ stream: {
503
+ [Symbol.asyncIterator]: () => ({
504
+ next,
505
+ return: async () => {
506
+ far.postMessage({ t: "stop" });
507
+ return { value: void 0, done: true };
508
+ }
509
+ })
510
+ },
511
+ answer(message) {
512
+ if (message.t === "chunk") waiting?.resolve({ value: message.bytes, done: false });
513
+ else if (message.t === "end") waiting?.resolve({ value: void 0, done: true });
514
+ else {
515
+ failure2 = new Error(message.message);
516
+ waiting?.reject(failure2);
517
+ }
518
+ waiting = void 0;
519
+ },
520
+ fail(reason) {
521
+ failure2 = reason;
522
+ waiting?.reject(reason);
523
+ waiting = void 0;
524
+ }
525
+ };
526
+ }
527
+ function answerFetch(hook, { req, port: far }, live) {
528
+ const control = new AbortController();
529
+ const { streamed, ...fields } = req;
530
+ const upload = uploadOf(far);
531
+ let source;
532
+ let reply;
533
+ let closed = false;
534
+ const settle = () => {
535
+ closed = true;
536
+ live.delete(answer);
537
+ upload.fail(new Error("the request ended"));
538
+ };
539
+ const answer = {
540
+ close(reason) {
541
+ if (closed) return;
542
+ settle();
543
+ control.abort(reason);
544
+ dropSealedReply(reply);
545
+ far.close();
546
+ }
547
+ };
548
+ const finish = (message) => {
549
+ if (closed) return;
550
+ settle();
551
+ far.postMessage(message);
552
+ };
553
+ const failWith = (err) => {
554
+ dropSealedReply(reply);
555
+ finish({ t: "error", message: textOf(err) });
556
+ };
557
+ const read = async () => {
558
+ try {
559
+ const next = await source.next();
560
+ if (closed) return;
561
+ if (next.done) finish({ t: "end" });
562
+ else if (next.value instanceof Uint8Array) sendBytes(far, next.value);
563
+ else failWith("a body chunk is not bytes");
564
+ } catch (err) {
565
+ failWith(err);
566
+ }
567
+ };
568
+ const ask = async () => {
569
+ try {
570
+ if (!hook) throw new Error("no vault is connected");
571
+ const request = {
572
+ ...fields,
573
+ ...streamed ? { bodyStream: upload.stream } : {}
574
+ };
575
+ const answered = await hook(request, { signal: control.signal });
576
+ if (!sealedReplyValid(answered)) {
577
+ dropSealedReply(answered);
578
+ throw new Error("the reply is not a scrubbed response");
579
+ }
580
+ if (closed) return dropSealedReply(answered);
581
+ reply = answered;
582
+ source = answered.body[Symbol.asyncIterator]();
583
+ const { status, statusText, headers, url } = answered;
584
+ far.postMessage({
585
+ t: "head",
586
+ status,
587
+ statusText,
588
+ headers,
589
+ ...url !== void 0 ? { url } : {}
590
+ });
591
+ } catch (err) {
592
+ failWith(err);
593
+ }
594
+ };
595
+ far.addEventListener("message", ({ data }) => {
596
+ if (data.t === "read") void read();
597
+ else if (data.t === "cancel") answer.close(data.reason);
598
+ else upload.answer(data);
599
+ });
600
+ far.addEventListener("close", () => answer.close("withdrawn"));
601
+ far.start();
602
+ live.add(answer);
603
+ void ask();
604
+ }
605
+ function sealedAsker(port) {
606
+ const asks = /* @__PURE__ */ new Map();
607
+ let next = 0;
608
+ const hook = (req, { signal }) => {
609
+ const id = ++next;
610
+ const waiting = Promise.withResolvers();
611
+ asks.set(id, waiting);
612
+ const abort = () => {
613
+ if (!asks.delete(id)) return;
614
+ port.postMessage({ sealedAbort: { id, reason: signal.reason } });
615
+ waiting.reject(new Error(String(signal.reason)));
616
+ };
617
+ signal.addEventListener("abort", abort, { once: true });
618
+ port.postMessage({ sealedAsk: { id, req } });
619
+ return waiting.promise.finally(() => signal.removeEventListener("abort", abort));
620
+ };
621
+ const answer = ({ id, reply, error }) => {
622
+ const waiting = asks.get(id);
623
+ if (!waiting) return;
624
+ asks.delete(id);
625
+ if (reply) waiting.resolve(reply);
626
+ else waiting.reject(new Error(error ?? "no reply"));
627
+ };
628
+ return { hook, fetch: (req, { signal }) => askFetch(port, req, signal), answer };
629
+ }
630
+ function sealedAnswerer(port, hook, fetchHook) {
631
+ const asked = /* @__PURE__ */ new Map();
632
+ const fetches = /* @__PURE__ */ new Set();
633
+ const ask = async ({ id, req }) => {
634
+ const control = new AbortController();
635
+ asked.set(id, control);
636
+ try {
637
+ if (!hook) throw new Error("no vault is connected");
638
+ const reply = await hook(req, { signal: control.signal });
639
+ port.postMessage({ sealedReply: { id, reply } });
640
+ } catch (err) {
641
+ const error = err instanceof Error ? err.message : String(err);
642
+ port.postMessage({ sealedReply: { id, error } });
643
+ } finally {
644
+ asked.delete(id);
645
+ }
646
+ };
647
+ const abort = ({ id, reason }) => {
648
+ asked.get(id)?.abort(reason);
649
+ asked.delete(id);
650
+ };
651
+ return {
652
+ handle(data) {
653
+ const message = data;
654
+ if (message?.sealedAsk) void ask(message.sealedAsk);
655
+ else if (message?.sealedAbort) abort(message.sealedAbort);
656
+ else if (message?.sealedFetch) answerFetch(fetchHook, message.sealedFetch, fetches);
657
+ else return false;
658
+ return true;
659
+ },
660
+ close() {
661
+ for (const control of asked.values()) control.abort("withdrawn");
662
+ asked.clear();
663
+ for (const answer of [...fetches]) answer.close("withdrawn");
664
+ }
665
+ };
666
+ }
667
+
177
668
  // src/testing.ts
178
669
  var failure = (code, message) => Object.assign(new Error(message), { code });
179
670
  var Stream = class {
@@ -336,6 +827,10 @@ function fakeUplink(options = {}) {
336
827
  export {
337
828
  fakeCronClock,
338
829
  fakeUplink,
830
+ placeholderFloor,
831
+ sealedAnswerer,
832
+ sealedAsker,
833
+ sealedFetcher,
339
834
  setTestSyscallHook
340
835
  };
341
836
  //# sourceMappingURL=testing.js.map