@polyengine/runtime 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/esm/cabi/async_values.js +6 -5
  2. package/esm/cabi/bulk_lists.js +0 -5
  3. package/esm/cabi/context.js +13 -3
  4. package/esm/cabi/flatten.js +41 -9
  5. package/esm/cabi/handles.js +57 -54
  6. package/esm/cabi/layout.js +113 -52
  7. package/esm/cabi/load.js +31 -23
  8. package/esm/cabi/store.js +33 -26
  9. package/esm/cabi/trap.js +2 -2
  10. package/esm/cabi/types.js +138 -25
  11. package/esm/cabi/values.js +25 -7
  12. package/esm/cache/core.js +2 -11
  13. package/esm/digest/digest.js +10 -8
  14. package/esm/digest/mod.js +1 -1
  15. package/esm/digest/verify.js +6 -86
  16. package/esm/embedder/casing.js +24 -9
  17. package/esm/embedder/copy.js +6 -6
  18. package/esm/embedder/errors.js +2 -2
  19. package/esm/embedder/imports.js +3 -3
  20. package/esm/embedder/instantiate.js +132 -37
  21. package/esm/embedder/mod.js +9 -8
  22. package/esm/embedder/resources.js +39 -16
  23. package/esm/embedder/streams.js +36 -37
  24. package/esm/embedder/sync.js +242 -0
  25. package/esm/embedder/values.js +84 -22
  26. package/esm/embedder/version.js +9 -9
  27. package/esm/exec/boundary.js +123 -161
  28. package/esm/exec/executor.js +37 -25
  29. package/esm/exec/host_streams.js +31 -31
  30. package/esm/intrinsics/async_builtins.js +15 -7
  31. package/esm/intrinsics/context.js +1 -1
  32. package/esm/intrinsics/errors.js +9 -9
  33. package/esm/intrinsics/fact_calls.js +37 -49
  34. package/esm/intrinsics/mod.js +54 -117
  35. package/esm/intrinsics/stream_builtins.js +2 -2
  36. package/esm/intrinsics/transcode.js +1 -1
  37. package/esm/jspi/bridge.js +4 -3
  38. package/esm/jspi/suspending.js +5 -5
  39. package/esm/plan/loader.js +5 -5
  40. package/esm/shim/translator.js +2 -2
  41. package/esm/task/mod.js +45 -182
  42. package/esm/task/scheduler.js +154 -185
  43. package/esm/task/streams.js +39 -54
  44. package/esm/task/subtask.js +2 -2
  45. package/esm/task/thread.js +20 -41
  46. package/esm/task/waitable.js +0 -1
  47. package/package.json +2 -2
  48. package/types/cabi/async_values.d.ts +3 -2
  49. package/types/cabi/bulk_lists.d.ts +0 -2
  50. package/types/cabi/context.d.ts +15 -5
  51. package/types/cabi/flatten.d.ts +2 -2
  52. package/types/cabi/handles.d.ts +15 -26
  53. package/types/cabi/layout.d.ts +22 -1
  54. package/types/cabi/load.d.ts +10 -2
  55. package/types/cabi/store.d.ts +4 -2
  56. package/types/cabi/types.d.ts +22 -3
  57. package/types/digest/mod.d.ts +1 -1
  58. package/types/digest/verify.d.ts +3 -19
  59. package/types/embedder/casing.d.ts +9 -1
  60. package/types/embedder/copy.d.ts +4 -4
  61. package/types/embedder/instantiate.d.ts +4 -4
  62. package/types/embedder/mod.d.ts +3 -2
  63. package/types/embedder/resources.d.ts +20 -7
  64. package/types/embedder/streams.d.ts +5 -6
  65. package/types/embedder/sync.d.ts +81 -0
  66. package/types/embedder/values.d.ts +2 -2
  67. package/types/exec/boundary.d.ts +55 -44
  68. package/types/exec/executor.d.ts +3 -2
  69. package/types/exec/host_streams.d.ts +8 -8
  70. package/types/intrinsics/errors.d.ts +3 -3
  71. package/types/intrinsics/mod.d.ts +1 -1
  72. package/types/intrinsics/stream_builtins.d.ts +2 -2
  73. package/types/jspi/bridge.d.ts +6 -5
  74. package/types/plan/format.d.ts +11 -10
  75. package/types/plan/loader.d.ts +2 -2
  76. package/types/shim/translator.d.ts +2 -2
  77. package/types/task/mod.d.ts +26 -97
  78. package/types/task/scheduler.d.ts +81 -62
  79. package/types/task/streams.d.ts +23 -38
  80. package/types/task/subtask.d.ts +2 -2
  81. package/types/task/waitable.d.ts +0 -1
