From 23f07d15769d414f391c63a9b34483df5b4031e1 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:09:12 -0700 Subject: [PATCH 01/24] docs(163): plan GAMEPAD implementation across repositories Co-Authored-By: Codex --- .planning/ROADMAP.md | 11 ++++++++++ .planning/STATE.md | 4 ++++ .../.gitkeep | 1 + .../163-01-PLAN.md | 21 +++++++++++++++++++ .../163-02-PLAN.md | 21 +++++++++++++++++++ .../163-03-PLAN.md | 21 +++++++++++++++++++ .../163-CONTEXT.md | 19 +++++++++++++++++ .../163-RESEARCH.md | 11 ++++++++++ 8 files changed, 109 insertions(+) create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/.gitkeep create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-01-PLAN.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-02-PLAN.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-03-PLAN.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-CONTEXT.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-RESEARCH.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 6ecfc2a2..72be5e22 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -929,3 +929,14 @@ Plans: - [x] 162-01-PLAN.md — Current-schema writer and readers - [x] 162-02-PLAN.md — CLI format boundary and event migration - [x] 162-03-PLAN.md — Docs, release metadata and draft PR shipment + +### Phase 163: NAP-GAMEPAD across repositories + +**Goal:** [To be planned] +**Requirements**: TBD +**Depends on:** Phase 162 +**Plans:** 0 plans + +Plans: + +- [ ] TBD (run /gsd-plan-phase 163 to break down) diff --git a/.planning/STATE.md b/.planning/STATE.md index 317f1609..66a1eb8e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -715,3 +715,7 @@ Resume: Phase 142 TERMINAL-COMPLETE — all 13 VER-IDs (VER-01..13) verified PAS | Phase 161 P12 | 4min | 1 tasks | 5 files | | Phase 161-ad-hoc-convention-package-contracts P15 | 2min | 1 tasks | 4 files | | Phase 161 P16 | 4 min | 2 tasks | 5 files | + +### Phase 163 roadmap evolution + +- Added NAP-GAMEPAD across repositories, including boilerplate and relevant Kehto runtime consumers. Planning and implementation evidence live in phases/163-nap-gamepad-across-repositories. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/.gitkeep b/.planning/phases/163-nap-gamepad-across-repositories/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/.gitkeep @@ -0,0 +1 @@ + diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-01-PLAN.md b/.planning/phases/163-nap-gamepad-across-repositories/163-01-PLAN.md new file mode 100644 index 00000000..bfd11fcd --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-01-PLAN.md @@ -0,0 +1,21 @@ +--- +phase: 163-nap-gamepad-across-repositories +plan: 01 +type: execute +wave: 01 +depends_on: [] +autonomous: true +requirements: [GP-API, GP-WIRE, GP-TRUST] +--- + +# Portable API and injection + +Non-normative execution plan; all protocol choices defer to the living references in 163-CONTEXT.md. + + +Add core schemas/API/domain identity; NAP types, cached shim, SDK helpers and exports; runtime injection and sdk namespace/subpath; exact wire and lifecycle tests. Verify through the runtime-provided namespace before expanding to runtime consumers. + + + +Verify the named requirements against current source, built import surfaces and task-relevant unit/browser tests. Record actual commands/results and unresolved specification conflicts. Preserve unrelated work. Commit each green checkpoint. Do not claim completion from import presence alone; prove the live/neutral input boundary and teardown. + diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-02-PLAN.md b/.planning/phases/163-nap-gamepad-across-repositories/163-02-PLAN.md new file mode 100644 index 00000000..453eec6a --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-02-PLAN.md @@ -0,0 +1,21 @@ +--- +phase: 163-nap-gamepad-across-repositories +plan: 02 +type: execute +wave: 02 +depends_on: [163-01] +autonomous: true +requirements: [GP-ISOLATION, GP-WIRE, GP-TRUST] +--- + +# Runtime isolation and conformance + +Non-normative execution plan; all protocol choices defer to the living references in 163-CONTEXT.md. + + +Implement shell-side focus-scoped polling/snapshots in Kehto and its Paja/host consumers. Add reference harness state behavior, exact envelope validation, native controller denial and real Chromium multi-napplet focus/visibility/unsubscribe tests. Keep physical device handles outside napplets. + + + +Verify the named requirements against current source, built import surfaces and task-relevant unit/browser tests. Record actual commands/results and unresolved specification conflicts. Preserve unrelated work. Commit each green checkpoint. Do not claim completion from import presence alone; prove the live/neutral input boundary and teardown. + diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-03-PLAN.md b/.planning/phases/163-nap-gamepad-across-repositories/163-03-PLAN.md new file mode 100644 index 00000000..8ec83dbc --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-03-PLAN.md @@ -0,0 +1,21 @@ +--- +phase: 163-nap-gamepad-across-repositories +plan: 03 +type: execute +wave: 03 +depends_on: [163-02] +autonomous: true +requirements: [GP-REPOS, GP-DOCS, GP-SHIP] +--- + +# All repositories, docs and shipment + +Non-normative execution plan; all protocol choices defer to the living references in 163-CONTEXT.md. + + +Audit all seven organization repos plus Kehto and every package. Add applicable generator/client/template integrations and evidence for unchanged surfaces. Sync package/site/skill/boilerplate docs, preserve keyboard/pointer fallbacks, run required gates, prepare release metadata and open PRs for each changed repo. + + + +Verify the named requirements against current source, built import surfaces and task-relevant unit/browser tests. Record actual commands/results and unresolved specification conflicts. Preserve unrelated work. Commit each green checkpoint. Do not claim completion from import presence alone; prove the live/neutral input boundary and teardown. + diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-CONTEXT.md b/.planning/phases/163-nap-gamepad-across-repositories/163-CONTEXT.md new file mode 100644 index 00000000..55f970d6 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-CONTEXT.md @@ -0,0 +1,19 @@ +# Phase 163 context + +Non-normative implementation planning. The living sources govern; this document creates no protocol requirements. + +User objective: implement NAP-GAMEPAD with coverage across every napplet repository and package, including boilerplate. Canonical proposal: https://github.com/napplet/naps/pull/108 (current head inspected: 0b28f77e3dfa0f07dd7c0f9d61a7090ee99f3a5e). Canonical NIP-5D: https://github.com/nostr-protocol/nips/pull/2303 (current head inspected: 020cb8b33a9e4c6b8ca4b2f9d0ed0a67843b68f7). Recheck heads before shipping. + +Implementation acceptance references: + +- GP-API: implement the four operations and initial cached state from NAP-GAMEPAD API Surface, with exported types and npm/JSR subpaths. +- GP-WIRE: implement exactly the three fire-and-forget envelopes from Wire Protocol; reject malformed state without adding requests, results or ids. +- GP-ISOLATION: implement Shell Behavior focus, neutral snapshots, visibility, connections, unavailable reasons, unsubscribe and session teardown; test native denial in a real browser. +- GP-TRUST: preserve NIP-5D Transport parent/source checks, domain presence and injection timing. +- GP-REPOS: audit every napplet organization repo and every web monorepo package; implement relevant integration and document evidence for surfaces needing no change. Include Kehto because it supplies runtime/Paja services consumed by these packages. +- GP-DOCS: update package, site, skills and boilerplate guidance with optional availability, focus transitions and keyboard/pointer fallback. +- GP-SHIP: meaningful unit/browser/package export tests; required repository build/type/unit/doc/slop checks; changesets/release metadata; atomic commits and open PRs for changed repos. + +Native API replacement is an unresolved spec conflict: NAP-GAMEPAD Web Projection Guidance permits navigator.getGamepads replacement, whereas current NIP-5D Security Rationale item 5 limits runtime injection to window.napplet. Preserve the native operation and deny physical access through Permissions-Policy; the required domain API does not need that optional replacement. Surface this conflict in PRs and coverage evidence. + +Existing unrelated work in the original worktrees remains untouched. Work happens on feat/nap-gamepad in separate clones/worktrees based on each repository default branch. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-RESEARCH.md b/.planning/phases/163-nap-gamepad-across-repositories/163-RESEARCH.md new file mode 100644 index 00000000..9558f59b --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-RESEARCH.md @@ -0,0 +1,11 @@ +# Research + +Non-normative; defer to the linked living specifications in CONTEXT.md. + +The active web monorepo separates protocol/global types in core, per-domain types/shim/helpers in nap, selected runtime injection in shim, and late-bound application wrappers in sdk. npm/JSR export maps and tsup entrypoints require explicit new subpaths. Existing core dispatch registration is private package plumbing; do not add wire handshakes. + +Conformance has JSON envelope validators, endpoint-bound reference responses, a browser boot harness and separate CLI/Paja transport. Add state/subscription coverage and controller denial to the existing boundaries. The browser harness presently creates allow-scripts frames without controller policy denial. Kehto owns production runtime/Paja device and focus mediation and therefore needs corresponding integration. + +Organization inventory: web, naps, boilerplate, .github, cvm, nappagochi, migrate. naps contains published GAMEPAD proposal #108; .github is community metadata. Audit other repos against their current default branches, not dirty existing local worktrees. Boilerplate documentation contains obsolete NIP-5A aggregate-hash guidance; correct it against current NIP-5D while adding controller authoring guidance. + +Verification will use canonical connected/neutral/blocked examples, multi-session focus transitions and native Permissions-Policy denial in Chromium. Test public import surfaces in built npm packages and JSR source maps. Do not require controllers on machines that lack them or declare gamepad as a hard capability for apps retaining other input. From ae7ecb276e58cf4f5cbce0c55cfc9eb94c8a81f5 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:28:36 -0700 Subject: [PATCH 02/24] feat(gamepad): add cached NAP binding and SDK exports Add the proposed GAMEPAD types, exact fire-and-forget envelopes, selected runtime injection, synchronous cached reads, and npm/JSR public entrypoints. Test mutation isolation, independent listeners, source trust and built SDK imports. Co-Authored-By: Codex --- packages/core/src/envelope.ts | 4 +- packages/core/src/index.ts | 2 + packages/core/src/types.ts | 2 + packages/core/src/types/gamepad.ts | 40 +++++++ packages/core/src/types/global.ts | 3 + packages/nap/jsr.json | 6 +- packages/nap/package.json | 16 +++ packages/nap/src/gamepad/index.ts | 10 ++ packages/nap/src/gamepad/sdk.ts | 37 +++++++ packages/nap/src/gamepad/shim.test.ts | 129 ++++++++++++++++++++++ packages/nap/src/gamepad/shim.ts | 88 +++++++++++++++ packages/nap/src/gamepad/state.ts | 49 ++++++++ packages/nap/src/gamepad/types.ts | 17 +++ packages/nap/tsup.config.ts | 4 + packages/sdk/jsr.json | 5 +- packages/sdk/package.json | 7 +- packages/sdk/src/gamepad.ts | 30 +++++ packages/sdk/src/index.ts | 2 + packages/sdk/src/nap-runtime.ts | 2 + packages/sdk/src/nap-types.ts | 2 + packages/sdk/test/gamepad.test.mjs | 35 ++++++ packages/sdk/tsup.config.ts | 2 +- packages/shim/src/gamepad.test.ts | 38 +++++++ packages/shim/src/runtime-globals-core.ts | 2 + packages/shim/src/runtime.ts | 5 + 25 files changed, 531 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/types/gamepad.ts create mode 100644 packages/nap/src/gamepad/index.ts create mode 100644 packages/nap/src/gamepad/sdk.ts create mode 100644 packages/nap/src/gamepad/shim.test.ts create mode 100644 packages/nap/src/gamepad/shim.ts create mode 100644 packages/nap/src/gamepad/state.ts create mode 100644 packages/nap/src/gamepad/types.ts create mode 100644 packages/sdk/src/gamepad.ts create mode 100644 packages/sdk/test/gamepad.test.mjs create mode 100644 packages/shim/src/gamepad.test.ts diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 5f0a2291..d3b00937 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -69,7 +69,7 @@ export interface NappletMessage { * const isValid = NAP_DOMAINS.includes(domain); // true * ``` */ -export type NapDomain = 'shell' | 'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'fs' | 'common' | 'dm'; +export type NapDomain = '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'; /** * Runtime-accessible constant array of all NAP domain names. @@ -80,4 +80,4 @@ export type NapDomain = 'shell' | 'relay' | 'identity' | 'storage' | 'inc' | 'th * const selected = NAP_DOMAINS.filter((domain) => domain !== 'ble'); * ``` */ -export const NAP_DOMAINS: readonly NapDomain[] = ['shell', 'relay', 'identity', 'storage', 'inc', 'theme', 'keys', 'media', 'notify', 'config', 'resource', 'cvm', 'outbox', 'upload', 'intent', 'ble', 'webrtc', 'link', 'count', 'lists', 'serial', 'fs', 'common', 'dm'] as const; +export const NAP_DOMAINS: readonly NapDomain[] = ['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'] as const; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e0dc325c..7b434569 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -237,3 +237,5 @@ export { } from './boundary.js'; export type { ShellApi, ShellCapabilities, ShellEnvironment } from './types/shell.js'; + +export type * from './types/gamepad.js'; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 2008c29a..9d48651f 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -36,3 +36,5 @@ export type { } from './types/global/runtime-api.js'; export type * from './types/shell.js'; + +export type * from './types/gamepad.js'; diff --git a/packages/core/src/types/gamepad.ts b/packages/core/src/types/gamepad.ts new file mode 100644 index 00000000..3f3e776e --- /dev/null +++ b/packages/core/src/types/gamepad.ts @@ -0,0 +1,40 @@ +/** Controller snapshots from the living NAP-GAMEPAD proposal: https://github.com/napplet/naps/pull/108. */ +import type { Subscription } from './nostr.js'; + +/** One controller button, with device-reported press and touch state. */ +export interface GamepadButton { + value: number; + pressed: boolean; + touched: boolean; +} + +/** A connected controller; its index is stable for the connection's lifetime. */ +export interface Gamepad { + index: number; + id: string; + mapping: string; + connected: boolean; + timestamp: number; + axes: number[]; + buttons: GamepadButton[]; +} + +/** An empty slot remains null, preserving the runtime's complete slot list. */ +export type GamepadSlot = Gamepad | null; +export type GamepadUnavailableReason = 'blocked' | 'unavailable'; + +/** Complete view of controllers for one napplet, with focus-scoped values. */ +export interface GamepadState { + available: boolean; + reason?: GamepadUnavailableReason; + focused: boolean; + pads: GamepadSlot[]; +} + +/** The optional, runtime-injected controller domain. Reads never wait for a request. */ +export interface GamepadApi { + readonly available: boolean; + readonly focused: boolean; + getGamepads(): GamepadSlot[]; + onChange(handler: (state: GamepadState) => void): Subscription; +} diff --git a/packages/core/src/types/global.ts b/packages/core/src/types/global.ts index 4293ae73..d9b97bb7 100644 --- a/packages/core/src/types/global.ts +++ b/packages/core/src/types/global.ts @@ -1,3 +1,4 @@ +import type { GamepadApi } from './gamepad.js'; import type { ShellApi } from './shell.js'; import type { RelayApi, IncApi, StorageApi, KeysApi } from './global/nostr-api.js'; @@ -49,6 +50,8 @@ import type { * domain to the napplet; absence means the domain is unavailable. */ export interface NappletGlobal { + /** Optional shell-mediated controller input, scoped to this napplet’s focus. */ + gamepad?: GamepadApi; /** Optional shell environment; other domains work without it. */ shell?: ShellApi; /** diff --git a/packages/nap/jsr.json b/packages/nap/jsr.json index 6a57e269..7aa197be 100644 --- a/packages/nap/jsr.json +++ b/packages/nap/jsr.json @@ -102,7 +102,11 @@ "./shell": "./src/shell/index.ts", "./shell/types": "./src/shell/types.ts", "./shell/shim": "./src/shell/shim.ts", - "./shell/sdk": "./src/shell/sdk.ts" + "./shell/sdk": "./src/shell/sdk.ts", + "./gamepad": "./src/gamepad/index.ts", + "./gamepad/types": "./src/gamepad/types.ts", + "./gamepad/shim": "./src/gamepad/shim.ts", + "./gamepad/sdk": "./src/gamepad/sdk.ts" }, "imports": { "@napplet/core": "jsr:@napplet/core@^0.33.0", diff --git a/packages/nap/package.json b/packages/nap/package.json index 51511e18..f3098cb9 100644 --- a/packages/nap/package.json +++ b/packages/nap/package.json @@ -403,6 +403,22 @@ "./shell/sdk": { "types": "./dist/shell/sdk.d.ts", "import": "./dist/shell/sdk.js" + }, + "./gamepad": { + "types": "./dist/gamepad/index.d.ts", + "import": "./dist/gamepad/index.js" + }, + "./gamepad/types": { + "types": "./dist/gamepad/types.d.ts", + "import": "./dist/gamepad/types.js" + }, + "./gamepad/shim": { + "types": "./dist/gamepad/shim.d.ts", + "import": "./dist/gamepad/shim.js" + }, + "./gamepad/sdk": { + "types": "./dist/gamepad/sdk.d.ts", + "import": "./dist/gamepad/sdk.js" } }, "files": [ diff --git a/packages/nap/src/gamepad/index.ts b/packages/nap/src/gamepad/index.ts new file mode 100644 index 00000000..0eb1001b --- /dev/null +++ b/packages/nap/src/gamepad/index.ts @@ -0,0 +1,10 @@ +/** NAP-GAMEPAD package entrypoint. Canonical proposal: https://github.com/napplet/naps/pull/108. */ +import { registerNap } from '@napplet/core'; +import { DOMAIN } from './types.js'; +export { DOMAIN } from './types.js'; +export type * from './types.js'; +export { gamepad, getGamepads, onChange, installGamepadShim, handleGamepadMessage } from './shim.js'; +export { gamepadGetGamepads, gamepadAvailable, gamepadFocused, gamepadOnChange } from './sdk.js'; +export { isGamepadState, copyGamepadState } from './state.js'; + +registerNap(DOMAIN, () => { /* The runtime supplies the domain handler. */ }); diff --git a/packages/nap/src/gamepad/sdk.ts b/packages/nap/src/gamepad/sdk.ts new file mode 100644 index 00000000..004e1a1b --- /dev/null +++ b/packages/nap/src/gamepad/sdk.ts @@ -0,0 +1,37 @@ +/** Application-side GAMEPAD helpers; the runtime injects the domain. */ +import type { GamepadSlot, GamepadState, NappletGlobal, Subscription } from '@napplet/core'; + +function requireGamepad(): NonNullable { + const napplet = (window as Window & { napplet?: NappletGlobal }).napplet; + if (!napplet?.gamepad) throw new Error('window.napplet.gamepad is unavailable -- runtime did not inject this domain'); + return napplet.gamepad; +} + +/** + * Read the runtime's latest controller slots without a wire request. + * @returns Complete cached slots. + * @example const pads = gamepadGetGamepads(); + */ +export function gamepadGetGamepads(): GamepadSlot[] { return requireGamepad().getGamepads(); } + +/** + * Read whether the runtime can access controllers. + * @returns Cached availability; true before the first state. + * @example if (gamepadAvailable()) renderControllers(); + */ +export function gamepadAvailable(): boolean { return requireGamepad().available; } + +/** + * Read whether this napplet currently receives live controller values. + * @returns Cached input focus; false before the first state. + * @example if (gamepadFocused()) updatePlayers(); + */ +export function gamepadFocused(): boolean { return requireGamepad().focused; } + +/** + * Observe shell-provided controller snapshots. + * @param handler Receives complete focused or neutral snapshots. + * @returns Subscription with close() to stop local delivery. + * @example const sub = gamepadOnChange((state) => render(state)); + */ +export function gamepadOnChange(handler: (state: GamepadState) => void): Subscription { return requireGamepad().onChange(handler); } diff --git a/packages/nap/src/gamepad/shim.test.ts b/packages/nap/src/gamepad/shim.test.ts new file mode 100644 index 00000000..d85ab9a3 --- /dev/null +++ b/packages/nap/src/gamepad/shim.test.ts @@ -0,0 +1,129 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { GamepadState } from './types.js'; + +// Connected and neutral examples from NAP-GAMEPAD Wire Protocol, proposal #108. +const focused: GamepadState = { + available: true, focused: true, + pads: [null, { + index: 1, id: 'Xbox Wireless Controller', mapping: 'standard', connected: true, + timestamp: 18235.4, axes: [0, -1, 0, 0], + buttons: [{ value: 1, pressed: true, touched: true }, { value: 0, pressed: false, touched: false }], + }], +}; +const neutral: GamepadState = { + ...focused, focused: false, + pads: [null, { ...focused.pads[1]!, timestamp: 0, axes: [0, 0, 0, 0], buttons: [{ value: 0, pressed: false, touched: false }, { value: 0, pressed: false, touched: false }] }], +}; + +let post: ReturnType; +beforeEach(() => { + vi.resetModules(); + post = vi.fn(); + vi.stubGlobal('window', { parent: { postMessage: post } }); +}); +afterEach(() => vi.unstubAllGlobals()); + +describe('NAP-GAMEPAD cached binding', () => { + it('starts neutral and subscribes once with no correlation id', async () => { + const shim = await import('./shim.js'); + const cleanup = shim.installGamepadShim(); + const duplicate = shim.installGamepadShim(); + expect(shim.gamepad.available).toBe(true); + expect(shim.gamepad.focused).toBe(false); + expect(shim.getGamepads()).toEqual([]); + expect(post.mock.calls).toEqual([[{ type: 'gamepad.subscribe' }, '*']]); + duplicate(); + expect(post).toHaveBeenCalledTimes(1); + cleanup(); cleanup(); + expect(post.mock.calls[1]).toEqual([{ type: 'gamepad.unsubscribe' }, '*']); + expect(post).toHaveBeenCalledTimes(2); + }); + + it('preserves empty slots, indexes and complete focused/neutral/blocked states', async () => { + const shim = await import('./shim.js'); + const handler = vi.fn(); + const cleanup = shim.installGamepadShim(); + shim.onChange(handler); + for (const state of [focused, neutral, { available: false, reason: 'blocked', focused: false, pads: [] }]) { + shim.handleGamepadMessage({ type: 'gamepad.state', ...state }); + expect(handler).toHaveBeenLastCalledWith(state); + expect(shim.gamepad.available).toBe(state.available); + expect(shim.gamepad.focused).toBe(state.focused); + expect(shim.getGamepads()).toEqual(state.pads); + } + expect(post).toHaveBeenCalledTimes(1); + cleanup(); + }); + + it('copies nested input separately for readers and each listener', async () => { + const shim = await import('./shim.js'); + const cleanup = shim.installGamepadShim(); + shim.onChange((state) => { state.pads[1]!.axes[1] = 0; state.pads[1]!.buttons[0].pressed = false; }); + const other = vi.fn(); + shim.onChange(other); + shim.handleGamepadMessage({ type: 'gamepad.state', ...focused }); + const read = shim.getGamepads(); + read[1]!.axes[1] = 1; + expect(other).toHaveBeenCalledWith(focused); + expect(shim.getGamepads()).toEqual(focused.pads); + cleanup(); + }); + + it('gives repeated handlers independent, idempotent subscription handles', async () => { + const shim = await import('./shim.js'); + const handler = vi.fn(); + const first = shim.onChange(handler); + const second = shim.onChange(handler); + first.close(); first.close(); + shim.handleGamepadMessage({ type: 'gamepad.state', ...focused }); + expect(handler).toHaveBeenCalledTimes(1); + second.close(); + const third = shim.onChange(handler); + first.close(); + shim.handleGamepadMessage({ type: 'gamepad.state', ...neutral }); + expect(handler).toHaveBeenCalledTimes(2); + third.close(); + }); + + it('defers listeners added during delivery until the next snapshot', async () => { + const shim = await import('./shim.js'); + const later = vi.fn(); + const first = shim.onChange(() => { first.close(); shim.onChange(later); }); + shim.handleGamepadMessage({ type: 'gamepad.state', ...focused }); + expect(later).not.toHaveBeenCalled(); + shim.handleGamepadMessage({ type: 'gamepad.state', ...neutral }); + expect(later).toHaveBeenCalledExactlyOnceWith(neutral); + }); + + it('ignores malformed snapshots, request envelopes and state after cleanup', async () => { + const shim = await import('./shim.js'); + const cleanup = shim.installGamepadShim(); + const handler = vi.fn(); + shim.onChange(handler); + for (const message of [ + { type: 'gamepad.subscribe' }, + { type: 'gamepad.state', ...focused, id: 'forbidden' }, + { type: 'gamepad.state', ...focused, focused: false }, + { type: 'gamepad.state', available: false, focused: false, pads: [] }, + { type: 'gamepad.state', ...focused, pads: [false] }, + ]) shim.handleGamepadMessage(message); + expect(handler).not.toHaveBeenCalled(); + cleanup(); + shim.handleGamepadMessage({ type: 'gamepad.state', ...focused }); + expect(handler).not.toHaveBeenCalled(); + const nextCleanup = shim.installGamepadShim(); + expect(shim.gamepad.focused).toBe(false); + expect(shim.getGamepads()).toEqual([]); + nextCleanup(); + }); + + it('checks schema bounds without restricting future mapping strings', async () => { + const { isGamepadState } = await import('./state.js'); + expect(isGamepadState(focused)).toBe(true); + expect(isGamepadState(neutral)).toBe(true); + expect(isGamepadState({ ...focused, pads: [{ ...focused.pads[1], mapping: 'future-layout' }] })).toBe(true); + for (const axis of [-2, 2, NaN, Infinity]) expect(isGamepadState({ ...focused, pads: [{ ...focused.pads[1], axes: [axis] }] })).toBe(false); + expect(isGamepadState({ ...focused, reason: 'blocked' })).toBe(false); + expect(isGamepadState({ available: false, reason: 'unavailable', focused: false, pads: [] })).toBe(true); + }); +}); diff --git a/packages/nap/src/gamepad/shim.ts b/packages/nap/src/gamepad/shim.ts new file mode 100644 index 00000000..414edc00 --- /dev/null +++ b/packages/nap/src/gamepad/shim.ts @@ -0,0 +1,88 @@ +/** Cached NAP-GAMEPAD binding. The runtime owns controller access and focus. */ +import type { GamepadApi, GamepadSlot, GamepadState, Subscription } from '@napplet/core'; +import { postToShell } from '../boundary.js'; +import { copyGamepadState, isGamepadState } from './state.js'; + +let state: GamepadState = { available: true, focused: false, pads: [] }; +let installed = false; +let subscribed = false; +const handlers = new Set<{ handler: (state: GamepadState) => void }>(); + +function subscribe(): void { + if (subscribed) return; + subscribed = true; + try { postToShell({ type: 'gamepad.subscribe' }); } + catch (error) { subscribed = false; throw error; } +} + +/** + * Read the latest complete slot list synchronously. + * @returns Cached slots; empty before the first snapshot. + * @example const pads = getGamepads(); + */ +export function getGamepads(): GamepadSlot[] { + subscribe(); + return copyGamepadState(state).pads; +} + +/** + * Observe each incoming complete state. + * @param handler Receives an independent snapshot for each shell update. + * @returns An idempotent local subscription handle. + * @example const sub = onChange((state) => render(state)); + */ +export function onChange(handler: (state: GamepadState) => void): Subscription { + const entry = { handler }; + handlers.add(entry); + try { subscribe(); } + catch (error) { handlers.delete(entry); throw error; } + return { close: () => { handlers.delete(entry); } }; +} + +/** Optional runtime domain; availability and focus are snapshot-backed getters. */ +export const gamepad: GamepadApi = { + get available() { subscribe(); return state.available; }, + get focused() { subscribe(); return state.focused; }, + getGamepads, + onChange, +}; + +/** + * Accept shell state through the central parent-source-checked message router. + * @param message Incoming envelope. + * @returns Nothing; malformed or unrecognized envelopes are ignored. + * @example handleGamepadMessage({ type: 'gamepad.state', available: true, focused: false, pads: [] }); + */ +export function handleGamepadMessage(message: { type: string; [key: string]: unknown }): void { + if (!subscribed || message.type !== 'gamepad.state' || 'id' in message || !isGamepadState(message)) return; + state = copyGamepadState(message); + const snapshot = copyGamepadState(state); + const recipients = [...handlers]; + for (const entry of recipients) { + if (handlers.has(entry)) entry.handler(copyGamepadState(snapshot)); + } +} + +/** + * Subscribe when the runtime installs this domain; cleanup stops the session's input. + * @returns Idempotent cleanup that unsubscribes and clears the cached state. + * @example const cleanup = installGamepadShim(); + */ +export function installGamepadShim(): () => void { + if (installed) return () => undefined; + installed = true; + try { subscribe(); } + catch (error) { installed = false; throw error; } + let closed = false; + return () => { + if (closed) return; + closed = true; + try { if (subscribed) postToShell({ type: 'gamepad.unsubscribe' }); } + finally { + subscribed = false; + installed = false; + handlers.clear(); + state = { available: true, focused: false, pads: [] }; + } + }; +} diff --git a/packages/nap/src/gamepad/state.ts b/packages/nap/src/gamepad/state.ts new file mode 100644 index 00000000..38067b92 --- /dev/null +++ b/packages/nap/src/gamepad/state.ts @@ -0,0 +1,49 @@ +/** Validate complete NAP-GAMEPAD snapshots before placing them in the local cache. */ +import type { GamepadState } from './types.js'; + +function record(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +function numberIn(value: unknown, min: number, max: number): value is number { + return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max; +} + +/** + * Check the schema and focus-neutrality constraints of a controller snapshot. + * @param value Untrusted snapshot payload. + * @returns Whether it matches NAP-GAMEPAD Schemas and Shell Behavior. + * @example if (isGamepadState(message)) handleGamepadMessage(message); + */ +export function isGamepadState(value: unknown): value is GamepadState { + if (!record(value) || typeof value.available !== 'boolean' || typeof value.focused !== 'boolean' || !Array.isArray(value.pads)) return false; + if (value.available) { + if (value.reason !== undefined) return false; + } else if (value.focused || (value.reason !== 'blocked' && value.reason !== 'unavailable')) return false; + return Array.from(value.pads).every((pad: unknown) => { + if (pad === null) return true; + if (!record(pad) || !Number.isInteger(pad.index) || (pad.index as number) < 0 || typeof pad.id !== 'string' || typeof pad.mapping !== 'string' || pad.connected !== true) return false; + if (typeof pad.timestamp !== 'number' || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false; + if (!value.focused && pad.timestamp !== 0) return false; + if (!Array.from(pad.axes).every((axis: unknown) => numberIn(axis, -1, 1) && (value.focused || axis === 0))) return false; + return Array.from(pad.buttons).every((button: unknown) => record(button) && numberIn(button.value, 0, 1) && typeof button.pressed === 'boolean' && typeof button.touched === 'boolean' && (value.focused || (button.value === 0 && !button.pressed && !button.touched))); + }); +} + +/** + * Copy a snapshot so local readers cannot mutate cached controller values. + * @param state Validated complete snapshot. + * @returns An independent state and all nested pad/button arrays. + * @example const copy = copyGamepadState(state); + */ +export function copyGamepadState(state: GamepadState): GamepadState { + return { + available: state.available, + focused: state.focused, + ...(state.reason === undefined ? {} : { reason: state.reason }), + pads: state.pads.map((pad) => pad === null ? null : { + index: pad.index, id: pad.id, mapping: pad.mapping, connected: pad.connected, + timestamp: pad.timestamp, axes: [...pad.axes], buttons: pad.buttons.map(({ value, pressed, touched }) => ({ value, pressed, touched })), + }), + }; +} diff --git a/packages/nap/src/gamepad/types.ts b/packages/nap/src/gamepad/types.ts new file mode 100644 index 00000000..3dcc51a5 --- /dev/null +++ b/packages/nap/src/gamepad/types.ts @@ -0,0 +1,17 @@ +/** NAP-GAMEPAD fire-and-forget envelopes; canonical source: https://github.com/napplet/naps/pull/108. */ +import type { GamepadState, NappletMessage } from '@napplet/core'; + +export const DOMAIN = 'gamepad' as const; +export type { Gamepad, GamepadApi, GamepadButton, GamepadSlot, GamepadState, GamepadUnavailableReason } from '@napplet/core'; + +export interface GamepadSubscribeMessage extends NappletMessage { + type: 'gamepad.subscribe'; +} +export interface GamepadUnsubscribeMessage extends NappletMessage { + type: 'gamepad.unsubscribe'; +} +export interface GamepadStateMessage extends NappletMessage, GamepadState { + type: 'gamepad.state'; +} +export type GamepadRequestMessage = GamepadSubscribeMessage | GamepadUnsubscribeMessage; +export type GamepadNapMessage = GamepadRequestMessage | GamepadStateMessage; diff --git a/packages/nap/tsup.config.ts b/packages/nap/tsup.config.ts index 3e70eff3..53269590 100644 --- a/packages/nap/tsup.config.ts +++ b/packages/nap/tsup.config.ts @@ -2,6 +2,10 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: { + 'gamepad/index': 'src/gamepad/index.ts', + 'gamepad/types': 'src/gamepad/types.ts', + 'gamepad/shim': 'src/gamepad/shim.ts', + 'gamepad/sdk': 'src/gamepad/sdk.ts', 'shell/index': 'src/shell/index.ts', 'shell/types': 'src/shell/types.ts', 'shell/shim': 'src/shell/shim.ts', diff --git a/packages/sdk/jsr.json b/packages/sdk/jsr.json index dd3d5a6c..1826e428 100644 --- a/packages/sdk/jsr.json +++ b/packages/sdk/jsr.json @@ -2,7 +2,10 @@ "name": "@napplet/sdk", "version": "0.29.0", "license": "MIT", - "exports": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./gamepad": "./src/gamepad.ts" + }, "imports": { "@napplet/core": "jsr:@napplet/core@^0.33.0", "@napplet/nap": "jsr:@napplet/nap@^0.33.0" diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4cb307ed..668731e3 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -10,6 +10,10 @@ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" + }, + "./gamepad": { + "types": "./dist/gamepad.d.ts", + "import": "./dist/gamepad.js" } }, "files": [ @@ -29,7 +33,8 @@ }, "scripts": { "build": "tsup", - "type-check": "tsc --noEmit" + "type-check": "tsc --noEmit", + "test:unit": "node --test test/*.test.mjs" }, "license": "MIT", "repository": { diff --git a/packages/sdk/src/gamepad.ts b/packages/sdk/src/gamepad.ts new file mode 100644 index 00000000..b0077e01 --- /dev/null +++ b/packages/sdk/src/gamepad.ts @@ -0,0 +1,30 @@ +/** Application-side cached controller input. Guard optional domain presence before use. */ +import type { GamepadApi, GamepadSlot, GamepadState, Subscription } from '@napplet/core'; +import { requireDomain } from './require-napplet.js'; + +/** + * Controller snapshots delivered by the runtime to this napplet. + * @example + * import { gamepad } from '@napplet/sdk/gamepad'; + * const pads = gamepad.getGamepads(); + */ +export const gamepad: GamepadApi = { + get available(): boolean { return requireDomain('gamepad').available; }, + get focused(): boolean { return requireDomain('gamepad').focused; }, + /** + * Read the complete cached slot list synchronously. + * @returns Latest controller slots. + * @example const pads = gamepad.getGamepads(); + */ + getGamepads(): GamepadSlot[] { return requireDomain('gamepad').getGamepads(); }, + /** + * Observe complete snapshots, including neutral focus-loss states. + * @param handler Receives each state update. + * @returns Local subscription handle. + * @example const sub = gamepad.onChange((state) => render(state)); + */ + onChange(handler: (state: GamepadState) => void): Subscription { return requireDomain('gamepad').onChange(handler); }, +}; + +export type { Gamepad, GamepadApi, GamepadButton, GamepadSlot, GamepadState, GamepadUnavailableReason } from '@napplet/core'; +export { gamepadGetGamepads, gamepadAvailable, gamepadFocused, gamepadOnChange } from '@napplet/nap/gamepad/sdk'; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index adfd56d6..3c889895 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -37,3 +37,5 @@ export type * from './nap-types.js'; export * from './nap-runtime.js'; export { shell } from './shell.js'; + +export { gamepad } from './gamepad.js'; diff --git a/packages/sdk/src/nap-runtime.ts b/packages/sdk/src/nap-runtime.ts index a853a2f6..faeee5da 100644 --- a/packages/sdk/src/nap-runtime.ts +++ b/packages/sdk/src/nap-runtime.ts @@ -205,3 +205,5 @@ export { } from '@napplet/nap/dm'; export { shellReady, shellSupports, shellOnReady, installShellShim, DOMAIN as SHELL_DOMAIN } from '@napplet/nap/shell'; + +export { DOMAIN as GAMEPAD_DOMAIN, installGamepadShim, gamepadGetGamepads, gamepadAvailable, gamepadFocused, gamepadOnChange } from '@napplet/nap/gamepad'; diff --git a/packages/sdk/src/nap-types.ts b/packages/sdk/src/nap-types.ts index f3ae5866..2ef8c9cd 100644 --- a/packages/sdk/src/nap-types.ts +++ b/packages/sdk/src/nap-types.ts @@ -9,3 +9,5 @@ export type * from './nap-types-services.js'; export type * from './nap-types-devices.js'; export type { ShellApi, ShellCapabilities, ShellEnvironment } from '@napplet/core'; + +export type * from '@napplet/nap/gamepad/types'; diff --git a/packages/sdk/test/gamepad.test.mjs b/packages/sdk/test/gamepad.test.mjs new file mode 100644 index 00000000..2f7dfad2 --- /dev/null +++ b/packages/sdk/test/gamepad.test.mjs @@ -0,0 +1,35 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { gamepad } from '@napplet/sdk/gamepad'; +import { gamepad as rootGamepad, GAMEPAD_DOMAIN, gamepadGetGamepads } from '@napplet/sdk'; + +test('built root and gamepad subpath delegate to the current injected domain', () => { + assert.equal(GAMEPAD_DOMAIN, 'gamepad'); + assert.equal(rootGamepad, gamepad); + const previous = globalThis.window; + let handler; + const pads = [null]; + const runtime = { + available: true, focused: false, + getGamepads: () => pads, + onChange: (callback) => { handler = callback; return { close: () => { handler = undefined; } }; }, + }; + try { + globalThis.window = { napplet: { gamepad: runtime } }; + assert.equal(gamepad.getGamepads(), pads); + assert.equal(gamepadGetGamepads(), pads); + assert.equal(gamepad.available, true); + runtime.focused = true; + assert.equal(gamepad.focused, true); + const callback = () => {}; + const sub = gamepad.onChange(callback); + assert.equal(handler, callback); + sub.close(); + assert.equal(handler, undefined); + globalThis.window.napplet = {}; + assert.throws(() => gamepad.getGamepads(), /gamepad/); + } finally { + if (previous === undefined) delete globalThis.window; + else globalThis.window = previous; + } +}); diff --git a/packages/sdk/tsup.config.ts b/packages/sdk/tsup.config.ts index 39620471..3c1cd825 100644 --- a/packages/sdk/tsup.config.ts +++ b/packages/sdk/tsup.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ['src/index.ts'], + entry: { index: 'src/index.ts', gamepad: 'src/gamepad.ts' }, format: ['esm'], dts: true, sourcemap: true, diff --git a/packages/shim/src/gamepad.test.ts b/packages/shim/src/gamepad.test.ts new file mode 100644 index 00000000..42db684c --- /dev/null +++ b/packages/shim/src/gamepad.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it, vi } from 'vitest'; +import { installNappletRuntimePrelude } from './prelude.js'; +import { gamepad as sdkGamepad } from '../../sdk/src/gamepad.js'; + +function deliver(data: unknown, source: MessageEventSource | null = window.parent): void { + window.dispatchEvent(new MessageEvent('message', { data, source })); +} + +describe('runtime-injected GAMEPAD', () => { + it('installs a callable optional API, accepts only parent state and backs the SDK getters', () => { + const state = { type: 'gamepad.state', available: true, focused: true, pads: [] }; + const post = vi.spyOn(window.parent, 'postMessage').mockImplementation((message) => { + if (message.type === 'gamepad.subscribe') deliver(state); + }); + try { + const installed = installNappletRuntimePrelude({ domains: ['gamepad'] }); + expect(Object.keys(installed)).toEqual(['gamepad']); + expect(installed.gamepad!.getGamepads()).toEqual([]); + expect(installed.gamepad!.focused).toBe(true); + expect(sdkGamepad.focused).toBe(true); + expect(post.mock.calls).toEqual([[{ type: 'gamepad.subscribe' }, '*']]); + const changed = vi.fn(); + const sub = sdkGamepad.onChange(changed); + const neutral = { ...state, focused: false }; + deliver(neutral, {} as MessageEventSource); + expect(sdkGamepad.focused).toBe(true); + expect(changed).not.toHaveBeenCalled(); + deliver(neutral); + expect(sdkGamepad.focused).toBe(false); + expect(changed).toHaveBeenCalledExactlyOnceWith({ available: true, focused: false, pads: [] }); + sub.close(); + installNappletRuntimePrelude({ domains: [] }); + expect(() => sdkGamepad.getGamepads()).toThrow('gamepad'); + deliver(state); + expect(changed).toHaveBeenCalledTimes(1); + } finally { post.mockRestore(); } + }); +}); diff --git a/packages/shim/src/runtime-globals-core.ts b/packages/shim/src/runtime-globals-core.ts index 1877ea24..37958869 100644 --- a/packages/shim/src/runtime-globals-core.ts +++ b/packages/shim/src/runtime-globals-core.ts @@ -1,3 +1,4 @@ +import { gamepad } from '@napplet/nap/gamepad/shim'; import { shell } from '@napplet/nap/shell/shim'; import type { NapDomain, NappletGlobal } from '@napplet/core'; import { @@ -43,6 +44,7 @@ import { export function installCoreDomains(domains: ReadonlySet, napplet: Partial): void { + if (domains.has('gamepad')) napplet.gamepad = gamepad; if (domains.has('shell')) napplet.shell = shell; installFoundationDomains(domains, napplet); installPresentationDomains(domains, napplet); diff --git a/packages/shim/src/runtime.ts b/packages/shim/src/runtime.ts index 5af69c42..a54b7eee 100644 --- a/packages/shim/src/runtime.ts +++ b/packages/shim/src/runtime.ts @@ -1,3 +1,4 @@ +import { installGamepadShim, handleGamepadMessage } from '@napplet/nap/gamepad/shim'; import { handleShellMessage, installShellShim } from '@napplet/nap/shell/shim'; import { NAP_DOMAINS, @@ -98,6 +99,7 @@ const installedDomainShims = new Set(); let messageListenerInstalled = false; const DOMAIN_ROUTERS: ReadonlyArray = [ + ['gamepad.', handleGamepadMessage], ['shell.', handleShellMessage], ['keys.', handleKeysMessage], ['media.', mediaShim.handleMediaMessage], @@ -148,6 +150,9 @@ function installDomainShim(domain: NapDomain): void { installedDomainShims.add(domain); switch (domain) { + case 'gamepad': + installGamepadShim(); + return; case 'shell': installShellShim(); return; From f67bdc6269ec1c8ce4bbbf4e1941648e1c5c3fc1 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:02:08 -0700 Subject: [PATCH 03/24] docs(163): record portable GAMEPAD checkpoint Co-Authored-By: Codex --- .planning/STATE.md | 8 ++++---- .../163-01-SUMMARY.md | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 66a1eb8e..eadc2d1d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,9 +2,9 @@ gsd_state_version: 1.0 milestone: v0.34.0 milestone_name: NIP-5D Runtime Injection -current_phase: 162 -current_phase_name: nip-5d-event-schema-migration -status: complete +current_phase: 163 +current_phase_name: nap-gamepad-across-repositories +status: in_progress stopped_at: "Phase 162 verified; draft PR #224 open with hzrd149 requested" last_updated: "2026-10-04T19:00:16.967369+00:00" last_activity: 2026-10-04 @@ -23,7 +23,7 @@ See: .planning/PROJECT.md (updated 2026-05-24 after v0.31.0 archive) **Core value:** Prove that sandboxed Nostr apps can securely delegate to a host shell over a simple, standardized protocol — and ship the spec + SDK so others can build on it. -**Current focus:** Phase 162 — NIP-5D event schema migration; draft PR #224 awaiting review +**Current focus:** Phase 163 — NAP-GAMEPAD across repositories; portable API checkpoint committed, runtime/conformance and repository coverage in progress. > **Provenance note:** The "Accumulated Context" section below preserves bullet records from BOTH branches' STATE.md histories. Records tagged "v0.29.0" from main's lineage refer to the milestone NOW renumbered as v0.30.0 (Class-Gated Decrypt — Phases 135-138). Records tagged "v0.29.0" from feat/strict-model refer to NUB-CONNECT (Phases 135-142). Phase number alone is not a unique identifier across the two; cross-reference the topic (decrypt/identity/NIP-07 → v0.30.0; connect/class/CSP-authority → v0.29.0). diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md new file mode 100644 index 00000000..c2ff62e9 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md @@ -0,0 +1,11 @@ +--- +status: in_progress +phase: 163 +plan: 01 +--- + +# Portable GAMEPAD checkpoint + +Commit ae7ecb27 implements types, exact fire-and-forget messages, selected runtime injection, cached reads, SDK namespace/helpers and npm/JSR subpaths. All four package builds and type checks passed. NAP unit suite: 194 passed; shim: 18 passed; built SDK import test: 1 passed. The focused shim suite subsequently passed 7 tests, including bounded listener fanout. Staged AI-slop: 100/100; no rules disabled. + +Remaining acceptance evidence: real browser injection/isolation and release/docs coverage are part of plans 02/03; this checkpoint alone does not prove the whole phase. The native API replacement conflict and Kehto’s incorrect shell.ready dependency are recorded in the context and research. The original dirty worktree remains untouched. From 6bd6ff318e113b7cc0d641353c209642aa4740a6 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:35:00 -0700 Subject: [PATCH 04/24] feat(conformance): validate GAMEPAD envelopes and inject cached API Co-Authored-By: Codex --- packages/conformance/src/run/boot.test.ts | 2 + packages/conformance/src/run/boot.ts | 25 +++++ packages/conformance/src/run/gamepad.test.ts | 34 ++++++ .../src/shell/reference-responses.ts | 3 + .../src/validators/envelope-specs.ts | 5 + .../src/validators/envelope-validation.ts | 2 +- .../src/validators/envelope.test.ts | 8 +- .../src/validators/gamepad.test.ts | 21 ++++ packages/nap/src/gamepad/index.ts | 2 +- packages/nap/src/gamepad/state.ts | 106 ++++++++++++------ 10 files changed, 165 insertions(+), 43 deletions(-) create mode 100644 packages/conformance/src/run/gamepad.test.ts create mode 100644 packages/conformance/src/validators/gamepad.test.ts diff --git a/packages/conformance/src/run/boot.test.ts b/packages/conformance/src/run/boot.test.ts index ae8fcdbe..1e98d2aa 100644 --- a/packages/conformance/src/run/boot.test.ts +++ b/packages/conformance/src/run/boot.test.ts @@ -59,6 +59,7 @@ describe('bootAndCollect — DOM wiring', () => { }]); window.dispatchEvent(new MessageEvent('message', { + source: window.parent, data: { type: 'resource.bytes.result', id: posted[0].id, @@ -82,6 +83,7 @@ describe('bootAndCollect — DOM wiring', () => { }], }); window.dispatchEvent(new MessageEvent('message', { + source: window.parent, data: { type: 'resource.bytesMany.result', id: posted[1].id, diff --git a/packages/conformance/src/run/boot.ts b/packages/conformance/src/run/boot.ts index 302388e3..4dbe2629 100644 --- a/packages/conformance/src/run/boot.ts +++ b/packages/conformance/src/run/boot.ts @@ -14,6 +14,7 @@ * @packageDocumentation */ +import { createGamepadStateTools } from '@napplet/nap/gamepad'; import { NAP_DOMAINS } from '@napplet/core'; import { createReferenceShell, type RecordedEnvelope } from '../shell/reference-shell.js'; import type { BootObservation } from './context.js'; @@ -74,6 +75,9 @@ export function runtimePrelude(domains: readonly string[]): string { const domains = ${JSON.stringify(domains)}; const napplet = {}; for (const domain of domains) napplet[domain] = {}; + const gamepadTools = (${createGamepadStateTools.toString()})(); + let gamepadState = { available: true, focused: false, pads: [] }; + const gamepadHandlers = new Set(); const pending = new Map(); let seq = 0; const nextId = () => { @@ -93,7 +97,14 @@ export function runtimePrelude(domains: readonly string[]): string { window.parent.postMessage({ ...message, id }, '*'); }); window.addEventListener('message', (event) => { + if (event.source !== window.parent) return; const msg = event.data; + if (napplet.gamepad && msg && msg.type === 'gamepad.state' && !('id' in msg) && gamepadTools.isGamepadState(msg)) { + gamepadState = gamepadTools.copyGamepadState(msg); + const recipients = Array.from(gamepadHandlers); + for (const entry of recipients) if (gamepadHandlers.has(entry)) entry.handler(gamepadTools.copyGamepadState(gamepadState)); + return; + } if (!msg || typeof msg !== 'object' || typeof msg.id !== 'string') return; const slot = pending.get(msg.id); if (!slot) return; @@ -106,6 +117,18 @@ export function runtimePrelude(domains: readonly string[]): string { } slot.resolve(msg); }); + if (napplet.gamepad) { + napplet.gamepad = { + get available() { return gamepadState.available; }, + get focused() { return gamepadState.focused; }, + getGamepads() { return gamepadTools.copyGamepadState(gamepadState).pads; }, + onChange(handler) { + const entry = { handler }; + gamepadHandlers.add(entry); + return { close() { gamepadHandlers.delete(entry); } }; + }, + }; + } if (napplet.resource) { napplet.resource.info = () => request({ type: 'resource.info' }, 'resource.info.result', 'resource.info.error') @@ -139,6 +162,7 @@ export function runtimePrelude(domains: readonly string[]): string { }; } window.napplet = napplet; + if (napplet.gamepad) window.parent.postMessage({ type: 'gamepad.subscribe' }, '*'); const report = (reason) => { try { window.parent.postMessage({ type: '${INTERNAL_BOOT_ERROR}', reason: String(reason && reason.message || reason) }, '*'); @@ -190,6 +214,7 @@ async function bootOnce( const shell = createReferenceShell(); const iframe = doc.createElement('iframe'); iframe.setAttribute('sandbox', opts.allowSameOrigin ? 'allow-scripts allow-same-origin' : 'allow-scripts'); + iframe.setAttribute('allow', "gamepad 'none'"); iframe.setAttribute('aria-hidden', 'true'); iframe.style.position = 'absolute'; iframe.style.width = '1px'; diff --git a/packages/conformance/src/run/gamepad.test.ts b/packages/conformance/src/run/gamepad.test.ts new file mode 100644 index 00000000..f2d00d08 --- /dev/null +++ b/packages/conformance/src/run/gamepad.test.ts @@ -0,0 +1,34 @@ +// @vitest-environment happy-dom +import { describe, expect, it, vi } from 'vitest'; +import type { GamepadApi } from '@napplet/core'; +import { runtimePrelude } from './boot.js'; + +describe('serialized GAMEPAD conformance binding', () => { + it('installs before subject code, accepts only parent state and isolates cached data', () => { + const previous = window.parent; + const post = vi.fn(); + Object.defineProperty(window, 'parent', { configurable: true, value: { postMessage: post } }); + try { + Function(runtimePrelude(['gamepad']))(); + const api = (window as unknown as { napplet: { gamepad: GamepadApi } }).napplet.gamepad; + expect(api.available).toBe(true); + expect(api.focused).toBe(false); + expect(api.getGamepads()).toEqual([]); + expect(post.mock.calls).toEqual([[{ type: 'gamepad.subscribe' }, '*']]); + const handler = vi.fn(); + const sub = api.onChange(handler); + const state = { type: 'gamepad.state', available: false, focused: false, reason: 'blocked', pads: [] }; + window.dispatchEvent(new MessageEvent('message', { data: state, source: {} as MessageEventSource })); + expect(handler).not.toHaveBeenCalled(); + window.dispatchEvent(new MessageEvent('message', { data: state, source: window.parent })); + expect(api.available).toBe(false); + expect(handler).toHaveBeenCalledExactlyOnceWith({ available: false, focused: false, reason: 'blocked', pads: [] }); + sub.close(); + window.dispatchEvent(new MessageEvent('message', { data: state, source: window.parent })); + expect(handler).toHaveBeenCalledTimes(1); + } finally { + Object.defineProperty(window, 'parent', { configurable: true, value: previous }); + delete (window as unknown as { napplet?: unknown }).napplet; + } + }); +}); diff --git a/packages/conformance/src/shell/reference-responses.ts b/packages/conformance/src/shell/reference-responses.ts index a7bb0692..3387670e 100644 --- a/packages/conformance/src/shell/reference-responses.ts +++ b/packages/conformance/src/shell/reference-responses.ts @@ -74,6 +74,9 @@ function dataUrlToBlob(url: unknown): { blob: Blob; mime: string } | null { * envelopes, not the shell's responses. */ export const RESPONDERS: Record = { + // This transport-neutral mock has no controller API. Production shells use a host reader. + 'gamepad.subscribe': () => ok({ type: 'gamepad.state', available: false, reason: 'unavailable', focused: false, pads: [] }), + 'gamepad.unsubscribe': none, // relay 'relay.subscribe': (e) => ok({ type: 'relay.eose', subId: e.subId }), 'relay.close': none, diff --git a/packages/conformance/src/validators/envelope-specs.ts b/packages/conformance/src/validators/envelope-specs.ts index 79dac9a5..3d90bddb 100644 --- a/packages/conformance/src/validators/envelope-specs.ts +++ b/packages/conformance/src/validators/envelope-specs.ts @@ -7,6 +7,11 @@ const ID = { id: 'string' } as const; * NAP domains, with its direction and (for outbound) required fields. */ export const ENVELOPE_SPECS: Record = { + // NAP-GAMEPAD Wire Protocol: all three messages are fire-and-forget, without ids. + // https://github.com/napplet/naps/pull/108 + 'gamepad.subscribe': { dir: 'out', forbiddenFields: ['id'] }, + 'gamepad.unsubscribe': { dir: 'out', forbiddenFields: ['id'] }, + 'gamepad.state': { dir: 'in', fields: { available: 'boolean', focused: 'boolean', pads: 'array' } }, // Optional NAP-SHELL environment, independent of other domains. 'shell.ready': { dir: 'out' }, 'shell.init': { dir: 'in', fields: { capabilities: 'object', services: 'array' } }, diff --git a/packages/conformance/src/validators/envelope-validation.ts b/packages/conformance/src/validators/envelope-validation.ts index badea43d..1173b7f2 100644 --- a/packages/conformance/src/validators/envelope-validation.ts +++ b/packages/conformance/src/validators/envelope-validation.ts @@ -210,7 +210,7 @@ export function validateEnvelope(message: unknown): EnvelopeVerdict { if (field in record) { errors.push({ code: 'forbidden-field', - message: `Field "${field}" must be runtime-derived and cannot be emitted by a napplet`, + message: domain === 'gamepad' ? `NAP-GAMEPAD fire-and-forget envelopes do not carry "${field}"` : `Field "${field}" must be runtime-derived and cannot be emitted by a napplet`, field, }); } diff --git a/packages/conformance/src/validators/envelope.test.ts b/packages/conformance/src/validators/envelope.test.ts index 423c7618..20fb96ff 100644 --- a/packages/conformance/src/validators/envelope.test.ts +++ b/packages/conformance/src/validators/envelope.test.ts @@ -237,13 +237,13 @@ describe('validateEnvelope — optional shell domain', () => { }); describe('ENVELOPE_SPECS invariants', () => { - it('has 240 discriminants split 115 outbound / 125 inbound', () => { + it('has 243 discriminants split 117 outbound / 126 inbound', () => { const all = knownEnvelopeTypes(); - expect(all).toHaveLength(240); + expect(all).toHaveLength(243); const out = all.filter((t) => ENVELOPE_SPECS[t].dir === 'out'); const inbound = all.filter((t) => ENVELOPE_SPECS[t].dir === 'in'); - expect(out).toHaveLength(115); - expect(inbound).toHaveLength(125); + expect(out).toHaveLength(117); + expect(inbound).toHaveLength(126); }); it('declares canonical inbound INC carrier fields', () => { diff --git a/packages/conformance/src/validators/gamepad.test.ts b/packages/conformance/src/validators/gamepad.test.ts new file mode 100644 index 00000000..62fe6026 --- /dev/null +++ b/packages/conformance/src/validators/gamepad.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; +import { validateEnvelope, knownEnvelopeTypes } from './envelope.js'; +import { createReferenceShell } from '../shell/reference-shell.js'; + +describe('NAP-GAMEPAD Wire Protocol', () => { + it('recognizes exactly the three published messages and no poll/result extension', () => { + expect(knownEnvelopeTypes().filter((type) => type.startsWith('gamepad.')).sort()).toEqual(['gamepad.state', 'gamepad.subscribe', 'gamepad.unsubscribe']); + expect(validateEnvelope({ type: 'gamepad.getGamepads', id: 'invented' }).ok).toBe(false); + expect(validateEnvelope({ type: 'gamepad.subscribe' }).ok).toBe(true); + expect(validateEnvelope({ type: 'gamepad.unsubscribe' }).ok).toBe(true); + expect(validateEnvelope({ type: 'gamepad.subscribe', id: 'not-defined' }).errors).toContainEqual(expect.objectContaining({ code: 'forbidden-field', field: 'id' })); + expect(validateEnvelope({ type: 'gamepad.state', available: true, focused: false, pads: [] }).errors[0].code).toBe('inbound-type-emitted'); + }); + + it('provides an immediate unavailability snapshot from the controller-free mock', () => { + const shell = createReferenceShell(); + expect(shell.handle({ type: 'gamepad.subscribe' })).toEqual([{ type: 'gamepad.state', available: false, reason: 'unavailable', focused: false, pads: [] }]); + expect(shell.handle({ type: 'gamepad.unsubscribe' })).toEqual([]); + expect(shell.handle({ type: 'gamepad.subscribe', id: 'invalid' })).toEqual([]); + }); +}); diff --git a/packages/nap/src/gamepad/index.ts b/packages/nap/src/gamepad/index.ts index 0eb1001b..7bfa8476 100644 --- a/packages/nap/src/gamepad/index.ts +++ b/packages/nap/src/gamepad/index.ts @@ -5,6 +5,6 @@ export { DOMAIN } from './types.js'; export type * from './types.js'; export { gamepad, getGamepads, onChange, installGamepadShim, handleGamepadMessage } from './shim.js'; export { gamepadGetGamepads, gamepadAvailable, gamepadFocused, gamepadOnChange } from './sdk.js'; -export { isGamepadState, copyGamepadState } from './state.js'; +export { createGamepadStateTools, isGamepadState, copyGamepadState } from './state.js'; registerNap(DOMAIN, () => { /* The runtime supplies the domain handler. */ }); diff --git a/packages/nap/src/gamepad/state.ts b/packages/nap/src/gamepad/state.ts index 38067b92..cbbecb84 100644 --- a/packages/nap/src/gamepad/state.ts +++ b/packages/nap/src/gamepad/state.ts @@ -1,49 +1,81 @@ -/** Validate complete NAP-GAMEPAD snapshots before placing them in the local cache. */ +/** Snapshot helpers shared by normal bindings and serialized browser preludes. */ import type { GamepadState } from './types.js'; -function record(value: unknown): value is Record { - return value !== null && typeof value === 'object' && !Array.isArray(value); +/** Pure helpers with no module closures, suitable for pre-execution runtime injection. */ +export interface GamepadStateTools { + isGamepadState(value: unknown): value is GamepadState; + copyGamepadState(state: GamepadState): GamepadState; } -function numberIn(value: unknown, min: number, max: number): value is number { - return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max; +/** + * Create self-contained helpers that can be serialized into an opaque iframe. + * @returns State validation and independent snapshot copying. + * @example const tools = createGamepadStateTools(); + */ +export function createGamepadStateTools(): GamepadStateTools { + function record(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value); + } + + function numberIn(value: unknown, min: number, max: number): value is number { + return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max; + } + + /** + * Check the schema and focus-neutrality constraints of a controller snapshot. + * @param value Untrusted snapshot payload. + * @returns Whether it matches NAP-GAMEPAD Schemas and Shell Behavior. + * @example if (isGamepadState(message)) handleGamepadMessage(message); + */ + function isGamepadState(value: unknown): value is GamepadState { + if (!record(value) || typeof value.available !== 'boolean' || typeof value.focused !== 'boolean' || !Array.isArray(value.pads)) return false; + if (value.available) { + if (value.reason !== undefined) return false; + } else if (value.focused || (value.reason !== 'blocked' && value.reason !== 'unavailable')) return false; + return Array.from(value.pads).every((pad: unknown) => { + if (pad === null) return true; + if (!record(pad) || !Number.isInteger(pad.index) || (pad.index as number) < 0 || typeof pad.id !== 'string' || typeof pad.mapping !== 'string' || pad.connected !== true) return false; + if (typeof pad.timestamp !== 'number' || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false; + if (!value.focused && pad.timestamp !== 0) return false; + if (!Array.from(pad.axes).every((axis: unknown) => numberIn(axis, -1, 1) && (value.focused || axis === 0))) return false; + return Array.from(pad.buttons).every((button: unknown) => record(button) && numberIn(button.value, 0, 1) && typeof button.pressed === 'boolean' && typeof button.touched === 'boolean' && (value.focused || (button.value === 0 && !button.pressed && !button.touched))); + }); + } + + /** + * Copy a snapshot so local readers cannot mutate cached controller values. + * @param state Validated complete snapshot. + * @returns An independent state and all nested pad/button arrays. + * @example const copy = copyGamepadState(state); + */ + function copyGamepadState(state: GamepadState): GamepadState { + return { + available: state.available, + focused: state.focused, + ...(state.reason === undefined ? {} : { reason: state.reason }), + pads: state.pads.map((pad) => pad === null ? null : { + index: pad.index, id: pad.id, mapping: pad.mapping, connected: pad.connected, + timestamp: pad.timestamp, axes: [...pad.axes], buttons: pad.buttons.map(({ value, pressed, touched }) => ({ value, pressed, touched })), + }), + }; + } + return { isGamepadState, copyGamepadState }; } +const tools = createGamepadStateTools(); + /** - * Check the schema and focus-neutrality constraints of a controller snapshot. - * @param value Untrusted snapshot payload. - * @returns Whether it matches NAP-GAMEPAD Schemas and Shell Behavior. - * @example if (isGamepadState(message)) handleGamepadMessage(message); + * Validate a complete snapshot against NAP-GAMEPAD Schemas and Shell Behavior. + * @param value Untrusted state payload. + * @returns Whether its fields and neutral values conform. + * @example if (isGamepadState(message)) accept(message); */ -export function isGamepadState(value: unknown): value is GamepadState { - if (!record(value) || typeof value.available !== 'boolean' || typeof value.focused !== 'boolean' || !Array.isArray(value.pads)) return false; - if (value.available) { - if (value.reason !== undefined) return false; - } else if (value.focused || (value.reason !== 'blocked' && value.reason !== 'unavailable')) return false; - return Array.from(value.pads).every((pad: unknown) => { - if (pad === null) return true; - if (!record(pad) || !Number.isInteger(pad.index) || (pad.index as number) < 0 || typeof pad.id !== 'string' || typeof pad.mapping !== 'string' || pad.connected !== true) return false; - if (typeof pad.timestamp !== 'number' || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false; - if (!value.focused && pad.timestamp !== 0) return false; - if (!Array.from(pad.axes).every((axis: unknown) => numberIn(axis, -1, 1) && (value.focused || axis === 0))) return false; - return Array.from(pad.buttons).every((button: unknown) => record(button) && numberIn(button.value, 0, 1) && typeof button.pressed === 'boolean' && typeof button.touched === 'boolean' && (value.focused || (button.value === 0 && !button.pressed && !button.touched))); - }); -} +export function isGamepadState(value: unknown): value is GamepadState { return tools.isGamepadState(value); } /** - * Copy a snapshot so local readers cannot mutate cached controller values. - * @param state Validated complete snapshot. - * @returns An independent state and all nested pad/button arrays. + * Copy all snapshot data without sharing mutable arrays or buttons. + * @param state Validated snapshot. + * @returns An independent state. * @example const copy = copyGamepadState(state); */ -export function copyGamepadState(state: GamepadState): GamepadState { - return { - available: state.available, - focused: state.focused, - ...(state.reason === undefined ? {} : { reason: state.reason }), - pads: state.pads.map((pad) => pad === null ? null : { - index: pad.index, id: pad.id, mapping: pad.mapping, connected: pad.connected, - timestamp: pad.timestamp, axes: [...pad.axes], buttons: pad.buttons.map(({ value, pressed, touched }) => ({ value, pressed, touched })), - }), - }; -} +export function copyGamepadState(state: GamepadState): GamepadState { return tools.copyGamepadState(state); } From 3e2f61b66ef107d8df631e1427f746e467d07b7e Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:24:51 -0700 Subject: [PATCH 05/24] feat(gamepad): integrate tooling and prepare coordinated releases Co-Authored-By: Codex --- .../163-01-SUMMARY.md | 8 +- .../163-02-SUMMARY.md | 11 + .../163-03-SUMMARY.md | 11 + .../163-REVIEW.md | 13 + .../163-SECURITY.md | 22 + .../COVERAGE.md | 45 ++ README.md | 21 + apps/docs/.vitepress/config.ts | 2 +- apps/docs/guide/build-note-drafts-napplet.md | 4 +- apps/docs/naps/gamepad.md | 22 + apps/docs/naps/index.md | 6 +- apps/docs/packages/nap.md | 22 +- apps/docs/packages/sdk.md | 21 + packages/boilerplate/README.md | 4 + packages/cli/CHANGELOG.md | 244 ++++--- packages/cli/README.md | 4 + packages/cli/deno.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/cli.ts | 2 +- packages/cli/src/managed-node.ts | 2 +- packages/cli/src/package-runner.ts | 2 +- packages/cli/tests/cli_test.ts | 2 +- packages/cli/tests/zapstore_test.ts | 2 +- packages/conformance-cli/CHANGELOG.md | 157 +++-- packages/conformance-cli/README.md | 4 + packages/conformance-cli/package.json | 8 +- packages/conformance/CHANGELOG.md | 312 ++++++--- packages/conformance/README.md | 4 + packages/conformance/jsr.json | 6 +- packages/conformance/package.json | 2 +- packages/conformance/src/run/boot.ts | 2 +- packages/core/CHANGELOG.md | 605 +++++++++++----- packages/core/README.md | 4 + packages/core/jsr.json | 2 +- packages/core/package.json | 2 +- packages/nap/CHANGELOG.md | 650 ++++++++++++------ packages/nap/README.md | 21 + packages/nap/jsr.json | 4 +- packages/nap/package.json | 4 +- packages/sdk/CHANGELOG.md | 567 ++++++++++----- packages/sdk/README.md | 21 + packages/sdk/jsr.json | 6 +- packages/sdk/package.json | 2 +- packages/shim/CHANGELOG.md | 617 ++++++++++++----- packages/shim/README.md | 4 + packages/shim/jsr.json | 6 +- packages/shim/package.json | 7 +- packages/shim/src/gamepad.test.ts | 2 +- packages/vite-plugin/CHANGELOG.md | 361 +++++++--- packages/vite-plugin/README.md | 4 + packages/vite-plugin/jsr.json | 4 +- packages/vite-plugin/package.json | 2 +- packages/vite-plugin/src/index.test.ts | 15 + packages/vite-plugin/src/requirements.ts | 1 + pnpm-lock.yaml | 86 +-- skills/napplet-build/SKILL.md | 4 + skills/napplet-design/SKILL.md | 4 + skills/napplet-sdk/SKILL.md | 4 + skills/napplet-test/SKILL.md | 4 + 59 files changed, 2890 insertions(+), 1092 deletions(-) create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-02-SUMMARY.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md create mode 100644 apps/docs/naps/gamepad.md diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md index c2ff62e9..5ad2d1b0 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-01-SUMMARY.md @@ -1,11 +1,11 @@ --- -status: in_progress phase: 163 plan: 01 +status: complete --- -# Portable GAMEPAD checkpoint +# Portable GAMEPAD API -Commit ae7ecb27 implements types, exact fire-and-forget messages, selected runtime injection, cached reads, SDK namespace/helpers and npm/JSR subpaths. All four package builds and type checks passed. NAP unit suite: 194 passed; shim: 18 passed; built SDK import test: 1 passed. The focused shim suite subsequently passed 7 tests, including bounded listener fanout. Staged AI-slop: 100/100; no rules disabled. +Core types/domain, NAP binding and validators, SDK root/subpath exports, npm/JSR maps, and selected shim installation are implemented. Built export tests, cached snapshot/range/parent-source tests, full build/type/unit gates and slop pass. -Remaining acceptance evidence: real browser injection/isolation and release/docs coverage are part of plans 02/03; this checkpoint alone does not prove the whole phase. The native API replacement conflict and Kehto’s incorrect shell.ready dependency are recorded in the context and research. The original dirty worktree remains untouched. +Non-normative evidence; protocol authority remains the living references in 163-CONTEXT.md. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-02-SUMMARY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-02-SUMMARY.md new file mode 100644 index 00000000..f904e272 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-02-SUMMARY.md @@ -0,0 +1,11 @@ +--- +phase: 163 +plan: 02 +status: complete +--- + +# Runtime and conformance + +Conformance implements the three envelopes and cached parent-bound browser binding. Kehto provides host-owned native reading, input-owner/visibility isolation, controller Permissions-Policy denial, subscription/rate/session cleanup, and optional SHELL-independent routing. Chromium proves native denial and frame/host/tab focus isolation with canonical host test samples. Native API replacement remains deferred for the disclosed specification conflict. + +Non-normative evidence; protocol authority remains the living references in 163-CONTEXT.md. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md new file mode 100644 index 00000000..64522c54 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md @@ -0,0 +1,11 @@ +--- +phase: 163 +plan: 03 +status: in_progress +--- + +# All repositories and shipment + +All seven organization repositories plus Kehto are audited. Applicable source, consumer/toolchain, template, migration, policy, organization, site/package/skill, and release surfaces are covered in COVERAGE.md. Primary build/type/unit/docs/slop gates pass. CVM controller suites pass; protected-authority and timing failures also reproduce on its untouched baseline. Linked PRs and final host gates are recorded before completion. + +Non-normative evidence; protocol authority remains the living references in 163-CONTEXT.md. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md b/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md new file mode 100644 index 00000000..a6182403 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md @@ -0,0 +1,13 @@ +--- +phase: 163 +status: passed +review_mode: inline +--- + +# Implementation review + +Reviewed the changed API, serialized bindings, source/session routing, host broker, frame policies, manifest inference, consumer ranges, release metadata, and documentation against the current canonical sources. No subagents or external reviewers were invoked. No unresolved GAMEPAD implementation finding remains in the reviewed scope. + +The review preserved existing public host registration contracts: trusted sessions registered directly by integrators also route independently of SHELL readiness, without trusting app payload identity. Regression coverage verifies this. Paja's retained handshake-domain export is empty and SHELL is an optional advertised interface. Every controller frame is denied native gamepad/HID/USB paths before execution. + +The optional native Gamepad API replacement remains deferred due to the disclosed NIP-5D/GAMEPAD conflict. CVM's existing protected-authority baseline failures remain visible. Release ordering is explicit: Kehto CLI 0.5.5 before napplet CLI 0.9.1; the napplet set contains SDK 0.30.0 and conformance CLI 0.3.4. CLI smoke uses locally assembled source-build cache entries and an empty host-tool PATH; it does not claim those future versions are already published. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md new file mode 100644 index 00000000..15cf70ab --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-SECURITY.md @@ -0,0 +1,22 @@ +--- +phase: 163 +status: passed +threats_open: 0 +verified: 2026-10-12 +--- + +# GAMEPAD security review + +Non-normative code review against the living [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108) Shell Behavior/Security Considerations and [NIP-5D](https://github.com/nostr-protocol/nips/pull/2303) Transport/Security Rationale. Canonical heads rechecked unchanged on 2026-10-12: GAMEPAD 0b28f77e3dfa0f07dd7c0f9d61a7090ee99f3a5e; NIP-5D 020cb8b33a9e4c6b8ca4b2f9d0ed0a67843b68f7. + +| Threat | Implemented boundary | Proof | +| --- | --- | --- | +| Untrusted state injection | Portable, reference, and Kehto bindings require parent message source and validate state before caching. | Parent/sibling trust regressions; malformed state/id/range tests. | +| Cross-frame live controller leakage | Runtime recipient identity comes from the registered Window; live controls require trusted input ownership and active host focus/visibility. | Two-window broker vectors; Chromium iframe focus transfer and host/tab focus loss. | +| Timing leakage through neutral state | Unfocused views retain connected metadata with axes/buttons neutral and timestamp zero; unchanged views do not receive repeated live timestamps. | Broker unit vectors and Chromium snapshots. | +| Native bypass | Every Paja/playground loading path applies controller Permissions-Policy denial before execution. Reference iframes deny gamepad, HID, and USB too. | Real opaque Chromium frames throw SecurityError on native getGamepads; policy preservation/idempotency regression. | +| Mutating shared cached state | Bindings copy nested arrays/buttons for callers and each listener. | Cache/fanout mutation regressions. | +| Polling/subscription abuse or teardown leak | Shared frame loop, per-source repeated-subscribe limit, unconditional unsubscribe, generation cancellation, session/service cleanup. | Broker frame cancellation, rate limit, stale callback, destruction and unregister regressions. | +| Fabricated identity/authority handshake | Source identity and frozen grants are host-owned and independent of optional SHELL readiness. No new capability or message is introduced. | Immediate subscribe without SHELL; unknown-source and disabled-domain routing tests. | + +The optional native Gamepad API replacement is not implemented. The proposal permits it while NIP-5D currently limits runtime injection to window.napplet; the conflict is surfaced for clarification. Required domain support and physical-access denial do not depend on it. No cloud deployment, account authority, key material, haptics, or raw device handles are introduced by this change. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md new file mode 100644 index 00000000..66320fc5 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md @@ -0,0 +1,45 @@ +# GAMEPAD coverage + +Non-normative implementation evidence for phase 163. The living [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108) and [NIP-5D](https://github.com/nostr-protocol/nips/pull/2303) govern protocol behavior. + +## Organization repositories + +| Repository | Work and evidence | +| --- | --- | +| napplet/web | Cached NAP binding, core types, SDK subpaths, shim injection, manifest inference, conformance envelopes/reference injection, managed CLI pins, site/package/skill docs, release metadata. | +| napplet/naps | PR 108 is the canonical proposed contract; implementation follows its actual head. No duplicate or private normative specification is added. | +| napplet/boilerplate | Optional controller presence row and guide, updated SDK/toolchain compatibility, current manifest schema, single-file build, type/guidance/browser conformance tests. | +| napplet/cvm | GAMEPAD generation evidence/schema, required/optional declaration checks, publication compatibility regression, toolchain promotion guidance. Existing protected-authority baseline failures remain surfaced. | +| napplet/nappagochi | SDK/Paja/toolchain compatibility updates; controller support stays optional and existing interactions and pet lifecycle remain intact. Existing app unit/build/type/browser conformance checks. | +| napplet/migrate | Regression proves required/optional GAMEPAD domain preservation without adding a domain whitelist; migration guidance, full types/tests/build. | +| napplet/.github | Organization repository inventory and controller guidance; retired app-owned shim/social relay examples corrected. No executable controller surface. | +| kehto/web (runtime dependency) | Host broker, focus/visibility reader, Permissions-Policy denial across Paja/playground frames, cached parent-bound namespace, source sessions independent of SHELL, lifecycle/rate controls, units and Chromium focus/native-denial proof. | + +## Package and fixture coverage + +| Package | Surface | Coverage | +| --- | --- | --- | +| `@napplet/core` | `packages/core` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/vite-plugin` | `packages/vite-plugin` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/cli` | `packages/cli` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/sdk` | `packages/sdk` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/boilerplate` | `packages/boilerplate` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet/shim` | `packages/shim` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/nap` | `packages/nap` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/conformance-cli` | `packages/conformance-cli` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/conformance` | `packages/conformance` | Implementation, exports, tests, docs and release metadata. | +| `@napplet/web` | `apps/web` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet/docs` | `apps/docs` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet/conformance-web` | `apps/conformance` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet/conformance-e2e` | `tests/e2e/harness` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet-fixture/broken` | `tests/fixtures/napplets/broken` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet-fixture/conformant` | `tests/fixtures/napplets/conformant` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | +| `@napplet-fixture/resource-data` | `tests/fixtures/napplets/resource-data` | Existing build/tests or browser consumers audited; no new hard controller requirement. See repository proof below. | + +## Release ordering and limits + +Kehto CLI 0.5.5 must be published before napplet CLI 0.9.1 selects it as the private Paja runner. The napplet package set includes conformance CLI 0.3.4 and SDK 0.30.0. Consumer ranges accept the currently published base release and the coordinated GAMEPAD release; installing the base release does not supply new SDK exports. CVM's sealed image remains subject to its existing candidate-promotion gates; this change does not promote a production image. + +Native Gamepad API replacement remains deferred: the GAMEPAD proposal permits an optional replacement while NIP-5D currently limits runtime injection to window.napplet. Required domain support and physical-access denial do not depend on that replacement. Reference mocks intentionally report unavailable controllers; live host isolation is demonstrated separately in Kehto Chromium tests with test-supplied host samples. + +Verification results and PR links will be filled after final gates. Original dirty worktrees and unrelated changes remain untouched. diff --git a/README.md b/README.md index 3ade374b..64d2ba17 100644 --- a/README.md +++ b/README.md @@ -188,3 +188,24 @@ pnpm --filter @napplet/conformance-web dev # conformance runtime ## License MIT + +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index 4f408d41..92c049a9 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -91,7 +91,7 @@ export default defineConfig({ }, { text: 'NAP domains', - items: [{ text: 'NAP domain reference', link: '/naps/' }], + items: [{ text: 'NAP domain reference', link: '/naps/' }, { text: 'GAMEPAD', link: '/naps/gamepad' }], }, { text: 'Reference', diff --git a/apps/docs/guide/build-note-drafts-napplet.md b/apps/docs/guide/build-note-drafts-napplet.md index bdd598a9..9bab8c32 100644 --- a/apps/docs/guide/build-note-drafts-napplet.md +++ b/apps/docs/guide/build-note-drafts-napplet.md @@ -41,7 +41,7 @@ napplet init --name notedrafts --title "Note Drafts" \ --description "Draft and publish short Nostr notes from a sandboxed napplet." \ --archetype napplet:note/open pnpm init -pnpm add @napplet/sdk@^0.29.0 +pnpm add @napplet/sdk@^0.30.0 pnpm add -D @napplet/vite-plugin@^0.16.0 @napplet/conformance-cli@^0.3.0 @kehto/cli@^0.2.11 typescript@^5.9.3 vite@^6.4.3 ``` @@ -62,7 +62,7 @@ Replace the generated `package.json` with this: "verify": "pnpm type-check && pnpm test:conformance" }, "dependencies": { - "@napplet/sdk": "^0.29.0" + "@napplet/sdk": "^0.30.0" }, "devDependencies": { "@kehto/cli": "^0.2.11", diff --git a/apps/docs/naps/gamepad.md b/apps/docs/naps/gamepad.md new file mode 100644 index 00000000..5bc023c8 --- /dev/null +++ b/apps/docs/naps/gamepad.md @@ -0,0 +1,22 @@ +# GAMEPAD + +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/apps/docs/naps/index.md b/apps/docs/naps/index.md index 51cc80c3..eb5310ff 100644 --- a/apps/docs/naps/index.md +++ b/apps/docs/naps/index.md @@ -267,7 +267,7 @@ if (window.napplet?.fs) { ## Core domain union -[`@napplet/core`](/packages/core) exports a `NapDomain` string union for the foundational 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` — used as the discriminant for envelope routing and domain presence. +[`@napplet/core`](/packages/core) exports a `NapDomain` string union for the foundational 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` — used as the discriminant for envelope routing and domain presence. ## Where to go next @@ -293,3 +293,7 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). Theme updates reach napplets with the exposed `theme` domain, independently of manifest requirement declarations. User identity changes report the connected signer, including an empty string at sign-out; they do not alter napplet identity. Profile picture/banner bytes use optional `resource` when exposed; otherwise show local placeholder artwork. Theme media URLs do not grant network access, so colors and local fonts provide the fallback. See [NAP-THEME](https://github.com/napplet/naps/blob/master/naps/NAP-THEME.md) and [NAP-IDENTITY](https://github.com/napplet/naps/blob/master/naps/NAP-IDENTITY.md). + +### gamepad + +Optional focus-scoped controller snapshots. See the non-normative [GAMEPAD guide](./gamepad) and living [NAP-GAMEPAD proposal](https://github.com/napplet/naps/pull/108). diff --git a/apps/docs/packages/nap.md b/apps/docs/packages/nap.md index f7e94a61..a998990b 100644 --- a/apps/docs/packages/nap.md +++ b/apps/docs/packages/nap.md @@ -152,4 +152,24 @@ intent.onDelivery(({ sender, convention, payload }) => { await intent.invoke('napplet:profile/open?pubkey=abc123'); ``` -The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). \ No newline at end of file +The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/apps/docs/packages/sdk.md b/apps/docs/packages/sdk.md index a830b3ea..c4bd47e2 100644 --- a/apps/docs/packages/sdk.md +++ b/apps/docs/packages/sdk.md @@ -153,3 +153,24 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). Theme updates reach napplets with the exposed `theme` domain, independently of manifest requirement declarations. User identity changes report the connected signer, including an empty string at sign-out; they do not alter napplet identity. Profile picture/banner bytes use optional `resource` when exposed; otherwise show local placeholder artwork. Theme media URLs do not grant network access, so colors and local fonts provide the fallback. See [NAP-THEME](https://github.com/napplet/naps/blob/master/naps/NAP-THEME.md) and [NAP-IDENTITY](https://github.com/napplet/naps/blob/master/naps/NAP-IDENTITY.md). + +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/packages/boilerplate/README.md b/packages/boilerplate/README.md index c5537215..6405299f 100644 --- a/packages/boilerplate/README.md +++ b/packages/boilerplate/README.md @@ -56,3 +56,7 @@ npx skills add napplet/napplet ``` Treat the [living NIP-5D proposal](https://github.com/nostr-protocol/nips/pull/2303), [NAP-OUTBOX](https://github.com/napplet/naps/pull/32), and [NAP-RELAY](https://github.com/napplet/naps/pull/2) as protocol authority for the claims below. The shipped skills provide non-normative authoring guidance: the runtime injects `window.napplet`, app calls use `@napplet/sdk`, direct domain-property checks are only for optional fallbacks, normal Nostr reads and publishes are OUTBOX-first, RELAY is an explicit relay-local escape hatch, and manifest `requires` lists hard requirements only. + +### GAMEPAD integration + +Generated projects keep controller input optional unless it is essential to the app’s core task. The template’s controller guide covers fallbacks, focus edges, and runtime-owned native access. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index ecf5fcc2..92f49b47 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,12 +1,27 @@ # @napplet/cli +## 0.9.1 + +### Patch Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, + conformance injection, and optional manifest-domain inference. Update the standalone CLI's private + conformance/Paja runners to GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API replacement remains + deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. + ## 0.9.0 ### Minor Changes -- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser tooling, or keychain utilities. Browser and Paja commands provision private cached tooling automatically; screenshots and conformance share it. Linux browser packs include libraries and fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. +- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser + tooling, or keychain utilities. Browser and Paja commands provision private cached tooling + automatically; screenshots and conformance share it. Linux browser packs include libraries and + fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. - Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is asynchronous because it may provision private tooling. + Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now + require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is + asynchronous because it may provision private tooling. ### Patch Changes @@ -16,43 +31,53 @@ ### Patch Changes -- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and optional Zapstore application events before signing. +- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and + optional Zapstore application events before signing. ## 0.8.8 ### Patch Changes -- Link current deployment manifests to optional Zapstore application metadata using the proposed NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata before referencing manifests. +- Link current deployment manifests to optional Zapstore application metadata using the proposed + NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata + before referencing manifests. ## 0.8.7 ### Patch Changes -- Pin screenshot and default conformance commands to the tested browser runner so older project-local dependencies cannot intercept them. Preserve custom conformance commands. +- Pin screenshot and default conformance commands to the tested browser runner so older + project-local dependencies cannot intercept them. Preserve custom conformance commands. ## 0.8.6 ### Patch Changes -- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to override the URL or suppress source publication with false. +- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to + override the URL or suppress source publication with false. ## 0.8.5 ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install + Chromium on first use and retain explicit preview URLs. ## 0.8.4 ### Patch Changes -- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run previews, publication failure reporting, the preview screenshot command, and inline capture with deploy --screenshot. +- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run + previews, publication failure reporting, the preview screenshot command, and inline capture with + deploy --screenshot. ## 0.8.3 ### Patch Changes -- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy CLI input, and correct setup prompts, reports, and documentation. +- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype + input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy + CLI input, and correct setup prompts, reports, and documentation. - Updated dependencies [2910c4b] - @napplet/boilerplate@0.3.4 @@ -60,44 +85,72 @@ ### Patch Changes -- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed package version in JSR and standalone installations. +- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed + package version in JSR and standalone installations. ## 0.8.1 ### Patch Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and + naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover + opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment + API independently of other domain calls. Align INC provenance with authenticated endpoint + identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, + options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing + IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper + extraction preserves existing exports and behavior. ## 0.8.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact + hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon + byte input while retaining hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML + without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and + event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change + the built artifact hash. - Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. + Validate configured and recovered Blossom upload destinations before signing manifests or + uploading, with clear errors for missing values and non-origin URLs. ## 0.7.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers + emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and + independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy + events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add + offline signed-event migration previews without altering source events or publishing. Vite builds + default to a self-contained artifact; compatibility helper and sidecar names remain available. See + the event migration guide for package cutoffs, immutable pointers and shell ACL/storage + implications. ### Patch Changes -- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, including trimmed non-empty requirements from legacy sidecars (#223). -- Current deploys fail with the list of built files they would drop instead of silently uploading only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead of crashing and accepts `--format current|legacy`; a domain listed as both required and optional is emitted only as `O`. +- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, + including trimmed non-empty requirements from legacy sidecars (#223). +- Current deploys fail with the list of built files they would drop instead of silently uploading + only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead + of crashing and accepts `--format current|legacy`; a domain listed as both required and optional + is emitted only as `O`. ## 0.6.0 ### Minor Changes -- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and the `napplet create` next-steps point at that command. +- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent + skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the + open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and + the `napplet create` next-steps point at that command. ### Patch Changes @@ -108,7 +161,9 @@ ### Patch Changes -- b17e944: Make standalone release binaries dispatch bundled create and skills package code without a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner compatibility API, and expose import-safe callable CLI entry points from the maintained packages. +- b17e944: Make standalone release binaries dispatch bundled create and skills package code without + a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner + compatibility API, and expose import-safe callable CLI entry points from the maintained packages. - Updated dependencies [b17e944] - @napplet/boilerplate@0.3.2 - @napplet/skills@0.3.2 @@ -117,18 +172,27 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require + structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing key, and preserve + `count` requirements during CLI deploys. ## 0.5.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, + `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — + plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, + `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and + `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte + range options, and read/write result counts refer to decoded bytes. ## 0.4.0 @@ -136,79 +200,82 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested + sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the + symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and + `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports + `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, + `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, + `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove + the draft-only `eventKinds` option and trailing `kind:` fields. ## 0.3.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public types, + bindings, runtime injection, manifest tooling, CLI, and reference shell: queryless exact + identities with URI-to-text-payload transposition at `emit` and `invoke`/`open`, runtime-attested + sender, immediate acceptance followed by source-independent no-ID target delivery, and optional + same-tag event-kind discovery metadata. ### Patch Changes -- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor - NIP-5A constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface - that rejected spec-conformant napplets — `napplet deploy` now accepts any - non-empty `[a-z0-9-]` `d` tag (still rejecting trailing `-`), and `napplet deploy ---all` no longer throws on workspace folders with names longer than 13 - characters. +- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor NIP-5A + constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface that rejected + spec-conformant napplets — `napplet deploy` now accepts any non-empty `[a-z0-9-]` `d` tag (still + rejecting trailing `-`), and `napplet deploy +--all` no longer throws on workspace folders with + names longer than 13 characters. ## 0.2.1 ### Patch Changes -- b335c40: Run the default conformance tool through its npm package instead of requiring a - global `napplet-conformance` executable, and preserve Kehto's managed-command - separator when `napplet paja` wraps a local app server. +- b335c40: Run the default conformance tool through its npm package instead of requiring a global + `napplet-conformance` executable, and preserve Kehto's managed-command separator when + `napplet paja` wraps a local app server. ## 0.2.0 ### Minor Changes -- 49a8658: Make developer onboarding CLI-first from installation through deployment. The - Napplet CLI now creates starters, owns deployment metadata, installs agent - skills, prints a linked developer guide, and ships as checksum-verified - standalone binaries. The boilerplate - generator no longer prompts for deployment metadata, and the bundled skills - teach the same ordered workflow as the CLI, docs, and web app. +- 49a8658: Make developer onboarding CLI-first from installation through deployment. The Napplet CLI + now creates starters, owns deployment metadata, installs agent skills, prints a linked developer + guide, and ships as checksum-verified standalone binaries. The boilerplate generator no longer + prompts for deployment metadata, and the bundled skills teach the same ordered workflow as the + CLI, docs, and web app. ## 0.1.3 ### Patch Changes -- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden - Enter-based secret prompts, terminal deploy reports with NIP-19 pointers, - configured-bunker reconnects, and raw `bunker://` signing while preserving JSON - output for CI. Root-source deploys now keep local control state such as - `.napplet/config.json`, hidden files, and `node_modules` out of signed manifest - content. Relay and Blossom suggestions are now Tab-completion candidates instead - of numbered selections, and Nostr Connect asks for bunker relays separately from - deploy relays with `wss://bucket.coracle.social` as the default. - Nostr Connect pairing and remote signing now use applesauce-signers so QR flows - accept the `ack` responses emitted by common bunkers and match nsyte behavior. - Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt - but fall back to unscoped and legacy base64 auth for deployed servers that reject - otherwise valid uploads. - Network deploys now treat unavailable redundant Blossom mirrors as warnings and - continue to relay publication whenever at least one server holds every required - blob, while still failing when no configured server is complete. Human reports - now share the command's success predicate, so redundant relay misses are also - warnings whenever every manifest reaches another relay. +- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden Enter-based + secret prompts, terminal deploy reports with NIP-19 pointers, configured-bunker reconnects, and + raw `bunker://` signing while preserving JSON output for CI. Root-source deploys now keep local + control state such as `.napplet/config.json`, hidden files, and `node_modules` out of signed + manifest content. Relay and Blossom suggestions are now Tab-completion candidates instead of + numbered selections, and Nostr Connect asks for bunker relays separately from deploy relays with + `wss://bucket.coracle.social` as the default. Nostr Connect pairing and remote signing now use + applesauce-signers so QR flows accept the `ack` responses emitted by common bunkers and match + nsyte behavior. Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt + but fall back to unscoped and legacy base64 auth for deployed servers that reject otherwise valid + uploads. Network deploys now treat unavailable redundant Blossom mirrors as warnings and continue + to relay publication whenever at least one server holds every required blob, while still failing + when no configured server is complete. Human reports now share the command's success predicate, so + redundant relay misses are also warnings whenever every manifest reaches another relay. ## 0.1.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, + and Vite plugin packages. ## 0.1.1 @@ -220,20 +287,35 @@ ### Minor Changes -- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, manifest, and signing readiness state. -- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` command surfaces for interactive/local signing references. -- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is available. -- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using canonical manifest kind values, path tags, aggregate hashes, and server hints. +- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, + manifest, and signing readiness state. +- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` + command surfaces for interactive/local signing references. +- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is + available. +- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using + canonical manifest kind values, path tags, aggregate hashes, and server hints. - fb434c3: Support nsyte-compatible nbunksec remote signing for CI and stored-key deploy flows. -- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and publish signed manifest events to configured relays. -- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy manifests. -- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are available. -- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value `["title", …]` and `["description", …]` manifest tags from its plain-HTML `` element and `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and companion snapshot manifests alongside the existing `requires` passthrough. -- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper command surfaces. -- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and points `.napplet` signing.keyReference at it. +- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and + publish signed manifest events to configured relays. +- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy + manifests. +- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are + available. +- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value + `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and + `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing + values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and + companion snapshot manifests alongside the existing `requires` passthrough. +- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet + discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper + command surfaces. +- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// + paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and + points `.napplet` signing.keyReference at it. ### Patch Changes -- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with installation, - quick-start, command, signing, project-layout, and troubleshooting guidance. +- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with + installation, quick-start, command, signing, project-layout, and troubleshooting guidance. - fb434c3: Publish NIP-5D napplet manifest kinds from deploy commands diff --git a/packages/cli/README.md b/packages/cli/README.md index ef853869..0275ea06 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -446,3 +446,7 @@ The opt-in integration test compiles the release entrypoint and runs with an emp ```sh NAPPLET_TEST_STANDALONE=1 deno test --node-modules-dir=none -A packages/cli/tests/standalone_integration_test.ts ``` + +### GAMEPAD integration + +GAMEPAD is a bare manifest domain accepted by `init`, `build`, and deployment metadata. Controller fallback apps use `O gamepad`; controller-required apps use `R gamepad`. Live `dev` controller input depends on a GAMEPAD-capable Kehto/Paja release. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). diff --git a/packages/cli/deno.json b/packages/cli/deno.json index 8ba905e9..9a25a072 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -1,7 +1,7 @@ { "extends": "../../deno.json", "name": "@napplet/cli", - "version": "0.9.0", + "version": "0.9.1", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "license": "MIT", "exports": { diff --git a/packages/cli/package.json b/packages/cli/package.json index d27bea63..2a1cc85d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/cli", - "version": "0.9.0", + "version": "0.9.1", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "type": "module", "files": [ diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 8ccc8184..6dfbb83b 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -403,7 +403,7 @@ async function commandPaja(argv: string[]): Promise<number> { const config = await loadConfig(flags); const configured = config.paja?.command?.trim(); const base: ResolvedCommand = !configured || configured === "kehto" - ? await managedPackageCommand("@kehto/cli@0.5.4", "dist/index.js") + ? await managedPackageCommand("@kehto/cli@0.5.5", "dist/index.js") : splitCommand(configured); const child = new Deno.Command(base.command, { args: [...base.args, "paja", ...resolvePajaArgs(flags.afterDoubleDash)], diff --git a/packages/cli/src/managed-node.ts b/packages/cli/src/managed-node.ts index ba8e71fe..2ebb3cfa 100644 --- a/packages/cli/src/managed-node.ts +++ b/packages/cli/src/managed-node.ts @@ -90,7 +90,7 @@ export async function ensureManagedNode(): Promise<ManagedNode> { * @param spec Exact package version. * @param entry Package-relative CLI script. * @returns Absolute executable/script and environment, independent of project dependencies. - * @example await managedPackageCommand('@napplet/conformance-cli@0.3.3', 'dist/cli.js') + * @example await managedPackageCommand('@napplet/conformance-cli@0.3.4', 'dist/cli.js') */ export async function managedPackageCommand(spec: string, entry: string): Promise<ResolvedCommand> { const node = await ensureManagedNode(); diff --git a/packages/cli/src/package-runner.ts b/packages/cli/src/package-runner.ts index 2e1f5870..cdbbb649 100644 --- a/packages/cli/src/package-runner.ts +++ b/packages/cli/src/package-runner.ts @@ -2,7 +2,7 @@ import { type CommandRunner, runCommand, splitCommand } from "./process.ts"; import { managedPackageCommand } from "./managed-node.ts"; -const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.3"; +const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.4"; /** Process and output adapters used to invoke a maintained package CLI. */ export interface PackageCliRunOptions { diff --git a/packages/cli/tests/cli_test.ts b/packages/cli/tests/cli_test.ts index 7ea61fde..076bed0a 100644 --- a/packages/cli/tests/cli_test.ts +++ b/packages/cli/tests/cli_test.ts @@ -258,7 +258,7 @@ Deno.test("resolveConformanceCommand uses private tooling for either platform", command: "/private/node", args: ["/private/conformance.js"], }); } - assertEquals(calls, ["@napplet/conformance-cli@0.3.3:dist/cli.js", "@napplet/conformance-cli@0.3.3:dist/cli.js"]); + assertEquals(calls, ["@napplet/conformance-cli@0.3.4:dist/cli.js", "@napplet/conformance-cli@0.3.4:dist/cli.js"]); }); Deno.test("resolveConformanceCommand preserves an explicit custom runner", async () => { diff --git a/packages/cli/tests/zapstore_test.ts b/packages/cli/tests/zapstore_test.ts index a2c78780..3110ee82 100644 --- a/packages/cli/tests/zapstore_test.ts +++ b/packages/cli/tests/zapstore_test.ts @@ -277,7 +277,7 @@ Deno.test("screenshot runner pins a compatible version and preserves literal arg }, os: "windows", resolvePackage: async (spec, entry) => { - assertEquals(spec, "@napplet/conformance-cli@0.3.3"); + assertEquals(spec, "@napplet/conformance-cli@0.3.4"); assertEquals(entry, "dist/cli.js"); return { command: "C:/private/node.exe", args: ["C:/private/screenshot.js"] }; }, diff --git a/packages/conformance-cli/CHANGELOG.md b/packages/conformance-cli/CHANGELOG.md index 7be54e40..1631f154 100644 --- a/packages/conformance-cli/CHANGELOG.md +++ b/packages/conformance-cli/CHANGELOG.md @@ -1,16 +1,35 @@ # @napplet/conformance-cli +## 0.3.4 + +### Patch Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. +- Updated dependencies + - @napplet/core@0.34.0 + - @napplet/nap@0.34.0 + - @napplet/conformance@0.20.0 + ## 0.3.3 ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto + Paja preview. Install Chromium on first use and retain explicit preview URLs. ## 0.3.2 ### Patch Changes -- Add a screenshot command that captures a napplet iframe from a running preview shell, with configurable dimensions and readiness controls and protection against overwriting files. +- Add a screenshot command that captures a napplet iframe from a running preview + shell, with configurable dimensions and readiness controls and protection + against overwriting files. ## 0.3.1 @@ -23,9 +42,18 @@ ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 + and 35129. Writers emit one direct artifact `x` hash, description content, + required/optional `R`/`O` domains and independent `z`/`i` advertisements. + Readers verify current events and raw artifact bytes; legacy events require + migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. + CLI deployment defaults to current output and offers temporary explicit legacy + serialization. Add offline signed-event migration previews without altering + source events or publishing. Vite builds default to a self-contained artifact; + compatibility helper and sidecar names remain available. See the event + migration guide for package cutoffs, immutable pointers and shell ACL/storage + implications. ### Patch Changes @@ -58,10 +86,10 @@ ### Patch Changes -- dd7b3a7: Update shipped generator, conformance CLI, and agent-skill guidance for - queryless convention identities, optional same-tag event kinds, authoritative - intent URIs, acceptance-before-delivery, runtime-attested sender, and - carrier-neutral delivery without a public NAP-INC dependency. +- dd7b3a7: Update shipped generator, conformance CLI, and agent-skill guidance + for queryless convention identities, optional same-tag event kinds, + authoritative intent URIs, acceptance-before-delivery, runtime-attested + sender, and carrier-neutral delivery without a public NAP-INC dependency. - Updated dependencies [dd7b3a7] - @napplet/conformance@0.14.0 @@ -79,9 +107,13 @@ - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, + and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check + accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level + authoring flow so generated napplets route bytes, state, relays, signing, + and links through shell-owned NAPs. - Updated dependencies [dd2b0bc] - Updated dependencies [f63f8af] @@ -121,9 +153,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/conformance@0.9.1 @@ -182,22 +214,47 @@ ### Patch Changes -- Resolve a napplet target to its built `dist/index.html` before a sibling source `index.html`. +- Resolve a napplet target to its built `dist/index.html` before a sibling + source `index.html`. - `napplet-conformance --ui .` (and `napplet-conformance .`) on a Vite **project root** previously served the source `index.html` — a Vite entry referencing `/src/*.ts` that cannot execute in the opaque-origin `srcdoc` sandbox — instead of the built single-file `dist/index.html`. The live UI runner then reported a spurious NON-CONFORMANT (no `napplet-type` meta, `window.napplet` never installed, no `shell.ready`) because it was testing the wrong, never-booting artifact. + `napplet-conformance --ui .` (and `napplet-conformance .`) on a Vite **project + root** previously served the source `index.html` — a Vite entry referencing + `/src/*.ts` that cannot execute in the opaque-origin `srcdoc` sandbox — + instead of the built single-file `dist/index.html`. The live UI runner then + reported a spurious NON-CONFORMANT (no `napplet-type` meta, `window.napplet` + never installed, no `shell.ready`) because it was testing the wrong, + never-booting artifact. - `resolveNappletDir` now prefers `<target>/dist/index.html` over `<target>/index.html`, so pointing the CLI at a project root tests its build output. A plain built directory (only `index.html`, no `dist/`) still resolves as before, and `--ui . --exec "vite build --watch"` now serves the built single-file while running the build in the project root. + `resolveNappletDir` now prefers `<target>/dist/index.html` over + `<target>/index.html`, so pointing the CLI at a project root tests its build + output. A plain built directory (only `index.html`, no `dist/`) still resolves + as before, and `--ui . --exec "vite build --watch"` now serves the built + single-file while running the build in the project root. ## 0.2.1 ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). - - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets + ([napplet/web#53](https://github.com/napplet/web/issues/53)). + + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via + `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an + opaque origin). There is no served origin from which to fetch an external + `<script src>`, so a napplet's executable JS **must** be inline. The toolchain + was enforcing the opposite under an invented "shell-as-CSP-authority / + `script-src 'self'`" model that NIP-5D never defines and that was justified by + NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its + unconditional `closeBundle` call. The build no longer aborts on inline + scripts; `artifactMode: 'single-file'` still folds local script/style assets + into `index.html` and now preserves any pre-existing inline scripts + verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): + removed the `manifest/no-inline-scripts` check, the `inline-script` error + code, and the `findInlineScripts` export. A conformant napplet that carries + inline JS no longer fails conformance. - Updated dependencies - @napplet/conformance@0.2.1 @@ -206,12 +263,31 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -222,22 +298,23 @@ ### Patch Changes -- b705d67: Rebuild against `@napplet/conformance@0.1.1`. The CLI bundles the engine into - `cli.js` (and the host UI bundle), so it must be republished to pick up the - manifest-parsing fixes (HTML-entity-decoded config schema; aggregate-hash check - removed). No CLI behavior change of its own. +- b705d67: Rebuild against `@napplet/conformance@0.1.1`. The CLI bundles the + engine into `cli.js` (and the host UI bundle), so it must be republished to + pick up the manifest-parsing fixes (HTML-entity-decoded config schema; + aggregate-hash check removed). No CLI behavior change of its own. ## 0.1.0 ### Minor Changes -- c8d0198: Initial release. Headless `napplet-conformance` runner that drives the - `@napplet/conformance` engine against a napplet in real Chromium via Playwright: - serves the built napplet (and the engine bundle) on loopback with permissive CORS, - boots it into a `sandbox="allow-scripts"` iframe, records every emitted envelope, - runs a graceful-degradation pass, then prints a `pretty`/`json`/`junit` report and - exits non-zero on any error-severity failure. Wire it up as `test:conformance`. - npm-only (Playwright dependency); the pure engine ships to both npm and JSR. +- c8d0198: Initial release. Headless `napplet-conformance` runner that drives + the `@napplet/conformance` engine against a napplet in real Chromium via + Playwright: serves the built napplet (and the engine bundle) on loopback with + permissive CORS, boots it into a `sandbox="allow-scripts"` iframe, records + every emitted envelope, runs a graceful-degradation pass, then prints a + `pretty`/`json`/`junit` report and exits non-zero on any error-severity + failure. Wire it up as `test:conformance`. npm-only (Playwright dependency); + the pure engine ships to both npm and JSR. ### Patch Changes diff --git a/packages/conformance-cli/README.md b/packages/conformance-cli/README.md index ef94e5e5..de457547 100644 --- a/packages/conformance-cli/README.md +++ b/packages/conformance-cli/README.md @@ -66,3 +66,7 @@ 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. + +### 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). diff --git a/packages/conformance-cli/package.json b/packages/conformance-cli/package.json index b10668c5..6e37a53c 100644 --- a/packages/conformance-cli/package.json +++ b/packages/conformance-cli/package.json @@ -1,7 +1,7 @@ { "name": "@napplet/conformance-cli", - "version": "0.3.3", - "description": "Headless CLI that runs @napplet/conformance against a napplet in real Chromium via Playwright — the `test:conformance` runner", + "version": "0.3.4", + "description": "Headless CLI that runs @napplet/conformance against a napplet in real Chromium via Playwright \u2014 the `test:conformance` runner", "type": "module", "bin": { "napplet-conformance": "./dist/cli.js" @@ -16,8 +16,8 @@ "dependencies": { "@kehto/paja": "0.19.0", "@napplet/conformance": "workspace:^", - "@napplet/core": "0.32.0", - "@napplet/nap": "0.32.0", + "@napplet/core": "workspace:*", + "@napplet/nap": "workspace:*", "playwright": "^1.59.1" }, "devDependencies": { diff --git a/packages/conformance/CHANGELOG.md b/packages/conformance/CHANGELOG.md index 010c6fb1..029e299d 100644 --- a/packages/conformance/CHANGELOG.md +++ b/packages/conformance/CHANGELOG.md @@ -1,14 +1,44 @@ # @napplet/conformance +## 0.20.0 + +### Minor Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + +### Patch Changes + +- Updated dependencies + - @napplet/core@0.34.0 + - @napplet/nap@0.34.0 + ## 0.19.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative + convention URIs and naddr recommendations, expose acceptance results and + buffered onDelivery callbacks, and discover opaque catalog identifiers with + parameter contracts. Restore the optional NAP-SHELL environment API + independently of other domain calls. Align INC provenance with authenticated + endpoint identifiers and deliver theme/identity updates only for exposed + domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, + options) and open(uri, options), from handled/windowId to acceptance, and from + candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK + helpers remain exported with the URI signatures. CLI helper extraction + preserves existing exports and behavior. -- Validate the optional `handler`, `behavior`, and `handlerHint` fields of `intent.invoke` requests: `handlerHint.address` must be a `35129:<pubkey>:<d>` coordinate and `relays` an array of strings. +- Validate the optional `handler`, `behavior`, and `handlerHint` fields of + `intent.invoke` requests: `handlerHint.address` must be a `35129:<pubkey>:<d>` + coordinate and `relays` an array of strings. ### Patch Changes @@ -20,19 +50,35 @@ ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 + and 35129. Writers emit one direct artifact `x` hash, description content, + required/optional `R`/`O` domains and independent `z`/`i` advertisements. + Readers verify current events and raw artifact bytes; legacy events require + migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. + CLI deployment defaults to current output and offers temporary explicit legacy + serialization. Add offline signed-event migration previews without altering + source events or publishing. Vite builds default to a self-contained artifact; + compatibility helper and sidecar names remain available. See the event + migration guide for package cutoffs, immutable pointers and shell ACL/storage + implications. ### Patch Changes -- Report domains outside the toolchain registry through the advisory `manifest/requires-known` check for current `R`/`O` tags; warnings never fail a run (#223). Prefixed, whitespace-separated, and `domain.action` values still fail the bare-domain check. No lowercase-only naming restriction is imposed beyond the live NIP-5D contract. +- Report domains outside the toolchain registry through the advisory + `manifest/requires-known` check for current `R`/`O` tags; warnings never fail + a run (#223). Prefixed, whitespace-separated, and `domain.action` values still + fail the bare-domain check. No lowercase-only naming restriction is imposed + beyond the live NIP-5D contract. ## 0.17.0 ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries + optional advisory Blossom `servers`, `bytesMany` accepts per-resource request + objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits + the amended envelopes. ### Patch Changes @@ -44,7 +90,8 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload + conventions, and require structured invoke results. - Updated dependencies [b3f0007] - @napplet/nap@0.31.2 @@ -52,7 +99,8 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol + behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -61,13 +109,25 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, + `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, + shim and SDK helpers, injected `window.napplet.fs` methods, and conformance + coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -82,11 +142,22 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ### Patch Changes @@ -98,12 +169,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ### Patch Changes @@ -115,7 +186,10 @@ ### Minor Changes -- 3dbced2: Validate NIP-5D manifest events instead of private HTML manifest metadata in the default conformance path. Adds `validateManifestEvent` and napplet manifest kind exports while keeping `validateManifest(html)` as an HTML-only compatibility wrapper. +- 3dbced2: Validate NIP-5D manifest events instead of private HTML manifest + metadata in the default conformance path. Adds `validateManifestEvent` and + napplet manifest kind exports while keeping `validateManifest(html)` as an + HTML-only compatibility wrapper. ## 0.12.6 @@ -129,13 +203,15 @@ ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, + conformance, shim, and Vite plugin packages. ## 0.12.4 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API + types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -146,12 +222,16 @@ - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. - -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, - helpers, injected runtime surface, and conformance coverage. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, + and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check + accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level + authoring flow so generated napplets route bytes, state, relays, signing, + and links through shell-owned NAPs. + +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` + wire types, helpers, injected runtime surface, and conformance coverage. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -166,7 +246,8 @@ ### Patch Changes -- 5371643: Expose `resource.bytes` in the conformance reference runtime and support deterministic `data:` URL bytes. +- 5371643: Expose `resource.bytes` in the conformance reference runtime and + support deterministic `data:` URL bytes. ## 0.12.1 @@ -180,7 +261,9 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw + read events now use `RelayEventResult` with optional `sidecar.resources` and + `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -192,7 +275,10 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / + `count.query.result` envelope types, `window.napplet.count.query(...)`, + `@napplet/nap/count` subpaths, SDK exports, and conformance + validator/reference-shell coverage. ### Patch Changes @@ -204,9 +290,15 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime + API, typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. ### Patch Changes @@ -227,9 +319,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/core@0.23.0 @@ -239,9 +331,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, - `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and - conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed + payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK + wrappers, and conformance validator coverage. ### Patch Changes @@ -253,8 +345,9 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including - wire types, shim and SDK helpers, core global types, and conformance coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, + including wire types, shim and SDK helpers, core global types, and conformance + coverage. ### Patch Changes @@ -266,7 +359,9 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` + subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -291,9 +386,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns - identity, consent, event construction, signing, publishing, relay access, and - NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell + owns identity, consent, event construction, signing, publishing, relay access, + and NIP-19 handling. - Updated dependencies [61431b7] - Updated dependencies [086f36e] @@ -304,7 +399,9 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` + subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -318,10 +415,10 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, - `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core + types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -336,9 +433,10 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from - `@napplet/sdk`, and teaches conformance validation/reference-shell handling - about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers + from `@napplet/sdk`, and teaches conformance validation/reference-shell + handling about `serial.open`, `serial.write`, `serial.close`, and + `serial.event`. ### Patch Changes @@ -358,11 +456,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a - deferred NAP's surface must not outlive the NAP). The opaque `class` integer was - re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, - but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is - now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md + rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` + integer was re-homed into NAP-SHELL's `shell.init` environment during the + NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The + `shell.init` wire shape is now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -372,7 +470,8 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option + (`@napplet/conformance`). ### Patch Changes @@ -392,23 +491,56 @@ ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). - - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets + ([napplet/web#53](https://github.com/napplet/web/issues/53)). + + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via + `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an + opaque origin). There is no served origin from which to fetch an external + `<script src>`, so a napplet's executable JS **must** be inline. The toolchain + was enforcing the opposite under an invented "shell-as-CSP-authority / + `script-src 'self'`" model that NIP-5D never defines and that was justified by + NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its + unconditional `closeBundle` call. The build no longer aborts on inline + scripts; `artifactMode: 'single-file'` still folds local script/style assets + into `index.html` and now preserves any pre-existing inline scripts + verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): + removed the `manifest/no-inline-scripts` check, the `inline-script` error + code, and the `findInlineScripts` export. A conformant napplet that carries + inline JS no longer fails conformance. ## 0.2.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -420,22 +552,26 @@ ### Patch Changes -- 06cfecf: Manifest validator fixes found by dogfooding against a real boilerplate build: - - Decode HTML entities when reading `<meta>` content, so an escaped config schema - (the build plugin serializes JSON with `"`) parses the same way a real - `getAttribute('content')` would — previously it failed as "not valid JSON". +- 06cfecf: Manifest validator fixes found by dogfooding against a real + boilerplate build: + - Decode HTML entities when reading `<meta>` content, so an escaped config + schema (the build plugin serializes JSON with `"`) parses the same way + a real `getAttribute('content')` would — previously it failed as "not valid + JSON". - Remove the `napplet-aggregate-hash` check entirely. A napplet cannot contain - its own aggregate hash, and the tag is not a spec artifact; the shell computes - the hash from the served files. The catalog drops to 13 checks. + its own aggregate hash, and the tag is not a spec artifact; the shell + computes the hash from the served files. The catalog drops to 13 checks. ## 0.1.0 ### Minor Changes -- c8d0198: Initial release. Framework-agnostic napplet protocol conformance engine: hand-written - per-NAP runtime envelope validators across all 16 NAP domains (drift-guarded against - `@napplet/nap` source), a manifest/meta validator, a scriptable reference mock shell - that records emitted envelopes and answers the `shell.ready`→`shell.init` handshake, a - browser-safe boot harness (`bootAndCollect`), the zero-config conformance check - catalog (`runConformance`), and `toPretty`/`toJson`/`toJUnit` reporters. Browser-safe - and dependency-light so it can be reused by both the headless CLI and the web runtime. +- c8d0198: Initial release. Framework-agnostic napplet protocol conformance + engine: hand-written per-NAP runtime envelope validators across all 16 NAP + domains (drift-guarded against `@napplet/nap` source), a manifest/meta + validator, a scriptable reference mock shell that records emitted envelopes + and answers the `shell.ready`→`shell.init` handshake, a browser-safe boot + harness (`bootAndCollect`), the zero-config conformance check catalog + (`runConformance`), and `toPretty`/`toJson`/`toJUnit` reporters. Browser-safe + and dependency-light so it can be reused by both the headless CLI and the web + runtime. diff --git a/packages/conformance/README.md b/packages/conformance/README.md index ad1c6f30..9d54dcb8 100644 --- a/packages/conformance/README.md +++ b/packages/conformance/README.md @@ -100,3 +100,7 @@ for (const check of run.checks) { ## CLI Pairing The package intentionally stays framework-agnostic. For a project-level command that launches real Chromium against a built napplet, use the companion `napplet-conformance` CLI shipped from `@napplet/conformance-cli`. + +### GAMEPAD integration + +GAMEPAD validation recognizes only `gamepad.subscribe`, `gamepad.unsubscribe`, and `gamepad.state`; correlation ids are rejected. The injected browser API shares snapshot validation with the portable binding and accepts only parent messages. The reference mock reports unavailable controllers rather than physical input. Focus and native-access isolation require host integration tests. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). diff --git a/packages/conformance/jsr.json b/packages/conformance/jsr.json index 73310d37..bbe717ce 100644 --- a/packages/conformance/jsr.json +++ b/packages/conformance/jsr.json @@ -1,11 +1,11 @@ { "name": "@napplet/conformance", - "version": "0.19.0", + "version": "0.20.0", "license": "MIT", "exports": "./src/index.ts", "imports": { - "@napplet/core": "jsr:@napplet/core@^0.33.0", - "@napplet/nap": "jsr:@napplet/nap@^0.33.0" + "@napplet/core": "jsr:@napplet/core@^0.34.0", + "@napplet/nap": "jsr:@napplet/nap@^0.34.0" }, "publish": { "include": [ diff --git a/packages/conformance/package.json b/packages/conformance/package.json index 9a349871..fde20df0 100644 --- a/packages/conformance/package.json +++ b/packages/conformance/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/conformance", - "version": "0.19.0", + "version": "0.20.0", "description": "Framework-agnostic napplet protocol conformance engine — reference mock shell, per-NAP envelope validators, manifest validator, check registry, and reporters", "type": "module", "main": "./dist/index.js", diff --git a/packages/conformance/src/run/boot.ts b/packages/conformance/src/run/boot.ts index 4dbe2629..f5b9a814 100644 --- a/packages/conformance/src/run/boot.ts +++ b/packages/conformance/src/run/boot.ts @@ -214,7 +214,7 @@ async function bootOnce( const shell = createReferenceShell(); const iframe = doc.createElement('iframe'); iframe.setAttribute('sandbox', opts.allowSameOrigin ? 'allow-scripts allow-same-origin' : 'allow-scripts'); - iframe.setAttribute('allow', "gamepad 'none'"); + iframe.setAttribute('allow', "gamepad 'none'; hid 'none'; usb 'none'"); iframe.setAttribute('aria-hidden', 'true'); iframe.style.position = 'absolute'; iframe.style.width = '1px'; diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index ef97f35f..b8b0956e 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,36 +1,74 @@ # @napplet/core +## 0.34.0 + +### Minor Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + ## 0.33.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative + convention URIs and naddr recommendations, expose acceptance results and + buffered onDelivery callbacks, and discover opaque catalog identifiers with + parameter contracts. Restore the optional NAP-SHELL environment API + independently of other domain calls. Align INC provenance with authenticated + endpoint identifiers and deliver theme/identity updates only for exposed + domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, + options) and open(uri, options), from handled/windowId to acceptance, and from + candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK + helpers remain exported with the URI signatures. CLI helper extraction + preserves existing exports and behavior. ## 0.32.0 ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries + optional advisory Blossom `servers`, `bytesMany` accepts per-resource request + objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits + the amended envelopes. ## 0.31.1 ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol + behavior. ## 0.31.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, + `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, + shim and SDK helpers, injected `window.napplet.fs` methods, and conformance + coverage for the new `fs.pick*` envelopes. ## 0.30.0 @@ -38,22 +76,33 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ## 0.29.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ## 0.28.0 @@ -67,43 +116,58 @@ ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related - public media context types. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned - `RelayEventResult` wrapper as `result` instead of split `event` and `relays` - fields. -- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and - export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, - helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource - scheme. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload + and related public media context types. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the + relay-owned `RelayEventResult` wrapper as `result` instead of split `event` + and `relays` fields. +- d88d674: Align NAP-DM result message types with success-or-error wire + envelopes and export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` + wire types, helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` + resource scheme. ## 0.27.0 ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible + routing and lifecycle controls. `OutboxStrategy` is no longer exported, + `strategy` is no longer accepted on outbox option objects, and + `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is + represented by the handle plus `outbox.close` / `outbox.closed`. ## 0.26.0 ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw + read events now use `RelayEventResult` with optional `sidecar.resources` and + `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ## 0.25.0 ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / + `count.query.result` envelope types, `window.napplet.count.query(...)`, + `@napplet/nap/count` subpaths, SDK exports, and conformance + validator/reference-shell coverage. ## 0.24.0 ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime + API, typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. ## 0.23.0 @@ -116,30 +180,33 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. ## 0.22.0 ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, - `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and - conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed + payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK + wrappers, and conformance validator coverage. ## 0.21.0 ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including - wire types, shim and SDK helpers, core global types, and conformance coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, + including wire types, shim and SDK helpers, core global types, and conformance + coverage. ## 0.20.0 ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` + subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and + reference-shell responses. ## 0.19.0 @@ -148,9 +215,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns - identity, consent, event construction, signing, publishing, relay access, and - NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell + owns identity, consent, event construction, signing, publishing, relay access, + and NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -164,7 +231,9 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` + subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and + reference-shell responses. ## 0.17.0 @@ -172,13 +241,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, - `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core + types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. ## 0.16.0 @@ -189,30 +258,35 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from - `@napplet/sdk`, and teaches conformance validation/reference-shell handling - about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers + from `@napplet/sdk`, and teaches conformance validation/reference-shell + handling about `serial.open`, `serial.write`, `serial.close`, and + `serial.event`. ## 0.15.0 ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. - - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON - envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue - `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so - `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; - the envelope simply never crossed (napplet/web#67). - - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, - `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` - types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, - snapshots the envelope (stripping reactive proxies while preserving binary, - `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a - loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this - boundary instead of calling `window.parent.postMessage(msg, '*')` directly. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no + longer fail silently. + + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a + JSON envelope through `postMessage`. Framework reactive values — Svelte 5 + `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't + structured-cloneable, so `postMessage` threw a `DataCloneError` that got + silently swallowed in async paths; the envelope simply never crossed + (napplet/web#67). + + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, + `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / + `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only + on a `DataCloneError`, snapshots the envelope (stripping reactive proxies + while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — + warning once. `'strict'` throws a loud, actionable, synchronous error + instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through + this boundary instead of calling `window.parent.postMessage(msg, '*')` + directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -221,11 +295,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a - deferred NAP's surface must not outlive the NAP). The opaque `class` integer was - re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, - but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is - now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md + rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` + integer was re-homed into NAP-SHELL's `shell.init` environment during the + NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The + `shell.init` wire shape is now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -235,95 +309,131 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option + (`@napplet/conformance`). ## 0.13.0 ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, + [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. + + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an + optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. + Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface + (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets + `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s + `storage.instance.*` and as `storageInstance*` helpers in + `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type + and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** + `scope` field, so they remain byte-identical on the wire to prior versions. ## 0.12.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ## 0.11.0 ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the + shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet - had no way to fetch the theme through `window.napplet`. It now mirrors the - read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a + napplet had no way to fetch the theme through `window.napplet`. It now mirrors + the read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` - updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus - `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed + `theme.changed` updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, + plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in + `@napplet/core`. ## 0.10.0 ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another - napplet by its role (archetype) through `window.napplet.intent` (`invoke`, - `open`, `available`, `handlers`, `onChanged`) without addressing it directly. - The shell resolves the archetype to an installed napplet (honoring the user's - default-handler preference), creates or focuses its window, and delivers the - payload using the named NAP-N protocol. Routing (`archetype`) and payload format - (`protocol`) are orthogonal; the shell owns resolution, default handling, window - lifecycle, and the cross-napplet trust boundary. - - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` - surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with - request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke + another napplet by its role (archetype) through `window.napplet.intent` + (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it + directly. The shell resolves the archetype to an installed napplet (honoring + the user's default-handler preference), creates or focuses its window, and + delivers the payload using the named NAP-N protocol. Routing (`archetype`) and + payload format (`protocol`) are orthogonal; the shell owns resolution, default + handling, window lifecycle, and the cross-napplet trust boundary. + + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, + `intent` surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) + with request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` + envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type + re-exports. ## 0.9.0 ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes - through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the - runtime selects the storage server, signs the rail authorization (NIP-98 for - NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable - URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server - credentials, or direct network access. - - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` - surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with - request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now + upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) + while the runtime selects the storage server, signs the rail authorization + (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and + returns a stable URL plus NIP-94 integrity metadata. Napplets never receive + signing keys, server credentials, or direct network access. + + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, + `upload` surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) + with request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` + envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type + re-exports. ## 0.8.0 ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM - servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, - `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while - the runtime owns all ContextVM transport — relay routing, signing, encryption, - JSON-RPC correlation, initialization, policy, and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover + ContextVM servers and run MCP operations (`tools/list`, `tools/call`, + `resources/list`, `resources/read`) over Nostr through the shell via + `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay + routing, signing, encryption, JSON-RPC correlation, initialization, policy, + and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -331,17 +441,19 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, - publish, and resolve relay plans through `window.napplet.outbox` while the - runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature - validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, + subscribe, publish, and resolve relay plans through `window.napplet.outbox` + while the runtime owns relay discovery, NIP-65 routing, fallback, + deduplication, signature validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` - surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with - request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, + `outbox` surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) + with request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` + envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type + re-exports. ## 0.7.0 @@ -349,13 +461,19 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and + TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and + `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over + the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for + downstream migration. ## 0.6.0 ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. +- Rename the public package and protocol surface to NAP terminology, including + `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` + identifiers. ## 0.5.0 @@ -367,79 +485,212 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / + `identity.onChanged`, make `getPublicKey()` represent signed-out state with + `""`, remove the old identity decrypt surface and rumor types, and normalize + shell support names around canonical `nap:` / `NAP-NN` identifiers. ## 0.3.1 ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene + changes across the changed runtime packages. ## 0.3.0 ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP + emission from the vite-plugin (build time) to the host shell (runtime) for + every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative + abstract security posture via `class.assigned` wire) and NAP-CONNECT + (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The + option is retained as an `@deprecated` accept-but-warn shim for one release + cycle — the plugin emits one `console.warn` per build per config-resolved if + a `strictCsp` value is present, and the option is ignored. Production builds + no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is + now the sole authority on runtime CSP for every napplet, per + `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a + future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for + v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` + scans `dist/index.html` and throws a diagnostic when any `<script>` element + lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so + inline scripts would be blocked at runtime anyway; failing at build time + surfaces the problem before deployment. The allow-list exempts + `application/json`, `application/ld+json`, `importmap`, and + `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on + `NamespacedCapability`. The template-literal type still accepts it so + existing code type-checks; capability advertisement is superseded by + `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as + `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required + origins. Each origin is validated via the shared `normalizeConnectOrigin()` + validator from `@napplet/nap/connect`, emitted as a signed + `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via + a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → + UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). + Dev-mode-only `<meta name="napplet-connect-requires">` is injected for + shell-less `vite serve` previews; the shell-authoritative + `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 + entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` + (mirrors `resource` block) and `class?: number` (optional — undefined until + `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `NappletConnect` interface, pure + `normalizeConnectOrigin(origin)` validator (shared source-of-truth for + build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests + pass), and `installConnectShim()` which reads + `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type + (`{ type: 'class.assigned'; id: string; class: number }`), + `handleClassMessage` dispatcher handler, and `installClassShim()` which + mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** + defaults to `{granted: false, origins: []}` (never `undefined`); + **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). + Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the + `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + + the `class` types namespace — parallel to the existing `resource` + re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte + fetches — it works in every class posture. Reach for NAP-CONNECT only when + the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, + WebSocket, Server-Sent Events, streaming responses, custom request headers, + long-lived connections, or third-party libraries that call `fetch`/open + sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: + `class === 2` iff `connect.granted === true` at `class.assigned` send time. + See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full + scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward + from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn + once per build if you leave it. (Required when REMOVE-STRICTCSP lands; + optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move + their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the + plugin's new `connect: string[]` option. See + `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your + host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` + before reading `window.napplet.class` / `window.napplet.connect` — both + gracefully degrade (`undefined` / `{granted: false, origins: []}` + respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds + `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — + shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt + primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext + access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict + baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet + main branch; it has been renumbered to v0.30.0 during a post-merge version + split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority + milestone (feat/strict-model, shipped 2026-04-21). This changeset describes + shipped behavior; the underlying commits already exist in main (Phases 135-138 + of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, + `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope + triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, + `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, + `impersonation`, `unsupported-encryption`, `policy-denied`), + `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), + `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` + method type. Zero new runtime symbols in the types-only entry point — pure + type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding + (request-correlation by id, reusing existing `pendingRequests` + infrastructure), the `identity.decrypt.result` / `.error` handler branches + (added to `handleIdentityMessage` with discriminated-union exhaustiveness + via `assertNever`), and the bare-name `identityDecrypt(event)` helper. + Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so + `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central + shim — no new install function; the addition is two surgical edits on + `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central + `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new + identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports + — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full + conformance table (4 shell MUSTs: class-gating, outer-sig-verify, + impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, + outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy + floor), 8-code error vocabulary, and Security Considerations subsection. + Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST + `(dTag, aggregateHash)` violation-correlation row + Security Considerations + subsection distinguishing observability from enforcement. Bundled into the + same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a + `### NIP-07 Extension Injection Residual` subsection documenting the + `all_frames: true` content-script injection vector, nonce-based `script-src` + legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical + lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and + `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as + the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero + occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably + blocks legacy `<script>`-tag content-script injection AND fires + `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and + `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member + triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. + Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 + browser extension injects `window.nostr` into the iframe via a content + script (architecturally unavoidable per WebExtension spec), shells MUST NOT + _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell + is free to use its own NIP-07 extension internally to fulfill + signing/encryption duties; what is forbidden is exposing `window.nostr` to + the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) + cannot decrypt — they receive `class-forbidden` at the shell boundary + because plaintext could exfiltrate to approved origins with zero shell + visibility. The class invariant ties plaintext exposure to zero-egress + posture. ## 0.2.1 ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 + tarballs on npm contained unresolved `workspace:*` specs in dependencies, + breaking installs. This patch bump exists solely to produce + correctly-assembled tarballs via `pnpm publish -r` (which rewrites + `workspace:*` → concrete versions at pack time). diff --git a/packages/core/README.md b/packages/core/README.md index 2925635c..2b3fce4e 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -416,3 +416,7 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); ``` The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). + +### GAMEPAD integration + +`GamepadState`, `Gamepad`, `GamepadButton`, `GamepadSlot`, `GamepadApi`, and the `gamepad` domain are exported for the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). Its fire-and-forget envelopes carry no correlation id. diff --git a/packages/core/jsr.json b/packages/core/jsr.json index 2fd52efc..be1321ab 100644 --- a/packages/core/jsr.json +++ b/packages/core/jsr.json @@ -1,6 +1,6 @@ { "name": "@napplet/core", - "version": "0.33.0", + "version": "0.34.0", "license": "MIT", "exports": "./src/index.ts", "publish": { diff --git a/packages/core/package.json b/packages/core/package.json index ebd2aa7d..d0ff4ca7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/core", - "version": "0.33.0", + "version": "0.34.0", "description": "Shared protocol types, constants, and message definitions for the napplet protocol", "type": "module", "main": "./dist/index.js", diff --git a/packages/nap/CHANGELOG.md b/packages/nap/CHANGELOG.md index acf73e56..7e85f8d9 100644 --- a/packages/nap/CHANGELOG.md +++ b/packages/nap/CHANGELOG.md @@ -1,12 +1,39 @@ # @napplet/nap +## 0.34.0 + +### Minor Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + +### Patch Changes + +- Updated dependencies + - @napplet/core@0.34.0 + ## 0.33.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative + convention URIs and naddr recommendations, expose acceptance results and + buffered onDelivery callbacks, and discover opaque catalog identifiers with + parameter contracts. Restore the optional NAP-SHELL environment API + independently of other domain calls. Align INC provenance with authenticated + endpoint identifiers and deliver theme/identity updates only for exposed + domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, + options) and open(uri, options), from handled/windowId to acceptance, and from + candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK + helpers remain exported with the URI signatures. CLI helper extraction + preserves existing exports and behavior. ### Patch Changes @@ -17,7 +44,10 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries + optional advisory Blossom `servers`, `bytesMany` accepts per-resource request + objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits + the amended envelopes. ### Patch Changes @@ -28,13 +58,15 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload + conventions, and require structured invoke results. ## 0.31.1 ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol + behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 @@ -42,13 +74,25 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, + `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, + shim and SDK helpers, injected `window.napplet.fs` methods, and conformance + coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -62,11 +106,22 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ### Patch Changes @@ -77,12 +132,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ### Patch Changes @@ -106,32 +161,37 @@ ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API + types. ## 0.27.2 ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related - public media context types. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload + and related public media context types. - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. - -- ae5eecf: Align NAP-LISTS add/remove result message types with their action-specific wire - count fields. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned - `RelayEventResult` wrapper as `result` instead of split `event` and `relays` - fields. -- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and - export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, - helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource - scheme. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, + and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check + accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level + authoring flow so generated napplets route bytes, state, relays, signing, + and links through shell-owned NAPs. + +- ae5eecf: Align NAP-LISTS add/remove result message types with their + action-specific wire count fields. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the + relay-owned `RelayEventResult` wrapper as `result` instead of split `event` + and `relays` fields. +- d88d674: Align NAP-DM result message types with success-or-error wire + envelopes and export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` + wire types, helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` + resource scheme. - Updated dependencies [e60f8a6] - Updated dependencies [bf246df] - Updated dependencies [d88d674] @@ -143,13 +203,19 @@ ### Patch Changes -- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt `make-napplet` orchestration skill, and align copied package examples so agents do not default social reads and publishes to low-level NAP-RELAY. +- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt + `make-napplet` orchestration skill, and align copied package examples so + agents do not default social reads and publishes to low-level NAP-RELAY. ## 0.27.0 ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible + routing and lifecycle controls. `OutboxStrategy` is no longer exported, + `strategy` is no longer accepted on outbox option objects, and + `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is + represented by the handle plus `outbox.close` / `outbox.closed`. ### Patch Changes @@ -160,7 +226,9 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw + read events now use `RelayEventResult` with optional `sidecar.resources` and + `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -171,7 +239,10 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / + `count.query.result` envelope types, `window.napplet.count.query(...)`, + `@napplet/nap/count` subpaths, SDK exports, and conformance + validator/reference-shell coverage. ### Patch Changes @@ -182,9 +253,15 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime + API, typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. ### Patch Changes @@ -204,9 +281,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. ### Patch Changes @@ -217,16 +294,17 @@ ### Patch Changes -- 0c45563: Guard relay query results so malformed host responses without an `events` array - resolve to `[]` instead of leaking `undefined` through the typed query contract. +- 0c45563: Guard relay query results so malformed host responses without an + `events` array resolve to `[]` instead of leaking `undefined` through the + typed query contract. ## 0.22.0 ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, - `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and - conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed + payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK + wrappers, and conformance validator coverage. ### Patch Changes @@ -237,8 +315,9 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including - wire types, shim and SDK helpers, core global types, and conformance coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, + including wire types, shim and SDK helpers, core global types, and conformance + coverage. ### Patch Changes @@ -249,7 +328,9 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` + subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -263,9 +344,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns - identity, consent, event construction, signing, publishing, relay access, and - NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell + owns identity, consent, event construction, signing, publishing, relay access, + and NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -285,7 +366,9 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` + subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -298,13 +381,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, - `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core + types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -318,9 +401,10 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from - `@napplet/sdk`, and teaches conformance validation/reference-shell handling - about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers + from `@napplet/sdk`, and teaches conformance validation/reference-shell + handling about `serial.open`, `serial.write`, `serial.close`, and + `serial.event`. ### Patch Changes @@ -331,22 +415,26 @@ ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. - - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON - envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue - `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so - `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; - the envelope simply never crossed (napplet/web#67). - - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, - `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` - types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, - snapshots the envelope (stripping reactive proxies while preserving binary, - `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a - loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this - boundary instead of calling `window.parent.postMessage(msg, '*')` directly. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no + longer fail silently. + + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a + JSON envelope through `postMessage`. Framework reactive values — Svelte 5 + `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't + structured-cloneable, so `postMessage` threw a `DataCloneError` that got + silently swallowed in async paths; the envelope simply never crossed + (napplet/web#67). + + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, + `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / + `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only + on a `DataCloneError`, snapshots the envelope (stripping reactive proxies + while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — + warning once. `'strict'` throws a loud, actionable, synchronous error + instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through + this boundary instead of calling `window.parent.postMessage(msg, '*')` + directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -360,11 +448,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a - deferred NAP's surface must not outlive the NAP). The opaque `class` integer was - re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, - but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is - now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md + rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` + integer was re-homed into NAP-SHELL's `shell.init` environment during the + NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The + `shell.init` wire shape is now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -374,7 +462,8 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option + (`@napplet/conformance`). ### Patch Changes @@ -385,11 +474,20 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, + [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an + optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. + Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface + (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets + `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s + `storage.instance.*` and as `storageInstance*` helpers in + `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type + and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** + `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -400,12 +498,31 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -416,20 +533,22 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the + shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet - had no way to fetch the theme through `window.napplet`. It now mirrors the - read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a + napplet had no way to fetch the theme through `window.napplet`. It now mirrors + the read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` - updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus - `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed + `theme.changed` updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, + plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in + `@napplet/core`. ### Patch Changes @@ -440,21 +559,23 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another - napplet by its role (archetype) through `window.napplet.intent` (`invoke`, - `open`, `available`, `handlers`, `onChanged`) without addressing it directly. - The shell resolves the archetype to an installed napplet (honoring the user's - default-handler preference), creates or focuses its window, and delivers the - payload using the named NAP-N protocol. Routing (`archetype`) and payload format - (`protocol`) are orthogonal; the shell owns resolution, default handling, window - lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke + another napplet by its role (archetype) through `window.napplet.intent` + (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it + directly. The shell resolves the archetype to an installed napplet (honoring + the user's default-handler preference), creates or focuses its window, and + delivers the payload using the named NAP-N protocol. Routing (`archetype`) and + payload format (`protocol`) are orthogonal; the shell owns resolution, default + handling, window lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` - surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with - request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, + `intent` surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) + with request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` + envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type + re-exports. ### Patch Changes @@ -465,19 +586,21 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes - through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the - runtime selects the storage server, signs the rail authorization (NIP-98 for - NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable - URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server - credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now + upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) + while the runtime selects the storage server, signs the rail authorization + (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and + returns a stable URL plus NIP-94 integrity metadata. Napplets never receive + signing keys, server credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` - surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with - request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, + `upload` surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) + with request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` + envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type + re-exports. ### Patch Changes @@ -488,21 +611,23 @@ ### Patch Changes -- 44c92ca: Fix JSR publishing: `@napplet/nap`'s `jsr.json` exports map was missing the - `cvm` and `outbox` subpaths (it had drifted from `package.json`), so JSR - consumers and dependent packages could not resolve `@napplet/nap/cvm` or - `@napplet/nap/outbox`. The version-sync tooling now regenerates the `jsr.json` - exports from `package.json` on every release so the two can no longer drift. +- 44c92ca: Fix JSR publishing: `@napplet/nap`'s `jsr.json` exports map was + missing the `cvm` and `outbox` subpaths (it had drifted from `package.json`), + so JSR consumers and dependent packages could not resolve `@napplet/nap/cvm` + or `@napplet/nap/outbox`. The version-sync tooling now regenerates the + `jsr.json` exports from `package.json` on every release so the two can no + longer drift. ## 0.8.0 ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM - servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, - `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while - the runtime owns all ContextVM transport — relay routing, signing, encryption, - JSON-RPC correlation, initialization, policy, and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover + ContextVM servers and run MCP operations (`tools/list`, `tools/call`, + `resources/list`, `resources/read`) over Nostr through the shell via + `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay + routing, signing, encryption, JSON-RPC correlation, initialization, policy, + and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -510,17 +635,19 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, - publish, and resolve relay plans through `window.napplet.outbox` while the - runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature - validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, + subscribe, publish, and resolve relay plans through `window.napplet.outbox` + while the runtime owns relay discovery, NIP-65 routing, fallback, + deduplication, signature validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` - surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with - request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, + `outbox` surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) + with request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` + envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type + re-exports. ### Patch Changes @@ -534,7 +661,11 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and + TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and + `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over + the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for + downstream migration. ### Patch Changes @@ -545,7 +676,9 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. +- Rename the public package and protocol surface to NAP terminology, including + `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` + identifiers. ### Patch Changes @@ -567,7 +700,10 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / + `identity.onChanged`, make `getPublicKey()` represent signed-out state with + `""`, remove the old identity decrypt surface and rumor types, and normalize + shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -578,7 +714,8 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene + changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 @@ -586,68 +723,195 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP + emission from the vite-plugin (build time) to the host shell (runtime) for + every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative + abstract security posture via `class.assigned` wire) and NAP-CONNECT + (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The + option is retained as an `@deprecated` accept-but-warn shim for one release + cycle — the plugin emits one `console.warn` per build per config-resolved if + a `strictCsp` value is present, and the option is ignored. Production builds + no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is + now the sole authority on runtime CSP for every napplet, per + `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a + future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for + v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` + scans `dist/index.html` and throws a diagnostic when any `<script>` element + lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so + inline scripts would be blocked at runtime anyway; failing at build time + surfaces the problem before deployment. The allow-list exempts + `application/json`, `application/ld+json`, `importmap`, and + `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on + `NamespacedCapability`. The template-literal type still accepts it so + existing code type-checks; capability advertisement is superseded by + `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as + `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required + origins. Each origin is validated via the shared `normalizeConnectOrigin()` + validator from `@napplet/nap/connect`, emitted as a signed + `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via + a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → + UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). + Dev-mode-only `<meta name="napplet-connect-requires">` is injected for + shell-less `vite serve` previews; the shell-authoritative + `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 + entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` + (mirrors `resource` block) and `class?: number` (optional — undefined until + `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `NappletConnect` interface, pure + `normalizeConnectOrigin(origin)` validator (shared source-of-truth for + build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests + pass), and `installConnectShim()` which reads + `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type + (`{ type: 'class.assigned'; id: string; class: number }`), + `handleClassMessage` dispatcher handler, and `installClassShim()` which + mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** + defaults to `{granted: false, origins: []}` (never `undefined`); + **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). + Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the + `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + + the `class` types namespace — parallel to the existing `resource` + re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte + fetches — it works in every class posture. Reach for NAP-CONNECT only when + the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, + WebSocket, Server-Sent Events, streaming responses, custom request headers, + long-lived connections, or third-party libraries that call `fetch`/open + sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: + `class === 2` iff `connect.granted === true` at `class.assigned` send time. + See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full + scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward + from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn + once per build if you leave it. (Required when REMOVE-STRICTCSP lands; + optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move + their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the + plugin's new `connect: string[]` option. See + `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your + host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` + before reading `window.napplet.class` / `window.napplet.connect` — both + gracefully degrade (`undefined` / `{granted: false, origins: []}` + respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds + `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — + shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt + primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext + access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict + baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet + main branch; it has been renumbered to v0.30.0 during a post-merge version + split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority + milestone (feat/strict-model, shipped 2026-04-21). This changeset describes + shipped behavior; the underlying commits already exist in main (Phases 135-138 + of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, + `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope + triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, + `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, + `impersonation`, `unsupported-encryption`, `policy-denied`), + `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), + `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` + method type. Zero new runtime symbols in the types-only entry point — pure + type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding + (request-correlation by id, reusing existing `pendingRequests` + infrastructure), the `identity.decrypt.result` / `.error` handler branches + (added to `handleIdentityMessage` with discriminated-union exhaustiveness + via `assertNever`), and the bare-name `identityDecrypt(event)` helper. + Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so + `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central + shim — no new install function; the addition is two surgical edits on + `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central + `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new + identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports + — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full + conformance table (4 shell MUSTs: class-gating, outer-sig-verify, + impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, + outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy + floor), 8-code error vocabulary, and Security Considerations subsection. + Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST + `(dTag, aggregateHash)` violation-correlation row + Security Considerations + subsection distinguishing observability from enforcement. Bundled into the + same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a + `### NIP-07 Extension Injection Residual` subsection documenting the + `all_frames: true` content-script injection vector, nonce-based `script-src` + legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical + lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and + `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as + the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero + occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably + blocks legacy `<script>`-tag content-script injection AND fires + `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and + `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member + triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. - -### Patch Changes - -- 066443f: Fix the package publish path so `@napplet/nap` tarballs resolve the internal `@napplet/core` dependency to a semver range instead of leaking the workspace protocol. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. + Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 + browser extension injects `window.nostr` into the iframe via a content + script (architecturally unavoidable per WebExtension spec), shells MUST NOT + _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell + is free to use its own NIP-07 extension internally to fulfill + signing/encryption duties; what is forbidden is exposing `window.nostr` to + the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) + cannot decrypt — they receive `class-forbidden` at the shell boundary + because plaintext could exfiltrate to approved origins with zero shell + visibility. The class invariant ties plaintext exposure to zero-egress + posture. + +### Patch Changes + +- 066443f: Fix the package publish path so `@napplet/nap` tarballs resolve the + internal `@napplet/core` dependency to a semver range instead of leaking the + workspace protocol. - Updated dependencies [3d22a10] - Updated dependencies [60c93a0] - @napplet/core@0.3.0 diff --git a/packages/nap/README.md b/packages/nap/README.md index 9ffd1e04..6ad320ed 100644 --- a/packages/nap/README.md +++ b/packages/nap/README.md @@ -280,3 +280,24 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); ``` The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). + +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/packages/nap/jsr.json b/packages/nap/jsr.json index 7aa197be..6d9219cc 100644 --- a/packages/nap/jsr.json +++ b/packages/nap/jsr.json @@ -1,6 +1,6 @@ { "name": "@napplet/nap", - "version": "0.33.0", + "version": "0.34.0", "license": "MIT", "exports": { "./relay": "./src/relay/index.ts", @@ -109,7 +109,7 @@ "./gamepad/sdk": "./src/gamepad/sdk.ts" }, "imports": { - "@napplet/core": "jsr:@napplet/core@^0.33.0", + "@napplet/core": "jsr:@napplet/core@^0.34.0", "nostr-tools/nip19": "npm:nostr-tools@^2.23.3/nip19" }, "publish": { diff --git a/packages/nap/package.json b/packages/nap/package.json index f3098cb9..3539bc55 100644 --- a/packages/nap/package.json +++ b/packages/nap/package.json @@ -1,7 +1,7 @@ { "name": "@napplet/nap", - "version": "0.33.0", - "description": "The active napplet NAP domains (relay, storage, inc, keys, theme, media, notify, identity, config, resource, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, serial, fs, common, dm, shell) as layered subpath exports", + "version": "0.34.0", + "description": "The active napplet NAP domains (relay, storage, inc, keys, theme, media, notify, identity, config, resource, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, serial, fs, common, dm, shell, gamepad) as layered subpath exports", "type": "module", "exports": { "./relay": { diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 95334372..e3259c0a 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,12 +1,40 @@ # @napplet/sdk +## 0.30.0 + +### Minor Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + +### Patch Changes + +- Updated dependencies + - @napplet/core@0.34.0 + - @napplet/nap@0.34.0 + ## 0.29.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative + convention URIs and naddr recommendations, expose acceptance results and + buffered onDelivery callbacks, and discover opaque catalog identifiers with + parameter contracts. Restore the optional NAP-SHELL environment API + independently of other domain calls. Align INC provenance with authenticated + endpoint identifiers and deliver theme/identity updates only for exposed + domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, + options) and open(uri, options), from handled/windowId to acceptance, and from + candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK + helpers remain exported with the URI signatures. CLI helper extraction + preserves existing exports and behavior. ### Patch Changes @@ -18,7 +46,10 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries + optional advisory Blossom `servers`, `bytesMany` accepts per-resource request + objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits + the amended envelopes. ### Patch Changes @@ -37,7 +68,8 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol + behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -46,13 +78,25 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, + `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, + shim and SDK helpers, injected `window.napplet.fs` methods, and conformance + coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -67,11 +111,22 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ### Patch Changes @@ -83,12 +138,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ### Patch Changes @@ -108,7 +163,8 @@ ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API + types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -116,19 +172,19 @@ ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related - public media context types. -- ae5eecf: Align NAP-LISTS add/remove result message types with their action-specific wire - count fields. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned - `RelayEventResult` wrapper as `result` instead of split `event` and `relays` - fields. -- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and - export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, - helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource - scheme. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload + and related public media context types. +- ae5eecf: Align NAP-LISTS add/remove result message types with their + action-specific wire count fields. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the + relay-owned `RelayEventResult` wrapper as `result` instead of split `event` + and `relays` fields. +- d88d674: Align NAP-DM result message types with success-or-error wire + envelopes and export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` + wire types, helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` + resource scheme. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -143,7 +199,9 @@ ### Patch Changes -- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt `make-napplet` orchestration skill, and align copied package examples so agents do not default social reads and publishes to low-level NAP-RELAY. +- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt + `make-napplet` orchestration skill, and align copied package examples so + agents do not default social reads and publishes to low-level NAP-RELAY. - Updated dependencies [82e50c2] - @napplet/nap@0.27.1 @@ -151,7 +209,11 @@ ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible + routing and lifecycle controls. `OutboxStrategy` is no longer exported, + `strategy` is no longer accepted on outbox option objects, and + `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is + represented by the handle plus `outbox.close` / `outbox.closed`. ### Patch Changes @@ -163,7 +225,9 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw + read events now use `RelayEventResult` with optional `sidecar.resources` and + `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -175,7 +239,10 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / + `count.query.result` envelope types, `window.napplet.count.query(...)`, + `@napplet/nap/count` subpaths, SDK exports, and conformance + validator/reference-shell coverage. ### Patch Changes @@ -187,9 +254,15 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime + API, typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. ### Patch Changes @@ -210,9 +283,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/core@0.23.0 @@ -229,9 +302,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, - `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and - conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed + payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK + wrappers, and conformance validator coverage. ### Patch Changes @@ -243,8 +316,9 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including - wire types, shim and SDK helpers, core global types, and conformance coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, + including wire types, shim and SDK helpers, core global types, and conformance + coverage. ### Patch Changes @@ -256,7 +330,9 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` + subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -271,9 +347,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns - identity, consent, event construction, signing, publishing, relay access, and - NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell + owns identity, consent, event construction, signing, publishing, relay access, + and NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -294,7 +370,9 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` + subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -308,13 +386,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, - `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core + types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -329,9 +407,10 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from - `@napplet/sdk`, and teaches conformance validation/reference-shell handling - about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers + from `@napplet/sdk`, and teaches conformance validation/reference-shell + handling about `serial.open`, `serial.write`, `serial.close`, and + `serial.event`. ### Patch Changes @@ -359,11 +438,20 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, + [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an + optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. + Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface + (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets + `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s + `storage.instance.*` and as `storageInstance*` helpers in + `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type + and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** + `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -375,12 +463,31 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -392,20 +499,22 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the + shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet - had no way to fetch the theme through `window.napplet`. It now mirrors the - read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a + napplet had no way to fetch the theme through `window.napplet`. It now mirrors + the read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` - updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus - `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed + `theme.changed` updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, + plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in + `@napplet/core`. ### Patch Changes @@ -417,21 +526,23 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another - napplet by its role (archetype) through `window.napplet.intent` (`invoke`, - `open`, `available`, `handlers`, `onChanged`) without addressing it directly. - The shell resolves the archetype to an installed napplet (honoring the user's - default-handler preference), creates or focuses its window, and delivers the - payload using the named NAP-N protocol. Routing (`archetype`) and payload format - (`protocol`) are orthogonal; the shell owns resolution, default handling, window - lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke + another napplet by its role (archetype) through `window.napplet.intent` + (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it + directly. The shell resolves the archetype to an installed napplet (honoring + the user's default-handler preference), creates or focuses its window, and + delivers the payload using the named NAP-N protocol. Routing (`archetype`) and + payload format (`protocol`) are orthogonal; the shell owns resolution, default + handling, window lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` - surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with - request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, + `intent` surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) + with request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` + envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type + re-exports. ### Patch Changes @@ -443,19 +554,21 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes - through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the - runtime selects the storage server, signs the rail authorization (NIP-98 for - NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable - URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server - credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now + upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) + while the runtime selects the storage server, signs the rail authorization + (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and + returns a stable URL plus NIP-94 integrity metadata. Napplets never receive + signing keys, server credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` - surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with - request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, + `upload` surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) + with request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` + envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type + re-exports. ### Patch Changes @@ -474,11 +587,12 @@ ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM - servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, - `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while - the runtime owns all ContextVM transport — relay routing, signing, encryption, - JSON-RPC correlation, initialization, policy, and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover + ContextVM servers and run MCP operations (`tools/list`, `tools/call`, + `resources/list`, `resources/read`) over Nostr through the shell via + `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay + routing, signing, encryption, JSON-RPC correlation, initialization, policy, + and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -486,17 +600,19 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, - publish, and resolve relay plans through `window.napplet.outbox` while the - runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature - validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, + subscribe, publish, and resolve relay plans through `window.napplet.outbox` + while the runtime owns relay discovery, NIP-65 routing, fallback, + deduplication, signature validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` - surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with - request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, + `outbox` surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) + with request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` + envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type + re-exports. ### Patch Changes @@ -511,7 +627,11 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and + TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and + `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over + the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for + downstream migration. ### Patch Changes @@ -523,7 +643,9 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. +- Rename the public package and protocol surface to NAP terminology, including + `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` + identifiers. ### Patch Changes @@ -547,7 +669,10 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / + `identity.onChanged`, make `getPublicKey()` represent signed-out state with + `""`, remove the old identity decrypt surface and rumor types, and normalize + shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -559,7 +684,8 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene + changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 - @napplet/nap@0.3.1 @@ -568,64 +694,189 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP + emission from the vite-plugin (build time) to the host shell (runtime) for + every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative + abstract security posture via `class.assigned` wire) and NAP-CONNECT + (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The + option is retained as an `@deprecated` accept-but-warn shim for one release + cycle — the plugin emits one `console.warn` per build per config-resolved if + a `strictCsp` value is present, and the option is ignored. Production builds + no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is + now the sole authority on runtime CSP for every napplet, per + `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a + future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for + v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` + scans `dist/index.html` and throws a diagnostic when any `<script>` element + lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so + inline scripts would be blocked at runtime anyway; failing at build time + surfaces the problem before deployment. The allow-list exempts + `application/json`, `application/ld+json`, `importmap`, and + `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on + `NamespacedCapability`. The template-literal type still accepts it so + existing code type-checks; capability advertisement is superseded by + `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as + `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required + origins. Each origin is validated via the shared `normalizeConnectOrigin()` + validator from `@napplet/nap/connect`, emitted as a signed + `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via + a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → + UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). + Dev-mode-only `<meta name="napplet-connect-requires">` is injected for + shell-less `vite serve` previews; the shell-authoritative + `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 + entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` + (mirrors `resource` block) and `class?: number` (optional — undefined until + `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `NappletConnect` interface, pure + `normalizeConnectOrigin(origin)` validator (shared source-of-truth for + build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests + pass), and `installConnectShim()` which reads + `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type + (`{ type: 'class.assigned'; id: string; class: number }`), + `handleClassMessage` dispatcher handler, and `installClassShim()` which + mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** + defaults to `{granted: false, origins: []}` (never `undefined`); + **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). + Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the + `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + + the `class` types namespace — parallel to the existing `resource` + re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte + fetches — it works in every class posture. Reach for NAP-CONNECT only when + the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, + WebSocket, Server-Sent Events, streaming responses, custom request headers, + long-lived connections, or third-party libraries that call `fetch`/open + sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: + `class === 2` iff `connect.granted === true` at `class.assigned` send time. + See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full + scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward + from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn + once per build if you leave it. (Required when REMOVE-STRICTCSP lands; + optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move + their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the + plugin's new `connect: string[]` option. See + `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your + host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` + before reading `window.napplet.class` / `window.napplet.connect` — both + gracefully degrade (`undefined` / `{granted: false, origins: []}` + respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds + `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — + shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt + primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext + access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict + baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet + main branch; it has been renumbered to v0.30.0 during a post-merge version + split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority + milestone (feat/strict-model, shipped 2026-04-21). This changeset describes + shipped behavior; the underlying commits already exist in main (Phases 135-138 + of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, + `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope + triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, + `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, + `impersonation`, `unsupported-encryption`, `policy-denied`), + `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), + `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` + method type. Zero new runtime symbols in the types-only entry point — pure + type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding + (request-correlation by id, reusing existing `pendingRequests` + infrastructure), the `identity.decrypt.result` / `.error` handler branches + (added to `handleIdentityMessage` with discriminated-union exhaustiveness + via `assertNever`), and the bare-name `identityDecrypt(event)` helper. + Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so + `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central + shim — no new install function; the addition is two surgical edits on + `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central + `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new + identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports + — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full + conformance table (4 shell MUSTs: class-gating, outer-sig-verify, + impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, + outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy + floor), 8-code error vocabulary, and Security Considerations subsection. + Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST + `(dTag, aggregateHash)` violation-correlation row + Security Considerations + subsection distinguishing observability from enforcement. Bundled into the + same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a + `### NIP-07 Extension Injection Residual` subsection documenting the + `all_frames: true` content-script injection vector, nonce-based `script-src` + legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical + lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and + `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as + the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero + occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably + blocks legacy `<script>`-tag content-script injection AND fires + `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and + `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member + triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. + Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 + browser extension injects `window.nostr` into the iframe via a content + script (architecturally unavoidable per WebExtension spec), shells MUST NOT + _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell + is free to use its own NIP-07 extension internally to fulfill + signing/encryption duties; what is forbidden is exposing `window.nostr` to + the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) + cannot decrypt — they receive `class-forbidden` at the shell boundary + because plaintext could exfiltrate to approved origins with zero shell + visibility. The class invariant ties plaintext exposure to zero-egress + posture. ### Patch Changes @@ -639,7 +890,11 @@ ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 + tarballs on npm contained unresolved `workspace:*` specs in dependencies, + breaking installs. This patch bump exists solely to produce + correctly-assembled tarballs via `pnpm publish -r` (which rewrites + `workspace:*` → concrete versions at pack time). - Updated dependencies - @napplet/core@0.2.1 - @napplet/nap-config@0.2.1 diff --git a/packages/sdk/README.md b/packages/sdk/README.md index b55d020e..76ccc175 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -506,3 +506,24 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); ``` The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). + +### Controller snapshots + +This non-normative guidance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). When the runtime exposes `window.napplet.gamepad`, the SDK provides cached `getGamepads()`, read-only `available` and `focused`, and `onChange(handler)` with an independently closable listener. Keep keyboard or pointer controls available when the domain or native controllers are unavailable. Ignore gameplay edges while unfocused, and reset the button baseline on regained focus. Preserve null slots and route controllers by their `index`. + +```ts +import { gamepad } from '@napplet/sdk/gamepad'; + +if (window.napplet?.gamepad) { + const subscription = gamepad.onChange(state => { + if (!state.available || !state.focused) return; + for (const pad of state.pads) { + if (pad) console.log(pad.index, pad.axes); + } + }); + // Close this listener when its owning view is disposed. + subscription.close(); +} +``` + +Declare `gamepad` as optional (`O`) when other controls fulfill the core task. Use a hard requirement (`R`) only when the core task needs controller input. The runtime owns controller access and focus isolation; app code does not call native `navigator.getGamepads()` or open raw HID devices. Haptics and remapping are outside this proposal. diff --git a/packages/sdk/jsr.json b/packages/sdk/jsr.json index 1826e428..f425833c 100644 --- a/packages/sdk/jsr.json +++ b/packages/sdk/jsr.json @@ -1,14 +1,14 @@ { "name": "@napplet/sdk", - "version": "0.29.0", + "version": "0.30.0", "license": "MIT", "exports": { ".": "./src/index.ts", "./gamepad": "./src/gamepad.ts" }, "imports": { - "@napplet/core": "jsr:@napplet/core@^0.33.0", - "@napplet/nap": "jsr:@napplet/nap@^0.33.0" + "@napplet/core": "jsr:@napplet/core@^0.34.0", + "@napplet/nap": "jsr:@napplet/nap@^0.34.0" }, "publish": { "include": [ diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 668731e3..020744c3 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/sdk", - "version": "0.29.0", + "version": "0.30.0", "description": "Typed named exports wrapping window.napplet for bundler-consuming napplet developers", "type": "module", "main": "./dist/index.js", diff --git a/packages/shim/CHANGELOG.md b/packages/shim/CHANGELOG.md index 6fd9c56c..70602f49 100644 --- a/packages/shim/CHANGELOG.md +++ b/packages/shim/CHANGELOG.md @@ -1,12 +1,40 @@ # @napplet/shim +## 0.32.0 + +### Minor Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + +### Patch Changes + +- Updated dependencies + - @napplet/core@0.34.0 + - @napplet/nap@0.34.0 + ## 0.31.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative + convention URIs and naddr recommendations, expose acceptance results and + buffered onDelivery callbacks, and discover opaque catalog identifiers with + parameter contracts. Restore the optional NAP-SHELL environment API + independently of other domain calls. Align INC provenance with authenticated + endpoint identifiers and deliver theme/identity updates only for exposed + domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, + options) and open(uri, options), from handled/windowId to acceptance, and from + candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK + helpers remain exported with the URI signatures. CLI helper extraction + preserves existing exports and behavior. ### Patch Changes @@ -18,7 +46,10 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries + optional advisory Blossom `servers`, `bytesMany` accepts per-resource request + objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits + the amended envelopes. ### Patch Changes @@ -37,7 +68,8 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol + behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -46,13 +78,25 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, + `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, + shim and SDK helpers, injected `window.napplet.fs` methods, and conformance + coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -67,11 +111,22 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ### Patch Changes @@ -83,12 +138,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ### Patch Changes @@ -108,13 +163,15 @@ ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, + conformance, shim, and Vite plugin packages. ## 0.26.6 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API + types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -122,10 +179,10 @@ ### Patch Changes -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, - helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource - scheme. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` + wire types, helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` + resource scheme. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -140,7 +197,9 @@ ### Patch Changes -- 8953eed: Add a host-injectable NIP-5D runtime prelude surface with an explicit NAP domain allowlist and a bundled browser IIFE artifact for shell `srcdoc` injection. +- 8953eed: Add a host-injectable NIP-5D runtime prelude surface with an explicit + NAP domain allowlist and a bundled browser IIFE artifact for shell `srcdoc` + injection. ## 0.26.3 @@ -169,7 +228,10 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / + `count.query.result` envelope types, `window.napplet.count.query(...)`, + `@napplet/nap/count` subpaths, SDK exports, and conformance + validator/reference-shell coverage. ### Patch Changes @@ -181,9 +243,15 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime + API, typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, + typed NAP helpers, SDK wrappers, injected global, and conformance + envelope/reference shell. ### Patch Changes @@ -204,9 +272,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. ### Patch Changes @@ -218,8 +286,9 @@ ### Patch Changes -- 0c45563: Guard relay query results so malformed host responses without an `events` array - resolve to `[]` instead of leaking `undefined` through the typed query contract. +- 0c45563: Guard relay query results so malformed host responses without an + `events` array resolve to `[]` instead of leaking `undefined` through the + typed query contract. - Updated dependencies [0c45563] - @napplet/nap@0.22.1 @@ -227,9 +296,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, - `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and - conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed + payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK + wrappers, and conformance validator coverage. ### Patch Changes @@ -241,8 +310,9 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including - wire types, shim and SDK helpers, core global types, and conformance coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, + including wire types, shim and SDK helpers, core global types, and conformance + coverage. ### Patch Changes @@ -254,7 +324,9 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` + subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -269,9 +341,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns - identity, consent, event construction, signing, publishing, relay access, and - NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell + owns identity, consent, event construction, signing, publishing, relay access, + and NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -292,7 +364,9 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` + subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and + reference-shell responses. ### Patch Changes @@ -306,13 +380,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, - `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core + types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -327,9 +401,10 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from - `@napplet/sdk`, and teaches conformance validation/reference-shell handling - about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers + from `@napplet/sdk`, and teaches conformance validation/reference-shell + handling about `serial.open`, `serial.write`, `serial.close`, and + `serial.event`. ### Patch Changes @@ -341,22 +416,26 @@ ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. - - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON - envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue - `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so - `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; - the envelope simply never crossed (napplet/web#67). - - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, - `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` - types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, - snapshots the envelope (stripping reactive proxies while preserving binary, - `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a - loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this - boundary instead of calling `window.parent.postMessage(msg, '*')` directly. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no + longer fail silently. + + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a + JSON envelope through `postMessage`. Framework reactive values — Svelte 5 + `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't + structured-cloneable, so `postMessage` threw a `DataCloneError` that got + silently swallowed in async paths; the envelope simply never crossed + (napplet/web#67). + + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, + `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / + `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only + on a `DataCloneError`, snapshots the envelope (stripping reactive proxies + while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — + warning once. `'strict'` throws a loud, actionable, synchronous error + instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through + this boundary instead of calling `window.parent.postMessage(msg, '*')` + directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -371,11 +450,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a - deferred NAP's surface must not outlive the NAP). The opaque `class` integer was - re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, - but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is - now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md + rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` + integer was re-homed into NAP-SHELL's `shell.init` environment during the + NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The + `shell.init` wire shape is now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -385,7 +464,8 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option + (`@napplet/conformance`). ### Patch Changes @@ -397,11 +477,20 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, + [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an + optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. + Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface + (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets + `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s + `storage.instance.*` and as `storageInstance*` helpers in + `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type + and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** + `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -413,12 +502,31 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -430,18 +538,19 @@ ### Minor Changes -- 2119604: Add a runtime guard that fails loudly when a napplet is loaded outside a napplet - runtime. Importing `@napplet/shim` now detects when no runtime is present — the - napplet is a top-level page, or is embedded in a frame that never answers the - `shell.ready` → `shell.init` handshake — and renders an explanatory modal instead - of silently failing. This makes the common confusing case legible: a napplet - opened directly from a NIP-5A nsite gateway can't work, because there's no shell - to proxy Nostr access over postMessage (NIP-5D). +- 2119604: Add a runtime guard that fails loudly when a napplet is loaded + outside a napplet runtime. Importing `@napplet/shim` now detects when no + runtime is present — the napplet is a top-level page, or is embedded in a + frame that never answers the `shell.ready` → `shell.init` handshake — and + renders an explanatory modal instead of silently failing. This makes the + common confusing case legible: a napplet opened directly from a NIP-5A nsite + gateway can't work, because there's no shell to proxy Nostr access over + postMessage (NIP-5D). The modal explains why nothing loaded and links to a runtime directory - (`napplet.run`), the reference runtime (`github.com/kehto/web`), and the NIP-5D - specification. It is style-isolated in a shadow root so the napplet's own CSS - can't distort it. + (`napplet.run`), the reference runtime (`github.com/kehto/web`), and the + NIP-5D specification. It is style-isolated in a shadow root so the napplet's + own CSS can't distort it. Local standalone development can opt out via `window.__NAPPLET_ALLOW_STANDALONE__ = true` (set before the shim loads) or a @@ -451,20 +560,22 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the + shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet - had no way to fetch the theme through `window.napplet`. It now mirrors the - read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a + napplet had no way to fetch the theme through `window.napplet`. It now mirrors + the read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` - updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus - `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed + `theme.changed` updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, + plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in + `@napplet/core`. ### Patch Changes @@ -476,21 +587,23 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another - napplet by its role (archetype) through `window.napplet.intent` (`invoke`, - `open`, `available`, `handlers`, `onChanged`) without addressing it directly. - The shell resolves the archetype to an installed napplet (honoring the user's - default-handler preference), creates or focuses its window, and delivers the - payload using the named NAP-N protocol. Routing (`archetype`) and payload format - (`protocol`) are orthogonal; the shell owns resolution, default handling, window - lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke + another napplet by its role (archetype) through `window.napplet.intent` + (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it + directly. The shell resolves the archetype to an installed napplet (honoring + the user's default-handler preference), creates or focuses its window, and + delivers the payload using the named NAP-N protocol. Routing (`archetype`) and + payload format (`protocol`) are orthogonal; the shell owns resolution, default + handling, window lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` - surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with - request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, + `intent` surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) + with request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` + envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type + re-exports. ### Patch Changes @@ -502,19 +615,21 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes - through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the - runtime selects the storage server, signs the rail authorization (NIP-98 for - NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable - URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server - credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now + upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) + while the runtime selects the storage server, signs the rail authorization + (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and + returns a stable URL plus NIP-94 integrity metadata. Napplets never receive + signing keys, server credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` - surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with - request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, + `upload` surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) + with request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` + envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type + re-exports. ### Patch Changes @@ -533,11 +648,12 @@ ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM - servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, - `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while - the runtime owns all ContextVM transport — relay routing, signing, encryption, - JSON-RPC correlation, initialization, policy, and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover + ContextVM servers and run MCP operations (`tools/list`, `tools/call`, + `resources/list`, `resources/read`) over Nostr through the shell via + `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay + routing, signing, encryption, JSON-RPC correlation, initialization, policy, + and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -545,17 +661,19 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, - publish, and resolve relay plans through `window.napplet.outbox` while the - runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature - validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, + subscribe, publish, and resolve relay plans through `window.napplet.outbox` + while the runtime owns relay discovery, NIP-65 routing, fallback, + deduplication, signature validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` - surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with - request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, + `outbox` surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) + with request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` + envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type + re-exports. ### Patch Changes @@ -570,7 +688,11 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and + TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and + `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over + the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for + downstream migration. ### Patch Changes @@ -582,7 +704,9 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. +- Rename the public package and protocol surface to NAP terminology, including + `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` + identifiers. ### Patch Changes @@ -606,7 +730,10 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / + `identity.onChanged`, make `getPublicKey()` represent signed-out state with + `""`, remove the old identity decrypt surface and rumor types, and normalize + shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -618,7 +745,8 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene + changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 - @napplet/nap@0.3.1 @@ -627,64 +755,189 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP + emission from the vite-plugin (build time) to the host shell (runtime) for + every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative + abstract security posture via `class.assigned` wire) and NAP-CONNECT + (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The + option is retained as an `@deprecated` accept-but-warn shim for one release + cycle — the plugin emits one `console.warn` per build per config-resolved if + a `strictCsp` value is present, and the option is ignored. Production builds + no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is + now the sole authority on runtime CSP for every napplet, per + `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a + future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for + v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` + scans `dist/index.html` and throws a diagnostic when any `<script>` element + lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so + inline scripts would be blocked at runtime anyway; failing at build time + surfaces the problem before deployment. The allow-list exempts + `application/json`, `application/ld+json`, `importmap`, and + `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on + `NamespacedCapability`. The template-literal type still accepts it so + existing code type-checks; capability advertisement is superseded by + `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as + `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required + origins. Each origin is validated via the shared `normalizeConnectOrigin()` + validator from `@napplet/nap/connect`, emitted as a signed + `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via + a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → + UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). + Dev-mode-only `<meta name="napplet-connect-requires">` is injected for + shell-less `vite serve` previews; the shell-authoritative + `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 + entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` + (mirrors `resource` block) and `class?: number` (optional — undefined until + `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `NappletConnect` interface, pure + `normalizeConnectOrigin(origin)` validator (shared source-of-truth for + build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests + pass), and `installConnectShim()` which reads + `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type + (`{ type: 'class.assigned'; id: string; class: number }`), + `handleClassMessage` dispatcher handler, and `installClassShim()` which + mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** + defaults to `{granted: false, origins: []}` (never `undefined`); + **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). + Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the + `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + + the `class` types namespace — parallel to the existing `resource` + re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte + fetches — it works in every class posture. Reach for NAP-CONNECT only when + the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, + WebSocket, Server-Sent Events, streaming responses, custom request headers, + long-lived connections, or third-party libraries that call `fetch`/open + sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: + `class === 2` iff `connect.granted === true` at `class.assigned` send time. + See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full + scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward + from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn + once per build if you leave it. (Required when REMOVE-STRICTCSP lands; + optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move + their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the + plugin's new `connect: string[]` option. See + `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your + host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` + before reading `window.napplet.class` / `window.napplet.connect` — both + gracefully degrade (`undefined` / `{granted: false, origins: []}` + respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds + `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — + shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt + primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext + access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict + baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet + main branch; it has been renumbered to v0.30.0 during a post-merge version + split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority + milestone (feat/strict-model, shipped 2026-04-21). This changeset describes + shipped behavior; the underlying commits already exist in main (Phases 135-138 + of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, + `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope + triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, + `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, + `impersonation`, `unsupported-encryption`, `policy-denied`), + `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), + `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` + method type. Zero new runtime symbols in the types-only entry point — pure + type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding + (request-correlation by id, reusing existing `pendingRequests` + infrastructure), the `identity.decrypt.result` / `.error` handler branches + (added to `handleIdentityMessage` with discriminated-union exhaustiveness + via `assertNever`), and the bare-name `identityDecrypt(event)` helper. + Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so + `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central + shim — no new install function; the addition is two surgical edits on + `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central + `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new + identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports + — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full + conformance table (4 shell MUSTs: class-gating, outer-sig-verify, + impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, + outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy + floor), 8-code error vocabulary, and Security Considerations subsection. + Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST + `(dTag, aggregateHash)` violation-correlation row + Security Considerations + subsection distinguishing observability from enforcement. Bundled into the + same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a + `### NIP-07 Extension Injection Residual` subsection documenting the + `all_frames: true` content-script injection vector, nonce-based `script-src` + legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical + lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and + `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as + the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero + occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably + blocks legacy `<script>`-tag content-script injection AND fires + `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and + `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member + triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. + Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 + browser extension injects `window.nostr` into the iframe via a content + script (architecturally unavoidable per WebExtension spec), shells MUST NOT + _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell + is free to use its own NIP-07 extension internally to fulfill + signing/encryption duties; what is forbidden is exposing `window.nostr` to + the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) + cannot decrypt — they receive `class-forbidden` at the shell boundary + because plaintext could exfiltrate to approved origins with zero shell + visibility. The class invariant ties plaintext exposure to zero-egress + posture. ### Patch Changes @@ -698,7 +951,11 @@ ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 + tarballs on npm contained unresolved `workspace:*` specs in dependencies, + breaking installs. This patch bump exists solely to produce + correctly-assembled tarballs via `pnpm publish -r` (which rewrites + `workspace:*` → concrete versions at pack time). - Updated dependencies - @napplet/core@0.2.1 - @napplet/nap-config@0.2.1 diff --git a/packages/shim/README.md b/packages/shim/README.md index 7a33f562..21ec9ff1 100644 --- a/packages/shim/README.md +++ b/packages/shim/README.md @@ -664,3 +664,7 @@ await intent.invoke('napplet:profile/open?pubkey=abc123'); ``` The runtime binding retains deliveries until a listener registers. Discovery candidates expose opaque catalog `id` values and `contracts` with parameter names. INC peers instead use identifiers for running authenticated endpoints. A NAP-INTENT URI may append a bare `#naddr1…` recommendation; the binding sends its coordinate and relay hints as `handlerHint`, outside convention identity and payload. An applicable user default takes precedence. INC URI operations reject fragments. See the living [NAP-INTENT](https://github.com/napplet/naps/blob/master/naps/NAP-INTENT.md) and [web projection](https://github.com/napplet/naps/blob/master/projections/web.md). + +### GAMEPAD integration + +The selected `gamepad` runtime binding installs the cached API, subscribes without SHELL readiness, validates parent state, and unsubscribes during cleanup. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). The native browser Gamepad API is left untouched pending clarification of the proposal’s optional replacement and NIP-5D’s namespace-only injection rule. diff --git a/packages/shim/jsr.json b/packages/shim/jsr.json index aa01ae88..d29855ec 100644 --- a/packages/shim/jsr.json +++ b/packages/shim/jsr.json @@ -1,14 +1,14 @@ { "name": "@napplet/shim", - "version": "0.31.0", + "version": "0.32.0", "license": "MIT", "exports": { ".": "./src/index.ts", "./prelude": "./src/prelude.ts" }, "imports": { - "@napplet/core": "jsr:@napplet/core@^0.33.0", - "@napplet/nap": "jsr:@napplet/nap@^0.33.0" + "@napplet/core": "jsr:@napplet/core@^0.34.0", + "@napplet/nap": "jsr:@napplet/nap@^0.34.0" }, "publish": { "include": [ diff --git a/packages/shim/package.json b/packages/shim/package.json index 7a0f1603..eded237a 100644 --- a/packages/shim/package.json +++ b/packages/shim/package.json @@ -1,7 +1,7 @@ { "name": "@napplet/shim", - "version": "0.31.0", - "description": "Napplet SDK — subscribe, publish, query, emit, on, nappStorage for Nostr-native iframe applications", + "version": "0.32.0", + "description": "Napplet SDK \u2014 subscribe, publish, query, emit, on, nappStorage for Nostr-native iframe applications", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -34,7 +34,8 @@ "devDependencies": { "tsup": "^8.5.0", "typescript": "^5.9.3", - "vitest": "^4.1.11" + "vitest": "^4.1.11", + "@napplet/sdk": "workspace:*" }, "scripts": { "build": "tsup", diff --git a/packages/shim/src/gamepad.test.ts b/packages/shim/src/gamepad.test.ts index 42db684c..b3a6ef4c 100644 --- a/packages/shim/src/gamepad.test.ts +++ b/packages/shim/src/gamepad.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { installNappletRuntimePrelude } from './prelude.js'; -import { gamepad as sdkGamepad } from '../../sdk/src/gamepad.js'; +import { gamepad as sdkGamepad } from '@napplet/sdk/gamepad'; function deliver(data: unknown, source: MessageEventSource | null = window.parent): void { window.dispatchEvent(new MessageEvent('message', { data, source })); diff --git a/packages/vite-plugin/CHANGELOG.md b/packages/vite-plugin/CHANGELOG.md index bdc8bf7d..221badb0 100644 --- a/packages/vite-plugin/CHANGELOG.md +++ b/packages/vite-plugin/CHANGELOG.md @@ -1,44 +1,90 @@ # @napplet/vite-plugin +## 0.16.1 + +### Patch Changes + +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, + envelope validation, conformance injection, and optional manifest-domain + inference. Update the standalone CLI's private conformance/Paja runners to + GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; + conformance CLI 0.3.4 is included in the same package set. Native Gamepad API + replacement remains deferred pending clarification of the proposal and NIP-5D + namespace-only injection restriction. + ## 0.16.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before + computing its artifact hash. Resolve plugin options, author declarations and + inferred capabilities consistently; add icon byte input while retaining + hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from + standalone HTML without a sidecar. Upload the same decoded icon bytes, respect + explicit deployment overrides and event-kind restrictions, and preserve + legacy-format behavior. Embedded metadata changes now change the built + artifact hash. - Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. + Validate configured and recovered Blossom upload destinations before signing + manifests or uploading, with clear errors for missing values and non-origin + URLs. ## 0.15.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 + and 35129. Writers emit one direct artifact `x` hash, description content, + required/optional `R`/`O` domains and independent `z`/`i` advertisements. + Readers verify current events and raw artifact bytes; legacy events require + migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. + CLI deployment defaults to current output and offers temporary explicit legacy + serialization. Add offline signed-event migration previews without altering + source events or publishing. Vite builds default to a self-contained artifact; + compatibility helper and sidecar names remain available. See the event + migration guide for package cutoffs, immutable pointers and shell ACL/storage + implications. ### Patch Changes -- Warn when explicit required domains are outside the built-in NAP list while preserving their `R` declarations. Source inference remains limited to known domains (#223). -- Builds without `dist/index.html` fail with a clear `[nip5a-manifest]` error instead of a raw ENOENT, and `artifactMode: 'external-assets'` builds warn that the manifest `x` hash covers only `index.html` and list the remaining local assets. +- Warn when explicit required domains are outside the built-in NAP list while + preserving their `R` declarations. Source inference remains limited to known + domains (#223). +- Builds without `dist/index.html` fail with a clear `[nip5a-manifest]` error + instead of a raw ENOENT, and `artifactMode: 'external-assets'` builds warn + that the manifest `x` hash covers only `index.html` and list the remaining + local assets. ## 0.14.1 ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload + conventions, and require structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing + key, and preserve `count` requirements during CLI deploys. ## 0.14.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, + `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, + `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` + event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and + `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` + and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, + `FsLimits.maxWriteBytes`, byte range options, and read/write result counts + refer to decoded bytes. ## 0.13.0 @@ -46,47 +92,69 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, + runtime-attested sender, and optional payload. The runtime no longer + fabricates a Nostr event. INC also exposes the symmetric channel API through + `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; + channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, + supports `behavior.newWindow`, and returns the canonical structured result + with required `ok`, `archetype`, `action`, and `handled` fields. Remove the + unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only + result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical + `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` + option and trailing `kind:<number>` fields. ## 0.12.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the + public types, bindings, runtime injection, manifest tooling, CLI, and + reference shell: queryless exact identities with URI-to-text-payload + transposition at `emit` and `invoke`/`open`, runtime-attested sender, + immediate acceptance followed by source-independent no-ID target delivery, and + optional same-tag event-kind discovery metadata. ## 0.11.3 ### Patch Changes -- d6291de: Point generated projects to the current skills, remove undocumented build-time config guidance, and keep hard `count` requirements from being dropped by the Vite plugin. +- d6291de: Point generated projects to the current skills, remove undocumented + build-time config guidance, and keep hard `count` requirements from being + dropped by the Vite plugin. ## 0.11.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, + conformance, shim, and Vite plugin packages. ## 0.11.1 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API + types. ## 0.11.0 ### Minor Changes -- fb434c3: Add `title` and `description` plugin options that inject/override the built HTML `<title>` element and `<meta name="description">` element. Both are plain HTML (not `napplet-*` protocol meta tags): when set they override any existing tag (inserting one after `<head>` if absent) and when omitted the author's HTML is left untouched. Injected values are HTML-escaped for their context. The napplet CLI reads these back out of the built `index.html` to emit the NIP-5A `title` / `description` manifest tags at deploy time. -- fb434c3: Add opt-in NAP requirement inference from static source imports and direct `window.napplet.<domain>` usage. +- fb434c3: Add `title` and `description` plugin options that inject/override the + built HTML `<title>` element and `<meta name="description">` element. Both are + plain HTML (not `napplet-*` protocol meta tags): when set they override any + existing tag (inserting one after `<head>` if absent) and when omitted the + author's HTML is left untouched. Injected values are HTML-escaped for their + context. The napplet CLI reads these back out of the built `index.html` to + emit the NIP-5A `title` / `description` manifest tags at deploy time. +- fb434c3: Add opt-in NAP requirement inference from static source imports and + direct `window.napplet.<domain>` usage. ## 0.10.1 @@ -99,9 +167,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and validates - only NAP domain envelopes. Skills and package guidance now teach domain-property - presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and + validates only NAP domain envelopes. Skills and package guidance now teach + domain-property presence instead of the retired shell supports API. ## 0.10.0 @@ -109,113 +177,244 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol with - optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol + with optional per-protocol `kind:<number>` constraints. ## 0.9.0 ### Minor Changes -- 6c99508: Emit NAAT archetype manifest tags (napplet/naps `ARCHETYPES.md`). Resolves #58. +- 6c99508: Emit NAAT archetype manifest tags (napplet/naps `ARCHETYPES.md`). + Resolves #58. - - New optional `archetypes` plugin option: `Array<string | { slug: string; naps?: string[] }>`. Each entry emits one `["archetype", slug, ...naps]` tag on the kind 35129 manifest, where the string shorthand `"feed"` is equivalent to `{ slug: "feed" }`. A napplet may declare several roles; declaring none stays fully valid. - - Like the `config` tag, archetype tags are excluded from the aggregate `x` hash (NIP-5D §Identity: the aggregate is recomputed from `path` tags alone), so declaring archetypes never changes a napplet's content address. + - New optional `archetypes` plugin option: + `Array<string | { slug: string; naps?: string[] }>`. Each entry emits one + `["archetype", slug, ...naps]` tag on the kind 35129 manifest, where the + string shorthand `"feed"` is equivalent to `{ slug: "feed" }`. A napplet may + declare several roles; declaring none stays fully valid. + - Like the `config` tag, archetype tags are excluded from the aggregate `x` + hash (NIP-5D §Identity: the aggregate is recomputed from `path` tags alone), + so declaring archetypes never changes a napplet's content address. ## 0.8.1 ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). - - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets + ([napplet/web#53](https://github.com/napplet/web/issues/53)). + + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via + `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an + opaque origin). There is no served origin from which to fetch an external + `<script src>`, so a napplet's executable JS **must** be inline. The toolchain + was enforcing the opposite under an invented "shell-as-CSP-authority / + `script-src 'self'`" model that NIP-5D never defines and that was justified by + NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its + unconditional `closeBundle` call. The build no longer aborts on inline + scripts; `artifactMode: 'single-file'` still folds local script/style assets + into `index.html` and now preserves any pre-existing inline scripts + verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): + removed the `manifest/no-inline-scripts` check, the `inline-script` error + code, and the `findInlineScripts` export. A conformant napplet that carries + inline JS no longer fails conformance. ## 0.8.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track + ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so + the `class` and `connect` domains are removed from the active surface: + `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / + `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` + subpaths, the shim installers + sdk re-exports, the conformance + validators/checks (incl. `manifest/connect-origins`), and the + `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and + `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → + `shell.init` bootstrap handshake is now a published NAP. `shell.init` + carries `{ capabilities: { domains, protocols }, services, class }`. + `window.napplet.shell` gains `services`, `class` (opaque integer), + `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside + `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the + NAP-SHELL types. The conformance engine recognizes + `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the + previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, + the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the + `@napplet/vite-plugin` `connect` option; migrates the `shell.init` + capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ## 0.7.0 ### Minor Changes -- 06cfecf: Stop emitting the `napplet-aggregate-hash` meta tag into `index.html`. A file - cannot contain a hash that covers itself (the hash includes `index.html`), so the - tag was either empty or — when a signing key was set — written back _after_ the - hash was computed, leaving the advertised hash permanently inconsistent with the - file. It is also not a NIP-5D/5A artifact. The aggregate hash now lives only in - the external `.nip5a-manifest.json` and the signed kind-35129 event, where the - shell/relay reads it; removing the post-hash rewrite makes that manifest - internally consistent. +- 06cfecf: Stop emitting the `napplet-aggregate-hash` meta tag into + `index.html`. A file cannot contain a hash that covers itself (the hash + includes `index.html`), so the tag was either empty or — when a signing key + was set — written back _after_ the hash was computed, leaving the advertised + hash permanently inconsistent with the file. It is also not a NIP-5D/5A + artifact. The aggregate hash now lives only in the external + `.nip5a-manifest.json` and the signed kind-35129 event, where the shell/relay + reads it; removing the post-hash rewrite makes that manifest internally + consistent. ## 0.6.0 ### Minor Changes -- Align the generated napplet manifest with NIP-5D and the NIP-5A tag schema it adopts. - - - Emit NIP-5D's own kind `35129` (named napplet) instead of NIP-5A's nsite kind `35128`. New `NAPPLET_KIND_NAMED` / `NAPPLET_KIND_ROOT` / `NAPPLET_KIND_SNAPSHOT` exports replace the removed `SYNTHETIC_XTAG_PATHS` export. - - Emit a NIP-5A-conformant manifest: one `["path", "/abs/path", "<sha256>"]` tag per file (absolute paths) plus a single aggregate `["x", "<aggregateHash>", "aggregate"]` tag, replacing the previous per-file `["x", hash, relpath]` projection. - - Compute `aggregateHash` from the `path` tags alone (NIP-5D §Identity), so a runtime can recompute and verify it. `config` / `connect` capability tags are still emitted but are no longer folded into the aggregate; grant invalidation on a capability change is keyed on those tags at the shell layer. - - BREAKING: manifest output changes (kind, tag shape, aggregate inputs) and the `SYNTHETIC_XTAG_PATHS` export is removed. +- Align the generated napplet manifest with NIP-5D and the NIP-5A tag schema it + adopts. + + - Emit NIP-5D's own kind `35129` (named napplet) instead of NIP-5A's nsite + kind `35128`. New `NAPPLET_KIND_NAMED` / `NAPPLET_KIND_ROOT` / + `NAPPLET_KIND_SNAPSHOT` exports replace the removed `SYNTHETIC_XTAG_PATHS` + export. + - Emit a NIP-5A-conformant manifest: one `["path", "/abs/path", "<sha256>"]` + tag per file (absolute paths) plus a single aggregate + `["x", "<aggregateHash>", "aggregate"]` tag, replacing the previous per-file + `["x", hash, relpath]` projection. + - Compute `aggregateHash` from the `path` tags alone (NIP-5D §Identity), so a + runtime can recompute and verify it. `config` / `connect` capability tags + are still emitted but are no longer folded into the aggregate; grant + invalidation on a capability change is keyed on those tags at the shell + layer. + + BREAKING: manifest output changes (kind, tag shape, aggregate inputs) and the + `SYNTHETIC_XTAG_PATHS` export is removed. ## 0.5.0 ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. +- Rename the public package and protocol surface to NAP terminology, including + `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` + identifiers. ## 0.4.0 ### Minor Changes -- 73d5ffc: Add an explicit `artifactMode: 'single-file'` build contract for NIP-5A gateway-portable napplet artifacts. In single-file mode the plugin asks Vite/Rollup for a single-entry artifact shape, inlines local Vite JS/CSS assets into `index.html` before manifest/hash generation, fails if local external assets remain, accepts those build-produced inline module scripts intentionally, and keeps config/connect synthetic hash inputs participating in `aggregateHash`. +- 73d5ffc: Add an explicit `artifactMode: 'single-file'` build contract for + NIP-5A gateway-portable napplet artifacts. In single-file mode the plugin asks + Vite/Rollup for a single-entry artifact shape, inlines local Vite JS/CSS + assets into `index.html` before manifest/hash generation, fails if local + external assets remain, accepts those build-produced inline module scripts + intentionally, and keeps config/connect synthetic hash inputs participating in + `aggregateHash`. ## 0.3.0 ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP + emission from the vite-plugin (build time) to the host shell (runtime) for + every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative + abstract security posture via `class.assigned` wire) and NAP-CONNECT + (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The + option is retained as an `@deprecated` accept-but-warn shim for one release + cycle — the plugin emits one `console.warn` per build per config-resolved if + a `strictCsp` value is present, and the option is ignored. Production builds + no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is + now the sole authority on runtime CSP for every napplet, per + `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a + future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for + v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` + scans `dist/index.html` and throws a diagnostic when any `<script>` element + lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so + inline scripts would be blocked at runtime anyway; failing at build time + surfaces the problem before deployment. The allow-list exempts + `application/json`, `application/ld+json`, `importmap`, and + `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on + `NamespacedCapability`. The template-literal type still accepts it so + existing code type-checks; capability advertisement is superseded by + `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as + `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required + origins. Each origin is validated via the shared `normalizeConnectOrigin()` + validator from `@napplet/nap/connect`, emitted as a signed + `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via + a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → + UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). + Dev-mode-only `<meta name="napplet-connect-requires">` is injected for + shell-less `vite serve` previews; the shell-authoritative + `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 + entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` + (mirrors `resource` block) and `class?: number` (optional — undefined until + `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `NappletConnect` interface, pure + `normalizeConnectOrigin(origin)` validator (shared source-of-truth for + build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests + pass), and `installConnectShim()` which reads + `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / + `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type + (`{ type: 'class.assigned'; id: string; class: number }`), + `handleClassMessage` dispatcher handler, and `installClassShim()` which + mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** + defaults to `{granted: false, origins: []}` (never `undefined`); + **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). + Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the + `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + + the `class` types namespace — parallel to the existing `resource` + re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte + fetches — it works in every class posture. Reach for NAP-CONNECT only when + the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, + WebSocket, Server-Sent Events, streaming responses, custom request headers, + long-lived connections, or third-party libraries that call `fetch`/open + sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: + `class === 2` iff `connect.granted === true` at `class.assigned` send time. + See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full + scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward + from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn + once per build if you leave it. (Required when REMOVE-STRICTCSP lands; + optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move + their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the + plugin's new `connect: string[]` option. See + `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your + host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` + before reading `window.napplet.class` / `window.napplet.connect` — both + gracefully degrade (`undefined` / `{granted: false, origins: []}` + respectively) on non-implementing shells. ## 0.2.1 ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 + tarballs on npm contained unresolved `workspace:*` specs in dependencies, + breaking installs. This patch bump exists solely to produce + correctly-assembled tarballs via `pnpm publish -r` (which rewrites + `workspace:*` → concrete versions at pack time). diff --git a/packages/vite-plugin/README.md b/packages/vite-plugin/README.md index d9be06f7..631269bb 100644 --- a/packages/vite-plugin/README.md +++ b/packages/vite-plugin/README.md @@ -100,3 +100,7 @@ nip5aManifest({ The hash covers decoded image bytes, not the data URL. If `sha256` is supplied alongside `data`, it is checked against those bytes. An existing supported head icon data URL can supply the bytes instead. Hash-only input retains its manifest tag; when no matching head image exists the plugin warns and omits the HTML icon, because a hash cannot recover an image. Unsupported, remote, empty, or malformed author icon URLs supply no embedded icon. Consumers still verify image decoding before display as specified by NIP-5D. Metadata and JS/CSS inlining finish before the plugin hashes `index.html`. Changing an embedded declaration or icon changes the artifact hash. The NAP-CONFIG schema remains in the sidecar: NIP-5D defines no HTML mapping for that extension. `napplet deploy` can recover the specified publishing fields and icon bytes from the built HTML without the sidecar. + +### GAMEPAD integration + +Requirement inference recognizes `@napplet/nap/gamepad`, `@napplet/sdk/gamepad`, and direct GAMEPAD domain reads. Declare `optional: ["gamepad"]` when keyboard or pointer controls fulfill the core task. See the proposed [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). diff --git a/packages/vite-plugin/jsr.json b/packages/vite-plugin/jsr.json index e0b09bb4..7f253939 100644 --- a/packages/vite-plugin/jsr.json +++ b/packages/vite-plugin/jsr.json @@ -1,11 +1,11 @@ { "name": "@napplet/vite-plugin", - "version": "0.16.0", + "version": "0.16.1", "license": "MIT", "exports": "./src/index.ts", "imports": { "parse5": "npm:parse5@^7.3.0", - "@napplet/nap": "jsr:@napplet/nap@^0.33.0", + "@napplet/nap": "jsr:@napplet/nap@^0.34.0", "nostr-tools": "npm:nostr-tools@^2.23.3", "vite": "npm:vite@^6.3.0" }, diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index 3cc16b07..abba22fc 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/vite-plugin", - "version": "0.16.0", + "version": "0.16.1", "description": "Vite plugin for napplet development — generates signed NIP-5A manifests at build time for local testing", "type": "module", "main": "./dist/index.js", diff --git a/packages/vite-plugin/src/index.test.ts b/packages/vite-plugin/src/index.test.ts index 4b9a8664..551e5a4f 100644 --- a/packages/vite-plugin/src/index.test.ts +++ b/packages/vite-plugin/src/index.test.ts @@ -532,6 +532,21 @@ describe('nip5aManifest artifact modes', () => { ]); }); + it('recognizes GAMEPAD imports and retains an explicitly optional controller domain', async () => { + const fixture = makeFixture(); + fs.writeFileSync(path.join(fixture.dist, 'index.html'), '<!doctype html>'); + const result = await runCloseBundle( + { nappletType: 'controller-fallback', optional: ['gamepad'], requires: { infer: true, explicit: [], mode: 'error' } }, + fixture, + {}, + [{ id: path.join(fixture.root, 'src/main.ts'), code: "import { gamepad } from '@napplet/sdk/gamepad';\ngamepad.getGamepads();" }], + ); + const tags = readManifest(fixture.dist).tags; + expect(tags).toContainEqual(['O', 'gamepad']); + expect(tags).not.toContainEqual(['R', 'gamepad']); + expect(result.warnings).toEqual([]); + }); + it('does not infer requirements from type-only imports or dynamic window access', async () => { const fixture = makeFixture(); fs.writeFileSync(path.join(fixture.dist, 'index.html'), '<!doctype html>'); diff --git a/packages/vite-plugin/src/requirements.ts b/packages/vite-plugin/src/requirements.ts index c63e8051..ed0cdb99 100644 --- a/packages/vite-plugin/src/requirements.ts +++ b/packages/vite-plugin/src/requirements.ts @@ -32,6 +32,7 @@ const NAP_DOMAINS = [ 'fs', 'common', 'dm', + 'gamepad', ] as const; const NAP_DOMAIN_SET = new Set<string>(NAP_DOMAINS); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 526fe875..b642caad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,16 +166,16 @@ importers: dependencies: '@kehto/paja': specifier: 0.19.0 - version: 0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3)) + version: 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@napplet/conformance': specifier: workspace:^ version: link:../conformance '@napplet/core': - specifier: 0.32.0 - version: 0.32.0 + specifier: workspace:* + version: link:../core '@napplet/nap': - specifier: 0.32.0 - version: 0.32.0(json-schema-to-ts@3.1.1) + specifier: workspace:* + version: link:../nap playwright: specifier: ^1.59.1 version: 1.61.0 @@ -258,6 +258,9 @@ importers: specifier: workspace:* version: link:../nap devDependencies: + '@napplet/sdk': + specifier: workspace:* + version: link:../sdk tsup: specifier: ^8.5.0 version: 8.5.1(jiti@2.6.1)(postcss@8.5.23)(typescript@5.9.3)(yaml@2.9.1) @@ -744,17 +747,6 @@ packages: resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} engines: {node: '>=20.0.0'} - '@napplet/core@0.32.0': - resolution: {integrity: sha512-gP+U7xd32gD7uFYINK+NWDJfW2TMvhLcIPr+Boafta7tZWTRIlyzozvqsooyFg7XUapCOnGKqnjIUa0DFo6Brw==} - - '@napplet/nap@0.32.0': - resolution: {integrity: sha512-DvlneleMpcUtOZmLGRGj9QSCor67veL4gWOYGTWaXPDkBEaEuiU4Ijtreb3+kVky/4ghjL3qmScOFMW6WorWQw==} - peerDependencies: - json-schema-to-ts: ^3.1.1 - peerDependenciesMeta: - json-schema-to-ts: - optional: true - '@noble/ciphers@0.5.3': resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==} @@ -2480,56 +2472,56 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@kehto/acl@0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))': + '@kehto/acl@0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)': dependencies: - '@napplet/core': 0.32.0 - '@napplet/nap': 0.32.0(json-schema-to-ts@3.1.1) + '@napplet/core': link:packages/core + '@napplet/nap': link:packages/nap - '@kehto/firewall@0.6.0(@napplet/core@0.32.0)': + '@kehto/firewall@0.6.0(@napplet/core@packages+core)': dependencies: - '@napplet/core': 0.32.0 + '@napplet/core': link:packages/core '@kehto/nip@0.6.0(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: '@noble/hashes': 2.0.1 nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/paja@0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/paja@0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@kehto/firewall': 0.6.0(@napplet/core@0.32.0) + '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/firewall': 0.6.0(@napplet/core@packages+core) '@kehto/nip': 0.6.0(nostr-tools@2.23.3(typescript@5.9.3)) - '@kehto/runtime': 0.24.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@kehto/services': 0.22.1(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3)) - '@kehto/shell': 0.22.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/services': 0.22.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/shell': 0.22.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@kehto/wm': 0.0.2 - '@napplet/core': 0.32.0 - '@napplet/nap': 0.32.0(json-schema-to-ts@3.1.1) + '@napplet/core': link:packages/core + '@napplet/nap': link:packages/nap nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/runtime@0.24.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))': + '@kehto/runtime@0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@kehto/firewall': 0.6.0(@napplet/core@0.32.0) - '@napplet/core': 0.32.0 - '@napplet/nap': 0.32.0(json-schema-to-ts@3.1.1) + '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/firewall': 0.6.0(@napplet/core@packages+core) + '@napplet/core': link:packages/core + '@napplet/nap': link:packages/nap '@noble/curves': 2.0.1 '@noble/hashes': 2.0.1 - '@kehto/services@0.22.1(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/services@0.22.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/runtime': 0.24.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@napplet/core': 0.32.0 - '@napplet/nap': 0.32.0(json-schema-to-ts@3.1.1) + '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@napplet/core': link:packages/core + '@napplet/nap': link:packages/nap optionalDependencies: nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/shell@0.22.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1))(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/shell@0.22.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@kehto/runtime': 0.24.0(@napplet/core@0.32.0)(@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)) - '@napplet/core': 0.32.0 - '@napplet/nap': 0.32.0(json-schema-to-ts@3.1.1) + '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@napplet/core': link:packages/core + '@napplet/nap': link:packages/nap nostr-tools: 2.23.3(typescript@5.9.3) '@kehto/wm@0.0.2': {} @@ -2549,14 +2541,6 @@ snapshots: tinyglobby: 0.2.17 yaml: 2.9.1 - '@napplet/core@0.32.0': {} - - '@napplet/nap@0.32.0(json-schema-to-ts@3.1.1)': - dependencies: - '@napplet/core': 0.32.0 - optionalDependencies: - json-schema-to-ts: 3.1.1 - '@noble/ciphers@0.5.3': {} '@noble/ciphers@2.1.1': {} diff --git a/skills/napplet-build/SKILL.md b/skills/napplet-build/SKILL.md index 0b2b9389..6c790069 100644 --- a/skills/napplet-build/SKILL.md +++ b/skills/napplet-build/SKILL.md @@ -100,3 +100,7 @@ Report the URL Paja prints. A raw Vite URL is an asset server without a runtime - `localStorage`, `fetch`, `<img src=https://…>`, `WebSocket`, `window.nostr` — none exist in the sandbox. - External `<script src>` in the artifact — JS must be inline; `artifactMode: 'single-file'` handles it. - Trusting upstream `Content-Type` for resource bytes — the shell delivers a byte-sniffed `mime`. + +## Optional controller input + +This non-normative guidance defers to the living [NAP-GAMEPAD proposal](https://github.com/napplet/naps/pull/108). Check injected `gamepad` presence and call the SDK namespace from `@napplet/sdk/gamepad`. Its cached API exposes `getGamepads()`, `available`, `focused`, and `onChange(handler)`; close listeners on disposal. Keep keyboard or pointer fallbacks, ignore gameplay edges while unfocused, reset held-button baselines on regained focus, preserve null slots, and route controllers by `index`. Use an optional manifest domain when the core task remains usable without controllers. Native controller access belongs to the runtime. Do not add haptics, raw HID access, a correlation id, or a prerequisite SHELL readiness exchange. Reference-mock conformance reports unavailable controllers; verify live focus isolation in a GAMEPAD-capable runtime. diff --git a/skills/napplet-design/SKILL.md b/skills/napplet-design/SKILL.md index 0ed4c6d2..416899a6 100644 --- a/skills/napplet-design/SKILL.md +++ b/skills/napplet-design/SKILL.md @@ -89,3 +89,7 @@ states: loading=<…> empty=<…> error=<…> signed-out=<…> ``` Hand this to `napplet-build`; verify with `napplet-test`. + +## Optional controller input + +This non-normative guidance defers to the living [NAP-GAMEPAD proposal](https://github.com/napplet/naps/pull/108). Check injected `gamepad` presence and call the SDK namespace from `@napplet/sdk/gamepad`. Its cached API exposes `getGamepads()`, `available`, `focused`, and `onChange(handler)`; close listeners on disposal. Keep keyboard or pointer fallbacks, ignore gameplay edges while unfocused, reset held-button baselines on regained focus, preserve null slots, and route controllers by `index`. Use an optional manifest domain when the core task remains usable without controllers. Native controller access belongs to the runtime. Do not add haptics, raw HID access, a correlation id, or a prerequisite SHELL readiness exchange. Reference-mock conformance reports unavailable controllers; verify live focus isolation in a GAMEPAD-capable runtime. diff --git a/skills/napplet-sdk/SKILL.md b/skills/napplet-sdk/SKILL.md index e6f8f2a4..a125a345 100644 --- a/skills/napplet-sdk/SKILL.md +++ b/skills/napplet-sdk/SKILL.md @@ -214,3 +214,7 @@ Use the exact method names exported by the installed `@napplet/sdk` version (`no `shell` exposes optional environment information through `supports`, `services`, `ready`, and `onReady`. Check injected domain presence for availability; other domains remain usable without waiting for the environment. See [NAP-SHELL](https://github.com/napplet/naps/blob/master/naps/NAP-SHELL.md). Theme updates reach napplets with the exposed `theme` domain, independently of manifest requirement declarations. User identity changes report the connected signer, including an empty string at sign-out; they do not alter napplet identity. Profile picture/banner bytes use optional `resource` when exposed; otherwise show local placeholder artwork. Theme media URLs do not grant network access, so colors and local fonts provide the fallback. See [NAP-THEME](https://github.com/napplet/naps/blob/master/naps/NAP-THEME.md) and [NAP-IDENTITY](https://github.com/napplet/naps/blob/master/naps/NAP-IDENTITY.md). + +## Optional controller input + +This non-normative guidance defers to the living [NAP-GAMEPAD proposal](https://github.com/napplet/naps/pull/108). Check injected `gamepad` presence and call the SDK namespace from `@napplet/sdk/gamepad`. Its cached API exposes `getGamepads()`, `available`, `focused`, and `onChange(handler)`; close listeners on disposal. Keep keyboard or pointer fallbacks, ignore gameplay edges while unfocused, reset held-button baselines on regained focus, preserve null slots, and route controllers by `index`. Use an optional manifest domain when the core task remains usable without controllers. Native controller access belongs to the runtime. Do not add haptics, raw HID access, a correlation id, or a prerequisite SHELL readiness exchange. Reference-mock conformance reports unavailable controllers; verify live focus isolation in a GAMEPAD-capable runtime. diff --git a/skills/napplet-test/SKILL.md b/skills/napplet-test/SKILL.md index 0c2049ea..ac2ca5c8 100644 --- a/skills/napplet-test/SKILL.md +++ b/skills/napplet-test/SKILL.md @@ -86,3 +86,7 @@ Report the Paja URL. If `napplet`, Kehto, or Paja is not installed, report the e ``` Cache Playwright's Chromium. Green conformance + a clean boundary audit + a self-contained single-file build + the four-frame UI check = ready to publish. + +## Optional controller input + +This non-normative guidance defers to the living [NAP-GAMEPAD proposal](https://github.com/napplet/naps/pull/108). Check injected `gamepad` presence and call the SDK namespace from `@napplet/sdk/gamepad`. Its cached API exposes `getGamepads()`, `available`, `focused`, and `onChange(handler)`; close listeners on disposal. Keep keyboard or pointer fallbacks, ignore gameplay edges while unfocused, reset held-button baselines on regained focus, preserve null slots, and route controllers by `index`. Use an optional manifest domain when the core task remains usable without controllers. Native controller access belongs to the runtime. Do not add haptics, raw HID access, a correlation id, or a prerequisite SHELL readiness exchange. Reference-mock conformance reports unavailable controllers; verify live focus isolation in a GAMEPAD-capable runtime. From 09f2916b4720d1b435a15cd171da0b4566f476c7 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:19:46 -0700 Subject: [PATCH 06/24] fix(screenshot): accept the GAMEPAD-capable Paja release Co-Authored-By: Codex <codex@openai.com> --- apps/docs/packages/conformance-cli.md | 2 ++ packages/conformance-cli/README.md | 2 ++ packages/conformance-cli/package.json | 2 +- packages/core/README.md | 4 ++-- packages/sdk/README.md | 3 ++- pnpm-lock.yaml | 34 +++++++++++++-------------- 6 files changed, 26 insertions(+), 21 deletions(-) diff --git a/apps/docs/packages/conformance-cli.md b/apps/docs/packages/conformance-cli.md index b258f551..2000b33c 100644 --- a/apps/docs/packages/conformance-cli.md +++ b/apps/docs/packages/conformance-cli.md @@ -56,3 +56,5 @@ source edits rebuild and re-check automatically. - [`@napplet/vite-plugin`](./vite-plugin) — generates the manifest the runner validates 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-optional-zapstore-metadata) for browser setup, iframe selection, and publication options. + +GAMEPAD screenshots need Paja 0.21.0 from the coordinated runtime release. The dependency range accepts the published base release and the GAMEPAD release; publish the latter before this package so fresh private runner installs use the controller-capable preview. diff --git a/packages/conformance-cli/README.md b/packages/conformance-cli/README.md index de457547..5bc52e7f 100644 --- a/packages/conformance-cli/README.md +++ b/packages/conformance-cli/README.md @@ -70,3 +70,5 @@ The CLI also provides the browser runner for `napplet screenshot`. Run `npx @nap ### 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). + +GAMEPAD screenshots need Paja 0.21.0 from the coordinated runtime release. The dependency range accepts the published base release and the GAMEPAD release; publish the latter before this package so fresh private runner installs use the controller-capable preview. diff --git a/packages/conformance-cli/package.json b/packages/conformance-cli/package.json index 6e37a53c..70d9f3df 100644 --- a/packages/conformance-cli/package.json +++ b/packages/conformance-cli/package.json @@ -14,7 +14,7 @@ "access": "public" }, "dependencies": { - "@kehto/paja": "0.19.0", + "@kehto/paja": ">=0.20.1 <0.22.0", "@napplet/conformance": "workspace:^", "@napplet/core": "workspace:*", "@napplet/nap": "workspace:*", diff --git a/packages/core/README.md b/packages/core/README.md index 2b3fce4e..46ca96e9 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -57,14 +57,14 @@ interface RelaySubscribe extends NappletMessage { } ``` -The `type` field domain prefix (`relay`, `identity`, `storage`, `inc`, `theme`, `keys`, `media`, `notify`, `config`, `resource`, `cvm`, `outbox`, `upload`, `intent`, `ble`, `webrtc`, `link`, `count`, `lists`, `serial`, `common`, `dm`) routes messages to the correct NAP handler via `dispatch()`. +The `type` field domain prefix (`relay`, `identity`, `storage`, `inc`, `theme`, `keys`, `media`, `notify`, `config`, `resource`, `cvm`, `outbox`, `upload`, `intent`, `ble`, `webrtc`, `link`, `count`, `lists`, `serial`, `fs`, `common`, `dm`, `shell`, `gamepad`) routes messages to the correct NAP handler via `dispatch()`. #### `NapDomain` String literal union of the NAP capability domains. ```ts -type NapDomain = 'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'common' | 'dm'; +type NapDomain = 'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'fs' | 'common' | 'dm' | 'shell' | 'gamepad'; ``` | Domain | Scope | diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 76ccc175..5c796fc0 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -418,6 +418,7 @@ These are discriminated union types covering all messages in each NAP domain. Us | `CountNapMessage` | `@napplet/nap/count` | Discriminated union of all count domain messages | | `ListsNapMessage` | `@napplet/nap/lists` | Discriminated union of all lists domain messages | | `CommonNapMessage` | `@napplet/nap/common` | Discriminated union of all common domain messages | +| `GamepadNapMessage` | `@napplet/nap/gamepad` | Subscribe, unsubscribe, and state envelopes without correlation ids | | `SerialNapMessage` | `@napplet/nap/serial` | Discriminated union of all serial domain messages | Individual message types (e.g., `RelaySubscribeMessage`, `IdentityGetPublicKeyMessage`) are also re-exported from `@napplet/sdk` for fine-grained typing. @@ -428,7 +429,7 @@ Each NAP domain has a string constant re-exported from its package: ```ts import { RELAY_DOMAIN, IDENTITY_DOMAIN, STORAGE_DOMAIN, INC_DOMAIN, THEME_DOMAIN, KEYS_DOMAIN, MEDIA_DOMAIN, NOTIFY_DOMAIN, CONFIG_DOMAIN, RESOURCE_DOMAIN, CVM_DOMAIN, OUTBOX_DOMAIN, UPLOAD_DOMAIN, INTENT_DOMAIN, BLE_DOMAIN, WEBRTC_DOMAIN, LINK_DOMAIN, COUNT_DOMAIN, LISTS_DOMAIN, COMMON_DOMAIN, SERIAL_DOMAIN, DM_DOMAIN } from '@napplet/sdk'; -// Values: 'relay', 'identity', 'storage', 'inc', 'theme', 'keys', 'media', 'notify', 'config', 'resource', 'cvm', 'outbox', 'upload', 'intent', 'ble', 'webrtc', 'link', 'count', 'lists', 'common', 'serial', 'dm' +// Values: 'relay', 'identity', 'storage', 'inc', 'theme', 'keys', 'media', 'notify', 'config', 'resource', 'cvm', 'outbox', 'upload', 'intent', 'ble', 'webrtc', 'link', 'count', 'lists', 'common', 'serial', 'fs', 'dm', 'shell', 'gamepad' ``` These constants are re-exported from the individual domain packages. Use property presence for type-safe conditional logic: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b642caad..802e6253 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -165,8 +165,8 @@ importers: packages/conformance-cli: dependencies: '@kehto/paja': - specifier: 0.19.0 - version: 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + specifier: '>=0.20.1 <0.22.0' + version: 0.20.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@napplet/conformance': specifier: workspace:^ version: link:../conformance @@ -697,13 +697,13 @@ packages: peerDependencies: '@napplet/core': '>=0.32.0 <0.33.0' - '@kehto/nip@0.6.0': - resolution: {integrity: sha512-RhiLX0NYbOZ+rxq+wbWrcZiHE7F7nasaDCXzpk8TTxDsJvDdLjUISe6o5udMwry5sTya9S40F91D7m0dYd2Gqg==} + '@kehto/nip@0.7.0': + resolution: {integrity: sha512-uDi2fvs8gr2jhhNKU8JbrDaq1cqRpAHdxDWBpoLv59tVTTFcTul/bWpT0aAHcq/SzHgT+3IHT0oIuqJXpqCwhw==} peerDependencies: nostr-tools: '>=2.23.3 <=2.x' - '@kehto/paja@0.19.0': - resolution: {integrity: sha512-UCUwER9thz/sP3B1BPhM/QkymesjnFKGfeNLtuIX0lT1xPH+q8Tbl149U7P2OdgAWt+p5n8p/T/WIOXTFE1CBQ==} + '@kehto/paja@0.20.1': + resolution: {integrity: sha512-fyghGf+lD1OtC1zfMILa/9Qqb+JMKkjL1Z9RTi8DyVWC5O9sf2nfH5VdegzlkgjDeyihSes2AoUEtlMNRrHcoQ==} peerDependencies: '@napplet/core': '>=0.32.0 <0.33.0' '@napplet/nap': '>=0.32.0 <0.33.0' @@ -715,8 +715,8 @@ packages: '@napplet/core': '>=0.32.0 <0.33.0' '@napplet/nap': '>=0.32.0 <0.33.0' - '@kehto/services@0.22.1': - resolution: {integrity: sha512-7rn8IGhqJmN922eO++UmcB9J1rMhqkDUMMne7zSX6Rd1krn2a98Z17req/rD/Ed67ByrMMjkbD11OU+avdRuig==} + '@kehto/services@0.23.0': + resolution: {integrity: sha512-MQn0yktwaUGanj2dSb/G/M5RMcQQNnBcdoQDSfziGaGk2Xtx/ULSslw8pw1QAdvrBoLAiX+5f/bkMZk62xxf6w==} peerDependencies: '@napplet/core': '>=0.32.0 <0.33.0' '@napplet/nap': '>=0.32.0 <0.33.0' @@ -725,8 +725,8 @@ packages: nostr-tools: optional: true - '@kehto/shell@0.22.0': - resolution: {integrity: sha512-YFPxZdFiZ73u40TKU5EOjnsH2P2pRfni4NWppyGwcBYUSkzsiNZs5G63ay3D/Ugr5hLznnNcu5HB88K6QbJ0HQ==} + '@kehto/shell@0.23.0': + resolution: {integrity: sha512-Pu4XOIiPWHWDg9lqkIPuGmoaUVj2GjOUXsXrq+MnynU5V/uJiU21JA/vthBx7/2vGDn5SGh+zRUwk55zdssi3w==} peerDependencies: '@napplet/core': '>=0.32.0 <0.33.0' '@napplet/nap': '>=0.32.0 <0.33.0' @@ -2481,19 +2481,19 @@ snapshots: dependencies: '@napplet/core': link:packages/core - '@kehto/nip@0.6.0(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/nip@0.7.0(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: '@noble/hashes': 2.0.1 nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/paja@0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/paja@0.20.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) '@kehto/firewall': 0.6.0(@napplet/core@packages+core) - '@kehto/nip': 0.6.0(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/nip': 0.7.0(nostr-tools@2.23.3(typescript@5.9.3)) '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) - '@kehto/services': 0.22.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) - '@kehto/shell': 0.22.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/services': 0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/shell': 0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@kehto/wm': 0.0.2 '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap @@ -2508,7 +2508,7 @@ snapshots: '@noble/curves': 2.0.1 '@noble/hashes': 2.0.1 - '@kehto/services@0.22.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/services@0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) '@napplet/core': link:packages/core @@ -2516,7 +2516,7 @@ snapshots: optionalDependencies: nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/shell@0.22.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/shell@0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) From aa9aff86a8cda0e9ec30ed3e3bd281e5db6e4c09 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:22:47 -0700 Subject: [PATCH 07/24] docs(163): record verified coverage and release ordering Co-Authored-By: Codex <codex@openai.com> --- .../163-VERIFICATION.md | 21 +++++++++++++++++++ .../COVERAGE.md | 4 +++- apps/docs/packages/core.md | 2 +- packages/nap/README.md | 4 ++-- 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md b/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md new file mode 100644 index 00000000..7de7d570 --- /dev/null +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md @@ -0,0 +1,21 @@ +--- +phase: 163 +status: passed +verified: 2026-10-12 +--- + +# GAMEPAD verification + +Non-normative implementation evidence against the current living NAP-GAMEPAD and NIP-5D sources linked in CONTEXT.md. Canonical heads and organization inventory were rechecked unchanged. + +| Acceptance reference | Proof | Result | +| --- | --- | --- | +| GP-API | Core types/domain, NAP binding, built SDK root/subpath import tests, npm/JSR exports, selected shim, synchronous cached access and independent listeners. | Passed | +| GP-WIRE | Three message discriminants/directions, no correlation ids, full state, ranges, mappings, null slots, unavailable and neutral views; reference mock and serialized injection tests. | Passed | +| GP-ISOLATION | Kehto broker units and Chromium prove live/neutral frame isolation, host/tab focus loss, native controller denial, connection updates, polling/rate/session cleanup. | Passed | +| GP-TRUST | Parent-source checks, trusted creation/prebound sessions, frozen domain environment, disabled-domain/unknown-source rejection, no SHELL prerequisite. | Passed | +| GP-REPOS | All seven organization repos, all web packages/apps/fixtures, and Kehto runtime dependencies inventoried in COVERAGE.md; appropriate changes and explicit unchanged surfaces recorded. | Passed | +| GP-DOCS | Package/site/skill/template guidance, consumer/migration/CVM/organization docs, current manifest schema, controller fallbacks and focus-edge guidance. | Passed | +| GP-SHIP | Primary build/types/recursive units/docs/slop; applicable consumer gates; source-build compiled CLI screenshot/Paja proof; release metadata and linked PR shipment. | Passed with disclosed existing CVM baseline failures | + +Native API replacement remains deferred for the disclosed optional-projection/NIP-5D injection conflict. No new protocol surface is fabricated. CVM full-suite protected authority failures also occur on untouched main; controller-specific tests pass. Kehto's prospective 0.34 package matrix deliberately uses temporary packed source builds; distribution guards reject those overrides, which are never committed. Release order remains Kehto CLI 0.5.5/Paja 0.21.0 before the napplet package set. No production deployment or image promotion is performed. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md index 66320fc5..84609403 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md @@ -42,4 +42,6 @@ Kehto CLI 0.5.5 must be published before napplet CLI 0.9.1 selects it as the pri Native Gamepad API replacement remains deferred: the GAMEPAD proposal permits an optional replacement while NIP-5D currently limits runtime injection to window.napplet. Required domain support and physical-access denial do not depend on that replacement. Reference mocks intentionally report unavailable controllers; live host isolation is demonstrated separately in Kehto Chromium tests with test-supplied host samples. -Verification results and PR links will be filled after final gates. Original dirty worktrees and unrelated changes remain untouched. +Verified primary `pnpm build`, `pnpm type-check`, `pnpm -r test:unit`, docs build and slop 100/100. Compiled macOS CLI rendered the controller page and ran Paja help using locally assembled release builds with no host runtime/package runner on PATH. The first screenshot exposed the obsolete Paja dependency; the corrected source build renders Controller available. Boilerplate verify/conformance and eight size/theme contexts pass. Nappagochi verify, 88 tests and conformance pass. Migration check/build and 40 tests pass. CVM's 15 controller tests and slop pass; its existing protected-authority/admission baseline failures are disclosed in its PR. Kehto build/types/docs/slop, controller browser proof and baseline unit suite pass; core/NAP 0.34 tarballs also pass build/types and 2105 behavior tests while distribution-only guards reject temporary unpublished overrides. Those overrides are removed before shipment. + +Opened consumer PRs: [boilerplate #7](https://github.com/napplet/boilerplate/pull/7), [Nappagochi #1](https://github.com/napplet/nappagochi/pull/1), [migrate #1](https://github.com/napplet/migrate/pull/1), [CVM #1](https://github.com/napplet/cvm/pull/1), [organization #1](https://github.com/napplet/.github/pull/1). Primary/runtime PR links are recorded after creation. Original dirty worktrees and unrelated changes remain untouched. diff --git a/apps/docs/packages/core.md b/apps/docs/packages/core.md index 0b77fac0..fe835bb3 100644 --- a/apps/docs/packages/core.md +++ b/apps/docs/packages/core.md @@ -32,7 +32,7 @@ import { - **`NappletMessage`** — base interface for every message: `{ type: string }` in `domain.action` format. Concrete message types extend it with payload fields. - **`NapDomain`** — string literal union of the NAP capability domains - (`'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'fs' | 'common' | 'dm'`). + (`'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'fs' | 'common' | 'dm' | 'shell' | 'gamepad'`). - **`NappletGlobal`** — the runtime-injected `window.napplet` namespace with optional domain properties. - **`NAP_DOMAINS`** — runtime constant array of all domain strings, for iteration diff --git a/packages/nap/README.md b/packages/nap/README.md index 6ad320ed..8e4668dd 100644 --- a/packages/nap/README.md +++ b/packages/nap/README.md @@ -1,6 +1,6 @@ # @napplet/nap -> Every active napplet NAP domain (relay, storage, inc, keys, theme, media, notify, identity, config, resource, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, serial, fs, common, dm) as layered subpath exports. The package name remains `@napplet/nap` for compatibility. +> Every active napplet NAP domain (relay, storage, inc, keys, theme, media, notify, identity, config, resource, cvm, outbox, upload, intent, ble, webrtc, link, count, lists, serial, fs, common, dm, shell, gamepad) as layered subpath exports. The package name remains `@napplet/nap` for compatibility. ## Install @@ -227,7 +227,7 @@ import { mediaCreateSession } from '@napplet/nap/media/sdk'; import type { MediaNapMessage } from '@napplet/nap/media/types'; ``` -Domain barrels are also available at `@napplet/nap/relay`, `@napplet/nap/storage`, `@napplet/nap/inc`, `@napplet/nap/keys`, `@napplet/nap/theme`, `@napplet/nap/media`, `@napplet/nap/notify`, `@napplet/nap/identity`, `@napplet/nap/config`, `@napplet/nap/resource`, `@napplet/nap/cvm`, `@napplet/nap/outbox`, `@napplet/nap/upload`, `@napplet/nap/intent`, `@napplet/nap/ble`, `@napplet/nap/webrtc`, `@napplet/nap/link`, `@napplet/nap/count`, `@napplet/nap/lists`, `@napplet/nap/common`, `@napplet/nap/serial`, `@napplet/nap/fs`, and `@napplet/nap/dm`. +Domain barrels are also available at `@napplet/nap/relay`, `@napplet/nap/storage`, `@napplet/nap/inc`, `@napplet/nap/keys`, `@napplet/nap/theme`, `@napplet/nap/media`, `@napplet/nap/notify`, `@napplet/nap/identity`, `@napplet/nap/config`, `@napplet/nap/resource`, `@napplet/nap/cvm`, `@napplet/nap/outbox`, `@napplet/nap/upload`, `@napplet/nap/intent`, `@napplet/nap/ble`, `@napplet/nap/webrtc`, `@napplet/nap/link`, `@napplet/nap/count`, `@napplet/nap/lists`, `@napplet/nap/common`, `@napplet/nap/serial`, `@napplet/nap/fs`, `@napplet/nap/dm`, `@napplet/nap/shell`, and `@napplet/nap/gamepad`. ## Optional Peer Dependency From cf9d3b8dedd499546119f02467ed161382382ac9 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:29:35 -0700 Subject: [PATCH 08/24] fix(release): separate CLI runner pins from their first publication Co-Authored-By: Codex <codex@openai.com> --- packages/cli/CHANGELOG.md | 244 +++++++++------------------- packages/cli/deno.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/cli.ts | 2 +- packages/cli/src/managed-node.ts | 2 +- packages/cli/src/package-runner.ts | 2 +- packages/cli/tests/cli_test.ts | 2 +- packages/cli/tests/zapstore_test.ts | 2 +- 8 files changed, 88 insertions(+), 170 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 92f49b47..ecf5fcc2 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,27 +1,12 @@ # @napplet/cli -## 0.9.1 - -### Patch Changes - -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, - conformance injection, and optional manifest-domain inference. Update the standalone CLI's private - conformance/Paja runners to GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API replacement remains - deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. - ## 0.9.0 ### Minor Changes -- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser - tooling, or keychain utilities. Browser and Paja commands provision private cached tooling - automatically; screenshots and conformance share it. Linux browser packs include libraries and - fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. +- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser tooling, or keychain utilities. Browser and Paja commands provision private cached tooling automatically; screenshots and conformance share it. Linux browser packs include libraries and fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. - Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now - require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is - asynchronous because it may provision private tooling. + Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is asynchronous because it may provision private tooling. ### Patch Changes @@ -31,53 +16,43 @@ ### Patch Changes -- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and - optional Zapstore application events before signing. +- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and optional Zapstore application events before signing. ## 0.8.8 ### Patch Changes -- Link current deployment manifests to optional Zapstore application metadata using the proposed - NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata - before referencing manifests. +- Link current deployment manifests to optional Zapstore application metadata using the proposed NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata before referencing manifests. ## 0.8.7 ### Patch Changes -- Pin screenshot and default conformance commands to the tested browser runner so older - project-local dependencies cannot intercept them. Preserve custom conformance commands. +- Pin screenshot and default conformance commands to the tested browser runner so older project-local dependencies cannot intercept them. Preserve custom conformance commands. ## 0.8.6 ### Patch Changes -- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to - override the URL or suppress source publication with false. +- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to override the URL or suppress source publication with false. ## 0.8.5 ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install - Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. ## 0.8.4 ### Patch Changes -- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run - previews, publication failure reporting, the preview screenshot command, and inline capture with - deploy --screenshot. +- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run previews, publication failure reporting, the preview screenshot command, and inline capture with deploy --screenshot. ## 0.8.3 ### Patch Changes -- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype - input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy - CLI input, and correct setup prompts, reports, and documentation. +- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy CLI input, and correct setup prompts, reports, and documentation. - Updated dependencies [2910c4b] - @napplet/boilerplate@0.3.4 @@ -85,72 +60,44 @@ ### Patch Changes -- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed - package version in JSR and standalone installations. +- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed package version in JSR and standalone installations. ## 0.8.1 ### Patch Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and - naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover - opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment - API independently of other domain calls. Align INC provenance with authenticated endpoint - identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, - options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing - IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper - extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ## 0.8.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact - hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon - byte input while retaining hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML - without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and - event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change - the built artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. - Validate configured and recovered Blossom upload destinations before signing manifests or - uploading, with clear errors for missing values and non-origin URLs. + Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. ## 0.7.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers - emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and - independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy - events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add - offline signed-event migration previews without altering source events or publishing. Vite builds - default to a self-contained artifact; compatibility helper and sidecar names remain available. See - the event migration guide for package cutoffs, immutable pointers and shell ACL/storage - implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. ### Patch Changes -- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, - including trimmed non-empty requirements from legacy sidecars (#223). -- Current deploys fail with the list of built files they would drop instead of silently uploading - only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead - of crashing and accepts `--format current|legacy`; a domain listed as both required and optional - is emitted only as `O`. +- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, including trimmed non-empty requirements from legacy sidecars (#223). +- Current deploys fail with the list of built files they would drop instead of silently uploading only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead of crashing and accepts `--format current|legacy`; a domain listed as both required and optional is emitted only as `O`. ## 0.6.0 ### Minor Changes -- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent - skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the - open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and - the `napplet create` next-steps point at that command. +- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and the `napplet create` next-steps point at that command. ### Patch Changes @@ -161,9 +108,7 @@ ### Patch Changes -- b17e944: Make standalone release binaries dispatch bundled create and skills package code without - a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner - compatibility API, and expose import-safe callable CLI entry points from the maintained packages. +- b17e944: Make standalone release binaries dispatch bundled create and skills package code without a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner compatibility API, and expose import-safe callable CLI entry points from the maintained packages. - Updated dependencies [b17e944] - @napplet/boilerplate@0.3.2 - @napplet/skills@0.3.2 @@ -172,27 +117,18 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require - structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing key, and preserve - `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. ## 0.5.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, - `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — - plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, - `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and - `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte - range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. ## 0.4.0 @@ -200,82 +136,79 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested - sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the - symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and - `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports - `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, - `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, - `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove - the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ## 0.3.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public types, - bindings, runtime injection, manifest tooling, CLI, and reference shell: queryless exact - identities with URI-to-text-payload transposition at `emit` and `invoke`/`open`, runtime-attested - sender, immediate acceptance followed by source-independent no-ID target delivery, and optional - same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes -- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor NIP-5A - constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface that rejected - spec-conformant napplets — `napplet deploy` now accepts any non-empty `[a-z0-9-]` `d` tag (still - rejecting trailing `-`), and `napplet deploy ---all` no longer throws on workspace folders with - names longer than 13 characters. +- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor + NIP-5A constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface + that rejected spec-conformant napplets — `napplet deploy` now accepts any + non-empty `[a-z0-9-]` `d` tag (still rejecting trailing `-`), and `napplet deploy +--all` no longer throws on workspace folders with names longer than 13 + characters. ## 0.2.1 ### Patch Changes -- b335c40: Run the default conformance tool through its npm package instead of requiring a global - `napplet-conformance` executable, and preserve Kehto's managed-command separator when - `napplet paja` wraps a local app server. +- b335c40: Run the default conformance tool through its npm package instead of requiring a + global `napplet-conformance` executable, and preserve Kehto's managed-command + separator when `napplet paja` wraps a local app server. ## 0.2.0 ### Minor Changes -- 49a8658: Make developer onboarding CLI-first from installation through deployment. The Napplet CLI - now creates starters, owns deployment metadata, installs agent skills, prints a linked developer - guide, and ships as checksum-verified standalone binaries. The boilerplate generator no longer - prompts for deployment metadata, and the bundled skills teach the same ordered workflow as the - CLI, docs, and web app. +- 49a8658: Make developer onboarding CLI-first from installation through deployment. The + Napplet CLI now creates starters, owns deployment metadata, installs agent + skills, prints a linked developer guide, and ships as checksum-verified + standalone binaries. The boilerplate + generator no longer prompts for deployment metadata, and the bundled skills + teach the same ordered workflow as the CLI, docs, and web app. ## 0.1.3 ### Patch Changes -- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden Enter-based - secret prompts, terminal deploy reports with NIP-19 pointers, configured-bunker reconnects, and - raw `bunker://` signing while preserving JSON output for CI. Root-source deploys now keep local - control state such as `.napplet/config.json`, hidden files, and `node_modules` out of signed - manifest content. Relay and Blossom suggestions are now Tab-completion candidates instead of - numbered selections, and Nostr Connect asks for bunker relays separately from deploy relays with - `wss://bucket.coracle.social` as the default. Nostr Connect pairing and remote signing now use - applesauce-signers so QR flows accept the `ack` responses emitted by common bunkers and match - nsyte behavior. Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt - but fall back to unscoped and legacy base64 auth for deployed servers that reject otherwise valid - uploads. Network deploys now treat unavailable redundant Blossom mirrors as warnings and continue - to relay publication whenever at least one server holds every required blob, while still failing - when no configured server is complete. Human reports now share the command's success predicate, so - redundant relay misses are also warnings whenever every manifest reaches another relay. +- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden + Enter-based secret prompts, terminal deploy reports with NIP-19 pointers, + configured-bunker reconnects, and raw `bunker://` signing while preserving JSON + output for CI. Root-source deploys now keep local control state such as + `.napplet/config.json`, hidden files, and `node_modules` out of signed manifest + content. Relay and Blossom suggestions are now Tab-completion candidates instead + of numbered selections, and Nostr Connect asks for bunker relays separately from + deploy relays with `wss://bucket.coracle.social` as the default. + Nostr Connect pairing and remote signing now use applesauce-signers so QR flows + accept the `ack` responses emitted by common bunkers and match nsyte behavior. + Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt + but fall back to unscoped and legacy base64 auth for deployed servers that reject + otherwise valid uploads. + Network deploys now treat unavailable redundant Blossom mirrors as warnings and + continue to relay publication whenever at least one server holds every required + blob, while still failing when no configured server is complete. Human reports + now share the command's success predicate, so redundant relay misses are also + warnings whenever every manifest reaches another relay. ## 0.1.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, - and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. ## 0.1.1 @@ -287,35 +220,20 @@ ### Minor Changes -- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, - manifest, and signing readiness state. -- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` - command surfaces for interactive/local signing references. -- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is - available. -- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using - canonical manifest kind values, path tags, aggregate hashes, and server hints. +- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, manifest, and signing readiness state. +- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` command surfaces for interactive/local signing references. +- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is available. +- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using canonical manifest kind values, path tags, aggregate hashes, and server hints. - fb434c3: Support nsyte-compatible nbunksec remote signing for CI and stored-key deploy flows. -- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and - publish signed manifest events to configured relays. -- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy - manifests. -- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are - available. -- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value - `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and - `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing - values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and - companion snapshot manifests alongside the existing `requires` passthrough. -- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet - discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper - command surfaces. -- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// - paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and - points `.napplet` signing.keyReference at it. +- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and publish signed manifest events to configured relays. +- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy manifests. +- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are available. +- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and companion snapshot manifests alongside the existing `requires` passthrough. +- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper command surfaces. +- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and points `.napplet` signing.keyReference at it. ### Patch Changes -- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with - installation, quick-start, command, signing, project-layout, and troubleshooting guidance. +- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with installation, + quick-start, command, signing, project-layout, and troubleshooting guidance. - fb434c3: Publish NIP-5D napplet manifest kinds from deploy commands diff --git a/packages/cli/deno.json b/packages/cli/deno.json index 9a25a072..8ba905e9 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -1,7 +1,7 @@ { "extends": "../../deno.json", "name": "@napplet/cli", - "version": "0.9.1", + "version": "0.9.0", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "license": "MIT", "exports": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 2a1cc85d..d27bea63 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/cli", - "version": "0.9.1", + "version": "0.9.0", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "type": "module", "files": [ diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 6dfbb83b..8ccc8184 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -403,7 +403,7 @@ async function commandPaja(argv: string[]): Promise<number> { const config = await loadConfig(flags); const configured = config.paja?.command?.trim(); const base: ResolvedCommand = !configured || configured === "kehto" - ? await managedPackageCommand("@kehto/cli@0.5.5", "dist/index.js") + ? await managedPackageCommand("@kehto/cli@0.5.4", "dist/index.js") : splitCommand(configured); const child = new Deno.Command(base.command, { args: [...base.args, "paja", ...resolvePajaArgs(flags.afterDoubleDash)], diff --git a/packages/cli/src/managed-node.ts b/packages/cli/src/managed-node.ts index 2ebb3cfa..ba8e71fe 100644 --- a/packages/cli/src/managed-node.ts +++ b/packages/cli/src/managed-node.ts @@ -90,7 +90,7 @@ export async function ensureManagedNode(): Promise<ManagedNode> { * @param spec Exact package version. * @param entry Package-relative CLI script. * @returns Absolute executable/script and environment, independent of project dependencies. - * @example await managedPackageCommand('@napplet/conformance-cli@0.3.4', 'dist/cli.js') + * @example await managedPackageCommand('@napplet/conformance-cli@0.3.3', 'dist/cli.js') */ export async function managedPackageCommand(spec: string, entry: string): Promise<ResolvedCommand> { const node = await ensureManagedNode(); diff --git a/packages/cli/src/package-runner.ts b/packages/cli/src/package-runner.ts index cdbbb649..2e1f5870 100644 --- a/packages/cli/src/package-runner.ts +++ b/packages/cli/src/package-runner.ts @@ -2,7 +2,7 @@ import { type CommandRunner, runCommand, splitCommand } from "./process.ts"; import { managedPackageCommand } from "./managed-node.ts"; -const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.4"; +const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.3"; /** Process and output adapters used to invoke a maintained package CLI. */ export interface PackageCliRunOptions { diff --git a/packages/cli/tests/cli_test.ts b/packages/cli/tests/cli_test.ts index 076bed0a..7ea61fde 100644 --- a/packages/cli/tests/cli_test.ts +++ b/packages/cli/tests/cli_test.ts @@ -258,7 +258,7 @@ Deno.test("resolveConformanceCommand uses private tooling for either platform", command: "/private/node", args: ["/private/conformance.js"], }); } - assertEquals(calls, ["@napplet/conformance-cli@0.3.4:dist/cli.js", "@napplet/conformance-cli@0.3.4:dist/cli.js"]); + assertEquals(calls, ["@napplet/conformance-cli@0.3.3:dist/cli.js", "@napplet/conformance-cli@0.3.3:dist/cli.js"]); }); Deno.test("resolveConformanceCommand preserves an explicit custom runner", async () => { diff --git a/packages/cli/tests/zapstore_test.ts b/packages/cli/tests/zapstore_test.ts index 3110ee82..a2c78780 100644 --- a/packages/cli/tests/zapstore_test.ts +++ b/packages/cli/tests/zapstore_test.ts @@ -277,7 +277,7 @@ Deno.test("screenshot runner pins a compatible version and preserves literal arg }, os: "windows", resolvePackage: async (spec, entry) => { - assertEquals(spec, "@napplet/conformance-cli@0.3.4"); + assertEquals(spec, "@napplet/conformance-cli@0.3.3"); assertEquals(entry, "dist/cli.js"); return { command: "C:/private/node.exe", args: ["C:/private/screenshot.js"] }; }, From e7a5565ae0636aedb7c17b006b4d4a145d1b6725 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:30:22 -0700 Subject: [PATCH 09/24] fix(cli): select GAMEPAD runners after their coordinated publication Co-Authored-By: Codex <codex@openai.com> --- .changeset/gamepad-cli-runners.md | 5 + packages/cli/CHANGELOG.md | 240 ++++++++++++++++++---------- packages/cli/deno.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/cli.ts | 2 +- packages/cli/src/managed-node.ts | 2 +- packages/cli/src/package-runner.ts | 2 +- packages/cli/tests/cli_test.ts | 2 +- packages/cli/tests/zapstore_test.ts | 2 +- 9 files changed, 171 insertions(+), 88 deletions(-) create mode 100644 .changeset/gamepad-cli-runners.md diff --git a/.changeset/gamepad-cli-runners.md b/.changeset/gamepad-cli-runners.md new file mode 100644 index 00000000..500532d3 --- /dev/null +++ b/.changeset/gamepad-cli-runners.md @@ -0,0 +1,5 @@ +--- +"@napplet/cli": patch +--- + +Select the GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 in the private runner cache. Merge after napplet/web #242 publishes conformance CLI 0.3.4 and kehto/web #297 publishes CLI 0.5.5/Paja 0.21.0; this avoids a self-publication cycle in fresh-cache tests. diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index ecf5fcc2..50d52674 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,12 +1,23 @@ # @napplet/cli +## 0.9.1 + +### Patch Changes + +- Select the published GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 through the existing private tool cache. Merge after the coordinated SDK/conformance and Kehto releases publish these packages. + ## 0.9.0 ### Minor Changes -- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser tooling, or keychain utilities. Browser and Paja commands provision private cached tooling automatically; screenshots and conformance share it. Linux browser packs include libraries and fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. +- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser + tooling, or keychain utilities. Browser and Paja commands provision private cached tooling + automatically; screenshots and conformance share it. Linux browser packs include libraries and + fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. - Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is asynchronous because it may provision private tooling. + Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now + require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is + asynchronous because it may provision private tooling. ### Patch Changes @@ -16,43 +27,53 @@ ### Patch Changes -- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and optional Zapstore application events before signing. +- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and + optional Zapstore application events before signing. ## 0.8.8 ### Patch Changes -- Link current deployment manifests to optional Zapstore application metadata using the proposed NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata before referencing manifests. +- Link current deployment manifests to optional Zapstore application metadata using the proposed + NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata + before referencing manifests. ## 0.8.7 ### Patch Changes -- Pin screenshot and default conformance commands to the tested browser runner so older project-local dependencies cannot intercept them. Preserve custom conformance commands. +- Pin screenshot and default conformance commands to the tested browser runner so older + project-local dependencies cannot intercept them. Preserve custom conformance commands. ## 0.8.6 ### Patch Changes -- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to override the URL or suppress source publication with false. +- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to + override the URL or suppress source publication with false. ## 0.8.5 ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install + Chromium on first use and retain explicit preview URLs. ## 0.8.4 ### Patch Changes -- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run previews, publication failure reporting, the preview screenshot command, and inline capture with deploy --screenshot. +- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run + previews, publication failure reporting, the preview screenshot command, and inline capture with + deploy --screenshot. ## 0.8.3 ### Patch Changes -- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy CLI input, and correct setup prompts, reports, and documentation. +- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype + input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy + CLI input, and correct setup prompts, reports, and documentation. - Updated dependencies [2910c4b] - @napplet/boilerplate@0.3.4 @@ -60,44 +81,72 @@ ### Patch Changes -- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed package version in JSR and standalone installations. +- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed + package version in JSR and standalone installations. ## 0.8.1 ### Patch Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and + naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover + opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment + API independently of other domain calls. Align INC provenance with authenticated endpoint + identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, + options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing + IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper + extraction preserves existing exports and behavior. ## 0.8.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact + hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon + byte input while retaining hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML + without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and + event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change + the built artifact hash. - Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. + Validate configured and recovered Blossom upload destinations before signing manifests or + uploading, with clear errors for missing values and non-origin URLs. ## 0.7.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers + emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and + independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy + events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add + offline signed-event migration previews without altering source events or publishing. Vite builds + default to a self-contained artifact; compatibility helper and sidecar names remain available. See + the event migration guide for package cutoffs, immutable pointers and shell ACL/storage + implications. ### Patch Changes -- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, including trimmed non-empty requirements from legacy sidecars (#223). -- Current deploys fail with the list of built files they would drop instead of silently uploading only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead of crashing and accepts `--format current|legacy`; a domain listed as both required and optional is emitted only as `O`. +- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, + including trimmed non-empty requirements from legacy sidecars (#223). +- Current deploys fail with the list of built files they would drop instead of silently uploading + only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead + of crashing and accepts `--format current|legacy`; a domain listed as both required and optional + is emitted only as `O`. ## 0.6.0 ### Minor Changes -- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and the `napplet create` next-steps point at that command. +- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent + skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the + open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and + the `napplet create` next-steps point at that command. ### Patch Changes @@ -108,7 +157,9 @@ ### Patch Changes -- b17e944: Make standalone release binaries dispatch bundled create and skills package code without a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner compatibility API, and expose import-safe callable CLI entry points from the maintained packages. +- b17e944: Make standalone release binaries dispatch bundled create and skills package code without + a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner + compatibility API, and expose import-safe callable CLI entry points from the maintained packages. - Updated dependencies [b17e944] - @napplet/boilerplate@0.3.2 - @napplet/skills@0.3.2 @@ -117,18 +168,27 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require + structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing key, and preserve + `count` requirements during CLI deploys. ## 0.5.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access + ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, + `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — + plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, + `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, + normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and + `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte + range options, and read/write result counts refer to decoded bytes. ## 0.4.0 @@ -136,79 +196,82 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested + sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the + symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and + `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including + bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports + `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, + `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, + `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove + the draft-only `eventKinds` option and trailing `kind:<number>` fields. ## 0.3.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public - types, bindings, runtime injection, manifest tooling, CLI, and reference shell: - queryless exact identities with URI-to-text-payload transposition at `emit` and - `invoke`/`open`, runtime-attested sender, immediate acceptance followed by - source-independent no-ID target delivery, and optional same-tag event-kind - discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public types, + bindings, runtime injection, manifest tooling, CLI, and reference shell: queryless exact + identities with URI-to-text-payload transposition at `emit` and `invoke`/`open`, runtime-attested + sender, immediate acceptance followed by source-independent no-ID target delivery, and optional + same-tag event-kind discovery metadata. ### Patch Changes -- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor - NIP-5A constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface - that rejected spec-conformant napplets — `napplet deploy` now accepts any - non-empty `[a-z0-9-]` `d` tag (still rejecting trailing `-`), and `napplet deploy ---all` no longer throws on workspace folders with names longer than 13 - characters. +- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor NIP-5A + constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface that rejected + spec-conformant napplets — `napplet deploy` now accepts any non-empty `[a-z0-9-]` `d` tag (still + rejecting trailing `-`), and `napplet deploy +--all` no longer throws on workspace folders with + names longer than 13 characters. ## 0.2.1 ### Patch Changes -- b335c40: Run the default conformance tool through its npm package instead of requiring a - global `napplet-conformance` executable, and preserve Kehto's managed-command - separator when `napplet paja` wraps a local app server. +- b335c40: Run the default conformance tool through its npm package instead of requiring a global + `napplet-conformance` executable, and preserve Kehto's managed-command separator when + `napplet paja` wraps a local app server. ## 0.2.0 ### Minor Changes -- 49a8658: Make developer onboarding CLI-first from installation through deployment. The - Napplet CLI now creates starters, owns deployment metadata, installs agent - skills, prints a linked developer guide, and ships as checksum-verified - standalone binaries. The boilerplate - generator no longer prompts for deployment metadata, and the bundled skills - teach the same ordered workflow as the CLI, docs, and web app. +- 49a8658: Make developer onboarding CLI-first from installation through deployment. The Napplet CLI + now creates starters, owns deployment metadata, installs agent skills, prints a linked developer + guide, and ships as checksum-verified standalone binaries. The boilerplate generator no longer + prompts for deployment metadata, and the bundled skills teach the same ordered workflow as the + CLI, docs, and web app. ## 0.1.3 ### Patch Changes -- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden - Enter-based secret prompts, terminal deploy reports with NIP-19 pointers, - configured-bunker reconnects, and raw `bunker://` signing while preserving JSON - output for CI. Root-source deploys now keep local control state such as - `.napplet/config.json`, hidden files, and `node_modules` out of signed manifest - content. Relay and Blossom suggestions are now Tab-completion candidates instead - of numbered selections, and Nostr Connect asks for bunker relays separately from - deploy relays with `wss://bucket.coracle.social` as the default. - Nostr Connect pairing and remote signing now use applesauce-signers so QR flows - accept the `ack` responses emitted by common bunkers and match nsyte behavior. - Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt - but fall back to unscoped and legacy base64 auth for deployed servers that reject - otherwise valid uploads. - Network deploys now treat unavailable redundant Blossom mirrors as warnings and - continue to relay publication whenever at least one server holds every required - blob, while still failing when no configured server is complete. Human reports - now share the command's success predicate, so redundant relay misses are also - warnings whenever every manifest reaches another relay. +- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden Enter-based + secret prompts, terminal deploy reports with NIP-19 pointers, configured-bunker reconnects, and + raw `bunker://` signing while preserving JSON output for CI. Root-source deploys now keep local + control state such as `.napplet/config.json`, hidden files, and `node_modules` out of signed + manifest content. Relay and Blossom suggestions are now Tab-completion candidates instead of + numbered selections, and Nostr Connect asks for bunker relays separately from deploy relays with + `wss://bucket.coracle.social` as the default. Nostr Connect pairing and remote signing now use + applesauce-signers so QR flows accept the `ack` responses emitted by common bunkers and match + nsyte behavior. Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt + but fall back to unscoped and legacy base64 auth for deployed servers that reject otherwise valid + uploads. Network deploys now treat unavailable redundant Blossom mirrors as warnings and continue + to relay publication whenever at least one server holds every required blob, while still failing + when no configured server is complete. Human reports now share the command's success predicate, so + redundant relay misses are also warnings whenever every manifest reaches another relay. ## 0.1.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, + and Vite plugin packages. ## 0.1.1 @@ -220,20 +283,35 @@ ### Minor Changes -- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, manifest, and signing readiness state. -- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` command surfaces for interactive/local signing references. -- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is available. -- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using canonical manifest kind values, path tags, aggregate hashes, and server hints. +- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, + manifest, and signing readiness state. +- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` + command surfaces for interactive/local signing references. +- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is + available. +- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using + canonical manifest kind values, path tags, aggregate hashes, and server hints. - fb434c3: Support nsyte-compatible nbunksec remote signing for CI and stored-key deploy flows. -- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and publish signed manifest events to configured relays. -- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy manifests. -- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are available. -- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and companion snapshot manifests alongside the existing `requires` passthrough. -- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper command surfaces. -- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and points `.napplet` signing.keyReference at it. +- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and + publish signed manifest events to configured relays. +- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy + manifests. +- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are + available. +- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value + `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and + `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing + values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and + companion snapshot manifests alongside the existing `requires` passthrough. +- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet + discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper + command surfaces. +- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// + paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and + points `.napplet` signing.keyReference at it. ### Patch Changes -- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with installation, - quick-start, command, signing, project-layout, and troubleshooting guidance. +- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with + installation, quick-start, command, signing, project-layout, and troubleshooting guidance. - fb434c3: Publish NIP-5D napplet manifest kinds from deploy commands diff --git a/packages/cli/deno.json b/packages/cli/deno.json index 8ba905e9..9a25a072 100644 --- a/packages/cli/deno.json +++ b/packages/cli/deno.json @@ -1,7 +1,7 @@ { "extends": "../../deno.json", "name": "@napplet/cli", - "version": "0.9.0", + "version": "0.9.1", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "license": "MIT", "exports": { diff --git a/packages/cli/package.json b/packages/cli/package.json index d27bea63..2a1cc85d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@napplet/cli", - "version": "0.9.0", + "version": "0.9.1", "description": "Standalone CLI for creating, configuring, testing, and deploying napplets", "type": "module", "files": [ diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 8ccc8184..6dfbb83b 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -403,7 +403,7 @@ async function commandPaja(argv: string[]): Promise<number> { const config = await loadConfig(flags); const configured = config.paja?.command?.trim(); const base: ResolvedCommand = !configured || configured === "kehto" - ? await managedPackageCommand("@kehto/cli@0.5.4", "dist/index.js") + ? await managedPackageCommand("@kehto/cli@0.5.5", "dist/index.js") : splitCommand(configured); const child = new Deno.Command(base.command, { args: [...base.args, "paja", ...resolvePajaArgs(flags.afterDoubleDash)], diff --git a/packages/cli/src/managed-node.ts b/packages/cli/src/managed-node.ts index ba8e71fe..2ebb3cfa 100644 --- a/packages/cli/src/managed-node.ts +++ b/packages/cli/src/managed-node.ts @@ -90,7 +90,7 @@ export async function ensureManagedNode(): Promise<ManagedNode> { * @param spec Exact package version. * @param entry Package-relative CLI script. * @returns Absolute executable/script and environment, independent of project dependencies. - * @example await managedPackageCommand('@napplet/conformance-cli@0.3.3', 'dist/cli.js') + * @example await managedPackageCommand('@napplet/conformance-cli@0.3.4', 'dist/cli.js') */ export async function managedPackageCommand(spec: string, entry: string): Promise<ResolvedCommand> { const node = await ensureManagedNode(); diff --git a/packages/cli/src/package-runner.ts b/packages/cli/src/package-runner.ts index 2e1f5870..cdbbb649 100644 --- a/packages/cli/src/package-runner.ts +++ b/packages/cli/src/package-runner.ts @@ -2,7 +2,7 @@ import { type CommandRunner, runCommand, splitCommand } from "./process.ts"; import { managedPackageCommand } from "./managed-node.ts"; -const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.3"; +const CONFORMANCE_PACKAGE = "@napplet/conformance-cli@0.3.4"; /** Process and output adapters used to invoke a maintained package CLI. */ export interface PackageCliRunOptions { diff --git a/packages/cli/tests/cli_test.ts b/packages/cli/tests/cli_test.ts index 7ea61fde..076bed0a 100644 --- a/packages/cli/tests/cli_test.ts +++ b/packages/cli/tests/cli_test.ts @@ -258,7 +258,7 @@ Deno.test("resolveConformanceCommand uses private tooling for either platform", command: "/private/node", args: ["/private/conformance.js"], }); } - assertEquals(calls, ["@napplet/conformance-cli@0.3.3:dist/cli.js", "@napplet/conformance-cli@0.3.3:dist/cli.js"]); + assertEquals(calls, ["@napplet/conformance-cli@0.3.4:dist/cli.js", "@napplet/conformance-cli@0.3.4:dist/cli.js"]); }); Deno.test("resolveConformanceCommand preserves an explicit custom runner", async () => { diff --git a/packages/cli/tests/zapstore_test.ts b/packages/cli/tests/zapstore_test.ts index a2c78780..3110ee82 100644 --- a/packages/cli/tests/zapstore_test.ts +++ b/packages/cli/tests/zapstore_test.ts @@ -277,7 +277,7 @@ Deno.test("screenshot runner pins a compatible version and preserves literal arg }, os: "windows", resolvePackage: async (spec, entry) => { - assertEquals(spec, "@napplet/conformance-cli@0.3.3"); + assertEquals(spec, "@napplet/conformance-cli@0.3.4"); assertEquals(entry, "dist/cli.js"); return { command: "C:/private/node.exe", args: ["C:/private/screenshot.js"] }; }, From 428fa3f9c7f6dcb7d8c30e16c5c7b9cfbaeffd2e Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:31:47 -0700 Subject: [PATCH 10/24] chore(release): consume the CLI follow-up changeset Co-Authored-By: Codex <codex@openai.com> --- .changeset/gamepad-cli-runners.md | 5 ----- packages/cli/CHANGELOG.md | 4 +++- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 .changeset/gamepad-cli-runners.md diff --git a/.changeset/gamepad-cli-runners.md b/.changeset/gamepad-cli-runners.md deleted file mode 100644 index 500532d3..00000000 --- a/.changeset/gamepad-cli-runners.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@napplet/cli": patch ---- - -Select the GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 in the private runner cache. Merge after napplet/web #242 publishes conformance CLI 0.3.4 and kehto/web #297 publishes CLI 0.5.5/Paja 0.21.0; this avoids a self-publication cycle in fresh-cache tests. diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 50d52674..652862b4 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,7 +4,9 @@ ### Patch Changes -- Select the published GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 through the existing private tool cache. Merge after the coordinated SDK/conformance and Kehto releases publish these packages. +- e7a5565: Select the GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 in the private + runner cache. Merge after napplet/web #242 publishes conformance CLI 0.3.4 and kehto/web #297 + publishes CLI 0.5.5/Paja 0.21.0; this avoids a self-publication cycle in fresh-cache tests. ## 0.9.0 From 69230d13a3e9484163399b72cb1a1a1fc1ecf558 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:31:49 -0700 Subject: [PATCH 11/24] docs(163): track coordinated PRs and the release-gated CLI follow-up Co-Authored-By: Codex <codex@openai.com> --- .planning/ROADMAP.md | 12 ++++---- .planning/STATE.md | 30 ++++++++++--------- .../163-03-SUMMARY.md | 6 +++- .../163-REVIEW.md | 2 ++ .../163-VERIFICATION.md | 6 ++++ .../COVERAGE.md | 6 ++-- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 72be5e22..26d51fd6 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -932,11 +932,13 @@ Plans: ### Phase 163: NAP-GAMEPAD across repositories -**Goal:** [To be planned] -**Requirements**: TBD +**Goal:** Implement the proposed GAMEPAD API/runtime/tooling across all organization repositories and applicable packages, including boilerplate. Non-normative acceptance defers to [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108) and [NIP-5D](https://github.com/nostr-protocol/nips/pull/2303). +**Requirements:** GP-API, GP-WIRE, GP-ISOLATION, GP-TRUST, GP-REPOS, GP-DOCS, GP-SHIP (defined in 163-CONTEXT.md) **Depends on:** Phase 162 -**Plans:** 0 plans +**Plans:** 3 plans — COMPLETE -Plans: +- [x] 163-01-PLAN.md — Portable API, core types, runtime binding and SDK exports +- [x] 163-02-PLAN.md — Host isolation, injection and conformance +- [x] 163-03-PLAN.md — All repositories, docs, releases and coordinated PR shipment -- [ ] TBD (run /gsd-plan-phase 163 to break down) +Verified and shipped in [web #242](https://github.com/napplet/web/pull/242), [Kehto #297](https://github.com/kehto/web/pull/297), and five consumer/community PRs linked in COVERAGE.md. Publish the Kehto package set before the primary release. The optional native API replacement conflict and existing CVM baseline failures remain disclosed. diff --git a/.planning/STATE.md b/.planning/STATE.md index eadc2d1d..6f91ceee 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,15 +4,17 @@ milestone: v0.34.0 milestone_name: NIP-5D Runtime Injection current_phase: 163 current_phase_name: nap-gamepad-across-repositories -status: in_progress -stopped_at: "Phase 162 verified; draft PR #224 open with hzrd149 requested" -last_updated: "2026-10-04T19:00:16.967369+00:00" -last_activity: 2026-10-04 -last_activity_desc: "Completed NIP-5D event schema migration; draft PR #224" +status: completed +stopped_at: Phase 163 verified; coordinated PRs open with CLI follow-up release gated +last_updated: "2026-10-12T19:26:09.613Z" +last_activity: 2026-10-12 +last_activity_desc: GAMEPAD verified across all repositories; PRs 242/243 and Kehto 297 progress: - total_plans: 3 - completed_plans: 3 - percent: 100 + total_phases: 8 + completed_phases: 3 + total_plans: 16 + completed_plans: 6 + percent: 38 --- # Project State @@ -23,18 +25,18 @@ See: .planning/PROJECT.md (updated 2026-05-24 after v0.31.0 archive) **Core value:** Prove that sandboxed Nostr apps can securely delegate to a host shell over a simple, standardized protocol — and ship the spec + SDK so others can build on it. -**Current focus:** Phase 163 — NAP-GAMEPAD across repositories; portable API checkpoint committed, runtime/conformance and repository coverage in progress. +**Current focus:** Phase 163 — NAP-GAMEPAD verified and shipped as coordinated PRs; release ordering recorded in COVERAGE.md. > **Provenance note:** The "Accumulated Context" section below preserves bullet records from BOTH branches' STATE.md histories. Records tagged "v0.29.0" from main's lineage refer to the milestone NOW renumbered as v0.30.0 (Class-Gated Decrypt — Phases 135-138). Records tagged "v0.29.0" from feat/strict-model refer to NUB-CONNECT (Phases 135-142). Phase number alone is not a unique identifier across the two; cross-reference the topic (decrypt/identity/NIP-07 → v0.30.0; connect/class/CSP-authority → v0.29.0). ## Current Position -Phase: 162 (nip-5d-event-schema-migration) — COMPLETE -Plan: 3 of 3 -Status: Verified; [draft PR #224](https://github.com/napplet/web/pull/224) open, hzrd149 requested -Last activity: 2026-10-09 - Completed quick task 261009-ur6: Remove commit-title gating from JSR publication +Phase: 155 — Implement NAP-SHELL +Plan: Not started +Status: Ready to plan +Last activity: 2026-10-12 — Phase 163 complete, transitioned to Phase 155 -See phase 162 VERIFICATION.md for evidence and disclosed compatibility/tooling findings. Earlier phase and quick-task records below are historical. +See phase 163 VERIFICATION.md and COVERAGE.md for evidence and disclosed release/specification/baseline limits. Earlier phase and quick-task records below are historical. ### Quick task 261011-hdg — COMPLETE diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md b/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md index 64522c54..f9e85cd9 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-03-SUMMARY.md @@ -1,7 +1,7 @@ --- phase: 163 plan: 03 -status: in_progress +status: complete --- # All repositories and shipment @@ -9,3 +9,7 @@ status: in_progress All seven organization repositories plus Kehto are audited. Applicable source, consumer/toolchain, template, migration, policy, organization, site/package/skill, and release surfaces are covered in COVERAGE.md. Primary build/type/unit/docs/slop gates pass. CVM controller suites pass; protected-authority and timing failures also reproduce on its untouched baseline. Linked PRs and final host gates are recorded before completion. Non-normative evidence; protocol authority remains the living references in 163-CONTEXT.md. + +Opened PRs: web #242, Kehto #297, boilerplate #7, Nappagochi #1, migrate #1, CVM #1, organization #1. naps proposal #108 remains the canonical open draft. Publish Kehto CLI 0.5.5/Paja 0.21.0 before the primary release. All owned working trees are clean; unrelated original work remains unchanged. + +Release sequencing correction: primary web #242 publishes the SDK/conformance set while retaining the already-published CLI runners. Draft follow-up [#243](https://github.com/napplet/web/pull/243) changes the private runner pins and CLI version only after conformance CLI 0.3.4 and Kehto CLI 0.5.5/Paja 0.21.0 publish. Fresh-cache CI correctly rejected nonexistent future versions, which prompted this split; no provisioning test was weakened. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md b/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md index a6182403..52cfdd7f 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-REVIEW.md @@ -11,3 +11,5 @@ Reviewed the changed API, serialized bindings, source/session routing, host brok The review preserved existing public host registration contracts: trusted sessions registered directly by integrators also route independently of SHELL readiness, without trusting app payload identity. Regression coverage verifies this. Paja's retained handshake-domain export is empty and SHELL is an optional advertised interface. Every controller frame is denied native gamepad/HID/USB paths before execution. The optional native Gamepad API replacement remains deferred due to the disclosed NIP-5D/GAMEPAD conflict. CVM's existing protected-authority baseline failures remain visible. Release ordering is explicit: Kehto CLI 0.5.5 before napplet CLI 0.9.1; the napplet set contains SDK 0.30.0 and conformance CLI 0.3.4. CLI smoke uses locally assembled source-build cache entries and an empty host-tool PATH; it does not claim those future versions are already published. + +Release sequencing correction: primary web #242 publishes the SDK/conformance set while retaining the already-published CLI runners. Draft follow-up [#243](https://github.com/napplet/web/pull/243) changes the private runner pins and CLI version only after conformance CLI 0.3.4 and Kehto CLI 0.5.5/Paja 0.21.0 publish. Fresh-cache CI correctly rejected nonexistent future versions, which prompted this split; no provisioning test was weakened. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md b/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md index 7de7d570..4c325216 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/163-VERIFICATION.md @@ -19,3 +19,9 @@ Non-normative implementation evidence against the current living NAP-GAMEPAD and | GP-SHIP | Primary build/types/recursive units/docs/slop; applicable consumer gates; source-build compiled CLI screenshot/Paja proof; release metadata and linked PR shipment. | Passed with disclosed existing CVM baseline failures | Native API replacement remains deferred for the disclosed optional-projection/NIP-5D injection conflict. No new protocol surface is fabricated. CVM full-suite protected authority failures also occur on untouched main; controller-specific tests pass. Kehto's prospective 0.34 package matrix deliberately uses temporary packed source builds; distribution guards reject those overrides, which are never committed. Release order remains Kehto CLI 0.5.5/Paja 0.21.0 before the napplet package set. No production deployment or image promotion is performed. + +Final shipment review: SDK/tooling https://github.com/napplet/web/pull/242 and runtime https://github.com/kehto/web/pull/297 are open, with all five consumer/community PRs linked in COVERAGE.md. Reviewed the final shipment summary after these links were added; source and verification results are unchanged. + +Release sequencing correction: primary web #242 publishes the SDK/conformance set while retaining the already-published CLI runners. Draft follow-up [#243](https://github.com/napplet/web/pull/243) changes the private runner pins and CLI version only after conformance CLI 0.3.4 and Kehto CLI 0.5.5/Paja 0.21.0 publish. Fresh-cache CI correctly rejected nonexistent future versions, which prompted this split; no provisioning test was weakened. + +Reviewed the final split-release shipment summary: all eight implementation/consumer/follow-up PRs are open; original worktrees remain untouched. Required local source gates and assembled release-build controller proof remain as recorded above. diff --git a/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md index 84609403..ae5e8762 100644 --- a/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md +++ b/.planning/phases/163-nap-gamepad-across-repositories/COVERAGE.md @@ -38,10 +38,12 @@ Non-normative implementation evidence for phase 163. The living [NAP-GAMEPAD](ht ## Release ordering and limits -Kehto CLI 0.5.5 must be published before napplet CLI 0.9.1 selects it as the private Paja runner. The napplet package set includes conformance CLI 0.3.4 and SDK 0.30.0. Consumer ranges accept the currently published base release and the coordinated GAMEPAD release; installing the base release does not supply new SDK exports. CVM's sealed image remains subject to its existing candidate-promotion gates; this change does not promote a production image. +Publish Kehto CLI 0.5.5/Paja 0.21.0 and the primary conformance CLI 0.3.4 release before [CLI runner follow-up #243](https://github.com/napplet/web/pull/243). The follow-up is a draft stacked PR that prepares CLI 0.9.1; the primary release retains CLI 0.9.0 and its published private runners to avoid a self-publication cycle. The napplet package set includes conformance CLI 0.3.4 and SDK 0.30.0. Consumer ranges accept the currently published base release and the coordinated GAMEPAD release; installing the base release does not supply new SDK exports. CVM's sealed image remains subject to its existing candidate-promotion gates; this change does not promote a production image. Native Gamepad API replacement remains deferred: the GAMEPAD proposal permits an optional replacement while NIP-5D currently limits runtime injection to window.napplet. Required domain support and physical-access denial do not depend on that replacement. Reference mocks intentionally report unavailable controllers; live host isolation is demonstrated separately in Kehto Chromium tests with test-supplied host samples. Verified primary `pnpm build`, `pnpm type-check`, `pnpm -r test:unit`, docs build and slop 100/100. Compiled macOS CLI rendered the controller page and ran Paja help using locally assembled release builds with no host runtime/package runner on PATH. The first screenshot exposed the obsolete Paja dependency; the corrected source build renders Controller available. Boilerplate verify/conformance and eight size/theme contexts pass. Nappagochi verify, 88 tests and conformance pass. Migration check/build and 40 tests pass. CVM's 15 controller tests and slop pass; its existing protected-authority/admission baseline failures are disclosed in its PR. Kehto build/types/docs/slop, controller browser proof and baseline unit suite pass; core/NAP 0.34 tarballs also pass build/types and 2105 behavior tests while distribution-only guards reject temporary unpublished overrides. Those overrides are removed before shipment. -Opened consumer PRs: [boilerplate #7](https://github.com/napplet/boilerplate/pull/7), [Nappagochi #1](https://github.com/napplet/nappagochi/pull/1), [migrate #1](https://github.com/napplet/migrate/pull/1), [CVM #1](https://github.com/napplet/cvm/pull/1), [organization #1](https://github.com/napplet/.github/pull/1). Primary/runtime PR links are recorded after creation. Original dirty worktrees and unrelated changes remain untouched. +Opened consumer PRs: [boilerplate #7](https://github.com/napplet/boilerplate/pull/7), [Nappagochi #1](https://github.com/napplet/nappagochi/pull/1), [migrate #1](https://github.com/napplet/migrate/pull/1), [CVM #1](https://github.com/napplet/cvm/pull/1), [organization #1](https://github.com/napplet/.github/pull/1). Primary SDK/tooling [web #242](https://github.com/napplet/web/pull/242) and runtime [Kehto #297](https://github.com/kehto/web/pull/297) are open. Kehto final baseline verification passes all 2110 tests; build/types/docs/slop 100/100 pass. Original dirty worktrees and unrelated changes remain untouched. + +Release sequencing correction: primary web #242 publishes the SDK/conformance set while retaining the already-published CLI runners. Draft follow-up [#243](https://github.com/napplet/web/pull/243) changes the private runner pins and CLI version only after conformance CLI 0.3.4 and Kehto CLI 0.5.5/Paja 0.21.0 publish. Fresh-cache CI correctly rejected nonexistent future versions, which prompted this split; no provisioning test was weakened. From b6bbd7472c71fed97ba260f20d3f3f49746a010d Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:38:30 -0700 Subject: [PATCH 12/24] docs(release): explain the split publication order Co-Authored-By: Codex <codex@openai.com> --- packages/conformance-cli/CHANGELOG.md | 8 +------- packages/conformance/CHANGELOG.md | 8 +------- packages/core/CHANGELOG.md | 8 +------- packages/nap/CHANGELOG.md | 8 +------- packages/sdk/CHANGELOG.md | 8 +------- packages/shim/CHANGELOG.md | 8 +------- packages/vite-plugin/CHANGELOG.md | 8 +------- 7 files changed, 7 insertions(+), 49 deletions(-) diff --git a/packages/conformance-cli/CHANGELOG.md b/packages/conformance-cli/CHANGELOG.md index 1631f154..9a6c64b8 100644 --- a/packages/conformance-cli/CHANGELOG.md +++ b/packages/conformance-cli/CHANGELOG.md @@ -4,13 +4,7 @@ ### Patch Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. - Updated dependencies - @napplet/core@0.34.0 - @napplet/nap@0.34.0 diff --git a/packages/conformance/CHANGELOG.md b/packages/conformance/CHANGELOG.md index 029e299d..985b97f3 100644 --- a/packages/conformance/CHANGELOG.md +++ b/packages/conformance/CHANGELOG.md @@ -4,13 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index b8b0956e..d439fc06 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -4,13 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ## 0.33.0 diff --git a/packages/nap/CHANGELOG.md b/packages/nap/CHANGELOG.md index 7e85f8d9..9cf21a92 100644 --- a/packages/nap/CHANGELOG.md +++ b/packages/nap/CHANGELOG.md @@ -4,13 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index e3259c0a..b87712ee 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -4,13 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes diff --git a/packages/shim/CHANGELOG.md b/packages/shim/CHANGELOG.md index 70602f49..dfe80425 100644 --- a/packages/shim/CHANGELOG.md +++ b/packages/shim/CHANGELOG.md @@ -4,13 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes diff --git a/packages/vite-plugin/CHANGELOG.md b/packages/vite-plugin/CHANGELOG.md index 221badb0..7a819e43 100644 --- a/packages/vite-plugin/CHANGELOG.md +++ b/packages/vite-plugin/CHANGELOG.md @@ -4,13 +4,7 @@ ### Patch Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, - envelope validation, conformance injection, and optional manifest-domain - inference. Update the standalone CLI's private conformance/Paja runners to - GAMEPAD-capable releases. Publish Kehto CLI 0.5.5 before this release; - conformance CLI 0.3.4 is included in the same package set. Native Gamepad API - replacement remains deferred pending clarification of the proposal and NIP-5D - namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ## 0.16.0 From 42167e1c2685d6fd2ab936b9221611a13ec2a289 Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:09:36 -0500 Subject: [PATCH 13/24] fix(nap): isolate throwing gamepad onChange listeners A throwing listener stopped delivery to every later listener, including neutral focus-loss snapshots. Each listener now runs in its own try/catch and the error is rethrown from a microtask so it is still reported. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/nap/src/gamepad/shim.test.ts | 14 ++++++++++++++ packages/nap/src/gamepad/shim.ts | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/nap/src/gamepad/shim.test.ts b/packages/nap/src/gamepad/shim.test.ts index d85ab9a3..53c3eafe 100644 --- a/packages/nap/src/gamepad/shim.test.ts +++ b/packages/nap/src/gamepad/shim.test.ts @@ -85,6 +85,20 @@ describe('NAP-GAMEPAD cached binding', () => { third.close(); }); + it('keeps delivering to later listeners when one throws', async () => { + const shim = await import('./shim.js'); + const failure = new Error('listener failed'); + const reported = vi.fn(); + vi.stubGlobal('queueMicrotask', reported); + shim.onChange(() => { throw failure; }); + const later = vi.fn(); + shim.onChange(later); + expect(() => shim.handleGamepadMessage({ type: 'gamepad.state', ...focused })).not.toThrow(); + expect(later).toHaveBeenCalledExactlyOnceWith(focused); + expect(reported).toHaveBeenCalledOnce(); + expect(() => reported.mock.calls[0][0]()).toThrow(failure); + }); + it('defers listeners added during delivery until the next snapshot', async () => { const shim = await import('./shim.js'); const later = vi.fn(); diff --git a/packages/nap/src/gamepad/shim.ts b/packages/nap/src/gamepad/shim.ts index 414edc00..7a9e8bfa 100644 --- a/packages/nap/src/gamepad/shim.ts +++ b/packages/nap/src/gamepad/shim.ts @@ -59,7 +59,10 @@ export function handleGamepadMessage(message: { type: string; [key: string]: unk const snapshot = copyGamepadState(state); const recipients = [...handlers]; for (const entry of recipients) { - if (handlers.has(entry)) entry.handler(copyGamepadState(snapshot)); + if (!handlers.has(entry)) continue; + // A throwing listener must not starve later listeners of state (including neutral snapshots). + try { entry.handler(copyGamepadState(snapshot)); } + catch (error) { queueMicrotask(() => { throw error; }); } } } From 0a9cd581dbd0e99f3e612ca2a0cf240f98b3c293 Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:09:36 -0500 Subject: [PATCH 14/24] fix(conformance): inject gamepad state tools as literal source The runtime prelude embedded createGamepadStateTools.toString(), which breaks inside the opaque iframe once a bundler or coverage transform adds helper references (__name, cov_*). The prelude now uses a literal JS source kept equivalent to @napplet/nap/gamepad by a vector test, and isolates throwing onChange listeners the same way the nap binding does. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/conformance/src/run/boot.ts | 44 +++++++++++- packages/conformance/src/run/gamepad.test.ts | 70 +++++++++++++++++++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/packages/conformance/src/run/boot.ts b/packages/conformance/src/run/boot.ts index f5b9a814..c3a8afa9 100644 --- a/packages/conformance/src/run/boot.ts +++ b/packages/conformance/src/run/boot.ts @@ -14,7 +14,6 @@ * @packageDocumentation */ -import { createGamepadStateTools } from '@napplet/nap/gamepad'; import { NAP_DOMAINS } from '@napplet/core'; import { createReferenceShell, type RecordedEnvelope } from '../shell/reference-shell.js'; import type { BootObservation } from './context.js'; @@ -69,13 +68,48 @@ function escapeHtmlAttr(value: string): string { .replace(/>/g, '>'); } +/** + * NAP-GAMEPAD snapshot validation and copying, written as literal browser source. + * The prelude must not embed `Function.prototype.toString()` output: bundler and + * coverage transforms can add helper references that do not exist in the iframe. + * `gamepad.test.ts` keeps this source equivalent to `@napplet/nap/gamepad`. + */ +export const GAMEPAD_STATE_TOOLS_SOURCE = `(() => { + const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value); + const numberIn = (value, min, max) => typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max; + const isGamepadState = (value) => { + if (!record(value) || typeof value.available !== 'boolean' || typeof value.focused !== 'boolean' || !Array.isArray(value.pads)) return false; + if (value.available) { + if (value.reason !== undefined) return false; + } else if (value.focused || (value.reason !== 'blocked' && value.reason !== 'unavailable')) return false; + return Array.from(value.pads).every((pad) => { + if (pad === null) return true; + if (!record(pad) || !Number.isInteger(pad.index) || pad.index < 0 || typeof pad.id !== 'string' || typeof pad.mapping !== 'string' || pad.connected !== true) return false; + if (typeof pad.timestamp !== 'number' || !Number.isFinite(pad.timestamp) || !Array.isArray(pad.axes) || !Array.isArray(pad.buttons)) return false; + if (!value.focused && pad.timestamp !== 0) return false; + if (!Array.from(pad.axes).every((axis) => numberIn(axis, -1, 1) && (value.focused || axis === 0))) return false; + return Array.from(pad.buttons).every((button) => record(button) && numberIn(button.value, 0, 1) && typeof button.pressed === 'boolean' && typeof button.touched === 'boolean' && (value.focused || (button.value === 0 && !button.pressed && !button.touched))); + }); + }; + const copyGamepadState = (state) => { + const copy = { available: state.available, focused: state.focused }; + if (state.reason !== undefined) copy.reason = state.reason; + copy.pads = state.pads.map((pad) => pad === null ? null : { + index: pad.index, id: pad.id, mapping: pad.mapping, connected: pad.connected, + timestamp: pad.timestamp, axes: pad.axes.slice(), buttons: pad.buttons.map((button) => ({ value: button.value, pressed: button.pressed, touched: button.touched })), + }); + return copy; + }; + return { isGamepadState, copyGamepadState }; +})()`; + export function runtimePrelude(domains: readonly string[]): string { return ` (() => { const domains = ${JSON.stringify(domains)}; const napplet = {}; for (const domain of domains) napplet[domain] = {}; - const gamepadTools = (${createGamepadStateTools.toString()})(); + const gamepadTools = ${GAMEPAD_STATE_TOOLS_SOURCE}; let gamepadState = { available: true, focused: false, pads: [] }; const gamepadHandlers = new Set(); const pending = new Map(); @@ -102,7 +136,11 @@ export function runtimePrelude(domains: readonly string[]): string { if (napplet.gamepad && msg && msg.type === 'gamepad.state' && !('id' in msg) && gamepadTools.isGamepadState(msg)) { gamepadState = gamepadTools.copyGamepadState(msg); const recipients = Array.from(gamepadHandlers); - for (const entry of recipients) if (gamepadHandlers.has(entry)) entry.handler(gamepadTools.copyGamepadState(gamepadState)); + for (const entry of recipients) { + if (!gamepadHandlers.has(entry)) continue; + try { entry.handler(gamepadTools.copyGamepadState(gamepadState)); } + catch (error) { queueMicrotask(() => { throw error; }); } + } return; } if (!msg || typeof msg !== 'object' || typeof msg.id !== 'string') return; diff --git a/packages/conformance/src/run/gamepad.test.ts b/packages/conformance/src/run/gamepad.test.ts index f2d00d08..1f8c9474 100644 --- a/packages/conformance/src/run/gamepad.test.ts +++ b/packages/conformance/src/run/gamepad.test.ts @@ -1,7 +1,8 @@ // @vitest-environment happy-dom import { describe, expect, it, vi } from 'vitest'; import type { GamepadApi } from '@napplet/core'; -import { runtimePrelude } from './boot.js'; +import { createGamepadStateTools } from '@napplet/nap/gamepad'; +import { GAMEPAD_STATE_TOOLS_SOURCE, runtimePrelude } from './boot.js'; describe('serialized GAMEPAD conformance binding', () => { it('installs before subject code, accepts only parent state and isolates cached data', () => { @@ -31,4 +32,71 @@ describe('serialized GAMEPAD conformance binding', () => { delete (window as unknown as { napplet?: unknown }).napplet; } }); + + it('isolates a throwing onChange listener from later listeners', () => { + const previous = window.parent; + Object.defineProperty(window, 'parent', { configurable: true, value: { postMessage: vi.fn() } }); + const reported = vi.fn(); + vi.stubGlobal('queueMicrotask', reported); + try { + Function(runtimePrelude(['gamepad']))(); + const api = (window as unknown as { napplet: { gamepad: GamepadApi } }).napplet.gamepad; + api.onChange(() => { throw new Error('listener failed'); }); + const later = vi.fn(); + api.onChange(later); + const state = { available: false, focused: false, reason: 'blocked', pads: [] }; + window.dispatchEvent(new MessageEvent('message', { data: { type: 'gamepad.state', ...state }, source: window.parent })); + expect(later).toHaveBeenCalledExactlyOnceWith(state); + expect(reported).toHaveBeenCalledOnce(); + } finally { + vi.unstubAllGlobals(); + Object.defineProperty(window, 'parent', { configurable: true, value: previous }); + delete (window as unknown as { napplet?: unknown }).napplet; + } + }); +}); + +describe('literal GAMEPAD state tools source', () => { + // Connected and neutral examples from NAP-GAMEPAD Wire Protocol, proposal #108. + const pad = { + index: 1, id: 'Xbox Wireless Controller', mapping: 'standard', connected: true, + timestamp: 18235.4, axes: [0, -1, 0, 0], + buttons: [{ value: 1, pressed: true, touched: true }, { value: 0, pressed: false, touched: false }], + }; + const focused = { available: true, focused: true, pads: [null, pad] }; + const neutralPad = { ...pad, timestamp: 0, axes: [0, 0, 0, 0], buttons: [{ value: 0, pressed: false, touched: false }, { value: 0, pressed: false, touched: false }] }; + const neutral = { available: true, focused: false, pads: [null, neutralPad] }; + const vectors: unknown[] = [ + focused, neutral, null, [], 'state', + { available: false, reason: 'blocked', focused: false, pads: [] }, + { available: false, reason: 'unavailable', focused: false, pads: [] }, + { available: false, focused: false, pads: [] }, + { available: false, reason: 'other', focused: false, pads: [] }, + { available: false, reason: 'blocked', focused: true, pads: [] }, + { ...focused, reason: 'blocked' }, + { ...focused, focused: false }, + { ...focused, pads: [false] }, + { ...focused, pads: [{ ...pad, mapping: 'future-layout' }] }, + { ...focused, pads: [{ ...pad, index: -1 }] }, + { ...focused, pads: [{ ...pad, index: 1.5 }] }, + { ...focused, pads: [{ ...pad, connected: false }] }, + { ...focused, pads: [{ ...pad, timestamp: Infinity }] }, + ...[-2, 2, NaN, Infinity].map((axis) => ({ ...focused, pads: [{ ...pad, axes: [axis] }] })), + { ...focused, pads: [{ ...pad, buttons: [{ value: 2, pressed: true, touched: true }] }] }, + { ...focused, pads: [{ ...pad, buttons: [{ value: 1, pressed: 'yes', touched: true }] }] }, + { ...neutral, pads: [{ ...neutralPad, timestamp: 1 }] }, + { ...neutral, pads: [{ ...neutralPad, axes: [0.5] }] }, + { ...neutral, pads: [{ ...neutralPad, buttons: [{ value: 0, pressed: true, touched: false }] }] }, + ]; + + it('is self-contained and matches @napplet/nap/gamepad on spec vectors', () => { + const literal = Function(`return ${GAMEPAD_STATE_TOOLS_SOURCE};`)() as ReturnType<typeof createGamepadStateTools>; + const reference = createGamepadStateTools(); + for (const value of vectors) expect(literal.isGamepadState(value)).toBe(reference.isGamepadState(value)); + for (const state of [focused, neutral, { available: false, reason: 'blocked', focused: false, pads: [] }] as const) { + const copy = literal.copyGamepadState(state as never); + expect(copy).toEqual(reference.copyGamepadState(state as never)); + if (state.pads[1]) expect(copy.pads[1]!.buttons[0]).not.toBe(state.pads[1].buttons[0]); + } + }); }); From 0fdd28148ab061222a2779de94fcab8b6fd4c855 Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:15:07 -0500 Subject: [PATCH 15/24] fix(conformance): limit harness iframe policy to the GAMEPAD guidance NAP-GAMEPAD's Web Projection Guidance gives allow="gamepad 'none'" for napplet frames. No NIP-5D or NAP text covers hid or usb, so the harness no longer adds them to its loading model. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/conformance/src/run/boot.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/conformance/src/run/boot.ts b/packages/conformance/src/run/boot.ts index c3a8afa9..6de2075d 100644 --- a/packages/conformance/src/run/boot.ts +++ b/packages/conformance/src/run/boot.ts @@ -252,7 +252,9 @@ async function bootOnce( const shell = createReferenceShell(); const iframe = doc.createElement('iframe'); iframe.setAttribute('sandbox', opts.allowSameOrigin ? 'allow-scripts allow-same-origin' : 'allow-scripts'); - iframe.setAttribute('allow', "gamepad 'none'; hid 'none'; usb 'none'"); + // NAP-GAMEPAD Web Projection Guidance: deny the native API with Permissions-Policy. + // https://github.com/napplet/naps/pull/108 + iframe.setAttribute('allow', "gamepad 'none'"); iframe.setAttribute('aria-hidden', 'true'); iframe.style.position = 'absolute'; iframe.style.width = '1px'; From 7539027ad28fd7936d50b37ec9b5ffb100b37c18 Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:15:07 -0500 Subject: [PATCH 16/24] fix(gamepad): subscribe on first use instead of at install installGamepadShim subscribed immediately, so every napplet using the default domain set sent gamepad.subscribe and received controller state for its lifetime, and the conformance prelude's own subscribe was credited to the napplet under test. NAP-GAMEPAD lets the binding subscribe "before or on first use"; both bindings now wait for the first read or onChange. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/conformance/src/run/boot.ts | 18 +++++++++++++----- packages/conformance/src/run/gamepad.test.ts | 1 + packages/nap/src/gamepad/shim.test.ts | 10 +++++++++- packages/nap/src/gamepad/shim.ts | 6 ++---- packages/shim/src/gamepad.test.ts | 1 + 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/conformance/src/run/boot.ts b/packages/conformance/src/run/boot.ts index 6de2075d..8d0ff2b4 100644 --- a/packages/conformance/src/run/boot.ts +++ b/packages/conformance/src/run/boot.ts @@ -112,6 +112,14 @@ export function runtimePrelude(domains: readonly string[]): string { const gamepadTools = ${GAMEPAD_STATE_TOOLS_SOURCE}; let gamepadState = { available: true, focused: false, pads: [] }; const gamepadHandlers = new Set(); + // Subscribe on first use, like the nap binding, so napplets that never touch + // gamepad emit no gamepad traffic for checks to attribute to them. + let gamepadSubscribed = false; + const gamepadSubscribe = () => { + if (gamepadSubscribed) return; + gamepadSubscribed = true; + window.parent.postMessage({ type: 'gamepad.subscribe' }, '*'); + }; const pending = new Map(); let seq = 0; const nextId = () => { @@ -133,7 +141,7 @@ export function runtimePrelude(domains: readonly string[]): string { window.addEventListener('message', (event) => { if (event.source !== window.parent) return; const msg = event.data; - if (napplet.gamepad && msg && msg.type === 'gamepad.state' && !('id' in msg) && gamepadTools.isGamepadState(msg)) { + if (gamepadSubscribed && msg && msg.type === 'gamepad.state' && !('id' in msg) && gamepadTools.isGamepadState(msg)) { gamepadState = gamepadTools.copyGamepadState(msg); const recipients = Array.from(gamepadHandlers); for (const entry of recipients) { @@ -157,12 +165,13 @@ export function runtimePrelude(domains: readonly string[]): string { }); if (napplet.gamepad) { napplet.gamepad = { - get available() { return gamepadState.available; }, - get focused() { return gamepadState.focused; }, - getGamepads() { return gamepadTools.copyGamepadState(gamepadState).pads; }, + get available() { gamepadSubscribe(); return gamepadState.available; }, + get focused() { gamepadSubscribe(); return gamepadState.focused; }, + getGamepads() { gamepadSubscribe(); return gamepadTools.copyGamepadState(gamepadState).pads; }, onChange(handler) { const entry = { handler }; gamepadHandlers.add(entry); + gamepadSubscribe(); return { close() { gamepadHandlers.delete(entry); } }; }, }; @@ -200,7 +209,6 @@ export function runtimePrelude(domains: readonly string[]): string { }; } window.napplet = napplet; - if (napplet.gamepad) window.parent.postMessage({ type: 'gamepad.subscribe' }, '*'); const report = (reason) => { try { window.parent.postMessage({ type: '${INTERNAL_BOOT_ERROR}', reason: String(reason && reason.message || reason) }, '*'); diff --git a/packages/conformance/src/run/gamepad.test.ts b/packages/conformance/src/run/gamepad.test.ts index 1f8c9474..9a1fb5f4 100644 --- a/packages/conformance/src/run/gamepad.test.ts +++ b/packages/conformance/src/run/gamepad.test.ts @@ -12,6 +12,7 @@ describe('serialized GAMEPAD conformance binding', () => { try { Function(runtimePrelude(['gamepad']))(); const api = (window as unknown as { napplet: { gamepad: GamepadApi } }).napplet.gamepad; + expect(post).not.toHaveBeenCalled(); expect(api.available).toBe(true); expect(api.focused).toBe(false); expect(api.getGamepads()).toEqual([]); diff --git a/packages/nap/src/gamepad/shim.test.ts b/packages/nap/src/gamepad/shim.test.ts index 53c3eafe..08c089ab 100644 --- a/packages/nap/src/gamepad/shim.test.ts +++ b/packages/nap/src/gamepad/shim.test.ts @@ -24,10 +24,11 @@ beforeEach(() => { afterEach(() => vi.unstubAllGlobals()); describe('NAP-GAMEPAD cached binding', () => { - it('starts neutral and subscribes once with no correlation id', async () => { + it('starts neutral and subscribes once, on first use, with no correlation id', async () => { const shim = await import('./shim.js'); const cleanup = shim.installGamepadShim(); const duplicate = shim.installGamepadShim(); + expect(post).not.toHaveBeenCalled(); expect(shim.gamepad.available).toBe(true); expect(shim.gamepad.focused).toBe(false); expect(shim.getGamepads()).toEqual([]); @@ -85,6 +86,13 @@ describe('NAP-GAMEPAD cached binding', () => { third.close(); }); + it('sends nothing when installed and cleaned up without use', async () => { + const shim = await import('./shim.js'); + const cleanup = shim.installGamepadShim(); + cleanup(); + expect(post).not.toHaveBeenCalled(); + }); + it('keeps delivering to later listeners when one throws', async () => { const shim = await import('./shim.js'); const failure = new Error('listener failed'); diff --git a/packages/nap/src/gamepad/shim.ts b/packages/nap/src/gamepad/shim.ts index 7a9e8bfa..04a7a9e6 100644 --- a/packages/nap/src/gamepad/shim.ts +++ b/packages/nap/src/gamepad/shim.ts @@ -67,15 +67,13 @@ export function handleGamepadMessage(message: { type: string; [key: string]: unk } /** - * Subscribe when the runtime installs this domain; cleanup stops the session's input. - * @returns Idempotent cleanup that unsubscribes and clears the cached state. + * Install the domain; the binding subscribes on first use, so unused installs send nothing. + * @returns Idempotent cleanup that unsubscribes (if subscribed) and clears the cached state. * @example const cleanup = installGamepadShim(); */ export function installGamepadShim(): () => void { if (installed) return () => undefined; installed = true; - try { subscribe(); } - catch (error) { installed = false; throw error; } let closed = false; return () => { if (closed) return; diff --git a/packages/shim/src/gamepad.test.ts b/packages/shim/src/gamepad.test.ts index b3a6ef4c..2a00c137 100644 --- a/packages/shim/src/gamepad.test.ts +++ b/packages/shim/src/gamepad.test.ts @@ -15,6 +15,7 @@ describe('runtime-injected GAMEPAD', () => { try { const installed = installNappletRuntimePrelude({ domains: ['gamepad'] }); expect(Object.keys(installed)).toEqual(['gamepad']); + expect(post).not.toHaveBeenCalled(); expect(installed.gamepad!.getGamepads()).toEqual([]); expect(installed.gamepad!.focused).toBe(true); expect(sdkGamepad.focused).toBe(true); From 7bee8252c1942af4e05c6709a350f1305c4df28e Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:15:07 -0500 Subject: [PATCH 17/24] refactor(conformance): carry forbidden-field reasons on envelope specs Replace the domain === 'gamepad' special case in the shared validator with an optional forbiddenReason on EnvelopeSpec. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/conformance/src/validators/envelope-specs.ts | 4 ++-- packages/conformance/src/validators/envelope-types.ts | 2 ++ packages/conformance/src/validators/envelope-validation.ts | 2 +- packages/conformance/src/validators/envelope.test.ts | 1 + packages/conformance/src/validators/gamepad.test.ts | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/conformance/src/validators/envelope-specs.ts b/packages/conformance/src/validators/envelope-specs.ts index 3d90bddb..6abd5759 100644 --- a/packages/conformance/src/validators/envelope-specs.ts +++ b/packages/conformance/src/validators/envelope-specs.ts @@ -9,8 +9,8 @@ const ID = { id: 'string' } as const; export const ENVELOPE_SPECS: Record<string, EnvelopeSpec> = { // NAP-GAMEPAD Wire Protocol: all three messages are fire-and-forget, without ids. // https://github.com/napplet/naps/pull/108 - 'gamepad.subscribe': { dir: 'out', forbiddenFields: ['id'] }, - 'gamepad.unsubscribe': { dir: 'out', forbiddenFields: ['id'] }, + 'gamepad.subscribe': { dir: 'out', forbiddenFields: ['id'], forbiddenReason: 'NAP-GAMEPAD fire-and-forget envelopes do not carry it' }, + 'gamepad.unsubscribe': { dir: 'out', forbiddenFields: ['id'], forbiddenReason: 'NAP-GAMEPAD fire-and-forget envelopes do not carry it' }, 'gamepad.state': { dir: 'in', fields: { available: 'boolean', focused: 'boolean', pads: 'array' } }, // Optional NAP-SHELL environment, independent of other domains. 'shell.ready': { dir: 'out' }, diff --git a/packages/conformance/src/validators/envelope-types.ts b/packages/conformance/src/validators/envelope-types.ts index 41dbee08..16497c5d 100644 --- a/packages/conformance/src/validators/envelope-types.ts +++ b/packages/conformance/src/validators/envelope-types.ts @@ -16,6 +16,8 @@ export interface EnvelopeSpec { fields?: Record<string, FieldKind>; /** Fields a napplet-emitted carrier must never supply. */ forbiddenFields?: readonly string[]; + /** Why `forbiddenFields` are rejected; defaults to "runtime-derived". */ + forbiddenReason?: string; } /** A single problem found while validating an envelope. */ diff --git a/packages/conformance/src/validators/envelope-validation.ts b/packages/conformance/src/validators/envelope-validation.ts index 1173b7f2..819159d5 100644 --- a/packages/conformance/src/validators/envelope-validation.ts +++ b/packages/conformance/src/validators/envelope-validation.ts @@ -210,7 +210,7 @@ export function validateEnvelope(message: unknown): EnvelopeVerdict { if (field in record) { errors.push({ code: 'forbidden-field', - message: domain === 'gamepad' ? `NAP-GAMEPAD fire-and-forget envelopes do not carry "${field}"` : `Field "${field}" must be runtime-derived and cannot be emitted by a napplet`, + message: `Field "${field}" cannot be emitted by a napplet: ${spec.forbiddenReason ?? 'it must be runtime-derived'}`, field, }); } diff --git a/packages/conformance/src/validators/envelope.test.ts b/packages/conformance/src/validators/envelope.test.ts index 20fb96ff..e2de28a4 100644 --- a/packages/conformance/src/validators/envelope.test.ts +++ b/packages/conformance/src/validators/envelope.test.ts @@ -73,6 +73,7 @@ describe('validateEnvelope — outbound field checks', () => { expect(forged.errors).toContainEqual(expect.objectContaining({ code: 'forbidden-field', field: 'sender', + message: expect.stringContaining('runtime-derived'), })); }); diff --git a/packages/conformance/src/validators/gamepad.test.ts b/packages/conformance/src/validators/gamepad.test.ts index 62fe6026..dabf0f65 100644 --- a/packages/conformance/src/validators/gamepad.test.ts +++ b/packages/conformance/src/validators/gamepad.test.ts @@ -8,7 +8,7 @@ describe('NAP-GAMEPAD Wire Protocol', () => { expect(validateEnvelope({ type: 'gamepad.getGamepads', id: 'invented' }).ok).toBe(false); expect(validateEnvelope({ type: 'gamepad.subscribe' }).ok).toBe(true); expect(validateEnvelope({ type: 'gamepad.unsubscribe' }).ok).toBe(true); - expect(validateEnvelope({ type: 'gamepad.subscribe', id: 'not-defined' }).errors).toContainEqual(expect.objectContaining({ code: 'forbidden-field', field: 'id' })); + expect(validateEnvelope({ type: 'gamepad.subscribe', id: 'not-defined' }).errors).toContainEqual(expect.objectContaining({ code: 'forbidden-field', field: 'id', message: expect.stringContaining('NAP-GAMEPAD fire-and-forget') })); expect(validateEnvelope({ type: 'gamepad.state', available: true, focused: false, pads: [] }).errors[0].code).toBe('inbound-type-emitted'); }); From 787ae14b10fe15d8c4a49422d151abbc6c06a6ec Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:15:07 -0500 Subject: [PATCH 18/24] docs(gamepad): stop closing the example listener immediately The snippet closed its onChange subscription in the same synchronous block, so the handler could never run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- README.md | 3 +-- apps/docs/naps/gamepad.md | 3 +-- apps/docs/packages/nap.md | 3 +-- apps/docs/packages/sdk.md | 3 +-- packages/nap/README.md | 3 +-- packages/sdk/README.md | 3 +-- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 64d2ba17..90a61c15 100644 --- a/README.md +++ b/README.md @@ -203,8 +203,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` diff --git a/apps/docs/naps/gamepad.md b/apps/docs/naps/gamepad.md index 5bc023c8..11204436 100644 --- a/apps/docs/naps/gamepad.md +++ b/apps/docs/naps/gamepad.md @@ -14,8 +14,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` diff --git a/apps/docs/packages/nap.md b/apps/docs/packages/nap.md index a998990b..901e19a0 100644 --- a/apps/docs/packages/nap.md +++ b/apps/docs/packages/nap.md @@ -167,8 +167,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` diff --git a/apps/docs/packages/sdk.md b/apps/docs/packages/sdk.md index c4bd47e2..131d7508 100644 --- a/apps/docs/packages/sdk.md +++ b/apps/docs/packages/sdk.md @@ -168,8 +168,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` diff --git a/packages/nap/README.md b/packages/nap/README.md index 8e4668dd..f42dd93e 100644 --- a/packages/nap/README.md +++ b/packages/nap/README.md @@ -295,8 +295,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 5c796fc0..cc970f8e 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -522,8 +522,7 @@ if (window.napplet?.gamepad) { if (pad) console.log(pad.index, pad.axes); } }); - // Close this listener when its owning view is disposed. - subscription.close(); + // Call subscription.close() when the owning view is disposed, not right away. } ``` From 44d663a14af0f91a3797614c76e5c3d684cec52e Mon Sep 17 00:00:00 2001 From: hzrd149 <github@hzrd149.com> Date: Mon, 12 Oct 2026 15:15:08 -0500 Subject: [PATCH 19/24] docs(changelog): restore unwrapped history and clean new entries The release step hard-wrapped every existing CHANGELOG entry and left runs of spaces in the new ones. Earlier entries now match main and only the new version sections are added. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --- packages/conformance-cli/CHANGELOG.md | 143 ++---- packages/conformance/CHANGELOG.md | 292 ++++-------- packages/core/CHANGELOG.md | 585 +++++++----------------- packages/nap/CHANGELOG.md | 627 ++++++++------------------ packages/sdk/CHANGELOG.md | 551 +++++++--------------- packages/shim/CHANGELOG.md | 601 ++++++++---------------- packages/vite-plugin/CHANGELOG.md | 351 ++++---------- 7 files changed, 911 insertions(+), 2239 deletions(-) diff --git a/packages/conformance-cli/CHANGELOG.md b/packages/conformance-cli/CHANGELOG.md index 9a6c64b8..c81ac14d 100644 --- a/packages/conformance-cli/CHANGELOG.md +++ b/packages/conformance-cli/CHANGELOG.md @@ -4,7 +4,7 @@ ### Patch Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. - Updated dependencies - @napplet/core@0.34.0 - @napplet/nap@0.34.0 @@ -14,16 +14,13 @@ ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto - Paja preview. Install Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. ## 0.3.2 ### Patch Changes -- Add a screenshot command that captures a napplet iframe from a running preview - shell, with configurable dimensions and readiness controls and protection - against overwriting files. +- Add a screenshot command that captures a napplet iframe from a running preview shell, with configurable dimensions and readiness controls and protection against overwriting files. ## 0.3.1 @@ -36,18 +33,9 @@ ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 - and 35129. Writers emit one direct artifact `x` hash, description content, - required/optional `R`/`O` domains and independent `z`/`i` advertisements. - Readers verify current events and raw artifact bytes; legacy events require - migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy - serialization. Add offline signed-event migration previews without altering - source events or publishing. Vite builds default to a self-contained artifact; - compatibility helper and sidecar names remain available. See the event - migration guide for package cutoffs, immutable pointers and shell ACL/storage - implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. ### Patch Changes @@ -80,10 +68,10 @@ ### Patch Changes -- dd7b3a7: Update shipped generator, conformance CLI, and agent-skill guidance - for queryless convention identities, optional same-tag event kinds, - authoritative intent URIs, acceptance-before-delivery, runtime-attested - sender, and carrier-neutral delivery without a public NAP-INC dependency. +- dd7b3a7: Update shipped generator, conformance CLI, and agent-skill guidance for + queryless convention identities, optional same-tag event kinds, authoritative + intent URIs, acceptance-before-delivery, runtime-attested sender, and + carrier-neutral delivery without a public NAP-INC dependency. - Updated dependencies [dd7b3a7] - @napplet/conformance@0.14.0 @@ -101,13 +89,9 @@ - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, - and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check - accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level - authoring flow so generated napplets route bytes, state, relays, signing, - and links through shell-owned NAPs. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. - Updated dependencies [dd2b0bc] - Updated dependencies [f63f8af] @@ -147,9 +131,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/conformance@0.9.1 @@ -208,47 +192,22 @@ ### Patch Changes -- Resolve a napplet target to its built `dist/index.html` before a sibling - source `index.html`. +- Resolve a napplet target to its built `dist/index.html` before a sibling source `index.html`. - `napplet-conformance --ui .` (and `napplet-conformance .`) on a Vite **project - root** previously served the source `index.html` — a Vite entry referencing - `/src/*.ts` that cannot execute in the opaque-origin `srcdoc` sandbox — - instead of the built single-file `dist/index.html`. The live UI runner then - reported a spurious NON-CONFORMANT (no `napplet-type` meta, `window.napplet` - never installed, no `shell.ready`) because it was testing the wrong, - never-booting artifact. + `napplet-conformance --ui .` (and `napplet-conformance .`) on a Vite **project root** previously served the source `index.html` — a Vite entry referencing `/src/*.ts` that cannot execute in the opaque-origin `srcdoc` sandbox — instead of the built single-file `dist/index.html`. The live UI runner then reported a spurious NON-CONFORMANT (no `napplet-type` meta, `window.napplet` never installed, no `shell.ready`) because it was testing the wrong, never-booting artifact. - `resolveNappletDir` now prefers `<target>/dist/index.html` over - `<target>/index.html`, so pointing the CLI at a project root tests its build - output. A plain built directory (only `index.html`, no `dist/`) still resolves - as before, and `--ui . --exec "vite build --watch"` now serves the built - single-file while running the build in the project root. + `resolveNappletDir` now prefers `<target>/dist/index.html` over `<target>/index.html`, so pointing the CLI at a project root tests its build output. A plain built directory (only `index.html`, no `dist/`) still resolves as before, and `--ui . --exec "vite build --watch"` now serves the built single-file while running the build in the project root. ## 0.2.1 ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets - ([napplet/web#53](https://github.com/napplet/web/issues/53)). - - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via - `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an - opaque origin). There is no served origin from which to fetch an external - `<script src>`, so a napplet's executable JS **must** be inline. The toolchain - was enforcing the opposite under an invented "shell-as-CSP-authority / - `script-src 'self'`" model that NIP-5D never defines and that was justified by - NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its - unconditional `closeBundle` call. The build no longer aborts on inline - scripts; `artifactMode: 'single-file'` still folds local script/style assets - into `index.html` and now preserves any pre-existing inline scripts - verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): - removed the `manifest/no-inline-scripts` check, the `inline-script` error - code, and the `findInlineScripts` export. A conformant napplet that carries - inline JS no longer fails conformance. +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). + + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. - Updated dependencies - @napplet/conformance@0.2.1 @@ -257,31 +216,12 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -292,23 +232,22 @@ ### Patch Changes -- b705d67: Rebuild against `@napplet/conformance@0.1.1`. The CLI bundles the - engine into `cli.js` (and the host UI bundle), so it must be republished to - pick up the manifest-parsing fixes (HTML-entity-decoded config schema; - aggregate-hash check removed). No CLI behavior change of its own. +- b705d67: Rebuild against `@napplet/conformance@0.1.1`. The CLI bundles the engine into + `cli.js` (and the host UI bundle), so it must be republished to pick up the + manifest-parsing fixes (HTML-entity-decoded config schema; aggregate-hash check + removed). No CLI behavior change of its own. ## 0.1.0 ### Minor Changes -- c8d0198: Initial release. Headless `napplet-conformance` runner that drives - the `@napplet/conformance` engine against a napplet in real Chromium via - Playwright: serves the built napplet (and the engine bundle) on loopback with - permissive CORS, boots it into a `sandbox="allow-scripts"` iframe, records - every emitted envelope, runs a graceful-degradation pass, then prints a - `pretty`/`json`/`junit` report and exits non-zero on any error-severity - failure. Wire it up as `test:conformance`. npm-only (Playwright dependency); - the pure engine ships to both npm and JSR. +- c8d0198: Initial release. Headless `napplet-conformance` runner that drives the + `@napplet/conformance` engine against a napplet in real Chromium via Playwright: + serves the built napplet (and the engine bundle) on loopback with permissive CORS, + boots it into a `sandbox="allow-scripts"` iframe, records every emitted envelope, + runs a graceful-degradation pass, then prints a `pretty`/`json`/`junit` report and + exits non-zero on any error-severity failure. Wire it up as `test:conformance`. + npm-only (Playwright dependency); the pure engine ships to both npm and JSR. ### Patch Changes diff --git a/packages/conformance/CHANGELOG.md b/packages/conformance/CHANGELOG.md index 985b97f3..325d6bb1 100644 --- a/packages/conformance/CHANGELOG.md +++ b/packages/conformance/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes @@ -16,23 +16,11 @@ ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative - convention URIs and naddr recommendations, expose acceptance results and - buffered onDelivery callbacks, and discover opaque catalog identifiers with - parameter contracts. Restore the optional NAP-SHELL environment API - independently of other domain calls. Align INC provenance with authenticated - endpoint identifiers and deliver theme/identity updates only for exposed - domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, - options) and open(uri, options), from handled/windowId to acceptance, and from - candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK - helpers remain exported with the URI signatures. CLI helper extraction - preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. -- Validate the optional `handler`, `behavior`, and `handlerHint` fields of - `intent.invoke` requests: `handlerHint.address` must be a `35129:<pubkey>:<d>` - coordinate and `relays` an array of strings. +- Validate the optional `handler`, `behavior`, and `handlerHint` fields of `intent.invoke` requests: `handlerHint.address` must be a `35129:<pubkey>:<d>` coordinate and `relays` an array of strings. ### Patch Changes @@ -44,35 +32,19 @@ ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 - and 35129. Writers emit one direct artifact `x` hash, description content, - required/optional `R`/`O` domains and independent `z`/`i` advertisements. - Readers verify current events and raw artifact bytes; legacy events require - migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy - serialization. Add offline signed-event migration previews without altering - source events or publishing. Vite builds default to a self-contained artifact; - compatibility helper and sidecar names remain available. See the event - migration guide for package cutoffs, immutable pointers and shell ACL/storage - implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. ### Patch Changes -- Report domains outside the toolchain registry through the advisory - `manifest/requires-known` check for current `R`/`O` tags; warnings never fail - a run (#223). Prefixed, whitespace-separated, and `domain.action` values still - fail the bare-domain check. No lowercase-only naming restriction is imposed - beyond the live NIP-5D contract. +- Report domains outside the toolchain registry through the advisory `manifest/requires-known` check for current `R`/`O` tags; warnings never fail a run (#223). Prefixed, whitespace-separated, and `domain.action` values still fail the bare-domain check. No lowercase-only naming restriction is imposed beyond the live NIP-5D contract. ## 0.17.0 ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries - optional advisory Blossom `servers`, `bytesMany` accepts per-resource request - objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits - the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. ### Patch Changes @@ -84,8 +56,7 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload - conventions, and require structured invoke results. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. - Updated dependencies [b3f0007] - @napplet/nap@0.31.2 @@ -93,8 +64,7 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol - behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -103,25 +73,13 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, - `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, - shim and SDK helpers, injected `window.napplet.fs` methods, and conformance - coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -136,22 +94,11 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ### Patch Changes @@ -163,12 +110,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes @@ -180,10 +127,7 @@ ### Minor Changes -- 3dbced2: Validate NIP-5D manifest events instead of private HTML manifest - metadata in the default conformance path. Adds `validateManifestEvent` and - napplet manifest kind exports while keeping `validateManifest(html)` as an - HTML-only compatibility wrapper. +- 3dbced2: Validate NIP-5D manifest events instead of private HTML manifest metadata in the default conformance path. Adds `validateManifestEvent` and napplet manifest kind exports while keeping `validateManifest(html)` as an HTML-only compatibility wrapper. ## 0.12.6 @@ -197,15 +141,13 @@ ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, - conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. ## 0.12.4 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API - types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -216,16 +158,12 @@ - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, - and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check - accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level - authoring flow so generated napplets route bytes, state, relays, signing, - and links through shell-owned NAPs. - -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` - wire types, helpers, injected runtime surface, and conformance coverage. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. + +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, + helpers, injected runtime surface, and conformance coverage. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -240,8 +178,7 @@ ### Patch Changes -- 5371643: Expose `resource.bytes` in the conformance reference runtime and - support deterministic `data:` URL bytes. +- 5371643: Expose `resource.bytes` in the conformance reference runtime and support deterministic `data:` URL bytes. ## 0.12.1 @@ -255,9 +192,7 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw - read events now use `RelayEventResult` with optional `sidecar.resources` and - `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -269,10 +204,7 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / - `count.query.result` envelope types, `window.napplet.count.query(...)`, - `@napplet/nap/count` subpaths, SDK exports, and conformance - validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. ### Patch Changes @@ -284,15 +216,9 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime - API, typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. ### Patch Changes @@ -313,9 +239,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/core@0.23.0 @@ -325,9 +251,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed - payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK - wrappers, and conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, + `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and + conformance validator coverage. ### Patch Changes @@ -339,9 +265,8 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, - including wire types, shim and SDK helpers, core global types, and conformance - coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including + wire types, shim and SDK helpers, core global types, and conformance coverage. ### Patch Changes @@ -353,9 +278,7 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` - subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and - reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -380,9 +303,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell - owns identity, consent, event construction, signing, publishing, relay access, - and NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns + identity, consent, event construction, signing, publishing, relay access, and + NIP-19 handling. - Updated dependencies [61431b7] - Updated dependencies [086f36e] @@ -393,9 +316,7 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` - subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and - reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -409,10 +330,10 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core - types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, + `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -427,10 +348,9 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers - from `@napplet/sdk`, and teaches conformance validation/reference-shell - handling about `serial.open`, `serial.write`, `serial.close`, and - `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from + `@napplet/sdk`, and teaches conformance validation/reference-shell handling + about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. ### Patch Changes @@ -450,11 +370,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md - rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` - integer was re-homed into NAP-SHELL's `shell.init` environment during the - NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The - `shell.init` wire shape is now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a + deferred NAP's surface must not outlive the NAP). The opaque `class` integer was + re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, + but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is + now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -464,8 +384,7 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option - (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). ### Patch Changes @@ -485,56 +404,23 @@ ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets - ([napplet/web#53](https://github.com/napplet/web/issues/53)). +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via - `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an - opaque origin). There is no served origin from which to fetch an external - `<script src>`, so a napplet's executable JS **must** be inline. The toolchain - was enforcing the opposite under an invented "shell-as-CSP-authority / - `script-src 'self'`" model that NIP-5D never defines and that was justified by - NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its - unconditional `closeBundle` call. The build no longer aborts on inline - scripts; `artifactMode: 'single-file'` still folds local script/style assets - into `index.html` and now preserves any pre-existing inline scripts - verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): - removed the `manifest/no-inline-scripts` check, the `inline-script` error - code, and the `findInlineScripts` export. A conformant napplet that carries - inline JS no longer fails conformance. + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. ## 0.2.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -546,26 +432,22 @@ ### Patch Changes -- 06cfecf: Manifest validator fixes found by dogfooding against a real - boilerplate build: - - Decode HTML entities when reading `<meta>` content, so an escaped config - schema (the build plugin serializes JSON with `"`) parses the same way - a real `getAttribute('content')` would — previously it failed as "not valid - JSON". +- 06cfecf: Manifest validator fixes found by dogfooding against a real boilerplate build: + - Decode HTML entities when reading `<meta>` content, so an escaped config schema + (the build plugin serializes JSON with `"`) parses the same way a real + `getAttribute('content')` would — previously it failed as "not valid JSON". - Remove the `napplet-aggregate-hash` check entirely. A napplet cannot contain - its own aggregate hash, and the tag is not a spec artifact; the shell - computes the hash from the served files. The catalog drops to 13 checks. + its own aggregate hash, and the tag is not a spec artifact; the shell computes + the hash from the served files. The catalog drops to 13 checks. ## 0.1.0 ### Minor Changes -- c8d0198: Initial release. Framework-agnostic napplet protocol conformance - engine: hand-written per-NAP runtime envelope validators across all 16 NAP - domains (drift-guarded against `@napplet/nap` source), a manifest/meta - validator, a scriptable reference mock shell that records emitted envelopes - and answers the `shell.ready`→`shell.init` handshake, a browser-safe boot - harness (`bootAndCollect`), the zero-config conformance check catalog - (`runConformance`), and `toPretty`/`toJson`/`toJUnit` reporters. Browser-safe - and dependency-light so it can be reused by both the headless CLI and the web - runtime. +- c8d0198: Initial release. Framework-agnostic napplet protocol conformance engine: hand-written + per-NAP runtime envelope validators across all 16 NAP domains (drift-guarded against + `@napplet/nap` source), a manifest/meta validator, a scriptable reference mock shell + that records emitted envelopes and answers the `shell.ready`→`shell.init` handshake, a + browser-safe boot harness (`bootAndCollect`), the zero-config conformance check + catalog (`runConformance`), and `toPretty`/`toJson`/`toJUnit` reporters. Browser-safe + and dependency-light so it can be reused by both the headless CLI and the web runtime. diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d439fc06..ece21c44 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -4,65 +4,39 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ## 0.33.0 ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative - convention URIs and naddr recommendations, expose acceptance results and - buffered onDelivery callbacks, and discover opaque catalog identifiers with - parameter contracts. Restore the optional NAP-SHELL environment API - independently of other domain calls. Align INC provenance with authenticated - endpoint identifiers and deliver theme/identity updates only for exposed - domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, - options) and open(uri, options), from handled/windowId to acceptance, and from - candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK - helpers remain exported with the URI signatures. CLI helper extraction - preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ## 0.32.0 ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries - optional advisory Blossom `servers`, `bytesMany` accepts per-resource request - objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits - the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. ## 0.31.1 ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol - behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. ## 0.31.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, - `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, - shim and SDK helpers, injected `window.napplet.fs` methods, and conformance - coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. ## 0.30.0 @@ -70,33 +44,22 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ## 0.29.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ## 0.28.0 @@ -110,58 +73,43 @@ ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload - and related public media context types. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the - relay-owned `RelayEventResult` wrapper as `result` instead of split `event` - and `relays` fields. -- d88d674: Align NAP-DM result message types with success-or-error wire - envelopes and export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` - wire types, helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` - resource scheme. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related + public media context types. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned + `RelayEventResult` wrapper as `result` instead of split `event` and `relays` + fields. +- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and + export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, + helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource + scheme. ## 0.27.0 ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible - routing and lifecycle controls. `OutboxStrategy` is no longer exported, - `strategy` is no longer accepted on outbox option objects, and - `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is - represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. ## 0.26.0 ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw - read events now use `RelayEventResult` with optional `sidecar.resources` and - `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ## 0.25.0 ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / - `count.query.result` envelope types, `window.napplet.count.query(...)`, - `@napplet/nap/count` subpaths, SDK exports, and conformance - validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. ## 0.24.0 ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime - API, typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. ## 0.23.0 @@ -174,33 +122,30 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. ## 0.22.0 ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed - payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK - wrappers, and conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, + `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and + conformance validator coverage. ## 0.21.0 ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, - including wire types, shim and SDK helpers, core global types, and conformance - coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including + wire types, shim and SDK helpers, core global types, and conformance coverage. ## 0.20.0 ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` - subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and - reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. ## 0.19.0 @@ -209,9 +154,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell - owns identity, consent, event construction, signing, publishing, relay access, - and NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns + identity, consent, event construction, signing, publishing, relay access, and + NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -225,9 +170,7 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` - subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and - reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. ## 0.17.0 @@ -235,13 +178,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core - types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, + `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. ## 0.16.0 @@ -252,35 +195,30 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers - from `@napplet/sdk`, and teaches conformance validation/reference-shell - handling about `serial.open`, `serial.write`, `serial.close`, and - `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from + `@napplet/sdk`, and teaches conformance validation/reference-shell handling + about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. ## 0.15.0 ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no - longer fail silently. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a - JSON envelope through `postMessage`. Framework reactive values — Svelte 5 - `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't - structured-cloneable, so `postMessage` threw a `DataCloneError` that got - silently swallowed in async paths; the envelope simply never crossed - (napplet/web#67). + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON + envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue + `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so + `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; + the envelope simply never crossed (napplet/web#67). - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, - `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / - `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only - on a `DataCloneError`, snapshots the envelope (stripping reactive proxies - while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — - warning once. `'strict'` throws a loud, actionable, synchronous error - instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through - this boundary instead of calling `window.parent.postMessage(msg, '*')` - directly. + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, + `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` + types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, + snapshots the envelope (stripping reactive proxies while preserving binary, + `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a + loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this + boundary instead of calling `window.parent.postMessage(msg, '*')` directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -289,11 +227,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md - rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` - integer was re-homed into NAP-SHELL's `shell.init` environment during the - NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The - `shell.init` wire shape is now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a + deferred NAP's surface must not outlive the NAP). The opaque `class` integer was + re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, + but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is + now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -303,131 +241,95 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option - (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). ## 0.13.0 ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, - [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an - optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. - Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface - (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets - `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s - `storage.instance.*` and as `storageInstance*` helpers in - `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type - and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** - `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. ## 0.12.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ## 0.11.0 ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the - shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a - napplet had no way to fetch the theme through `window.napplet`. It now mirrors - the read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet + had no way to fetch the theme through `window.napplet`. It now mirrors the + read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed - `theme.changed` updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, - plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in - `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` + updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus + `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. ## 0.10.0 ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke - another napplet by its role (archetype) through `window.napplet.intent` - (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it - directly. The shell resolves the archetype to an installed napplet (honoring - the user's default-handler preference), creates or focuses its window, and - delivers the payload using the named NAP-N protocol. Routing (`archetype`) and - payload format (`protocol`) are orthogonal; the shell owns resolution, default - handling, window lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another + napplet by its role (archetype) through `window.napplet.intent` (`invoke`, + `open`, `available`, `handlers`, `onChanged`) without addressing it directly. + The shell resolves the archetype to an installed napplet (honoring the user's + default-handler preference), creates or focuses its window, and delivers the + payload using the named NAP-N protocol. Routing (`archetype`) and payload format + (`protocol`) are orthogonal; the shell owns resolution, default handling, window + lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, - `intent` surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) - with request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` - envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type - re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` + surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with + request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. ## 0.9.0 ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now - upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) - while the runtime selects the storage server, signs the rail authorization - (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and - returns a stable URL plus NIP-94 integrity metadata. Napplets never receive - signing keys, server credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes + through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the + runtime selects the storage server, signs the rail authorization (NIP-98 for + NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable + URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server + credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, - `upload` surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) - with request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` - envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type - re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` + surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with + request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. ## 0.8.0 ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover - ContextVM servers and run MCP operations (`tools/list`, `tools/call`, - `resources/list`, `resources/read`) over Nostr through the shell via - `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay - routing, signing, encryption, JSON-RPC correlation, initialization, policy, - and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM + servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, + `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while + the runtime owns all ContextVM transport — relay routing, signing, encryption, + JSON-RPC correlation, initialization, policy, and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -435,19 +337,17 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, - subscribe, publish, and resolve relay plans through `window.napplet.outbox` - while the runtime owns relay discovery, NIP-65 routing, fallback, - deduplication, signature validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, + publish, and resolve relay plans through `window.napplet.outbox` while the + runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature + validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, - `outbox` surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) - with request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` - envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type - re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` + surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with + request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. ## 0.7.0 @@ -455,19 +355,13 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and - TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and - `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over - the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for - downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. ## 0.6.0 ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including - `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` - identifiers. +- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. ## 0.5.0 @@ -479,212 +373,79 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / - `identity.onChanged`, make `getPublicKey()` represent signed-out state with - `""`, remove the old identity decrypt surface and rumor types, and normalize - shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. ## 0.3.1 ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene - changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. ## 0.3.0 ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP - emission from the vite-plugin (build time) to the host shell (runtime) for - every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative - abstract security posture via `class.assigned` wire) and NAP-CONNECT - (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The - option is retained as an `@deprecated` accept-but-warn shim for one release - cycle — the plugin emits one `console.warn` per build per config-resolved if - a `strictCsp` value is present, and the option is ignored. Production builds - no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is - now the sole authority on runtime CSP for every napplet, per - `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a - future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for - v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` - scans `dist/index.html` and throws a diagnostic when any `<script>` element - lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so - inline scripts would be blocked at runtime anyway; failing at build time - surfaces the problem before deployment. The allow-list exempts - `application/json`, `application/ld+json`, `importmap`, and - `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on - `NamespacedCapability`. The template-literal type still accepts it so - existing code type-checks; capability advertisement is superseded by - `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as - `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required - origins. Each origin is validated via the shared `normalizeConnectOrigin()` - validator from `@napplet/nap/connect`, emitted as a signed - `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via - a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → - UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). - Dev-mode-only `<meta name="napplet-connect-requires">` is injected for - shell-less `vite serve` previews; the shell-authoritative - `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 - entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` - (mirrors `resource` block) and `class?: number` (optional — undefined until - `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `NappletConnect` interface, pure - `normalizeConnectOrigin(origin)` validator (shared source-of-truth for - build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests - pass), and `installConnectShim()` which reads - `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type - (`{ type: 'class.assigned'; id: string; class: number }`), - `handleClassMessage` dispatcher handler, and `installClassShim()` which - mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** - defaults to `{granted: false, origins: []}` (never `undefined`); - **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). - Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the - `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + - the `class` types namespace — parallel to the existing `resource` - re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte - fetches — it works in every class posture. Reach for NAP-CONNECT only when - the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, - WebSocket, Server-Sent Events, streaming responses, custom request headers, - long-lived connections, or third-party libraries that call `fetch`/open - sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: - `class === 2` iff `connect.granted === true` at `class.assigned` send time. - See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full - scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward - from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn - once per build if you leave it. (Required when REMOVE-STRICTCSP lands; - optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move - their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the - plugin's new `connect: string[]` option. See - `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your - host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` - before reading `window.napplet.class` / `window.napplet.connect` — both - gracefully degrade (`undefined` / `{granted: false, origins: []}` - respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds - `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — - shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt - primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext - access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict - baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet - main branch; it has been renumbered to v0.30.0 during a post-merge version - split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority - milestone (feat/strict-model, shipped 2026-04-21). This changeset describes - shipped behavior; the underlying commits already exist in main (Phases 135-138 - of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, - `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope - triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, - `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, - `impersonation`, `unsupported-encryption`, `policy-denied`), - `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), - `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` - method type. Zero new runtime symbols in the types-only entry point — pure - type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding - (request-correlation by id, reusing existing `pendingRequests` - infrastructure), the `identity.decrypt.result` / `.error` handler branches - (added to `handleIdentityMessage` with discriminated-union exhaustiveness - via `assertNever`), and the bare-name `identityDecrypt(event)` helper. - Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so - `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central - shim — no new install function; the addition is two surgical edits on - `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central - `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new - identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports - — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full - conformance table (4 shell MUSTs: class-gating, outer-sig-verify, - impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, - outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy - floor), 8-code error vocabulary, and Security Considerations subsection. - Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST - `(dTag, aggregateHash)` violation-correlation row + Security Considerations - subsection distinguishing observability from enforcement. Bundled into the - same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a - `### NIP-07 Extension Injection Residual` subsection documenting the - `all_frames: true` content-script injection vector, nonce-based `script-src` - legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical - lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and - `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as - the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero - occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably - blocks legacy `<script>`-tag content-script injection AND fires - `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and - `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member - triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. - Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 - browser extension injects `window.nostr` into the iframe via a content - script (architecturally unavoidable per WebExtension spec), shells MUST NOT - _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell - is free to use its own NIP-07 extension internally to fulfill - signing/encryption duties; what is forbidden is exposing `window.nostr` to - the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) - cannot decrypt — they receive `class-forbidden` at the shell boundary - because plaintext could exfiltrate to approved origins with zero shell - visibility. The class invariant ties plaintext exposure to zero-egress - posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. ## 0.2.1 ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 - tarballs on npm contained unresolved `workspace:*` specs in dependencies, - breaking installs. This patch bump exists solely to produce - correctly-assembled tarballs via `pnpm publish -r` (which rewrites - `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). diff --git a/packages/nap/CHANGELOG.md b/packages/nap/CHANGELOG.md index 9cf21a92..a71360d4 100644 --- a/packages/nap/CHANGELOG.md +++ b/packages/nap/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes @@ -15,19 +15,9 @@ ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative - convention URIs and naddr recommendations, expose acceptance results and - buffered onDelivery callbacks, and discover opaque catalog identifiers with - parameter contracts. Restore the optional NAP-SHELL environment API - independently of other domain calls. Align INC provenance with authenticated - endpoint identifiers and deliver theme/identity updates only for exposed - domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, - options) and open(uri, options), from handled/windowId to acceptance, and from - candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK - helpers remain exported with the URI signatures. CLI helper extraction - preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ### Patch Changes @@ -38,10 +28,7 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries - optional advisory Blossom `servers`, `bytesMany` accepts per-resource request - objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits - the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. ### Patch Changes @@ -52,15 +39,13 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload - conventions, and require structured invoke results. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. ## 0.31.1 ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol - behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 @@ -68,25 +53,13 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, - `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, - shim and SDK helpers, injected `window.napplet.fs` methods, and conformance - coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -100,22 +73,11 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ### Patch Changes @@ -126,12 +88,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes @@ -155,37 +117,32 @@ ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API - types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. ## 0.27.2 ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload - and related public media context types. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related + public media context types. - dd2b0bc: Harden napplet sandbox authoring and verification. - `@napplet/nap` decodes `data:` resource URLs without using browser `fetch`. - - `@napplet/conformance-cli` flags direct browser network, storage, cookie, - and external network-loaded asset surfaces in served napplet code. - - `@napplet/conformance` reports the broader forbidden-surface check - accurately. - - `@napplet/skills` moves the sandbox authority contract into the top-level - authoring flow so generated napplets route bytes, state, relays, signing, - and links through shell-owned NAPs. - -- ae5eecf: Align NAP-LISTS add/remove result message types with their - action-specific wire count fields. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the - relay-owned `RelayEventResult` wrapper as `result` instead of split `event` - and `relays` fields. -- d88d674: Align NAP-DM result message types with success-or-error wire - envelopes and export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` - wire types, helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` - resource scheme. + - `@napplet/conformance-cli` flags direct browser network, storage, cookie, and external network-loaded asset surfaces in served napplet code. + - `@napplet/conformance` reports the broader forbidden-surface check accurately. + - `@napplet/skills` moves the sandbox authority contract into the top-level authoring flow so generated napplets route bytes, state, relays, signing, and links through shell-owned NAPs. + +- ae5eecf: Align NAP-LISTS add/remove result message types with their action-specific wire + count fields. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned + `RelayEventResult` wrapper as `result` instead of split `event` and `relays` + fields. +- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and + export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, + helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource + scheme. - Updated dependencies [e60f8a6] - Updated dependencies [bf246df] - Updated dependencies [d88d674] @@ -197,19 +154,13 @@ ### Patch Changes -- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt - `make-napplet` orchestration skill, and align copied package examples so - agents do not default social reads and publishes to low-level NAP-RELAY. +- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt `make-napplet` orchestration skill, and align copied package examples so agents do not default social reads and publishes to low-level NAP-RELAY. ## 0.27.0 ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible - routing and lifecycle controls. `OutboxStrategy` is no longer exported, - `strategy` is no longer accepted on outbox option objects, and - `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is - represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. ### Patch Changes @@ -220,9 +171,7 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw - read events now use `RelayEventResult` with optional `sidecar.resources` and - `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -233,10 +182,7 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / - `count.query.result` envelope types, `window.napplet.count.query(...)`, - `@napplet/nap/count` subpaths, SDK exports, and conformance - validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. ### Patch Changes @@ -247,15 +193,9 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime - API, typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. ### Patch Changes @@ -275,9 +215,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. ### Patch Changes @@ -288,17 +228,16 @@ ### Patch Changes -- 0c45563: Guard relay query results so malformed host responses without an - `events` array resolve to `[]` instead of leaking `undefined` through the - typed query contract. +- 0c45563: Guard relay query results so malformed host responses without an `events` array + resolve to `[]` instead of leaking `undefined` through the typed query contract. ## 0.22.0 ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed - payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK - wrappers, and conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, + `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and + conformance validator coverage. ### Patch Changes @@ -309,9 +248,8 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, - including wire types, shim and SDK helpers, core global types, and conformance - coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including + wire types, shim and SDK helpers, core global types, and conformance coverage. ### Patch Changes @@ -322,9 +260,7 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` - subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and - reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -338,9 +274,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell - owns identity, consent, event construction, signing, publishing, relay access, - and NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns + identity, consent, event construction, signing, publishing, relay access, and + NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -360,9 +296,7 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` - subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and - reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -375,13 +309,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core - types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, + `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -395,10 +329,9 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers - from `@napplet/sdk`, and teaches conformance validation/reference-shell - handling about `serial.open`, `serial.write`, `serial.close`, and - `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from + `@napplet/sdk`, and teaches conformance validation/reference-shell handling + about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. ### Patch Changes @@ -409,26 +342,22 @@ ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no - longer fail silently. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a - JSON envelope through `postMessage`. Framework reactive values — Svelte 5 - `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't - structured-cloneable, so `postMessage` threw a `DataCloneError` that got - silently swallowed in async paths; the envelope simply never crossed - (napplet/web#67). + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON + envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue + `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so + `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; + the envelope simply never crossed (napplet/web#67). - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, - `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / - `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only - on a `DataCloneError`, snapshots the envelope (stripping reactive proxies - while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — - warning once. `'strict'` throws a loud, actionable, synchronous error - instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through - this boundary instead of calling `window.parent.postMessage(msg, '*')` - directly. + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, + `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` + types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, + snapshots the envelope (stripping reactive proxies while preserving binary, + `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a + loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this + boundary instead of calling `window.parent.postMessage(msg, '*')` directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -442,11 +371,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md - rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` - integer was re-homed into NAP-SHELL's `shell.init` environment during the - NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The - `shell.init` wire shape is now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a + deferred NAP's surface must not outlive the NAP). The opaque `class` integer was + re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, + but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is + now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -456,8 +385,7 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option - (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). ### Patch Changes @@ -468,20 +396,11 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, - [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an - optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. - Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface - (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets - `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s - `storage.instance.*` and as `storageInstance*` helpers in - `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type - and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** - `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -492,31 +411,12 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -527,22 +427,20 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the - shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a - napplet had no way to fetch the theme through `window.napplet`. It now mirrors - the read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet + had no way to fetch the theme through `window.napplet`. It now mirrors the + read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed - `theme.changed` updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, - plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in - `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` + updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus + `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. ### Patch Changes @@ -553,23 +451,21 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke - another napplet by its role (archetype) through `window.napplet.intent` - (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it - directly. The shell resolves the archetype to an installed napplet (honoring - the user's default-handler preference), creates or focuses its window, and - delivers the payload using the named NAP-N protocol. Routing (`archetype`) and - payload format (`protocol`) are orthogonal; the shell owns resolution, default - handling, window lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another + napplet by its role (archetype) through `window.napplet.intent` (`invoke`, + `open`, `available`, `handlers`, `onChanged`) without addressing it directly. + The shell resolves the archetype to an installed napplet (honoring the user's + default-handler preference), creates or focuses its window, and delivers the + payload using the named NAP-N protocol. Routing (`archetype`) and payload format + (`protocol`) are orthogonal; the shell owns resolution, default handling, window + lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, - `intent` surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) - with request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` - envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type - re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` + surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with + request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. ### Patch Changes @@ -580,21 +476,19 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now - upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) - while the runtime selects the storage server, signs the rail authorization - (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and - returns a stable URL plus NIP-94 integrity metadata. Napplets never receive - signing keys, server credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes + through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the + runtime selects the storage server, signs the rail authorization (NIP-98 for + NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable + URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server + credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, - `upload` surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) - with request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` - envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type - re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` + surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with + request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. ### Patch Changes @@ -605,23 +499,21 @@ ### Patch Changes -- 44c92ca: Fix JSR publishing: `@napplet/nap`'s `jsr.json` exports map was - missing the `cvm` and `outbox` subpaths (it had drifted from `package.json`), - so JSR consumers and dependent packages could not resolve `@napplet/nap/cvm` - or `@napplet/nap/outbox`. The version-sync tooling now regenerates the - `jsr.json` exports from `package.json` on every release so the two can no - longer drift. +- 44c92ca: Fix JSR publishing: `@napplet/nap`'s `jsr.json` exports map was missing the + `cvm` and `outbox` subpaths (it had drifted from `package.json`), so JSR + consumers and dependent packages could not resolve `@napplet/nap/cvm` or + `@napplet/nap/outbox`. The version-sync tooling now regenerates the `jsr.json` + exports from `package.json` on every release so the two can no longer drift. ## 0.8.0 ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover - ContextVM servers and run MCP operations (`tools/list`, `tools/call`, - `resources/list`, `resources/read`) over Nostr through the shell via - `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay - routing, signing, encryption, JSON-RPC correlation, initialization, policy, - and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM + servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, + `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while + the runtime owns all ContextVM transport — relay routing, signing, encryption, + JSON-RPC correlation, initialization, policy, and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -629,19 +521,17 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, - subscribe, publish, and resolve relay plans through `window.napplet.outbox` - while the runtime owns relay discovery, NIP-65 routing, fallback, - deduplication, signature validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, + publish, and resolve relay plans through `window.napplet.outbox` while the + runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature + validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, - `outbox` surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) - with request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` - envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type - re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` + surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with + request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. ### Patch Changes @@ -655,11 +545,7 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and - TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and - `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over - the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for - downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. ### Patch Changes @@ -670,9 +556,7 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including - `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` - identifiers. +- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. ### Patch Changes @@ -694,10 +578,7 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / - `identity.onChanged`, make `getPublicKey()` represent signed-out state with - `""`, remove the old identity decrypt surface and rumor types, and normalize - shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -708,8 +589,7 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene - changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 @@ -717,195 +597,68 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP - emission from the vite-plugin (build time) to the host shell (runtime) for - every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative - abstract security posture via `class.assigned` wire) and NAP-CONNECT - (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The - option is retained as an `@deprecated` accept-but-warn shim for one release - cycle — the plugin emits one `console.warn` per build per config-resolved if - a `strictCsp` value is present, and the option is ignored. Production builds - no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is - now the sole authority on runtime CSP for every napplet, per - `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a - future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for - v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` - scans `dist/index.html` and throws a diagnostic when any `<script>` element - lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so - inline scripts would be blocked at runtime anyway; failing at build time - surfaces the problem before deployment. The allow-list exempts - `application/json`, `application/ld+json`, `importmap`, and - `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on - `NamespacedCapability`. The template-literal type still accepts it so - existing code type-checks; capability advertisement is superseded by - `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as - `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required - origins. Each origin is validated via the shared `normalizeConnectOrigin()` - validator from `@napplet/nap/connect`, emitted as a signed - `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via - a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → - UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). - Dev-mode-only `<meta name="napplet-connect-requires">` is injected for - shell-less `vite serve` previews; the shell-authoritative - `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 - entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` - (mirrors `resource` block) and `class?: number` (optional — undefined until - `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `NappletConnect` interface, pure - `normalizeConnectOrigin(origin)` validator (shared source-of-truth for - build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests - pass), and `installConnectShim()` which reads - `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type - (`{ type: 'class.assigned'; id: string; class: number }`), - `handleClassMessage` dispatcher handler, and `installClassShim()` which - mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** - defaults to `{granted: false, origins: []}` (never `undefined`); - **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). - Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the - `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + - the `class` types namespace — parallel to the existing `resource` - re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte - fetches — it works in every class posture. Reach for NAP-CONNECT only when - the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, - WebSocket, Server-Sent Events, streaming responses, custom request headers, - long-lived connections, or third-party libraries that call `fetch`/open - sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: - `class === 2` iff `connect.granted === true` at `class.assigned` send time. - See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full - scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward - from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn - once per build if you leave it. (Required when REMOVE-STRICTCSP lands; - optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move - their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the - plugin's new `connect: string[]` option. See - `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your - host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` - before reading `window.napplet.class` / `window.napplet.connect` — both - gracefully degrade (`undefined` / `{granted: false, origins: []}` - respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds - `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — - shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt - primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext - access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict - baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet - main branch; it has been renumbered to v0.30.0 during a post-merge version - split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority - milestone (feat/strict-model, shipped 2026-04-21). This changeset describes - shipped behavior; the underlying commits already exist in main (Phases 135-138 - of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, - `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope - triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, - `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, - `impersonation`, `unsupported-encryption`, `policy-denied`), - `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), - `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` - method type. Zero new runtime symbols in the types-only entry point — pure - type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding - (request-correlation by id, reusing existing `pendingRequests` - infrastructure), the `identity.decrypt.result` / `.error` handler branches - (added to `handleIdentityMessage` with discriminated-union exhaustiveness - via `assertNever`), and the bare-name `identityDecrypt(event)` helper. - Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so - `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central - shim — no new install function; the addition is two surgical edits on - `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central - `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new - identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports - — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full - conformance table (4 shell MUSTs: class-gating, outer-sig-verify, - impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, - outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy - floor), 8-code error vocabulary, and Security Considerations subsection. - Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST - `(dTag, aggregateHash)` violation-correlation row + Security Considerations - subsection distinguishing observability from enforcement. Bundled into the - same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a - `### NIP-07 Extension Injection Residual` subsection documenting the - `all_frames: true` content-script injection vector, nonce-based `script-src` - legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical - lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and - `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as - the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero - occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably - blocks legacy `<script>`-tag content-script injection AND fires - `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and - `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member - triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. - Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 - browser extension injects `window.nostr` into the iframe via a content - script (architecturally unavoidable per WebExtension spec), shells MUST NOT - _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell - is free to use its own NIP-07 extension internally to fulfill - signing/encryption duties; what is forbidden is exposing `window.nostr` to - the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) - cannot decrypt — they receive `class-forbidden` at the shell boundary - because plaintext could exfiltrate to approved origins with zero shell - visibility. The class invariant ties plaintext exposure to zero-egress - posture. - -### Patch Changes - -- 066443f: Fix the package publish path so `@napplet/nap` tarballs resolve the - internal `@napplet/core` dependency to a semver range instead of leaking the - workspace protocol. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. + +### Patch Changes + +- 066443f: Fix the package publish path so `@napplet/nap` tarballs resolve the internal `@napplet/core` dependency to a semver range instead of leaking the workspace protocol. - Updated dependencies [3d22a10] - Updated dependencies [60c93a0] - @napplet/core@0.3.0 diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index b87712ee..a9445ee6 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes @@ -16,19 +16,9 @@ ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative - convention URIs and naddr recommendations, expose acceptance results and - buffered onDelivery callbacks, and discover opaque catalog identifiers with - parameter contracts. Restore the optional NAP-SHELL environment API - independently of other domain calls. Align INC provenance with authenticated - endpoint identifiers and deliver theme/identity updates only for exposed - domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, - options) and open(uri, options), from handled/windowId to acceptance, and from - candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK - helpers remain exported with the URI signatures. CLI helper extraction - preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ### Patch Changes @@ -40,10 +30,7 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries - optional advisory Blossom `servers`, `bytesMany` accepts per-resource request - objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits - the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. ### Patch Changes @@ -62,8 +49,7 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol - behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -72,25 +58,13 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, - `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, - shim and SDK helpers, injected `window.napplet.fs` methods, and conformance - coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -105,22 +79,11 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ### Patch Changes @@ -132,12 +95,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes @@ -157,8 +120,7 @@ ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API - types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -166,19 +128,19 @@ ### Patch Changes -- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload - and related public media context types. -- ae5eecf: Align NAP-LISTS add/remove result message types with their - action-specific wire count fields. -- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the - relay-owned `RelayEventResult` wrapper as `result` instead of split `event` - and `relays` fields. -- d88d674: Align NAP-DM result message types with success-or-error wire - envelopes and export the `DmError` schema. -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` - wire types, helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` - resource scheme. +- e60f8a6: Align NAP-MEDIA session creation with the optional `context` payload and related + public media context types. +- ae5eecf: Align NAP-LISTS add/remove result message types with their action-specific wire + count fields. +- bf246df: Align `CommonProfileResult` with NAP-COMMON by returning the relay-owned + `RelayEventResult` wrapper as `result` instead of split `event` and `relays` + fields. +- d88d674: Align NAP-DM result message types with success-or-error wire envelopes and + export the `DmError` schema. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, + helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource + scheme. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -193,9 +155,7 @@ ### Patch Changes -- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt - `make-napplet` orchestration skill, and align copied package examples so - agents do not default social reads and publishes to low-level NAP-RELAY. +- 82e50c2: Teach OUTBOX-first napplet construction, add a one-prompt `make-napplet` orchestration skill, and align copied package examples so agents do not default social reads and publishes to low-level NAP-RELAY. - Updated dependencies [82e50c2] - @napplet/nap@0.27.1 @@ -203,11 +163,7 @@ ### Minor Changes -- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible - routing and lifecycle controls. `OutboxStrategy` is no longer exported, - `strategy` is no longer accepted on outbox option objects, and - `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is - represented by the handle plus `outbox.close` / `outbox.closed`. +- 332f785: Align NAP-OUTBOX with the current draft by removing caller-visible routing and lifecycle controls. `OutboxStrategy` is no longer exported, `strategy` is no longer accepted on outbox option objects, and `OutboxSubscribeOptions` no longer accepts `live`; subscription lifecycle is represented by the handle plus `outbox.close` / `outbox.closed`. ### Patch Changes @@ -219,9 +175,7 @@ ### Minor Changes -- 6ccb056: Align relay and outbox read results with the current NAPs track: raw - read events now use `RelayEventResult` with optional `sidecar.resources` and - `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. +- 6ccb056: Align relay and outbox read results with the current NAPs track: raw read events now use `RelayEventResult` with optional `sidecar.resources` and `sidecar.relayHints`, and NAP-OUTBOX no longer defines `outbox.eose`. ### Patch Changes @@ -233,10 +187,7 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / - `count.query.result` envelope types, `window.napplet.count.query(...)`, - `@napplet/nap/count` subpaths, SDK exports, and conformance - validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. ### Patch Changes @@ -248,15 +199,9 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime - API, typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. ### Patch Changes @@ -277,9 +222,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. - Updated dependencies [688fb59] - @napplet/core@0.23.0 @@ -296,9 +241,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed - payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK - wrappers, and conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, + `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and + conformance validator coverage. ### Patch Changes @@ -310,9 +255,8 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, - including wire types, shim and SDK helpers, core global types, and conformance - coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including + wire types, shim and SDK helpers, core global types, and conformance coverage. ### Patch Changes @@ -324,9 +268,7 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` - subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and - reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -341,9 +283,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell - owns identity, consent, event construction, signing, publishing, relay access, - and NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns + identity, consent, event construction, signing, publishing, relay access, and + NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -364,9 +306,7 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` - subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and - reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -380,13 +320,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core - types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, + `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -401,10 +341,9 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers - from `@napplet/sdk`, and teaches conformance validation/reference-shell - handling about `serial.open`, `serial.write`, `serial.close`, and - `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from + `@napplet/sdk`, and teaches conformance validation/reference-shell handling + about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. ### Patch Changes @@ -432,20 +371,11 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, - [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an - optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. - Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface - (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets - `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s - `storage.instance.*` and as `storageInstance*` helpers in - `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type - and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** - `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -457,31 +387,12 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -493,22 +404,20 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the - shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a - napplet had no way to fetch the theme through `window.napplet`. It now mirrors - the read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet + had no way to fetch the theme through `window.napplet`. It now mirrors the + read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed - `theme.changed` updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, - plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in - `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` + updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus + `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. ### Patch Changes @@ -520,23 +429,21 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke - another napplet by its role (archetype) through `window.napplet.intent` - (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it - directly. The shell resolves the archetype to an installed napplet (honoring - the user's default-handler preference), creates or focuses its window, and - delivers the payload using the named NAP-N protocol. Routing (`archetype`) and - payload format (`protocol`) are orthogonal; the shell owns resolution, default - handling, window lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another + napplet by its role (archetype) through `window.napplet.intent` (`invoke`, + `open`, `available`, `handlers`, `onChanged`) without addressing it directly. + The shell resolves the archetype to an installed napplet (honoring the user's + default-handler preference), creates or focuses its window, and delivers the + payload using the named NAP-N protocol. Routing (`archetype`) and payload format + (`protocol`) are orthogonal; the shell owns resolution, default handling, window + lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, - `intent` surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) - with request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` - envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type - re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` + surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with + request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. ### Patch Changes @@ -548,21 +455,19 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now - upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) - while the runtime selects the storage server, signs the rail authorization - (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and - returns a stable URL plus NIP-94 integrity metadata. Napplets never receive - signing keys, server credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes + through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the + runtime selects the storage server, signs the rail authorization (NIP-98 for + NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable + URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server + credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, - `upload` surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) - with request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` - envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type - re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` + surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with + request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. ### Patch Changes @@ -581,12 +486,11 @@ ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover - ContextVM servers and run MCP operations (`tools/list`, `tools/call`, - `resources/list`, `resources/read`) over Nostr through the shell via - `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay - routing, signing, encryption, JSON-RPC correlation, initialization, policy, - and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM + servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, + `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while + the runtime owns all ContextVM transport — relay routing, signing, encryption, + JSON-RPC correlation, initialization, policy, and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -594,19 +498,17 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, - subscribe, publish, and resolve relay plans through `window.napplet.outbox` - while the runtime owns relay discovery, NIP-65 routing, fallback, - deduplication, signature validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, + publish, and resolve relay plans through `window.napplet.outbox` while the + runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature + validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, - `outbox` surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) - with request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` - envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type - re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` + surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with + request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. ### Patch Changes @@ -621,11 +523,7 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and - TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and - `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over - the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for - downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. ### Patch Changes @@ -637,9 +535,7 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including - `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` - identifiers. +- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. ### Patch Changes @@ -663,10 +559,7 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / - `identity.onChanged`, make `getPublicKey()` represent signed-out state with - `""`, remove the old identity decrypt surface and rumor types, and normalize - shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -678,8 +571,7 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene - changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 - @napplet/nap@0.3.1 @@ -688,189 +580,64 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP - emission from the vite-plugin (build time) to the host shell (runtime) for - every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative - abstract security posture via `class.assigned` wire) and NAP-CONNECT - (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The - option is retained as an `@deprecated` accept-but-warn shim for one release - cycle — the plugin emits one `console.warn` per build per config-resolved if - a `strictCsp` value is present, and the option is ignored. Production builds - no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is - now the sole authority on runtime CSP for every napplet, per - `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a - future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for - v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` - scans `dist/index.html` and throws a diagnostic when any `<script>` element - lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so - inline scripts would be blocked at runtime anyway; failing at build time - surfaces the problem before deployment. The allow-list exempts - `application/json`, `application/ld+json`, `importmap`, and - `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on - `NamespacedCapability`. The template-literal type still accepts it so - existing code type-checks; capability advertisement is superseded by - `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as - `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required - origins. Each origin is validated via the shared `normalizeConnectOrigin()` - validator from `@napplet/nap/connect`, emitted as a signed - `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via - a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → - UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). - Dev-mode-only `<meta name="napplet-connect-requires">` is injected for - shell-less `vite serve` previews; the shell-authoritative - `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 - entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` - (mirrors `resource` block) and `class?: number` (optional — undefined until - `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `NappletConnect` interface, pure - `normalizeConnectOrigin(origin)` validator (shared source-of-truth for - build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests - pass), and `installConnectShim()` which reads - `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type - (`{ type: 'class.assigned'; id: string; class: number }`), - `handleClassMessage` dispatcher handler, and `installClassShim()` which - mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** - defaults to `{granted: false, origins: []}` (never `undefined`); - **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). - Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the - `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + - the `class` types namespace — parallel to the existing `resource` - re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte - fetches — it works in every class posture. Reach for NAP-CONNECT only when - the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, - WebSocket, Server-Sent Events, streaming responses, custom request headers, - long-lived connections, or third-party libraries that call `fetch`/open - sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: - `class === 2` iff `connect.granted === true` at `class.assigned` send time. - See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full - scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward - from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn - once per build if you leave it. (Required when REMOVE-STRICTCSP lands; - optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move - their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the - plugin's new `connect: string[]` option. See - `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your - host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` - before reading `window.napplet.class` / `window.napplet.connect` — both - gracefully degrade (`undefined` / `{granted: false, origins: []}` - respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds - `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — - shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt - primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext - access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict - baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet - main branch; it has been renumbered to v0.30.0 during a post-merge version - split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority - milestone (feat/strict-model, shipped 2026-04-21). This changeset describes - shipped behavior; the underlying commits already exist in main (Phases 135-138 - of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, - `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope - triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, - `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, - `impersonation`, `unsupported-encryption`, `policy-denied`), - `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), - `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` - method type. Zero new runtime symbols in the types-only entry point — pure - type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding - (request-correlation by id, reusing existing `pendingRequests` - infrastructure), the `identity.decrypt.result` / `.error` handler branches - (added to `handleIdentityMessage` with discriminated-union exhaustiveness - via `assertNever`), and the bare-name `identityDecrypt(event)` helper. - Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so - `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central - shim — no new install function; the addition is two surgical edits on - `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central - `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new - identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports - — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full - conformance table (4 shell MUSTs: class-gating, outer-sig-verify, - impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, - outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy - floor), 8-code error vocabulary, and Security Considerations subsection. - Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST - `(dTag, aggregateHash)` violation-correlation row + Security Considerations - subsection distinguishing observability from enforcement. Bundled into the - same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a - `### NIP-07 Extension Injection Residual` subsection documenting the - `all_frames: true` content-script injection vector, nonce-based `script-src` - legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical - lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and - `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as - the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero - occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably - blocks legacy `<script>`-tag content-script injection AND fires - `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and - `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member - triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. - Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 - browser extension injects `window.nostr` into the iframe via a content - script (architecturally unavoidable per WebExtension spec), shells MUST NOT - _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell - is free to use its own NIP-07 extension internally to fulfill - signing/encryption duties; what is forbidden is exposing `window.nostr` to - the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) - cannot decrypt — they receive `class-forbidden` at the shell boundary - because plaintext could exfiltrate to approved origins with zero shell - visibility. The class invariant ties plaintext exposure to zero-egress - posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. ### Patch Changes @@ -884,11 +651,7 @@ ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 - tarballs on npm contained unresolved `workspace:*` specs in dependencies, - breaking installs. This patch bump exists solely to produce - correctly-assembled tarballs via `pnpm publish -r` (which rewrites - `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). - Updated dependencies - @napplet/core@0.2.1 - @napplet/nap-config@0.2.1 diff --git a/packages/shim/CHANGELOG.md b/packages/shim/CHANGELOG.md index dfe80425..42d7dbd2 100644 --- a/packages/shim/CHANGELOG.md +++ b/packages/shim/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ### Patch Changes @@ -16,19 +16,9 @@ ### Minor Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative - convention URIs and naddr recommendations, expose acceptance results and - buffered onDelivery callbacks, and discover opaque catalog identifiers with - parameter contracts. Restore the optional NAP-SHELL environment API - independently of other domain calls. Align INC provenance with authenticated - endpoint identifiers and deliver theme/identity updates only for exposed - domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, - options) and open(uri, options), from handled/windowId to acceptance, and from - candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK - helpers remain exported with the URI signatures. CLI helper extraction - preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ### Patch Changes @@ -40,10 +30,7 @@ ### Minor Changes -- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries - optional advisory Blossom `servers`, `bytesMany` accepts per-resource request - objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits - the amended envelopes. +- 19e0029: Adopt the NAP-RESOURCE server-hint amendment: `bytes` carries optional advisory Blossom `servers`, `bytesMany` accepts per-resource request objects, `ResourceInfo` exposes optional `maxServers`, and conformance emits the amended envelopes. ### Patch Changes @@ -62,8 +49,7 @@ ### Patch Changes -- 1861f4d: Restore maintainable module boundaries without changing protocol - behavior. +- 1861f4d: Restore maintainable module boundaries without changing protocol behavior. - Updated dependencies [1861f4d] - @napplet/core@0.31.1 - @napplet/nap@0.31.1 @@ -72,25 +58,13 @@ ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. -- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, - `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, - shim and SDK helpers, injected `window.napplet.fs` methods, and conformance - coverage for the new `fs.pick*` envelopes. +- d201bd0: Add NAP-FS picker support for `pickFile`, `pickFiles`, `pickDirectory`, and `pickSaveFile`, including typed picker result shapes, shim and SDK helpers, injected `window.napplet.fs` methods, and conformance coverage for the new `fs.pick*` envelopes. ### Patch Changes @@ -105,22 +79,11 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ### Patch Changes @@ -132,12 +95,12 @@ ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes @@ -157,15 +120,13 @@ ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, - conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. ## 0.26.6 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API - types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. - Updated dependencies [954b6bb] - @napplet/nap@0.27.3 @@ -173,10 +134,10 @@ ### Patch Changes -- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` - wire types, helpers, injected runtime surface, and conformance coverage. -- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` - resource scheme. +- f63f8af: Align NAP-CVM with the live registry API by adding `cvm.registry.*` wire types, + helpers, injected runtime surface, and conformance coverage. +- 22d2e45: Align NAP-RESOURCE public type and docs with the canonical `htree:` resource + scheme. - Updated dependencies [e60f8a6] - Updated dependencies [dd2b0bc] - Updated dependencies [ae5eecf] @@ -191,9 +152,7 @@ ### Patch Changes -- 8953eed: Add a host-injectable NIP-5D runtime prelude surface with an explicit - NAP domain allowlist and a bundled browser IIFE artifact for shell `srcdoc` - injection. +- 8953eed: Add a host-injectable NIP-5D runtime prelude surface with an explicit NAP domain allowlist and a bundled browser IIFE artifact for shell `srcdoc` injection. ## 0.26.3 @@ -222,10 +181,7 @@ ### Minor Changes -- 284e100: Add the NAP-COUNT `count` domain with `count.query` / - `count.query.result` envelope types, `window.napplet.count.query(...)`, - `@napplet/nap/count` subpaths, SDK exports, and conformance - validator/reference-shell coverage. +- 284e100: Add the NAP-COUNT `count` domain with `count.query` / `count.query.result` envelope types, `window.napplet.count.query(...)`, `@napplet/nap/count` subpaths, SDK exports, and conformance validator/reference-shell coverage. ### Patch Changes @@ -237,15 +193,9 @@ ### Minor Changes -- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime - API, typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. -- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, - typed NAP helpers, SDK wrappers, injected global, and conformance - envelope/reference shell. +- c711a3e: Add NAP-OUTBOX `outbox.getEvent` support across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- ce41387: Add NAP-RESOURCE `resource.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. +- 50b3c1b: Add NAP-UPLOAD `upload.info` introspection across the runtime API, typed NAP helpers, SDK wrappers, injected global, and conformance envelope/reference shell. ### Patch Changes @@ -266,9 +216,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. ### Patch Changes @@ -280,9 +230,8 @@ ### Patch Changes -- 0c45563: Guard relay query results so malformed host responses without an - `events` array resolve to `[]` instead of leaking `undefined` through the - typed query contract. +- 0c45563: Guard relay query results so malformed host responses without an `events` array + resolve to `[]` instead of leaking `undefined` through the typed query contract. - Updated dependencies [0c45563] - @napplet/nap@0.22.1 @@ -290,9 +239,9 @@ ### Minor Changes -- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed - payloads, `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK - wrappers, and conformance validator coverage. +- 7e0c5bc: Add NAP-DM as a runtime-mediated direct-message capability with typed payloads, + `@napplet/nap/dm` subpath exports, `window.napplet.dm`, SDK wrappers, and + conformance validator coverage. ### Patch Changes @@ -304,9 +253,8 @@ ### Minor Changes -- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, - including wire types, shim and SDK helpers, core global types, and conformance - coverage. +- b0e0c76: Add the NAP-RESOURCE `resource.bytesMany` bulk byte-fetch surface, including + wire types, shim and SDK helpers, core global types, and conformance coverage. ### Patch Changes @@ -318,9 +266,7 @@ ### Minor Changes -- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` - subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and - reference-shell responses. +- c6f8645: Add NAP-BLE package support with core types, `@napplet/nap/ble` subpaths, `window.napplet.ble`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -335,9 +281,9 @@ - 61431b7: Add NAP-COMMON common social actions. The new `common` domain exposes shell-mediated public NIP-19 encode/decode, - profile lookup, follows, follow/unfollow, reactions, and reports. The shell - owns identity, consent, event construction, signing, publishing, relay access, - and NIP-19 handling. + profile lookup, follows, follow/unfollow, reactions, and reports. The shell owns + identity, consent, event construction, signing, publishing, relay access, and + NIP-19 handling. - 086f36e: Implement the draft NAP-LISTS surface from napplet/naps#68. @@ -358,9 +304,7 @@ ### Minor Changes -- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` - subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and - reference-shell responses. +- 5cb3187: Add NAP-WEBRTC package support with core types, `@napplet/nap/webrtc` subpaths, `window.napplet.webrtc`, SDK helpers, conformance validators, and reference-shell responses. ### Patch Changes @@ -374,13 +318,13 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. ### Patch Changes -- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core - types, `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and +- ef8ad6b: Add the NAP-LINK shell-mediated link opening surface, including core types, + `@napplet/nap/link` subpaths, `window.napplet.link`, SDK helpers, and conformance/reference-shell support. - Updated dependencies [51b2ff1] - Updated dependencies [ef8ad6b] @@ -395,10 +339,9 @@ This adds the `serial` NAP domain to core capability typing, exposes `@napplet/nap/serial` types/shim/sdk/barrel subpaths, installs - `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers - from `@napplet/sdk`, and teaches conformance validation/reference-shell - handling about `serial.open`, `serial.write`, `serial.close`, and - `serial.event`. + `window.napplet.serial` through `@napplet/shim`, re-exports serial helpers from + `@napplet/sdk`, and teaches conformance validation/reference-shell handling + about `serial.open`, `serial.write`, `serial.close`, and `serial.event`. ### Patch Changes @@ -410,26 +353,22 @@ ### Minor Changes -- 49b4660: Make the iframe boundary clone-safe so framework reactive values no - longer fail silently. - - Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a - JSON envelope through `postMessage`. Framework reactive values — Svelte 5 - `$state`, Vue `reactive`, Solid stores — are `Proxy` objects that aren't - structured-cloneable, so `postMessage` threw a `DataCloneError` that got - silently swallowed in async paths; the envelope simply never crossed - (napplet/web#67). - - - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, - `setCloneMode`, `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / - `PostMessageTarget` types. The default `'auto'` mode posts as-is and, only - on a `DataCloneError`, snapshots the envelope (stripping reactive proxies - while preserving binary, `Date`, `Map`, `Set`, and cycles) and retries — - warning once. `'strict'` throws a loud, actionable, synchronous error - instead; `'snapshot'` normalizes eagerly. - - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through - this boundary instead of calling `window.parent.postMessage(msg, '*')` - directly. +- 49b4660: Make the iframe boundary clone-safe so framework reactive values no longer fail silently. + + Every NAP shim crosses the napplet ⇄ shell boundary by structured-cloning a JSON + envelope through `postMessage`. Framework reactive values — Svelte 5 `$state`, Vue + `reactive`, Solid stores — are `Proxy` objects that aren't structured-cloneable, so + `postMessage` threw a `DataCloneError` that got silently swallowed in async paths; + the envelope simply never crossed (napplet/web#67). + + - **`@napplet/core`** adds `sendEnvelope`, `toCloneableSnapshot`, `setCloneMode`, + `getCloneMode`, `clearCloneWarnings`, and the `CloneMode` / `PostMessageTarget` + types. The default `'auto'` mode posts as-is and, only on a `DataCloneError`, + snapshots the envelope (stripping reactive proxies while preserving binary, + `Date`, `Map`, `Set`, and cycles) and retries — warning once. `'strict'` throws a + loud, actionable, synchronous error instead; `'snapshot'` normalizes eagerly. + - **`@napplet/nap`** and **`@napplet/shim`** now post every envelope through this + boundary instead of calling `window.parent.postMessage(msg, '*')` directly. These are SDK plumbing only — the bytes placed on the wire are identical plain envelopes, so no NAP/protocol surface changes. @@ -444,11 +383,11 @@ ### Minor Changes -- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md - rule 8: a deferred NAP's surface must not outlive the NAP). The opaque `class` - integer was re-homed into NAP-SHELL's `shell.init` environment during the - NAP-CLASS defer, but canonical NAP-SHELL carries no class field. The - `shell.init` wire shape is now `{ capabilities, services }`. +- 6dcb2ac: Retire the residual NAP-CLASS `class` field from NAP-SHELL (AGENTS.md rule 8: a + deferred NAP's surface must not outlive the NAP). The opaque `class` integer was + re-homed into NAP-SHELL's `shell.init` environment during the NAP-CLASS defer, + but canonical NAP-SHELL carries no class field. The `shell.init` wire shape is + now `{ capabilities, services }`. **BREAKING CHANGE:** @@ -458,8 +397,7 @@ - `window.napplet.shell.class` is removed (`@napplet/shim`). - `@napplet/nap/shell` no longer exports `shellClass()`. - The conformance reference shell emits a class-free `shell.init` and its - `createReferenceShell` no longer accepts a `class` option - (`@napplet/conformance`). + `createReferenceShell` no longer accepts a `class` option (`@napplet/conformance`). ### Patch Changes @@ -471,20 +409,11 @@ ### Minor Changes -- 6c99508: Add per-instance storage scope (NAP-STORAGE, - [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. +- 6c99508: Add per-instance storage scope (NAP-STORAGE, [github.com/napplet/naps](https://github.com/napplet/naps)). Resolves #57. - - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an - optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. - Result messages are unchanged (no `scope`). - - New `window.napplet.storage.instance.*` surface - (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets - `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s - `storage.instance.*` and as `storageInstance*` helpers in - `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type - and `StorageScope` type exported. - - Fully backward compatible: top-level (shared) storage calls emit **no** - `scope` field, so they remain byte-identical on the wire to prior versions. + - Storage request messages (`storage.get`/`set`/`remove`/`keys`) gain an optional `scope?: 'shared' | 'instance'` field; `"shared"` is the default. Result messages are unchanged (no `scope`). + - New `window.napplet.storage.instance.*` surface (`getItem`/`setItem`/`removeItem`/`keys`) — sugar that sets `scope: "instance"` on the wire. Mirrored on `@napplet/sdk`'s `storage.instance.*` and as `storageInstance*` helpers in `@napplet/nap/storage` / `@napplet/sdk`. New `NappletInstanceStorage` type and `StorageScope` type exported. + - Fully backward compatible: top-level (shared) storage calls emit **no** `scope` field, so they remain byte-identical on the wire to prior versions. ### Patch Changes @@ -496,31 +425,12 @@ ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ### Patch Changes @@ -532,19 +442,18 @@ ### Minor Changes -- 2119604: Add a runtime guard that fails loudly when a napplet is loaded - outside a napplet runtime. Importing `@napplet/shim` now detects when no - runtime is present — the napplet is a top-level page, or is embedded in a - frame that never answers the `shell.ready` → `shell.init` handshake — and - renders an explanatory modal instead of silently failing. This makes the - common confusing case legible: a napplet opened directly from a NIP-5A nsite - gateway can't work, because there's no shell to proxy Nostr access over - postMessage (NIP-5D). +- 2119604: Add a runtime guard that fails loudly when a napplet is loaded outside a napplet + runtime. Importing `@napplet/shim` now detects when no runtime is present — the + napplet is a top-level page, or is embedded in a frame that never answers the + `shell.ready` → `shell.init` handshake — and renders an explanatory modal instead + of silently failing. This makes the common confusing case legible: a napplet + opened directly from a NIP-5A nsite gateway can't work, because there's no shell + to proxy Nostr access over postMessage (NIP-5D). The modal explains why nothing loaded and links to a runtime directory - (`napplet.run`), the reference runtime (`github.com/kehto/web`), and the - NIP-5D specification. It is style-isolated in a shadow root so the napplet's - own CSS can't distort it. + (`napplet.run`), the reference runtime (`github.com/kehto/web`), and the NIP-5D + specification. It is style-isolated in a shadow root so the napplet's own CSS + can't distort it. Local standalone development can opt out via `window.__NAPPLET_ALLOW_STANDALONE__ = true` (set before the shim loads) or a @@ -554,22 +463,20 @@ ### Minor Changes -- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the - shell theme. +- 65b7ef1: Add the NAP-THEME shim and SDK so napplets can actually read the shell theme. `theme` previously shipped types only — it defined the `theme.get` / - `theme.get.result` / `theme.changed` wire protocol but no runtime, so a - napplet had no way to fetch the theme through `window.napplet`. It now mirrors - the read-only `identity` NAP: + `theme.get.result` / `theme.changed` wire protocol but no runtime, so a napplet + had no way to fetch the theme through `window.napplet`. It now mirrors the + read-only `identity` NAP: - `window.napplet.theme.get()` resolves the current `Theme` (colors, optional fonts, background media, and title). - - `window.napplet.theme.onChanged(handler)` fires on shell-pushed - `theme.changed` updates. - - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, - plus `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from - `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in - `@napplet/core`. + - `window.napplet.theme.onChanged(handler)` fires on shell-pushed `theme.changed` + updates. + - New entry points `@napplet/nap/theme/shim` and `@napplet/nap/theme/sdk`, plus + `themeGet` / `themeOnChanged` / `installThemeShim` re-exported from + `@napplet/sdk`, and `theme` added to the `NappletGlobal` type in `@napplet/core`. ### Patch Changes @@ -581,23 +488,21 @@ ### Minor Changes -- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke - another napplet by its role (archetype) through `window.napplet.intent` - (`invoke`, `open`, `available`, `handlers`, `onChanged`) without addressing it - directly. The shell resolves the archetype to an installed napplet (honoring - the user's default-handler preference), creates or focuses its window, and - delivers the payload using the named NAP-N protocol. Routing (`archetype`) and - payload format (`protocol`) are orthogonal; the shell owns resolution, default - handling, window lifecycle, and the cross-napplet trust boundary. +- 731473e: Add NAP-INTENT, archetype intent dispatch. Napplets can now invoke another + napplet by its role (archetype) through `window.napplet.intent` (`invoke`, + `open`, `available`, `handlers`, `onChanged`) without addressing it directly. + The shell resolves the archetype to an installed napplet (honoring the user's + default-handler preference), creates or focuses its window, and delivers the + payload using the named NAP-N protocol. Routing (`archetype`) and payload format + (`protocol`) are orthogonal; the shell owns resolution, default handling, window + lifecycle, and the cross-napplet trust boundary. - - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, - `intent` surface added to `NappletGlobal`, plus the intent value types. - - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) - with request/response correlation and an availability-change listener. - - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` - envelopes. - - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type - re-exports. + - `@napplet/core`: `intent` domain added to `NapDomain`/`NAP_DOMAINS`, `intent` + surface added to `NappletGlobal`, plus the intent value types. + - `@napplet/nap`: new `@napplet/nap/intent` domain (types/shim/sdk subpaths) with + request/response correlation and an availability-change listener. + - `@napplet/shim`: mounts `window.napplet.intent` and routes `intent.*` envelopes. + - `@napplet/sdk`: `intent` namespace, `intent*` helpers, and INTENT type re-exports. ### Patch Changes @@ -609,21 +514,19 @@ ### Minor Changes -- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now - upload bytes through `window.napplet.upload` (`upload`, `status`, `onStatus`) - while the runtime selects the storage server, signs the rail authorization - (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP upload, and - returns a stable URL plus NIP-94 integrity metadata. Napplets never receive - signing keys, server credentials, or direct network access. +- 2124d9d: Add NAP-UPLOAD, shell-mediated file/blob upload. Napplets can now upload bytes + through `window.napplet.upload` (`upload`, `status`, `onStatus`) while the + runtime selects the storage server, signs the rail authorization (NIP-98 for + NIP-96, kind 24242 for Blossom), performs the HTTP upload, and returns a stable + URL plus NIP-94 integrity metadata. Napplets never receive signing keys, server + credentials, or direct network access. - - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, - `upload` surface added to `NappletGlobal`, plus the upload value types. - - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) - with request/response correlation and a status-push listener. - - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` - envelopes. - - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type - re-exports. + - `@napplet/core`: `upload` domain added to `NapDomain`/`NAP_DOMAINS`, `upload` + surface added to `NappletGlobal`, plus the upload value types. + - `@napplet/nap`: new `@napplet/nap/upload` domain (types/shim/sdk subpaths) with + request/response correlation and a status-push listener. + - `@napplet/shim`: mounts `window.napplet.upload` and routes `upload.*` envelopes. + - `@napplet/sdk`: `upload` namespace, `upload*` helpers, and UPLOAD type re-exports. ### Patch Changes @@ -642,12 +545,11 @@ ### Minor Changes -- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover - ContextVM servers and run MCP operations (`tools/list`, `tools/call`, - `resources/list`, `resources/read`) over Nostr through the shell via - `window.napplet.cvm`, while the runtime owns all ContextVM transport — relay - routing, signing, encryption, JSON-RPC correlation, initialization, policy, - and optional payment prompts. +- 026a8d3: Add NAP-CVM, the native ContextVM bridge. Napplets can now discover ContextVM + servers and run MCP operations (`tools/list`, `tools/call`, `resources/list`, + `resources/read`) over Nostr through the shell via `window.napplet.cvm`, while + the runtime owns all ContextVM transport — relay routing, signing, encryption, + JSON-RPC correlation, initialization, policy, and optional payment prompts. - `@napplet/core`: `cvm` domain added to `NapDomain`/`NAP_DOMAINS`, `cvm` surface added to `NappletGlobal`, plus MCP and ContextVM value types. @@ -655,19 +557,17 @@ - `@napplet/shim`: mounts `window.napplet.cvm` and routes `cvm.*` envelopes. - `@napplet/sdk`: `cvm` namespace, `cvm*` helpers, and CVM type re-exports. -- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, - subscribe, publish, and resolve relay plans through `window.napplet.outbox` - while the runtime owns relay discovery, NIP-65 routing, fallback, - deduplication, signature validation, and publish fanout. +- f371581: Add NAP-OUTBOX, outbox-aware relay routing. Napplets can now query, subscribe, + publish, and resolve relay plans through `window.napplet.outbox` while the + runtime owns relay discovery, NIP-65 routing, fallback, deduplication, signature + validation, and publish fanout. - - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, - `outbox` surface added to `NappletGlobal`, plus the outbox value types. - - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) - with request/response correlation and an event-emitter subscription handle. - - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` - envelopes. - - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type - re-exports. + - `@napplet/core`: `outbox` domain added to `NapDomain`/`NAP_DOMAINS`, `outbox` + surface added to `NappletGlobal`, plus the outbox value types. + - `@napplet/nap`: new `@napplet/nap/outbox` domain (types/shim/sdk subpaths) with + request/response correlation and an event-emitter subscription handle. + - `@napplet/shim`: mounts `window.napplet.outbox` and routes `outbox.*` envelopes. + - `@napplet/sdk`: `outbox` namespace, `outbox*` helpers, and OUTBOX type re-exports. ### Patch Changes @@ -682,11 +582,7 @@ - Rename the inter-napplet communication surface from NAP-IFC to NAP-INC. - The canonical domain, capability, runtime namespace, wire messages, and - TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and - `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over - the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for - downstream migration. + The canonical domain, capability, runtime namespace, wire messages, and TypeScript names are now `inc`, `nap:inc`, `window.napplet.inc`, `inc.*`, and `Inc*`. `@napplet/nap` keeps deprecated `ifc` subpaths as thin wrappers over the INC implementation, and `@napplet/sdk` keeps deprecated `ifc` aliases for downstream migration. ### Patch Changes @@ -698,9 +594,7 @@ ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including - `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` - identifiers. +- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. ### Patch Changes @@ -724,10 +618,7 @@ ### Minor Changes -- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / - `identity.onChanged`, make `getPublicKey()` represent signed-out state with - `""`, remove the old identity decrypt surface and rumor types, and normalize - shell support names around canonical `nap:` / `NAP-NN` identifiers. +- Align the package APIs with read-only NAP-IDENTITY: add `identity.changed` / `identity.onChanged`, make `getPublicKey()` represent signed-out state with `""`, remove the old identity decrypt surface and rumor types, and normalize shell support names around canonical `nap:` / `NAP-NN` identifiers. ### Patch Changes @@ -739,8 +630,7 @@ ### Patch Changes -- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene - changes across the changed runtime packages. +- Ship the cleanup milestone's scanner, type-boundary, and dependency hygiene changes across the changed runtime packages. - Updated dependencies - @napplet/core@0.3.1 - @napplet/nap@0.3.1 @@ -749,189 +639,64 @@ ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP - emission from the vite-plugin (build time) to the host shell (runtime) for - every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative - abstract security posture via `class.assigned` wire) and NAP-CONNECT - (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The - option is retained as an `@deprecated` accept-but-warn shim for one release - cycle — the plugin emits one `console.warn` per build per config-resolved if - a `strictCsp` value is present, and the option is ignored. Production builds - no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is - now the sole authority on runtime CSP for every napplet, per - `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a - future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for - v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` - scans `dist/index.html` and throws a diagnostic when any `<script>` element - lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so - inline scripts would be blocked at runtime anyway; failing at build time - surfaces the problem before deployment. The allow-list exempts - `application/json`, `application/ld+json`, `importmap`, and - `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on - `NamespacedCapability`. The template-literal type still accepts it so - existing code type-checks; capability advertisement is superseded by - `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as - `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required - origins. Each origin is validated via the shared `normalizeConnectOrigin()` - validator from `@napplet/nap/connect`, emitted as a signed - `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via - a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → - UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). - Dev-mode-only `<meta name="napplet-connect-requires">` is injected for - shell-less `vite serve` previews; the shell-authoritative - `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 - entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` - (mirrors `resource` block) and `class?: number` (optional — undefined until - `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `NappletConnect` interface, pure - `normalizeConnectOrigin(origin)` validator (shared source-of-truth for - build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests - pass), and `installConnectShim()` which reads - `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type - (`{ type: 'class.assigned'; id: string; class: number }`), - `handleClassMessage` dispatcher handler, and `installClassShim()` which - mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** - defaults to `{granted: false, origins: []}` (never `undefined`); - **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). - Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the - `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + - the `class` types namespace — parallel to the existing `resource` - re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte - fetches — it works in every class posture. Reach for NAP-CONNECT only when - the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, - WebSocket, Server-Sent Events, streaming responses, custom request headers, - long-lived connections, or third-party libraries that call `fetch`/open - sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: - `class === 2` iff `connect.granted === true` at `class.assigned` send time. - See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full - scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward - from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn - once per build if you leave it. (Required when REMOVE-STRICTCSP lands; - optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move - their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the - plugin's new `connect: string[]` option. See - `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your - host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` - before reading `window.napplet.class` / `window.napplet.connect` — both - gracefully degrade (`undefined` / `{granted: false, origins: []}` - respectively) on non-implementing shells. - -- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds - `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — - shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt - primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext - access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict - baseline posture, `connect-src 'none'`, zero direct network egress). - - This work was originally written and self-labeled as v0.29.0 on the napplet - main branch; it has been renumbered to v0.30.0 during a post-merge version - split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority - milestone (feat/strict-model, shipped 2026-04-21). This changeset describes - shipped behavior; the underlying commits already exist in main (Phases 135-138 - of the migrated `.planning/milestones/v0.30.0-phases/` archive). + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. + +- 60c93a0: v0.30.0 — Class-Gated Decrypt Surface. Adds `identity.decrypt(event) → Promise<{ rumor, sender }>` on NAP-IDENTITY — shell-mediated NIP-04 / direct NIP-44 / NIP-17 gift-wrap auto-detect decrypt primitive. Closes the NIP-17 / NIP-59 receive-side gap and gates plaintext access shell-side to napplets assigned `class: 1` per NAP-CLASS-1 (strict baseline posture, `connect-src 'none'`, zero direct network egress). + + This work was originally written and self-labeled as v0.29.0 on the napplet main branch; it has been renumbered to v0.30.0 during a post-merge version split to avoid collision with the v0.29.0 NAP-CONNECT + Shell as CSP Authority milestone (feat/strict-model, shipped 2026-04-21). This changeset describes shipped behavior; the underlying commits already exist in main (Phases 135-138 of the migrated `.planning/milestones/v0.30.0-phases/` archive). **New surface:** - - `@napplet/core` adds `IdentityDecryptMessage`, - `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope - triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, - `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, - `impersonation`, `unsupported-encryption`, `policy-denied`), - `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), - `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` - method type. Zero new runtime symbols in the types-only entry point — pure - type contract. - - `@napplet/nap/identity` ships the runtime `decrypt()` binding - (request-correlation by id, reusing existing `pendingRequests` - infrastructure), the `identity.decrypt.result` / `.error` handler branches - (added to `handleIdentityMessage` with discriminated-union exhaustiveness - via `assertNever`), and the bare-name `identityDecrypt(event)` helper. - Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so - `import { type Rumor } from '@napplet/nap/identity'` resolves. - - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central - shim — no new install function; the addition is two surgical edits on - `packages/shim/src/index.ts` (import block + namespace assembly). - - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central - `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new - identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports - — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. + - `@napplet/core` adds `IdentityDecryptMessage`, `IdentityDecryptResultMessage`, `IdentityDecryptErrorMessage` (envelope triad), `IdentityDecryptErrorCode` (8-code union: `class-forbidden`, `signer-denied`, `signer-unavailable`, `decrypt-failed`, `malformed-wrap`, `impersonation`, `unsupported-encryption`, `policy-denied`), `Rumor = UnsignedEvent & { id: string }` (nostr-tools canonical), `UnsignedEvent` interface, and a `NappletGlobal.identity.decrypt(event)` method type. Zero new runtime symbols in the types-only entry point — pure type contract. + - `@napplet/nap/identity` ships the runtime `decrypt()` binding (request-correlation by id, reusing existing `pendingRequests` infrastructure), the `identity.decrypt.result` / `.error` handler branches (added to `handleIdentityMessage` with discriminated-union exhaustiveness via `assertNever`), and the bare-name `identityDecrypt(event)` helper. Cross-package type-only re-exports of `Rumor` and `UnsignedEvent` so `import { type Rumor } from '@napplet/nap/identity'` resolves. + - `@napplet/shim` mounts `window.napplet.identity.decrypt` via the central shim — no new install function; the addition is two surgical edits on `packages/shim/src/index.ts` (import block + namespace assembly). + - `@napplet/sdk` re-exports `identity.decrypt(event)` on the central `identity` namespace, the bare-name `identityDecrypt` helper, and 4 new identity type re-exports plus `Rumor` + `UnsignedEvent` core type re-exports — exactly the v0.28.0 Phase 129 4-surgical-edit pattern. **Cross-repo amendments (separate PRs on `napplet/naps`):** - - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full - conformance table (4 shell MUSTs: class-gating, outer-sig-verify, - impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, - outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy - floor), 8-code error vocabulary, and Security Considerations subsection. - Authored on branch `nap-identity-decrypt`. - - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST - `(dTag, aggregateHash)` violation-correlation row + Security Considerations - subsection distinguishing observability from enforcement. Bundled into the - same PR per CLASS1-03 review-convenience clause. - - `specs/NIP-5D.md` (in-repo) gains a - `### NIP-07 Extension Injection Residual` subsection documenting the - `all_frames: true` content-script injection vector, nonce-based `script-src` - legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical - lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and - `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as - the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. + - `NAP-IDENTITY.md` amended with the `identity.decrypt` envelope triad, full conformance table (4 shell MUSTs: class-gating, outer-sig-verify, impersonation-check on `seal.pubkey === rumor.pubkey` for NIP-17 flows, outer-`created_at` hiding to preserve NIP-59 ±2-day randomization privacy floor), 8-code error vocabulary, and Security Considerations subsection. Authored on branch `nap-identity-decrypt`. + - `NAP-CLASS-1.md` amended with SHOULD `report-to` row + MUST `(dTag, aggregateHash)` violation-correlation row + Security Considerations subsection distinguishing observability from enforcement. Bundled into the same PR per CLASS1-03 review-convenience clause. + - `specs/NIP-5D.md` (in-repo) gains a `### NIP-07 Extension Injection Residual` subsection documenting the `all_frames: true` content-script injection vector, nonce-based `script-src` legacy-injection mitigation (Chromium 144+ `script-src-elem` empirical lock), honest `world: 'MAIN'` extension-API residual acknowledgment, and `connect-src 'none'` structural mitigation pointing at `identity.decrypt` as the spec-legal receive-side decrypt path for NAP-CLASS-1 napplets. **Acceptance gates:** - `pnpm -r build` + `pnpm -r type-check` exit 0 across all 14 packages. - - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero - occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). - - Empirical Chromium 144+ verification: nonce-based `script-src` provably - blocks legacy `<script>`-tag content-script injection AND fires - `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and - `blockedURI: 'inline'` (4-field report shape captured). - - `assertNever` exhaustiveness proof captured: deliberate bogus union member - triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. + - Identity-types-only esbuild tree-shake bundle is 129 bytes and contains zero occurrences of the 7 forbidden runtime symbols (decryption gates VER-05). + - Empirical Chromium 144+ verification: nonce-based `script-src` provably blocks legacy `<script>`-tag content-script injection AND fires `securitypolicyviolation` with `violatedDirective: 'script-src-elem'` and `blockedURI: 'inline'` (4-field report shape captured). + - `assertNever` exhaustiveness proof captured: deliberate bogus union member triggers TS2345 at the `assertNever` call site in `handleIdentityMessage`. **Guidance:** - - `identity.decrypt` is the only spec-legal receive-side decrypt path. - Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 - browser extension injects `window.nostr` into the iframe via a content - script (architecturally unavoidable per WebExtension spec), shells MUST NOT - _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell - is free to use its own NIP-07 extension internally to fulfill - signing/encryption duties; what is forbidden is exposing `window.nostr` to - the napplet. - - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) - cannot decrypt — they receive `class-forbidden` at the shell boundary - because plaintext could exfiltrate to approved origins with zero shell - visibility. The class invariant ties plaintext exposure to zero-egress - posture. + - `identity.decrypt` is the only spec-legal receive-side decrypt path. Napplets MUST NOT call `window.nostr.*` for decrypt — even if a NIP-07 browser extension injects `window.nostr` into the iframe via a content script (architecturally unavoidable per WebExtension spec), shells MUST NOT _provide_ `window.nostr` to napplet iframes per NIP-5D §Transport. A shell is free to use its own NIP-07 extension internally to fulfill signing/encryption duties; what is forbidden is exposing `window.nostr` to the napplet. + - NAP-CLASS-2 napplets (user-approved direct-network posture per NAP-CONNECT) cannot decrypt — they receive `class-forbidden` at the shell boundary because plaintext could exfiltrate to approved origins with zero shell visibility. The class invariant ties plaintext exposure to zero-egress posture. ### Patch Changes @@ -945,11 +710,7 @@ ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 - tarballs on npm contained unresolved `workspace:*` specs in dependencies, - breaking installs. This patch bump exists solely to produce - correctly-assembled tarballs via `pnpm publish -r` (which rewrites - `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). - Updated dependencies - @napplet/core@0.2.1 - @napplet/nap-config@0.2.1 diff --git a/packages/vite-plugin/CHANGELOG.md b/packages/vite-plugin/CHANGELOG.md index 7a819e43..405d839d 100644 --- a/packages/vite-plugin/CHANGELOG.md +++ b/packages/vite-plugin/CHANGELOG.md @@ -4,81 +4,47 @@ ### Patch Changes -- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. +- Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. ## 0.16.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before - computing its artifact hash. Resolve plugin options, author declarations and - inferred capabilities consistently; add icon byte input while retaining - hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from - standalone HTML without a sidecar. Upload the same decoded icon bytes, respect - explicit deployment overrides and event-kind restrictions, and preserve - legacy-format behavior. Embedded metadata changes now change the built - artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. - Validate configured and recovered Blossom upload destinations before signing - manifests or uploading, with clear errors for missing values and non-origin - URLs. + Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. ## 0.15.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 - and 35129. Writers emit one direct artifact `x` hash, description content, - required/optional `R`/`O` domains and independent `z`/`i` advertisements. - Readers verify current events and raw artifact bytes; legacy events require - migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy - serialization. Add offline signed-event migration previews without altering - source events or publishing. Vite builds default to a self-contained artifact; - compatibility helper and sidecar names remain available. See the event - migration guide for package cutoffs, immutable pointers and shell ACL/storage - implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. ### Patch Changes -- Warn when explicit required domains are outside the built-in NAP list while - preserving their `R` declarations. Source inference remains limited to known - domains (#223). -- Builds without `dist/index.html` fail with a clear `[nip5a-manifest]` error - instead of a raw ENOENT, and `artifactMode: 'external-assets'` builds warn - that the manifest `x` hash covers only `index.html` and list the remaining - local assets. +- Warn when explicit required domains are outside the built-in NAP list while preserving their `R` declarations. Source inference remains limited to known domains (#223). +- Builds without `dist/index.html` fail with a clear `[nip5a-manifest]` error instead of a raw ENOENT, and `artifactMode: 'external-assets'` builds warn that the manifest `x` hash covers only `index.html` and list the remaining local assets. ## 0.14.1 ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload - conventions, and require structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing - key, and preserve `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. ## 0.14.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, - `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, - `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` - event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and - `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` - and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, - `FsLimits.maxWriteBytes`, byte range options, and read/write result counts - refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. ## 0.13.0 @@ -86,69 +52,47 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, - runtime-attested sender, and optional payload. The runtime no longer - fabricates a Nostr event. INC also exposes the symmetric channel API through - `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; - channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, - supports `behavior.newWindow`, and returns the canonical structured result - with required `ok`, `archetype`, `action`, and `handled` fields. Remove the - unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only - result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical - `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` - option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ## 0.12.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the - public types, bindings, runtime injection, manifest tooling, CLI, and - reference shell: queryless exact identities with URI-to-text-payload - transposition at `emit` and `invoke`/`open`, runtime-attested sender, - immediate acceptance followed by source-independent no-ID target delivery, and - optional same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ## 0.11.3 ### Patch Changes -- d6291de: Point generated projects to the current skills, remove undocumented - build-time config guidance, and keep hard `count` requirements from being - dropped by the Vite plugin. +- d6291de: Point generated projects to the current skills, remove undocumented build-time config guidance, and keep hard `count` requirements from being dropped by the Vite plugin. ## 0.11.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, - conformance, shim, and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. ## 0.11.1 ### Patch Changes -- 954b6bb: Improve JSR score readiness with module docs and explicit public API - types. +- 954b6bb: Improve JSR score readiness with module docs and explicit public API types. ## 0.11.0 ### Minor Changes -- fb434c3: Add `title` and `description` plugin options that inject/override the - built HTML `<title>` element and `<meta name="description">` element. Both are - plain HTML (not `napplet-*` protocol meta tags): when set they override any - existing tag (inserting one after `<head>` if absent) and when omitted the - author's HTML is left untouched. Injected values are HTML-escaped for their - context. The napplet CLI reads these back out of the built `index.html` to - emit the NIP-5A `title` / `description` manifest tags at deploy time. -- fb434c3: Add opt-in NAP requirement inference from static source imports and - direct `window.napplet.<domain>` usage. +- fb434c3: Add `title` and `description` plugin options that inject/override the built HTML `<title>` element and `<meta name="description">` element. Both are plain HTML (not `napplet-*` protocol meta tags): when set they override any existing tag (inserting one after `<head>` if absent) and when omitted the author's HTML is left untouched. Injected values are HTML-escaped for their context. The napplet CLI reads these back out of the built `index.html` to emit the NIP-5A `title` / `description` manifest tags at deploy time. +- fb434c3: Add opt-in NAP requirement inference from static source imports and direct `window.napplet.<domain>` usage. ## 0.10.1 @@ -161,9 +105,9 @@ surface is removed from active package APIs: no `window.napplet.shell`, no `shell.ready` / `shell.init` handshake, and no `@napplet/nap/shell` subpath. - Conformance now injects the runtime namespace before fixture code and - validates only NAP domain envelopes. Skills and package guidance now teach - domain-property presence instead of the retired shell supports API. + Conformance now injects the runtime namespace before fixture code and validates + only NAP domain envelopes. Skills and package guidance now teach domain-property + presence instead of the retired shell supports API. ## 0.10.0 @@ -171,244 +115,113 @@ - 51b2ff1: Align NAP-INTENT availability with manifest-derived contracts from `napplet/naps` PR #55. Intent candidates now expose required `contracts` - records, and the Vite plugin emits one archetype manifest tag per protocol - with optional per-protocol `kind:<number>` constraints. + records, and the Vite plugin emits one archetype manifest tag per protocol with + optional per-protocol `kind:<number>` constraints. ## 0.9.0 ### Minor Changes -- 6c99508: Emit NAAT archetype manifest tags (napplet/naps `ARCHETYPES.md`). - Resolves #58. +- 6c99508: Emit NAAT archetype manifest tags (napplet/naps `ARCHETYPES.md`). Resolves #58. - - New optional `archetypes` plugin option: - `Array<string | { slug: string; naps?: string[] }>`. Each entry emits one - `["archetype", slug, ...naps]` tag on the kind 35129 manifest, where the - string shorthand `"feed"` is equivalent to `{ slug: "feed" }`. A napplet may - declare several roles; declaring none stays fully valid. - - Like the `config` tag, archetype tags are excluded from the aggregate `x` - hash (NIP-5D §Identity: the aggregate is recomputed from `path` tags alone), - so declaring archetypes never changes a napplet's content address. + - New optional `archetypes` plugin option: `Array<string | { slug: string; naps?: string[] }>`. Each entry emits one `["archetype", slug, ...naps]` tag on the kind 35129 manifest, where the string shorthand `"feed"` is equivalent to `{ slug: "feed" }`. A napplet may declare several roles; declaring none stays fully valid. + - Like the `config` tag, archetype tags are excluded from the aggregate `x` hash (NIP-5D §Identity: the aggregate is recomputed from `path` tags alone), so declaring archetypes never changes a napplet's content address. ## 0.8.1 ### Patch Changes -- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets - ([napplet/web#53](https://github.com/napplet/web/issues/53)). - - Per NIP-5D a napplet is a single self-contained `/index.html` loaded via - `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an - opaque origin). There is no served origin from which to fetch an external - `<script src>`, so a napplet's executable JS **must** be inline. The toolchain - was enforcing the opposite under an invented "shell-as-CSP-authority / - `script-src 'self'`" model that NIP-5D never defines and that was justified by - NAP-CONNECT/strict-CSP — since deferred from the NAPs track. - - - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its - unconditional `closeBundle` call. The build no longer aborts on inline - scripts; `artifactMode: 'single-file'` still folds local script/style assets - into `index.html` and now preserves any pre-existing inline scripts - verbatim. - - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): - removed the `manifest/no-inline-scripts` check, the `inline-script` error - code, and the `findInlineScripts` export. A conformant napplet that carries - inline JS no longer fails conformance. +- Stop rejecting inline `<script>` — it broke spec-faithful `srcdoc` napplets ([napplet/web#53](https://github.com/napplet/web/issues/53)). + + Per NIP-5D a napplet is a single self-contained `/index.html` loaded via `iframe.srcdoc` with `sandbox="allow-scripts"` and no `allow-same-origin` (an opaque origin). There is no served origin from which to fetch an external `<script src>`, so a napplet's executable JS **must** be inline. The toolchain was enforcing the opposite under an invented "shell-as-CSP-authority / `script-src 'self'`" model that NIP-5D never defines and that was justified by NAP-CONNECT/strict-CSP — since deferred from the NAPs track. + + - **`@napplet/vite-plugin`**: removed `assertNoInlineScripts` and its unconditional `closeBundle` call. The build no longer aborts on inline scripts; `artifactMode: 'single-file'` still folds local script/style assets into `index.html` and now preserves any pre-existing inline scripts verbatim. + - **`@napplet/conformance`** (and bundled **`@napplet/conformance-cli`**): removed the `manifest/no-inline-scripts` check, the `inline-script` error code, and the `findInlineScripts` export. A conformant napplet that carries inline JS no longer fails conformance. ## 0.8.0 ### Minor Changes -- b75880f: Align the SDK to the updated NAPs track - ([github.com/napplet/naps](https://github.com/napplet/naps)). - - - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so - the `class` and `connect` domains are removed from the active surface: - `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / - `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` - subpaths, the shim installers + sdk re-exports, the conformance - validators/checks (incl. `manifest/connect-origins`), and the - `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and - `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. - - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → - `shell.init` bootstrap handshake is now a published NAP. `shell.init` - carries `{ capabilities: { domains, protocols }, services, class }`. - `window.napplet.shell` gains `services`, `class` (opaque integer), - `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside - `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the - NAP-SHELL types. The conformance engine recognizes - `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the - previous special-case) and cites NAP-SHELL in its boot/degradation checks. - - BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, - the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the - `@napplet/vite-plugin` `connect` option; migrates the `shell.init` - capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. +- b75880f: Align the SDK to the updated NAPs track ([github.com/napplet/naps](https://github.com/napplet/naps)). + + - **Defer NAP-CLASS and NAP-CONNECT.** Both are now inactive on the track, so the `class` and `connect` domains are removed from the active surface: `NAP_DOMAINS`/`NapDomain`, `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` + `@napplet/nap/connect` subpaths, the shim installers + sdk re-exports, the conformance validators/checks (incl. `manifest/connect-origins`), and the `@napplet/vite-plugin` `connect` option, manifest `connect` tags, and `napplet-connect-requires` meta. `NAP_DOMAINS` now has 14 entries. + - **Implement NAP-SHELL** (mandatory, foundational). The `shell.ready` → `shell.init` bootstrap handshake is now a published NAP. `shell.init` carries `{ capabilities: { domains, protocols }, services, class }`. `window.napplet.shell` gains `services`, `class` (opaque integer), `ready(): Promise<ShellEnvironment>`, and `onReady(handler)` alongside `supports(domain, protocol?)`. A new `@napplet/nap/shell` subpath ships the NAP-SHELL types. The conformance engine recognizes `shell.ready`/`shell.init` as the foundational `shell` domain (dropping the previous special-case) and cites NAP-SHELL in its boot/degradation checks. + + BREAKING CHANGE: removes `window.napplet.class` / `window.napplet.connect`, the `@napplet/nap/class` and `@napplet/nap/connect` subpaths, and the `@napplet/vite-plugin` `connect` option; migrates the `shell.init` capabilities shape from `{ naps, sandbox }` to `{ domains, protocols }`. ## 0.7.0 ### Minor Changes -- 06cfecf: Stop emitting the `napplet-aggregate-hash` meta tag into - `index.html`. A file cannot contain a hash that covers itself (the hash - includes `index.html`), so the tag was either empty or — when a signing key - was set — written back _after_ the hash was computed, leaving the advertised - hash permanently inconsistent with the file. It is also not a NIP-5D/5A - artifact. The aggregate hash now lives only in the external - `.nip5a-manifest.json` and the signed kind-35129 event, where the shell/relay - reads it; removing the post-hash rewrite makes that manifest internally - consistent. +- 06cfecf: Stop emitting the `napplet-aggregate-hash` meta tag into `index.html`. A file + cannot contain a hash that covers itself (the hash includes `index.html`), so the + tag was either empty or — when a signing key was set — written back _after_ the + hash was computed, leaving the advertised hash permanently inconsistent with the + file. It is also not a NIP-5D/5A artifact. The aggregate hash now lives only in + the external `.nip5a-manifest.json` and the signed kind-35129 event, where the + shell/relay reads it; removing the post-hash rewrite makes that manifest + internally consistent. ## 0.6.0 ### Minor Changes -- Align the generated napplet manifest with NIP-5D and the NIP-5A tag schema it - adopts. - - - Emit NIP-5D's own kind `35129` (named napplet) instead of NIP-5A's nsite - kind `35128`. New `NAPPLET_KIND_NAMED` / `NAPPLET_KIND_ROOT` / - `NAPPLET_KIND_SNAPSHOT` exports replace the removed `SYNTHETIC_XTAG_PATHS` - export. - - Emit a NIP-5A-conformant manifest: one `["path", "/abs/path", "<sha256>"]` - tag per file (absolute paths) plus a single aggregate - `["x", "<aggregateHash>", "aggregate"]` tag, replacing the previous per-file - `["x", hash, relpath]` projection. - - Compute `aggregateHash` from the `path` tags alone (NIP-5D §Identity), so a - runtime can recompute and verify it. `config` / `connect` capability tags - are still emitted but are no longer folded into the aggregate; grant - invalidation on a capability change is keyed on those tags at the shell - layer. - - BREAKING: manifest output changes (kind, tag shape, aggregate inputs) and the - `SYNTHETIC_XTAG_PATHS` export is removed. +- Align the generated napplet manifest with NIP-5D and the NIP-5A tag schema it adopts. + + - Emit NIP-5D's own kind `35129` (named napplet) instead of NIP-5A's nsite kind `35128`. New `NAPPLET_KIND_NAMED` / `NAPPLET_KIND_ROOT` / `NAPPLET_KIND_SNAPSHOT` exports replace the removed `SYNTHETIC_XTAG_PATHS` export. + - Emit a NIP-5A-conformant manifest: one `["path", "/abs/path", "<sha256>"]` tag per file (absolute paths) plus a single aggregate `["x", "<aggregateHash>", "aggregate"]` tag, replacing the previous per-file `["x", hash, relpath]` projection. + - Compute `aggregateHash` from the `path` tags alone (NIP-5D §Identity), so a runtime can recompute and verify it. `config` / `connect` capability tags are still emitted but are no longer folded into the aggregate; grant invalidation on a capability change is keyed on those tags at the shell layer. + + BREAKING: manifest output changes (kind, tag shape, aggregate inputs) and the `SYNTHETIC_XTAG_PATHS` export is removed. ## 0.5.0 ### Minor Changes -- Rename the public package and protocol surface to NAP terminology, including - `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` - identifiers. +- Rename the public package and protocol surface to NAP terminology, including `@napplet/nap`, `packages/nap`, `registerNap`, `nap:*`, and `NAP-*` identifiers. ## 0.4.0 ### Minor Changes -- 73d5ffc: Add an explicit `artifactMode: 'single-file'` build contract for - NIP-5A gateway-portable napplet artifacts. In single-file mode the plugin asks - Vite/Rollup for a single-entry artifact shape, inlines local Vite JS/CSS - assets into `index.html` before manifest/hash generation, fails if local - external assets remain, accepts those build-produced inline module scripts - intentionally, and keeps config/connect synthetic hash inputs participating in - `aggregateHash`. +- 73d5ffc: Add an explicit `artifactMode: 'single-file'` build contract for NIP-5A gateway-portable napplet artifacts. In single-file mode the plugin asks Vite/Rollup for a single-entry artifact shape, inlines local Vite JS/CSS assets into `index.html` before manifest/hash generation, fails if local external assets remain, accepts those build-produced inline module scripts intentionally, and keeps config/connect synthetic hash inputs participating in `aggregateHash`. ## 0.3.0 ### Minor Changes -- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP - emission from the vite-plugin (build time) to the host shell (runtime) for - every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative - abstract security posture via `class.assigned` wire) and NAP-CONNECT - (user-gated direct network access declared via `connect` manifest tags). +- 3d22a10: v0.29.0 — NAP-CONNECT + Shell as CSP Authority. Shifts strict CSP emission from the vite-plugin (build time) to the host shell (runtime) for every napplet, and introduces two new NAPs: NAP-CLASS (shell-authoritative abstract security posture via `class.assigned` wire) and NAP-CONNECT (user-gated direct network access declared via `connect` manifest tags). **Breaking surface (migration required):** - - `@napplet/vite-plugin` **strictCsp removed from production path**. The - option is retained as an `@deprecated` accept-but-warn shim for one release - cycle — the plugin emits one `console.warn` per build per config-resolved if - a `strictCsp` value is present, and the option is ignored. Production builds - no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is - now the sole authority on runtime CSP for every napplet, per - `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a - future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for - v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). - - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` - scans `dist/index.html` and throws a diagnostic when any `<script>` element - lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so - inline scripts would be blocked at runtime anyway; failing at build time - surfaces the problem before deployment. The allow-list exempts - `application/json`, `application/ld+json`, `importmap`, and - `speculationrules` blocks. - - `@napplet/core` **perm:strict-csp is now @deprecated** on - `NamespacedCapability`. The template-literal type still accepts it so - existing code type-checks; capability advertisement is superseded by - `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as - `REMOVE-STRICTCSP-CAP`. + - `@napplet/vite-plugin` **strictCsp removed from production path**. The option is retained as an `@deprecated` accept-but-warn shim for one release cycle — the plugin emits one `console.warn` per build per config-resolved if a `strictCsp` value is present, and the option is ignored. Production builds no longer emit `<meta http-equiv="Content-Security-Policy">`. The shell is now the sole authority on runtime CSP for every napplet, per `specs/SHELL-CLASS-POLICY.md`. The option is scheduled for hard-remove in a future milestone (tracked as `REMOVE-STRICTCSP`; originally scheduled for v0.30.0 but deferred when v0.30.0 shipped Class-Gated Decrypt instead). + - `@napplet/vite-plugin` **inline scripts now break the build.** `closeBundle` scans `dist/index.html` and throws a diagnostic when any `<script>` element lacks a `src` attribute. The shell's baseline CSP is `script-src 'self'`, so inline scripts would be blocked at runtime anyway; failing at build time surfaces the problem before deployment. The allow-list exempts `application/json`, `application/ld+json`, `importmap`, and `speculationrules` blocks. + - `@napplet/core` **perm:strict-csp is now @deprecated** on `NamespacedCapability`. The template-literal type still accepts it so existing code type-checks; capability advertisement is superseded by `nap:connect` + `nap:class` in v0.29.0. Hard-removal tracked as `REMOVE-STRICTCSP-CAP`. **New surface:** - - `@napplet/vite-plugin` **`connect?: string[]`** option declares required - origins. Each origin is validated via the shared `normalizeConnectOrigin()` - validator from `@napplet/nap/connect`, emitted as a signed - `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via - a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → - UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). - Dev-mode-only `<meta name="napplet-connect-requires">` is injected for - shell-less `vite serve` previews; the shell-authoritative - `napplet-connect-granted` meta is NEVER emitted by the plugin. - - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 - entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` - (mirrors `resource` block) and `class?: number` (optional — undefined until - `class.assigned` wire arrives or when `nap:class` unsupported). - - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `NappletConnect` interface, pure - `normalizeConnectOrigin(origin)` validator (shared source-of-truth for - build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests - pass), and `installConnectShim()` which reads - `<meta name="napplet-connect-granted">` at install time. - - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / - `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type - (`{ type: 'class.assigned'; id: string; class: number }`), - `handleClassMessage` dispatcher handler, and `installClassShim()` which - mounts `window.napplet.class` as a readonly getter. - - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** - defaults to `{granted: false, origins: []}` (never `undefined`); - **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). - Central dispatcher routes `class.*` envelopes to the class shim's handler. - - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the - `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + - the `class` types namespace — parallel to the existing `resource` - re-exports. + - `@napplet/vite-plugin` **`connect?: string[]`** option declares required origins. Each origin is validated via the shared `normalizeConnectOrigin()` validator from `@napplet/nap/connect`, emitted as a signed `["connect", "<origin>"]` manifest tag, and folded into `aggregateHash` via a synthetic `connect:origins` entry (lowercase → ASCII-sort → LF-join → UTF-8 → SHA-256 → lowercase hex per NAP-CONNECT §Canonical Fold). Dev-mode-only `<meta name="napplet-connect-requires">` is injected for shell-less `vite serve` previews; the shell-authoritative `napplet-connect-granted` meta is NEVER emitted by the plugin. + - `@napplet/core` **`NapDomain` union gains `'connect'` and `'class'`** (12 entries in `NAP_DOMAINS`). `NappletGlobal` gains `connect: NappletConnect` (mirrors `resource` block) and `class?: number` (optional — undefined until `class.assigned` wire arrives or when `nap:class` unsupported). + - `@napplet/nap/connect` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `NappletConnect` interface, pure `normalizeConnectOrigin(origin)` validator (shared source-of-truth for build-side and shell-side — 21 rule violations enumerated, 28/28 smoke tests pass), and `installConnectShim()` which reads `<meta name="napplet-connect-granted">` at install time. + - `@napplet/nap/class` **4-file subpath** (`types` / `shim` / `sdk` / `index`). Exports `DOMAIN`, `ClassAssignedMessage` wire type (`{ type: 'class.assigned'; id: string; class: number }`), `handleClassMessage` dispatcher handler, and `installClassShim()` which mounts `window.napplet.class` as a readonly getter. + - `@napplet/shim` bootstraps both installers; **`window.napplet.connect`** defaults to `{granted: false, origins: []}` (never `undefined`); **`window.napplet.class`** defaults to `undefined` (never `0` or `null`). Central dispatcher routes `class.*` envelopes to the class shim's handler. + - `@napplet/sdk` adds **`CONNECT_DOMAIN`** + **`installConnectShim`** + the `connect` types namespace, and **`CLASS_DOMAIN`** + **`installClassShim`** + the `class` types namespace — parallel to the existing `resource` re-exports. **Guidance:** - - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte - fetches — it works in every class posture. Reach for NAP-CONNECT only when - the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, - WebSocket, Server-Sent Events, streaming responses, custom request headers, - long-lived connections, or third-party libraries that call `fetch`/open - sockets directly. - - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: - `class === 2` iff `connect.granted === true` at `class.assigned` send time. - See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full - scenario table. - - Demo napplets remain in the downstream shell repo (Option B carried forward - from v0.28.0). This SDK ships spec + code only. + - Default to NAP-RESOURCE (`window.napplet.resource.bytes(url)`) for byte fetches — it works in every class posture. Reach for NAP-CONNECT only when the resource NAP cannot express what you need: `POST`/`PUT`/`PATCH` methods, WebSocket, Server-Sent Events, streaming responses, custom request headers, long-lived connections, or third-party libraries that call `fetch`/open sockets directly. + - Shells implementing BOTH NAPs MUST maintain the cross-NAP invariant: `class === 2` iff `connect.granted === true` at `class.assigned` send time. See `specs/SHELL-CLASS-POLICY.md` § Cross-NAP Invariant for the full scenario table. + - Demo napplets remain in the downstream shell repo (Option B carried forward from v0.28.0). This SDK ships spec + code only. **Migration path from v0.28.0:** - 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn - once per build if you leave it. (Required when REMOVE-STRICTCSP lands; - optional through v0.30.0.) - 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move - their bodies to external `.js` files imported as modules. - 3. If your napplet needs direct network access, declare origins via the - plugin's new `connect: string[]` option. See - `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your - host must satisfy. - 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` - before reading `window.napplet.class` / `window.napplet.connect` — both - gracefully degrade (`undefined` / `{granted: false, origins: []}` - respectively) on non-implementing shells. + 1. Drop any `strictCsp` option from `vite.config.ts` — the plugin will warn once per build if you leave it. (Required when REMOVE-STRICTCSP lands; optional through v0.30.0.) + 2. Audit `dist/index.html` for inline `<script>` elements without `src`. Move their bodies to external `.js` files imported as modules. + 3. If your napplet needs direct network access, declare origins via the plugin's new `connect: string[]` option. See `specs/SHELL-CONNECT-POLICY.md` for the shell-deployer preconditions your host must satisfy. + 4. Check `shell.supports('nap:class')` and `shell.supports('nap:connect')` before reading `window.napplet.class` / `window.napplet.connect` — both gracefully degrade (`undefined` / `{granted: false, origins: []}` respectively) on non-implementing shells. ## 0.2.1 ### Patch Changes -- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 - tarballs on npm contained unresolved `workspace:*` specs in dependencies, - breaking installs. This patch bump exists solely to produce - correctly-assembled tarballs via `pnpm publish -r` (which rewrites - `workspace:*` → concrete versions at pack time). +- Republish at 0.2.1 to ship resolved workspace dependency versions. The 0.2.0 tarballs on npm contained unresolved `workspace:*` specs in dependencies, breaking installs. This patch bump exists solely to produce correctly-assembled tarballs via `pnpm publish -r` (which rewrites `workspace:*` → concrete versions at pack time). From 7371838a923210c7435b61a2760b4c009593f573 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Tue, 13 Oct 2026 23:08:48 -0700 Subject: [PATCH 20/24] docs(cli): clarify runner publication prerequisite Record the shared npm ETARGET failure across all five standalone platforms and restore the existing changelog formatting. Co-Authored-By: Codex <noreply@openai.com> --- .planning/debug/pr243-unpublished-runner.md | 34 +++ packages/cli/CHANGELOG.md | 238 +++++++------------- 2 files changed, 116 insertions(+), 156 deletions(-) create mode 100644 .planning/debug/pr243-unpublished-runner.md diff --git a/.planning/debug/pr243-unpublished-runner.md b/.planning/debug/pr243-unpublished-runner.md new file mode 100644 index 00000000..1ea8bd65 --- /dev/null +++ b/.planning/debug/pr243-unpublished-runner.md @@ -0,0 +1,34 @@ +--- +status: awaiting-release-approval +trigger: "PR #243 tests are red across the board" +created: 2026-10-13 +updated: 2026-10-13 +--- + +# PR #243 runner publication dependency + +## Symptoms + +- Expected: the standalone CLI provisions its pinned tools from npm with an empty PATH and a fresh cache on all five platforms. +- Actual: every job in run 38299057704 fails during the first screenshot command. +- Error: `No matching version found for @napplet/conformance-cli@0.3.4.` +- Reproduction: install that exact package version from npm; it is absent. Latest published version is 0.3.3. + +## Current Focus + +- Hypothesis: this is the documented release dependency, not five independent platform defects. +- Evidence: every platform has the same npm ETARGET error. PR #242 remains unmerged and contains conformance CLI 0.3.4. Its nine checks pass and GitHub reports it mergeable. Kehto CLI 0.5.5 and Paja 0.21.0 are now published. +- Next action: merge #242 and wait for publication only if the user authorizes that separate feature release. Retarget and rerun #243 after the real package exists. + +## Evidence + +- [Failed standalone run](https://github.com/napplet/web/actions/runs/38299057704): Linux x64/ARM64, macOS x64/ARM64, and Windows all fail on the same missing package. +- [PR #242](https://github.com/napplet/web/pull/242), head `44d663a14af0f91a3797614c76e5c3d684cec52e`: CI, conformance, links, slop, and all five standalone jobs passed. Release versions are already applied and no unconsumed changeset remains. +- npm registry checked directly: conformance CLI 0.3.4 absent; Kehto CLI 0.5.5 and Paja 0.21.0 present. +- The pinned package installation is the behavior under test. Skipping the check, substituting a local package, or downgrading to the old runner would conceal or remove the intended release behavior. + +## Resolution + +Root cause confirmed. Updated the stacked branch with #242's latest reviewed fixes and restored the CLI changelog's existing unwrapped history, leaving one focused 0.9.1 entry. Local verification passed: build (12 tasks), type-check (18 tasks), recursive unit tests, and diff whitespace checks. AI-slop passed at 98/100 with zero errors; the existing `fflate` dependency advisory is the sole warning. + +Publication and final fresh-cache CI verification remain pending. The user was asked to authorize merging #242 because that ships a separate feature release. No dependency downgrade, local-package substitution, or test bypass was added. diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 652862b4..e2f18459 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -4,22 +4,15 @@ ### Patch Changes -- e7a5565: Select the GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 in the private - runner cache. Merge after napplet/web #242 publishes conformance CLI 0.3.4 and kehto/web #297 - publishes CLI 0.5.5/Paja 0.21.0; this avoids a self-publication cycle in fresh-cache tests. +- Select the GAMEPAD-capable conformance CLI 0.3.4 and Kehto CLI 0.5.5 in the private runner cache. These package versions must be published before this CLI release. ## 0.9.0 ### Minor Changes -- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser - tooling, or keychain utilities. Browser and Paja commands provision private cached tooling - automatically; screenshots and conformance share it. Linux browser packs include libraries and - fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. +- Run standalone CLI commands without installed host runtimes, package managers, Git, Kehto, browser tooling, or keychain utilities. Browser and Paja commands provision private cached tooling automatically; screenshots and conformance share it. Linux browser packs include libraries and fonts for x64 and ARM64. Hosts without native keychains use password-encrypted local storage. - Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now - require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is - asynchronous because it may provision private tooling. + Remote scaffolding downloads GitHub or HTTP(S) ZIP templates without Git. Other Git transports now require a local directory or archive URL. The exported CLI `resolveConformanceCommand` helper is asynchronous because it may provision private tooling. ### Patch Changes @@ -29,53 +22,43 @@ ### Patch Changes -- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and - optional Zapstore application events before signing. +- Add napplet.run client attribution to current and legacy deployment manifests, snapshots, and optional Zapstore application events before signing. ## 0.8.8 ### Patch Changes -- Link current deployment manifests to optional Zapstore application metadata using the proposed - NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata - before referencing manifests. +- Link current deployment manifests to optional Zapstore application metadata using the proposed NIP-5D app reference, allowing clients to discover screenshots. Publish application metadata before referencing manifests. ## 0.8.7 ### Patch Changes -- Pin screenshot and default conformance commands to the tested browser runner so older - project-local dependencies cannot intercept them. Preserve custom conformance commands. +- Pin screenshot and default conformance commands to the tested browser runner so older project-local dependencies cannot intercept them. Preserve custom conformance commands. ## 0.8.6 ### Patch Changes -- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to - override the URL or suppress source publication with false. +- Infer missing NIP-5D source tags from the selected build's Git origin. Add metadata.source to override the URL or suppress source publication with false. ## 0.8.5 ### Patch Changes -- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install - Chromium on first use and retain explicit preview URLs. +- Capture deploy screenshots without a host URL using an automatic local Kehto Paja preview. Install Chromium on first use and retain explicit preview URLs. ## 0.8.4 ### Patch Changes -- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run - previews, publication failure reporting, the preview screenshot command, and inline capture with - deploy --screenshot. +- Add optional Zapstore application metadata publishing with local screenshot uploads, dry-run previews, publication failure reporting, the preview screenshot command, and inline capture with deploy --screenshot. ## 0.8.3 ### Patch Changes -- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype - input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy - CLI input, and correct setup prompts, reports, and documentation. +- 2910c4b: Accept queryless convention URIs such as `napplet:note/open` directly in CLI archetype input and JSON metadata. Derive the role from the URI, preserve existing object configs and legacy CLI input, and correct setup prompts, reports, and documentation. - Updated dependencies [2910c4b] - @napplet/boilerplate@0.3.4 @@ -83,72 +66,44 @@ ### Patch Changes -- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed - package version in JSR and standalone installations. +- f33d9d6: Add `napplet --version`, `napplet -v`, and `napplet version` to report the installed package version in JSR and standalone installations. ## 0.8.1 ### Patch Changes -- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and - naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover - opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment - API independently of other domain calls. Align INC provenance with authenticated endpoint - identifiers and deliver theme/identity updates only for exposed domains. +- Follow the NAP-INTENT lifecycle and manifest amendments: accept authoritative convention URIs and naddr recommendations, expose acceptance results and buffered onDelivery callbacks, and discover opaque catalog identifiers with parameter contracts. Restore the optional NAP-SHELL environment API independently of other domain calls. Align INC provenance with authenticated endpoint identifiers and deliver theme/identity updates only for exposed domains. - INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, - options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing - IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper - extraction preserves existing exports and behavior. + INTENT callers migrate from archetype/request arguments to invoke(uri, options) and open(uri, options), from handled/windowId to acceptance, and from candidate dTag to id/contracts. Existing IntentOpenOptions and named SDK helpers remain exported with the URI signatures. CLI helper extraction preserves existing exports and behavior. ## 0.8.0 ### Minor Changes -- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact - hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon - byte input while retaining hash-only callers. +- b3c8ace: Preserve NIP-5D publishing metadata in the built HTML head before computing its artifact hash. Resolve plugin options, author declarations and inferred capabilities consistently; add icon byte input while retaining hash-only callers. - Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML - without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and - event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change - the built artifact hash. + Recover current-format deploy metadata and supported data-URL icon bytes from standalone HTML without a sidecar. Upload the same decoded icon bytes, respect explicit deployment overrides and event-kind restrictions, and preserve legacy-format behavior. Embedded metadata changes now change the built artifact hash. - Validate configured and recovered Blossom upload destinations before signing manifests or - uploading, with clear errors for missing values and non-origin URLs. + Validate configured and recovered Blossom upload destinations before signing manifests or uploading, with clear errors for missing values and non-origin URLs. ## 0.7.0 ### Minor Changes -- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers - emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and - independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy - events require migration or older readers. +- Adopt the current NIP-5D artifact-hash manifest schema for kinds 5129, 15129 and 35129. Writers emit one direct artifact `x` hash, description content, required/optional `R`/`O` domains and independent `z`/`i` advertisements. Readers verify current events and raw artifact bytes; legacy events require migration or older readers. - CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add - offline signed-event migration previews without altering source events or publishing. Vite builds - default to a self-contained artifact; compatibility helper and sidecar names remain available. See - the event migration guide for package cutoffs, immutable pointers and shell ACL/storage - implications. + CLI deployment defaults to current output and offers temporary explicit legacy serialization. Add offline signed-event migration previews without altering source events or publishing. Vite builds default to a self-contained artifact; compatibility helper and sidecar names remain available. See the event migration guide for package cutoffs, immutable pointers and shell ACL/storage implications. ### Patch Changes -- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, - including trimmed non-empty requirements from legacy sidecars (#223). -- Current deploys fail with the list of built files they would drop instead of silently uploading - only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead - of crashing and accepts `--format current|legacy`; a domain listed as both required and optional - is emitted only as `O`. +- Preserve unknown required domains from plugin sidecars in both current and legacy deployments, including trimmed non-empty requirements from legacy sidecars (#223). +- Current deploys fail with the list of built files they would drop instead of silently uploading only `/index.html`; `napplet debug` reports manifest template errors in `manifests.error` instead of crashing and accepts `--format current|legacy`; a domain listed as both required and optional is emitted only as `O`. ## 0.6.0 ### Minor Changes -- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent - skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the - open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and - the `napplet create` next-steps point at that command. +- 976ad05: Remove the `napplet skills` subcommand and the bundled `@napplet/skills` installer. Agent skills now live in the repository's `skills/` directory as `napplet-*` skills and install with the open skills.sh CLI: `npx skills add napplet/napplet`. `napplet guide`, `napplet init` output, and the `napplet create` next-steps point at that command. ### Patch Changes @@ -159,9 +114,7 @@ ### Patch Changes -- b17e944: Make standalone release binaries dispatch bundled create and skills package code without - a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner - compatibility API, and expose import-safe callable CLI entry points from the maintained packages. +- b17e944: Make standalone release binaries dispatch bundled create and skills package code without a runtime Node.js or package-resolver dependency, preserve the JSR CLI's package-runner compatibility API, and expose import-safe callable CLI entry points from the maintained packages. - Updated dependencies [b17e944] - @napplet/boilerplate@0.3.2 - @napplet/skills@0.3.2 @@ -170,27 +123,18 @@ ### Patch Changes -- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require - structured invoke results. -- 2e25f92: Write the build manifest template even without a development signing key, and preserve - `count` requirements during CLI deploys. +- b3f0007: Accept independent NAP-INTENT archetype roles and payload conventions, and require structured invoke results. +- 2e25f92: Write the build manifest template even without a development signing key, and preserve `count` requirements during CLI deploys. ## 0.5.0 ### Minor Changes -- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access - ([NAP-FS](https://github.com/napplet/naps/pull/88)). +- d201bd0: Add the `fs` NAP domain — shell-mediated virtual filesystem access ([NAP-FS](https://github.com/napplet/naps/pull/88)). - Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, - `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — - plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, - `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, - normalization, policy, and authorization; the napplet sees only virtual paths. + Ships the virtual filesystem operations — `info`, `pickFile`, `pickFiles`, `pickDirectory`, `pickSaveFile`, `stat`, `list`, `read`, `write`, `mkdir`, `remove`, `move`, `watch`, `unwatch` — plus the runtime-pushed `fs.changed` event, reachable through `window.napplet.fs`, `@napplet/nap/fs`, and `@napplet/sdk`. The runtime owns host paths, mounts, backing store, normalization, policy, and authorization; the napplet sees only virtual paths. - Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and - `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte - range options, and read/write result counts refer to decoded bytes. + Byte transfer uses RFC 4648 standard padded base64 text for `fs.write.data` and `FsReadResult.data` on the JSON wire. `FsLimits.maxReadBytes`, `FsLimits.maxWriteBytes`, byte range options, and read/write result counts refer to decoded bytes. ## 0.4.0 @@ -198,82 +142,79 @@ - 7b67562: Align INC and INTENT with their merged canonical contracts. - INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested - sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the - symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and - `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including - bounded early-notification replay and terminal close retention. + INC topic subscriptions now receive one `IncEvent` containing the exact topic, runtime-attested sender, and optional payload. The runtime no longer fabricates a Nostr event. INC also exposes the symmetric channel API through `channel.open`, `channel.onOpened`, `channel.list`, and `channel.broadcast`; channel handles support `emit`, `on`, `onClosed`, and `close`, including bounded early-notification replay and terminal close retention. - INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports - `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, - `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, - `intent.deliver`, and acceptance-only result model. + INTENT now accepts `invoke(request)` and `open(archetype, payload?, opts?)`, supports `behavior.newWindow`, and returns the canonical structured result with required `ok`, `archetype`, `action`, and `handled` fields. Remove the unmerged URI invocation, `onDelivery`, `intent.deliver`, and acceptance-only result model. - Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove - the draft-only `eventKinds` option and trailing `kind:<number>` fields. + Archetype metadata now emits only the canonical `["archetype", slug, convention]` shape. Remove the draft-only `eventKinds` option and trailing `kind:<number>` fields. ## 0.3.0 ### Minor Changes -- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public types, - bindings, runtime injection, manifest tooling, CLI, and reference shell: queryless exact - identities with URI-to-text-payload transposition at `emit` and `invoke`/`open`, runtime-attested - sender, immediate acceptance followed by source-independent no-ID target delivery, and optional - same-tag event-kind discovery metadata. +- dd7b3a7: Adopt the draft PR #89-#91 convention and intent contracts across the public + types, bindings, runtime injection, manifest tooling, CLI, and reference shell: + queryless exact identities with URI-to-text-payload transposition at `emit` and + `invoke`/`open`, runtime-attested sender, immediate acceptance followed by + source-independent no-ID target delivery, and optional same-tag event-kind + discovery metadata. ### Patch Changes -- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor NIP-5A - constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface that rejected - spec-conformant napplets — `napplet deploy` now accepts any non-empty `[a-z0-9-]` `d` tag (still - rejecting trailing `-`), and `napplet deploy ---all` no longer throws on workspace folders with - names longer than 13 characters. +- 4916777: Remove the 13-character upper bound on named napplet `d` tags. Neither NIP-5D nor + NIP-5A constrains `d` tag length, so `^[a-z0-9-]{1,13}$` was CLI-invented surface + that rejected spec-conformant napplets — `napplet deploy` now accepts any + non-empty `[a-z0-9-]` `d` tag (still rejecting trailing `-`), and `napplet deploy +--all` no longer throws on workspace folders with names longer than 13 + characters. ## 0.2.1 ### Patch Changes -- b335c40: Run the default conformance tool through its npm package instead of requiring a global - `napplet-conformance` executable, and preserve Kehto's managed-command separator when - `napplet paja` wraps a local app server. +- b335c40: Run the default conformance tool through its npm package instead of requiring a + global `napplet-conformance` executable, and preserve Kehto's managed-command + separator when `napplet paja` wraps a local app server. ## 0.2.0 ### Minor Changes -- 49a8658: Make developer onboarding CLI-first from installation through deployment. The Napplet CLI - now creates starters, owns deployment metadata, installs agent skills, prints a linked developer - guide, and ships as checksum-verified standalone binaries. The boilerplate generator no longer - prompts for deployment metadata, and the bundled skills teach the same ordered workflow as the - CLI, docs, and web app. +- 49a8658: Make developer onboarding CLI-first from installation through deployment. The + Napplet CLI now creates starters, owns deployment metadata, installs agent + skills, prints a linked developer guide, and ships as checksum-verified + standalone binaries. The boilerplate + generator no longer prompts for deployment metadata, and the bundled skills + teach the same ordered workflow as the CLI, docs, and web app. ## 0.1.3 ### Patch Changes -- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden Enter-based - secret prompts, terminal deploy reports with NIP-19 pointers, configured-bunker reconnects, and - raw `bunker://` signing while preserving JSON output for CI. Root-source deploys now keep local - control state such as `.napplet/config.json`, hidden files, and `node_modules` out of signed - manifest content. Relay and Blossom suggestions are now Tab-completion candidates instead of - numbered selections, and Nostr Connect asks for bunker relays separately from deploy relays with - `wss://bucket.coracle.social` as the default. Nostr Connect pairing and remote signing now use - applesauce-signers so QR flows accept the `ack` responses emitted by common bunkers and match - nsyte behavior. Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt - but fall back to unscoped and legacy base64 auth for deployed servers that reject otherwise valid - uploads. Network deploys now treat unavailable redundant Blossom mirrors as warnings and continue - to relay publication whenever at least one server holds every required blob, while still failing - when no configured server is complete. Human reports now share the command's success predicate, so - redundant relay misses are also warnings whenever every manifest reaches another relay. +- faf9763: Improve interactive CLI deploy/init UX with guided init suggestions, hidden + Enter-based secret prompts, terminal deploy reports with NIP-19 pointers, + configured-bunker reconnects, and raw `bunker://` signing while preserving JSON + output for CI. Root-source deploys now keep local control state such as + `.napplet/config.json`, hidden files, and `node_modules` out of signed manifest + content. Relay and Blossom suggestions are now Tab-completion candidates instead + of numbered selections, and Nostr Connect asks for bunker relays separately from + deploy relays with `wss://bucket.coracle.social` as the default. + Nostr Connect pairing and remote signing now use applesauce-signers so QR flows + accept the `ack` responses emitted by common bunkers and match nsyte behavior. + Blossom uploads now keep the BUD-11 scoped base64url token as the first attempt + but fall back to unscoped and legacy base64 auth for deployed servers that reject + otherwise valid uploads. + Network deploys now treat unavailable redundant Blossom mirrors as warnings and + continue to relay publication whenever at least one server holds every required + blob, while still failing when no configured server is complete. Human reports + now share the command's success predicate, so redundant relay misses are also + warnings whenever every manifest reaches another relay. ## 0.1.2 ### Patch Changes -- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, - and Vite plugin packages. +- 35a593a: Improve the JSR-facing package overview and README content for CLI, conformance, shim, and Vite plugin packages. ## 0.1.1 @@ -285,35 +226,20 @@ ### Minor Changes -- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, - manifest, and signing readiness state. -- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` - command surfaces for interactive/local signing references. -- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is - available. -- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using - canonical manifest kind values, path tags, aggregate hashes, and server hints. +- fb434c3: Add a read-only `napplet debug` command for inspecting config, discovery, deploy-plan, manifest, and signing readiness state. +- fb434c3: Add native platform key storage providers and `napplet keys store/use/list/delete/doctor` command surfaces for interactive/local signing references. +- fb434c3: Sign dry-run root and named manifest templates when a local hex or nsec private key is available. +- fb434c3: Build NIP-5A root/named/snapshot deploy manifest templates in dry-run output using canonical manifest kind values, path tags, aggregate hashes, and server hints. - fb434c3: Support nsyte-compatible nbunksec remote signing for CI and stored-key deploy flows. -- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and - publish signed manifest events to configured relays. -- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy - manifests. -- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are - available. -- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value - `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and - `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing - values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and - companion snapshot manifests alongside the existing `requires` passthrough. -- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet - discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper - command surfaces. -- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// - paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and - points `.napplet` signing.keyReference at it. +- fb434c3: Enable local-signer network deploys that upload files to configured Blossom servers and publish signed manifest events to configured relays. +- fb434c3: Preserve plugin-emitted `requires` tags when building root, named, and snapshot deploy manifests. +- fb434c3: Generate and sign dry-run snapshot manifest templates when local signer pubkeys are available. +- fb434c3: Read the built `index.html` at deploy time and emit the NIP-5A single-value `["title", …]` and `["description", …]` manifest tags from its plain-HTML `<title>` element and `<meta name="description">` element. Values are entity-decoded and trimmed; empty or missing values emit no tag, at most one of each is emitted, and the tags propagate to root, named, and companion snapshot manifests alongside the existing `requires` passthrough. +- fb434c3: Add the initial Deno CLI package with `.napplet` config initialization, napplet discovery, dry-run deploy planning, signing-method classification, and conformance/Paja wrapper command surfaces. +- fb434c3: Add `napplet keys connect` NIP-46 remote signer login (nostrconnect QR + bunker:// paste). It pairs a remote signer, stores the resulting nbunksec in the platform keychain, and points `.napplet` signing.keyReference at it. ### Patch Changes -- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with - installation, quick-start, command, signing, project-layout, and troubleshooting guidance. +- b7d6561: Expose the CLI entrypoint as a JSR installable subpath and refresh the README with installation, + quick-start, command, signing, project-layout, and troubleshooting guidance. - fb434c3: Publish NIP-5D napplet manifest kinds from deploy commands From 3c3f1a660cd42724b06e8c7c78cbd4e7640d3a3a Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:38:57 -0700 Subject: [PATCH 21/24] fix(conformance-cli): require compatible published Paja peers Raise the Paja minimum to 0.21.0 and update the lockfile to its compatible core/NAP peer graph. Verify the packed 0.3.4 release with strict npm and pnpm installs while retaining GAMEPAD correlation-id validation. Co-Authored-By: Codex <noreply@openai.com> --- .planning/debug/pr242-paja-release-order.md | 41 +++++++++++ apps/docs/packages/conformance-cli.md | 2 +- packages/conformance-cli/CHANGELOG.md | 1 + packages/conformance-cli/README.md | 2 +- packages/conformance-cli/package.json | 2 +- pnpm-lock.yaml | 82 ++++++++++----------- 6 files changed, 86 insertions(+), 44 deletions(-) create mode 100644 .planning/debug/pr242-paja-release-order.md diff --git a/.planning/debug/pr242-paja-release-order.md b/.planning/debug/pr242-paja-release-order.md new file mode 100644 index 00000000..35514340 --- /dev/null +++ b/.planning/debug/pr242-paja-release-order.md @@ -0,0 +1,41 @@ +--- +status: verifying +trigger: "Resolve PR #242 review comment 6131874990" +created: 2026-10-14 +updated: 2026-10-14 +--- + +# PR #242 Paja release compatibility + +## Requirements + +Resolve [the review follow-up](https://github.com/napplet/web/pull/242#issuecomment-6131874990): make the conformance CLI release install with compatible Paja peers, and preserve the GAMEPAD subscription `id` rejection and silent reference-shell drop required by the living proposal. + +## Evidence and decision + +- npm now publishes Paja 0.21.0 (published 2026-10-13T01:38:08.664Z). Its core/NAP peers are `>=0.32.0 <0.35.0`, admitting this PR's 0.34.0 packages. Paja 0.20.1 peers remain `>=0.32.0 <0.33.0`. +- The branch permits `>=0.20.1 <0.22.0` and still locks 0.20.1. Publication alone does not remove that incompatible locked dependency. +- Keep the planned core/NAP 0.34.0 release and require the already-published Paja 0.21.x line. Update the lockfile and docs. Include the correction in the prepared, unpublished conformance CLI 0.3.4 release. +- Current [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108) head `0b28f77e3dfa0f07dd7c0f9d61a7090ee99f3a5e`, Wire Protocol and Error Handling, defines messages without correlation IDs and silent handling of malformed subscriptions. Current NIP-5D PR #2303 head is `020cb8b33a9e4c6b8ca4b2f9d0ed0a67843b68f7`. Keep the existing validator and reference-shell behavior. + +## Verification plan + +1. Reproduce the peer conflict with packed core/NAP 0.34.0 and published Paja 0.20.1 using strict peer installation. +2. Pack the updated CLI and its unpublished workspace dependencies; install those exact tarballs with npm strict peers and published Paja 0.21.0. Verify resolved package versions and run the installed CLI. +3. Run build, type-check, recursive unit tests, conformance/browser checks, and AI-slop. Confirm both subscribe and unsubscribe reject IDs and are silently dropped by the reference shell. +4. Push the reviewed fix, record the evidence on the PR, and inspect current CI results. No merge or publication is needed to resolve this review comment. + +## Resolution and verification + +Raised the Paja minimum to `>=0.21.0 <0.22.0`; the lockfile selects 0.21.0 and the compatible Kehto peer graph. Updated the package README, site guide, and the prepared 0.3.4 changelog. The existing `workspace:*` core/NAP dependencies and GAMEPAD validators are preserved. + +- Reproduced the old Paja 0.20.1/core 0.34.0 conflict with npm strict peers: `ERESOLVE` and the incompatible `<0.33.0` peer range. +- Packed the actual core 0.34.0, NAP 0.34.0, conformance 0.20.0, and conformance CLI 0.3.4 builds with `pnpm pack`; verified that packed manifests contain normal version dependencies, not workspace protocols. +- A temporary loopback registry served these four unchanged, unpublished tarballs and redirected all other packages to npm. Fresh consumer manifests depended only on conformance CLI 0.3.4. `npm install --strict-peer-deps` and `pnpm install --strict-peer-dependencies` passed without overrides, `--force`, or legacy peer handling. Dependency trees resolved core/NAP 0.34.0 throughout. +- npm initially selected published Paja 0.21.0. Paja 0.21.1 was published during verification; an initial tarball 404 cleared, after which isolated npm and pnpm installs selected 0.21.1 and passed. Both releases retain compatible core/NAP peers. +- The installed CLI captured 640×360 PNGs with both Paja 0.21.0 and 0.21.1 after a fixture received its first GAMEPAD state callback. No GUI browser was opened. +- Direct assertions against the built conformance package accepted valid subscribe/unsubscribe envelopes, rejected an `id` on both, and verified silent reference-shell drops. No protocol behavior changed. +- Build: 12 tasks passed. Types: 18 tasks passed. Recursive unit tests passed. Conformance browser suite: 5 tests passed. Release-tooling and workflow contract tests passed. Frozen workspace install with strict peers and whitespace checks passed. +- AI-slop: 98/100, zero errors, only the existing `fflate` dependency advisory. + +Local verification logs and packed-install evidence are under `/tmp/napplet-pr242-*`; the scratch registry root is `/tmp/napplet-pr242-peers.a32qMw`. These tests establish release compatibility without claiming the napplet package set is already on npm. Remote CI and the PR response remain to be verified before closing this debug session. diff --git a/apps/docs/packages/conformance-cli.md b/apps/docs/packages/conformance-cli.md index 2000b33c..48d16e5b 100644 --- a/apps/docs/packages/conformance-cli.md +++ b/apps/docs/packages/conformance-cli.md @@ -57,4 +57,4 @@ 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-optional-zapstore-metadata) for browser setup, iframe selection, and publication options. -GAMEPAD screenshots need Paja 0.21.0 from the coordinated runtime release. The dependency range accepts the published base release and the GAMEPAD release; publish the latter before this package so fresh private runner installs use the controller-capable preview. +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. diff --git a/packages/conformance-cli/CHANGELOG.md b/packages/conformance-cli/CHANGELOG.md index c81ac14d..dd427608 100644 --- a/packages/conformance-cli/CHANGELOG.md +++ b/packages/conformance-cli/CHANGELOG.md @@ -4,6 +4,7 @@ ### Patch Changes +- Require Paja 0.21.x, whose published peer range supports core/NAP 0.34.0, and update the lockfile to remove the incompatible Paja 0.20.1 resolution. - Add the proposed NAP-GAMEPAD cached API, types and subpaths, runtime binding, envelope validation, conformance injection, and optional manifest-domain inference. The coordinated CLI runner follow-up selects these packages after publication. Publish Kehto Paja 0.21.0 before the screenshot package release; conformance CLI 0.3.4 is included in this package set. Native Gamepad API replacement remains deferred pending clarification of the proposal and NIP-5D namespace-only injection restriction. - Updated dependencies - @napplet/core@0.34.0 diff --git a/packages/conformance-cli/README.md b/packages/conformance-cli/README.md index 5bc52e7f..79bdeab0 100644 --- a/packages/conformance-cli/README.md +++ b/packages/conformance-cli/README.md @@ -71,4 +71,4 @@ The CLI also provides the browser runner for `napplet screenshot`. Run `npx @nap 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). -GAMEPAD screenshots need Paja 0.21.0 from the coordinated runtime release. The dependency range accepts the published base release and the GAMEPAD release; publish the latter before this package so fresh private runner installs use the controller-capable preview. +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. diff --git a/packages/conformance-cli/package.json b/packages/conformance-cli/package.json index 70d9f3df..9ca09452 100644 --- a/packages/conformance-cli/package.json +++ b/packages/conformance-cli/package.json @@ -14,7 +14,7 @@ "access": "public" }, "dependencies": { - "@kehto/paja": ">=0.20.1 <0.22.0", + "@kehto/paja": ">=0.21.0 <0.22.0", "@napplet/conformance": "workspace:^", "@napplet/core": "workspace:*", "@napplet/nap": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 802e6253..a80bcb43 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -165,8 +165,8 @@ importers: packages/conformance-cli: dependencies: '@kehto/paja': - specifier: '>=0.20.1 <0.22.0' - version: 0.20.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + specifier: '>=0.21.0 <0.22.0' + version: 0.21.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@napplet/conformance': specifier: workspace:^ version: link:../conformance @@ -686,50 +686,50 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@kehto/acl@0.19.0': - resolution: {integrity: sha512-A2wxhWOLWbN3wiCofFs+WKZ5NcHXgbJvXucwJK51Z03Qh705JQ0T/cW1FYVYrJZTEvMyVN0QEGfybOwMLln/Kw==} + '@kehto/acl@0.19.1': + resolution: {integrity: sha512-4Ib3Gql2AtR1mmjyaLm0VATLIQkbZvCXV31NcaD6jAWZoeQy0dHg+6iSDu8XPGroIJdBMbPUDfGFGwVMi17Gmw==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' - '@napplet/nap': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' + '@napplet/nap': '>=0.32.0 <0.35.0' - '@kehto/firewall@0.6.0': - resolution: {integrity: sha512-KRWlh2NJrT/7L1LbXnL6IY+ASgrqivuoOwBn892xDuVHoOfBZrafH7bWxcwsLahDxu4iQmXRhEa/ToiaMDhyaQ==} + '@kehto/firewall@0.6.1': + resolution: {integrity: sha512-TlrwRjVlmEWdx8CpUeaMjlVoVQ7N2cGLxYKyK/b03eoUNzUjK8xvwMuWrbRH2gJJLP0c8k51YipWF1tedpsNfA==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' '@kehto/nip@0.7.0': resolution: {integrity: sha512-uDi2fvs8gr2jhhNKU8JbrDaq1cqRpAHdxDWBpoLv59tVTTFcTul/bWpT0aAHcq/SzHgT+3IHT0oIuqJXpqCwhw==} peerDependencies: nostr-tools: '>=2.23.3 <=2.x' - '@kehto/paja@0.20.1': - resolution: {integrity: sha512-fyghGf+lD1OtC1zfMILa/9Qqb+JMKkjL1Z9RTi8DyVWC5O9sf2nfH5VdegzlkgjDeyihSes2AoUEtlMNRrHcoQ==} + '@kehto/paja@0.21.0': + resolution: {integrity: sha512-Tk5X3XKVjXzIEhXJWtxpmlNNwCv+jcmfWyuZIby3V/dm+uB4eHjwhYijhzoZvUaHY1DmawCI3wRvbKjCVQyydw==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' - '@napplet/nap': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' + '@napplet/nap': '>=0.32.0 <0.35.0' nostr-tools: '>=2.23.3 <=2.x' - '@kehto/runtime@0.24.0': - resolution: {integrity: sha512-uWjopIDhFTbduBZ1iPmm6dKZyV/4nySb52ODBeqeWo9ErVIbYt0v3j/RGgeYvGNRJbCxqqN/Rllpec1FWFgoqA==} + '@kehto/runtime@0.24.1': + resolution: {integrity: sha512-cEaGbhVlVss6xZJAiGFQAssFaG5PXtPS/kZL/EjsgtT7KX6chybXialLwAj9HUGrIADUO2S2SMY36zvIzmH5Ew==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' - '@napplet/nap': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' + '@napplet/nap': '>=0.32.0 <0.35.0' - '@kehto/services@0.23.0': - resolution: {integrity: sha512-MQn0yktwaUGanj2dSb/G/M5RMcQQNnBcdoQDSfziGaGk2Xtx/ULSslw8pw1QAdvrBoLAiX+5f/bkMZk62xxf6w==} + '@kehto/services@0.24.0': + resolution: {integrity: sha512-hky7aZjK13+fZqyKnNIiv8sfkpEc8Xx+sQivJ3qOrXz3Jw3WCczTcSK1+XpPsCHIBbFKXXOzvktQJsCh67N4xg==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' - '@napplet/nap': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' + '@napplet/nap': '>=0.32.0 <0.35.0' nostr-tools: '>=2.23.3 <=2.x' peerDependenciesMeta: nostr-tools: optional: true - '@kehto/shell@0.23.0': - resolution: {integrity: sha512-Pu4XOIiPWHWDg9lqkIPuGmoaUVj2GjOUXsXrq+MnynU5V/uJiU21JA/vthBx7/2vGDn5SGh+zRUwk55zdssi3w==} + '@kehto/shell@0.24.0': + resolution: {integrity: sha512-dfi/REfv7bWoCyTutZeLC3ZKIuVs7VxA84FHFXJYcHGouwecTzneBPmHnM+lgFCq7wi8tuPKIdVS43uxMNsR/Q==} peerDependencies: - '@napplet/core': '>=0.32.0 <0.33.0' - '@napplet/nap': '>=0.32.0 <0.33.0' + '@napplet/core': '>=0.32.0 <0.35.0' + '@napplet/nap': '>=0.32.0 <0.35.0' nostr-tools: '>=2.23.3 <=2.x' '@kehto/wm@0.0.2': @@ -2472,12 +2472,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@kehto/acl@0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)': + '@kehto/acl@0.19.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)': dependencies: '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap - '@kehto/firewall@0.6.0(@napplet/core@packages+core)': + '@kehto/firewall@0.6.1(@napplet/core@packages+core)': dependencies: '@napplet/core': link:packages/core @@ -2486,40 +2486,40 @@ snapshots: '@noble/hashes': 2.0.1 nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/paja@0.20.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/paja@0.21.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) - '@kehto/firewall': 0.6.0(@napplet/core@packages+core) + '@kehto/acl': 0.19.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/firewall': 0.6.1(@napplet/core@packages+core) '@kehto/nip': 0.7.0(nostr-tools@2.23.3(typescript@5.9.3)) - '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) - '@kehto/services': 0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) - '@kehto/shell': 0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/runtime': 0.24.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/services': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) + '@kehto/shell': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3)) '@kehto/wm': 0.0.2 '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/runtime@0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)': + '@kehto/runtime@0.24.1(@napplet/core@packages+core)(@napplet/nap@packages+nap)': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) - '@kehto/firewall': 0.6.0(@napplet/core@packages+core) + '@kehto/acl': 0.19.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/firewall': 0.6.1(@napplet/core@packages+core) '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap '@noble/curves': 2.0.1 '@noble/hashes': 2.0.1 - '@kehto/services@0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/services@0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/runtime': 0.24.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap optionalDependencies: nostr-tools: 2.23.3(typescript@5.9.3) - '@kehto/shell@0.23.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': + '@kehto/shell@0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap)(nostr-tools@2.23.3(typescript@5.9.3))': dependencies: - '@kehto/acl': 0.19.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) - '@kehto/runtime': 0.24.0(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/acl': 0.19.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) + '@kehto/runtime': 0.24.1(@napplet/core@packages+core)(@napplet/nap@packages+nap) '@napplet/core': link:packages/core '@napplet/nap': link:packages/nap nostr-tools: 2.23.3(typescript@5.9.3) From 5e11d4ca22e726b8227c4851472c1b6e4fd7b1ac Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:40:03 -0700 Subject: [PATCH 22/24] docs(debug): record Paja peer compatibility resolution Co-Authored-By: Codex <noreply@openai.com> --- .planning/debug/{ => resolved}/pr242-paja-release-order.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .planning/debug/{ => resolved}/pr242-paja-release-order.md (90%) diff --git a/.planning/debug/pr242-paja-release-order.md b/.planning/debug/resolved/pr242-paja-release-order.md similarity index 90% rename from .planning/debug/pr242-paja-release-order.md rename to .planning/debug/resolved/pr242-paja-release-order.md index 35514340..8eb8785b 100644 --- a/.planning/debug/pr242-paja-release-order.md +++ b/.planning/debug/resolved/pr242-paja-release-order.md @@ -1,5 +1,5 @@ --- -status: verifying +status: resolved trigger: "Resolve PR #242 review comment 6131874990" created: 2026-10-14 updated: 2026-10-14 @@ -38,4 +38,4 @@ Raised the Paja minimum to `>=0.21.0 <0.22.0`; the lockfile selects 0.21.0 and t - Build: 12 tasks passed. Types: 18 tasks passed. Recursive unit tests passed. Conformance browser suite: 5 tests passed. Release-tooling and workflow contract tests passed. Frozen workspace install with strict peers and whitespace checks passed. - AI-slop: 98/100, zero errors, only the existing `fflate` dependency advisory. -Local verification logs and packed-install evidence are under `/tmp/napplet-pr242-*`; the scratch registry root is `/tmp/napplet-pr242-peers.a32qMw`. These tests establish release compatibility without claiming the napplet package set is already on npm. Remote CI and the PR response remain to be verified before closing this debug session. +The fix is committed as `3c3f1a66` and pushed to PR #242. Local verification logs and packed-install evidence are under `/tmp/napplet-pr242-*`; the scratch registry root is `/tmp/napplet-pr242-peers.a32qMw`. These tests establish release compatibility without claiming the napplet package set is already on npm. The PR check runs and review response carry the remote verification record. No merge or publication was performed. From 24f8be4ba26edb4d5954a0c0e76e69ffd3f8ddb6 Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:26:22 -0700 Subject: [PATCH 23/24] docs(debug): resume CLI runner release verification Co-Authored-By: Codex <noreply@openai.com> --- .planning/debug/pr243-unpublished-runner.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.planning/debug/pr243-unpublished-runner.md b/.planning/debug/pr243-unpublished-runner.md index 1ea8bd65..efea5368 100644 --- a/.planning/debug/pr243-unpublished-runner.md +++ b/.planning/debug/pr243-unpublished-runner.md @@ -1,8 +1,8 @@ --- -status: awaiting-release-approval +status: fixing trigger: "PR #243 tests are red across the board" created: 2026-10-13 -updated: 2026-10-13 +updated: 2026-10-14 --- # PR #243 runner publication dependency @@ -16,9 +16,9 @@ updated: 2026-10-13 ## Current Focus -- Hypothesis: this is the documented release dependency, not five independent platform defects. -- Evidence: every platform has the same npm ETARGET error. PR #242 remains unmerged and contains conformance CLI 0.3.4. Its nine checks pass and GitHub reports it mergeable. Kehto CLI 0.5.5 and Paja 0.21.0 are now published. -- Next action: merge #242 and wait for publication only if the user authorizes that separate feature release. Retarget and rerun #243 after the real package exists. +- Cause: the documented release dependency. All five jobs in the current run 38520521467 still fail with npm ETARGET for conformance CLI 0.3.4. +- Current state: PR #242's peer compatibility review is resolved and its original nine checks passed. Main's subsequently merged icon skill introduced two text conflicts, now resolved on the prerequisite branch while retaining both icon and GAMEPAD guidance. Combined-branch verification is running before publication. +- Next action: complete the prerequisite release, verify the actual npm artifacts, integrate main into this follow-up, retarget #243 to main, and verify fresh-cache execution on all five platforms. ## Evidence @@ -31,4 +31,4 @@ updated: 2026-10-13 Root cause confirmed. Updated the stacked branch with #242's latest reviewed fixes and restored the CLI changelog's existing unwrapped history, leaving one focused 0.9.1 entry. Local verification passed: build (12 tasks), type-check (18 tasks), recursive unit tests, and diff whitespace checks. AI-slop passed at 98/100 with zero errors; the existing `fflate` dependency advisory is the sole warning. -Publication and final fresh-cache CI verification remain pending. The user was asked to authorize merging #242 because that ships a separate feature release. No dependency downgrade, local-package substitution, or test bypass was added. +The user resumed the goal to fix #243. The required release sequence is in progress; fresh-cache success remains unproven until the actual package is published and the platform tests pass. No dependency downgrade, local-package substitution, or test bypass was added. From 3732571086d9e9a2276d682b49720ddbade82e2b Mon Sep 17 00:00:00 2001 From: sandwich <299465+dskvr@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:36:57 -0700 Subject: [PATCH 24/24] docs(debug): record published runner smoke verification Co-Authored-By: Codex <noreply@openai.com> --- .planning/debug/pr243-unpublished-runner.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.planning/debug/pr243-unpublished-runner.md b/.planning/debug/pr243-unpublished-runner.md index efea5368..e912285c 100644 --- a/.planning/debug/pr243-unpublished-runner.md +++ b/.planning/debug/pr243-unpublished-runner.md @@ -1,5 +1,5 @@ --- -status: fixing +status: resolved trigger: "PR #243 tests are red across the board" created: 2026-10-13 updated: 2026-10-14 @@ -12,23 +12,26 @@ updated: 2026-10-14 - Expected: the standalone CLI provisions its pinned tools from npm with an empty PATH and a fresh cache on all five platforms. - Actual: every job in run 38299057704 fails during the first screenshot command. - Error: `No matching version found for @napplet/conformance-cli@0.3.4.` -- Reproduction: install that exact package version from npm; it is absent. Latest published version is 0.3.3. +- Original reproduction: installing that exact package version from npm failed because only 0.3.3 was published. ## Current Focus -- Cause: the documented release dependency. All five jobs in the current run 38520521467 still fail with npm ETARGET for conformance CLI 0.3.4. -- Current state: PR #242's peer compatibility review is resolved and its original nine checks passed. Main's subsequently merged icon skill introduced two text conflicts, now resolved on the prerequisite branch while retaining both icon and GAMEPAD guidance. Combined-branch verification is running before publication. -- Next action: complete the prerequisite release, verify the actual npm artifacts, integrate main into this follow-up, retarget #243 to main, and verify fresh-cache execution on all five platforms. +- Cause: the documented release dependency. All five jobs in run 38520521467 failed with npm ETARGET for conformance CLI 0.3.4. +- Current state: PR #242 merged after nine fresh checks passed. Its npm and JSR publication workflows succeeded, the four required npm tarballs return HTTP 200, and this branch includes the released main commit. +- Next action: retarget #243 to main and verify all five platforms on the updated PR head. ## Evidence - [Failed standalone run](https://github.com/napplet/web/actions/runs/38299057704): Linux x64/ARM64, macOS x64/ARM64, and Windows all fail on the same missing package. - [PR #242](https://github.com/napplet/web/pull/242), head `44d663a14af0f91a3797614c76e5c3d684cec52e`: CI, conformance, links, slop, and all five standalone jobs passed. Release versions are already applied and no unconsumed changeset remains. -- npm registry checked directly: conformance CLI 0.3.4 absent; Kehto CLI 0.5.5 and Paja 0.21.0 present. +- Initial npm registry check: conformance CLI 0.3.4 absent; Kehto CLI 0.5.5 and Paja 0.21.0 present. +- [PR #242](https://github.com/napplet/web/pull/242) merged as `839000563f6b92293a1e94ae0cd3a63011139448` on October 14 after its updated head passed all nine checks. [npm publication](https://github.com/napplet/web/actions/runs/38598414641) and [JSR publication](https://github.com/napplet/web/actions/runs/38598414611) succeeded. +- Direct npm download checks returned HTTP 200 for core 0.34.0, nap 0.34.0, conformance 0.20.0, and conformance CLI 0.3.4. Published conformance CLI metadata requires Paja `>=0.21.0 <0.22.0` and core/nap 0.34.0, matching the reviewed peer compatibility fix. +- The compiled macOS ARM64 binary passed `standalone_integration_test.ts` against real npm packages with an empty PATH and a fresh cache: 1 passed, 0 failed, 1m20s. This covered screenshot generation, deployment dry-run, conformance, Paja startup, and cache reuse with network access disabled by invalid proxies. - The pinned package installation is the behavior under test. Skipping the check, substituting a local package, or downgrading to the old runner would conceal or remove the intended release behavior. ## Resolution -Root cause confirmed. Updated the stacked branch with #242's latest reviewed fixes and restored the CLI changelog's existing unwrapped history, leaving one focused 0.9.1 entry. Local verification passed: build (12 tasks), type-check (18 tasks), recursive unit tests, and diff whitespace checks. AI-slop passed at 98/100 with zero errors; the existing `fflate` dependency advisory is the sole warning. +Root cause confirmed. Updated the stacked branch with #242's latest reviewed fixes and restored the CLI changelog's existing unwrapped history, leaving one focused 0.9.1 entry. Local verification passed: build (12 tasks), type-check (18 tasks), recursive unit tests, and diff whitespace checks. The final full AI-slop scan passed at 95/100 with zero errors; warnings concern existing function length, nesting, and the `fflate` dependency advisory in files unchanged by this PR. -The user resumed the goal to fix #243. The required release sequence is in progress; fresh-cache success remains unproven until the actual package is published and the platform tests pass. No dependency downgrade, local-package substitution, or test bypass was added. +The release blocker is resolved: required packages are published and downloadable, the branch includes released main, and the local compiled binary passed fresh-cache verification. Remote platform results are tracked in #243's checks. No dependency downgrade, local-package substitution, or test bypass was added.