refactor(catalog): relocate to paged-introspect + publish via introsp… #23
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: publish-wasm | |
| # Builds the engine's wasm-bindgen surfaces and publishes them as | |
| # versioned npm packages that the (private) editor consumes across the | |
| # package boundary — "decision B" of the migration plan: the editor never | |
| # takes a Rust path dependency on the engine; it depends on these packages. | |
| # | |
| # PREREQUISITES before this can publish (one-time setup): | |
| # 1. An npm organisation `@paged-media` with publish access. | |
| # 2. A repo secret `NPM_TOKEN` (npm automation token, publish scope). | |
| # 3. Tag a release, e.g. `git tag v0.1.0 && git push --tags`. The package | |
| # version is derived from the tag. | |
| # 4. In the editor repo, point `packages/client` at the published | |
| # `@paged-media/canvas-wasm` (and devtools at `@paged-media/introspect-wasm`) | |
| # instead of the in-repo `build-wasm.sh` output. | |
| # | |
| # Until those exist, this runs the build steps but the publish step is a | |
| # no-op without NPM_TOKEN. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| workflow_dispatch: | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Pin the toolchain from rust-toolchain.toml (audit B21) — the | |
| # published wasm must be built with the same compiler the rest of | |
| # CI uses, not a drifting `@stable`. | |
| - name: resolve pinned toolchain | |
| id: pin | |
| run: echo "channel=$(bash scripts/rust-channel.sh)" >> "$GITHUB_OUTPUT" | |
| - uses: dtolnay/rust-toolchain@master | |
| with: | |
| toolchain: ${{ steps.pin.outputs.channel }} | |
| targets: wasm32-unknown-unknown | |
| - name: Install wasm-bindgen-cli (lockfile pin) + binaryen (pinned) | |
| run: | | |
| set -euo pipefail | |
| WB=$(awk '/^name = "wasm-bindgen"$/{getline; gsub(/version = "|"/,""); print; exit}' Cargo.lock) | |
| echo "wasm-bindgen pin: $WB" | |
| cargo install wasm-bindgen-cli --version "$WB" --locked | |
| # Do NOT use apt's binaryen: ubuntu-latest ships v116, which | |
| # miscompiles wasm-bindgen's reference-types tables ("Table.grow | |
| # failed" at runtime). Same pin as docs' pages.yml. | |
| BINARYEN=126 | |
| curl -fsSL -o /tmp/binaryen.tgz \ | |
| "https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN}/binaryen-version_${BINARYEN}-x86_64-linux.tar.gz" | |
| tar -xzf /tmp/binaryen.tgz -C /tmp | |
| echo "/tmp/binaryen-version_${BINARYEN}/bin" >> "$GITHUB_PATH" | |
| "/tmp/binaryen-version_${BINARYEN}/bin/wasm-opt" --version | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| registry-url: https://registry.npmjs.org | |
| - name: Resolve version from tag | |
| id: ver | |
| run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| # W0.9 dist-tag rider (protocol-governance §4): prerelease tags | |
| # (`v0.N.0-rc.x` — anything with a `-suffix`) publish under the | |
| # `next` dist-tag, never `latest`. A clean `0.N.P` publishes under | |
| # npm's default (`latest`). This sets a `dist-tag` output the | |
| # publish step appends as `--tag` only for prereleases. | |
| - name: Derive npm dist-tag from version | |
| id: disttag | |
| run: | | |
| set -euo pipefail | |
| V="${{ steps.ver.outputs.version }}" | |
| if [[ "$V" == *-* ]]; then | |
| echo "tag=next" >> "$GITHUB_OUTPUT" | |
| echo "prerelease version '$V' → npm dist-tag 'next' (not latest)" | |
| else | |
| echo "tag=latest" >> "$GITHUB_OUTPUT" | |
| echo "release version '$V' → npm dist-tag 'latest' (default)" | |
| fi | |
| - name: Build @paged-media/canvas-wasm | |
| run: | | |
| set -euo pipefail | |
| cargo build --release --target wasm32-unknown-unknown -p paged-canvas-wasm --features gpu | |
| OUT=dist/canvas-wasm | |
| mkdir -p "$OUT" | |
| wasm-bindgen target/wasm32-unknown-unknown/release/paged_canvas_wasm.wasm \ | |
| --target web --out-dir "$OUT" | |
| wasm-opt -Oz "$OUT/paged_canvas_wasm_bg.wasm" -o "$OUT/paged_canvas_wasm_bg.wasm.opt" \ | |
| && mv "$OUT/paged_canvas_wasm_bg.wasm.opt" "$OUT/paged_canvas_wasm_bg.wasm" | |
| node -e "require('fs').writeFileSync('$OUT/package.json', JSON.stringify({ | |
| name: '@paged-media/canvas-wasm', | |
| version: '${{ steps.ver.outputs.version }}', | |
| type: 'module', | |
| main: 'paged_canvas_wasm.js', | |
| types: 'paged_canvas_wasm.d.ts', | |
| files: ['paged_canvas_wasm.js','paged_canvas_wasm_bg.wasm','paged_canvas_wasm.d.ts'], | |
| license: 'MPL-2.0 OR LicenseRef-PMEL' | |
| }, null, 2))" | |
| - name: Build @paged-media/introspect-wasm | |
| run: | | |
| set -euo pipefail | |
| cargo build --release --target wasm32-unknown-unknown -p paged-introspect-wasm | |
| OUT=dist/introspect-wasm | |
| mkdir -p "$OUT" | |
| wasm-bindgen target/wasm32-unknown-unknown/release/paged_introspect_wasm.wasm \ | |
| --target web --out-dir "$OUT" | |
| wasm-opt -Oz "$OUT/paged_introspect_wasm_bg.wasm" -o "$OUT/paged_introspect_wasm_bg.wasm.opt" \ | |
| && mv "$OUT/paged_introspect_wasm_bg.wasm.opt" "$OUT/paged_introspect_wasm_bg.wasm" | |
| # The static capability catalog (ADR 019) ships alongside the wasm so | |
| # build-time consumers (plugin SDK sync, state ingest) read it without | |
| # booting wasm; describeCatalog() returns the same data at runtime. | |
| cp crates/paged-introspect/catalog.json "$OUT/catalog.json" | |
| node -e "require('fs').writeFileSync('$OUT/package.json', JSON.stringify({ | |
| name: '@paged-media/introspect-wasm', | |
| version: '${{ steps.ver.outputs.version }}', | |
| type: 'module', | |
| main: 'paged_introspect_wasm.js', | |
| types: 'paged_introspect_wasm.d.ts', | |
| files: ['paged_introspect_wasm.js','paged_introspect_wasm_bg.wasm','paged_introspect_wasm.d.ts','catalog.json'], | |
| license: 'MPL-2.0 OR LicenseRef-PMEL' | |
| }, null, 2))" | |
| - name: Subset audit — paged-sdk must not link editor crates | |
| run: | | |
| set -euo pipefail | |
| # "Sibling, not a shrunk app": the viewer/SDK wasm is | |
| # renderer-core only. Any of these in its dependency tree is | |
| # a boundary breach — fail loudly. | |
| TREE=$(cargo tree -p paged-sdk --target wasm32-unknown-unknown --features gpu -e normal) | |
| if echo "$TREE" | grep -E "paged-(mutate|canvas|script)"; then | |
| echo "::error::paged-sdk wasm links an editor crate"; exit 1 | |
| fi | |
| echo "subset audit OK — no paged-mutate/-canvas/-script in the tree" | |
| - name: Build @paged-media/sdk (paged-sdk WebGPU ViewerSession) | |
| run: | | |
| set -euo pipefail | |
| cargo build --release --target wasm32-unknown-unknown -p paged-sdk --features gpu | |
| OUT=dist/sdk | |
| mkdir -p "$OUT" | |
| wasm-bindgen target/wasm32-unknown-unknown/release/paged_sdk.wasm \ | |
| --target web --out-dir "$OUT" | |
| wasm-opt -Oz "$OUT/paged_sdk_bg.wasm" -o "$OUT/paged_sdk_bg.wasm.opt" \ | |
| && mv "$OUT/paged_sdk_bg.wasm.opt" "$OUT/paged_sdk_bg.wasm" | |
| # Size ratchet (§4.2): print opt + brotli sizes next to the | |
| # recorded baseline (2026-06-06: opt 7.20 MB, brotli 2.21 MB). | |
| brotli -k -q 11 "$OUT/paged_sdk_bg.wasm" | |
| ls -la "$OUT"/paged_sdk_bg.wasm* | |
| node -e "require('fs').writeFileSync('$OUT/package.json', JSON.stringify({ | |
| name: '@paged-media/sdk', | |
| version: '${{ steps.ver.outputs.version }}', | |
| type: 'module', | |
| main: 'paged_sdk.js', | |
| types: 'paged_sdk.d.ts', | |
| files: ['paged_sdk.js','paged_sdk_bg.wasm','paged_sdk.d.ts'], | |
| license: 'MPL-2.0 OR LicenseRef-PMEL' | |
| }, null, 2))" | |
| rm -f "$OUT/paged_sdk_bg.wasm.br" | |
| - name: Build @paged-media/idml-viewer (TS wrapper + bundled wasm) | |
| run: | | |
| set -euo pipefail | |
| cd web/idml-viewer | |
| npm install --no-fund --no-audit | |
| npm test | |
| npm run build | |
| mkdir -p wasm | |
| cp ../../dist/sdk/paged_sdk.js ../../dist/sdk/paged_sdk_bg.wasm ../../dist/sdk/paged_sdk.d.ts wasm/ | |
| npm pkg set version="${{ steps.ver.outputs.version }}" | |
| - name: Publish | |
| if: ${{ env.NPM_TOKEN != '' }} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| # Leading ./ is load-bearing: a bare `dist/canvas-wasm` is | |
| # parsed by npm as a GitHub owner/repo shorthand, not a path | |
| # (it tried git ls-remote github.com/dist/canvas-wasm.git). | |
| # | |
| # --tag from the dist-tag rider (W0.9): prereleases land on | |
| # `next`, releases on `latest`. Passing `--tag latest` | |
| # explicitly is a no-op vs npm's default, so the command is | |
| # uniform either way. | |
| TAG="${{ steps.disttag.outputs.tag }}" | |
| echo "publishing under npm dist-tag: $TAG" | |
| npm publish --access public --tag "$TAG" ./dist/canvas-wasm | |
| npm publish --access public --tag "$TAG" ./dist/introspect-wasm | |
| npm publish --access public --tag "$TAG" ./dist/sdk | |
| npm publish --access public --tag "$TAG" ./web/idml-viewer |