@kehto/services 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Reference service handlers for the napplet protocol — audio, notifications, identity, relay pool, cache, keys, media, notify, theme.
4
4
 
5
5
  > **Alpha status:** Kehto is an early runtime implementation for a draft NIP-5D
6
- > protocol. NUB contracts and service envelopes are not final; treat these
6
+ > protocol. NAP contracts and service envelopes are not final; treat these
7
7
  > handlers as reference implementations for the current draft.
8
8
 
9
9
  ## Install
@@ -22,7 +22,7 @@ Current draft posture:
22
22
 
23
23
  - The v1.1 signer service is deleted outright. Its responsibilities split into two: read-only identity lookups go through `createIdentityService` (`getPublicKey`, `getRelays`, `getProfile`, `getFollows`, `getList`, `getZaps`, `getMutes`, `getBlocked`, `getBadges`); signing happens inside the shell as part of `relay.publish` / `relay.publishEncrypted` and is never exposed to napplets.
24
24
  - `createKeysService` and `createMediaService` ship real reference backends as of v1.4 (see the dedicated sections below). `createKeysService` attaches a document-level `keydown` listener by default and delivers `keys.action` push envelopes to registered napplets; `createMediaService` mirrors session metadata and playback state to `navigator.mediaSession` and emits `media.command` push envelopes on OS transport events. Both accept a host-bridge option (`HostKeysBridge` / `HostMediaBridge`) so Electron / Tauri / native shells can swap in OS-level backends without re-implementing the wire-protocol bookkeeping.
25
- - `createNotifyService` (NIP-5D `notify.*` NUB) coexists with the legacy `createNotificationService` (ifc-emit `notifications:*` channel). Both may be registered simultaneously while hosts migrate.
25
+ - `createNotifyService` (NIP-5D `notify.*` NAP) coexists with the legacy `createNotificationService` (ifc-emit `notifications:*` channel). Both may be registered simultaneously while hosts migrate.
26
26
 
27
27
  ## Quick Start
28
28
 
