@ai-ecoverse/slicc-kernel 1.37.1 → 1.39.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 +3 -1
- package/dist/driver-worker.js +123 -0
- package/dist/driver-worker.js.map +3 -3
- package/dist/index.js +309 -6
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +647 -214
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +123 -0
- package/dist/node-driver-worker.js.map +3 -3
- package/dist/node-process-worker.js +370 -99
- package/dist/node-process-worker.js.map +4 -4
- package/dist/node.js +578 -66
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +370 -99
- package/dist/process-worker.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -509,7 +509,153 @@ async function readWithin(reader, ms) {
|
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
+
// src/kernel/net/ws-queue.ts
|
|
513
|
+
var INBOUND_LIMIT = 16 * 1024 * 1024;
|
|
514
|
+
var MESSAGE_LIMIT = 64 * 1024 * 1024;
|
|
515
|
+
var TOO_BIG = 1009;
|
|
516
|
+
function payloadSize(message) {
|
|
517
|
+
return typeof message === "string" ? new TextEncoder().encode(message).length : message.length;
|
|
518
|
+
}
|
|
519
|
+
var MessageQueue = class {
|
|
520
|
+
items = [];
|
|
521
|
+
limit;
|
|
522
|
+
waiter;
|
|
523
|
+
size = 0;
|
|
524
|
+
ended = false;
|
|
525
|
+
overflow;
|
|
526
|
+
taken;
|
|
527
|
+
closed;
|
|
528
|
+
settle;
|
|
529
|
+
constructor(overflow, limit = INBOUND_LIMIT, taken) {
|
|
530
|
+
this.overflow = overflow;
|
|
531
|
+
this.limit = limit;
|
|
532
|
+
this.taken = taken;
|
|
533
|
+
this.closed = new Promise((resolve) => {
|
|
534
|
+
this.settle = resolve;
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
push(message) {
|
|
538
|
+
if (this.ended) return;
|
|
539
|
+
const size = payloadSize(message);
|
|
540
|
+
const huge = size > MESSAGE_LIMIT;
|
|
541
|
+
if (huge || this.items.length > 0 && this.size + size > this.limit) {
|
|
542
|
+
const reason = huge ? `a message is over ${MESSAGE_LIMIT} bytes` : `over ${this.limit} bytes of messages are waiting`;
|
|
543
|
+
this.end({ code: TOO_BIG, reason });
|
|
544
|
+
this.overflow();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
this.size += size;
|
|
548
|
+
this.items.push(message);
|
|
549
|
+
this.wake();
|
|
550
|
+
}
|
|
551
|
+
end(close) {
|
|
552
|
+
if (this.ended) return;
|
|
553
|
+
this.ended = true;
|
|
554
|
+
this.settle(close);
|
|
555
|
+
this.wake();
|
|
556
|
+
}
|
|
557
|
+
wake() {
|
|
558
|
+
const waiter = this.waiter;
|
|
559
|
+
this.waiter = void 0;
|
|
560
|
+
waiter?.();
|
|
561
|
+
}
|
|
562
|
+
async *[Symbol.asyncIterator]() {
|
|
563
|
+
for (; ; ) {
|
|
564
|
+
const next = this.items.shift();
|
|
565
|
+
if (next !== void 0) {
|
|
566
|
+
const n = payloadSize(next);
|
|
567
|
+
this.size -= n;
|
|
568
|
+
this.taken?.(n);
|
|
569
|
+
yield next;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (this.ended) return;
|
|
573
|
+
await new Promise((resolve) => {
|
|
574
|
+
this.waiter = resolve;
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/websocket-transport.ts
|
|
581
|
+
var SENT_HEADERS_DROP = /* @__PURE__ */ new Set([
|
|
582
|
+
"host",
|
|
583
|
+
"connection",
|
|
584
|
+
"upgrade",
|
|
585
|
+
"sec-websocket-key",
|
|
586
|
+
"sec-websocket-version",
|
|
587
|
+
"sec-websocket-extensions",
|
|
588
|
+
"sec-websocket-protocol"
|
|
589
|
+
]);
|
|
590
|
+
function headerRecord(headers) {
|
|
591
|
+
const out = {};
|
|
592
|
+
for (const [name, value] of headers) {
|
|
593
|
+
if (!SENT_HEADERS_DROP.has(name.toLowerCase())) out[name] = value;
|
|
594
|
+
}
|
|
595
|
+
return out;
|
|
596
|
+
}
|
|
597
|
+
function sendableClose(code) {
|
|
598
|
+
return code === 1e3 || code !== void 0 && code >= 3e3 && code <= 4999 ? code : 1e3;
|
|
599
|
+
}
|
|
600
|
+
function blockedReason(url, e) {
|
|
601
|
+
const name = e?.name;
|
|
602
|
+
if (name === "SecurityError" && url.startsWith("ws:")) {
|
|
603
|
+
return `${url}: ws:// is blocked from an https page; use wss:// or slicc-node`;
|
|
604
|
+
}
|
|
605
|
+
return `${url}: ${e instanceof Error ? e.message : String(e)}`;
|
|
606
|
+
}
|
|
607
|
+
function openWebSocket(Socket, request, withHeaders) {
|
|
608
|
+
const { url, protocols, signal } = request;
|
|
609
|
+
signal.throwIfAborted();
|
|
610
|
+
let socket;
|
|
611
|
+
try {
|
|
612
|
+
socket = withHeaders ? new Socket(url, { protocols, headers: headerRecord(request.headers) }) : new Socket(url, protocols);
|
|
613
|
+
} catch (e) {
|
|
614
|
+
return Promise.reject(Object.assign(new Error(blockedReason(url, e)), { status: 502 }));
|
|
615
|
+
}
|
|
616
|
+
socket.binaryType = "arraybuffer";
|
|
617
|
+
const queue = new MessageQueue(() => socket.close(1e3, "too many waiting messages"));
|
|
618
|
+
return new Promise((resolve, reject) => {
|
|
619
|
+
let open = false;
|
|
620
|
+
const abort = () => {
|
|
621
|
+
socket.close(1e3, "aborted");
|
|
622
|
+
reject(signal.reason);
|
|
623
|
+
};
|
|
624
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
625
|
+
socket.addEventListener("open", () => {
|
|
626
|
+
open = true;
|
|
627
|
+
signal.removeEventListener("abort", abort);
|
|
628
|
+
resolve({
|
|
629
|
+
protocol: socket.protocol,
|
|
630
|
+
get buffered() {
|
|
631
|
+
return socket.bufferedAmount;
|
|
632
|
+
},
|
|
633
|
+
send: (data) => socket.send(data),
|
|
634
|
+
messages: queue,
|
|
635
|
+
close: (code, reason) => socket.close(sendableClose(code), reason),
|
|
636
|
+
closed: queue.closed
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
socket.addEventListener("message", (event) => {
|
|
640
|
+
queue.push(typeof event.data === "string" ? event.data : new Uint8Array(event.data));
|
|
641
|
+
});
|
|
642
|
+
socket.addEventListener("close", (event) => {
|
|
643
|
+
queue.end({ code: event.code, reason: event.reason });
|
|
644
|
+
if (open) return;
|
|
645
|
+
signal.removeEventListener("abort", abort);
|
|
646
|
+
reject(
|
|
647
|
+
Object.assign(new Error(`${url}: the WebSocket closed (${event.code}) before it opened`), {
|
|
648
|
+
status: 502
|
|
649
|
+
})
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
512
655
|
// src/transport.ts
|
|
656
|
+
var SEND_BUFFER = 1024 * 1024;
|
|
657
|
+
var CLOSE_WAIT_MS = 5e3;
|
|
658
|
+
var NO_WEBSOCKET = "WebSocket not supported by this transport (fetchTransport needs { webSocket: true })";
|
|
513
659
|
var MAX_REQUEST_BODY = 64 * 1024 * 1024;
|
|
514
660
|
function joinHeaders(headers) {
|
|
515
661
|
const out = new Headers();
|
|
@@ -534,13 +680,17 @@ async function* chunks(body, idleMs) {
|
|
|
534
680
|
}
|
|
535
681
|
function fetchTransport(options = {}) {
|
|
536
682
|
const send2 = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
683
|
+
const Socket = options.webSocket === true ? globalThis.WebSocket : options.webSocket || void 0;
|
|
684
|
+
const headers = options.webSocketHeaders === true;
|
|
537
685
|
return {
|
|
538
686
|
traits: {
|
|
539
687
|
manualRedirects: false,
|
|
540
688
|
encodedBodies: false,
|
|
541
689
|
maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,
|
|
542
|
-
crossOrigin: "cors"
|
|
690
|
+
crossOrigin: "cors",
|
|
691
|
+
...Socket ? { websocket: true } : {}
|
|
543
692
|
},
|
|
693
|
+
...Socket ? { websocket: (request) => openWebSocket(Socket, request, headers) } : {},
|
|
544
694
|
async fetch(request) {
|
|
545
695
|
let response;
|
|
546
696
|
try {
|
|
@@ -562,15 +712,15 @@ function fetchTransport(options = {}) {
|
|
|
562
712
|
{ status: 502 }
|
|
563
713
|
);
|
|
564
714
|
}
|
|
565
|
-
const
|
|
715
|
+
const headers2 = [];
|
|
566
716
|
response.headers.forEach((value, name) => {
|
|
567
|
-
|
|
717
|
+
headers2.push([name, value]);
|
|
568
718
|
});
|
|
569
719
|
const body = chunks(response.body, options.bodyIdleMs ?? BODY_IDLE_MS);
|
|
570
720
|
return {
|
|
571
721
|
status: response.status,
|
|
572
722
|
statusText: response.statusText,
|
|
573
|
-
headers,
|
|
723
|
+
headers: headers2,
|
|
574
724
|
body,
|
|
575
725
|
cancel: async () => {
|
|
576
726
|
await body.return(void 0);
|
|
@@ -580,7 +730,7 @@ function fetchTransport(options = {}) {
|
|
|
580
730
|
}
|
|
581
731
|
};
|
|
582
732
|
}
|
|
583
|
-
function serveTransport(peer, transport) {
|
|
733
|
+
function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS } = {}) {
|
|
584
734
|
const open = /* @__PURE__ */ new Map();
|
|
585
735
|
const fail = (nid, e) => {
|
|
586
736
|
open.delete(nid);
|
|
@@ -621,6 +771,96 @@ function serveTransport(peer, transport) {
|
|
|
621
771
|
fail(call.nid, e);
|
|
622
772
|
}
|
|
623
773
|
};
|
|
774
|
+
const sockets = /* @__PURE__ */ new Map();
|
|
775
|
+
const credit = /* @__PURE__ */ new Map();
|
|
776
|
+
const refill = (nid, n, closing = false) => {
|
|
777
|
+
const flow = credit.get(nid);
|
|
778
|
+
if (!flow) return;
|
|
779
|
+
flow.inflight -= n;
|
|
780
|
+
if (closing && !flow.closing) {
|
|
781
|
+
flow.closing = true;
|
|
782
|
+
flow.deadline();
|
|
783
|
+
}
|
|
784
|
+
flow.wake?.();
|
|
785
|
+
};
|
|
786
|
+
const openSocket = async (call) => {
|
|
787
|
+
const abort = new AbortController();
|
|
788
|
+
open.set(call.nid, { abort });
|
|
789
|
+
try {
|
|
790
|
+
if (!transport.websocket) throw new Error(NO_WEBSOCKET);
|
|
791
|
+
const socket = await transport.websocket({
|
|
792
|
+
url: call.url,
|
|
793
|
+
protocols: call.protocols,
|
|
794
|
+
headers: call.headers,
|
|
795
|
+
signal: abort.signal
|
|
796
|
+
});
|
|
797
|
+
if (!open.delete(call.nid)) {
|
|
798
|
+
socket.close(1e3, "cancelled");
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
sockets.set(call.nid, socket);
|
|
802
|
+
peer.postMessage({ net: "ws-opened", nid: call.nid, protocol: socket.protocol });
|
|
803
|
+
void relay(call.nid, socket);
|
|
804
|
+
} catch (e) {
|
|
805
|
+
fail(call.nid, e);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
const relay = async (nid, socket) => {
|
|
809
|
+
let ending;
|
|
810
|
+
let late;
|
|
811
|
+
const unanswered = new Promise((resolve) => {
|
|
812
|
+
late = () => resolve("late");
|
|
813
|
+
});
|
|
814
|
+
const flow = {
|
|
815
|
+
inflight: 0,
|
|
816
|
+
closing: false,
|
|
817
|
+
deadline: () => AbortSignal.timeout(closeWaitMs).addEventListener("abort", late, { once: true })
|
|
818
|
+
};
|
|
819
|
+
credit.set(nid, flow);
|
|
820
|
+
const timedOut = { code: 1006, reason: "the far end did not answer the close" };
|
|
821
|
+
let oversize = false;
|
|
822
|
+
try {
|
|
823
|
+
const messages = socket.messages[Symbol.asyncIterator]();
|
|
824
|
+
for (; ; ) {
|
|
825
|
+
const next = await Promise.race([messages.next(), unanswered]);
|
|
826
|
+
if (next === "late" || next.done) break;
|
|
827
|
+
const data = next.value;
|
|
828
|
+
if (flow.closing) continue;
|
|
829
|
+
if (payloadSize(data) > MESSAGE_LIMIT) {
|
|
830
|
+
oversize = true;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
if (typeof data === "string") peer.postMessage({ net: "ws-message", nid, data });
|
|
834
|
+
else {
|
|
835
|
+
const bytes = data.slice();
|
|
836
|
+
peer.postMessage({ net: "ws-message", nid, data: bytes }, [bytes.buffer]);
|
|
837
|
+
}
|
|
838
|
+
flow.inflight += payloadSize(data);
|
|
839
|
+
while (flow.inflight > INBOUND_LIMIT && !flow.closing) {
|
|
840
|
+
await new Promise((resolve) => {
|
|
841
|
+
flow.wake = resolve;
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (oversize) socket.close(1e3, "a message is too big");
|
|
846
|
+
ending = oversize ? { code: 1009, reason: `a message is over ${MESSAGE_LIMIT} bytes` } : await Promise.race([socket.closed, unanswered.then(() => timedOut)]);
|
|
847
|
+
} catch (e) {
|
|
848
|
+
socket.close(1e3, "the relay failed");
|
|
849
|
+
ending = { code: 1011, reason: e instanceof Error ? e.message : String(e) };
|
|
850
|
+
}
|
|
851
|
+
sockets.delete(nid);
|
|
852
|
+
credit.delete(nid);
|
|
853
|
+
peer.postMessage({ net: "ws-closed", nid, ...ending });
|
|
854
|
+
};
|
|
855
|
+
const sendOn = async (call) => {
|
|
856
|
+
const socket = sockets.get(call.nid);
|
|
857
|
+
if (!socket) return;
|
|
858
|
+
socket.send(call.data);
|
|
859
|
+
while (socket.buffered > SEND_BUFFER && sockets.has(call.nid)) {
|
|
860
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
861
|
+
}
|
|
862
|
+
peer.postMessage({ net: "ws-acked", nid: call.nid, n: payloadSize(call.data) });
|
|
863
|
+
};
|
|
624
864
|
const read = async (nid) => {
|
|
625
865
|
const body = open.get(nid)?.body;
|
|
626
866
|
if (!body) return;
|
|
@@ -638,6 +878,8 @@ function serveTransport(peer, transport) {
|
|
|
638
878
|
}
|
|
639
879
|
};
|
|
640
880
|
const drop = (nid) => {
|
|
881
|
+
sockets.get(nid)?.close(1e3, "cancelled");
|
|
882
|
+
refill(nid, 0, true);
|
|
641
883
|
const entry = open.get(nid);
|
|
642
884
|
open.delete(nid);
|
|
643
885
|
entry?.abort.abort();
|
|
@@ -647,10 +889,16 @@ function serveTransport(peer, transport) {
|
|
|
647
889
|
answer(call) {
|
|
648
890
|
if (call.net === "fetch") void start(call);
|
|
649
891
|
else if (call.net === "read") void read(call.nid);
|
|
892
|
+
else if (call.net === "ws-open") void openSocket(call);
|
|
893
|
+
else if (call.net === "ws-send") void sendOn(call);
|
|
894
|
+
else if (call.net === "ws-close") {
|
|
895
|
+
sockets.get(call.nid)?.close(call.code, call.reason);
|
|
896
|
+
refill(call.nid, 0, true);
|
|
897
|
+
} else if (call.net === "ws-consumed") refill(call.nid, call.n);
|
|
650
898
|
else drop(call.nid);
|
|
651
899
|
},
|
|
652
900
|
close() {
|
|
653
|
-
for (const nid of [...open.keys()]) drop(nid);
|
|
901
|
+
for (const nid of [...open.keys(), ...sockets.keys()]) drop(nid);
|
|
654
902
|
}
|
|
655
903
|
};
|
|
656
904
|
}
|
|
@@ -670,12 +918,15 @@ var RemoteTransport = class {
|
|
|
670
918
|
port;
|
|
671
919
|
waiting = /* @__PURE__ */ new Map();
|
|
672
920
|
nextId = 0;
|
|
921
|
+
sockets = /* @__PURE__ */ new Map();
|
|
673
922
|
failure;
|
|
674
923
|
constructor(port, traits) {
|
|
675
924
|
this.port = port;
|
|
676
925
|
this.traits = traits;
|
|
677
926
|
}
|
|
678
927
|
receive(reply) {
|
|
928
|
+
const socket = this.sockets.get(reply.nid);
|
|
929
|
+
if (socket && this.toSocket(reply, socket)) return;
|
|
679
930
|
const waiter = this.waiting.get(reply.nid);
|
|
680
931
|
if (!waiter) return;
|
|
681
932
|
this.waiting.delete(reply.nid);
|
|
@@ -683,10 +934,22 @@ var RemoteTransport = class {
|
|
|
683
934
|
waiter.reject(new TransportError(reply.message, reply.status, reply.code));
|
|
684
935
|
} else waiter.resolve(reply);
|
|
685
936
|
}
|
|
937
|
+
toSocket(reply, socket) {
|
|
938
|
+
if (reply.net === "ws-message") socket.queue.push(reply.data);
|
|
939
|
+
else if (reply.net === "ws-acked") socket.acked(reply.n);
|
|
940
|
+
else if (reply.net === "ws-closed") {
|
|
941
|
+
this.sockets.delete(reply.nid);
|
|
942
|
+
socket.queue.end({ code: reply.code, reason: reply.reason });
|
|
943
|
+
} else return false;
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
686
946
|
fail(error) {
|
|
687
947
|
this.failure = error;
|
|
688
948
|
for (const waiter of this.waiting.values()) waiter.reject(error);
|
|
689
949
|
this.waiting.clear();
|
|
950
|
+
for (const socket of this.sockets.values())
|
|
951
|
+
socket.queue.end({ code: 1011, reason: error.message });
|
|
952
|
+
this.sockets.clear();
|
|
690
953
|
}
|
|
691
954
|
ask(call) {
|
|
692
955
|
if (this.failure) return Promise.reject(this.failure);
|
|
@@ -765,6 +1028,46 @@ var RemoteTransport = class {
|
|
|
765
1028
|
}
|
|
766
1029
|
};
|
|
767
1030
|
}
|
|
1031
|
+
async websocket(request) {
|
|
1032
|
+
const nid = ++this.nextId;
|
|
1033
|
+
const { signal } = request;
|
|
1034
|
+
signal.throwIfAborted();
|
|
1035
|
+
const abort = () => this.cancel(nid);
|
|
1036
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1037
|
+
let opened;
|
|
1038
|
+
try {
|
|
1039
|
+
opened = await this.ask({
|
|
1040
|
+
net: "ws-open",
|
|
1041
|
+
nid,
|
|
1042
|
+
url: request.url,
|
|
1043
|
+
protocols: request.protocols,
|
|
1044
|
+
headers: request.headers
|
|
1045
|
+
});
|
|
1046
|
+
} finally {
|
|
1047
|
+
signal.removeEventListener("abort", abort);
|
|
1048
|
+
}
|
|
1049
|
+
const close = (code, reason) => this.port.postMessage({ net: "ws-close", nid, code, reason });
|
|
1050
|
+
const queue = new MessageQueue(
|
|
1051
|
+
() => close(1e3, "a message is too big"),
|
|
1052
|
+
Number.POSITIVE_INFINITY,
|
|
1053
|
+
(n) => this.port.postMessage({ net: "ws-consumed", nid, n })
|
|
1054
|
+
);
|
|
1055
|
+
let unacked = 0;
|
|
1056
|
+
this.sockets.set(nid, { queue, acked: (n) => void (unacked -= n) });
|
|
1057
|
+
return {
|
|
1058
|
+
protocol: opened.protocol,
|
|
1059
|
+
get buffered() {
|
|
1060
|
+
return unacked;
|
|
1061
|
+
},
|
|
1062
|
+
send: (data) => {
|
|
1063
|
+
unacked += payloadSize(data);
|
|
1064
|
+
this.port.postMessage({ net: "ws-send", nid, data });
|
|
1065
|
+
},
|
|
1066
|
+
messages: queue,
|
|
1067
|
+
close,
|
|
1068
|
+
closed: queue.closed
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
768
1071
|
};
|
|
769
1072
|
|
|
770
1073
|
// src/client/protocol.ts
|