@@ -1,5 +1,5 @@
1
1
  // Bidirectional value-shape adaptation (contracts/embedder-api.md
2
- // §"Value mapping"; C2 checklist item 5).
2
+ // §"Value mapping").
3
3
  //
4
4
  // FROM: the runtime's raw boundary, whose shapes are the definitions.py
5
5
  // interpreter's — single-key variants (`{ "circle": 1.5 }`), `{some}` /
@@ -15,7 +15,7 @@
15
15
  //
16
16
  // The adapter is driven entirely by the plan's `ValType`s — no generated code
17
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,
18
+ // and a bindgen-typed one (the design ruling: runtime-driven facade,
19
19
  // compile-time-only bindgen).
20
20
  import { despecialize } from "../cabi/types.js";
21
21
  import { ERROR_CONTEXT, hasBrand } from "@polyengine/protocol";
@@ -74,6 +74,66 @@ export function checkNoCollisions(key, labels, what) {
74
74
  }
75
75
  checkedLabels.add(key);
76
76
  }
77
+ const recordTables = new WeakMap();
78
+ function recordTable(t) {
79
+ const hit = recordTables.get(t);
80
+ if (hit !== undefined)
81
+ return hit;
82
+ const table = {
83
+ labels: t.fields.map((f) => f.label),
84
+ fields: t.fields.map((f) => ({
85
+ label: f.label,
86
+ js: camelCase(f.label),
87
+ type: f.type,
88
+ isOption: f.type.kind === "option",
89
+ optionInner: f.type.kind === "option" ? f.type.type : null,
90
+ })),
91
+ };
92
+ recordTables.set(t, table);
93
+ return table;
94
+ }
95
+ /**
96
+ * Label -> case, memoized on the `cases` array. Replaces the linear
97
+ * `t.cases.find(...)` both directions ran per element.
98
+ *
99
+ * **First wins on a duplicated label** — deliberately NOT `caseIndexOf` from
100
+ * cabi/types.ts, which maps a duplicate to -1. The two sites replace scans
101
+ * with different pre-existing behavior (`find` takes the first match; the cabi
102
+ * scan asserted on exactly one), so each keeps its own; merging them would be
103
+ * a behavior change, not a deduplication.
104
+ */
105
+ const variantTables = new WeakMap();
106
+ function variantTable(cases) {
107
+ const hit = variantTables.get(cases);
108
+ if (hit !== undefined)
109
+ return hit;
110
+ const m = new Map();
111
+ for (const c of cases)
112
+ if (!m.has(c.label))
113
+ m.set(c.label, c);
114
+ variantTables.set(cases, m);
115
+ return m;
116
+ }
117
+ /** The enum's labels as a set, replacing `t.labels.includes(v)`. */
118
+ const enumTables = new WeakMap();
119
+ function enumTable(t) {
120
+ const hit = enumTables.get(t);
121
+ if (hit !== undefined)
122
+ return hit;
123
+ const s = new Set(t.labels);
124
+ enumTables.set(t, s);
125
+ return s;
126
+ }
127
+ /** Flags labels paired with their JS names, in source order. */
128
+ const flagsTables = new WeakMap();
129
+ function flagsTable(t) {
130
+ const hit = flagsTables.get(t);
131
+ if (hit !== undefined)
132
+ return hit;
133
+ const table = t.labels.map((label) => ({ label, js: camelCase(label) }));
134
+ flagsTables.set(t, table);
135
+ return table;
136
+ }
77
137
  // ---------------------------------------------------------------------------
78
138
  // internal -> conventions
79
139
  // ---------------------------------------------------------------------------
@@ -117,21 +177,22 @@ export function toHost(v, t, o, scope = NO_BORROWS, inOption = false) {
117
177
  return v.map((e) => toHost(e, t.element, o, scope));
118
178
  }
