@napplet/conformance-cli 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/server.ts","../src/static.ts","../src/ui-server.ts","../src/scan.ts","../src/resolve-napplet.ts","../src/screenshot.ts","../src/screenshot-preview.ts","../src/manifest.ts"],"sourcesContent":["/**\n * @napplet/conformance-cli -- `napplet-conformance` entry point.\n *\n * Runs the @napplet/conformance engine against a napplet in real headless Chromium:\n *\n * 1. Resolve the napplet's built directory (or a remote `--url`).\n * 2. Serve it on loopback alongside a host harness page + the engine bundle.\n * 3. Drive Playwright Chromium to `bootAndCollect` inside the host page.\n * 4. Assemble a ConformanceContext on the node side (manifest HTML + static scan),\n * run the check catalog, print the chosen report, and set the exit code.\n *\n * Exit codes: `0` conformant, `1` non-conformant, `2` usage/runtime error.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile, stat } from 'node:fs/promises';\nimport { join, dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawn } from 'node:child_process';\nimport { chromium } from 'playwright';\nimport {\n buildContext,\n runConformance,\n report,\n type BootCollection,\n type ReporterFormat,\n} from '@napplet/conformance';\nimport { startHarnessServer } from './server.js';\nimport { startUiServer } from './ui-server.js';\nimport { scanForbiddenGlobals } from './scan.js';\nimport { resolveNappletDir } from './resolve-napplet.js';\nimport { captureScreenshot, parseScreenshotArgs } from './screenshot.js';\nimport { verifyManifestArtifact } from './manifest.js';\n\ninterface CliOptions {\n target?: string;\n url?: string;\n manifest?: string;\n reporter: ReporterFormat;\n out?: string;\n readyTimeoutMs: number;\n settleMs: number;\n runDegraded: boolean;\n allowSameOrigin: boolean;\n ui: boolean;\n port?: number;\n open: boolean;\n exec?: string;\n help: boolean;\n}\n\nconst HELP = `napplet-conformance — run NAP protocol conformance against a napplet\n\nUsage:\n napplet-conformance [dir] [options] # headless, one-shot, CI exit code\n napplet-conformance --ui [dir] [options] # open the live web runtime (like vitest --ui)\n napplet-conformance --url https://my.napplet/ [options]\n napplet-conformance screenshot [directory|preview-url] [--output preview.png]\n\nArguments:\n dir Built napplet directory (prefers ./dist/index.html, falls back to ./index.html)\n\nOptions:\n --url <url> Test a remotely-served napplet instead of a local dir (headless)\n --manifest <file> Signed NIP-5D event JSON; verify its signature and raw artifact hash before boot (headless)\n --reporter <fmt> pretty | json | junit (default: pretty)\n --out <file> Write the report to a file instead of stdout\n --ready-timeout <ms> Boot timeout waiting for iframe load (default: 5000)\n --settle <ms> Envelope-collection window after boot (default: 600)\n --no-degraded Skip the no-capability graceful-degradation pass\n --allow-same-origin Debug: also grant allow-same-origin (a conformant napplet must not need it)\n\nUI / watch mode (--ui):\n --port <n> Port for the UI server (default: random)\n --no-open Do not open a browser automatically\n --exec <cmd> Run a command (e.g. \"vite build --watch\") so edits rebuild the served dir;\n the watcher then re-runs conformance live on every change\n -h, --help Show this help\n\nExit codes (headless): 0 conformant, 1 non-conformant, 2 usage/runtime error.\n`;\n\nfunction parseArgs(argv: string[]): CliOptions {\n const opts: CliOptions = {\n reporter: 'pretty',\n readyTimeoutMs: 5000,\n settleMs: 600,\n runDegraded: true,\n allowSameOrigin: false,\n ui: false,\n open: true,\n help: false,\n };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const value = (): string => {\n const v = argv[i + 1];\n if (v === undefined || v.startsWith('--')) throw new Error(`Missing value for ${arg}`);\n i += 1;\n return v;\n };\n switch (arg) {\n case '-h':\n case '--help':\n opts.help = true;\n break;\n case '--url':\n opts.url = value();\n break;\n case '--manifest':\n opts.manifest = value();\n break;\n case '--reporter': {\n const r = value();\n if (r !== 'pretty' && r !== 'json' && r !== 'junit') throw new Error(`Invalid reporter: ${r}`);\n opts.reporter = r;\n break;\n }\n case '--out':\n opts.out = value();\n break;\n case '--ready-timeout':\n opts.readyTimeoutMs = Number(value());\n break;\n case '--settle':\n opts.settleMs = Number(value());\n break;\n case '--no-degraded':\n opts.runDegraded = false;\n break;\n case '--allow-same-origin':\n opts.allowSameOrigin = true;\n break;\n case '--ui':\n opts.ui = true;\n break;\n case '--port':\n opts.port = Number(value());\n break;\n case '--no-open':\n opts.open = false;\n break;\n case '--exec':\n opts.exec = value();\n break;\n default:\n if (arg.startsWith('--')) throw new Error(`Unknown option: ${arg}`);\n if (opts.target) throw new Error(`Unexpected extra argument: ${arg}`);\n opts.target = arg;\n }\n }\n return opts;\n}\n\nasync function collectBoot(hostUrl: string, totalTimeoutMs: number): Promise<BootCollection> {\n const browser = await chromium.launch({ args: ['--no-sandbox'] });\n try {\n const page = await browser.newPage();\n page.on('pageerror', (e) => process.stderr.write(`[host page error] ${e.message}\\n`));\n await page.goto(hostUrl, { waitUntil: 'load' });\n await page.waitForFunction(\n () => window.__conformanceBoot__ !== undefined || window.__conformanceError__ !== undefined,\n undefined,\n { timeout: totalTimeoutMs },\n );\n const error = await page.evaluate(() => window.__conformanceError__);\n if (error) throw new Error(`Host harness failed: ${error}`);\n const boot = await page.evaluate(() => window.__conformanceBoot__);\n if (!boot) throw new Error('Host harness produced no result');\n return boot;\n } finally {\n await browser.close();\n }\n}\n\nasync function isDir(path: string): Promise<boolean> {\n return (await stat(path).catch(() => null))?.isDirectory() ?? false;\n}\n\n/** Best-effort: open a URL in the default browser. */\nfunction openBrowser(url: string): void {\n try {\n if (process.platform === 'darwin') {\n spawn('open', [url], { stdio: 'ignore', detached: true }).unref();\n } else if (process.platform === 'win32') {\n spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true }).unref();\n } else {\n spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();\n }\n } catch {\n /* best-effort — the URL is printed regardless */\n }\n}\n\n/** `--ui`: serve the live web runtime + the napplet, watch, and re-run on change. */\nasync function runUiMode(opts: CliOptions): Promise<number> {\n if (opts.manifest) {\n process.stderr.write('--manifest is supported in headless mode; use a NIP-5D pointer in the web runtime for signed manifests.\\n');\n return 2;\n }\n if (opts.url) {\n process.stderr.write('--ui serves a local napplet directory; --url is not supported in UI mode.\\n');\n return 2;\n }\n if (!opts.target) {\n process.stderr.write(`Provide a napplet directory.\\n\\n${HELP}`);\n return 2;\n }\n const resolved = await resolveNappletDir(opts.target);\n const here = dirname(fileURLToPath(import.meta.url));\n const appDir = join(here, 'ui');\n if (!(await isDir(appDir))) {\n process.stderr.write(`Conformance UI assets are missing at ${appDir}. Rebuild @napplet/conformance-cli.\\n`);\n return 2;\n }\n\n const server = await startUiServer({ appDir, nappletDir: resolved.dir, port: opts.port });\n\n const child = opts.exec ? spawn(opts.exec, { cwd: resolve(opts.target), shell: true, stdio: 'inherit' }) : null;\n\n process.stdout.write(\n `\\n napplet conformance UI → ${server.appUrl}\\n` +\n ` watching ${resolved.dir}${opts.exec ? ` (running: ${opts.exec})` : ''}\\n` +\n ` Ctrl-C to stop\\n\\n`,\n );\n if (opts.open) openBrowser(server.appUrl);\n\n await new Promise<void>((resolveWait) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n child?.kill();\n void server.close().finally(resolveWait);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n });\n return 0;\n}\n\nasync function main(argv: string[]): Promise<number> {\n let opts: CliOptions;\n try {\n opts = parseArgs(argv);\n } catch (err) {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n\\n${HELP}`);\n return 2;\n }\n if (opts.help) {\n process.stdout.write(HELP);\n return 0;\n }\n\n if (opts.ui) {\n return runUiMode(opts);\n }\n\n // Resolve the subject: local dir or remote URL.\n let nappletDir: string | null = null;\n let artifact: Uint8Array;\n let forbiddenGlobals: string[] = [];\n let bootUrl: string;\n\n if (opts.url) {\n bootUrl = opts.url;\n const response = await fetch(opts.url);\n if (!response.ok) throw new Error(`Failed to fetch napplet HTML: ${response.status} ${response.statusText}`);\n artifact = new Uint8Array(await response.arrayBuffer());\n } else {\n if (!opts.target) {\n process.stderr.write(`Provide a napplet directory or --url.\\n\\n${HELP}`);\n return 2;\n }\n const resolved = await resolveNappletDir(opts.target);\n nappletDir = resolved.dir;\n artifact = await readFile(resolved.indexHtml);\n forbiddenGlobals = await scanForbiddenGlobals(resolved.dir);\n bootUrl = '/';\n }\n\n const manifestEvent = opts.manifest\n ? verifyManifestArtifact(JSON.parse(await readFile(opts.manifest, 'utf8')) as unknown, artifact)\n : null;\n const manifestHtml = new TextDecoder().decode(artifact);\n\n const here = dirname(fileURLToPath(import.meta.url));\n const hostBundle = await readFile(join(here, 'host-bundle.global.js'), 'utf8');\n const runtimePreludeSource = await readFile(fileURLToPath(import.meta.resolve('@napplet/shim/prelude.global')), 'utf8');\n\n const server = await startHarnessServer({\n nappletDir,\n hostBundle,\n bootConfig: {\n url: bootUrl,\n html: manifestHtml,\n readyTimeoutMs: opts.readyTimeoutMs,\n settleMs: opts.settleMs,\n runDegraded: opts.runDegraded,\n runtimePreludeSource,\n allowSameOrigin: opts.allowSameOrigin,\n },\n });\n\n let boot: BootCollection;\n try {\n // Generous overall budget: two boots (primary + degraded), each up to\n // readyTimeout + settle, plus browser startup.\n const totalTimeout = (opts.readyTimeoutMs + opts.settleMs) * 2 + 30000;\n boot = await collectBoot(server.hostUrl, totalTimeout);\n } finally {\n await server.close();\n }\n\n const context = buildContext({ manifestHtml, manifestEvent, boot, forbiddenGlobals, sandbox: { allowScripts: true, allowSameOrigin: opts.allowSameOrigin } });\n const run = runConformance(context);\n const output = report(run, opts.reporter);\n\n if (opts.out) {\n await writeFile(opts.out, `${output}\\n`, 'utf8');\n process.stdout.write(`Report written to ${opts.out}\\n`);\n } else {\n process.stdout.write(`${output}\\n`);\n }\n\n return run.ok ? 0 : 1;\n}\n\nasync function run(args: string[]): Promise<number> {\n if (args[0] !== 'screenshot') return main(args);\n if (args.includes('--help') || args.includes('-h')) {\n console.log('Usage: napplet screenshot [directory|preview-url] [--output preview.png] [--selector iframe] [--ready-selector <css>] [--width 1200] [--height 750] [--delay 1500]');\n return 0;\n }\n const options = parseScreenshotArgs(args.slice(1));\n await captureScreenshot(options);\n console.log(`Screenshot saved to ${options.output}`);\n return 0;\n}\n\nrun(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((err: unknown) => {\n process.stderr.write(`napplet-conformance: ${err instanceof Error ? err.stack ?? err.message : String(err)}\\n`);\n process.exitCode = 2;\n });\n","/**\n * @napplet/conformance-cli -- Loopback harness server.\n *\n * Serves the napplet's files plus two reserved harness routes (the host page and\n * the browser bundle). Every response carries `Access-Control-Allow-Origin: *`:\n * the napplet iframe runs at an opaque origin (sandbox without `allow-same-origin`),\n * so its `<script type=\"module\">` subresources are fetched in CORS mode and would\n * otherwise be blocked.\n *\n * @packageDocumentation\n */\n\nimport { createServer, type Server, type ServerResponse } from 'node:http';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, normalize, extname, resolve } from 'node:path';\nimport type { AddressInfo } from 'node:net';\nimport { MIME } from './static.js';\n\n/** The napplet URL + timing knobs the host page passes to `bootAndCollect`. */\nexport interface BootConfig {\n url: string;\n html?: string;\n readyTimeoutMs: number;\n settleMs: number;\n runDegraded: boolean;\n runtimePreludeSource: string;\n allowSameOrigin?: boolean;\n}\n\n/** Options for {@link startHarnessServer}. */\nexport interface HarnessServerOptions {\n /** Directory of napplet files to serve, or `null` in remote-URL mode. */\n nappletDir: string | null;\n /** JavaScript source of the host browser bundle. */\n hostBundle: string;\n /** Boot configuration injected into the host page. */\n bootConfig: BootConfig;\n}\n\n/** A running harness server. */\nexport interface HarnessServer {\n /** Base origin, e.g. `http://127.0.0.1:54213`. */\n origin: string;\n /** URL of the host harness page. */\n hostUrl: string;\n /** Shut the server down. */\n close(): Promise<void>;\n}\n\nconst HOST_HTML_ROUTE = '/__conformance__/host.html';\nconst HOST_BUNDLE_ROUTE = '/__conformance__/host-bundle.js';\n\nfunction renderHostPage(boot: BootConfig): string {\n // The inline script lives on the harness page (full privilege, our origin), not\n // in the sandboxed napplet — so it is not subject to the napplet CSP.\n const cfg = JSON.stringify(boot).replace(/</g, '\\\\u003c');\n return `<!doctype html>\n<html lang=\"en\">\n<head><meta charset=\"utf-8\"><title>napplet conformance host</title></head>\n<body>\n<script src=\"${HOST_BUNDLE_ROUTE}\"></script>\n<script>\n (function () {\n var cfg = ${cfg};\n window.NappletConformanceHost.run(cfg).then(function (boot) {\n window.__conformanceBoot__ = boot;\n }).catch(function (err) {\n window.__conformanceError__ = String((err && err.stack) || err);\n });\n })();\n</script>\n</body>\n</html>`;\n}\n\n/** Start the loopback harness server on a random port. */\nexport async function startHarnessServer(options: HarnessServerOptions): Promise<HarnessServer> {\n const dir = options.nappletDir ? resolve(options.nappletDir) : null;\n const hostHtml = renderHostPage(options.bootConfig);\n\n const server: Server = createServer((req, res) => {\n void serve(req.url ?? '/', res);\n });\n\n async function serve(rawUrl: string, res: ServerResponse): Promise<void> {\n res.setHeader('Access-Control-Allow-Origin', '*');\n const path = decodeURIComponent(rawUrl.split('?')[0]);\n\n if (path === HOST_HTML_ROUTE) {\n res.writeHead(200, { 'Content-Type': MIME['.html'] });\n res.end(hostHtml);\n return;\n }\n if (path === HOST_BUNDLE_ROUTE) {\n res.writeHead(200, { 'Content-Type': MIME['.js'] });\n res.end(options.hostBundle);\n return;\n }\n\n if (!dir) {\n res.writeHead(404).end('not found');\n return;\n }\n\n // Resolve a safe path under the napplet dir.\n const relative = normalize(path).replace(/^(\\.\\.[/\\\\])+/, '').replace(/^[/\\\\]+/, '');\n let filePath = join(dir, relative);\n if (!filePath.startsWith(dir)) {\n res.writeHead(403).end('forbidden');\n return;\n }\n try {\n let info = await stat(filePath).catch(() => null);\n if (info?.isDirectory()) {\n filePath = join(filePath, 'index.html');\n info = await stat(filePath).catch(() => null);\n }\n if (path === '/' && !info) {\n filePath = join(dir, 'index.html');\n info = await stat(filePath).catch(() => null);\n }\n if (!info?.isFile()) {\n res.writeHead(404).end('not found');\n return;\n }\n const body = await readFile(filePath);\n res.writeHead(200, { 'Content-Type': MIME[extname(filePath).toLowerCase()] ?? 'application/octet-stream' });\n res.end(body);\n } catch {\n res.writeHead(500).end('server error');\n }\n }\n\n await new Promise<void>((resolveListen) => server.listen(0, '127.0.0.1', resolveListen));\n const address = server.address() as AddressInfo;\n const origin = `http://127.0.0.1:${address.port}`;\n\n return {\n origin,\n hostUrl: `${origin}${HOST_HTML_ROUTE}`,\n close: () => new Promise<void>((res, rej) => server.close((err) => (err ? rej(err) : res()))),\n };\n}\n","/**\n * @napplet/conformance-cli -- Shared static-serving helpers used by both the\n * one-shot harness server and the `--ui` watch server.\n *\n * @packageDocumentation\n */\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { ServerResponse } from 'node:http';\n\n/** Content types by extension. */\nexport const MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n '.wasm': 'application/wasm',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n};\n\n/** The napplet iframe runs at an opaque origin; allow its module subresources. */\nexport function setCors(res: ServerResponse): void {\n res.setHeader('Access-Control-Allow-Origin', '*');\n}\n\n/** Send a file if it exists; return false (without writing) when it does not. */\nexport async function sendFile(res: ServerResponse, absPath: string): Promise<boolean> {\n const info = await stat(absPath).catch(() => null);\n if (!info?.isFile()) return false;\n res.writeHead(200, { 'Content-Type': MIME[extname(absPath).toLowerCase()] ?? 'application/octet-stream' });\n res.end(await readFile(absPath));\n return true;\n}\n","/**\n * @napplet/conformance-cli -- The `--ui` watch server.\n *\n * Serves the standalone conformance web app (bundled with the CLI), the napplet\n * under test (same origin, with permissive CORS for the sandboxed iframe), and a\n * Server-Sent-Events stream. It watches the napplet's served directory and pushes\n * a `rerun` event on every change, so the open app re-runs conformance live — the\n * `vitest --ui` model: edit, save, see the verdict update.\n *\n * @packageDocumentation\n */\n\nimport { createServer, type Server, type ServerResponse } from 'node:http';\nimport { watch, type FSWatcher } from 'node:fs';\nimport { join, normalize, resolve } from 'node:path';\nimport type { AddressInfo } from 'node:net';\nimport { setCors, sendFile } from './static.js';\n\n/** Path the app under `?live=1` is served the napplet from. */\nconst NAPPLET_PREFIX = '/__napplet__/';\n/** SSE endpoint the live app subscribes to. */\nconst EVENTS_ROUTE = '/__conformance__/events';\n\n/** Options for {@link startUiServer}. */\nexport interface UiServerOptions {\n /** Directory of the built conformance web app (served at `/`). */\n appDir: string;\n /** Directory of the built napplet under test (served at `/__napplet__/`). */\n nappletDir: string;\n /** Preferred port (0 = random). */\n port?: number;\n /** Debounce window (ms) for coalescing file-change bursts. Default 120. */\n debounceMs?: number;\n}\n\n/** A running `--ui` server. */\nexport interface UiServer {\n /** Base origin, e.g. `http://127.0.0.1:5219`. */\n origin: string;\n /** The app URL to open (already pointed at the napplet, live mode on). */\n appUrl: string;\n /** Force a `rerun` broadcast (used for the manual/initial trigger). */\n notify(): void;\n /** Shut the server + watcher down. */\n close(): Promise<void>;\n}\n\nfunction safeJoin(dir: string, urlPath: string): string | null {\n const relative = normalize(urlPath).replace(/^(\\.\\.[/\\\\])+/, '').replace(/^[/\\\\]+/, '');\n const full = join(dir, relative);\n return full.startsWith(dir) ? full : null;\n}\n\n/** Register an SSE subscriber and keep the connection open. */\nfunction handleSse(res: ServerResponse, clients: Set<ServerResponse>): void {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n });\n res.write('retry: 1000\\n\\n');\n clients.add(res);\n res.on('close', () => clients.delete(res));\n}\n\n/** Serve a file under `/__napplet__/`. */\nasync function handleNapplet(res: ServerResponse, nappletDir: string, path: string): Promise<void> {\n const rel = path.slice(NAPPLET_PREFIX.length) || 'index.html';\n const abs = safeJoin(nappletDir, rel);\n if (abs && (await sendFile(res, abs))) return;\n res.writeHead(404).end('napplet asset not found');\n}\n\n/** Serve the conformance web app, with an SPA fallback to its index. */\nasync function handleApp(res: ServerResponse, appDir: string, path: string): Promise<void> {\n const abs = safeJoin(appDir, path === '/' ? 'index.html' : path);\n if (abs && (await sendFile(res, abs))) return;\n if (await sendFile(res, join(appDir, 'index.html'))) return;\n res.writeHead(404).end('not found');\n}\n\n/** Watch a directory (recursively when supported) and call `fire` debounced. */\nfunction startWatcher(dir: string, debounceMs: number, fire: () => void): { close(): void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const onEvent = (): void => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(fire, debounceMs);\n };\n let watcher: FSWatcher;\n try {\n watcher = watch(dir, { recursive: true }, onEvent);\n } catch {\n // Recursive watch may be unsupported on some platforms.\n watcher = watch(dir, onEvent);\n }\n return {\n close() {\n if (timer) clearTimeout(timer);\n watcher.close();\n },\n };\n}\n\n/** Start the `--ui` watch server. */\nexport async function startUiServer(options: UiServerOptions): Promise<UiServer> {\n const appDir = resolve(options.appDir);\n const nappletDir = resolve(options.nappletDir);\n const clients = new Set<ServerResponse>();\n const broadcast = (): void => {\n for (const res of clients) res.write(`event: rerun\\ndata: ${Date.now()}\\n\\n`);\n };\n\n const server: Server = createServer((req, res) => {\n setCors(res);\n res.setHeader('Cache-Control', 'no-store'); // never serve stale napplet code\n const path = decodeURIComponent((req.url ?? '/').split('?')[0]);\n if (path === EVENTS_ROUTE) {\n handleSse(res, clients);\n } else if (path === NAPPLET_PREFIX.slice(0, -1) || path.startsWith(NAPPLET_PREFIX)) {\n void handleNapplet(res, nappletDir, path);\n } else {\n void handleApp(res, appDir, path);\n }\n });\n\n await new Promise<void>((r) => server.listen(options.port ?? 0, '127.0.0.1', r));\n const { port } = server.address() as AddressInfo;\n const origin = `http://127.0.0.1:${port}`;\n const appUrl = `${origin}/?url=${encodeURIComponent(NAPPLET_PREFIX)}&live=1`;\n const watcher = startWatcher(nappletDir, options.debounceMs ?? 120, broadcast);\n\n return {\n origin,\n appUrl,\n notify: broadcast,\n close: () =>\n new Promise<void>((res, rej) => {\n watcher.close();\n for (const c of clients) c.end();\n clients.clear();\n server.close((err) => (err ? rej(err) : res()));\n }),\n };\n}\n","/**\n * @napplet/conformance-cli -- Static scan for forbidden napplet authority.\n *\n * A napplet runs in a `sandbox=\"allow-scripts\"` iframe at an opaque origin, so the\n * host cannot observe every forbidden browser surface at runtime. Instead we scan\n * served source for references to authority the shell owns: signer globals,\n * browser storage, direct network primitives, and external network-loaded assets.\n * This is a heuristic, but a true positive is meaningful: NIP-5D and the NAPs\n * route these capabilities through `window.napplet` domains.\n *\n * @packageDocumentation\n */\n\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { join, extname } from 'node:path';\n\n/** Patterns considered forbidden, keyed by the label reported in the verdict. */\nconst FORBIDDEN: Array<{ label: string; re: RegExp }> = [\n { label: 'window.nostr', re: /\\bwindow\\s*\\.\\s*nostr\\b/ },\n { label: 'globalThis.nostr', re: /\\bglobalThis\\s*\\.\\s*nostr\\b/ },\n { label: 'fetch', re: /\\b(?:window\\s*\\.\\s*|globalThis\\s*\\.\\s*)?fetch\\s*\\(/ },\n { label: 'XMLHttpRequest', re: /\\b(?:new\\s+)?XMLHttpRequest\\s*\\(/ },\n { label: 'WebSocket', re: /\\b(?:new\\s+)?WebSocket\\s*\\(/ },\n { label: 'localStorage', re: /\\b(?:window\\s*\\.\\s*)?localStorage\\b/ },\n { label: 'sessionStorage', re: /\\b(?:window\\s*\\.\\s*)?sessionStorage\\b/ },\n { label: 'indexedDB', re: /\\b(?:window\\s*\\.\\s*)?indexedDB\\b/ },\n { label: 'document.cookie', re: /\\bdocument\\s*\\.\\s*cookie\\b/ },\n { label: 'external img src', re: /<img\\b[^>]*\\bsrc\\s*=\\s*[\"']?https?:/i },\n { label: 'external script src', re: /<script\\b[^>]*\\bsrc\\s*=\\s*[\"']?https?:/i },\n { label: 'external stylesheet href', re: /<link\\b[^>]*\\brel\\s*=\\s*[\"']?stylesheet[\"']?[^>]*\\bhref\\s*=\\s*[\"']?https?:/i },\n { label: 'external stylesheet href', re: /<link\\b[^>]*\\bhref\\s*=\\s*[\"']?https?:[^>]*\\brel\\s*=\\s*[\"']?stylesheet[\"']?/i },\n];\n\nconst SCANNABLE = new Set(['.js', '.mjs', '.cjs', '.html', '.htm']);\nconst SKIP_DIRS = new Set(['node_modules', '.git', '.turbo', 'coverage']);\n\nasync function collectFiles(dir: string, acc: string[]): Promise<void> {\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await collectFiles(join(dir, entry.name), acc);\n } else if (entry.isFile() && SCANNABLE.has(extname(entry.name).toLowerCase())) {\n acc.push(join(dir, entry.name));\n }\n }\n}\n\n/**\n * Scan a napplet directory for forbidden browser authority references.\n *\n * @param dir - The directory of served napplet files.\n * @returns The distinct forbidden labels found (e.g. `['window.nostr']`).\n */\nexport async function scanForbiddenGlobals(dir: string): Promise<string[]> {\n const info = await stat(dir).catch(() => null);\n if (!info?.isDirectory()) return [];\n const files: string[] = [];\n await collectFiles(dir, files);\n const found = new Set<string>();\n for (const file of files) {\n const text = await readFile(file, 'utf8').catch(() => '');\n for (const { label, re } of FORBIDDEN) {\n if (re.test(text)) found.add(label);\n }\n }\n return [...found];\n}\n","/**\n * @napplet/conformance-cli -- Resolve which local artifact to conformance-test.\n *\n * Kept in its own module (not `cli.ts`, whose top level runs `main()`) so tests\n * can import the resolver without launching the CLI.\n */\n\nimport { stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nasync function isFile(path: string): Promise<boolean> {\n return (await stat(path).catch(() => null))?.isFile() ?? false;\n}\n\n/**\n * Resolve the directory + index.html for a local napplet target.\n *\n * A built napplet is the artifact we test, so `dist/index.html` is preferred over\n * a sibling `index.html`. When `target` is a project root, its source\n * `index.html` (a Vite entry referencing `/src/*.ts` that cannot run in the\n * opaque-origin `srcdoc` sandbox) would otherwise shadow the built single-file\n * output and make conformance test the wrong, never-booting artifact.\n */\nexport async function resolveNappletDir(target: string): Promise<{ dir: string; indexHtml: string }> {\n const base = resolve(target);\n const dist = join(base, 'dist');\n if (await isFile(join(dist, 'index.html'))) return { dir: dist, indexHtml: join(dist, 'index.html') };\n if (await isFile(join(base, 'index.html'))) return { dir: base, indexHtml: join(base, 'index.html') };\n throw new Error(`No index.html found in ${join(dist, 'index.html')} or ${join(base, 'index.html')}. Build the napplet first.`);\n}\n","/** Capture a local build or an existing shell preview through its iframe. */\nimport { access, writeFile } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { chromium } from 'playwright';\nimport { startScreenshotPreview } from './screenshot-preview.js';\nimport { resolveNappletDir } from './resolve-napplet.js';\n\n/** Options for capturing a shell-hosted napplet. */\nexport interface ScreenshotOptions {\n url?: string;\n directory?: string;\n artifact?: string;\n output: string;\n selector: string;\n readySelector?: string;\n width: number;\n height: number;\n delay: number;\n}\n\n/**\n * Parse screenshot arguments without invoking a browser.\n * @param args CLI arguments after the screenshot command.\n * @returns Validated capture options.\n * @example parseScreenshotArgs(['http://localhost:5173', '--output', 'preview.png'])\n */\nexport function parseScreenshotArgs(args: readonly string[]): ScreenshotOptions {\n const hasTarget = args[0] && !args[0].startsWith('--');\n const target = hasTarget ? args[0] : '.';\n const rest = hasTarget ? args.slice(1) : args;\n const remote = /^https?:\\/\\//.test(target);\n if (remote) new URL(target);\n else if (/^[a-z][a-z0-9+.-]*:/i.test(target) && !/^[a-z]:[\\\\/]/i.test(target)) throw new Error('Preview URLs must use HTTP(S)');\n const options: ScreenshotOptions = { ...(remote ? { url: target } : { directory: target }), output: 'preview.png', selector: 'iframe', width: 1200, height: 750, delay: 1500 };\n for (let index = 0; index < rest.length; index += 2) {\n const flag = rest[index];\n const value = rest[index + 1];\n if (!value || value.startsWith('--')) throw new Error(`Missing value for ${flag}`);\n switch (flag) {\n case '--output': options.output = value; break;\n case '--artifact': options.artifact = value; break;\n case '--selector': options.selector = value; break;\n case '--ready-selector': options.readySelector = value; break;\n case '--width': options.width = integer(value, flag, 1, 4096); break;\n case '--height': options.height = integer(value, flag, 1, 4096); break;\n case '--delay': options.delay = integer(value, flag, 0, 30000); break;\n default: throw new Error(`Unknown screenshot option: ${flag}`);\n }\n }\n if (!options.output.toLowerCase().endsWith('.png')) throw new Error('--output must end in .png');\n if (options.artifact && hasTarget) throw new Error('Use a target or --artifact, not both');\n return options;\n}\n\n/**\n * Save a PNG of a napplet iframe, starting a local preview when needed.\n * @param options Local build or URL, output, dimensions and readiness settings.\n * @returns Resolves after capture and cleanup of Chromium and any local preview.\n * @example await captureScreenshot(parseScreenshotArgs(['http://localhost:5173']))\n */\nexport async function captureScreenshot(options: ScreenshotOptions): Promise<void> {\n await ensureChromium();\n const preview = options.url ? undefined : await startScreenshotPreview(\n options.artifact ?? (await resolveNappletDir(options.directory ?? '.')).indexHtml,\n );\n let browser;\n try {\n browser = await chromium.launch({ channel: 'chromium' });\n const page = await browser.newPage({ viewport: { width: options.width, height: options.height } });\n page.setDefaultTimeout(30000);\n await page.goto(options.url ?? preview!.url, { waitUntil: 'domcontentloaded' });\n if (preview) {\n await page.waitForFunction(() => {\n const iframe = document.querySelector<HTMLIFrameElement>('#napplet-frame');\n return Boolean(iframe?.srcdoc);\n });\n }\n const frame = page.locator(options.selector);\n await frame.waitFor({ state: 'visible' });\n await frame.evaluate((element, size) => {\n if (!(element instanceof HTMLIFrameElement)) throw new Error('Screenshot selector must select one iframe');\n Object.assign(element.style, { width: `${size.width}px`, height: `${size.height}px`, maxWidth: 'none', maxHeight: 'none', boxSizing: 'border-box' });\n }, { width: options.width, height: options.height });\n const content = frame.contentFrame();\n await content.locator('body').waitFor({ state: 'visible' });\n if (options.readySelector) await content.locator(options.readySelector).waitFor({ state: 'visible' });\n await content.locator('body').evaluate(async () => { await document.fonts.ready; });\n await page.waitForTimeout(options.delay);\n const png = await frame.screenshot({ type: 'png', timeout: 30000 });\n await writeFile(options.output, png, { flag: 'wx' });\n } finally {\n try { await browser?.close(); } finally { await preview?.close(); }\n }\n}\n\nasync function ensureChromium(): Promise<void> {\n try { await access(chromium.executablePath()); return; } catch { /* Install the matching browser on first use. */ }\n console.error('Installing Chromium for screenshot capture...');\n const require = createRequire(import.meta.url);\n const cli = join(dirname(require.resolve('playwright/package.json')), 'cli.js');\n await promisify(execFile)(process.execPath, [cli, 'install', 'chromium'], { timeout: 180000 });\n}\n\nfunction integer(value: string, flag: string, min: number, max: number): number {\n const number = Number(value);\n if (!Number.isInteger(number) || number < min || number > max) throw new Error(`${flag} must be an integer from ${min} to ${max}`);\n return number;\n}\n","/** Disposable Kehto Paja preview for a local build. */\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { readFile, realpath } from 'node:fs/promises';\nimport { dirname, resolve, sep } from 'node:path';\nimport { startPajaServer } from '@kehto/paja';\nimport { sendFile, setCors } from './static.js';\n\n/**\n * Host a built artifact in Paja on ephemeral loopback ports.\n * @param artifact Exact index.html selected by deployment.\n * @returns Shell URL and an async cleanup handle for both servers.\n * @example const preview = await startScreenshotPreview('/project/dist/index.html');\n */\nexport async function startScreenshotPreview(artifact: string): Promise<{ url: string; close(): Promise<void> }> {\n const index = await realpath(artifact);\n const root = dirname(index);\n const html = await readFile(index);\n const server = createServer((request, response) => {\n setCors(response);\n void (async () => {\n const pathname = decodeURIComponent(new URL(request.url ?? '/', 'http://localhost').pathname);\n if (pathname === '/' || pathname === '/index.html') {\n response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n response.end(html);\n return;\n }\n const file = await realpath(resolve(root, `.${pathname}`));\n if (!file.startsWith(`${root}${sep}`)) { response.writeHead(403).end(); return; }\n if (!await sendFile(response, file)) response.writeHead(404).end();\n })().catch(() => { if (!response.headersSent) response.writeHead(404); response.end(); });\n });\n await new Promise<void>((done, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', () => { server.off('error', reject); done(); });\n });\n const closeTarget = () => new Promise<void>((done, reject) => {\n server.closeAllConnections();\n server.close((error) => error ? reject(error) : done());\n });\n try {\n const targetUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;\n const paja = await startPajaServer({ options: {\n host: '127.0.0.1', port: 0, targetUrl,\n } });\n return { url: paja.url, close: async () => { try { await paja.close(); } finally { await closeTarget(); } } };\n } catch (error) {\n await closeTarget();\n throw error;\n }\n}\n","import { createHash } from 'node:crypto';\nimport { validateEvent, verifyEvent, type Event } from 'nostr-tools/pure';\nimport { validateManifestEvent } from '@napplet/conformance';\n\n/** Verify the signed event and exact raw artifact before the browser executes it (NIP-5D §Security Considerations). */\nexport function verifyManifestArtifact(input: unknown, artifact: Uint8Array): Event {\n if (!validateEvent(input)) throw new Error('Manifest is not a Nostr event');\n // Verify a fresh object: nostr-tools caches verification on a symbol which a\n // caller could otherwise copy from a previously signed event and then mutate.\n const { id, pubkey, created_at, kind, tags, content, sig } = input as Event;\n const event: Event = { id, pubkey, created_at, kind, tags, content, sig };\n let validSignature = false;\n try {\n validSignature = verifyEvent(event);\n } catch {\n // Malformed signature fields are rejected along with cryptographic failures.\n }\n if (!validSignature) throw new Error('Manifest signature or event id is invalid');\n const verdict = validateManifestEvent(event);\n if (!verdict.ok) throw new Error(`Invalid NIP-5D manifest: ${verdict.errors.map((error) => error.message).join('; ')}`);\n const expected = event.tags.find((tag) => tag[0] === 'x')?.[1];\n const actual = createHash('sha256').update(artifact).digest('hex');\n if (actual !== expected) throw new Error(`Artifact SHA-256 mismatch: expected ${expected}, got ${actual}`);\n return event;\n}\n"],"mappings":"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","names":["readFile","writeFile","stat","join","dirname","resolve","chromium","readFile","stat","extname","stat","readFile","extname","createServer","join","normalize","resolve","normalize","join","resolve","createServer","readFile","stat","join","extname","stat","join","resolve","dirname","join","createServer","readFile","resolve","readFile","createServer","resolve","require","join","dirname","chromium","stat","dirname","join","resolve","readFile","run","writeFile"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/server.ts","../src/static.ts","../src/ui-server.ts","../src/scan.ts","../src/resolve-napplet.ts","../src/screenshot.ts","../src/screenshot-preview.ts","../src/manifest.ts"],"sourcesContent":["/**\n * @napplet/conformance-cli -- `napplet-conformance` entry point.\n *\n * Runs the @napplet/conformance engine against a napplet in real headless Chromium:\n *\n * 1. Resolve the napplet's built directory (or a remote `--url`).\n * 2. Serve it on loopback alongside a host harness page + the engine bundle.\n * 3. Drive Playwright Chromium to `bootAndCollect` inside the host page.\n * 4. Assemble a ConformanceContext on the node side (manifest HTML + static scan),\n * run the check catalog, print the chosen report, and set the exit code.\n *\n * Exit codes: `0` conformant, `1` non-conformant, `2` usage/runtime error.\n *\n * @packageDocumentation\n */\n\nimport { readFile, writeFile, stat } from 'node:fs/promises';\nimport { join, dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawn } from 'node:child_process';\nimport { chromium } from 'playwright';\nimport {\n buildContext,\n runConformance,\n report,\n type BootCollection,\n type ReporterFormat,\n} from '@napplet/conformance';\nimport { startHarnessServer } from './server.js';\nimport { startUiServer } from './ui-server.js';\nimport { scanForbiddenGlobals } from './scan.js';\nimport { resolveNappletDir } from './resolve-napplet.js';\nimport { captureScreenshot, parseScreenshotArgs } from './screenshot.js';\nimport { verifyManifestArtifact } from './manifest.js';\n\ninterface CliOptions {\n target?: string;\n url?: string;\n manifest?: string;\n reporter: ReporterFormat;\n out?: string;\n readyTimeoutMs: number;\n settleMs: number;\n runDegraded: boolean;\n allowSameOrigin: boolean;\n ui: boolean;\n port?: number;\n open: boolean;\n exec?: string;\n help: boolean;\n}\n\nconst HELP = `napplet-conformance — run NAP protocol conformance against a napplet\n\nUsage:\n napplet-conformance [dir] [options] # headless, one-shot, CI exit code\n napplet-conformance --ui [dir] [options] # open the live web runtime (like vitest --ui)\n napplet-conformance --url https://my.napplet/ [options]\n napplet-conformance screenshot [directory|preview-url] [--output preview.png]\n\nArguments:\n dir Built napplet directory (prefers ./dist/index.html, falls back to ./index.html)\n\nOptions:\n --url <url> Test a remotely-served napplet instead of a local dir (headless)\n --manifest <file> Signed NIP-5D event JSON; verify its signature and raw artifact hash before boot (headless)\n --reporter <fmt> pretty | json | junit (default: pretty)\n --out <file> Write the report to a file instead of stdout\n --ready-timeout <ms> Boot timeout waiting for iframe load (default: 5000)\n --settle <ms> Envelope-collection window after boot (default: 600)\n --no-degraded Skip the no-capability graceful-degradation pass\n --allow-same-origin Debug: also grant allow-same-origin (a conformant napplet must not need it)\n\nUI / watch mode (--ui):\n --port <n> Port for the UI server (default: random)\n --no-open Do not open a browser automatically\n --exec <cmd> Run a command (e.g. \"vite build --watch\") so edits rebuild the served dir;\n the watcher then re-runs conformance live on every change\n -h, --help Show this help\n\nExit codes (headless): 0 conformant, 1 non-conformant, 2 usage/runtime error.\n`;\n\nfunction parseArgs(argv: string[]): CliOptions {\n const opts: CliOptions = {\n reporter: 'pretty',\n readyTimeoutMs: 5000,\n settleMs: 600,\n runDegraded: true,\n allowSameOrigin: false,\n ui: false,\n open: true,\n help: false,\n };\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n const value = (): string => {\n const v = argv[i + 1];\n if (v === undefined || v.startsWith('--')) throw new Error(`Missing value for ${arg}`);\n i += 1;\n return v;\n };\n switch (arg) {\n case '-h':\n case '--help':\n opts.help = true;\n break;\n case '--url':\n opts.url = value();\n break;\n case '--manifest':\n opts.manifest = value();\n break;\n case '--reporter': {\n const r = value();\n if (r !== 'pretty' && r !== 'json' && r !== 'junit') throw new Error(`Invalid reporter: ${r}`);\n opts.reporter = r;\n break;\n }\n case '--out':\n opts.out = value();\n break;\n case '--ready-timeout':\n opts.readyTimeoutMs = Number(value());\n break;\n case '--settle':\n opts.settleMs = Number(value());\n break;\n case '--no-degraded':\n opts.runDegraded = false;\n break;\n case '--allow-same-origin':\n opts.allowSameOrigin = true;\n break;\n case '--ui':\n opts.ui = true;\n break;\n case '--port':\n opts.port = Number(value());\n break;\n case '--no-open':\n opts.open = false;\n break;\n case '--exec':\n opts.exec = value();\n break;\n default:\n if (arg.startsWith('--')) throw new Error(`Unknown option: ${arg}`);\n if (opts.target) throw new Error(`Unexpected extra argument: ${arg}`);\n opts.target = arg;\n }\n }\n return opts;\n}\n\nasync function collectBoot(hostUrl: string, totalTimeoutMs: number): Promise<BootCollection> {\n const browser = await chromium.launch({ args: ['--no-sandbox'] });\n try {\n const page = await browser.newPage();\n page.on('pageerror', (e) => process.stderr.write(`[host page error] ${e.message}\\n`));\n await page.goto(hostUrl, { waitUntil: 'load' });\n await page.waitForFunction(\n () => window.__conformanceBoot__ !== undefined || window.__conformanceError__ !== undefined,\n undefined,\n { timeout: totalTimeoutMs },\n );\n const error = await page.evaluate(() => window.__conformanceError__);\n if (error) throw new Error(`Host harness failed: ${error}`);\n const boot = await page.evaluate(() => window.__conformanceBoot__);\n if (!boot) throw new Error('Host harness produced no result');\n return boot;\n } finally {\n await browser.close();\n }\n}\n\nasync function isDir(path: string): Promise<boolean> {\n return (await stat(path).catch(() => null))?.isDirectory() ?? false;\n}\n\n/** Best-effort: open a URL in the default browser. */\nfunction openBrowser(url: string): void {\n try {\n if (process.platform === 'darwin') {\n spawn('open', [url], { stdio: 'ignore', detached: true }).unref();\n } else if (process.platform === 'win32') {\n spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true }).unref();\n } else {\n spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();\n }\n } catch {\n /* best-effort — the URL is printed regardless */\n }\n}\n\n/** `--ui`: serve the live web runtime + the napplet, watch, and re-run on change. */\nasync function runUiMode(opts: CliOptions): Promise<number> {\n if (opts.manifest) {\n process.stderr.write('--manifest is supported in headless mode; use a NIP-5D pointer in the web runtime for signed manifests.\\n');\n return 2;\n }\n if (opts.url) {\n process.stderr.write('--ui serves a local napplet directory; --url is not supported in UI mode.\\n');\n return 2;\n }\n if (!opts.target) {\n process.stderr.write(`Provide a napplet directory.\\n\\n${HELP}`);\n return 2;\n }\n const resolved = await resolveNappletDir(opts.target);\n const here = dirname(fileURLToPath(import.meta.url));\n const appDir = join(here, 'ui');\n if (!(await isDir(appDir))) {\n process.stderr.write(`Conformance UI assets are missing at ${appDir}. Rebuild @napplet/conformance-cli.\\n`);\n return 2;\n }\n\n const server = await startUiServer({ appDir, nappletDir: resolved.dir, port: opts.port });\n\n const child = opts.exec ? spawn(opts.exec, { cwd: resolve(opts.target), shell: true, stdio: 'inherit' }) : null;\n\n process.stdout.write(\n `\\n napplet conformance UI → ${server.appUrl}\\n` +\n ` watching ${resolved.dir}${opts.exec ? ` (running: ${opts.exec})` : ''}\\n` +\n ` Ctrl-C to stop\\n\\n`,\n );\n if (opts.open) openBrowser(server.appUrl);\n\n await new Promise<void>((resolveWait) => {\n let closing = false;\n const shutdown = (): void => {\n if (closing) return;\n closing = true;\n child?.kill();\n void server.close().finally(resolveWait);\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n });\n return 0;\n}\n\nasync function main(argv: string[]): Promise<number> {\n let opts: CliOptions;\n try {\n opts = parseArgs(argv);\n } catch (err) {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n\\n${HELP}`);\n return 2;\n }\n if (opts.help) {\n process.stdout.write(HELP);\n return 0;\n }\n\n if (opts.ui) {\n return runUiMode(opts);\n }\n\n // Resolve the subject: local dir or remote URL.\n let nappletDir: string | null = null;\n let artifact: Uint8Array;\n let forbiddenGlobals: string[] = [];\n let bootUrl: string;\n\n if (opts.url) {\n bootUrl = opts.url;\n const response = await fetch(opts.url);\n if (!response.ok) throw new Error(`Failed to fetch napplet HTML: ${response.status} ${response.statusText}`);\n artifact = new Uint8Array(await response.arrayBuffer());\n } else {\n if (!opts.target) {\n process.stderr.write(`Provide a napplet directory or --url.\\n\\n${HELP}`);\n return 2;\n }\n const resolved = await resolveNappletDir(opts.target);\n nappletDir = resolved.dir;\n artifact = await readFile(resolved.indexHtml);\n forbiddenGlobals = await scanForbiddenGlobals(resolved.dir);\n bootUrl = '/';\n }\n\n const manifestEvent = opts.manifest\n ? verifyManifestArtifact(JSON.parse(await readFile(opts.manifest, 'utf8')) as unknown, artifact)\n : null;\n const manifestHtml = new TextDecoder().decode(artifact);\n\n const here = dirname(fileURLToPath(import.meta.url));\n const hostBundle = await readFile(join(here, 'host-bundle.global.js'), 'utf8');\n const runtimePreludeSource = await readFile(fileURLToPath(import.meta.resolve('@napplet/shim/prelude.global')), 'utf8');\n\n const server = await startHarnessServer({\n nappletDir,\n hostBundle,\n bootConfig: {\n url: bootUrl,\n html: manifestHtml,\n readyTimeoutMs: opts.readyTimeoutMs,\n settleMs: opts.settleMs,\n runDegraded: opts.runDegraded,\n runtimePreludeSource,\n allowSameOrigin: opts.allowSameOrigin,\n },\n });\n\n let boot: BootCollection;\n try {\n // Generous overall budget: two boots (primary + degraded), each up to\n // readyTimeout + settle, plus browser startup.\n const totalTimeout = (opts.readyTimeoutMs + opts.settleMs) * 2 + 30000;\n boot = await collectBoot(server.hostUrl, totalTimeout);\n } finally {\n await server.close();\n }\n\n const context = buildContext({ manifestHtml, manifestEvent, boot, forbiddenGlobals, sandbox: { allowScripts: true, allowSameOrigin: opts.allowSameOrigin } });\n const run = runConformance(context);\n const output = report(run, opts.reporter);\n\n if (opts.out) {\n await writeFile(opts.out, `${output}\\n`, 'utf8');\n process.stdout.write(`Report written to ${opts.out}\\n`);\n } else {\n process.stdout.write(`${output}\\n`);\n }\n\n return run.ok ? 0 : 1;\n}\n\nasync function run(args: string[]): Promise<number> {\n if (args[0] !== 'screenshot') return main(args);\n if (args.includes('--help') || args.includes('-h')) {\n console.log('Usage: napplet screenshot [directory|preview-url] [--output preview.png] [--selector iframe] [--ready-selector <css>] [--width 1200] [--height 750] [--delay 1500]');\n return 0;\n }\n const options = parseScreenshotArgs(args.slice(1));\n await captureScreenshot(options);\n console.log(`Screenshot saved to ${options.output}`);\n return 0;\n}\n\nrun(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code;\n })\n .catch((err: unknown) => {\n process.stderr.write(`napplet-conformance: ${err instanceof Error ? err.stack ?? err.message : String(err)}\\n`);\n process.exitCode = 2;\n });\n","/**\n * @napplet/conformance-cli -- Loopback harness server.\n *\n * Serves the napplet's files plus two reserved harness routes (the host page and\n * the browser bundle). Every response carries `Access-Control-Allow-Origin: *`:\n * the napplet iframe runs at an opaque origin (sandbox without `allow-same-origin`),\n * so its `<script type=\"module\">` subresources are fetched in CORS mode and would\n * otherwise be blocked.\n *\n * @packageDocumentation\n */\n\nimport type { BootOptions } from '@napplet/conformance';\nimport { createServer, type Server, type ServerResponse } from 'node:http';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, normalize, extname, resolve } from 'node:path';\nimport type { AddressInfo } from 'node:net';\nimport { MIME } from './static.js';\n\n/** The napplet URL + timing knobs the host page passes to `bootAndCollect`. */\nexport interface BootConfig {\n url: string;\n html?: string;\n readyTimeoutMs: number;\n settleMs: number;\n runDegraded: boolean;\n runtimePreludeSource: string;\n allowSameOrigin?: boolean;\n domains?: BootOptions['domains'];\n maturity?: BootOptions['maturity'];\n}\n\n/** Options for {@link startHarnessServer}. */\nexport interface HarnessServerOptions {\n /** Directory of napplet files to serve, or `null` in remote-URL mode. */\n nappletDir: string | null;\n /** JavaScript source of the host browser bundle. */\n hostBundle: string;\n /** Boot configuration injected into the host page. */\n bootConfig: BootConfig;\n}\n\n/** A running harness server. */\nexport interface HarnessServer {\n /** Base origin, e.g. `http://127.0.0.1:54213`. */\n origin: string;\n /** URL of the host harness page. */\n hostUrl: string;\n /** Shut the server down. */\n close(): Promise<void>;\n}\n\nconst HOST_HTML_ROUTE = '/__conformance__/host.html';\nconst HOST_BUNDLE_ROUTE = '/__conformance__/host-bundle.js';\n\nfunction renderHostPage(boot: BootConfig): string {\n // The inline script lives on the harness page (full privilege, our origin), not\n // in the sandboxed napplet — so it is not subject to the napplet CSP.\n const cfg = JSON.stringify(boot).replace(/</g, '\\\\u003c');\n return `<!doctype html>\n<html lang=\"en\">\n<head><meta charset=\"utf-8\"><title>napplet conformance host</title></head>\n<body>\n<script src=\"${HOST_BUNDLE_ROUTE}\"></script>\n<script>\n (function () {\n var cfg = ${cfg};\n window.NappletConformanceHost.run(cfg).then(function (boot) {\n window.__conformanceBoot__ = boot;\n }).catch(function (err) {\n window.__conformanceError__ = String((err && err.stack) || err);\n });\n })();\n</script>\n</body>\n</html>`;\n}\n\n/** Start the loopback harness server on a random port. */\nexport async function startHarnessServer(options: HarnessServerOptions): Promise<HarnessServer> {\n const dir = options.nappletDir ? resolve(options.nappletDir) : null;\n const hostHtml = renderHostPage(options.bootConfig);\n\n const server: Server = createServer((req, res) => {\n void serve(req.url ?? '/', res);\n });\n\n async function serve(rawUrl: string, res: ServerResponse): Promise<void> {\n res.setHeader('Access-Control-Allow-Origin', '*');\n const path = decodeURIComponent(rawUrl.split('?')[0]);\n\n if (path === HOST_HTML_ROUTE) {\n res.writeHead(200, { 'Content-Type': MIME['.html'] });\n res.end(hostHtml);\n return;\n }\n if (path === HOST_BUNDLE_ROUTE) {\n res.writeHead(200, { 'Content-Type': MIME['.js'] });\n res.end(options.hostBundle);\n return;\n }\n\n if (!dir) {\n res.writeHead(404).end('not found');\n return;\n }\n\n // Resolve a safe path under the napplet dir.\n const relative = normalize(path).replace(/^(\\.\\.[/\\\\])+/, '').replace(/^[/\\\\]+/, '');\n let filePath = join(dir, relative);\n if (!filePath.startsWith(dir)) {\n res.writeHead(403).end('forbidden');\n return;\n }\n try {\n let info = await stat(filePath).catch(() => null);\n if (info?.isDirectory()) {\n filePath = join(filePath, 'index.html');\n info = await stat(filePath).catch(() => null);\n }\n if (path === '/' && !info) {\n filePath = join(dir, 'index.html');\n info = await stat(filePath).catch(() => null);\n }\n if (!info?.isFile()) {\n res.writeHead(404).end('not found');\n return;\n }\n const body = await readFile(filePath);\n res.writeHead(200, { 'Content-Type': MIME[extname(filePath).toLowerCase()] ?? 'application/octet-stream' });\n res.end(body);\n } catch {\n res.writeHead(500).end('server error');\n }\n }\n\n await new Promise<void>((resolveListen) => server.listen(0, '127.0.0.1', resolveListen));\n const address = server.address() as AddressInfo;\n const origin = `http://127.0.0.1:${address.port}`;\n\n return {\n origin,\n hostUrl: `${origin}${HOST_HTML_ROUTE}`,\n close: () => new Promise<void>((res, rej) => server.close((err) => (err ? rej(err) : res()))),\n };\n}\n","/**\n * @napplet/conformance-cli -- Shared static-serving helpers used by both the\n * one-shot harness server and the `--ui` watch server.\n *\n * @packageDocumentation\n */\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { ServerResponse } from 'node:http';\n\n/** Content types by extension. */\nexport const MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n '.wasm': 'application/wasm',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n};\n\n/** The napplet iframe runs at an opaque origin; allow its module subresources. */\nexport function setCors(res: ServerResponse): void {\n res.setHeader('Access-Control-Allow-Origin', '*');\n}\n\n/** Send a file if it exists; return false (without writing) when it does not. */\nexport async function sendFile(res: ServerResponse, absPath: string): Promise<boolean> {\n const info = await stat(absPath).catch(() => null);\n if (!info?.isFile()) return false;\n res.writeHead(200, { 'Content-Type': MIME[extname(absPath).toLowerCase()] ?? 'application/octet-stream' });\n res.end(await readFile(absPath));\n return true;\n}\n","/**\n * @napplet/conformance-cli -- The `--ui` watch server.\n *\n * Serves the standalone conformance web app (bundled with the CLI), the napplet\n * under test (same origin, with permissive CORS for the sandboxed iframe), and a\n * Server-Sent-Events stream. It watches the napplet's served directory and pushes\n * a `rerun` event on every change, so the open app re-runs conformance live — the\n * `vitest --ui` model: edit, save, see the verdict update.\n *\n * @packageDocumentation\n */\n\nimport { createServer, type Server, type ServerResponse } from 'node:http';\nimport { watch, type FSWatcher } from 'node:fs';\nimport { join, normalize, resolve } from 'node:path';\nimport type { AddressInfo } from 'node:net';\nimport { setCors, sendFile } from './static.js';\n\n/** Path the app under `?live=1` is served the napplet from. */\nconst NAPPLET_PREFIX = '/__napplet__/';\n/** SSE endpoint the live app subscribes to. */\nconst EVENTS_ROUTE = '/__conformance__/events';\n\n/** Options for {@link startUiServer}. */\nexport interface UiServerOptions {\n /** Directory of the built conformance web app (served at `/`). */\n appDir: string;\n /** Directory of the built napplet under test (served at `/__napplet__/`). */\n nappletDir: string;\n /** Preferred port (0 = random). */\n port?: number;\n /** Debounce window (ms) for coalescing file-change bursts. Default 120. */\n debounceMs?: number;\n}\n\n/** A running `--ui` server. */\nexport interface UiServer {\n /** Base origin, e.g. `http://127.0.0.1:5219`. */\n origin: string;\n /** The app URL to open (already pointed at the napplet, live mode on). */\n appUrl: string;\n /** Force a `rerun` broadcast (used for the manual/initial trigger). */\n notify(): void;\n /** Shut the server + watcher down. */\n close(): Promise<void>;\n}\n\nfunction safeJoin(dir: string, urlPath: string): string | null {\n const relative = normalize(urlPath).replace(/^(\\.\\.[/\\\\])+/, '').replace(/^[/\\\\]+/, '');\n const full = join(dir, relative);\n return full.startsWith(dir) ? full : null;\n}\n\n/** Register an SSE subscriber and keep the connection open. */\nfunction handleSse(res: ServerResponse, clients: Set<ServerResponse>): void {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n });\n res.write('retry: 1000\\n\\n');\n clients.add(res);\n res.on('close', () => clients.delete(res));\n}\n\n/** Serve a file under `/__napplet__/`. */\nasync function handleNapplet(res: ServerResponse, nappletDir: string, path: string): Promise<void> {\n const rel = path.slice(NAPPLET_PREFIX.length) || 'index.html';\n const abs = safeJoin(nappletDir, rel);\n if (abs && (await sendFile(res, abs))) return;\n res.writeHead(404).end('napplet asset not found');\n}\n\n/** Serve the conformance web app, with an SPA fallback to its index. */\nasync function handleApp(res: ServerResponse, appDir: string, path: string): Promise<void> {\n const abs = safeJoin(appDir, path === '/' ? 'index.html' : path);\n if (abs && (await sendFile(res, abs))) return;\n if (await sendFile(res, join(appDir, 'index.html'))) return;\n res.writeHead(404).end('not found');\n}\n\n/** Watch a directory (recursively when supported) and call `fire` debounced. */\nfunction startWatcher(dir: string, debounceMs: number, fire: () => void): { close(): void } {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const onEvent = (): void => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(fire, debounceMs);\n };\n let watcher: FSWatcher;\n try {\n watcher = watch(dir, { recursive: true }, onEvent);\n } catch {\n // Recursive watch may be unsupported on some platforms.\n watcher = watch(dir, onEvent);\n }\n return {\n close() {\n if (timer) clearTimeout(timer);\n watcher.close();\n },\n };\n}\n\n/** Start the `--ui` watch server. */\nexport async function startUiServer(options: UiServerOptions): Promise<UiServer> {\n const appDir = resolve(options.appDir);\n const nappletDir = resolve(options.nappletDir);\n const clients = new Set<ServerResponse>();\n const broadcast = (): void => {\n for (const res of clients) res.write(`event: rerun\\ndata: ${Date.now()}\\n\\n`);\n };\n\n const server: Server = createServer((req, res) => {\n setCors(res);\n res.setHeader('Cache-Control', 'no-store'); // never serve stale napplet code\n const path = decodeURIComponent((req.url ?? '/').split('?')[0]);\n if (path === EVENTS_ROUTE) {\n handleSse(res, clients);\n } else if (path === NAPPLET_PREFIX.slice(0, -1) || path.startsWith(NAPPLET_PREFIX)) {\n void handleNapplet(res, nappletDir, path);\n } else {\n void handleApp(res, appDir, path);\n }\n });\n\n await new Promise<void>((r) => server.listen(options.port ?? 0, '127.0.0.1', r));\n const { port } = server.address() as AddressInfo;\n const origin = `http://127.0.0.1:${port}`;\n const appUrl = `${origin}/?url=${encodeURIComponent(NAPPLET_PREFIX)}&live=1`;\n const watcher = startWatcher(nappletDir, options.debounceMs ?? 120, broadcast);\n\n return {\n origin,\n appUrl,\n notify: broadcast,\n close: () =>\n new Promise<void>((res, rej) => {\n watcher.close();\n for (const c of clients) c.end();\n clients.clear();\n server.close((err) => (err ? rej(err) : res()));\n }),\n };\n}\n","/**\n * @napplet/conformance-cli -- Static scan for forbidden napplet authority.\n *\n * A napplet runs in a `sandbox=\"allow-scripts\"` iframe at an opaque origin, so the\n * host cannot observe every forbidden browser surface at runtime. Instead we scan\n * served source for references to authority the shell owns: signer globals,\n * browser storage, direct network primitives, and external network-loaded assets.\n * This is a heuristic, but a true positive is meaningful: NIP-5D and the NAPs\n * route these capabilities through `window.napplet` domains.\n *\n * @packageDocumentation\n */\n\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { join, extname } from 'node:path';\n\n/** Patterns considered forbidden, keyed by the label reported in the verdict. */\nconst FORBIDDEN: Array<{ label: string; re: RegExp }> = [\n { label: 'window.nostr', re: /\\bwindow\\s*\\.\\s*nostr\\b/ },\n { label: 'globalThis.nostr', re: /\\bglobalThis\\s*\\.\\s*nostr\\b/ },\n { label: 'fetch', re: /\\b(?:window\\s*\\.\\s*|globalThis\\s*\\.\\s*)?fetch\\s*\\(/ },\n { label: 'XMLHttpRequest', re: /\\b(?:new\\s+)?XMLHttpRequest\\s*\\(/ },\n { label: 'WebSocket', re: /\\b(?:new\\s+)?WebSocket\\s*\\(/ },\n { label: 'localStorage', re: /\\b(?:window\\s*\\.\\s*)?localStorage\\b/ },\n { label: 'sessionStorage', re: /\\b(?:window\\s*\\.\\s*)?sessionStorage\\b/ },\n { label: 'indexedDB', re: /\\b(?:window\\s*\\.\\s*)?indexedDB\\b/ },\n { label: 'document.cookie', re: /\\bdocument\\s*\\.\\s*cookie\\b/ },\n { label: 'external img src', re: /<img\\b[^>]*\\bsrc\\s*=\\s*[\"']?https?:/i },\n { label: 'external script src', re: /<script\\b[^>]*\\bsrc\\s*=\\s*[\"']?https?:/i },\n { label: 'external stylesheet href', re: /<link\\b[^>]*\\brel\\s*=\\s*[\"']?stylesheet[\"']?[^>]*\\bhref\\s*=\\s*[\"']?https?:/i },\n { label: 'external stylesheet href', re: /<link\\b[^>]*\\bhref\\s*=\\s*[\"']?https?:[^>]*\\brel\\s*=\\s*[\"']?stylesheet[\"']?/i },\n];\n\nconst SCANNABLE = new Set(['.js', '.mjs', '.cjs', '.html', '.htm']);\nconst SKIP_DIRS = new Set(['node_modules', '.git', '.turbo', 'coverage']);\n\nasync function collectFiles(dir: string, acc: string[]): Promise<void> {\n for (const entry of await readdir(dir, { withFileTypes: true })) {\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await collectFiles(join(dir, entry.name), acc);\n } else if (entry.isFile() && SCANNABLE.has(extname(entry.name).toLowerCase())) {\n acc.push(join(dir, entry.name));\n }\n }\n}\n\n/**\n * Scan a napplet directory for forbidden browser authority references.\n *\n * @param dir - The directory of served napplet files.\n * @returns The distinct forbidden labels found (e.g. `['window.nostr']`).\n */\nexport async function scanForbiddenGlobals(dir: string): Promise<string[]> {\n const info = await stat(dir).catch(() => null);\n if (!info?.isDirectory()) return [];\n const files: string[] = [];\n await collectFiles(dir, files);\n const found = new Set<string>();\n for (const file of files) {\n const text = await readFile(file, 'utf8').catch(() => '');\n for (const { label, re } of FORBIDDEN) {\n if (re.test(text)) found.add(label);\n }\n }\n return [...found];\n}\n","/**\n * @napplet/conformance-cli -- Resolve which local artifact to conformance-test.\n *\n * Kept in its own module (not `cli.ts`, whose top level runs `main()`) so tests\n * can import the resolver without launching the CLI.\n */\n\nimport { stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\n\nasync function isFile(path: string): Promise<boolean> {\n return (await stat(path).catch(() => null))?.isFile() ?? false;\n}\n\n/**\n * Resolve the directory + index.html for a local napplet target.\n *\n * A built napplet is the artifact we test, so `dist/index.html` is preferred over\n * a sibling `index.html`. When `target` is a project root, its source\n * `index.html` (a Vite entry referencing `/src/*.ts` that cannot run in the\n * opaque-origin `srcdoc` sandbox) would otherwise shadow the built single-file\n * output and make conformance test the wrong, never-booting artifact.\n */\nexport async function resolveNappletDir(target: string): Promise<{ dir: string; indexHtml: string }> {\n const base = resolve(target);\n const dist = join(base, 'dist');\n if (await isFile(join(dist, 'index.html'))) return { dir: dist, indexHtml: join(dist, 'index.html') };\n if (await isFile(join(base, 'index.html'))) return { dir: base, indexHtml: join(base, 'index.html') };\n throw new Error(`No index.html found in ${join(dist, 'index.html')} or ${join(base, 'index.html')}. Build the napplet first.`);\n}\n","/** Capture a local build or an existing shell preview through its iframe. */\nimport { access, writeFile } from 'node:fs/promises';\nimport { execFile } from 'node:child_process';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { chromium } from 'playwright';\nimport { startScreenshotPreview } from './screenshot-preview.js';\nimport { resolveNappletDir } from './resolve-napplet.js';\n\n/** Options for capturing a shell-hosted napplet. */\nexport interface ScreenshotOptions {\n url?: string;\n directory?: string;\n artifact?: string;\n output: string;\n selector: string;\n readySelector?: string;\n width: number;\n height: number;\n delay: number;\n}\n\n/**\n * Parse screenshot arguments without invoking a browser.\n * @param args CLI arguments after the screenshot command.\n * @returns Validated capture options.\n * @example parseScreenshotArgs(['http://localhost:5173', '--output', 'preview.png'])\n */\nexport function parseScreenshotArgs(args: readonly string[]): ScreenshotOptions {\n const hasTarget = args[0] && !args[0].startsWith('--');\n const target = hasTarget ? args[0] : '.';\n const rest = hasTarget ? args.slice(1) : args;\n const remote = /^https?:\\/\\//.test(target);\n if (remote) new URL(target);\n else if (/^[a-z][a-z0-9+.-]*:/i.test(target) && !/^[a-z]:[\\\\/]/i.test(target)) throw new Error('Preview URLs must use HTTP(S)');\n const options: ScreenshotOptions = { ...(remote ? { url: target } : { directory: target }), output: 'preview.png', selector: 'iframe', width: 1200, height: 750, delay: 1500 };\n for (let index = 0; index < rest.length; index += 2) {\n const flag = rest[index];\n const value = rest[index + 1];\n if (!value || value.startsWith('--')) throw new Error(`Missing value for ${flag}`);\n switch (flag) {\n case '--output': options.output = value; break;\n case '--artifact': options.artifact = value; break;\n case '--selector': options.selector = value; break;\n case '--ready-selector': options.readySelector = value; break;\n case '--width': options.width = integer(value, flag, 1, 4096); break;\n case '--height': options.height = integer(value, flag, 1, 4096); break;\n case '--delay': options.delay = integer(value, flag, 0, 30000); break;\n default: throw new Error(`Unknown screenshot option: ${flag}`);\n }\n }\n if (!options.output.toLowerCase().endsWith('.png')) throw new Error('--output must end in .png');\n if (options.artifact && hasTarget) throw new Error('Use a target or --artifact, not both');\n return options;\n}\n\n/**\n * Save a PNG of a napplet iframe, starting a local preview when needed.\n * @param options Local build or URL, output, dimensions and readiness settings.\n * @returns Resolves after capture and cleanup of Chromium and any local preview.\n * @example await captureScreenshot(parseScreenshotArgs(['http://localhost:5173']))\n */\nexport async function captureScreenshot(options: ScreenshotOptions): Promise<void> {\n await ensureChromium();\n const preview = options.url ? undefined : await startScreenshotPreview(\n options.artifact ?? (await resolveNappletDir(options.directory ?? '.')).indexHtml,\n );\n let browser;\n try {\n browser = await chromium.launch({ channel: 'chromium' });\n const page = await browser.newPage({ viewport: { width: options.width, height: options.height } });\n page.setDefaultTimeout(30000);\n await page.goto(options.url ?? preview!.url, { waitUntil: 'domcontentloaded' });\n if (preview) {\n await page.waitForFunction(() => {\n const iframe = document.querySelector<HTMLIFrameElement>('#napplet-frame');\n return Boolean(iframe?.srcdoc);\n });\n }\n const frame = page.locator(options.selector);\n await frame.waitFor({ state: 'visible' });\n await frame.evaluate((element, size) => {\n if (!(element instanceof HTMLIFrameElement)) throw new Error('Screenshot selector must select one iframe');\n Object.assign(element.style, { width: `${size.width}px`, height: `${size.height}px`, maxWidth: 'none', maxHeight: 'none', boxSizing: 'border-box' });\n }, { width: options.width, height: options.height });\n const content = frame.contentFrame();\n await content.locator('body').waitFor({ state: 'visible' });\n if (options.readySelector) await content.locator(options.readySelector).waitFor({ state: 'visible' });\n await content.locator('body').evaluate(async () => { await document.fonts.ready; });\n await page.waitForTimeout(options.delay);\n const png = await frame.screenshot({ type: 'png', timeout: 30000 });\n await writeFile(options.output, png, { flag: 'wx' });\n } finally {\n try { await browser?.close(); } finally { await preview?.close(); }\n }\n}\n\nasync function ensureChromium(): Promise<void> {\n try { await access(chromium.executablePath()); return; } catch { /* Install the matching browser on first use. */ }\n console.error('Installing Chromium for screenshot capture...');\n const require = createRequire(import.meta.url);\n const cli = join(dirname(require.resolve('playwright/package.json')), 'cli.js');\n await promisify(execFile)(process.execPath, [cli, 'install', 'chromium'], { timeout: 180000 });\n}\n\nfunction integer(value: string, flag: string, min: number, max: number): number {\n const number = Number(value);\n if (!Number.isInteger(number) || number < min || number > max) throw new Error(`${flag} must be an integer from ${min} to ${max}`);\n return number;\n}\n","/** Disposable Kehto Paja preview for a local build. */\nimport { createServer } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { readFile, realpath } from 'node:fs/promises';\nimport { dirname, resolve, sep } from 'node:path';\nimport { startPajaServer } from '@kehto/paja';\nimport { sendFile, setCors } from './static.js';\n\n/**\n * Host a built artifact in Paja on ephemeral loopback ports.\n * @param artifact Exact index.html selected by deployment.\n * @returns Shell URL and an async cleanup handle for both servers.\n * @example const preview = await startScreenshotPreview('/project/dist/index.html');\n */\nexport async function startScreenshotPreview(artifact: string): Promise<{ url: string; close(): Promise<void> }> {\n const index = await realpath(artifact);\n const root = dirname(index);\n const html = await readFile(index);\n const server = createServer((request, response) => {\n setCors(response);\n void (async () => {\n const pathname = decodeURIComponent(new URL(request.url ?? '/', 'http://localhost').pathname);\n if (pathname === '/' || pathname === '/index.html') {\n response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n response.end(html);\n return;\n }\n const file = await realpath(resolve(root, `.${pathname}`));\n if (!file.startsWith(`${root}${sep}`)) { response.writeHead(403).end(); return; }\n if (!await sendFile(response, file)) response.writeHead(404).end();\n })().catch(() => { if (!response.headersSent) response.writeHead(404); response.end(); });\n });\n await new Promise<void>((done, reject) => {\n server.once('error', reject);\n server.listen(0, '127.0.0.1', () => { server.off('error', reject); done(); });\n });\n const closeTarget = () => new Promise<void>((done, reject) => {\n server.closeAllConnections();\n server.close((error) => error ? reject(error) : done());\n });\n try {\n const targetUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;\n const paja = await startPajaServer({ options: {\n host: '127.0.0.1', port: 0, targetUrl,\n } });\n return { url: paja.url, close: async () => { try { await paja.close(); } finally { await closeTarget(); } } };\n } catch (error) {\n await closeTarget();\n throw error;\n }\n}\n","import { createHash } from 'node:crypto';\nimport { validateEvent, verifyEvent, type Event } from 'nostr-tools/pure';\nimport { validateManifestEvent } from '@napplet/conformance';\n\n/** Verify the signed event and exact raw artifact before the browser executes it (NIP-5D §Security Considerations). */\nexport function verifyManifestArtifact(input: unknown, artifact: Uint8Array): Event {\n if (!validateEvent(input)) throw new Error('Manifest is not a Nostr event');\n // Verify a fresh object: nostr-tools caches verification on a symbol which a\n // caller could otherwise copy from a previously signed event and then mutate.\n const { id, pubkey, created_at, kind, tags, content, sig } = input as Event;\n const event: Event = { id, pubkey, created_at, kind, tags, content, sig };\n let validSignature = false;\n try {\n validSignature = verifyEvent(event);\n } catch {\n // Malformed signature fields are rejected along with cryptographic failures.\n }\n if (!validSignature) throw new Error('Manifest signature or event id is invalid');\n const verdict = validateManifestEvent(event);\n if (!verdict.ok) throw new Error(`Invalid NIP-5D manifest: ${verdict.errors.map((error) => error.message).join('; ')}`);\n const expected = event.tags.find((tag) => tag[0] === 'x')?.[1];\n const actual = createHash('sha256').update(artifact).digest('hex');\n if (actual !== expected) throw new Error(`Artifact SHA-256 mismatch: expected ${expected}, got ${actual}`);\n return event;\n}\n"],"mappings":"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","names":["readFile","writeFile","stat","join","dirname","resolve","chromium","readFile","stat","extname","stat","readFile","extname","createServer","join","normalize","resolve","normalize","join","resolve","createServer","readFile","stat","join","extname","stat","join","resolve","dirname","join","createServer","readFile","resolve","readFile","createServer","resolve","require","join","dirname","chromium","stat","dirname","join","resolve","readFile","run","writeFile"]}
@@ -2,6 +2,197 @@
2
2
  var NappletConformanceHostBundle = (() => {
3
3
  // ../core/dist/index.js
4
4
  var NAP_DOMAINS = ["shell", "relay", "identity", "storage", "inc", "theme", "keys", "media", "notify", "config", "resource", "cvm", "outbox", "upload", "intent", "ble", "webrtc", "link", "count", "lists", "serial", "fs", "common", "dm", "gamepad", "llm"];
5
+ var NAP_IMPLEMENTATIONS = {
6
+ shell: {
7
+ proposal: { url: "https://github.com/napplet/naps/pull/101", status: "merged", checkedAt: "2026-10-31" },
8
+ maturity: "supported",
9
+ rationale: "Optional readiness and environment binding has focused shim and reference-runtime coverage.",
10
+ evidence: ["packages/nap/src/shell/shim.test.ts"]
11
+ },
12
+ relay: {
13
+ proposal: { url: "https://github.com/napplet/naps/pull/2", status: "in-review", checkedAt: "2026-10-31" },
14
+ maturity: "supported",
15
+ rationale: "Relay reads, publishing, and subscription lifecycle have maintained shim and integration tests; upstream naming remains under review.",
16
+ evidence: ["packages/nap/src/relay/shim.test.ts"],
17
+ implementationReference: { revision: "cb8f8eabb2d09439e9d174cb9eb6ef2caad0eca1", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "RelayEventResult sidecar migration; subsequent changes are not covered." }
18
+ },
19
+ identity: {
20
+ proposal: { url: "https://github.com/napplet/naps/pull/102", status: "merged", checkedAt: "2026-10-31" },
21
+ maturity: "supported",
22
+ rationale: "Read-only identity queries and updates have focused binding and reference-runtime coverage.",
23
+ evidence: ["packages/nap/src/identity/shim.test.ts"]
24
+ },
25
+ storage: {
26
+ proposal: { url: "https://github.com/napplet/naps/pull/3", status: "in-review", checkedAt: "2026-10-31" },
27
+ maturity: "supported",
28
+ rationale: "Key-value binding has focused request/result tests and tutorial usage; host storage isolation remains the host responsibility.",
29
+ evidence: ["packages/nap/src/storage/shim.test.ts"]
30
+ },
31
+ inc: {
32
+ proposal: { url: "https://github.com/napplet/naps/pull/104", status: "merged", checkedAt: "2026-10-31" },
33
+ maturity: "supported",
34
+ rationale: "Topic and channel bindings have compatibility tests and authenticated-endpoint reference-runtime coverage.",
35
+ evidence: ["packages/nap/src/inc/shim.ts", "packages/conformance-cli/src/browser.test.ts"]
36
+ },
37
+ theme: {
38
+ proposal: { url: "https://github.com/napplet/naps/pull/103", status: "merged", checkedAt: "2026-10-31" },
39
+ maturity: "supported",
40
+ rationale: "Theme queries and pushed updates have focused binding and opaque-iframe integration coverage.",
41
+ evidence: ["packages/nap/src/theme/shim.test.ts", "packages/conformance-cli/src/browser.test.ts"]
42
+ },
43
+ keys: {
44
+ proposal: { url: "https://github.com/napplet/naps/pull/9", status: "in-review", checkedAt: "2026-10-31" },
45
+ maturity: "experimental",
46
+ rationale: "Keyboard binding ships, but dedicated domain regression coverage is limited; retain explicit evaluation opt-in.",
47
+ evidence: ["packages/nap/src/keys/shim.ts"]
48
+ },
49
+ media: {
50
+ proposal: { url: "https://github.com/napplet/naps/pull/10", status: "in-review", checkedAt: "2026-10-31" },
51
+ maturity: "supported",
52
+ rationale: "Session creation and playback binding have focused regression coverage; proposed torrent playback amendments are not implied.",
53
+ evidence: ["packages/nap/src/media/shim.test.ts"]
54
+ },
55
+ notify: {
56
+ proposal: { url: "https://github.com/napplet/naps/pull/11", status: "in-review", checkedAt: "2026-10-31" },
57
+ maturity: "experimental",
58
+ rationale: "Notification binding ships with limited dedicated domain regression evidence; broader host integration confidence is unverified.",
59
+ evidence: ["packages/nap/src/notify/shim.ts"]
60
+ },
61
+ config: {
62
+ proposal: { url: "https://github.com/napplet/naps/pull/14", status: "draft", checkedAt: "2026-10-31" },
63
+ maturity: "exploratory",
64
+ rationale: "Configuration binding is tested, but the upstream draft is marked MAY DEFER; use for design exploration.",
65
+ evidence: ["packages/nap/src/config/shim.test.ts"]
66
+ },
67
+ resource: {
68
+ proposal: { url: "https://github.com/napplet/naps/pull/80", status: "in-review", checkedAt: "2026-10-31" },
69
+ maturity: "supported",
70
+ rationale: "Resource fetching has focused shim and type tests; the reverted merge and reopened proposal do not change that implementation assessment.",
71
+ evidence: ["packages/nap/src/resource/shim.test.ts"],
72
+ implementationReference: { revision: "8e75eadf317d7fe809415e4755cd48f63cfb63f3", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "Shared ResourceSidecarEntry types during the sidecar migration; subsequent resource changes are not covered." }
73
+ },
74
+ cvm: {
75
+ proposal: { url: "https://github.com/napplet/naps/pull/31", status: "in-review", checkedAt: "2026-10-31" },
76
+ maturity: "experimental",
77
+ rationale: "Session and registry bindings are tested; upstream explicitly marks the contract NEEDS WORK.",
78
+ evidence: ["packages/nap/src/cvm/shim.test.ts"]
79
+ },
80
+ outbox: {
81
+ proposal: { url: "https://github.com/napplet/naps/pull/32", status: "in-review", checkedAt: "2026-10-31" },
82
+ maturity: "supported",
83
+ rationale: "Routing and subscription bindings have focused tests and an integration regression for structured-clone boundaries.",
84
+ evidence: ["packages/nap/src/outbox/shim.test.ts", "packages/nap/src/boundary-integration.test.ts"],
85
+ implementationReference: { revision: "57db924bac022cc14c67d9fca1a4d92cf1bd4725", evidence: ".planning/quick/260703-ghg-align-napplet-packages-with-nap-relayeve/260703-ghg-SUMMARY.md", scope: "RelayEventResult and stream lifecycle migration; subsequent changes are not covered." }
86
+ },
87
+ upload: {
88
+ proposal: { url: "https://github.com/napplet/naps/pull/33", status: "in-review", checkedAt: "2026-10-31" },
89
+ maturity: "supported",
90
+ rationale: "Upload binding has focused tests for request/result behavior; host rail support remains runtime-specific.",
91
+ evidence: ["packages/nap/src/upload/shim.test.ts"]
92
+ },
93
+ intent: {
94
+ proposal: { url: "https://github.com/napplet/naps/pull/91", status: "merged", checkedAt: "2026-10-31" },
95
+ maturity: "supported",
96
+ rationale: "Convention normalization and dispatch have focused binding and reference-runtime integration tests.",
97
+ evidence: ["packages/nap/src/intent/shim.test.ts"]
98
+ },
99
+ ble: {
100
+ proposal: { url: "https://github.com/napplet/naps/pull/62", status: "in-review", checkedAt: "2026-10-31" },
101
+ maturity: "experimental",
102
+ rationale: "GATT binding has unit coverage; real-device behavior across host backends is not established by these tests.",
103
+ evidence: ["packages/nap/src/ble/shim.test.ts"]
104
+ },
105
+ webrtc: {
106
+ proposal: { url: "https://github.com/napplet/naps/pull/59", status: "in-review", checkedAt: "2026-10-31" },
107
+ maturity: "experimental",
108
+ rationale: "Signaling and session binding have unit coverage; interoperable live peer sessions remain unverified here.",
109
+ evidence: ["packages/nap/src/webrtc/shim.test.ts"]
110
+ },
111
+ link: {
112
+ proposal: { url: "https://github.com/napplet/naps/pull/53", status: "in-review", checkedAt: "2026-10-31" },
113
+ maturity: "supported",
114
+ rationale: "The small link-opening binding has focused regression coverage; navigation authorization stays with the host.",
115
+ evidence: ["packages/nap/src/link/shim.test.ts"]
116
+ },
117
+ count: {
118
+ proposal: { url: "https://github.com/napplet/naps/pull/69", status: "in-review", checkedAt: "2026-10-31" },
119
+ maturity: "experimental",
120
+ rationale: "Count binding is tested, but accuracy and aggregation across real relay backends remain outside that evidence.",
121
+ evidence: ["packages/nap/src/count/shim.test.ts"]
122
+ },
123
+ lists: {
124
+ proposal: { url: "https://github.com/napplet/naps/pull/68", status: "in-review", checkedAt: "2026-10-31" },
125
+ maturity: "experimental",
126
+ rationale: "List mutation binding is tested; concurrent real-host mutation behavior is not established by binding tests.",
127
+ evidence: ["packages/nap/src/lists/shim.test.ts"]
128
+ },
129
+ serial: {
130
+ proposal: { url: "https://github.com/napplet/naps/pull/61", status: "in-review", checkedAt: "2026-10-31" },
131
+ maturity: "experimental",
132
+ rationale: "Serial binding has unit coverage; device and native-host integration evidence is limited.",
133
+ evidence: ["packages/nap/src/serial/shim.test.ts"]
134
+ },
135
+ fs: {
136
+ proposal: { url: "https://github.com/napplet/naps/pull/88", status: "in-review", checkedAt: "2026-10-31" },
137
+ maturity: "experimental",
138
+ rationale: "Filesystem binding has MVP coverage; portability and authorization across storage backends remain under evaluation.",
139
+ evidence: ["packages/nap/src/fs/shim.test.ts"]
140
+ },
141
+ common: {
142
+ proposal: { url: "https://github.com/napplet/naps/pull/67", status: "in-review", checkedAt: "2026-10-31" },
143
+ maturity: "experimental",
144
+ rationale: "Social action binding is tested; broader host integration and evolving action semantics warrant explicit opt-in.",
145
+ evidence: ["packages/nap/src/common/shim.test.ts"]
146
+ },
147
+ dm: {
148
+ proposal: { url: "https://github.com/napplet/naps/pull/74", status: "draft", checkedAt: "2026-10-31" },
149
+ maturity: "experimental",
150
+ rationale: "Direct-message binding has shim and type coverage; transport interoperability is not established by those tests.",
151
+ evidence: ["packages/nap/src/dm/shim.test.ts"]
152
+ },
153
+ gamepad: {
154
+ proposal: { url: "https://github.com/napplet/naps/pull/130", status: "in-review", checkedAt: "2026-10-31" },
155
+ maturity: "supported",
156
+ rationale: "Cross-repository delivery includes binding, security, runtime, and conformance verification; the reopened proposal is still subject to change.",
157
+ evidence: ["packages/nap/src/gamepad/shim.test.ts", ".planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md"],
158
+ implementationReference: { revision: "0b28f77e3dfa0f07dd7c0f9d61a7090ee99f3a5e", evidence: ".planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md", scope: "Phase 163 binding, runtime isolation, and conformance verification." }
159
+ },
160
+ llm: {
161
+ proposal: { url: "https://github.com/napplet/naps/pull/134", status: "draft", checkedAt: "2026-10-31" },
162
+ maturity: "exploratory",
163
+ rationale: "Early published draft has binding and mock-runtime tests; provider behavior and API design remain exploratory.",
164
+ evidence: ["packages/nap/src/llm/shim.test.ts", "packages/conformance-cli/src/browser.test.ts"]
165
+ }
166
+ };
167
+ for (const entry of Object.values(NAP_IMPLEMENTATIONS)) {
168
+ Object.freeze(entry.proposal);
169
+ Object.freeze(entry.evidence);
170
+ if (entry.implementationReference) Object.freeze(entry.implementationReference);
171
+ Object.freeze(entry);
172
+ }
173
+ Object.freeze(NAP_IMPLEMENTATIONS);
174
+ function getNapImplementation(domain) {
175
+ return Object.hasOwn(NAP_IMPLEMENTATIONS, domain) ? NAP_IMPLEMENTATIONS[domain] : void 0;
176
+ }
177
+ function selectNapDomains(options) {
178
+ const allowed = new Set(options.allowedMaturities ?? ["supported"]);
179
+ const optIn = new Set(options.optIn ?? []);
180
+ const domains = [];
181
+ const excluded = [];
182
+ for (const domain of new Set(options.candidates)) {
183
+ const metadata = getNapImplementation(domain);
184
+ if (!metadata) {
185
+ excluded.push({ domain, reason: "unknown-domain" });
186
+ } else if (!metadata.maturity || !["supported", "experimental", "exploratory"].includes(metadata.maturity)) {
187
+ excluded.push({ domain, reason: "unclassified" });
188
+ } else if (allowed.has(metadata.maturity) || optIn.has(domain)) {
189
+ domains.push(domain);
190
+ } else {
191
+ excluded.push({ domain, reason: "maturity-not-allowed", maturity: metadata.maturity });
192
+ }
193
+ }
194
+ return { domains, excluded };
195
+ }
5
196
  function createDispatch() {
6
197
  const handlers = /* @__PURE__ */ new Map();
7
198
  function registerNap2(domain, handler) {
@@ -1110,13 +1301,15 @@ var NappletConformanceHostBundle = (() => {
1110
1301
  }
1111
1302
  function createReferenceHandler(options) {
1112
1303
  const initialized = /* @__PURE__ */ new Set();
1304
+ const hostDomains = options.domains ? new Set(options.domains) : null;
1113
1305
  const llm = createLlmHandlers();
1114
1306
  const handle2 = (endpoint, envelope) => {
1115
1307
  const type = getEnvelopeType(envelope);
1116
1308
  const verdict = validateEnvelope(envelope);
1117
1309
  options.records.push({ envelope, verdict, timestamp: options.now() });
1118
1310
  if (!type || !verdict.ok) return [];
1119
- const domains = endpoint.domains ?? NAP_DOMAINS;
1311
+ const candidates = endpoint.domains ?? NAP_DOMAINS;
1312
+ const domains = hostDomains ? candidates.filter((domain) => hostDomains.has(domain)) : candidates;
1120
1313
  if (!domains.includes(type.slice(0, type.indexOf(".")))) return [];
1121
1314
  if (type === "shell.ready") {
1122
1315
  if (initialized.has(endpoint.id)) return [];
@@ -1222,6 +1415,7 @@ var NappletConformanceHostBundle = (() => {
1222
1415
  const handleFrom = createReferenceHandler({
1223
1416
  now: options.now ?? Date.now,
1224
1417
  records,
1418
+ domains: options.domains,
1225
1419
  queueDelivery: deliveries.queue,
1226
1420
  intents: createIntentHandlers(deliveries.queue, options.manifests, options.defaults)
1227
1421
  });
@@ -1310,7 +1504,7 @@ ${installed}`;
1310
1504
  return response.text();
1311
1505
  }
1312
1506
  async function bootOnce(domains, opts, doc, win) {
1313
- const shell = createReferenceShell();
1507
+ const shell = createReferenceShell({ domains });
1314
1508
  const iframe = doc.createElement("iframe");
1315
1509
  iframe.setAttribute("sandbox", opts.allowSameOrigin ? "allow-scripts allow-same-origin" : "allow-scripts");
1316
1510
  iframe.setAttribute("allow", "gamepad 'none'");
@@ -1378,7 +1572,9 @@ ${installed}`;
1378
1572
  allowSameOrigin: options.allowSameOrigin ?? false,
1379
1573
  runtimePreludeSource: options.runtimePreludeSource
1380
1574
  };
1381
- const primary = await bootOnce(NAP_DOMAINS, resolved, doc, win);
1575
+ const candidates = options.domains ?? NAP_DOMAINS;
1576
+ const domains = options.maturity ? selectNapDomains({ ...options.maturity, candidates }).domains : candidates;
1577
+ const primary = await bootOnce(domains, resolved, doc, win);
1382
1578
  let degraded = null;
1383
1579
  if (options.runDegraded ?? true) {
1384
1580
  const d = await bootOnce([], resolved, doc, win);