@@ -51,7 +51,7 @@ runtime.registerService(
51
51
 
52
52
  ## Keys Service
53
53
 
54
- Reference keyboard / chord backend for the `keys.*` NIP-5D NUB. By default attaches a single `document`-level `keydown` listener that matches incoming events against registered chord subscriptions and delivers a `keys.action` push envelope back to the owning napplet. Implement the [`HostKeysBridge`](#hostkeysbridge-interface) interface to swap in OS-level backends (Electron `globalShortcut`, Tauri `GlobalShortcut`).
54
+ Reference keyboard / chord backend for the `keys.*` NIP-5D NAP. By default attaches a single `document`-level `keydown` listener that matches incoming events against registered chord subscriptions and delivers a `keys.action` push envelope back to the owning napplet. Implement the [`HostKeysBridge`](#hostkeysbridge-interface) interface to swap in OS-level backends (Electron `globalShortcut`, Tauri `GlobalShortcut`).
55
55
 
56
56
  ### Factory
57
57
 
@@ -85,7 +85,7 @@ export interface HostKeysBridge {
85
85
  * - invoke `callback` exactly once per matching chord event (implementations
86
86
  * are responsible for any OS-autorepeat filtering)
87
87
  * - invoke `callback` synchronously during the event delivery
88
- * - accept the string chord format documented by @napplet/nub/keys
88
+ * - accept the string chord format documented by @napplet/nap/keys
89
89
  * (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`)
90
90
  */
91
91
  subscribe(chord: string, callback: (event: KeyboardEvent | HostKeyEvent) => void): () => void;
@@ -154,7 +154,7 @@ runtime.registerService('keys', createKeysService({ hostBridge: electronBridge }
154
154
 
155
155
  Plug a `HostKeysBridge` when the default document listener is insufficient: Electron or Tauri apps that need to register OS-level global hotkeys (chords delivered even when the host window is not focused), native shells that route chords through a platform-specific hotkey manager (macOS Carbon, Linux X11 grab, Windows RegisterHotKey), or test harnesses that inject synthetic events through a controlled `EventTarget`. The bridge owns subscription lifecycle; the service retains per-window bookkeeping (so `onWindowDestroyed` cleanup stays identical across paths).
156
156
 
157
- See the demo: [`apps/playground/napplets/hotkey-chord/src/main.ts`](../../apps/playground/napplets/hotkey-chord/src/main.ts) (the Phase 26 end-to-end exemplar — uses `@napplet/sdk` `keys.registerAction` + `keys.onAction` against the real backend).
157
+ A napplet drives this end to end via `@napplet/sdk` — `keys.registerAction` to claim a chord and `keys.onAction` to receive dispatches against the real backend.
158
158
 
159
159
  ### Reserved Chords
160
160
 
@@ -214,7 +214,7 @@ runtime.registerService('keys', keys);
214
214
 
215
215
  ## Media Service
216
216
 
217
- Reference media backend for the `media.*` NIP-5D NUB. By default mirrors session metadata + playback state to `navigator.mediaSession` via the DOM `MediaSession` API and installs `setActionHandler` callbacks that emit `media.command` push envelopes on OS transport events (play / pause / next / previous / seek). Implement the [`HostMediaBridge`](#hostmediabridge-interface) interface to swap in native backends (Electron bridge, MPRIS on Linux, MediaRemote on macOS).
217
+ Reference media backend for the `media.*` NIP-5D NAP. By default mirrors session metadata + playback state to `navigator.mediaSession` via the DOM `MediaSession` API and installs `setActionHandler` callbacks that emit `media.command` push envelopes on OS transport events (play / pause / next / previous / seek). Implement the [`HostMediaBridge`](#hostmediabridge-interface) interface to swap in native backends (Electron bridge, MPRIS on Linux, MediaRemote on macOS).
218
218
 
219
219
  ### Factory
220
220
 
@@ -344,31 +344,31 @@ runtime.registerService('media', createMediaService({ hostBridge: electronBridge
344
344
 
345
345
  Plug a `HostMediaBridge` when `navigator.mediaSession` is insufficient: Electron apps that need to route transport events through the main process (lock-screen integration on Windows, Now Playing integration on macOS), Linux shells that speak MPRIS over D-Bus, native mobile wrappers that forward to AVPlayer / ExoPlayer, or test harnesses that record action events without touching the DOM. The bridge owns metadata/state mirroring and OS action routing; the service retains per-session bookkeeping (sessionRegistry + per-window send handles) so `media.command` dispatch semantics stay identical across paths.
346
346
 
347
- See the demo: [`apps/playground/napplets/media-controller/src/main.ts`](../../apps/playground/napplets/media-controller/src/main.ts) (the Phase 27 end-to-end exemplar — uses `@napplet/nub/media` `mediaCreateSession({ owner: 'napplet', ... })` + `mediaReportState` + `mediaOnCommand` against the real backend).
347
+ A napplet drives this end to end via `@napplet/nap/media` — `mediaCreateSession({ owner: 'napplet', ... })`, `mediaReportState`, and `mediaOnCommand` against the real backend.
348
348
 
349
349
  ## Public API
350
350
 
351
351
  Each factory returns a `ServiceHandler` registrable via `runtime.registerService()`. The bullets below note the current NIP-5D domain the handler owns and the ACL capability napplets need in order to reach it.
352
352
 
353
- ### Identity NUB
353
+ ### Identity NAP
354
354
  - `createIdentityService` — `identity.*` reads (`identity:read`). No signing surface; shell mediates signing internally.
355
355
 
356
- ### Notify NUB
356
+ ### Notify NAP
357
357
  - `createNotifyService` — canonical `notify.*` envelopes (`notify:send` / `notify:channel`).
358
358
  - `createNotificationService` — legacy ifc-emit `notifications:*` channel; coexists with `createNotifyService` until retired.
359
359
 
360
- ### Relay NUB
360
+ ### Relay NAP
361
361
  - `createRelayPoolService` — `relay.publish`, `relay.publishEncrypted`, `relay.subscribe` fan-out (`relay:read` / `relay:write`).
362
362
  - `createCacheService` — offline event cache (`cache:read` / `cache:write`).
363
363
  - `createCoordinatedRelay` — composite service that bundles relay-pool + cache with read-through behavior.
364
364
 
365
- ### Keys NUB
365
+ ### Keys NAP
366
366
  - `createKeysService` — `keys.registerAction` / `keys.unregisterAction` / `keys.forward` + `keys.action` push envelopes (`keys:forward`). Document-level chord listener by default; implement the `HostKeysBridge` interface to swap in Electron / Tauri / OS-level backends. See [Keys Service](#keys-service) for the full contract.
367
367
 
368
- ### Media NUB
368
+ ### Media NAP
369
369
  - `createMediaService` — owner-aware `media.session.create` / `update` / `destroy` / `media.state` / `media.capabilities` + `media.command` push envelopes (`media:control`). Napplet-owned sessions mirror to `navigator.mediaSession` by default; shell-owned creates are rejected until a host playback/fetch bridge is supplied. Implement the `HostMediaBridge` interface to swap in native backends. See [Media Service](#media-service) for the full contract.
370
370
 
371
- ### Theme NUB
371
+ ### Theme NAP
372
372
  - `createThemeService` — `theme.get` + `theme.changed` fan-out (`theme:read`). Returns a `ThemeService` with `publishTheme()` / `setTheme()` utilities for host-side updates.
373
373
 
374
374
  ### Audio (legacy ifc-emit)
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { ServiceHandler, Signer } from '@kehto/runtime';
2
2
  import { NostrFilter, NostrEvent, EventTemplate } from '@napplet/core';
3
- import { MediaMetadata, MediaAction } from '@napplet/nub/media/types';
4
- export { MediaAction } from '@napplet/nub/media/types';
5
- import { NotifySendMessage } from '@napplet/nub/notify/types';
6
- import { Theme, ThemeChangedMessage } from '@napplet/nub/theme/types';
7
- import { ConfigSchemaErrorCode, ConfigValues, NappletConfigSchema } from '@napplet/nub/config/types';
3
+ import { MediaMetadata, MediaAction } from '@napplet/nap/media/types';
4
+ export { MediaAction } from '@napplet/nap/media/types';
5
+ import { NotifySendMessage } from '@napplet/nap/notify/types';
6
+ import { Theme, ThemeChangedMessage } from '@napplet/nap/theme/types';
7
+ import { ConfigSchemaErrorCode, ConfigValues, NappletConfigSchema } from '@napplet/nap/config/types';
8
8
  export { a as CvmCloseMessage, b as CvmCloseResultMessage, c as CvmDiscoverMessage, d as CvmDiscoverQuery, e as CvmDiscoverResultMessage, f as CvmEventMessage, g as CvmInboundMessage, h as CvmOutboundMessage, i as CvmRequestMessage, j as CvmRequestOptions, k as CvmRequestResultMessage, l as CvmServer, m as CvmServerRef, n as CvmService, o as CvmServiceOptions, C as CvmTransport, p as CvmTransportError, M as McpContentBlock, q as McpMessage, r as McpTool, s as McpToolResult, t as createCvmService } from './cvm-service-CXcOVQ0m.js';
9
9
 
10
10
  /**
@@ -187,7 +187,7 @@ declare function createNotificationService(options?: NotificationServiceOptions)
187
187
  * (An `identity.decrypt` capability was briefly added in v1.8 and removed as a
188
188
  * spec violation; decryption belongs to the runtime, inline over the wire.)
189
189
  *
190
- * Handles 9 identity.* request types from @napplet/nub/identity. getPublicKey
190
+ * Handles 9 identity.* request types from @napplet/nap/identity. getPublicKey
191
191
  * and getRelays return real values sourced from hooks.auth.getSigner(); the
192
192
  * remaining 7 (getProfile/getFollows/getList/getZaps/getMutes/getBlocked/
193
193
  * getBadges) are stub-level. Host apps plug real backends via
@@ -216,7 +216,7 @@ interface IdentityServiceOptions {
216
216
  /**
217
217
  * Create an identity service that handles NIP-5D identity.* envelope messages.
218
218
  *
219
- * Supports the 9 read-only identity.* request types from @napplet/nub/identity.
219
+ * Supports the 9 read-only identity.* request types from @napplet/nap/identity.
220
220
  * The two nostr-info queries (getPublicKey, getRelays) resolve through the
221
221
  * caller-supplied signer; the remaining 7 return default/empty payloads with
222
222
  * spec-correct envelope shapes so napplets always receive a result envelope.
@@ -472,7 +472,7 @@ declare function createCoordinatedRelay(options: CoordinatedRelayOptions): Servi
472
472
  /**
473
473
  * keys-service.ts — NIP-5D keys NUB reference document-level listener implementation.
474
474
  *
475
- * Handles the 3 napplet -> shell request types from @napplet/nub/keys:
475
+ * Handles the 3 napplet -> shell request types from @napplet/nap/keys:
476
476
  * - keys.forward -> invokes options.onForward (hotkey passthrough, fire-and-forget)
477
477
  * - keys.registerAction -> parses action.defaultKey into a chord spec, stores the
478
478
  * subscription in an in-memory registry keyed by actionId,
@@ -491,14 +491,14 @@ declare function createCoordinatedRelay(options: CoordinatedRelayOptions): Servi
491
491
  *
492
492
  * On each document keydown matching a registered action, the service
493
493
  * additionally emits a `keys.action` envelope to the action's owning napplet
494
- * via the per-window `send` callback — this is the canonical @napplet/nub/keys
494
+ * via the per-window `send` callback — this is the canonical @napplet/nap/keys
495
495
  * surface the SDK's `keys.onAction(...)` helper consumes. The shape is a
496
496
  * superset of `KeysActionMessage`: `{ type, actionId, chord }` where `chord`
497
497
  * is the parsed `{ ctrl, alt, shift, meta, key }` struct (extension field;
498
498
  * base shape unchanged, downstream SDKs that only read `{ type, actionId }`
499
499
  * ignore `chord` silently).
500
500
  *
501
- * Field-name translation: @napplet/nub/keys uses the compact
501
+ * Field-name translation: @napplet/nap/keys uses the compact
502
502
  * { ctrl, alt, shift, meta } form on the wire; the shell's HotkeyHooks
503
503
  * (packages/shell/src/types.ts) expects the DOM-compatible
504
504
  * { ctrlKey, altKey, shiftKey, metaKey } form. This service performs the
@@ -567,7 +567,7 @@ interface HostKeysBridge {
567
567
  * - invoke `callback` exactly once per matching chord event (implementations
568
568
  * are responsible for any OS-autorepeat filtering)
569
569
  * - invoke `callback` synchronously during the event delivery
570
- * - accept the string chord format documented by @napplet/nub/keys
570
+ * - accept the string chord format documented by @napplet/nap/keys
571
571
  * (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`)
572
572
  */
573
573
  subscribe(chord: string, callback: (event: KeyboardEvent | HostKeyEvent) => void): () => void;
@@ -639,7 +639,7 @@ interface KeysServiceOptions {
639
639
  */
640
640
  hostBridge?: HostKeysBridge;
641
641
  /**
642
- * Optional set of shell-reserved chords. Strings in the `@napplet/nub/keys`
642
+ * Optional set of shell-reserved chords. Strings in the `@napplet/nap/keys`
643
643
  * wire format (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`). When a napplet sends
644
644
  * `keys.forward` with a chord matching this set — or when a document
645
645
  * keydown matches a reserved chord — the service invokes `onForward`
@@ -748,7 +748,7 @@ declare function createBrowserMediaBridge(opts?: {
748
748
  * media-service.ts — NIP-5D media NUB reference service (navigator.mediaSession
749
749
  * reference implementation).
750
750
  *
751
- * Handles the napplet-owned subset of @napplet/nub/media:
751
+ * Handles the napplet-owned subset of @napplet/nap/media:
752
752
  * media.session.create (result), media.session.update, media.session.destroy,
753
753
  * media.state, media.capabilities.
754
754
  *
@@ -763,7 +763,7 @@ declare function createBrowserMediaBridge(opts?: {
763
763
  * napplet-supplied metadata to navigator.mediaSession.metadata via new MediaMetadata()
764
764
  * and installs setActionHandler callbacks for the 5 OS transport actions
765
765
  * (play / pause / nexttrack / previoustrack / seekto). Each callback emits a canonical
766
- * media.command envelope to the owning napplet — that is the @napplet/nub/media
766
+ * media.command envelope to the owning napplet — that is the @napplet/nap/media
767
767
  * MediaCommandMessage shape consumed by the SDK's mediaOnCommand() helper.
768
768
  *
769
769
  * NAP-MEDIA now distinguishes napplet-owned playback from shell-owned
@@ -794,7 +794,7 @@ declare function createBrowserMediaBridge(opts?: {
794
794
  *
795
795
  * Note: this is SEPARATE from packages/services/src/audio-service.ts, which
796
796
  * is the legacy ifc-topic-based audio source registry (audio:* topic events
797
- * over ifc.emit). media-service is the canonical @napplet/nub/media NIP-5D
797
+ * over ifc.emit). media-service is the canonical @napplet/nap/media NIP-5D
798
798
  * path and they coexist — audio-service continues to track audio sources for
799
799
  * shell UI, while media-service handles the NUB protocol envelope surface.
800
800
  *
@@ -963,7 +963,7 @@ interface MediaServiceOptions {
963
963
  * Create a media NUB service handler with navigator.mediaSession integration.
964
964
  *
965
965
  * Implements the 5 napplet->shell media.* request types defined in
966
- * `@napplet/nub/media`. Only `media.session.create` produces a reply
966
+ * `@napplet/nap/media`. Only `media.session.create` produces a reply
967
967
  * envelope (`media.session.create.result`) — the remaining four
968
968
  * (`session.update`, `session.destroy`, `state`, `capabilities`) are
969
969
  * fire-and-forget per the NUB spec.
@@ -993,7 +993,7 @@ declare function createMediaService(options?: MediaServiceOptions): ServiceHandl
993
993
  /**
994
994
  * notify-service.ts — NIP-5D notify NUB reference service (stub-level).
995
995
  *
996
- * Handles the 5 napplet -> shell request types from `@napplet/nub/notify`:
996
+ * Handles the 5 napplet -> shell request types from `@napplet/nap/notify`:
997
997
  * - `notify.send` -> `notify.send.result` (shell-assigned id)
998
998
  * - `notify.dismiss` -> fire-and-forget
999
999
  * - `notify.badge` -> fire-and-forget
@@ -1008,9 +1008,9 @@ declare function createMediaService(options?: MediaServiceOptions): ServiceHandl
1008
1008
  * `packages/services/src/notification-service.ts`, which is the legacy
1009
1009
  * ifc-topic-based notification registry (operates on `ifc.emit` topics
1010
1010
  * under `notifications:*`). `notify-service.ts` is the canonical
1011
- * `@napplet/nub/notify` NIP-5D path and lives alongside the legacy module.
1011
+ * `@napplet/nap/notify` NIP-5D path and lives alongside the legacy module.
1012
1012
  * If the host app registers this service via
1013
- * `runtime.registerService('notify', ...)`, @napplet/nub/notify messages
1013
+ * `runtime.registerService('notify', ...)`, @napplet/nap/notify messages
1014
1014
  * land here; the legacy ifc-emit topic path remains untouched.
1015
1015
  *
1016
1016
  * Shell -> napplet push messages (`notify.action`, `notify.clicked`,
@@ -1054,7 +1054,7 @@ interface NotifyServiceOptions {
1054
1054
  /**
1055
1055
  * Create a stub-level notify service handler.
1056
1056
  *
1057
- * Answers the 5 napplet->shell request types from `@napplet/nub/notify`.
1057
+ * Answers the 5 napplet->shell request types from `@napplet/nap/notify`.
1058
1058
  * Does NOT implement a real backend (no DOM Notification API, no channel
1059
1059
  * registry, no permission prompt). Host apps replace this via
1060
1060
  * `runtime.registerService('notify', realHandler)` when a real backend is
@@ -1076,7 +1076,7 @@ declare function createNotifyService(options?: NotifyServiceOptions): ServiceHan
1076
1076
  /**
1077
1077
  * theme-service.ts — NIP-5D theme NUB reference service.
1078
1078
  *
1079
- * Handles the single napplet->shell request type from @napplet/nub/theme:
1079
+ * Handles the single napplet->shell request type from @napplet/nap/theme:
1080
1080
  * - `theme.get` -> `theme.get.result { theme }` (current theme)
1081
1081
  *
1082
1082
  * Exposes a host-facing `publishTheme(theme)` handle that:
@@ -1181,7 +1181,7 @@ declare function createThemeService(options?: ThemeServiceOptions): ThemeService
1181
1181
  * config-service.ts — NUB-CONFIG reference service (9th NUB domain, v1.7 Phase 39).
1182
1182
  *
1183
1183
  * Shell-side reference implementation for the canonical NUB-CONFIG wire
1184
- * protocol (`@napplet/nub/config`, published at `^0.3.0`). Handles the full
1184
+ * protocol (`@napplet/nap/config`, published at `^0.3.0`). Handles the full
1185
1185
  * 8-message discriminated union: 5 napplet→shell request types + 3
1186
1186
  * shell→napplet result/push types.
1187
1187
  *
@@ -1304,7 +1304,7 @@ interface ConfigService {
1304
1304
  /**
1305
1305
  * Create a NUB-CONFIG reference service.
1306
1306
  *
1307
- * Shell-writes, napplet-reads. Handles the full `@napplet/nub/config` wire
1307
+ * Shell-writes, napplet-reads. Handles the full `@napplet/nap/config` wire
1308
1308
  * protocol: `config.get` (correlated snapshot), `config.subscribe` /
1309
1309
  * `config.unsubscribe` (live push stream), `config.registerSchema` (optional
1310
1310
  * schema registration + Core Subset validation), `config.openSettings`
@@ -1343,7 +1343,7 @@ declare function createConfigService(options: ConfigServiceOptions): ConfigServi
1343
1343
  *
1344
1344
  * Shell-side reference implementation for the canonical NUB-RESOURCE wire
1345
1345
  * protocol (`internal-resource.ts` in @kehto/shell/src/types; kehto-internal
1346
- * model per PROJECT.md Decision #31 — diverges from upstream `@napplet/nub/
1346
+ * model per PROJECT.md Decision #31 — diverges from upstream `@napplet/nap/
1347
1347
  * resource` in field names + error vocabulary). Handles the canonical
1348
1348
  * 4-message protocol:
1349
1349
  * Inbound: resource.bytes, resource.cancel
@@ -1981,4 +1981,361 @@ interface HttpUploaderOptions {
1981
1981
  */
1982
1982
  declare function createHttpUploader(options: HttpUploaderOptions): Uploader;
1983
1983
 
1984
- export { type AudioServiceOptions, type AudioSource, type CacheServiceOptions, type ConfigSchemaValidation, type ConfigService, type ConfigServiceOptions, type CoordinatedRelayOptions, type HostCacheBridge, type HostKeyEvent, type HostKeysBridge, type HostMediaBridge, type HttpUploaderOptions, type HttpUploaderRails, type IdentityServiceOptions, type KeysServiceOptions, type MediaMetadataLike, type MediaPlaybackOwner, type MediaServiceOptions, type MediaSessionCreateOptions, type MediaSessionTarget, type MediaSourceRef, type NostrTag, type Notification, type NotificationServiceOptions, type NotifyServiceOptions, type OutboxPublishOptions, type OutboxPublishResult, type OutboxQueryOptions, type OutboxRelayPlan, type OutboxRelayPool, type OutboxResult, type OutboxRouter, type OutboxRouterSubscription, type OutboxServiceOptions, type OutboxStrategy, type OutboxSubscribeOptions, type OutboxSubscriptionSink, type OutboxTarget, type RailServerConfig, type RelayListEntry, type RelayPoolOutboxRouterOptions, type RelayPoolServiceOptions, type ResourceService, type ResourceServiceOptions, type SignEvent, type ThemeService, type ThemeServiceOptions, type UploadDimensions, type UploadRail, type UploadRequest, type UploadResult, type UploadServiceOptions, type UploadState, type UploadStatus, type Uploader, type UploaderContext, createAudioService, createBrowserMediaBridge, createCacheService, createConfigService, createCoordinatedRelay, createHttpUploader, createIdentityService, createKeysService, createMediaService, createNotificationService, createNotifyService, createOutboxService, createRelayPoolOutboxRouter, createRelayPoolService, createResourceService, createThemeService, createUploadService };
1984
+ /**
1985
+ * intent-types.ts — NAP-INTENT (archetype intent dispatch) value types.
1986
+ *
1987
+ * Kehto-internal model for the NAP-INTENT wire contract (upstream draft:
1988
+ * napplet/nubs NAP-INTENT, namespace `window.napplet.intent`). The installed
1989
+ * `@napplet/core` peer predates these types, so — as with {@link ./cvm-types} —
1990
+ * kehto defines them here, wire-compatible with the upstream shapes, rather
1991
+ * than redefining the generic NIP-5D envelope.
1992
+ *
1993
+ * NAP-INTENT standardizes the *envelope*, not the payload. `archetype` is the
1994
+ * routing axis (which role, whose default), `protocol` is the parsing axis
1995
+ * (which NAP-N wire format shapes `payload`); the two are orthogonal.
1996
+ *
1997
+ * @packageDocumentation
1998
+ */
1999
+ /**
2000
+ * How the shell should pick the handling napplet for an intent.
2001
+ *
2002
+ * - `"default"` — route to the user's default handler for the archetype.
2003
+ * - `"choose"` — prompt the user with an "open with…" chooser.
2004
+ * - any other string — a specific napplet dTag (cross-napplet targeting; the
2005
+ * shell SHOULD require the user to have authorized this for the caller).
2006
+ */
2007
+ type IntentHandlerPreference = 'default' | 'choose' | (string & {});
2008
+ /** Window-behavior hints carried with an intent invoke. */
2009
+ interface IntentBehavior {
2010
+ /** Bring the handler window to the foreground. */
2011
+ focus?: boolean;
2012
+ /** Force a new window rather than reusing an existing handler instance. */
2013
+ newWindow?: boolean;
2014
+ /** Prefer reusing an already-open handler instance when one exists. */
2015
+ reuse?: boolean;
2016
+ }
2017
+ /** A request to dispatch an action to a napplet of a given archetype. */
2018
+ interface IntentRequest {
2019
+ /** Role slug, e.g. `"note"` (see the archetype registry). */
2020
+ archetype: string;
2021
+ /** Verb, default `"open"` (e.g. `"open"` | `"edit"` | `"pick"` | `"share"`). */
2022
+ action?: string;
2023
+ /** NAP-N id shaping `payload`; omit to use the archetype's recommended default. */
2024
+ protocol?: string;
2025
+ /** Opaque payload, typed by `protocol`. */
2026
+ payload?: unknown;
2027
+ /** Handler-selection preference. */
2028
+ handler?: IntentHandlerPreference;
2029
+ /** Window-behavior hints. */
2030
+ behavior?: IntentBehavior;
2031
+ }
2032
+ /** A napplet that can fulfill an archetype, sourced from the manifest catalog. */
2033
+ interface IntentCandidate {
2034
+ /** dTag of the napplet that can fulfill the archetype. */
2035
+ dTag: string;
2036
+ /** Human-readable title from the manifest. */
2037
+ title?: string;
2038
+ /** Verbs this candidate supports for the archetype. */
2039
+ actions: string[];
2040
+ /** NAP-N ids this candidate accepts for the archetype. */
2041
+ protocols: string[];
2042
+ /** Whether this candidate is the user/runtime default for the archetype. */
2043
+ isDefault?: boolean;
2044
+ }
2045
+ /** Availability of an archetype, sourced from the installed-napplet catalog. */
2046
+ interface IntentAvailability {
2047
+ /** The archetype this availability describes. */
2048
+ archetype: string;
2049
+ /** True when at least one installed napplet fulfills the archetype. */
2050
+ available: boolean;
2051
+ /** Candidate napplets that fulfill the archetype (from manifests, not instances). */
2052
+ candidates: IntentCandidate[];
2053
+ /** True when a user/runtime default handler is set for the archetype. */
2054
+ hasDefault: boolean;
2055
+ }
2056
+ /** The result of an intent invocation. */
2057
+ interface IntentResult {
2058
+ /** Whether the intent was dispatched successfully. */
2059
+ ok: boolean;
2060
+ /** The requested archetype, echoed back. */
2061
+ archetype: string;
2062
+ /** The resolved action (defaults to `"open"` when the request omits it). */
2063
+ action: string;
2064
+ /** True when a handler accepted the dispatch. */
2065
+ handled: boolean;
2066
+ /** dTag of the napplet that handled the intent, when one did. */
2067
+ handler?: string;
2068
+ /** Window id the handler was created or focused in. */
2069
+ windowId?: string;
2070
+ /** The wire format actually used to deliver `payload`. */
2071
+ protocol?: string;
2072
+ /** Error reason when the intent could not be fulfilled. */
2073
+ error?: string;
2074
+ }
2075
+
2076
+ /**
2077
+ * intent-service.ts — NAP-INTENT (archetype intent dispatch) reference service.
2078
+ *
2079
+ * Shell-side handler for the NAP-INTENT wire protocol. It is a pure envelope
2080
+ * router: it validates `intent.*` envelopes, delegates archetype resolution,
2081
+ * default handling, window lifecycle, and payload delivery to an injected
2082
+ * {@link IntentResolver}, and posts the correlated result / push messages
2083
+ * (echoing the request `id`) back to the napplet.
2084
+ *
2085
+ * The resolver is injected (options-as-bridge) so this service has no shell or
2086
+ * window-manager dependency and is fully unit-testable. A concrete
2087
+ * catalog-backed resolver ships alongside as {@link createCatalogIntentResolver}.
2088
+ *
2089
+ * ──────────────────────────── Responsibilities ────────────────────────────
2090
+ * Inbound: intent.invoke, intent.available, intent.handlers
2091
+ * Outbound: intent.invoke.result, intent.available.result,
2092
+ * intent.handlers.result, intent.changed
2093
+ *
2094
+ * The shell owns archetype→handler resolution, the user's default-handler
2095
+ * preference, the "open with…" chooser, and window creation/focus — all behind
2096
+ * the {@link IntentResolver}. This service only marshals the wire protocol and
2097
+ * fans `intent.changed` pushes out to the napplets it has served.
2098
+ *
2099
+ * @example
2100
+ * ```ts
2101
+ * import { createIntentService, createCatalogIntentResolver } from '@kehto/services';
2102
+ *
2103
+ * const resolver = createCatalogIntentResolver({ loadCatalog, windows });
2104
+ * runtime.registerService('intent', createIntentService({ resolver }));
2105
+ * ```
2106
+ *
2107
+ * @packageDocumentation
2108
+ */
2109
+
2110
+ /** Context passed to {@link IntentResolver.invoke} for trust/attribution. */
2111
+ interface IntentResolverContext {
2112
+ /** Window id of the napplet that issued the request. */
2113
+ windowId: string;
2114
+ }
2115
+ /**
2116
+ * Abstract intent resolver. Implementors own the installed-napplet catalog,
2117
+ * archetype→handler resolution, the user's default-handler preference, the
2118
+ * "open with…" chooser, window creation/focus, and payload delivery. The
2119
+ * service translates wire envelopes into these calls and back.
2120
+ */
2121
+ interface IntentResolver {
2122
+ /**
2123
+ * Resolve `request.archetype` to a handler, create or focus its window, and
2124
+ * deliver `payload`. Returns once the handler is resolved and the window
2125
+ * created; delivery MAY complete asynchronously.
2126
+ */
2127
+ invoke(request: IntentRequest, context: IntentResolverContext): IntentResult | Promise<IntentResult>;
2128
+ /** Report whether the runtime can currently satisfy `archetype`, and how. */
2129
+ available(archetype: string): IntentAvailability | Promise<IntentAvailability>;
2130
+ /** Report availability for every archetype the runtime can currently satisfy. */
2131
+ handlers(): IntentAvailability[] | Promise<IntentAvailability[]>;
2132
+ /**
2133
+ * Register for availability changes (a napplet installed/removed, or a default
2134
+ * handler changed). The service forwards each change to served napplets as an
2135
+ * `intent.changed` push. Returns an unsubscribe handle. Resolvers whose
2136
+ * catalog never changes at runtime MAY omit this.
2137
+ */
2138
+ onChanged?(listener: (availability: IntentAvailability) => void): () => void;
2139
+ }
2140
+ /** Options for {@link createIntentService}. */
2141
+ interface IntentServiceOptions {
2142
+ /** The intent resolver the shell uses to route archetypes. Required. */
2143
+ resolver: IntentResolver;
2144
+ }
2145
+ /**
2146
+ * Create the NAP-INTENT service handler.
2147
+ *
2148
+ * @param options - Must provide an {@link IntentResolver}.
2149
+ * @returns A `ServiceHandler` ready for `runtime.registerService('intent', handler)`.
2150
+ * @throws If `options.resolver` is missing.
2151
+ */
2152
+ declare function createIntentService(options: IntentServiceOptions): ServiceHandler;
2153
+
2154
+ /**
2155
+ * catalog-intent-resolver.ts — NAP-INTENT concrete {@link IntentResolver}.
2156
+ *
2157
+ * A reference resolver that satisfies {@link IntentResolver} from an
2158
+ * installed-napplet catalog plus host-supplied policy: the user's
2159
+ * default-handler preference, an optional "open with…" chooser, the archetype's
2160
+ * recommended default protocol, and a window controller that creates or focuses
2161
+ * the handler window and delivers the payload.
2162
+ *
2163
+ * Resolution policy (mirrors an OS implicit-intent + default-app model):
2164
+ * 1. Gather candidates for the archetype from the catalog.
2165
+ * 2. Pick the handler — an explicit `handler` dTag, a user choice
2166
+ * (`handler: "choose"`), the user's default, the sole candidate, or a
2167
+ * deterministic first-candidate fallback.
2168
+ * 3. Validate the resolved handler supports the requested `action` and
2169
+ * `protocol` (or fall back to the archetype's recommended default protocol,
2170
+ * then the candidate's first accepted protocol).
2171
+ * 4. Open or focus the handler window and return its id.
2172
+ *
2173
+ * The catalog, defaults, chooser, and window controller are all injected, so
2174
+ * this resolver carries no shell, manifest, or DOM dependency and is fully
2175
+ * unit-testable.
2176
+ *
2177
+ * @packageDocumentation
2178
+ */
2179
+
2180
+ /** The actions/protocols a napplet fulfills for a single archetype. */
2181
+ interface IntentArchetypeSupport {
2182
+ /** Verbs this napplet supports for the archetype (e.g. `["open", "edit"]`). */
2183
+ actions: string[];
2184
+ /** NAP-N protocol ids this napplet accepts for the archetype. */
2185
+ protocols: string[];
2186
+ }
2187
+ /**
2188
+ * One installed napplet's intent surface, derived from its signed NIP-5A
2189
+ * manifest. Keyed by archetype slug so a single napplet can fulfill several
2190
+ * roles.
2191
+ */
2192
+ interface IntentCatalogEntry {
2193
+ /** The napplet's dTag. */
2194
+ dTag: string;
2195
+ /** Human-readable title from the manifest. */
2196
+ title?: string;
2197
+ /** Archetype slug → the actions/protocols this napplet fulfills for it. */
2198
+ archetypes: Record<string, IntentArchetypeSupport>;
2199
+ }
2200
+ /** Parameters handed to {@link IntentWindowController.open}. */
2201
+ interface IntentOpenParams {
2202
+ /** dTag of the resolved handler napplet. */
2203
+ dTag: string;
2204
+ /** The archetype being dispatched. */
2205
+ archetype: string;
2206
+ /** The resolved action (e.g. `"open"`). */
2207
+ action: string;
2208
+ /** The wire format the payload is delivered with, when one was resolved. */
2209
+ protocol?: string;
2210
+ /** The opaque payload to deliver. */
2211
+ payload?: unknown;
2212
+ /** Window-behavior hints from the request. */
2213
+ behavior?: IntentBehavior;
2214
+ /** Window id of the napplet that issued the intent. */
2215
+ callerWindowId: string;
2216
+ }
2217
+ /** Creates or focuses the handler window and delivers the intent payload. */
2218
+ interface IntentWindowController {
2219
+ /**
2220
+ * Create or focus the handler window and deliver `payload` via `protocol`.
2221
+ * Returns the window id the handler was created or focused in.
2222
+ */
2223
+ open(params: IntentOpenParams): {
2224
+ windowId: string;
2225
+ } | Promise<{
2226
+ windowId: string;
2227
+ }>;
2228
+ }
2229
+ /** Options for {@link createCatalogIntentResolver}. */
2230
+ interface CatalogIntentResolverOptions {
2231
+ /** Return the installed-napplet catalog (signed NIP-5A manifests). Required. */
2232
+ loadCatalog(): IntentCatalogEntry[] | Promise<IntentCatalogEntry[]>;
2233
+ /** Window controller used to create/focus the resolved handler's window. Required. */
2234
+ windows: IntentWindowController;
2235
+ /**
2236
+ * The user's default handler dTag for an archetype, or `undefined` when none
2237
+ * is set. Default-handler settings are user state — never set by napplets.
2238
+ */
2239
+ getDefaultHandler?(archetype: string): string | undefined;
2240
+ /**
2241
+ * Resolve a `handler: "choose"` prompt (or a no-default ambiguity) to a dTag.
2242
+ * Returning `undefined` means the user cancelled. When omitted, ambiguous
2243
+ * resolution falls back to the first candidate.
2244
+ */
2245
+ chooseHandler?(archetype: string, candidates: IntentCandidate[], callerWindowId: string): string | undefined | Promise<string | undefined>;
2246
+ /** The archetype's recommended default protocol when the caller omits one. */
2247
+ defaultProtocol?(archetype: string): string | undefined;
2248
+ }
2249
+ /**
2250
+ * A {@link IntentResolver} backed by a catalog, with a host hook to announce
2251
+ * catalog/default changes.
2252
+ */
2253
+ interface CatalogIntentResolver extends IntentResolver {
2254
+ /**
2255
+ * Announce that the catalog or default handler for `archetype` changed. The
2256
+ * resolver recomputes availability and notifies `onChanged` listeners, which
2257
+ * the intent service forwards to napplets as `intent.changed` pushes.
2258
+ */
2259
+ notifyChanged(archetype: string): void;
2260
+ }
2261
+ /**
2262
+ * Create a catalog-backed NAP-INTENT resolver.
2263
+ *
2264
+ * @param options - Catalog loader and window controller (required) plus
2265
+ * optional default-handler, chooser, and default-protocol policy hooks.
2266
+ * @returns A {@link CatalogIntentResolver} for `createIntentService({ resolver })`.
2267
+ * @throws If `options.loadCatalog` or `options.windows` is missing.
2268
+ *
2269
+ * @example
2270
+ * ```ts
2271
+ * const resolver = createCatalogIntentResolver({
2272
+ * loadCatalog: () => installedNapplets,
2273
+ * windows: { open: ({ dTag }) => ({ windowId: openWindow(dTag) }) },
2274
+ * getDefaultHandler: (a) => userDefaults[a],
2275
+ * });
2276
+ * ```
2277
+ */
2278
+ declare function createCatalogIntentResolver(options: CatalogIntentResolverOptions): CatalogIntentResolver;
2279
+
2280
+ /**
2281
+ * manifest-intent-catalog.ts — signed-manifest → NAP-INTENT catalog adapter.
2282
+ *
2283
+ * Adapts a resolved NIP-5A/5D napplet manifest's archetype tags into an
2284
+ * {@link IntentCatalogEntry} — the shape `createCatalogIntentResolver.loadCatalog`
2285
+ * consumes. This lets NAP-INTENT availability and handler candidacy flow from
2286
+ * verified manifest tags rather than host-injected catalog data.
2287
+ *
2288
+ * To avoid a `@kehto/services → @kehto/nip` dependency cycle (services must stay
2289
+ * dependency-light and `@kehto/nip` is a lower-level NIP utility), the adapter
2290
+ * takes a minimal STRUCTURAL input {@link ManifestArchetypeInput} that the
2291
+ * `@kehto/nip/5d` `NappletManifest` satisfies by duck typing — callers pass
2292
+ * `resolved.manifest` directly without any package coupling.
2293
+ *
2294
+ * @packageDocumentation
2295
+ */
2296
+
2297
+ /**
2298
+ * The structural subset of `@kehto/nip/5d` `NappletManifest` the adapter needs.
2299
+ * Intentionally a duck-typed shape so the playground (or any caller) can pass a
2300
+ * resolved manifest without importing `@kehto/nip`.
2301
+ */
2302
+ interface ManifestArchetypeInput {
2303
+ /** The napplet's `d` identifier. */
2304
+ dTag: string;
2305
+ /** Optional human-readable title from the manifest. */
2306
+ title?: string;
2307
+ /**
2308
+ * Archetype slugs this napplet fulfills, from the manifest's `archetype` tags;
2309
+ * the optional `nap` is the recommended default wire protocol.
2310
+ */
2311
+ archetypes: Array<{
2312
+ slug: string;
2313
+ nap?: string;
2314
+ }>;
2315
+ }
2316
+ /**
2317
+ * Map a resolved napplet manifest's archetype data into an
2318
+ * {@link IntentCatalogEntry}.
2319
+ *
2320
+ * Each archetype `{ slug, nap }` becomes a keyed support record where `actions`
2321
+ * defaults to `['open']` (the NAP-INTENT default action — manifests do not
2322
+ * enumerate actions in this phase) and `protocols` is `[nap]` when a NAP-N is
2323
+ * present, else `[]`. Duplicate slugs keep the last occurrence.
2324
+ *
2325
+ * @param manifest - A resolved manifest's structural archetype data.
2326
+ * @returns The `IntentCatalogEntry` for `createCatalogIntentResolver`.
2327
+ *
2328
+ * @example
2329
+ * ```ts
2330
+ * manifestToIntentCatalogEntry({
2331
+ * dTag: 'profile-viewer',
2332
+ * title: 'Profile',
2333
+ * archetypes: [{ slug: 'profile', nap: 'NAP-1' }],
2334
+ * });
2335
+ * // → { dTag: 'profile-viewer', title: 'Profile',
2336
+ * // archetypes: { profile: { actions: ['open'], protocols: ['NAP-1'] } } }
2337
+ * ```
2338
+ */
2339
+ declare function manifestToIntentCatalogEntry(manifest: ManifestArchetypeInput): IntentCatalogEntry;
2340
+
2341
+ export { type AudioServiceOptions, type AudioSource, type CacheServiceOptions, type CatalogIntentResolver, type CatalogIntentResolverOptions, type ConfigSchemaValidation, type ConfigService, type ConfigServiceOptions, type CoordinatedRelayOptions, type HostCacheBridge, type HostKeyEvent, type HostKeysBridge, type HostMediaBridge, type HttpUploaderOptions, type HttpUploaderRails, type IdentityServiceOptions, type IntentArchetypeSupport, type IntentAvailability, type IntentBehavior, type IntentCandidate, type IntentCatalogEntry, type IntentHandlerPreference, type IntentOpenParams, type IntentRequest, type IntentResolver, type IntentResolverContext, type IntentResult, type IntentServiceOptions, type IntentWindowController, type KeysServiceOptions, type ManifestArchetypeInput, type MediaMetadataLike, type MediaPlaybackOwner, type MediaServiceOptions, type MediaSessionCreateOptions, type MediaSessionTarget, type MediaSourceRef, type NostrTag, type Notification, type NotificationServiceOptions, type NotifyServiceOptions, type OutboxPublishOptions, type OutboxPublishResult, type OutboxQueryOptions, type OutboxRelayPlan, type OutboxRelayPool, type OutboxResult, type OutboxRouter, type OutboxRouterSubscription, type OutboxServiceOptions, type OutboxStrategy, type OutboxSubscribeOptions, type OutboxSubscriptionSink, type OutboxTarget, type RailServerConfig, type RelayListEntry, type RelayPoolOutboxRouterOptions, type RelayPoolServiceOptions, type ResourceService, type ResourceServiceOptions, type SignEvent, type ThemeService, type ThemeServiceOptions, type UploadDimensions, type UploadRail, type UploadRequest, type UploadResult, type UploadServiceOptions, type UploadState, type UploadStatus, type Uploader, type UploaderContext, createAudioService, createBrowserMediaBridge, createCacheService, createCatalogIntentResolver, createConfigService, createCoordinatedRelay, createHttpUploader, createIdentityService, createIntentService, createKeysService, createMediaService, createNotificationService, createNotifyService, createOutboxService, createRelayPoolOutboxRouter, createRelayPoolService, createResourceService, createThemeService, createUploadService, manifestToIntentCatalogEntry };