119
179
  case "record": {
120
- checkNoCollisions(t, t.fields.map((f) => f.label), `${o.where}: record`);
180
+ const table = recordTable(t);
181
+ checkNoCollisions(t, table.labels, `${o.where}: record`);
121
182
  const src = v;
122
183
  const out = {};
123
- for (const f of t.fields) {
184
+ for (const f of table.fields) {
124
185
  // "fields of option type are optional properties (`field?: T`)": a
125
186
  // `none` field is *absent*, not `undefined`-valued, so the object has
126
187
  // one canonical shape rather than two indistinguishable ones.
127
- if (f.type.kind === "option") {
188
+ if (f.isOption) {
128
189
  const inner = src[f.label];
129
190
  if ("none" in inner)
130
191
  continue;
131
- out[camelCase(f.label)] = toHost(inner["some"], f.type.type, o, scope, true);
192
+ out[f.js] = toHost(inner["some"], f.optionInner, o, scope, true);
132
193
  continue;
133
194
  }
134
- out[camelCase(f.label)] = toHost(src[f.label], f.type, o, scope);
195
+ out[f.js] = toHost(src[f.label], f.type, o, scope);
135
196
  }
136
197
  return out;
137
198
  }
@@ -141,7 +202,7 @@ export function toHost(v, t, o, scope = NO_BORROWS, inOption = false) {
141
202
  }
142
203
  case "variant": {
143
204
  const [label, payload] = single(v, o);
144
- const c = t.cases.find((c) => c.label === label);
205
+ const c = variantTable(t.cases).get(label);
145
206
  if (c === undefined) {
146
207
  throw new TypeError(`${o.where}: unknown variant case '${label}'`);
147
208
  }
@@ -177,8 +238,8 @@ export function toHost(v, t, o, scope = NO_BORROWS, inOption = false) {
177
238
  checkNoCollisions(t, t.labels, `${o.where}: flags`);
178
239
  const src = v;
179
240
  const out = {};
180
- for (const l of t.labels)
181
- out[camelCase(l)] = Boolean(src[l]);
241
+ for (const f of flagsTable(t))
242
+ out[f.js] = Boolean(src[f.label]);
182
243
  return out;
183
244
  }
184
245
  case "map": {
@@ -263,7 +324,7 @@ export function fromHost(v, t, o, inOption = false) {
263
324
  }
264
325
  if (v instanceof InternalErrorContext)
265
326
  return v;
266
- // A20 (contracts/embedder-api.md §"Error-context is message-valued";
327
+ // realm boundary (contracts/embedder-api.md §"Error-context is message-valued";
267
328
  // issue #131; definitions.py — an error-context's state is exactly
268
329
  // its debug message): a branded carrier of a string `message`, from
269
330
  // any copy (or hand-rolled), is accepted by minting a FRESH local
@@ -272,7 +333,7 @@ export function fromHost(v, t, o, inOption = false) {
272
333
  typeof v.message === "string") {
273
334
  return new InternalErrorContext(v.message);
274
335
  }
275
- // Branded but no string `message` (amendment A9, superseded above only
336
+ // Branded but no string `message` (§"Module identity and @polyengine/protocol", superseded above only
276
337
  // for the message-valued case): a genuinely foreign stateful handle —
277
338
  // it lives in another copy's handle table, so it can never be lowered
278
339
  // here — but "recognized and named" beats the generic "expected an
@@ -300,18 +361,19 @@ export function fromHost(v, t, o, inOption = false) {
300
361
  if (v === null || typeof v !== "object") {
301
362
  throw new TypeError(`${o.where}: record expects an object`);
302
363
  }
303
- checkNoCollisions(t, t.fields.map((f) => f.label), `${o.where}: record`);
364
+ const table = recordTable(t);
365
+ checkNoCollisions(t, table.labels, `${o.where}: record`);
304
366
  const src = v;
305
367
  const out = {};
306
- for (const f of t.fields) {
307
- const key = camelCase(f.label);
308
- if (f.type.kind === "option") {
368
+ for (const f of table.fields) {
369
+ const key = f.js;
370
+ if (f.isOption) {
309
371
  // Absent and `undefined` both mean `none` — the two spellings of an
310
372
  // optional property.
311
373
  const inner = src[key];
312
374
  out[f.label] = inner === undefined
313
375
  ? { none: null }
314
- : { some: fromHost(inner, f.type.type, o, true) };
376
+ : { some: fromHost(inner, f.optionInner, o, true) };
315
377
  continue;
316
378
  }
317
379
  if (!(key in src)) {
@@ -333,7 +395,7 @@ export function fromHost(v, t, o, inOption = false) {
333
395
  }
334
396
  case "variant": {
335
397
  const { kind, value, has } = tagged(v, o);
336
- const c = t.cases.find((c) => c.label === kind);
398
+ const c = variantTable(t.cases).get(kind);
337
399
  if (c === undefined) {
338
400
  throw new TypeError(`${o.where}: unknown variant case '${kind}'`);
339
401
  }
@@ -345,7 +407,7 @@ export function fromHost(v, t, o, inOption = false) {
345
407
  return { [kind]: fromHost(value, c.type, o) };
346
408
  }
347
409
  case "enum": {
348
- if (typeof v !== "string" || !t.labels.includes(v)) {
410
+ if (typeof v !== "string" || !enumTable(t).has(v)) {
349
411
  throw new TypeError(`${o.where}: enum expects one of ${t.labels.join(" | ")}, got ` +
350
412
  describe(v));
351
413
  }
@@ -390,8 +452,8 @@ export function fromHost(v, t, o, inOption = false) {
390
452
  const src = v;
391
453
  const out = {};
392
454
  // "lower: absent = false" — an omitted flag is not an error.
393
- for (const l of t.labels)
394
- out[l] = Boolean(src[camelCase(l)]);
455
+ for (const f of flagsTable(t))
456
+ out[f.label] = Boolean(src[f.js]);
395
457
  return out;
396
458
  }
397
459
  case "map":
@@ -446,7 +508,7 @@ function elemCodec(element, o) {
446
508
  where: o.where,
447
509
  toHost: (v) => element === null ? undefined : toHost(v, element, o),
448
510
  fromHost: (v) => element === null ? null : fromHost(v, element, o),
449
- // A13: `own` elements a producer lowered but the reader never took
511
+ // resource stream: `own` elements a producer lowered but the reader never took
450
512
  // must be destroyed, not leaked — an un-taken element may hold a live
451
513
  // platform resource (the tcp `listen` shape: an accepted connection).
452
514
  // Top-level `own` is the supported element shape; nested owns inside
@@ -1,5 +1,5 @@
1
1
  // Version-canonical import resolution (contracts/embedder-api.md
2
- // §"Version canonicalization"; C2 checklist item 1).
2
+ // §"Version canonicalization").
3
3
  //
4
4
  // Authorities, read before writing this:
5
5
  // * Explainer.md §"canonical interface names" (`canonversion`) — the spec's
@@ -149,9 +149,9 @@ export class ImportResolver {
149
149
  `to '@0' and this registration could never be resolved.`);
150
150
  }
151
151
  if (p.version === null) {
152
- // Unversioned: matched exactly and never folded onto a track (C0 D-1's
153
- // actual defect was version-agnostic keys merging distinct semver
154
- // tracks). Recorded so resolution can *say so* instead of reporting a
152
+ // Unversioned: matched exactly and never folded onto a track (the banned
153
+ // defect is version-agnostic keys merging distinct semver tracks).
154
+ // Recorded so resolution can *say so* instead of reporting a
155
155
  // bare "not provided".
156
156
  if (p.base.includes("/") || p.base.includes(":")) {
157
157
  this.#unversioned.set(p.base, key);
@@ -212,9 +212,9 @@ export class ImportResolver {
212
212
  return { key: claim.key, value: this.#exact.get(claim.key) };
213
213
  }
214
214
  }
215
- // CONTRACT: contracts/embedder-api.md bans "unversioned folding" (C0
216
- // D-1) and the C2 dispatch summarises that as "unversioned keys ->
217
- // error". Read conservatively: an unversioned key is still a legal
215
+ // CONTRACT: contracts/embedder-api.md §"Version canonicalization" bans
216
+ // "unversioned folding": "unversioned keys -> error". Read
217
+ // conservatively: an unversioned key is still a legal
218
218
  // *exact* match for an unversioned import (the ban is about folding
219
219
  // distinct semver tracks together, and unversioned WIT interfaces
220
220
  // exist), but it may never serve a *versioned* import. That attempt is
@@ -223,7 +223,7 @@ export class ImportResolver {
223
223
  if (un !== undefined) {
224
224
  throw new ImportResolutionError(`import '${id}' is versioned but the only registration for ` +
225
225
  `'${p.base}' is the unversioned key '${un}'. Version-agnostic ` +
226
- `folding is banned (contracts/embedder-api.md, C0 finding D-1): ` +
226
+ `folding is banned (contracts/embedder-api.md §"Version canonicalization"): ` +
227
227
  `register '${p.base}@${p.version}' or the compatibility-track ` +
228
228
  `key '${trackKeyOf(p.base, p.semver) ?? p.base + "@" + p.version}'.`);
229
229
  }
@@ -236,7 +236,7 @@ export class ImportResolver {
236
236
  if (near.length > 0) {
237
237
  throw new ImportResolutionError(`import '${id}' is unversioned but the registrations for it are ` +
238
238
  `versioned (${near.join(", ")}). Version-agnostic folding is ` +
239
- `banned (contracts/embedder-api.md, C0 finding D-1).`);
239
+ `banned (contracts/embedder-api.md §"Version canonicalization").`);
240
240
  }
241
241
  }
242
242
  return undefined;