@ai-ecoverse/slicc-kernel 1.6.1 → 1.7.1

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
@@ -67,6 +67,21 @@ Starts `argv` as the session leader on a new terminal and resolves once it is ru
67
67
 
68
68
  Stops the kernel worker and every process. Pending and later calls reject.
69
69
 
70
+ ### Headless in Node, for tests
71
+
72
+ `@ai-ecoverse/slicc-kernel/node` runs the same kernel in Node without a browser, so packages built for SLICC can test against it. It is a testing entry, not a supported runtime: there is no OPFS and no isolation, and nothing beyond what its tests use is promised.
73
+
74
+ ```js
75
+ import { createNodeKernel, nodeTransport } from '@ai-ecoverse/slicc-kernel/node';
76
+
77
+ const kernel = await createNodeKernel({ network: { transport: nodeTransport() } });
78
+ await kernel.writeFile('/node_modules/@ai-ecoverse/wasm-bash/package.json', manifest);
79
+ const { status, stdout } = await kernel.run(['bash', '-c', 'echo hi'], { cwd: '/home' });
80
+ kernel.terminate();
81
+ ```
82
+
83
+ `createNodeKernel({ root, modules, env, network, worker })` takes the options of `createKernel` except `metadata`. `root` is an in-memory directory by default (`memoryRoot()` makes another), and POSIX metadata stays in memory. Processes and threads run on `worker_threads`. The kernel has `run`, `openTerminal` and `terminate` as above, plus `root`, `writeFile(path, data)` (creating the parent directories) and `readFile(path)` to put files in place and read results. `nodeTransport()` is `fetchTransport()` with Node's `fetch`, which no CORS binds (`crossOrigin: 'any'`).
84
+
70
85
  ## Commands
71
86
 
72
87
  Commands come from installed packages in npm's `node_modules` layout: every `<modules>/<name>/package.json` and `<modules>/@scope/<name>/package.json` with a `slicc.commands` block. An Emscripten program names its glue and module, for example bash's:
@@ -119,8 +134,8 @@ await kernel.run(['curl', '-sS', 'https://registry.npmjs.org/@ai-ecoverse/wasm-b
119
134
  - **Sockets**: `AF_INET` stream sockets on `127.x` and `AF_UNIX` sockets are kernel descriptors on one loopback network per kernel, so processes can serve and connect to each other. They survive `dup`, `fork` and `exec` and work with `select` and `poll`, like pipe ends. A bind of an `AF_UNIX` socket creates its path in OPFS. Other addresses are unreachable: programs reach the outside world only through the proxy.
120
135
  - **The proxy** listens on `127.0.0.1:3128` from the first connection on. It takes absolute-form `http:` requests and `CONNECT` tunnels, which it terminates with a certificate for the requested host, issued by the kernel's own CA, then hands each request to the transport. Programs start with `http_proxy`, `https_proxy` (and the upper-case names) pointing at it, `no_proxy` covering loopback, and `SSL_CERT_FILE`, `CURL_CA_BUNDLE` and `GIT_SSL_CAINFO` pointing at the CA certificate in `/etc/ssl/certs/slicc-kernel-ca.pem`; variables in `env` override them. The CA's key never leaves WebCrypto; it is kept in IndexedDB (`<metadata>-ca`, or in memory with `metadata: false`).
121
136
  - **TLS** needs [`@ai-ecoverse/wasm-tls-engine`](https://www.npmjs.com/package/@ai-ecoverse/wasm-tls-engine) installed under `modules`, like a command. Without it, `CONNECT` is answered `501` with the reason.
122
- - **The transport** is an object on the page with `traits` (`manualRedirects`, `encodedBodies`, `maxRequestBody`, and `crossOrigin`: `'cors'` or `'any'`) and `fetch(request)`, which answers `{ status, statusText, headers, body, cancel }` with `body` an async iterable of `Uint8Array`. The kernel pulls the body one chunk at a time, so a slow program slows the download. A rejection with a numeric `status` is answered with that status; any other with `502`. `fetchTransport()` is the plain `fetch` of the page, bound by CORS (`registry.npmjs.org` and jsDelivr allow it); an embedder with a way around CORS passes its own.
123
- - **`localProxyTransport({ url, key })`** sends every request to a local proxy on loopback, such as [slicc-node](https://github.com/ai-ecoverse/slicc-node) or slicc-swift, which fetches it without CORS. Redirects reach the program unfollowed, with every `Set-Cookie`; bodies arrive decoded. `probeLocalProxy({ url, key })` resolves with the proxy's capabilities, or `null` when it is gone or refuses the key, so a page can fall back to `fetchTransport()`. Both take a `fetch` option, and the transport a `maxRequestBody` (default 64 MiB).
137
+ - **The transport** is an object on the page with `traits` (`manualRedirects`, `encodedBodies`, `maxRequestBody`, and `crossOrigin`: `'cors'` or `'any'`) and `fetch(request)`, which answers `{ status, statusText, headers, body, cancel }` with `body` an async iterable of `Uint8Array`. The kernel pulls the body one chunk at a time, so a slow program slows the download. A rejection with a numeric `status` is answered with that status; any other with `502`. `fetchTransport()` is the plain `fetch` of the page, bound by CORS; its `hint` option is appended to the message of a request it could not make, so a program's 502 can name a way around CORS (`registry.npmjs.org` and jsDelivr allow it); an embedder with a way around CORS passes its own. A response body that delivers no data for `bodyIdleMs` (default 5 minutes; `0` or `Infinity` turns it off) fails with `ETIMEDOUT` instead of waiting forever, which matters because Chrome under heavy load can stop handing a page the rest of a body. A slow body that keeps delivering is never cut off.
138
+ - **`localProxyTransport({ url, key })`** sends every request to a local proxy on loopback, such as [slicc-node](https://github.com/ai-ecoverse/slicc-node) or slicc-swift, which fetches it without CORS. Redirects reach the program unfollowed, with every `Set-Cookie`; bodies arrive decoded. `probeLocalProxy({ url, key })` resolves with the proxy's capabilities, or `null` when it is gone or refuses the key, so a page can fall back to `fetchTransport()`. `checkLocalProxy({ url, key })` says why: it resolves with `{ state }`, which is `ready` (with `probe`), `blocked` (the user denied Chrome's Local Network Access permission, `loopback-network`), `unreachable` (nothing answered; its `permission` is `granted`, `prompt` or `unknown`, and with `prompt` the page cannot tell a dismissed prompt from a proxy that is not running, since Chrome asks only once it has a connection), `refused` (with `status` and `error`, such as a stale key) or `incompatible`. The permission is not consulted when the page itself is on loopback. Both take a `fetch` option, and the transport `maxRequestBody` (default 64 MiB) and `bodyIdleMs` like `fetchTransport`.
124
139
  - **Without a transport**, every request is answered `502` with `slicc-kernel: no network transport (createKernel({ network: { transport } }))`.
125
140
 
126
141
  Process workers can also use the transport directly, without a socket: the `net-request`, `net-read` and `net-close` syscalls (`Module.sliccKernel.http` in a process) open a request and read its body in pieces. Requests are per process and closed when it exits, and a request body has no size limit of its own (the transport's `maxRequestBody` applies to the proxy). `net-traits` answers the transport's `traits` with `crossOrigin`: `'cors'` when it is bound by CORS like `fetchTransport()`, `'any'` (the default for a transport that does not say) when it is not; without a transport it fails with `ENETUNREACH`.
@@ -170,7 +185,7 @@ The kernel is ported from SLICC's `packages/webapp/src/kernel/` with the browser
170
185
  - `src/process/`: the runtime inside each process worker. It evaluates the Emscripten glue, mounts the live VFS, routes descriptors through the kernel and implements `fork` by copying the whole linear memory into a new worker (Asyncify). `fork` is the only call allowed to suspend through Asyncify: an `fsync` that Emscripten made asynchronous is answered synchronously by the file's own stream instead. `src/process/wasi/` runs WASI and WASIX programs.
171
186
  - `src/realm/`: the synchronous bridge (`SharedArrayBuffer` + `Atomics.wait`) and the live Emscripten filesystem on top of it.
172
187
  - `src/fs/`: the OPFS filesystem and the virtual command directories.
173
- - `src/launcher.ts`, `src/commands.ts`, `src/serve.ts`, `src/index.ts`: command resolution, the kernel worker protocol and the page API. An embedder that uses `Launcher` directly, without `createKernel`, calls `await launcher.prepare()` first: it creates `/tmp` and `/home` and writes the CA certificate.
188
+ - `src/launcher.ts`, `src/commands.ts`, `src/serve.ts`, `src/index.ts`: command resolution, the kernel worker protocol and the page API; `src/node.ts`, `src/node-process-worker.ts` and `src/node/` the headless Node entry. An embedder that uses `Launcher` directly, without `createKernel`, calls `await launcher.prepare()` first: it creates `/tmp` and `/home` and writes the CA certificate.
174
189
  - `src/kernel/net/`: the proxy, HTTP/1.1, TLS termination and the local CA, and the bridge to the page's transport; `src/transport.ts` and `src/local-proxy-transport.ts` are the page side.
175
190
 
176
191
 
package/dist/index.js CHANGED
@@ -1,3 +1,23 @@
1
+ // src/body-idle.ts
2
+ var BODY_IDLE_MS = 5 * 60 * 1e3;
3
+ function idleFailure(ms) {
4
+ return Object.assign(new Error(`the response body sent no data for ${ms} ms`), {
5
+ code: "ETIMEDOUT"
6
+ });
7
+ }
8
+ async function readWithin(reader, ms) {
9
+ if (!(ms > 0 && ms < Number.POSITIVE_INFINITY)) return reader.read();
10
+ let timer;
11
+ const idle = new Promise((_, reject) => {
12
+ timer = setTimeout(() => reject(idleFailure(ms)), ms);
13
+ });
14
+ try {
15
+ return await Promise.race([reader.read(), idle]);
16
+ } finally {
17
+ clearTimeout(timer);
18
+ }
19
+ }
20
+
1
21
  // src/transport.ts
2
22
  var MAX_REQUEST_BODY = 64 * 1024 * 1024;
3
23
  function joinHeaders(headers) {
@@ -10,11 +30,11 @@ function joinHeaders(headers) {
10
30
  }
11
31
  return out;
12
32
  }
13
- async function* chunks(body) {
33
+ async function* chunks(body, idleMs) {
14
34
  if (!body) return;
15
35
  const reader = body.getReader();
16
36
  try {
17
- for (let next = await reader.read(); !next.done; next = await reader.read()) {
37
+ for (let next = await readWithin(reader, idleMs); !next.done; next = await readWithin(reader, idleMs)) {
18
38
  if (next.value.byteLength > 0) yield next.value;
19
39
  }
20
40
  } finally {
@@ -45,7 +65,9 @@ function fetchTransport(options = {}) {
45
65
  } catch (e) {
46
66
  if (request.signal.aborted) throw e;
47
67
  throw Object.assign(
48
- new Error(`fetch ${request.url} failed (unreachable, or not allowed by CORS)`),
68
+ new Error(
69
+ `fetch ${request.url} failed (unreachable, or not allowed by CORS)${options.hint ? `: ${options.hint}` : ""}`
70
+ ),
49
71
  { status: 502 }
50
72
  );
51
73
  }
@@ -53,7 +75,7 @@ function fetchTransport(options = {}) {
53
75
  response.headers.forEach((value, name) => {
54
76
  headers.push([name, value]);
55
77
  });
56
- const body = chunks(response.body);
78
+ const body = chunks(response.body, options.bodyIdleMs ?? BODY_IDLE_MS);
57
79
  return {
58
80
  status: response.status,
59
81
  statusText: response.statusText,
@@ -71,12 +93,13 @@ function serveTransport(peer, transport) {
71
93
  const open = /* @__PURE__ */ new Map();
72
94
  const fail = (nid, e) => {
73
95
  open.delete(nid);
74
- const status = e.status;
96
+ const { status, code } = e;
75
97
  peer.postMessage({
76
98
  net: "error",
77
99
  nid,
78
100
  message: e instanceof Error ? e.message : String(e),
79
- ...typeof status === "number" ? { status } : {}
101
+ ...typeof status === "number" ? { status } : {},
102
+ ...typeof code === "string" ? { code } : {}
80
103
  });
81
104
  };
82
105
  const start = async (call) => {
@@ -146,6 +169,8 @@ var PATH = "/api/fetch-proxy";
146
169
  var RAW_CONTENT_TYPE = "application/vnd.slicc.raw-fetch";
147
170
  var MAX_HEAD = 1024 * 1024;
148
171
  var MAX_REQUEST_BODY2 = 64 * 1024 * 1024;
172
+ var PERMISSIONS = ["loopback-network", "local-network-access"];
173
+ var LOOPBACK_PAGE = /^(127\.\d+\.\d+\.\d+|localhost|\[::1\])$/;
149
174
  var failure = (message, status = 502) => Object.assign(new Error(`local proxy: ${message}`), { status });
150
175
  function encodeHead(url, method, headers) {
151
176
  return JSON.stringify({ url, method, headers }).replace(
@@ -159,15 +184,27 @@ function hopHeaders(options, extra) {
159
184
  function send(options) {
160
185
  return options.fetch ?? globalThis.fetch.bind(globalThis);
161
186
  }
162
- async function refusal(response) {
187
+ async function refusalMessage(response) {
163
188
  const text = await response.text().catch(() => "");
164
- let message = text || response.statusText || `answered ${response.status}`;
165
189
  try {
166
190
  const parsed = JSON.parse(text);
167
- if (typeof parsed.error === "string") message = parsed.error;
191
+ if (typeof parsed.error === "string") return parsed.error;
168
192
  } catch {
169
193
  }
170
- return failure(message, response.ok ? 502 : response.status);
194
+ return text || response.statusText || `answered ${response.status}`;
195
+ }
196
+ async function refusal(response) {
197
+ return failure(await refusalMessage(response), response.ok ? 502 : response.status);
198
+ }
199
+ async function permission(permissions) {
200
+ if (!permissions) return "unknown";
201
+ for (const name of PERMISSIONS) {
202
+ try {
203
+ return (await permissions.query({ name })).state;
204
+ } catch {
205
+ }
206
+ }
207
+ return "unknown";
171
208
  }
172
209
  function isHead(value) {
173
210
  const head = value;
@@ -181,7 +218,7 @@ function concat(a, b) {
181
218
  out.set(b, a.byteLength);
182
219
  return out;
183
220
  }
184
- async function readHead(reader) {
221
+ async function readHead(reader, idleMs) {
185
222
  let buffer = new Uint8Array(0);
186
223
  for (; ; ) {
187
224
  if (buffer.byteLength >= 4) {
@@ -197,42 +234,60 @@ async function readHead(reader) {
197
234
  return { head, rest: buffer.subarray(4 + length) };
198
235
  }
199
236
  }
200
- const next = await reader.read();
237
+ const next = await readWithin(reader, idleMs);
201
238
  if (next.done) throw failure("closed before the response head");
202
239
  buffer = concat(buffer, next.value);
203
240
  }
204
241
  }
205
- async function* chunks2(rest, reader) {
242
+ async function* chunks2(rest, reader, idleMs) {
206
243
  try {
207
244
  if (rest.byteLength > 0) yield rest;
208
- for (let next = await reader.read(); !next.done; next = await reader.read()) {
245
+ for (let next = await readWithin(reader, idleMs); !next.done; next = await readWithin(reader, idleMs)) {
209
246
  if (next.value.byteLength > 0) yield next.value;
210
247
  }
211
248
  } finally {
212
249
  await reader.cancel().catch(() => void 0);
213
250
  }
214
251
  }
215
- async function probeLocalProxy(options) {
252
+ async function checkLocalProxy(options) {
253
+ const onLoopback = LOOPBACK_PAGE.test(globalThis.location?.hostname ?? "");
254
+ const permissions = onLoopback ? void 0 : options.permissions ?? globalThis.navigator?.permissions;
255
+ if (await permission(permissions) === "denied") return { state: "blocked" };
256
+ let response;
216
257
  try {
217
- const response = await send(options)(new URL(PATH, options.url), {
258
+ response = await send(options)(new URL(PATH, options.url), {
218
259
  method: "POST",
219
260
  headers: hopHeaders(options, { "X-Slicc-Raw-Probe": "1" }),
220
261
  credentials: "omit",
221
262
  mode: "cors"
222
263
  });
223
- if (!response.ok) return null;
224
- const reply = await response.json();
225
- if (typeof reply.rawFetch !== "number" || reply.rawFetch < 1) return null;
226
- if (typeof reply.requestBodyStreaming !== "boolean") return null;
227
- if (typeof reply.maxRequestBodyBytes !== "number") return null;
264
+ } catch {
265
+ const now = await permission(permissions);
266
+ if (now === "denied") return { state: "blocked" };
228
267
  return {
268
+ state: "unreachable",
269
+ permission: now === "granted" || now === "prompt" ? now : "unknown"
270
+ };
271
+ }
272
+ if (!response.ok) {
273
+ return { state: "refused", status: response.status, error: await refusalMessage(response) };
274
+ }
275
+ const reply = await response.json().catch(() => null);
276
+ if (!reply || typeof reply.rawFetch !== "number" || reply.rawFetch < 1 || typeof reply.requestBodyStreaming !== "boolean" || typeof reply.maxRequestBodyBytes !== "number") {
277
+ return { state: "incompatible" };
278
+ }
279
+ return {
280
+ state: "ready",
281
+ probe: {
229
282
  rawFetch: reply.rawFetch,
230
283
  requestBodyStreaming: reply.requestBodyStreaming,
231
284
  maxRequestBodyBytes: reply.maxRequestBodyBytes
232
- };
233
- } catch {
234
- return null;
235
- }
285
+ }
286
+ };
287
+ }
288
+ async function probeLocalProxy(options) {
289
+ const status = await checkLocalProxy(options);
290
+ return status.state === "ready" ? status.probe : null;
236
291
  }
237
292
  function localProxyTransport(options) {
238
293
  const endpoint = new URL(PATH, options.url);
@@ -264,14 +319,15 @@ function localProxyTransport(options) {
264
319
  throw await refusal(response);
265
320
  }
266
321
  const reader = response.body.getReader();
322
+ const idleMs = options.bodyIdleMs ?? BODY_IDLE_MS;
267
323
  let framed;
268
324
  try {
269
- framed = await readHead(reader);
325
+ framed = await readHead(reader, idleMs);
270
326
  } catch (e) {
271
327
  await reader.cancel().catch(() => void 0);
272
328
  throw e;
273
329
  }
274
- const body = chunks2(framed.rest, reader);
330
+ const body = chunks2(framed.rest, reader, idleMs);
275
331
  return {
276
332
  status: framed.head.status,
277
333
  statusText: framed.head.statusText,
@@ -397,6 +453,7 @@ async function createKernel(options = {}) {
397
453
  };
398
454
  }
399
455
  export {
456
+ checkLocalProxy,
400
457
  createKernel,
401
458
  fetchTransport,
402
459
  localProxyTransport,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/transport.ts", "../src/local-proxy-transport.ts", "../src/index.ts"],
4
- "sourcesContent": ["import type { TransportCall, TransportReply } from './kernel/net/remote-transport.ts';\nimport type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n RealmTransportTraits,\n} from './kernel/net/transport.ts';\n\nexport type {\n HeaderList,\n RealmTransport as NetworkTransport,\n RealmTransportRequest as NetworkRequest,\n RealmTransportResponse as NetworkResponse,\n RealmTransportTraits as NetworkTraits,\n};\n\nexport interface FetchTransportOptions {\n fetch?: typeof globalThis.fetch;\n maxRequestBody?: number;\n}\n\nconst MAX_REQUEST_BODY = 64 * 1024 * 1024;\n\nfunction joinHeaders(headers: HeaderList): Headers {\n const out = new Headers();\n for (const [name, value] of headers) {\n try {\n out.append(name, value);\n } catch {}\n }\n return out;\n}\n\nasync function* chunks(body: ReadableStream<Uint8Array> | null): AsyncGenerator<Uint8Array> {\n if (!body) return;\n const reader = body.getReader();\n try {\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n if (next.value.byteLength > 0) yield next.value;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n}\n\nexport function fetchTransport(options: FetchTransportOptions = {}): RealmTransport {\n const send = options.fetch ?? globalThis.fetch.bind(globalThis);\n return {\n traits: {\n manualRedirects: false,\n encodedBodies: false,\n maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,\n crossOrigin: 'cors',\n },\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n let response: Response;\n try {\n response = await send(request.url, {\n method: request.method,\n headers: joinHeaders(request.headers),\n ...(request.body ? { body: request.body as BodyInit } : {}),\n signal: request.signal,\n redirect: 'follow',\n credentials: 'omit',\n mode: 'cors',\n });\n } catch (e) {\n if (request.signal.aborted) throw e;\n throw Object.assign(\n new Error(`fetch ${request.url} failed (unreachable, or not allowed by CORS)`),\n { status: 502 }\n );\n }\n const headers: Array<[string, string]> = [];\n response.headers.forEach((value, name) => {\n headers.push([name, value]);\n });\n const body = chunks(response.body);\n return {\n status: response.status,\n statusText: response.statusText,\n headers,\n body,\n cancel: async () => {\n await body.return(undefined);\n await response.body?.cancel().catch(() => undefined);\n },\n };\n },\n };\n}\n\nexport interface TransportPeer {\n postMessage(message: TransportReply, transfer?: Transferable[]): void;\n}\n\nexport interface TransportServer {\n answer(call: TransportCall): void;\n close(): void;\n}\n\nexport function serveTransport(peer: TransportPeer, transport: RealmTransport): TransportServer {\n const open = new Map<\n number,\n { abort: AbortController; body?: AsyncIterator<Uint8Array>; response?: RealmTransportResponse }\n >();\n const fail = (nid: number, e: unknown) => {\n open.delete(nid);\n const status = (e as { status?: unknown }).status;\n peer.postMessage({\n net: 'error',\n nid,\n message: e instanceof Error ? e.message : String(e),\n ...(typeof status === 'number' ? { status } : {}),\n });\n };\n const start = async (call: Extract<TransportCall, { net: 'fetch' }>) => {\n const abort = new AbortController();\n const entry: { abort: AbortController; body?: AsyncIterator<Uint8Array> } = { abort };\n open.set(call.nid, entry);\n try {\n const response = await transport.fetch({\n url: call.url,\n method: call.method,\n headers: call.headers,\n ...(call.body ? { body: call.body } : {}),\n signal: abort.signal,\n });\n if (!open.has(call.nid)) {\n await response.cancel();\n return;\n }\n open.set(call.nid, { abort, body: response.body[Symbol.asyncIterator](), response });\n peer.postMessage({\n net: 'head',\n nid: call.nid,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n } catch (e) {\n fail(call.nid, e);\n }\n };\n const read = async (nid: number) => {\n const body = open.get(nid)?.body;\n if (!body) return;\n try {\n const next = await body.next();\n if (next.done) {\n open.delete(nid);\n peer.postMessage({ net: 'end', nid });\n } else {\n const bytes = next.value.slice();\n peer.postMessage({ net: 'chunk', nid, bytes }, [bytes.buffer]);\n }\n } catch (e) {\n fail(nid, e);\n }\n };\n const drop = (nid: number) => {\n const entry = open.get(nid);\n open.delete(nid);\n entry?.abort.abort();\n void entry?.response?.cancel().catch(() => undefined);\n };\n return {\n answer(call) {\n if (call.net === 'fetch') void start(call);\n else if (call.net === 'read') void read(call.nid);\n else drop(call.nid);\n },\n close() {\n for (const nid of [...open.keys()]) drop(nid);\n },\n };\n}\n", "import type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n} from './kernel/net/transport.ts';\n\nexport interface LocalProxyOptions {\n url: string;\n key: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface LocalProxyTransportOptions extends LocalProxyOptions {\n maxRequestBody?: number;\n}\n\nexport interface LocalProxyProbe {\n rawFetch: number;\n requestBodyStreaming: boolean;\n maxRequestBodyBytes: number;\n}\n\nconst PATH = '/api/fetch-proxy';\nconst RAW_CONTENT_TYPE = 'application/vnd.slicc.raw-fetch';\nconst MAX_HEAD = 1024 * 1024;\nconst MAX_REQUEST_BODY = 64 * 1024 * 1024;\n\ninterface ResponseHead {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n}\n\nconst failure = (message: string, status = 502) =>\n Object.assign(new Error(`local proxy: ${message}`), { status });\n\nfunction encodeHead(url: string, method: string, headers: HeaderList): string {\n return JSON.stringify({ url, method, headers }).replace(\n /[\\u007f-\uFFFF]/g,\n (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`\n );\n}\n\nfunction hopHeaders(options: LocalProxyOptions, extra: Record<string, string>): Headers {\n return new Headers({ 'X-Bridge-Token': options.key, ...extra });\n}\n\nfunction send(options: LocalProxyOptions) {\n return options.fetch ?? globalThis.fetch.bind(globalThis);\n}\n\nasync function refusal(response: Response): Promise<Error> {\n const text = await response.text().catch(() => '');\n let message = text || response.statusText || `answered ${response.status}`;\n try {\n const parsed = JSON.parse(text) as { error?: unknown };\n if (typeof parsed.error === 'string') message = parsed.error;\n } catch {}\n return failure(message, response.ok ? 502 : response.status);\n}\n\nfunction isHead(value: unknown): value is ResponseHead {\n const head = value as Partial<ResponseHead> | null;\n return (\n !!head &&\n typeof head.status === 'number' &&\n typeof head.statusText === 'string' &&\n Array.isArray(head.headers) &&\n head.headers.every(\n (pair) =>\n Array.isArray(pair) &&\n pair.length === 2 &&\n typeof pair[0] === 'string' &&\n typeof pair[1] === 'string'\n )\n );\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.byteLength + b.byteLength);\n out.set(a);\n out.set(b, a.byteLength);\n return out;\n}\n\nasync function readHead(\n reader: ReadableStreamDefaultReader<Uint8Array>\n): Promise<{ head: ResponseHead; rest: Uint8Array }> {\n let buffer: Uint8Array = new Uint8Array(0);\n for (;;) {\n if (buffer.byteLength >= 4) {\n const length = new DataView(buffer.buffer, buffer.byteOffset, 4).getUint32(0);\n if (length > MAX_HEAD) throw failure(`response head of ${length} bytes`);\n if (buffer.byteLength >= 4 + length) {\n let head: unknown;\n try {\n head = JSON.parse(new TextDecoder().decode(buffer.subarray(4, 4 + length)));\n } catch {}\n if (!isHead(head)) throw failure('malformed response head');\n return { head, rest: buffer.subarray(4 + length) };\n }\n }\n const next = await reader.read();\n if (next.done) throw failure('closed before the response head');\n buffer = concat(buffer, next.value);\n }\n}\n\nasync function* chunks(\n rest: Uint8Array,\n reader: ReadableStreamDefaultReader<Uint8Array>\n): AsyncGenerator<Uint8Array> {\n try {\n if (rest.byteLength > 0) yield rest;\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n if (next.value.byteLength > 0) yield next.value;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n}\n\nexport async function probeLocalProxy(options: LocalProxyOptions): Promise<LocalProxyProbe | null> {\n try {\n const response = await send(options)(new URL(PATH, options.url), {\n method: 'POST',\n headers: hopHeaders(options, { 'X-Slicc-Raw-Probe': '1' }),\n credentials: 'omit',\n mode: 'cors',\n });\n if (!response.ok) return null;\n const reply = (await response.json()) as Partial<LocalProxyProbe>;\n if (typeof reply.rawFetch !== 'number' || reply.rawFetch < 1) return null;\n if (typeof reply.requestBodyStreaming !== 'boolean') return null;\n if (typeof reply.maxRequestBodyBytes !== 'number') return null;\n return {\n rawFetch: reply.rawFetch,\n requestBodyStreaming: reply.requestBodyStreaming,\n maxRequestBodyBytes: reply.maxRequestBodyBytes,\n };\n } catch {\n return null;\n }\n}\n\nexport function localProxyTransport(options: LocalProxyTransportOptions): RealmTransport {\n const endpoint = new URL(PATH, options.url);\n return {\n traits: {\n manualRedirects: true,\n encodedBodies: false,\n maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,\n },\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n let response: Response;\n try {\n response = await send(options)(endpoint, {\n method: 'POST',\n headers: hopHeaders(options, {\n 'X-Slicc-Raw-Request': encodeHead(request.url, request.method, request.headers),\n }),\n ...(request.body ? { body: request.body as BodyInit } : {}),\n signal: request.signal,\n credentials: 'omit',\n mode: 'cors',\n });\n } catch (e) {\n if (request.signal.aborted) throw e;\n throw failure(`${endpoint.origin} is unreachable or refuses this origin`);\n }\n const type = response.headers.get('content-type') ?? '';\n if (response.status !== 200 || !type.startsWith(RAW_CONTENT_TYPE) || !response.body) {\n throw await refusal(response);\n }\n const reader = response.body.getReader();\n let framed: { head: ResponseHead; rest: Uint8Array };\n try {\n framed = await readHead(reader);\n } catch (e) {\n await reader.cancel().catch(() => undefined);\n throw e;\n }\n const body = chunks(framed.rest, reader);\n return {\n status: framed.head.status,\n statusText: framed.head.statusText,\n headers: framed.head.headers,\n body,\n cancel: async () => {\n await body.return(undefined);\n await reader.cancel().catch(() => undefined);\n },\n };\n },\n };\n}\n", "import type { TransportCall } from './kernel/net/remote-transport.ts';\nimport type { KernelCall, TerminalAction } from './serve.ts';\nimport { type NetworkTransport, serveTransport } from './transport.ts';\n\nexport {\n type LocalProxyOptions,\n type LocalProxyProbe,\n type LocalProxyTransportOptions,\n localProxyTransport,\n probeLocalProxy,\n} from './local-proxy-transport.ts';\nexport {\n type FetchTransportOptions,\n fetchTransport,\n type HeaderList,\n type NetworkRequest,\n type NetworkResponse,\n type NetworkTraits,\n type NetworkTransport,\n} from './transport.ts';\n\nexport interface NetworkOptions {\n transport?: NetworkTransport;\n}\n\nexport interface KernelOptions {\n root?: FileSystemDirectoryHandle;\n modules?: string;\n env?: Record<string, string>;\n metadata?: string | false;\n worker?: string | URL;\n network?: NetworkOptions;\n}\n\nexport interface RunOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n onStdout?: (text: string) => void;\n onStderr?: (text: string) => void;\n}\n\nexport interface RunResult {\n status: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface Kernel {\n run(argv: string[], options?: RunOptions): Promise<RunResult>;\n openTerminal(argv: string[], options?: TerminalOptions): Promise<Terminal>;\n terminate(): void;\n}\n\nexport interface TerminalOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n onData?: (bytes: Uint8Array) => void;\n}\n\nexport type TerminalSignal =\n | 'SIGINT'\n | 'SIGTSTP'\n | 'SIGQUIT'\n | 'SIGHUP'\n | 'SIGTERM'\n | 'SIGKILL'\n | 'SIGCONT';\n\nexport interface Terminal {\n readonly pid: number;\n readonly exited: Promise<number>;\n onData: ((bytes: Uint8Array) => void) | null;\n write(data: string | Uint8Array): void;\n resize(cols: number, rows: number): void;\n signal(name: TerminalSignal): void;\n close(): void;\n}\n\ninterface Reply {\n id: number;\n result?: unknown;\n error?: string;\n fd?: 1 | 2;\n bytes?: Uint8Array;\n started?: number;\n}\n\ninterface Pending {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n output?: (fd: 1 | 2, bytes: Uint8Array) => void;\n started?: (pid: number) => void;\n}\n\ninterface Handlers {\n output?: Pending['output'];\n started?: Pending['started'];\n}\n\nconst ISOLATION =\n 'slicc-kernel needs a cross-origin isolated page (Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp or credentialless)';\n\nfunction streamer(callback: ((text: string) => void) | undefined) {\n const decoder = new TextDecoder();\n return (bytes: Uint8Array) => callback?.(decoder.decode(bytes, { stream: true }));\n}\n\nexport async function createKernel(options: KernelOptions = {}): Promise<Kernel> {\n if (!globalThis.crossOriginIsolated) throw new Error(ISOLATION);\n const url = options.worker ?? new URL('./kernel-worker.js', import.meta.url);\n const worker = new Worker(url, { type: 'module', name: 'slicc-kernel' });\n const pending = new Map<number, Pending>();\n let nextId = 0;\n let failure: Error | undefined;\n\n const fail = (error: Error) => {\n failure = error;\n bridge?.close();\n for (const call of pending.values()) call.reject(error);\n pending.clear();\n };\n const transport = options.network?.transport;\n const bridge = transport ? serveTransport(worker, transport) : undefined;\n worker.addEventListener('message', ({ data }: MessageEvent<Reply | TransportCall>) => {\n if ('net' in data) return bridge?.answer(data);\n const call = pending.get(data.id);\n if (!call) return;\n if (data.fd !== undefined) return call.output?.(data.fd, data.bytes as Uint8Array);\n if (data.started !== undefined) return call.started?.(data.started);\n pending.delete(data.id);\n if (data.error !== undefined) call.reject(new Error(data.error));\n else call.resolve(data.result);\n });\n worker.addEventListener('error', (event) => {\n event.preventDefault();\n fail(new Error(`slicc-kernel worker failed: ${event.message}`));\n });\n\n function request(\n req: KernelCall,\n handlers: Handlers = {}\n ): { id: number; done: Promise<unknown> } {\n const id = ++nextId;\n if (failure) return { id, done: Promise.reject(failure) };\n const done = new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject, ...handlers });\n worker.postMessage({ ...req, id });\n });\n return { id, done };\n }\n\n const call = (req: KernelCall, handlers?: Handlers) => request(req, handlers).done;\n\n async function openTerminal(argv: string[], opts: TerminalOptions = {}): Promise<Terminal> {\n const { onData, ...options } = opts;\n let listener = onData ?? null;\n const backlog: Uint8Array[] = [];\n const started = Promise.withResolvers<number>();\n const { id, done } = request(\n { op: 'open-terminal', argv, options },\n {\n output: (_fd, bytes) => (listener ? listener(bytes) : backlog.push(bytes)),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const send = (action: TerminalAction) =>\n void call({ op: 'terminal', terminal: id, ...action }).catch(() => undefined);\n return {\n pid,\n exited: done as Promise<number>,\n get onData() {\n return listener;\n },\n set onData(next) {\n listener = next;\n if (next) for (const bytes of backlog.splice(0)) next(bytes);\n },\n write: (data) =>\n send({\n action: 'write',\n bytes: typeof data === 'string' ? new TextEncoder().encode(data) : data,\n }),\n resize: (cols, rows) => send({ action: 'resize', cols, rows }),\n signal: (name) => send({ action: 'signal', signal: name }),\n close: () => send({ action: 'close' }),\n };\n }\n\n await call({\n op: 'init',\n ...(options.root ? { root: options.root } : {}),\n ...(options.modules ? { modules: options.modules } : {}),\n ...(options.env ? { env: options.env } : {}),\n ...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n ...(transport ? { transport: transport.traits } : {}),\n });\n\n return {\n async run(argv, runOptions = {}) {\n const { onStdout, onStderr, stdin, ...rest } = runOptions;\n const streams = { 1: streamer(onStdout), 2: streamer(onStderr) };\n const input = typeof stdin === 'string' ? new TextEncoder().encode(stdin) : stdin;\n const result = (await call(\n { op: 'run', argv, options: { ...rest, ...(input ? { stdin: input } : {}) } },\n { output: (fd, bytes) => streams[fd](bytes) }\n )) as { status: number; stdout: Uint8Array; stderr: Uint8Array };\n const decoder = new TextDecoder();\n return {\n status: result.status,\n stdout: decoder.decode(result.stdout),\n stderr: decoder.decode(result.stderr),\n };\n },\n openTerminal,\n terminate() {\n fail(new Error('slicc-kernel terminated'));\n worker.terminate();\n },\n };\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/body-idle.ts", "../src/transport.ts", "../src/local-proxy-transport.ts", "../src/index.ts"],
4
+ "sourcesContent": ["export const BODY_IDLE_MS = 5 * 60 * 1000;\n\nexport function idleFailure(ms: number): Error {\n return Object.assign(new Error(`the response body sent no data for ${ms} ms`), {\n code: 'ETIMEDOUT',\n });\n}\n\nexport async function readWithin<T>(\n reader: ReadableStreamDefaultReader<T>,\n ms: number\n): Promise<ReadableStreamReadResult<T>> {\n if (!(ms > 0 && ms < Number.POSITIVE_INFINITY)) return reader.read();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const idle = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(idleFailure(ms)), ms);\n });\n try {\n return await Promise.race([reader.read(), idle]);\n } finally {\n clearTimeout(timer);\n }\n}\n", "import { BODY_IDLE_MS, readWithin } from './body-idle.ts';\nimport type { TransportCall, TransportReply } from './kernel/net/remote-transport.ts';\nimport type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n RealmTransportTraits,\n} from './kernel/net/transport.ts';\n\nexport type {\n HeaderList,\n RealmTransport as NetworkTransport,\n RealmTransportRequest as NetworkRequest,\n RealmTransportResponse as NetworkResponse,\n RealmTransportTraits as NetworkTraits,\n};\n\nexport interface FetchTransportOptions {\n fetch?: typeof globalThis.fetch;\n maxRequestBody?: number;\n hint?: string;\n bodyIdleMs?: number;\n}\n\nconst MAX_REQUEST_BODY = 64 * 1024 * 1024;\n\nfunction joinHeaders(headers: HeaderList): Headers {\n const out = new Headers();\n for (const [name, value] of headers) {\n try {\n out.append(name, value);\n } catch {}\n }\n return out;\n}\n\nasync function* chunks(\n body: ReadableStream<Uint8Array> | null,\n idleMs: number\n): AsyncGenerator<Uint8Array> {\n if (!body) return;\n const reader = body.getReader();\n try {\n for (\n let next = await readWithin(reader, idleMs);\n !next.done;\n next = await readWithin(reader, idleMs)\n ) {\n if (next.value.byteLength > 0) yield next.value;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n}\n\nexport function fetchTransport(options: FetchTransportOptions = {}): RealmTransport {\n const send = options.fetch ?? globalThis.fetch.bind(globalThis);\n return {\n traits: {\n manualRedirects: false,\n encodedBodies: false,\n maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,\n crossOrigin: 'cors',\n },\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n let response: Response;\n try {\n response = await send(request.url, {\n method: request.method,\n headers: joinHeaders(request.headers),\n ...(request.body ? { body: request.body as BodyInit } : {}),\n signal: request.signal,\n redirect: 'follow',\n credentials: 'omit',\n mode: 'cors',\n });\n } catch (e) {\n if (request.signal.aborted) throw e;\n throw Object.assign(\n new Error(\n `fetch ${request.url} failed (unreachable, or not allowed by CORS)${options.hint ? `: ${options.hint}` : ''}`\n ),\n { status: 502 }\n );\n }\n const headers: Array<[string, string]> = [];\n response.headers.forEach((value, name) => {\n headers.push([name, value]);\n });\n const body = chunks(response.body, options.bodyIdleMs ?? BODY_IDLE_MS);\n return {\n status: response.status,\n statusText: response.statusText,\n headers,\n body,\n cancel: async () => {\n await body.return(undefined);\n await response.body?.cancel().catch(() => undefined);\n },\n };\n },\n };\n}\n\nexport interface TransportPeer {\n postMessage(message: TransportReply, transfer?: Transferable[]): void;\n}\n\nexport interface TransportServer {\n answer(call: TransportCall): void;\n close(): void;\n}\n\nexport function serveTransport(peer: TransportPeer, transport: RealmTransport): TransportServer {\n const open = new Map<\n number,\n { abort: AbortController; body?: AsyncIterator<Uint8Array>; response?: RealmTransportResponse }\n >();\n const fail = (nid: number, e: unknown) => {\n open.delete(nid);\n const { status, code } = e as { status?: unknown; code?: unknown };\n peer.postMessage({\n net: 'error',\n nid,\n message: e instanceof Error ? e.message : String(e),\n ...(typeof status === 'number' ? { status } : {}),\n ...(typeof code === 'string' ? { code } : {}),\n });\n };\n const start = async (call: Extract<TransportCall, { net: 'fetch' }>) => {\n const abort = new AbortController();\n const entry: { abort: AbortController; body?: AsyncIterator<Uint8Array> } = { abort };\n open.set(call.nid, entry);\n try {\n const response = await transport.fetch({\n url: call.url,\n method: call.method,\n headers: call.headers,\n ...(call.body ? { body: call.body } : {}),\n signal: abort.signal,\n });\n if (!open.has(call.nid)) {\n await response.cancel();\n return;\n }\n open.set(call.nid, { abort, body: response.body[Symbol.asyncIterator](), response });\n peer.postMessage({\n net: 'head',\n nid: call.nid,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n } catch (e) {\n fail(call.nid, e);\n }\n };\n const read = async (nid: number) => {\n const body = open.get(nid)?.body;\n if (!body) return;\n try {\n const next = await body.next();\n if (next.done) {\n open.delete(nid);\n peer.postMessage({ net: 'end', nid });\n } else {\n const bytes = next.value.slice();\n peer.postMessage({ net: 'chunk', nid, bytes }, [bytes.buffer]);\n }\n } catch (e) {\n fail(nid, e);\n }\n };\n const drop = (nid: number) => {\n const entry = open.get(nid);\n open.delete(nid);\n entry?.abort.abort();\n void entry?.response?.cancel().catch(() => undefined);\n };\n return {\n answer(call) {\n if (call.net === 'fetch') void start(call);\n else if (call.net === 'read') void read(call.nid);\n else drop(call.nid);\n },\n close() {\n for (const nid of [...open.keys()]) drop(nid);\n },\n };\n}\n", "import { BODY_IDLE_MS, readWithin } from './body-idle.ts';\nimport type {\n HeaderList,\n RealmTransport,\n RealmTransportRequest,\n RealmTransportResponse,\n} from './kernel/net/transport.ts';\n\nexport interface LocalProxyOptions {\n url: string;\n key: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface LocalProxyCheckOptions extends LocalProxyOptions {\n permissions?: Pick<Permissions, 'query'>;\n}\n\nexport interface LocalProxyTransportOptions extends LocalProxyOptions {\n maxRequestBody?: number;\n bodyIdleMs?: number;\n}\n\nexport interface LocalProxyProbe {\n rawFetch: number;\n requestBodyStreaming: boolean;\n maxRequestBodyBytes: number;\n}\n\nexport type LocalProxyStatus =\n | { state: 'ready'; probe: LocalProxyProbe }\n | { state: 'blocked' }\n | { state: 'unreachable'; permission: 'granted' | 'prompt' | 'unknown' }\n | { state: 'refused'; status: number; error: string }\n | { state: 'incompatible' };\n\nconst PATH = '/api/fetch-proxy';\nconst RAW_CONTENT_TYPE = 'application/vnd.slicc.raw-fetch';\nconst MAX_HEAD = 1024 * 1024;\nconst MAX_REQUEST_BODY = 64 * 1024 * 1024;\nconst PERMISSIONS = ['loopback-network', 'local-network-access'];\nconst LOOPBACK_PAGE = /^(127\\.\\d+\\.\\d+\\.\\d+|localhost|\\[::1\\])$/;\n\ninterface ResponseHead {\n status: number;\n statusText: string;\n headers: Array<[string, string]>;\n}\n\nconst failure = (message: string, status = 502) =>\n Object.assign(new Error(`local proxy: ${message}`), { status });\n\nfunction encodeHead(url: string, method: string, headers: HeaderList): string {\n return JSON.stringify({ url, method, headers }).replace(\n /[\\u007f-\uFFFF]/g,\n (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`\n );\n}\n\nfunction hopHeaders(options: LocalProxyOptions, extra: Record<string, string>): Headers {\n return new Headers({ 'X-Bridge-Token': options.key, ...extra });\n}\n\nfunction send(options: LocalProxyOptions) {\n return options.fetch ?? globalThis.fetch.bind(globalThis);\n}\n\nasync function refusalMessage(response: Response): Promise<string> {\n const text = await response.text().catch(() => '');\n try {\n const parsed = JSON.parse(text) as { error?: unknown };\n if (typeof parsed.error === 'string') return parsed.error;\n } catch {}\n return text || response.statusText || `answered ${response.status}`;\n}\n\nasync function refusal(response: Response): Promise<Error> {\n return failure(await refusalMessage(response), response.ok ? 502 : response.status);\n}\n\nasync function permission(permissions: Pick<Permissions, 'query'> | undefined): Promise<string> {\n if (!permissions) return 'unknown';\n for (const name of PERMISSIONS) {\n try {\n return (await permissions.query({ name } as PermissionDescriptor)).state;\n } catch {}\n }\n return 'unknown';\n}\n\nfunction isHead(value: unknown): value is ResponseHead {\n const head = value as Partial<ResponseHead> | null;\n return (\n !!head &&\n typeof head.status === 'number' &&\n typeof head.statusText === 'string' &&\n Array.isArray(head.headers) &&\n head.headers.every(\n (pair) =>\n Array.isArray(pair) &&\n pair.length === 2 &&\n typeof pair[0] === 'string' &&\n typeof pair[1] === 'string'\n )\n );\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n const out = new Uint8Array(a.byteLength + b.byteLength);\n out.set(a);\n out.set(b, a.byteLength);\n return out;\n}\n\nasync function readHead(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n idleMs: number\n): Promise<{ head: ResponseHead; rest: Uint8Array }> {\n let buffer: Uint8Array = new Uint8Array(0);\n for (;;) {\n if (buffer.byteLength >= 4) {\n const length = new DataView(buffer.buffer, buffer.byteOffset, 4).getUint32(0);\n if (length > MAX_HEAD) throw failure(`response head of ${length} bytes`);\n if (buffer.byteLength >= 4 + length) {\n let head: unknown;\n try {\n head = JSON.parse(new TextDecoder().decode(buffer.subarray(4, 4 + length)));\n } catch {}\n if (!isHead(head)) throw failure('malformed response head');\n return { head, rest: buffer.subarray(4 + length) };\n }\n }\n const next = await readWithin(reader, idleMs);\n if (next.done) throw failure('closed before the response head');\n buffer = concat(buffer, next.value);\n }\n}\n\nasync function* chunks(\n rest: Uint8Array,\n reader: ReadableStreamDefaultReader<Uint8Array>,\n idleMs: number\n): AsyncGenerator<Uint8Array> {\n try {\n if (rest.byteLength > 0) yield rest;\n for (\n let next = await readWithin(reader, idleMs);\n !next.done;\n next = await readWithin(reader, idleMs)\n ) {\n if (next.value.byteLength > 0) yield next.value;\n }\n } finally {\n await reader.cancel().catch(() => undefined);\n }\n}\n\nexport async function checkLocalProxy(options: LocalProxyCheckOptions): Promise<LocalProxyStatus> {\n const onLoopback = LOOPBACK_PAGE.test(globalThis.location?.hostname ?? '');\n const permissions = onLoopback\n ? undefined\n : (options.permissions ?? globalThis.navigator?.permissions);\n if ((await permission(permissions)) === 'denied') return { state: 'blocked' };\n let response: Response;\n try {\n response = await send(options)(new URL(PATH, options.url), {\n method: 'POST',\n headers: hopHeaders(options, { 'X-Slicc-Raw-Probe': '1' }),\n credentials: 'omit',\n mode: 'cors',\n });\n } catch {\n const now = await permission(permissions);\n if (now === 'denied') return { state: 'blocked' };\n return {\n state: 'unreachable',\n permission: now === 'granted' || now === 'prompt' ? now : 'unknown',\n };\n }\n if (!response.ok) {\n return { state: 'refused', status: response.status, error: await refusalMessage(response) };\n }\n const reply = (await response.json().catch(() => null)) as Partial<LocalProxyProbe> | null;\n if (\n !reply ||\n typeof reply.rawFetch !== 'number' ||\n reply.rawFetch < 1 ||\n typeof reply.requestBodyStreaming !== 'boolean' ||\n typeof reply.maxRequestBodyBytes !== 'number'\n ) {\n return { state: 'incompatible' };\n }\n return {\n state: 'ready',\n probe: {\n rawFetch: reply.rawFetch,\n requestBodyStreaming: reply.requestBodyStreaming,\n maxRequestBodyBytes: reply.maxRequestBodyBytes,\n },\n };\n}\n\nexport async function probeLocalProxy(\n options: LocalProxyCheckOptions\n): Promise<LocalProxyProbe | null> {\n const status = await checkLocalProxy(options);\n return status.state === 'ready' ? status.probe : null;\n}\n\nexport function localProxyTransport(options: LocalProxyTransportOptions): RealmTransport {\n const endpoint = new URL(PATH, options.url);\n return {\n traits: {\n manualRedirects: true,\n encodedBodies: false,\n maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,\n },\n async fetch(request: RealmTransportRequest): Promise<RealmTransportResponse> {\n let response: Response;\n try {\n response = await send(options)(endpoint, {\n method: 'POST',\n headers: hopHeaders(options, {\n 'X-Slicc-Raw-Request': encodeHead(request.url, request.method, request.headers),\n }),\n ...(request.body ? { body: request.body as BodyInit } : {}),\n signal: request.signal,\n credentials: 'omit',\n mode: 'cors',\n });\n } catch (e) {\n if (request.signal.aborted) throw e;\n throw failure(`${endpoint.origin} is unreachable or refuses this origin`);\n }\n const type = response.headers.get('content-type') ?? '';\n if (response.status !== 200 || !type.startsWith(RAW_CONTENT_TYPE) || !response.body) {\n throw await refusal(response);\n }\n const reader = response.body.getReader();\n const idleMs = options.bodyIdleMs ?? BODY_IDLE_MS;\n let framed: { head: ResponseHead; rest: Uint8Array };\n try {\n framed = await readHead(reader, idleMs);\n } catch (e) {\n await reader.cancel().catch(() => undefined);\n throw e;\n }\n const body = chunks(framed.rest, reader, idleMs);\n return {\n status: framed.head.status,\n statusText: framed.head.statusText,\n headers: framed.head.headers,\n body,\n cancel: async () => {\n await body.return(undefined);\n await reader.cancel().catch(() => undefined);\n },\n };\n },\n };\n}\n", "import type { TransportCall } from './kernel/net/remote-transport.ts';\nimport type { KernelCall, TerminalAction } from './serve.ts';\nimport { type NetworkTransport, serveTransport } from './transport.ts';\n\nexport {\n checkLocalProxy,\n type LocalProxyCheckOptions,\n type LocalProxyOptions,\n type LocalProxyProbe,\n type LocalProxyStatus,\n type LocalProxyTransportOptions,\n localProxyTransport,\n probeLocalProxy,\n} from './local-proxy-transport.ts';\nexport {\n type FetchTransportOptions,\n fetchTransport,\n type HeaderList,\n type NetworkRequest,\n type NetworkResponse,\n type NetworkTraits,\n type NetworkTransport,\n} from './transport.ts';\n\nexport interface NetworkOptions {\n transport?: NetworkTransport;\n}\n\nexport interface KernelOptions {\n root?: FileSystemDirectoryHandle;\n modules?: string;\n env?: Record<string, string>;\n metadata?: string | false;\n worker?: string | URL;\n network?: NetworkOptions;\n}\n\nexport interface RunOptions {\n cwd?: string;\n env?: Record<string, string>;\n stdin?: string | Uint8Array;\n onStdout?: (text: string) => void;\n onStderr?: (text: string) => void;\n}\n\nexport interface RunResult {\n status: number;\n stdout: string;\n stderr: string;\n}\n\nexport interface Kernel {\n run(argv: string[], options?: RunOptions): Promise<RunResult>;\n openTerminal(argv: string[], options?: TerminalOptions): Promise<Terminal>;\n terminate(): void;\n}\n\nexport interface TerminalOptions {\n cwd?: string;\n env?: Record<string, string>;\n cols?: number;\n rows?: number;\n onData?: (bytes: Uint8Array) => void;\n}\n\nexport type TerminalSignal =\n | 'SIGINT'\n | 'SIGTSTP'\n | 'SIGQUIT'\n | 'SIGHUP'\n | 'SIGTERM'\n | 'SIGKILL'\n | 'SIGCONT';\n\nexport interface Terminal {\n readonly pid: number;\n readonly exited: Promise<number>;\n onData: ((bytes: Uint8Array) => void) | null;\n write(data: string | Uint8Array): void;\n resize(cols: number, rows: number): void;\n signal(name: TerminalSignal): void;\n close(): void;\n}\n\ninterface Reply {\n id: number;\n result?: unknown;\n error?: string;\n fd?: 1 | 2;\n bytes?: Uint8Array;\n started?: number;\n}\n\ninterface Pending {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n output?: (fd: 1 | 2, bytes: Uint8Array) => void;\n started?: (pid: number) => void;\n}\n\ninterface Handlers {\n output?: Pending['output'];\n started?: Pending['started'];\n}\n\nconst ISOLATION =\n 'slicc-kernel needs a cross-origin isolated page (Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp or credentialless)';\n\nfunction streamer(callback: ((text: string) => void) | undefined) {\n const decoder = new TextDecoder();\n return (bytes: Uint8Array) => callback?.(decoder.decode(bytes, { stream: true }));\n}\n\nexport async function createKernel(options: KernelOptions = {}): Promise<Kernel> {\n if (!globalThis.crossOriginIsolated) throw new Error(ISOLATION);\n const url = options.worker ?? new URL('./kernel-worker.js', import.meta.url);\n const worker = new Worker(url, { type: 'module', name: 'slicc-kernel' });\n const pending = new Map<number, Pending>();\n let nextId = 0;\n let failure: Error | undefined;\n\n const fail = (error: Error) => {\n failure = error;\n bridge?.close();\n for (const call of pending.values()) call.reject(error);\n pending.clear();\n };\n const transport = options.network?.transport;\n const bridge = transport ? serveTransport(worker, transport) : undefined;\n worker.addEventListener('message', ({ data }: MessageEvent<Reply | TransportCall>) => {\n if ('net' in data) return bridge?.answer(data);\n const call = pending.get(data.id);\n if (!call) return;\n if (data.fd !== undefined) return call.output?.(data.fd, data.bytes as Uint8Array);\n if (data.started !== undefined) return call.started?.(data.started);\n pending.delete(data.id);\n if (data.error !== undefined) call.reject(new Error(data.error));\n else call.resolve(data.result);\n });\n worker.addEventListener('error', (event) => {\n event.preventDefault();\n fail(new Error(`slicc-kernel worker failed: ${event.message}`));\n });\n\n function request(\n req: KernelCall,\n handlers: Handlers = {}\n ): { id: number; done: Promise<unknown> } {\n const id = ++nextId;\n if (failure) return { id, done: Promise.reject(failure) };\n const done = new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject, ...handlers });\n worker.postMessage({ ...req, id });\n });\n return { id, done };\n }\n\n const call = (req: KernelCall, handlers?: Handlers) => request(req, handlers).done;\n\n async function openTerminal(argv: string[], opts: TerminalOptions = {}): Promise<Terminal> {\n const { onData, ...options } = opts;\n let listener = onData ?? null;\n const backlog: Uint8Array[] = [];\n const started = Promise.withResolvers<number>();\n const { id, done } = request(\n { op: 'open-terminal', argv, options },\n {\n output: (_fd, bytes) => (listener ? listener(bytes) : backlog.push(bytes)),\n started: started.resolve,\n }\n );\n done.catch(started.reject);\n const pid = await started.promise;\n const send = (action: TerminalAction) =>\n void call({ op: 'terminal', terminal: id, ...action }).catch(() => undefined);\n return {\n pid,\n exited: done as Promise<number>,\n get onData() {\n return listener;\n },\n set onData(next) {\n listener = next;\n if (next) for (const bytes of backlog.splice(0)) next(bytes);\n },\n write: (data) =>\n send({\n action: 'write',\n bytes: typeof data === 'string' ? new TextEncoder().encode(data) : data,\n }),\n resize: (cols, rows) => send({ action: 'resize', cols, rows }),\n signal: (name) => send({ action: 'signal', signal: name }),\n close: () => send({ action: 'close' }),\n };\n }\n\n await call({\n op: 'init',\n ...(options.root ? { root: options.root } : {}),\n ...(options.modules ? { modules: options.modules } : {}),\n ...(options.env ? { env: options.env } : {}),\n ...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n ...(transport ? { transport: transport.traits } : {}),\n });\n\n return {\n async run(argv, runOptions = {}) {\n const { onStdout, onStderr, stdin, ...rest } = runOptions;\n const streams = { 1: streamer(onStdout), 2: streamer(onStderr) };\n const input = typeof stdin === 'string' ? new TextEncoder().encode(stdin) : stdin;\n const result = (await call(\n { op: 'run', argv, options: { ...rest, ...(input ? { stdin: input } : {}) } },\n { output: (fd, bytes) => streams[fd](bytes) }\n )) as { status: number; stdout: Uint8Array; stderr: Uint8Array };\n const decoder = new TextDecoder();\n return {\n status: result.status,\n stdout: decoder.decode(result.stdout),\n stderr: decoder.decode(result.stderr),\n };\n },\n openTerminal,\n terminate() {\n fail(new Error('slicc-kernel terminated'));\n worker.terminate();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["send", "MAX_REQUEST_BODY", "chunks", "failure", "call", "options", "send"]
7
7
  }
@@ -2400,9 +2400,11 @@ function enableNetwork(net, options) {
2400
2400
  // src/kernel/net/remote-transport.ts
2401
2401
  var TransportError = class extends Error {
2402
2402
  status;
2403
- constructor(message2, status) {
2403
+ code;
2404
+ constructor(message2, status, code) {
2404
2405
  super(message2);
2405
2406
  this.status = status;
2407
+ this.code = code;
2406
2408
  }
2407
2409
  };
2408
2410
  var RemoteTransport = class {
@@ -2418,8 +2420,9 @@ var RemoteTransport = class {
2418
2420
  const waiter = this.waiting.get(reply.nid);
2419
2421
  if (!waiter) return;
2420
2422
  this.waiting.delete(reply.nid);
2421
- if (reply.net === "error") waiter.reject(new TransportError(reply.message, reply.status));
2422
- else waiter.resolve(reply);
2423
+ if (reply.net === "error") {
2424
+ waiter.reject(new TransportError(reply.message, reply.status, reply.code));
2425
+ } else waiter.resolve(reply);
2423
2426
  }
2424
2427
  ask(call) {
2425
2428
  return new Promise((resolve, reject) => {
@@ -3472,7 +3475,8 @@ var HttpHandles = class {
3472
3475
  else entry.left = next.value;
3473
3476
  } catch (e) {
3474
3477
  entry.done = true;
3475
- return { ok: false, errno: "EIO", message: message(e) };
3478
+ const timedOut = e?.code === "ETIMEDOUT";
3479
+ return { ok: false, errno: timedOut ? "ETIMEDOUT" : "EIO", message: message(e) };
3476
3480
  }
3477
3481
  }
3478
3482
  const bytes = entry.left.subarray(0, Math.max(0, max));