@polyengine/runtime 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/cabi/async_values.js +6 -5
- package/esm/cabi/bulk_lists.js +0 -5
- package/esm/cabi/context.js +13 -3
- package/esm/cabi/flatten.js +41 -9
- package/esm/cabi/handles.js +57 -54
- package/esm/cabi/layout.js +113 -52
- package/esm/cabi/load.js +31 -23
- package/esm/cabi/store.js +33 -26
- package/esm/cabi/trap.js +2 -2
- package/esm/cabi/types.js +138 -25
- package/esm/cabi/values.js +25 -7
- package/esm/cache/core.js +2 -11
- package/esm/digest/digest.js +10 -8
- package/esm/digest/mod.js +1 -1
- package/esm/digest/verify.js +6 -86
- package/esm/embedder/casing.js +24 -9
- package/esm/embedder/copy.js +6 -6
- package/esm/embedder/errors.js +2 -2
- package/esm/embedder/imports.js +3 -3
- package/esm/embedder/instantiate.js +170 -41
- package/esm/embedder/mod.js +9 -8
- package/esm/embedder/resources.js +39 -16
- package/esm/embedder/streams.js +36 -37
- package/esm/embedder/sync.js +242 -0
- package/esm/embedder/values.js +84 -22
- package/esm/embedder/version.js +9 -9
- package/esm/exec/boundary.js +318 -165
- package/esm/exec/executor.js +51 -24
- package/esm/exec/host_streams.js +31 -31
- package/esm/intrinsics/async_builtins.js +19 -8
- package/esm/intrinsics/context.js +1 -1
- package/esm/intrinsics/errors.js +9 -9
- package/esm/intrinsics/fact_calls.js +37 -49
- package/esm/intrinsics/mod.js +54 -117
- package/esm/intrinsics/stream_builtins.js +2 -2
- package/esm/intrinsics/transcode.js +1 -1
- package/esm/jspi/bridge.js +4 -3
- package/esm/jspi/suspending.js +10 -5
- package/esm/plan/loader.js +5 -5
- package/esm/shim/translator.js +2 -2
- package/esm/task/mod.js +45 -182
- package/esm/task/scheduler.js +154 -185
- package/esm/task/streams.js +39 -54
- package/esm/task/subtask.js +2 -2
- package/esm/task/thread.js +20 -41
- package/esm/task/waitable.js +0 -1
- package/package.json +2 -2
- package/types/cabi/async_values.d.ts +3 -2
- package/types/cabi/bulk_lists.d.ts +0 -2
- package/types/cabi/context.d.ts +15 -5
- package/types/cabi/flatten.d.ts +2 -2
- package/types/cabi/handles.d.ts +15 -26
- package/types/cabi/layout.d.ts +22 -1
- package/types/cabi/load.d.ts +10 -2
- package/types/cabi/store.d.ts +4 -2
- package/types/cabi/types.d.ts +22 -3
- package/types/digest/mod.d.ts +1 -1
- package/types/digest/verify.d.ts +3 -19
- package/types/embedder/casing.d.ts +9 -1
- package/types/embedder/copy.d.ts +4 -4
- package/types/embedder/instantiate.d.ts +4 -4
- package/types/embedder/mod.d.ts +3 -2
- package/types/embedder/resources.d.ts +20 -7
- package/types/embedder/streams.d.ts +5 -6
- package/types/embedder/sync.d.ts +81 -0
- package/types/embedder/values.d.ts +2 -2
- package/types/exec/boundary.d.ts +66 -42
- package/types/exec/executor.d.ts +3 -2
- package/types/exec/host_streams.d.ts +8 -8
- package/types/intrinsics/errors.d.ts +3 -3
- package/types/intrinsics/mod.d.ts +1 -1
- package/types/intrinsics/stream_builtins.d.ts +2 -2
- package/types/jspi/bridge.d.ts +6 -5
- package/types/jspi/suspending.d.ts +1 -1
- package/types/plan/format.d.ts +11 -10
- package/types/plan/loader.d.ts +2 -2
- package/types/shim/translator.d.ts +2 -2
- package/types/task/mod.d.ts +26 -97
- package/types/task/scheduler.d.ts +81 -62
- package/types/task/streams.d.ts +23 -38
- package/types/task/subtask.d.ts +2 -2
- package/types/task/waitable.d.ts +0 -1
package/types/digest/verify.d.ts
CHANGED
|
@@ -3,17 +3,11 @@ import type { WirePlan } from "../plan/format.js";
|
|
|
3
3
|
export interface DigestMismatch {
|
|
4
4
|
expected: string;
|
|
5
5
|
actual: string;
|
|
6
|
-
/** Human-readable description of the first structural divergence found,
|
|
7
|
-
* or `null` if a divergence exists but no finer-grained cause could be
|
|
8
|
-
* isolated (e.g. the two canonical JSON trees have the same top-level
|
|
9
|
-
* shape but the digest still differs — should not happen in practice
|
|
10
|
-
* since the digest is a pure hash of that JSON, but guarded anyway). */
|
|
11
|
-
firstDivergence: string | null;
|
|
12
6
|
}
|
|
13
7
|
/**
|
|
14
8
|
* Thrown by generated `instantiate` wrappers when the loaded plan's world
|
|
15
9
|
* digest does not match the constant bindgen embedded at generation time
|
|
16
|
-
* (contracts/digest.md: "fails fast
|
|
10
|
+
* (contracts/digest.md: "fails fast on mismatch").
|
|
17
11
|
* Raised BEFORE the component is instantiated, so no guest code has run
|
|
18
12
|
* when a caller catches this.
|
|
19
13
|
*
|
|
@@ -25,7 +19,7 @@ export declare class WorldDigestMismatchError extends Error {
|
|
|
25
19
|
readonly name = "WorldDigestMismatchError";
|
|
26
20
|
/** The world these bindings were generated from. */
|
|
27
21
|
readonly world: string;
|
|
28
|
-
/** The full mismatch report (expected/actual digest
|
|
22
|
+
/** The full mismatch report (expected/actual digest). */
|
|
29
23
|
readonly mismatch: DigestMismatch;
|
|
30
24
|
constructor(world: string, mismatch: DigestMismatch);
|
|
31
25
|
/** Digest the generated bindings were built against. */
|
|
@@ -36,17 +30,7 @@ export declare class WorldDigestMismatchError extends Error {
|
|
|
36
30
|
/**
|
|
37
31
|
* Verify `plan`'s computed world digest against `expectedDigest` (the
|
|
38
32
|
* constant bindgen embedded at generation time). Returns `null` on match,
|
|
39
|
-
* or a `DigestMismatch` report
|
|
40
|
-
* mismatch.
|
|
33
|
+
* or a `DigestMismatch` report on mismatch.
|
|
41
34
|
* @internal
|
|
42
35
|
*/
|
|
43
36
|
export declare function verifyWorldDigest(plan: WirePlan, expectedDigest: string): Promise<DigestMismatch | null>;
|
|
44
|
-
/**
|
|
45
|
-
* `diffWorldDigest` — richer variant for tests/tooling: compare against
|
|
46
|
-
* another plan's (or a WIT-derived) canonical JSON directly, walking both
|
|
47
|
-
* trees in parallel to name the first divergent import/export/type path.
|
|
48
|
-
* `expectedCanonicalJson` is normally produced by `crates/bindgen`'s
|
|
49
|
-
* `digest --json` output, or by `computeWorldDigest` on a reference plan.
|
|
50
|
-
* @internal
|
|
51
|
-
*/
|
|
52
|
-
export declare function diffWorldDigest(plan: WirePlan, expectedCanonicalJson: string): Promise<DigestMismatch | null>;
|
|
@@ -25,7 +25,7 @@ export declare function pascalCase(label: string): string;
|
|
|
25
25
|
*
|
|
26
26
|
* The Component Model mangles resource membership into the name itself
|
|
27
27
|
* (`[constructor]counter`, `[method]counter.increment`,
|
|
28
|
-
* `[static]counter.merge`);
|
|
28
|
+
* `[static]counter.merge`); embedders would otherwise hand-transcribe
|
|
29
29
|
* these. Assembling and disassembling them is a runtime obligation.
|
|
30
30
|
*/
|
|
31
31
|
export type LeafName = {
|
|
@@ -45,6 +45,14 @@ export type LeafName = {
|
|
|
45
45
|
};
|
|
46
46
|
/**
|
|
47
47
|
* Decode a mangled leaf name; unmangled names come back as `plain`.
|
|
48
|
+
*
|
|
49
|
+
* An unknown bracket form throws rather than falling back to `plain`
|
|
50
|
+
* (contracts/embedder-api.md §"Getters and setters (pre-ruling…)", final
|
|
51
|
+
* paragraph): "the runtime refuses unknown bracket forms in mangled names
|
|
52
|
+
* loudly at instantiation (rather than misbinding them as plain names — a
|
|
53
|
+
* `[get]foo` treated as a function named `[get]foo` would be wrong in both
|
|
54
|
+
* directions)". Getter/setter support (`[get]`/`[set]`, upstream
|
|
55
|
+
* WebAssembly/component-model#701) is tracked in polyengine#254.
|
|
48
56
|
* @internal — leaf-name demangling, performed by the runtime and by
|
|
49
57
|
* bindgen-generated code.
|
|
50
58
|
*/
|
package/types/embedder/copy.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The URL of this copy of the runtime. Identity of the copy.
|
|
3
|
-
* @internal — copy-identity constant for the
|
|
3
|
+
* @internal — copy-identity constant for the module identity multi-copy diagnostics; not
|
|
4
4
|
* host-facing.
|
|
5
5
|
*/
|
|
6
6
|
export declare const COPY_URL: string;
|
|
@@ -14,15 +14,15 @@ export declare const COPY_URL: string;
|
|
|
14
14
|
*
|
|
15
15
|
* INVARIANT: keep in sync with `version` in runtime/deno.json — pinned by
|
|
16
16
|
* runtime/tests/embedder/cross_copy_test.ts.
|
|
17
|
-
* @internal — copy-identity constant for the
|
|
17
|
+
* @internal — copy-identity constant for the module identity multi-copy diagnostics; not
|
|
18
18
|
* host-facing.
|
|
19
19
|
*/
|
|
20
|
-
export declare const RUNTIME_VERSION = "0.
|
|
20
|
+
export declare const RUNTIME_VERSION = "0.6.0";
|
|
21
21
|
/**
|
|
22
22
|
* Compose a cross-copy diagnostic: what was foreign, which copy is speaking,
|
|
23
23
|
* the census of every copy in the graph, and the by-value remediation.
|
|
24
24
|
*
|
|
25
|
-
* Kept to one line but complete — the whole point of
|
|
25
|
+
* Kept to one line but complete — the whole point of module identity's stateful half is
|
|
26
26
|
* that "recognized but foreign" is a NAMED failure, never a silent
|
|
27
27
|
* adaptation (a foreign `Stream` pumped as an async iterable) and never a
|
|
28
28
|
* misleading generic ("handle is not an error-context").
|
|
@@ -10,7 +10,7 @@ export interface ComponentArtifacts {
|
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Untranslated alternative to `ComponentArtifacts` (embedder-api.md
|
|
13
|
-
*
|
|
13
|
+
* §"Module wiring and instantiation"): hand `instantiate` the raw component plus the translator
|
|
14
14
|
* and it runs the translation internally — the pipeline collapses to
|
|
15
15
|
* bytes-in, instance-out.
|
|
16
16
|
*
|
|
@@ -33,7 +33,7 @@ export type InstantiateSource = ComponentArtifacts | UntranslatedArtifacts;
|
|
|
33
33
|
* single-file JSON emitted by build-time translation (`tools/translate`,
|
|
34
34
|
* or `Translator.translateRaw`), carrying the plan and the FACT adapter
|
|
35
35
|
* modules. The production deploy set is `component.wasm` + its envelope +
|
|
36
|
-
* the runtime: no translator ships (embedder-api.md
|
|
36
|
+
* the runtime: no translator ships (contracts/embedder-api.md §"Module wiring and instantiation").
|
|
37
37
|
*
|
|
38
38
|
* Pure and fetch-agnostic: acquire the two blobs however the platform
|
|
39
39
|
* likes (HTTP, fs, bundler asset) and hand them over. The envelope embeds
|
|
@@ -48,7 +48,7 @@ export declare function artifactsFromEnvelope(envelopeJson: string, componentByt
|
|
|
48
48
|
* guest code runs: generated `instantiate` wrappers call this, verify the
|
|
49
49
|
* plan, and only then delegate to `instantiate` below.
|
|
50
50
|
* @internal — bindgen-generated code only — the digest handshake needs the
|
|
51
|
-
* plan before instantiating (
|
|
51
|
+
* plan before instantiating (§"Module wiring and instantiation").
|
|
52
52
|
*/
|
|
53
53
|
export declare function resolveArtifacts(src: InstantiateSource): Promise<ComponentArtifacts>;
|
|
54
54
|
export interface EmbedderOptions {
|
|
@@ -80,7 +80,7 @@ export interface EmbedderInstance {
|
|
|
80
80
|
*/
|
|
81
81
|
export declare function instantiate(source: InstantiateSource, imports?: Record<string, unknown>, opts?: EmbedderOptions): Promise<EmbedderInstance>;
|
|
82
82
|
/**
|
|
83
|
-
* Alias matching
|
|
83
|
+
* Alias matching bindgen's generated import spelling.
|
|
84
84
|
* @internal — alias kept for bindgen-generated code only; hosts call
|
|
85
85
|
* `instantiate`.
|
|
86
86
|
*/
|
package/types/embedder/mod.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare function createStream<T>(): {
|
|
|
9
9
|
writer: ProtocolStreamWriter<T>;
|
|
10
10
|
};
|
|
11
11
|
export { GuestResource, HostResourceRegistry } from "./resources.js";
|
|
12
|
-
export { camelCase,
|
|
13
|
-
export {
|
|
12
|
+
export { camelCase, pascalCase } from "./casing.js";
|
|
13
|
+
export { ImportRegistrationError, ImportResolutionError, ImportResolver, } from "./version.js";
|
|
14
14
|
export { type AdapterOptions, BorrowScope, fromHost, toHost, type ValueBridge, } from "./values.js";
|
|
15
|
+
export { type Sync, sync } from "./sync.js";
|
|
@@ -2,17 +2,17 @@ import type { ResourceTypeInfo, ValType } from "../cabi/types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Internal state of a guest-resource wrapper.
|
|
4
4
|
*
|
|
5
|
-
* The KEY is the process-global `polyengine.resourceState/1` brand
|
|
6
|
-
*
|
|
5
|
+
* The KEY is the process-global `polyengine.resourceState/1` brand
|
|
6
|
+
* (contracts/embedder-api.md §"Module identity": it used to be a module-local `Symbol(...)`, on the now-repealed
|
|
7
7
|
* assumption that bundle and source runtimes are never mixed in one process —
|
|
8
8
|
* issue #83 showed they routinely are). The state SHAPE stays strictly
|
|
9
|
-
* runtime-internal, exactly as the
|
|
9
|
+
* runtime-internal, exactly as the module identity brand table notes: another copy may
|
|
10
10
|
* RECOGNIZE a wrapper, and must never read or write this object. `copyUrl` is
|
|
11
11
|
* what lets this copy tell its own wrappers from a foreign copy's.
|
|
12
12
|
*/
|
|
13
13
|
declare const STATE: symbol;
|
|
14
14
|
interface WrapperState {
|
|
15
|
-
/** The runtime copy that minted this wrapper
|
|
15
|
+
/** The runtime copy that minted this wrapper. */
|
|
16
16
|
copyUrl: string;
|
|
17
17
|
rep: number;
|
|
18
18
|
/** False once the handle was transferred away or dropped. */
|
|
@@ -69,7 +69,7 @@ export declare function initWrapper(w: GuestResource, state: Omit<WrapperState,
|
|
|
69
69
|
*
|
|
70
70
|
* A wrapper minted by ANOTHER copy carries the same (process-global) brand key
|
|
71
71
|
* but its state belongs to that copy — reading it here would be reading a
|
|
72
|
-
* foreign copy's private shape
|
|
72
|
+
* foreign copy's private shape. So it is not a state: it is
|
|
73
73
|
* `undefined` here, and `requireLive` turns that into the named cross-copy
|
|
74
74
|
* error rather than a misleading "not a resource handle" / "not live".
|
|
75
75
|
*/
|
|
@@ -100,15 +100,28 @@ export interface GuestResourceSpec {
|
|
|
100
100
|
raw: (...a: unknown[]) => unknown;
|
|
101
101
|
params: ValType[];
|
|
102
102
|
results: ValType[];
|
|
103
|
+
/** True for an `async func` — see sync()'s `{ kind: "async" }` brand. */
|
|
104
|
+
async: boolean;
|
|
103
105
|
}[];
|
|
104
106
|
statics: {
|
|
105
107
|
member: string;
|
|
106
108
|
raw: (...a: unknown[]) => unknown;
|
|
107
109
|
params: ValType[];
|
|
108
110
|
results: ValType[];
|
|
111
|
+
async: boolean;
|
|
109
112
|
}[];
|
|
110
113
|
}
|
|
111
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Build (once, at class-build time — never per call) the Promise-shaped
|
|
116
|
+
* wrapper for one method/static's raw lifted function, exactly as
|
|
117
|
+
* `Facade#wrapExportFn` would for a plain export. `buildGuestResourceClass`
|
|
118
|
+
* reads the sync() brand off the returned wrapper to install the matching
|
|
119
|
+
* `"method"`/`"free"`/`"async"` brand on the class member it builds around
|
|
120
|
+
* it — the wrapper itself IS what a per-call closure invokes, so a `self`
|
|
121
|
+
* receiver is `wrapper(self, ...args)` for a method the same way a bare
|
|
122
|
+
* export is `wrapper(...args)`.
|
|
123
|
+
*/
|
|
124
|
+
export type ExportWrapper = (raw: (...a: unknown[]) => unknown, params: ValType[], results: ValType[], async: boolean, where: string) => (...args: unknown[]) => Promise<unknown>;
|
|
112
125
|
/**
|
|
113
126
|
* Build the class for a guest-implemented resource.
|
|
114
127
|
*
|
|
@@ -118,7 +131,7 @@ export type CallAdapter = (raw: (...a: unknown[]) => unknown, params: ValType[],
|
|
|
118
131
|
* synchronously is reported as such rather than silently returning a
|
|
119
132
|
* half-built object (see the report's contract-friction list).
|
|
120
133
|
*/
|
|
121
|
-
export declare function buildGuestResourceClass(spec: GuestResourceSpec, rt: ResourceTypeInfo,
|
|
134
|
+
export declare function buildGuestResourceClass(spec: GuestResourceSpec, rt: ResourceTypeInfo, wrapExport: ExportWrapper, lowerArgs: (args: unknown[], params: ValType[], where: string) => unknown[]): any;
|
|
122
135
|
/** Materialize an `own`/`borrow` wrapper for a rep coming out of a guest. */
|
|
123
136
|
export declare function makeWrapper(cls: any, rep: number, rt: ResourceTypeInfo, owns: boolean): GuestResource;
|
|
124
137
|
/**
|
|
@@ -15,7 +15,7 @@ export interface ElemCodec<T> {
|
|
|
15
15
|
/** conventions value -> internal component value */
|
|
16
16
|
fromHost(v: T): ComponentValue;
|
|
17
17
|
/**
|
|
18
|
-
* Destroy a LOWERED element the reader will never take (
|
|
18
|
+
* Destroy a LOWERED element the reader will never take (§"Streams and futures");
|
|
19
19
|
* present only for element types that hold resources (`own<R>`), where
|
|
20
20
|
* abandonment without destruction is a leak.
|
|
21
21
|
*/
|
|
@@ -69,7 +69,7 @@ export declare class Stream<T> implements ProtocolStream<T> {
|
|
|
69
69
|
read(max: number): Promise<Chunk<T>>;
|
|
70
70
|
/**
|
|
71
71
|
* Consume the writer's bytes in place, without an intermediate chunk
|
|
72
|
-
* (`stream<u8>` only — contracts/embedder-api.md
|
|
72
|
+
* (`stream<u8>` only — contracts/embedder-api.md §"Streams and futures" ("Direct-access byte edges"),
|
|
73
73
|
* polyengine#128).
|
|
74
74
|
*
|
|
75
75
|
* At every rendezvous with a writer of nonzero capacity, `consume` runs
|
|
@@ -86,7 +86,7 @@ export declare class Stream<T> implements ProtocolStream<T> {
|
|
|
86
86
|
* writer's parked operation survives and the stream stays alive.
|
|
87
87
|
*
|
|
88
88
|
* Refusals mirror `read`: an unbound `Stream.create()` handle and a handle
|
|
89
|
-
* already passed to a guest (the
|
|
89
|
+
* already passed to a guest (the deadlock-verdict suppression transfer guard) both throw, as does a
|
|
90
90
|
* non-`u8` element type.
|
|
91
91
|
*/
|
|
92
92
|
readDirect(consume: (src: DirectSource) => DirectVerdict): Promise<number>;
|
|
@@ -113,7 +113,7 @@ export declare class Stream<T> implements ProtocolStream<T> {
|
|
|
113
113
|
* DROPPED event into the trapping instance's waitables, and a later
|
|
114
114
|
* driving loop asserted on the corpse).
|
|
115
115
|
*
|
|
116
|
-
* The arm is released on this path too (#162,
|
|
116
|
+
* The arm is released on this path too (#162, §"Streams and futures"): the wrapper's
|
|
117
117
|
* `HostActivity` now closes through the shared object's drop observers,
|
|
118
118
|
* which `dropSharedForTeardown` fires unconditionally — so a teardown with
|
|
119
119
|
* nothing parked no longer leaves the arm outliving the stream. (This
|
|
@@ -143,7 +143,7 @@ export declare class StreamWriter<T> implements ProtocolStreamWriter<T> {
|
|
|
143
143
|
write(values: Chunk<T>): Promise<number>;
|
|
144
144
|
/**
|
|
145
145
|
* Fill the reader's landing zone in place, without an intermediate chunk
|
|
146
|
-
* (`stream<u8>` only — contracts/embedder-api.md
|
|
146
|
+
* (`stream<u8>` only — contracts/embedder-api.md §"Streams and futures" ("Direct-access byte edges"),
|
|
147
147
|
* polyengine#128).
|
|
148
148
|
*
|
|
149
149
|
* At every rendezvous with a reader of nonzero capacity, `produce` runs
|
|
@@ -197,7 +197,6 @@ export declare class Future<T> implements ProtocolFuture<T> {
|
|
|
197
197
|
* is itself PromiseLike, so `await` still works and still yields `T`), which
|
|
198
198
|
* keeps `drop()`/`cancel()` reachable for a caller that does not await. The
|
|
199
199
|
* alternative — resolving a Promise *to* the handle — is not expressible.
|
|
200
|
-
* Flagged in the C2 report.
|
|
201
200
|
*/
|
|
202
201
|
static deferred<T>(pending: Promise<ComponentValue>, codec: ElemCodec<T>): Future<T>;
|
|
203
202
|
/** @internal */
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** The registry symbol. `Symbol.for` per module identity: N runtime copies agree on it
|
|
2
|
+
* without sharing modules. */
|
|
3
|
+
export declare const SYNC_CALLABLE: unique symbol;
|
|
4
|
+
/**
|
|
5
|
+
* The brand payload, keyed by what the branded value is.
|
|
6
|
+
*
|
|
7
|
+
* - `"free"` — a lifted export function (plain export, interface member, or
|
|
8
|
+
* resource static): `fn` is the fully-wrapped synchronous form.
|
|
9
|
+
* - `"method"` — a guest-resource prototype method: `fn` takes the resource
|
|
10
|
+
* instance as its first argument (the `borrow<R>`/`own<R>` self param the
|
|
11
|
+
* lifted function already declares).
|
|
12
|
+
* - `"async"` — an async-typed export: carries no synchronous form, named so
|
|
13
|
+
* `sync()` can report the real reason.
|
|
14
|
+
*/
|
|
15
|
+
export type SyncPayload = {
|
|
16
|
+
kind: "free";
|
|
17
|
+
fn: (...args: unknown[]) => unknown;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "method";
|
|
20
|
+
fn: (self: unknown, ...args: unknown[]) => unknown;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "async";
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Stamp `payload` on `target` under the brand: non-enumerable, non-writable,
|
|
26
|
+
* matching `@polyengine/protocol`'s `defineBrand` (protocol/src/brands.ts) —
|
|
27
|
+
* implemented locally since the runtime does not add application-tier
|
|
28
|
+
* vocabulary to the protocol package.
|
|
29
|
+
*
|
|
30
|
+
* @internal — written by `instantiate.ts` and `resources.ts` at wrap/
|
|
31
|
+
* class-build time; not part of the public `sync()` surface.
|
|
32
|
+
*/
|
|
33
|
+
export declare function markSyncCallable(target: object, payload: SyncPayload): void;
|
|
34
|
+
/**
|
|
35
|
+
* Read the brand payload off `target`, or `undefined` if unbranded.
|
|
36
|
+
* Structural, like `hasBrand`: accepts a payload minted by any copy.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export declare function syncPayloadOf(target: unknown): SyncPayload | undefined;
|
|
40
|
+
/** `Promise<R>`-returning functions synchronize to `R`; records map
|
|
41
|
+
* recursively; everything else passes through. Type-level refusal of an
|
|
42
|
+
* async export is not attempted (the contract only requires the runtime
|
|
43
|
+
* error) — `Sync<F>` stays structural.
|
|
44
|
+
*
|
|
45
|
+
* CONTRACT: the naive `F extends Record<string, unknown>` branch (checked
|
|
46
|
+
* before this fix) only matches object-LITERAL type aliases — named
|
|
47
|
+
* interfaces (generated `*Exports`) and class instance types (generated
|
|
48
|
+
* resource classes, e.g. `Counter`) have no implicit index signature and
|
|
49
|
+
* are not assignable to it, so they fell through to the `: F` passthrough
|
|
50
|
+
* and stayed Promise-shaped. Ordering matters: a non-Promise function type
|
|
51
|
+
* (e.g. a resource's `drop(): void`, or `[Symbol.dispose]`) must be checked
|
|
52
|
+
* and passed through BEFORE the generic `object` branch, or `{ [K in keyof
|
|
53
|
+
* F]: ... }` would try to map over a function's call signature (losing it)
|
|
54
|
+
* instead of leaving the function itself alone. */
|
|
55
|
+
export type Sync<F> = F extends (...a: infer A) => Promise<infer R> ? (...a: A) => R : F extends (...a: never[]) => unknown ? F : F extends object ? {
|
|
56
|
+
[K in keyof F]: Sync<F[K]>;
|
|
57
|
+
} : F;
|
|
58
|
+
/**
|
|
59
|
+
* The synchronous form of a WIT-sync export (contracts/embedder-api.md
|
|
60
|
+
* §"Functions and async", §"Functions and async").
|
|
61
|
+
*
|
|
62
|
+
* - `sync(fn)` — a lifted export function (plain export, interface member,
|
|
63
|
+
* or resource static): returns the synchronous form `(...args) => T`.
|
|
64
|
+
* - `sync(instance)` — a guest-resource wrapper: a view whose members call
|
|
65
|
+
* the synchronous forms with `instance` as receiver.
|
|
66
|
+
* - `sync(cls)` — a guest-resource class: a view of synchronous statics
|
|
67
|
+
* (constructors are already synchronous; `new` the class itself).
|
|
68
|
+
* - `sync(record)` — an exports record or nested interface record: a view
|
|
69
|
+
* with every member mapped by these same rules, recursively; non-branded
|
|
70
|
+
* members pass through unchanged.
|
|
71
|
+
* - Views are stable: `sync(x) === sync(x)`.
|
|
72
|
+
* - An async-typed export, a bare resource-method function, or anything
|
|
73
|
+
* unbranded throws `TypeError`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function sync<F extends (...a: never[]) => Promise<unknown>>(target: F): Sync<F>;
|
|
76
|
+
export declare function sync<T extends object>(target: T): Sync<T>;
|
|
77
|
+
/** Fallback for a non-branded/primitive target — always throws at runtime
|
|
78
|
+
* (see the dispatch above); typed loosely so a caller passing an arbitrary
|
|
79
|
+
* value (as opposed to a known export/record/instance/class shape) still
|
|
80
|
+
* type-checks, matching the runtime's willingness to name the mistake. */
|
|
81
|
+
export declare function sync(target: unknown): unknown;
|
|
@@ -23,8 +23,8 @@ export interface ValueBridge {
|
|
|
23
23
|
kind: "borrow";
|
|
24
24
|
}): number;
|
|
25
25
|
/**
|
|
26
|
-
* Destroy a LOWERED `own<R>` the guest will never receive (
|
|
27
|
-
*
|
|
26
|
+
* Destroy a LOWERED `own<R>` the guest will never receive (a resource
|
|
27
|
+
* stream element the producer lowered but the reader never took).
|
|
28
28
|
* Runs the resource's destructor — for a host-implemented R the
|
|
29
29
|
* instance's `[Symbol.dispose]`, for a guest-implemented R the guest
|
|
30
30
|
* dtor — exactly as if the guest had taken the handle and dropped it.
|
package/types/exec/boundary.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ export declare function normalizeCoreValues(values: CoreValue[], lanes: CoreType
|
|
|
114
114
|
* repeat. Reimplementing it there diverged: that copy only drained
|
|
115
115
|
* `store.awaiting` and never awaited `pendingHostCalls`, so a guest parked on
|
|
116
116
|
* a Promise-returning host import was never resumed and the host's read of
|
|
117
|
-
* the stream it was feeding hung (
|
|
117
|
+
* the stream it was feeding hung (host-pump starvation of `pendingHostCalls`).
|
|
118
118
|
*
|
|
119
119
|
* Callers that must not hit the deadlock traps below (the host pump: an
|
|
120
120
|
* embedder that never does its half is documented to hang, not trap) can
|
|
@@ -146,26 +146,35 @@ export declare function ensureSettlementPump(store: Store): void;
|
|
|
146
146
|
* needs genuine wasm-frame suspension: `needsJspi`, at the precise point.
|
|
147
147
|
*/
|
|
148
148
|
/**
|
|
149
|
-
* The plain-entered variant of a
|
|
150
|
-
* attached to the promising-wrapped lifted function under this symbol
|
|
149
|
+
* The plain-entered variant of a **sync-typed** lifted export in jspi mode,
|
|
150
|
+
* attached to the promising-wrapped lifted function under this symbol
|
|
151
|
+
* (contracts/embedder-api.md §"Functions and async", §"Functions and async").
|
|
151
152
|
*
|
|
152
|
-
*
|
|
153
|
-
* (
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* overwhelmingly common case; WIT constructors are always sync-typed —
|
|
159
|
-
* returns its rep synchronously through it.
|
|
153
|
+
* In jspi mode every promising-wrapped entry returns a Promise even when the
|
|
154
|
+
* activation completes without suspending (jspi pin (e)). Some host contexts
|
|
155
|
+
* cannot use a Promise no matter how promptly it resolves, so each sync-typed
|
|
156
|
+
* export carries a second lifted function whose ENTRY is plain (unwrapped):
|
|
157
|
+
* a guest activation that completes synchronously — the overwhelmingly common
|
|
158
|
+
* case for sync-typed WIT — delivers its results synchronously through it.
|
|
160
159
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
160
|
+
* Two consumers, one mechanism:
|
|
161
|
+
*
|
|
162
|
+
* * **resource constructors** — a WIT constructor is surfaced as a JS class
|
|
163
|
+
* constructor (§"Resources") and a JS constructor cannot await, so the
|
|
164
|
+
* embedder layer reads this symbol unconditionally for `[constructor]`
|
|
165
|
+
* exports;
|
|
166
|
+
* * **the embedder `sync()` adapter** — the explicit per-use
|
|
167
|
+
* synchronous view of any sync-typed export.
|
|
168
|
+
*
|
|
169
|
+
* The cost is confined to genuinely-suspending activations: a blocking
|
|
170
|
+
* built-in reached through the plain entry signals `NeedsJspi` (a capability
|
|
171
|
+
* error, instance left enterable), and a `Suspending`-wrapped host import
|
|
172
|
+
* reached from the unwrapped frame fails as a trap. Both name the export
|
|
173
|
+
* rather than silently deadlocking. A call made while the instance has
|
|
174
|
+
* hop-parked activations refuses with `SyncEntryBusy` before entering
|
|
175
|
+
* (`refuseOnEntryHops` below).
|
|
167
176
|
*/
|
|
168
|
-
export declare const
|
|
177
|
+
export declare const SYNC_ENTRY: unique symbol;
|
|
169
178
|
export declare function createLiftedFunction(input: {
|
|
170
179
|
name: string;
|
|
171
180
|
ft: FuncType;
|
|
@@ -212,6 +221,22 @@ export declare function createLiftedFunction(input: {
|
|
|
212
221
|
* Promise, which it can never advance, and declares a bogus deadlock.
|
|
213
222
|
*/
|
|
214
223
|
allowAsyncCompletion?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Refuse — synchronously, before entering — a call made while the instance
|
|
226
|
+
* has HOP-parked activations, instead of deferring it (§"Functions and async",
|
|
227
|
+
* failure-ladder arm 2).
|
|
228
|
+
*
|
|
229
|
+
* Set for the `SYNC_ENTRY` variant, which is built with
|
|
230
|
+
* `suspensionMode: "plain"` inside a *jspi-mode* instantiation: the
|
|
231
|
+
* hop-quiescence gate below is keyed on this function's own mode and so is
|
|
232
|
+
* dead for that variant, yet the hazard it exists to prevent is the
|
|
233
|
+
* instance's, not the entry's — a hop-parked activation's pending lift
|
|
234
|
+
* reads memory a fresh guest turn would mutate (see the gate's comment).
|
|
235
|
+
* A synchronous caller cannot be deferred, so it refuses instead. The
|
|
236
|
+
* refusal is pre-enter, hence non-poisoning: nothing was entered, so there
|
|
237
|
+
* is nothing to poison — the same structural safety as `entryRefusal`.
|
|
238
|
+
*/
|
|
239
|
+
refuseOnEntryHops?: boolean;
|
|
215
240
|
}): (...args: ComponentValue[]) => unknown;
|
|
216
241
|
/**
|
|
217
242
|
* Build the host-callable entry for a resource destructor — a full canonical
|
|
@@ -224,27 +249,13 @@ export declare function createLiftedFunction(input: {
|
|
|
224
249
|
* callee = inst.store.lift(dtor, ft, opts, rt.impl)
|
|
225
250
|
* ```
|
|
226
251
|
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* so `Store.tick`'s enterability filter (#155) could never resume a
|
|
235
|
-
* suspension point belonging to the dtor's own activation. The completion
|
|
236
|
-
* promise sat in `pendingHostCalls` looking like external work, and every
|
|
237
|
-
* driver parked on it forever.
|
|
238
|
-
* - it was the runtime's only `enterFrom(null)` bracket spanning an await —
|
|
239
|
-
* the macro-scale reachability window of the #156 class, through which a
|
|
240
|
-
* sibling instance looked non-enterable from the synthetic root.
|
|
241
|
-
* - built-ins reached inside the dtor had no ambient task (`currentTask()`
|
|
242
|
-
* → `PendingCapability`, or a foreign-task misattribution, the #24 class).
|
|
243
|
-
*
|
|
244
|
-
* Under the lift harness all three go away structurally: the activation has a
|
|
245
|
-
* real `Task` + implicit `Thread`, the entry bracket is released when the
|
|
246
|
-
* first segment parks (`leave()` before `drive`), and settled tails flow
|
|
247
|
-
* through `serviceSettled` like any other lifted sync call.
|
|
252
|
+
* The host-initiated paths (embedder `drop()`, the GC backstop, `dropOwn`)
|
|
253
|
+
* route through this harness rather than calling the dtor bare, because the
|
|
254
|
+
* activation then has a real `Task` + implicit `Thread`: built-ins reached
|
|
255
|
+
* inside the dtor are well-attributed (a bare call leaves `currentTask()`
|
|
256
|
+
* with no ambient task — `PendingCapability`, or a foreign-task
|
|
257
|
+
* misattribution, the #24 class), and settled tails flow through
|
|
258
|
+
* `serviceSettled` like any other lifted sync call.
|
|
248
259
|
*
|
|
249
260
|
* The returned function takes the rep and returns either `undefined` (the
|
|
250
261
|
* activation completed synchronously — the overwhelmingly common case) or a
|
|
@@ -256,7 +267,7 @@ export declare function createDtorEntry(input: {
|
|
|
256
267
|
/**
|
|
257
268
|
* The destructor's core function, unwrapped: `createLiftedFunction` applies
|
|
258
269
|
* `enterWasm` itself per `suspensionMode`. `null` is the reference's
|
|
259
|
-
* `rt.dtor or (lambda rep: [])` — the
|
|
270
|
+
* `rt.dtor or (lambda rep: [])` — the lift still runs.
|
|
260
271
|
*/
|
|
261
272
|
dtor: CoreFn | null;
|
|
262
273
|
/** `rt.impl`, the implementing instance the lift enters. */
|
|
@@ -293,7 +304,7 @@ export declare function hostDtorCall(rt: ResourceTypeInfo, rep: number): void;
|
|
|
293
304
|
* This is the whole of the jspi entry seam. In **plain** mode the entry is not
|
|
294
305
|
* `promising`-wrapped, `callCore` returns core values, and this returns them
|
|
295
306
|
* without yielding — no await, no Promise allocation, the identical
|
|
296
|
-
* synchronous path
|
|
307
|
+
* synchronous path plain mode always used. In **jspi** mode the entry *is* wrapped, so the
|
|
297
308
|
* call returns a Promise (jspi pin (e)) and we park the thread on it via the
|
|
298
309
|
* `awaitValue` block request; the driving loop resumes us with the values, or
|
|
299
310
|
* throws the rejection in (a post-resume trap).
|
|
@@ -334,8 +345,21 @@ export declare function createLoweredImport(input: {
|
|
|
334
345
|
stats: ExecutionStats;
|
|
335
346
|
/** Executor's suspension mode; decides whether a sync lower may park. */
|
|
336
347
|
mode: SuspensionMode;
|
|
337
|
-
/** Host fn carries the `suspending()` brand (embedder-api.md
|
|
348
|
+
/** Host fn carries the `suspending()` brand (embedder-api.md suspending mark). */
|
|
338
349
|
suspendable: boolean;
|
|
350
|
+
/**
|
|
351
|
+
* Host fn carries the `deferCancel()` brand (embedder-api.md cancellation discard): the
|
|
352
|
+
* import must run to completion, so a cancellation is accepted and ignored
|
|
353
|
+
* instead of taking the default discard.
|
|
354
|
+
*/
|
|
355
|
+
deferCancel: boolean;
|
|
356
|
+
/**
|
|
357
|
+
* Host fn carries the `abortable()` brand (embedder-api.md abortable()): every call
|
|
358
|
+
* receives a fresh `AbortSignal` appended after the WIT-declared params, and
|
|
359
|
+
* the runtime aborts it when — and only when — the call is discarded by a
|
|
360
|
+
* guest cancellation.
|
|
361
|
+
*/
|
|
362
|
+
abortable: boolean;
|
|
339
363
|
}): CoreFn;
|
|
340
364
|
/**
|
|
341
365
|
* The callback-ABI dispatch loop of `canon_lift` (definitions.py lines
|
package/types/exec/executor.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { type ExecutionStats } from "./boundary.js";
|
|
|
6
6
|
/**
|
|
7
7
|
* Host-provided imports: a nested record keyed by the component's *exact*
|
|
8
8
|
* import strings. A plan import with a non-empty `path` (an item extracted
|
|
9
|
-
* from an imported instance — plan-format.md
|
|
9
|
+
* from an imported instance — `imports[].path`, contracts/plan-format.md
|
|
10
|
+
* schema) is looked up
|
|
10
11
|
* by walking `imports[name]` then each path segment in order. So an import
|
|
11
12
|
* of `"ns:pkg/iface"` exposing `f` is supplied as
|
|
12
13
|
* `{ "ns:pkg/iface": { f: (…) => … } }`.
|
|
@@ -65,7 +66,7 @@ export interface InstantiateInput {
|
|
|
65
66
|
* returns a Promise (empirical fact (e) — `WebAssembly.promising` always
|
|
66
67
|
* does), which is an API-shape change. Ignored on an engine without JSPI,
|
|
67
68
|
* where every blocking site keeps raising the precise `NeedsJspi` it raises
|
|
68
|
-
* today (the
|
|
69
|
+
* today (the browser-matrix degradation path; see `just browsers`).
|
|
69
70
|
*/
|
|
70
71
|
jspi?: boolean;
|
|
71
72
|
/**
|
|
@@ -41,7 +41,7 @@ export declare class HostBuffer {
|
|
|
41
41
|
advanceBytes(k: number): void;
|
|
42
42
|
endWindow(): void;
|
|
43
43
|
}
|
|
44
|
-
/** The scoped landing zone handed to a `writeDirect` producer (
|
|
44
|
+
/** The scoped landing zone handed to a `writeDirect` producer (direct-access byte edge, #128). */
|
|
45
45
|
export interface DirectDestination {
|
|
46
46
|
/**
|
|
47
47
|
* The reader's still-unfilled bytes. Re-derived on every call (a
|
|
@@ -56,7 +56,7 @@ export interface DirectDestination {
|
|
|
56
56
|
*/
|
|
57
57
|
markWritten(n: number): void;
|
|
58
58
|
}
|
|
59
|
-
/** The scoped view handed to a `readDirect` consumer (
|
|
59
|
+
/** The scoped view handed to a `readDirect` consumer (direct-access byte edge, #128). */
|
|
60
60
|
export interface DirectSource {
|
|
61
61
|
/**
|
|
62
62
|
* The writer's unread bytes; read-only by contract. Same scoping and
|
|
@@ -66,14 +66,14 @@ export interface DirectSource {
|
|
|
66
66
|
/** Acknowledge bytes consumed from the view. See `markWritten`. */
|
|
67
67
|
markRead(n: number): void;
|
|
68
68
|
}
|
|
69
|
-
/** The callback's poll cadence, spelled event-style
|
|
69
|
+
/** The callback's poll cadence, spelled event-style. */
|
|
70
70
|
export type DirectVerdict = "more" | "done";
|
|
71
71
|
/**
|
|
72
72
|
* Out-parameter of the low-level direct forms: `true` iff the session ended
|
|
73
73
|
* because the callback itself returned `"done"`, rather than because the peer
|
|
74
74
|
* dropped / the operation was cancelled / the peer's instance trapped.
|
|
75
75
|
*
|
|
76
|
-
* The conventions layer needs the distinction for
|
|
76
|
+
* The conventions layer needs the distinction for loud component fault precision — a session
|
|
77
77
|
* the producer already completed keeps its resolution even if the peer then
|
|
78
78
|
* trapped — and `Promise<number>` is the contract's return shape, so it rides
|
|
79
79
|
* here rather than in the resolved value.
|
|
@@ -110,7 +110,7 @@ export interface HostWritableEnd<T> {
|
|
|
110
110
|
writeAll(values: T[]): Promise<number>;
|
|
111
111
|
/**
|
|
112
112
|
* Park a **direct session** on this end (`stream<u8>` only — embedder-api
|
|
113
|
-
*
|
|
113
|
+
* §"Streams and futures" ("Direct-access byte edges") (polyengine#128)).
|
|
114
114
|
*
|
|
115
115
|
* At every rendezvous with a reader of nonzero capacity, `produce` runs
|
|
116
116
|
* exactly once, synchronously, inside the rendezvous, with a
|
|
@@ -138,9 +138,9 @@ export interface HostWritableEnd<T> {
|
|
|
138
138
|
drop(): void;
|
|
139
139
|
/**
|
|
140
140
|
* Fire `fn` once the stream becomes dropped — by either end, including
|
|
141
|
-
* the
|
|
141
|
+
* the loud component fault teardown walk (immediately, if it already is). The embedder's
|
|
142
142
|
* producer pump uses it to cancel a producer parked on an external
|
|
143
|
-
* event (
|
|
143
|
+
* event (§"Streams and futures"'s cancellation companion).
|
|
144
144
|
*/
|
|
145
145
|
onDropped(fn: () => void): void;
|
|
146
146
|
}
|
|
@@ -155,7 +155,7 @@ export interface HostReadableEnd<T> {
|
|
|
155
155
|
read(max: number): Promise<T[]>;
|
|
156
156
|
/**
|
|
157
157
|
* Park a **direct session** on this end (`stream<u8>` only — embedder-api
|
|
158
|
-
*
|
|
158
|
+
* §"Streams and futures" ("Direct-access byte edges") (polyengine#128)). The mirror of
|
|
159
159
|
* `HostWritableEnd.writeDirect`: `consume` receives a `DirectSource` over
|
|
160
160
|
* the writer's unread bytes (a view of guest memory, or of the offered
|
|
161
161
|
* host chunk itself) and may take a prefix — a partial take is normal, and
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Instantiate-time failure for functionality
|
|
1
|
+
/** Instantiate-time failure for functionality gated on a missing capability. */
|
|
2
2
|
export declare class UnsupportedFeatureError extends Error {
|
|
3
|
-
|
|
4
|
-
constructor(
|
|
3
|
+
capability: "resources" | "task-core" | "streams" | "jspi";
|
|
4
|
+
constructor(capability: "resources" | "task-core" | "streams" | "jspi", what: string);
|
|
5
5
|
}
|
|
@@ -182,6 +182,6 @@ export interface TrampolineContext {
|
|
|
182
182
|
* unsupported kind fails instantiation, not the first call
|
|
183
183
|
* (plan-format.md "Executor obligations"). Unreferenced trampolines are
|
|
184
184
|
* never created and therefore never fail (intrinsics.md §B tolerates e.g.
|
|
185
|
-
* an unreferenced task-return until
|
|
185
|
+
* an unreferenced task-return until the task core exists).
|
|
186
186
|
*/
|
|
187
187
|
export declare function createTrampoline(decl: WireTrampoline, ctx: TrampolineContext): CoreFn;
|
|
@@ -99,8 +99,8 @@ export declare function createFutureTransfer(ctx: AsyncTransferContext): CoreFn;
|
|
|
99
99
|
* rather than removing it, so the source keeps its own.
|
|
100
100
|
*/
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
102
|
+
* `instanceOf` resolves through the plan's `errorContextTables` section
|
|
103
|
+
* (contracts/plan-format.md schema) — the
|
|
104
104
|
* `TypeComponentLocalErrorContextTableIndex` space these arguments actually
|
|
105
105
|
* live in. It replaced a resource-table lookup, which shared neither the
|
|
106
106
|
* index space nor (in a multi-instance composition) the answer.
|