Skip to content

fix(ui): give the wheel back after a <select> is used with the mouse … #117

fix(ui): give the wheel back after a <select> is used with the mouse …

fix(ui): give the wheel back after a <select> is used with the mouse … #117

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
# A superseded run on the same PR tells you nothing worth waiting for.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# package.json declares engines.node >=24, so prove it on the floor
# (24, Active LTS) as well as on Current, rather than only on whatever
# the dev happens to have. Keep the spread: a single version hides
# version-dependent bugs. The Node 20 runner previously caught a test
# that leaked an event-loop handle, which the newer runner exited past.
node: ["24", "26"]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
# Deliberately runs from a cold checkout with no build state. Every
# workspace typechecks against its dependencies' emitted `dist`, so a
# missing build step surfaces here as "cannot find module" in source that
# is perfectly correct. `tsc -b` walks the project references and builds
# them in order; this job is what keeps that wiring honest.
- name: Lint (typecheck, builds project references)
run: npm run lint
- name: Build
run: npm run build
- name: Test
run: npm test
# The MV3 service worker may not call dynamic `import()` (HTML spec; see
# w3c/ServiceWorker#1356), so background.js must stay a single
# self-contained bundle. Rollup silently emits extra chunks if the
# `codeSplitting: false` output option is lost in a future upgrade, and
# the failure would land at runtime in users' browsers rather than here.
- name: Assert MV3 worker is a single self-contained bundle
run: |
bundle=packages/extension/dist/background.js
test -f "$bundle" || { echo "::error::$bundle was not emitted"; exit 1; }
if grep -qE '\bimport\s*\(' "$bundle"; then
echo "::error::$bundle contains a dynamic import(); MV3 service workers cannot load it"
exit 1
fi
echo "OK: $bundle is a single bundle with no dynamic import()"
# `@openvtc/pnm-core/admin` is operator surface — granting authority at an
# agent, revoking it, destroying contexts. A wallet has no business
# shipping any of it, and the way it would arrive is someone importing it
# from the package root instead of the subpath. The task URIs are the
# tell: they only appear in a bundle that pulled the module in.
- name: Assert the wallet ships no agent-administration surface
run: |
for task in 'acl/grant/0.1' 'acl/revoke/0.1' 'acl/update/0.1' 'contexts/delete/1.0' 'keys/create/0.1' 'keys/sign/0.1' 'policy/upsert/0.2' 'device/wipe/0.1' 'config/patch/0.1' 'vta/did-templates/create/2.0' 'consent/approver-set/1.0' 'keys/import/0.1' 'did-management/did/delete/0.1' 'vta/services/enable/1.0' 'vta/services/disable/1.0' 'vta/credentials/issue/0.1' 'vta/credentials/revoke/0.1'; do
if grep -rlF "$task" packages/extension/dist/; then
echo "::error::the extension bundle contains $task — @openvtc/pnm-core/admin must not be reachable from the wallet (check for a root-barrel import)"
exit 1
fi
done
echo "OK: no admin task URIs in the extension bundle"
# Build the Chrome Web Store upload artefact from the dist/ that the
# Build step just produced (scripts/package.mjs re-stages it; it does
# not rebuild). Doing this every run means a submission is never the
# first time the packaging path is exercised.
- name: Package Web Store zip
working-directory: packages/extension
run: node scripts/package.mjs
# Two ways a package passes CI and then fails at upload, both silent:
#
# - a `key` field, which the Store's "+ New item" upload rejects
# outright (it issues its own key). `dist/` carries one on purpose so
# local unpacked installs hold a stable ID; only the zip must not.
# - a version that drifted from package.json, which the Store rejects
# as non-increasing against the last accepted upload.
- name: Assert Web Store zip is uploadable
working-directory: packages/extension
run: |
version=$(node -p "require('./package.json').version")
zip="release/vta-wallet-${version}.zip"
test -f "$zip" || { echo "::error::$zip was not produced"; exit 1; }
manifest=$(unzip -p "$zip" manifest.json)
if [ "$(echo "$manifest" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).key ?? ''")" != "" ]; then
echo "::error::$zip manifest contains a 'key' field; the Web Store rejects it on a new item"
exit 1
fi
got=$(echo "$manifest" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version")
if [ "$got" != "$version" ]; then
echo "::error::packaged manifest version $got != package.json version $version"
exit 1
fi
if [ "$(echo "$manifest" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).content_scripts ? 'yes' : ''")" = "yes" ]; then
echo "::error::manifest declares content_scripts; the page provider is registered dynamically for granted origins only (src/content-registration.ts). A static match would re-grant blanket host access and double-inject."
exit 1
fi
# The wallet writes nothing into the browser on a site's behalf, and
# that is the claim docs/web-store-review.md makes to a reviewer. It
# holds for exactly as long as nobody re-adds the permission, so
# assert it rather than trust it: `cookies` is the permission a
# reviewer looks hardest at, and its cost is an extended review.
if [ "$(echo "$manifest" | node -p "(JSON.parse(require('fs').readFileSync(0,'utf8')).permissions ?? []).includes('cookies') ? 'yes' : ''")" = "yes" ]; then
echo "::error::manifest requests the 'cookies' permission; the legacy password-site login that needed it was removed (see README 'The wallet writes nothing into your browser')."
exit 1
fi
# Checked against dist/, not src/: the built bundle has no comments,
# so prose *about* the removed path (README, code comments) doesn't
# trip it, while a real call site would.
if grep -rl "chrome\.cookies" dist/ >/dev/null 2>&1; then
echo "::error::the built bundle calls chrome.cookies; the wallet must not write to the cookie jar"
grep -rl "chrome\.cookies" dist/
exit 1
fi
test -f dist/content.js || { echo "::error::dist/content.js missing; chrome.scripting registers it by path"; exit 1; }
echo "OK: $zip is version $version, no 'key', no static content_scripts, no cookie access"
# One upload only — actions/upload-artifact errors on a duplicate name,
# and the zip is identical across the Node matrix.
- name: Upload Web Store zip
if: matrix.node == '26'
uses: actions/upload-artifact@v4
with:
name: vta-wallet-extension
path: packages/extension/release/*.zip
if-no-files-found: error