@polyengine/runtime 0.1.0-pre.g633468a

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.
Files changed (126) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +23 -0
  3. package/esm/cabi/async_values.js +162 -0
  4. package/esm/cabi/bulk_lists.js +198 -0
  5. package/esm/cabi/context.js +42 -0
  6. package/esm/cabi/flatten.js +145 -0
  7. package/esm/cabi/float.js +67 -0
  8. package/esm/cabi/handles.js +253 -0
  9. package/esm/cabi/layout.js +149 -0
  10. package/esm/cabi/lift.js +196 -0
  11. package/esm/cabi/load.js +146 -0
  12. package/esm/cabi/lower.js +141 -0
  13. package/esm/cabi/memory.js +182 -0
  14. package/esm/cabi/mod.js +22 -0
  15. package/esm/cabi/store.js +186 -0
  16. package/esm/cabi/strings.js +336 -0
  17. package/esm/cabi/trap.js +38 -0
  18. package/esm/cabi/types.js +264 -0
  19. package/esm/cabi/values.js +64 -0
  20. package/esm/cache/core.js +156 -0
  21. package/esm/cache/dir.js +170 -0
  22. package/esm/cache/mod.js +4 -0
  23. package/esm/cache/web.js +136 -0
  24. package/esm/digest/digest.js +332 -0
  25. package/esm/digest/mod.js +3 -0
  26. package/esm/digest/verify.js +129 -0
  27. package/esm/embedder/casing.js +56 -0
  28. package/esm/embedder/copy.js +42 -0
  29. package/esm/embedder/errors.js +26 -0
  30. package/esm/embedder/imports.js +63 -0
  31. package/esm/embedder/instantiate.js +978 -0
  32. package/esm/embedder/mod.js +40 -0
  33. package/esm/embedder/resources.js +406 -0
  34. package/esm/embedder/streams.js +770 -0
  35. package/esm/embedder/values.js +450 -0
  36. package/esm/embedder/version.js +273 -0
  37. package/esm/exec/boundary.js +1902 -0
  38. package/esm/exec/executor.js +1004 -0
  39. package/esm/exec/host_streams.js +818 -0
  40. package/esm/exec/mod.js +4 -0
  41. package/esm/intrinsics/async_builtins.js +510 -0
  42. package/esm/intrinsics/context.js +90 -0
  43. package/esm/intrinsics/errors.js +13 -0
  44. package/esm/intrinsics/fact_calls.js +865 -0
  45. package/esm/intrinsics/mod.js +564 -0
  46. package/esm/intrinsics/stream_builtins.js +578 -0
  47. package/esm/intrinsics/transcode.js +443 -0
  48. package/esm/jspi/bridge.js +579 -0
  49. package/esm/jspi/mechanics.js +89 -0
  50. package/esm/jspi/mod.js +5 -0
  51. package/esm/jspi/suspending.js +15 -0
  52. package/esm/jspi/types.js +29 -0
  53. package/esm/package.json +3 -0
  54. package/esm/plan/format.js +5 -0
  55. package/esm/plan/loader.js +657 -0
  56. package/esm/plan/mod.js +3 -0
  57. package/esm/shim/mod.js +2 -0
  58. package/esm/shim/translator.js +94 -0
  59. package/esm/task/mod.js +483 -0
  60. package/esm/task/scheduler.js +1028 -0
  61. package/esm/task/streams.js +786 -0
  62. package/esm/task/subtask.js +135 -0
  63. package/esm/task/thread.js +255 -0
  64. package/esm/task/waitable.js +144 -0
  65. package/package.json +91 -0
  66. package/types/cabi/async_values.d.ts +35 -0
  67. package/types/cabi/bulk_lists.d.ts +18 -0
  68. package/types/cabi/context.d.ts +59 -0
  69. package/types/cabi/flatten.d.ts +14 -0
  70. package/types/cabi/float.d.ts +14 -0
  71. package/types/cabi/handles.d.ts +70 -0
  72. package/types/cabi/layout.d.ts +13 -0
  73. package/types/cabi/lift.d.ts +25 -0
  74. package/types/cabi/load.d.ts +14 -0
  75. package/types/cabi/lower.d.ts +10 -0
  76. package/types/cabi/memory.d.ts +58 -0
  77. package/types/cabi/mod.d.ts +15 -0
  78. package/types/cabi/store.d.ts +12 -0
  79. package/types/cabi/strings.d.ts +23 -0
  80. package/types/cabi/trap.d.ts +11 -0
  81. package/types/cabi/types.d.ts +206 -0
  82. package/types/cabi/values.d.ts +5 -0
  83. package/types/cache/core.d.ts +97 -0
  84. package/types/cache/dir.d.ts +6 -0
  85. package/types/cache/mod.d.ts +3 -0
  86. package/types/cache/web.d.ts +10 -0
  87. package/types/digest/digest.d.ts +17 -0
  88. package/types/digest/mod.d.ts +2 -0
  89. package/types/digest/verify.d.ts +48 -0
  90. package/types/embedder/casing.d.ts +40 -0
  91. package/types/embedder/copy.d.ts +24 -0
  92. package/types/embedder/errors.d.ts +11 -0
  93. package/types/embedder/imports.d.ts +47 -0
  94. package/types/embedder/instantiate.d.ts +88 -0
  95. package/types/embedder/mod.d.ts +11 -0
  96. package/types/embedder/resources.d.ts +158 -0
  97. package/types/embedder/streams.d.ts +202 -0
  98. package/types/embedder/values.d.ts +70 -0
  99. package/types/embedder/version.d.ts +85 -0
  100. package/types/exec/boundary.d.ts +360 -0
  101. package/types/exec/executor.d.ts +125 -0
  102. package/types/exec/host_streams.d.ts +165 -0
  103. package/types/exec/mod.d.ts +3 -0
  104. package/types/intrinsics/async_builtins.d.ts +69 -0
  105. package/types/intrinsics/context.d.ts +28 -0
  106. package/types/intrinsics/errors.d.ts +5 -0
  107. package/types/intrinsics/fact_calls.d.ts +120 -0
  108. package/types/intrinsics/mod.d.ts +187 -0
  109. package/types/intrinsics/stream_builtins.d.ts +113 -0
  110. package/types/intrinsics/transcode.d.ts +21 -0
  111. package/types/jspi/bridge.d.ts +227 -0
  112. package/types/jspi/mechanics.d.ts +50 -0
  113. package/types/jspi/mod.d.ts +3 -0
  114. package/types/jspi/suspending.d.ts +1 -0
  115. package/types/jspi/types.d.ts +26 -0
  116. package/types/plan/format.d.ts +369 -0
  117. package/types/plan/loader.d.ts +113 -0
  118. package/types/plan/mod.d.ts +2 -0
  119. package/types/shim/mod.d.ts +1 -0
  120. package/types/shim/translator.d.ts +55 -0
  121. package/types/task/mod.d.ts +257 -0
  122. package/types/task/scheduler.d.ts +421 -0
  123. package/types/task/streams.d.ts +370 -0
  124. package/types/task/subtask.d.ts +96 -0
  125. package/types/task/thread.d.ts +73 -0
  126. package/types/task/waitable.d.ts +67 -0
