diff --git a/.changeset/conformance-cli-optional-paja.md b/.changeset/conformance-cli-optional-paja.md new file mode 100644 index 00000000..5469288a --- /dev/null +++ b/.changeset/conformance-cli-optional-paja.md @@ -0,0 +1,5 @@ +--- +"@napplet/conformance-cli": minor +--- + +Make `@kehto/paja` an optional peer dependency, loaded only for local `screenshot` previews. Installing the CLI no longer pulls in a second, older `@napplet/core` / `@napplet/nap` generation through Paja 0.21.x. Local directory previews now need a compatible `@kehto/paja` installed next to the CLI and fail with an install hint when it's missing; `screenshot ` and conformance runs are unchanged. diff --git a/.planning/STATE.md b/.planning/STATE.md index f8d2d683..1d2b12c8 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -735,6 +735,7 @@ Surfaced by research (informational — each belongs to a specific phase plan): | # | Description | Date | Commit | Directory | |---|-------------|------|--------|-----------| +| 261110-g3f | Make @kehto/paja an optional conformance-cli peer (web#324) | 2026-11-10 | 26ddb4d5 | [261110-g3f](./quick/261110-g3f-conformance-cli-optional-paja-peer-324/) | | 261104-kdn | Merge main into PR #280 (NAP-NAVIGATION) and resolve conflicts | 2026-11-04 | 5d7d2116 | [261104-kdn](./quick/261104-kdn-resolve-pr-280-merge-conflicts-with-main/) | | 261104-iwc | Try the next Blossom auth token after a gateway error (web#269) | 2026-11-04 | 26e2e391 | [261104-iwc](./quick/261104-iwc-fix-blossom-upload-auth-fallback-masked-/) | | 261031-hbe | Schedule reviewed NAP proposal metadata refreshes in PR #288 | 2026-10-31 | c72dd849 | [261031-hbe](./quick/261031-hbe-automate-daily-nap-proposal-metadata-rev/) | diff --git a/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-PLAN.md b/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-PLAN.md new file mode 100644 index 00000000..0cee2cd9 --- /dev/null +++ b/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-PLAN.md @@ -0,0 +1,21 @@ +--- +quick_id: 261110-g3f +description: Make @kehto/paja an optional peer of @napplet/conformance-cli (napplet/web#324) +date: 2026-11-10 +--- + +# Make @kehto/paja an optional peer of conformance-cli + +Resolve napplet/web#324. conformance-cli pins core 0.40 / nap 0.39 but depends on Paja 0.21.x (peers core/nap <0.35). No Paja release accepts core 0.40 / nap 0.39 yet (newest 0.32.1 peers core 0.38 / nap 0.37), and Paja only backs local screenshot previews. The user chose to make it an optional peer. + +## Tasks + +1. Move `@kehto/paja` from `dependencies` to an optional `peerDependencies` entry and keep it as a devDependency for tests. +2. Load Paja through a dynamic import in `src/screenshot-preview.ts`. A missing module gives an install hint; other load errors are rethrown. +3. Unit-test both load paths. +4. Update the README and `apps/docs/packages/conformance-cli.md`, and add a minor changeset. +5. File the upstream Paja issue in kehto/web and link it. + +## Verification + +`pnpm build`, `pnpm type-check`, `pnpm -r test:unit`, aislop scan. diff --git a/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-SUMMARY.md b/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-SUMMARY.md new file mode 100644 index 00000000..59bc8dd7 --- /dev/null +++ b/.planning/quick/261110-g3f-conformance-cli-optional-paja-peer-324/261110-g3f-SUMMARY.md @@ -0,0 +1,16 @@ +--- +quick_id: 261110-g3f +status: complete +date: 2026-11-10 +commit: 26ddb4d5 +--- + +# Summary: optional @kehto/paja peer for conformance-cli + +- `@kehto/paja` is now an optional peer (`>=0.21.0`) plus a devDependency. `loadPaja()` imports it on demand and explains how to install it when `ERR_MODULE_NOT_FOUND` is raised. +- Tests: `screenshot-preview-missing.test.ts` covers the hint and the rethrow; the existing preview test still passes against the dev copy of Paja. +- Docs: the README and the docs page replace the stale "requires Paja 0.21.x" paragraph. +- Changeset: `@napplet/conformance-cli` minor. +- Upstream: kehto/web#416 asks for a Paja release that accepts core 0.40 / nap 0.39. +- Lockfile: only the conformance-cli importer entry changed. pnpm's re-resolution of security-lab's Kehto peers to the newly published core 0.40 was dropped as unrelated. +- Verification: build 13/13, type-check 21/21, all unit suites green, aislop 99/100 (known deepmerge advisory). diff --git a/apps/docs/packages/conformance-cli.md b/apps/docs/packages/conformance-cli.md index 7a43a80e..9c902ecd 100644 --- a/apps/docs/packages/conformance-cli.md +++ b/apps/docs/packages/conformance-cli.md @@ -57,7 +57,7 @@ source edits rebuild and re-check automatically. The browser runner also supports `npx @napplet/conformance-cli@0.3.3 screenshot [directory|preview-url] --output preview.png`, exposed as `napplet screenshot` by the standalone CLI. See [screenshots and application metadata](./cli.md#screenshots-and-zapstore-metadata) for browser setup, iframe selection, and publication options. -Conformance CLI 0.3.4 requires the published Paja 0.21.x line for GAMEPAD screenshots and compatibility with core/NAP 0.34.0. Paja 0.20.x is excluded because its peer range does not accept those core/NAP versions. Publish this package after its core/NAP dependencies; the standalone CLI runner update follows conformance CLI publication. +`@kehto/paja` is an optional peer dependency. Conformance runs and `screenshot ` don't use it, so installing the CLI never brings in a second, older core/NAP generation for Paja. Local previews (`screenshot` with a directory or a built `index.html`) need it. Install a Paja release whose peer range accepts the `@napplet/core` and `@napplet/nap` versions this CLI pins, or pass a preview URL. Until a Paja release accepts core 0.40 and NAP 0.39 ([kehto/web#416](https://github.com/kehto/web/issues/416)), pass a preview URL from a compatible shell. The standalone `napplet screenshot` command provisions its own pinned runner and is unaffected. Publish this package after its core/NAP dependencies; the standalone CLI runner update follows conformance CLI publication. ## Signed manifests and runtime bindings diff --git a/packages/conformance-cli/README.md b/packages/conformance-cli/README.md index ad2bb44a..a4d20083 100644 --- a/packages/conformance-cli/README.md +++ b/packages/conformance-cli/README.md @@ -65,13 +65,13 @@ Exit codes: `0` conformant, `1` non-conformant, `2` usage/runtime error. ## Capture a preview screenshot -The CLI also provides the browser runner for `napplet screenshot`. Run `npx @napplet/conformance-cli@0.3.3 screenshot --output preview.png` from a built project; it prefers `dist/index.html` and starts a temporary Kehto Paja preview automatically. Supply a project directory or an HTTP(S) shell URL to override the target. Use `--selector` to select one iframe, `--ready-selector` to wait for an element inside it, and `--width`, `--height`, or `--delay` to adjust the default 1200 × 750 capture after 1500 ms. Existing output files are preserved. Chromium is installed automatically on first use. Local previews use Paja's development identity and live relays, and close after capture or failure. +The CLI also provides the browser runner for `napplet screenshot`. Run `npx @napplet/conformance-cli@0.3.3 screenshot --output preview.png` from a built project; it prefers `dist/index.html` and starts a temporary Kehto Paja preview automatically when the optional `@kehto/paja` peer is installed (see below). Supply a project directory or an HTTP(S) shell URL to override the target. Use `--selector` to select one iframe, `--ready-selector` to wait for an element inside it, and `--width`, `--height`, or `--delay` to adjust the default 1200 × 750 capture after 1500 ms. Existing output files are preserved. Chromium is installed automatically on first use. Local previews use Paja's development identity and live relays, and close after capture or failure. ### GAMEPAD integration Controller artifacts use the engine’s GAMEPAD envelope validation and injected cached API. The reference mock reports unavailable controllers; validate live focus isolation in a compatible runtime. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). -Conformance CLI 0.3.4 requires the published Paja 0.21.x line for GAMEPAD screenshots and compatibility with core/NAP 0.34.0. Paja 0.20.x is excluded because its peer range does not accept those core/NAP versions. Publish this package after its core/NAP dependencies; the standalone CLI runner update follows conformance CLI publication. +`@kehto/paja` is an optional peer dependency. Conformance runs and `screenshot ` don't use it, so installing the CLI never brings in a second, older core/NAP generation for Paja. Local previews (`screenshot` with a directory or a built `index.html`) need it. Install a Paja release whose peer range accepts the `@napplet/core` and `@napplet/nap` versions this CLI pins, or pass a preview URL. Until a Paja release accepts core 0.40 and NAP 0.39 ([kehto/web#416](https://github.com/kehto/web/issues/416)), pass a preview URL from a compatible shell. The standalone `napplet screenshot` command provisions its own pinned runner and is unaffected. Publish this package after its core/NAP dependencies; the standalone CLI runner update follows conformance CLI publication. ## Signed manifests and runtime bindings diff --git a/packages/conformance-cli/package.json b/packages/conformance-cli/package.json index 8dea5a86..f3388b8a 100644 --- a/packages/conformance-cli/package.json +++ b/packages/conformance-cli/package.json @@ -14,7 +14,6 @@ "access": "public" }, "dependencies": { - "@kehto/paja": ">=0.21.0 <0.22.0", "@napplet/conformance": "workspace:^", "@napplet/core": "workspace:*", "@napplet/nap": "workspace:*", @@ -22,7 +21,16 @@ "nostr-tools": "^2.23.3", "playwright": "^1.59.1" }, + "peerDependencies": { + "@kehto/paja": ">=0.21.0" + }, + "peerDependenciesMeta": { + "@kehto/paja": { + "optional": true + } + }, "devDependencies": { + "@kehto/paja": ">=0.21.0 <0.22.0", "@napplet/conformance-web": "workspace:*", "@types/node": "^22.15.0", "tsup": "^8.5.0", diff --git a/packages/conformance-cli/src/screenshot-preview-missing.test.ts b/packages/conformance-cli/src/screenshot-preview-missing.test.ts new file mode 100644 index 00000000..43ee8877 --- /dev/null +++ b/packages/conformance-cli/src/screenshot-preview-missing.test.ts @@ -0,0 +1,14 @@ +import { expect, it } from 'vitest'; +import { loadPaja } from './screenshot-preview.js'; + +it('explains the optional Paja peer when it is not installed', async () => { + const missing = Object.assign(new Error("Cannot find package '@kehto/paja'"), { code: 'ERR_MODULE_NOT_FOUND' }); + await expect(loadPaja(() => Promise.reject(missing))).rejects.toThrow( + /optional peer @kehto\/paja.*pass a preview URL/, + ); +}); + +it('rethrows other Paja load failures unchanged', async () => { + const broken = new SyntaxError('bad export'); + await expect(loadPaja(() => Promise.reject(broken))).rejects.toBe(broken); +}); diff --git a/packages/conformance-cli/src/screenshot-preview.ts b/packages/conformance-cli/src/screenshot-preview.ts index af9a95ab..7402ac73 100644 --- a/packages/conformance-cli/src/screenshot-preview.ts +++ b/packages/conformance-cli/src/screenshot-preview.ts @@ -3,9 +3,32 @@ import { createServer } from 'node:http'; import type { AddressInfo } from 'node:net'; import { readFile, realpath } from 'node:fs/promises'; import { dirname, resolve, sep } from 'node:path'; -import { startPajaServer } from '@kehto/paja'; import { sendFile, setCors } from './static.js'; +type StartPajaServer = typeof import('@kehto/paja').startPajaServer; + +/** + * Load Paja on demand. It is an optional peer so installing this CLI never + * drags in a second core/NAP generation (napplet/web#324); only local + * screenshot previews need it. + * @param load Module loader; tests substitute a failing one. + * @returns Paja's server entry point. + * @example const start = await loadPaja(); + */ +export async function loadPaja( + load: () => Promise<{ startPajaServer: StartPajaServer }> = () => import('@kehto/paja'), +): Promise { + try { + return (await load()).startPajaServer; + } catch (error) { + if ((error as { code?: string }).code !== 'ERR_MODULE_NOT_FOUND') throw error; + throw new Error( + 'Local screenshot previews need the optional peer @kehto/paja. Install a release whose peers accept your @napplet/core and @napplet/nap versions, or pass a preview URL instead of a directory.', + { cause: error }, + ); + } +} + /** * Host a built artifact in Paja on ephemeral loopback ports. * @param artifact Exact index.html selected by deployment. @@ -13,6 +36,7 @@ import { sendFile, setCors } from './static.js'; * @example const preview = await startScreenshotPreview('/project/dist/index.html'); */ export async function startScreenshotPreview(artifact: string): Promise<{ url: string; close(): Promise }> { + const startPajaServer = await loadPaja(); const index = await realpath(artifact); const root = dirname(index); const html = await readFile(index); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6c16890..a3112984 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -243,9 +243,6 @@ importers: packages/conformance-cli: dependencies: - '@kehto/paja': - specifier: '>=0.21.0 <0.22.0' - version: 0.21.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@napplet/conformance': specifier: workspace:^ version: link:../conformance @@ -265,6 +262,9 @@ importers: specifier: ^1.59.1 version: 1.61.0 devDependencies: + '@kehto/paja': + specifier: '>=0.21.0 <0.22.0' + version: 0.21.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@napplet/conformance-web': specifier: workspace:* version: link:../../apps/conformance