@ai-ecoverse/slicc-kernel 1.47.1 → 1.47.2
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
CHANGED
|
@@ -225,7 +225,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
225
225
|
- `wait(pid)` resolves with `{ status }` once a child ends, or `{ status: 128 + signal, signal }` when a signal ended it, without blocking the program; `kill(pid, signal)` signals any process, by number or as `'SIGTERM'` (the default);
|
|
226
226
|
- `net`: `listen({ port, host, backlog })` and `connect({ host, port, signal })` (`host` defaults to `127.0.0.1`) on the kernel's loopback, the same sockets WASIX programs use, so a JS server is reached by other processes, by the page as `<port>.kernel.localhost` (`loopbackFetch`, `dial`) and by attached clients. A listener has its `port` (as bound, so port `0` picks a free one), `accept({ signal })`, `close()` and `for await` over its connections. A connection has `local` and `remote` addresses, `readable` and `writable` web streams (closing the writable shuts down the sending side, so the peer reads the end), and `close()`. The loopback is IPv4 (`127.0.0.1` by default, or `0.0.0.0`), so an IPv6 host such as `::1` fails with `EADDRNOTAVAIL` for both `listen` and `connect`, and binding and connecting fail as they would for a WASIX program (`EADDRINUSE`, `ECONNREFUSED`, `EADDRNOTAVAIL`). A blocked accept, read or write waits without holding anything in the kernel, so aborting it or closing the socket leaves nothing behind. A write to a connection the peer closed fails with `EPIPE` and no `SIGPIPE`;
|
|
227
227
|
- `websocket(url, { protocols, headers, signal })`, a WebSocket over the kernel's transport (its `websocket`, so `fetchTransport({ webSocket: true })` in a browser), shaped like `WebSocketStream`. It resolves once the socket is open, with `url`, the `protocol` the far end chose (always one the program offered: a transport that hands back a socket with another fails the open with `EPROTO`, while browsers and Node's `WebSocket`, which `fetchTransport()` uses, fail such a handshake themselves without saying why, so the open fails with `ECONNREFUSED`), a `readable` of messages (`string` for text, `Uint8Array` for binary), a `writable` for sending them, `closed` (the `{ code, reason }` of the close, from either side, whether or not the program reads) and `close({ code, reason })`. An `http(s):` URL is taken as `ws(s):`. Sending waits while more than 1 MiB is buffered. A signal aborts the open, and after it the socket. A transport without WebSockets rejects with a `TypeError` whose `cause.code` is `ENOTSUP`, and one that cannot connect with `ECONNREFUSED`;
|
|
228
|
-
- `fetch(input, init)`, the web's `fetch` over the kernel's transport, the one the proxy uses, so the embedder's transport, its WebSocket and proxy settings and the kernel's routes apply. The response body streams as the program reads it. The request body is read whole first, since a transport takes it whole (streaming it is [#238](https://github.com/ai-ecoverse/slicc-kernel/issues/238)), and one larger than the transport's `maxRequestBody` is answered `413`, as through the proxy. A signal aborts the request at any stage. Redirects follow the fetch rules: at most 20, a 301 or 302 after a `POST` and a 303 turn into a `GET` without a body, `Authorization` is dropped across origins, and `redirect: 'manual'` returns the redirect itself. On a transport that follows redirects on its own (`fetchTransport()` in a browser) only `'follow'` works: `'manual'` and `'error'` reject with a `TypeError` whose `cause.code` is `ENOTSUP`, since such a transport never shows the program a redirect. A status the transport chose comes back as a response whose body is the reason. An unreachable host, or no transport at all, rejects with a `TypeError` whose `cause.code` is `ECONNREFUSED` or `ENETUNREACH`, as Node's `fetch` does. The global `fetch` stays withheld;
|
|
228
|
+
- `fetch(input, init)`, the web's `fetch` over the kernel's transport, the one the proxy uses, so the embedder's transport, its WebSocket and proxy settings and the kernel's routes apply. The response body streams as the program reads it. The request body is read whole first, since a transport takes it whole (streaming it is [#238](https://github.com/ai-ecoverse/slicc-kernel/issues/238)), and one larger than the transport's `maxRequestBody` is answered `413`, as through the proxy. A signal aborts the request at any stage. Redirects follow the fetch rules: at most 20, a 301 or 302 after a `POST` and a 303 turn into a `GET` without a body, `Authorization` is dropped across origins, and `redirect: 'manual'` returns the redirect itself. On a transport that follows redirects on its own (`fetchTransport()` in a browser) only `'follow'` works: `'manual'` and `'error'` reject with a `TypeError` whose `cause.code` is `ENOTSUP`, since such a transport never shows the program a redirect. A status the transport chose comes back as a response whose body is the reason. An unreachable host, or no transport at all, rejects with a `TypeError` whose `cause.code` is `ECONNREFUSED` or `ENETUNREACH`, as Node's `fetch` does. A `data:` URL is answered by `fetch` itself, as the fetch standard decodes it (a `GET` only, as in Node), without a transport; any other scheme than `http:` and `https:` rejects with a `TypeError` whose `cause.code` is `EPROTONOSUPPORT`, at the start as for a redirect target. The global `fetch` stays withheld;
|
|
229
229
|
- `tty`, for programs on a terminal: `getAttr(fd)` and `setAttr(fd, termios)` read and set its termios (`{ c_iflag, c_oflag, c_cflag, c_lflag, c_cc }`, Linux values), `setRaw(fd, raw)` switches raw mode on as libuv (and Node's `setRawMode`) does (no echo, no line editing, no `^C` signal, each key read as it comes) and off again to the termios it had before, `size(fd)` answers `{ columns, rows }`, and `foreground(fd)` and `setForeground(fd, pgrp)` get and set the foreground process group, under the same job-control rules as other programs. A window resize arrives as `SIGWINCH` through `signals`. A terminal the program left raw is set back when the program ends, whether it returns, exits or throws (a program killed by a signal cannot do so). On a descriptor that is not a terminal, each call fails with `ENOTTY`;
|
|
230
230
|
- `exit(status)`, which ends the program once its pending writes are done.
|
|
231
231
|
|
|
@@ -8889,6 +8889,58 @@ function rewrite(hop, status2, location) {
|
|
|
8889
8889
|
body: toGet ? void 0 : hop.body
|
|
8890
8890
|
};
|
|
8891
8891
|
}
|
|
8892
|
+
function isHex(byte) {
|
|
8893
|
+
return byte !== void 0 && /^[0-9A-Fa-f]$/.test(String.fromCharCode(byte));
|
|
8894
|
+
}
|
|
8895
|
+
function percentDecode(input) {
|
|
8896
|
+
const bytes = new TextEncoder().encode(input);
|
|
8897
|
+
const out = new Uint8Array(bytes.length);
|
|
8898
|
+
let at = 0;
|
|
8899
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
8900
|
+
const byte = bytes[i];
|
|
8901
|
+
if (byte === 37 && isHex(bytes[i + 1]) && isHex(bytes[i + 2])) {
|
|
8902
|
+
out[at++] = Number.parseInt(
|
|
8903
|
+
String.fromCharCode(bytes[i + 1], bytes[i + 2]),
|
|
8904
|
+
16
|
|
8905
|
+
);
|
|
8906
|
+
i += 2;
|
|
8907
|
+
} else out[at++] = byte;
|
|
8908
|
+
}
|
|
8909
|
+
return out.subarray(0, at);
|
|
8910
|
+
}
|
|
8911
|
+
function latin1(bytes) {
|
|
8912
|
+
let text = "";
|
|
8913
|
+
for (let at = 0; at < bytes.length; at += SLICE) {
|
|
8914
|
+
text += String.fromCharCode(...bytes.subarray(at, at + SLICE));
|
|
8915
|
+
}
|
|
8916
|
+
return text;
|
|
8917
|
+
}
|
|
8918
|
+
function dataResponse(request) {
|
|
8919
|
+
const url = withoutFragment(new URL(request.url));
|
|
8920
|
+
const rest2 = url.slice("data:".length);
|
|
8921
|
+
const comma = rest2.indexOf(",");
|
|
8922
|
+
if (comma < 0) throw failed2(new JsCallError("EINVAL", "a data: URL without a comma"));
|
|
8923
|
+
let type = rest2.slice(0, comma).replace(/^[\t\n\f\r ]+|[\t\n\f\r ]+$/g, "");
|
|
8924
|
+
let body = percentDecode(rest2.slice(comma + 1));
|
|
8925
|
+
const base642 = BASE64.exec(type);
|
|
8926
|
+
if (base642) {
|
|
8927
|
+
type = type.slice(0, base642.index).replace(/[\t\n\f\r ]+$/, "");
|
|
8928
|
+
let binary;
|
|
8929
|
+
try {
|
|
8930
|
+
binary = atob(latin1(body));
|
|
8931
|
+
} catch {
|
|
8932
|
+
throw failed2(new JsCallError("EINVAL", "a data: URL whose base64 does not decode"));
|
|
8933
|
+
}
|
|
8934
|
+
body = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
8935
|
+
}
|
|
8936
|
+
if (type.startsWith(";")) type = `text/plain${type}`;
|
|
8937
|
+
if (!MIME.test(type)) type = "text/plain;charset=US-ASCII";
|
|
8938
|
+
const response = new Response(body, {
|
|
8939
|
+
status: 200,
|
|
8940
|
+
headers: { "content-type": type }
|
|
8941
|
+
});
|
|
8942
|
+
return withMeta(response, url, false);
|
|
8943
|
+
}
|
|
8892
8944
|
function withMeta(response, url, redirected) {
|
|
8893
8945
|
const clone = response.clone.bind(response);
|
|
8894
8946
|
Object.defineProperty(response, "url", { value: url });
|
|
@@ -9037,6 +9089,11 @@ function fetchOp(kernel) {
|
|
|
9037
9089
|
const request = new Request(input, init);
|
|
9038
9090
|
const { signal } = request;
|
|
9039
9091
|
signal.throwIfAborted();
|
|
9092
|
+
const { protocol } = new URL(request.url);
|
|
9093
|
+
if (protocol === "data:") return dataResponse(request);
|
|
9094
|
+
if (protocol !== "http:" && protocol !== "https:") {
|
|
9095
|
+
throw failed2(new JsCallError("EPROTONOSUPPORT", protocol));
|
|
9096
|
+
}
|
|
9040
9097
|
const known = await traitsOf().catch((err) => {
|
|
9041
9098
|
throw failed2(err);
|
|
9042
9099
|
});
|
|
@@ -9069,7 +9126,7 @@ function fetchOp(kernel) {
|
|
|
9069
9126
|
}
|
|
9070
9127
|
};
|
|
9071
9128
|
}
|
|
9072
|
-
var CHUNK, MAX_REDIRECTS, NULL_BODY, REDIRECTS, BODY_HEADERS, DECODED, CREDENTIALS, OVER;
|
|
9129
|
+
var CHUNK, MAX_REDIRECTS, NULL_BODY, REDIRECTS, BODY_HEADERS, DECODED, CREDENTIALS, OVER, MIME, BASE64, SLICE;
|
|
9073
9130
|
var init_js_fetch = __esm({
|
|
9074
9131
|
"src/process/js/js-fetch.ts"() {
|
|
9075
9132
|
"use strict";
|
|
@@ -9088,6 +9145,9 @@ var init_js_fetch = __esm({
|
|
|
9088
9145
|
DECODED = /* @__PURE__ */ new Set(["gzip", "x-gzip", "deflate"]);
|
|
9089
9146
|
CREDENTIALS = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2"]);
|
|
9090
9147
|
OVER = /* @__PURE__ */ Symbol("over");
|
|
9148
|
+
MIME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+(?:[\t ]*;.*)?$/s;
|
|
9149
|
+
BASE64 = /; *base64$/i;
|
|
9150
|
+
SLICE = 32768;
|
|
9091
9151
|
}
|
|
9092
9152
|
});
|
|
9093
9153
|
|
|
@@ -9590,7 +9650,7 @@ function ttyOps(kernel) {
|
|
|
9590
9650
|
}
|
|
9591
9651
|
};
|
|
9592
9652
|
const restore = async () => {
|
|
9593
|
-
for (const [fd, original] of [...saved]) {
|
|
9653
|
+
for (const [fd, original] of [...saved].reverse()) {
|
|
9594
9654
|
saved.delete(fd);
|
|
9595
9655
|
await setAttr(fd, original).catch(() => void 0);
|
|
9596
9656
|
}
|