Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ Layout (each directory's justfile module in parentheses):
(`conformance/driver-ct/polyengine/`, the WPT parity round trip, the demo).
Its knobs are exported functions (`configure`,
`setMaxInboundBufferBytes`, `setConnectTimeoutMs`, `setCloseTimeoutMs`);
the module reads no ambient configuration. The
polyengine release is pinned in TWO import maps — `js/polyengine/deno.json` and
`conformance/driver-ct/polyengine/deno.json` — as exact-pinned JSR
prereleases (`jsr:@polyengine/<pkg>@0.3.0-pre.g<hash>`; a version names one
upstream commit) which must agree on the `@polyengine/runtime` version
(polyengine's `wasi` module imports `@polyengine/runtime/embedder` by bare
specifier internally, so a drift would load the embedder module twice
and split the runtime/translator plan-format pairing; value recognition
itself is brand-based — polyengine's `@polyengine/protocol` predicates, not
`instanceof` — and survives a multi-copy graph); the root
justfile's `exam-polyengine` recipe (CI-wired) asserts one version
repo-wide. Bump procedure: `conformance/driver-ct/polyengine/README.md`.
the module reads no ambient configuration. Since the A22
protocol/runtime split, this published package (`@polymorph/websocket`)
depends on `@polyengine/protocol` only (`js/polyengine/deno.json`,
caret-pinned `^0.2.2`) — no `@polyengine/runtime` import anywhere in
it; value recognition is brand-based (`@polyengine/protocol` predicates,
not `instanceof`), so its copy of `@polyengine/protocol` is harmless
even in a multi-copy graph. The runtime-linked application legs
(`conformance/driver-ct/polyengine/`, the WPT parity round trip, the
demo) separately exact-pin the `@polyengine/{runtime,translator,wasi,
ct-runner}` lockstep family (`0.5.0`) plus `@polyengine/protocol`
(`0.2.2`) in `conformance/driver-ct/polyengine/deno.json`; the root
justfile's `exam-polyengine` recipe (CI-wired) asserts one resolved
runtime-family version, one resolved protocol version, and that
`js/polyengine` names no `@polyengine/runtime` specifier. Bump
procedure: `conformance/driver-ct/polyengine/README.md`.
- `js/componentize/` (`just wpt::…`) — `websocket.js`, the WHATWG-API
shim for componentize-js guests (deviations registry in its header), and
the WPT parity gate (`wpt/README.md` is the vendoring policy; losses
Expand Down
55 changes: 31 additions & 24 deletions conformance/driver-ct/polyengine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,46 @@ module graph — no fetch step, no net grant.
## The pin

polyengine publishes `@polyengine/{runtime,translator,wasi,ct-runner}` to
JSR as exact-pinned prereleases (`0.3.0-pre.g<shorthash>`, the same short
hash as the corresponding GitHub release — a version names one exact
upstream commit; see polyengine's README "Consuming the unstable
prereleases"). It is pinned in **two** places, both required to agree:

- `deno.json` (this directory) — import-map versions
(`jsr:@polyengine/<pkg>@0.3.0-pre.g<hash>`) for `@polyengine/ct-runner`,
`@polyengine/runtime/embedder`, `@polyengine/runtime/shim`, `@polyengine/wasi`,
`@polyengine/translator`. `deno.lock` carries integrity hashes for that
module graph, enforced with `--frozen`.
- [`../../../js/polyengine/deno.json`](../../../js/polyengine/deno.json) — the
SAME `@polyengine/runtime` version (the module-identity constraint: polyengine's
`wasi` module imports `@polyengine/runtime/embedder` by bare specifier
internally, so every config resolving it must agree, or the embedder
module loads twice — the host module's brand-based value recognition
survives that, but a second copy splits the runtime/translator
plan-format pairing).
JSR as a lockstep-versioned line (this directory exact-pins `0.5.0`) and
`@polyengine/protocol` (the host-ABI vocabulary package) as an
independently-versioned line (exact-pinned `0.2.2` here). It is pinned in
this directory's `deno.json` import map
(`jsr:@polyengine/<pkg>@0.5.0` for `@polyengine/ct-runner`,
`@polyengine/runtime/embedder`, `@polyengine/runtime/shim`,
`@polyengine/wasi`, `@polyengine/translator`; `jsr:@polyengine/protocol@0.2.2`
for vocabulary consumed by driver code such as `examples/polyengine-demo/run.ts`).
`deno.lock` carries integrity hashes for that module graph, enforced with
`--frozen`.

As of the A22 protocol/runtime split, `@polymorph/websocket`
([`../../../js/polyengine/deno.json`](../../../js/polyengine/deno.json))
depends on `@polyengine/protocol` only — it resolves no
`@polyengine/runtime` at all, so its copy of `@polyengine/protocol` need
not match this directory's (the host module's brand-based value
recognition is harmless across copies by construction). This directory's
own runtime-family pins still need to be internally consistent: if this
repo ever loads the embedder from more than one config, stateful handles
(streams/futures) minted by one copy are refused by another.

`@polyengine/translator` ships the translator wasm asset **for the same
commit** as `@polyengine/runtime`, so the plan-format coupling is
self-consistent per graph by construction — there is no separate sha256
to track. The root justfile's `exam-polyengine` recipe (wired into CI) is the
one-version-everywhere gate: it asserts every `jsr:@polyengine/*` import
across both `deno.json` files names the identical version.
to track. The root justfile's `exam-polyengine` recipe (wired into CI) is
the pin gate: it asserts one resolved `@polyengine/runtime`-family
version and one resolved `@polyengine/protocol` version across both
`deno.lock` files, plus a cheap guard that `js/polyengine` (the published
host module) names no `@polyengine/runtime` specifier at all.

Both `deno.json`s carry
`"minimumDependencyAge": { "age": "P1D", "exclude": ["jsr:@polyengine/*"] }`
(verbatim from polyengine's README): Deno's 24-hour supply-chain gate would
otherwise block resolving a same-day publish.

To bump: update the version in both `deno.json` import maps, delete
**both** `deno.lock` files (this directory and `js/polyengine/`), regenerate
with `deno install` (or `deno check`) in each directory, then re-run
To bump: update the version in this directory's `deno.json` import map
(and `js/polyengine/deno.json`'s `@polyengine/protocol` caret range, if
bumping that independently-versioned line), delete **both** `deno.lock`
files (this directory and `js/polyengine/`), regenerate with
`deno install` (or `deno check`) in each directory, then re-run
`just conformance-ct::run-polyengine` and commit the diff (including the
regenerated `matrix.md`, via `just conformance-ct::matrix-update`).
`exam-polyengine` fails loud on any drift between the two configs.
`exam-polyengine` fails loud on any drift.
8 changes: 7 additions & 1 deletion conformance/driver-ct/polyengine/browser-bundle-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
// browser/worker-entry.ts imports as `@polyengine/release-bundle-entry`
// used to. Same export surface: instantiate/Translator/runSuite/
// wasi/ComponentException/artifactsFromEnvelope and friends (49 exports
// verified against the retired upstream entry).
// verified against the retired upstream entry). A22 split the vocabulary
// (ComponentException and friends) out of @polyengine/runtime/embedder
// into @polyengine/protocol; both re-exports are combined here so the
// bundle's consumers (the worker message loop, @polymorph/test's
// polyengine-worker-main injection path) keep seeing the same names off
// one namespace — no collisions, since the embedder no longer exports them.
export * from "@polyengine/runtime/embedder";
export * from "@polyengine/protocol";
export { Translator } from "@polyengine/runtime/shim";
export * from "@polyengine/ct-runner";
export { wasi } from "@polyengine/wasi";
Expand Down
13 changes: 7 additions & 6 deletions conformance/driver-ct/polyengine/deno.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"//": "MODULE-IDENTITY CONSTRAINT: polyengine's wasi module imports @polyengine/runtime/embedder by bare specifier internally. The @polyengine/runtime/embedder entry here must resolve to the IDENTICAL @polyengine/runtime version as ../../../js/polyengine/deno.json's caret range (in both deno.locks), or the embedder module loads twice: value recognition survives that (websocket.ts recognizes polyengine values by @polyengine/protocol brand predicates, not instanceof), but a second copy splits the runtime/translator plan-format pairing, and stateful handles (streams/futures) minted by one copy are refused by the other. Pin story: exact-pinned JSR releases (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor; per-commit `<next>-pre.g<hash>` prereleases exist between releases — pin exactly); @polyengine/translator ships the translator wasm for that SAME commit; deno.lock carries integrity, --frozen enforced everywhere; one version repo-wide is asserted by the pin gate (justfile: exam-polyengine).",
"//": "A22 MODULE-IDENTITY POSTURE: @polyengine/wasi@0.5.0 is protocol-only internally (the old module-identity clause tying it to @polyengine/runtime/embedder is gone), so this file's runtime-family pins only need to be exact and mutually consistent with each other and with ../../../js/polyengine/deno.json when that package's caret range resolves the same commit — this repo's driver code (run.ts, examples/polyengine-demo/run.ts) that also loads @polyengine/protocol vocabulary must still resolve to ONE @polyengine/runtime version if it loads the embedder in more than one config, since stateful handles (streams/futures) minted by one copy are refused by another; the published @polymorph/websocket host module, by contrast, now couples only to @polyengine/protocol, whose copies are harmless by construction. Pin story: exact-pinned JSR releases (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor; per-commit `<next>-pre.g<hash>` prereleases exist between releases — pin exactly); @polyengine/translator ships the translator wasm for that SAME commit; deno.lock carries integrity, --frozen enforced everywhere; the pin gate (justfile: exam-polyengine) asserts one resolved @polyengine/runtime version and one resolved @polyengine/protocol version repo-wide.",
"imports": {
"@polyengine/ct-runner": "jsr:@polyengine/ct-runner@0.4.0",
"@polyengine/runtime/embedder": "jsr:@polyengine/runtime@0.4.0/embedder",
"@polyengine/runtime/shim": "jsr:@polyengine/runtime@0.4.0/shim",
"@polyengine/wasi": "jsr:@polyengine/wasi@0.4.0",
"@polyengine/translator": "jsr:@polyengine/translator@0.4.0",
"@polyengine/ct-runner": "jsr:@polyengine/ct-runner@0.5.0",
"@polyengine/runtime/embedder": "jsr:@polyengine/runtime@0.5.0/embedder",
"@polyengine/runtime/shim": "jsr:@polyengine/runtime@0.5.0/shim",
"@polyengine/wasi": "jsr:@polyengine/wasi@0.5.0",
"@polyengine/translator": "jsr:@polyengine/translator@0.5.0",
"@polyengine/protocol": "jsr:@polyengine/protocol@0.2.2",
"@polymorph/test/polyengine-worker-main": "jsr:@polymorph/test@0.2.1/polyengine-worker-main"
},
"minimumDependencyAge": { "age": "P1D", "exclude": ["jsr:@polyengine/*", "jsr:@polymorph/*"] },
Expand Down
66 changes: 40 additions & 26 deletions conformance/driver-ct/polyengine/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/polyengine-demo/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

import { Translator } from "@polyengine/runtime/shim";
import type { ComponentArtifacts } from "@polyengine/runtime/embedder";
import { instantiate, isComponentException } from "@polyengine/runtime/embedder";
import { instantiate } from "@polyengine/runtime/embedder";
import { isComponentException } from "@polyengine/protocol";
import { defaultTranslator } from "@polyengine/translator";
import { wasi } from "@polyengine/wasi";
import { websocketImports } from "../../js/polyengine/websocket.ts";
Expand Down
6 changes: 3 additions & 3 deletions js/componentize/wpt/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
#
# The polyengine pin is single-site: the carrier bundle resolves @polyengine/*
# through conformance/driver-ct/polyengine/deno.json (exact-pinned JSR
# prereleases; see that deno.json's MODULE-IDENTITY comment) and its
# translator asset is extracted from the packaged `@polyengine/translator`
# JSR package's lock-pinned module cache (no fetch step).
# releases) and its translator asset is extracted from the packaged
# `@polyengine/translator` JSR package's lock-pinned module cache (no
# fetch step).

import '../../../justfile.shared.just'

Expand Down
22 changes: 12 additions & 10 deletions js/polyengine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ identically on Deno.

## Module identity

`deno.json`'s `@polyengine/runtime/embedder` import maps to the exact same
pinned URL as `conformance/driver-ct/polyengine/deno.json`. polyengine's
`wasi-shims` module imports that specifier by bare name internally; if the
two configs ever disagreed, the embedder module would load twice. This
module recognizes polyengine values by `@polyengine/protocol` brand
predicates (`isComponentException`, the `STREAM` brand), so recognition
holds even in a multi-copy graph — but a duplicated embedder still splits
the runtime/translator plan-format pairing, and stateful handles
(streams/futures) minted by one copy are refused by the other. Keep both
import maps byte-identical for that one entry.
This package (`@polymorph/websocket`) depends on `@polyengine/protocol`
only (A22: published host modules must not import
`@polyengine/runtime`). It recognizes polyengine values by
`@polyengine/protocol` brand predicates (`isComponentException`, the
`STREAM` brand), not `instanceof`, so recognition holds even in a
multi-copy graph — this package's own copy of `@polyengine/protocol` is
harmless by construction and need not agree with any other config's.
Applications that separately load the `@polyengine/runtime` embedder
(e.g. `conformance/driver-ct/polyengine/deno.json`) still need ONE
resolved runtime version across their own configs, since stateful
handles (streams/futures) minted by one copy are refused by another —
but that constraint is theirs, not this package's.

## Unit tests

Expand Down
6 changes: 3 additions & 3 deletions js/polyengine/deno.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "@polymorph/websocket",
"version": "0.4.0",
"version": "0.5.0",
"license": "Apache-2.0",
"exports": "./websocket.ts",
"//": "MODULE-IDENTITY CONSTRAINT: polyengine's wasi module imports @polyengine/runtime/embedder by bare specifier internally. This file's @polyengine/runtime range and conformance/driver-ct/polyengine/deno.json's exact pin must resolve to the IDENTICAL @polyengine/runtime version in both deno.locks, or the embedder module loads twice: value recognition survives that (websocket.ts recognizes polyengine values by @polyengine/protocol brand predicates, not instanceof), but a second copy splits the runtime/translator plan-format pairing, and stateful handles (streams/futures) minted by one copy are refused by the other. Pin story: this published package takes a caret range (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor); the repo-internal conformance harness exact-pins instead so its lock is the single source of truth for the resolved version; deno.lock carries integrity, --frozen enforced everywhere; one RESOLVED version repo-wide (across both locks) is asserted by the pin gate (justfile: exam-polyengine).",
"//": "A22 MODULE-IDENTITY POSTURE: this package couples only to @polyengine/protocol (the host-ABI vocabulary package) — no @polyengine/runtime import anywhere in this module, per polyengine's A22 rule that published host modules must not import @polyengine/runtime. websocket.ts recognizes polyengine values by @polyengine/protocol brand predicates, not instanceof, so its copies are harmless by construction: any application that also loads the embedder resolves its own runtime version independently, and this package neither constrains nor is constrained by that choice. Pin story: this published package takes a caret range on @polyengine/protocol (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor); deno.lock carries integrity, --frozen enforced everywhere; the pin gate (justfile: exam-polyengine) asserts this file names no @polyengine/runtime specifier.",
"imports": {
"@polyengine/runtime/embedder": "jsr:@polyengine/runtime@^0.4.0/embedder"
"@polyengine/protocol": "jsr:@polyengine/protocol@^0.2.2"
},
"minimumDependencyAge": { "age": "P1D", "exclude": ["jsr:@polyengine/*"] },
"compilerOptions": {
Expand Down
Loading
Loading