Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/conformance-cli-optional-paja.md
Original file line number Diff line number Diff line change
@@ -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 <preview-url>` and conformance runs are unchanged.
1 change: 1 addition & 0 deletions .planning/STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/) |
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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).
2 changes: 1 addition & 1 deletion apps/docs/packages/conformance-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <preview-url>` 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

Expand Down
4 changes: 2 additions & 2 deletions packages/conformance-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <preview-url>` 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

Expand Down
10 changes: 9 additions & 1 deletion packages/conformance-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@
"access": "public"
},
"dependencies": {
"@kehto/paja": ">=0.21.0 <0.22.0",
"@napplet/conformance": "workspace:^",
"@napplet/core": "workspace:*",
"@napplet/nap": "workspace:*",
"@napplet/shim": "workspace:*",
"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",
Expand Down
14 changes: 14 additions & 0 deletions packages/conformance-cli/src/screenshot-preview-missing.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
26 changes: 25 additions & 1 deletion packages/conformance-cli/src/screenshot-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,40 @@ 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<StartPajaServer> {
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.
* @returns Shell URL and an async cleanup handle for both servers.
* @example const preview = await startScreenshotPreview('/project/dist/index.html');
*/
export async function startScreenshotPreview(artifact: string): Promise<{ url: string; close(): Promise<void> }> {
const startPajaServer = await loadPaja();
const index = await realpath(artifact);
const root = dirname(index);
const html = await readFile(index);
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading