diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39d51f73..db5589cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,6 +105,9 @@ jobs: with: js-locks: | conformance/driver-ct/jco/package-lock.json + examples/jco-demo/package-lock.json + js/jco/package-lock.json + js/componentize/wpt/parity/package-lock.json - name: Install Node 24 uses: actions/setup-node@v4 diff --git a/conformance/driver-ct/jco/host-imports.mjs b/conformance/driver-ct/jco/host-imports.mjs new file mode 100644 index 00000000..d02ed023 --- /dev/null +++ b/conformance/driver-ct/jco/host-imports.mjs @@ -0,0 +1,42 @@ +// The suites' import object for `-I async` instantiation, shared by +// the Node and browser workers: the polymorph:webcrypto host +// interfaces discovered from the jco host module's lowercase namespace +// exports (the wildcard-map convention, applied at instantiate time — +// a new interface is a new namespace export, enumerated nowhere), the +// driver's test-context provider, and the caller's wasi shim +// namespaces (Node or browser build). Relative specifiers only: module +// Web Workers cannot see a page's import map. + +import { bindImports } from "./node_modules/@polymorph/component-test-js/js/viewer/imports.mjs"; +import * as webcrypto from "../../../js/jco/webcrypto.js"; +import { Context } from "../context.js"; + +/** The camelCased namespace export's WIT interface name. */ +const kebab = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); + +/** Build the full import object over the given wasi shim namespaces. */ +export function suiteImports(wasi) { + const sut = {}; + for (const [name, impl] of Object.entries(webcrypto)) { + if (!/^[a-z]/.test(name) || typeof impl !== "object" || impl === null) { + continue; + } + sut[`polymorph:webcrypto/${kebab(name)}`] = impl; + } + // The driver's own context (diagnostic sink wiring), not the + // upstream default. + sut["polymorph:test/test-context"] = { Context }; + return bindImports({ wasi, env: [], sut }); +} + +/** Instantiate one fresh suite: compiled cores by file name, plus the + * exported `tests` interface whichever spelling the transpile used. */ +export async function instantiateSuite({ instantiate, modules, wasi }) { + const instance = await instantiate((name) => modules.get(name), suiteImports(wasi)); + const tests = + instance.tests ?? instance["polymorph:test/tests@0.1.0"] ?? instance["polymorph:test/tests"]; + if (!tests) { + throw new Error(`suite instance exports no tests interface: ${Object.keys(instance)}`); + } + return tests; +} diff --git a/conformance/driver-ct/jco/package-lock.json b/conformance/driver-ct/jco/package-lock.json index 6ccd8f70..efc925eb 100644 --- a/conformance/driver-ct/jco/package-lock.json +++ b/conformance/driver-ct/jco/package-lock.json @@ -6,28 +6,28 @@ "": { "name": "conformance-driver-ct-jco", "dependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", - "@bytecodealliance/preview2-shim": "^0.17.4", + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "@bytecodealliance/preview2-shim": "0.17.9", "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5", - "playwright-core": "^1.61.1" + "playwright-core": "1.62.1" } }, "node_modules/@bytecodealliance/jco-transpile": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@bytecodealliance/jco-transpile/-/jco-transpile-0.5.2.tgz", - "integrity": "sha512-zXrPtrJkn5jq6MBAOLZUlPnmgdLa8RDER9T4gCclgbAPHWIZSWEjDzv4MrrO3sz6Tvh1eJK1ou2SrrNnJxdTfA==", + "version": "0.6.2", + "resolved": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "integrity": "sha512-9lvxYCxw7G4kD5lA3iGXYphPeccegoHqMT1m3I1O8XO4BVAaYrOuDxmZg67rrs3VMOXHBTt2uVXnl/SeYUCARQ==", "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { - "@bytecodealliance/preview2-shim": "^0.19.0", - "@bytecodealliance/preview3-shim": "^0.2.0", + "@bytecodealliance/preview2-shim": "^0.20.1", + "@bytecodealliance/preview3-shim": "^0.2.2", "binaryen": "^130.0.0", "oxc-minify": "^0.136.0" } }, "node_modules/@bytecodealliance/jco-transpile/node_modules/@bytecodealliance/preview2-shim": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.19.0.tgz", - "integrity": "sha512-OSUlcM62fXLuzYP0DjBn/SwjKMZ/y/2PMXZ8v5cRiiFcroauntlPRl/oCnx0xYjYiqdYvE0PY50VCNutll0xOw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.20.1.tgz", + "integrity": "sha512-gvQP6DWfeQIYKHBb+nPykku1DRtfXQjM685+e75YYr8ZF2ZxisE4+nWrpwf84+NaWJRfuL5HvoLKQFbup1iwkQ==", "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/preview2-shim": { @@ -37,9 +37,9 @@ "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/preview3-shim": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.1.tgz", - "integrity": "sha512-btWNOQPpo+rpxfz2bCu8GVTOGEBQvO0LmSee9GdjkMTWZjMAiiPoO03G4dLitWP9gKwrHcTWwF/2adh6wS6Ugw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.2.tgz", + "integrity": "sha512-9z4tjc+qixUhyeYo3ycoT4cZ9yIlUfkowJxGoKeMfbRd8zT3uvIr1YNfXyU0I93GPu+gdaTVsr8iMr55utEJSA==", "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { "@bytecodealliance/preview2-shim": "^0.19.0" diff --git a/conformance/driver-ct/jco/package.json b/conformance/driver-ct/jco/package.json index edefb7a1..dd9dfda5 100644 --- a/conformance/driver-ct/jco/package.json +++ b/conformance/driver-ct/jco/package.json @@ -4,15 +4,15 @@ "type": "module", "description": "jco drivers (Node + headless browser) for the ported conformance suites: transpiles conformance-guest-ct and conformance-signing-guest-ct against the browser-first host (js/jco/webcrypto.js) plus a JS test-context provider, and runs them with mark scheduling under Node (runner.mjs) or headless Chromium (run-browser.mjs).", "scripts": { - "transpile": "component-test-jco-transpile transpile ../../../target/wasm32-wasip2/release/conformance_guest_ct.wasm --name conformance-guest-ct --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../js/jco/webcrypto.js#*' --map 'polymorph:test/test-context@0.1.0=../../context.js' --map 'wasi:cli/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/cli.js#*' --map 'wasi:clocks/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/clocks.js#*' --map 'wasi:io/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/io.js#*' --map 'wasi:random/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/random.js#*' --map 'wasi:filesystem/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/filesystem.js#*' -o generated", - "transpile:signing": "component-test-jco-transpile transpile ../../../target/wasm32-wasip2/release/conformance_signing_guest_ct.wasm --name conformance-signing-guest-ct --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../js/jco/webcrypto.js#*' --map 'polymorph:test/test-context@0.1.0=../../context.js' --map 'wasi:cli/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/cli.js#*' --map 'wasi:clocks/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/clocks.js#*' --map 'wasi:io/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/io.js#*' --map 'wasi:random/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/random.js#*' --map 'wasi:filesystem/*=../node_modules/@bytecodealliance/preview2-shim/lib/browser/filesystem.js#*' -o generated", + "transpile": "component-test-jco-transpile transpile ../../../target/wasm32-wasip2/release/conformance_guest_ct.wasm --name conformance-guest-ct -I async -o generated", + "transpile:signing": "component-test-jco-transpile transpile ../../../target/wasm32-wasip2/release/conformance_signing_guest_ct.wasm --name conformance-signing-guest-ct -I async -o generated", "run": "node --experimental-wasm-jspi runner.mjs", "run:browser": "node run-browser.mjs" }, "dependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", - "@bytecodealliance/preview2-shim": "^0.17.4", + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "@bytecodealliance/preview2-shim": "0.17.9", "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5", - "playwright-core": "^1.61.1" + "playwright-core": "1.62.1" } } diff --git a/conformance/driver-ct/jco/run-browser.mjs b/conformance/driver-ct/jco/run-browser.mjs index 59177cc3..040f6999 100644 --- a/conformance/driver-ct/jco/run-browser.mjs +++ b/conformance/driver-ct/jco/run-browser.mjs @@ -47,27 +47,24 @@ const SUITES = [ ]; // Resolve each suite's core-module list Node-side (the transpile emits -// one or two cores), so the page never fetches a missing file — a 404 -// would be tolerated but pollutes the console the driver mirrors. +// however many cores the composition needs), so the page never fetches +// a missing file — a 404 would be tolerated but pollutes the console +// the driver mirrors. +import { readdir } from "node:fs/promises"; for (const entry of SUITES) { - entry.cores = []; - for (const core of [`${entry.suite}.core.wasm`, `${entry.suite}.core2.wasm`]) { - try { - await access(new URL(`./generated/${core}`, import.meta.url)); - entry.cores.push(core); - } catch { - // Not emitted by this transpile. - } - } + const names = await readdir(new URL("./generated/", import.meta.url)); + entry.cores = names + .filter((n) => n.startsWith(`${entry.suite}.core`) && n.endsWith(".wasm")) + .sort(); } // The in-page harness: spawns a pool of module Web Workers // (worker-browser.mjs), each running one shard of a suite's case loop -// with its own instance of the transpiled suite (whose polymorph:webcrypto -// imports resolve to js/jco/webcrypto.js — the browser-first host, -// feature-detecting per call — and whose wasi imports resolve to -// relative paths into the preview2-shim browser build, mapped at -// transpile time: module workers cannot see a page's import map). +// with its own instance of the transpiled suite, instantiated with +// imports bound at run time (host-imports.mjs: js/jco/webcrypto.js — +// the browser-first host, feature-detecting per call — the driver's +// test-context, and the preview2-shim browser build, all by relative +// path: module workers cannot see a page's import map). // Rows come back tagged with their suite-order index and are re-sorted // before reporting. // diff --git a/conformance/driver-ct/jco/worker-browser.mjs b/conformance/driver-ct/jco/worker-browser.mjs index e2543085..c28a1b80 100644 --- a/conformance/driver-ct/jco/worker-browser.mjs +++ b/conformance/driver-ct/jco/worker-browser.mjs @@ -1,11 +1,18 @@ // The Web Worker half of the jco-browser driver (run-browser.mjs): its -// own instance of the transpiled suite runs one shard of the case loop -// (harness.mjs — module workers cannot see the page's import map, which -// is why the transpile maps the wasi shim to relative paths), streaming -// each results-JSONL event back with its suite-order index, then the -// shard's counts. The browser counterpart of worker-node.mjs. +// own instance of the transpiled suite runs one shard of the case loop, +// streaming each results-JSONL event back with its suite-order index, +// then the shard's counts. The browser counterpart of worker-node.mjs. +// Relative specifiers throughout: module workers cannot see the page's +// import map, so the wasi shim (browser build), the harness core, and +// the host module all resolve by path. import { inventoryLookup, runCases } from "./node_modules/@polymorph/component-test-js/js/viewer/harness.mjs"; +import * as cli from "./node_modules/@bytecodealliance/preview2-shim/lib/browser/cli.js"; +import * as clocks from "./node_modules/@bytecodealliance/preview2-shim/lib/browser/clocks.js"; +import * as io from "./node_modules/@bytecodealliance/preview2-shim/lib/browser/io.js"; +import * as random from "./node_modules/@bytecodealliance/preview2-shim/lib/browser/random.js"; +import * as filesystem from "./node_modules/@bytecodealliance/preview2-shim/lib/browser/filesystem.js"; import { Context } from "../context.js"; +import { instantiateSuite } from "./host-imports.mjs"; // A rejection escaping the awaited chain (e.g. a platform quirk // surfacing through the transpiled guest's async plumbing) would @@ -19,14 +26,22 @@ self.onunhandledrejection = (event) => { self.onmessage = async ({ data }) => { const { suite, missing, cores, shard } = data; try { - const coreModules = []; + const coreBytes = []; + const modules = new Map(); for (const core of cores) { const res = await fetch(new URL(`./generated/${core}`, import.meta.url)); if (!res.ok) throw new Error(`fetching ${core}: ${res.status}`); - coreModules.push(new Uint8Array(await res.arrayBuffer())); + const bytes = new Uint8Array(await res.arrayBuffer()); + coreBytes.push(bytes); + modules.set(core, await WebAssembly.compile(bytes)); } - const tagsOf = inventoryLookup(coreModules); - const { tests } = await import(`./generated/${suite}.js`); + const tagsOf = inventoryLookup(coreBytes); + const { instantiate } = await import(`./generated/${suite}.js`); + const tests = await instantiateSuite({ + instantiate, + modules, + wasi: { cli, clocks, io, random, filesystem }, + }); const counts = await runCases({ cases: await tests.all(), Context, diff --git a/conformance/driver-ct/jco/worker-node.mjs b/conformance/driver-ct/jco/worker-node.mjs index 81f17773..d2b87172 100644 --- a/conformance/driver-ct/jco/worker-node.mjs +++ b/conformance/driver-ct/jco/worker-node.mjs @@ -4,23 +4,32 @@ // suite-order index so the parent can restore suite order — back through // `parentPort`, then reporting the shard's counts. Workers inherit the // parent's execArgv, so `--experimental-wasm-jspi` carries over. -import { readFile } from "node:fs/promises"; +import { readFile, readdir } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; import { parentPort, workerData } from "node:worker_threads"; +import { cli, clocks, io, random, filesystem } from "@bytecodealliance/preview2-shim"; import { inventoryLookup, runCases } from "@polymorph/component-test-js/harness"; import { Context } from "../context.js"; +import { instantiateSuite } from "./host-imports.mjs"; const { suite, missing, only, shard } = workerData; -const coreModules = []; -for (const core of [`${suite}.core.wasm`, `${suite}.core2.wasm`]) { - try { - coreModules.push(new Uint8Array(await readFile(new URL(`./generated/${core}`, import.meta.url)))); - } catch { - continue; - } +const generatedDir = new URL("./generated/", import.meta.url); +const coreBytes = []; +const modules = new Map(); +for (const name of (await readdir(fileURLToPath(generatedDir))).sort()) { + if (!name.startsWith(`${suite}.core`) || !name.endsWith(".wasm")) continue; + const bytes = new Uint8Array(await readFile(new URL(`./generated/${name}`, import.meta.url))); + coreBytes.push(bytes); + modules.set(name, await WebAssembly.compile(bytes)); } -const tagsOf = inventoryLookup(coreModules); -const { tests } = await import(`./generated/${suite}.js`); +const tagsOf = inventoryLookup(coreBytes); +const { instantiate } = await import(`./generated/${suite}.js`); +const tests = await instantiateSuite({ + instantiate, + modules, + wasi: { cli, clocks, io, random, filesystem }, +}); const counts = await runCases({ cases: await tests.all(), diff --git a/conformance/driver-ct/justfile b/conformance/driver-ct/justfile index 71fa3ed9..814ae0bb 100644 --- a/conformance/driver-ct/justfile +++ b/conformance/driver-ct/justfile @@ -83,7 +83,11 @@ _ct-tools: # generator. Bump both together: the rev in Cargo.toml + `npm # install --save-exact github:polymorph-components/polymorph-test#` in jco/. "{{ct-tools}}/bin/component-test" pins --cargo-lock "{{root}}/Cargo.lock" \ - --js-lock jco/package-lock.json --expect "$rev" > /dev/null + --js-lock jco/package-lock.json \ + --js-lock ../../examples/jco-demo/package-lock.json \ + --js-lock ../../js/jco/package-lock.json \ + --js-lock ../../js/componentize/wpt/parity/package-lock.json \ + --expect "$rev" > /dev/null # Enumerate each suite's full case census (ct-driver instantiates the # suite against the wasmtime host) into the `lock --leaves` inputs: the @@ -184,8 +188,10 @@ _jco-prepared: build # cwd but not from jco/ — and not always relative: module-from-root # invocation resolves `root` absolute (justfile.shared.just), while # in-directory invocation leaves it `../..`. Never re-anchor these - # variables with a textual prefix. - stamp=$(cat "{{guest}}" "{{signing}}" | sha256sum | cut -d' ' -f1) + # variables with a textual prefix. The package.json rides in the + # stamp: the transpile flags and the pinned transpiler both live + # there, and either changing must invalidate the generated tree. + stamp=$(cat "{{guest}}" "{{signing}}" jco/package.json | sha256sum | cut -d' ' -f1) cd jco exec 9> .prepare.lock flock 9 diff --git a/examples/jco-demo/package-lock.json b/examples/jco-demo/package-lock.json index 03066f35..fa276b9e 100644 --- a/examples/jco-demo/package-lock.json +++ b/examples/jco-demo/package-lock.json @@ -8,39 +8,47 @@ "name": "@polymorph/webcrypto-jco-demo", "version": "0.1.0", "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2" + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5" } }, "node_modules/@bytecodealliance/jco-transpile": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@bytecodealliance/jco-transpile/-/jco-transpile-0.5.2.tgz", - "integrity": "sha512-zXrPtrJkn5jq6MBAOLZUlPnmgdLa8RDER9T4gCclgbAPHWIZSWEjDzv4MrrO3sz6Tvh1eJK1ou2SrrNnJxdTfA==", + "version": "0.6.2", + "resolved": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "integrity": "sha512-9lvxYCxw7G4kD5lA3iGXYphPeccegoHqMT1m3I1O8XO4BVAaYrOuDxmZg67rrs3VMOXHBTt2uVXnl/SeYUCARQ==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { - "@bytecodealliance/preview2-shim": "^0.19.0", - "@bytecodealliance/preview3-shim": "^0.2.0", + "@bytecodealliance/preview2-shim": "^0.20.1", + "@bytecodealliance/preview3-shim": "^0.2.2", "binaryen": "^130.0.0", "oxc-minify": "^0.136.0" } }, "node_modules/@bytecodealliance/preview2-shim": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.19.0.tgz", - "integrity": "sha512-OSUlcM62fXLuzYP0DjBn/SwjKMZ/y/2PMXZ8v5cRiiFcroauntlPRl/oCnx0xYjYiqdYvE0PY50VCNutll0xOw==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.20.1.tgz", + "integrity": "sha512-gvQP6DWfeQIYKHBb+nPykku1DRtfXQjM685+e75YYr8ZF2ZxisE4+nWrpwf84+NaWJRfuL5HvoLKQFbup1iwkQ==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/preview3-shim": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.0.tgz", - "integrity": "sha512-44zSPzlOyF/hWTNK32odet+Jt/nVXgtboSvgvZqMEJKP9GuM+Ln3L6js31WAakDEe+TYgG4iNEBk39rByINZkA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.2.tgz", + "integrity": "sha512-9z4tjc+qixUhyeYo3ycoT4cZ9yIlUfkowJxGoKeMfbRd8zT3uvIr1YNfXyU0I93GPu+gdaTVsr8iMr55utEJSA==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { "@bytecodealliance/preview2-shim": "^0.19.0" } }, + "node_modules/@bytecodealliance/preview3-shim/node_modules/@bytecodealliance/preview2-shim": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.19.0.tgz", + "integrity": "sha512-OSUlcM62fXLuzYP0DjBn/SwjKMZ/y/2PMXZ8v5cRiiFcroauntlPRl/oCnx0xYjYiqdYvE0PY50VCNutll0xOw==", + "dev": true, + "license": "(Apache-2.0 WITH LLVM-exception)" + }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", @@ -463,6 +471,16 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@polymorph/component-test-js": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/polymorph-components/polymorph-test.git#76c094576076284c1a7d0ea1b7bb724e9e7384a5", + "integrity": "sha512-nvkB8aRSsA/B+eQnIrH92Uj1v4qmzj5a9N7lylDhDMqc97IeWPox4eZqTtSX6mxl0lsiotZeU9z0u/BcLBVuqg==", + "dev": true, + "license": "Apache-2.0 WITH LLVM-exception", + "bin": { + "component-test-jco-transpile": "js/jco-transpile.mjs" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", diff --git a/examples/jco-demo/package.json b/examples/jco-demo/package.json index 04a87ec9..a2998532 100644 --- a/examples/jco-demo/package.json +++ b/examples/jco-demo/package.json @@ -6,11 +6,12 @@ "description": "crypto-demo driver for the jco host (js/jco/webcrypto.js): transpiles the guest with jco and runs it on Node 24+.", "scripts": { "build:component": "cd ../.. && just demo::build-component", - "transpile": "node ../../scripts/jco-transpile.mjs transpile ../crypto-demo/build/crypto-demo.component.wasm --name crypto-demo --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../js/jco/webcrypto.js#*' -o generated", + "transpile": "component-test-jco-transpile transpile ../crypto-demo/build/crypto-demo.component.wasm --name crypto-demo --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../js/jco/webcrypto.js#*' -o generated", "start": "node --experimental-wasm-jspi src/run.mjs", "test": "node --experimental-wasm-jspi src/run.mjs" }, "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2" + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5" } } diff --git a/js/componentize/wpt/parity/package-lock.json b/js/componentize/wpt/parity/package-lock.json index d600aa83..7460f749 100644 --- a/js/componentize/wpt/parity/package-lock.json +++ b/js/componentize/wpt/parity/package-lock.json @@ -8,24 +8,32 @@ "name": "@polymorph/wpt-parity-webcrypto", "version": "0.1.0", "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", "@bytecodealliance/preview2-shim": "^0.19.0", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5", "playwright-core": "^1.62.1" } }, "node_modules/@bytecodealliance/jco-transpile": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@bytecodealliance/jco-transpile/-/jco-transpile-0.5.2.tgz", - "integrity": "sha512-zXrPtrJkn5jq6MBAOLZUlPnmgdLa8RDER9T4gCclgbAPHWIZSWEjDzv4MrrO3sz6Tvh1eJK1ou2SrrNnJxdTfA==", + "version": "0.6.2", + "resolved": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", + "integrity": "sha512-9lvxYCxw7G4kD5lA3iGXYphPeccegoHqMT1m3I1O8XO4BVAaYrOuDxmZg67rrs3VMOXHBTt2uVXnl/SeYUCARQ==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { - "@bytecodealliance/preview2-shim": "^0.19.0", - "@bytecodealliance/preview3-shim": "^0.2.0", + "@bytecodealliance/preview2-shim": "^0.20.1", + "@bytecodealliance/preview3-shim": "^0.2.2", "binaryen": "^130.0.0", "oxc-minify": "^0.136.0" } }, + "node_modules/@bytecodealliance/jco-transpile/node_modules/@bytecodealliance/preview2-shim": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.20.1.tgz", + "integrity": "sha512-gvQP6DWfeQIYKHBb+nPykku1DRtfXQjM685+e75YYr8ZF2ZxisE4+nWrpwf84+NaWJRfuL5HvoLKQFbup1iwkQ==", + "dev": true, + "license": "(Apache-2.0 WITH LLVM-exception)" + }, "node_modules/@bytecodealliance/preview2-shim": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.19.0.tgz", @@ -34,9 +42,9 @@ "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/preview3-shim": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.0.tgz", - "integrity": "sha512-44zSPzlOyF/hWTNK32odet+Jt/nVXgtboSvgvZqMEJKP9GuM+Ln3L6js31WAakDEe+TYgG4iNEBk39rByINZkA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.2.tgz", + "integrity": "sha512-9z4tjc+qixUhyeYo3ycoT4cZ9yIlUfkowJxGoKeMfbRd8zT3uvIr1YNfXyU0I93GPu+gdaTVsr8iMr55utEJSA==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { @@ -465,6 +473,16 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@polymorph/component-test-js": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/polymorph-components/polymorph-test.git#76c094576076284c1a7d0ea1b7bb724e9e7384a5", + "integrity": "sha512-nvkB8aRSsA/B+eQnIrH92Uj1v4qmzj5a9N7lylDhDMqc97IeWPox4eZqTtSX6mxl0lsiotZeU9z0u/BcLBVuqg==", + "dev": true, + "license": "Apache-2.0 WITH LLVM-exception", + "bin": { + "component-test-jco-transpile": "js/jco-transpile.mjs" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", diff --git a/js/componentize/wpt/parity/package.json b/js/componentize/wpt/parity/package.json index 7ac8fbf3..57c585b7 100644 --- a/js/componentize/wpt/parity/package.json +++ b/js/componentize/wpt/parity/package.json @@ -5,8 +5,8 @@ "type": "module", "description": "WPT parity gate: the vendored WebCryptoAPI suites run directly against the platform's crypto.subtle (baseline) and through the componentized shim transpiled by jco against webcrypto-jco (round trip); compare.mjs holds the round trip to the baseline's pass set.", "scripts": { - "transpile": "node ../../../../scripts/jco-transpile.mjs transpile ../build/parity-runner.component.wasm --name parity-runner --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../../js/jco/webcrypto.js#*' --map 'wpt:parity/reporter@0.1.0=../../reporter.js#reporter' -o generated", - "transpile:web": "node ../../../../scripts/jco-transpile.mjs transpile ../build/parity-runner.component.wasm --name parity-runner --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../../js/jco/webcrypto.js#*' --map 'wpt:parity/reporter@0.1.0=../../reporter.js#reporter' --map 'wasi:cli/*=../../web/preview2-shim/cli.js#*' --map 'wasi:clocks/*=../../web/preview2-shim/clocks.js#*' --map 'wasi:filesystem/*=../../web/preview2-shim/filesystem.js#*' --map 'wasi:io/*=../../web/preview2-shim/io.js#*' --map 'wasi:random/*=../../web/preview2-shim/random.js#*' --map 'wasi:sockets/*=../../web/sockets-stub.js#*' -o generated-web", + "transpile": "component-test-jco-transpile transpile ../build/parity-runner.component.wasm --name parity-runner --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../../js/jco/webcrypto.js#*' --map 'wpt:parity/reporter@0.1.0=../../reporter.js#reporter' -o generated", + "transpile:web": "component-test-jco-transpile transpile ../build/parity-runner.component.wasm --name parity-runner --async-mode jspi --map 'polymorph:webcrypto/*@0.1.0=../../../../../js/jco/webcrypto.js#*' --map 'wpt:parity/reporter@0.1.0=../../reporter.js#reporter' --map 'wasi:cli/*=../../web/preview2-shim/cli.js#*' --map 'wasi:clocks/*=../../web/preview2-shim/clocks.js#*' --map 'wasi:filesystem/*=../../web/preview2-shim/filesystem.js#*' --map 'wasi:io/*=../../web/preview2-shim/io.js#*' --map 'wasi:random/*=../../web/preview2-shim/random.js#*' --map 'wasi:sockets/*=../../web/sockets-stub.js#*' -o generated-web", "baseline": "node baseline.mjs", "roundtrip": "node --experimental-wasm-jspi roundtrip.mjs", "run:firefox": "node run-browser.mjs --engine firefox", @@ -15,8 +15,9 @@ "compare": "node compare.mjs" }, "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", + "@bytecodealliance/jco-transpile": "https://github.com/lann/jco/releases/download/jco-transpile-0.6.2-fa6990d/bytecodealliance-jco-transpile-0.6.2.tgz", "@bytecodealliance/preview2-shim": "^0.19.0", - "playwright-core": "^1.62.1" + "playwright-core": "^1.62.1", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5" } } diff --git a/js/jco/package-lock.json b/js/jco/package-lock.json index e858453d..bbdb3479 100644 --- a/js/jco/package-lock.json +++ b/js/jco/package-lock.json @@ -8,7 +8,8 @@ "name": "@polymorph/webcrypto-jco", "version": "0.1.0", "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", + "@bytecodealliance/jco-transpile": "0.5.2", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5", "typescript": "^5.9.3" } }, @@ -33,9 +34,9 @@ "license": "(Apache-2.0 WITH LLVM-exception)" }, "node_modules/@bytecodealliance/preview3-shim": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.0.tgz", - "integrity": "sha512-44zSPzlOyF/hWTNK32odet+Jt/nVXgtboSvgvZqMEJKP9GuM+Ln3L6js31WAakDEe+TYgG4iNEBk39rByINZkA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview3-shim/-/preview3-shim-0.2.2.tgz", + "integrity": "sha512-9z4tjc+qixUhyeYo3ycoT4cZ9yIlUfkowJxGoKeMfbRd8zT3uvIr1YNfXyU0I93GPu+gdaTVsr8iMr55utEJSA==", "dev": true, "license": "(Apache-2.0 WITH LLVM-exception)", "dependencies": { @@ -464,6 +465,16 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@polymorph/component-test-js": { + "version": "0.2.0", + "resolved": "git+ssh://git@github.com/polymorph-components/polymorph-test.git#76c094576076284c1a7d0ea1b7bb724e9e7384a5", + "integrity": "sha512-nvkB8aRSsA/B+eQnIrH92Uj1v4qmzj5a9N7lylDhDMqc97IeWPox4eZqTtSX6mxl0lsiotZeU9z0u/BcLBVuqg==", + "dev": true, + "license": "Apache-2.0 WITH LLVM-exception", + "bin": { + "component-test-jco-transpile": "js/jco-transpile.mjs" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", diff --git a/js/jco/package.json b/js/jco/package.json index b9691c0a..c25c157b 100644 --- a/js/jco/package.json +++ b/js/jco/package.json @@ -6,12 +6,13 @@ "description": "Browser-compatible host for polymorph:webcrypto over the Web Crypto API. No dependencies; map the polymorph:webcrypto imports to webcrypto.js when transpiling with jco.", "exports": "./webcrypto.js", "scripts": { - "types": "node ../../scripts/jco-transpile.mjs types wit --world-name imports --async-mode jspi -o generated --feature sha1-checked --feature rsa-sign --feature rsa-oaep-decrypt", + "types": "component-test-jco-transpile types wit --world-name imports --async-mode jspi -o generated --feature sha1-checked --feature rsa-sign --feature rsa-oaep-decrypt", "typecheck": "npm run types && tsc --noEmit -p tsconfig.json", "test": "node --test 'test/*.test.js'" }, "devDependencies": { - "@bytecodealliance/jco-transpile": "^0.5.2", - "typescript": "^5.9.3" + "@bytecodealliance/jco-transpile": "0.5.2", + "typescript": "^5.9.3", + "@polymorph/component-test-js": "github:polymorph-components/polymorph-test#76c094576076284c1a7d0ea1b7bb724e9e7384a5" } } diff --git a/scripts/jco-transpile.mjs b/scripts/jco-transpile.mjs deleted file mode 100644 index d969a2e0..00000000 --- a/scripts/jco-transpile.mjs +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node -// Thin CLI over @bytecodealliance/jco-transpile, covering the two jco -// commands this repository uses: `transpile` (component to ES module) and -// `types` (host-side type definitions for a WIT world). jco-transpile is -// the transpilation half of jco, published without the componentization -// toolchain (componentize-js, weval) that the full jco CLI drags in and -// nothing here runs. -// -// The library is resolved from the invoking package's node_modules — each -// npm tree pins its own toolchain version — so this script must be run -// with the package directory as the working directory, which is how every -// package.json script invokes it. -// -// The option spellings match the jco CLI's, and the library applies the -// same defaults the CLI did (name derivation, the wasi-shim map entries, -// output-path prefixing), so the generated trees are bit-identical to -// `jco transpile` / `jco types` output for these invocations. - -import { createRequire } from "node:module"; -import { join } from "node:path"; -import { pathToFileURL } from "node:url"; -import { parseArgs } from "node:util"; - -const require = createRequire(join(process.cwd(), "package.json")); -const { transpile, generateHostTypes, writeFiles } = await import( - pathToFileURL(require.resolve("@bytecodealliance/jco-transpile")).href -); - -const [command, path, ...rest] = process.argv.slice(2); -const { values } = parseArgs({ - args: rest, - options: { - name: { type: "string" }, - "async-mode": { type: "string" }, - map: { type: "string", multiple: true }, - "world-name": { type: "string" }, - "out-dir": { type: "string", short: "o" }, - // `types` only: WIT `@unstable` gates to enable, like `jco types - // --feature` (repeatable). - feature: { type: "string", multiple: true }, - }, -}); - -switch (command) { - case "transpile": { - const map = Object.fromEntries( - (values.map ?? []).map((entry) => { - const eq = entry.indexOf("="); - if (eq === -1) { - throw new Error(`--map entry has no '=': ${entry}`); - } - return [entry.slice(0, eq), entry.slice(eq + 1)]; - }), - ); - const { files } = await transpile(path, { - name: values.name, - asyncMode: values["async-mode"], - map, - outDir: values["out-dir"], - }); - await writeFiles(files); - break; - } - case "types": { - const files = await generateHostTypes(path, { - worldName: values["world-name"], - asyncMode: values["async-mode"], - outDir: values["out-dir"], - features: values.feature, - }); - await writeFiles(files); - break; - } - default: - throw new Error(`unknown command: ${command} (expected transpile or types)`); -}