@polyengine/wasi 0.5.0 → 0.6.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/esm/cli.js +9 -14
- package/esm/cli_stdio.js +10 -9
- package/esm/clocks.js +6 -7
- package/esm/filesystem_node.js +1 -1
- package/esm/filesystem_web.js +3 -2
- package/esm/http.js +13 -12
- package/esm/internal/cli_shared.js +2 -2
- package/esm/internal/fs_provider.js +6 -5
- package/esm/internal/sockets_02.js +12 -17
- package/esm/internal/sockets_03.js +16 -18
- package/esm/internal/sockets_platform.js +1 -1
- package/esm/io.js +34 -34
- package/esm/mod.js +5 -5
- package/esm/sockets.js +7 -7
- package/package.json +2 -2
- package/types/cli.d.ts +3 -5
- package/types/http.d.ts +1 -8
- package/types/internal/cli_shared.d.ts +3 -4
- package/types/internal/fs_provider.d.ts +1 -1
- package/types/internal/sockets_platform.d.ts +21 -24
- package/types/internal/sockets_shared.d.ts +10 -9
- package/types/io.d.ts +13 -12
package/esm/cli.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
// tuple<stream<u8>, future<result<_, error-code>>>` (the tcp-receive
|
|
13
13
|
// tuple shape) and `stdout/stderr.write-via-stream: func(data:
|
|
14
14
|
// stream<u8>) -> future<result<_, error-code>>` (the tcp-send shape:
|
|
15
|
-
// the async method's promise IS the future source,
|
|
15
|
+
// the async method's promise IS the future source, per embedder-api.md
|
|
16
|
+
// §"Streams and futures"); exit
|
|
16
17
|
// gains `exit-with-code: func(status-code: u8)`, and environment's cwd
|
|
17
18
|
// getter is renamed `get-initial-cwd` (0.2 spells it `initial-cwd`).
|
|
18
19
|
import { InputStream, OutputStream } from "./io.js";
|
|
@@ -36,27 +37,22 @@ function concat(chunks) {
|
|
|
36
37
|
* `exit`'s WIT signature is `exit: func(status: result)` — `result` with no
|
|
37
38
|
* type parameters, i.e. `result<_, _>`. Per contracts/embedder-api.md's value
|
|
38
39
|
* table, a `result` in **parameter** (non-return) position is plain nested
|
|
39
|
-
* data: `{ kind: "ok" } | { kind: "err" }` (
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* data: `{ kind: "ok" } | { kind: "err" }` (embedder-api.md §"Naming and
|
|
41
|
+
* casing" — enum/variant case names are data, not `{tag}` wrappers),
|
|
42
|
+
* never a throw. Only a
|
|
42
43
|
* function's own *return*-position result throws/rejects.
|
|
43
44
|
*/
|
|
44
45
|
export function cli(options = {}) {
|
|
45
46
|
const stdoutChunks = [];
|
|
46
47
|
const stderrChunks = [];
|
|
47
|
-
const passthrough = options.passthrough ?? false;
|
|
48
48
|
let exited = false;
|
|
49
49
|
let exitOk;
|
|
50
50
|
let exitCode;
|
|
51
51
|
const stdout = new OutputStream((chunk) => {
|
|
52
52
|
stdoutChunks.push(chunk);
|
|
53
|
-
if (passthrough)
|
|
54
|
-
console.log(new TextDecoder().decode(chunk));
|
|
55
53
|
});
|
|
56
54
|
const stderr = new OutputStream((chunk) => {
|
|
57
55
|
stderrChunks.push(chunk);
|
|
58
|
-
if (passthrough)
|
|
59
|
-
console.error(new TextDecoder().decode(chunk));
|
|
60
56
|
});
|
|
61
57
|
const captured = {
|
|
62
58
|
stdout: () => concat(stdoutChunks),
|
|
@@ -67,12 +63,11 @@ export function cli(options = {}) {
|
|
|
67
63
|
exitOk: () => exitOk,
|
|
68
64
|
exitCode: () => exitCode,
|
|
69
65
|
};
|
|
70
|
-
/** 0.3 write-via-stream into a capture buffer (
|
|
71
|
-
const captureViaStream = (chunks
|
|
66
|
+
/** 0.3 write-via-stream into a capture buffer (the promise IS the future — embedder-api.md §"Streams and futures"). */
|
|
67
|
+
const captureViaStream = (chunks) => async (data) => {
|
|
72
68
|
for await (const chunk of data) {
|
|
73
69
|
const bytes = chunk instanceof Uint8Array ? chunk : Uint8Array.from(chunk);
|
|
74
70
|
chunks.push(bytes);
|
|
75
|
-
mirror?.(new TextDecoder().decode(bytes));
|
|
76
71
|
}
|
|
77
72
|
return { kind: "ok" };
|
|
78
73
|
};
|
|
@@ -139,10 +134,10 @@ export function cli(options = {}) {
|
|
|
139
134
|
],
|
|
140
135
|
},
|
|
141
136
|
"wasi:cli/stdout@0.3": {
|
|
142
|
-
writeViaStream: captureViaStream(stdoutChunks
|
|
137
|
+
writeViaStream: captureViaStream(stdoutChunks),
|
|
143
138
|
},
|
|
144
139
|
"wasi:cli/stderr@0.3": {
|
|
145
|
-
writeViaStream: captureViaStream(stderrChunks
|
|
140
|
+
writeViaStream: captureViaStream(stderrChunks),
|
|
146
141
|
},
|
|
147
142
|
"wasi:cli/terminal-input@0.3": { TerminalInput },
|
|
148
143
|
"wasi:cli/terminal-output@0.3": { TerminalOutput },
|
package/esm/cli_stdio.js
CHANGED
|
@@ -18,15 +18,16 @@
|
|
|
18
18
|
// are SYNC WIT functions. Against capture buffers they degenerate to
|
|
19
19
|
// their non-blocking forms (io.ts base classes, sync fast path); against
|
|
20
20
|
// a REAL stdin/stdout they must genuinely wait, which parks the calling
|
|
21
|
-
// wasm frame through the suspending kernel (embedder-api
|
|
22
|
-
// io.ts marks the blocking declarations on the
|
|
23
|
-
// prototypes; these duck-typed stream impls override
|
|
24
|
-
//
|
|
25
|
-
// leaves auto-select jspi mode on V8 engines, and on engines
|
|
26
|
-
// JSPI a genuine wait raises a clean `NeedsJspi` at the park site.
|
|
27
|
-
// `@0.3` track has no such dependence — its stdio is stream-shaped and
|
|
21
|
+
// wasm frame through the suspending kernel (embedder-api.md §"The WASI
|
|
22
|
+
// parking kernel" — io.ts marks the blocking declarations on the
|
|
23
|
+
// REGISTERED stream prototypes; these duck-typed stream impls override
|
|
24
|
+
// the behavior, and the mark relays). Consequences: guests linking the
|
|
25
|
+
// blocking leaves auto-select jspi mode on V8 engines, and on engines
|
|
26
|
+
// without JSPI a genuine wait raises a clean `NeedsJspi` at the park site.
|
|
27
|
+
// The `@0.3` track has no such dependence — its stdio is stream-shaped and
|
|
28
28
|
// async by construction (`read-via-stream` returns the tcp-receive
|
|
29
|
-
// tuple; `write-via-stream`'s promise is the future source,
|
|
29
|
+
// tuple; `write-via-stream`'s promise is the future source, per
|
|
30
|
+
// embedder-api.md §"Streams and futures").
|
|
30
31
|
//
|
|
31
32
|
// Semantics:
|
|
32
33
|
//
|
|
@@ -110,7 +111,7 @@ export function cliStdio(options = {}) {
|
|
|
110
111
|
const p2Stdout = new SinkOutputStream(stdoutSink);
|
|
111
112
|
const p2Stderr = new SinkOutputStream(stderrSink);
|
|
112
113
|
// 0.3 write-via-stream: drain the guest's stream to the sink; the
|
|
113
|
-
// promise is the future source (
|
|
114
|
+
// promise is the future source (embedder-api.md §"Streams and futures").
|
|
114
115
|
const writeViaStream = (sink) => async (data) => {
|
|
115
116
|
try {
|
|
116
117
|
for await (const chunk of data) {
|
package/esm/clocks.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// `wasi:clocks@0.2` + `wasi:clocks@0.3` (contracts/embedder-api.md
|
|
2
|
-
// §"WASI examination"; the
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
2
|
+
// §"WASI examination"; the union clock: `monotonic-clock@0.3.0` exposes
|
|
3
|
+
// different function sets across the consumer corpus (`wait-for` vs
|
|
4
|
+
// `now`+`wait-until`) at the SAME version string — same track, divergent
|
|
5
|
+
// drafts, served by one union provider per contracts/embedder-api.md
|
|
6
|
+
// §"Version canonicalization").
|
|
7
7
|
// The @0.3 track also carries system-clock (0.3's wall-clock reshape) and
|
|
8
8
|
// the type-only types interface, per the WASI 0.3.1 release WIT.
|
|
9
9
|
import { Pollable } from "./io.js";
|
|
@@ -39,8 +39,7 @@ export function clocks(options = {}) {
|
|
|
39
39
|
nanoseconds: 1_000_000,
|
|
40
40
|
}),
|
|
41
41
|
};
|
|
42
|
-
// The
|
|
43
|
-
// both drafts' functions live on the one `@0.3` track provider; per-leaf
|
|
42
|
+
// The union provider: both drafts' functions live on the one `@0.3` track provider; per-leaf
|
|
44
43
|
// structural resolution (contracts/embedder-api.md §"Version
|
|
45
44
|
// canonicalization") lets each consumer link only the subset it imports.
|
|
46
45
|
const monotonic03 = {
|
package/esm/filesystem_node.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SYNC BY CONSTRUCTION: every backend op uses node's `*Sync` API, so the
|
|
15
15
|
// 0.2 track's sync WIT functions are served without parking — guests run
|
|
16
|
-
// in plain callback mode, no JSPI required (the
|
|
16
|
+
// in plain callback mode, no JSPI required (the park-capable marks stay off; see
|
|
17
17
|
// fs_provider.ts). The 0.3 track returns plain values from async funcs,
|
|
18
18
|
// which the runtime accepts.
|
|
19
19
|
//
|
package/esm/filesystem_web.js
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
// for the enforcement site: it is the provider, not this backend).
|
|
14
14
|
//
|
|
15
15
|
// ASYNC BY CONSTRUCTION: every OPFS op returns a promise, so the 0.2
|
|
16
|
-
// track's sync WIT descriptor methods are marked park-capable (
|
|
17
|
-
// the per-call class prototypes — fs_provider.ts):
|
|
16
|
+
// track's sync WIT descriptor methods are marked park-capable (embedder-api.md
|
|
17
|
+
// §"The WASI parking kernel", on the per-call class prototypes — fs_provider.ts):
|
|
18
|
+
// a p2 guest that
|
|
18
19
|
// touches the filesystem parks through the suspending kernel and needs
|
|
19
20
|
// JSPI; on engines without it a genuine wait raises `NeedsJspi` at the
|
|
20
21
|
// park site. The 0.3 track is async in WIT and needs no parking. (The
|
package/esm/http.js
CHANGED
|
@@ -23,17 +23,18 @@
|
|
|
23
23
|
// VERSION KEYS: 0.3.x releases fold onto the `@0.3` compatibility track
|
|
24
24
|
// (contracts/embedder-api.md §"Version canonicalization"), so the
|
|
25
25
|
// default registration serves every released 0.3.x with one provider —
|
|
26
|
-
// the same flagship track-key pattern as the rest of this package.
|
|
27
|
-
// pre-consolidation rc
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
26
|
+
// the same flagship track-key pattern as the rest of this package. A
|
|
27
|
+
// guest pinned to a pre-consolidation rc SNAPSHOT (`0.3.0-rc-*`, which
|
|
28
|
+
// resolves exact-only) is out of scope for this fragment; an embedder
|
|
29
|
+
// serving one re-keys the `imports` object it gets back from `http()`
|
|
30
|
+
// manually (documented escape, unaffected by this fragment's surface).
|
|
31
31
|
//
|
|
32
32
|
// Body/trailers plumbing is the same stream+future choreography the TCP
|
|
33
33
|
// provider proved: constructors return `[resource, transmission-future]`
|
|
34
|
-
// (the future is a Promise —
|
|
35
|
-
// source), `consume-body` returns
|
|
36
|
-
// guest-abandoned streams are
|
|
34
|
+
// (the future is a Promise — embedder-api.md §"Streams and futures"
|
|
35
|
+
// lowers it as the future source), `consume-body` returns
|
|
36
|
+
// `[stream<u8>, trailers-future]`, and guest-abandoned streams are
|
|
37
|
+
// retired by the runtime's producer-cancellation machinery
|
|
37
38
|
// (`ReadableStream` sources are cancel()ed, which aborts the underlying
|
|
38
39
|
// fetch body).
|
|
39
40
|
//
|
|
@@ -62,7 +63,8 @@
|
|
|
62
63
|
// Error model: `client.send` and the fallible fields/options methods
|
|
63
64
|
// throw branded `ComponentException`s whose payloads use the WIT case
|
|
64
65
|
// names VERBATIM (`DNS-timeout`, `TLS-protocol-error`, `internal-error` —
|
|
65
|
-
//
|
|
66
|
+
// embedder-api.md §"Naming and casing": case names are data, kebab-case
|
|
67
|
+
// as written, including capitals).
|
|
66
68
|
// Fetch failures are TypeErrors with prose; a small sniff table maps the
|
|
67
69
|
// recognizable ones and everything else is `internal-error(message)`.
|
|
68
70
|
import { ComponentException, isComponentException } from "@polyengine/protocol";
|
|
@@ -142,7 +144,6 @@ const decoder = new TextDecoder();
|
|
|
142
144
|
*/
|
|
143
145
|
export function http(options = {}) {
|
|
144
146
|
const onCall = options.onCall ?? (() => { });
|
|
145
|
-
const v = options.version ?? HTTP_TRACK;
|
|
146
147
|
const allowRequest = options.allowRequest;
|
|
147
148
|
// --- fields -----------------------------------------------------------------
|
|
148
149
|
class Fields {
|
|
@@ -713,8 +714,8 @@ export function http(options = {}) {
|
|
|
713
714
|
}
|
|
714
715
|
return {
|
|
715
716
|
imports: {
|
|
716
|
-
|
|
717
|
-
|
|
717
|
+
"wasi:http/types@0.3": { Fields, Request, RequestOptions, Response },
|
|
718
|
+
"wasi:http/client@0.3": { send },
|
|
718
719
|
},
|
|
719
720
|
Fields: Fields,
|
|
720
721
|
Request: Request,
|
|
@@ -15,9 +15,9 @@ export class ExitError extends Error {
|
|
|
15
15
|
this.name = "ExitError";
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
//
|
|
18
|
+
// Brand: an exit unwind propagates out through the embedder and any host
|
|
19
19
|
// frames in between, so it must be recognizable across runtime copies
|
|
20
|
-
// (contracts/embedder-api.md §"Module identity", issue #83).
|
|
20
|
+
// (contracts/embedder-api.md §"Module identity and @polyengine/protocol", issue #83).
|
|
21
21
|
defineBrand(ExitError.prototype, WASI_EXIT);
|
|
22
22
|
/** `terminal-input`/`terminal-output` are opaque resources; never produced (no terminal). */
|
|
23
23
|
export class TerminalInput {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// track gets its OWN resource class per `makeFilesystem` call — a guest
|
|
13
13
|
// links one track and never mixes instances.
|
|
14
14
|
//
|
|
15
|
-
// ERROR SHAPES (
|
|
15
|
+
// ERROR SHAPES (embedder-api.md §"Naming and casing", and the reason this file exists twice
|
|
16
16
|
// over): 0.2's `error-code` is an ENUM — the err payload is the bare
|
|
17
17
|
// kebab-case string ("no-entry") — while 0.3's is a VARIANT (it grew
|
|
18
18
|
// `other(option<string>)`) — the payload is `{ kind: "no-entry" }`.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// code passes through untouched; an unmapped throw would be a trap, so
|
|
22
22
|
// the guards map everything.
|
|
23
23
|
//
|
|
24
|
-
// SYNC vs PARKING (
|
|
24
|
+
// SYNC vs PARKING (embedder-api.md §"The WASI parking kernel"). 0.2 descriptor methods are sync WIT functions.
|
|
25
25
|
// A sync backend (node) returns plain values from every op — no parking,
|
|
26
26
|
// callback-mode guests work untouched. An async backend (OPFS) returns
|
|
27
27
|
// promises, so every backend-touching 0.2 method is wrapped `suspending`
|
|
@@ -212,7 +212,8 @@ function hashIdentity(id) {
|
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
const READ_CHUNK = 65536;
|
|
215
|
-
/** 0.2 methods wrapped `suspending` for async backends (
|
|
215
|
+
/** 0.2 methods wrapped `suspending` for async backends (embedder-api.md
|
|
216
|
+
* §"The WASI parking kernel"; module
|
|
216
217
|
* header). Everything that touches the backend — stream CONSTRUCTION
|
|
217
218
|
* stays plain (the streams themselves park via io.ts's marks). */
|
|
218
219
|
const PARKED_02 = [
|
|
@@ -652,7 +653,7 @@ export function makeFilesystem(backend, preopens, access = {}) {
|
|
|
652
653
|
})();
|
|
653
654
|
return [source, done];
|
|
654
655
|
}
|
|
655
|
-
/** The promise IS the future source (
|
|
656
|
+
/** The promise IS the future source (embedder-api.md §"Streams and futures"): drain the guest's stream. */
|
|
656
657
|
async writeViaStream(data, offset) {
|
|
657
658
|
try {
|
|
658
659
|
requireWritable(err03);
|
|
@@ -849,7 +850,7 @@ export function makeFilesystem(backend, preopens, access = {}) {
|
|
|
849
850
|
}
|
|
850
851
|
}
|
|
851
852
|
// Async backends: mark the 0.2 track's backend-touching methods
|
|
852
|
-
// park-capable on the freshly-minted prototype (module header;
|
|
853
|
+
// park-capable on the freshly-minted prototype (module header; embedder-api.md §"The WASI parking kernel").
|
|
853
854
|
if (!backend.isSync) {
|
|
854
855
|
const proto = Descriptor02.prototype;
|
|
855
856
|
for (const name of PARKED_02) {
|
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
// `accept` returning `would-block` until `subscribe`'s pollable is ready,
|
|
11
11
|
// datagram streams with `receive(max)`/`check-send`+`send` batches — and
|
|
12
12
|
// wasi-libc emulates POSIX blocking by `pollable.block()` (the io.ts
|
|
13
|
-
// parking kernel,
|
|
13
|
+
// parking kernel, embedder-api.md §"The WASI parking kernel"). Socket byte I/O rides `wasi:io/streams@0.2`:
|
|
14
14
|
// wasi-libc links the NON-blocking `input-stream.read` + `subscribe`
|
|
15
15
|
// (never `blocking-read`) and `check-write`/`write`/`blocking-flush` —
|
|
16
16
|
// exactly the surfaces of io.ts's async-backed `FedInputStream` /
|
|
17
17
|
// `SinkOutputStream`, which this module mints over connections. The
|
|
18
18
|
// PARKING therefore happens in `Pollable.block`/`poll` and
|
|
19
|
-
// `blocking-flush`, all already
|
|
19
|
+
// `blocking-flush`, all already park-capable-marked: 0.2 socket guests need JSPI
|
|
20
20
|
// on V8 engines, like the 0.3 track's `listen`.
|
|
21
21
|
//
|
|
22
|
-
// 0.2's `error-code` is an ENUM (bare strings —
|
|
22
|
+
// 0.2's `error-code` is an ENUM (bare strings — embedder-api.md §"Naming and casing"), with a
|
|
23
23
|
// different vocabulary than 0.3's variant: it grew `unknown`,
|
|
24
24
|
// `would-block`, `not-in-progress`, `concurrency-conflict`,
|
|
25
25
|
// `new-socket-limit` and the name-lookup codes, and it lacks
|
|
@@ -314,9 +314,7 @@ export function sockets02(onCall) {
|
|
|
314
314
|
}
|
|
315
315
|
let conn;
|
|
316
316
|
try {
|
|
317
|
-
conn = state.listener.tryAccept
|
|
318
|
-
? undefined
|
|
319
|
-
: state.listener.tryAccept();
|
|
317
|
+
conn = state.listener.tryAccept();
|
|
320
318
|
}
|
|
321
319
|
catch (e) {
|
|
322
320
|
raise02(e, "tcp-socket.accept");
|
|
@@ -447,7 +445,7 @@ export function sockets02(onCall) {
|
|
|
447
445
|
}
|
|
448
446
|
if (this.#state === "listening" && listen !== undefined) {
|
|
449
447
|
const l = listen.listener;
|
|
450
|
-
return new Pollable(() => l.acceptReady
|
|
448
|
+
return new Pollable(() => l.acceptReady(), () => l.waitAccept());
|
|
451
449
|
}
|
|
452
450
|
return new Pollable(); // no pending operation: ready
|
|
453
451
|
}
|
|
@@ -498,9 +496,6 @@ export function sockets02(onCall) {
|
|
|
498
496
|
const conn = this.#conn;
|
|
499
497
|
if (this.#state !== "connected" || conn === undefined)
|
|
500
498
|
return;
|
|
501
|
-
if (conn.setKeepAlive === undefined) {
|
|
502
|
-
throw err02("not-supported", "tcp-socket: no keep-alive control on this backend");
|
|
503
|
-
}
|
|
504
499
|
try {
|
|
505
500
|
conn.setKeepAlive(this.#keepAliveEnabled, Number(this.#keepAliveIdleNs / 1000000n));
|
|
506
501
|
}
|
|
@@ -612,7 +607,7 @@ export function sockets02(onCall) {
|
|
|
612
607
|
this.#streams = streams;
|
|
613
608
|
// OS-level (dis)connect, fire-and-forget: failures surface on the
|
|
614
609
|
// first datagram op (doc comment above).
|
|
615
|
-
if (remoteAddress !== undefined
|
|
610
|
+
if (remoteAddress !== undefined) {
|
|
616
611
|
void conn.connect({
|
|
617
612
|
transport: "udp",
|
|
618
613
|
hostname: ipHostname(remoteAddress),
|
|
@@ -623,7 +618,7 @@ export function sockets02(onCall) {
|
|
|
623
618
|
}
|
|
624
619
|
else if (remoteAddress === undefined && wasConnected) {
|
|
625
620
|
try {
|
|
626
|
-
conn.disconnect
|
|
621
|
+
conn.disconnect();
|
|
627
622
|
}
|
|
628
623
|
catch {
|
|
629
624
|
// Not connected at the OS level (compat backends).
|
|
@@ -713,11 +708,11 @@ export function sockets02(onCall) {
|
|
|
713
708
|
return;
|
|
714
709
|
try {
|
|
715
710
|
if (this.#hopLimit !== undefined)
|
|
716
|
-
conn.setTtl
|
|
711
|
+
conn.setTtl(this.#hopLimit);
|
|
717
712
|
if (this.#recvBuffer !== undefined)
|
|
718
|
-
conn.setRecvBufferSize
|
|
713
|
+
conn.setRecvBufferSize(Number(this.#recvBuffer));
|
|
719
714
|
if (this.#sendBuffer !== undefined)
|
|
720
|
-
conn.setSendBufferSize
|
|
715
|
+
conn.setSendBufferSize(Number(this.#sendBuffer));
|
|
721
716
|
}
|
|
722
717
|
catch (e) {
|
|
723
718
|
raise02(e, "udp-socket (applying cached options)");
|
|
@@ -758,7 +753,7 @@ export function sockets02(onCall) {
|
|
|
758
753
|
while (out.length < max) {
|
|
759
754
|
let item;
|
|
760
755
|
try {
|
|
761
|
-
item = this.#conn.tryReceive
|
|
756
|
+
item = this.#conn.tryReceive();
|
|
762
757
|
}
|
|
763
758
|
catch (e) {
|
|
764
759
|
raise02(e, "incoming-datagram-stream.receive");
|
|
@@ -777,7 +772,7 @@ export function sockets02(onCall) {
|
|
|
777
772
|
subscribe() {
|
|
778
773
|
onCall("incoming-datagram-stream.subscribe");
|
|
779
774
|
const conn = this.#conn;
|
|
780
|
-
return new Pollable(() => conn.receiveReady
|
|
775
|
+
return new Pollable(() => conn.receiveReady(), () => conn.waitReceive());
|
|
781
776
|
}
|
|
782
777
|
[Symbol.dispose]() {
|
|
783
778
|
// The socket owns the OS resources; streams are views.
|
|
@@ -103,7 +103,7 @@ export function sockets03(onCall) {
|
|
|
103
103
|
* the remote and `send` needs no explicit address. An unbound socket
|
|
104
104
|
* implicitly binds to the family wildcard first (wasmtime parity).
|
|
105
105
|
*
|
|
106
|
-
* SUSPENDING (
|
|
106
|
+
* SUSPENDING (embedder-api.md §"The WASI parking kernel"): node's `dgram.connect` settles via callback one
|
|
107
107
|
* tick later, so this sync WIT func parks the calling frame for that
|
|
108
108
|
* tick — the same shape as tcp `listen`.
|
|
109
109
|
*/
|
|
@@ -134,9 +134,6 @@ export function sockets03(onCall) {
|
|
|
134
134
|
}
|
|
135
135
|
this.#applyCachedOptions();
|
|
136
136
|
}
|
|
137
|
-
if (this.#conn.connect === undefined) {
|
|
138
|
-
throw componentError({ kind: "not-supported" }, "udp-socket.connect: this host's datagram backend has no connected mode");
|
|
139
|
-
}
|
|
140
137
|
try {
|
|
141
138
|
await this.#conn.connect({
|
|
142
139
|
transport: "udp",
|
|
@@ -155,7 +152,7 @@ export function sockets03(onCall) {
|
|
|
155
152
|
throw componentError({ kind: "invalid-state" }, "udp-socket.disconnect: the socket is not connected");
|
|
156
153
|
}
|
|
157
154
|
try {
|
|
158
|
-
this.#conn.disconnect
|
|
155
|
+
this.#conn.disconnect();
|
|
159
156
|
}
|
|
160
157
|
catch (e) {
|
|
161
158
|
throw mapPlatformError(e, "udp-socket.disconnect");
|
|
@@ -342,12 +339,12 @@ export function sockets03(onCall) {
|
|
|
342
339
|
return;
|
|
343
340
|
try {
|
|
344
341
|
if (this.#hopLimit !== undefined)
|
|
345
|
-
conn.setTtl
|
|
342
|
+
conn.setTtl(this.#hopLimit);
|
|
346
343
|
if (this.#recvBuffer !== undefined) {
|
|
347
|
-
conn.setRecvBufferSize
|
|
344
|
+
conn.setRecvBufferSize(Number(this.#recvBuffer));
|
|
348
345
|
}
|
|
349
346
|
if (this.#sendBuffer !== undefined) {
|
|
350
|
-
conn.setSendBufferSize
|
|
347
|
+
conn.setSendBufferSize(Number(this.#sendBuffer));
|
|
351
348
|
}
|
|
352
349
|
}
|
|
353
350
|
catch (e) {
|
|
@@ -511,11 +508,12 @@ export function sockets03(onCall) {
|
|
|
511
508
|
* WIT: `listen: func() -> result<stream<tcp-socket>, error-code>` —
|
|
512
509
|
* transitions to `listening` and returns the perpetual accept stream,
|
|
513
510
|
* whose elements are connected `TcpSocket` resources (lowered as
|
|
514
|
-
* `own<tcp-socket>` —
|
|
515
|
-
* never takes, closing that accepted connection).
|
|
511
|
+
* `own<tcp-socket>` — embedder-api.md §"Streams and futures" destroys
|
|
512
|
+
* any element the guest never takes, closing that accepted connection).
|
|
513
|
+
* An unbound socket
|
|
516
514
|
* implicitly binds to the family wildcard with an ephemeral port.
|
|
517
515
|
*
|
|
518
|
-
* SUSPENDING (embedder-api
|
|
516
|
+
* SUSPENDING (embedder-api.md §"The WASI parking kernel", the wasi:io `block` kernel): the OS
|
|
519
517
|
* bind is deferred one event-loop turn by `net.Server.listen` (module
|
|
520
518
|
* header), so this async method awaits the settle and the runtime
|
|
521
519
|
* parks the calling guest frame for that one tick. Full listener
|
|
@@ -584,7 +582,8 @@ export function sockets03(onCall) {
|
|
|
584
582
|
release();
|
|
585
583
|
}
|
|
586
584
|
})();
|
|
587
|
-
// The
|
|
585
|
+
// The producer-cancellation hook (embedder-api.md §"Streams and
|
|
586
|
+
// futures"): when the guest drops the
|
|
588
587
|
// stream while the loop above is PARKED in accept(), the runtime's
|
|
589
588
|
// pump invokes this — closing the listener is what unparks the
|
|
590
589
|
// accept (it rejects; classified fatal; the generator retires).
|
|
@@ -601,7 +600,8 @@ export function sockets03(onCall) {
|
|
|
601
600
|
}
|
|
602
601
|
/**
|
|
603
602
|
* WIT: `send: func(data: stream<u8>) -> future<result<_, error-code>>`
|
|
604
|
-
* — a sync func; the returned promise is the future source
|
|
603
|
+
* — a sync func; the returned promise is the future source
|
|
604
|
+
* (embedder-api.md §"Streams and futures").
|
|
605
605
|
* NEVER throws: the function has no error channel of its own, so every
|
|
606
606
|
* failure — including the state-machine ones — resolves the future as
|
|
607
607
|
* an err value. The argument stream is dropped on failure so its
|
|
@@ -660,7 +660,7 @@ export function sockets03(onCall) {
|
|
|
660
660
|
* close — the future distinguishes them (`ok` vs `err`). Dropping the
|
|
661
661
|
* stream's reader (guest SHUT_RD) stops the pump, discards queued
|
|
662
662
|
* data, and settles the future ok — the canceller is the observer
|
|
663
|
-
* (the same logic as embedder-api
|
|
663
|
+
* (the same logic as embedder-api.md §"Streams and futures"'s cancelRead ruling).
|
|
664
664
|
*/
|
|
665
665
|
receive() {
|
|
666
666
|
onCall("tcp-socket.receive");
|
|
@@ -677,7 +677,8 @@ export function sockets03(onCall) {
|
|
|
677
677
|
try {
|
|
678
678
|
for (;;) {
|
|
679
679
|
// The chunk is node's own buffer (no copy); it is borrowed by
|
|
680
|
-
// the rendezvous until the peer takes it (
|
|
680
|
+
// the rendezvous until the peer takes it (embedder-api.md
|
|
681
|
+
// §"Streams and futures": round-trip idempotence), which is safe —
|
|
681
682
|
// each read hands back a distinct buffer.
|
|
682
683
|
let chunk;
|
|
683
684
|
try {
|
|
@@ -823,9 +824,6 @@ export function sockets03(onCall) {
|
|
|
823
824
|
const conn = this.#conn;
|
|
824
825
|
if (this.#state !== "connected" || conn === undefined)
|
|
825
826
|
return;
|
|
826
|
-
if (conn.setKeepAlive === undefined) {
|
|
827
|
-
throw componentError({ kind: "not-supported" }, "tcp-socket: this host's TCP backend has no keep-alive control");
|
|
828
|
-
}
|
|
829
827
|
try {
|
|
830
828
|
conn.setKeepAlive(this.#keepAliveEnabled, Number(this.#keepAliveIdleNs / 1000000n));
|
|
831
829
|
}
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
// `'error'` arrive later). The seam exposes that settle as
|
|
48
48
|
// `TcpListener.settled()`; the provider awaits it inside a
|
|
49
49
|
// `suspending`-marked `listen`, parking the calling guest frame for
|
|
50
|
-
// the one tick (embedder-api
|
|
50
|
+
// the one tick (embedder-api.md §"The WASI parking kernel" — the same kernel that serves
|
|
51
51
|
// wasi:io's sync `block`). Full listener fidelity follows: real
|
|
52
52
|
// ephemeral addresses, real bind error codes.
|
|
53
53
|
// * dgram receive is push-shaped (`'message'` events); the adapter
|
package/esm/io.js
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
// `wasi:io@0.2` — error, poll, streams (contracts/embedder-api.md
|
|
2
2
|
// §"WASI examination").
|
|
3
3
|
//
|
|
4
|
-
// THE PARKING KERNEL
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// retired "three-tier strategy", grounded in C0 finding #6: no consumer
|
|
8
|
-
// leg ever called a pollable method) with real parking documented as
|
|
9
|
-
// "never (c) in this package". Both halves of that ruling expired:
|
|
4
|
+
// THE PARKING KERNEL (embedder-api.md §"The WASI parking kernel").
|
|
5
|
+
// `pollable.block()`, `poll()` and `blocking-*` are sync WIT functions
|
|
6
|
+
// that must genuinely wait — the one p2 idiom that fights a JS host.
|
|
10
7
|
//
|
|
11
8
|
// * the polymorph-iroh upstream-iroh consumer class (unmodified
|
|
12
9
|
// iroh/tokio) parks its reactor in `poll()` with timer + socket
|
|
13
|
-
// pollables —
|
|
14
|
-
//
|
|
10
|
+
// pollables — an always-ready stub wouldn't degrade for such a guest,
|
|
11
|
+
// it would LIVELOCK it (block() no-ops, reads return empty, the frame
|
|
15
12
|
// never suspends, so the event loop never turns and no host pump can
|
|
16
13
|
// ever make progress);
|
|
17
|
-
// * the runtime's suspending-import machinery (embedder-api.md
|
|
18
|
-
//
|
|
19
|
-
// degradation, so the kernel is ALWAYS ON
|
|
20
|
-
// profile: on engines without JSPI, `chooseMode`
|
|
21
|
-
// and everything behaves like
|
|
22
|
-
// parks — which then raises a clean
|
|
23
|
-
// instead of livelocking. Embedders
|
|
24
|
-
// instantiation pass `jspi: false`.
|
|
14
|
+
// * the runtime's suspending-import machinery (embedder-api.md
|
|
15
|
+
// §"Functions and async") makes real parking a per-declaration
|
|
16
|
+
// capability with graceful degradation, so the kernel is ALWAYS ON
|
|
17
|
+
// rather than an opt-in profile: on engines without JSPI, `chooseMode`
|
|
18
|
+
// falls back to plain and everything behaves like an always-ready stub
|
|
19
|
+
// until a guest genuinely parks — which then raises a clean
|
|
20
|
+
// `NeedsJspi` at the park site instead of livelocking. Embedders
|
|
21
|
+
// wanting guaranteed-plain instantiation pass `jspi: false`.
|
|
25
22
|
//
|
|
26
23
|
// Costs, deliberately confined: only the park-capable declarations are
|
|
27
24
|
// marked (`block`, `poll`) — hot-path `read`/`check-write` stay plain —
|
|
@@ -36,15 +33,17 @@
|
|
|
36
33
|
// wake pattern is polymorph-iroh's shim (promise-swap edge triggering).
|
|
37
34
|
//
|
|
38
35
|
// Streams: `read`/`check-write` stay plain (sync, never park), but the
|
|
39
|
-
// `blocking-*` declarations are MARKED park-capable (
|
|
36
|
+
// `blocking-*` declarations are MARKED park-capable (embedder-api.md
|
|
37
|
+
// §"The WASI parking kernel"): the
|
|
40
38
|
// buffer-backed base impls below always take the sync fast path, while
|
|
41
39
|
// the genuinely-async impls — `FedInputStream`/`SinkOutputStream` below,
|
|
42
40
|
// serving cli-stdio's host stdin/stdout and filesystem-web's OPFS files,
|
|
43
41
|
// where "blocking" cannot be served from a buffer — return a Promise and
|
|
44
42
|
// park the frame. Marking follows the WIT declaration on the REGISTERED
|
|
45
|
-
// class's prototype (
|
|
46
|
-
//
|
|
47
|
-
//
|
|
43
|
+
// class's prototype (embedder-api.md §"Functions and async" —
|
|
44
|
+
// "prototype declares, instances behave": instance-level overrides
|
|
45
|
+
// change behavior, not suspendability), which is what lets the
|
|
46
|
+
// duck-typed async streams park through the resource types registered here.
|
|
48
47
|
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
49
48
|
var useValue = arguments.length > 2;
|
|
50
49
|
for (var i = 0; i < initializers.length; i++) {
|
|
@@ -108,8 +107,9 @@ export class IoError {
|
|
|
108
107
|
* A pollable over host-supplied readiness.
|
|
109
108
|
*
|
|
110
109
|
* WIT-facing surface: `ready()` and `block()` (the latter parks the
|
|
111
|
-
* calling wasm frame when unready — @suspending, embedder-api.md
|
|
112
|
-
* the class prototype is the brand authority
|
|
110
|
+
* calling wasm frame when unready — @suspending, embedder-api.md
|
|
111
|
+
* §"Functions and async": the class prototype is the brand authority
|
|
112
|
+
* ("prototype declares, instances behave").
|
|
113
113
|
*
|
|
114
114
|
* Host-facing surface: the constructor and `waitPromise()`. `ready` must
|
|
115
115
|
* be cheap and side-effect-free; `wait` returns a promise that settles
|
|
@@ -135,7 +135,7 @@ let Pollable = (() => {
|
|
|
135
135
|
constructor(ready = () => true, wait = () => Promise.resolve()) {
|
|
136
136
|
this.#ready = ready;
|
|
137
137
|
this.#wait = wait;
|
|
138
|
-
//
|
|
138
|
+
// realm-local pill (contracts/embedder-api.md §"Realm boundaries
|
|
139
139
|
// and structured-clone-safe forms"; issue #131): stateful handles fail
|
|
140
140
|
// loud at a raw structuredClone/postMessage instead of husking.
|
|
141
141
|
defineRealmLocal(this);
|
|
@@ -190,7 +190,7 @@ let Pollable = (() => {
|
|
|
190
190
|
};
|
|
191
191
|
})();
|
|
192
192
|
export { Pollable };
|
|
193
|
-
//
|
|
193
|
+
// brand (contracts/embedder-api.md §"Module identity and @polyengine/protocol"): pollables cross
|
|
194
194
|
// into host provider code, which may resolve a different @polyengine copy. The
|
|
195
195
|
// brand makes them recognizable there; same-copy `instanceof` is unchanged
|
|
196
196
|
// and stays the documented spelling (issue #83). `poll()` itself needs no
|
|
@@ -275,14 +275,14 @@ let InputStream = (() => {
|
|
|
275
275
|
}
|
|
276
276
|
return out;
|
|
277
277
|
}
|
|
278
|
-
/** Park-capable
|
|
278
|
+
/** Park-capable: the buffer-backed base never parks. */
|
|
279
279
|
blockingRead(len) {
|
|
280
280
|
return this.read(len);
|
|
281
281
|
}
|
|
282
282
|
skip(len) {
|
|
283
283
|
return BigInt(this.read(len).length);
|
|
284
284
|
}
|
|
285
|
-
/** Park-capable
|
|
285
|
+
/** Park-capable: the buffer-backed base never parks. */
|
|
286
286
|
blockingSkip(len) {
|
|
287
287
|
return this.skip(len);
|
|
288
288
|
}
|
|
@@ -331,7 +331,7 @@ let OutputStream = (() => {
|
|
|
331
331
|
throw closedError();
|
|
332
332
|
this.#sink(contents);
|
|
333
333
|
}
|
|
334
|
-
/** Park-capable
|
|
334
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
335
335
|
blockingWriteAndFlush(contents) {
|
|
336
336
|
this.write(contents);
|
|
337
337
|
}
|
|
@@ -339,7 +339,7 @@ let OutputStream = (() => {
|
|
|
339
339
|
if (this.#closed)
|
|
340
340
|
throw closedError();
|
|
341
341
|
}
|
|
342
|
-
/** Park-capable
|
|
342
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
343
343
|
blockingFlush() {
|
|
344
344
|
this.flush();
|
|
345
345
|
}
|
|
@@ -349,7 +349,7 @@ let OutputStream = (() => {
|
|
|
349
349
|
writeZeroes(len) {
|
|
350
350
|
this.write(new Uint8Array(Number(len)));
|
|
351
351
|
}
|
|
352
|
-
/** Park-capable
|
|
352
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
353
353
|
blockingWriteZeroesAndFlush(len) {
|
|
354
354
|
this.writeZeroes(len);
|
|
355
355
|
}
|
|
@@ -358,7 +358,7 @@ let OutputStream = (() => {
|
|
|
358
358
|
this.write(chunk);
|
|
359
359
|
return BigInt(chunk.length);
|
|
360
360
|
}
|
|
361
|
-
/** Park-capable
|
|
361
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
362
362
|
blockingSplice(src, len) {
|
|
363
363
|
return this.splice(src, len);
|
|
364
364
|
}
|
|
@@ -380,7 +380,7 @@ export const STREAM_HIGH_WATER = 65536;
|
|
|
380
380
|
* generic bridge from any `AsyncIterable<Uint8Array>` (host stdin, an
|
|
381
381
|
* OPFS file read) to p2 stream semantics. `read` on an empty open stream
|
|
382
382
|
* returns an empty list (p2's non-blocking contract), `blocking-read`
|
|
383
|
-
* parks until bytes or EOF (
|
|
383
|
+
* parks until bytes or EOF (mark relay — duck-typed against the
|
|
384
384
|
* registered `InputStream`, the marks relay from that prototype), and
|
|
385
385
|
* EOF-with-drained-buffer is the `closed` stream-error. The feed pauses
|
|
386
386
|
* past the high-water mark (no unbounded buffering).
|
|
@@ -481,7 +481,7 @@ let FedInputStream = (() => {
|
|
|
481
481
|
throw closedError(); // drained + ended = closed
|
|
482
482
|
return new Uint8Array(0); // open, nothing available: p2 non-blocking read
|
|
483
483
|
}
|
|
484
|
-
/** Parks (
|
|
484
|
+
/** Parks (mark relay from the registered prototype). */
|
|
485
485
|
blockingRead(len) {
|
|
486
486
|
if (this.#buffered > 0 || this.#eof || this.#closed)
|
|
487
487
|
return this.read(len);
|
|
@@ -517,7 +517,7 @@ export { FedInputStream };
|
|
|
517
517
|
* budget: `check-write` reports the remaining permit (writing past it is
|
|
518
518
|
* the guest's contract violation and traps via unbranded throw),
|
|
519
519
|
* `blocking-flush`/`blocking-write-and-flush` park until the sink
|
|
520
|
-
* drained everything (
|
|
520
|
+
* drained everything (mark relay), `subscribe` wakes when budget
|
|
521
521
|
* frees. A sink failure surfaces as the `last-operation-failed`
|
|
522
522
|
* stream-error carrying an `IoError`.
|
|
523
523
|
*
|
|
@@ -602,7 +602,7 @@ let SinkOutputStream = (() => {
|
|
|
602
602
|
flush() {
|
|
603
603
|
this.#checkOpen();
|
|
604
604
|
}
|
|
605
|
-
/** Parks until the sink drained everything (
|
|
605
|
+
/** Parks until the sink drained everything (mark relay). */
|
|
606
606
|
blockingFlush() {
|
|
607
607
|
this.#checkOpen();
|
|
608
608
|
if (this.#queued === 0)
|
package/esm/mod.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// `@polyengine/wasi` — the WASI providers for polyengine hosts, and the
|
|
2
2
|
// executable check that the embedder conventions
|
|
3
|
-
// (`@polyengine/protocol
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// (`@polyengine/protocol` — this package is protocol-only, per
|
|
4
|
+
// embedder-api.md §"The host-ABI surface and its version")
|
|
5
|
+
// serve WASI (docs/architecture.md §2 keeps implementations out of
|
|
6
6
|
// the RUNTIME — this package is where they live). Scope: p2
|
|
7
7
|
// baseline + p3 clocks + à la carte sockets on BOTH tracks (the
|
|
8
8
|
// poll-shaped `@0.2` surface std::net links, and `@0.3` UDP + TCP
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
// canonicalization" (`@0.2`, `@0.3`) — this package is the flagship
|
|
55
55
|
// track-key-registration consumer: one `@0.2` provider serves every p2
|
|
56
56
|
// leaf regardless of whether the guest's binary says `0.2.6`, `0.2.9` or
|
|
57
|
-
// `0.2.12
|
|
57
|
+
// `0.2.12`, and one `@0.3` union provider serves both
|
|
58
58
|
// divergent `monotonic-clock@0.3.0` drafts the corpus actually links
|
|
59
|
-
// (
|
|
59
|
+
// (§"Version canonicalization").
|
|
60
60
|
import { cli } from "./cli.js";
|
|
61
61
|
import { clocks } from "./clocks.js";
|
|
62
62
|
import { filesystem } from "./filesystem.js";
|
package/esm/sockets.js
CHANGED
|
@@ -101,11 +101,11 @@
|
|
|
101
101
|
// once from `unbound` (a failed attempt closes the socket); `listen` once
|
|
102
102
|
// from `unbound` (implicit wildcard-ephemeral bind) or `bound`;
|
|
103
103
|
// `send`/`receive` once each, only when `connected`, and their failures
|
|
104
|
-
// NEVER throw — `send`'s error channel is its returned future (
|
|
105
|
-
//
|
|
106
|
-
// is the future half of its tuple, resolved as result values.
|
|
107
|
-
// returns the perpetual accept stream, whose elements are connected
|
|
108
|
-
// `tcp-socket` resources (
|
|
104
|
+
// NEVER throw — `send`'s error channel is its returned future (embedder-api.md
|
|
105
|
+
// §"Streams and futures": the async method's promise IS the future source)
|
|
106
|
+
// and `receive`'s is the future half of its tuple, resolved as result values.
|
|
107
|
+
// `listen` returns the perpetual accept stream, whose elements are connected
|
|
108
|
+
// `tcp-socket` resources (§"Streams and futures": un-taken elements are destroyed
|
|
109
109
|
// at teardown, closing their connections); per-connection accept failures
|
|
110
110
|
// are skipped, listener-fatal ones end the stream. Stream teardown
|
|
111
111
|
// follows the WIT's shared-ownership note: the OS socket closes only when
|
|
@@ -117,8 +117,8 @@
|
|
|
117
117
|
// failures while consuming `send`'s stream (a peer trap) are NOT socket
|
|
118
118
|
// errors: they propagate as producer failures on the host-failure channel.
|
|
119
119
|
//
|
|
120
|
-
// `listen` is SUSPENDING (embedder-api
|
|
121
|
-
// kernel): node defers the OS bind one event-loop turn, so `listen` parks
|
|
120
|
+
// `listen` is SUSPENDING (embedder-api.md §"The WASI parking kernel" —
|
|
121
|
+
// the wasi:io `block` kernel): node defers the OS bind one event-loop turn, so `listen` parks
|
|
122
122
|
// the calling guest frame for that tick and returns fully settled — real
|
|
123
123
|
// ephemeral addresses from `get-local-address`, real error codes
|
|
124
124
|
// (`address-in-use`) from a failed bind. Guests that link `listen`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polyengine/wasi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "WASI providers for polyengine hosts: the p2 baseline and p3 clocks, one module per semver track.",
|
|
5
5
|
"homepage": "https://github.com/polymorph-components/polyengine#readme",
|
|
6
6
|
"repository": {
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"access": "public"
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
|
-
"@polyengine/protocol": "^0.
|
|
94
|
+
"@polyengine/protocol": "^0.3.0"
|
|
95
95
|
},
|
|
96
96
|
"_generatedBy": "dnt@0.43.2"
|
|
97
97
|
}
|
package/types/cli.d.ts
CHANGED
|
@@ -8,8 +8,6 @@ export interface CliOptions {
|
|
|
8
8
|
cwd?: string;
|
|
9
9
|
/** `get-stdin`'s buffer contents; default empty (matches contract: "stdin (empty)"). */
|
|
10
10
|
stdinBuffer?: Uint8Array;
|
|
11
|
-
/** Also `console.log`/`console.error` captured stdout/stderr writes. Default false. */
|
|
12
|
-
passthrough?: boolean;
|
|
13
11
|
/** `exit()` throws `ExitError` instead of merely recording. Default false. */
|
|
14
12
|
throwOnExit?: boolean;
|
|
15
13
|
}
|
|
@@ -36,9 +34,9 @@ export interface CliResult {
|
|
|
36
34
|
* `exit`'s WIT signature is `exit: func(status: result)` — `result` with no
|
|
37
35
|
* type parameters, i.e. `result<_, _>`. Per contracts/embedder-api.md's value
|
|
38
36
|
* table, a `result` in **parameter** (non-return) position is plain nested
|
|
39
|
-
* data: `{ kind: "ok" } | { kind: "err" }` (
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* data: `{ kind: "ok" } | { kind: "err" }` (embedder-api.md §"Naming and
|
|
38
|
+
* casing" — enum/variant case names are data, not `{tag}` wrappers),
|
|
39
|
+
* never a throw. Only a
|
|
42
40
|
* function's own *return*-position result throws/rejects.
|
|
43
41
|
*/
|
|
44
42
|
export declare function cli(options?: CliOptions): CliResult;
|
package/types/http.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type Stream } from "@polyengine/protocol";
|
|
|
6
6
|
* track literally, but the public entry point is `http()`.
|
|
7
7
|
*/
|
|
8
8
|
export declare const HTTP_TRACK = "0.3";
|
|
9
|
-
/** `method` — case names verbatim (
|
|
9
|
+
/** `method` — case names verbatim (embedder-api.md §"Naming and casing"). */
|
|
10
10
|
export type Method = {
|
|
11
11
|
kind: "get";
|
|
12
12
|
} | {
|
|
@@ -94,13 +94,6 @@ export type BodySource = Stream<number> | AsyncIterable<Uint8Array | number[]> |
|
|
|
94
94
|
/** What future params accept: the lifted handle or a promise of the value. */
|
|
95
95
|
export type FutureLike<T> = PromiseLike<T>;
|
|
96
96
|
export interface HttpOptions {
|
|
97
|
-
/**
|
|
98
|
-
* Override the registration keys for a guest pinned to a PRERELEASE
|
|
99
|
-
* snapshot (`0.3.0-rc-*`), which the resolver matches exactly — no
|
|
100
|
-
* track exists for prereleases. Default: the `@0.3` track, serving
|
|
101
|
-
* every released 0.3.x.
|
|
102
|
-
*/
|
|
103
|
-
version?: string;
|
|
104
97
|
/** Observe every entry point the guest reaches (see sockets' onCall). */
|
|
105
98
|
onCall?: (call: string) => void;
|
|
106
99
|
/**
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Stream } from "@polyengine/protocol";
|
|
2
|
-
/** `wasi:cli/types@0.3`'s `error-code` ENUM: bare kebab-case strings
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* path had exercised). */
|
|
2
|
+
/** `wasi:cli/types@0.3`'s `error-code` ENUM: bare kebab-case strings
|
|
3
|
+
* (embedder-api.md §"Naming and casing" — enums are data strings, not
|
|
4
|
+
* `{kind}` variants). */
|
|
6
5
|
export type CliErrorCode = "io" | "illegal-byte-sequence" | "pipe";
|
|
7
6
|
/** `result<_, error-code>` AS A VALUE (the 0.3 stdio futures). */
|
|
8
7
|
export type CliIoResult = {
|
|
@@ -123,7 +123,7 @@ export interface MetadataHashValue {
|
|
|
123
123
|
lower: bigint;
|
|
124
124
|
upper: bigint;
|
|
125
125
|
}
|
|
126
|
-
/** 0.3 `result<_, error-code>` as a future/tuple VALUE (
|
|
126
|
+
/** 0.3 `result<_, error-code>` as a future/tuple VALUE (embedder-api.md §"Streams and futures"). */
|
|
127
127
|
export type FsResult03 = {
|
|
128
128
|
kind: "ok";
|
|
129
129
|
} | {
|
|
@@ -17,25 +17,23 @@ export interface DatagramConn {
|
|
|
17
17
|
send(p: Uint8Array, addr?: NetAddr): Promise<number>;
|
|
18
18
|
receive(): Promise<[Uint8Array, NetAddr]>;
|
|
19
19
|
close(): void;
|
|
20
|
-
/** OS-level connected mode (kernel filters + default destination).
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
setSendBufferSize?(size: number): void;
|
|
20
|
+
/** OS-level connected mode (kernel filters + default destination). */
|
|
21
|
+
connect(addr: NetAddr): Promise<void>;
|
|
22
|
+
disconnect(): void;
|
|
23
|
+
/** IP_TTL / IPV6_UNICAST_HOPS. */
|
|
24
|
+
setTtl(ttl: number): void;
|
|
25
|
+
/** SO_RCVBUF / SO_SNDBUF. */
|
|
26
|
+
getRecvBufferSize(): number;
|
|
27
|
+
setRecvBufferSize(size: number): void;
|
|
28
|
+
getSendBufferSize(): number;
|
|
29
|
+
setSendBufferSize(size: number): void;
|
|
31
30
|
/** Non-blocking queue access + readiness (the 0.2 datagram streams:
|
|
32
|
-
* poll-shaped receive instead of the promise-shaped one above).
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
receiveReady?(): boolean;
|
|
31
|
+
* poll-shaped receive instead of the promise-shaped one above). */
|
|
32
|
+
tryReceive(): [Uint8Array, NetAddr] | undefined;
|
|
33
|
+
receiveReady(): boolean;
|
|
36
34
|
/** The CURRENT epoch's wake promise (promise-swap: settles when a
|
|
37
35
|
* datagram arrives, the socket errors, or it closes; re-armed per event). */
|
|
38
|
-
waitReceive
|
|
36
|
+
waitReceive(): Promise<void>;
|
|
39
37
|
}
|
|
40
38
|
export type ListenDatagram = (options: {
|
|
41
39
|
transport: "udp";
|
|
@@ -51,9 +49,8 @@ export interface TcpConn {
|
|
|
51
49
|
write(p: Uint8Array): Promise<number>;
|
|
52
50
|
closeWrite(): Promise<void>;
|
|
53
51
|
close(): void;
|
|
54
|
-
/** SO_KEEPALIVE + TCP_KEEPIDLE (node exposes exactly this pair).
|
|
55
|
-
|
|
56
|
-
setKeepAlive?(enabled: boolean, idleMs: number): void;
|
|
52
|
+
/** SO_KEEPALIVE + TCP_KEEPIDLE (node exposes exactly this pair). */
|
|
53
|
+
setKeepAlive(enabled: boolean, idleMs: number): void;
|
|
57
54
|
}
|
|
58
55
|
export type TcpConnect = (options: {
|
|
59
56
|
transport: "tcp";
|
|
@@ -73,11 +70,11 @@ export interface TcpListener {
|
|
|
73
70
|
settled(): Promise<void>;
|
|
74
71
|
accept(): Promise<TcpConn>;
|
|
75
72
|
close(): void;
|
|
76
|
-
/** Non-blocking accept + readiness (the 0.2 poll-shaped accept)
|
|
77
|
-
*
|
|
78
|
-
tryAccept
|
|
79
|
-
acceptReady
|
|
80
|
-
waitAccept
|
|
73
|
+
/** Non-blocking accept + readiness (the 0.2 poll-shaped accept);
|
|
74
|
+
* same promise-swap contract as `waitReceive`. */
|
|
75
|
+
tryAccept(): TcpConn | undefined;
|
|
76
|
+
acceptReady(): boolean;
|
|
77
|
+
waitAccept(): Promise<void>;
|
|
81
78
|
}
|
|
82
79
|
export type TcpListen = (options: {
|
|
83
80
|
transport: "tcp";
|
|
@@ -26,7 +26,7 @@ export interface Ipv6SocketAddress {
|
|
|
26
26
|
address: Ipv6Address;
|
|
27
27
|
scopeId: number;
|
|
28
28
|
}
|
|
29
|
-
/** The `ip-socket-address` variant, in `{ kind, value }` form (
|
|
29
|
+
/** The `ip-socket-address` variant, in `{ kind, value }` form (embedder-api.md §"Naming and casing"). */
|
|
30
30
|
export type IpSocketAddress = {
|
|
31
31
|
kind: "ipv4";
|
|
32
32
|
value: Ipv4SocketAddress;
|
|
@@ -222,7 +222,8 @@ export type TcpSendSource = Stream<number> | AsyncIterable<Uint8Array | number[]
|
|
|
222
222
|
export type TcpByteStream = AsyncIterable<Uint8Array> | Iterable<Uint8Array>;
|
|
223
223
|
/**
|
|
224
224
|
* What tcp `listen` returns: the perpetual accept stream. `cancel` is the
|
|
225
|
-
*
|
|
225
|
+
* producer-cancellation hook (embedder-api.md §"Streams and futures")
|
|
226
|
+
* the runtime's pump invokes when the
|
|
226
227
|
* guest drops the stream while the loop is parked in accept(); direct
|
|
227
228
|
* (non-runtime) consumers may call it themselves to stop accepting.
|
|
228
229
|
*/
|
|
@@ -233,13 +234,13 @@ export type TcpAcceptStream = AsyncIterable<TcpSocket> & {
|
|
|
233
234
|
* The host-implemented `tcp-socket` resource surface (client + listener
|
|
234
235
|
* halves — module header). `send` is a WIT sync func returning
|
|
235
236
|
* `future<result>`: the async method's promise is lowered as the future
|
|
236
|
-
* source (
|
|
237
|
-
* future settles when transmission completes.
|
|
238
|
-
* the byte stream and the future that reports
|
|
239
|
-
* close (`err`). `listen` returns the perpetual
|
|
240
|
-
* async iterable of connected `TcpSocket` resources,
|
|
241
|
-
* `stream<own<tcp-socket>>` (
|
|
242
|
-
* takes are destroyed, closing their connections). Dropping the guest
|
|
237
|
+
* source (embedder-api.md §"Streams and futures"), so the guest's call
|
|
238
|
+
* returns immediately and the future settles when transmission completes.
|
|
239
|
+
* `receive`'s tuple carries the byte stream and the future that reports
|
|
240
|
+
* FIN (`ok`) vs abnormal close (`err`). `listen` returns the perpetual
|
|
241
|
+
* accept stream — an async iterable of connected `TcpSocket` resources,
|
|
242
|
+
* lowered as `stream<own<tcp-socket>>` (§"Streams and futures": elements
|
|
243
|
+
* the guest never takes are destroyed, closing their connections). Dropping the guest
|
|
243
244
|
* handle does NOT close a socket with live pumps or a live accept stream
|
|
244
245
|
* (the WIT's shared-ownership note); the OS socket closes when the
|
|
245
246
|
* handle and every derived stream are all retired.
|
package/types/io.d.ts
CHANGED
|
@@ -26,8 +26,9 @@ export declare class IoError {
|
|
|
26
26
|
* A pollable over host-supplied readiness.
|
|
27
27
|
*
|
|
28
28
|
* WIT-facing surface: `ready()` and `block()` (the latter parks the
|
|
29
|
-
* calling wasm frame when unready — @suspending, embedder-api.md
|
|
30
|
-
* the class prototype is the brand authority
|
|
29
|
+
* calling wasm frame when unready — @suspending, embedder-api.md
|
|
30
|
+
* §"Functions and async": the class prototype is the brand authority
|
|
31
|
+
* ("prototype declares, instances behave").
|
|
31
32
|
*
|
|
32
33
|
* Host-facing surface: the constructor and `waitPromise()`. `ready` must
|
|
33
34
|
* be cheap and side-effect-free; `wait` returns a promise that settles
|
|
@@ -86,10 +87,10 @@ export declare class InputStream {
|
|
|
86
87
|
#private;
|
|
87
88
|
constructor(buf?: Uint8Array);
|
|
88
89
|
read(len: bigint): Uint8Array;
|
|
89
|
-
/** Park-capable
|
|
90
|
+
/** Park-capable: the buffer-backed base never parks. */
|
|
90
91
|
blockingRead(len: bigint): Uint8Array | Promise<Uint8Array>;
|
|
91
92
|
skip(len: bigint): bigint;
|
|
92
|
-
/** Park-capable
|
|
93
|
+
/** Park-capable: the buffer-backed base never parks. */
|
|
93
94
|
blockingSkip(len: bigint): bigint | Promise<bigint>;
|
|
94
95
|
subscribe(): Pollable;
|
|
95
96
|
[Symbol.dispose](): void;
|
|
@@ -105,17 +106,17 @@ export declare class OutputStream {
|
|
|
105
106
|
constructor(sink: (chunk: Uint8Array) => void);
|
|
106
107
|
checkWrite(): bigint;
|
|
107
108
|
write(contents: Uint8Array): void;
|
|
108
|
-
/** Park-capable
|
|
109
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
109
110
|
blockingWriteAndFlush(contents: Uint8Array): void | Promise<void>;
|
|
110
111
|
flush(): void;
|
|
111
|
-
/** Park-capable
|
|
112
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
112
113
|
blockingFlush(): void | Promise<void>;
|
|
113
114
|
subscribe(): Pollable;
|
|
114
115
|
writeZeroes(len: bigint): void;
|
|
115
|
-
/** Park-capable
|
|
116
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
116
117
|
blockingWriteZeroesAndFlush(len: bigint): void | Promise<void>;
|
|
117
118
|
splice(src: InputStream, len: bigint): bigint;
|
|
118
|
-
/** Park-capable
|
|
119
|
+
/** Park-capable: the never-backpressured base never parks. */
|
|
119
120
|
blockingSplice(src: InputStream, len: bigint): bigint | Promise<bigint>;
|
|
120
121
|
[Symbol.dispose](): void;
|
|
121
122
|
}
|
|
@@ -133,7 +134,7 @@ export type ByteSink = (chunk: Uint8Array) => void | Promise<void>;
|
|
|
133
134
|
* generic bridge from any `AsyncIterable<Uint8Array>` (host stdin, an
|
|
134
135
|
* OPFS file read) to p2 stream semantics. `read` on an empty open stream
|
|
135
136
|
* returns an empty list (p2's non-blocking contract), `blocking-read`
|
|
136
|
-
* parks until bytes or EOF (
|
|
137
|
+
* parks until bytes or EOF (mark relay — duck-typed against the
|
|
137
138
|
* registered `InputStream`, the marks relay from that prototype), and
|
|
138
139
|
* EOF-with-drained-buffer is the `closed` stream-error. The feed pauses
|
|
139
140
|
* past the high-water mark (no unbounded buffering).
|
|
@@ -147,7 +148,7 @@ export declare class FedInputStream {
|
|
|
147
148
|
#private;
|
|
148
149
|
constructor(source: AsyncIterable<Uint8Array>, highWater?: number);
|
|
149
150
|
read(len: bigint): Uint8Array;
|
|
150
|
-
/** Parks (
|
|
151
|
+
/** Parks (mark relay from the registered prototype). */
|
|
151
152
|
blockingRead(len: bigint): Uint8Array | Promise<Uint8Array>;
|
|
152
153
|
skip(len: bigint): bigint;
|
|
153
154
|
blockingSkip(len: bigint): bigint | Promise<bigint>;
|
|
@@ -159,7 +160,7 @@ export declare class FedInputStream {
|
|
|
159
160
|
* budget: `check-write` reports the remaining permit (writing past it is
|
|
160
161
|
* the guest's contract violation and traps via unbranded throw),
|
|
161
162
|
* `blocking-flush`/`blocking-write-and-flush` park until the sink
|
|
162
|
-
* drained everything (
|
|
163
|
+
* drained everything (mark relay), `subscribe` wakes when budget
|
|
163
164
|
* frees. A sink failure surfaces as the `last-operation-failed`
|
|
164
165
|
* stream-error carrying an `IoError`.
|
|
165
166
|
*
|
|
@@ -173,7 +174,7 @@ export declare class SinkOutputStream {
|
|
|
173
174
|
checkWrite(): bigint;
|
|
174
175
|
write(contents: Uint8Array): void;
|
|
175
176
|
flush(): void;
|
|
176
|
-
/** Parks until the sink drained everything (
|
|
177
|
+
/** Parks until the sink drained everything (mark relay). */
|
|
177
178
|
blockingFlush(): void | Promise<void>;
|
|
178
179
|
/** Parks until this write (and everything before it) drained. */
|
|
179
180
|
blockingWriteAndFlush(contents: Uint8Array): void | Promise<void>;
|