diff --git a/experiments/iroh-relay-ws/host/deno.json b/experiments/iroh-relay-ws/host/deno.json index 61e5c2e..2fa9b44 100644 --- a/experiments/iroh-relay-ws/host/deno.json +++ b/experiments/iroh-relay-ws/host/deno.json @@ -1,5 +1,5 @@ { - "//": "MODULE-IDENTITY CONSTRAINT: polyengine's wasi package and the sibling @polymorph host modules (JSR) import @polyengine/runtime/embedder by bare specifier internally; this file maps that specifier ONCE for the whole module graph, so `instanceof ComponentException` holds across every boundary. This is the ONE deno config for all three experiments (iroh-relay-ws, iroh-blobs, ping-demo): the others pass --config pointing here. polyengine arrives as exact-pinned JSR releases (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor; per-commit `-pre.g` prereleases exist between releases — pin exactly; @polyengine/translator ships the translator wasm for the SAME commit); the version matches host-polyengine/deno.json by repo convention (exam-polyengine asserts it; see host-polyengine/README.md, 'The pin'). The @polymorph packages are exact-pinned the same way. minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @polyengine/@polymorph publishes resolve. The webrtc module's npm dependencies (node-datachannel, werift) arrive through @polymorph/webrtc-datachannels' own manifest; a browser build externalizes them and resolves the RTCPeerConnection global instead. compilerOptions.lib carries dom next to deno.ns so the browser entries (browser-entry.ts, ping-demo's demo.ts/overlay.ts) type-check in the same graph as the Deno drivers.", + "//": "MODULE-IDENTITY CONSTRAINT: this application config loads @polyengine/runtime/embedder ONCE for the whole experiment graph (iroh-relay-ws, iroh-blobs, ping-demo), so `instanceof`-checked embedder handles hold across every boundary here by construction. As of A22 (polyengine 0.5.0), @polyengine/wasi is protocol-only internally and the @polymorph sibling packages (@polymorph/{websocket,webrtc-datachannels}) now depend only on @polyengine/protocol — neither couples to @polyengine/runtime, so they no longer bear on embedder identity; only this config's own two @polyengine/runtime import lines (embedder, shim) do, and mapping them once here is what keeps that true. This is the ONE deno config for all three experiments: the others pass --config pointing here. polyengine's lockstep family (runtime, translator, wasi) arrives as exact-pinned JSR releases (caret-honest upstream: within a minor line releases stay compatible, breaking bumps the minor; per-commit `-pre.g` prereleases exist between releases — pin exactly; @polyengine/translator ships the translator wasm for the SAME commit); @polyengine/protocol is versioned independently (the A22 host-ABI vocabulary line) and is also exact-pinned here. The runtime family's version matches host-polyengine/deno.json by repo convention (exam-polyengine asserts it; see host-polyengine/README.md, 'The pin'). The @polymorph packages are exact-pinned the same way. minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @polyengine/@polymorph publishes resolve. The webrtc module's npm dependencies (node-datachannel, werift) arrive through @polymorph/webrtc-datachannels' own manifest; a browser build externalizes them and resolves the RTCPeerConnection global instead. compilerOptions.lib carries dom next to deno.ns so the browser entries (browser-entry.ts, ping-demo's demo.ts/overlay.ts) type-check in the same graph as the Deno drivers.", "nodeModulesDir": "auto", "minimumDependencyAge": { "age": "P1D", @@ -9,13 +9,14 @@ "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] }, "imports": { - "@polyengine/runtime/embedder": "jsr:@polyengine/runtime@0.4.0/embedder", - "@polyengine/runtime/shim": "jsr:@polyengine/runtime@0.4.0/shim", - "@polyengine/translator": "jsr:@polyengine/translator@0.4.0", - "@polyengine/wasi": "jsr:@polyengine/wasi@0.4.0", - "@polyengine/wasi/io": "jsr:@polyengine/wasi@0.4.0/io", - "@polymorph/webrtc-datachannels": "jsr:@polymorph/webrtc-datachannels@0.4.0", - "@polymorph/websocket": "jsr:@polymorph/websocket@0.4.0" + "@polyengine/runtime/embedder": "jsr:@polyengine/runtime@0.5.0/embedder", + "@polyengine/runtime/shim": "jsr:@polyengine/runtime@0.5.0/shim", + "@polyengine/protocol": "jsr:@polyengine/protocol@0.2.2", + "@polyengine/translator": "jsr:@polyengine/translator@0.5.0", + "@polyengine/wasi": "jsr:@polyengine/wasi@0.5.0", + "@polyengine/wasi/io": "jsr:@polyengine/wasi@0.5.0/io", + "@polymorph/webrtc-datachannels": "jsr:@polymorph/webrtc-datachannels@0.5.0", + "@polymorph/websocket": "jsr:@polymorph/websocket@0.5.0" }, "tasks": { "check": "deno check ." diff --git a/experiments/iroh-relay-ws/host/deno.lock b/experiments/iroh-relay-ws/host/deno.lock index a3aceaa..96cef24 100644 --- a/experiments/iroh-relay-ws/host/deno.lock +++ b/experiments/iroh-relay-ws/host/deno.lock @@ -1,52 +1,52 @@ { "version": "5", "specifiers": { - "jsr:@polyengine/protocol@~0.2.1": "0.2.1", - "jsr:@polyengine/runtime@0.4": "0.4.0", - "jsr:@polyengine/runtime@0.4.0": "0.4.0", - "jsr:@polyengine/translator@0.4.0": "0.4.0", - "jsr:@polyengine/wasi@0.4.0": "0.4.0", - "jsr:@polymorph/webrtc-datachannels@0.4.0": "0.4.0", - "jsr:@polymorph/websocket@0.4.0": "0.4.0", + "jsr:@polyengine/protocol@0.2.2": "0.2.2", + "jsr:@polyengine/protocol@~0.2.2": "0.2.2", + "jsr:@polyengine/runtime@0.5": "0.5.0", + "jsr:@polyengine/runtime@0.5.0": "0.5.0", + "jsr:@polyengine/translator@0.5.0": "0.5.0", + "jsr:@polyengine/wasi@0.5.0": "0.5.0", + "jsr:@polymorph/webrtc-datachannels@0.5.0": "0.5.0", + "jsr:@polymorph/websocket@0.5.0": "0.5.0", "npm:node-datachannel@0.32.3": "0.32.3", "npm:playwright@^1.62.1": "1.62.1", "npm:werift@0.22.2": "0.22.2" }, "jsr": { - "@polyengine/protocol@0.2.1": { - "integrity": "857bef84afedc414d47daedf203448f72e8803cf96301afb783b1ac975f3dcec" + "@polyengine/protocol@0.2.2": { + "integrity": "ff470d7183924119ae181cc513093318f1587f7ad5c4bc02db7344e90c9ff52b" }, - "@polyengine/runtime@0.4.0": { - "integrity": "d34a59d9df58bd48541483619d1987319d8aad4d2e0f3775890177ffb0878a5a", + "@polyengine/runtime@0.5.0": { + "integrity": "36ea9b74dd38c0f5fb941695cfa3223190fda54bc18f7328600ece082dbbb794", "dependencies": [ - "jsr:@polyengine/protocol" + "jsr:@polyengine/protocol@~0.2.2" ] }, - "@polyengine/translator@0.4.0": { - "integrity": "227cf2f92a5b11bce6155742b2a42cf14a642a89d3867ffd1789d6e6e8d49fdc", + "@polyengine/translator@0.5.0": { + "integrity": "2f9770f468bd0c649732e6107d69715dea715c71564fe5b0ba0997b5d70e81c3", "dependencies": [ - "jsr:@polyengine/runtime@0.4" + "jsr:@polyengine/runtime@0.5" ] }, - "@polyengine/wasi@0.4.0": { - "integrity": "b946443255e98cb6156237de90f051af7e483d2afdc0d721bdba64392cdbd896", + "@polyengine/wasi@0.5.0": { + "integrity": "61275e66895084807393bf93876b74be9cc1ca37641d56a07cf8c7b9a78ce091", "dependencies": [ - "jsr:@polyengine/protocol", - "jsr:@polyengine/runtime@0.4" + "jsr:@polyengine/protocol@~0.2.2" ] }, - "@polymorph/webrtc-datachannels@0.4.0": { - "integrity": "98022ce52d150532ac422faf86fba50d9ae55676d3cf803888bae79255ed35b1", + "@polymorph/webrtc-datachannels@0.5.0": { + "integrity": "fef8803ef87826ce8c5cc6545460ce2e41ef63d5c31d10f63e9fb0666a752734", "dependencies": [ - "jsr:@polyengine/runtime@0.4", + "jsr:@polyengine/protocol@~0.2.2", "npm:node-datachannel", "npm:werift" ] }, - "@polymorph/websocket@0.4.0": { - "integrity": "f2b77720ca02ba95d405a51abd4ca930074a22188ce23a5731c0a6b40f9c4bf2", + "@polymorph/websocket@0.5.0": { + "integrity": "84a7862e9bdeaedca92f7b57b12f5ee1348878734906feebb4d37634ac6659ae", "dependencies": [ - "jsr:@polyengine/runtime@0.4" + "jsr:@polyengine/protocol@~0.2.2" ] } }, @@ -624,11 +624,12 @@ }, "workspace": { "dependencies": [ - "jsr:@polyengine/runtime@0.4.0", - "jsr:@polyengine/translator@0.4.0", - "jsr:@polyengine/wasi@0.4.0", - "jsr:@polymorph/webrtc-datachannels@0.4.0", - "jsr:@polymorph/websocket@0.4.0" + "jsr:@polyengine/protocol@0.2.2", + "jsr:@polyengine/runtime@0.5.0", + "jsr:@polyengine/translator@0.5.0", + "jsr:@polyengine/wasi@0.5.0", + "jsr:@polymorph/webrtc-datachannels@0.5.0", + "jsr:@polymorph/websocket@0.5.0" ], "packageJson": { "dependencies": [ diff --git a/experiments/iroh-relay-ws/host/errors.ts b/experiments/iroh-relay-ws/host/errors.ts index 98306be..9801dd4 100644 --- a/experiments/iroh-relay-ws/host/errors.ts +++ b/experiments/iroh-relay-ws/host/errors.ts @@ -2,7 +2,7 @@ // `ComponentException<{kind, value?}>` payloads (the sibling host modules // throw these). -import { ComponentException } from "@polyengine/runtime/embedder"; +import { ComponentException } from "@polyengine/protocol"; export function describeErr(err: unknown): string { if (err instanceof ComponentException) { diff --git a/experiments/iroh-relay-ws/host/harness.ts b/experiments/iroh-relay-ws/host/harness.ts index 51881a0..9766b19 100644 --- a/experiments/iroh-relay-ws/host/harness.ts +++ b/experiments/iroh-relay-ws/host/harness.ts @@ -6,11 +6,13 @@ // globals only. // // MODULE-IDENTITY CONSTRAINT (host-polyengine/README.md "Module identity"): -// polyengine's wasi package and this package import `@polyengine/runtime/embedder` -// by bare specifier; the `deno.json` next to this file maps that -// specifier once for the whole module graph, so there is exactly one -// `ComponentException` module instance and `instanceof` holds across every -// boundary — including the branded errors sockets.ts throws. +// this application config maps `@polyengine/runtime/embedder` ONCE for the +// whole experiment module graph, so there is exactly one embedder instance +// and stateful handles (component instantiation, streams minted through it) +// stay portable across every boundary here. `ComponentException` and other +// vocabulary (thrown by sockets.ts) come from `@polyengine/protocol` +// instead (A22) — its copies are brand-checked, not `instanceof`-checked, +// so they don't depend on this constraint. import type { ComponentArtifacts } from "@polyengine/runtime/embedder"; import { artifactsFromEnvelope, instantiate } from "@polyengine/runtime/embedder"; diff --git a/experiments/iroh-relay-ws/host/sockets.ts b/experiments/iroh-relay-ws/host/sockets.ts index 1628c51..dfb1ac6 100644 --- a/experiments/iroh-relay-ws/host/sockets.ts +++ b/experiments/iroh-relay-ws/host/sockets.ts @@ -22,7 +22,7 @@ // Environment-portable: standard globals only; works under Deno and in // browsers. -import { ComponentException } from "@polyengine/runtime/embedder"; +import { ComponentException } from "@polyengine/protocol"; import { Pollable } from "@polyengine/wasi/io"; // --------------------------------------------------------------------------- diff --git a/host-polyengine/README.md b/host-polyengine/README.md index f91df20..fd1d15e 100644 --- a/host-polyengine/README.md +++ b/host-polyengine/README.md @@ -54,8 +54,10 @@ guest's and is latent on every host. ## The pin polyengine and the sibling host modules arrive from JSR under caret -constraints on one minor line (`jsr:@polyengine/*@^0.4.0`, -`jsr:@polymorph/*@^0.4.0`); `deno.lock` pins the resolved versions and +constraints on one minor line: the `@polyengine/{runtime,translator,wasi}@^0.5.0` +lockstep family, plus `@polyengine/protocol@^0.2.2` (versioned independently +of the lockstep family — the A22 host-ABI vocabulary line) and +`jsr:@polymorph/*@^0.5.0`. `deno.lock` pins the resolved versions and carries integrity, enforced with `--frozen`. `@polyengine/translator` ships the translator wasm for the same commit as the runtime, so the plan-format coupling between runtime and translator is self-consistent @@ -65,7 +67,8 @@ no fetch step. `minimumDependencyAge` exempts `jsr:@polyengine/*` and same-day releases resolve; everything else keeps the default. `experiments/iroh-relay-ws/host/deno.json` (the upstream-iroh spikes' shared config) exact-pins the same polyengine packages; the `exam-polyengine` -recipe asserts both `deno.lock`s resolve to one polyengine version. +recipe asserts both `deno.lock`s resolve to one `@polyengine/runtime` +version and one `@polyengine/protocol` version. To bump: adjust the constraints (a new minor line) or just delete the `deno.lock` files (within the line), re-run `just polyengine-setup` and @@ -75,16 +78,23 @@ diff. ## Module identity -polyengine's wasi package and the sibling host modules import -`@polyengine/runtime/embedder` by bare specifier internally, each resolved -through its own package manifest. Identity rests on every manifest in -the graph carrying a constraint the resolver can satisfy with ONE -`@polyengine/runtime` version, so it dedupes to one -`ComponentException`/`Stream` module instance — caret constraints on -one minor line guarantee that; an exact pin outside every other -manifest's range (or a raw-URL embedder module) splits the graph, and -`instanceof ComponentException` silently stops holding across that -boundary. Two gates in `just exam-polyengine` keep it true: the lock check -(this repo's `deno.lock`s resolve to one polyengine version) and -`scripts/polyengine-identity-gate.ts` (the RESOLVED run-endpoint graph -carries exactly one `@polyengine/runtime` and no raw URLs). +This package instantiates the packaged endpoint component, so it still +loads `@polyengine/runtime/embedder` by bare specifier, resolved through +its own `deno.json`. As of A22 (polyengine 0.5.0), `@polyengine/wasi` is +protocol-only internally and the sibling host modules +(`@polymorph/{webcrypto,websocket,webrtc-datachannels}@^0.5.0`) depend +only on `@polyengine/protocol` — neither couples to `@polyengine/runtime` +at all, so they no longer contribute to embedder identity. The remaining +true constraint: any consumer graph that loads the embedder in more than +one config (this package does; the `experiments/iroh-relay-ws/host` +config does too, for the upstream-iroh spikes) must still resolve to ONE +`@polyengine/runtime` version — a component instantiated under one +embedder copy is refused by another (stateful handles are not portable +across runtime copies). `@polyengine/protocol` copies are harmless by +construction: its error classes and handle-vocabulary types are brand- +checked, not `instanceof`-checked, across copies. Two gates in +`just exam-polyengine` keep this true: the lock check (this repo's +`deno.lock`s resolve to one `@polyengine/runtime` version and one +`@polyengine/protocol` version) and `scripts/polyengine-identity-gate.ts` +(the RESOLVED run-endpoint graph carries exactly one +`@polyengine/runtime` and no raw URLs). diff --git a/host-polyengine/deno.json b/host-polyengine/deno.json index 2ea8526..fe797e7 100644 --- a/host-polyengine/deno.json +++ b/host-polyengine/deno.json @@ -1,21 +1,22 @@ { "name": "@polymorph/iroh", - "version": "0.4.0", + "version": "0.5.0", "license": "Apache-2.0", "exports": { ".": "./src/mod.ts" }, - "//": "MODULE-IDENTITY CONSTRAINT: polyengine's wasi package and the sibling @polymorph host modules import @polyengine/runtime/embedder by bare specifier internally. Every dependency constraint here is a caret range on one minor line (published-package convention, polyengine's own precedent), so a consumer's resolver dedupes the whole family onto ONE @polyengine/runtime module instance and `instanceof ComponentException` holds across every boundary; deno.lock carries integrity, --frozen enforced in the repo. The exam gate (justfile: exam-polyengine) asserts the repo's deno.locks resolve to one @polyengine version and pipes the resolved graph through scripts/polyengine-identity-gate.ts (one runtime module, no raw URLs). src/endpoint_component.ts is GENERATED by `just build-components` (gitignored; the publish workflow regenerates it) — the packaged endpoint component, base64 in the module graph. minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @polyengine/@polymorph publishes resolve.", + "//": "MODULE-IDENTITY CONSTRAINT: this package instantiates the packaged endpoint component, so it still loads @polyengine/runtime/embedder — every consumer's resolver must still resolve exactly ONE @polyengine/runtime version for stateful-handle interop (a component instantiated under one embedder copy is refused by another). @polyengine/wasi@0.5.0 is protocol-only internally (A22) and the sibling @polymorph host modules (@polymorph/{webcrypto,websocket,webrtc-datachannels}@0.5.0) depend only on @polyengine/protocol — neither couples to @polyengine/runtime at all, so they no longer contribute to that constraint; @polyengine/protocol copies are harmless by construction (A22, contracts/embedder-api.md). Every dependency constraint here is a caret range on one minor line (published-package convention, polyengine's own precedent); deno.lock carries integrity, --frozen enforced in the repo. The exam gate (justfile: exam-polyengine) asserts the repo's deno.locks resolve to one @polyengine/runtime version and one @polyengine/protocol version, and pipes the resolved graph through scripts/polyengine-identity-gate.ts (one runtime module, no raw URLs). src/endpoint_component.ts is GENERATED by `just build-components` (gitignored; the publish workflow regenerates it) — the packaged endpoint component, base64 in the module graph. minimumDependencyAge keeps Deno's default supply-chain gate for everything else while letting same-day @polyengine/@polymorph publishes resolve.", "nodeModulesDir": "auto", "minimumDependencyAge": { "age": "P1D", "exclude": ["jsr:@polyengine/*", "jsr:@polymorph/*"] }, "imports": { - "@polyengine/runtime/embedder": "jsr:@polyengine/runtime@^0.4.0/embedder", - "@polyengine/translator": "jsr:@polyengine/translator@^0.4.0", - "@polyengine/wasi": "jsr:@polyengine/wasi@^0.4.0", - "@polymorph/webcrypto": "jsr:@polymorph/webcrypto@^0.4.0", - "@polymorph/websocket": "jsr:@polymorph/websocket@^0.4.0", - "@polymorph/webrtc-datachannels": "jsr:@polymorph/webrtc-datachannels@^0.4.0" + "@polyengine/runtime/embedder": "jsr:@polyengine/runtime@^0.5.0/embedder", + "@polyengine/protocol": "jsr:@polyengine/protocol@^0.2.2", + "@polyengine/translator": "jsr:@polyengine/translator@^0.5.0", + "@polyengine/wasi": "jsr:@polyengine/wasi@^0.5.0", + "@polymorph/webcrypto": "jsr:@polymorph/webcrypto@^0.5.0", + "@polymorph/websocket": "jsr:@polymorph/websocket@^0.5.0", + "@polymorph/webrtc-datachannels": "jsr:@polymorph/webrtc-datachannels@^0.5.0" }, "publish": { "exclude": ["src/run-endpoint.ts", "src/scenario.ts", "src/repo.ts", "!src/endpoint_component.ts"] diff --git a/host-polyengine/deno.lock b/host-polyengine/deno.lock index b0140cf..04328da 100644 --- a/host-polyengine/deno.lock +++ b/host-polyengine/deno.lock @@ -1,57 +1,56 @@ { "version": "5", "specifiers": { - "jsr:@polyengine/protocol@~0.2.1": "0.2.1", - "jsr:@polyengine/runtime@0.4": "0.4.0", - "jsr:@polyengine/translator@0.4": "0.4.0", - "jsr:@polyengine/wasi@0.4": "0.4.0", - "jsr:@polymorph/webcrypto@0.4": "0.4.0", - "jsr:@polymorph/webrtc-datachannels@0.4": "0.4.0", - "jsr:@polymorph/websocket@0.4": "0.4.0", + "jsr:@polyengine/protocol@~0.2.2": "0.2.2", + "jsr:@polyengine/runtime@0.5": "0.5.0", + "jsr:@polyengine/translator@0.5": "0.5.0", + "jsr:@polyengine/wasi@0.5": "0.5.0", + "jsr:@polymorph/webcrypto@0.5": "0.5.0", + "jsr:@polymorph/webrtc-datachannels@0.5": "0.5.0", + "jsr:@polymorph/websocket@0.5": "0.5.0", "npm:node-datachannel@0.32.3": "0.32.3", "npm:werift@0.22.2": "0.22.2" }, "jsr": { - "@polyengine/protocol@0.2.1": { - "integrity": "857bef84afedc414d47daedf203448f72e8803cf96301afb783b1ac975f3dcec" + "@polyengine/protocol@0.2.2": { + "integrity": "ff470d7183924119ae181cc513093318f1587f7ad5c4bc02db7344e90c9ff52b" }, - "@polyengine/runtime@0.4.0": { - "integrity": "d34a59d9df58bd48541483619d1987319d8aad4d2e0f3775890177ffb0878a5a", + "@polyengine/runtime@0.5.0": { + "integrity": "36ea9b74dd38c0f5fb941695cfa3223190fda54bc18f7328600ece082dbbb794", "dependencies": [ "jsr:@polyengine/protocol" ] }, - "@polyengine/translator@0.4.0": { - "integrity": "227cf2f92a5b11bce6155742b2a42cf14a642a89d3867ffd1789d6e6e8d49fdc", + "@polyengine/translator@0.5.0": { + "integrity": "2f9770f468bd0c649732e6107d69715dea715c71564fe5b0ba0997b5d70e81c3", "dependencies": [ "jsr:@polyengine/runtime" ] }, - "@polyengine/wasi@0.4.0": { - "integrity": "b946443255e98cb6156237de90f051af7e483d2afdc0d721bdba64392cdbd896", + "@polyengine/wasi@0.5.0": { + "integrity": "61275e66895084807393bf93876b74be9cc1ca37641d56a07cf8c7b9a78ce091", "dependencies": [ - "jsr:@polyengine/protocol", - "jsr:@polyengine/runtime" + "jsr:@polyengine/protocol" ] }, - "@polymorph/webcrypto@0.4.0": { - "integrity": "ffedef7d0141571a81aa55c47d892d3ff95e3ca6796815e231729f81f0286d3d", + "@polymorph/webcrypto@0.5.0": { + "integrity": "b9b03e2855a83f8817bb9d6f6a0cb5ec261a4dd3c09c957b741eeb7eba639ef9", "dependencies": [ - "jsr:@polyengine/runtime" + "jsr:@polyengine/protocol" ] }, - "@polymorph/webrtc-datachannels@0.4.0": { - "integrity": "98022ce52d150532ac422faf86fba50d9ae55676d3cf803888bae79255ed35b1", + "@polymorph/webrtc-datachannels@0.5.0": { + "integrity": "fef8803ef87826ce8c5cc6545460ce2e41ef63d5c31d10f63e9fb0666a752734", "dependencies": [ - "jsr:@polyengine/runtime", + "jsr:@polyengine/protocol", "npm:node-datachannel", "npm:werift" ] }, - "@polymorph/websocket@0.4.0": { - "integrity": "f2b77720ca02ba95d405a51abd4ca930074a22188ce23a5731c0a6b40f9c4bf2", + "@polymorph/websocket@0.5.0": { + "integrity": "84a7862e9bdeaedca92f7b57b12f5ee1348878734906feebb4d37634ac6659ae", "dependencies": [ - "jsr:@polyengine/runtime" + "jsr:@polyengine/protocol" ] } }, @@ -610,12 +609,13 @@ }, "workspace": { "dependencies": [ - "jsr:@polyengine/runtime@0.4", - "jsr:@polyengine/translator@0.4", - "jsr:@polyengine/wasi@0.4", - "jsr:@polymorph/webcrypto@0.4", - "jsr:@polymorph/webrtc-datachannels@0.4", - "jsr:@polymorph/websocket@0.4" + "jsr:@polyengine/protocol@~0.2.2", + "jsr:@polyengine/runtime@0.5", + "jsr:@polyengine/translator@0.5", + "jsr:@polyengine/wasi@0.5", + "jsr:@polymorph/webcrypto@0.5", + "jsr:@polymorph/webrtc-datachannels@0.5", + "jsr:@polymorph/websocket@0.5" ] } } diff --git a/host-polyengine/src/harness.ts b/host-polyengine/src/harness.ts index c6b467e..ac8a33d 100644 --- a/host-polyengine/src/harness.ts +++ b/host-polyengine/src/harness.ts @@ -6,16 +6,17 @@ // host's retired `iroh.Endpoint.bind(...)` driving shape over // `instantiate` + the polyengine embedder facade. // -// MODULE-IDENTITY CONSTRAINT: polyengine's wasi package and the sibling host -// modules import `@polyengine/runtime/embedder` by bare specifier internally; -// this package's `deno.json` and the sibling packages' manifests all -// carry caret constraints on one `@polyengine/runtime` minor line, so a -// consumer's resolver dedupes them onto one module instance and -// `instanceof ComponentException`/`Stream` holds across every boundary. +// MODULE-IDENTITY CONSTRAINT: this package instantiates the packaged endpoint +// component, so it still loads `@polyengine/runtime/embedder`; this +// package's `deno.json` and every consumer graph must resolve exactly one +// `@polyengine/runtime` version for stateful-handle interop. Vocabulary +// (`ComponentException`, etc.) comes from `@polyengine/protocol` (A22), +// whose copies are harmless even if a consumer resolves more than one. import { defaultTranslator } from "@polyengine/translator"; import type { ComponentArtifacts } from "@polyengine/runtime/embedder"; -import { instantiate, ComponentException } from "@polyengine/runtime/embedder"; +import { instantiate } from "@polyengine/runtime/embedder"; +import { ComponentException } from "@polyengine/protocol"; import { wasi } from "@polyengine/wasi"; import { webcryptoImports } from "@polymorph/webcrypto"; import { websocketImports } from "@polymorph/websocket"; diff --git a/host-polyengine/src/run-endpoint.ts b/host-polyengine/src/run-endpoint.ts index 6331f98..5d93695 100644 --- a/host-polyengine/src/run-endpoint.ts +++ b/host-polyengine/src/run-endpoint.ts @@ -74,7 +74,7 @@ async function newEndpointInstance( ...options, }); } -import { ComponentException } from "@polyengine/runtime/embedder"; +import { ComponentException } from "@polyengine/protocol"; import { resetUdpCallLog, udpCallLog } from "./sockets.ts"; import type { CloseInfo, diff --git a/host-polyengine/src/sockets.ts b/host-polyengine/src/sockets.ts index d834a66..bd6a211 100644 --- a/host-polyengine/src/sockets.ts +++ b/host-polyengine/src/sockets.ts @@ -26,7 +26,7 @@ // extension once polyengine's wasi package grows p3 `wasi:sockets` providers // (polymorph-components/polyengine#4); this file is where that provider would be wired. -import { ComponentException } from "@polyengine/runtime/embedder"; +import { ComponentException } from "@polyengine/protocol"; /** `wasi:sockets/types@0.3.0`'s `ip-address-family` enum. */ export type IpAddressFamily = "ipv4" | "ipv6"; diff --git a/justfile b/justfile index 5ac0e0c..141dd09 100644 --- a/justfile +++ b/justfile @@ -88,15 +88,21 @@ polyengine-setup: exam-polyengine: build-components polyengine-setup #!/usr/bin/env bash set -euo pipefail - # One RESOLVED polyengine version repo-wide (host-polyengine/README.md "The - # pin"): the exam is the natural fail-loud point, replacing the - # retired fetch-translator gate. host-polyengine's published manifest - # carries caret ranges, so specifier strings no longer pin identity; - # what module identity needs is that every @polyengine/* package (except - # @polyengine/protocol, versioned independently) RESOLVES to the same - # version across both deno.locks — a translator/runtime split is - # exactly the plan-format skew (or double-runtime ComponentException - # identity break) the packaged translator exists to rule out. + # The two-line world (A22): exactly one RESOLVED @polyengine/runtime + # lockstep-family version repo-wide (host-polyengine/README.md "The + # pin"), and separately exactly one RESOLVED @polyengine/protocol + # version — @polyengine/protocol is versioned independently, so its + # presence alongside the runtime family in the lock is NOT drift, but + # it must still be singular (protocol appearing at two versions would + # mean two incompatible host-ABI vocabularies in one graph). The exam + # is the natural fail-loud point, replacing the retired + # fetch-translator gate. host-polyengine's published manifest carries + # caret ranges, so specifier strings no longer pin identity; what + # module identity needs is that every @polyengine/{runtime,translator, + # wasi} package RESOLVES to the same version across both deno.locks — + # a translator/runtime split is exactly the plan-format skew (or + # double-runtime ComponentException identity break) the packaged + # translator exists to rule out. v=$(jq -r '.jsr | keys[]' host-polyengine/deno.lock \ experiments/iroh-relay-ws/host/deno.lock \ | grep '^@polyengine/' | grep -v '^@polyengine/protocol@' \ @@ -105,6 +111,14 @@ exam-polyengine: build-components polyengine-setup echo "polyengine pin drift across deno.locks: $v" >&2 exit 1 fi + p=$(jq -r '.jsr | keys[]' host-polyengine/deno.lock \ + experiments/iroh-relay-ws/host/deno.lock \ + | grep '^@polyengine/protocol@' \ + | sed 's/.*@//' | sort -u) + if [ "$(printf '%s\n' "$p" | wc -l)" != 1 ]; then + echo "@polyengine/protocol pin drift across deno.locks: $p" >&2 + exit 1 + fi # ...and the RESOLVED graph must agree: one embedder instance, no raw # URLs (a sibling module's own config can silently split module # identity in a way no config grep catches; see the gate script).