@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,450 @@
1
+ // Bidirectional value-shape adaptation (contracts/embedder-api.md
2
+ // §"Value mapping"; C2 checklist item 5).
3
+ //
4
+ // FROM: the runtime's raw boundary, whose shapes are the definitions.py
5
+ // interpreter's — single-key variants (`{ "circle": 1.5 }`), `{some}` /
6
+ // `{none}`, tuple-as-record (`{ "0": …, "1": … }`), `{ok}` / `{error}`
7
+ // (note: the internal despecialization labels result's err case
8
+ // `"error"`, not `"err"` — cabi/types.ts `despecialize`), kebab-case
9
+ // record keys, resource handles as bare reps.
10
+ // TO: the conventions: `{ kind, value? }` for variants and nested results,
11
+ // outermost-option-as-`undefined` with nested boxing, real tuples,
12
+ // camelCase record fields, flags objects, enum strings verbatim,
13
+ // `Uint8Array` for `list<u8>`, class instances for resources,
14
+ // `Stream`/`Future`/`ErrorContext` handles.
15
+ //
16
+ // The adapter is driven entirely by the plan's `ValType`s — no generated code
17
+ // participates, which is what lets the same facade serve an untyped embedder
18
+ // and a bindgen-typed one (the C2 design ruling: runtime-driven facade,
19
+ // compile-time-only bindgen).
20
+ import { despecialize } from "../cabi/types.js";
21
+ import { ERROR_CONTEXT, hasBrand } from "@polyengine/protocol";
22
+ import { describeCrossCopy } from "./copy.js";
23
+ import { ErrorContext as InternalErrorContext } from "../task/mod.js";
24
+ import { camelCase } from "./casing.js";
25
+ import { NameCollisionError } from "./errors.js";
26
+ import { ErrorContext, Future, lowerFutureSource, lowerStreamSource, Stream, } from "./streams.js";
27
+ /**
28
+ * Wrappers materialized for `borrow<R>` arguments of one call. The contract:
29
+ * "instance valid **only during the call** (retention throws)", so the scope
30
+ * invalidates them when the call returns.
31
+ */
32
+ export class BorrowScope {
33
+ #invalidate = [];
34
+ add(f) {
35
+ this.#invalidate.push(f);
36
+ }
37
+ end() {
38
+ for (const f of this.#invalidate)
39
+ f();
40
+ this.#invalidate.length = 0;
41
+ }
42
+ }
43
+ /** A no-op scope for positions where no borrow can appear. */
44
+ export const NO_BORROWS = new BorrowScope();
45
+ /**
46
+ * Label sets already checked for camelCase collisions. `ValType` objects are
47
+ * stable for the lifetime of a loaded plan, so this is a one-time cost per
48
+ * record/flags type rather than a per-call one.
49
+ */
50
+ const checkedLabels = new WeakSet();
51
+ /**
52
+ * Refuse two labels in one scope that camelCase to the same JS name.
53
+ *
54
+ * `read-only` and `readOnly` are distinct WIT labels but one JS property, so
55
+ * one would silently shadow the other at the boundary — values corrupted with
56
+ * no diagnostic anywhere. Contract principle 2: footguns are design defects.
57
+ */
58
+ export function checkNoCollisions(key, labels, what) {
59
+ if (checkedLabels.has(key))
60
+ return;
61
+ const seen = new Map();
62
+ for (const l of labels) {
63
+ const js = camelCase(l);
64
+ const held = seen.get(js);
65
+ if (held !== undefined) {
66
+ throw new NameCollisionError(`${what}: the labels '${held}' and '${l}' both map to the JS name ` +
67
+ `'${js}'. Rename one in the WIT; the conventions layer will not ` +
68
+ `guess which one wins.`);
69
+ }
70
+ seen.set(js, l);
71
+ }
72
+ checkedLabels.add(key);
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // internal -> conventions
76
+ // ---------------------------------------------------------------------------
77
+ /**
78
+ * Adapt one lifted value to its conventions shape.
79
+ *
80
+ * `inOption` implements the contract's option rule: the *outermost* option in
81
+ * a chain maps to `T | undefined`; an option nested **directly inside another
82
+ * option** boxes as `{ kind: "some", value } | { kind: "none" }`. Only option maps
83
+ * to `undefined`, so this is the only ambiguity, and the flag is set only when
84
+ * descending through an option's payload — every other constructor resets it.
85
+ */
86
+ export function toHost(v, t, o, scope = NO_BORROWS, inOption = false) {
87
+ switch (t.kind) {
88
+ case "bool":
89
+ case "s8":
90
+ case "u8":
91
+ case "s16":
92
+ case "u16":
93
+ case "s32":
94
+ case "u32":
95
+ case "s64":
96
+ case "u64":
97
+ case "f32":
98
+ case "f64":
99
+ case "char":
100
+ case "string":
101
+ return v;
102
+ case "error-context":
103
+ return new ErrorContext(v);
104
+ case "list": {
105
+ const elem = despecialize(t.element);
106
+ if (elem.kind === "u8") {
107
+ // Already a Uint8Array from the raw boundary (docs/architecture.md §7); copy defensively
108
+ // only if the interpreter handed back a plain array (fixed-length lists
109
+ // take the Uint8Array path too, but be tolerant).
110
+ return v instanceof Uint8Array ? v : Uint8Array.from(v);
111
+ }
112
+ return v.map((e) => toHost(e, t.element, o, scope));
113
+ }
114
+ case "record": {
115
+ checkNoCollisions(t, t.fields.map((f) => f.label), `${o.where}: record`);
116
+ const src = v;
117
+ const out = {};
118
+ for (const f of t.fields) {
119
+ // "fields of option type are optional properties (`field?: T`)": a
120
+ // `none` field is *absent*, not `undefined`-valued, so the object has
121
+ // one canonical shape rather than two indistinguishable ones.
122
+ if (f.type.kind === "option") {
123
+ const inner = src[f.label];
124
+ if ("none" in inner)
125
+ continue;
126
+ out[camelCase(f.label)] = toHost(inner["some"], f.type.type, o, scope, true);
127
+ continue;
128
+ }
129
+ out[camelCase(f.label)] = toHost(src[f.label], f.type, o, scope);
130
+ }
131
+ return out;
132
+ }
133
+ case "tuple": {
134
+ const src = v;
135
+ return t.elements.map((et, i) => toHost(src[String(i)], et, o, scope));
136
+ }
137
+ case "variant": {
138
+ const [label, payload] = single(v, o);
139
+ const c = t.cases.find((c) => c.label === label);
140
+ if (c === undefined) {
141
+ throw new TypeError(`${o.where}: unknown variant case '${label}'`);
142
+ }
143
+ return c.type === null
144
+ ? { kind: label }
145
+ : { kind: label, value: toHost(payload, c.type, o, scope) };
146
+ }
147
+ case "enum": {
148
+ // Enum values are data: kebab-case verbatim, never camelCased.
149
+ const [label] = single(v, o);
150
+ return label;
151
+ }
152
+ case "option": {
153
+ const [label, payload] = single(v, o);
154
+ if (inOption) {
155
+ return label === "none"
156
+ ? { kind: "none" }
157
+ : { kind: "some", value: toHost(payload, t.type, o, scope, true) };
158
+ }
159
+ return label === "none"
160
+ ? undefined
161
+ : toHost(payload, t.type, o, scope, true);
162
+ }
163
+ case "result": {
164
+ const [label, payload] = single(v, o);
165
+ // Internal despecialization names the err case "error"; the contract's
166
+ // kind is "err" (cabi/types.ts `despecialize`).
167
+ const kind = label === "error" ? "err" : "ok";
168
+ const ct = label === "error" ? t.error : t.ok;
169
+ return ct === null ? { kind } : { kind, value: toHost(payload, ct, o, scope) };
170
+ }
171
+ case "flags": {
172
+ checkNoCollisions(t, t.labels, `${o.where}: flags`);
173
+ const src = v;
174
+ const out = {};
175
+ for (const l of t.labels)
176
+ out[camelCase(l)] = Boolean(src[l]);
177
+ return out;
178
+ }
179
+ case "map": {
180
+ // `map<K,V>` despecializes to `list<tuple<K,V>>`; keep that shape.
181
+ return toHost(v, despecialize(t), o, scope);
182
+ }
183
+ case "own":
184
+ return o.bridge.liftOwn(v, t);
185
+ case "borrow":
186
+ return o.bridge.liftBorrow(v, t, scope);
187
+ case "stream":
188
+ return Stream.fromLifted(v, elemCodec(t.element, o));
189
+ case "future":
190
+ return Future.fromLifted(v, elemCodec(t.element, o));
191
+ }
192
+ }
193
+ function single(v, o) {
194
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
195
+ throw new TypeError(`${o.where}: expected a single-key case object, got ${describe(v)}`);
196
+ }
197
+ const keys = Object.keys(v);
198
+ if (keys.length !== 1) {
199
+ throw new TypeError(`${o.where}: expected exactly one case key, got ${keys.length}`);
200
+ }
201
+ return [keys[0], v[keys[0]]];
202
+ }
203
+ // ---------------------------------------------------------------------------
204
+ // conventions -> internal
205
+ // ---------------------------------------------------------------------------
206
+ export function fromHost(v, t, o, inOption = false) {
207
+ switch (t.kind) {
208
+ case "bool":
209
+ return Boolean(v);
210
+ case "u8":
211
+ return int(v, t.kind, 0, 0xff, o);
212
+ case "u16":
213
+ return int(v, t.kind, 0, 0xffff, o);
214
+ case "u32":
215
+ return int(v, t.kind, 0, 0xffffffff, o);
216
+ case "s8":
217
+ return int(v, t.kind, -0x80, 0x7f, o);
218
+ case "s16":
219
+ return int(v, t.kind, -0x8000, 0x7fff, o);
220
+ case "s32":
221
+ return int(v, t.kind, -0x80000000, 0x7fffffff, o);
222
+ case "u64":
223
+ return big(v, t.kind, 0n, (1n << 64n) - 1n, o);
224
+ case "s64":
225
+ return big(v, t.kind, -(1n << 63n), (1n << 63n) - 1n, o);
226
+ case "f32":
227
+ case "f64":
228
+ if (typeof v !== "number") {
229
+ throw new TypeError(`${o.where}: ${t.kind} expects a number`);
230
+ }
231
+ return v;
232
+ case "char": {
233
+ if (typeof v !== "string" || [...v].length !== 1) {
234
+ throw new TypeError(`${o.where}: char expects a single-code-point string`);
235
+ }
236
+ // A lone surrogate has `[...v].length === 1` but is not a Unicode
237
+ // scalar value, so `char` cannot hold it. Reject here, where the site is
238
+ // known: the interpreter's own check reports only "not a valid char".
239
+ const cp = v.codePointAt(0);
240
+ if (cp >= 0xd800 && cp <= 0xdfff) {
241
+ throw new TypeError(`${o.where}: char expects a Unicode scalar value, got the lone ` +
242
+ `surrogate U+${cp.toString(16).toUpperCase()}`);
243
+ }
244
+ return v;
245
+ }
246
+ case "string":
247
+ if (typeof v !== "string") {
248
+ throw new TypeError(`${o.where}: string expects a string`);
249
+ }
250
+ return v;
251
+ case "error-context": {
252
+ if (v instanceof ErrorContext) {
253
+ return v.internal;
254
+ }
255
+ if (v instanceof InternalErrorContext)
256
+ return v;
257
+ // Branded but not ours (amendment A9): an `ErrorContext` minted by
258
+ // another runtime copy. It is stateful (it lives in that copy's handle
259
+ // table), so it can never be lowered here — but "recognized and named"
260
+ // beats the generic "expected an ErrorContext" that sent issue #83
261
+ // hunting in the wrong direction.
262
+ if (hasBrand(v, ERROR_CONTEXT)) {
263
+ throw new TypeError(`${o.where}: ${describeCrossCopy("this error-context")}`);
264
+ }
265
+ throw new TypeError(`${o.where}: expected an ErrorContext`);
266
+ }
267
+ case "list": {
268
+ const elem = despecialize(t.element);
269
+ if (elem.kind === "u8") {
270
+ if (v instanceof Uint8Array)
271
+ return v;
272
+ if (Array.isArray(v))
273
+ return Uint8Array.from(v);
274
+ throw new TypeError(`${o.where}: list<u8> expects a Uint8Array`);
275
+ }
276
+ if (!Array.isArray(v)) {
277
+ throw new TypeError(`${o.where}: list expects an array`);
278
+ }
279
+ return v.map((e) => fromHost(e, t.element, o));
280
+ }
281
+ case "record": {
282
+ if (v === null || typeof v !== "object") {
283
+ throw new TypeError(`${o.where}: record expects an object`);
284
+ }
285
+ checkNoCollisions(t, t.fields.map((f) => f.label), `${o.where}: record`);
286
+ const src = v;
287
+ const out = {};
288
+ for (const f of t.fields) {
289
+ const key = camelCase(f.label);
290
+ if (f.type.kind === "option") {
291
+ // Absent and `undefined` both mean `none` — the two spellings of an
292
+ // optional property.
293
+ const inner = src[key];
294
+ out[f.label] = inner === undefined
295
+ ? { none: null }
296
+ : { some: fromHost(inner, f.type.type, o, true) };
297
+ continue;
298
+ }
299
+ if (!(key in src)) {
300
+ throw new TypeError(`${o.where}: record field '${key}' is missing`);
301
+ }
302
+ out[f.label] = fromHost(src[key], f.type, o);
303
+ }
304
+ return out;
305
+ }
306
+ case "tuple": {
307
+ if (!Array.isArray(v) || v.length !== t.elements.length) {
308
+ throw new TypeError(`${o.where}: tuple expects an array of ${t.elements.length}`);
309
+ }
310
+ const out = {};
311
+ t.elements.forEach((et, i) => {
312
+ out[String(i)] = fromHost(v[i], et, o);
313
+ });
314
+ return out;
315
+ }
316
+ case "variant": {
317
+ const { kind, value, has } = tagged(v, o);
318
+ const c = t.cases.find((c) => c.label === kind);
319
+ if (c === undefined) {
320
+ throw new TypeError(`${o.where}: unknown variant case '${kind}'`);
321
+ }
322
+ if (c.type === null)
323
+ return { [kind]: null };
324
+ if (!has) {
325
+ throw new TypeError(`${o.where}: variant case '${kind}' needs a 'value'`);
326
+ }
327
+ return { [kind]: fromHost(value, c.type, o) };
328
+ }
329
+ case "enum": {
330
+ if (typeof v !== "string" || !t.labels.includes(v)) {
331
+ throw new TypeError(`${o.where}: enum expects one of ${t.labels.join(" | ")}, got ` +
332
+ describe(v));
333
+ }
334
+ return { [v]: null };
335
+ }
336
+ case "option": {
337
+ if (inOption) {
338
+ const { kind, value, has } = tagged(v, o);
339
+ if (kind === "none")
340
+ return { none: null };
341
+ if (kind !== "some") {
342
+ throw new TypeError(`${o.where}: a nested option must be { kind: "some" | "none" }`);
343
+ }
344
+ return { some: has ? fromHost(value, t.type, o, true) : null };
345
+ }
346
+ return v === undefined
347
+ ? { none: null }
348
+ : { some: fromHost(v, t.type, o, true) };
349
+ }
350
+ case "result": {
351
+ const { kind, value, has } = tagged(v, o);
352
+ if (kind !== "ok" && kind !== "err") {
353
+ throw new TypeError(`${o.where}: a result value must be { kind: "ok" | "err" }`);
354
+ }
355
+ const label = kind === "err" ? "error" : "ok";
356
+ const ct = kind === "err" ? t.error : t.ok;
357
+ if (ct === null)
358
+ return { [label]: null };
359
+ // Symmetric with the variant path above: a case that carries a payload
360
+ // must be given one. Silently lowering `null` would put a zero where the
361
+ // guest expects data.
362
+ if (!has) {
363
+ throw new TypeError(`${o.where}: result case '${kind}' carries a payload and needs a 'value'`);
364
+ }
365
+ return { [label]: fromHost(value, ct, o) };
366
+ }
367
+ case "flags": {
368
+ if (v === null || typeof v !== "object") {
369
+ throw new TypeError(`${o.where}: flags expects an object`);
370
+ }
371
+ checkNoCollisions(t, t.labels, `${o.where}: flags`);
372
+ const src = v;
373
+ const out = {};
374
+ // "lower: absent = false" — an omitted flag is not an error.
375
+ for (const l of t.labels)
376
+ out[l] = Boolean(src[camelCase(l)]);
377
+ return out;
378
+ }
379
+ case "map":
380
+ return fromHost(v, despecialize(t), o);
381
+ case "own":
382
+ return o.bridge.lowerOwn(v, t);
383
+ case "borrow":
384
+ return o.bridge.lowerBorrow(v, t);
385
+ case "stream":
386
+ // deno-lint-ignore no-explicit-any
387
+ return lowerStreamSource(v, elemCodec(t.element, o));
388
+ case "future":
389
+ // deno-lint-ignore no-explicit-any
390
+ return lowerFutureSource(v, elemCodec(t.element, o));
391
+ }
392
+ }
393
+ function tagged(v, o) {
394
+ if (v === null || typeof v !== "object" || !("kind" in v)) {
395
+ throw new TypeError(`${o.where}: expected a { kind, value? } value, got ${describe(v)}`);
396
+ }
397
+ const rec = v;
398
+ if (typeof rec.kind !== "string") {
399
+ throw new TypeError(`${o.where}: 'kind' must be a string`);
400
+ }
401
+ return { kind: rec.kind, value: rec.value, has: "value" in rec };
402
+ }
403
+ function int(v, kind, lo, hi, o) {
404
+ if (typeof v !== "number" || !Number.isInteger(v)) {
405
+ throw new TypeError(`${o.where}: ${kind} expects an integer number`);
406
+ }
407
+ if (v < lo || v > hi) {
408
+ throw new TypeError(`${o.where}: ${kind} out of range: ${v}`);
409
+ }
410
+ // The raw boundary takes unsigned lane values for the signed types too?
411
+ // No: cabi `lowerFlatSigned32` does the two's-complement fold, so the
412
+ // interpreter wants the *signed* number here. Pass it through.
413
+ return v;
414
+ }
415
+ function big(v, kind, lo, hi, o) {
416
+ if (typeof v !== "bigint") {
417
+ throw new TypeError(`${o.where}: ${kind} expects a bigint`);
418
+ }
419
+ if (v < lo || v > hi) {
420
+ throw new TypeError(`${o.where}: ${kind} out of range: ${v}`);
421
+ }
422
+ return v;
423
+ }
424
+ /** Per-element codec for a `stream<T>` / `future<T>`. */
425
+ function elemCodec(element, o) {
426
+ return {
427
+ element,
428
+ where: o.where,
429
+ toHost: (v) => element === null ? undefined : toHost(v, element, o),
430
+ fromHost: (v) => element === null ? null : fromHost(v, element, o),
431
+ // A13: `own` elements a producer lowered but the reader never took
432
+ // must be destroyed, not leaked — an un-taken element may hold a live
433
+ // platform resource (the tcp `listen` shape: an accepted connection).
434
+ // Top-level `own` is the supported element shape; nested owns inside
435
+ // composite stream elements remain out of scope until a consumer
436
+ // links one.
437
+ release: element !== null && element.kind === "own"
438
+ ? (v) => o.bridge.dropOwn(v, element)
439
+ : undefined,
440
+ };
441
+ }
442
+ export function describe(v) {
443
+ if (v === null)
444
+ return "null";
445
+ if (v === undefined)
446
+ return "undefined";
447
+ if (typeof v === "object")
448
+ return `a ${v.constructor?.name ?? "object"}`;
449
+ return `a ${typeof v} (${String(v)})`;
450
+ }