@@ -0,0 +1,360 @@
1
+ import { type CanonicalOptions, type ComponentValue, type CoreFuncType, type CoreType, type CoreValue, type FuncType, type PtrType, ResourceTypeInfo } from "../cabi/mod.js";
2
+ import { type BlockRequest, type Cancelled, ComponentInstanceState, Store, Task, Thread } from "../task/mod.js";
3
+ import { type SuspensionMode } from "../jspi/mod.js";
4
+ /**
5
+ * Structural view of an intrinsics `SyncCallScope`: everything this module
6
+ * needs in order to unwind a FACT sync-call bracket a trap escaped.
7
+ */
8
+ export interface LenderScope {
9
+ releaseLenders(): void;
10
+ }
11
+ /** A raw core function as exposed through the JS WebAssembly API. */
12
+ export type CoreFn = (...args: any[]) => unknown;
13
+ /** Counters exposed for tests/diagnostics on the component handle. */
14
+ export interface ExecutionStats {
15
+ liftedCalls: number;
16
+ tasksResolved: number;
17
+ postReturnsRun: number;
18
+ loweredCalls: number;
19
+ enterSyncCalls: number;
20
+ exitSyncCalls: number;
21
+ /** Callback-export invocations of the async lift loop (`canon_lift`). */
22
+ callbackInvocations: number;
23
+ }
24
+ export declare function newStats(): ExecutionStats;
25
+ /**
26
+ * A `MemInst`-shaped view over a `WebAssembly.Memory` that never goes stale:
27
+ * `bytes`/`view` re-derive from `memory.buffer` whenever the buffer identity
28
+ * changes (memory.grow detaches the previous ArrayBuffer — a cached
29
+ * Uint8Array would silently drop writes). The provider indirection also
30
+ * covers plan-order effects: canonical options can reference a memory whose
31
+ * `extract-memory` initializer runs later; accesses before extraction fail
32
+ * with a PlanError.
33
+ *
34
+ * Structurally compatible with cabi's `MemInst` (same public surface).
35
+ */
36
+ export declare class LiveMemory {
37
+ #private;
38
+ readonly addrType: PtrType;
39
+ constructor(provider: () => WebAssembly.Memory | undefined, label: string);
40
+ get bytes(): Uint8Array;
41
+ get view(): DataView;
42
+ get length(): number;
43
+ ptrType(): PtrType;
44
+ ptrSize(): 4 | 8;
45
+ }
46
+ /**
47
+ * Canonical options resolved against executor state. `memory` is a
48
+ * LiveMemory (or null); `realloc`/`postReturn`/`callback` resolve lazily so
49
+ * options can be constructed before the corresponding extract initializers
50
+ * run (wasmtime semantics: options hold indices, resolved at use).
51
+ */
52
+ export interface ResolvedOptions {
53
+ stringEncoding: "utf8" | "utf16" | "latin1+utf16";
54
+ memory: LiveMemory | null;
55
+ realloc: (() => CoreFn | undefined) | null;
56
+ postReturn: (() => CoreFn | undefined) | null;
57
+ callback: (() => CoreFn | undefined) | null;
58
+ async: boolean;
59
+ /**
60
+ * `CanonicalOptions.cancellable` (wasmtime-environ 47.0.3
61
+ * `component/info.rs:540`), i.e. whether a built-in reached through these
62
+ * options is a *cancellable* block point.
63
+ *
64
+ * It lives in the options, not in the trampoline: `Trampoline::
65
+ * WaitableSetWait`/`WaitableSetPoll` carry only `{instance, options}`
66
+ * (info.rs:815-831). definitions.py takes it as the first parameter of
67
+ * `canon_waitable_set_wait` / `canon_waitable_set_poll` (lines 2414/2431),
68
+ * which is the same information arriving by a different route.
69
+ *
70
+ * (`thread.yield` and `subtask.cancel` are the exceptions: wasmtime puts
71
+ * their `cancellable` / `async` flags on the *trampoline*, and those
72
+ * built-ins read them from the decl.)
73
+ */
74
+ cancellable: boolean;
75
+ coreType: CoreFuncType;
76
+ instance: ComponentInstanceState;
77
+ }
78
+ /** cabi-facing options object (LiftLowerOptions + flatten inputs). */
79
+ export declare function cabiOptions(opts: ResolvedOptions): CanonicalOptions;
80
+ /**
81
+ * Call a core function, mapping core-wasm exceptions to canonical-ABI traps
82
+ * (reference `call_and_trap_on_throw`). Component traps and internal errors
83
+ * of ours propagate unchanged.
84
+ */
85
+ /**
86
+ * Layering rule: a core-wasm trap's message is engine-specific text (V8,
87
+ * SpiderMonkey, JSC each word `unreachable` differently, for instance) and is
88
+ * passed through here UNTOUCHED — it is diagnostics only, "engine-flavored"
89
+ * and not normalized to any particular host's wording. The runtime never
90
+ * emulates another host's (e.g. wasmtime's) message text.
91
+ *
92
+ * Suite-wording normalization (matching the official test suite's
93
+ * `assert_trap` expectations, which are typically worded per wasmtime) lives
94
+ * in the harness instead: see `TRAP_MESSAGE_EQUIVALENTS` in
95
+ * harness/src/runner.ts, which maps engine-specific spellings to the
96
+ * suite-expected forms at comparison time. (The FACT *adapter* traps take a
97
+ * different route entirely — they arrive as numeric codes through the `trap`
98
+ * trampoline and are runtime-authored text, see `FACT_TRAP_MESSAGES` in
99
+ * intrinsics/mod.ts; that table is untouched by this layering rule.)
100
+ */
101
+ export declare function callCore(fn: CoreFn, args: CoreValue[]): CoreValue[];
102
+ /**
103
+ * Normalize raw JS-API core values to cabi's canonical lane representation:
104
+ * i32 lanes as unsigned numbers (the JS API yields signed), i64 lanes as
105
+ * unsigned bigints, floats as numbers.
106
+ */
107
+ export declare function normalizeCoreValues(values: CoreValue[], lanes: CoreType[], what: string): CoreValue[];
108
+ /**
109
+ * THE asynchronous driving loop, exported for the one other driver in the
110
+ * runtime: `HostActivity` in exec/host_streams.ts, which must pump the store
111
+ * BETWEEN export calls (when no lifted call is in flight) with exactly these
112
+ * semantics — service settled tails, tick to quiescence, then await the race
113
+ * of every outstanding promise (parked activations AND `pendingHostCalls`),
114
+ * repeat. Reimplementing it there diverged: that copy only drained
115
+ * `store.awaiting` and never awaited `pendingHostCalls`, so a guest parked on
116
+ * a Promise-returning host import was never resumed and the host's read of
117
+ * the stream it was feeding hung (C0 finding R-1).
118
+ *
119
+ * Callers that must not hit the deadlock traps below (the host pump: an
120
+ * embedder that never does its half is documented to hang, not trap) can
121
+ * exclude them entirely — BOTH trap sites require
122
+ * `store.pendingHostCalls.size === 0`, and both are reached only through the
123
+ * synchronous fall-through from `done()`, so a `done` that returns true
124
+ * whenever `pendingHostCalls` is empty provably never traps.
125
+ */
126
+ export declare function driveStoreAsync(store: Store, done: () => boolean, what: string): Promise<void>;
127
+ export declare function storeDriverDepth(store: Store): number;
128
+ /** Resolves once no `driveAsync` loop is live on `store`. */
129
+ export declare function whenStoreDriverIdle(store: Store): Promise<void>;
130
+ /**
131
+ * Ensure a settlement pump is watching `store`'s real outstanding host calls.
132
+ * Idempotent and cheap; called at every driver exit. Never throws.
133
+ */
134
+ export declare function ensureSettlementPump(store: Store): void;
135
+ /**
136
+ * Build the host-callable function for one lifted export (reference
137
+ * `Store.lift` + `canon_lift`, definitions.py lines 578 and 2154).
138
+ *
139
+ * All three lift shapes go through one `Task` + implicit `Thread`:
140
+ *
141
+ * * **sync** (`not ft.async`) — call, lift results, `task.return_`,
142
+ * post-return, then the sync driving loop until the task resolves;
143
+ * * **async + callback** (stackless) — the packed-code loop
144
+ * (EXIT / YIELD / WAIT), fully implemented here;
145
+ * * **async, no callback** (stackful) — the guest blocks mid-stack, which
146
+ * needs genuine wasm-frame suspension: `needsJspi`, at the precise point.
147
+ */
148
+ /**
149
+ * The plain-entered variant of a `[constructor]` export in jspi mode,
150
+ * attached to the promising-wrapped lifted function under this symbol.
151
+ *
152
+ * A WIT constructor is surfaced as a JS class constructor
153
+ * (contracts/embedder-api.md §"Resources"), and a JS constructor cannot
154
+ * await — but in jspi mode every promising-wrapped entry returns a Promise
155
+ * even when the activation completes without suspending (jspi pin (e)). So
156
+ * constructor exports carry a second lifted function whose ENTRY is plain
157
+ * (unwrapped): a constructor that completes synchronously — the
158
+ * overwhelmingly common case; WIT constructors are always sync-typed —
159
+ * returns its rep synchronously through it.
160
+ *
161
+ * The cost is confined to genuinely-suspending constructors, which no JS
162
+ * host can surface as `new` anyway: a blocking built-in reached through the
163
+ * plain entry signals `NeedsJspi` (a capability error, instance left
164
+ * enterable), and a Suspending-wrapped host import reached from the
165
+ * unwrapped frame fails as a trap. Both name the constructor rather than
166
+ * silently deadlocking.
167
+ */
168
+ export declare const CONSTRUCTOR_SYNC_ENTRY: unique symbol;
169
+ export declare function createLiftedFunction(input: {
170
+ name: string;
171
+ ft: FuncType;
172
+ opts: ResolvedOptions;
173
+ core: CoreFn;
174
+ stats: ExecutionStats;
175
+ /**
176
+ * Suspension discipline for this instantiation (jspi/bridge.ts). In `jspi`
177
+ * mode the export's core function is `promising`-wrapped, so the whole
178
+ * activation can suspend and the lifted function necessarily returns a
179
+ * Promise.
180
+ */
181
+ suspensionMode?: SuspensionMode;
182
+ /** Optional; see intrinsics `HostTrapState`. */
183
+ trapState?: {
184
+ pending: unknown;
185
+ };
186
+ /**
187
+ * Optional; the executor's sync-call scope stack (intrinsics
188
+ * `SyncCallScope`). Structural, to keep this module free of an import
189
+ * cycle with `../intrinsics/`.
190
+ */
191
+ syncCallStack?: LenderScope[];
192
+ /**
193
+ * Optional; every component instance of this component, for restoring
194
+ * `may_leave` when a trap unwinds out of a FACT adapter.
195
+ */
196
+ allInstances?: () => Iterable<{
197
+ mayLeave: boolean;
198
+ }>;
199
+ /**
200
+ * Opt out of the reference's *synchronous* driving loop (`driveSyncLift`,
201
+ * definitions.py `canon_lift` line 2213) for a sync-typed lift whose caller
202
+ * does not need a synchronous answer — today only the host-initiated
203
+ * resource destructor (#160; `createDtorEntry` below, `drop(): void` is
204
+ * documented non-blocking).
205
+ *
206
+ * This is not a weakening of the deadlock trap: `drive` below enforces the
207
+ * same "no ready thread, no pending host call, nothing awaiting" trap, just
208
+ * asynchronously — which is exactly the substitution jspi mode already
209
+ * makes unconditionally (see the comment at the `driveSyncLift` call).
210
+ * It matters only when a *plain*-mode core returns a thenable, i.e. a
211
+ * host-supplied JS destructor: the sync loop sees a thread parked on a
212
+ * Promise, which it can never advance, and declares a bogus deadlock.
213
+ */
214
+ allowAsyncCompletion?: boolean;
215
+ }): (...args: ComponentValue[]) => unknown;
216
+ /**
217
+ * Build the host-callable entry for a resource destructor — a full canonical
218
+ * **lift**, exactly as definitions.py `canon_resource_drop` (line 2319) does:
219
+ *
220
+ * ```python
221
+ * opts = CanonicalOptions(async_ = False)
222
+ * ft = FuncType([U32Type()], [], async_ = False)
223
+ * dtor = rt.dtor or (lambda rep: [])
224
+ * callee = inst.store.lift(dtor, ft, opts, rt.impl)
225
+ * ```
226
+ *
227
+ * Before #160 the host-initiated path (embedder `drop()`, the GC backstop,
228
+ * `dropOwn`) hand-rolled the bracket in cabi/handles.ts `callDtorGated`: a
229
+ * bare call to the dtor with `enterFrom(null)` HELD across the returned
230
+ * promise. Three defects followed from having no Task/Thread behind the
231
+ * activation:
232
+ *
233
+ * - **#160 itself**: the held bracket left the impl instance non-enterable,
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.
248
+ *
249
+ * The returned function takes the rep and returns either `undefined` (the
250
+ * activation completed synchronously — the overwhelmingly common case) or a
251
+ * Promise, exactly like any lifted sync export in jspi mode.
252
+ */
253
+ export declare function createDtorEntry(input: {
254
+ /** Diagnostic name; appears in deadlock/trap messages. */
255
+ name?: string;
256
+ /**
257
+ * The destructor's core function, unwrapped: `createLiftedFunction` applies
258
+ * `enterWasm` itself per `suspensionMode`. `null` is the reference's
259
+ * `rt.dtor or (lambda rep: [])` — the bracket still runs.
260
+ */
261
+ dtor: CoreFn | null;
262
+ /** `rt.impl`, the implementing instance the lift enters. */
263
+ instance: ComponentInstanceState;
264
+ suspensionMode?: SuspensionMode;
265
+ stats?: ExecutionStats;
266
+ trapState?: {
267
+ pending: unknown;
268
+ };
269
+ syncCallStack?: LenderScope[];
270
+ allInstances?: () => Iterable<{
271
+ mayLeave: boolean;
272
+ }>;
273
+ }): (rep: number) => unknown;
274
+ /**
275
+ * Run a host-initiated drop of a guest (or host-implemented) resource rep —
276
+ * the observable remainder of `canon_resource_drop` for an owning handle when
277
+ * the holder is the host (`caller = None`, `Store.invoke`).
278
+ *
279
+ * A failure that arrives asynchronously has no frame to propagate into, so it
280
+ * is parked on the store's host-failure channel (first failure wins), where
281
+ * the next driven call surfaces it. The completion promise is deliberately
282
+ * NOT registered in `store.pendingHostCalls`: that registration was #160's
283
+ * lie — it claims *external* work for a promise whose settlement may need
284
+ * this very scheduler. The dtor's genuine external dependencies (its host
285
+ * imports) register themselves when they park. Poisoning on a trap now
286
+ * happens inside the lift harness (`poison()` in `createLiftedFunction`).
287
+ */
288
+ export declare function hostDtorCall(rt: ResourceTypeInfo, rep: number): void;
289
+ /**
290
+ * Call into wasm and hand back the result, awaiting it only if it is a
291
+ * Promise.
292
+ *
293
+ * This is the whole of the jspi entry seam. In **plain** mode the entry is not
294
+ * `promising`-wrapped, `callCore` returns core values, and this returns them
295
+ * without yielding — no await, no Promise allocation, the identical
296
+ * synchronous path M1 shipped. In **jspi** mode the entry *is* wrapped, so the
297
+ * call returns a Promise (jspi pin (e)) and we park the thread on it via the
298
+ * `awaitValue` block request; the driving loop resumes us with the values, or
299
+ * throws the rejection in (a post-resume trap).
300
+ */
301
+ export declare function awaitCore(fn: CoreFn, args: CoreValue[], thread: any): Generator<BlockRequest, CoreValue[], unknown>;
302
+ /** definitions.py `CallbackCode` (line 2220). */
303
+ declare enum CallbackCode {
304
+ EXIT = 0,
305
+ YIELD = 1,
306
+ WAIT = 2
307
+ }
308
+ /** definitions.py `unpack_callback_result` (line 2226). */
309
+ export declare function unpackCallbackResult(packed: number): [code: CallbackCode, waitableSetIndex: number];
310
+ /**
311
+ * Build the core-callable body for one lowered host import (reference
312
+ * `canon_lower`, definitions.py line 2242).
313
+ *
314
+ * Sync and async lowers share one `Subtask` and one pair of
315
+ * `on_start`/`on_resolve` closures, exactly as the reference does; the sync
316
+ * case is the degenerate one where the callee resolves before returning.
317
+ *
318
+ * The host callee is a plain JS function. If it returns a **Promise**, the
319
+ * subtask resolves when that promise settles:
320
+ *
321
+ * * async lower — fully supported and JSPI-free. The guest gets a STARTED
322
+ * subtask back, joins it to a waitable set, returns WAIT from its
323
+ * callback, and the scheduler delivers the SUBTASK event once the promise
324
+ * settles. This is the flagship capability of this phase: an ordinary
325
+ * `async` JS function is a valid Component Model async import.
326
+ * * sync lower — the guest's wasm frame would have to block
327
+ * (`thread.wait_until(subtask.resolved)`, line 2286), so: `needsJspi`.
328
+ */
329
+ export declare function createLoweredImport(input: {
330
+ name: string;
331
+ ft: FuncType;
332
+ opts: ResolvedOptions;
333
+ hostFn: (...args: unknown[]) => unknown;
334
+ stats: ExecutionStats;
335
+ /** Executor's suspension mode; decides whether a sync lower may park. */
336
+ mode: SuspensionMode;
337
+ /** Host fn carries the `suspending()` brand (embedder-api.md A1). */
338
+ suspendable: boolean;
339
+ }): CoreFn;
340
+ /**
341
+ * The callback-ABI dispatch loop of `canon_lift` (definitions.py lines
342
+ * 2183-2214), factored out so both entry points share one implementation:
343
+ *
344
+ * * a host-boundary lift (`liftBody` above), and
345
+ * * a FACT cross-component call, where the host invokes an async-lifted
346
+ * callee on the caller's behalf (`intrinsics/fact_calls.ts`).
347
+ *
348
+ * `packed` is the code the *initial* activation returned; the loop runs until
349
+ * it sees EXIT, invoking the callback export with each delivered event.
350
+ */
351
+ export declare function runCallbackLoop(input: {
352
+ name: string;
353
+ task: Task;
354
+ thread: Thread;
355
+ inst: ComponentInstanceState;
356
+ callback: CoreFn;
357
+ packed: number;
358
+ stats: ExecutionStats;
359
+ }): Generator<BlockRequest, void, Cancelled>;
360
+ export {};
@@ -0,0 +1,125 @@
1
+ import type { ComponentValue } from "../cabi/types.js";
2
+ import { ComponentInstanceState } from "../task/mod.js";
3
+ import type { WirePlan } from "../plan/format.js";
4
+ import type { LoadedPlan } from "../plan/loader.js";
5
+ import { type ExecutionStats } from "./boundary.js";
6
+ /**
7
+ * Host-provided imports: a nested record keyed by the component's *exact*
8
+ * import strings. A plan import with a non-empty `path` (an item extracted
9
+ * from an imported instance — plan-format.md v0.1 amendment #4) is looked up
10
+ * by walking `imports[name]` then each path segment in order. So an import
11
+ * of `"ns:pkg/iface"` exposing `f` is supplied as
12
+ * `{ "ns:pkg/iface": { f: (…) => … } }`.
13
+ *
14
+ * Leaf values by import kind:
15
+ * - `func` — a JS function; arguments/results are host-shaped
16
+ * component values (contracts/descriptor-ir.md).
17
+ * - `resource` — a `HostResourceType` (see `hostResourceType`).
18
+ * - `instance` — a plain object; only its leaves are ever read.
19
+ * - `module` — not supported (see `InstantiateModule::Import` below).
20
+ */
21
+ export type HostImports = Record<string, unknown>;
22
+ /**
23
+ * Identity token for a resource type **defined by the host** and imported by
24
+ * a component (plan `importedResources`). One object per resource type;
25
+ * object identity is the type identity, exactly as for guest-defined
26
+ * resources whose identity is the per-instantiation `ResourceTypeInfo`.
27
+ */
28
+ export declare class HostResourceType {
29
+ readonly options: {
30
+ /** Debug name, used in error messages only. */
31
+ readonly name?: string;
32
+ /**
33
+ * Destructor for handles owned by a component and dropped there.
34
+ * Per docs/architecture.md §7 / CanonicalABI.md `canon resource.drop`, it runs
35
+ * synchronously and may not block.
36
+ */
37
+ readonly dtor?: (rep: number) => void;
38
+ };
39
+ constructor(options?: {
40
+ /** Debug name, used in error messages only. */
41
+ readonly name?: string;
42
+ /**
43
+ * Destructor for handles owned by a component and dropped there.
44
+ * Per docs/architecture.md §7 / CanonicalABI.md `canon resource.drop`, it runs
45
+ * synchronously and may not block.
46
+ */
47
+ readonly dtor?: (rep: number) => void;
48
+ });
49
+ }
50
+ /** Convenience constructor for {@link HostResourceType}. */
51
+ export declare function hostResourceType(options?: HostResourceType["options"]): HostResourceType;
52
+ export interface InstantiateInput {
53
+ plan: WirePlan;
54
+ /** The original component binary (embedded modules are sliced from it). */
55
+ componentBytes: Uint8Array;
56
+ /** FACT adapter artifacts keyed by `plan.modules[].file`. */
57
+ adapters?: Map<string, Uint8Array>;
58
+ imports?: HostImports;
59
+ /** Verify plan.component.sha256 against componentBytes (default true). */
60
+ verifyHash?: boolean;
61
+ /**
62
+ * Opt in to JSPI-backed suspension (docs/architecture.md §6 role 1-3).
63
+ *
64
+ * Off by default, and deliberately so: in this mode every lifted export
65
+ * returns a Promise (empirical fact (e) — `WebAssembly.promising` always
66
+ * does), which is an API-shape change. Ignored on an engine without JSPI,
67
+ * where every blocking site keeps raising the precise `NeedsJspi` it raises
68
+ * today (the M3 degradation path).
69
+ */
70
+ jspi?: boolean;
71
+ /**
72
+ * A plan already converted by `loadPlan`, used instead of re-loading.
73
+ *
74
+ * Why this exists: the conventions layer (`src/embedder/`) must have the
75
+ * per-instantiation `ResourceTypeInfo` identity tokens and the converted
76
+ * types table *before* instantiation begins, because host imports genuinely
77
+ * fire DURING it — a core module's `start` function runs inside
78
+ * `runInitializers`, and real guests do call imports from it (Go's runtime
79
+ * calls `monotonic-clock.now()` from `schedinit`). Reading them off the
80
+ * returned `ComponentHandle.loadedPlan` is therefore too late. Handing the
81
+ * same `LoadedPlan` in keeps the tokens identical on both sides.
82
+ *
83
+ * Contract: one `LoadedPlan` per instantiation (tokens must be fresh per
84
+ * component instance), and `loadedPlan.wire` must be `plan`. Both are
85
+ * checked.
86
+ */
87
+ loadedPlan?: LoadedPlan;
88
+ }
89
+ /** An instantiated component: its export surface plus introspection state. */
90
+ export interface ComponentHandle {
91
+ /** Lifted functions / nested instance objects, by export name. */
92
+ exports: Record<string, unknown>;
93
+ stats: ExecutionStats;
94
+ componentInstances: ComponentInstanceState[];
95
+ coreInstances: WebAssembly.Instance[];
96
+ /** See `Executor.suspendableFuncs`. */
97
+ suspendableFuncs: WeakSet<object>;
98
+ taskMayBlock: WebAssembly.Global;
99
+ /** `ResourceIndex` -> the `HostResourceType` bound to it, if any. */
100
+ hostResourceTypes: Map<number, HostResourceType>;
101
+ /**
102
+ * Plan exports deliberately absent from `exports`, by export path, with the
103
+ * reason. Only `type` exports appear here; a missing *function* export is
104
+ * always an error, never an omission (see `Executor.buildExport`).
105
+ */
106
+ omittedExports: Map<string, string>;
107
+ /**
108
+ * The plan as loaded for THIS instantiation.
109
+ *
110
+ * Exposed for the conventions layer (`src/embedder/`), which needs the two
111
+ * things only the executor's own `loadPlan` call can supply: the per-instance
112
+ * `ResourceTypeInfo` identity tokens (`resourceTokens`) — the same objects
113
+ * the `own`/`borrow` types in every signature point at, and the only route to
114
+ * a resource's destructor for a *host-initiated* drop of a guest handle
115
+ * (definitions.py `canon_resource_drop` runs `rt.dtor(rep)`; the host holds
116
+ * reps, never table indices, so there is no handle to drop through) — and the
117
+ * converted `types` table it reads function signatures from.
118
+ *
119
+ * Introspection only: mutating it is undefined behaviour.
120
+ */
121
+ loadedPlan: LoadedPlan;
122
+ }
123
+ export declare function instantiateComponent(input: InstantiateInput): Promise<ComponentHandle>;
124
+ /** Convenience for callers: typed view of a lifted export. */
125
+ export type LiftedFunction = (...args: ComponentValue[]) => unknown;
@@ -0,0 +1,165 @@
1
+ import type { ComponentValue, ValType } from "../cabi/types.js";
2
+ import { CopyResult, type PayloadChunk, sameElemType } from "../task/mod.js";
3
+ /**
4
+ * A buffer over JS values. Sibling of `GuestBuffer`, same four methods, no
5
+ * memory access. Used in one of two directions:
6
+ *
7
+ * * as a *readable* buffer (host supplies `values`, the guest reads them);
8
+ * * as a *writable* buffer (host supplies capacity, the guest fills it and
9
+ * `taken()` is what arrived).
10
+ *
11
+ * u8 payloads stay `Uint8Array` through both directions (issue #54): `read`
12
+ * slices the typed array (bulk, and the ONE semantically required copy — the
13
+ * chunk is only borrowed by the stream until the write settles, so the reader
14
+ * must receive owned bytes), and `write` keeps arriving chunks whole instead
15
+ * of exploding them element-by-element into a plain array.
16
+ */
17
+ export declare class HostBuffer {
18
+ #private;
19
+ readonly t: ValType | null;
20
+ private readonly values;
21
+ readonly length: number;
22
+ progress: number;
23
+ constructor(t: ValType | null, values: PayloadChunk | null, length: number);
24
+ remain(): number;
25
+ isZeroLength(): boolean;
26
+ /** Guest side is reading from us. */
27
+ read(n: number): PayloadChunk;
28
+ /** Guest side is writing into us. */
29
+ write(vs: PayloadChunk): void;
30
+ /**
31
+ * Everything written into this buffer, in arrival order.
32
+ *
33
+ * For a u8 element type the result is a `Uint8Array`; in the common case —
34
+ * one rendezvous before the read resolves — the writer's chunk is returned
35
+ * as-is, so the whole host-side read costs exactly the one rendezvous copy.
36
+ * Every other element type yields a plain array regardless of the shape the
37
+ * writer used.
38
+ */
39
+ taken(): PayloadChunk;
40
+ }
41
+ /** Host end the embedder WRITES; the guest reads. */
42
+ export interface HostWritableEnd<T> {
43
+ /**
44
+ * Offer `values`. Resolves with how many the guest actually took — a
45
+ * partial copy is normal, not an error (definitions.py copies
46
+ * `min(remain, remain)`). Re-offer the remainder to finish.
47
+ *
48
+ * `values` is BORROWED until the returned promise settles (the buffer may
49
+ * stay parked across several partial reads); mutating it in that window is
50
+ * misuse. Readers always receive their own copy.
51
+ */
52
+ write(values: T[]): Promise<number>;
53
+ /**
54
+ * Offer `values` repeatedly until all of them have been taken or the reader
55
+ * goes away. Convenience over `write`, and the shape most embedders want.
56
+ *
57
+ * The loop is unavoidable in the single-shot form because of *which side
58
+ * arrives second*: when the host arrives second the reference completes the
59
+ * arriving call with just the count copied in that rendezvous
60
+ * (`SharedStreamImpl.write` -> `on_copy_done(COMPLETED)`), leaving the rest
61
+ * of the offer unsent. When the host arrives *first* it stays parked and is
62
+ * drained across several guest reads. `writeAll` papers over the difference.
63
+ *
64
+ * Resolves with the total accepted, which is less than `values.length` only
65
+ * if the reader dropped.
66
+ */
67
+ writeAll(values: T[]): Promise<number>;
68
+ /**
69
+ * Cancel an in-flight `write`/`writeAll` (definitions.py
70
+ * `SharedStreamImpl.cancel` -> `CopyResult.CANCELLED`). No-op when nothing
71
+ * of ours is parked. Surfaced per the R-fix review's stream advisory 1: the
72
+ * cancel channel existed on the shared object but had no embedder-facing
73
+ * spelling, so a host writer could only be abandoned, never retracted.
74
+ */
75
+ cancelWrite(): void;
76
+ /** definitions.py `SharedStreamImpl.drop`: notifies a parked reader. */
77
+ drop(): void;
78
+ /**
79
+ * Fire `fn` once the stream becomes dropped — by either end, including
80
+ * the A7 teardown walk (immediately, if it already is). The embedder's
81
+ * producer pump uses it to cancel a producer parked on an external
82
+ * event (amendment A13's cancellation companion).
83
+ */
84
+ onDropped(fn: () => void): void;
85
+ }
86
+ /** Host end the embedder READS; the guest writes. */
87
+ export interface HostReadableEnd<T> {
88
+ /**
89
+ * Resolves with up to `max` values once the guest writes (or an empty
90
+ * chunk on drop). A u8 stream resolves with a `Uint8Array` (see
91
+ * `HostBuffer.taken`); every other element type resolves with a plain
92
+ * array.
93
+ */
94
+ read(max: number): Promise<T[]>;
95
+ /** Cancel an in-flight `read`; see `HostWritableEnd.cancelWrite`. */
96
+ cancelRead(): void;
97
+ drop(): void;
98
+ }
99
+ export interface HostStream<T> {
100
+ readable: HostReadableEnd<T>;
101
+ writable: HostWritableEnd<T>;
102
+ /**
103
+ * The value to pass across the boundary. Lowering it into a guest gives the
104
+ * guest the **readable** end (`lower_stream`), so an embedder feeding a
105
+ * guest uses `writable`; an embedder consuming a guest-produced stream gets
106
+ * its shared object from the lift and wraps it with `hostStreamFor`.
107
+ */
108
+ value: ComponentValue;
109
+ }
110
+ /** Create a host-owned stream of `element` (`null` = zero-width payload). */
111
+ export declare function hostStream<T>(element: ValType | null): HostStream<T>;
112
+ /**
113
+ * Wrap a stream that came *out* of a guest (from `liftStream`). Idempotent:
114
+ * a shared object that already has a host wrapper (it was created by
115
+ * `hostStream`, or lifted before) yields that same wrapper.
116
+ */
117
+ export declare function hostStreamFor<T>(value: ComponentValue): HostStream<T>;
118
+ export interface HostFuture<T> {
119
+ /** Deliver the future's single value. */
120
+ write(value: T): Promise<void>;
121
+ /** Await the future's single value. */
122
+ read(): Promise<T | undefined>;
123
+ /**
124
+ * `read`, but reporting *why* it settled. A future carries at most one
125
+ * value, so `read`'s `undefined` is ambiguous between "the value was
126
+ * `undefined`" (a `future<void>`) and "the write end dropped without ever
127
+ * writing" — the case the conventions layer must turn into a
128
+ * `DroppedError` (R-fix review advisory 4). `result` disambiguates:
129
+ * `COMPLETED` iff `value` is real.
130
+ */
131
+ readResult(): Promise<{
132
+ value: T | undefined;
133
+ result: CopyResult;
134
+ }>;
135
+ /** Cancel an in-flight `read`/`write`; see `HostWritableEnd.cancelWrite`. */
136
+ cancel(): void;
137
+ /**
138
+ * Release this future. Total and idempotent (#90): it never throws, and a
139
+ * second call is a no-op.
140
+ *
141
+ * Three cases, per the #90 ruling:
142
+ *
143
+ * * the value was already delivered (the normal write-then-drop path) —
144
+ * plain state cleanup, the spec's `WritableFutureEnd.drop` precondition
145
+ * (definitions.py:1183-1184) is satisfied;
146
+ * * never written, and the future was **lowered** into a guest (the guest
147
+ * holds the readable end, so this wrapper plays the spec's writable
148
+ * role) — *abandon*: the reader can never be satisfied, so it is armed
149
+ * with the rendezvous-point trap (task/streams.ts `abandonSharedFuture`)
150
+ * rather than being handed a DROPPED it may not observe;
151
+ * * never written and never lowered — no guest ever saw it; plain
152
+ * cleanup.
153
+ */
154
+ drop(): void;
155
+ value: ComponentValue;
156
+ }
157
+ /** Create a host-owned future of `element`. */
158
+ export declare function hostFuture<T>(element: ValType | null): HostFuture<T>;
159
+ /**
160
+ * Wrap a future that came *out* of a guest (from `liftFuture`). Idempotent —
161
+ * see `hostStreamFor`.
162
+ */
163
+ export declare function hostFutureFor<T>(value: ComponentValue): HostFuture<T>;
164
+ /** Re-exported so embedders can build element types without importing cabi. */
165
+ export { sameElemType };
@@ -0,0 +1,3 @@
1
+ export * from "./boundary.js";
2
+ export * from "./executor.js";
3
+ export * from "./host_streams.js";