@kehto/services 0.21.1 → 0.22.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 +42 -6
- package/dist/cvm-nostr-transport.d.ts +6 -0
- package/dist/cvm-nostr-transport.js +447 -13
- package/dist/cvm-nostr-transport.js.map +1 -1
- package/dist/index.d.ts +6 -13
- package/dist/index.js +39 -31
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/notification-service.ts","../src/identity-service.ts","../src/relay-pool-service.ts","../src/cache-service.ts","../src/count-service.ts","../src/coordinated-relay.ts","../src/keys-service-internals.ts","../src/keys-service.ts","../src/browser-media-bridge.ts","../src/media-service.ts","../src/notify-service.ts","../src/theme-service.ts","../src/config-schema.ts","../src/config-service.ts","../src/resource-service.ts","../src/outbox-service.ts","../src/relay-pool-outbox-router.ts","../src/upload-service.ts","../src/http-uploader.ts","../src/intent-service.ts","../src/catalog-intent-resolver.ts","../src/manifest-intent-catalog.ts","../src/cvm-service.ts","../src/link-service.ts","../src/lists-service.ts","../src/serial-service.ts","../src/ble-service.ts","../src/webrtc-service.ts","../src/common-service.ts","../src/dm-service.ts","../src/dm-nip17-adapter.ts","../src/dm-memory-store.ts","../src/dm-ndr-adapter.ts","../src/dm-cordn-adapter.ts","../src/fs-service.ts"],"sourcesContent":["\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type { Notification, NotificationServiceOptions } from './types.js';\n\n/** Notification service version — follows semver. */\nconst NOTIFICATION_SERVICE_VERSION = '1.0.0';\n\n/** Default maximum notifications per window. */\nconst DEFAULT_MAX_PER_WINDOW = 100;\n\n/** Counter for generating unique notification IDs. */\nlet idCounter = 0;\n\n/**\n * Generate a unique notification ID.\n */\nfunction generateId(): string {\n idCounter++;\n return `notif-${Date.now()}-${idCounter}`;\n}\n\ninterface NotificationStore {\n notifications: Map<string, Notification[]>;\n onChange?: (notifications: Notification[]) => void;\n maxPerWindow: number;\n}\n\nfunction getAllNotifications(store: NotificationStore): Notification[] {\n const all: Notification[] = [];\n for (const windowNotifs of store.notifications.values()) {\n all.push(...windowNotifs);\n }\n return all;\n}\n\nfunction notify(store: NotificationStore): void {\n store.onChange?.(getAllNotifications(store));\n}\n\nfunction getWindowNotifications(store: NotificationStore, windowId: string): Notification[] {\n let list = store.notifications.get(windowId);\n if (!list) {\n list = [];\n store.notifications.set(windowId, list);\n }\n return list;\n}\n\nfunction enforceLimit(store: NotificationStore, list: Notification[]): void {\n while (list.length > store.maxPerWindow) {\n list.shift();\n }\n}\n\nfunction findById(store: NotificationStore, windowId: string, id: string): [Notification, number] | undefined {\n const list = store.notifications.get(windowId);\n const index = list?.findIndex((notification) => notification.id === id) ?? -1;\n if (!list || index === -1) return undefined;\n return [list[index], index];\n}\n\nfunction createNotification(\n store: NotificationStore,\n windowId: string,\n title: string,\n body: string,\n): Notification {\n const notification: Notification = {\n id: generateId(),\n windowId,\n title,\n body,\n read: false,\n createdAt: Math.floor(Date.now() / 1000),\n };\n const list = getWindowNotifications(store, windowId);\n list.push(notification);\n enforceLimit(store, list);\n notify(store);\n return notification;\n}\n\nfunction dismissNotification(store: NotificationStore, windowId: string, id: string): void {\n const found = findById(store, windowId, id);\n if (!found) return;\n const [, index] = found;\n const list = store.notifications.get(windowId);\n if (!list) return;\n list.splice(index, 1);\n if (list.length === 0) store.notifications.delete(windowId);\n notify(store);\n}\n\nfunction markNotificationRead(store: NotificationStore, windowId: string, id: string): void {\n const found = findById(store, windowId, id);\n if (!found) return;\n const [notification] = found;\n if (!notification.read) {\n notification.read = true;\n notify(store);\n }\n}\n\nfunction handleNotifyEnvelope(\n store: NotificationStore,\n windowId: string,\n action: string,\n msg: NappletMessage & Record<string, unknown>,\n send: (msg: NappletMessage) => void,\n): void {\n switch (action) {\n case 'create': {\n const title = typeof msg.title === 'string' ? msg.title : '';\n const body = typeof msg.body === 'string' ? msg.body : '';\n const notification = createNotification(store, windowId, title, body);\n send({ type: 'notify.created', id: notification.id } as NappletMessage);\n break;\n }\n\n case 'dismiss': {\n const notifId = typeof msg.notificationId === 'string' ? msg.notificationId : '';\n if (notifId) dismissNotification(store, windowId, notifId);\n break;\n }\n\n case 'read': {\n const notifId = typeof msg.notificationId === 'string' ? msg.notificationId : '';\n if (notifId) markNotificationRead(store, windowId, notifId);\n break;\n }\n\n case 'list': {\n const windowNotifs = store.notifications.get(windowId) ?? [];\n send({ type: 'notify.listed', notifications: windowNotifs } as NappletMessage);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Create a notification service handler.\n *\n * The notification service is a state registry that tracks notifications\n * per napplet window. It accepts direct `notify.*` service envelopes; INC\n * topics are opaque application data and never select a host service. The\n * shell host controls presentation through the onChange callback.\n *\n * @param options - Optional configuration (onChange callback, maxPerWindow limit)\n * @returns A ServiceHandler to register with the runtime\n *\n * @example\n * ```ts\n * import { createNotificationService } from '@kehto/services';\n *\n * const notifications = createNotificationService({\n * onChange: (list) => {\n * const unread = list.filter(n => !n.read);\n * updateBadge(unread.length);\n * },\n * maxPerWindow: 50,\n * });\n *\n * runtime.registerService('notify', notifications);\n * ```\n */\nexport function createNotificationService(options?: NotificationServiceOptions): ServiceHandler {\n const store: NotificationStore = {\n notifications: new Map<string, Notification[]>(),\n onChange: options?.onChange,\n maxPerWindow: options?.maxPerWindow ?? DEFAULT_MAX_PER_WINDOW,\n };\n\n const descriptor: ServiceDescriptor = {\n name: 'notify',\n version: NOTIFICATION_SERVICE_VERSION,\n description: 'Notification state registry — tracks notifications per napplet window',\n };\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (!message.type.startsWith('notify.')) return;\n handleNotifyEnvelope(\n store,\n windowId,\n message.type.slice(7),\n message as NappletMessage & Record<string, unknown>,\n send,\n );\n },\n\n onWindowDestroyed(windowId: string): void {\n if (store.notifications.delete(windowId)) {\n notify(store);\n }\n },\n };\n}\n","/**\n * identity-service.ts — NIP-5D identity nap reference service.\n *\n * MIGRATION from signer-service (v1.1 -> v1.2):\n * - signer.getPublicKey -> identity.getPublicKey (same shell state)\n * - signer.getRelays -> identity.getRelays (same shell state)\n * - signer.signEvent -> DELETED (no napplet-visible path; shell signs\n * internally inside relay.publish)\n * - signer.nip04.encrypt/decrypt -> DELETED\n * - signer.nip44.encrypt/decrypt -> DELETED (shell encrypts internally\n * inside relay.publishEncrypted)\n *\n * Identity is strictly read-only per NAP-IDENTITY: napplets learn *about* the\n * user but cannot act *as* the user — no signing, encryption, or decryption.\n * (An `identity.decrypt` capability was briefly added in v1.8 and removed as a\n * spec violation; decryption belongs to the runtime, inline over the wire.)\n *\n * Handles 9 identity.* request types from @napplet/nap/identity. getPublicKey\n * and getRelays return real values sourced from hooks.auth.getSigner(); the\n * remaining read-only queries can be backed by optional host providers. When a\n * provider is absent, the service keeps returning spec-shaped empty results.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceHandler, Signer } from '@kehto/runtime';\nimport type {\n IdentityGetBadgesMessage,\n IdentityGetBlockedMessage,\n IdentityGetFollowsMessage,\n IdentityGetListMessage,\n IdentityGetMutesMessage,\n IdentityGetProfileMessage,\n IdentityGetPublicKeyResultMessage,\n IdentityGetRelaysResultMessage,\n IdentityGetProfileResultMessage,\n IdentityGetFollowsResultMessage,\n IdentityGetListResultMessage,\n IdentityGetZapsResultMessage,\n IdentityGetZapsMessage,\n IdentityGetMutesResultMessage,\n IdentityGetBlockedResultMessage,\n IdentityGetBadgesResultMessage,\n RelayPermission,\n} from '@napplet/nap/identity/types';\n\n/** Identity service version — follows semver. */\nconst IDENTITY_SERVICE_VERSION = '1.0.0';\n\n/** A value that may be returned synchronously or through a Promise. */\nexport type MaybePromise<T> = T | Promise<T>;\n\ntype IdentityProviderResult = NappletMessage & { error?: string };\ntype SendIdentityMessage = (msg: NappletMessage) => void;\n\nfunction sendProviderError<T extends IdentityProviderResult>(\n send: SendIdentityMessage,\n result: T,\n fallback: string,\n _err: unknown,\n): void {\n send({\n ...result,\n error: fallback,\n });\n}\n\nasync function getCurrentPubkey(options: IdentityServiceOptions): Promise<string> {\n try {\n const currentSigner = options.getSigner();\n if (!currentSigner?.getPublicKey) return '';\n return (await currentSigner.getPublicKey()) ?? '';\n } catch {\n return '';\n }\n}\n\nfunction sendOptionalProviderResult<T extends IdentityProviderResult>(\n options: IdentityServiceOptions,\n send: SendIdentityMessage,\n fallbackResult: T,\n errorFallback: string,\n buildResult?: (pubkey: string) => MaybePromise<T>,\n): void {\n if (!buildResult) {\n send(fallbackResult);\n return;\n }\n\n Promise.resolve(getCurrentPubkey(options))\n .then((pubkey) => buildResult(pubkey))\n .then((result) => send(result))\n .catch((err: unknown) => sendProviderError(send, fallbackResult, errorFallback, err));\n}\n\n/**\n * Options for creating the identity service.\n *\n * @example\n * ```ts\n * const identityService = createIdentityService({\n * getSigner: () => window.nostr ?? null,\n * });\n * runtime.registerService('identity', identityService);\n * ```\n */\nexport interface IdentityServiceOptions {\n /**\n * Return the NIP-07-compatible signer (or null) used to resolve\n * identity.getPublicKey / identity.getRelays. Called on every request —\n * availability can change dynamically.\n */\n getSigner: () => Signer | null;\n\n /**\n * Optional host-backed profile lookup for the current user.\n *\n * Kehto does not query relays itself; hosts that already maintain kind-0\n * metadata can provide it here without replacing the whole identity service.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getProfile request envelope.\n * @returns Profile metadata, or null when unavailable.\n */\n getProfile?: (\n pubkey: string,\n message: IdentityGetProfileMessage,\n ) => MaybePromise<IdentityGetProfileResultMessage['profile']>;\n\n /**\n * Optional host-backed follow-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getFollows request envelope.\n * @returns Hex-encoded followed pubkeys.\n */\n getFollows?: (\n pubkey: string,\n message: IdentityGetFollowsMessage,\n ) => MaybePromise<IdentityGetFollowsResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed categorized-list lookup for the current user.\n *\n * @param listType - Requested list category from the wire envelope.\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getList request envelope.\n * @returns List entries.\n */\n getList?: (\n listType: string,\n pubkey: string,\n message: IdentityGetListMessage,\n ) => MaybePromise<IdentityGetListResultMessage['entries']>;\n\n /**\n * Optional host-backed zap receipt lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getZaps request envelope.\n * @returns Zap receipts.\n */\n getZaps?: (\n pubkey: string,\n message: IdentityGetZapsMessage,\n ) => MaybePromise<IdentityGetZapsResultMessage['zaps']>;\n\n /**\n * Optional host-backed mute-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getMutes request envelope.\n * @returns Hex-encoded muted pubkeys.\n */\n getMutes?: (\n pubkey: string,\n message: IdentityGetMutesMessage,\n ) => MaybePromise<IdentityGetMutesResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed block-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getBlocked request envelope.\n * @returns Hex-encoded blocked pubkeys.\n */\n getBlocked?: (\n pubkey: string,\n message: IdentityGetBlockedMessage,\n ) => MaybePromise<IdentityGetBlockedResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed badge lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getBadges request envelope.\n * @returns Badges awarded to the user.\n */\n getBadges?: (\n pubkey: string,\n message: IdentityGetBadgesMessage,\n ) => MaybePromise<IdentityGetBadgesResultMessage['badges']>;\n}\n\nfunction handleGetPublicKey(options: IdentityServiceOptions, id: string, send: SendIdentityMessage): void {\n Promise.resolve()\n .then(() => options.getSigner())\n .then(async (signer) => signer?.getPublicKey ? signer.getPublicKey() : '')\n .catch(() => '')\n .then((pubkey) => {\n const result: IdentityGetPublicKeyResultMessage = {\n type: 'identity.getPublicKey.result',\n id,\n pubkey: pubkey ?? '',\n };\n send(result);\n });\n}\n\nfunction handleGetRelays(options: IdentityServiceOptions, id: string, send: SendIdentityMessage): void {\n const fallbackResult: IdentityGetRelaysResultMessage = {\n type: 'identity.getRelays.result',\n id,\n relays: {},\n };\n\n Promise.resolve()\n .then(() => options.getSigner())\n .then(async (signer): Promise<IdentityGetRelaysResultMessage> => {\n if (!signer?.getRelays) return fallbackResult;\n return {\n ...fallbackResult,\n relays: (await signer.getRelays()) ?? {} as Record<string, RelayPermission>,\n };\n })\n .catch(() => ({ ...fallbackResult, error: 'identity.getRelays failed' }))\n .then(send);\n}\n\nfunction handleReadProvider(\n options: IdentityServiceOptions,\n id: string,\n message: NappletMessage,\n send: SendIdentityMessage,\n): boolean {\n switch (message.type) {\n case 'identity.getProfile':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getProfile.result', id, profile: null },\n 'identity.getProfile failed',\n options.getProfile\n ? async (pubkey): Promise<IdentityGetProfileResultMessage> => ({\n type: 'identity.getProfile.result',\n id,\n profile: (await options.getProfile!(pubkey, message as IdentityGetProfileMessage)) ?? null,\n })\n : undefined,\n );\n return true;\n\n case 'identity.getFollows':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getFollows.result', id, pubkeys: [] },\n 'identity.getFollows failed',\n options.getFollows\n ? async (pubkey): Promise<IdentityGetFollowsResultMessage> => ({\n type: 'identity.getFollows.result',\n id,\n pubkeys: (await options.getFollows!(pubkey, message as IdentityGetFollowsMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getList':\n handleGetList(options, id, message as IdentityGetListMessage, send);\n return true;\n\n case 'identity.getZaps':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getZaps.result', id, zaps: [] },\n 'identity.getZaps failed',\n options.getZaps\n ? async (pubkey): Promise<IdentityGetZapsResultMessage> => ({\n type: 'identity.getZaps.result',\n id,\n zaps: (await options.getZaps!(pubkey, message as IdentityGetZapsMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getMutes':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getMutes.result', id, pubkeys: [] },\n 'identity.getMutes failed',\n options.getMutes\n ? async (pubkey): Promise<IdentityGetMutesResultMessage> => ({\n type: 'identity.getMutes.result',\n id,\n pubkeys: (await options.getMutes!(pubkey, message as IdentityGetMutesMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getBlocked':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getBlocked.result', id, pubkeys: [] },\n 'identity.getBlocked failed',\n options.getBlocked\n ? async (pubkey): Promise<IdentityGetBlockedResultMessage> => ({\n type: 'identity.getBlocked.result',\n id,\n pubkeys: (await options.getBlocked!(pubkey, message as IdentityGetBlockedMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getBadges':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getBadges.result', id, badges: [] },\n 'identity.getBadges failed',\n options.getBadges\n ? async (pubkey): Promise<IdentityGetBadgesResultMessage> => ({\n type: 'identity.getBadges.result',\n id,\n badges: (await options.getBadges!(pubkey, message as IdentityGetBadgesMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n default:\n return false;\n }\n}\n\nfunction handleGetList(\n options: IdentityServiceOptions,\n id: string,\n message: IdentityGetListMessage,\n send: SendIdentityMessage,\n): void {\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getList.result', id, entries: [] },\n 'identity.getList failed',\n options.getList\n ? async (pubkey): Promise<IdentityGetListResultMessage> => ({\n type: 'identity.getList.result',\n id,\n entries: (await options.getList!(message.listType, pubkey, message)) ?? [],\n })\n : undefined,\n );\n}\n\nfunction handleIdentityServiceMessage(\n options: IdentityServiceOptions,\n message: NappletMessage,\n send: SendIdentityMessage,\n): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n\n switch (message.type) {\n case 'identity.getPublicKey':\n // Per NIP-5D, no signer resolves to the empty-pubkey sentinel, not an error.\n handleGetPublicKey(options, id, send);\n return;\n\n case 'identity.getRelays':\n handleGetRelays(options, id, send);\n return;\n\n default:\n handleReadProvider(options, id, message, send);\n }\n}\n\n/**\n * Create an identity service that handles NIP-5D identity.* envelope messages.\n *\n * Supports the 9 read-only identity.* request types from @napplet/nap/identity.\n * The two nostr-info queries (getPublicKey, getRelays) resolve through the\n * caller-supplied signer; the remaining read-only queries resolve through\n * optional host providers or return default/empty payloads with spec-correct\n * envelope shapes so napplets always receive a result envelope.\n *\n * @param options - Identity service configuration (getSigner)\n * @returns A ServiceHandler ready for runtime.registerService('identity', handler)\n *\n * @example\n * ```ts\n * import { createIdentityService } from '@kehto/services';\n *\n * const identity = createIdentityService({\n * getSigner: () => mySignerAdapter,\n * });\n * runtime.registerService('identity', identity);\n * ```\n */\nexport function createIdentityService(options: IdentityServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'identity',\n version: IDENTITY_SERVICE_VERSION,\n description: 'NIP-5D identity NAP reference handler (9 read-only identity queries)',\n },\n\n handleMessage(\n _windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n handleIdentityServiceMessage(options, message, send);\n },\n\n // Identity service has no per-window state to clean up.\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n}\n","/**\n * relay-pool-service.ts — Relay pool as a ServiceHandler.\n *\n * Wraps an existing relay pool implementation (subscribe, publish,\n * selectRelayTier, isAvailable) as a ServiceHandler that receives\n * relay NAP envelope messages and manages subscription lifecycle.\n *\n * Handles: relay.subscribe, relay.close, relay.publish, relay.publishEncrypted.\n *\n * Note on `relay.publishEncrypted`: the canonical napplet→shell path routes\n * through @kehto/runtime handleRelayMessage, which performs shell-internal\n * NIP-44/NIP-04 encryption via the signer, then synthesizes a relay.publish\n * envelope handed to this service. The publishEncrypted branch here is a\n * fallback for alternate wirings; by the time the service sees the\n * envelope, content MUST already be ciphertext (the service never encrypts\n * or decrypts).\n */\n\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResultWithHints } from '@kehto/runtime';\n\n// Timer globals available in all JS runtimes\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** EOSE fallback timeout in milliseconds. */\nconst EOSE_FALLBACK_MS = 15_000;\n\n/**\n * Options for creating a relay pool service.\n *\n * @example\n * ```ts\n * const relayPoolService = createRelayPoolService({\n * subscribe: (filters, cb, urls) => myPool.subscribe(filters, cb, urls),\n * publish: (event) => myPool.publish(event),\n * selectRelayTier: (filters) => myPool.selectRelays(filters),\n * isAvailable: () => myPool.connected,\n * });\n * ```\n */\nexport interface RelayPoolServiceOptions {\n /**\n * Subscribe to events matching filters. Returns handle with unsubscribe().\n *\n * @param filters - NIP-01 filter objects\n * @param callback - Receives matching events or 'EOSE' plus optional observed relay URLs\n * @param relayUrls - Optional relay URL hints\n * @returns Handle to cancel the subscription\n */\n subscribe(\n filters: NostrFilter[],\n callback: (item: NostrEvent | 'EOSE', observedRelayUrls?: string[]) => void,\n relayUrls?: string[],\n ): { unsubscribe(): void };\n\n /**\n * Publish an event to relays.\n *\n * @param event - The event to publish\n * @returns Nothing, or a promise that settles when the publish request completes\n */\n publish(event: NostrEvent): void | Promise<void>;\n\n /**\n * Select relay URLs appropriate for the given filters.\n *\n * @param filters - NIP-01 filter objects\n * @returns Array of relay URLs\n */\n selectRelayTier(filters: NostrFilter[]): string[];\n\n /**\n * Whether the relay pool is available and connected.\n *\n * @returns true if the relay pool can handle requests\n */\n isAvailable(): boolean;\n}\n\n/** Internal subscription tracking entry. */\ninterface TrackedSubscription {\n handle: { unsubscribe(): void };\n eoseTimer: unknown;\n}\n\ntype RelayServiceMessage = NappletMessage & {\n id?: unknown;\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n relay?: unknown;\n};\n\n/**\n * Create a relay pool service that wraps an existing relay pool\n * implementation as a ServiceHandler.\n *\n * Handles relay.subscribe, relay.close, and relay.publish envelopes.\n * Tracks subscriptions per windowId:subId for lifecycle management.\n * Sets a 15-second EOSE fallback timer on each subscription.\n *\n * @param options - Relay pool implementation to wrap\n * @returns A ServiceHandler ready for runtime.registerService('relay', handler)\n *\n * @example\n * ```ts\n * import { createRelayPoolService } from '@kehto/services';\n *\n * const pool = createRelayPoolService({\n * subscribe: (f, cb, urls) => applesauce.subscribe(f, cb, urls),\n * publish: (e) => applesauce.publish(e),\n * selectRelayTier: (f) => applesauce.getRelays(f),\n * isAvailable: () => applesauce.connected,\n * });\n * runtime.registerService('relay', pool);\n * ```\n */\nexport function createRelayPoolService(options: RelayPoolServiceOptions): ServiceHandler {\n const tracked = new Map<string, TrackedSubscription>();\n\n function publishEvent(\n relayMessage: RelayServiceMessage,\n resultType: 'relay.publish.result' | 'relay.publishEncrypted.result',\n send: (msg: NappletMessage) => void,\n ): void {\n const id = typeof relayMessage.id === 'string' ? relayMessage.id : '';\n const event = relayMessage.event as NostrEvent | undefined;\n if (!event || typeof event !== 'object') {\n send({ type: resultType, id, ok: false, error: 'invalid event' } as NappletMessage);\n return;\n }\n if (!options.isAvailable()) {\n send({ type: resultType, id, ok: false, error: 'no relay pool available' } as NappletMessage);\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n await options.publish(event);\n send({\n type: resultType,\n id,\n ok: true,\n event,\n eventId: event.id,\n } as NappletMessage);\n } catch (error) {\n send({\n type: resultType,\n id,\n ok: false,\n error: error instanceof Error && error.message ? error.message : 'publish failed',\n } as NappletMessage);\n }\n })();\n }\n\n return {\n descriptor: {\n name: 'relay-pool',\n version: '1.0.0',\n description: 'Relay pool subscription and publishing',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n const subKey = `${windowId}:${subId}`;\n\n // Cancel existing subscription for this key if any\n const existing = tracked.get(subKey);\n if (existing) {\n existing.handle.unsubscribe();\n clearTimeout(existing.eoseTimer);\n tracked.delete(subKey);\n }\n\n if (!options.isAvailable()) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n const relayHint = typeof relayMessage.relay === 'string' && relayMessage.relay.length > 0\n ? relayMessage.relay\n : undefined;\n const relayUrls = relayHint ? [relayHint] : options.selectRelayTier(filters);\n let eoseSent = false;\n\n const eoseTimer = setTimeout(() => {\n if (!eoseSent) {\n eoseSent = true;\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n }, EOSE_FALLBACK_MS);\n\n const handle = options.subscribe(filters, (item, observedRelayUrls) => {\n if (item === 'EOSE') {\n clearTimeout(eoseTimer);\n if (!eoseSent) {\n eoseSent = true;\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n return;\n }\n send({\n type: 'relay.event',\n subId,\n result: createRelayEventResultWithHints(item, observedRelayUrls ?? relayUrls),\n } as NappletMessage);\n }, relayUrls);\n\n tracked.set(subKey, { handle, eoseTimer });\n return;\n }\n\n if (message.type === 'relay.close') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n const entry = tracked.get(subKey);\n if (entry) {\n entry.handle.unsubscribe();\n clearTimeout(entry.eoseTimer);\n tracked.delete(subKey);\n }\n return;\n }\n\n if (message.type === 'relay.publish') {\n publishEvent(relayMessage, 'relay.publish.result', send);\n return;\n }\n\n if (message.type === 'relay.publishEncrypted') {\n publishEvent(relayMessage, 'relay.publishEncrypted.result', send);\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of tracked) {\n if (key.startsWith(prefix)) {\n entry.handle.unsubscribe();\n clearTimeout(entry.eoseTimer);\n tracked.delete(key);\n }\n }\n },\n };\n}\n","\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResult } from '@kehto/runtime';\n\ntype RelayServiceMessage = NappletMessage & {\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n};\n\n/**\n * Options for creating a cache service.\n *\n * @example\n * ```ts\n * const cacheService = createCacheService({\n * query: (filters) => myIndexedDB.query(filters),\n * store: (event) => myIndexedDB.store(event),\n * isAvailable: () => true,\n * });\n * ```\n */\nexport interface CacheServiceOptions {\n /**\n * Query cached events matching the given filters.\n *\n * @param filters - NIP-01 filter objects\n * @returns Promise resolving to matching cached events\n */\n query(filters: NostrFilter[]): Promise<NostrEvent[]>;\n\n /**\n * Store an event in cache. Best-effort, may silently fail.\n *\n * @param event - The event to store\n */\n store(event: NostrEvent): void;\n\n /**\n * Whether the cache is available.\n *\n * @returns true if the cache can handle requests\n */\n isAvailable(): boolean;\n}\n\n/**\n * @kehto/services cross-package naming-parity alias for {@link CacheServiceOptions}.\n *\n * `HostCacheBridge` matches the v1.4 `HostKeysBridge` / `HostMediaBridge`\n * convention — it is a pure type alias for `CacheServiceOptions`, NOT a new\n * type. Existing consumers of `CacheServiceOptions` continue to work\n * unchanged; new consumers may prefer `HostCacheBridge` for consistency\n * with the other Host*Bridge names in `@kehto/services`.\n *\n * Anti-feature note (PITFALLS.md M-02): `CacheServiceOptions` MUST remain\n * the primary export. This alias is additive; do not rename or delete\n * `CacheServiceOptions` when other Host*Bridge names eventually\n * stabilize.\n *\n * @example\n * ```ts\n * import type { HostCacheBridge } from '@kehto/services';\n * const cache: HostCacheBridge = {\n * query: (filters) => myIndexedDB.query(filters),\n * store: (event) => myIndexedDB.store(event),\n * isAvailable: () => true,\n * };\n * ```\n */\nexport type HostCacheBridge = CacheServiceOptions;\n\n/**\n * Create a cache service that wraps an existing cache implementation\n * as a ServiceHandler.\n *\n * Cache relay.subscribe subscriptions are one-shot — they query, deliver\n * results, send EOSE, and are done. No long-lived subscription tracking needed.\n * Cache query failures are best-effort: EOSE is sent even on failure.\n *\n * @param options - Cache implementation to wrap\n * @returns A ServiceHandler ready for runtime.registerService('cache', handler)\n *\n * @example\n * ```ts\n * import { createCacheService } from '@kehto/services';\n *\n * const cache = createCacheService({\n * query: (f) => workerRelay.query(f),\n * store: (e) => workerRelay.store(e),\n * isAvailable: () => workerRelay.ready,\n * });\n * runtime.registerService('cache', cache);\n * ```\n */\nexport function createCacheService(options: CacheServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'cache',\n version: '1.0.0',\n description: 'Local event cache (IndexedDB, worker relay, etc.)',\n },\n\n handleMessage(_windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n\n if (!options.isAvailable()) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n options\n .query(filters)\n .then((events) => {\n for (const event of events) {\n send({ type: 'relay.event', subId, result: createRelayEventResult(event) } as NappletMessage);\n }\n send({ type: 'relay.eose', subId } as NappletMessage);\n })\n .catch(() => {\n // Cache query is best-effort — send EOSE even on failure\n send({ type: 'relay.eose', subId } as NappletMessage);\n });\n return;\n }\n\n if (message.type === 'relay.publish') {\n const event = relayMessage.event as NostrEvent | undefined;\n if (event && typeof event === 'object' && options.isAvailable()) {\n try {\n options.store(event);\n } catch {\n /* Cache write is best-effort */\n }\n }\n return;\n }\n },\n\n // Cache has no per-window state to clean up\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n}\n","import type { NappletMessage, NostrFilter } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\n\nexport interface CountRequest {\n /** NIP-01 filters with NIP-45 COUNT OR semantics. */\n readonly filters: NostrFilter[];\n /** Optional runtime-owned count policy hints. */\n readonly options?: Record<string, unknown>;\n /** Window requesting the count. */\n readonly windowId: string;\n}\n\nexport interface CountResult {\n readonly ok: boolean;\n readonly count?: number;\n readonly approximate?: boolean;\n readonly hll?: string;\n readonly relays?: readonly string[];\n readonly error?: string;\n readonly reason?: string;\n}\n\nexport interface CountServiceOptions {\n /**\n * Count matching events without returning event payloads.\n *\n * @param request - Validated count request.\n * @returns Exact/approximate count metadata, or a number for an exact count.\n */\n count(request: CountRequest): CountResult | number | Promise<CountResult | number>;\n\n /**\n * Optional refusal policy for unsupported or too-expensive filters.\n *\n * Return `true` to allow the filter, `false` for a generic refusal, or a\n * string with the refusal reason.\n *\n * @param filter - Filter being evaluated.\n * @param index - Filter index in the OR filter set.\n */\n isFilterSupported?(filter: NostrFilter, index: number): boolean | string;\n\n /** Maximum number of OR filters accepted before refusing the request. */\n maxFilters?: number;\n}\n\ntype CountServiceMessage = NappletMessage & {\n id?: unknown;\n filters?: unknown;\n options?: unknown;\n};\n\nconst DEFAULT_MAX_FILTERS = 16;\n\nexport function createCountService(options: CountServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'count',\n version: '1.0.0',\n description: 'Runtime-mediated NIP-01 filter counts',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (message.type !== 'count.query') return;\n\n const countMessage = message as CountServiceMessage;\n const id = typeof countMessage.id === 'string' ? countMessage.id : '';\n const filters = countMessage.filters;\n if (!isValidFilterArray(filters)) {\n send(result(id, { ok: false, error: 'invalid-filter', reason: 'count.query requires a non-empty filters array' }));\n return;\n }\n\n const maxFilters = options.maxFilters ?? DEFAULT_MAX_FILTERS;\n if (filters.length > maxFilters) {\n send(result(id, { ok: false, error: 'too-expensive', reason: `count.query accepts at most ${maxFilters} filters` }));\n return;\n }\n\n for (let i = 0; i < filters.length; i++) {\n const supported = options.isFilterSupported?.(filters[i]!, i) ?? true;\n if (supported !== true) {\n send(result(id, {\n ok: false,\n error: 'unsupported-filter',\n reason: typeof supported === 'string' ? supported : `filter ${i} is not supported by this count backend`,\n }));\n return;\n }\n }\n\n const request: CountRequest = {\n filters,\n windowId,\n ...(isPlainObject(countMessage.options) ? { options: countMessage.options } : {}),\n };\n\n Promise.resolve(options.count(request))\n .then((countResult) => {\n send(result(id, normalizeCountResult(countResult)));\n })\n .catch((error: unknown) => {\n send(result(id, {\n ok: false,\n error: 'count-unavailable',\n reason: error instanceof Error ? error.message : 'count backend failed',\n }));\n });\n },\n };\n}\n\nfunction result(id: string, value: CountResult): NappletMessage {\n return {\n type: 'count.query.result',\n id,\n ok: value.ok,\n ...(typeof value.count === 'number' ? { count: value.count } : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays.filter((relay): relay is string => typeof relay === 'string') } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n } as NappletMessage;\n}\n\nfunction normalizeCountResult(value: CountResult | number): CountResult {\n if (typeof value === 'number') {\n return Number.isFinite(value) && value >= 0\n ? { ok: true, count: Math.floor(value), approximate: false }\n : { ok: false, error: 'count-unavailable', reason: 'count backend returned an invalid count' };\n }\n\n const count = typeof value.count === 'number' && Number.isFinite(value.count) && value.count >= 0\n ? Math.floor(value.count)\n : undefined;\n return {\n ok: value.ok === true,\n ...(count !== undefined ? { count } : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n };\n}\n\nfunction isValidFilterArray(value: unknown): value is NostrFilter[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((filter) => isPlainObject(filter));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResult, createRelayEventResultWithHints } from '@kehto/runtime';\nimport type { RelayPoolServiceOptions } from './relay-pool-service.js';\nimport type { CacheServiceOptions } from './cache-service.js';\n\n// Timer globals available in all JS runtimes\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** Default EOSE fallback timeout. */\nconst DEFAULT_EOSE_TIMEOUT_MS = 15_000;\n\n/**\n * Options for creating a coordinated relay service.\n *\n * @example\n * ```ts\n * const relay = createCoordinatedRelay({\n * relayPool: {\n * subscribe: (f, cb, urls) => pool.subscribe(f, cb, urls),\n * publish: (e) => pool.publish(e),\n * selectRelayTier: (f) => pool.selectRelays(f),\n * isAvailable: () => pool.connected,\n * },\n * cache: {\n * query: (f) => db.query(f),\n * store: (e) => db.store(e),\n * isAvailable: () => db.ready,\n * },\n * });\n * runtime.registerService('relay', relay);\n * ```\n */\nexport interface CoordinatedRelayOptions {\n /**\n * Relay pool implementation.\n * Uses the same interface as RelayPoolServiceOptions.\n */\n relayPool: RelayPoolServiceOptions;\n\n /**\n * Local cache implementation.\n * Uses the same interface as CacheServiceOptions.\n */\n cache: CacheServiceOptions;\n\n /**\n * EOSE fallback timeout in milliseconds.\n * Sent if relay pool doesn't respond within this time.\n * Default: 15000 (15 seconds).\n */\n eoseTimeoutMs?: number;\n}\n\n/** Internal state for a tracked subscription. */\ninterface TrackedSub {\n seenIds: Set<string>;\n cacheEose: boolean;\n relayEose: boolean;\n eoseSent: boolean;\n eoseTimer: unknown;\n relayHandle: { unsubscribe(): void } | null;\n}\n\ntype RelayServiceMessage = NappletMessage & {\n id?: unknown;\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n relay?: unknown;\n};\n\nfunction publishCoordinatedEvent(\n options: CoordinatedRelayOptions,\n relayMessage: RelayServiceMessage,\n send: (message: NappletMessage) => void,\n): void {\n const id = typeof relayMessage.id === 'string' ? relayMessage.id : '';\n const event = relayMessage.event as NostrEvent | undefined;\n if (!event || typeof event !== 'object') {\n send({ type: 'relay.publish.result', id, ok: false, error: 'invalid event' } as NappletMessage);\n return;\n }\n if (!options.relayPool.isAvailable()) {\n send({\n type: 'relay.publish.result',\n id,\n ok: false,\n error: 'no relay pool available',\n } as NappletMessage);\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n await options.relayPool.publish(event);\n if (options.cache.isAvailable()) {\n try { options.cache.store(event); } catch { /* cache storage is best-effort */ }\n }\n send({\n type: 'relay.publish.result',\n id,\n ok: true,\n event,\n eventId: event.id,\n } as NappletMessage);\n } catch (error) {\n send({\n type: 'relay.publish.result',\n id,\n ok: false,\n error: error instanceof Error && error.message ? error.message : 'publish failed',\n } as NappletMessage);\n }\n })();\n}\n\n/**\n * Create a coordinated relay service that combines relay pool and cache\n * into a single ServiceHandler with dedup and unified EOSE.\n *\n * On relay.subscribe: queries cache first, then subscribes to relay pool.\n * Events are deduplicated by ID. EOSE is sent after both sources complete.\n * On relay.publish: publishes to relay pool and stores in cache.\n * On relay.close: cancels relay pool subscription.\n *\n * @param options - Relay pool and cache implementations to coordinate\n * @returns A ServiceHandler ready for runtime.registerService('relay', handler)\n *\n * @example\n * ```ts\n * import { createCoordinatedRelay } from '@kehto/services';\n *\n * const relay = createCoordinatedRelay({ relayPool: myPool, cache: myCache });\n * runtime.registerService('relay', relay);\n * ```\n */\nexport function createCoordinatedRelay(options: CoordinatedRelayOptions): ServiceHandler {\n const timeoutMs = options.eoseTimeoutMs ?? DEFAULT_EOSE_TIMEOUT_MS;\n const subs = new Map<string, TrackedSub>();\n\n function maybeSendEose(subKey: string, subId: string, send: (msg: NappletMessage) => void): void {\n const sub = subs.get(subKey);\n if (!sub || sub.eoseSent) return;\n if (sub.cacheEose && sub.relayEose) {\n sub.eoseSent = true;\n clearTimeout(sub.eoseTimer);\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n }\n\n return {\n descriptor: {\n name: 'relay',\n version: '1.0.0',\n description: 'Coordinated relay pool + cache with dedup and unified EOSE',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n const subKey = `${windowId}:${subId}`;\n\n // Cancel existing subscription for this key\n const existing = subs.get(subKey);\n if (existing) {\n existing.relayHandle?.unsubscribe();\n clearTimeout(existing.eoseTimer);\n subs.delete(subKey);\n }\n\n const cacheAvailable = options.cache.isAvailable();\n const relayAvailable = options.relayPool.isAvailable();\n\n // Neither source available — send EOSE immediately\n if (!cacheAvailable && !relayAvailable) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n const tracked: TrackedSub = {\n seenIds: new Set(),\n cacheEose: !cacheAvailable, // mark done if cache not available\n relayEose: !relayAvailable, // mark done if relay not available\n eoseSent: false,\n eoseTimer: null as unknown,\n relayHandle: null,\n };\n subs.set(subKey, tracked);\n\n function deliver(event: NostrEvent, relayHints?: string[]): void {\n if (tracked.seenIds.has(event.id)) return;\n tracked.seenIds.add(event.id);\n if (subs.has(subKey)) {\n const result = relayHints ? createRelayEventResultWithHints(event, relayHints) : createRelayEventResult(event);\n send({ type: 'relay.event', subId, result } as NappletMessage);\n }\n }\n\n // Query cache (async)\n if (cacheAvailable) {\n options.cache\n .query(filters)\n .then((events) => {\n for (const event of events) deliver(event);\n tracked.cacheEose = true;\n maybeSendEose(subKey, subId, send);\n })\n .catch(() => {\n // Cache query is best-effort\n tracked.cacheEose = true;\n maybeSendEose(subKey, subId, send);\n });\n }\n\n // Subscribe to relay pool\n if (relayAvailable) {\n tracked.eoseTimer = setTimeout(() => {\n if (!tracked.eoseSent) {\n tracked.relayEose = true;\n maybeSendEose(subKey, subId, send);\n }\n }, timeoutMs);\n\n const relayHint = typeof relayMessage.relay === 'string' && relayMessage.relay.length > 0\n ? relayMessage.relay\n : undefined;\n const relayUrls = relayHint ? [relayHint] : options.relayPool.selectRelayTier(filters);\n tracked.relayHandle = options.relayPool.subscribe(filters, (item, observedRelayUrls) => {\n if (item === 'EOSE') {\n clearTimeout(tracked.eoseTimer);\n tracked.relayEose = true;\n maybeSendEose(subKey, subId, send);\n return;\n }\n deliver(item, observedRelayUrls ?? relayUrls);\n // Store relay events in cache\n if (cacheAvailable) {\n try { options.cache.store(item); } catch { /* best-effort */ }\n }\n }, relayUrls);\n }\n return;\n }\n\n if (message.type === 'relay.close') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n const entry = subs.get(subKey);\n if (entry) {\n entry.relayHandle?.unsubscribe();\n clearTimeout(entry.eoseTimer);\n subs.delete(subKey);\n }\n return;\n }\n\n if (message.type === 'relay.publish') {\n publishCoordinatedEvent(options, relayMessage, send);\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of subs) {\n if (key.startsWith(prefix)) {\n entry.relayHandle?.unsubscribe();\n clearTimeout(entry.eoseTimer);\n subs.delete(key);\n }\n }\n },\n };\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type {\n KeysBindingsMessage,\n KeysForwardMessage,\n KeyBinding,\n} from '@napplet/nap/keys/types';\n\n/**\n * Parsed chord struct. The napplet-facing API uses strings like\n * `\"Ctrl+Shift+K\"`; the service lowers them into this shape for matching.\n */\nexport interface ChordSpec {\n ctrl: boolean;\n alt: boolean;\n shift: boolean;\n meta: boolean;\n key: string;\n}\n/** Registry entry mapping a registered actionId to its owning window + chord. */\nexport interface ActionEntry {\n chord: ChordSpec;\n chordString: string;\n windowId: string;\n}\n\nconst MODIFIER_ALIASES: Record<string, keyof Pick<ChordSpec, 'ctrl' | 'alt' | 'shift' | 'meta'>> = {\n ctrl: 'ctrl',\n control: 'ctrl',\n alt: 'alt',\n option: 'alt',\n shift: 'shift',\n meta: 'meta',\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n super: 'meta',\n};\n\nexport function parseChord(chord: string): ChordSpec {\n if (chord.length === 0) throw new Error('empty chord');\n const parts = chord.split('+');\n const out: ChordSpec = { ctrl: false, alt: false, shift: false, meta: false, key: '' };\n for (let i = 0; i < parts.length - 1; i++) {\n const tok = parts[i].trim().toLowerCase();\n if (tok.length === 0) continue;\n const slot = MODIFIER_ALIASES[tok];\n if (!slot) throw new Error(`unknown modifier: ${parts[i]}`);\n out[slot] = true;\n }\n const keyTok = parts[parts.length - 1].trim();\n if (keyTok.length === 0) throw new Error(`empty key in chord: ${chord}`);\n out.key = keyTok.length === 1 ? keyTok.toUpperCase() : keyTok;\n return out;\n}\n\n/**\n * Format a parsed chord in the canonical NAP-KEYS wire order:\n * Alt+Ctrl+Shift+Meta+Key.\n */\nexport function formatChord(spec: ChordSpec): string {\n const parts: string[] = [];\n if (spec.alt) parts.push('Alt');\n if (spec.ctrl) parts.push('Ctrl');\n if (spec.shift) parts.push('Shift');\n if (spec.meta) parts.push('Meta');\n parts.push(spec.key);\n return parts.join('+');\n}\n\nconst BROWSER_RESERVED_CTRL_KEYS = new Set(['L', 'N', 'R', 'T', 'W']);\n\n/**\n * NAP-KEYS reserved keys must never be bound or added to a napplet suppress\n * list. The browser-reserved set is intentionally conservative and covers the\n * shortcuts named by the draft plus the common tab/window variants.\n */\nexport function isReservedKeyChord(spec: ChordSpec): boolean {\n if (spec.key === 'Tab' || spec.key === 'Escape') return true;\n if ((spec.ctrl || spec.meta) && !spec.alt && BROWSER_RESERVED_CTRL_KEYS.has(spec.key)) return true;\n if (spec.alt && !spec.ctrl && !spec.meta && (spec.key === 'ArrowLeft' || spec.key === 'ArrowRight')) {\n return true;\n }\n return false;\n}\n\nexport function chordSpecKey(spec: {\n ctrl: boolean;\n alt: boolean;\n shift: boolean;\n meta: boolean;\n key: string;\n}): string {\n return `${spec.ctrl}|${spec.alt}|${spec.shift}|${spec.meta}|${spec.key}`;\n}\n\nexport function forwardPayload(m: KeysForwardMessage): {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n} {\n return {\n key: m.key,\n code: m.code,\n ctrlKey: m.ctrl,\n altKey: m.alt,\n shiftKey: m.shift,\n metaKey: m.meta,\n };\n}\n\nexport function eventKey(ev: KeyboardEvent): string {\n const k = ev.key.length === 1 ? ev.key.toUpperCase() : ev.key;\n return `${ev.ctrlKey}|${ev.altKey}|${ev.shiftKey}|${ev.metaKey}|${k}`;\n}\n\nexport function bindingsForWindow(\n windowId: string,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n): KeyBinding[] {\n const actionIds = windowIndex.get(windowId);\n if (!actionIds) return [];\n const bindings: KeyBinding[] = [];\n for (const actionId of actionIds) {\n const entry = registry.get(actionId);\n if (!entry || entry.windowId !== windowId) continue;\n bindings.push({ actionId, key: entry.chordString });\n }\n return bindings;\n}\n\nexport function pushBindings(\n windowId: string,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n send: (msg: NappletMessage) => void,\n): void {\n const payload: KeysBindingsMessage = {\n type: 'keys.bindings',\n bindings: bindingsForWindow(windowId, registry, windowIndex),\n };\n send(payload as NappletMessage);\n}\n\nexport function removeActionFromWindowIndex(\n actionId: string,\n windowIndex: Map<string, Set<string>>,\n): void {\n for (const [wid, set] of windowIndex.entries()) {\n if (set.delete(actionId) && set.size === 0) windowIndex.delete(wid);\n }\n}\n","/**\n * keys-service.ts — NIP-5D keys NAP reference document-level listener implementation.\n *\n * Handles the 3 napplet -> shell request types from @napplet/nap/keys:\n * - keys.forward -> invokes options.onForward (hotkey passthrough, fire-and-forget)\n * - keys.registerAction -> parses action.defaultKey into a normalized chord binding,\n * stores bound subscriptions in an in-memory registry keyed\n * by actionId, tracks windowId ownership so onWindowDestroyed\n * can auto-unsubscribe, echoes { actionId, binding } as .result\n * when a binding is assigned, and pushes the window's complete\n * keys.bindings list\n * - keys.unregisterAction -> removes the subscription and pushes the window's\n * updated keys.bindings list\n *\n * Real listener: on service construction the handler attaches a single\n * `keydown` listener to `options.listenerTarget` (default: `document`). Each\n * keydown is matched against the chord-subscription registry; matches invoke\n * `options.onForward` with the DOM-shape payload and push a canonical\n * `keys.action` envelope back to the owning napplet via the per-window `send`\n * callback captured at `keys.registerAction` time. Subscriptions persist\n * across messages; `onWindowDestroyed(windowId)` drops all subscriptions owned\n * by the destroyed window as well as its cached `send` handle.\n *\n * On each document keydown matching a registered action, the service emits a\n * `keys.action` envelope to the action's owning napplet via the per-window\n * `send` callback — this is the canonical @napplet/nap/keys surface the SDK's\n * `keys.onAction(...)` helper consumes. Forwarded keydowns do not trigger\n * `keys.action`; the active napplet suppresses bound keys locally from the\n * `keys.bindings` list before forwarding.\n *\n * Field-name translation: @napplet/nap/keys uses the compact\n * { ctrl, alt, shift, meta } form on the wire; the shell's HotkeyHooks\n * (packages/shell/src/types.ts) expects the DOM-compatible\n * { ctrlKey, altKey, shiftKey, metaKey } form. This service performs the\n * translation so callers of `onForward` see the DOM shape.\n *\n * Shell -> napplet push envelopes `keys.bindings` are emitted here whenever\n * the registered bindings for a window change. The injected shim consumes\n * that complete list to suppress locally-bound keydowns before forwarding.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n KeysForwardMessage,\n KeysRegisterActionMessage,\n KeysRegisterActionResultMessage,\n KeysActionMessage,\n} from '@napplet/nap/keys/types';\nimport {\n chordSpecKey,\n eventKey,\n formatChord,\n forwardPayload,\n isReservedKeyChord,\n parseChord,\n pushBindings,\n removeActionFromWindowIndex,\n type ActionEntry,\n type ChordSpec,\n} from './keys-service-internals.js';\n\n/**\n * Minimal structural subset of the DOM `KeyboardEvent` exposed to\n * `HostKeysBridge` subscribe callbacks. DOM `KeyboardEvent` satisfies this\n * structurally with no adapter needed. OS-bridge impls (Electron, Tauri —\n * out of v1.4 scope) synthesize this from native key events.\n */\nexport interface HostKeyEvent {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n /** True for OS autorepeat; the service filters these by default. */\n repeat?: boolean;\n}\n\n/**\n * Host-bridge contract for pluggable keyboard backends.\n *\n * The browser reference implementation (the default {@link createKeysService}\n * behaviour when `hostBridge` is omitted) registers a `document`-level keydown\n * listener and satisfies this interface structurally — it exposes\n * `subscribe(chord, callback) => unsubscribe` semantics but omits the two\n * OS-level optional fields (browsers cannot register global hotkeys without\n * privileged APIs).\n *\n * Host apps (Electron, Tauri) implement this interface in their own code and\n * pass it via `createKeysService({ hostBridge: myBridge })` — the service\n * then delegates subscription lifecycle to the bridge and remains browser-free.\n *\n * Reference implementations for Electron / Tauri are explicitly out of v1.4\n * scope and live in host-app examples / follow-up milestones (see\n * REQUIREMENTS.md \"Future Requirements\").\n *\n * @example\n * ```ts\n * // Host-app pseudocode (Electron main-process relay):\n * const electronBridge: HostKeysBridge = {\n * subscribe(chord, cb) {\n * const handle = globalShortcut.register(chord, () => cb({ key: '', code: '', ctrlKey: false, altKey: false, shiftKey: false, metaKey: false }));\n * return () => globalShortcut.unregister(chord);\n * },\n * registerGlobalHotkey: (chord) => globalShortcut.register(chord, () => {}),\n * onGlobalHotkey: (cb) => globalHotkeyBridge.on('global-hotkey', (_, chord) => cb(chord)),\n * };\n *\n * const keys = createKeysService({ hostBridge: electronBridge });\n * runtime.registerService('keys', keys);\n * ```\n */\nexport interface HostKeysBridge {\n /**\n * Subscribe a callback to a chord. Returns an unsubscribe handle.\n *\n * Implementations MUST:\n * - invoke `callback` exactly once per matching chord event (implementations\n * are responsible for any OS-autorepeat filtering)\n * - invoke `callback` synchronously during the event delivery\n * - accept the string chord format documented by @napplet/nap/keys\n * (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`)\n */\n subscribe(chord: string, callback: (event: KeyboardEvent | HostKeyEvent) => void): () => void;\n\n /**\n * Optional: register an OS-level global hotkey (works even when the host\n * window is not focused). Returns true on success, false if the chord\n * cannot be registered (e.g. already claimed by another app).\n *\n * Omitted by the browser reference implementation — browsers cannot\n * register OS-level global hotkeys without privileged APIs. Electron\n * (`globalShortcut`) and Tauri (`GlobalShortcut`) provide this.\n */\n registerGlobalHotkey?(chord: string): boolean;\n\n /**\n * Optional: subscribe to OS-level global hotkey events (regardless of\n * focus). Returns an unsubscribe handle.\n *\n * Omitted by the browser reference implementation. See\n * {@link HostKeysBridge.registerGlobalHotkey}.\n */\n onGlobalHotkey?(callback: (chord: string) => void): () => void;\n}\n\n/** Keys service version — follows semver. */\nconst KEYS_SERVICE_VERSION = '1.3.0';\n\n/**\n * Options for creating a keys service via createKeysService().\n *\n * @example\n * ```ts\n * const keys = createKeysService({\n * onForward: (event) => {\n * // event has DOM-compatible field names: ctrlKey, altKey, etc.\n * hotkeyDispatcher.dispatch(event);\n * },\n * });\n * ```\n */\nexport interface KeysServiceOptions {\n /**\n * Called on `keys.forward` (napplet-forwarded chord) and on document keydown\n * handled by the reference listener. Receives the DOM-style field names\n * (ctrlKey/altKey/shiftKey/metaKey) to match the shell's HotkeyHooks\n * contract. The service translates from the wire shape\n * ({ ctrl, alt, shift, meta }) before invoking this callback.\n */\n onForward?: (event: {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n }) => void;\n /**\n * EventTarget to attach the default keydown listener to. Defaults to the\n * global `document` when running in a DOM environment, else an isolated\n * `new EventTarget()` (SSR / Node-test safe). Passing a fresh\n * `new EventTarget()` is useful for unit tests. Mirrors the pattern used\n * by the shell and service test harnesses.\n *\n * Ignored when `hostBridge` is provided — the bridge owns subscription\n * lifecycle and no document listener is attached.\n */\n listenerTarget?: EventTarget;\n /**\n * Optional pluggable backend for chord subscription. When provided, the\n * service delegates `keys.registerAction` → `bridge.subscribe(chord, cb)`\n * and stores the returned unsubscribe handle keyed on `actionId`. The\n * default document-listener path is NOT attached when `hostBridge` is\n * provided — the bridge is authoritative. See {@link HostKeysBridge}.\n */\n hostBridge?: HostKeysBridge;\n /**\n * Optional set of shell-reserved chords. Strings in the `@napplet/nap/keys`\n * wire format (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`). Reserved chords are not\n * assigned as napplet action bindings; document keydowns that match them\n * invoke `onForward` but do not push `keys.action`.\n *\n * Normalized once at service construction via the same chord parser used\n * for `action.defaultKey` — `'Ctrl+K'` / `'Control+k'` / `'ctrl+K'` all\n * match. Static; no runtime mutation. For dynamic reservation see the\n * deferred `HostKeysBridge.reserveAbsolute(chords)` extension.\n *\n * @example\n * ```ts\n * const keys = createKeysService({\n * reservedChords: ['Ctrl+Alt+T', 'Super+Space'],\n * onForward: (event) => wm.dispatch(event),\n * });\n * ```\n */\n reservedChords?: ReadonlyArray<string>;\n}\n\ntype SendNappletMessage = (msg: NappletMessage) => void;\n\nfunction rememberActionForWindow(\n windowIndex: Map<string, Set<string>>,\n windowId: string,\n actionId: string,\n): void {\n if (!windowIndex.has(windowId)) windowIndex.set(windowId, new Set());\n windowIndex.get(windowId)!.add(actionId);\n}\n\nfunction bindActionEntry(\n registry: Map<string, ActionEntry>,\n windowIndex: Map<string, Set<string>>,\n actionId: string,\n windowId: string,\n chord: ChordSpec,\n chordString: string,\n): void {\n registry.set(actionId, { chord, chordString, windowId });\n rememberActionForWindow(windowIndex, windowId, actionId);\n}\n\nfunction bindActionAndMarkChanged(\n registry: Map<string, ActionEntry>,\n windowIndex: Map<string, Set<string>>,\n changedWindowIds: Set<string>,\n actionId: string,\n windowId: string,\n chord: ChordSpec,\n chordString: string,\n): string {\n bindActionEntry(registry, windowIndex, actionId, windowId, chord, chordString);\n changedWindowIds.add(windowId);\n return chordString;\n}\n\nfunction handleForwardMessage(\n message: NappletMessage,\n onForward?: KeysServiceOptions['onForward'],\n): void {\n onForward?.(forwardPayload(message as KeysForwardMessage));\n}\n\ntype KeysUnregisterActionMessage = NappletMessage & { actionId?: string };\ntype RegisterActionHandler = (\n windowId: string,\n message: KeysRegisterActionMessage,\n send: SendNappletMessage,\n) => void;\ntype UnregisterActionHandler = (\n windowId: string,\n message: KeysUnregisterActionMessage,\n send: SendNappletMessage,\n) => void;\n\nfunction sendUnknownKeysMethod(send: SendNappletMessage, message: NappletMessage): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown keys method: ${message.type}`,\n } as NappletMessage);\n}\n\nfunction createKeysMessageHandler(options: {\n onForward?: KeysServiceOptions['onForward'];\n onRegisterAction: RegisterActionHandler;\n onUnregisterAction: UnregisterActionHandler;\n}): (windowId: string, message: NappletMessage, send: SendNappletMessage) => void {\n return (windowId, message, send): void => {\n switch (message.type) {\n case 'keys.forward':\n handleForwardMessage(message, options.onForward);\n return;\n case 'keys.registerAction':\n options.onRegisterAction(windowId, message as KeysRegisterActionMessage, send);\n return;\n case 'keys.unregisterAction':\n options.onUnregisterAction(windowId, message as KeysUnregisterActionMessage, send);\n return;\n default:\n sendUnknownKeysMethod(send, message);\n }\n };\n}\n\nfunction sendRegisterActionResult(\n send: SendNappletMessage,\n message: KeysRegisterActionMessage,\n binding?: string,\n error?: string,\n): void {\n const result: KeysRegisterActionResultMessage & { error?: string } = {\n type: 'keys.registerAction.result',\n id: message.id,\n actionId: message.action.id,\n ...(binding ? { binding } : {}),\n ...(error ? { error } : {}),\n };\n send(result as NappletMessage);\n}\n\nfunction pushChangedBindings(\n changedWindowIds: Iterable<string>,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n sendHandles: Map<string, SendNappletMessage>,\n fallbackSend: SendNappletMessage,\n): void {\n for (const changedWindowId of changedWindowIds) {\n const ownerSend = sendHandles.get(changedWindowId) ?? fallbackSend;\n pushBindings(changedWindowId, registry, windowIndex, ownerSend);\n if (!windowIndex.has(changedWindowId)) sendHandles.delete(changedWindowId);\n }\n}\n\n/**\n * Create a keys service handler.\n *\n * Attaches a single `keydown` listener to `options.listenerTarget`\n * (default `document`). Matching chord subscriptions invoke `options.onForward`\n * with a DOM-shape payload and push a `keys.action` envelope back to the\n * owning napplet via the per-window `send` callback captured at\n * `keys.registerAction` time. Returns a `ServiceHandler` augmented with a\n * `destroy()` method that detaches the listener and clears all registries.\n *\n * @param options - Optional configuration (onForward callback, listenerTarget)\n * @returns A ServiceHandler (with `destroy()`) to register with the runtime\n *\n * @example\n * ```ts\n * import { createKeysService } from '@kehto/services';\n *\n * const keys = createKeysService({\n * onForward: (event) => shellHotkeyDispatcher.execute(event),\n * });\n *\n * runtime.registerService('keys', keys);\n * // Later, on shell teardown:\n * keys.destroy();\n * ```\n */\nexport function createKeysService(\n options: KeysServiceOptions = {},\n): ServiceHandler & { destroy(): void } {\n const descriptor: ServiceDescriptor = {\n name: 'keys',\n version: KEYS_SERVICE_VERSION,\n description: options.hostBridge\n ? 'NIP-5D keys NAP reference handler (host-bridge delegated)'\n : 'NIP-5D keys NAP reference handler (document-level chord listener)',\n };\n\n const reservedChordKeys: Set<string> = new Set();\n if (options.reservedChords) {\n for (const chordStr of options.reservedChords) {\n // parseChord throws on malformed input — let it bubble up at construction\n // so misconfigured shells fail loudly at boot, not silently at runtime.\n reservedChordKeys.add(chordSpecKey(parseChord(chordStr)));\n }\n }\n\n const isUnavailableBinding = (chord: ChordSpec): boolean =>\n reservedChordKeys.has(chordSpecKey(chord)) || isReservedKeyChord(chord);\n\n if (options.hostBridge) {\n const bridge = options.hostBridge;\n // windowId → Set<actionId> — parallels Branch B for scoped cleanup.\n const bridgeWindowActions = new Map<string, Set<string>>();\n const bridgeActionRegistry = new Map<string, ActionEntry>();\n const bridgeSendHandles = new Map<string, (msg: NappletMessage) => void>();\n // actionId → unsubscribe handle returned from bridge.subscribe.\n const unsubscribeHandles = new Map<string, () => void>();\n\n const handleBridgeRegisterAction: RegisterActionHandler = (windowId, m, send) => {\n bridgeSendHandles.set(windowId, send);\n let binding: string | undefined;\n const changedWindowIds = new Set<string>();\n\n if (m.action.defaultKey) {\n try {\n const chord = parseChord(m.action.defaultKey);\n const normalizedChord = formatChord(chord);\n const existingEntry = bridgeActionRegistry.get(m.action.id);\n const existing = unsubscribeHandles.get(m.action.id);\n let nextUnsubscribe: (() => void) | undefined;\n\n if (!isUnavailableBinding(chord)) {\n nextUnsubscribe = bridge.subscribe(normalizedChord, (ev) => {\n // Normalize either KeyboardEvent or HostKeyEvent to the DOM-shape onForward payload.\n const e = ev as KeyboardEvent | HostKeyEvent;\n // Bridges may not filter autorepeat — we do, matching Branch B semantics.\n if ('repeat' in e && e.repeat) return;\n options.onForward?.({\n key: e.key,\n code: e.code,\n ctrlKey: e.ctrlKey,\n altKey: e.altKey,\n shiftKey: e.shiftKey,\n metaKey: e.metaKey,\n });\n const payload: KeysActionMessage = {\n type: 'keys.action',\n actionId: m.action.id,\n };\n send(payload as NappletMessage);\n });\n }\n\n if (existing) {\n try {\n existing();\n } catch {\n /* best-effort */\n }\n unsubscribeHandles.delete(m.action.id);\n }\n if (existingEntry) changedWindowIds.add(existingEntry.windowId);\n removeActionFromWindowIndex(m.action.id, bridgeWindowActions);\n bridgeActionRegistry.delete(m.action.id);\n\n if (nextUnsubscribe) {\n unsubscribeHandles.set(m.action.id, nextUnsubscribe);\n binding = bindActionAndMarkChanged(\n bridgeActionRegistry,\n bridgeWindowActions,\n changedWindowIds,\n m.action.id,\n windowId,\n chord,\n normalizedChord,\n );\n }\n } catch (err) {\n sendRegisterActionResult(\n send,\n m,\n undefined,\n `bridge subscribe failed: ${(err as Error).message}`,\n );\n return;\n }\n }\n sendRegisterActionResult(send, m, binding);\n pushChangedBindings(\n changedWindowIds,\n bridgeActionRegistry,\n bridgeWindowActions,\n bridgeSendHandles,\n send,\n );\n };\n\n const handleBridgeUnregisterAction: UnregisterActionHandler = (_windowId, m, send) => {\n if (!m.actionId) return;\n const unsubscribe = unsubscribeHandles.get(m.actionId);\n const entry = bridgeActionRegistry.get(m.actionId);\n if (!unsubscribe) return;\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n unsubscribeHandles.delete(m.actionId);\n bridgeActionRegistry.delete(m.actionId);\n removeActionFromWindowIndex(m.actionId, bridgeWindowActions);\n if (entry) {\n const ownerSend = bridgeSendHandles.get(entry.windowId) ?? send;\n pushBindings(entry.windowId, bridgeActionRegistry, bridgeWindowActions, ownerSend);\n if (!bridgeWindowActions.has(entry.windowId)) {\n bridgeSendHandles.delete(entry.windowId);\n }\n }\n };\n\n return {\n descriptor,\n\n handleMessage: createKeysMessageHandler({\n onForward: options.onForward,\n onRegisterAction: handleBridgeRegisterAction,\n onUnregisterAction: handleBridgeUnregisterAction,\n }),\n\n onWindowDestroyed(windowId: string): void {\n const actions = bridgeWindowActions.get(windowId);\n if (!actions) return;\n for (const actionId of actions) {\n const unsubscribe = unsubscribeHandles.get(actionId);\n if (unsubscribe) {\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n }\n unsubscribeHandles.delete(actionId);\n bridgeActionRegistry.delete(actionId);\n }\n bridgeWindowActions.delete(windowId);\n bridgeSendHandles.delete(windowId);\n },\n\n destroy(): void {\n for (const unsubscribe of unsubscribeHandles.values()) {\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n }\n unsubscribeHandles.clear();\n bridgeActionRegistry.clear();\n bridgeWindowActions.clear();\n bridgeSendHandles.clear();\n },\n };\n }\n\n const actionRegistry = new Map<string, ActionEntry>(); // actionId → {chord, chordString, windowId}\n const windowActions = new Map<string, Set<string>>(); // windowId → Set<actionId>\n // Per-window `send` callback captured at registerAction time. Used to push\n // keys.action envelopes back to the owning napplet on chord match — this is\n // the canonical @napplet/nap/keys surface the SDK's `keys.onAction(...)`\n // helper consumes.\n const sendHandles = new Map<string, (msg: NappletMessage) => void>(); // windowId → send\n\n // ─── Listener target (SSR / test-safe fallback) ─\n const target: EventTarget =\n options.listenerTarget ??\n (typeof document !== 'undefined' ? document : new EventTarget());\n\n function chordMatches(spec: ChordSpec, ev: KeyboardEvent): boolean {\n if (spec.ctrl !== ev.ctrlKey) return false;\n if (spec.alt !== ev.altKey) return false;\n if (spec.shift !== ev.shiftKey) return false;\n if (spec.meta !== ev.metaKey) return false;\n const evKey = ev.key.length === 1 ? ev.key.toUpperCase() : ev.key;\n return spec.key === evKey;\n }\n\n const listener = (rawEv: Event): void => {\n const ev = rawEv as KeyboardEvent;\n if (ev.repeat) return; // ignore OS autorepeat — matches \"I pressed it once\" intent (CONTEXT Area 1)\n\n // Reserved chords fire onForward but suppress keys.action fan-out to napplets.\n const isReserved = reservedChordKeys.has(eventKey(ev));\n\n // Determine if any registered action would match — needed to decide\n // whether to fire onForward on a non-reserved keydown (legacy parity).\n // A reserved chord fires onForward regardless of napplet registration\n // (WM-launcher case: shell declares chord, no napplet registers it).\n let anyMatch = false;\n for (const entry of actionRegistry.values()) {\n if (chordMatches(entry.chord, ev)) {\n anyMatch = true;\n break;\n }\n }\n\n if (isReserved || anyMatch) {\n options.onForward?.({\n key: ev.key,\n code: ev.code,\n ctrlKey: ev.ctrlKey,\n altKey: ev.altKey,\n shiftKey: ev.shiftKey,\n metaKey: ev.metaKey,\n });\n }\n\n if (isReserved) return; // reserved → no napplet fan-out\n\n // Canonical shell→napplet push: emit keys.action to the owning napplet via\n // its captured send callback. The SDK's keys.onAction helper subscribes to\n // this envelope.\n for (const [actionId, entry] of actionRegistry.entries()) {\n if (chordMatches(entry.chord, ev)) {\n const send = sendHandles.get(entry.windowId);\n if (send) {\n const payload: KeysActionMessage = {\n type: 'keys.action',\n actionId,\n };\n send(payload as NappletMessage);\n }\n // Intentionally no `break` — two actions subscribing to the same chord\n // both receive the event. Conflict resolution is an explicit v1.5+\n // concern per CONTEXT.md Deferred Ideas.\n }\n }\n };\n\n target.addEventListener('keydown', listener);\n\n const handleDocumentRegisterAction: RegisterActionHandler = (windowId, m, send) => {\n // Capture (or refresh) the per-window send callback. The runtime's service\n // handler contract keeps `send` valid until onWindowDestroyed(windowId).\n sendHandles.set(windowId, send);\n let binding: string | undefined;\n const changedWindowIds = new Set<string>();\n\n if (m.action.defaultKey) {\n try {\n const chord = parseChord(m.action.defaultKey);\n const normalizedChord = formatChord(chord);\n const existing = actionRegistry.get(m.action.id);\n if (existing) {\n changedWindowIds.add(existing.windowId);\n removeActionFromWindowIndex(m.action.id, windowActions);\n actionRegistry.delete(m.action.id);\n }\n if (!isUnavailableBinding(chord)) {\n binding = bindActionAndMarkChanged(actionRegistry, windowActions, changedWindowIds, m.action.id, windowId, chord, normalizedChord);\n }\n } catch (err) {\n sendRegisterActionResult(\n send,\n m,\n undefined,\n `invalid chord: ${(err as Error).message}`,\n );\n return;\n }\n }\n sendRegisterActionResult(send, m, binding);\n pushChangedBindings(changedWindowIds, actionRegistry, windowActions, sendHandles, send);\n };\n\n const handleDocumentUnregisterAction: UnregisterActionHandler = (_windowId, m, send) => {\n if (!m.actionId || !actionRegistry.has(m.actionId)) return;\n const entry = actionRegistry.get(m.actionId)!;\n actionRegistry.delete(m.actionId);\n const set = windowActions.get(entry.windowId);\n if (set) {\n set.delete(m.actionId);\n // If the window has no remaining actions, drop its cached send that no\n // longer subscribes to anything.\n if (set.size === 0) windowActions.delete(entry.windowId);\n }\n const ownerSend = sendHandles.get(entry.windowId) ?? send;\n pushBindings(entry.windowId, actionRegistry, windowActions, ownerSend);\n if (!windowActions.has(entry.windowId)) sendHandles.delete(entry.windowId);\n };\n\n return {\n descriptor,\n\n handleMessage: createKeysMessageHandler({\n onForward: options.onForward,\n onRegisterAction: handleDocumentRegisterAction,\n onUnregisterAction: handleDocumentUnregisterAction,\n }),\n\n onWindowDestroyed(windowId: string): void {\n const actions = windowActions.get(windowId);\n if (actions) {\n for (const actionId of actions) actionRegistry.delete(actionId);\n windowActions.delete(windowId);\n }\n sendHandles.delete(windowId);\n },\n\n destroy(): void {\n target.removeEventListener('keydown', listener);\n actionRegistry.clear();\n windowActions.clear();\n sendHandles.clear();\n },\n };\n}\n","import type { MediaAction, MediaMetadata } from '@napplet/nap/media/types';\nimport type { HostMediaBridge } from './media-service.js';\n\n/** Silent-audio prime data URL (4 kHz silent WAV, 44 bytes, zero network dependency).\n * Browsers refuse to render OS media controls without a playing audio element —\n * this silent loop primes the MediaSession API. Per CONTEXT.md Area 1. */\nconst SILENT_AUDIO_DATA_URL =\n 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=';\n\n/** Default action set — all 5 nap-media transport actions. */\nexport const DEFAULT_MEDIA_ACTIONS: readonly MediaAction[] = ['play', 'pause', 'next', 'prev', 'seek'];\n\n/** Minimal subset of navigator.mediaSession the browser bridge depends on. Makes the bridge\n * Node/test-safe: unit tests pass a MockMediaSession via mediaSessionTarget.\n * The handler parameter uses `details?` (optional) so both the real DOM impl\n * (which always passes an object) and test mocks that omit details both satisfy\n * this type structurally. */\nexport type MediaSessionTarget = {\n metadata: MediaMetadataLike | null;\n playbackState: 'none' | 'playing' | 'paused';\n setActionHandler(action: string, handler: ((details?: { action?: string; seekTime?: number }) => void) | null): void;\n};\n\n/** Structural subset of the DOM MediaMetadata class — assignable from a plain object\n * with title/artist/album/artwork fields. The browser impl uses `new MediaMetadata({...})`;\n * tests can pass a plain object. */\nexport type MediaMetadataLike = { title?: string; artist?: string; album?: string; artwork?: unknown };\n\n/** Mapping from DOM MediaSession action names to nap-media MediaAction literals. */\nconst ACTION_MATRIX: ReadonlyArray<[string, MediaAction]> = [\n ['play', 'play'],\n ['pause', 'pause'],\n ['nexttrack', 'next'],\n ['previoustrack', 'prev'],\n ['seekto', 'seek'],\n];\n\n/**\n * Reference browser implementation of {@link HostMediaBridge}.\n *\n * Mirrors metadata to `navigator.mediaSession.metadata` (via the DOM\n * `MediaMetadata` constructor when available; plain-object fallback in test\n * envs). Mirrors playback state to `navigator.mediaSession.playbackState`\n * with the canonical mapping: 'playing' maps to 'playing', 'paused' maps to\n * 'paused', 'buffering' maps to 'paused', 'stopped' maps to 'none'. Installs\n * `setActionHandler` callbacks for play/pause/nexttrack/previoustrack/seekto\n * that fan into the onAction subscriber with the mapped `MediaAction` literal\n * (and `value` from `details.seekTime` for seekto). When `setActiveSession` is\n * called with a non-null `actions` parameter, only the declared actions get\n * active handlers — the remaining are cleared (matching the capabilities\n * narrowing behavior of Plan 27-01's inline implementation).\n *\n * Installs a silent-audio prime (4 kHz silent WAV data URL) when the first\n * session becomes active (setActiveSession called with a non-null sessionId) —\n * browsers refuse to render OS media controls without a playing audio element.\n * Removes the element when destroySession brings the active session count to\n * zero.\n *\n * @param opts.mediaSessionTarget - Override navigator.mediaSession (tests).\n * @param opts.documentTarget - Override document (tests; pass null to disable silent-audio prime).\n */\nexport function createBrowserMediaBridge(opts: {\n mediaSessionTarget?: MediaSessionTarget;\n documentTarget?: Document | null;\n} = {}): HostMediaBridge {\n const ms: MediaSessionTarget | null =\n opts.mediaSessionTarget\n ?? (typeof navigator !== 'undefined' && 'mediaSession' in navigator\n ? (navigator.mediaSession as MediaSessionTarget)\n : null);\n const doc: Document | null =\n opts.documentTarget !== undefined\n ? opts.documentTarget\n : (typeof document !== 'undefined' ? document : null);\n\n let silentAudioEl: HTMLAudioElement | null = null;\n let activeSessionId: string | null = null;\n let sessionsActive = 0;\n const actionCallbacks = new Set<(sessionId: string, action: MediaAction, value?: number) => void>();\n\n function primeSilentAudio(): void {\n if (silentAudioEl || !doc) return;\n const el = doc.createElement('audio') as HTMLAudioElement;\n el.src = SILENT_AUDIO_DATA_URL;\n el.loop = true;\n el.style.display = 'none';\n (el as HTMLAudioElement).setAttribute('data-kehto-silent-audio-prime', 'true');\n doc.body.appendChild(el);\n void el.play().catch(() => { /* autoplay refused — metadata mirror still works */ });\n silentAudioEl = el;\n }\n\n function teardownSilentAudio(): void {\n if (!silentAudioEl) return;\n try { silentAudioEl.pause(); } catch { /* best-effort */ }\n try { silentAudioEl.remove(); } catch { /* best-effort */ }\n silentAudioEl = null;\n }\n\n /**\n * Install action handlers for the given set of nap-media actions. Actions not\n * in the set get their handler cleared. Matches Plan 27-01's installActionHandlersFor\n * behavior exactly so capabilities-narrowing tests continue to pass.\n */\n function applyActionHandlers(actions: readonly MediaAction[] = DEFAULT_MEDIA_ACTIONS): void {\n if (!ms) return;\n for (const [domAction, napAction] of ACTION_MATRIX) {\n if (!actions.includes(napAction)) {\n try { ms.setActionHandler(domAction, null); } catch { /* best-effort */ }\n continue;\n }\n ms.setActionHandler(domAction, (details) => {\n if (!activeSessionId) return;\n const value = napAction === 'seek' && typeof details?.seekTime === 'number' ? details.seekTime : undefined;\n for (const cb of actionCallbacks) {\n cb(activeSessionId, napAction, value);\n }\n });\n }\n }\n\n function writeMetadata(metadata: MediaMetadata | undefined): void {\n if (!ms) return;\n if (!metadata) { ms.metadata = null; return; }\n const artwork = metadata.artwork?.url ? [{ src: metadata.artwork.url }] : undefined;\n const init: MediaMetadataLike & { artwork?: unknown } = {\n title: metadata.title ?? '',\n artist: metadata.artist ?? '',\n album: metadata.album ?? '',\n ...(artwork ? { artwork } : {}),\n };\n try {\n const ctor = (globalThis as typeof globalThis & { MediaMetadata?: new (init: MediaMetadataLike) => MediaMetadataLike }).MediaMetadata;\n ms.metadata = ctor ? new ctor(init) : (init as MediaMetadataLike);\n } catch {\n ms.metadata = init as MediaMetadataLike;\n }\n }\n\n return {\n setMetadata(sessionId, metadata) {\n if (sessionId === activeSessionId) writeMetadata(metadata);\n },\n setPlaybackState(sessionId, state) {\n if (!ms || sessionId !== activeSessionId) return;\n ms.playbackState =\n state === 'playing' ? 'playing'\n : state === 'paused' || state === 'buffering' ? 'paused'\n : 'none';\n },\n onAction(callback) {\n actionCallbacks.add(callback);\n return () => { actionCallbacks.delete(callback); };\n },\n setActiveSession(sessionId, actions) {\n activeSessionId = sessionId;\n if (!sessionId) {\n if (ms) {\n ms.metadata = null;\n ms.playbackState = 'none';\n for (const [domAction] of ACTION_MATRIX) {\n try { ms.setActionHandler(domAction, null); } catch { /* best-effort */ }\n }\n }\n return;\n }\n // Prime silent-audio on first session becoming active.\n if (sessionsActive === 0) { primeSilentAudio(); sessionsActive = 1; }\n applyActionHandlers(actions ?? DEFAULT_MEDIA_ACTIONS);\n },\n destroySession(_sessionId) {\n sessionsActive = Math.max(0, sessionsActive - 1);\n if (sessionsActive === 0) teardownSilentAudio();\n },\n };\n}\n","/**\n * media-service.ts — NIP-5D media NAP reference service (navigator.mediaSession\n * reference implementation).\n *\n * Handles the napplet-owned subset of @napplet/nap/media:\n * media.session.create (result), media.session.update, media.session.destroy,\n * media.state, media.capabilities.\n *\n * HostMediaBridge contract: {@link HostMediaBridge} defines the pluggable backend\n * contract for metadata/state mirroring + action routing. The browser reference\n * implementation is {@link createBrowserMediaBridge} (mirrors to navigator.mediaSession\n * with setActionHandler matrix). When no hostBridge option is passed, createMediaService\n * internally uses createBrowserMediaBridge as the default — behavior is identical\n * to the Plan 27-01 single-path implementation.\n *\n * navigator.mediaSession mirroring: on session.create the browser bridge mirrors the\n * napplet-supplied metadata to navigator.mediaSession.metadata via new MediaMetadata()\n * and installs setActionHandler callbacks for the 5 OS transport actions\n * (play / pause / nexttrack / previoustrack / seekto). Each callback emits a canonical\n * media.command envelope to the owning napplet — that is the @napplet/nap/media\n * MediaCommandMessage shape consumed by the SDK's mediaOnCommand() helper.\n *\n * NAP-MEDIA now distinguishes napplet-owned playback from shell-owned\n * playback. This reference backend supports `owner: \"napplet\"` because it\n * mirrors a napplet's own media element to navigator.mediaSession. It rejects\n * `owner: \"shell\"` until a host bridge provides policy-checked source fetching\n * and playback.\n *\n * media.command push: when the OS user clicks a media control (hardware key, lock-screen\n * transport, OS media overlay), the bridge's onAction callback fires. The service looks\n * up the active session's owning napplet, invokes the per-window send callback captured\n * at session.create time, and delivers:\n * { type: 'media.command', sessionId, action, value? }\n * where action is the nap-media MediaAction literal (play|pause|next|prev|seek) and\n * value is the seekTime (seconds) for seek.\n *\n * Silent-audio prime: the browser bridge creates a hidden <audio> element with a\n * 4 kHz silent WAV data URL and plays it when the first session becomes active\n * (setActiveSession with a non-null sessionId). Without a playing audio element in\n * the host page, most browsers refuse to render OS media controls. The element is\n * cleaned up when the last session is destroyed (via bridge.destroySession).\n *\n * Multi-session registry with last-active-wins semantics: every session.create is\n * tracked in a Map<sessionId, SessionEntry>; any media.state report promotes that\n * session to active. The active session's metadata and playback state are reflected\n * via bridge.setMetadata / bridge.setPlaybackState. When the active session is\n * destroyed, the next-most-recently-touched session is promoted automatically.\n *\n * This is the canonical @napplet/nap/media NIP-5D path. It handles media\n * protocol envelopes directly and does not depend on convention-topic routing.\n *\n * Shell -> Napplet push types (media.command) are emitted here when\n * bridge.onAction callbacks fire — this is the canonical Phase 27 real backend.\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n MediaAction,\n MediaCapabilitiesMessage,\n MediaCommandMessage,\n MediaMetadata,\n MediaSessionDestroyMessage,\n MediaSessionUpdateMessage,\n MediaStateMessage,\n} from '@napplet/nap/media/types';\nimport {\n createBrowserMediaBridge,\n DEFAULT_MEDIA_ACTIONS,\n type MediaSessionTarget,\n} from './browser-media-bridge.js';\n\nexport { createBrowserMediaBridge } from './browser-media-bridge.js';\nexport type {\n MediaMetadataLike,\n MediaSessionTarget,\n} from './browser-media-bridge.js';\n\n/** Registry entry — maps a sessionId back to its owning window, metadata snapshot,\n * state snapshot, and declared capabilities. Internal; never on the wire. */\ninterface SessionEntry {\n sessionId: string;\n windowId: string;\n owner: MediaPlaybackOwner;\n source: MediaSourceRef | undefined;\n metadata: MediaMetadata | undefined;\n state: { status: 'playing' | 'paused' | 'stopped' | 'buffering'; position?: number; duration?: number; volume?: number } | undefined;\n actions: readonly MediaAction[]; // from media.capabilities; defaults to ['play','pause','next','prev','seek']\n /** Monotonic tick updated on every session.create / session.update / state — used for last-active-wins resolution when the current active session is destroyed. */\n lastTouched: number;\n}\n\n/** Media service version — follows semver. */\nconst MEDIA_SERVICE_VERSION = '1.1.0';\n\nexport type MediaPlaybackOwner = 'shell' | 'napplet';\n\nexport interface MediaSourceRef {\n url?: string;\n blossomHash?: string;\n nostr?: {\n eventId?: string;\n address?: string;\n relays?: string[];\n };\n mimeType?: string;\n}\n\nexport interface MediaSessionCreateOptions {\n owner: MediaPlaybackOwner;\n sessionId?: string;\n source?: MediaSourceRef;\n metadata?: MediaMetadata;\n capabilities?: MediaAction[];\n autoplay?: boolean;\n live?: boolean;\n}\n\ntype MediaSessionCreateEnvelope = NappletMessage & Partial<MediaSessionCreateOptions> & {\n type: 'media.session.create';\n id?: string;\n};\n\ntype MediaSessionCreateResultEnvelope = NappletMessage & {\n type: 'media.session.create.result';\n id: string;\n sessionId?: string;\n owner?: MediaPlaybackOwner;\n error?: string;\n};\n\n/**\n * Host-bridge contract for pluggable media backends.\n *\n * The browser reference implementation ({@link createBrowserMediaBridge}) mirrors\n * napplet-reported metadata/state to `navigator.mediaSession` and installs\n * `setActionHandler` callbacks that fan into the bridge's onAction subscribers.\n * It satisfies this interface with all 5 fields implemented (setActiveSession\n * switches the active session and optionally re-applies action-handler narrowing;\n * destroySession tears down the silent-audio prime on last-session teardown).\n *\n * Host apps (Electron, Tauri) implement this interface in their own code and\n * pass it via `createMediaService({ hostBridge: myBridge })` — the service\n * then delegates metadata/state mirroring + action routing to the bridge and\n * remains browser-free. Session-ownership bookkeeping (which windowId owns\n * which sessionId, which send callback routes media.command back to which\n * napplet) stays in the service layer — that's wire-protocol concern, not a\n * bridge concern.\n *\n * Reference implementations for Electron / Tauri are explicitly out of v1.4\n * scope and live in host-app examples / follow-up milestones (see\n * REQUIREMENTS.md \"Future Requirements\").\n *\n * @example\n * ```ts\n * // Host-app pseudocode (Electron main-process relay):\n * const electronBridge: HostMediaBridge = {\n * setMetadata(sessionId, md) { mediaBridge.sendMetadata({ sessionId, md }); },\n * setPlaybackState(sessionId, state) { mediaBridge.sendPlaybackState({ sessionId, state }); },\n * onAction(cb) {\n * const handler = (_: unknown, msg: { sessionId: string; action: MediaAction; value?: number }) =>\n * cb(msg.sessionId, msg.action, msg.value);\n * mediaBridge.onAction(handler);\n * return () => mediaBridge.offAction(handler);\n * },\n * };\n * const media = createMediaService({ hostBridge: electronBridge });\n * runtime.registerService('media', media);\n * ```\n */\nexport interface HostMediaBridge {\n /**\n * Set the metadata displayed on the OS transport surface for a session.\n * Called on session.create (with initial metadata) and on session.update\n * (with merged metadata) whenever the session is the active session.\n * Implementations MUST be idempotent.\n */\n setMetadata(sessionId: string, metadata: MediaMetadata): void;\n\n /**\n * Set the playback state for a session. Called on media.state reports\n * whenever the session is the active session. State strings match\n * nap-media MediaState.status exactly. Implementations MUST be idempotent.\n */\n setPlaybackState(sessionId: string, state: 'playing' | 'paused' | 'stopped' | 'buffering'): void;\n\n /**\n * Subscribe to OS-level action events (user clicks play/pause/seek/next/prev\n * on the transport surface). Returns an unsubscribe handle.\n *\n * The callback receives `(sessionId, action, value?)`. `sessionId` is the\n * bridge's currently-active session (the browser impl tracks this internally\n * via setActionHandler-at-fire-time; native impls track via setActiveSession).\n * `value` is populated for `action === 'seek'` (seek target in seconds) and\n * for `action === 'volume'` (0.0-1.0). The service dispatches the resulting\n * `media.command` envelope to the owning napplet of that session.\n */\n onAction(callback: (sessionId: string, action: MediaAction, value?: number) => void): () => void;\n\n /**\n * Optional: notify the bridge that the active session has changed. The\n * browser reference impl uses this to switch which session's metadata/state\n * is mirrored to the singleton navigator.mediaSession and to install (or\n * clear) action handlers for the session's declared capabilities.\n *\n * The optional `actions` parameter carries the session's declared capability\n * set so the bridge can narrow which OS transport buttons are active. When\n * omitted, the bridge applies its default set. Native OS bridges that track\n * active-session state internally may omit this field entirely.\n */\n setActiveSession?(sessionId: string | null, actions?: readonly MediaAction[]): void;\n\n /**\n * Optional: tear down per-session resources. The browser reference impl\n * uses this to remove the silent-audio prime element when the last session\n * is destroyed. Bridges that need no per-session teardown may omit this field.\n */\n destroySession?(sessionId: string): void;\n}\n\n/**\n * Optional host callbacks for the media service.\n *\n * Host shells can hook session creation and state updates to drive\n * their own UI (e.g., now-playing banner) without replacing the\n * ServiceHandler wholesale.\n *\n * @example\n * ```ts\n * const media = createMediaService({\n * onSessionCreate: (windowId, sessionId, metadata) => {\n * console.log(`[${windowId}] created session ${sessionId}`, metadata);\n * },\n * onState: (windowId, sessionId, state) => {\n * nowPlaying.update(windowId, state);\n * },\n * });\n *\n * runtime.registerService('media', media);\n * ```\n */\nexport interface MediaServiceOptions {\n /** Called when a napplet creates a session. May inspect/record the session. */\n onSessionCreate?: (windowId: string, sessionId: string, metadata?: unknown) => void;\n /** Called on media.state updates — high-frequency; keep handler work minimal. */\n onState?: (windowId: string, sessionId: string, state: unknown) => void;\n /** Called when a napplet destroys a session. */\n onSessionDestroy?: (windowId: string, sessionId: string) => void;\n /** Called when a napplet updates session metadata. */\n onSessionUpdate?: (windowId: string, sessionId: string, metadata: unknown) => void;\n /** Called when a napplet declares capabilities for a session. */\n onCapabilities?: (windowId: string, sessionId: string, actions: unknown) => void;\n\n /**\n * MediaSession target override (used by default browser bridge only).\n * Defaults to `navigator.mediaSession` when running in a browser. Pass a\n * MockMediaSession in unit tests. Ignored when `hostBridge` is provided.\n */\n mediaSessionTarget?: MediaSessionTarget;\n\n /**\n * DOM document override (used by default browser bridge only).\n * Defaults to `document` when available. Set to null to disable the silent-audio\n * prime entirely — useful in unit tests. Ignored when `hostBridge` is provided.\n */\n documentTarget?: Document | null;\n\n /**\n * Optional pluggable backend for metadata/state mirroring + OS action handling.\n * When provided, the service delegates setMetadata / setPlaybackState / onAction\n * to the bridge and skips navigator.mediaSession entirely. When omitted, the\n * service internally uses {@link createBrowserMediaBridge} as the default.\n * See {@link HostMediaBridge}.\n */\n hostBridge?: HostMediaBridge;\n}\n\ninterface MediaServiceState {\n bridge: HostMediaBridge;\n options: MediaServiceOptions;\n sessionRegistry: Map<string, SessionEntry>;\n windowSessions: Map<string, Set<string>>;\n sendHandles: Map<string, (msg: NappletMessage) => void>;\n activeSessionId: string | null;\n touchCounter: number;\n sessionCounter: number;\n}\n\nfunction createMediaServiceState(options: MediaServiceOptions, bridge: HostMediaBridge): MediaServiceState {\n return {\n bridge,\n options,\n sessionRegistry: new Map<string, SessionEntry>(),\n windowSessions: new Map<string, Set<string>>(),\n sendHandles: new Map<string, (msg: NappletMessage) => void>(),\n activeSessionId: null,\n touchCounter: 0,\n sessionCounter: 0,\n };\n}\n\nfunction setActive(state: MediaServiceState, sessionId: string | null, actions?: readonly MediaAction[]): void {\n state.activeSessionId = sessionId;\n state.bridge.setActiveSession?.(sessionId, actions);\n if (!sessionId) return;\n const entry = state.sessionRegistry.get(sessionId);\n if (!entry) return;\n if (entry.metadata) state.bridge.setMetadata(sessionId, entry.metadata);\n if (entry.state) state.bridge.setPlaybackState(sessionId, entry.state.status);\n}\n\nfunction promoteNextActiveOrClear(state: MediaServiceState): void {\n if (state.sessionRegistry.size === 0) {\n setActive(state, null);\n return;\n }\n let latest: SessionEntry | null = null;\n for (const entry of state.sessionRegistry.values()) {\n if (!latest || entry.lastTouched > latest.lastTouched) latest = entry;\n }\n setActive(state, latest ? latest.sessionId : null, latest?.actions);\n}\n\nfunction sendMediaCommand(state: MediaServiceState, sessionId: string, action: MediaAction, value?: number): void {\n const entry = state.sessionRegistry.get(sessionId);\n if (!entry) return;\n const send = state.sendHandles.get(entry.windowId);\n if (!send) return;\n const payload: MediaCommandMessage = {\n type: 'media.command',\n sessionId,\n action,\n ...(typeof value === 'number' ? { value } : {}),\n };\n send(payload as NappletMessage);\n}\n\nfunction registerWindowSession(state: MediaServiceState, windowId: string, sessionId: string): void {\n if (!state.windowSessions.has(windowId)) state.windowSessions.set(windowId, new Set());\n state.windowSessions.get(windowId)!.add(sessionId);\n}\n\nfunction sendSessionCreateResult(\n send: (msg: NappletMessage) => void,\n id: string | undefined,\n fields: Omit<MediaSessionCreateResultEnvelope, 'type' | 'id'>,\n): void {\n send({\n type: 'media.session.create.result',\n id: id ?? '',\n ...fields,\n } as NappletMessage);\n}\n\nfunction isMediaPlaybackOwner(value: unknown): value is MediaPlaybackOwner {\n return value === 'shell' || value === 'napplet';\n}\n\nfunction hasSourceRef(source: MediaSourceRef | undefined): boolean {\n if (!source) return false;\n if (typeof source.url === 'string' && source.url.length > 0) return true;\n if (typeof source.blossomHash === 'string' && source.blossomHash.length > 0) return true;\n if (source.nostr) {\n return Boolean(source.nostr.eventId || source.nostr.address);\n }\n return false;\n}\n\nfunction canonicalizeSessionId(\n state: MediaServiceState,\n windowId: string,\n preferredSessionId: string | undefined,\n): string {\n const trimmed = typeof preferredSessionId === 'string' ? preferredSessionId.trim() : '';\n const hint = trimmed || `session-${++state.sessionCounter}`;\n if (!state.sessionRegistry.has(hint)) return hint;\n\n let next: string;\n do {\n next = `${windowId}:${hint}:${++state.sessionCounter}`;\n } while (state.sessionRegistry.has(next));\n return next;\n}\n\nfunction handleSessionCreate(\n state: MediaServiceState,\n windowId: string,\n message: MediaSessionCreateEnvelope,\n send: (msg: NappletMessage) => void,\n): void {\n if (!isMediaPlaybackOwner(message.owner)) {\n sendSessionCreateResult(send, message.id, { error: 'missing owner' });\n return;\n }\n\n if (message.owner === 'shell') {\n if (!hasSourceRef(message.source)) {\n sendSessionCreateResult(send, message.id, { owner: 'shell', error: 'missing source' });\n return;\n }\n sendSessionCreateResult(send, message.id, { owner: 'shell', error: 'unsupported owner mode' });\n return;\n }\n\n state.sendHandles.set(windowId, send);\n const sessionId = canonicalizeSessionId(state, windowId, message.sessionId);\n const entry: SessionEntry = {\n sessionId,\n windowId,\n owner: message.owner,\n source: message.source,\n metadata: message.metadata,\n state: undefined,\n actions: message.capabilities ?? DEFAULT_MEDIA_ACTIONS,\n lastTouched: ++state.touchCounter,\n };\n state.sessionRegistry.set(sessionId, entry);\n registerWindowSession(state, windowId, sessionId);\n setActive(state, sessionId, entry.actions);\n state.options.onSessionCreate?.(windowId, sessionId, message.metadata);\n sendSessionCreateResult(send, message.id, { sessionId, owner: message.owner });\n}\n\nfunction handleSessionUpdate(state: MediaServiceState, windowId: string, message: MediaSessionUpdateMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry) {\n entry.metadata = { ...entry.metadata, ...message.metadata };\n entry.lastTouched = ++state.touchCounter;\n if (entry.owner === 'napplet' && message.sessionId === state.activeSessionId && entry.metadata) {\n state.bridge.setMetadata(message.sessionId, entry.metadata);\n }\n }\n state.options.onSessionUpdate?.(windowId, message.sessionId, message.metadata);\n}\n\nfunction handleSessionDestroy(state: MediaServiceState, windowId: string, message: MediaSessionDestroyMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry) {\n state.sessionRegistry.delete(message.sessionId);\n const set = state.windowSessions.get(entry.windowId);\n if (set) {\n set.delete(message.sessionId);\n if (set.size === 0) state.windowSessions.delete(entry.windowId);\n }\n state.bridge.destroySession?.(message.sessionId);\n if (message.sessionId === state.activeSessionId) promoteNextActiveOrClear(state);\n }\n state.options.onSessionDestroy?.(windowId, message.sessionId);\n}\n\nfunction handleMediaState(state: MediaServiceState, windowId: string, message: MediaStateMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry?.owner === 'napplet') {\n entry.state = {\n status: message.status,\n position: message.position,\n duration: message.duration,\n volume: message.volume,\n };\n entry.lastTouched = ++state.touchCounter;\n if (state.activeSessionId !== message.sessionId) setActive(state, message.sessionId, entry.actions);\n else state.bridge.setPlaybackState(message.sessionId, message.status);\n }\n state.options.onState?.(windowId, message.sessionId, message);\n}\n\nfunction handleMediaCapabilities(\n state: MediaServiceState,\n windowId: string,\n message: MediaCapabilitiesMessage,\n): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry?.owner === 'napplet') {\n entry.actions = message.actions;\n entry.lastTouched = ++state.touchCounter;\n if (message.sessionId === state.activeSessionId) {\n state.bridge.setActiveSession?.(message.sessionId, entry.actions);\n }\n }\n state.options.onCapabilities?.(windowId, message.sessionId, message.actions);\n}\n\nfunction handleMediaMessage(\n state: MediaServiceState,\n windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n): void {\n switch (message.type) {\n case 'media.session.create':\n handleSessionCreate(state, windowId, message as MediaSessionCreateEnvelope, send);\n return;\n case 'media.session.update':\n handleSessionUpdate(state, windowId, message as MediaSessionUpdateMessage);\n return;\n case 'media.session.destroy':\n handleSessionDestroy(state, windowId, message as MediaSessionDestroyMessage);\n return;\n case 'media.state':\n handleMediaState(state, windowId, message as MediaStateMessage);\n return;\n case 'media.capabilities':\n handleMediaCapabilities(state, windowId, message as MediaCapabilitiesMessage);\n return;\n default: {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown media method: ${message.type}`,\n } as NappletMessage);\n }\n }\n}\n\nfunction destroyWindowSessions(state: MediaServiceState, windowId: string): void {\n const sessions = state.windowSessions.get(windowId);\n if (sessions) {\n const ownedActive = state.activeSessionId !== null && sessions.has(state.activeSessionId);\n for (const sessionId of sessions) {\n state.sessionRegistry.delete(sessionId);\n state.bridge.destroySession?.(sessionId);\n }\n state.windowSessions.delete(windowId);\n if (ownedActive) promoteNextActiveOrClear(state);\n }\n state.sendHandles.delete(windowId);\n}\n\nfunction destroyMediaState(state: MediaServiceState, unsubscribeAction: () => void): void {\n unsubscribeAction();\n for (const sessionId of state.sessionRegistry.keys()) state.bridge.destroySession?.(sessionId);\n state.bridge.setActiveSession?.(null);\n state.sessionRegistry.clear();\n state.windowSessions.clear();\n state.sendHandles.clear();\n state.activeSessionId = null;\n state.touchCounter = 0;\n state.sessionCounter = 0;\n}\n\n/**\n * Create a media NAP service handler with navigator.mediaSession integration.\n *\n * Implements the 5 napplet->shell media.* request types defined in\n * `@napplet/nap/media`. Only `media.session.create` produces a reply\n * envelope (`media.session.create.result`) — the remaining four\n * (`session.update`, `session.destroy`, `state`, `capabilities`) are\n * fire-and-forget per the NAP spec.\n *\n * Unknown `media.*` actions produce a `<type>.error` envelope so\n * napplets are never left hanging on a malformed request.\n *\n * @param options - Optional host callbacks for session lifecycle + state, plus\n * mediaSessionTarget and documentTarget for test injection, and an optional\n * hostBridge for native-OS media backend delegation.\n * @returns A ServiceHandler (with `destroy()`) to register with the runtime\n *\n * @example\n * ```ts\n * import { createMediaService } from '@kehto/services';\n *\n * const media = createMediaService();\n * runtime.registerService('media', media);\n * // Later, on shell teardown:\n * media.destroy();\n * ```\n */\nexport function createMediaService(options: MediaServiceOptions = {}): ServiceHandler & { destroy(): void } {\n const descriptor: ServiceDescriptor = {\n name: 'media',\n version: MEDIA_SERVICE_VERSION,\n description: options.hostBridge\n ? 'NIP-5D media NAP reference handler (host-bridge delegated)'\n : 'NIP-5D media NAP reference handler (navigator.mediaSession mirror)',\n };\n\n const bridge: HostMediaBridge = options.hostBridge\n ?? createBrowserMediaBridge({\n mediaSessionTarget: options.mediaSessionTarget,\n documentTarget: options.documentTarget,\n });\n const state = createMediaServiceState(options, bridge);\n\n const unsubscribeAction = bridge.onAction((sessionId, action, value) => {\n sendMediaCommand(state, sessionId, action, value);\n });\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n handleMediaMessage(state, windowId, message, send);\n },\n\n onWindowDestroyed(windowId: string): void {\n destroyWindowSessions(state, windowId);\n },\n\n destroy(): void {\n destroyMediaState(state, unsubscribeAction);\n },\n };\n}\n","/**\n * NAP-NOTIFY reference service.\n *\n * The service owns protocol correlation and per-window lifecycle while a host\n * backend owns presentation, permission, badges, and channels.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n NotifyActionMessage,\n NotifyBadgeMessage,\n NotifyChannelRegisterMessage,\n NotifyClickedMessage,\n NotifyControl,\n NotifyControlsMessage,\n NotifyDismissedMessage,\n NotifyDismissMessage,\n NotifyPermissionRequestMessage,\n NotifyPermissionResultMessage,\n NotifySendMessage,\n NotifySendResultMessage,\n} from '@napplet/nap/notify/types';\n\nconst NOTIFY_SERVICE_VERSION = '1.0.0';\n\n/** User-interaction events emitted by a host notification presentation. */\nexport type NotifyInteractionMessage =\n | NotifyActionMessage\n | NotifyClickedMessage\n | NotifyDismissedMessage;\n\n/** Host presentation request for one accepted notification. */\nexport interface NotifyPresentation {\n /** Window that requested the notification. */\n readonly windowId: string;\n /** Shell-assigned notification identifier. */\n readonly notificationId: string;\n /** Original, untrusted notification payload. */\n readonly message: NotifySendMessage;\n /** Route a host-side user interaction back to the requesting napplet. */\n readonly emit: (message: NotifyInteractionMessage) => void;\n}\n\n/** Host hooks required to turn NAP-NOTIFY messages into observable behavior. */\nexport interface NotifyServiceOptions {\n /** Generate a shell-assigned notification identifier. */\n generateId?: () => string;\n /** Render or otherwise deliver a notification. Missing backends fail closed. */\n present?: (presentation: NotifyPresentation) => void | Promise<void>;\n /** Remove an active notification from host presentation. */\n dismiss?: (windowId: string, notificationId: string) => void | Promise<void>;\n /** Display or clear the requesting napplet's badge count. */\n setBadge?: (windowId: string, count: number) => void | Promise<void>;\n /** Persist a channel registration in host-owned state. */\n registerChannel?: (windowId: string, channel: NotifyChannelRegisterMessage) => void | Promise<void>;\n /** Apply policy or prompt the user for notification permission. */\n requestPermission?: (windowId: string, channel?: string) => boolean | Promise<boolean>;\n /** Release host-owned state when the runtime destroys a napplet window. */\n destroyWindow?: (windowId: string) => void | Promise<void>;\n /** Notification controls implemented by the host backend. */\n controls?: readonly NotifyControl[];\n /** Observe asynchronous fire-and-forget backend failures. */\n onError?: (error: unknown) => void;\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction forget(result: void | Promise<void>, onError?: (error: unknown) => void): void {\n void Promise.resolve(result).catch((error: unknown) => onError?.(error));\n}\n\n/**\n * Create a NAP-NOTIFY service backed by host-owned notification behavior.\n *\n * A backendless instance is safe to register for conformance testing: send\n * requests return an error and permission requests return `granted: false`\n * instead of manufacturing successful delivery.\n *\n * @param options - Host presentation, policy, and lifecycle hooks.\n * @returns Runtime service handler for the `notify` domain.\n *\n * @example\n * ```ts\n * const notify = createNotifyService({\n * present: ({ message }) => showToast(message.title, message.body),\n * requestPermission: () => promptUser(),\n * });\n * runtime.registerService('notify', notify);\n * ```\n */\nexport function createNotifyService(options: NotifyServiceOptions = {}): ServiceHandler {\n let counter = 0;\n const generateId = options.generateId ?? ((): string => `shell-${++counter}`);\n const active = new Map<string, Set<string>>();\n const controlsSent = new Set<string>();\n\n const descriptor: ServiceDescriptor = {\n name: 'notify',\n version: NOTIFY_SERVICE_VERSION,\n description: 'NAP-NOTIFY host-backed notification handler',\n };\n\n const pushControls = (windowId: string, send: (msg: NappletMessage) => void): void => {\n if (!options.controls || controlsSent.has(windowId)) return;\n controlsSent.add(windowId);\n const message: NotifyControlsMessage = { type: 'notify.controls', controls: [...options.controls] };\n send(message as NappletMessage);\n };\n\n const sendNotification = async (\n windowId: string,\n message: NotifySendMessage,\n send: (msg: NappletMessage) => void,\n ): Promise<void> => {\n if (!options.present) {\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n error: 'notification presentation unavailable',\n };\n send(result as NappletMessage);\n return;\n }\n\n const notificationId = generateId();\n const windowNotifications = active.get(windowId) ?? new Set<string>();\n windowNotifications.add(notificationId);\n active.set(windowId, windowNotifications);\n const ownsWindow = (): boolean => active.get(windowId) === windowNotifications;\n const ownsNotification = (): boolean => ownsWindow() && windowNotifications.has(notificationId);\n const emit = (interaction: NotifyInteractionMessage): void => {\n if (!ownsNotification()) return;\n if (interaction.type === 'notify.dismissed') windowNotifications.delete(notificationId);\n send(interaction as NappletMessage);\n };\n\n try {\n await options.present({ windowId, notificationId, message, emit });\n if (!ownsWindow()) {\n if (options.dismiss) forget(options.dismiss(windowId, notificationId), options.onError);\n return;\n }\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n notificationId,\n };\n send(result as NappletMessage);\n } catch (error) {\n if (!ownsWindow()) return;\n windowNotifications.delete(notificationId);\n if (windowNotifications.size === 0) active.delete(windowId);\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n error: errorText(error),\n };\n send(result as NappletMessage);\n }\n };\n\n const requestPermission = async (\n windowId: string,\n message: NotifyPermissionRequestMessage,\n send: (msg: NappletMessage) => void,\n ): Promise<void> => {\n let granted = false;\n try {\n granted = options.requestPermission\n ? await options.requestPermission(windowId, message.channel)\n : false;\n } catch (error) {\n options.onError?.(error);\n }\n const result: NotifyPermissionResultMessage = {\n type: 'notify.permission.result',\n id: message.id,\n granted,\n };\n send(result as NappletMessage);\n };\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n pushControls(windowId, send);\n switch (message.type) {\n case 'notify.send':\n void sendNotification(windowId, message as NotifySendMessage, send);\n return;\n case 'notify.dismiss': {\n const notificationId = (message as NotifyDismissMessage).notificationId;\n const windowNotifications = active.get(windowId);\n if (!windowNotifications?.delete(notificationId)) return;\n if (options.dismiss) forget(options.dismiss(windowId, notificationId), options.onError);\n return;\n }\n case 'notify.badge': {\n if (options.setBadge) {\n const count = Math.max(0, Math.floor((message as NotifyBadgeMessage).count));\n forget(options.setBadge(windowId, count), options.onError);\n }\n return;\n }\n case 'notify.channel.register':\n if (options.registerChannel) {\n forget(options.registerChannel(windowId, message as NotifyChannelRegisterMessage), options.onError);\n }\n return;\n case 'notify.permission.request':\n void requestPermission(windowId, message as NotifyPermissionRequestMessage, send);\n return;\n default: {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown notify method: ${message.type}`,\n } as NappletMessage);\n }\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const windowNotifications = active.get(windowId);\n active.delete(windowId);\n controlsSent.delete(windowId);\n if (options.dismiss && windowNotifications) {\n for (const notificationId of windowNotifications) {\n forget(options.dismiss(windowId, notificationId), options.onError);\n }\n }\n if (options.destroyWindow) forget(options.destroyWindow(windowId), options.onError);\n },\n };\n}\n","/**\n * theme-service.ts — NIP-5D theme NAP reference service.\n *\n * Handles the single napplet->shell request type from @napplet/nap/theme:\n * - `theme.get` -> `theme.get.result { theme }` (current theme)\n *\n * Exposes a host-facing `publishTheme(theme)` handle that:\n * 1. Replaces the service's internal current theme.\n * 2. Invokes `options.onBroadcast(envelope)` synchronously with a\n * `theme.changed` envelope so the shell adapter (Plan 13-02) can\n * fan-out the push to every registered napplet.\n * 3. Returns the envelope so callers can use it directly if they prefer.\n *\n * The default theme values are centralized here and mirrored in the runtime's\n * fallback path (`packages/runtime/src/runtime.ts`) — runtime does NOT import\n * from @kehto/services because services depends on runtime (one-way only).\n *\n * Host apps replace this via `runtime.registerService('theme', realHandler)`\n * when real CSS injection / storage / dark-mode logic is needed.\n *\n * @example\n * ```ts\n * import { createThemeService } from '@kehto/services';\n *\n * const theme = createThemeService({\n * onBroadcast: (envelope) => broadcastToAllNapplets(envelope),\n * });\n * runtime.registerService('theme', theme.handler);\n *\n * // Later, when the user flips dark/light mode:\n * theme.publishTheme(newTheme);\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from\n// @napplet/core v0.2.0+ (napplet phase-81). Re-exported from\n// @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n Theme,\n ThemeChangedMessage,\n ThemeGetResultMessage,\n} from '@napplet/nap/theme/types';\n\n/** Theme service version — follows semver. */\nconst THEME_SERVICE_VERSION = '1.0.0';\n\n/**\n * Canonical default theme values.\n *\n * Must stay synchronized with the runtime fallback constant at\n * `packages/runtime/src/runtime.ts` (THEME_FALLBACK_DEFAULT). The runtime\n * keeps a local copy instead of importing from @kehto/services to avoid a\n * runtime->services dependency (services depends on runtime; one-way).\n */\nconst DEFAULT_THEME: Theme = {\n colors: {\n background: '#0a0a0a',\n text: '#e0e0e0',\n primary: '#7aa2f7',\n },\n // fonts, background, title intentionally undefined — all optional per\n // @napplet/nap/theme Theme interface.\n};\n\nfunction isCompleteTheme(theme: Theme | undefined): theme is Theme {\n const colors = theme?.colors;\n return typeof colors?.background === 'string'\n && typeof colors.text === 'string'\n && typeof colors.primary === 'string';\n}\n\nfunction normalizeTheme(theme: Theme | undefined): Theme {\n return isCompleteTheme(theme) ? theme : DEFAULT_THEME;\n}\n\n/**\n * Configuration for `createThemeService`.\n *\n * @example\n * ```ts\n * const theme = createThemeService({\n * initialTheme: { colors: { background: '#fff', text: '#000', primary: '#00f' } },\n * onBroadcast: (envelope) => shellBridge.broadcastToAll(envelope),\n * });\n * ```\n */\nexport interface ThemeServiceOptions {\n /**\n * Override the default theme payload. If omitted, the service starts with\n * the canonical defaults (`#0a0a0a / #e0e0e0 / #7aa2f7`).\n */\n initialTheme?: Theme;\n\n /**\n * Called synchronously from `publishTheme(theme)` with a `theme.changed`\n * envelope. Intended for the shell adapter (Plan 13-02) to fan-out the\n * push to every registered napplet via the runtime's sendToNapplet\n * primitive.\n *\n * Keep this callback shape framework-agnostic — the service does NOT\n * import any shell / browser APIs.\n */\n onBroadcast?: (envelope: ThemeChangedMessage) => void;\n}\n\n/**\n * A theme service bundle — the ServiceHandler that handles `theme.*`\n * envelopes, the host-facing `publishTheme(theme)` handle for theme-change\n * broadcasts, and a `getCurrentTheme()` accessor for host-side reads.\n */\nexport interface ThemeService {\n /** Register this with the runtime via `runtime.registerService('theme', handler)`. */\n handler: ServiceHandler;\n\n /**\n * Publish a theme-change to the shell adapter. Updates the service's\n * internal current theme, invokes `options.onBroadcast` with a\n * `theme.changed` envelope, and returns the envelope.\n *\n * @param theme - The new theme payload\n * @returns A `theme.changed` envelope (same one passed to onBroadcast)\n */\n publishTheme: (theme: Theme) => ThemeChangedMessage;\n\n /** Return the current theme. Equivalent to the payload a napplet's `theme.get` would receive. */\n getCurrentTheme: () => Theme;\n}\n\n/**\n * Create a theme service that handles the NIP-5D `theme.*` NAP.\n *\n * Answers `theme.get` with the current theme (default or\n * `options.initialTheme`). Exposes `publishTheme(theme)` for the host app to\n * broadcast theme changes to every registered napplet — the shell adapter\n * (Plan 13-02) wires `onBroadcast` to `runtime.sendToNapplet` fan-out.\n *\n * @param options - Optional service configuration (see ThemeServiceOptions)\n * @returns A ThemeService bundle ready for `runtime.registerService('theme', service.handler)`\n *\n * @example\n * ```ts\n * import { createThemeService } from '@kehto/services';\n *\n * const theme = createThemeService();\n * runtime.registerService('theme', theme.handler);\n * theme.publishTheme({ colors: { background: '#fff', text: '#000', primary: '#00f' } });\n * ```\n */\nexport function createThemeService(options: ThemeServiceOptions = {}): ThemeService {\n let currentTheme: Theme = normalizeTheme(options.initialTheme);\n\n const descriptor: ServiceDescriptor = {\n name: 'theme',\n version: THEME_SERVICE_VERSION,\n description: 'NIP-5D theme NAP reference handler',\n };\n\n const handler: ServiceHandler = {\n descriptor,\n\n handleMessage(\n _windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n\n if (message.type === 'theme.get') {\n const result: ThemeGetResultMessage = {\n type: 'theme.get.result',\n id,\n theme: currentTheme,\n };\n send(result as NappletMessage);\n return;\n }\n\n return;\n },\n\n // Theme service has no per-window state to clean up.\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n\n function publishTheme(theme: Theme): ThemeChangedMessage {\n currentTheme = normalizeTheme(theme);\n const envelope: ThemeChangedMessage = { type: 'theme.changed', theme: currentTheme };\n options.onBroadcast?.(envelope);\n return envelope;\n }\n\n function getCurrentTheme(): Theme {\n return currentTheme;\n }\n\n return { handler, publishTheme, getCurrentTheme };\n}\n","import type {\n ConfigSchemaErrorCode,\n ConfigValues,\n NappletConfigSchema,\n} from '@napplet/nap/config/types';\n\n/** Result of validating a NAP-CONFIG Core Subset schema. */\nexport type ConfigSchemaValidation =\n | { ok: true }\n | { ok: false; code: ConfigSchemaErrorCode; error: string };\n\n/**\n * NAP-CONFIG's bounded JSON Schema vocabulary. Unknown standard keywords are\n * rejected because accepting a constraint that is not enforced would allow\n * invalid values to cross the host boundary. Unknown `x-napplet-*`\n * annotations remain opaque metadata as required by the draft.\n */\nconst CONFIG_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'array', 'object']);\nconst CONFIG_SCHEMA_KEYS = new Set([\n '$schema', '$version', 'type', 'properties', 'required', 'items',\n 'additionalProperties', 'default', 'title', 'description', 'enum',\n 'enumDescriptions', 'minimum', 'maximum', 'minLength', 'maxLength',\n 'minItems', 'maxItems', 'format', 'deprecationMessage',\n 'markdownDescription',\n]);\nconst FORBIDDEN_SCHEMA_KEYS = new Set([\n '$ref', '$defs', 'definitions', 'pattern', 'patternProperties', 'propertyNames',\n 'dependencies', 'dependentSchemas', 'unevaluatedProperties', 'oneOf', 'anyOf',\n 'allOf', 'not', 'if', 'then', 'else',\n]);\n\n/** Validate the complete bounded NAP-CONFIG Core Subset recursively. */\nexport function validateConfigSchema(schema: unknown): ConfigSchemaValidation {\n if (!isRecord(schema)) {\n return { ok: false, code: 'invalid-schema', error: 'schema root must be an object' };\n }\n if (schema.type !== 'object') {\n return { ok: false, code: 'invalid-schema', error: 'schema root must have type: \"object\"' };\n }\n if (schema.$schema !== undefined) {\n if (typeof schema.$schema !== 'string' || !isSupportedSchemaDraft(schema.$schema)) {\n return { ok: false, code: 'unsupported-draft', error: 'schema draft is not supported' };\n }\n }\n if (schema.$version !== undefined && (!Number.isSafeInteger(schema.$version) || Number(schema.$version) < 0)) {\n return { ok: false, code: 'invalid-schema', error: '$version must be a non-negative integer' };\n }\n return validateSchemaNode(schema, 0, '$');\n}\n\nfunction validateSchemaNode(schema: Record<string, unknown>, depth: number, path: string): ConfigSchemaValidation {\n if (schema.type === 'object' && depth > 4) {\n return { ok: false, code: 'schema-too-deep', error: `${path} exceeds the four-level object depth limit` };\n }\n for (const key of Object.keys(schema)) {\n if (FORBIDDEN_SCHEMA_KEYS.has(key)) {\n const code: ConfigSchemaErrorCode = key === '$ref' || key === '$defs' || key === 'definitions'\n ? 'ref-not-allowed'\n : key === 'pattern'\n ? 'pattern-not-allowed'\n : 'invalid-schema';\n return { ok: false, code, error: `${key} is not permitted in the Core Subset at ${path}` };\n }\n if (!CONFIG_SCHEMA_KEYS.has(key) && !key.startsWith('x-napplet-')) {\n return { ok: false, code: 'invalid-schema', error: `${key} is outside the NAP-CONFIG Core Subset at ${path}` };\n }\n if (path !== '$' && (key === '$schema' || key === '$version')) {\n return { ok: false, code: 'invalid-schema', error: `${key} is only valid at the schema root` };\n }\n }\n if (typeof schema.type !== 'string' || !CONFIG_TYPES.has(schema.type)) {\n return { ok: false, code: 'invalid-schema', error: `${path} has an unsupported or missing type` };\n }\n if (schema['x-napplet-secret'] === true && Object.hasOwn(schema, 'default')) {\n return { ok: false, code: 'secret-with-default', error: `${path} combines x-napplet-secret with default` };\n }\n const annotations = validateAnnotations(schema, path);\n if (!annotations.ok) return annotations;\n if (schema.type === 'object') return validateObjectSchema(schema, depth, path);\n if (schema.type === 'array') {\n if (!isRecord(schema.items)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.items must be one homogeneous schema` };\n }\n const itemType = schema.items.type;\n if (itemType === 'object' || itemType === 'array') {\n return { ok: false, code: 'invalid-schema', error: `${path} arrays may contain primitives only` };\n }\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n const itemValidation = validateSchemaNode(schema.items, depth, `${path}[]`);\n if (!itemValidation.ok) return itemValidation;\n return validateDefault(schema, path);\n }\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n return validateDefault(schema, path);\n}\n\nfunction validateObjectSchema(schema: Record<string, unknown>, depth: number, path: string): ConfigSchemaValidation {\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n if (schema.properties !== undefined && !isRecord(schema.properties)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.properties must be an object` };\n }\n if (schema.required !== undefined && (\n !Array.isArray(schema.required)\n || schema.required.some((key) => typeof key !== 'string')\n || new Set(schema.required).size !== schema.required.length\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.required must contain unique strings` };\n }\n if (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== 'boolean') {\n return { ok: false, code: 'invalid-schema', error: `${path}.additionalProperties must be boolean` };\n }\n const properties = schema.properties ?? {};\n if (Array.isArray(schema.required) && schema.required.some((key) => !Object.hasOwn(properties, key))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.required references an undeclared property` };\n }\n for (const [key, child] of Object.entries(properties)) {\n if (!isRecord(child)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be an object schema` };\n }\n const validation = validateSchemaNode(child, child.type === 'object' ? depth + 1 : depth, `${path}.${key}`);\n if (!validation.ok) return validation;\n }\n return validateDefault(schema, path);\n}\n\nfunction validateScalarConstraints(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n const numericKeys = schema.type === 'number' || schema.type === 'integer' ? ['minimum', 'maximum'] : [];\n const countKeys = schema.type === 'string'\n ? ['minLength', 'maxLength']\n : schema.type === 'array'\n ? ['minItems', 'maxItems']\n : [];\n for (const key of ['minimum', 'maximum', 'minLength', 'maxLength', 'minItems', 'maxItems']) {\n if (schema[key] !== undefined && !numericKeys.includes(key) && !countKeys.includes(key)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} does not apply to ${String(schema.type)}` };\n }\n }\n for (const key of numericKeys) {\n if (schema[key] !== undefined && (typeof schema[key] !== 'number' || !Number.isFinite(schema[key]))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a finite number` };\n }\n }\n for (const key of countKeys) {\n if (schema[key] !== undefined && (!Number.isSafeInteger(schema[key]) || Number(schema[key]) < 0)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a non-negative integer` };\n }\n }\n if (typeof schema.minimum === 'number' && typeof schema.maximum === 'number' && schema.minimum > schema.maximum) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minimum cannot exceed maximum` };\n }\n if (typeof schema.minLength === 'number' && typeof schema.maxLength === 'number' && schema.minLength > schema.maxLength) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minLength cannot exceed maxLength` };\n }\n if (typeof schema.minItems === 'number' && typeof schema.maxItems === 'number' && schema.minItems > schema.maxItems) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minItems cannot exceed maxItems` };\n }\n if (schema.enum !== undefined) {\n if (!Array.isArray(schema.enum) || schema.enum.length === 0) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum must be a non-empty array` };\n }\n if (schema.enum.some((value) => !isPrimitiveForType(value, String(schema.type)))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum contains a value outside its declared type` };\n }\n if (new Set(schema.enum.map((value) => JSON.stringify(value))).size !== schema.enum.length) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum values must be unique` };\n }\n }\n return { ok: true };\n}\n\nfunction validateAnnotations(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n for (const key of ['title', 'description', 'format', 'x-napplet-section', 'deprecationMessage', 'markdownDescription']) {\n if (schema[key] !== undefined && typeof schema[key] !== 'string') {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a string` };\n }\n }\n if (schema['x-napplet-secret'] !== undefined && typeof schema['x-napplet-secret'] !== 'boolean') {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-secret must be boolean` };\n }\n if (schema['x-napplet-secret'] === true && schema.type !== 'string') {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-secret applies only to strings` };\n }\n if (schema['x-napplet-order'] !== undefined && (\n typeof schema['x-napplet-order'] !== 'number'\n || !Number.isFinite(schema['x-napplet-order'])\n || schema['x-napplet-order'] < 0\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-order must be a non-negative number` };\n }\n if (schema.enumDescriptions !== undefined && (\n !Array.isArray(schema.enumDescriptions)\n || schema.enumDescriptions.some((value) => typeof value !== 'string')\n || !Array.isArray(schema.enum)\n || schema.enumDescriptions.length !== schema.enum.length\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enumDescriptions must parallel enum` };\n }\n return { ok: true };\n}\n\nfunction validateDefault(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n if (!Object.hasOwn(schema, 'default')) return { ok: true };\n if (!configValueConforms(schema, schema.default, path === '$')) {\n return { ok: false, code: 'invalid-schema', error: `${path}.default does not validate against its schema` };\n }\n return { ok: true };\n}\n\nexport function configValuesConform(schema: Record<string, unknown>, value: unknown): boolean {\n return configValueConforms(schema, value, true);\n}\n\nfunction configValueConforms(schema: Record<string, unknown>, value: unknown, topLevel: boolean): boolean {\n if (Array.isArray(schema.enum) && !schema.enum.some((item) => Object.is(item, value))) return false;\n if (schema.type === 'string') {\n return typeof value === 'string' && withinLength(value.length, schema.minLength, schema.maxLength);\n }\n if (schema.type === 'number') {\n return typeof value === 'number' && Number.isFinite(value) && withinLength(value, schema.minimum, schema.maximum);\n }\n if (schema.type === 'integer') {\n return Number.isSafeInteger(value) && withinLength(value as number, schema.minimum, schema.maximum);\n }\n if (schema.type === 'boolean') return typeof value === 'boolean';\n if (schema.type === 'array') {\n return Array.isArray(value)\n && isRecord(schema.items)\n && withinLength(value.length, schema.minItems, schema.maxItems)\n && value.every((item) => configValueConforms(schema.items as Record<string, unknown>, item, false));\n }\n if (schema.type === 'object') {\n if (!isRecord(value)) return false;\n const properties = isRecord(schema.properties) ? schema.properties : {};\n if (Array.isArray(schema.required) && schema.required.some((key) => typeof key === 'string' && !Object.hasOwn(value, key))) {\n return false;\n }\n return Object.entries(value).every(([key, child]) => {\n const childSchema = properties[key];\n return isRecord(childSchema)\n ? configValueConforms(childSchema, child, false)\n : allowsAdditionalProperties(schema, topLevel) && cloneJsonConfigValue(child) !== undefined;\n });\n }\n return false;\n}\n\nfunction allowsAdditionalProperties(schema: Record<string, unknown>, topLevel: boolean): boolean {\n return schema.additionalProperties === true\n || (!topLevel && schema.additionalProperties === undefined);\n}\n\nfunction isPrimitiveForType(value: unknown, type: string): boolean {\n if (type === 'string') return typeof value === 'string';\n if (type === 'number') return typeof value === 'number' && Number.isFinite(value);\n if (type === 'integer') return Number.isSafeInteger(value);\n if (type === 'boolean') return typeof value === 'boolean';\n return false;\n}\n\nfunction isSupportedSchemaDraft(value: string): boolean {\n return /^https?:\\/\\/json-schema\\.org\\/(?:draft-0?7\\/schema#?|draft\\/2019-09\\/schema#?|draft\\/2020-12\\/schema#?)$/u.test(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Apply schema defaults and drop values that do not validate. */\nexport function resolveConfigValues(schema: NappletConfigSchema, values: ConfigValues): ConfigValues {\n return resolveObjectValues(schema as Record<string, unknown>, isRecord(values) ? values : {}, {}, true) as ConfigValues;\n}\n\nfunction resolveObjectValues(\n schema: Record<string, unknown>,\n input: Record<string, unknown>,\n inheritedDefault: Record<string, unknown>,\n topLevel: boolean,\n): Record<string, unknown> {\n const output: Record<string, unknown> = {};\n const ownDefault = isRecord(schema.default) ? schema.default : {};\n const defaults = { ...inheritedDefault, ...ownDefault };\n for (const [key, childValue] of Object.entries(schema.properties ?? {})) {\n if (!isRecord(childValue)) continue;\n const candidate = Object.hasOwn(input, key) ? input[key] : defaults[key];\n const resolved = resolveConfigValue(childValue, candidate);\n if (resolved !== undefined) setConfigValue(output, key, resolved);\n }\n if (allowsAdditionalProperties(schema, topLevel)) {\n const properties = isRecord(schema.properties) ? schema.properties : {};\n for (const [key, candidate] of Object.entries({ ...defaults, ...input })) {\n if (Object.hasOwn(properties, key)) continue;\n const resolved = cloneJsonConfigValue(candidate);\n if (resolved !== undefined) setConfigValue(output, key, resolved);\n }\n }\n return output;\n}\n\nfunction resolveConfigValue(schema: Record<string, unknown>, candidate: unknown): unknown {\n const value = candidate === undefined ? schema.default : candidate;\n if (value === undefined) return undefined;\n const resolved = resolveTypedConfigValue(schema, value);\n if (resolved !== undefined) return resolved;\n return candidate !== undefined && schema.default !== undefined\n ? resolveTypedConfigValue(schema, schema.default)\n : undefined;\n}\n\nfunction resolveTypedConfigValue(schema: Record<string, unknown>, value: unknown): unknown {\n if (schema.enum && Array.isArray(schema.enum) && !schema.enum.some((item) => Object.is(item, value))) return undefined;\n switch (schema.type) {\n case 'object': {\n if (!isRecord(value)) return undefined;\n const resolved = resolveObjectValues(schema, value, {}, false);\n return Array.isArray(schema.required)\n && schema.required.some((key) => typeof key === 'string' && !Object.hasOwn(resolved, key))\n ? undefined\n : resolved;\n }\n case 'array': {\n if (!Array.isArray(value) || !isRecord(schema.items) || !withinLength(value.length, schema.minItems, schema.maxItems)) {\n return undefined;\n }\n const resolved = value.map((item) => resolveConfigValue(schema.items as Record<string, unknown>, item));\n if (resolved.some((item) => item === undefined)) return undefined;\n return resolved;\n }\n case 'string': return typeof value === 'string' && withinLength(value.length, schema.minLength, schema.maxLength) ? value : undefined;\n case 'number': return typeof value === 'number' && Number.isFinite(value) && withinLength(value, schema.minimum, schema.maximum) ? value : undefined;\n case 'integer': return Number.isSafeInteger(value) && withinLength(value as number, schema.minimum, schema.maximum) ? value : undefined;\n case 'boolean': return typeof value === 'boolean' ? value : undefined;\n default: return undefined;\n }\n}\n\nfunction cloneJsonConfigValue(value: unknown, seen = new Set<object>()): unknown {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n if (typeof value !== 'object' || seen.has(value)) return undefined;\n seen.add(value);\n try {\n if (Array.isArray(value)) {\n const output = value.map((item) => cloneJsonConfigValue(item, seen));\n return output.some((item) => item === undefined) ? undefined : output;\n }\n const entries: Array<[string, unknown]> = [];\n for (const [key, child] of Object.entries(value)) {\n const resolved = cloneJsonConfigValue(child, seen);\n if (resolved === undefined) return undefined;\n entries.push([key, resolved]);\n }\n return Object.fromEntries(entries);\n } finally {\n seen.delete(value);\n }\n}\n\nfunction setConfigValue(output: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(output, key, { value, enumerable: true, configurable: true, writable: true });\n}\n\nfunction withinLength(value: number, minimum: unknown, maximum: unknown): boolean {\n return (typeof minimum !== 'number' || value >= minimum) && (typeof maximum !== 'number' || value <= maximum);\n}\n","/**\n * config-service.ts — NAP-CONFIG reference service (9th NAP domain, v1.7 Phase 39).\n *\n * Shell-side reference implementation for the canonical NAP-CONFIG wire\n * protocol (`@napplet/nap/config`, published at `^0.3.0`). Handles the full\n * 8-message discriminated union: 5 napplet→shell request types + 3\n * shell→napplet result/push types.\n *\n * ──────────────────────────── SCOPE BOUNDARY (CONFIG-04) ─────────────────────────\n * NAP-CONFIG is **shell-managed per-napplet configuration**. Napplets observe\n * values via `config.get` (one-shot) or `config.subscribe` (snapshot + live\n * push). The shell is the **sole writer** — there is intentionally **NO**\n * `config.set` wire message. Napplets cannot mutate configuration values;\n * the shell owns persistence and the update flow.\n *\n * Do NOT use this service as a general key-value store. NAP-STORAGE\n * (`state:read` / `state:write`) remains the general KV surface. Using\n * NAP-CONFIG to store e.g. `{ lastScrollPosition: 420 }` is an anti-pattern\n * (H-07 in PITFALLS.md) — such state belongs in NAP-STORAGE.\n * ──────────────────────────────────────────────────────────────────────────────────\n *\n * Host integration: provide `getValues(windowId)` and `saveValues(windowId,\n * values)` backed by storage scoped to the source window's napplet identity.\n * The service validates and defaults every snapshot before delivery.\n *\n * Provide `openSettings` to render the shell-owned settings UI. Optional\n * hooks may add host policy around schema acceptance and lifecycle cleanup.\n *\n * @example\n * ```ts\n * import { createConfigService } from '@kehto/services';\n *\n * const configFixtures = new Map<string, ConfigValues>();\n * const config = createConfigService({\n * getValues: (windowId) => ({ ...configFixtures.get(windowId) }),\n * saveValues: (windowId, values) => configFixtures.set(windowId, values),\n * });\n * runtime.registerService('config', config.handler);\n *\n * // Later, when shell-side values change:\n * config.publishValues({ theme: 'light' }, 'window-1');\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceHandler } from '@kehto/runtime';\nimport type {\n ConfigGetMessage,\n ConfigRegisterSchemaMessage,\n ConfigOpenSettingsMessage,\n ConfigValues,\n ConfigValuesMessage,\n ConfigRegisterSchemaResultMessage,\n ConfigSchemaErrorCode,\n NappletConfigSchema,\n} from '@napplet/nap/config/types';\nimport {\n configValuesConform,\n resolveConfigValues,\n validateConfigSchema,\n type ConfigSchemaValidation,\n} from './config-schema.js';\n\nexport { resolveConfigValues, validateConfigSchema } from './config-schema.js';\nexport type { ConfigSchemaValidation } from './config-schema.js';\n\n/** Config service version — follows semver. */\nconst CONFIG_SERVICE_VERSION = '1.0.0';\n\n/**\n * Configuration options for `createConfigService` (options-as-bridge\n * per v1.6 Decision 18).\n *\n * @example\n * ```ts\n * const config = createConfigService({\n * getValues: (windowId) => loadScopedValues(windowId),\n * saveValues: (windowId, values) => saveScopedValues(windowId, values),\n * openSettings: (windowId, section, context) => showSettingsPanel(windowId, section, context),\n * });\n * ```\n */\nexport interface ConfigServiceOptions {\n /**\n * Returns the current configuration values snapshot.\n * Called on every `config.get` and at every `config.subscribe` initial push.\n * Implementations should return a fresh object (not a mutable reference).\n */\n getValues(windowId: string): ConfigValues;\n\n /** Persist one shell-validated full snapshot after settings UI commit. */\n saveValues?: (windowId: string, values: ConfigValues) => void;\n\n /**\n * Optional: receive notification when a napplet subscribes to config updates.\n * Fire-and-forget — the service tracks the subscription internally regardless.\n */\n onSubscribe?: (windowId: string) => void;\n\n /**\n * Optional: receive notification when a napplet unsubscribes.\n */\n onUnsubscribe?: (windowId: string) => void;\n\n /**\n * Optional: validate and store a napplet-provided schema.\n *\n * The reference implementation always runs its complete bounded Core Subset\n * validator first. This hook may add host policy such as version acceptance\n * or durable schema persistence, but cannot weaken protocol validation.\n *\n * Return shape mirrors `config.registerSchema.result` wire envelope\n * (minus the `id` — the dispatch layer correlates).\n */\n registerSchema?: (\n windowId: string,\n schema: NappletConfigSchema,\n version: number | undefined,\n ) => ConfigSchemaValidation;\n\n /**\n * Optional: open the shell-side settings UI for this napplet.\n * Fire-and-forget — no response envelope per the wire spec.\n * If omitted, `config.openSettings` is silently dropped. A conformant host\n * must supply this hook before advertising the CONFIG domain.\n */\n openSettings?: (\n windowId: string,\n section: string | undefined,\n context: ConfigSettingsContext,\n ) => void;\n\n /** Release host-owned transient UI or state for a destroyed window. */\n onWindowDestroyed?: (windowId: string) => void;\n}\n\n/** Shell-owned settings state and commit boundary passed to host UI code. */\nexport interface ConfigSettingsContext {\n /** Accepted schema for this live napplet window. */\n readonly schema: NappletConfigSchema;\n /** Current validated/defaulted values. */\n readonly values: ConfigValues;\n /** Validate, persist, and publish a full settings snapshot. */\n commit(values: ConfigValues): ConfigValues;\n}\n\n/**\n * NAP-CONFIG reference service bundle — `handler` to register with the\n * runtime, `publishValues` for the host app to push updates live to all\n * subscribed napplets.\n */\nexport interface ConfigService {\n /** Register this with the runtime via `runtime.registerService('config', handler)`. */\n handler: ServiceHandler;\n\n /**\n * Broadcast a new values snapshot to every napplet with an active\n * `config.subscribe`. Each subscriber receives a `config.values` envelope\n * with no `id` (push form per wire spec — absence of `id` distinguishes\n * push from correlated `config.get` response).\n *\n * @param values - The new configuration snapshot (full object, not a diff)\n * @param windowId - Optional source window scope. Omit only for a host-wide\n * update that should be independently validated for every subscriber.\n */\n publishValues(values: ConfigValues, windowId?: string): void;\n\n /** Return the accepted schema for one live window, or null. */\n getSchema(windowId: string): NappletConfigSchema | null;\n\n /** Return the current validated/defaulted values for one live window, or null. */\n getValues(windowId: string): ConfigValues | null;\n}\n\n/**\n * Create a NAP-CONFIG reference service.\n *\n * Shell-writes, napplet-reads. Handles the full `@napplet/nap/config` wire\n * protocol: `config.get` (correlated snapshot), `config.subscribe` /\n * `config.unsubscribe` (live push stream), `config.registerSchema` (optional\n * schema registration + Core Subset validation), `config.openSettings`\n * (optional UI deep-link, fire-and-forget).\n *\n * Returns a `ConfigService` bundle: `{ handler, publishValues }`.\n * Register `handler` with the runtime; call `publishValues(newValues)` from\n * the shell whenever config state changes.\n *\n * @param options - Host-supplied implementation hooks (options-as-bridge,\n * v1.6 Decision 18). `getValues` is required; all other fields are optional.\n * @returns A ConfigService bundle.\n *\n * @see ConfigServiceOptions for the options shape.\n * @see packages/services/src/theme-service.ts for the sibling pattern.\n * @see SCOPE BOUNDARY comment at the top of this file re: NAP-STORAGE separation.\n *\n * @example\n * ```ts\n * import { createConfigService } from '@kehto/services';\n *\n * const config = createConfigService({\n * getValues: (windowId) => loadScopedValues(windowId),\n * saveValues: (windowId, values) => saveScopedValues(windowId, values),\n * openSettings: (windowId, section, context) => openSettingsUI(section, context),\n * });\n * runtime.registerService('config', config.handler);\n *\n * // Push one externally-originated scoped update:\n * config.publishValues({ theme: 'light', density: 'compact' }, 'window-1');\n * ```\n */\nexport function createConfigService(options: ConfigServiceOptions): ConfigService {\n return new ConfigServiceController(options);\n}\n\ninterface RegisteredConfigSchema {\n readonly schema: NappletConfigSchema;\n readonly version?: number;\n}\n\nclass ConfigServiceController implements ConfigService {\n private readonly subscribers = new Map<string, (message: NappletMessage) => void>();\n private readonly schemas = new Map<string, RegisteredConfigSchema>();\n readonly handler: ServiceHandler;\n\n constructor(private readonly options: ConfigServiceOptions) {\n this.handler = {\n descriptor: {\n name: 'config',\n version: CONFIG_SERVICE_VERSION,\n description: 'NAP-CONFIG reference service — shell-writes, napplet-reads configuration',\n },\n handleMessage: (windowId, message, send) => this.handleMessage(windowId, message, send),\n onWindowDestroyed: (windowId) => this.destroyWindow(windowId),\n };\n }\n\n getSchema(windowId: string): NappletConfigSchema | null {\n const schema = this.schemas.get(windowId)?.schema;\n return schema ? structuredClone(schema) : null;\n }\n\n getValues(windowId: string): ConfigValues | null {\n const schema = this.schemas.get(windowId)?.schema;\n if (!schema) return null;\n const values = resolveConfigValues(schema, this.options.getValues(windowId));\n return configValuesConform(schema as Record<string, unknown>, values) ? values : null;\n }\n\n publishValues(values: ConfigValues, windowId?: string): void {\n if (windowId) {\n this.publishWindowValues(windowId, values);\n return;\n }\n for (const target of this.subscribers.keys()) this.publishWindowValues(target, values);\n }\n\n private handleMessage(\n windowId: string,\n message: NappletMessage,\n send: (message: NappletMessage) => void,\n ): void {\n switch (message.type) {\n case 'config.get':\n this.handleGet(windowId, message as ConfigGetMessage, send);\n return;\n case 'config.subscribe':\n this.handleSubscribe(windowId, send);\n return;\n case 'config.unsubscribe':\n this.subscribers.delete(windowId);\n this.options.onUnsubscribe?.(windowId);\n return;\n case 'config.registerSchema':\n this.handleRegisterSchema(windowId, message as ConfigRegisterSchemaMessage, send);\n return;\n case 'config.openSettings':\n this.handleOpenSettings(windowId, message as ConfigOpenSettingsMessage, send);\n return;\n default:\n return;\n }\n }\n\n private handleGet(\n windowId: string,\n message: ConfigGetMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const values = this.readValues(windowId, send);\n if (values === undefined) return;\n if (!values) {\n this.sendValuesUnavailable(send, windowId);\n return;\n }\n const reply: ConfigValuesMessage = {\n type: 'config.values',\n id: message.id,\n values,\n };\n send(reply as NappletMessage);\n }\n\n private handleSubscribe(\n windowId: string,\n send: (message: NappletMessage) => void,\n ): void {\n this.subscribers.set(windowId, send);\n const values = this.readValues(windowId, send);\n if (values === undefined) return;\n if (!values) {\n this.sendValuesUnavailable(send, windowId);\n return;\n }\n send({ type: 'config.values', values } as NappletMessage);\n this.options.onSubscribe?.(windowId);\n }\n\n private handleRegisterSchema(\n windowId: string,\n message: ConfigRegisterSchemaMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const nextVersion = message.version ?? readSchemaVersion(message.schema);\n let validation = this.validateRegistration(windowId, message, nextVersion);\n if (validation.ok) validation = this.persistRegisteredSchema(windowId, message.schema, nextVersion);\n const result: ConfigRegisterSchemaResultMessage = validation.ok\n ? { type: 'config.registerSchema.result', id: message.id, ok: true }\n : {\n type: 'config.registerSchema.result',\n id: message.id,\n ok: false,\n code: validation.code,\n error: validation.error,\n };\n send(result as NappletMessage);\n if (validation.ok) this.publishWindowValues(windowId);\n }\n\n private validateRegistration(\n windowId: string,\n message: ConfigRegisterSchemaMessage,\n nextVersion: number | undefined,\n ): ConfigSchemaValidation {\n let validation = validateConfigSchema(message.schema);\n if (validation.ok && message.version !== undefined && (!Number.isSafeInteger(message.version) || message.version < 0)) {\n validation = { ok: false, code: 'invalid-schema', error: 'version must be a non-negative integer' };\n }\n const priorVersion = this.schemas.get(windowId)?.version;\n if (validation.ok && priorVersion !== undefined && nextVersion !== undefined && nextVersion < priorVersion) {\n validation = { ok: false, code: 'version-conflict', error: 'schema version cannot move backwards' };\n }\n if (!validation.ok || !this.options.registerSchema) return validation;\n try {\n return this.options.registerSchema(windowId, message.schema, message.version);\n } catch (cause) {\n return {\n ok: false,\n code: 'invalid-schema',\n error: cause instanceof Error ? cause.message : 'host schema policy failed',\n };\n }\n }\n\n private persistRegisteredSchema(\n windowId: string,\n schema: NappletConfigSchema,\n version: number | undefined,\n ): ConfigSchemaValidation {\n try {\n const resolved = resolveConfigValues(schema, this.options.getValues(windowId));\n this.options.saveValues?.(windowId, resolved);\n this.schemas.set(windowId, {\n schema: structuredClone(schema),\n ...(version !== undefined ? { version } : {}),\n });\n return { ok: true };\n } catch (cause) {\n return {\n ok: false,\n code: 'invalid-schema',\n error: cause instanceof Error ? cause.message : 'host configuration persistence failed',\n };\n }\n }\n\n private handleOpenSettings(\n windowId: string,\n message: ConfigOpenSettingsMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const record = this.schemas.get(windowId);\n if (!record) {\n sendSchemaError(send, 'no-schema', 'no configuration schema is registered');\n return;\n }\n let values: ConfigValues;\n try {\n values = resolveConfigValues(record.schema, this.options.getValues(windowId));\n } catch (cause) {\n sendSchemaError(\n send,\n 'invalid-schema',\n cause instanceof Error ? cause.message : 'host configuration read failed',\n );\n return;\n }\n const section = message.section && schemaHasSection(record.schema, message.section)\n ? message.section\n : undefined;\n this.options.openSettings?.(windowId, section, {\n schema: structuredClone(record.schema),\n values,\n commit: (next) => this.commitValues(windowId, next),\n });\n }\n\n private commitValues(windowId: string, values: ConfigValues): ConfigValues {\n const schema = this.schemas.get(windowId)?.schema;\n if (!schema) throw new Error('no configuration schema is registered');\n const resolved = resolveConfigValues(schema, values);\n if (!configValuesConform(schema as Record<string, unknown>, resolved)) {\n throw new Error('required configuration values are missing or invalid');\n }\n this.options.saveValues?.(windowId, resolved);\n this.publishWindowValues(windowId, resolved);\n return resolved;\n }\n\n private publishWindowValues(windowId: string, supplied?: ConfigValues): void {\n const send = this.subscribers.get(windowId);\n const schema = this.schemas.get(windowId)?.schema;\n if (!send || !schema) return;\n const values = supplied ? resolveConfigValues(schema, supplied) : this.getValues(windowId);\n if (!values || !configValuesConform(schema as Record<string, unknown>, values)) return;\n try {\n send({ type: 'config.values', values } as NappletMessage);\n } catch {\n this.subscribers.delete(windowId);\n }\n }\n\n private readValues(\n windowId: string,\n send: (message: NappletMessage) => void,\n ): ConfigValues | null | undefined {\n try {\n return this.getValues(windowId);\n } catch (cause) {\n sendSchemaError(\n send,\n 'invalid-schema',\n cause instanceof Error ? cause.message : 'host configuration read failed',\n );\n return undefined;\n }\n }\n\n private sendValuesUnavailable(\n send: (message: NappletMessage) => void,\n windowId: string,\n ): void {\n if (!this.schemas.has(windowId)) {\n sendSchemaError(send, 'no-schema', 'no configuration schema is registered');\n return;\n }\n sendSchemaError(send, 'invalid-schema', 'configuration requires valid values before delivery');\n }\n\n private destroyWindow(windowId: string): void {\n this.subscribers.delete(windowId);\n this.schemas.delete(windowId);\n this.options.onWindowDestroyed?.(windowId);\n }\n}\nfunction sendSchemaError(\n send: (message: NappletMessage) => void,\n code: ConfigSchemaErrorCode,\n error: string,\n): void {\n send({ type: 'config.schemaError', code, error } as NappletMessage);\n}\n\nfunction readSchemaVersion(schema: NappletConfigSchema): number | undefined {\n const version = (schema as Record<string, unknown>).$version;\n return Number.isSafeInteger(version) && Number(version) >= 0 ? Number(version) : undefined;\n}\n\nfunction schemaHasSection(schema: NappletConfigSchema, section: string): boolean {\n const properties = (schema as Record<string, unknown>).properties;\n if (!isRecord(properties)) return false;\n for (const child of Object.values(properties)) {\n if (!isRecord(child)) continue;\n if (child['x-napplet-section'] === section) return true;\n if (child.type === 'object' && schemaHasSection(child as NappletConfigSchema, section)) return true;\n }\n return false;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * resource-service.ts — NAP-RESOURCE reference service (10th NAP domain, v1.7 Phase 40).\n *\n * Shell-side reference implementation for the canonical NAP-RESOURCE wire\n * protocol (`internal-resource.ts` in @kehto/shell/src/types; kehto-internal\n * model per PROJECT.md Decision #31.\n * Handles:\n * Inbound: resource.info, resource.bytes, resource.bytesMany, resource.cancel\n * Outbound: resource.info.result, resource.info.error,\n * resource.bytes.result, resource.bytes.error,\n * resource.bytesMany.result, resource.bytesMany.error\n *\n * ──────────────────────── SCOPE BOUNDARY (RESOURCE-01) ────────────────────────\n * NAP-RESOURCE is a runtime-mediated byte resolver — read-only, atomic.\n *\n * Kehto owns neutral wire lifecycle, cancellation, result projection, and\n * policy tooling. The runtime supplies the resolver and decides whether to use\n * the origin-grant hooks, which schemes to resolve, and which limits to expose.\n * Advisory `resource.info` scheme disclosure never authorizes or blocks a byte\n * request.\n * ──────────────────────────────────────────────────────────────────────────────\n *\n * Runtime integration requires only `fetch`. A runtime MAY also install the\n * optional origin-policy adapter (`isOriginGranted`, `getConnectGrants`, and\n * `resolveIdentity` together).\n *\n * @example\n * ```ts\n * import { createResourceService } from '@kehto/services';\n *\n * const resourceSvc = createResourceService({\n * fetch: (url, init) => globalThis.fetch(url, init),\n * });\n * runtime.registerService('resource', resourceSvc);\n * ```\n *\n * @example\n * ```ts\n * const resourceSvc = createResourceService({\n * fetch: (url, init) => runtimeResources.resolve(url, init),\n * isOriginGranted: (origin, grants) => grants.includes(origin),\n * getConnectGrants: (dTag, hash) => myOriginGrantStore.getOrigins(dTag, hash),\n * resolveIdentity: (windowId) => sessionRegistry.getEntryByWindowId(windowId) ?? null,\n * });\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type {\n ResourceBytesRequest,\n ResourceInfo,\n} from '@napplet/nap/resource/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nexport type {\n ResourceBytesRequest,\n ResourceInfo,\n ResourceSchemeInfo,\n} from '@napplet/nap/resource/types';\n\n/** Resource service version — follows semver. */\nconst RESOURCE_SERVICE_VERSION = '1.0.0';\n\n/** Read-only metadata passed from the resource service to a runtime resolver. */\nexport interface ResourceFetchInit {\n /** Read-only HTTP method selected by the service. */\n method?: string;\n /** Optional runtime-owned request headers. */\n headers?: Record<string, string>;\n /** Request-scoped cancellation signal. */\n signal: AbortSignal;\n /** Authenticated source window, for runtime-private scoped policy state. */\n windowId?: string;\n /** Advisory Blossom server origins for this resource only. */\n servers?: readonly string[];\n}\n\n/** Context passed to a host-provided resource info resolver. */\nexport interface ResourceInfoContext {\n windowId: string;\n identity: { dTag: string; aggregateHash: string } | null;\n}\n\n/** Static or dynamic advisory resource info exposed through `resource.info`. */\nexport type ResourceInfoProvider =\n | ResourceInfo\n | ((context: ResourceInfoContext) => ResourceInfo | Promise<ResourceInfo>);\n\n/**\n * Options for `createResourceService` (options-as-bridge per v1.6 Decision 18).\n *\n * `fetch` is the only required field. Kehto does not select a runtime's scheme\n * or origin policy. The optional origin-policy fields form one adapter and must\n * therefore be supplied together when a runtime chooses to use it.\n */\nexport interface ResourceServiceOptions {\n /**\n * Runtime-supplied resource resolver. Receives the requested URL and returns\n * a byte-classified `Response`.\n *\n * The runtime owns scheme dispatch and every NAP-RESOURCE policy decision.\n * The returned `content-type` must describe the runtime-classified bytes, not\n * an untrusted upstream header. Throw `ResourceServiceError` to preserve a\n * canonical protocol error classification.\n *\n * @param url - The URL from the resource.bytes request\n * @param init - Read-only method, request-scoped AbortSignal, and optional advisory Blossom servers\n */\n fetch(\n url: string,\n init: ResourceFetchInit,\n ): Promise<Response>;\n\n /**\n * Returns true if `origin` is present in `grants` (the list returned by\n * `getConnectGrants` for the napplet's dTag + aggregateHash).\n * Optional: when omitted with `getConnectGrants`, Kehto delegates directly\n * to the runtime resolver without an origin-grant decision.\n *\n * The reference implementation is simply `grants.includes(origin)`. Host apps\n * may provide normalized-origin comparison if needed.\n *\n * @param origin - Parsed origin of the requested URL (scheme + host + port)\n * @param grants - Readonly list from getConnectGrants for this napplet identity\n */\n isOriginGranted?(origin: string, grants: readonly string[]): boolean;\n\n /**\n * Returns the list of allowed fetch origins for the given napplet identity.\n * Called on every `resource.bytes` request — must be synchronous and fast.\n * Optional and paired with `isOriginGranted` plus `resolveIdentity`.\n *\n * Host-supplied grant source (e.g. a static per-dTag allowlist map, or any\n * other host-controlled policy). Returns an empty array to deny all origins.\n *\n * @param dTag - The napplet's d-tag (from session registry)\n * @param aggregateHash - The napplet's aggregate hash (from session registry)\n */\n getConnectGrants?(dTag: string, aggregateHash: string): readonly string[];\n\n /**\n * Resolve a windowId to the napplet's identity (dTag + aggregateHash).\n * Returns null if the window is not in the session registry.\n *\n * Typically wraps `sessionRegistry.getEntryByWindowId(windowId)`.\n *\n * @param windowId - The iframe window identifier\n */\n resolveIdentity?(windowId: string): { dTag: string; aggregateHash: string } | null;\n\n /**\n * Advisory NAP-RESOURCE introspection exposed through `resource.info`.\n *\n * Provide a static snapshot or a resolver when the shell wants to disclose\n * configured schemes and coarse limits. Scheme disclosure is advisory and is\n * never used as fetch authorization. Omit to disclose no schemes or limits.\n */\n resourceInfo?: ResourceInfoProvider;\n}\n\n/**\n * Type alias for the ServiceHandler returned by `createResourceService`.\n * Exported for host apps that need to type-annotate the handler reference.\n */\nexport type ResourceService = ServiceHandler;\n\ninterface ResourceRequestState {\n inFlight: Map<string, { controller: AbortController; windowId: string; requestId: string }>;\n perWindow: Map<string, Set<string>>;\n}\n\nexport type ResourceErrorCode =\n | 'invalid-request'\n | 'not-found'\n | 'blocked-by-policy'\n | 'timeout'\n | 'too-large'\n | 'unsupported-scheme'\n | 'decode-failed'\n | 'network-error'\n | 'quota-exceeded';\n\n/** A runtime resolver failure with a stable NAP-RESOURCE error classification. */\nexport class ResourceServiceError extends Error {\n /**\n * @param code - Canonical NAP-RESOURCE error code.\n * @param message - Diagnostic detail safe to return to the napplet.\n */\n constructor(readonly code: ResourceErrorCode, message: string) {\n super(message);\n this.name = 'ResourceServiceError';\n }\n}\n\ntype ResourceFetchSuccess = {\n ok: true;\n url: string;\n blob: Blob;\n mime: string;\n};\n\ntype ResourceFetchFailure = {\n ok: false;\n url: string;\n error: ResourceErrorCode;\n message: string;\n};\n\ntype ResourceFetchItem = ResourceFetchSuccess | ResourceFetchFailure;\n\nconst DEFAULT_RESOURCE_INFO: ResourceInfo = {\n schemes: [],\n};\n\nfunction requestKey(windowId: string, requestId: string): string {\n return `${windowId}\\u0000${requestId}`;\n}\n\nfunction assertResourceOptions(options: ResourceServiceOptions): void {\n if (typeof options?.fetch !== 'function') {\n throw new Error(\n '[RESOURCE-01] createResourceService requires a runtime-provided fetch resolver.',\n );\n }\n const hasOriginPolicy =\n options.isOriginGranted !== undefined || options.getConnectGrants !== undefined;\n if (\n hasOriginPolicy && (\n typeof options.isOriginGranted !== 'function'\n || typeof options.getConnectGrants !== 'function'\n || typeof options.resolveIdentity !== 'function'\n )\n ) {\n throw new Error(\n '[RESOURCE-01] optional origin policy requires {isOriginGranted, getConnectGrants, resolveIdentity} together.',\n );\n }\n if (options.resolveIdentity !== undefined && typeof options.resolveIdentity !== 'function') {\n throw new Error('[RESOURCE-01] resolveIdentity must be a function when provided.');\n }\n}\n\nfunction trackRequest(\n state: ResourceRequestState,\n requestId: string,\n windowId: string,\n controller: AbortController,\n): void {\n const key = requestKey(windowId, requestId);\n state.inFlight.set(key, { controller, windowId, requestId });\n if (!state.perWindow.has(windowId)) {\n state.perWindow.set(windowId, new Set());\n }\n state.perWindow.get(windowId)!.add(key);\n}\n\nfunction untrackRequest(state: ResourceRequestState, windowId: string, requestId: string): void {\n const key = requestKey(windowId, requestId);\n const entry = state.inFlight.get(key);\n if (entry) {\n state.inFlight.delete(key);\n state.perWindow.get(entry.windowId)?.delete(key);\n }\n}\n\nfunction sendResourceError(\n send: (m: NappletMessage) => void,\n requestId: string,\n message: string,\n error: ResourceErrorCode,\n type: 'resource.bytes.error' | 'resource.bytesMany.error' = 'resource.bytes.error',\n): void {\n send({\n type,\n id: requestId,\n error,\n message,\n } as NappletMessage);\n}\n\nfunction sendBytesManyError(\n send: (m: NappletMessage) => void,\n requestId: string,\n message: string,\n error: ResourceErrorCode,\n): void {\n sendResourceError(send, requestId, message, error, 'resource.bytesMany.error');\n}\n\nfunction parseResourceUrl(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\nfunction responseMime(response: Response): string {\n return response.headers.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase()\n || 'application/octet-stream';\n}\n\nfunction responseBlob(buffer: ArrayBuffer, mime: string): Blob {\n return new Blob([buffer], { type: mime });\n}\n\nfunction requestIdFromMessage(message: NappletMessage & { id?: unknown; requestId?: unknown }): string | null {\n if (typeof message.id === 'string' && message.id.length > 0) return message.id;\n if (typeof message.requestId === 'string' && message.requestId.length > 0) return message.requestId;\n return null;\n}\n\nfunction normalizeResourceInfo(info: ResourceInfo): ResourceInfo {\n return {\n schemes: Array.isArray(info.schemes)\n ? info.schemes\n .filter((scheme) => typeof scheme?.scheme === 'string')\n .map((scheme) => ({ scheme: scheme.scheme, enabled: scheme.enabled !== false }))\n : [],\n ...(typeof info.maxBytes === 'number' ? { maxBytes: info.maxBytes } : {}),\n ...(typeof info.maxUrls === 'number' ? { maxUrls: info.maxUrls } : {}),\n ...(typeof info.maxServers === 'number' ? { maxServers: info.maxServers } : {}),\n };\n}\n\nasync function resolveResourceInfo(\n options: ResourceServiceOptions,\n windowId: string,\n): Promise<ResourceInfo> {\n const configured = options.resourceInfo ?? DEFAULT_RESOURCE_INFO;\n const info = typeof configured === 'function'\n ? await configured({ windowId, identity: options.resolveIdentity?.(windowId) ?? null })\n : configured;\n return normalizeResourceInfo(info);\n}\n\nasync function handleInfo(\n options: ResourceServiceOptions,\n windowId: string,\n requestId: string,\n send: (m: NappletMessage) => void,\n): Promise<void> {\n try {\n const info = await resolveResourceInfo(options, windowId);\n send({\n type: 'resource.info.result',\n id: requestId,\n info,\n } as NappletMessage);\n } catch (err: unknown) {\n send({\n type: 'resource.info.error',\n id: requestId,\n error: 'unavailable',\n message: err instanceof Error ? err.message : String(err),\n } as NappletMessage);\n }\n}\n\nfunction resourceInvalidRequest(url: string, message: string): ResourceFetchFailure {\n return {\n ok: false,\n url,\n error: 'invalid-request',\n message,\n };\n}\n\nfunction classifyResourceFailure(error: unknown): { error: ResourceErrorCode; message: string } {\n return {\n error: error instanceof ResourceServiceError ? error.code : 'network-error',\n message: error instanceof Error ? error.message : String(error),\n };\n}\n\nasync function fetchResourceItem(\n options: ResourceServiceOptions,\n windowId: string,\n info: ResourceInfo,\n request: ResourceBytesRequest,\n signal: AbortSignal,\n): Promise<ResourceFetchItem> {\n const { url } = request;\n const parsedUrl = parseResourceUrl(url);\n if (!parsedUrl) return resourceInvalidRequest(url, `invalid URL: ${url}`);\n if (parsedUrl.origin !== 'null' && options.isOriginGranted && options.getConnectGrants) {\n const identity = options.resolveIdentity?.(windowId) ?? null;\n if (!identity) {\n return {\n ok: false,\n url,\n error: 'blocked-by-policy',\n message: 'napplet identity not resolvable for runtime origin policy',\n };\n }\n const origin = parsedUrl.origin;\n const grants = options.getConnectGrants(identity.dTag, identity.aggregateHash);\n if (!options.isOriginGranted(origin, grants)) {\n return {\n ok: false,\n url,\n error: 'blocked-by-policy',\n message: `origin ${origin} not granted by runtime policy`,\n };\n }\n }\n\n try {\n const response = await options.fetch(url, {\n method: 'GET',\n signal,\n windowId,\n ...(parsedUrl.protocol === 'blossom:' && request.servers !== undefined\n ? { servers: [...request.servers] }\n : {}),\n });\n const buffer = await response.arrayBuffer();\n if (info.maxBytes !== undefined && buffer.byteLength > info.maxBytes) {\n return { ok: false, url, error: 'too-large', message: `resource exceeds ${info.maxBytes} bytes` };\n }\n const mime = responseMime(response);\n return {\n ok: true,\n url,\n blob: responseBlob(buffer, mime),\n mime,\n };\n } catch (err: unknown) {\n const isAbort =\n signal.aborted ||\n (err instanceof Error && (err.name === 'AbortError' || err.name === 'DOMException'));\n return {\n ok: false,\n url,\n error: isAbort ? 'timeout' : err instanceof ResourceServiceError ? err.code : 'network-error',\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nasync function handleBytes(\n options: ResourceServiceOptions,\n state: ResourceRequestState,\n windowId: string,\n msg: { requestId: string } & ResourceBytesRequest,\n send: (m: NappletMessage) => void,\n): Promise<void> {\n const { requestId, ...request } = msg;\n const controller = new AbortController();\n trackRequest(state, requestId, windowId, controller);\n\n try {\n const info = await resolveResourceInfo(options, windowId);\n const item = await fetchResourceItem(options, windowId, info, request, controller.signal);\n if (controller.signal.aborted) return;\n if (!item.ok) {\n sendResourceError(send, requestId, item.message, item.error);\n return;\n }\n send({\n type: 'resource.bytes.result',\n id: requestId,\n blob: item.blob,\n mime: item.mime,\n } as NappletMessage);\n } catch (error: unknown) {\n if (!controller.signal.aborted) {\n const failure = classifyResourceFailure(error);\n sendResourceError(send, requestId, failure.message, failure.error);\n }\n } finally {\n untrackRequest(state, windowId, requestId);\n }\n}\n\nasync function handleBytesMany(\n options: ResourceServiceOptions,\n state: ResourceRequestState,\n windowId: string,\n msg: { requestId: string; requests: readonly ResourceBytesRequest[] },\n send: (m: NappletMessage) => void,\n): Promise<void> {\n const { requestId, requests } = msg;\n if (!isResourceRequestList(requests)) {\n sendBytesManyError(send, requestId, 'resource.bytesMany requires a non-empty requests array', 'invalid-request');\n return;\n }\n\n let info: ResourceInfo;\n try {\n info = await resolveResourceInfo(options, windowId);\n } catch (error: unknown) {\n const failure = classifyResourceFailure(error);\n sendBytesManyError(send, requestId, failure.message, failure.error);\n return;\n }\n if (info.maxUrls !== undefined && requests.length > info.maxUrls) {\n sendBytesManyError(send, requestId, `resource.bytesMany exceeds ${info.maxUrls} URLs`, 'too-large');\n return;\n }\n\n const controller = new AbortController();\n trackRequest(state, requestId, windowId, controller);\n try {\n const items: Array<Record<string, unknown>> = [];\n for (const request of requests) {\n const item = await fetchResourceItem(options, windowId, info, request, controller.signal);\n if (controller.signal.aborted) return;\n if (item.ok) {\n items.push({\n url: item.url,\n ok: true,\n blob: item.blob,\n mime: item.mime,\n });\n } else {\n items.push({\n url: item.url,\n ok: false,\n error: item.error,\n message: item.message,\n });\n }\n }\n send({\n type: 'resource.bytesMany.result',\n id: requestId,\n items,\n } as NappletMessage);\n } catch (error: unknown) {\n if (!controller.signal.aborted) {\n const failure = classifyResourceFailure(error);\n sendBytesManyError(send, requestId, failure.message, failure.error);\n }\n } finally {\n untrackRequest(state, windowId, requestId);\n }\n}\n\nfunction hasValidServerHints(request: { servers?: readonly unknown[] }): boolean {\n return request.servers === undefined\n || (Array.isArray(request.servers) && request.servers.every((server) => typeof server === 'string'));\n}\n\nfunction isResourceRequestList(value: unknown): value is readonly ResourceBytesRequest[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((request) => (\n typeof request === 'object'\n && request !== null\n && typeof (request as ResourceBytesRequest).url === 'string'\n && hasValidServerHints(request as ResourceBytesRequest)\n ));\n}\n\nfunction handleCancel(state: ResourceRequestState, windowId: string, requestId: string): void {\n const entry = state.inFlight.get(requestKey(windowId, requestId));\n if (entry) {\n entry.controller.abort();\n }\n}\n\nfunction destroyWindowRequests(state: ResourceRequestState, windowId: string): void {\n const requestIds = state.perWindow.get(windowId);\n if (!requestIds) return;\n for (const key of requestIds) {\n const entry = state.inFlight.get(key);\n if (entry) {\n entry.controller.abort();\n state.inFlight.delete(key);\n }\n }\n state.perWindow.delete(windowId);\n}\n\n/**\n * Create a NAP-RESOURCE reference service.\n *\n * Implements the NAP-RESOURCE request/response protocol: `resource.info`,\n * `resource.bytes`, `resource.bytesMany`, `resource.cancel`, and their\n * result/error envelopes.\n *\n * The runtime resolver is validated at construction. Runtime implementers may\n * additionally install the origin-grant adapter; Kehto provides and enforces\n * the hook contract without selecting the grants or requiring that policy.\n *\n * Returns a `ServiceHandler` (no `publishValues`-style surface — resource has\n * no shell-initiated push beyond the response/error path).\n *\n * @param options - Runtime resolver plus optional runtime-owned policy helpers\n * @returns ServiceHandler to register via `runtime.registerService('resource', handler)`\n *\n * @example\n * ```ts\n * import { createResourceService } from '@kehto/services';\n *\n * const svc = createResourceService({\n * fetch: (url, init) => runtimeResources.resolve(url, init),\n * isOriginGranted: (origin, grants) => grants.includes(origin),\n * getConnectGrants: (dTag, hash) => myOriginGrantStore.getOrigins(dTag, hash),\n * resolveIdentity: (windowId) => sessionRegistry.getEntryByWindowId(windowId) ?? null,\n * });\n * runtime.registerService('resource', svc);\n * ```\n */\nexport function createResourceService(options: ResourceServiceOptions): ResourceService {\n assertResourceOptions(options);\n const state: ResourceRequestState = {\n inFlight: new Map(),\n perWindow: new Map<string, Set<string>>(),\n };\n\n const descriptor: ServiceDescriptor = {\n name: 'resource',\n version: RESOURCE_SERVICE_VERSION,\n description:\n 'NAP-RESOURCE service — runtime-resolved byte transport (RESOURCE-01..06)',\n };\n\n const handler: ServiceHandler = {\n descriptor,\n\n handleMessage(\n windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n switch (message.type) {\n case 'resource.bytes': {\n const m = message as NappletMessage & {\n id?: string;\n requestId?: string;\n url: string;\n servers?: readonly string[];\n };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n if (typeof m.url !== 'string' || !hasValidServerHints(m)) {\n sendResourceError(send, requestId, 'resource.bytes requires a URL and optional string servers', 'invalid-request');\n return;\n }\n handleBytes(options, state, windowId, {\n requestId,\n url: m.url,\n ...(m.servers !== undefined ? { servers: [...m.servers] } : {}),\n }, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.info': {\n const m = message as NappletMessage & { id?: string; requestId?: string };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n handleInfo(options, windowId, requestId, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.bytesMany': {\n const m = message as NappletMessage & {\n id?: string;\n requestId?: string;\n requests?: readonly ResourceBytesRequest[];\n };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n handleBytesMany(options, state, windowId, { requestId, requests: m.requests ?? [] }, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.cancel': {\n const m = message as NappletMessage & { id?: string; requestId?: string };\n const requestId = requestIdFromMessage(m);\n if (requestId) handleCancel(state, windowId, requestId);\n return;\n }\n\n default:\n // Unknown resource.* message — silently ignored per NIP-5D.\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n destroyWindowRequests(state, windowId);\n },\n };\n\n return handler;\n}\n","/**\n * outbox-service.ts — NAP-OUTBOX (outbox-aware relay routing) reference service.\n *\n * Shell-side handler for the NAP-OUTBOX wire protocol. It is a pure envelope\n * router: it validates `outbox.*` envelopes, delegates the actual relay\n * discovery / routing / dedup / publish-fanout work to an injected\n * {@link OutboxRouter}, and posts the correlated result / lifecycle messages\n * (echoing the request `id` or `subId`) back to the napplet.\n *\n * The router is injected (options-as-bridge) so this service has no Nostr\n * dependency and is fully unit-testable. A concrete relay-pool-backed router\n * ships alongside as {@link createRelayPoolOutboxRouter}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: outbox.getEvent, outbox.query, outbox.subscribe, outbox.close,\n * outbox.publish, outbox.resolveRelays\n * Outbound: outbox.getEvent.result, outbox.query.result, outbox.event,\n * outbox.closed, outbox.publish.result,\n * outbox.resolveRelays.result\n *\n * The shell owns relay discovery, routing, fallback, deduplication, signature\n * validation, signing, and publish fanout policy — all of which live behind\n * the {@link OutboxRouter}. This service only marshals the wire protocol.\n *\n * @example\n * ```ts\n * import { createOutboxService, createRelayPoolOutboxRouter } from '@kehto/services';\n *\n * const router = createRelayPoolOutboxRouter({ relayPool, loadRelayLists, fallbackRelays });\n * runtime.registerService('outbox', createOutboxService({ router }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayEventResult, ServiceDescriptor, ServiceHandler, ServiceRuntimeContext } from '@kehto/runtime';\n\n/** Outbox service version — follows semver. */\nconst OUTBOX_SERVICE_VERSION = '1.0.0';\n\n/** Options for a single-event outbox lookup. */\nexport interface OutboxEventOptions {\n /** Author hint for routing through the author's outbox relays. */\n author?: string;\n /** Relay hints; treated as a hint subject to shell validation, not a bypass. */\n relays?: string[];\n /** Wall-clock budget for the lookup, in milliseconds. */\n timeoutMs?: number;\n}\n\n/** Options for a one-shot outbox query. */\nexport interface OutboxQueryOptions {\n /** Explicit author hints (augment/override authors derived from filters). */\n authors?: string[];\n /** Relay hints; treated as a hint subject to shell validation, not a bypass. */\n relays?: string[];\n /** Maximum events to collect. */\n limit?: number;\n /** Wall-clock budget for the query, in milliseconds. */\n timeoutMs?: number;\n}\n\n/** Options for a live outbox subscription. */\nexport interface OutboxSubscribeOptions extends OutboxQueryOptions {}\n\n/** Options for an outbox publish. */\nexport interface OutboxPublishOptions {\n /** Explicit relay URL fanout candidates, subject to shell validation. */\n relays?: string[];\n /** Include the shell user's NIP-65 write relays. Defaults to true. */\n toOutbox?: boolean;\n /** Recipient pubkeys whose NIP-65 read relays are required fanout targets. */\n toInboxes?: string[];\n}\n\n/** A read/write target for relay-plan resolution. */\nexport interface OutboxTarget {\n /** Authors to resolve relays for. */\n authors?: string[];\n /** Single pubkey to resolve relays for. */\n pubkey?: string;\n /** Whether the plan is for reading (their write relays) or writing (their read relays). */\n direction?: 'read' | 'write';\n}\n\n/** The relay plan the shell would use for a target. */\nexport interface OutboxRelayPlan {\n /** Resolved relay URLs. */\n relays: string[];\n /** Where the plan came from. */\n source: 'nip65' | 'cache' | 'policy' | 'fallback';\n /** Authors for which no relay list could be resolved. */\n missingAuthors?: string[];\n}\n\n/** Outcome of a single-event outbox lookup. */\nexport interface OutboxEventResult {\n /** The signature-validated event result matching the requested event id, when found. */\n result?: RelayEventResult;\n /** True when some relay lists or connections failed and the lookup was partial. */\n incomplete?: boolean;\n /** Error reason when the lookup could not complete or did not find the event. */\n error?: string;\n}\n\n/** Outcome of an outbox query, as returned by the {@link OutboxRouter}. */\nexport interface OutboxResult {\n /** Deduplicated, signature-validated event results. */\n events: RelayEventResult[];\n /** True when some relay lists or connections failed and results are partial. */\n incomplete?: boolean;\n /** Error reason when the query could not complete. */\n error?: string;\n}\n\n/** Outcome of an outbox publish, as returned by the {@link OutboxRouter}. */\nexport interface OutboxPublishResult {\n /** Whether the publish succeeded on at least the required relays. */\n ok: boolean;\n /** The signed event returned by the shell. */\n event?: NostrEvent;\n /** The published event id. */\n eventId?: string;\n /** Map of relay URL -> per-relay publish success. */\n relays?: Record<string, boolean>;\n /** Error reason when the publish failed. */\n error?: string;\n}\n\n/** Sink an {@link OutboxRouter} streams subscription lifecycle through. */\nexport interface OutboxSubscriptionSink {\n /** Deliver a matching event result. */\n event(result: RelayEventResult): void;\n /** Signal that the subscription was closed upstream; `reason` is optional. */\n closed(reason?: string): void;\n}\n\n/** Sink a bounded outbox query streams verified events through as they arrive. */\nexport interface OutboxQueryStreamSink {\n /** Deliver one deduplicated, signature-validated event result. */\n event(result: RelayEventResult): void;\n}\n\n/** Handle for a bounded streaming query and its compatibility aggregation. */\nexport interface OutboxQueryStream {\n /** Aggregated result, settled on all-relay EOSE or the overall deadline. */\n readonly result: Promise<OutboxResult>;\n /** Stop relay reads and settle {@link result} as incomplete. */\n close(): void;\n}\n\n/** Handle to a router-owned subscription. */\nexport interface OutboxRouterSubscription {\n /** Stop the subscription and release its relay connections. */\n close(): void;\n}\n\n/**\n * Abstract outbox router. Implementors own relay discovery (NIP-65 / NIP-66),\n * routing, fallback, deduplication, signature validation, signing, and publish\n * fanout. The service translates wire envelopes into these calls and back.\n */\nexport interface OutboxRouter {\n /** Fetch and validate one event by id through shell-owned outbox routing. */\n getEvent?(eventId: string, options?: OutboxEventOptions): Promise<OutboxEventResult>;\n /** Resolve relays, query them, dedup by id, validate signatures, collect events. */\n query(filters: NostrFilter[], options?: OutboxQueryOptions): Promise<OutboxResult>;\n /** Open a live outbox-aware subscription, streaming through `sink`. */\n subscribe(\n filters: NostrFilter[],\n options: OutboxSubscribeOptions | undefined,\n sink: OutboxSubscriptionSink,\n ): OutboxRouterSubscription;\n /** Sign `template` and fan it out to the relevant write/inbox relays. */\n publish(template: EventTemplate, options?: OutboxPublishOptions): Promise<OutboxPublishResult>;\n /** Return the relay plan the shell would use for a read/write target. */\n resolveRelays(target: OutboxTarget): Promise<OutboxRelayPlan>;\n}\n\n/** Outbox router with an incremental surface for bounded queries. */\nexport interface StreamingOutboxRouter extends OutboxRouter {\n /**\n * Start policy/fallback reads immediately, stream verified events through\n * `sink`, and attach author relays when concurrent discovery resolves.\n * `options.limit` applies to the final aggregate; the sink receives every\n * verified unique arrival until EOSE, close, or the overall deadline.\n */\n queryStream(\n filters: NostrFilter[],\n options: OutboxQueryOptions | undefined,\n sink: OutboxQueryStreamSink,\n ): OutboxQueryStream;\n}\n\n/** Options for {@link createOutboxService}. */\nexport interface OutboxServiceOptions {\n /** The outbox router the shell uses to reach relays. Required. */\n router: OutboxRouter;\n /**\n * Optionally selects a source-scoped router for event-returning reads.\n *\n * The selected router is used for `outbox.getEvent`, `outbox.query`, and\n * `outbox.subscribe`. A query-specific router takes precedence when both\n * hooks are supplied.\n *\n * @param windowId - The authenticated source window for the request.\n * @param context - Narrow runtime context for source-bound policy checks.\n * @returns Router used for that source's event-returning reads.\n */\n getReadRouter?(windowId: string, context: ServiceRuntimeContext | undefined): OutboxRouter;\n /**\n * Optionally selects a query-specific router for a live napplet source.\n *\n * @param windowId - The authenticated source window for the request.\n * @param context - Narrow runtime context for source-bound policy checks.\n * @returns Router used only for that source's one-shot outbox queries.\n */\n getQueryRouter?(windowId: string, context: ServiceRuntimeContext | undefined): OutboxRouter;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst OUTBOX_DESCRIPTOR: ServiceDescriptor = {\n name: 'outbox',\n version: OUTBOX_SERVICE_VERSION,\n description: 'NAP-OUTBOX outbox-aware relay routing — getEvent/query/subscribe/publish/resolveRelays',\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringArray(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const strings = value.filter((item): item is string => typeof item === 'string');\n return strings.length > 0 ? strings : undefined;\n}\n\nfunction uint(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\nfunction sanitizeEventOptions(raw: unknown): OutboxEventOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxEventOptions = {};\n if (typeof raw.author === 'string') out.author = raw.author;\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n const timeoutMs = uint(raw.timeoutMs);\n if (timeoutMs !== undefined) out.timeoutMs = timeoutMs;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeQueryOptions(raw: unknown): OutboxQueryOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxQueryOptions = {};\n const authors = stringArray(raw.authors);\n if (authors) out.authors = authors;\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n const limit = uint(raw.limit);\n if (limit !== undefined) out.limit = limit;\n const timeoutMs = uint(raw.timeoutMs);\n if (timeoutMs !== undefined) out.timeoutMs = timeoutMs;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nconst sanitizeSubscribeOptions = sanitizeQueryOptions;\n\nfunction sanitizePublishOptions(raw: unknown): OutboxPublishOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxPublishOptions = {};\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n if (typeof raw.toOutbox === 'boolean') out.toOutbox = raw.toOutbox;\n const toInboxes = stringArray(raw.toInboxes);\n if (toInboxes) out.toInboxes = toInboxes;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeTarget(raw: unknown): OutboxTarget | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxTarget = {};\n const authors = stringArray(raw.authors);\n if (authors) out.authors = authors;\n if (typeof raw.pubkey === 'string') out.pubkey = raw.pubkey;\n if (raw.direction === 'read' || raw.direction === 'write') out.direction = raw.direction;\n return out;\n}\n\n/**\n * Normalize a wire `filters` field (a single NIP-01 filter or an array) into a\n * filter array. Returns `null` when the input is missing or has no usable\n * filter objects.\n */\nfunction normalizeFilters(raw: unknown): NostrFilter[] | null {\n if (Array.isArray(raw)) {\n const filters = raw.filter((f): f is NostrFilter => typeof f === 'object' && f !== null);\n return filters.length > 0 ? filters : null;\n }\n if (typeof raw === 'object' && raw !== null) return [raw as NostrFilter];\n return null;\n}\n\nfunction buildEventQuery(eventId: string, eventOptions?: OutboxEventOptions): { filter: NostrFilter; queryOptions: OutboxQueryOptions } {\n const filter: NostrFilter = { ids: [eventId] };\n const queryOptions: OutboxQueryOptions = { limit: 1 };\n if (typeof eventOptions?.author === 'string' && eventOptions.author.length > 0) {\n filter.authors = [eventOptions.author];\n queryOptions.authors = [eventOptions.author];\n }\n if (Array.isArray(eventOptions?.relays)) queryOptions.relays = eventOptions.relays;\n if (eventOptions?.timeoutMs !== undefined) queryOptions.timeoutMs = eventOptions.timeoutMs;\n return { filter, queryOptions };\n}\n\nfunction eventResultFromQuery(eventId: string, result: OutboxResult): OutboxEventResult {\n const relayResult = result.events.find((candidate) => candidate.event.id === eventId);\n const eventResult: OutboxEventResult = {};\n if (relayResult) eventResult.result = relayResult;\n if (!relayResult) eventResult.error = result.error ?? 'not found';\n else if (result.error !== undefined) eventResult.error = result.error;\n if (result.incomplete !== undefined) eventResult.incomplete = result.incomplete;\n return eventResult;\n}\n\nfunction fallbackGetEvent(router: OutboxRouter, eventId: string, eventOptions?: OutboxEventOptions): Promise<OutboxEventResult> {\n const { filter, queryOptions } = buildEventQuery(eventId, eventOptions);\n return router.query([filter], queryOptions).then((result) => eventResultFromQuery(eventId, result));\n}\n\nfunction sendEventResult(id: string, eventId: string, result: OutboxEventResult, send: Send): void {\n if (result.result && result.result.event.id !== eventId) {\n send({ type: 'outbox.getEvent.result', id, error: 'not found' } as NappletMessage);\n return;\n }\n send({\n type: 'outbox.getEvent.result',\n id,\n ...(result.result === undefined ? {} : { result: result.result }),\n ...(result.incomplete === undefined ? {} : { incomplete: result.incomplete }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage);\n}\n\nfunction handleGetEvent(router: OutboxRouter, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; eventId?: unknown; options?: unknown };\n const id = m.id ?? '';\n if (typeof m.eventId !== 'string' || m.eventId.length === 0) {\n send({ type: 'outbox.getEvent.result', id, error: 'invalid filter' } as NappletMessage);\n return;\n }\n const getEvent = router.getEvent ?? ((eventId, eventOptions) => fallbackGetEvent(router, eventId, eventOptions));\n void getEvent(m.eventId, sanitizeEventOptions(m.options))\n .then((result) => sendEventResult(id, m.eventId as string, result, send))\n .catch((err) =>\n send({ type: 'outbox.getEvent.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n}\n\n/**\n * Create the NAP-OUTBOX service handler.\n *\n * @param options - Must provide an {@link OutboxRouter}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('outbox', handler)`.\n * @throws If `options.router` is missing.\n */\nexport function createOutboxService(options: OutboxServiceOptions): ServiceHandler {\n if (!options || typeof options.router !== 'object' || options.router === null) {\n throw new Error('createOutboxService: options.router is required');\n }\n const { router, getReadRouter, getQueryRouter } = options;\n let runtimeContext: ServiceRuntimeContext | undefined;\n\n // Active subscriptions keyed by `windowId:subId` for lifecycle management.\n const subscriptions = new Map<string, OutboxRouterSubscription>();\n\n function handleQuery(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; filters?: unknown; options?: unknown };\n const id = m.id ?? '';\n const filters = normalizeFilters(m.filters);\n if (!filters) {\n send({ type: 'outbox.query.result', id, events: [], error: 'invalid filter' } as NappletMessage);\n return;\n }\n const queryRouter = getQueryRouter?.(windowId, runtimeContext)\n ?? getReadRouter?.(windowId, runtimeContext)\n ?? router;\n void queryRouter\n .query(filters, sanitizeQueryOptions(m.options))\n .then((result) =>\n send({\n type: 'outbox.query.result',\n id,\n events: result.events,\n ...(result.incomplete === undefined ? {} : { incomplete: result.incomplete }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage),\n )\n .catch((err) =>\n send({ type: 'outbox.query.result', id, events: [], error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleSubscribe(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { subId?: string; filters?: unknown; options?: unknown };\n const subId = m.subId;\n if (typeof subId !== 'string' || subId.length === 0) return;\n const subKey = `${windowId}:${subId}`;\n\n // Replace any existing subscription for this key.\n subscriptions.get(subKey)?.close();\n subscriptions.delete(subKey);\n\n const filters = normalizeFilters(m.filters);\n if (!filters) {\n send({ type: 'outbox.closed', subId, reason: 'invalid filter' } as NappletMessage);\n return;\n }\n\n const sink: OutboxSubscriptionSink = {\n event: (result) => send({ type: 'outbox.event', subId, result } as NappletMessage),\n closed: (reason) => {\n subscriptions.delete(subKey);\n send({ type: 'outbox.closed', subId, ...(reason === undefined ? {} : { reason }) } as NappletMessage);\n },\n };\n\n const readRouter = getReadRouter?.(windowId, runtimeContext) ?? router;\n subscriptions.set(subKey, readRouter.subscribe(filters, sanitizeSubscribeOptions(m.options), sink));\n }\n\n function handleClose(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { subId?: string };\n const subId = m.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n subscriptions.get(subKey)?.close();\n subscriptions.delete(subKey);\n send({ type: 'outbox.closed', subId } as NappletMessage);\n }\n\n function handlePublish(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; event?: EventTemplate; options?: unknown };\n const id = m.id ?? '';\n if (!m.event || typeof m.event !== 'object') {\n send({ type: 'outbox.publish.result', id, ok: false, error: 'invalid filter' } as NappletMessage);\n return;\n }\n void router\n .publish(m.event, sanitizePublishOptions(m.options))\n .then((result) =>\n send({\n type: 'outbox.publish.result',\n id,\n ok: result.ok,\n ...(result.event === undefined ? {} : { event: result.event }),\n ...(result.eventId === undefined ? {} : { eventId: result.eventId }),\n ...(result.relays === undefined ? {} : { relays: result.relays }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage),\n )\n .catch((err) =>\n send({ type: 'outbox.publish.result', id, ok: false, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleResolveRelays(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; target?: unknown };\n const id = m.id ?? '';\n const target = sanitizeTarget(m.target);\n if (!target) {\n send({ type: 'outbox.resolveRelays.result', id, error: 'invalid filter' } as NappletMessage);\n return;\n }\n void router\n .resolveRelays(target)\n .then((plan) => send({ type: 'outbox.resolveRelays.result', id, plan } as NappletMessage))\n .catch((err) =>\n send({ type: 'outbox.resolveRelays.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n return {\n descriptor: OUTBOX_DESCRIPTOR,\n onRegistered(context: ServiceRuntimeContext): void {\n runtimeContext = context;\n },\n onUnregistered(): void {\n runtimeContext = undefined;\n },\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'outbox.getEvent':\n handleGetEvent(getReadRouter?.(windowId, runtimeContext) ?? router, message, send);\n return;\n case 'outbox.query':\n handleQuery(windowId, message, send);\n return;\n case 'outbox.subscribe':\n handleSubscribe(windowId, message, send);\n return;\n case 'outbox.close':\n handleClose(windowId, message, send);\n return;\n case 'outbox.publish':\n handlePublish(message, send);\n return;\n case 'outbox.resolveRelays':\n handleResolveRelays(message, send);\n return;\n default:\n // Unknown outbox.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, sub] of subscriptions) {\n if (key.startsWith(prefix)) {\n sub.close();\n subscriptions.delete(key);\n }\n }\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'outbox request failed';\n}\n","/**\n * relay-pool-outbox-router.ts — concrete {@link StreamingOutboxRouter} backed by a relay pool.\n *\n * Implements the outbox-model routing that NAP-OUTBOX centralizes so napplets\n * don't each reinvent it: derive authors, resolve their NIP-65 relays, fan a\n * per-relay subscription out across the plan, deduplicate by event id (while\n * recording every relay an event was observed on), validate signatures, and —\n * for publish — sign the template and fan it out to the relevant write/inbox\n * relays.\n *\n * NIP-65 relay-list *fetching* is the host's concern (it may come from a\n * kind-10002 cache, a NIP-66 indexer via `@kehto/nip/66`, or a live query), so\n * it is injected via {@link RelayPoolOutboxRouterOptions.loadRelayLists}. The\n * relay pool, signer, and signature verifier are injected too — keeping this\n * router browser-agnostic and unit-testable with mocks.\n *\n * Relay-selection model (per the outbox model):\n * - reading an author's events → their **write** relays (where they publish)\n * - writing to reach an author → their **read** relays (their inbox)\n *\n * @example\n * ```ts\n * import { createOutboxService, createRelayPoolOutboxRouter } from '@kehto/services';\n *\n * const router = createRelayPoolOutboxRouter({\n * relayPool: myOutboxPool,\n * loadRelayLists: (pubkeys) => relayListCache.getMany(pubkeys),\n * fallbackRelays: ['wss://relay.damus.io', 'wss://nos.lol'],\n * signEvent: (tmpl) => signer.signEvent(tmpl),\n * verifyEvent: (ev) => verifyEvent(ev),\n * });\n * runtime.registerService('outbox', createOutboxService({ router }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NostrEvent, NostrFilter } from '@napplet/core';\nimport { createRelayEventResultWithHints } from '@kehto/runtime';\nimport type { RelayEventResult } from '@kehto/runtime';\nimport type {\n OutboxResult,\n OutboxPublishResult,\n OutboxRelayPlan,\n OutboxEventOptions,\n OutboxEventResult,\n OutboxQueryOptions,\n OutboxSubscribeOptions,\n OutboxPublishOptions,\n OutboxTarget,\n OutboxSubscriptionSink,\n OutboxRouterSubscription,\n OutboxQueryStream,\n OutboxQueryStreamSink,\n StreamingOutboxRouter,\n} from './outbox-service.js';\nimport type {\n DiscoveredReadRelays,\n LiveOutboxSubscription,\n OutboxCollector,\n PublishTargetResolution,\n RelayListEntry,\n RelayPoolOutboxRouterOptions,\n RequiredRelayResolution,\n RouterCtx,\n} from './relay-pool-outbox-types.js';\n\nexport type {\n OutboxRelayPool,\n RelayListEntry,\n RelayPoolOutboxRouterOptions,\n} from './relay-pool-outbox-types.js';\n\n// Timer globals available in all JS runtimes.\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** Default per-query wall-clock budget when `options.timeoutMs` is unset. */\nconst DEFAULT_QUERY_TIMEOUT_MS = 4000;\n\n/** Default relay gate: only ws(s):// URLs are permitted. */\nfunction defaultRelayAllowed(url: string): boolean {\n return typeof url === 'string' && (url.startsWith('wss://') || url.startsWith('ws://'));\n}\n\n/** Collect a deduplicated author set from filters + explicit option hints. */\nfunction deriveAuthors(filters: NostrFilter[], optionAuthors?: string[]): string[] {\n const authors = new Set<string>();\n for (const filter of filters) {\n for (const author of filter.authors ?? []) authors.add(author);\n }\n for (const author of optionAuthors ?? []) authors.add(author);\n return [...authors];\n}\n\n/** Apply the relay gate to a candidate set, deduplicating. */\nfunction allowed(ctx: RouterCtx, urls: Iterable<string>): string[] {\n const out = new Set<string>();\n for (const url of urls) {\n if (ctx.isRelayAllowed(url)) out.add(url);\n }\n return [...out];\n}\n\nasync function resolveRequiredRelays(\n ctx: RouterCtx,\n pubkeys: string[],\n direction: 'read' | 'write',\n): Promise<RequiredRelayResolution> {\n const useWrite = direction === 'read';\n const relays = new Set<string>();\n const missingAuthors: string[] = [];\n const deniedAuthors: string[] = [];\n const uniquePubkeys = [...new Set(pubkeys)];\n\n if (uniquePubkeys.length === 0) return { relays: [], missingAuthors, deniedAuthors };\n\n const lists = await ctx.loadRelayLists(uniquePubkeys);\n for (const pubkey of uniquePubkeys) {\n const entry = lists.get(pubkey);\n const candidateRelays = entry ? (useWrite ? entry.write : entry.read) : undefined;\n if (!candidateRelays || candidateRelays.length === 0) {\n missingAuthors.push(pubkey);\n continue;\n }\n\n const allowedRelays = allowed(ctx, candidateRelays);\n if (allowedRelays.length === 0) {\n deniedAuthors.push(pubkey);\n continue;\n }\n\n for (const url of allowedRelays) relays.add(url);\n }\n\n return { relays: [...relays], missingAuthors, deniedAuthors };\n}\n\n/**\n * Resolve the relay plan for a set of pubkeys. Returns the allowed relay set,\n * its provenance, and any pubkeys whose relay list was unavailable.\n */\nasync function resolvePlan(\n ctx: RouterCtx,\n pubkeys: string[],\n direction: 'read' | 'write',\n relayHints?: string[],\n): Promise<OutboxRelayPlan> {\n const useWrite = direction === 'read';\n const discovered = new Set<string>();\n const missingAuthors: string[] = [];\n let sawNip65 = false;\n\n if (pubkeys.length > 0) {\n const lists = await ctx.loadRelayLists(pubkeys);\n for (const pubkey of pubkeys) {\n const entry = lists.get(pubkey);\n const relays = entry ? (useWrite ? entry.write : entry.read) : undefined;\n if (relays && relays.length > 0) {\n sawNip65 = true;\n for (const url of relays) discovered.add(url);\n } else {\n missingAuthors.push(pubkey);\n }\n }\n }\n\n const hintRelays = allowed(ctx, relayHints ?? []);\n let relays = allowed(ctx, discovered);\n let source: OutboxRelayPlan['source'];\n if (relays.length > 0) {\n source = sawNip65 ? 'nip65' : 'policy';\n } else {\n relays = allowed(ctx, ctx.fallbackRelays);\n source = relays.length > 0 ? 'fallback' : 'policy';\n }\n\n // Relay hints from the napplet augment the host-owned plan, subject to the relay gate.\n for (const url of hintRelays) {\n if (!relays.includes(url)) relays.push(url);\n }\n if (relays.length === 0) source = 'fallback';\n\n const plan: OutboxRelayPlan = { relays, source };\n if (missingAuthors.length > 0) plan.missingAuthors = missingAuthors;\n return plan;\n}\n\n/** Extract policy-allowed author write relays from already-loaded NIP-65 data. */\nfunction discoveredReadRelays(\n ctx: RouterCtx,\n authors: string[],\n lists: Map<string, RelayListEntry>,\n): DiscoveredReadRelays {\n const relays = new Set<string>();\n const missingAuthors: string[] = [];\n for (const author of new Set(authors)) {\n const writeRelays = lists.get(author)?.write;\n if (!writeRelays || writeRelays.length === 0) {\n missingAuthors.push(author);\n continue;\n }\n for (const url of allowed(ctx, writeRelays)) relays.add(url);\n }\n return { relays: [...relays], missingAuthors };\n}\n\n/** Pick immediate policy hints, falling back only when none survive validation. */\nfunction immediateReadRelays(ctx: RouterCtx, relayHints?: string[]): string[] {\n const hints = allowed(ctx, relayHints ?? []);\n return hints.length > 0 ? hints : allowed(ctx, ctx.fallbackRelays);\n}\n\n/** Record that `id` was observed on `relayUrl`. */\nfunction recordRelay(collector: OutboxCollector, id: string, relayUrl: string): void {\n let set = collector.relayMap.get(id);\n if (!set) { set = new Set<string>(); collector.relayMap.set(id, set); }\n set.add(relayUrl);\n}\n\n/** Materialize one event using every relay sighting known at call time. */\nfunction collectedEventResult(collector: OutboxCollector, event: NostrEvent): RelayEventResult {\n return createRelayEventResultWithHints(event, [...(collector.relayMap.get(event.id) ?? [])]);\n}\n\n/** Verify one event id once, then stream it and wake completion checks. */\nfunction admitEvent(\n ctx: RouterCtx,\n collector: OutboxCollector,\n event: NostrEvent,\n sink: OutboxQueryStreamSink,\n isOpen: () => boolean,\n onSettled: () => void,\n): void {\n if (collector.admitted.has(event.id)) return;\n collector.admitted.add(event.id);\n collector.pendingVerifications += 1;\n void ctx.verify(event).then((ok) => {\n if (!isOpen()) return;\n if (!ok) {\n collector.relayMap.delete(event.id);\n return;\n }\n collector.seen.set(event.id, event);\n try {\n sink.event(collectedEventResult(collector, event));\n } catch {\n // A consumer callback cannot poison relay collection or completion.\n }\n }).finally(() => {\n collector.pendingVerifications -= 1;\n onSettled();\n });\n}\n\n/** Build the compatibility aggregate without changing its existing limit rule. */\nfunction buildQueryResult(\n collector: OutboxCollector,\n options: OutboxQueryOptions | undefined,\n incomplete: boolean,\n subscribedAny: boolean,\n): OutboxResult {\n let events = [...collector.seen.values()].map((event) => collectedEventResult(collector, event));\n if (options?.limit !== undefined && events.length > options.limit) {\n events = [...events].sort((a, b) => b.event.created_at - a.event.created_at).slice(0, options.limit);\n }\n const result: OutboxResult = { events };\n if (incomplete || !subscribedAny) result.incomplete = true;\n if (!subscribedAny) result.error = 'relay list unavailable';\n return result;\n}\n\n/** Thin wrapper so callers read as a single-relay subscribe. */\nfunction relayPoolSubscribe(\n ctx: RouterCtx,\n filters: NostrFilter[],\n relayUrl: string,\n cb: (item: NostrEvent | 'EOSE') => void,\n): { unsubscribe(): void } {\n return ctx.relayPool.subscribe(filters, [relayUrl], cb);\n}\n\nfunction startQueryImpl(\n ctx: RouterCtx,\n filters: NostrFilter[],\n options: OutboxQueryOptions | undefined,\n sink: OutboxQueryStreamSink,\n): OutboxQueryStream {\n if (!ctx.relayPool.isAvailable()) {\n return {\n result: Promise.resolve({ events: [], incomplete: true, error: 'relay list unavailable' }),\n close(): void { /* no relay handles */ },\n };\n }\n\n const authors = deriveAuthors(filters, options?.authors);\n const collector: OutboxCollector = {\n seen: new Map(),\n admitted: new Set(),\n relayMap: new Map(),\n pendingVerifications: 0,\n };\n const handles = new Map<string, { unsubscribe(): void }>();\n const activeRelays = new Set<string>();\n const eoseRelays = new Set<string>();\n let discoveryPending = authors.length > 0;\n let incomplete = false;\n let subscribedAny = false;\n let finished = false;\n let resolveResult!: (result: OutboxResult) => void;\n const result = new Promise<OutboxResult>((resolve) => { resolveResult = resolve; });\n let timer: unknown;\n\n function closeHandles(): void {\n for (const handle of handles.values()) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n }\n handles.clear();\n }\n\n function finalize(forceIncomplete = false): void {\n if (finished) return;\n finished = true;\n incomplete ||= forceIncomplete;\n clearTimeout(timer);\n closeHandles();\n resolveResult(buildQueryResult(collector, options, incomplete, subscribedAny));\n }\n\n function maybeFinalize(): void {\n if (finished || discoveryPending || collector.pendingVerifications > 0) return;\n if (activeRelays.size === 0 || eoseRelays.size >= activeRelays.size) finalize();\n }\n\n function attachRelay(relayUrl: string): void {\n if (finished || activeRelays.has(relayUrl)) return;\n activeRelays.add(relayUrl);\n try {\n const handle = relayPoolSubscribe(ctx, filters, relayUrl, (item) => {\n if (finished) return;\n if (item === 'EOSE') {\n eoseRelays.add(relayUrl);\n maybeFinalize();\n return;\n }\n recordRelay(collector, item.id, relayUrl);\n admitEvent(ctx, collector, item, sink, () => !finished, maybeFinalize);\n });\n subscribedAny = true;\n if (finished) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n } else {\n handles.set(relayUrl, handle);\n }\n } catch {\n activeRelays.delete(relayUrl);\n incomplete = true;\n }\n }\n\n function attachMany(relayUrls: Iterable<string>): void {\n for (const relayUrl of relayUrls) attachRelay(relayUrl);\n }\n\n timer = setTimeout(() => finalize(true), options?.timeoutMs ?? ctx.defaultTimeoutMs);\n\n if (authors.length === 0) {\n discoveryPending = false;\n attachMany(allowed(ctx, [...ctx.fallbackRelays, ...(options?.relays ?? [])]));\n maybeFinalize();\n } else {\n let loaded: Promise<Map<string, RelayListEntry>> | Map<string, RelayListEntry>;\n try {\n loaded = ctx.loadRelayLists(authors);\n } catch {\n discoveryPending = false;\n incomplete = true;\n attachMany(immediateReadRelays(ctx, options?.relays));\n maybeFinalize();\n return { result, close: () => finalize(true) };\n }\n\n if (loaded instanceof Map) {\n const discovery = discoveredReadRelays(ctx, authors, loaded);\n discoveryPending = false;\n incomplete ||= discovery.missingAuthors.length > 0;\n const initial = allowed(ctx, [...(options?.relays ?? []), ...discovery.relays]);\n attachMany(initial.length > 0 ? initial : allowed(ctx, ctx.fallbackRelays));\n maybeFinalize();\n } else {\n attachMany(immediateReadRelays(ctx, options?.relays));\n void Promise.resolve(loaded).then((lists) => {\n if (finished) return;\n const discovery = discoveredReadRelays(ctx, authors, lists);\n discoveryPending = false;\n incomplete ||= discovery.missingAuthors.length > 0;\n attachMany(discovery.relays);\n maybeFinalize();\n }).catch(() => {\n if (finished) return;\n discoveryPending = false;\n incomplete = true;\n maybeFinalize();\n });\n }\n }\n\n return { result, close: () => finalize(true) };\n}\n\nfunction queryImpl(ctx: RouterCtx, filters: NostrFilter[], options?: OutboxQueryOptions): Promise<OutboxResult> {\n return startQueryImpl(ctx, filters, options, { event: () => { /* aggregate only */ } }).result;\n}\n\nasync function getEventImpl(ctx: RouterCtx, eventId: string, options?: OutboxEventOptions): Promise<OutboxEventResult> {\n if (typeof eventId !== 'string' || eventId.length === 0) {\n return { error: 'invalid filter' };\n }\n\n const filter: NostrFilter = { ids: [eventId] };\n const queryOptions: OutboxQueryOptions = { limit: 1 };\n if (typeof options?.author === 'string' && options.author.length > 0) {\n filter.authors = [options.author];\n queryOptions.authors = [options.author];\n }\n if (Array.isArray(options?.relays)) queryOptions.relays = options.relays;\n if (options?.timeoutMs !== undefined) queryOptions.timeoutMs = options.timeoutMs;\n\n const queryResult = await queryImpl(ctx, [filter], queryOptions);\n const eventResult = queryResult.events.find((candidate) => candidate.event.id === eventId);\n const result: OutboxEventResult = {};\n if (eventResult) result.result = eventResult;\n if (!eventResult) result.error = queryResult.error ?? 'not found';\n else if (queryResult.error !== undefined) result.error = queryResult.error;\n if (queryResult.incomplete !== undefined) result.incomplete = queryResult.incomplete;\n return result;\n}\n\nfunction closeLiveSub(sub: LiveOutboxSubscription): void {\n for (const handle of sub.handles.values()) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n }\n sub.handles.clear();\n}\n\n/** Wire one relay's subscription into an outbox subscription's event flow. */\nfunction attachLiveRelay(\n ctx: RouterCtx,\n sub: LiveOutboxSubscription,\n filters: NostrFilter[],\n relayUrl: string,\n sink: OutboxSubscriptionSink,\n): boolean {\n if (sub.closed || sub.relays.has(relayUrl)) return false;\n sub.relays.add(relayUrl);\n try {\n const handle = relayPoolSubscribe(ctx, filters, relayUrl, (item) => {\n if (sub.closed || item === 'EOSE' || sub.admitted.has(item.id)) return;\n sub.admitted.add(item.id);\n void ctx.verify(item).then((ok) => {\n if (!ok || sub.closed || sub.seen.has(item.id)) return;\n sub.seen.add(item.id);\n sink.event(createRelayEventResultWithHints(item, [relayUrl]));\n });\n });\n if (sub.closed) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n } else {\n sub.handles.set(relayUrl, handle);\n }\n return true;\n } catch {\n sub.relays.delete(relayUrl);\n return false;\n }\n}\n\nfunction startSubscription(\n ctx: RouterCtx,\n filters: NostrFilter[],\n options: OutboxSubscribeOptions | undefined,\n sink: OutboxSubscriptionSink,\n): OutboxRouterSubscription {\n const authors = deriveAuthors(filters, options?.authors);\n const sub: LiveOutboxSubscription = {\n handles: new Map(),\n relays: new Set(),\n admitted: new Set(),\n seen: new Set(),\n closed: false,\n };\n let discoveryOpen = authors.length > 0;\n let discoveryTimer: unknown;\n\n function attachMany(relayUrls: Iterable<string>): void {\n for (const relayUrl of relayUrls) attachLiveRelay(ctx, sub, filters, relayUrl, sink);\n }\n\n function closeUnavailable(): void {\n if (sub.closed || sub.handles.size > 0) return;\n sub.closed = true;\n sink.closed('relay list unavailable');\n }\n\n if (!ctx.relayPool.isAvailable()) {\n closeUnavailable();\n } else if (authors.length === 0) {\n discoveryOpen = false;\n attachMany(immediateReadRelays(ctx, options?.relays));\n closeUnavailable();\n } else {\n let loaded: Promise<Map<string, RelayListEntry>> | Map<string, RelayListEntry>;\n try {\n loaded = ctx.loadRelayLists(authors);\n } catch {\n discoveryOpen = false;\n attachMany(immediateReadRelays(ctx, options?.relays));\n closeUnavailable();\n loaded = new Map();\n }\n\n if (discoveryOpen && loaded instanceof Map) {\n const discovery = discoveredReadRelays(ctx, authors, loaded);\n discoveryOpen = false;\n const initial = allowed(ctx, [...(options?.relays ?? []), ...discovery.relays]);\n attachMany(initial.length > 0 ? initial : allowed(ctx, ctx.fallbackRelays));\n closeUnavailable();\n } else if (discoveryOpen) {\n attachMany(immediateReadRelays(ctx, options?.relays));\n discoveryTimer = setTimeout(() => {\n discoveryOpen = false;\n closeUnavailable();\n }, options?.timeoutMs ?? ctx.defaultTimeoutMs);\n void Promise.resolve(loaded).then((lists) => {\n if (!discoveryOpen || sub.closed) return;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n attachMany(discoveredReadRelays(ctx, authors, lists).relays);\n closeUnavailable();\n }).catch(() => {\n if (!discoveryOpen || sub.closed) return;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n closeUnavailable();\n });\n }\n }\n\n return {\n close(): void {\n if (sub.closed) return;\n sub.closed = true;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n closeLiveSub(sub);\n },\n };\n}\n\n/** Resolve the full write/inbox/hint relay set a publish should fan out to. */\nasync function resolvePublishTargets(\n ctx: RouterCtx,\n signed: NostrEvent,\n options?: OutboxPublishOptions,\n): Promise<PublishTargetResolution> {\n const targets = new Set<string>();\n const requiredTargets = new Set<string>();\n\n if (options?.toOutbox !== false) {\n // The author's own write relays (outbox model for the user's own event).\n const outboxPlan = await resolveRequiredRelays(ctx, [signed.pubkey], 'read');\n if (outboxPlan.missingAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'relay list unavailable' };\n if (outboxPlan.deniedAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'policy denied' };\n for (const url of outboxPlan.relays) {\n targets.add(url);\n requiredTargets.add(url);\n }\n }\n\n if (options?.toInboxes && options.toInboxes.length > 0) {\n const inboxPlan = await resolveRequiredRelays(ctx, options.toInboxes, 'write');\n if (inboxPlan.missingAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'relay list unavailable' };\n if (inboxPlan.deniedAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'policy denied' };\n for (const url of inboxPlan.relays) {\n targets.add(url);\n requiredTargets.add(url);\n }\n }\n\n for (const url of options?.relays ?? []) targets.add(url);\n return {\n relayUrls: allowed(ctx, targets),\n requiredRelayUrls: allowed(ctx, requiredTargets),\n };\n}\n\nasync function publishImpl(ctx: RouterCtx, template: EventTemplate, options?: OutboxPublishOptions): Promise<OutboxPublishResult> {\n if (!ctx.signEvent) return { ok: false, error: 'publish denied' };\n if (!ctx.relayPool.isAvailable()) return { ok: false, error: 'relay list unavailable' };\n\n let signed: NostrEvent;\n try {\n signed = await ctx.signEvent(template);\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : 'sign failed' };\n }\n\n const publishTargets = await resolvePublishTargets(ctx, signed, options);\n if (publishTargets.error) {\n return { ok: false, event: signed, eventId: signed.id, error: publishTargets.error };\n }\n const relayUrls = publishTargets.relayUrls;\n if (relayUrls.length === 0) return { ok: false, event: signed, eventId: signed.id, error: 'relay list unavailable' };\n\n let relays: Record<string, boolean>;\n try {\n relays = normalizePublishResult(await ctx.relayPool.publish(signed, relayUrls), relayUrls);\n } catch (err) {\n return { ok: false, event: signed, eventId: signed.id, error: err instanceof Error ? err.message : 'publish failed' };\n }\n\n const ok = publishTargets.requiredRelayUrls.length > 0\n ? publishTargets.requiredRelayUrls.every((url) => relays[url] === true)\n : Object.values(relays).some(Boolean);\n const result: OutboxPublishResult = { ok, event: signed, eventId: signed.id, relays };\n if (!ok) result.error = 'publish denied';\n return result;\n}\n\n/**\n * Create a relay-pool-backed {@link StreamingOutboxRouter}.\n *\n * @param options - Relay pool, NIP-65 loader, fallback relays, and optional\n * signer / verifier / relay gate / timeout.\n * @returns A {@link StreamingOutboxRouter} for direct incremental reads and\n * compatibility use through {@link createOutboxService}.\n * @throws If `relayPool`, `loadRelayLists`, or `fallbackRelays` are missing.\n */\nexport function createRelayPoolOutboxRouter(options: RelayPoolOutboxRouterOptions): StreamingOutboxRouter {\n if (!options || typeof options.relayPool !== 'object' || options.relayPool === null) {\n throw new Error('createRelayPoolOutboxRouter: options.relayPool is required');\n }\n if (typeof options.loadRelayLists !== 'function') {\n throw new Error('createRelayPoolOutboxRouter: options.loadRelayLists is required');\n }\n if (!Array.isArray(options.fallbackRelays)) {\n throw new Error('createRelayPoolOutboxRouter: options.fallbackRelays is required');\n }\n\n const verifyEvent = options.verifyEvent;\n const ctx: RouterCtx = {\n relayPool: options.relayPool,\n loadRelayLists: options.loadRelayLists,\n fallbackRelays: options.fallbackRelays,\n signEvent: options.signEvent,\n isRelayAllowed: options.isRelayAllowed ?? defaultRelayAllowed,\n defaultTimeoutMs: options.defaultTimeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n async verify(event: NostrEvent): Promise<boolean> {\n if (!verifyEvent) return true;\n try {\n return await verifyEvent(event);\n } catch {\n return false;\n }\n },\n };\n\n return {\n getEvent: (eventId, eventOptions) => getEventImpl(ctx, eventId, eventOptions),\n query: (filters, queryOptions) => queryImpl(ctx, filters, queryOptions),\n queryStream: (filters, queryOptions, sink) => startQueryImpl(ctx, filters, queryOptions, sink),\n subscribe: (filters, subscribeOptions, sink) => startSubscription(ctx, filters, subscribeOptions, sink),\n publish: (template, publishOptions) => publishImpl(ctx, template, publishOptions),\n resolveRelays: (target: OutboxTarget) => {\n const pubkeys = target.authors ?? (target.pubkey ? [target.pubkey] : []);\n return resolvePlan(ctx, pubkeys, target.direction ?? 'read');\n },\n };\n}\n\n/** Normalize a pool publish return into a per-relay success map. */\nfunction normalizePublishResult(\n res: Record<string, boolean> | void,\n relayUrls: string[],\n): Record<string, boolean> {\n const out: Record<string, boolean> = {};\n if (res && typeof res === 'object') {\n for (const url of relayUrls) out[url] = res[url] ?? false;\n } else {\n // void return → optimistic success on every targeted relay.\n for (const url of relayUrls) out[url] = true;\n }\n return out;\n}\n","/**\n * upload-service.ts — NAP-UPLOAD (shell-mediated file/blob upload) reference service.\n *\n * Shell-side handler for the NAP-UPLOAD wire protocol. It is a pure envelope\n * router: it validates `upload.*` envelopes, delegates the actual byte transfer\n * (server selection, rail authorization signing, the HTTP upload) to an injected\n * {@link Uploader}, and posts the correlated result / status messages back to the\n * napplet.\n *\n * The uploader is injected (options-as-bridge) so this service has no transport\n * or Nostr dependency and is fully unit-testable. NAP-UPLOAD is deliberately\n * abstract over the backend — the runtime decides *how* it uploads (NIP-96,\n * Blossom, …). A concrete HTTP-backed uploader ships alongside as\n * {@link createHttpUploader}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: upload.info, upload.upload, upload.status\n * Outbound: upload.info.result, upload.upload.result,\n * upload.status.result, upload.status.changed\n *\n * The service owns the `uploadId` (generated per request, scoped to the\n * requesting napplet), tracks the latest {@link UploadStatus} per upload for\n * `upload.status` queries, and cleans up on window teardown. The shell owns\n * consent, policy, server selection, signing, and the HTTP upload — all behind\n * the {@link Uploader}.\n *\n * @example\n * ```ts\n * import { createUploadService, createHttpUploader } from '@kehto/services';\n *\n * const uploader = createHttpUploader({ rails: { nip96: { servers } }, signEvent });\n * runtime.registerService('upload', createUploadService({ uploader }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\n/** Upload service version — follows semver. */\nconst UPLOAD_SERVICE_VERSION = '1.0.0';\n\n/**\n * Storage rail. `nip96` (NIP-96 HTTP file storage) and `blossom` (Blossom blob\n * storage) are the first concrete backends; the open string keeps the API\n * stable as shells add rails (torrents, usenet, …).\n */\nexport type UploadRail = 'nip96' | 'blossom' | (string & {});\n\n/** Lifecycle state of an upload. */\nexport type UploadState = 'pending' | 'uploading' | 'complete' | 'failed' | 'cancelled';\n\n/** Pixel dimensions of an uploaded image/video. */\nexport interface UploadDimensions {\n width: number;\n height: number;\n}\n\n/**\n * A napplet's upload request. `data` crosses the postMessage boundary by\n * structured clone — shells never require base64 encoding.\n */\nexport interface UploadRequest {\n /** Storage rail; omit to let the shell pick a configured default. */\n rail?: UploadRail;\n /** The bytes to upload. */\n data: ArrayBuffer | Blob;\n /** MIME type; inferred from `data` when omitted. */\n mimeType?: string;\n /** Suggested filename. */\n filename?: string;\n /** Alt text / description for the file event. */\n caption?: string;\n /** Request the server not re-encode the file (NIP-96 `no_transform`). */\n noTransform?: boolean;\n /** Rail-specific or shell-specific extra metadata. */\n metadata?: Record<string, unknown>;\n}\n\n/** A single Nostr tag (NIP-94 / imeta entries are arrays of strings). */\nexport type NostrTag = string[];\n\n/** The result of an upload. */\nexport interface UploadResult {\n /** Whether the upload succeeded (or is progressing) vs failed/cancelled. */\n ok: boolean;\n /** Shell-generated id, scoped to the requesting napplet. */\n uploadId: string;\n /** Current lifecycle state. */\n status: UploadState;\n /** The rail the shell used. */\n rail: UploadRail;\n /** Primary download URL. */\n url?: string;\n /** Mirrors / alternative server URLs. */\n fallbackUrls?: string[];\n /** Hash of the stored blob (NIP-94 `x`). */\n sha256?: string;\n /** Hash before server transforms (NIP-94 `ox`). */\n originalSha256?: string;\n /** Size in bytes. */\n size?: number;\n /** Stored MIME type. */\n mimeType?: string;\n /** Image/video dimensions when known. */\n dimensions?: UploadDimensions;\n /** Blurhash placeholder when known. */\n blurhash?: string;\n /** Ready-to-attach NIP-94 / imeta tags. */\n nip94?: NostrTag[];\n /** Error reason when the upload failed or was cancelled. */\n error?: string;\n}\n\n/** A status snapshot for an upload, including progress counters. */\nexport interface UploadStatus extends UploadResult {\n /** Bytes sent so far (while uploading). */\n bytesSent?: number;\n /** Total bytes to send. */\n bytesTotal?: number;\n /** Unix ms timestamp of this status. */\n updatedAt: number;\n}\n\n/** Runtime-disclosed support for one upload rail. */\nexport interface UploadRailInfo {\n rail: UploadRail;\n enabled: boolean;\n returns?: string[];\n}\n\n/** Advisory upload rails and coarse policy limits disclosed by the runtime. */\nexport interface UploadInfo {\n rails: UploadRailInfo[];\n maxBytes?: number;\n mimeTypes?: string[];\n}\n\n/** Context passed to a host-provided upload info resolver. */\nexport interface UploadInfoContext {\n windowId: string;\n}\n\n/** Static or dynamic advisory upload info exposed through `upload.info`. */\nexport type UploadInfoProvider =\n | UploadInfo\n | ((context: UploadInfoContext) => UploadInfo | Promise<UploadInfo>);\n\n/**\n * Context handed to an {@link Uploader} for a single upload. Carries the\n * service-owned `uploadId` and a sink for streaming progress / state changes.\n */\nexport interface UploaderContext {\n /** The service-generated upload id (authoritative; scoped to the napplet). */\n uploadId: string;\n /** The napplet window that requested the upload. */\n windowId: string;\n /**\n * Push a status update (progress, or a transition to complete/failed). The\n * service stamps `uploadId` and `updatedAt` before forwarding to the napplet\n * as `upload.status.changed`, and records it as the latest tracked status.\n */\n onStatus(status: UploadStatus): void;\n}\n\n/**\n * Abstract upload backend. Implementors own server selection, rail\n * authorization signing (NIP-98 for NIP-96, kind 24242 for Blossom), the HTTP\n * upload, and integrity-hash reporting. The service translates wire envelopes\n * into these calls and back. A concrete reference implementation ships as\n * {@link createHttpUploader}.\n */\nexport interface Uploader {\n /** Upload `request.data`, streaming progress through `ctx.onStatus`. */\n upload(request: UploadRequest, ctx: UploaderContext): Promise<UploadResult>;\n /** Optional: resolve the latest status for an upload the service is not tracking. */\n status?(uploadId: string): Promise<UploadStatus | undefined>;\n /** Optional: abort an in-flight upload (called on window teardown). */\n cancel?(uploadId: string): void;\n}\n\n/** Options for {@link createUploadService}. */\nexport interface UploadServiceOptions {\n /** The upload backend the shell uses. Required. */\n uploader: Uploader;\n /**\n * Advisory NAP-UPLOAD introspection exposed through `upload.info`.\n *\n * Provide a static snapshot or resolver when the shell wants to disclose\n * configured rails and coarse limits. Omit to expose no rails and no limits.\n */\n uploadInfo?: UploadInfoProvider;\n /** Generate an upload id; defaults to `crypto.randomUUID()`. */\n generateId?: () => string;\n /** Current time in unix ms; defaults to `Date.now()`. */\n now?: () => number;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst UPLOAD_DESCRIPTOR: ServiceDescriptor = {\n name: 'upload',\n version: UPLOAD_SERVICE_VERSION,\n description: 'NAP-UPLOAD shell-mediated file/blob upload — upload/status with progress pushes',\n};\n\nconst DEFAULT_UPLOAD_INFO: UploadInfo = {\n rails: [],\n};\n\n/** Per-upload tracking entry, keyed by `windowId:uploadId`. */\ninterface UploadEntry {\n uploadId: string;\n status?: UploadStatus;\n}\n\ndeclare const crypto: { randomUUID(): string };\n\n/**\n * Create the NAP-UPLOAD service handler.\n *\n * @param options - Must provide an {@link Uploader}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('upload', handler)`.\n * @throws If `options.uploader` is missing.\n */\nexport function createUploadService(options: UploadServiceOptions): ServiceHandler {\n if (!options || typeof options.uploader !== 'object' || options.uploader === null) {\n throw new Error('createUploadService: options.uploader is required');\n }\n const { uploader } = options;\n const uploadInfo = options.uploadInfo ?? DEFAULT_UPLOAD_INFO;\n const generateId = options.generateId ?? (() => crypto.randomUUID());\n const now = options.now ?? (() => Date.now());\n\n // Tracked uploads keyed by `windowId:uploadId` for status lookup + cleanup.\n const entries = new Map<string, UploadEntry>();\n\n function normalizeUploadInfo(info: UploadInfo): UploadInfo {\n return {\n rails: Array.isArray(info.rails)\n ? info.rails\n .filter((rail) => typeof rail?.rail === 'string')\n .map((rail) => ({\n rail: rail.rail,\n enabled: rail.enabled !== false,\n ...(Array.isArray(rail.returns) ? { returns: rail.returns.filter((value) => typeof value === 'string') } : {}),\n }))\n : [],\n ...(typeof info.maxBytes === 'number' ? { maxBytes: info.maxBytes } : {}),\n ...(Array.isArray(info.mimeTypes) ? { mimeTypes: info.mimeTypes.filter((value) => typeof value === 'string') } : {}),\n };\n }\n\n function handleInfo(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string };\n const id = m.id ?? '';\n void Promise.resolve()\n .then(() => (typeof uploadInfo === 'function' ? uploadInfo({ windowId }) : uploadInfo))\n .then((info) => {\n send({ type: 'upload.info.result', id, info: normalizeUploadInfo(info) } as NappletMessage);\n })\n .catch((err) => {\n send({ type: 'upload.info.result', id, error: toErrorMessage(err) } as NappletMessage);\n });\n }\n\n function handleUpload(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; request?: UploadRequest };\n const id = m.id ?? '';\n const request = m.request;\n if (!request || typeof request !== 'object' || request.data == null) {\n send({ type: 'upload.upload.result', id, error: 'invalid request' } as NappletMessage);\n return;\n }\n\n const uploadId = generateId();\n const key = `${windowId}:${uploadId}`;\n entries.set(key, { uploadId });\n\n const ctx: UploaderContext = {\n uploadId,\n windowId,\n onStatus: (status) => {\n const stamped: UploadStatus = { ...status, uploadId, updatedAt: status.updatedAt || now() };\n const entry = entries.get(key);\n if (entry) entry.status = stamped;\n send({ type: 'upload.status.changed', status: stamped } as NappletMessage);\n },\n };\n\n void uploader\n .upload(request, ctx)\n .then((result) => {\n const stamped: UploadResult = { ...result, uploadId };\n const entry = entries.get(key);\n if (entry) entry.status = { ...stamped, updatedAt: now() };\n send({ type: 'upload.upload.result', id, result: stamped } as NappletMessage);\n })\n .catch((err) => {\n entries.delete(key);\n send({ type: 'upload.upload.result', id, error: toErrorMessage(err) } as NappletMessage);\n });\n }\n\n function handleStatus(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; uploadId?: string };\n const id = m.id ?? '';\n const uploadId = m.uploadId;\n if (typeof uploadId !== 'string' || uploadId.length === 0) {\n send({ type: 'upload.status.result', id, error: 'invalid uploadId' } as NappletMessage);\n return;\n }\n\n const tracked = entries.get(`${windowId}:${uploadId}`)?.status;\n if (tracked) {\n send({ type: 'upload.status.result', id, status: tracked } as NappletMessage);\n return;\n }\n\n if (uploader.status) {\n void uploader\n .status(uploadId)\n .then((status) =>\n send(\n status\n ? ({ type: 'upload.status.result', id, status } as NappletMessage)\n : ({ type: 'upload.status.result', id, error: 'unknown upload' } as NappletMessage),\n ),\n )\n .catch((err) =>\n send({ type: 'upload.status.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n return;\n }\n\n send({ type: 'upload.status.result', id, error: 'unknown upload' } as NappletMessage);\n }\n\n return {\n descriptor: UPLOAD_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'upload.info':\n handleInfo(windowId, message, send);\n return;\n case 'upload.upload':\n handleUpload(windowId, message, send);\n return;\n case 'upload.status':\n handleStatus(windowId, message, send);\n return;\n default:\n // Unknown upload.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of entries) {\n if (key.startsWith(prefix)) {\n uploader.cancel?.(entry.uploadId);\n entries.delete(key);\n }\n }\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'upload request failed';\n}\n","/**\n * http-uploader.ts — NAP-UPLOAD concrete HTTP-backed {@link Uploader}.\n *\n * The reference upload backend for {@link createUploadService}. Implements two\n * storage rails over HTTP:\n *\n * - **NIP-96** — signs a NIP-98 (kind 27235) HTTP-auth event, POSTs the file as\n * `multipart/form-data`, and maps the returned NIP-94 event tags into an\n * {@link UploadResult}.\n * - **Blossom** — signs a kind 24242 authorization event, PUTs the raw bytes to\n * `<server>/upload`, and maps the returned blob descriptor.\n *\n * Signing (`signEvent`) and transport (`fetch`) are injected so the uploader\n * carries no Nostr or network dependency and is fully unit-testable. The shell\n * holds the signing key and never exposes it to napplets — the uploader only\n * receives a signing callback. Server URLs are shell configuration, not napplet\n * input: a napplet may *hint* a rail, but never a server.\n *\n * The configured server URL is used directly as the upload endpoint (the\n * NIP-96 `api_url` / Blossom base). Hosts that need `.well-known` discovery can\n * resolve it before constructing the uploader.\n *\n * @example\n * ```ts\n * const uploader = createHttpUploader({\n * rails: { nip96: { servers: ['https://nostr.build/api/v2/nip96/upload'] } },\n * signEvent: (tmpl) => signer.signEvent(tmpl),\n * });\n * runtime.registerService('upload', createUploadService({ uploader }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NostrEvent } from '@napplet/core';\nimport type {\n NostrTag,\n UploadDimensions,\n UploadRail,\n UploadRequest,\n UploadResult,\n Uploader,\n UploaderContext,\n} from './upload-service.js';\n\n/** NIP-98 HTTP-auth event kind. */\nconst KIND_NIP98 = 27235;\n/** Blossom authorization event kind. */\nconst KIND_BLOSSOM_AUTH = 24242;\n/** Blossom auth-event lifetime, in seconds. */\nconst BLOSSOM_AUTH_TTL_S = 3600;\n\n/** Per-rail server configuration. The first server is the primary endpoint. */\nexport interface RailServerConfig {\n /** Ordered server endpoint URLs; index 0 is primary. */\n servers: string[];\n}\n\n/** Storage rails this uploader can serve. */\nexport interface HttpUploaderRails {\n /** NIP-96 HTTP file storage. */\n nip96?: RailServerConfig;\n /** Blossom blob storage. */\n blossom?: RailServerConfig;\n}\n\n/** Signs an event template on the user's behalf (shell holds the key). */\nexport type SignEvent = (template: EventTemplate) => Promise<NostrEvent>;\n\n/** Options for {@link createHttpUploader}. */\nexport interface HttpUploaderOptions {\n /** Configured rails + their servers. */\n rails: HttpUploaderRails;\n /** Rail to use when a request omits one; defaults to the first configured rail. */\n defaultRail?: UploadRail;\n /** Signs NIP-98 / Blossom auth events. Required. */\n signEvent: SignEvent;\n /** Fetch implementation; defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Hex SHA-256 of the payload bytes; defaults to Web Crypto. */\n digestSha256?: (bytes: Uint8Array) => Promise<string>;\n /** Unix *seconds* clock for event timestamps; defaults to `Date.now()/1000`. */\n now?: () => number;\n}\n\ndeclare const btoa: (data: string) => string;\ndeclare const crypto: { subtle: { digest(alg: string, data: ArrayBuffer): Promise<ArrayBuffer> } };\n\n/**\n * Create the reference HTTP {@link Uploader} (NIP-96 + Blossom rails).\n *\n * @param options - Rails, server config, and the injected `signEvent`.\n * @returns An {@link Uploader} for `createUploadService({ uploader })`.\n * @throws If `options.signEvent` is missing.\n */\nexport function createHttpUploader(options: HttpUploaderOptions): Uploader {\n if (!options || typeof options.signEvent !== 'function') {\n throw new Error('createHttpUploader: options.signEvent is required');\n }\n const rails = options.rails ?? {};\n const signEvent = options.signEvent;\n const fetchFn = options.fetch ?? fetch;\n const digest = options.digestSha256 ?? defaultDigestSha256;\n const nowS = options.now ?? (() => Math.floor(Date.now() / 1000));\n const defaultRail = options.defaultRail ?? firstConfiguredRail(rails);\n const inFlight = new Map<string, AbortController>();\n\n async function upload(request: UploadRequest, ctx: UploaderContext): Promise<UploadResult> {\n const rail = request.rail ?? defaultRail;\n const config = rail === 'nip96' ? rails.nip96 : rail === 'blossom' ? rails.blossom : undefined;\n if (rail !== 'nip96' && rail !== 'blossom') {\n return failed(ctx.uploadId, rail ?? 'unknown', 'unsupported rail');\n }\n\n const server = config?.servers?.[0];\n if (!server) {\n return failed(ctx.uploadId, rail, 'no server configured');\n }\n\n const controller = new AbortController();\n inFlight.set(ctx.uploadId, controller);\n let sha256: string | undefined;\n\n try {\n const bytes = await toBytes(request.data);\n sha256 = await digest(bytes);\n if (controller.signal.aborted) {\n return cancelled(ctx.uploadId, rail, sha256);\n }\n return rail === 'nip96'\n ? await uploadNip96({ request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller })\n : await uploadBlossom({ request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller });\n } catch (err) {\n if (controller.signal.aborted) {\n return cancelled(ctx.uploadId, rail, sha256);\n }\n return failed(ctx.uploadId, rail, toErrorMessage(err), sha256);\n } finally {\n inFlight.delete(ctx.uploadId);\n }\n }\n\n function cancel(uploadId: string): void {\n inFlight.get(uploadId)?.abort();\n }\n\n return { upload, cancel };\n}\n\ninterface RailUploadArgs {\n request: UploadRequest;\n ctx: UploaderContext;\n server: string;\n bytes: Uint8Array;\n sha256: string;\n signEvent: SignEvent;\n fetchFn: typeof fetch;\n nowS: () => number;\n controller: AbortController;\n}\n\nasync function uploadNip96(args: RailUploadArgs): Promise<UploadResult> {\n const { request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller } = args;\n\n const auth = await signEvent({\n kind: KIND_NIP98,\n created_at: nowS(),\n content: '',\n tags: [\n ['u', server],\n ['method', 'POST'],\n ['payload', sha256],\n ],\n });\n\n const form = new FormData();\n form.append('file', new Blob([bytesToArrayBuffer(bytes)], { type: request.mimeType }), request.filename ?? 'file');\n if (request.caption !== undefined) form.append('caption', request.caption);\n if (request.mimeType !== undefined) form.append('content_type', request.mimeType);\n if (request.noTransform) form.append('no_transform', 'true');\n\n emitUploading(ctx, 'nip96', bytes.byteLength, nowS);\n\n const res = await fetchFn(server, {\n method: 'POST',\n headers: { Authorization: nostrAuthHeader(auth) },\n body: form,\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return failed(ctx.uploadId, 'nip96', `server rejected (HTTP ${res.status})`, sha256);\n }\n\n const body = (await res.json()) as Nip96Response;\n if (body.status === 'error') {\n return failed(ctx.uploadId, 'nip96', body.message ?? 'upload failed', sha256);\n }\n\n const tags = body.nip94_event?.tags ?? [];\n return fromNip94Tags(ctx.uploadId, 'nip96', tags, bytes.byteLength, sha256);\n}\n\ninterface Nip96Response {\n status?: 'success' | 'error' | string;\n message?: string;\n nip94_event?: { tags?: string[][] };\n}\n\n/** Map NIP-94 / imeta tags into an {@link UploadResult}. */\nfunction fromNip94Tags(\n uploadId: string,\n rail: UploadRail,\n tags: string[][],\n fallbackSize: number,\n fallbackSha: string,\n): UploadResult {\n const get = (name: string): string | undefined => tags.find((t) => t[0] === name)?.[1];\n const url = get('url');\n const result: UploadResult = {\n ok: Boolean(url),\n uploadId,\n status: url ? 'complete' : 'failed',\n rail,\n sha256: get('x') ?? fallbackSha,\n nip94: tags as NostrTag[],\n };\n if (url) result.url = url;\n const ox = get('ox');\n if (ox) result.originalSha256 = ox;\n const size = get('size');\n result.size = size ? Number(size) : fallbackSize;\n const m = get('m');\n if (m) result.mimeType = m;\n const dim = parseDimensions(get('dim'));\n if (dim) result.dimensions = dim;\n const blurhash = get('blurhash');\n if (blurhash) result.blurhash = blurhash;\n if (!url) result.error = 'server returned no url';\n return result;\n}\n\nasync function uploadBlossom(args: RailUploadArgs): Promise<UploadResult> {\n const { request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller } = args;\n\n const auth = await signEvent({\n kind: KIND_BLOSSOM_AUTH,\n created_at: nowS(),\n content: `Upload ${request.filename ?? 'file'}`,\n tags: [\n ['t', 'upload'],\n ['x', sha256],\n ['expiration', String(nowS() + BLOSSOM_AUTH_TTL_S)],\n ],\n });\n\n const endpoint = `${trimTrailingSlash(server)}/upload`;\n const headers: Record<string, string> = { Authorization: nostrAuthHeader(auth) };\n if (request.mimeType) headers['Content-Type'] = request.mimeType;\n\n emitUploading(ctx, 'blossom', bytes.byteLength, nowS);\n\n const res = await fetchFn(endpoint, {\n method: 'PUT',\n headers,\n body: bytesToArrayBuffer(bytes),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return failed(ctx.uploadId, 'blossom', `server rejected (HTTP ${res.status})`, sha256);\n }\n\n const blob = (await res.json()) as BlossomDescriptor;\n if (typeof blob.url !== 'string' || blob.url.length === 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned no url', sha256);\n }\n if (typeof blob.sha256 !== 'string' || blob.sha256.length === 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned no sha256', sha256);\n }\n if (blob.sha256 !== sha256) {\n return failed(ctx.uploadId, 'blossom', 'server returned mismatched sha256', sha256);\n }\n if (!Number.isSafeInteger(blob.size) || (blob.size ?? -1) < 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned invalid size', sha256);\n }\n if (blob.size !== bytes.byteLength) {\n return failed(ctx.uploadId, 'blossom', 'server returned mismatched size', sha256);\n }\n\n const mimeType = blob.type ?? request.mimeType;\n const nip94: NostrTag[] = [['url', blob.url]];\n if (mimeType) nip94.push(['m', mimeType]);\n nip94.push(['x', sha256], ['size', String(bytes.byteLength)]);\n\n const result: UploadResult = {\n ok: true,\n uploadId: ctx.uploadId,\n status: 'complete',\n rail: 'blossom',\n url: blob.url,\n sha256,\n size: bytes.byteLength,\n nip94,\n };\n if (mimeType) result.mimeType = mimeType;\n return result;\n}\n\ninterface BlossomDescriptor {\n url?: string;\n sha256?: string;\n size?: number;\n type?: string;\n}\n\nfunction failed(uploadId: string, rail: UploadRail, error: string, sha256?: string): UploadResult {\n return { ok: false, uploadId, status: 'failed', rail, error, ...(sha256 ? { sha256 } : {}) };\n}\n\nfunction cancelled(uploadId: string, rail: UploadRail, sha256?: string): UploadResult {\n return { ok: false, uploadId, status: 'cancelled', rail, error: 'user cancelled', ...(sha256 ? { sha256 } : {}) };\n}\n\nfunction emitUploading(ctx: UploaderContext, rail: UploadRail, bytesTotal: number, nowS: () => number): void {\n ctx.onStatus({\n ok: true,\n uploadId: ctx.uploadId,\n status: 'uploading',\n rail,\n bytesTotal,\n updatedAt: nowS() * 1_000,\n });\n}\n\nfunction firstConfiguredRail(rails: HttpUploaderRails): UploadRail | undefined {\n if (rails.nip96?.servers?.length) return 'nip96';\n if (rails.blossom?.servers?.length) return 'blossom';\n return undefined;\n}\n\nfunction nostrAuthHeader(event: NostrEvent): string {\n return `Nostr ${base64Utf8(JSON.stringify(event))}`;\n}\n\nfunction base64Utf8(s: string): string {\n // UTF-8 safe base64 (event content/tags may contain non-ASCII).\n return btoa(String.fromCharCode(...new TextEncoder().encode(s)));\n}\n\nfunction parseDimensions(dim: string | undefined): UploadDimensions | undefined {\n if (!dim) return undefined;\n const m = /^(\\d+)x(\\d+)$/.exec(dim);\n if (!m) return undefined;\n return { width: Number(m[1]), height: Number(m[2]) };\n}\n\nasync function toBytes(data: ArrayBuffer | Blob): Promise<Uint8Array> {\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n return new Uint8Array(await data.arrayBuffer());\n}\n\nfunction bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n\nasync function defaultDigestSha256(bytes: Uint8Array): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-256', bytesToArrayBuffer(bytes));\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'upload failed';\n}\n","/**\n * intent-service.ts — NAP-INTENT (archetype intent dispatch) reference service.\n *\n * Shell-side handler for the NAP-INTENT wire protocol. It is a pure envelope\n * router: it validates `intent.*` envelopes, delegates archetype resolution,\n * default handling and target dispatch to an injected\n * {@link IntentResolver}, then posts correlated result and push messages back\n * to the napplet.\n *\n * The resolver is injected (options-as-bridge) so this service has no shell or\n * target-lifecycle dependency and is fully unit-testable. A concrete\n * catalog-backed resolver ships alongside as {@link createCatalogIntentResolver}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: intent.invoke, intent.available, intent.handlers\n * Outbound: intent.invoke.result, intent.available.result,\n * intent.handlers.result, intent.changed\n *\n * The shell owns archetype→handler resolution, the user's default-handler\n * preference, chooser, and target lifecycle policy behind the\n * {@link IntentResolver}. This service only marshals the wire protocol and\n * fans `intent.changed` pushes out to eligible napplets.\n *\n * @example\n * ```ts\n * import { createIntentService, createCatalogIntentResolver } from '@kehto/services';\n *\n * const resolver = createCatalogIntentResolver({ loadCatalog, targets });\n * runtime.registerService('intent', createIntentService({ resolver }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type {\n IntentAvailability,\n IntentRequest,\n IntentResult,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n ServiceDescriptor,\n ServiceHandler,\n ServiceRuntimeContext,\n} from '@kehto/runtime';\n\n/** Intent service version — follows semver. */\nconst INTENT_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to {@link IntentResolver.invoke} for trust/attribution. */\nexport interface IntentResolverContext {\n /** Runtime-attested dTag of the napplet that issued the request. */\n sender: string;\n}\n\n/**\n * Abstract intent resolver. Implementors own the installed-napplet catalog,\n * archetype→handler resolution, the user's default-handler preference, the\n * chooser and target lifecycle/delivery policy. The\n * service translates wire envelopes into these calls and back.\n */\nexport interface IntentResolver {\n /**\n * Resolve and dispatch the request.\n *\n * @param request - Validated normalized intent request.\n * @param context - Runtime-attested source identity.\n * @returns The canonical structured dispatch result.\n */\n invoke(\n request: IntentRequest,\n context: IntentResolverContext,\n ): IntentResult | Promise<IntentResult>;\n /** Report whether the runtime can currently satisfy `archetype`, and how. */\n available(archetype: string): IntentAvailability | Promise<IntentAvailability>;\n /** Report availability for every archetype the runtime can currently satisfy. */\n handlers(): IntentAvailability[] | Promise<IntentAvailability[]>;\n /**\n * Register for availability changes (a napplet installed/removed, or a default\n * handler changed). The service forwards each change to served napplets as an\n * `intent.changed` push. Returns an unsubscribe handle. Resolvers whose\n * catalog never changes at runtime MAY omit this.\n */\n onChanged?(listener: (availability: IntentAvailability) => void): () => void;\n}\n\n/** Options for {@link createIntentService}. */\nexport interface IntentServiceOptions {\n /** The intent resolver the shell uses to route archetypes. Required. */\n resolver: IntentResolver;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst INTENT_DESCRIPTOR: ServiceDescriptor = {\n name: 'intent',\n version: INTENT_SERVICE_VERSION,\n description: 'NAP-INTENT archetype intent dispatch — invoke/available/handlers',\n};\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'intent request failed';\n}\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\ntype RequestValidation =\n | { request: IntentRequest }\n | { error: 'invalid request' | 'invalid convention' | 'invoke rejected' };\n\nfunction validateIntentRequest(value: unknown): RequestValidation {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return { error: 'invalid request' };\n }\n const request = value as Record<string, unknown>;\n if (\n typeof request.archetype !== 'string'\n || request.archetype.length === 0\n ) {\n return { error: 'invalid request' };\n }\n\n const action = request.action === undefined ? 'open' : request.action;\n if (typeof action !== 'string' || action.length === 0) {\n return { error: 'invalid request' };\n }\n if (\n request.convention !== undefined\n && (\n typeof request.convention !== 'string'\n || !/^napplet:[^/?#\\s]+\\/[^/?#\\s]+$/.test(request.convention)\n )\n ) {\n return { error: 'invalid convention' };\n }\n\n const allowedKeys = ['archetype', 'action', 'convention', 'payload', 'handler', 'behavior'];\n if (Object.keys(request).some((key) => !allowedKeys.includes(key))) {\n return { error: 'invoke rejected' };\n }\n\n if (\n hasOwn(request, 'handler')\n && request.handler !== undefined\n && (typeof request.handler !== 'string' || request.handler.length === 0)\n ) {\n return { error: 'invoke rejected' };\n }\n\n let behavior: IntentRequest['behavior'];\n if (hasOwn(request, 'behavior') && request.behavior !== undefined) {\n if (\n typeof request.behavior !== 'object'\n || request.behavior === null\n || Array.isArray(request.behavior)\n ) {\n return { error: 'invoke rejected' };\n }\n const record = request.behavior as Record<string, unknown>;\n if (\n Object.keys(record).some((key) =>\n key !== 'focus' && key !== 'newWindow' && key !== 'reuse')\n || (hasOwn(record, 'focus') && typeof record.focus !== 'boolean')\n || (hasOwn(record, 'newWindow') && typeof record.newWindow !== 'boolean')\n || (hasOwn(record, 'reuse') && typeof record.reuse !== 'boolean')\n ) {\n return { error: 'invoke rejected' };\n }\n behavior = {\n ...(record.focus === undefined ? {} : { focus: record.focus as boolean }),\n ...(record.newWindow === undefined\n ? {}\n : { newWindow: record.newWindow as boolean }),\n ...(record.reuse === undefined ? {} : { reuse: record.reuse as boolean }),\n };\n }\n\n return {\n request: {\n archetype: request.archetype,\n action,\n ...(typeof request.convention === 'string'\n ? { convention: request.convention }\n : {}),\n ...(hasOwn(request, 'payload') ? { payload: request.payload } : {}),\n ...(typeof request.handler === 'string' ? { handler: request.handler } : {}),\n ...(behavior === undefined ? {} : { behavior }),\n },\n };\n}\n\nfunction settleResolverCall<T>(\n call: () => T | Promise<T>,\n send: Send,\n resultType: string,\n id: string,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (error) {\n send({ type: resultType, id, error: toErrorMessage(error) } as NappletMessage);\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((error) => send({ type: resultType, id, error: toErrorMessage(error) } as NappletMessage));\n}\n\nfunction rejectedResult(\n request: Pick<IntentRequest, 'archetype' | 'action'>,\n error: string,\n): IntentResult {\n return {\n ok: false,\n archetype: request.archetype,\n action: request.action ?? 'open',\n handled: false,\n error,\n };\n}\n\nfunction rejectInvoke(\n send: Send,\n id: string,\n request: Pick<IntentRequest, 'archetype' | 'action'>,\n error = 'invoke rejected',\n): void {\n send({\n type: 'intent.invoke.result',\n id,\n result: rejectedResult(request, error),\n } as NappletMessage);\n}\n\n/**\n * Create the NAP-INTENT service handler.\n *\n * @param options - Must provide an {@link IntentResolver}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('intent', handler)`.\n * @throws If `options.resolver` is missing.\n */\nexport function createIntentService(options: IntentServiceOptions): ServiceHandler {\n if (!options || typeof options.resolver !== 'object' || options.resolver === null) {\n throw new Error('createIntentService: options.resolver is required');\n }\n const { resolver } = options;\n let runtimeContext: ServiceRuntimeContext | undefined;\n let unsubscribeChanged: (() => void) | undefined;\n\n function handleInvoke(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; request?: unknown };\n const id = m.id ?? '';\n const validation = validateIntentRequest(m.request);\n if ('error' in validation) {\n const raw = typeof m.request === 'object' && m.request !== null\n ? m.request as Record<string, unknown>\n : {};\n rejectInvoke(\n send,\n id,\n {\n archetype: typeof raw.archetype === 'string' ? raw.archetype : '',\n action: typeof raw.action === 'string' ? raw.action : 'open',\n },\n validation.error,\n );\n return;\n }\n const sender = runtimeContext?.resolveDTag(windowId);\n if (!sender) {\n rejectInvoke(send, id, validation.request);\n return;\n }\n\n let pending: Promise<IntentResult>;\n try {\n pending = Promise.resolve(resolver.invoke(validation.request, { sender }));\n } catch {\n rejectInvoke(send, id, validation.request);\n return;\n }\n void pending.then(\n (result) => {\n send({\n type: 'intent.invoke.result',\n id,\n result,\n } as NappletMessage);\n },\n () => {\n rejectInvoke(send, id, validation.request);\n },\n ).catch(() => {\n // Adapter send failures must not manufacture a second source result.\n });\n }\n\n function handleAvailable(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; archetype?: unknown };\n const id = m.id ?? '';\n if (typeof m.archetype !== 'string' || m.archetype.length === 0) {\n send({ type: 'intent.available.result', id, error: 'invalid archetype' } as NappletMessage);\n return;\n }\n const archetype = m.archetype;\n settleResolverCall(\n () => resolver.available(archetype),\n send, 'intent.available.result', id,\n (availability) => ({ type: 'intent.available.result', id, availability } as NappletMessage),\n );\n }\n\n function handleHandlers(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string };\n const id = m.id ?? '';\n settleResolverCall(\n () => resolver.handlers(),\n send, 'intent.handlers.result', id,\n (handlers) => ({ type: 'intent.handlers.result', id, handlers } as NappletMessage),\n );\n }\n\n return {\n descriptor: INTENT_DESCRIPTOR,\n onRegistered(context): void {\n try {\n unsubscribeChanged?.();\n } catch {\n // Resolver cleanup is best-effort during replacement.\n }\n runtimeContext = context;\n unsubscribeChanged = resolver.onChanged?.((availability) => {\n const current = runtimeContext;\n if (!current) return;\n const message = { type: 'intent.changed', availability } as NappletMessage;\n for (const windowId of current.listWindowIds()) {\n current.sendToEligibleNapplet(windowId, message);\n }\n });\n },\n onUnregistered(): void {\n const unsubscribe = unsubscribeChanged;\n unsubscribeChanged = undefined;\n runtimeContext = undefined;\n try {\n unsubscribe?.();\n } catch {\n // Resolver cleanup cannot block runtime teardown.\n }\n },\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'intent.invoke':\n handleInvoke(windowId, message, send);\n return;\n case 'intent.available':\n handleAvailable(message, send);\n return;\n case 'intent.handlers':\n handleHandlers(message, send);\n return;\n default:\n // Unknown intent.* action — silently ignored (forward-compatible).\n return;\n }\n },\n };\n}\n","/**\n * catalog-intent-resolver.ts — NAP-INTENT concrete {@link IntentResolver}.\n *\n * A reference resolver backed by installed, verified NIP-5A manifest\n * archetype/convention tags plus host-supplied user policy. Catalog order and\n * payload contents never select a handler.\n *\n * Selection policy:\n * 1. Filter installed candidates by declared action and convention support.\n * 2. Require positive host authorization for an explicit handler dTag.\n * 3. Otherwise use an explicit chooser, a compatible user default, the sole\n * compatible candidate, or an injected chooser.\n * 4. Reject ambiguity when no chooser policy exists.\n *\n * The catalog, defaults, chooser, authorization hook, and target controller are\n * injected, so this resolver has no shell, manifest, or DOM dependency.\n *\n * @packageDocumentation\n */\n\nimport type {\n IntentAvailability,\n IntentBehavior,\n IntentCandidate,\n IntentRequest,\n IntentResult,\n} from '@napplet/core';\nimport type {\n IntentResolver,\n IntentResolverContext,\n} from './intent-service.js';\n\n/** The exact manifest-derived conventions a napplet fulfills for one archetype. */\nexport interface IntentArchetypeSupport {\n /** Verbs derived from this napplet's accepted conventions. */\n actions: string[];\n /** Stable queryless convention identities declared by the manifest. */\n conventions: string[];\n}\n\n/**\n * One installed napplet's intent surface, derived from its signed NIP-5A\n * manifest. Keyed by archetype slug so a single napplet can fulfill several\n * roles.\n */\nexport interface IntentCatalogEntry {\n /** The napplet's dTag. */\n dTag: string;\n /** Human-readable title from the manifest. */\n title?: string;\n /** Archetype slug to exact manifest-derived support. */\n archetypes: Record<string, IntentArchetypeSupport>;\n}\n\n/** Exact values dispatched to one selected intent target. */\nexport interface IntentDispatchParams {\n /** Selected target napplet dTag. */\n readonly handler: string;\n /** Runtime-attested source napplet dTag. */\n readonly sender: string;\n /** Requested target archetype. */\n readonly archetype: string;\n /** Requested action. */\n readonly action: string;\n /** Selected stable convention used to deliver the payload. */\n readonly convention: string;\n /** Opaque convention payload. */\n readonly payload?: unknown;\n /** Copied target lifecycle hints. */\n readonly behavior?: Readonly<IntentBehavior>;\n}\n\n/** Result returned by a host after creating/focusing and dispatching to a target. */\nexport interface IntentTargetDispatch {\n /** Shell-assigned target window identifier. */\n readonly windowId: string;\n}\n\n/** Host controller that owns target lifecycle and convention delivery policy. */\nexport interface IntentTargetController {\n /**\n * Create or focus the selected target, wait until it can receive the\n * convention, then enqueue delivery through the host's ordinary carrier.\n *\n * @param params - Immutable selected target and dispatch values.\n * @returns The created/focused target identity.\n */\n dispatch(\n params: IntentDispatchParams,\n ): IntentTargetDispatch | Promise<IntentTargetDispatch>;\n}\n\n/** Options for {@link createCatalogIntentResolver}. */\nexport interface CatalogIntentResolverOptions {\n /** Return the installed-napplet catalog sourced from signed manifests. */\n loadCatalog(): IntentCatalogEntry[] | Promise<IntentCatalogEntry[]>;\n /** Target controller that creates/readies the selected target and dispatches its convention. */\n targets: IntentTargetController;\n /**\n * Return the user's default handler dTag for an archetype.\n *\n * @param archetype - Normalized archetype slug.\n * @returns The user-selected default dTag, or `undefined`.\n */\n getDefaultHandler?(archetype: string): string | undefined;\n /**\n * Ask user policy to select one exact-compatible candidate.\n *\n * @param archetype - Normalized archetype slug.\n * @param candidates - Only candidates with an exact matching contract.\n * @param sender - Runtime-attested source napplet dTag.\n * @returns A candidate dTag, or `undefined` when the user cancels.\n */\n chooseHandler?(\n archetype: string,\n candidates: IntentCandidate[],\n sender: string,\n ): string | undefined | Promise<string | undefined>;\n /**\n * Authorize a caller's explicit handler dTag preference.\n *\n * @param sender - Runtime-attested source napplet dTag.\n * @param handler - Explicit requested handler dTag.\n * @param request - Normalized intent request.\n * @param candidate - Installed exact-compatible candidate.\n * @returns `true` only when explicit targeting is user-authorized.\n */\n authorizeExplicitHandler?(\n sender: string,\n handler: string,\n request: IntentRequest,\n candidate: IntentCandidate,\n ): boolean | Promise<boolean>;\n}\n\n/**\n * A {@link IntentResolver} backed by a catalog, with a host hook to announce\n * catalog/default changes.\n */\nexport interface CatalogIntentResolver extends IntentResolver {\n /**\n * Announce that the catalog or default handler for `archetype` changed.\n *\n * @param archetype - Changed archetype slug.\n * @returns Nothing.\n */\n notifyChanged(archetype: string): void;\n}\n\n/** Build the candidate list for an archetype, marking the user's default. */\nfunction candidatesFor(\n catalog: IntentCatalogEntry[],\n archetype: string,\n defaultHandler: string | undefined,\n): IntentCandidate[] {\n const candidates: IntentCandidate[] = [];\n for (const entry of catalog) {\n const support = entry.archetypes[archetype];\n if (!support) continue;\n candidates.push({\n dTag: entry.dTag,\n ...(entry.title === undefined ? {} : { title: entry.title }),\n actions: [...support.actions],\n conventions: [...support.conventions],\n ...(entry.dTag === defaultHandler ? { isDefault: true } : {}),\n });\n }\n return candidates;\n}\n\nfunction reject(request: IntentRequest, error: string): IntentResult {\n return {\n ok: false,\n archetype: request.archetype,\n action: request.action ?? 'open',\n handled: false,\n error,\n };\n}\n\ntype HandlerSelection =\n | { candidate: IntentCandidate }\n | { error: 'invoke rejected' | 'user cancelled' };\n\nasync function availabilityForCatalog(\n options: CatalogIntentResolverOptions,\n archetype: string,\n): Promise<IntentAvailability> {\n const catalog = await options.loadCatalog();\n const defaultHandler = options.getDefaultHandler?.(archetype);\n const candidates = candidatesFor(catalog, archetype, defaultHandler);\n return {\n archetype,\n available: candidates.length > 0,\n candidates,\n hasDefault: defaultHandler !== undefined\n && candidates.some((candidate) => candidate.dTag === defaultHandler),\n };\n}\n\nasync function chooseCompatible(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n candidates: IntentCandidate[],\n sender: string,\n): Promise<HandlerSelection> {\n if (!options.chooseHandler) {\n return request.handler === 'choose'\n ? { error: 'user cancelled' }\n : { error: 'invoke rejected' };\n }\n const picked = await options.chooseHandler(request.archetype, candidates, sender);\n if (picked === undefined) return { error: 'user cancelled' };\n const candidate = candidates.find((item) => item.dTag === picked);\n return candidate ? { candidate } : { error: 'invoke rejected' };\n}\n\nasync function pickHandler(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n compatible: IntentCandidate[],\n sender: string,\n): Promise<HandlerSelection> {\n const preference = request.handler;\n if (typeof preference === 'string' && preference !== 'default' && preference !== 'choose') {\n const candidate = compatible.find((item) => item.dTag === preference);\n if (!candidate || !options.authorizeExplicitHandler) return { error: 'invoke rejected' };\n let authorized = false;\n try {\n authorized = await options.authorizeExplicitHandler(\n sender,\n preference,\n request,\n candidate,\n );\n } catch {\n return { error: 'invoke rejected' };\n }\n return authorized ? { candidate } : { error: 'invoke rejected' };\n }\n\n if (preference === 'choose') {\n return chooseCompatible(options, request, compatible, sender);\n }\n\n const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);\n if (preference === 'default') {\n return defaultCandidate ? { candidate: defaultCandidate } : { error: 'invoke rejected' };\n }\n if (defaultCandidate) return { candidate: defaultCandidate };\n if (compatible.length === 1) return { candidate: compatible[0] };\n return chooseCompatible(options, request, compatible, sender);\n}\n\nasync function invokeFromCatalog(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n context: IntentResolverContext,\n): Promise<IntentResult> {\n const catalog = await options.loadCatalog();\n const defaultHandler = options.getDefaultHandler?.(request.archetype);\n const candidates = candidatesFor(catalog, request.archetype, defaultHandler);\n if (candidates.length === 0) return reject(request, 'no handler');\n\n const action = request.action ?? 'open';\n const actionCompatible = candidates.filter((candidate) =>\n candidate.actions.includes(action));\n if (actionCompatible.length === 0) return reject(request, 'unsupported action');\n const compatible = request.convention === undefined\n ? actionCompatible\n : actionCompatible.filter((candidate) =>\n candidate.conventions.includes(request.convention as string));\n if (compatible.length === 0) return reject(request, 'unsupported convention');\n\n const sender = context.sender;\n if (typeof sender !== 'string' || sender.length === 0) {\n return reject(request, 'invoke rejected');\n }\n const selected = await pickHandler(options, request, compatible, sender);\n if ('error' in selected) return reject(request, selected.error);\n\n const convention = request.convention\n ?? selected.candidate.conventions.find((value) => {\n const match = /^napplet:[^/?#\\s]+\\/([^/?#\\s]+)$/.exec(value);\n return match?.[1] === action;\n });\n if (!convention) return reject(request, 'unsupported convention');\n const behavior = request.behavior === undefined\n ? undefined\n : Object.freeze({\n ...(request.behavior.focus === undefined ? {} : { focus: request.behavior.focus }),\n ...(request.behavior.newWindow === undefined\n ? {}\n : { newWindow: request.behavior.newWindow }),\n ...(request.behavior.reuse === undefined ? {} : { reuse: request.behavior.reuse }),\n });\n const params = Object.freeze({\n handler: selected.candidate.dTag,\n sender,\n archetype: request.archetype,\n action,\n convention,\n ...(request.payload === undefined ? {} : { payload: request.payload }),\n ...(behavior === undefined ? {} : { behavior }),\n }) satisfies IntentDispatchParams;\n\n let target: IntentTargetDispatch;\n try {\n target = await options.targets.dispatch(params);\n } catch {\n return reject(request, 'invoke failed');\n }\n if (!target || typeof target.windowId !== 'string' || target.windowId.length === 0) {\n return reject(request, 'invoke failed');\n }\n\n return {\n ok: true,\n archetype: request.archetype,\n action,\n handled: true,\n handler: selected.candidate.dTag,\n windowId: target.windowId,\n convention,\n };\n}\n\n/**\n * Create a catalog-backed NAP-INTENT resolver.\n *\n * @param options - Catalog loader and target controller plus optional user\n * default, chooser, and explicit-handler authorization hooks.\n * @returns A catalog-backed resolver.\n * @throws If required catalog or target-controller options are missing.\n *\n * @example\n * ```ts\n * const resolver = createCatalogIntentResolver({\n * loadCatalog: () => installedNapplets,\n * targets: { dispatch: (params) => openAndDispatch(params) },\n * getDefaultHandler: (archetype) => userDefaults[archetype],\n * });\n * ```\n */\nexport function createCatalogIntentResolver(options: CatalogIntentResolverOptions): CatalogIntentResolver {\n if (!options || typeof options.loadCatalog !== 'function') {\n throw new Error('createCatalogIntentResolver: options.loadCatalog is required');\n }\n if (!options.targets || typeof options.targets.dispatch !== 'function') {\n throw new Error('createCatalogIntentResolver: options.targets is required');\n }\n const listeners = new Set<(availability: IntentAvailability) => void>();\n const availabilityFor = (archetype: string): Promise<IntentAvailability> =>\n availabilityForCatalog(options, archetype);\n\n async function handlers(): Promise<IntentAvailability[]> {\n const catalog = await options.loadCatalog();\n const archetypes = new Set<string>();\n for (const entry of catalog) {\n for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);\n }\n return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));\n }\n\n return {\n invoke: (request, context) => invokeFromCatalog(options, request, context),\n available: availabilityFor,\n handlers,\n onChanged(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n notifyChanged(archetype) {\n if (listeners.size === 0) return;\n void availabilityFor(archetype).then((availability) => {\n for (const listener of listeners) listener(availability);\n });\n },\n };\n}\n","/**\n * manifest-intent-catalog.ts — signed-manifest → NAP-INTENT catalog adapter.\n *\n * Adapts a resolved NIP-5A/5D napplet manifest's archetype tags into an\n * {@link IntentCatalogEntry} — the shape `createCatalogIntentResolver.loadCatalog`\n * consumes. This lets NAP-INTENT availability and handler candidacy flow from\n * verified manifest tags rather than host-injected catalog data.\n *\n * To avoid a `@kehto/services → @kehto/nip` dependency cycle (services must stay\n * dependency-light and `@kehto/nip` is a lower-level NIP utility), the adapter\n * takes a minimal STRUCTURAL input {@link ManifestArchetypeInput} that the\n * `@kehto/nip/5d` `NappletManifest` satisfies by duck typing — callers pass\n * `resolved.manifest` directly without any package coupling.\n *\n * @packageDocumentation\n */\n\nimport type { IntentArchetypeSupport, IntentCatalogEntry } from './catalog-intent-resolver.js';\n\n/**\n * The structural subset of `@kehto/nip/5d` `NappletManifest` the adapter needs.\n * Intentionally a duck-typed shape so the playground (or any caller) can pass a\n * resolved manifest without importing `@kehto/nip`.\n */\nexport interface ManifestArchetypeInput {\n /** The napplet's `d` identifier. */\n dTag: string;\n /** Optional human-readable title from the manifest. */\n title?: string;\n /**\n * Ordered convention contracts from the manifest's `archetype` tags.\n */\n archetypes: Array<{ slug: string; convention: string }>;\n}\n\nfunction actionFromConvention(convention: string): string {\n const match = /^napplet:([^/?#\\s]+)\\/([^/?#\\s]+)$/.exec(convention);\n if (!match) {\n throw new TypeError('manifest archetype convention is invalid');\n }\n return match[2];\n}\n\n/**\n * Map a resolved napplet manifest's archetype data into an\n * {@link IntentCatalogEntry}.\n *\n * Repeated slugs group into one support record; action and convention arrays\n * remain stable and deduplicated.\n *\n * @param manifest - A resolved manifest's structural archetype data.\n * @returns The `IntentCatalogEntry` for `createCatalogIntentResolver`.\n *\n * @example\n * ```ts\n * manifestToIntentCatalogEntry({\n * dTag: 'profile-viewer',\n * title: 'Profile',\n * archetypes: [{ slug: 'profile', convention: 'napplet:profile/open' }],\n * });\n * // → { dTag: 'profile-viewer', title: 'Profile',\n * // archetypes: { profile: {\n * // actions: ['open'],\n * // conventions: ['napplet:profile/open'],\n * // } } }\n * ```\n */\nexport function manifestToIntentCatalogEntry(manifest: ManifestArchetypeInput): IntentCatalogEntry {\n const archetypes: Record<string, IntentArchetypeSupport> = {};\n for (const { slug, convention } of manifest.archetypes) {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {\n throw new TypeError('manifest archetype slug is invalid');\n }\n // NAP-INTENT deliberately keeps routing archetypes and payload conventions\n // orthogonal, so the convention's URI archetype need not equal this slug.\n const action = actionFromConvention(convention);\n const support = archetypes[slug] ??= {\n actions: [],\n conventions: [],\n };\n if (!support.actions.includes(action)) support.actions.push(action);\n if (!support.conventions.includes(convention)) support.conventions.push(convention);\n }\n return {\n dTag: manifest.dTag,\n ...(manifest.title === undefined ? {} : { title: manifest.title }),\n archetypes,\n };\n}\n","/**\n * cvm-service.ts — NAP-CVM (ContextVM bridge) reference service.\n *\n * Shell-side handler for the NAP-CVM wire protocol. It is a pure envelope\n * router: it validates `cvm.*` envelopes, delegates the actual ContextVM /\n * MCP-over-Nostr work to an injected {@link CvmTransport}, and posts the\n * correlated `*.result` (echoing the request `id`) back to the napplet.\n *\n * The transport is injected (options-as-bridge) so this service has no Nostr\n * dependency and is fully unit-testable. A concrete ContextVM transport ships\n * separately at `@kehto/services/cvm-nostr-transport`.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: cvm.discover, cvm.request, cvm.close\n * Outbound: cvm.discover.result, cvm.request.result, cvm.close.result,\n * cvm.event (server-pushed MCP notifications)\n *\n * MCP-level errors are returned inside `request.result.message.error`;\n * transport/shell-policy failures are returned in the envelope `error` field.\n *\n * `cvm.event` is fanned out to every window that holds an active session with\n * the originating server (a window opens a session by issuing a `cvm.request`\n * and closes it via `cvm.close` or window teardown).\n *\n * @example\n * ```ts\n * import { createCvmService } from '@kehto/services';\n * import { createNostrCvmTransport } from '@kehto/services/cvm-nostr-transport';\n *\n * const transport = createNostrCvmTransport({ defaultRelays: ['wss://relay.contextvm.org'] });\n * runtime.registerService('cvm', createCvmService({ transport }));\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n CvmDiscoverQuery,\n CvmRequestOptions,\n CvmServer,\n CvmServerRef,\n McpMessage,\n} from './cvm-types.js';\n\n/** CVM service version — follows semver. */\nconst CVM_SERVICE_VERSION = '1.0.0';\n\n/**\n * Abstract ContextVM transport. Implementors own Nostr relay access, signing,\n * encryption (CEP-4 gift wrap), JSON-RPC correlation, and MCP initialization.\n */\nexport interface CvmTransport {\n /** Resolve public ContextVM server announcements matching the query. */\n discover(query?: CvmDiscoverQuery): Promise<CvmServer[]>;\n /** Send a raw MCP message to a server and resolve with the MCP response. */\n request(server: CvmServerRef, message: McpMessage, options?: CvmRequestOptions): Promise<McpMessage>;\n /** Release any session state held for a server (subscriptions, init cache). */\n close(server: CvmServerRef): Promise<void>;\n /**\n * Subscribe to server-pushed MCP messages not correlated to a single\n * request (e.g. notifications). Returns a handle whose `close()` detaches.\n */\n onEvent(handler: (server: CvmServerRef, message: McpMessage) => void): { close(): void };\n /** Release all transport resources when the owning service is disposed. */\n dispose?(): void;\n}\n\n/** Options for {@link createCvmService}. */\nexport interface CvmServiceOptions {\n /** The ContextVM transport the shell uses to reach servers. Required. */\n transport: CvmTransport;\n}\n\n/** The created CVM service, exposing the handler plus a disposal hook. */\nexport interface CvmService extends ServiceHandler {\n /** Detach the transport event subscription. Idempotent. */\n dispose(): void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst CVM_DESCRIPTOR: ServiceDescriptor = {\n name: 'cvm',\n version: CVM_SERVICE_VERSION,\n description: 'NAP-CVM ContextVM bridge — MCP over Nostr',\n};\n\n/**\n * Create the NAP-CVM service handler.\n *\n * @param options - Must provide a {@link CvmTransport}.\n * @returns A {@link CvmService} (a `ServiceHandler` with a `dispose()` hook).\n * @throws If `options.transport` is missing.\n */\nexport function createCvmService(options: CvmServiceOptions): CvmService {\n if (!options || typeof options.transport !== 'object' || options.transport === null) {\n throw new Error('createCvmService: options.transport is required');\n }\n const { transport } = options;\n\n // Per-window send callbacks, captured at request time for cvm.event fan-out.\n const sendByWindow = new Map<string, Send>();\n // serverPubkey -> set of windowIds with an active session.\n const windowsByServer = new Map<string, Set<string>>();\n\n function openSession(windowId: string, server: CvmServerRef, send: Send): void {\n sendByWindow.set(windowId, send);\n let windows = windowsByServer.get(server.pubkey);\n if (!windows) {\n windows = new Set<string>();\n windowsByServer.set(server.pubkey, windows);\n }\n windows.add(windowId);\n }\n\n function closeSession(windowId: string, serverPubkey: string): void {\n const windows = windowsByServer.get(serverPubkey);\n if (windows) {\n windows.delete(windowId);\n if (windows.size === 0) windowsByServer.delete(serverPubkey);\n }\n }\n\n const eventSub = transport.onEvent((server, message) => {\n const windows = windowsByServer.get(server.pubkey);\n if (!windows) return;\n for (const windowId of windows) {\n const send = sendByWindow.get(windowId);\n send?.({ type: 'cvm.event', server, message } as NappletMessage);\n }\n });\n\n function handleDiscover(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; query?: CvmDiscoverQuery };\n const id = m.id ?? '';\n void transport\n .discover(m.query)\n .then((servers) => send({ type: 'cvm.discover.result', id, servers } as NappletMessage))\n .catch((err) =>\n send({ type: 'cvm.discover.result', id, servers: [], error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleRequest(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & {\n id?: string;\n server?: CvmServerRef;\n message?: McpMessage;\n options?: CvmRequestOptions;\n };\n const id = m.id ?? '';\n if (!m.server || typeof m.server.pubkey !== 'string' || m.server.pubkey.length === 0) {\n send({ type: 'cvm.request.result', id, error: 'server not found' } as NappletMessage);\n return;\n }\n if (!m.message || typeof m.message !== 'object') {\n send({ type: 'cvm.request.result', id, error: 'unsupported method' } as NappletMessage);\n return;\n }\n openSession(windowId, m.server, send);\n void transport\n .request(m.server, m.message, m.options)\n .then((message) => send({ type: 'cvm.request.result', id, message } as NappletMessage))\n .catch((err) =>\n send({ type: 'cvm.request.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleClose(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; server?: CvmServerRef };\n const id = m.id ?? '';\n if (!m.server || typeof m.server.pubkey !== 'string') {\n send({ type: 'cvm.close.result', id, error: 'server not found' } as NappletMessage);\n return;\n }\n closeSession(windowId, m.server.pubkey);\n void transport\n .close(m.server)\n .then(() => send({ type: 'cvm.close.result', id } as NappletMessage))\n .catch((err) => send({ type: 'cvm.close.result', id, error: toErrorMessage(err) } as NappletMessage));\n }\n\n return {\n descriptor: CVM_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'cvm.discover':\n handleDiscover(message, send);\n return;\n case 'cvm.request':\n handleRequest(windowId, message, send);\n return;\n case 'cvm.close':\n handleClose(windowId, message, send);\n return;\n default:\n // Unknown cvm.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n sendByWindow.delete(windowId);\n for (const [pubkey, windows] of windowsByServer) {\n windows.delete(windowId);\n if (windows.size === 0) {\n windowsByServer.delete(pubkey);\n void transport.close({ pubkey }).catch(() => {});\n }\n }\n },\n dispose(): void {\n eventSub.close();\n transport.dispose?.();\n sendByWindow.clear();\n windowsByServer.clear();\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'cvm request failed';\n}\n","/**\n * link-service.ts — NAP-LINK reference service.\n *\n * Shell-mediated user-visible navigation. The napplet requests `link.open`;\n * the shell validates policy and hands navigation to a host-owned context.\n */\n\nimport type { LinkOpenErrorCode, LinkOpenOptions, LinkOpenResult, NappletMessage } from '@napplet/core';\nimport type { LinkOpenMessage, LinkOpenResultMessage } from '@napplet/nap/link/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst LINK_SERVICE_VERSION = '1.0.0';\nconst DEFAULT_ALLOWED_PROTOCOLS = ['http:', 'https:'] as const;\n\n/**\n * Host context passed to {@link LinkServiceOptions.open}.\n */\nexport interface LinkOpenContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Parsed absolute URL after validation. */\n url: URL;\n /** Optional untrusted napplet display hints. */\n options?: LinkOpenOptions;\n}\n\n/**\n * Options for {@link createLinkService}.\n */\nexport interface LinkServiceOptions {\n /**\n * Protocols allowed before host policy runs. Defaults to `http:` and `https:`.\n */\n allowedProtocols?: readonly string[];\n /**\n * Host-owned opener. Return `{ status: \"opened\" }` after handing navigation\n * to the host context, or `{ status: \"denied\" }` to reject by policy/user.\n * When omitted, all otherwise-valid URLs are denied.\n */\n open?: (context: LinkOpenContext) => LinkOpenResult | Promise<LinkOpenResult>;\n}\n\nfunction denied(id: string, error: LinkOpenErrorCode): LinkOpenResultMessage {\n return { type: 'link.open.result', id, status: 'denied', error };\n}\n\nfunction parseLinkUrl(id: string, rawUrl: string): URL | LinkOpenResultMessage {\n try {\n return new URL(rawUrl);\n } catch {\n return denied(id, 'invalid-url');\n }\n}\n\nfunction isProtocolAllowed(url: URL, allowedProtocols: readonly string[]): boolean {\n return allowedProtocols.includes(url.protocol);\n}\n\nasync function handleOpen(\n options: LinkServiceOptions,\n windowId: string,\n message: LinkOpenMessage,\n send: (msg: NappletMessage) => void,\n): Promise<void> {\n const parsed = parseLinkUrl(message.id, message.url);\n if ('type' in parsed) {\n send(parsed as NappletMessage);\n return;\n }\n\n const allowedProtocols = options.allowedProtocols ?? DEFAULT_ALLOWED_PROTOCOLS;\n if (!isProtocolAllowed(parsed, allowedProtocols)) {\n send(denied(message.id, 'unsupported-scheme') as NappletMessage);\n return;\n }\n\n if (!options.open) {\n send(denied(message.id, 'blocked-by-policy') as NappletMessage);\n return;\n }\n\n try {\n const result = await options.open({ windowId, url: parsed, options: message.options });\n send({\n type: 'link.open.result',\n id: message.id,\n status: result.status,\n ...(result.status === 'denied' ? { error: 'blocked-by-policy' as LinkOpenErrorCode } : {}),\n } as NappletMessage);\n } catch {\n send(denied(message.id, 'blocked-by-policy') as NappletMessage);\n }\n}\n\n/**\n * Create the NAP-LINK reference service.\n *\n * @param options - Host policy and opener hooks for link.open requests.\n * @returns A runtime service handler for the `link` domain.\n *\n * @example\n * ```ts\n * const service = createLinkService({\n * open: ({ url }) => ({ status: url.protocol === 'https:' ? 'opened' : 'denied' }),\n * });\n * ```\n */\nexport function createLinkService(options: LinkServiceOptions = {}): ServiceHandler {\n const descriptor: ServiceDescriptor = {\n name: 'link',\n version: LINK_SERVICE_VERSION,\n description: 'NAP-LINK reference handler for shell-mediated link opening',\n };\n\n return {\n descriptor,\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (message.type === 'link.open') {\n void handleOpen(options, windowId, message as LinkOpenMessage, send);\n return;\n }\n\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown link method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n","/**\n * lists-service.ts — NAP-LISTS reference service.\n *\n * Shell-mediated NIP-51 list metadata and mutation hooks. The napplet requests\n * `lists.*`; the shell owns list lookup, validation, signing, publishing, and\n * private-item policy.\n */\n\nimport type {\n ListItem,\n ListMutationResult,\n ListOptions,\n ListRef,\n ListSupport,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n ListsAddMessage,\n ListsRemoveMessage,\n} from '@napplet/nap/lists/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst LISTS_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-LISTS hooks. */\nexport interface ListsServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n}\n\n/** Options for {@link createListsService}. */\nexport interface ListsServiceOptions {\n /** Host-owned supported list metadata. */\n supported?: (context: ListsServiceContext) => readonly ListSupport[] | Promise<readonly ListSupport[]>;\n /** Host-owned add mutation. */\n add?: (\n list: ListRef,\n items: readonly ListItem[],\n options: ListOptions | undefined,\n context: ListsServiceContext,\n ) => ListMutationResult | Promise<ListMutationResult>;\n /** Host-owned remove mutation. */\n remove?: (\n list: ListRef,\n items: readonly ListItem[],\n options: ListOptions | undefined,\n context: ListsServiceContext,\n ) => ListMutationResult | Promise<ListMutationResult>;\n}\n\ntype Send = (msg: NappletMessage) => void;\ntype MutationHook = NonNullable<ListsServiceOptions['add'] | ListsServiceOptions['remove']>;\n\nconst LISTS_DESCRIPTOR: ServiceDescriptor = {\n name: 'lists',\n version: LISTS_SERVICE_VERSION,\n description: 'NAP-LISTS reference handler for shell-mediated NIP-51 list mutations',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'lists request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'lists request failed'))));\n}\n\nfunction unsupportedMutation(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n ok: false,\n error: 'unsupported',\n reason: `${resultType.replace('.result', '')} unavailable`,\n supported: [],\n } as NappletMessage;\n}\n\nfunction settleMutation(\n hook: MutationHook | undefined,\n message: ListsAddMessage | ListsRemoveMessage,\n context: ListsServiceContext,\n send: Send,\n resultType: 'lists.add.result' | 'lists.remove.result',\n): void {\n if (!hook) {\n send(unsupportedMutation(resultType, message.id));\n return;\n }\n settle(\n () => hook(message.list, message.items, message.options, context),\n send,\n (error) => ({ type: resultType, id: message.id, ok: false, error: 'list-unavailable', reason: error } as NappletMessage),\n (result) => ({ type: resultType, id: message.id, ...result } as NappletMessage),\n );\n}\n\n/**\n * Create the NAP-LISTS reference service.\n *\n * @param options - Host list metadata and mutation hooks for lists.* requests.\n * @returns A runtime service handler for the `lists` domain.\n */\nexport function createListsService(options: ListsServiceOptions = {}): ServiceHandler {\n return {\n descriptor: LISTS_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = { windowId };\n\n if (message.type === 'lists.supported') {\n if (!options.supported) {\n send({ type: 'lists.supported.result', id, lists: [] } as NappletMessage);\n return;\n }\n settle(\n () => options.supported!(context),\n send,\n (error) => ({ type: 'lists.supported.result', id, error } as NappletMessage),\n (lists) => ({ type: 'lists.supported.result', id, lists: [...lists] } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'lists.add') {\n settleMutation(options.add, message as ListsAddMessage, context, send, 'lists.add.result');\n return;\n }\n\n if (message.type === 'lists.remove') {\n settleMutation(options.remove, message as ListsRemoveMessage, context, send, 'lists.remove.result');\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown lists method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n","/**\n * serial-service.ts — NAP-SERIAL reference service.\n *\n * Shell-mediated serial sessions. The napplet requests `serial.*`; the shell\n * owns device selection, permissions, session handles, reads, write ordering,\n * close events, and policy.\n */\n\nimport type {\n NappletMessage,\n SerialEvent,\n SerialOpenRequest,\n SerialOpenResult,\n} from '@napplet/core';\nimport type {\n SerialCloseMessage,\n SerialOpenMessage,\n SerialWriteMessage,\n} from '@napplet/nap/serial/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst SERIAL_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-SERIAL hooks. */\nexport interface SerialServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned serial event back to the requesting napplet. */\n emit(event: SerialEvent): void;\n}\n\n/** Options for {@link createSerialService}. */\nexport interface SerialServiceOptions {\n /** Host-owned serial open hook. */\n open?: (\n request: SerialOpenRequest,\n context: SerialServiceContext,\n ) => SerialOpenResult | Promise<SerialOpenResult>;\n /** Host-owned serial write hook. */\n write?: (\n sessionId: string,\n data: readonly number[],\n context: SerialServiceContext,\n ) => void | Promise<void>;\n /** Host-owned serial close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: SerialServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\ninterface SerialConnection {\n state: 'opening' | 'open' | 'closed';\n readonly bufferedEvents: SerialEvent[];\n}\n\ninterface SerialWindow {\n readonly connections: Map<string, SerialConnection>;\n readonly pendingConnections: Set<SerialConnection>;\n}\n\nconst SERIAL_DESCRIPTOR: ServiceDescriptor = {\n name: 'serial',\n version: SERIAL_SERVICE_VERSION,\n description: 'NAP-SERIAL reference handler for shell-mediated serial sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'serial request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'serial request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, emit: (event: SerialEvent) => void): SerialServiceContext {\n return {\n windowId,\n emit,\n };\n}\n\n/**\n * Create the NAP-SERIAL reference service.\n *\n * @param options - Host serial session hooks for serial.* requests.\n * @returns A runtime service handler for the `serial` domain.\n */\nexport function createSerialService(options: SerialServiceOptions = {}): ServiceHandler {\n const windows = new Map<string, SerialWindow>();\n\n const currentWindow = (windowId: string): SerialWindow => {\n const existing = windows.get(windowId);\n if (existing) return existing;\n const record: SerialWindow = {\n connections: new Map<string, SerialConnection>(),\n pendingConnections: new Set<SerialConnection>(),\n };\n windows.set(windowId, record);\n return record;\n };\n\n const emit = (\n windowId: string,\n record: SerialWindow,\n connection: SerialConnection | undefined,\n send: Send,\n event: SerialEvent,\n ): void => {\n if (!connection || windows.get(windowId) !== record || connection.state === 'closed') return;\n if (connection.state === 'opening') {\n connection.bufferedEvents.push(event);\n return;\n }\n send({ type: 'serial.event', event } as NappletMessage);\n };\n\n return {\n descriptor: SERIAL_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const record = currentWindow(windowId);\n const isCurrent = (): boolean => windows.get(windowId) === record;\n const currentSend: Send = (outbound) => {\n if (isCurrent()) send(outbound);\n };\n if (message.type === 'serial.open') {\n if (!options.open) {\n currentSend(unsupported('serial.open.result', id));\n return;\n }\n const serialMessage = message as SerialOpenMessage;\n const connection: SerialConnection = { state: 'opening', bufferedEvents: [] };\n record.pendingConnections.add(connection);\n const context = createContext(windowId, (event) => emit(windowId, record, connection, currentSend, event));\n let pending: Promise<SerialOpenResult>;\n try {\n pending = Promise.resolve(options.open(serialMessage.request, context));\n } catch (error) {\n record.pendingConnections.delete(connection);\n connection.state = 'closed';\n currentSend({\n type: 'serial.open.result',\n id,\n error: errorMessage(error, 'serial request failed'),\n } as NappletMessage);\n return;\n }\n void pending\n .then((result) => {\n record.pendingConnections.delete(connection);\n if (!isCurrent() || connection.state === 'closed') return;\n connection.state = 'open';\n record.connections.set(result.session.id, connection);\n currentSend({ type: 'serial.open.result', id, session: result.session } as NappletMessage);\n if (!isCurrent() || connection.state !== 'open') return;\n for (const event of connection.bufferedEvents) {\n if (!isCurrent() || connection.state !== 'open') break;\n currentSend({ type: 'serial.event', event } as NappletMessage);\n }\n connection.bufferedEvents.length = 0;\n })\n .catch((error) => {\n record.pendingConnections.delete(connection);\n connection.state = 'closed';\n currentSend({\n type: 'serial.open.result',\n id,\n error: errorMessage(error, 'serial request failed'),\n } as NappletMessage);\n });\n return;\n }\n\n if (message.type === 'serial.write') {\n if (!options.write) {\n currentSend(unsupported('serial.write.result', id));\n return;\n }\n const serialMessage = message as SerialWriteMessage;\n const context = createContext(windowId, (event) => emit(\n windowId,\n record,\n record.connections.get(serialMessage.sessionId),\n currentSend,\n event,\n ));\n settle(\n () => options.write!(serialMessage.sessionId, serialMessage.data, context),\n currentSend,\n (error) => ({ type: 'serial.write.result', id, error } as NappletMessage),\n () => ({ type: 'serial.write.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'serial.close') {\n if (!options.close) {\n currentSend(unsupported('serial.close.result', id));\n return;\n }\n const serialMessage = message as SerialCloseMessage;\n const connection = record.connections.get(serialMessage.sessionId);\n if (connection) {\n connection.state = 'closed';\n record.connections.delete(serialMessage.sessionId);\n }\n const context = createContext(windowId, (event) => emit(\n windowId,\n record,\n connection,\n currentSend,\n event,\n ));\n settle(\n () => options.close!(serialMessage.sessionId, serialMessage.reason, context),\n currentSend,\n (error) => ({ type: 'serial.close.result', id, error } as NappletMessage),\n () => ({ type: 'serial.close.result', id } as NappletMessage),\n );\n return;\n }\n\n currentSend({\n type: `${message.type}.error`,\n id,\n error: `Unknown serial method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n const record = windows.get(windowId);\n if (record) {\n for (const connection of record.connections.values()) connection.state = 'closed';\n for (const connection of record.pendingConnections) connection.state = 'closed';\n record.connections.clear();\n record.pendingConnections.clear();\n windows.delete(windowId);\n }\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * ble-service.ts — NAP-BLE reference service.\n *\n * Shell-mediated BLE/GATT sessions. The napplet requests `ble.*`; the shell\n * owns chooser UI, permissions, GATT handles, notifications, and policy.\n */\n\nimport type {\n BleAttribute,\n BleEvent,\n BleOpenRequest,\n BleOpenResult,\n BleService,\n BleWriteOptions,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n BleCloseMessage,\n BleOpenMessage,\n BleReadMessage,\n BleServicesMessage,\n BleSubscribeMessage,\n BleUnsubscribeMessage,\n BleWriteMessage,\n} from '@napplet/nap/ble/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst BLE_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-BLE hooks. */\nexport interface BleServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned BLE event back to the requesting napplet. */\n emit(event: BleEvent): void;\n}\n\n/** Options for {@link createBleService}. */\nexport interface BleServiceOptions {\n /** Host-owned BLE open hook. */\n open?: (\n request: BleOpenRequest,\n context: BleServiceContext,\n ) => BleOpenResult | Promise<BleOpenResult>;\n /** Host-owned GATT service listing hook. */\n services?: (\n sessionId: string,\n context: BleServiceContext,\n ) => BleService[] | Promise<BleService[]>;\n /** Host-owned GATT read hook. */\n read?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => number[] | Promise<number[]>;\n /** Host-owned GATT write hook. */\n write?: (\n sessionId: string,\n target: BleAttribute,\n data: readonly number[],\n options: BleWriteOptions | undefined,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned notification subscription hook. */\n subscribe?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned notification unsubscribe hook. */\n unsubscribe?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned BLE close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst BLE_DESCRIPTOR: ServiceDescriptor = {\n name: 'ble',\n version: BLE_SERVICE_VERSION,\n description: 'NAP-BLE reference handler for shell-mediated BLE/GATT sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'ble request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'ble request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, send: Send): BleServiceContext {\n return {\n windowId,\n emit(event) {\n send({ type: 'ble.event', event } as NappletMessage);\n },\n };\n}\n\n/**\n * Create the NAP-BLE reference service.\n *\n * @param options - Host BLE session hooks for ble.* requests.\n * @returns A runtime service handler for the `ble` domain.\n */\nexport function createBleService(options: BleServiceOptions = {}): ServiceHandler {\n return {\n descriptor: BLE_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = createContext(windowId, send);\n\n if (message.type === 'ble.open') {\n if (!options.open) {\n send(unsupported('ble.open.result', id));\n return;\n }\n const bleMessage = message as BleOpenMessage;\n settle(\n () => options.open!(bleMessage.request, context),\n send,\n (error) => ({ type: 'ble.open.result', id, error } as NappletMessage),\n (result) => ({ type: 'ble.open.result', id, session: result.session } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.services') {\n if (!options.services) {\n send(unsupported('ble.services.result', id));\n return;\n }\n const bleMessage = message as BleServicesMessage;\n settle(\n () => options.services!(bleMessage.sessionId, context),\n send,\n (error) => ({ type: 'ble.services.result', id, error } as NappletMessage),\n (services) => ({ type: 'ble.services.result', id, services } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.read') {\n if (!options.read) {\n send(unsupported('ble.read.result', id));\n return;\n }\n const bleMessage = message as BleReadMessage;\n settle(\n () => options.read!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.read.result', id, error } as NappletMessage),\n (data) => ({ type: 'ble.read.result', id, data } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.write') {\n if (!options.write) {\n send(unsupported('ble.write.result', id));\n return;\n }\n const bleMessage = message as BleWriteMessage;\n settle(\n () => options.write!(bleMessage.sessionId, bleMessage.target, bleMessage.data, bleMessage.options, context),\n send,\n (error) => ({ type: 'ble.write.result', id, error } as NappletMessage),\n () => ({ type: 'ble.write.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.subscribe') {\n if (!options.subscribe) {\n send(unsupported('ble.subscribe.result', id));\n return;\n }\n const bleMessage = message as BleSubscribeMessage;\n settle(\n () => options.subscribe!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.subscribe.result', id, error } as NappletMessage),\n () => ({ type: 'ble.subscribe.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.unsubscribe') {\n if (!options.unsubscribe) {\n send(unsupported('ble.unsubscribe.result', id));\n return;\n }\n const bleMessage = message as BleUnsubscribeMessage;\n settle(\n () => options.unsubscribe!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.unsubscribe.result', id, error } as NappletMessage),\n () => ({ type: 'ble.unsubscribe.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.close') {\n if (!options.close) {\n send(unsupported('ble.close.result', id));\n return;\n }\n const bleMessage = message as BleCloseMessage;\n settle(\n () => options.close!(bleMessage.sessionId, bleMessage.reason, context),\n send,\n (error) => ({ type: 'ble.close.result', id, error } as NappletMessage),\n () => ({ type: 'ble.close.result', id } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown ble method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * webrtc-service.ts — NAP-WEBRTC reference service.\n *\n * Shell-mediated WebRTC sessions. The napplet requests `webrtc.*`; the shell\n * owns signaling, signing/encryption, SDP, ICE, peer connections, and policy.\n */\n\nimport type {\n NappletMessage,\n WebrtcEvent,\n WebrtcOpenRequest,\n WebrtcOpenResult,\n} from '@napplet/core';\nimport type {\n WebrtcCloseMessage,\n WebrtcOpenMessage,\n WebrtcSendMessage,\n} from '@napplet/nap/webrtc/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst WEBRTC_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-WEBRTC hooks. */\nexport interface WebrtcServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned WebRTC event back to the requesting napplet. */\n emit(event: WebrtcEvent): void;\n}\n\n/** Options for {@link createWebrtcService}. */\nexport interface WebrtcServiceOptions {\n /** Host-owned WebRTC open hook. */\n open?: (\n request: WebrtcOpenRequest,\n context: WebrtcServiceContext,\n ) => WebrtcOpenResult | Promise<WebrtcOpenResult>;\n /** Host-owned WebRTC send hook. */\n send?: (\n sessionId: string,\n payload: unknown,\n context: WebrtcServiceContext,\n ) => void | Promise<void>;\n /** Host-owned WebRTC close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: WebrtcServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst WEBRTC_DESCRIPTOR: ServiceDescriptor = {\n name: 'webrtc',\n version: WEBRTC_SERVICE_VERSION,\n description: 'NAP-WEBRTC reference handler for shell-mediated WebRTC sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'webrtc request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'webrtc request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, send: Send): WebrtcServiceContext {\n return {\n windowId,\n emit(event) {\n send({ type: 'webrtc.event', event } as NappletMessage);\n },\n };\n}\n\n/**\n * Create the NAP-WEBRTC reference service.\n *\n * @param options - Host WebRTC session hooks for webrtc.* requests.\n * @returns A runtime service handler for the `webrtc` domain.\n */\nexport function createWebrtcService(options: WebrtcServiceOptions = {}): ServiceHandler {\n return {\n descriptor: WEBRTC_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = createContext(windowId, send);\n\n if (message.type === 'webrtc.open') {\n if (!options.open) {\n send(unsupported('webrtc.open.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcOpenMessage;\n settle(\n () => options.open!(webrtcMessage.request, context),\n send,\n (error) => ({ type: 'webrtc.open.result', id, error } as NappletMessage),\n (result) => ({ type: 'webrtc.open.result', id, session: result.session } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'webrtc.send') {\n if (!options.send) {\n send(unsupported('webrtc.send.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcSendMessage;\n settle(\n () => options.send!(webrtcMessage.sessionId, webrtcMessage.payload, context),\n send,\n (error) => ({ type: 'webrtc.send.result', id, error } as NappletMessage),\n () => ({ type: 'webrtc.send.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'webrtc.close') {\n if (!options.close) {\n send(unsupported('webrtc.close.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcCloseMessage;\n settle(\n () => options.close!(webrtcMessage.sessionId, webrtcMessage.reason, context),\n send,\n (error) => ({ type: 'webrtc.close.result', id, error } as NappletMessage),\n () => ({ type: 'webrtc.close.result', id } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown webrtc method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * common-service.ts — NAP-COMMON reference service.\n *\n * Shell-mediated public NIP-19 helpers and common social actions. The napplet\n * requests `common.*`; the shell owns identity, consent, event construction,\n * signing, publishing, relay access, and NIP-19 handling.\n */\n\nimport type {\n CommonActionResult,\n CommonFollowsResult,\n CommonNip19DecodeResult,\n CommonNip19EncodeInput,\n CommonNip19EncodeResult,\n CommonProfileResult,\n CommonProfileTarget,\n CommonReaction,\n CommonReportReason,\n CommonReportTarget,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n CommonDecodeNip19Message,\n CommonEncodeNip19Message,\n CommonFollowMessage,\n CommonGetProfileMessage,\n CommonReactMessage,\n CommonReportMessage,\n CommonUnfollowMessage,\n} from '@napplet/nap/common/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport {\n decode,\n encodeBytes,\n naddrEncode,\n neventEncode,\n noteEncode,\n nprofileEncode,\n npubEncode,\n} from 'nostr-tools/nip19';\n\nconst COMMON_SERVICE_VERSION = '1.0.0';\nconst BECH32_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\n/** Context passed to host-provided NAP-COMMON hooks. */\nexport interface CommonServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n}\n\n/** Options for {@link createCommonService}. */\nexport interface CommonServiceOptions {\n /** Host-owned profile lookup. */\n getProfile?: (target: CommonProfileTarget, context: CommonServiceContext) => CommonProfileResult | Promise<CommonProfileResult>;\n /** Host-owned current-user follows lookup. */\n follows?: (context: CommonServiceContext) => CommonFollowsResult | Promise<CommonFollowsResult>;\n /** Host-owned follow action. */\n follow?: (pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned unfollow action. */\n unfollow?: (pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned reaction action. */\n react?: (\n targetEventId: string,\n reaction: CommonReaction,\n customEmojiHref: string | undefined,\n context: CommonServiceContext,\n ) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned report action. */\n report?: (\n target: CommonReportTarget,\n reason: CommonReportReason,\n text: string,\n context: CommonServiceContext,\n ) => CommonActionResult | Promise<CommonActionResult>;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst COMMON_DESCRIPTOR: ServiceDescriptor = {\n name: 'common',\n version: COMMON_SERVICE_VERSION,\n description: 'NAP-COMMON reference handler for shell-mediated social helpers',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction encodeNip19(input: CommonNip19EncodeInput): CommonNip19EncodeResult {\n try {\n switch (input.type) {\n case 'npub':\n return { ok: true, value: npubEncode(input.hex), nip19Type: input.type };\n case 'note':\n return { ok: true, value: noteEncode(input.hex), nip19Type: input.type };\n case 'nprofile':\n return { ok: true, value: nprofileEncode({ pubkey: input.pubkey, relays: input.relays }), nip19Type: input.type };\n case 'nevent':\n return {\n ok: true,\n value: neventEncode({ id: input.eventId, relays: input.relays, author: input.author, kind: input.kind }),\n nip19Type: input.type,\n };\n case 'naddr':\n return {\n ok: true,\n value: naddrEncode({ identifier: input.identifier, pubkey: input.pubkey, kind: input.kind, relays: input.relays }),\n nip19Type: input.type,\n };\n case 'nrelay':\n return { ok: true, value: encodeBytes('nrelay', new TextEncoder().encode(input.relay)), nip19Type: input.type };\n default:\n return { ok: false, error: 'unsupported NIP-19 type' };\n }\n } catch (err) {\n return { ok: false, error: errorMessage(err, 'NIP-19 encode failed') };\n }\n}\n\nfunction decodeNip19Value(value: string): CommonNip19DecodeResult {\n try {\n const decoded = decode(value);\n switch (decoded.type) {\n case 'npub':\n case 'note':\n return { ok: true, nip19Type: decoded.type, hex: decoded.data };\n case 'nprofile':\n return { ok: true, nip19Type: decoded.type, pubkey: decoded.data.pubkey, relays: decoded.data.relays };\n case 'nevent':\n return {\n ok: true,\n nip19Type: decoded.type,\n eventId: decoded.data.id,\n relays: decoded.data.relays,\n author: decoded.data.author,\n kind: decoded.data.kind,\n };\n case 'naddr':\n return {\n ok: true,\n nip19Type: decoded.type,\n identifier: decoded.data.identifier,\n pubkey: decoded.data.pubkey,\n kind: decoded.data.kind,\n relays: decoded.data.relays,\n };\n default:\n return { ok: false, error: 'unsupported NIP-19 type' };\n }\n } catch (err) {\n if (value.startsWith('nrelay1')) return decodeNrelay(value);\n return { ok: false, error: errorMessage(err, 'NIP-19 decode failed') };\n }\n}\n\nfunction decodeNrelay(value: string): CommonNip19DecodeResult {\n try {\n const separator = value.lastIndexOf('1');\n if (separator <= 0 || separator + 7 > value.length) return { ok: false, error: 'invalid nrelay value' };\n const dataPart = value.slice(separator + 1);\n const words = [...dataPart].map((char) => {\n const index = BECH32_CHARSET.indexOf(char);\n if (index === -1) throw new Error('invalid nrelay character');\n return index;\n });\n const payloadWords = words.slice(0, -6);\n const bytes = convertBits(payloadWords, 5, 8, false);\n return { ok: true, nip19Type: 'nrelay', relay: new TextDecoder().decode(Uint8Array.from(bytes)) };\n } catch (err) {\n return { ok: false, error: errorMessage(err, 'invalid nrelay value') };\n }\n}\n\nfunction convertBits(data: readonly number[], fromBits: number, toBits: number, pad: boolean): number[] {\n let acc = 0;\n let bits = 0;\n const result: number[] = [];\n const maxv = (1 << toBits) - 1;\n const maxAcc = (1 << (fromBits + toBits - 1)) - 1;\n for (const value of data) {\n if (value < 0 || value >> fromBits !== 0) throw new Error('invalid bech32 data');\n acc = ((acc << fromBits) | value) & maxAcc;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n result.push((acc >> bits) & maxv);\n }\n }\n if (pad) {\n if (bits > 0) result.push((acc << (toBits - bits)) & maxv);\n } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) !== 0) {\n throw new Error('invalid bech32 padding');\n }\n return result;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'common request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'common request failed'))));\n}\n\n/**\n * Create the NAP-COMMON reference service.\n *\n * @param options - Host profile/social hooks for common.* requests.\n * @returns A runtime service handler for the `common` domain.\n */\nexport function createCommonService(options: CommonServiceOptions = {}): ServiceHandler {\n return {\n descriptor: COMMON_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = { windowId };\n\n if (message.type === 'common.encodeNip19') {\n const input = (message as CommonEncodeNip19Message).input;\n const result = input ? encodeNip19(input) : { ok: false, error: 'invalid input' };\n send({ type: 'common.encodeNip19.result', id, ...result } as NappletMessage);\n return;\n }\n\n if (message.type === 'common.decodeNip19') {\n const value = (message as CommonDecodeNip19Message).value;\n const result = typeof value === 'string' ? decodeNip19Value(value) : { ok: false, error: 'invalid value' };\n send({ type: 'common.decodeNip19.result', id, ...result } as NappletMessage);\n return;\n }\n\n if (message.type === 'common.getProfile') {\n const target = (message as CommonGetProfileMessage).target;\n if (!options.getProfile) {\n send({ type: 'common.getProfile.result', id, ok: false, pubkey: target ?? '', error: 'profile lookup unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.getProfile!(target, context),\n send,\n (error) => ({ type: 'common.getProfile.result', id, ok: false, pubkey: target ?? '', error } as NappletMessage),\n (result) => ({ type: 'common.getProfile.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.follows') {\n if (!options.follows) {\n send({ type: 'common.follows.result', id, ok: false, pubkeys: [], error: 'follows lookup unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.follows!(context),\n send,\n (error) => ({ type: 'common.follows.result', id, ok: false, pubkeys: [], error } as NappletMessage),\n (result) => ({ type: 'common.follows.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.follow') {\n const pubkeys = (message as CommonFollowMessage).pubkeys ?? [];\n settleAction(options.follow, pubkeys, context, send, 'common.follow.result', id, 'follow unavailable');\n return;\n }\n\n if (message.type === 'common.unfollow') {\n const pubkeys = (message as CommonUnfollowMessage).pubkeys ?? [];\n settleAction(options.unfollow, pubkeys, context, send, 'common.unfollow.result', id, 'unfollow unavailable');\n return;\n }\n\n if (message.type === 'common.react') {\n const m = message as CommonReactMessage;\n if (!options.react) {\n send({ type: 'common.react.result', id, ok: false, error: 'react unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.react!(m.targetEventId, m.reaction, m.customEmojiHref, context),\n send,\n (error) => ({ type: 'common.react.result', id, ok: false, error } as NappletMessage),\n (result) => ({ type: 'common.react.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.report') {\n const m = message as CommonReportMessage;\n if (!options.report) {\n send({ type: 'common.report.result', id, ok: false, error: 'report unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.report!(m.target, m.reason, m.text, context),\n send,\n (error) => ({ type: 'common.report.result', id, ok: false, error } as NappletMessage),\n (result) => ({ type: 'common.report.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown common method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n\nfunction settleAction(\n action: ((pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>) | undefined,\n pubkeys: string[],\n context: CommonServiceContext,\n send: Send,\n resultType: string,\n id: string,\n unavailable: string,\n): void {\n if (!action) {\n send({ type: resultType, id, ok: false, error: unavailable } as NappletMessage);\n return;\n }\n settle(\n () => action(pubkeys, context),\n send,\n (error) => ({ type: resultType, id, ok: false, error } as NappletMessage),\n (result) => ({ type: resultType, id, ...result } as NappletMessage),\n );\n}\n","/**\n * dm-service.ts -- NAP-DM reference service.\n *\n * Routes `dm.*` envelopes to an injected adapter. The service owns request\n * correlation, per-window live subscription fan-out, and lifecycle cleanup;\n * adapters own protocol-specific transport/encryption/storage behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n DmAdapter,\n DmConversationsMessage,\n DmMessagesMessage,\n DmSendMessage,\n DmSubscribeMessage,\n DmUnsubscribeMessage,\n} from './dm-types.js';\n\n/** Options for {@link createDmService}. */\nexport interface DmServiceOptions {\n /** Adapter that implements one or more chat backends under NAP-DM. */\n adapter: DmAdapter;\n}\n\n/** Created DM service. */\nexport interface DmService extends ServiceHandler {\n /** Close adapter resources. Idempotent when adapter close is idempotent. */\n dispose(): void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst DM_DESCRIPTOR: ServiceDescriptor = {\n name: 'dm',\n version: '1.0.0',\n description: 'NAP-DM runtime-mediated direct messages',\n};\n\nfunction err(error: unknown, fallback = 'dm request failed'): string {\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n return fallback;\n}\n\n/**\n * Create a NAP-DM service handler.\n *\n * @param options - DM adapter to route requests through.\n * @returns Service handler for `runtime.registerService('dm', handler)`.\n */\nexport function createDmService(options: DmServiceOptions): DmService {\n if (!options || typeof options.adapter !== 'object' || options.adapter === null) {\n throw new Error('createDmService: options.adapter is required');\n }\n const { adapter } = options;\n const ownedSubscriptions = new Map<string, Set<string>>();\n\n function remember(windowId: string, subscriptionId: string): void {\n let subs = ownedSubscriptions.get(windowId);\n if (!subs) {\n subs = new Set();\n ownedSubscriptions.set(windowId, subs);\n }\n subs.add(subscriptionId);\n }\n\n function forget(windowId: string, subscriptionId: string): void {\n const subs = ownedSubscriptions.get(windowId);\n if (!subs) return;\n subs.delete(subscriptionId);\n if (subs.size === 0) ownedSubscriptions.delete(windowId);\n }\n\n return {\n descriptor: DM_DESCRIPTOR,\n\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as { id?: unknown }).id;\n const requestId = typeof id === 'string' ? id : '';\n\n switch (message.type) {\n case 'dm.status':\n void Promise.resolve().then(() => adapter.status())\n .then((status) => send({ type: 'dm.status.result', id: requestId, ...status } as NappletMessage))\n .catch((error) => send({ type: 'dm.status.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n\n case 'dm.conversations':\n void Promise.resolve().then(() => adapter.conversations(message as DmConversationsMessage))\n .then((page) => send({ type: 'dm.conversations.result', id: requestId, ...page } as NappletMessage))\n .catch((error) =>\n send({ type: 'dm.conversations.result', id: requestId, error: err(error) } as NappletMessage),\n );\n return;\n\n case 'dm.messages':\n void Promise.resolve().then(() => adapter.messages(message as DmMessagesMessage))\n .then((page) => send({ type: 'dm.messages.result', id: requestId, ...page } as NappletMessage))\n .catch((error) =>\n send({ type: 'dm.messages.result', id: requestId, error: err(error) } as NappletMessage),\n );\n return;\n\n case 'dm.send':\n void Promise.resolve()\n .then(() => adapter.send(message as DmSendMessage))\n .then((result) => send({ type: 'dm.send.result', id: requestId, ...result } as NappletMessage))\n .catch((error) => send({ type: 'dm.send.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n\n case 'dm.subscribe': {\n let activeSubscriptionId = '';\n const pending: Parameters<Parameters<DmAdapter['subscribe']>[1]>[0][] = [];\n void Promise.resolve().then(() =>\n adapter.subscribe(message as DmSubscribeMessage, (dmMessage) => {\n if (activeSubscriptionId.length > 0) {\n send({ type: 'dm.message', subscriptionId: activeSubscriptionId, message: dmMessage } as NappletMessage);\n } else {\n pending.push(dmMessage);\n }\n }),\n )\n .then((subscription) => {\n remember(windowId, subscription.subscriptionId);\n activeSubscriptionId = subscription.subscriptionId;\n send({ type: 'dm.subscribe.result', id: requestId, ...subscription } as NappletMessage);\n for (const dmMessage of pending) {\n send({ type: 'dm.message', subscriptionId: activeSubscriptionId, message: dmMessage } as NappletMessage);\n }\n pending.length = 0;\n })\n .catch((error) => {\n send({ type: 'dm.subscribe.result', id: requestId, error: err(error) } as NappletMessage);\n });\n return;\n }\n\n case 'dm.unsubscribe': {\n const subId = (message as DmUnsubscribeMessage).subscriptionId;\n if (\n typeof subId !== 'string'\n || subId.length === 0\n || ownedSubscriptions.get(windowId)?.has(subId) !== true\n ) {\n send({ type: 'dm.unsubscribe.result', id: requestId, error: 'subscription not found' } as NappletMessage);\n return;\n }\n void Promise.resolve().then(() => adapter.unsubscribe(subId))\n .then((ok) => {\n forget(windowId, subId);\n send({ type: 'dm.unsubscribe.result', id: requestId, ...ok } as NappletMessage);\n })\n .catch((error) => send({ type: 'dm.unsubscribe.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n }\n\n default:\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const subs = ownedSubscriptions.get(windowId);\n if (!subs) return;\n for (const subId of subs) void Promise.resolve(adapter.unsubscribe(subId)).catch(() => undefined);\n ownedSubscriptions.delete(windowId);\n },\n\n dispose(): void {\n adapter.close?.();\n ownedSubscriptions.clear();\n },\n };\n}\n","/**\n * dm-nip17-adapter.ts -- concrete NIP-17 adapter for NAP-DM.\n *\n * Uses nostr-tools NIP-17 gift wrapping and an injected relay pool. The owner\n * key is shell/runtime-owned; napplets only see normalized cleartext messages.\n */\n\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\nimport * as nip44 from 'nostr-tools/nip44';\nimport {\n getEventHash,\n getPublicKey,\n validateEvent,\n verifyEvent,\n} from 'nostr-tools/pure';\nimport * as nip17 from 'nostr-tools/nip17';\nimport type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\n\nconst NIP17_GIFT_WRAP_KIND = 1059;\nconst NIP59_SEAL_KIND = 13;\nlet subscriptionCounter = 0;\n\n/** Options for {@link createNip17DmAdapter}. */\nexport interface Nip17DmAdapterOptions {\n /** Shell-owned secret key used for NIP-17 seal creation/unwrapping. */\n ownerSecretKey: Uint8Array;\n /** Relay pool used for publish/subscribe. */\n relayPool: DmRelayPool;\n /** Default relays for NIP-17 DM traffic. */\n relays?: string[];\n /** Optional normalized message store. Defaults to an in-memory store. */\n store?: DmMemoryStore;\n /** Runtime policy invoked once before encrypted relay publication. */\n authorizeSend?(request: DmSendRequest): boolean | Promise<boolean>;\n}\n\ninterface LiveSub {\n handle?: { unsubscribe(): void };\n conversationId?: string;\n onMessage(message: DmMessage): void;\n}\n\nfunction tagsFor(tags: string[][], name: string): string[] {\n return tags.filter((tag) => tag[0] === name && typeof tag[1] === 'string').map((tag) => tag[1]);\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nclass Nip17DmRuntime {\n private readonly ownerPubkey: string;\n private readonly store: DmMemoryStore;\n private readonly live = new Map<string, LiveSub>();\n private historyLoaded = false;\n private historyLoading: Promise<void> | null = null;\n\n constructor(private readonly options: Nip17DmAdapterOptions) {\n this.ownerPubkey = getPublicKey(options.ownerSecretKey);\n this.store = options.store ?? new DmMemoryStore();\n }\n\n adapter(): DmAdapter {\n return {\n status: () => this.status(),\n conversations: (query) => this.conversations(query),\n messages: (query) => this.messages(query),\n send: (request) => this.send(request),\n subscribe: (request, onMessage) => this.subscribe(request, onMessage),\n unsubscribe: (subscriptionId) => this.unsubscribe(subscriptionId),\n close: () => this.close(),\n };\n }\n\n private relaysFor(filters: NostrFilter[]): string[] {\n return this.options.relays && this.options.relays.length > 0\n ? this.options.relays\n : this.options.relayPool.selectRelayTier(filters);\n }\n\n private normalizeRumor(rumor: NostrEvent, status: DmMessage['status']): DmMessage | null {\n if (rumor.kind !== 14) return null;\n const participants = [...new Set([this.ownerPubkey, rumor.pubkey, ...tagsFor(rumor.tags, 'p')])];\n const conversationId = this.store.conversationIdFor(participants);\n return this.store.upsertMessage({\n id: rumor.id,\n conversationId,\n senderPubkey: rumor.pubkey,\n createdAt: rumor.created_at,\n content: rumor.content,\n status,\n }, participants);\n }\n\n private decryptEvent(event: NostrEvent): unknown {\n return JSON.parse(nip44.decrypt(\n event.content,\n nip44.getConversationKey(this.options.ownerSecretKey, event.pubkey),\n ));\n }\n\n private unwrapVerified(wrap: NostrEvent): NostrEvent | null {\n if (\n wrap.kind !== NIP17_GIFT_WRAP_KIND\n || !verifyEvent(wrap)\n || !wrap.tags.some((tag) => tag[0] === 'p' && tag[1] === this.ownerPubkey)\n ) return null;\n try {\n const seal = this.decryptEvent(wrap);\n if (\n !validateEvent(seal)\n || typeof (seal as { id?: unknown }).id !== 'string'\n || typeof (seal as { sig?: unknown }).sig !== 'string'\n || seal.kind !== NIP59_SEAL_KIND\n ) return null;\n const signedSeal = seal as NostrEvent;\n if (!verifyEvent(signedSeal)) return null;\n const rumor = this.decryptEvent(signedSeal);\n if (!validateEvent(rumor)) return null;\n const candidate = rumor as NostrEvent;\n if (\n candidate.kind !== 14\n || candidate.pubkey !== signedSeal.pubkey\n || typeof candidate.id !== 'string'\n || candidate.id !== getEventHash(candidate)\n ) return null;\n return candidate;\n } catch {\n return null;\n }\n }\n\n private ingestWrap(wrap: NostrEvent, status: DmMessage['status']): DmMessage | null {\n const rumor = this.unwrapVerified(wrap);\n return rumor ? this.normalizeRumor(rumor, status) : null;\n }\n\n private async loadHistory(): Promise<void> {\n if (this.historyLoaded || !this.options.relayPool.query) return;\n this.historyLoading ??= (async () => {\n const filters = [{ kinds: [NIP17_GIFT_WRAP_KIND], '#p': [this.ownerPubkey], limit: 1_000 }] as NostrFilter[];\n const wraps = await this.options.relayPool.query!(filters, this.relaysFor(filters));\n for (const wrap of wraps) this.ingestWrap(wrap, 'received');\n this.historyLoaded = true;\n })().finally(() => { this.historyLoading = null; });\n await this.historyLoading;\n }\n\n private status(): DmStatus {\n return {\n available: this.options.relayPool.isAvailable(),\n ownerPubkey: this.ownerPubkey,\n implementations: ['nip17'],\n capabilities: ['send', 'receive', 'subscribe', 'history'],\n };\n }\n\n private async conversations(query?: DmConversationQuery): Promise<DmConversationPage> {\n await this.loadHistory();\n return this.store.conversations(query);\n }\n\n private async messages(query: DmMessageQuery): Promise<DmMessagePage> {\n await this.loadHistory();\n return this.store.messages(query);\n }\n\n private async send(request: DmSendRequest): Promise<DmSendResult> {\n if (!this.options.relayPool.isAvailable()) throw new Error('relay unavailable');\n if (!Array.isArray(request.recipients) || request.recipients.length === 0) throw new Error('invalid recipient');\n if (request.recipients.some((recipient) => !/^[0-9a-f]{64}$/.test(recipient))) throw new Error('invalid recipient');\n if (typeof request.content !== 'string' || request.content.length === 0) throw new Error('content required');\n if (this.options.authorizeSend && !await this.options.authorizeSend(request)) throw new Error('forbidden');\n const recipients = [...new Set(request.recipients)];\n const wraps = nip17.wrapManyEvents(\n this.options.ownerSecretKey,\n recipients.map((publicKey) => ({ publicKey })),\n request.content,\n ) as NostrEvent[];\n for (const wrap of wraps) await Promise.resolve(this.options.relayPool.publish(wrap));\n const conversationId = request.conversationId ?? this.store.conversationIdFor([this.ownerPubkey, ...recipients]);\n const message = this.store.upsertMessage({\n id: request.clientMessageId ?? wraps[0]?.id ?? `dm-${nowSeconds()}`,\n conversationId,\n senderPubkey: this.ownerPubkey,\n createdAt: nowSeconds(),\n content: request.content,\n status: 'sent',\n }, [this.ownerPubkey, ...recipients]);\n return { ok: true, message };\n }\n\n private async subscribe(\n request: DmSubscribeRequest,\n onMessage: (message: DmMessage) => void,\n ): Promise<DmSubscription> {\n if (!this.options.relayPool.isAvailable()) throw new Error('relay unavailable');\n await this.loadHistory();\n const subscriptionId = `dm-nip17-${++subscriptionCounter}`;\n const filters = [{ kinds: [NIP17_GIFT_WRAP_KIND], '#p': [this.ownerPubkey] }] as NostrFilter[];\n const sub: LiveSub = { conversationId: request.conversationId, onMessage };\n this.live.set(subscriptionId, sub);\n try {\n const handle = this.options.relayPool.subscribe(filters, (item) => {\n if (item === 'EOSE') return;\n const message = this.ingestWrap(item, 'received');\n if (!message) return;\n const active = this.live.get(subscriptionId);\n if (active && (!active.conversationId || active.conversationId === message.conversationId)) active.onMessage(message);\n }, this.relaysFor(filters));\n if (this.live.get(subscriptionId) !== sub) {\n handle.unsubscribe();\n throw new Error('subscription cancelled');\n }\n sub.handle = handle;\n return { subscriptionId };\n } catch (error) {\n this.live.delete(subscriptionId);\n throw error;\n }\n }\n\n private unsubscribe(subscriptionId: string): { ok: boolean } {\n const sub = this.live.get(subscriptionId);\n if (!sub) return { ok: false };\n sub.handle?.unsubscribe();\n this.live.delete(subscriptionId);\n return { ok: true };\n }\n\n private close(): void {\n for (const sub of this.live.values()) sub.handle?.unsubscribe();\n this.live.clear();\n }\n}\n\n/**\n * Create a concrete NIP-17 NAP-DM adapter.\n *\n * @param options - Owner key, relay pool, relay hints, optional store.\n * @returns DM adapter for {@link createDmService}.\n */\nexport function createNip17DmAdapter(options: Nip17DmAdapterOptions): DmAdapter {\n return new Nip17DmRuntime(options).adapter();\n}\n","import type {\n DmConversation,\n DmConversationPage,\n DmConversationQuery,\n DmHexPubkey,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmPeer,\n} from './dm-types.js';\n\n/** In-memory normalized DM store. */\nexport class DmMemoryStore {\n private readonly messagesByConversation = new Map<string, DmMessage[]>();\n private readonly participantsByConversation = new Map<string, DmPeer[]>();\n\n conversationIdFor(participants: readonly DmHexPubkey[]): string {\n return `direct:${[...new Set(participants)].sort().join(',')}`;\n }\n\n upsertMessage(message: DmMessage, participants: readonly DmHexPubkey[]): DmMessage {\n const peers = [...new Set(participants)].sort().map((pubkey) => ({ pubkey }));\n this.participantsByConversation.set(message.conversationId, peers);\n const list = this.messagesByConversation.get(message.conversationId) ?? [];\n const existing = list.findIndex((entry) => entry.id === message.id);\n if (existing >= 0) list[existing] = message;\n else list.push(message);\n list.sort((a, b) => a.createdAt - b.createdAt);\n this.messagesByConversation.set(message.conversationId, list);\n return message;\n }\n\n conversations(query: DmConversationQuery = {}): DmConversationPage {\n const offset = query.cursor ? Number.parseInt(query.cursor, 10) || 0 : 0;\n const limit = query.limit ?? 50;\n const conversations: DmConversation[] = [...this.messagesByConversation.entries()]\n .map(([id, messages]) => {\n const latest = messages[messages.length - 1];\n return {\n id,\n kind: id.startsWith('group:') ? 'group' : 'direct',\n participants: this.participantsByConversation.get(id) ?? [],\n unread: messages.filter((message) => message.status === 'received').length,\n ...(latest ? { updatedAt: latest.createdAt } : {}),\n } satisfies DmConversation;\n })\n .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));\n const page = conversations.slice(offset, offset + limit);\n const next = offset + page.length < conversations.length ? String(offset + page.length) : undefined;\n return { conversations: page, ...(next ? { cursor: next } : {}) };\n }\n\n messages(query: DmMessageQuery): DmMessagePage {\n const offset = query.cursor ? Number.parseInt(query.cursor, 10) || 0 : 0;\n const limit = query.limit ?? 100;\n const all = this.messagesByConversation.get(query.conversationId) ?? [];\n const page = all.slice(offset, offset + limit);\n const next = offset + page.length < all.length ? String(offset + page.length) : undefined;\n return { messages: page, ...(next ? { cursor: next } : {}) };\n }\n}\n","/**\n * dm-ndr-adapter.ts -- Nostr Double Ratchet adapter for NAP-DM.\n *\n * Kehto accepts a structural NDR runtime instead of depending directly on the\n * moving nostr-double-ratchet package API. Hosts can pass NdrRuntime instances.\n */\n\nimport type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\nimport { finalizeEvent } from 'nostr-tools/pure';\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\n\n/** Minimal event shape returned by NDR runtimes for sent or received messages. */\nexport interface NdrRumorLike {\n id: string;\n pubkey?: string;\n created_at?: number;\n content?: string;\n}\n\n/** Minimal structural subset of nostr-double-ratchet NdrRuntime. */\nexport interface NdrRuntimeLike {\n getState?(): { ownerPubkey?: string | null; currentDevicePubkey?: string | null };\n initForOwner?(ownerPubkey: string): Promise<unknown>;\n setupUser?(pubkey: string, ownerPubkey?: string): Promise<void>;\n sendMessage(recipientPubkey: string, content: string, options?: unknown, ownerPubkey?: string): Promise<NdrRumorLike>;\n onSessionEvent(callback: (event: NdrRumorLike, from: string, meta?: unknown) => void): () => void;\n close?(): void;\n}\n\nexport interface NdrRelayTransport {\n nostrSubscribe(filter: NostrFilter, onEvent: (event: NostrEvent) => void): () => void;\n nostrFetch(filter: NostrFilter): Promise<NostrEvent[]>;\n nostrPublish(event: Partial<NostrEvent>, innerEventId?: string): Promise<NostrEvent>;\n}\n\nexport interface NdrRelayTransportOptions {\n relayPool: DmRelayPool;\n publishSecretKey?: Uint8Array;\n relays?: string[];\n fetchTimeoutMs?: number;\n}\n\n/** Options for {@link createNdrDmAdapter}. */\nexport interface NdrDmAdapterOptions {\n runtime: NdrRuntimeLike;\n ownerPubkey: string;\n store?: DmMemoryStore;\n available?: boolean;\n}\n\nlet ndrSubCounter = 0;\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nfunction relaysFor(options: NdrRelayTransportOptions, filter: NostrFilter): string[] {\n return options.relays && options.relays.length > 0\n ? options.relays\n : options.relayPool.selectRelayTier([filter]);\n}\n\nfunction signedEvent(event: Partial<NostrEvent>, secretKey?: Uint8Array): NostrEvent {\n if (\n typeof event.id === 'string' &&\n typeof event.pubkey === 'string' &&\n typeof event.sig === 'string' &&\n typeof event.kind === 'number' &&\n typeof event.created_at === 'number' &&\n Array.isArray(event.tags) &&\n typeof event.content === 'string'\n ) {\n return event as NostrEvent;\n }\n if (!secretKey) throw new Error('publishSecretKey required for unsigned NDR events');\n return finalizeEvent(\n {\n kind: event.kind ?? 1060,\n created_at: event.created_at ?? nowSeconds(),\n tags: event.tags ?? [],\n content: event.content ?? '',\n },\n secretKey,\n ) as NostrEvent;\n}\n\n/** Build the transport contract expected by nostr-double-ratchet over Kehto relay service hooks. */\nexport function createNdrRelayTransport(options: NdrRelayTransportOptions): NdrRelayTransport {\n return {\n nostrSubscribe(filter, onEvent) {\n const handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item !== 'EOSE') onEvent(item);\n },\n relaysFor(options, filter),\n );\n return () => handle.unsubscribe();\n },\n\n nostrFetch(filter) {\n const events: NostrEvent[] = [];\n return new Promise((resolve) => {\n let done = false;\n let handle: { unsubscribe(): void } | undefined;\n const finish = () => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n handle?.unsubscribe();\n resolve(events);\n };\n const timer = setTimeout(finish, options.fetchTimeoutMs ?? 500);\n handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') finish();\n else events.push(item);\n },\n relaysFor(options, filter),\n );\n });\n },\n\n async nostrPublish(event) {\n const signed = signedEvent(event, options.publishSecretKey);\n await Promise.resolve(options.relayPool.publish(signed));\n return signed;\n },\n };\n}\n\n/** Create an NDR-backed NAP-DM adapter. */\nexport function createNdrDmAdapter(options: NdrDmAdapterOptions): DmAdapter {\n const store = options.store ?? new DmMemoryStore();\n const live = new Map<string, { conversationId?: string; onMessage(message: DmMessage): void }>();\n const detach = options.runtime.onSessionEvent((event, from) => {\n const participants = [options.ownerPubkey, from];\n const conversationId = store.conversationIdFor(participants);\n const message = store.upsertMessage(\n {\n id: event.id,\n conversationId,\n senderPubkey: from,\n createdAt: event.created_at ?? Math.floor(Date.now() / 1000),\n content: event.content ?? '',\n status: 'received',\n },\n participants,\n );\n for (const sub of live.values()) {\n if (!sub.conversationId || sub.conversationId === message.conversationId) sub.onMessage(message);\n }\n });\n\n return {\n status(): DmStatus {\n return {\n available: options.available ?? true,\n ownerPubkey: options.ownerPubkey,\n implementations: ['ndr'],\n capabilities: ['send', 'receive', 'subscribe', 'history', 'double-ratchet'],\n };\n },\n\n conversations(query?: DmConversationQuery): DmConversationPage {\n return store.conversations(query);\n },\n\n messages(query: DmMessageQuery): DmMessagePage {\n return store.messages(query);\n },\n\n async send(request: DmSendRequest): Promise<DmSendResult> {\n const recipient = request.recipients[0];\n if (!recipient) throw new Error('recipient required');\n await options.runtime.setupUser?.(recipient, options.ownerPubkey);\n const rumor = await options.runtime.sendMessage(recipient, request.content, undefined, options.ownerPubkey);\n const message = store.upsertMessage(\n {\n id: request.clientMessageId ?? rumor.id,\n conversationId: request.conversationId ?? store.conversationIdFor([options.ownerPubkey, recipient]),\n senderPubkey: options.ownerPubkey,\n createdAt: rumor.created_at ?? Math.floor(Date.now() / 1000),\n content: request.content,\n status: 'sent',\n },\n [options.ownerPubkey, recipient],\n );\n return { ok: true, message };\n },\n\n subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): DmSubscription {\n const subscriptionId = `dm-ndr-${++ndrSubCounter}`;\n live.set(subscriptionId, { conversationId: request.conversationId, onMessage });\n return { subscriptionId };\n },\n\n unsubscribe(subscriptionId: string) {\n return { ok: live.delete(subscriptionId) };\n },\n\n close(): void {\n live.clear();\n detach();\n options.runtime.close?.();\n },\n };\n}\n","import type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\nimport { finalizeEvent, getPublicKey } from 'nostr-tools/pure';\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\n\ndeclare const btoa: (data: string) => string;\ndeclare const atob: (data: string) => string;\n\n/** Cordn DM client contract. */\nexport interface CordnDmClient {\n status?(): Promise<Partial<DmStatus>> | Partial<DmStatus>;\n conversations(query?: DmConversationQuery): Promise<DmConversationPage> | DmConversationPage;\n messages(query: DmMessageQuery): Promise<DmMessagePage> | DmMessagePage;\n send(request: DmSendRequest): Promise<DmSendResult>;\n subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): Promise<DmSubscription> | DmSubscription;\n unsubscribe(subscriptionId: string): Promise<{ ok: boolean }> | { ok: boolean };\n close?(): void;\n}\n\nexport interface CordnGroupMessage {\n cursor: number;\n gid: string;\n msg_64: string;\n at: number;\n}\n\nexport interface CordnCoordinatorSubscription {\n stream: AsyncIterable<CordnGroupMessage>;\n result: Promise<{ subscribed: true }>;\n abort(reason?: string): Promise<void>;\n}\n\nexport interface CordnCoordinatorClient {\n PostGroupMessage(input: { msg_64: string }): Promise<{ cursor: number; gid: string; at: number }>;\n FetchGroupMessages(input: { gid: string; after?: number }): Promise<{ messages: CordnGroupMessage[] }>;\n SubscribeGroupMessages(input: { gid: string; after?: number }): Promise<CordnCoordinatorSubscription>;\n close?(): void;\n}\n\nexport interface CordnCodecResult {\n msg_64: string;\n gid: string;\n}\n\nexport interface CordnRelayCoordinatorOptions {\n relayPool: DmRelayPool;\n ownerSecretKey: Uint8Array;\n relays?: string[];\n kind?: number;\n fetchTimeoutMs?: number;\n}\n\n/** Options for {@link createCordnDmAdapter}. */\nexport interface CordnDmAdapterOptions {\n client?: CordnDmClient;\n coordinator?: CordnCoordinatorClient;\n ownerPubkey?: string;\n defaultGroupId?: string;\n store?: DmMemoryStore;\n encodeMessage?(request: DmSendRequest, ownerPubkey?: string): CordnCodecResult;\n decodeMessage?(message: CordnGroupMessage, ownerPubkey?: string): DmMessage;\n}\n\nconst CORDN_RELAY_GROUP_MESSAGE_KIND = 30383;\nlet cordnSubCounter = 0;\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nfunction encodeBase64(value: string): string {\n return btoa(String.fromCharCode(...new TextEncoder().encode(value)));\n}\n\nfunction decodeBase64(value: string): string {\n return new TextDecoder().decode(Uint8Array.from(atob(value), (char) => char.charCodeAt(0)));\n}\n\nfunction groupIdFor(request: DmSendRequest, defaultGroupId?: string): string {\n if (request.conversationId?.startsWith('group:')) return request.conversationId.slice('group:'.length);\n if (request.conversationId) return request.conversationId;\n if (defaultGroupId) return defaultGroupId;\n const firstRecipient = request.recipients[0];\n if (firstRecipient) return firstRecipient;\n throw new Error('group id required');\n}\n\nfunction defaultEncodeMessage(request: DmSendRequest, ownerPubkey?: string, defaultGroupId?: string): CordnCodecResult {\n const gid = groupIdFor(request, defaultGroupId);\n return {\n gid,\n msg_64: encodeBase64(JSON.stringify({\n gid,\n pubkey: ownerPubkey ?? '',\n created_at: nowSeconds(),\n kind: 9,\n tags: [],\n content: request.content,\n })),\n };\n}\n\nfunction defaultDecodeMessage(message: CordnGroupMessage, ownerPubkey?: string): DmMessage {\n let payload: { pubkey?: string; created_at?: number; content?: string } = {};\n try {\n payload = JSON.parse(decodeBase64(message.msg_64)) as typeof payload;\n } catch {\n payload = { content: decodeBase64(message.msg_64) };\n }\n return {\n id: `cordn:${message.gid}:${message.cursor}`,\n conversationId: `group:${message.gid}`,\n senderPubkey: payload.pubkey || ownerPubkey || '',\n createdAt: payload.created_at ?? message.at,\n content: payload.content ?? '',\n status: payload.pubkey && payload.pubkey === ownerPubkey ? 'sent' : 'received',\n };\n}\n\nfunction relayUrls(options: CordnRelayCoordinatorOptions, filter: NostrFilter): string[] {\n return options.relays && options.relays.length > 0\n ? options.relays\n : options.relayPool.selectRelayTier([filter]);\n}\n\nfunction parseRelayGroupMessage(event: NostrEvent): CordnGroupMessage | null {\n try {\n const parsed = JSON.parse(event.content) as CordnGroupMessage;\n if (typeof parsed.gid !== 'string' || typeof parsed.cursor !== 'number' || typeof parsed.msg_64 !== 'string') return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function createCordnRelayCoordinatorClient(options: CordnRelayCoordinatorOptions): CordnCoordinatorClient {\n const ownerPubkey = getPublicKey(options.ownerSecretKey);\n const kind = options.kind ?? CORDN_RELAY_GROUP_MESSAGE_KIND;\n const cursors = new Map<string, number>();\n\n async function fetchGroup(gid: string, after = 0): Promise<CordnGroupMessage[]> {\n const filter = { kinds: [kind], '#g': [gid] } as NostrFilter;\n const records: CordnGroupMessage[] = [];\n await new Promise<void>((resolve) => {\n let done = false;\n let handle: { unsubscribe(): void } | undefined;\n const finish = () => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n handle?.unsubscribe();\n resolve();\n };\n const timer = setTimeout(finish, options.fetchTimeoutMs ?? 500);\n handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') finish();\n else {\n const parsed = parseRelayGroupMessage(item);\n if (parsed && parsed.cursor > after) records.push(parsed);\n }\n },\n relayUrls(options, filter),\n );\n });\n return records.sort((a, b) => a.cursor - b.cursor);\n }\n\n return {\n async PostGroupMessage(input) {\n const decoded = JSON.parse(decodeBase64(input.msg_64)) as { gid?: string };\n const gid = decoded.gid;\n if (!gid) throw new Error('group id required');\n const cursor = (cursors.get(gid) ?? 0) + 1;\n cursors.set(gid, cursor);\n const at = nowSeconds();\n const record: CordnGroupMessage = { cursor, gid, msg_64: input.msg_64, at };\n const event = finalizeEvent(\n {\n kind,\n created_at: at,\n tags: [['g', gid], ['p', ownerPubkey]],\n content: JSON.stringify(record),\n },\n options.ownerSecretKey,\n ) as NostrEvent;\n await Promise.resolve(options.relayPool.publish(event));\n return { cursor, gid, at };\n },\n\n async FetchGroupMessages(input) {\n return { messages: await fetchGroup(input.gid, input.after ?? 0) };\n },\n\n async SubscribeGroupMessages(input) {\n const filter = { kinds: [kind], '#g': [input.gid] } as NostrFilter;\n const queue: CordnGroupMessage[] = [];\n const waiters: Array<(next: IteratorResult<CordnGroupMessage>) => void> = [];\n let closed = false;\n const push = (message: CordnGroupMessage) => {\n if (message.cursor <= (input.after ?? 0)) return;\n const waiter = waiters.shift();\n if (waiter) waiter({ value: message, done: false });\n else queue.push(message);\n };\n const handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') return;\n const message = parseRelayGroupMessage(item);\n if (message) push(message);\n },\n relayUrls(options, filter),\n );\n return {\n result: Promise.resolve({ subscribed: true }),\n stream: {\n [Symbol.asyncIterator]() {\n return {\n next() {\n const existing = queue.shift();\n if (existing) return Promise.resolve({ value: existing, done: false });\n if (closed) return Promise.resolve({ value: undefined, done: true } as IteratorResult<CordnGroupMessage>);\n return new Promise<IteratorResult<CordnGroupMessage>>((resolve) => waiters.push(resolve));\n },\n };\n },\n },\n async abort() {\n closed = true;\n handle.unsubscribe();\n for (const waiter of waiters.splice(0)) waiter({ value: undefined, done: true } as IteratorResult<CordnGroupMessage>);\n },\n };\n },\n };\n}\n\n/** Create a Cordn/ContextVM-backed NAP-DM adapter. */\nexport function createCordnDmAdapter(options: CordnDmAdapterOptions): DmAdapter {\n const store = options.store ?? new DmMemoryStore();\n const live = new Map<string, { abort(): Promise<void>; conversationId?: string }>();\n const ownerPubkey = options.ownerPubkey;\n\n if (!options.client && !options.coordinator) {\n throw new Error('createCordnDmAdapter: client or coordinator is required');\n }\n\n return {\n async status(): Promise<DmStatus> {\n const status = await options.client?.status?.();\n return {\n available: status?.available ?? true,\n ownerPubkey: status?.ownerPubkey ?? ownerPubkey,\n implementations: ['cordn', ...(status?.implementations ?? [])],\n capabilities: ['send', 'receive', 'subscribe', 'history', 'contextvm', ...(status?.capabilities ?? [])],\n };\n },\n conversations(query?: DmConversationQuery) {\n if (options.client) return options.client.conversations(query);\n return store.conversations(query);\n },\n async messages(query: DmMessageQuery): Promise<DmMessagePage> {\n if (options.client) return options.client.messages(query);\n const gid = query.conversationId.startsWith('group:') ? query.conversationId.slice('group:'.length) : query.conversationId;\n const fetched = await options.coordinator!.FetchGroupMessages({ gid });\n for (const record of fetched.messages) store.upsertMessage(\n (options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey),\n [ownerPubkey ?? '', gid],\n );\n return store.messages(query);\n },\n async send(request: DmSendRequest): Promise<DmSendResult> {\n if (options.client) return options.client.send(request);\n const encoded = (options.encodeMessage ?? ((req, pk) => defaultEncodeMessage(req, pk, options.defaultGroupId)))(request, ownerPubkey);\n const posted = await options.coordinator!.PostGroupMessage({ msg_64: encoded.msg_64 });\n const record: CordnGroupMessage = { ...posted, msg_64: encoded.msg_64 };\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', encoded.gid]);\n return { ok: true, message: { ...message, status: 'sent' } };\n },\n async subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): Promise<DmSubscription> {\n if (options.client) return options.client.subscribe(request, onMessage);\n const conversationId = request.conversationId ?? (options.defaultGroupId ? `group:${options.defaultGroupId}` : undefined);\n if (!conversationId) throw new Error('group id required');\n const gid = conversationId.startsWith('group:') ? conversationId.slice('group:'.length) : conversationId;\n const fetched = await options.coordinator!.FetchGroupMessages({ gid });\n let after = 0;\n for (const record of fetched.messages) {\n after = Math.max(after, record.cursor);\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', gid]);\n onMessage(message);\n }\n const subscription = await options.coordinator!.SubscribeGroupMessages({ gid, after });\n const subscriptionId = `dm-cordn-${++cordnSubCounter}`;\n live.set(subscriptionId, { abort: () => subscription.abort('dm.unsubscribe'), conversationId });\n void (async () => {\n for await (const record of subscription.stream) {\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', gid]);\n onMessage(message);\n }\n })();\n return { subscriptionId };\n },\n unsubscribe(subscriptionId: string) {\n if (options.client) return options.client.unsubscribe(subscriptionId);\n const sub = live.get(subscriptionId);\n if (!sub) return { ok: false };\n live.delete(subscriptionId);\n void sub.abort();\n return { ok: true };\n },\n close(): void {\n for (const [id, sub] of live) {\n live.delete(id);\n void sub.abort();\n }\n options.client?.close?.();\n options.coordinator?.close?.();\n },\n };\n}\n","/**\n * NAP-FS service boundary for runtime-owned virtual filesystems.\n *\n * The service owns wire correlation and per-window watch identity. Backends own\n * virtual-path policy, authorization, persistence, picker mediation, and I/O.\n */\n\nimport type {\n FsChange,\n FsDirectoryEntry,\n FsError,\n FsInfo,\n FsMetadata,\n FsMkdirOptions,\n FsPickOptions,\n FsPickResult,\n FsReadOptions,\n FsReadResult,\n FsWatchOptions,\n FsWriteOptions,\n FsWriteResult,\n NappletMessage,\n} from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\n/** Change details emitted by a backend before the service adds its scoped watch id. */\nexport type FsBackendChange = Omit<FsChange, 'watchId'>;\n\n/** Active backend watch handle. */\nexport interface FsBackendWatch {\n /** Stop the backing watch. Must be idempotent. */\n close(): void | Promise<void>;\n}\n\n/** Filesystem operations supplied to the NAP-FS service. */\nexport interface FsBackend {\n info(windowId: string): FsInfo | Promise<FsInfo>;\n pickFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickFiles(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickDirectory(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickSaveFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n stat(windowId: string, path: string): FsMetadata | Promise<FsMetadata>;\n list(windowId: string, path: string): FsDirectoryEntry[] | Promise<FsDirectoryEntry[]>;\n read(windowId: string, path: string, options?: FsReadOptions): FsReadResult | Promise<FsReadResult>;\n write(windowId: string, path: string, data: string, options?: FsWriteOptions): FsWriteResult | Promise<FsWriteResult>;\n mkdir(windowId: string, path: string, options?: FsMkdirOptions): void | Promise<void>;\n remove(windowId: string, path: string, recursive?: boolean): void | Promise<void>;\n move(windowId: string, fromPath: string, toPath: string): void | Promise<void>;\n watch(\n windowId: string,\n path: string,\n options: FsWatchOptions | undefined,\n onChange: (change: FsBackendChange) => void,\n ): FsBackendWatch | Promise<FsBackendWatch>;\n /** Release session-scoped backend state for a destroyed runtime window. */\n onWindowDestroyed?(windowId: string): void;\n /** Release all backend resources. */\n close?(): void;\n}\n\n/** Error with a closed NAP-FS wire reason. */\nexport class FsServiceError extends Error {\n constructor(readonly code: FsError, message = code) {\n super(message);\n this.name = 'FsServiceError';\n }\n}\n\n/** Options for {@link createFsService}. */\nexport interface FsServiceOptions {\n backend: FsBackend;\n}\n\n/** Created NAP-FS service. */\nexport interface FsService extends ServiceHandler {\n /** Close watches and backend resources. */\n dispose(): void;\n}\n\ninterface OwnedWatch {\n readonly windowId: string;\n handle?: FsBackendWatch;\n}\n\nconst FS_DESCRIPTOR: ServiceDescriptor = {\n name: 'fs',\n version: '1.0.0',\n description: 'NAP-FS runtime-owned virtual filesystem',\n};\n\nlet watchCounter = 0;\n\nfunction fsError(error: unknown): FsError {\n return error instanceof FsServiceError ? error.code : 'io-error';\n}\n\n/**\n * Create a NAP-FS wire service over a runtime-owned backend.\n *\n * @param options - Filesystem backend.\n * @returns Service handler for `runtime.registerService('fs', handler)`.\n */\nexport function createFsService(options: FsServiceOptions): FsService {\n if (!options?.backend) throw new Error('createFsService: options.backend is required');\n const watches = new Map<string, OwnedWatch>();\n const watchesByWindow = new Map<string, Set<string>>();\n\n function ownWatch(watchId: string, owned: OwnedWatch): void {\n watches.set(watchId, owned);\n let ids = watchesByWindow.get(owned.windowId);\n if (!ids) {\n ids = new Set();\n watchesByWindow.set(owned.windowId, ids);\n }\n ids.add(watchId);\n }\n\n function forgetWatch(watchId: string): OwnedWatch | undefined {\n const owned = watches.get(watchId);\n if (!owned) return undefined;\n watches.delete(watchId);\n const ids = watchesByWindow.get(owned.windowId);\n ids?.delete(watchId);\n if (ids?.size === 0) watchesByWindow.delete(owned.windowId);\n return owned;\n }\n\n function result(\n send: (message: NappletMessage) => void,\n type: string,\n id: string,\n operation: () => unknown | Promise<unknown>,\n field?: string,\n ): void {\n void Promise.resolve()\n .then(operation)\n .then((value) => send({\n type,\n id,\n ...(field ? { [field]: value } : {}),\n } as NappletMessage))\n .catch((error) => send({ type, id, error: fsError(error) } as NappletMessage));\n }\n\n function closeWindowWatches(windowId: string): void {\n for (const watchId of watchesByWindow.get(windowId) ?? []) {\n const owned = forgetWatch(watchId);\n if (owned?.handle) void Promise.resolve(owned.handle.close()).catch(() => undefined);\n }\n }\n\n return {\n descriptor: FS_DESCRIPTOR,\n\n handleMessage(windowId, message, send): void {\n const input = message as NappletMessage & Record<string, unknown>;\n const id = typeof input.id === 'string' ? input.id : '';\n switch (input.type) {\n case 'fs.info':\n result(send, 'fs.info.result', id, () => options.backend.info(windowId), 'info');\n return;\n case 'fs.pickFile':\n result(send, 'fs.pickFile.result', id, () => options.backend.pickFile(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickFiles':\n result(send, 'fs.pickFiles.result', id, () => options.backend.pickFiles(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickDirectory':\n result(send, 'fs.pickDirectory.result', id, () => options.backend.pickDirectory(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickSaveFile':\n result(send, 'fs.pickSaveFile.result', id, () => options.backend.pickSaveFile(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.stat':\n result(send, 'fs.stat.result', id, () => options.backend.stat(windowId, input.path as string), 'metadata');\n return;\n case 'fs.list':\n result(send, 'fs.list.result', id, () => options.backend.list(windowId, input.path as string), 'entries');\n return;\n case 'fs.read':\n result(send, 'fs.read.result', id, () => options.backend.read(windowId, input.path as string, input.options as FsReadOptions | undefined), 'result');\n return;\n case 'fs.write':\n result(send, 'fs.write.result', id, () => options.backend.write(windowId, input.path as string, input.data as string, input.options as FsWriteOptions | undefined), 'result');\n return;\n case 'fs.mkdir':\n result(send, 'fs.mkdir.result', id, () => options.backend.mkdir(windowId, input.path as string, input.options as FsMkdirOptions | undefined));\n return;\n case 'fs.remove':\n result(send, 'fs.remove.result', id, () => options.backend.remove(windowId, input.path as string, input.recursive as boolean | undefined));\n return;\n case 'fs.move':\n result(send, 'fs.move.result', id, () => options.backend.move(windowId, input.fromPath as string, input.toPath as string));\n return;\n case 'fs.watch': {\n const watchId = `fs-watch-${++watchCounter}`;\n result(send, 'fs.watch.result', id, async () => {\n const owned: OwnedWatch = { windowId };\n ownWatch(watchId, owned);\n const handle = await options.backend.watch(\n windowId,\n input.path as string,\n input.options as FsWatchOptions | undefined,\n (change) => {\n if (watches.get(watchId) !== owned || owned.handle === undefined) return;\n send({ type: 'fs.changed', change: { ...change, watchId } } as NappletMessage);\n },\n );\n if (watches.get(watchId) !== owned) {\n await Promise.resolve(handle.close()).catch(() => undefined);\n throw new FsServiceError('cancelled');\n }\n owned.handle = handle;\n return watchId;\n }, 'watchId');\n return;\n }\n case 'fs.unwatch': {\n const watchId = typeof input.watchId === 'string' ? input.watchId : '';\n const owned = watches.get(watchId);\n if (!owned || owned.windowId !== windowId) {\n send({ type: 'fs.unwatch.result', id } as NappletMessage);\n return;\n }\n const forgotten = forgetWatch(watchId);\n result(send, 'fs.unwatch.result', id, () => forgotten?.handle?.close());\n return;\n }\n default:\n return;\n }\n },\n\n onWindowDestroyed(windowId): void {\n closeWindowWatches(windowId);\n options.backend.onWindowDestroyed?.(windowId);\n },\n\n dispose(): void {\n for (const windowId of watchesByWindow.keys()) closeWindowWatches(windowId);\n options.backend.close?.();\n },\n };\n}\n"],"mappings":"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","names":["result","err","relayUrls","createRelayEventResult","createRelayEventResultWithHints","deliver","result","relayUrls","result","err","result","generateId","result","constraints","result","isRecord","err","isRecord","result","err","createRelayEventResultWithHints","relays","result","relayUrls","err","verifyEvent","generateId","handleInfo","err","toErrorMessage","result","err","toErrorMessage","result","toErrorMessage","err","result","err","toErrorMessage","result","err","result","errorMessage","err","settle","result","errorMessage","err","settle","unsupported","createContext","result","errorMessage","err","settle","unsupported","createContext","result","errorMessage","err","result","settle","forget","result","nowSeconds","finalizeEvent","getPublicKey","nowSeconds","result"]}
|
|
1
|
+
{"version":3,"sources":["../src/notification-service.ts","../src/identity-service.ts","../src/relay-pool-service.ts","../src/cache-service.ts","../src/count-service.ts","../src/coordinated-relay.ts","../src/keys-service-internals.ts","../src/keys-service.ts","../src/browser-media-bridge.ts","../src/media-service.ts","../src/notify-service.ts","../src/theme-service.ts","../src/config-schema.ts","../src/config-service.ts","../src/resource-service.ts","../src/outbox-service.ts","../src/relay-pool-outbox-router.ts","../src/upload-service.ts","../src/http-uploader.ts","../src/intent-service.ts","../src/catalog-intent-resolver.ts","../src/manifest-intent-catalog.ts","../src/cvm-service.ts","../src/link-service.ts","../src/lists-service.ts","../src/serial-service.ts","../src/ble-service.ts","../src/webrtc-service.ts","../src/common-service.ts","../src/dm-service.ts","../src/dm-nip17-adapter.ts","../src/dm-memory-store.ts","../src/dm-ndr-adapter.ts","../src/dm-cordn-adapter.ts","../src/fs-service.ts"],"sourcesContent":["\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type { Notification, NotificationServiceOptions } from './types.js';\n\n/** Notification service version — follows semver. */\nconst NOTIFICATION_SERVICE_VERSION = '1.0.0';\n\n/** Default maximum notifications per window. */\nconst DEFAULT_MAX_PER_WINDOW = 100;\n\n/** Counter for generating unique notification IDs. */\nlet idCounter = 0;\n\n/**\n * Generate a unique notification ID.\n */\nfunction generateId(): string {\n idCounter++;\n return `notif-${Date.now()}-${idCounter}`;\n}\n\ninterface NotificationStore {\n notifications: Map<string, Notification[]>;\n onChange?: (notifications: Notification[]) => void;\n maxPerWindow: number;\n}\n\nfunction getAllNotifications(store: NotificationStore): Notification[] {\n const all: Notification[] = [];\n for (const windowNotifs of store.notifications.values()) {\n all.push(...windowNotifs);\n }\n return all;\n}\n\nfunction notify(store: NotificationStore): void {\n store.onChange?.(getAllNotifications(store));\n}\n\nfunction getWindowNotifications(store: NotificationStore, windowId: string): Notification[] {\n let list = store.notifications.get(windowId);\n if (!list) {\n list = [];\n store.notifications.set(windowId, list);\n }\n return list;\n}\n\nfunction enforceLimit(store: NotificationStore, list: Notification[]): void {\n while (list.length > store.maxPerWindow) {\n list.shift();\n }\n}\n\nfunction findById(store: NotificationStore, windowId: string, id: string): [Notification, number] | undefined {\n const list = store.notifications.get(windowId);\n const index = list?.findIndex((notification) => notification.id === id) ?? -1;\n if (!list || index === -1) return undefined;\n return [list[index], index];\n}\n\nfunction createNotification(\n store: NotificationStore,\n windowId: string,\n title: string,\n body: string,\n): Notification {\n const notification: Notification = {\n id: generateId(),\n windowId,\n title,\n body,\n read: false,\n createdAt: Math.floor(Date.now() / 1000),\n };\n const list = getWindowNotifications(store, windowId);\n list.push(notification);\n enforceLimit(store, list);\n notify(store);\n return notification;\n}\n\nfunction dismissNotification(store: NotificationStore, windowId: string, id: string): void {\n const found = findById(store, windowId, id);\n if (!found) return;\n const [, index] = found;\n const list = store.notifications.get(windowId);\n if (!list) return;\n list.splice(index, 1);\n if (list.length === 0) store.notifications.delete(windowId);\n notify(store);\n}\n\nfunction markNotificationRead(store: NotificationStore, windowId: string, id: string): void {\n const found = findById(store, windowId, id);\n if (!found) return;\n const [notification] = found;\n if (!notification.read) {\n notification.read = true;\n notify(store);\n }\n}\n\nfunction handleNotifyEnvelope(\n store: NotificationStore,\n windowId: string,\n action: string,\n msg: NappletMessage & Record<string, unknown>,\n send: (msg: NappletMessage) => void,\n): void {\n switch (action) {\n case 'create': {\n const title = typeof msg.title === 'string' ? msg.title : '';\n const body = typeof msg.body === 'string' ? msg.body : '';\n const notification = createNotification(store, windowId, title, body);\n send({ type: 'notify.created', id: notification.id } as NappletMessage);\n break;\n }\n\n case 'dismiss': {\n const notifId = typeof msg.notificationId === 'string' ? msg.notificationId : '';\n if (notifId) dismissNotification(store, windowId, notifId);\n break;\n }\n\n case 'read': {\n const notifId = typeof msg.notificationId === 'string' ? msg.notificationId : '';\n if (notifId) markNotificationRead(store, windowId, notifId);\n break;\n }\n\n case 'list': {\n const windowNotifs = store.notifications.get(windowId) ?? [];\n send({ type: 'notify.listed', notifications: windowNotifs } as NappletMessage);\n break;\n }\n\n default:\n break;\n }\n}\n\n/**\n * Create a notification service handler.\n *\n * The notification service is a state registry that tracks notifications\n * per napplet window. It accepts direct `notify.*` service envelopes; INC\n * topics are opaque application data and never select a host service. The\n * shell host controls presentation through the onChange callback.\n *\n * @param options - Optional configuration (onChange callback, maxPerWindow limit)\n * @returns A ServiceHandler to register with the runtime\n *\n * @example\n * ```ts\n * import { createNotificationService } from '@kehto/services';\n *\n * const notifications = createNotificationService({\n * onChange: (list) => {\n * const unread = list.filter(n => !n.read);\n * updateBadge(unread.length);\n * },\n * maxPerWindow: 50,\n * });\n *\n * runtime.registerService('notify', notifications);\n * ```\n */\nexport function createNotificationService(options?: NotificationServiceOptions): ServiceHandler {\n const store: NotificationStore = {\n notifications: new Map<string, Notification[]>(),\n onChange: options?.onChange,\n maxPerWindow: options?.maxPerWindow ?? DEFAULT_MAX_PER_WINDOW,\n };\n\n const descriptor: ServiceDescriptor = {\n name: 'notify',\n version: NOTIFICATION_SERVICE_VERSION,\n description: 'Notification state registry — tracks notifications per napplet window',\n };\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (!message.type.startsWith('notify.')) return;\n handleNotifyEnvelope(\n store,\n windowId,\n message.type.slice(7),\n message as NappletMessage & Record<string, unknown>,\n send,\n );\n },\n\n onWindowDestroyed(windowId: string): void {\n if (store.notifications.delete(windowId)) {\n notify(store);\n }\n },\n };\n}\n","/**\n * identity-service.ts — NIP-5D identity nap reference service.\n *\n * MIGRATION from signer-service (v1.1 -> v1.2):\n * - signer.getPublicKey -> identity.getPublicKey (same shell state)\n * - signer.getRelays -> identity.getRelays (same shell state)\n * - signer.signEvent -> DELETED (no napplet-visible path; shell signs\n * internally inside relay.publish)\n * - signer.nip04.encrypt/decrypt -> DELETED\n * - signer.nip44.encrypt/decrypt -> DELETED (shell encrypts internally\n * inside relay.publishEncrypted)\n *\n * Identity is strictly read-only per NAP-IDENTITY: napplets learn *about* the\n * user but cannot act *as* the user — no signing, encryption, or decryption.\n * (An `identity.decrypt` capability was briefly added in v1.8 and removed as a\n * spec violation; decryption belongs to the runtime, inline over the wire.)\n *\n * Handles 9 identity.* request types from @napplet/nap/identity. getPublicKey\n * and getRelays return real values sourced from hooks.auth.getSigner(); the\n * remaining read-only queries can be backed by optional host providers. When a\n * provider is absent, the service keeps returning spec-shaped empty results.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceHandler, Signer } from '@kehto/runtime';\nimport type {\n IdentityGetBadgesMessage,\n IdentityGetBlockedMessage,\n IdentityGetFollowsMessage,\n IdentityGetListMessage,\n IdentityGetMutesMessage,\n IdentityGetProfileMessage,\n IdentityGetPublicKeyResultMessage,\n IdentityGetRelaysResultMessage,\n IdentityGetProfileResultMessage,\n IdentityGetFollowsResultMessage,\n IdentityGetListResultMessage,\n IdentityGetZapsResultMessage,\n IdentityGetZapsMessage,\n IdentityGetMutesResultMessage,\n IdentityGetBlockedResultMessage,\n IdentityGetBadgesResultMessage,\n RelayPermission,\n} from '@napplet/nap/identity/types';\n\n/** Identity service version — follows semver. */\nconst IDENTITY_SERVICE_VERSION = '1.0.0';\n\n/** A value that may be returned synchronously or through a Promise. */\nexport type MaybePromise<T> = T | Promise<T>;\n\ntype IdentityProviderResult = NappletMessage & { error?: string };\ntype SendIdentityMessage = (msg: NappletMessage) => void;\n\nfunction sendProviderError<T extends IdentityProviderResult>(\n send: SendIdentityMessage,\n result: T,\n fallback: string,\n _err: unknown,\n): void {\n send({\n ...result,\n error: fallback,\n });\n}\n\nasync function getCurrentPubkey(options: IdentityServiceOptions): Promise<string> {\n try {\n const currentSigner = options.getSigner();\n if (!currentSigner?.getPublicKey) return '';\n return (await currentSigner.getPublicKey()) ?? '';\n } catch {\n return '';\n }\n}\n\nfunction sendOptionalProviderResult<T extends IdentityProviderResult>(\n options: IdentityServiceOptions,\n send: SendIdentityMessage,\n fallbackResult: T,\n errorFallback: string,\n buildResult?: (pubkey: string) => MaybePromise<T>,\n): void {\n if (!buildResult) {\n send(fallbackResult);\n return;\n }\n\n Promise.resolve(getCurrentPubkey(options))\n .then((pubkey) => buildResult(pubkey))\n .then((result) => send(result))\n .catch((err: unknown) => sendProviderError(send, fallbackResult, errorFallback, err));\n}\n\n/**\n * Options for creating the identity service.\n *\n * @example\n * ```ts\n * const identityService = createIdentityService({\n * getSigner: () => window.nostr ?? null,\n * });\n * runtime.registerService('identity', identityService);\n * ```\n */\nexport interface IdentityServiceOptions {\n /**\n * Return the NIP-07-compatible signer (or null) used to resolve\n * identity.getPublicKey / identity.getRelays. Called on every request —\n * availability can change dynamically.\n */\n getSigner: () => Signer | null;\n\n /**\n * Optional host-backed profile lookup for the current user.\n *\n * Kehto does not query relays itself; hosts that already maintain kind-0\n * metadata can provide it here without replacing the whole identity service.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getProfile request envelope.\n * @returns Profile metadata, or null when unavailable.\n */\n getProfile?: (\n pubkey: string,\n message: IdentityGetProfileMessage,\n ) => MaybePromise<IdentityGetProfileResultMessage['profile']>;\n\n /**\n * Optional host-backed follow-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getFollows request envelope.\n * @returns Hex-encoded followed pubkeys.\n */\n getFollows?: (\n pubkey: string,\n message: IdentityGetFollowsMessage,\n ) => MaybePromise<IdentityGetFollowsResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed categorized-list lookup for the current user.\n *\n * @param listType - Requested list category from the wire envelope.\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getList request envelope.\n * @returns List entries.\n */\n getList?: (\n listType: string,\n pubkey: string,\n message: IdentityGetListMessage,\n ) => MaybePromise<IdentityGetListResultMessage['entries']>;\n\n /**\n * Optional host-backed zap receipt lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getZaps request envelope.\n * @returns Zap receipts.\n */\n getZaps?: (\n pubkey: string,\n message: IdentityGetZapsMessage,\n ) => MaybePromise<IdentityGetZapsResultMessage['zaps']>;\n\n /**\n * Optional host-backed mute-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getMutes request envelope.\n * @returns Hex-encoded muted pubkeys.\n */\n getMutes?: (\n pubkey: string,\n message: IdentityGetMutesMessage,\n ) => MaybePromise<IdentityGetMutesResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed block-list lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getBlocked request envelope.\n * @returns Hex-encoded blocked pubkeys.\n */\n getBlocked?: (\n pubkey: string,\n message: IdentityGetBlockedMessage,\n ) => MaybePromise<IdentityGetBlockedResultMessage['pubkeys']>;\n\n /**\n * Optional host-backed badge lookup for the current user.\n *\n * @param pubkey - Current signer pubkey, or \"\" when no signer is connected.\n * @param message - Original identity.getBadges request envelope.\n * @returns Badges awarded to the user.\n */\n getBadges?: (\n pubkey: string,\n message: IdentityGetBadgesMessage,\n ) => MaybePromise<IdentityGetBadgesResultMessage['badges']>;\n}\n\nfunction handleGetPublicKey(options: IdentityServiceOptions, id: string, send: SendIdentityMessage): void {\n Promise.resolve()\n .then(() => options.getSigner())\n .then(async (signer) => signer?.getPublicKey ? signer.getPublicKey() : '')\n .catch(() => '')\n .then((pubkey) => {\n const result: IdentityGetPublicKeyResultMessage = {\n type: 'identity.getPublicKey.result',\n id,\n pubkey: pubkey ?? '',\n };\n send(result);\n });\n}\n\nfunction handleGetRelays(options: IdentityServiceOptions, id: string, send: SendIdentityMessage): void {\n const fallbackResult: IdentityGetRelaysResultMessage = {\n type: 'identity.getRelays.result',\n id,\n relays: {},\n };\n\n Promise.resolve()\n .then(() => options.getSigner())\n .then(async (signer): Promise<IdentityGetRelaysResultMessage> => {\n if (!signer?.getRelays) return fallbackResult;\n return {\n ...fallbackResult,\n relays: (await signer.getRelays()) ?? {} as Record<string, RelayPermission>,\n };\n })\n .catch(() => ({ ...fallbackResult, error: 'identity.getRelays failed' }))\n .then(send);\n}\n\nfunction handleReadProvider(\n options: IdentityServiceOptions,\n id: string,\n message: NappletMessage,\n send: SendIdentityMessage,\n): boolean {\n switch (message.type) {\n case 'identity.getProfile':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getProfile.result', id, profile: null },\n 'identity.getProfile failed',\n options.getProfile\n ? async (pubkey): Promise<IdentityGetProfileResultMessage> => ({\n type: 'identity.getProfile.result',\n id,\n profile: (await options.getProfile!(pubkey, message as IdentityGetProfileMessage)) ?? null,\n })\n : undefined,\n );\n return true;\n\n case 'identity.getFollows':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getFollows.result', id, pubkeys: [] },\n 'identity.getFollows failed',\n options.getFollows\n ? async (pubkey): Promise<IdentityGetFollowsResultMessage> => ({\n type: 'identity.getFollows.result',\n id,\n pubkeys: (await options.getFollows!(pubkey, message as IdentityGetFollowsMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getList':\n handleGetList(options, id, message as IdentityGetListMessage, send);\n return true;\n\n case 'identity.getZaps':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getZaps.result', id, zaps: [] },\n 'identity.getZaps failed',\n options.getZaps\n ? async (pubkey): Promise<IdentityGetZapsResultMessage> => ({\n type: 'identity.getZaps.result',\n id,\n zaps: (await options.getZaps!(pubkey, message as IdentityGetZapsMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getMutes':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getMutes.result', id, pubkeys: [] },\n 'identity.getMutes failed',\n options.getMutes\n ? async (pubkey): Promise<IdentityGetMutesResultMessage> => ({\n type: 'identity.getMutes.result',\n id,\n pubkeys: (await options.getMutes!(pubkey, message as IdentityGetMutesMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getBlocked':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getBlocked.result', id, pubkeys: [] },\n 'identity.getBlocked failed',\n options.getBlocked\n ? async (pubkey): Promise<IdentityGetBlockedResultMessage> => ({\n type: 'identity.getBlocked.result',\n id,\n pubkeys: (await options.getBlocked!(pubkey, message as IdentityGetBlockedMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n case 'identity.getBadges':\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getBadges.result', id, badges: [] },\n 'identity.getBadges failed',\n options.getBadges\n ? async (pubkey): Promise<IdentityGetBadgesResultMessage> => ({\n type: 'identity.getBadges.result',\n id,\n badges: (await options.getBadges!(pubkey, message as IdentityGetBadgesMessage)) ?? [],\n })\n : undefined,\n );\n return true;\n\n default:\n return false;\n }\n}\n\nfunction handleGetList(\n options: IdentityServiceOptions,\n id: string,\n message: IdentityGetListMessage,\n send: SendIdentityMessage,\n): void {\n sendOptionalProviderResult(\n options,\n send,\n { type: 'identity.getList.result', id, entries: [] },\n 'identity.getList failed',\n options.getList\n ? async (pubkey): Promise<IdentityGetListResultMessage> => ({\n type: 'identity.getList.result',\n id,\n entries: (await options.getList!(message.listType, pubkey, message)) ?? [],\n })\n : undefined,\n );\n}\n\nfunction handleIdentityServiceMessage(\n options: IdentityServiceOptions,\n message: NappletMessage,\n send: SendIdentityMessage,\n): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n\n switch (message.type) {\n case 'identity.getPublicKey':\n // Per NIP-5D, no signer resolves to the empty-pubkey sentinel, not an error.\n handleGetPublicKey(options, id, send);\n return;\n\n case 'identity.getRelays':\n handleGetRelays(options, id, send);\n return;\n\n default:\n handleReadProvider(options, id, message, send);\n }\n}\n\n/**\n * Create an identity service that handles NIP-5D identity.* envelope messages.\n *\n * Supports the 9 read-only identity.* request types from @napplet/nap/identity.\n * The two nostr-info queries (getPublicKey, getRelays) resolve through the\n * caller-supplied signer; the remaining read-only queries resolve through\n * optional host providers or return default/empty payloads with spec-correct\n * envelope shapes so napplets always receive a result envelope.\n *\n * @param options - Identity service configuration (getSigner)\n * @returns A ServiceHandler ready for runtime.registerService('identity', handler)\n *\n * @example\n * ```ts\n * import { createIdentityService } from '@kehto/services';\n *\n * const identity = createIdentityService({\n * getSigner: () => mySignerAdapter,\n * });\n * runtime.registerService('identity', identity);\n * ```\n */\nexport function createIdentityService(options: IdentityServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'identity',\n version: IDENTITY_SERVICE_VERSION,\n description: 'NIP-5D identity NAP reference handler (9 read-only identity queries)',\n },\n\n handleMessage(\n _windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n handleIdentityServiceMessage(options, message, send);\n },\n\n // Identity service has no per-window state to clean up.\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n}\n","/**\n * relay-pool-service.ts — Relay pool as a ServiceHandler.\n *\n * Wraps an existing relay pool implementation (subscribe, publish,\n * selectRelayTier, isAvailable) as a ServiceHandler that receives\n * relay NAP envelope messages and manages subscription lifecycle.\n *\n * Handles: relay.subscribe, relay.close, relay.publish, relay.publishEncrypted.\n *\n * Note on `relay.publishEncrypted`: the canonical napplet→shell path routes\n * through @kehto/runtime handleRelayMessage, which performs shell-internal\n * NIP-44/NIP-04 encryption via the signer, then synthesizes a relay.publish\n * envelope handed to this service. The publishEncrypted branch here is a\n * fallback for alternate wirings; by the time the service sees the\n * envelope, content MUST already be ciphertext (the service never encrypts\n * or decrypts).\n */\n\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResultWithHints } from '@kehto/runtime';\n\n// Timer globals available in all JS runtimes\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** EOSE fallback timeout in milliseconds. */\nconst EOSE_FALLBACK_MS = 15_000;\n\n/**\n * Options for creating a relay pool service.\n *\n * @example\n * ```ts\n * const relayPoolService = createRelayPoolService({\n * subscribe: (filters, cb, urls) => myPool.subscribe(filters, cb, urls),\n * publish: (event) => myPool.publish(event),\n * selectRelayTier: (filters) => myPool.selectRelays(filters),\n * isAvailable: () => myPool.connected,\n * });\n * ```\n */\nexport interface RelayPoolServiceOptions {\n /**\n * Subscribe to events matching filters. Returns handle with unsubscribe().\n *\n * @param filters - NIP-01 filter objects\n * @param callback - Receives matching events or 'EOSE' plus optional observed relay URLs\n * @param relayUrls - Optional relay URL hints\n * @returns Handle to cancel the subscription\n */\n subscribe(\n filters: NostrFilter[],\n callback: (item: NostrEvent | 'EOSE', observedRelayUrls?: string[]) => void,\n relayUrls?: string[],\n ): { unsubscribe(): void };\n\n /**\n * Publish an event to relays.\n *\n * @param event - The event to publish\n * @returns Nothing, or a promise that settles when the publish request completes\n */\n publish(event: NostrEvent): void | Promise<void>;\n\n /**\n * Select relay URLs appropriate for the given filters.\n *\n * @param filters - NIP-01 filter objects\n * @returns Array of relay URLs\n */\n selectRelayTier(filters: NostrFilter[]): string[];\n\n /**\n * Whether the relay pool is available and connected.\n *\n * @returns true if the relay pool can handle requests\n */\n isAvailable(): boolean;\n}\n\n/** Internal subscription tracking entry. */\ninterface TrackedSubscription {\n handle: { unsubscribe(): void };\n eoseTimer: unknown;\n}\n\ntype RelayServiceMessage = NappletMessage & {\n id?: unknown;\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n relay?: unknown;\n};\n\n/**\n * Create a relay pool service that wraps an existing relay pool\n * implementation as a ServiceHandler.\n *\n * Handles relay.subscribe, relay.close, and relay.publish envelopes.\n * Tracks subscriptions per windowId:subId for lifecycle management.\n * Sets a 15-second EOSE fallback timer on each subscription.\n *\n * @param options - Relay pool implementation to wrap\n * @returns A ServiceHandler ready for runtime.registerService('relay', handler)\n *\n * @example\n * ```ts\n * import { createRelayPoolService } from '@kehto/services';\n *\n * const pool = createRelayPoolService({\n * subscribe: (f, cb, urls) => applesauce.subscribe(f, cb, urls),\n * publish: (e) => applesauce.publish(e),\n * selectRelayTier: (f) => applesauce.getRelays(f),\n * isAvailable: () => applesauce.connected,\n * });\n * runtime.registerService('relay', pool);\n * ```\n */\nexport function createRelayPoolService(options: RelayPoolServiceOptions): ServiceHandler {\n const tracked = new Map<string, TrackedSubscription>();\n\n function publishEvent(\n relayMessage: RelayServiceMessage,\n resultType: 'relay.publish.result' | 'relay.publishEncrypted.result',\n send: (msg: NappletMessage) => void,\n ): void {\n const id = typeof relayMessage.id === 'string' ? relayMessage.id : '';\n const event = relayMessage.event as NostrEvent | undefined;\n if (!event || typeof event !== 'object') {\n send({ type: resultType, id, ok: false, error: 'invalid event' } as NappletMessage);\n return;\n }\n if (!options.isAvailable()) {\n send({ type: resultType, id, ok: false, error: 'no relay pool available' } as NappletMessage);\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n await options.publish(event);\n send({\n type: resultType,\n id,\n ok: true,\n event,\n eventId: event.id,\n } as NappletMessage);\n } catch (error) {\n send({\n type: resultType,\n id,\n ok: false,\n error: error instanceof Error && error.message ? error.message : 'publish failed',\n } as NappletMessage);\n }\n })();\n }\n\n return {\n descriptor: {\n name: 'relay-pool',\n version: '1.0.0',\n description: 'Relay pool subscription and publishing',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n const subKey = `${windowId}:${subId}`;\n\n // Cancel existing subscription for this key if any\n const existing = tracked.get(subKey);\n if (existing) {\n existing.handle.unsubscribe();\n clearTimeout(existing.eoseTimer);\n tracked.delete(subKey);\n }\n\n if (!options.isAvailable()) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n const relayHint = typeof relayMessage.relay === 'string' && relayMessage.relay.length > 0\n ? relayMessage.relay\n : undefined;\n const relayUrls = relayHint ? [relayHint] : options.selectRelayTier(filters);\n let eoseSent = false;\n\n const eoseTimer = setTimeout(() => {\n if (!eoseSent) {\n eoseSent = true;\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n }, EOSE_FALLBACK_MS);\n\n const handle = options.subscribe(filters, (item, observedRelayUrls) => {\n if (item === 'EOSE') {\n clearTimeout(eoseTimer);\n if (!eoseSent) {\n eoseSent = true;\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n return;\n }\n send({\n type: 'relay.event',\n subId,\n result: createRelayEventResultWithHints(item, observedRelayUrls ?? relayUrls),\n } as NappletMessage);\n }, relayUrls);\n\n tracked.set(subKey, { handle, eoseTimer });\n return;\n }\n\n if (message.type === 'relay.close') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n const entry = tracked.get(subKey);\n if (entry) {\n entry.handle.unsubscribe();\n clearTimeout(entry.eoseTimer);\n tracked.delete(subKey);\n }\n return;\n }\n\n if (message.type === 'relay.publish') {\n publishEvent(relayMessage, 'relay.publish.result', send);\n return;\n }\n\n if (message.type === 'relay.publishEncrypted') {\n publishEvent(relayMessage, 'relay.publishEncrypted.result', send);\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of tracked) {\n if (key.startsWith(prefix)) {\n entry.handle.unsubscribe();\n clearTimeout(entry.eoseTimer);\n tracked.delete(key);\n }\n }\n },\n };\n}\n","\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResult } from '@kehto/runtime';\n\ntype RelayServiceMessage = NappletMessage & {\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n};\n\n/**\n * Options for creating a cache service.\n *\n * @example\n * ```ts\n * const cacheService = createCacheService({\n * query: (filters) => myIndexedDB.query(filters),\n * store: (event) => myIndexedDB.store(event),\n * isAvailable: () => true,\n * });\n * ```\n */\nexport interface CacheServiceOptions {\n /**\n * Query cached events matching the given filters.\n *\n * @param filters - NIP-01 filter objects\n * @returns Promise resolving to matching cached events\n */\n query(filters: NostrFilter[]): Promise<NostrEvent[]>;\n\n /**\n * Store an event in cache. Best-effort, may silently fail.\n *\n * @param event - The event to store\n */\n store(event: NostrEvent): void;\n\n /**\n * Whether the cache is available.\n *\n * @returns true if the cache can handle requests\n */\n isAvailable(): boolean;\n}\n\n/**\n * @kehto/services cross-package naming-parity alias for {@link CacheServiceOptions}.\n *\n * `HostCacheBridge` matches the v1.4 `HostKeysBridge` / `HostMediaBridge`\n * convention — it is a pure type alias for `CacheServiceOptions`, NOT a new\n * type. Existing consumers of `CacheServiceOptions` continue to work\n * unchanged; new consumers may prefer `HostCacheBridge` for consistency\n * with the other Host*Bridge names in `@kehto/services`.\n *\n * Anti-feature note (PITFALLS.md M-02): `CacheServiceOptions` MUST remain\n * the primary export. This alias is additive; do not rename or delete\n * `CacheServiceOptions` when other Host*Bridge names eventually\n * stabilize.\n *\n * @example\n * ```ts\n * import type { HostCacheBridge } from '@kehto/services';\n * const cache: HostCacheBridge = {\n * query: (filters) => myIndexedDB.query(filters),\n * store: (event) => myIndexedDB.store(event),\n * isAvailable: () => true,\n * };\n * ```\n */\nexport type HostCacheBridge = CacheServiceOptions;\n\n/**\n * Create a cache service that wraps an existing cache implementation\n * as a ServiceHandler.\n *\n * Cache relay.subscribe subscriptions are one-shot — they query, deliver\n * results, send EOSE, and are done. No long-lived subscription tracking needed.\n * Cache query failures are best-effort: EOSE is sent even on failure.\n *\n * @param options - Cache implementation to wrap\n * @returns A ServiceHandler ready for runtime.registerService('cache', handler)\n *\n * @example\n * ```ts\n * import { createCacheService } from '@kehto/services';\n *\n * const cache = createCacheService({\n * query: (f) => workerRelay.query(f),\n * store: (e) => workerRelay.store(e),\n * isAvailable: () => workerRelay.ready,\n * });\n * runtime.registerService('cache', cache);\n * ```\n */\nexport function createCacheService(options: CacheServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'cache',\n version: '1.0.0',\n description: 'Local event cache (IndexedDB, worker relay, etc.)',\n },\n\n handleMessage(_windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n\n if (!options.isAvailable()) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n options\n .query(filters)\n .then((events) => {\n for (const event of events) {\n send({ type: 'relay.event', subId, result: createRelayEventResult(event) } as NappletMessage);\n }\n send({ type: 'relay.eose', subId } as NappletMessage);\n })\n .catch(() => {\n // Cache query is best-effort — send EOSE even on failure\n send({ type: 'relay.eose', subId } as NappletMessage);\n });\n return;\n }\n\n if (message.type === 'relay.publish') {\n const event = relayMessage.event as NostrEvent | undefined;\n if (event && typeof event === 'object' && options.isAvailable()) {\n try {\n options.store(event);\n } catch {\n /* Cache write is best-effort */\n }\n }\n return;\n }\n },\n\n // Cache has no per-window state to clean up\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n}\n","import type { NappletMessage, NostrFilter } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\n\nexport interface CountRequest {\n /** NIP-01 filters with NIP-45 COUNT OR semantics. */\n readonly filters: NostrFilter[];\n /** Optional runtime-owned count policy hints. */\n readonly options?: Record<string, unknown>;\n /** Window requesting the count. */\n readonly windowId: string;\n}\n\nexport interface CountResult {\n readonly ok: boolean;\n readonly count?: number;\n readonly approximate?: boolean;\n readonly hll?: string;\n readonly relays?: readonly string[];\n readonly error?: string;\n readonly reason?: string;\n}\n\nexport interface CountServiceOptions {\n /**\n * Count matching events without returning event payloads.\n *\n * @param request - Validated count request.\n * @returns Exact/approximate count metadata, or a number for an exact count.\n */\n count(request: CountRequest): CountResult | number | Promise<CountResult | number>;\n\n /**\n * Optional refusal policy for unsupported or too-expensive filters.\n *\n * Return `true` to allow the filter, `false` for a generic refusal, or a\n * string with the refusal reason.\n *\n * @param filter - Filter being evaluated.\n * @param index - Filter index in the OR filter set.\n */\n isFilterSupported?(filter: NostrFilter, index: number): boolean | string;\n\n /** Maximum number of OR filters accepted before refusing the request. */\n maxFilters?: number;\n}\n\ntype CountServiceMessage = NappletMessage & {\n id?: unknown;\n filters?: unknown;\n options?: unknown;\n};\n\nconst DEFAULT_MAX_FILTERS = 16;\n\nexport function createCountService(options: CountServiceOptions): ServiceHandler {\n return {\n descriptor: {\n name: 'count',\n version: '1.0.0',\n description: 'Runtime-mediated NIP-01 filter counts',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (message.type !== 'count.query') return;\n\n const countMessage = message as CountServiceMessage;\n const id = typeof countMessage.id === 'string' ? countMessage.id : '';\n const filters = countMessage.filters;\n if (!isValidFilterArray(filters)) {\n send(result(id, { ok: false, error: 'invalid-filter', reason: 'count.query requires a non-empty filters array' }));\n return;\n }\n\n const maxFilters = options.maxFilters ?? DEFAULT_MAX_FILTERS;\n if (filters.length > maxFilters) {\n send(result(id, { ok: false, error: 'too-expensive', reason: `count.query accepts at most ${maxFilters} filters` }));\n return;\n }\n\n for (let i = 0; i < filters.length; i++) {\n const supported = options.isFilterSupported?.(filters[i]!, i) ?? true;\n if (supported !== true) {\n send(result(id, {\n ok: false,\n error: 'unsupported-filter',\n reason: typeof supported === 'string' ? supported : `filter ${i} is not supported by this count backend`,\n }));\n return;\n }\n }\n\n const request: CountRequest = {\n filters,\n windowId,\n ...(isPlainObject(countMessage.options) ? { options: countMessage.options } : {}),\n };\n\n Promise.resolve(options.count(request))\n .then((countResult) => {\n send(result(id, normalizeCountResult(countResult)));\n })\n .catch((error: unknown) => {\n send(result(id, {\n ok: false,\n error: 'count-unavailable',\n reason: error instanceof Error ? error.message : 'count backend failed',\n }));\n });\n },\n };\n}\n\nfunction result(id: string, value: CountResult): NappletMessage {\n return {\n type: 'count.query.result',\n id,\n ok: value.ok,\n ...(typeof value.count === 'number' ? { count: value.count } : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays.filter((relay): relay is string => typeof relay === 'string') } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n } as NappletMessage;\n}\n\nfunction normalizeCountResult(value: CountResult | number): CountResult {\n if (typeof value === 'number') {\n return Number.isFinite(value) && value >= 0\n ? { ok: true, count: Math.floor(value), approximate: false }\n : { ok: false, error: 'count-unavailable', reason: 'count backend returned an invalid count' };\n }\n\n const count = typeof value.count === 'number' && Number.isFinite(value.count) && value.count >= 0\n ? Math.floor(value.count)\n : undefined;\n return {\n ok: value.ok === true,\n ...(count !== undefined ? { count } : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n };\n}\n\nfunction isValidFilterArray(value: unknown): value is NostrFilter[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((filter) => isPlainObject(filter));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","\nimport type { NostrEvent, NostrFilter, NappletMessage } from '@napplet/core';\nimport type { ServiceHandler } from '@kehto/runtime';\nimport { createRelayEventResult, createRelayEventResultWithHints } from '@kehto/runtime';\nimport type { RelayPoolServiceOptions } from './relay-pool-service.js';\nimport type { CacheServiceOptions } from './cache-service.js';\n\n// Timer globals available in all JS runtimes\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** Default EOSE fallback timeout. */\nconst DEFAULT_EOSE_TIMEOUT_MS = 15_000;\n\n/**\n * Options for creating a coordinated relay service.\n *\n * @example\n * ```ts\n * const relay = createCoordinatedRelay({\n * relayPool: {\n * subscribe: (f, cb, urls) => pool.subscribe(f, cb, urls),\n * publish: (e) => pool.publish(e),\n * selectRelayTier: (f) => pool.selectRelays(f),\n * isAvailable: () => pool.connected,\n * },\n * cache: {\n * query: (f) => db.query(f),\n * store: (e) => db.store(e),\n * isAvailable: () => db.ready,\n * },\n * });\n * runtime.registerService('relay', relay);\n * ```\n */\nexport interface CoordinatedRelayOptions {\n /**\n * Relay pool implementation.\n * Uses the same interface as RelayPoolServiceOptions.\n */\n relayPool: RelayPoolServiceOptions;\n\n /**\n * Local cache implementation.\n * Uses the same interface as CacheServiceOptions.\n */\n cache: CacheServiceOptions;\n\n /**\n * EOSE fallback timeout in milliseconds.\n * Sent if relay pool doesn't respond within this time.\n * Default: 15000 (15 seconds).\n */\n eoseTimeoutMs?: number;\n}\n\n/** Internal state for a tracked subscription. */\ninterface TrackedSub {\n seenIds: Set<string>;\n cacheEose: boolean;\n relayEose: boolean;\n eoseSent: boolean;\n eoseTimer: unknown;\n relayHandle: { unsubscribe(): void } | null;\n}\n\ntype RelayServiceMessage = NappletMessage & {\n id?: unknown;\n subId?: unknown;\n filters?: unknown;\n event?: unknown;\n relay?: unknown;\n};\n\nfunction publishCoordinatedEvent(\n options: CoordinatedRelayOptions,\n relayMessage: RelayServiceMessage,\n send: (message: NappletMessage) => void,\n): void {\n const id = typeof relayMessage.id === 'string' ? relayMessage.id : '';\n const event = relayMessage.event as NostrEvent | undefined;\n if (!event || typeof event !== 'object') {\n send({ type: 'relay.publish.result', id, ok: false, error: 'invalid event' } as NappletMessage);\n return;\n }\n if (!options.relayPool.isAvailable()) {\n send({\n type: 'relay.publish.result',\n id,\n ok: false,\n error: 'no relay pool available',\n } as NappletMessage);\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n await options.relayPool.publish(event);\n if (options.cache.isAvailable()) {\n try { options.cache.store(event); } catch { /* cache storage is best-effort */ }\n }\n send({\n type: 'relay.publish.result',\n id,\n ok: true,\n event,\n eventId: event.id,\n } as NappletMessage);\n } catch (error) {\n send({\n type: 'relay.publish.result',\n id,\n ok: false,\n error: error instanceof Error && error.message ? error.message : 'publish failed',\n } as NappletMessage);\n }\n })();\n}\n\n/**\n * Create a coordinated relay service that combines relay pool and cache\n * into a single ServiceHandler with dedup and unified EOSE.\n *\n * On relay.subscribe: queries cache first, then subscribes to relay pool.\n * Events are deduplicated by ID. EOSE is sent after both sources complete.\n * On relay.publish: publishes to relay pool and stores in cache.\n * On relay.close: cancels relay pool subscription.\n *\n * @param options - Relay pool and cache implementations to coordinate\n * @returns A ServiceHandler ready for runtime.registerService('relay', handler)\n *\n * @example\n * ```ts\n * import { createCoordinatedRelay } from '@kehto/services';\n *\n * const relay = createCoordinatedRelay({ relayPool: myPool, cache: myCache });\n * runtime.registerService('relay', relay);\n * ```\n */\nexport function createCoordinatedRelay(options: CoordinatedRelayOptions): ServiceHandler {\n const timeoutMs = options.eoseTimeoutMs ?? DEFAULT_EOSE_TIMEOUT_MS;\n const subs = new Map<string, TrackedSub>();\n\n function maybeSendEose(subKey: string, subId: string, send: (msg: NappletMessage) => void): void {\n const sub = subs.get(subKey);\n if (!sub || sub.eoseSent) return;\n if (sub.cacheEose && sub.relayEose) {\n sub.eoseSent = true;\n clearTimeout(sub.eoseTimer);\n send({ type: 'relay.eose', subId } as NappletMessage);\n }\n }\n\n return {\n descriptor: {\n name: 'relay',\n version: '1.0.0',\n description: 'Coordinated relay pool + cache with dedup and unified EOSE',\n },\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n const relayMessage = message as RelayServiceMessage;\n if (message.type === 'relay.subscribe') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const filters = Array.isArray(relayMessage.filters)\n ? relayMessage.filters as NostrFilter[]\n : [];\n const subKey = `${windowId}:${subId}`;\n\n // Cancel existing subscription for this key\n const existing = subs.get(subKey);\n if (existing) {\n existing.relayHandle?.unsubscribe();\n clearTimeout(existing.eoseTimer);\n subs.delete(subKey);\n }\n\n const cacheAvailable = options.cache.isAvailable();\n const relayAvailable = options.relayPool.isAvailable();\n\n // Neither source available — send EOSE immediately\n if (!cacheAvailable && !relayAvailable) {\n send({ type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n\n const tracked: TrackedSub = {\n seenIds: new Set(),\n cacheEose: !cacheAvailable, // mark done if cache not available\n relayEose: !relayAvailable, // mark done if relay not available\n eoseSent: false,\n eoseTimer: null as unknown,\n relayHandle: null,\n };\n subs.set(subKey, tracked);\n\n function deliver(event: NostrEvent, relayHints?: string[]): void {\n if (tracked.seenIds.has(event.id)) return;\n tracked.seenIds.add(event.id);\n if (subs.has(subKey)) {\n const result = relayHints ? createRelayEventResultWithHints(event, relayHints) : createRelayEventResult(event);\n send({ type: 'relay.event', subId, result } as NappletMessage);\n }\n }\n\n // Query cache (async)\n if (cacheAvailable) {\n options.cache\n .query(filters)\n .then((events) => {\n for (const event of events) deliver(event);\n tracked.cacheEose = true;\n maybeSendEose(subKey, subId, send);\n })\n .catch(() => {\n // Cache query is best-effort\n tracked.cacheEose = true;\n maybeSendEose(subKey, subId, send);\n });\n }\n\n // Subscribe to relay pool\n if (relayAvailable) {\n tracked.eoseTimer = setTimeout(() => {\n if (!tracked.eoseSent) {\n tracked.relayEose = true;\n maybeSendEose(subKey, subId, send);\n }\n }, timeoutMs);\n\n const relayHint = typeof relayMessage.relay === 'string' && relayMessage.relay.length > 0\n ? relayMessage.relay\n : undefined;\n const relayUrls = relayHint ? [relayHint] : options.relayPool.selectRelayTier(filters);\n tracked.relayHandle = options.relayPool.subscribe(filters, (item, observedRelayUrls) => {\n if (item === 'EOSE') {\n clearTimeout(tracked.eoseTimer);\n tracked.relayEose = true;\n maybeSendEose(subKey, subId, send);\n return;\n }\n deliver(item, observedRelayUrls ?? relayUrls);\n // Store relay events in cache\n if (cacheAvailable) {\n try { options.cache.store(item); } catch { /* best-effort */ }\n }\n }, relayUrls);\n }\n return;\n }\n\n if (message.type === 'relay.close') {\n const subId = relayMessage.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n const entry = subs.get(subKey);\n if (entry) {\n entry.relayHandle?.unsubscribe();\n clearTimeout(entry.eoseTimer);\n subs.delete(subKey);\n }\n return;\n }\n\n if (message.type === 'relay.publish') {\n publishCoordinatedEvent(options, relayMessage, send);\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of subs) {\n if (key.startsWith(prefix)) {\n entry.relayHandle?.unsubscribe();\n clearTimeout(entry.eoseTimer);\n subs.delete(key);\n }\n }\n },\n };\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type {\n KeysBindingsMessage,\n KeysForwardMessage,\n KeyBinding,\n} from '@napplet/nap/keys/types';\n\n/**\n * Parsed chord struct. The napplet-facing API uses strings like\n * `\"Ctrl+Shift+K\"`; the service lowers them into this shape for matching.\n */\nexport interface ChordSpec {\n ctrl: boolean;\n alt: boolean;\n shift: boolean;\n meta: boolean;\n key: string;\n}\n/** Registry entry mapping a registered actionId to its owning window + chord. */\nexport interface ActionEntry {\n actionId: string;\n chord: ChordSpec;\n chordString: string;\n windowId: string;\n}\n\n// NAP-KEYS PR #9 at cecb642 leaves uniqueness scope implicit. Kehto scopes\n// registrations to their trusted source window; wire IDs remain app-local.\nexport function actionRegistryKey(windowId: string, actionId: string): string {\n return JSON.stringify([windowId, actionId]);\n}\n\nconst MODIFIER_ALIASES: Record<string, keyof Pick<ChordSpec, 'ctrl' | 'alt' | 'shift' | 'meta'>> = {\n ctrl: 'ctrl',\n control: 'ctrl',\n alt: 'alt',\n option: 'alt',\n shift: 'shift',\n meta: 'meta',\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n super: 'meta',\n};\n\nexport function parseChord(chord: string): ChordSpec {\n if (chord.length === 0) throw new Error('empty chord');\n const parts = chord.split('+');\n const out: ChordSpec = { ctrl: false, alt: false, shift: false, meta: false, key: '' };\n for (let i = 0; i < parts.length - 1; i++) {\n const tok = parts[i].trim().toLowerCase();\n if (tok.length === 0) continue;\n const slot = MODIFIER_ALIASES[tok];\n if (!slot) throw new Error(`unknown modifier: ${parts[i]}`);\n out[slot] = true;\n }\n const keyTok = parts[parts.length - 1].trim();\n if (keyTok.length === 0) throw new Error(`empty key in chord: ${chord}`);\n out.key = keyTok.length === 1 ? keyTok.toUpperCase() : keyTok;\n return out;\n}\n\n/**\n * Format a parsed chord in the canonical NAP-KEYS wire order:\n * Alt+Ctrl+Shift+Meta+Key.\n */\nexport function formatChord(spec: ChordSpec): string {\n const parts: string[] = [];\n if (spec.alt) parts.push('Alt');\n if (spec.ctrl) parts.push('Ctrl');\n if (spec.shift) parts.push('Shift');\n if (spec.meta) parts.push('Meta');\n parts.push(spec.key);\n return parts.join('+');\n}\n\nconst BROWSER_RESERVED_CTRL_KEYS = new Set(['L', 'N', 'R', 'T', 'W']);\n\n/**\n * NAP-KEYS reserved keys must never be bound or added to a napplet suppress\n * list. The browser-reserved set is intentionally conservative and covers the\n * shortcuts named by the draft plus the common tab/window variants.\n */\nexport function isReservedKeyChord(spec: ChordSpec): boolean {\n if (spec.key === 'Tab' || spec.key === 'Escape') return true;\n if ((spec.ctrl || spec.meta) && !spec.alt && BROWSER_RESERVED_CTRL_KEYS.has(spec.key)) return true;\n if (spec.alt && !spec.ctrl && !spec.meta && (spec.key === 'ArrowLeft' || spec.key === 'ArrowRight')) {\n return true;\n }\n return false;\n}\n\nexport function chordSpecKey(spec: {\n ctrl: boolean;\n alt: boolean;\n shift: boolean;\n meta: boolean;\n key: string;\n}): string {\n return `${spec.ctrl}|${spec.alt}|${spec.shift}|${spec.meta}|${spec.key}`;\n}\n\nexport function forwardPayload(m: KeysForwardMessage): {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n} {\n return {\n key: m.key,\n code: m.code,\n ctrlKey: m.ctrl,\n altKey: m.alt,\n shiftKey: m.shift,\n metaKey: m.meta,\n };\n}\n\nexport function eventKey(ev: KeyboardEvent): string {\n const k = ev.key.length === 1 ? ev.key.toUpperCase() : ev.key;\n return `${ev.ctrlKey}|${ev.altKey}|${ev.shiftKey}|${ev.metaKey}|${k}`;\n}\n\nexport function bindingsForWindow(\n windowId: string,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n): KeyBinding[] {\n const actionIds = windowIndex.get(windowId);\n if (!actionIds) return [];\n const bindings: KeyBinding[] = [];\n for (const actionId of actionIds) {\n const entry = registry.get(actionRegistryKey(windowId, actionId));\n if (!entry || entry.windowId !== windowId) continue;\n bindings.push({ actionId, key: entry.chordString });\n }\n return bindings;\n}\n\nexport function pushBindings(\n windowId: string,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n send: (msg: NappletMessage) => void,\n): void {\n const payload: KeysBindingsMessage = {\n type: 'keys.bindings',\n bindings: bindingsForWindow(windowId, registry, windowIndex),\n };\n send(payload as NappletMessage);\n}\n\nexport function removeActionFromWindowIndex(\n windowId: string,\n actionId: string,\n windowIndex: Map<string, Set<string>>,\n): void {\n const actions = windowIndex.get(windowId);\n if (actions?.delete(actionId) && actions.size === 0) windowIndex.delete(windowId);\n}\n","/**\n * keys-service.ts — NIP-5D keys NAP reference document-level listener implementation.\n *\n * Handles the 3 napplet -> shell request types from @napplet/nap/keys:\n * - keys.forward -> invokes options.onForward (hotkey passthrough, fire-and-forget)\n * - keys.registerAction -> parses action.defaultKey into a normalized chord binding,\n * stores bound subscriptions in an in-memory registry keyed\n * by windowId + actionId so onWindowDestroyed\n * can auto-unsubscribe, echoes { actionId, binding } as .result\n * when a binding is assigned, and pushes the window's complete\n * keys.bindings list\n * - keys.unregisterAction -> removes the subscription and pushes the window's\n * updated keys.bindings list\n *\n * Real listener: on service construction the handler attaches a single\n * `keydown` listener to `options.listenerTarget` (default: `document`). Each\n * keydown is matched against the chord-subscription registry; matches invoke\n * `options.onForward` with the DOM-shape payload and push a canonical\n * `keys.action` envelope back to the owning napplet via the per-window `send`\n * callback captured at `keys.registerAction` time. Subscriptions persist\n * across messages; `onWindowDestroyed(windowId)` drops all subscriptions owned\n * by the destroyed window as well as its cached `send` handle.\n *\n * On each document keydown matching a registered action, the service emits a\n * `keys.action` envelope to the action's owning napplet via the per-window\n * `send` callback — this is the canonical @napplet/nap/keys surface the SDK's\n * `keys.onAction(...)` helper consumes. Forwarded keydowns do not trigger\n * `keys.action`; the active napplet suppresses bound keys locally from the\n * `keys.bindings` list before forwarding.\n *\n * Field-name translation: @napplet/nap/keys uses the compact\n * { ctrl, alt, shift, meta } form on the wire; the shell's HotkeyHooks\n * (packages/shell/src/types.ts) expects the DOM-compatible\n * { ctrlKey, altKey, shiftKey, metaKey } form. This service performs the\n * translation so callers of `onForward` see the DOM shape.\n *\n * Shell -> napplet push envelopes `keys.bindings` are emitted here whenever\n * the registered bindings for a window change. The injected shim consumes\n * that complete list to suppress locally-bound keydowns before forwarding.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n KeysForwardMessage,\n KeysRegisterActionMessage,\n KeysRegisterActionResultMessage,\n KeysActionMessage,\n} from '@napplet/nap/keys/types';\nimport {\n actionRegistryKey,\n chordSpecKey,\n eventKey,\n formatChord,\n forwardPayload,\n isReservedKeyChord,\n parseChord,\n pushBindings,\n removeActionFromWindowIndex,\n type ActionEntry,\n type ChordSpec,\n} from './keys-service-internals.js';\n\n/**\n * Minimal structural subset of the DOM `KeyboardEvent` exposed to\n * `HostKeysBridge` subscribe callbacks. DOM `KeyboardEvent` satisfies this\n * structurally with no adapter needed. OS-bridge impls (Electron, Tauri —\n * out of v1.4 scope) synthesize this from native key events.\n */\nexport interface HostKeyEvent {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n /** True for OS autorepeat; the service filters these by default. */\n repeat?: boolean;\n}\n\n/**\n * Host-bridge contract for pluggable keyboard backends.\n *\n * The browser reference implementation (the default {@link createKeysService}\n * behaviour when `hostBridge` is omitted) registers a `document`-level keydown\n * listener and satisfies this interface structurally — it exposes\n * `subscribe(chord, callback) => unsubscribe` semantics but omits the two\n * OS-level optional fields (browsers cannot register global hotkeys without\n * privileged APIs).\n *\n * Host apps (Electron, Tauri) implement this interface in their own code and\n * pass it via `createKeysService({ hostBridge: myBridge })` — the service\n * then delegates subscription lifecycle to the bridge and remains browser-free.\n *\n * Reference implementations for Electron / Tauri are explicitly out of v1.4\n * scope and live in host-app examples / follow-up milestones (see\n * REQUIREMENTS.md \"Future Requirements\").\n *\n * @example\n * ```ts\n * declare const hostBridge: HostKeysBridge;\n * const keys = createKeysService({ hostBridge });\n * runtime.registerService('keys', keys);\n * ```\n */\nexport interface HostKeysBridge {\n /**\n * Subscribe a callback to a chord. Returns an unsubscribe handle.\n *\n * Implementations MUST:\n * - invoke `callback` exactly once per matching chord event (implementations\n * are responsible for any OS-autorepeat filtering)\n * - invoke `callback` synchronously during the event delivery\n * - remove only this subscription when its unsubscribe handle is called;\n * other callbacks for the same chord must remain live\n * - accept the string chord format documented by @napplet/nap/keys\n * (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`)\n */\n subscribe(chord: string, callback: (event: KeyboardEvent | HostKeyEvent) => void): () => void;\n\n /**\n * Optional: register an OS-level global hotkey (works even when the host\n * window is not focused). Returns true on success, false if the chord\n * cannot be registered (e.g. already claimed by another app).\n *\n * Omitted by the browser reference implementation — browsers cannot\n * register OS-level global hotkeys without privileged APIs. Electron\n * (`globalShortcut`) and Tauri (`GlobalShortcut`) provide this.\n */\n registerGlobalHotkey?(chord: string): boolean;\n\n /**\n * Optional: subscribe to OS-level global hotkey events (regardless of\n * focus). Returns an unsubscribe handle.\n *\n * Omitted by the browser reference implementation. See\n * {@link HostKeysBridge.registerGlobalHotkey}.\n */\n onGlobalHotkey?(callback: (chord: string) => void): () => void;\n}\n\n/** Keys service version — follows semver. */\nconst KEYS_SERVICE_VERSION = '1.3.0';\n\n/**\n * Options for creating a keys service via createKeysService().\n *\n * @example\n * ```ts\n * const keys = createKeysService({\n * onForward: (event) => {\n * // event has DOM-compatible field names: ctrlKey, altKey, etc.\n * hotkeyDispatcher.dispatch(event);\n * },\n * });\n * ```\n */\nexport interface KeysServiceOptions {\n /**\n * Called on `keys.forward` (napplet-forwarded chord) and on document keydown\n * handled by the reference listener. Receives the DOM-style field names\n * (ctrlKey/altKey/shiftKey/metaKey) to match the shell's HotkeyHooks\n * contract. The service translates from the wire shape\n * ({ ctrl, alt, shift, meta }) before invoking this callback.\n */\n onForward?: (event: {\n key: string;\n code: string;\n ctrlKey: boolean;\n altKey: boolean;\n shiftKey: boolean;\n metaKey: boolean;\n }) => void;\n /**\n * EventTarget to attach the default keydown listener to. Defaults to the\n * global `document` when running in a DOM environment, else an isolated\n * `new EventTarget()` (SSR / Node-test safe). Passing a fresh\n * `new EventTarget()` is useful for unit tests. Mirrors the pattern used\n * by the shell and service test harnesses.\n *\n * Ignored when `hostBridge` is provided — the bridge owns subscription\n * lifecycle and no document listener is attached.\n */\n listenerTarget?: EventTarget;\n /**\n * Optional pluggable backend for chord subscription. When provided, the\n * service delegates `keys.registerAction` → `bridge.subscribe(chord, cb)`\n * and stores the returned unsubscribe handle keyed on windowId + actionId. The\n * default document-listener path is NOT attached when `hostBridge` is\n * provided — the bridge is authoritative. See {@link HostKeysBridge}.\n */\n hostBridge?: HostKeysBridge;\n /**\n * Optional set of shell-reserved chords. Strings in the `@napplet/nap/keys`\n * wire format (e.g. `'Ctrl+Shift+K'`, `'Cmd+P'`). Reserved chords are not\n * assigned as napplet action bindings; document keydowns that match them\n * invoke `onForward` but do not push `keys.action`.\n *\n * Normalized once at service construction via the same chord parser used\n * for `action.defaultKey` — `'Ctrl+K'` / `'Control+k'` / `'ctrl+K'` all\n * match. Static; no runtime mutation. For dynamic reservation see the\n * deferred `HostKeysBridge.reserveAbsolute(chords)` extension.\n *\n * @example\n * ```ts\n * const keys = createKeysService({\n * reservedChords: ['Ctrl+Alt+T', 'Super+Space'],\n * onForward: (event) => wm.dispatch(event),\n * });\n * ```\n */\n reservedChords?: ReadonlyArray<string>;\n}\n\ntype SendNappletMessage = (msg: NappletMessage) => void;\n\nfunction rememberActionForWindow(\n windowIndex: Map<string, Set<string>>,\n windowId: string,\n actionId: string,\n): void {\n if (!windowIndex.has(windowId)) windowIndex.set(windowId, new Set());\n windowIndex.get(windowId)!.add(actionId);\n}\n\nfunction bindActionEntry(\n registry: Map<string, ActionEntry>,\n windowIndex: Map<string, Set<string>>,\n actionId: string,\n windowId: string,\n chord: ChordSpec,\n chordString: string,\n): void {\n registry.set(actionRegistryKey(windowId, actionId), { actionId, chord, chordString, windowId });\n rememberActionForWindow(windowIndex, windowId, actionId);\n}\n\nfunction bindActionAndMarkChanged(\n registry: Map<string, ActionEntry>,\n windowIndex: Map<string, Set<string>>,\n changedWindowIds: Set<string>,\n actionId: string,\n windowId: string,\n chord: ChordSpec,\n chordString: string,\n): string {\n bindActionEntry(registry, windowIndex, actionId, windowId, chord, chordString);\n changedWindowIds.add(windowId);\n return chordString;\n}\n\nfunction handleForwardMessage(\n message: NappletMessage,\n onForward?: KeysServiceOptions['onForward'],\n): void {\n onForward?.(forwardPayload(message as KeysForwardMessage));\n}\n\ntype KeysUnregisterActionMessage = NappletMessage & { actionId?: string };\ntype RegisterActionHandler = (\n windowId: string,\n message: KeysRegisterActionMessage,\n send: SendNappletMessage,\n) => void;\ntype UnregisterActionHandler = (\n windowId: string,\n message: KeysUnregisterActionMessage,\n send: SendNappletMessage,\n) => void;\n\nfunction sendUnknownKeysMethod(send: SendNappletMessage, message: NappletMessage): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown keys method: ${message.type}`,\n } as NappletMessage);\n}\n\nfunction createKeysMessageHandler(options: {\n onForward?: KeysServiceOptions['onForward'];\n onRegisterAction: RegisterActionHandler;\n onUnregisterAction: UnregisterActionHandler;\n}): (windowId: string, message: NappletMessage, send: SendNappletMessage) => void {\n return (windowId, message, send): void => {\n switch (message.type) {\n case 'keys.forward':\n handleForwardMessage(message, options.onForward);\n return;\n case 'keys.registerAction':\n options.onRegisterAction(windowId, message as KeysRegisterActionMessage, send);\n return;\n case 'keys.unregisterAction':\n options.onUnregisterAction(windowId, message as KeysUnregisterActionMessage, send);\n return;\n default:\n sendUnknownKeysMethod(send, message);\n }\n };\n}\n\nfunction sendRegisterActionResult(\n send: SendNappletMessage,\n message: KeysRegisterActionMessage,\n binding?: string,\n error?: string,\n): void {\n const result: KeysRegisterActionResultMessage & { error?: string } = {\n type: 'keys.registerAction.result',\n id: message.id,\n actionId: message.action.id,\n ...(binding ? { binding } : {}),\n ...(error ? { error } : {}),\n };\n send(result as NappletMessage);\n}\n\nfunction pushChangedBindings(\n changedWindowIds: Iterable<string>,\n registry: ReadonlyMap<string, ActionEntry>,\n windowIndex: ReadonlyMap<string, ReadonlySet<string>>,\n sendHandles: Map<string, SendNappletMessage>,\n fallbackSend: SendNappletMessage,\n): void {\n for (const changedWindowId of changedWindowIds) {\n const ownerSend = sendHandles.get(changedWindowId) ?? fallbackSend;\n pushBindings(changedWindowId, registry, windowIndex, ownerSend);\n if (!windowIndex.has(changedWindowId)) sendHandles.delete(changedWindowId);\n }\n}\n\n/**\n * Create a keys service handler.\n *\n * Attaches a single `keydown` listener to `options.listenerTarget`\n * (default `document`). Matching chord subscriptions invoke `options.onForward`\n * with a DOM-shape payload and push a `keys.action` envelope back to the\n * owning napplet via the per-window `send` callback captured at\n * `keys.registerAction` time. Returns a `ServiceHandler` augmented with a\n * `destroy()` method that detaches the listener and clears all registries.\n *\n * @param options - Optional configuration (onForward callback, listenerTarget)\n * @returns A ServiceHandler (with `destroy()`) to register with the runtime\n *\n * @example\n * ```ts\n * import { createKeysService } from '@kehto/services';\n *\n * const keys = createKeysService({\n * onForward: (event) => shellHotkeyDispatcher.execute(event),\n * });\n *\n * runtime.registerService('keys', keys);\n * // Later, on shell teardown:\n * keys.destroy();\n * ```\n */\nexport function createKeysService(\n options: KeysServiceOptions = {},\n): ServiceHandler & { destroy(): void } {\n const descriptor: ServiceDescriptor = {\n name: 'keys',\n version: KEYS_SERVICE_VERSION,\n description: options.hostBridge\n ? 'NIP-5D keys NAP reference handler (host-bridge delegated)'\n : 'NIP-5D keys NAP reference handler (document-level chord listener)',\n };\n\n const reservedChordKeys: Set<string> = new Set();\n if (options.reservedChords) {\n for (const chordStr of options.reservedChords) {\n // parseChord throws on malformed input — let it bubble up at construction\n // so misconfigured shells fail loudly at boot, not silently at runtime.\n reservedChordKeys.add(chordSpecKey(parseChord(chordStr)));\n }\n }\n\n const isUnavailableBinding = (chord: ChordSpec): boolean =>\n reservedChordKeys.has(chordSpecKey(chord)) || isReservedKeyChord(chord);\n\n if (options.hostBridge) {\n const bridge = options.hostBridge;\n // windowId → Set<actionId> — parallels Branch B for scoped cleanup.\n const bridgeWindowActions = new Map<string, Set<string>>();\n const bridgeActionRegistry = new Map<string, ActionEntry>();\n const bridgeSendHandles = new Map<string, (msg: NappletMessage) => void>();\n // (windowId, actionId) → unsubscribe handle returned from bridge.subscribe.\n const unsubscribeHandles = new Map<string, () => void>();\n\n const handleBridgeRegisterAction: RegisterActionHandler = (windowId, m, send) => {\n const registryKey = actionRegistryKey(windowId, m.action.id);\n bridgeSendHandles.set(windowId, send);\n let binding: string | undefined;\n const changedWindowIds = new Set<string>();\n\n if (m.action.defaultKey) {\n try {\n const chord = parseChord(m.action.defaultKey);\n const normalizedChord = formatChord(chord);\n const existingEntry = bridgeActionRegistry.get(registryKey);\n const existing = unsubscribeHandles.get(registryKey);\n let nextUnsubscribe: (() => void) | undefined;\n\n if (!isUnavailableBinding(chord)) {\n nextUnsubscribe = bridge.subscribe(normalizedChord, (ev) => {\n // Normalize either KeyboardEvent or HostKeyEvent to the DOM-shape onForward payload.\n const e = ev as KeyboardEvent | HostKeyEvent;\n // Bridges may not filter autorepeat — we do, matching Branch B semantics.\n if ('repeat' in e && e.repeat) return;\n options.onForward?.({\n key: e.key,\n code: e.code,\n ctrlKey: e.ctrlKey,\n altKey: e.altKey,\n shiftKey: e.shiftKey,\n metaKey: e.metaKey,\n });\n const payload: KeysActionMessage = {\n type: 'keys.action',\n actionId: m.action.id,\n };\n send(payload as NappletMessage);\n });\n }\n\n if (existing) {\n try {\n existing();\n } catch {\n /* best-effort */\n }\n unsubscribeHandles.delete(registryKey);\n }\n if (existingEntry) changedWindowIds.add(existingEntry.windowId);\n removeActionFromWindowIndex(windowId, m.action.id, bridgeWindowActions);\n bridgeActionRegistry.delete(registryKey);\n\n if (nextUnsubscribe) {\n unsubscribeHandles.set(registryKey, nextUnsubscribe);\n binding = bindActionAndMarkChanged(\n bridgeActionRegistry,\n bridgeWindowActions,\n changedWindowIds,\n m.action.id,\n windowId,\n chord,\n normalizedChord,\n );\n }\n } catch (err) {\n sendRegisterActionResult(\n send,\n m,\n undefined,\n `bridge subscribe failed: ${(err as Error).message}`,\n );\n return;\n }\n }\n sendRegisterActionResult(send, m, binding);\n pushChangedBindings(\n changedWindowIds,\n bridgeActionRegistry,\n bridgeWindowActions,\n bridgeSendHandles,\n send,\n );\n };\n\n const handleBridgeUnregisterAction: UnregisterActionHandler = (windowId, m, send) => {\n if (!m.actionId) return;\n const registryKey = actionRegistryKey(windowId, m.actionId);\n const unsubscribe = unsubscribeHandles.get(registryKey);\n const entry = bridgeActionRegistry.get(registryKey);\n if (!unsubscribe) return;\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n unsubscribeHandles.delete(registryKey);\n bridgeActionRegistry.delete(registryKey);\n removeActionFromWindowIndex(windowId, m.actionId, bridgeWindowActions);\n if (entry) {\n const ownerSend = bridgeSendHandles.get(entry.windowId) ?? send;\n pushBindings(entry.windowId, bridgeActionRegistry, bridgeWindowActions, ownerSend);\n if (!bridgeWindowActions.has(entry.windowId)) {\n bridgeSendHandles.delete(entry.windowId);\n }\n }\n };\n\n return {\n descriptor,\n\n handleMessage: createKeysMessageHandler({\n onForward: options.onForward,\n onRegisterAction: handleBridgeRegisterAction,\n onUnregisterAction: handleBridgeUnregisterAction,\n }),\n\n onWindowDestroyed(windowId: string): void {\n const actions = bridgeWindowActions.get(windowId);\n if (!actions) return;\n for (const actionId of actions) {\n const registryKey = actionRegistryKey(windowId, actionId);\n const unsubscribe = unsubscribeHandles.get(registryKey);\n if (unsubscribe) {\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n }\n unsubscribeHandles.delete(registryKey);\n bridgeActionRegistry.delete(registryKey);\n }\n bridgeWindowActions.delete(windowId);\n bridgeSendHandles.delete(windowId);\n },\n\n destroy(): void {\n for (const unsubscribe of unsubscribeHandles.values()) {\n try {\n unsubscribe();\n } catch {\n /* best-effort */\n }\n }\n unsubscribeHandles.clear();\n bridgeActionRegistry.clear();\n bridgeWindowActions.clear();\n bridgeSendHandles.clear();\n },\n };\n }\n\n const actionRegistry = new Map<string, ActionEntry>(); // (windowId, actionId) → owned binding\n const windowActions = new Map<string, Set<string>>(); // windowId → Set<actionId>\n // Per-window `send` callback captured at registerAction time. Used to push\n // keys.action envelopes back to the owning napplet on chord match — this is\n // the canonical @napplet/nap/keys surface the SDK's `keys.onAction(...)`\n // helper consumes.\n const sendHandles = new Map<string, (msg: NappletMessage) => void>(); // windowId → send\n\n // ─── Listener target (SSR / test-safe fallback) ─\n const target: EventTarget =\n options.listenerTarget ??\n (typeof document !== 'undefined' ? document : new EventTarget());\n\n function chordMatches(spec: ChordSpec, ev: KeyboardEvent): boolean {\n if (spec.ctrl !== ev.ctrlKey) return false;\n if (spec.alt !== ev.altKey) return false;\n if (spec.shift !== ev.shiftKey) return false;\n if (spec.meta !== ev.metaKey) return false;\n const evKey = ev.key.length === 1 ? ev.key.toUpperCase() : ev.key;\n return spec.key === evKey;\n }\n\n const listener = (rawEv: Event): void => {\n const ev = rawEv as KeyboardEvent;\n if (ev.repeat) return; // ignore OS autorepeat — matches \"I pressed it once\" intent (CONTEXT Area 1)\n\n // Reserved chords fire onForward but suppress keys.action fan-out to napplets.\n const isReserved = reservedChordKeys.has(eventKey(ev));\n\n // Determine if any registered action would match — needed to decide\n // whether to fire onForward on a non-reserved keydown (legacy parity).\n // A reserved chord fires onForward regardless of napplet registration\n // (WM-launcher case: shell declares chord, no napplet registers it).\n let anyMatch = false;\n for (const entry of actionRegistry.values()) {\n if (chordMatches(entry.chord, ev)) {\n anyMatch = true;\n break;\n }\n }\n\n if (isReserved || anyMatch) {\n options.onForward?.({\n key: ev.key,\n code: ev.code,\n ctrlKey: ev.ctrlKey,\n altKey: ev.altKey,\n shiftKey: ev.shiftKey,\n metaKey: ev.metaKey,\n });\n }\n\n if (isReserved) return; // reserved → no napplet fan-out\n\n // Canonical shell→napplet push: emit keys.action to the owning napplet via\n // its captured send callback. The SDK's keys.onAction helper subscribes to\n // this envelope.\n for (const entry of actionRegistry.values()) {\n if (chordMatches(entry.chord, ev)) {\n const send = sendHandles.get(entry.windowId);\n if (send) {\n const payload: KeysActionMessage = {\n type: 'keys.action',\n actionId: entry.actionId,\n };\n send(payload as NappletMessage);\n }\n // Intentionally no `break` — two actions subscribing to the same chord\n // both receive the event. Conflict resolution is an explicit v1.5+\n // concern per CONTEXT.md Deferred Ideas.\n }\n }\n };\n\n target.addEventListener('keydown', listener);\n\n const handleDocumentRegisterAction: RegisterActionHandler = (windowId, m, send) => {\n const registryKey = actionRegistryKey(windowId, m.action.id);\n // Capture (or refresh) the per-window send callback. The runtime's service\n // handler contract keeps `send` valid until onWindowDestroyed(windowId).\n sendHandles.set(windowId, send);\n let binding: string | undefined;\n const changedWindowIds = new Set<string>();\n\n if (m.action.defaultKey) {\n try {\n const chord = parseChord(m.action.defaultKey);\n const normalizedChord = formatChord(chord);\n const existing = actionRegistry.get(registryKey);\n if (existing) {\n changedWindowIds.add(existing.windowId);\n removeActionFromWindowIndex(windowId, m.action.id, windowActions);\n actionRegistry.delete(registryKey);\n }\n if (!isUnavailableBinding(chord)) {\n binding = bindActionAndMarkChanged(actionRegistry, windowActions, changedWindowIds, m.action.id, windowId, chord, normalizedChord);\n }\n } catch (err) {\n sendRegisterActionResult(\n send,\n m,\n undefined,\n `invalid chord: ${(err as Error).message}`,\n );\n return;\n }\n }\n sendRegisterActionResult(send, m, binding);\n pushChangedBindings(changedWindowIds, actionRegistry, windowActions, sendHandles, send);\n };\n\n const handleDocumentUnregisterAction: UnregisterActionHandler = (windowId, m, send) => {\n if (!m.actionId) return;\n const registryKey = actionRegistryKey(windowId, m.actionId);\n const entry = actionRegistry.get(registryKey);\n if (!entry) return;\n actionRegistry.delete(registryKey);\n const set = windowActions.get(entry.windowId);\n if (set) {\n set.delete(m.actionId);\n // If the window has no remaining actions, drop its cached send that no\n // longer subscribes to anything.\n if (set.size === 0) windowActions.delete(entry.windowId);\n }\n const ownerSend = sendHandles.get(entry.windowId) ?? send;\n pushBindings(entry.windowId, actionRegistry, windowActions, ownerSend);\n if (!windowActions.has(entry.windowId)) sendHandles.delete(entry.windowId);\n };\n\n return {\n descriptor,\n\n handleMessage: createKeysMessageHandler({\n onForward: options.onForward,\n onRegisterAction: handleDocumentRegisterAction,\n onUnregisterAction: handleDocumentUnregisterAction,\n }),\n\n onWindowDestroyed(windowId: string): void {\n const actions = windowActions.get(windowId);\n if (actions) {\n for (const actionId of actions) actionRegistry.delete(actionRegistryKey(windowId, actionId));\n windowActions.delete(windowId);\n }\n sendHandles.delete(windowId);\n },\n\n destroy(): void {\n target.removeEventListener('keydown', listener);\n actionRegistry.clear();\n windowActions.clear();\n sendHandles.clear();\n },\n };\n}\n","import type { MediaAction, MediaMetadata } from '@napplet/nap/media/types';\nimport type { HostMediaBridge } from './media-service.js';\n\n/** Silent-audio prime data URL (4 kHz silent WAV, 44 bytes, zero network dependency).\n * Browsers refuse to render OS media controls without a playing audio element —\n * this silent loop primes the MediaSession API. Per CONTEXT.md Area 1. */\nconst SILENT_AUDIO_DATA_URL =\n 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=';\n\n/** Default action set — all 5 nap-media transport actions. */\nexport const DEFAULT_MEDIA_ACTIONS: readonly MediaAction[] = ['play', 'pause', 'next', 'prev', 'seek'];\n\n/** Minimal subset of navigator.mediaSession the browser bridge depends on. Makes the bridge\n * Node/test-safe: unit tests pass a MockMediaSession via mediaSessionTarget.\n * The handler parameter uses `details?` (optional) so both the real DOM impl\n * (which always passes an object) and test mocks that omit details both satisfy\n * this type structurally. */\nexport type MediaSessionTarget = {\n metadata: MediaMetadataLike | null;\n playbackState: 'none' | 'playing' | 'paused';\n setActionHandler(action: string, handler: ((details?: { action?: string; seekTime?: number }) => void) | null): void;\n};\n\n/** Structural subset of the DOM MediaMetadata class — assignable from a plain object\n * with title/artist/album/artwork fields. The browser impl uses `new MediaMetadata({...})`;\n * tests can pass a plain object. */\nexport type MediaMetadataLike = { title?: string; artist?: string; album?: string; artwork?: unknown };\n\n/** Mapping from DOM MediaSession action names to nap-media MediaAction literals. */\nconst ACTION_MATRIX: ReadonlyArray<[string, MediaAction]> = [\n ['play', 'play'],\n ['pause', 'pause'],\n ['nexttrack', 'next'],\n ['previoustrack', 'prev'],\n ['seekto', 'seek'],\n];\n\n/**\n * Reference browser implementation of {@link HostMediaBridge}.\n *\n * Mirrors metadata to `navigator.mediaSession.metadata` (via the DOM\n * `MediaMetadata` constructor when available; plain-object fallback in test\n * envs). Mirrors playback state to `navigator.mediaSession.playbackState`\n * with the canonical mapping: 'playing' maps to 'playing', 'paused' maps to\n * 'paused', 'buffering' maps to 'paused', 'stopped' maps to 'none'. Installs\n * `setActionHandler` callbacks for play/pause/nexttrack/previoustrack/seekto\n * that fan into the onAction subscriber with the mapped `MediaAction` literal\n * (and `value` from `details.seekTime` for seekto). When `setActiveSession` is\n * called with a non-null `actions` parameter, only the declared actions get\n * active handlers — the remaining are cleared (matching the capabilities\n * narrowing behavior of Plan 27-01's inline implementation).\n *\n * Installs a silent-audio prime (4 kHz silent WAV data URL) when the first\n * session becomes active (setActiveSession called with a non-null sessionId) —\n * browsers refuse to render OS media controls without a playing audio element.\n * Removes the element when destroySession brings the active session count to\n * zero.\n *\n * @param opts.mediaSessionTarget - Override navigator.mediaSession (tests).\n * @param opts.documentTarget - Override document (tests; pass null to disable silent-audio prime).\n */\nexport function createBrowserMediaBridge(opts: {\n mediaSessionTarget?: MediaSessionTarget;\n documentTarget?: Document | null;\n} = {}): HostMediaBridge {\n const ms: MediaSessionTarget | null =\n opts.mediaSessionTarget\n ?? (typeof navigator !== 'undefined' && 'mediaSession' in navigator\n ? (navigator.mediaSession as MediaSessionTarget)\n : null);\n const doc: Document | null =\n opts.documentTarget !== undefined\n ? opts.documentTarget\n : (typeof document !== 'undefined' ? document : null);\n\n let silentAudioEl: HTMLAudioElement | null = null;\n let activeSessionId: string | null = null;\n let sessionsActive = 0;\n const actionCallbacks = new Set<(sessionId: string, action: MediaAction, value?: number) => void>();\n\n function primeSilentAudio(): void {\n if (silentAudioEl || !doc) return;\n const el = doc.createElement('audio') as HTMLAudioElement;\n el.src = SILENT_AUDIO_DATA_URL;\n el.loop = true;\n el.style.display = 'none';\n (el as HTMLAudioElement).setAttribute('data-kehto-silent-audio-prime', 'true');\n doc.body.appendChild(el);\n void el.play().catch(() => { /* autoplay refused — metadata mirror still works */ });\n silentAudioEl = el;\n }\n\n function teardownSilentAudio(): void {\n if (!silentAudioEl) return;\n try { silentAudioEl.pause(); } catch { /* best-effort */ }\n try { silentAudioEl.remove(); } catch { /* best-effort */ }\n silentAudioEl = null;\n }\n\n /**\n * Install action handlers for the given set of nap-media actions. Actions not\n * in the set get their handler cleared. Matches Plan 27-01's installActionHandlersFor\n * behavior exactly so capabilities-narrowing tests continue to pass.\n */\n function applyActionHandlers(actions: readonly MediaAction[] = DEFAULT_MEDIA_ACTIONS): void {\n if (!ms) return;\n for (const [domAction, napAction] of ACTION_MATRIX) {\n if (!actions.includes(napAction)) {\n try { ms.setActionHandler(domAction, null); } catch { /* best-effort */ }\n continue;\n }\n ms.setActionHandler(domAction, (details) => {\n if (!activeSessionId) return;\n const value = napAction === 'seek' && typeof details?.seekTime === 'number' ? details.seekTime : undefined;\n for (const cb of actionCallbacks) {\n cb(activeSessionId, napAction, value);\n }\n });\n }\n }\n\n function writeMetadata(metadata: MediaMetadata | undefined): void {\n if (!ms) return;\n if (!metadata) { ms.metadata = null; return; }\n const artwork = metadata.artwork?.url ? [{ src: metadata.artwork.url }] : undefined;\n const init: MediaMetadataLike & { artwork?: unknown } = {\n title: metadata.title ?? '',\n artist: metadata.artist ?? '',\n album: metadata.album ?? '',\n ...(artwork ? { artwork } : {}),\n };\n try {\n const ctor = (globalThis as typeof globalThis & { MediaMetadata?: new (init: MediaMetadataLike) => MediaMetadataLike }).MediaMetadata;\n ms.metadata = ctor ? new ctor(init) : (init as MediaMetadataLike);\n } catch {\n ms.metadata = init as MediaMetadataLike;\n }\n }\n\n return {\n setMetadata(sessionId, metadata) {\n if (sessionId === activeSessionId) writeMetadata(metadata);\n },\n setPlaybackState(sessionId, state) {\n if (!ms || sessionId !== activeSessionId) return;\n ms.playbackState =\n state === 'playing' ? 'playing'\n : state === 'paused' || state === 'buffering' ? 'paused'\n : 'none';\n },\n onAction(callback) {\n actionCallbacks.add(callback);\n return () => { actionCallbacks.delete(callback); };\n },\n setActiveSession(sessionId, actions) {\n activeSessionId = sessionId;\n if (!sessionId) {\n if (ms) {\n ms.metadata = null;\n ms.playbackState = 'none';\n for (const [domAction] of ACTION_MATRIX) {\n try { ms.setActionHandler(domAction, null); } catch { /* best-effort */ }\n }\n }\n return;\n }\n // Prime silent-audio on first session becoming active.\n if (sessionsActive === 0) { primeSilentAudio(); sessionsActive = 1; }\n applyActionHandlers(actions ?? DEFAULT_MEDIA_ACTIONS);\n },\n destroySession(_sessionId) {\n sessionsActive = Math.max(0, sessionsActive - 1);\n if (sessionsActive === 0) teardownSilentAudio();\n },\n };\n}\n","/**\n * media-service.ts — NIP-5D media NAP reference service (navigator.mediaSession\n * reference implementation).\n *\n * Handles the napplet-owned subset of @napplet/nap/media:\n * media.session.create (result), media.session.update, media.session.destroy,\n * media.state, media.capabilities.\n *\n * HostMediaBridge contract: {@link HostMediaBridge} defines the pluggable backend\n * contract for metadata/state mirroring + action routing. The browser reference\n * implementation is {@link createBrowserMediaBridge} (mirrors to navigator.mediaSession\n * with setActionHandler matrix). When no hostBridge option is passed, createMediaService\n * internally uses createBrowserMediaBridge as the default — behavior is identical\n * to the Plan 27-01 single-path implementation.\n *\n * navigator.mediaSession mirroring: on session.create the browser bridge mirrors the\n * napplet-supplied metadata to navigator.mediaSession.metadata via new MediaMetadata()\n * and installs setActionHandler callbacks for the 5 OS transport actions\n * (play / pause / nexttrack / previoustrack / seekto). Each callback emits a canonical\n * media.command envelope to the owning napplet — that is the @napplet/nap/media\n * MediaCommandMessage shape consumed by the SDK's mediaOnCommand() helper.\n *\n * NAP-MEDIA now distinguishes napplet-owned playback from shell-owned\n * playback. This reference backend supports `owner: \"napplet\"` because it\n * mirrors a napplet's own media element to navigator.mediaSession. It rejects\n * `owner: \"shell\"` until a host bridge provides policy-checked source fetching\n * and playback.\n *\n * media.command push: when the OS user clicks a media control (hardware key, lock-screen\n * transport, OS media overlay), the bridge's onAction callback fires. The service looks\n * up the active session's owning napplet, invokes the per-window send callback captured\n * at session.create time, and delivers:\n * { type: 'media.command', sessionId, action, value? }\n * where action is the nap-media MediaAction literal (play|pause|next|prev|seek) and\n * value is the seekTime (seconds) for seek.\n *\n * Silent-audio prime: the browser bridge creates a hidden <audio> element with a\n * 4 kHz silent WAV data URL and plays it when the first session becomes active\n * (setActiveSession with a non-null sessionId). Without a playing audio element in\n * the host page, most browsers refuse to render OS media controls. The element is\n * cleaned up when the last session is destroyed (via bridge.destroySession).\n *\n * Multi-session registry with last-active-wins semantics: every session.create is\n * tracked in a Map<sessionId, SessionEntry>; any media.state report promotes that\n * session to active. The active session's metadata and playback state are reflected\n * via bridge.setMetadata / bridge.setPlaybackState. When the active session is\n * destroyed, the next-most-recently-touched session is promoted automatically.\n *\n * This is the canonical @napplet/nap/media NIP-5D path. It handles media\n * protocol envelopes directly and does not depend on convention-topic routing.\n *\n * Shell -> Napplet push types (media.command) are emitted here when\n * bridge.onAction callbacks fire — this is the canonical Phase 27 real backend.\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n MediaAction,\n MediaCapabilitiesMessage,\n MediaCommandMessage,\n MediaMetadata,\n MediaSessionDestroyMessage,\n MediaSessionUpdateMessage,\n MediaStateMessage,\n} from '@napplet/nap/media/types';\nimport {\n createBrowserMediaBridge,\n DEFAULT_MEDIA_ACTIONS,\n type MediaSessionTarget,\n} from './browser-media-bridge.js';\n\nexport { createBrowserMediaBridge } from './browser-media-bridge.js';\nexport type {\n MediaMetadataLike,\n MediaSessionTarget,\n} from './browser-media-bridge.js';\n\n/** Registry entry — maps a sessionId back to its owning window, metadata snapshot,\n * state snapshot, and declared capabilities. Internal; never on the wire. */\ninterface SessionEntry {\n sessionId: string;\n windowId: string;\n owner: MediaPlaybackOwner;\n source: MediaSourceRef | undefined;\n metadata: MediaMetadata | undefined;\n state: { status: 'playing' | 'paused' | 'stopped' | 'buffering'; position?: number; duration?: number; volume?: number } | undefined;\n actions: readonly MediaAction[]; // from media.capabilities; defaults to ['play','pause','next','prev','seek']\n /** Monotonic tick updated on every session.create / session.update / state — used for last-active-wins resolution when the current active session is destroyed. */\n lastTouched: number;\n}\n\n/** Media service version — follows semver. */\nconst MEDIA_SERVICE_VERSION = '1.1.0';\n\nexport type MediaPlaybackOwner = 'shell' | 'napplet';\n\nexport interface MediaSourceRef {\n url?: string;\n blossomHash?: string;\n nostr?: {\n eventId?: string;\n address?: string;\n relays?: string[];\n };\n mimeType?: string;\n}\n\nexport interface MediaSessionCreateOptions {\n owner: MediaPlaybackOwner;\n sessionId?: string;\n source?: MediaSourceRef;\n metadata?: MediaMetadata;\n capabilities?: MediaAction[];\n autoplay?: boolean;\n live?: boolean;\n}\n\ntype MediaSessionCreateEnvelope = NappletMessage & Partial<MediaSessionCreateOptions> & {\n type: 'media.session.create';\n id?: string;\n};\n\ntype MediaSessionCreateResultEnvelope = NappletMessage & {\n type: 'media.session.create.result';\n id: string;\n sessionId?: string;\n owner?: MediaPlaybackOwner;\n error?: string;\n};\n\n/**\n * Host-bridge contract for pluggable media backends.\n *\n * The browser reference implementation ({@link createBrowserMediaBridge}) mirrors\n * napplet-reported metadata/state to `navigator.mediaSession` and installs\n * `setActionHandler` callbacks that fan into the bridge's onAction subscribers.\n * It satisfies this interface with all 5 fields implemented (setActiveSession\n * switches the active session and optionally re-applies action-handler narrowing;\n * destroySession tears down the silent-audio prime on last-session teardown).\n *\n * Host apps (Electron, Tauri) implement this interface in their own code and\n * pass it via `createMediaService({ hostBridge: myBridge })` — the service\n * then delegates metadata/state mirroring + action routing to the bridge and\n * remains browser-free. Session-ownership bookkeeping (which windowId owns\n * which sessionId, which send callback routes media.command back to which\n * napplet) stays in the service layer — that's wire-protocol concern, not a\n * bridge concern.\n *\n * Reference implementations for Electron / Tauri are explicitly out of v1.4\n * scope and live in host-app examples / follow-up milestones (see\n * REQUIREMENTS.md \"Future Requirements\").\n *\n * @example\n * ```ts\n * // Host-app pseudocode (Electron main-process relay):\n * const electronBridge: HostMediaBridge = {\n * setMetadata(sessionId, md) { mediaBridge.sendMetadata({ sessionId, md }); },\n * setPlaybackState(sessionId, state) { mediaBridge.sendPlaybackState({ sessionId, state }); },\n * onAction(cb) {\n * const handler = (_: unknown, msg: { sessionId: string; action: MediaAction; value?: number }) =>\n * cb(msg.sessionId, msg.action, msg.value);\n * mediaBridge.onAction(handler);\n * return () => mediaBridge.offAction(handler);\n * },\n * };\n * const media = createMediaService({ hostBridge: electronBridge });\n * runtime.registerService('media', media);\n * ```\n */\nexport interface HostMediaBridge {\n /**\n * Set the metadata displayed on the OS transport surface for a session.\n * Called on session.create (with initial metadata) and on session.update\n * (with merged metadata) whenever the session is the active session.\n * Implementations MUST be idempotent.\n */\n setMetadata(sessionId: string, metadata: MediaMetadata): void;\n\n /**\n * Set the playback state for a session. Called on media.state reports\n * whenever the session is the active session. State strings match\n * nap-media MediaState.status exactly. Implementations MUST be idempotent.\n */\n setPlaybackState(sessionId: string, state: 'playing' | 'paused' | 'stopped' | 'buffering'): void;\n\n /**\n * Subscribe to OS-level action events (user clicks play/pause/seek/next/prev\n * on the transport surface). Returns an unsubscribe handle.\n *\n * The callback receives `(sessionId, action, value?)`. `sessionId` is the\n * bridge's currently-active session (the browser impl tracks this internally\n * via setActionHandler-at-fire-time; native impls track via setActiveSession).\n * `value` is populated for `action === 'seek'` (seek target in seconds) and\n * for `action === 'volume'` (0.0-1.0). The service dispatches the resulting\n * `media.command` envelope to the owning napplet of that session.\n */\n onAction(callback: (sessionId: string, action: MediaAction, value?: number) => void): () => void;\n\n /**\n * Optional: notify the bridge that the active session has changed. The\n * browser reference impl uses this to switch which session's metadata/state\n * is mirrored to the singleton navigator.mediaSession and to install (or\n * clear) action handlers for the session's declared capabilities.\n *\n * The optional `actions` parameter carries the session's declared capability\n * set so the bridge can narrow which OS transport buttons are active. When\n * omitted, the bridge applies its default set. Native OS bridges that track\n * active-session state internally may omit this field entirely.\n */\n setActiveSession?(sessionId: string | null, actions?: readonly MediaAction[]): void;\n\n /**\n * Optional: tear down per-session resources. The browser reference impl\n * uses this to remove the silent-audio prime element when the last session\n * is destroyed. Bridges that need no per-session teardown may omit this field.\n */\n destroySession?(sessionId: string): void;\n}\n\n/**\n * Optional host callbacks for the media service.\n *\n * Host shells can hook session creation and state updates to drive\n * their own UI (e.g., now-playing banner) without replacing the\n * ServiceHandler wholesale.\n *\n * @example\n * ```ts\n * const media = createMediaService({\n * onSessionCreate: (windowId, sessionId, metadata) => {\n * console.log(`[${windowId}] created session ${sessionId}`, metadata);\n * },\n * onState: (windowId, sessionId, state) => {\n * nowPlaying.update(windowId, state);\n * },\n * });\n *\n * runtime.registerService('media', media);\n * ```\n */\nexport interface MediaServiceOptions {\n /** Called when a napplet creates a session. May inspect/record the session. */\n onSessionCreate?: (windowId: string, sessionId: string, metadata?: unknown) => void;\n /** Called on media.state updates — high-frequency; keep handler work minimal. */\n onState?: (windowId: string, sessionId: string, state: unknown) => void;\n /** Called when a napplet destroys a session. */\n onSessionDestroy?: (windowId: string, sessionId: string) => void;\n /** Called when a napplet updates session metadata. */\n onSessionUpdate?: (windowId: string, sessionId: string, metadata: unknown) => void;\n /** Called when a napplet declares capabilities for a session. */\n onCapabilities?: (windowId: string, sessionId: string, actions: unknown) => void;\n\n /**\n * MediaSession target override (used by default browser bridge only).\n * Defaults to `navigator.mediaSession` when running in a browser. Pass a\n * MockMediaSession in unit tests. Ignored when `hostBridge` is provided.\n */\n mediaSessionTarget?: MediaSessionTarget;\n\n /**\n * DOM document override (used by default browser bridge only).\n * Defaults to `document` when available. Set to null to disable the silent-audio\n * prime entirely — useful in unit tests. Ignored when `hostBridge` is provided.\n */\n documentTarget?: Document | null;\n\n /**\n * Optional pluggable backend for metadata/state mirroring + OS action handling.\n * When provided, the service delegates setMetadata / setPlaybackState / onAction\n * to the bridge and skips navigator.mediaSession entirely. When omitted, the\n * service internally uses {@link createBrowserMediaBridge} as the default.\n * See {@link HostMediaBridge}.\n */\n hostBridge?: HostMediaBridge;\n}\n\ninterface MediaServiceState {\n bridge: HostMediaBridge;\n options: MediaServiceOptions;\n sessionRegistry: Map<string, SessionEntry>;\n windowSessions: Map<string, Set<string>>;\n sendHandles: Map<string, (msg: NappletMessage) => void>;\n activeSessionId: string | null;\n touchCounter: number;\n sessionCounter: number;\n}\n\nfunction createMediaServiceState(options: MediaServiceOptions, bridge: HostMediaBridge): MediaServiceState {\n return {\n bridge,\n options,\n sessionRegistry: new Map<string, SessionEntry>(),\n windowSessions: new Map<string, Set<string>>(),\n sendHandles: new Map<string, (msg: NappletMessage) => void>(),\n activeSessionId: null,\n touchCounter: 0,\n sessionCounter: 0,\n };\n}\n\nfunction setActive(state: MediaServiceState, sessionId: string | null, actions?: readonly MediaAction[]): void {\n state.activeSessionId = sessionId;\n state.bridge.setActiveSession?.(sessionId, actions);\n if (!sessionId) return;\n const entry = state.sessionRegistry.get(sessionId);\n if (!entry) return;\n if (entry.metadata) state.bridge.setMetadata(sessionId, entry.metadata);\n if (entry.state) state.bridge.setPlaybackState(sessionId, entry.state.status);\n}\n\nfunction promoteNextActiveOrClear(state: MediaServiceState): void {\n if (state.sessionRegistry.size === 0) {\n setActive(state, null);\n return;\n }\n let latest: SessionEntry | null = null;\n for (const entry of state.sessionRegistry.values()) {\n if (!latest || entry.lastTouched > latest.lastTouched) latest = entry;\n }\n setActive(state, latest ? latest.sessionId : null, latest?.actions);\n}\n\nfunction sendMediaCommand(state: MediaServiceState, sessionId: string, action: MediaAction, value?: number): void {\n const entry = state.sessionRegistry.get(sessionId);\n if (!entry) return;\n const send = state.sendHandles.get(entry.windowId);\n if (!send) return;\n const payload: MediaCommandMessage = {\n type: 'media.command',\n sessionId,\n action,\n ...(typeof value === 'number' ? { value } : {}),\n };\n send(payload as NappletMessage);\n}\n\nfunction registerWindowSession(state: MediaServiceState, windowId: string, sessionId: string): void {\n if (!state.windowSessions.has(windowId)) state.windowSessions.set(windowId, new Set());\n state.windowSessions.get(windowId)!.add(sessionId);\n}\n\nfunction sendSessionCreateResult(\n send: (msg: NappletMessage) => void,\n id: string | undefined,\n fields: Omit<MediaSessionCreateResultEnvelope, 'type' | 'id'>,\n): void {\n send({\n type: 'media.session.create.result',\n id: id ?? '',\n ...fields,\n } as NappletMessage);\n}\n\nfunction isMediaPlaybackOwner(value: unknown): value is MediaPlaybackOwner {\n return value === 'shell' || value === 'napplet';\n}\n\nfunction hasSourceRef(source: MediaSourceRef | undefined): boolean {\n if (!source) return false;\n if (typeof source.url === 'string' && source.url.length > 0) return true;\n if (typeof source.blossomHash === 'string' && source.blossomHash.length > 0) return true;\n if (source.nostr) {\n return Boolean(source.nostr.eventId || source.nostr.address);\n }\n return false;\n}\n\nfunction canonicalizeSessionId(\n state: MediaServiceState,\n windowId: string,\n preferredSessionId: string | undefined,\n): string {\n const trimmed = typeof preferredSessionId === 'string' ? preferredSessionId.trim() : '';\n const hint = trimmed || `session-${++state.sessionCounter}`;\n if (!state.sessionRegistry.has(hint)) return hint;\n\n let next: string;\n do {\n next = `${windowId}:${hint}:${++state.sessionCounter}`;\n } while (state.sessionRegistry.has(next));\n return next;\n}\n\nfunction handleSessionCreate(\n state: MediaServiceState,\n windowId: string,\n message: MediaSessionCreateEnvelope,\n send: (msg: NappletMessage) => void,\n): void {\n if (!isMediaPlaybackOwner(message.owner)) {\n sendSessionCreateResult(send, message.id, { error: 'missing owner' });\n return;\n }\n\n if (message.owner === 'shell') {\n if (!hasSourceRef(message.source)) {\n sendSessionCreateResult(send, message.id, { owner: 'shell', error: 'missing source' });\n return;\n }\n sendSessionCreateResult(send, message.id, { owner: 'shell', error: 'unsupported owner mode' });\n return;\n }\n\n state.sendHandles.set(windowId, send);\n const sessionId = canonicalizeSessionId(state, windowId, message.sessionId);\n const entry: SessionEntry = {\n sessionId,\n windowId,\n owner: message.owner,\n source: message.source,\n metadata: message.metadata,\n state: undefined,\n actions: message.capabilities ?? DEFAULT_MEDIA_ACTIONS,\n lastTouched: ++state.touchCounter,\n };\n state.sessionRegistry.set(sessionId, entry);\n registerWindowSession(state, windowId, sessionId);\n setActive(state, sessionId, entry.actions);\n state.options.onSessionCreate?.(windowId, sessionId, message.metadata);\n sendSessionCreateResult(send, message.id, { sessionId, owner: message.owner });\n}\n\nfunction handleSessionUpdate(state: MediaServiceState, windowId: string, message: MediaSessionUpdateMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry) {\n entry.metadata = { ...entry.metadata, ...message.metadata };\n entry.lastTouched = ++state.touchCounter;\n if (entry.owner === 'napplet' && message.sessionId === state.activeSessionId && entry.metadata) {\n state.bridge.setMetadata(message.sessionId, entry.metadata);\n }\n }\n state.options.onSessionUpdate?.(windowId, message.sessionId, message.metadata);\n}\n\nfunction handleSessionDestroy(state: MediaServiceState, windowId: string, message: MediaSessionDestroyMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry) {\n state.sessionRegistry.delete(message.sessionId);\n const set = state.windowSessions.get(entry.windowId);\n if (set) {\n set.delete(message.sessionId);\n if (set.size === 0) state.windowSessions.delete(entry.windowId);\n }\n state.bridge.destroySession?.(message.sessionId);\n if (message.sessionId === state.activeSessionId) promoteNextActiveOrClear(state);\n }\n state.options.onSessionDestroy?.(windowId, message.sessionId);\n}\n\nfunction handleMediaState(state: MediaServiceState, windowId: string, message: MediaStateMessage): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry?.owner === 'napplet') {\n entry.state = {\n status: message.status,\n position: message.position,\n duration: message.duration,\n volume: message.volume,\n };\n entry.lastTouched = ++state.touchCounter;\n if (state.activeSessionId !== message.sessionId) setActive(state, message.sessionId, entry.actions);\n else state.bridge.setPlaybackState(message.sessionId, message.status);\n }\n state.options.onState?.(windowId, message.sessionId, message);\n}\n\nfunction handleMediaCapabilities(\n state: MediaServiceState,\n windowId: string,\n message: MediaCapabilitiesMessage,\n): void {\n const entry = state.sessionRegistry.get(message.sessionId);\n if (entry?.owner === 'napplet') {\n entry.actions = message.actions;\n entry.lastTouched = ++state.touchCounter;\n if (message.sessionId === state.activeSessionId) {\n state.bridge.setActiveSession?.(message.sessionId, entry.actions);\n }\n }\n state.options.onCapabilities?.(windowId, message.sessionId, message.actions);\n}\n\nfunction handleMediaMessage(\n state: MediaServiceState,\n windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n): void {\n switch (message.type) {\n case 'media.session.create':\n handleSessionCreate(state, windowId, message as MediaSessionCreateEnvelope, send);\n return;\n case 'media.session.update':\n handleSessionUpdate(state, windowId, message as MediaSessionUpdateMessage);\n return;\n case 'media.session.destroy':\n handleSessionDestroy(state, windowId, message as MediaSessionDestroyMessage);\n return;\n case 'media.state':\n handleMediaState(state, windowId, message as MediaStateMessage);\n return;\n case 'media.capabilities':\n handleMediaCapabilities(state, windowId, message as MediaCapabilitiesMessage);\n return;\n default: {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown media method: ${message.type}`,\n } as NappletMessage);\n }\n }\n}\n\nfunction destroyWindowSessions(state: MediaServiceState, windowId: string): void {\n const sessions = state.windowSessions.get(windowId);\n if (sessions) {\n const ownedActive = state.activeSessionId !== null && sessions.has(state.activeSessionId);\n for (const sessionId of sessions) {\n state.sessionRegistry.delete(sessionId);\n state.bridge.destroySession?.(sessionId);\n }\n state.windowSessions.delete(windowId);\n if (ownedActive) promoteNextActiveOrClear(state);\n }\n state.sendHandles.delete(windowId);\n}\n\nfunction destroyMediaState(state: MediaServiceState, unsubscribeAction: () => void): void {\n unsubscribeAction();\n for (const sessionId of state.sessionRegistry.keys()) state.bridge.destroySession?.(sessionId);\n state.bridge.setActiveSession?.(null);\n state.sessionRegistry.clear();\n state.windowSessions.clear();\n state.sendHandles.clear();\n state.activeSessionId = null;\n state.touchCounter = 0;\n state.sessionCounter = 0;\n}\n\n/**\n * Create a media NAP service handler with navigator.mediaSession integration.\n *\n * Implements the 5 napplet->shell media.* request types defined in\n * `@napplet/nap/media`. Only `media.session.create` produces a reply\n * envelope (`media.session.create.result`) — the remaining four\n * (`session.update`, `session.destroy`, `state`, `capabilities`) are\n * fire-and-forget per the NAP spec.\n *\n * Unknown `media.*` actions produce a `<type>.error` envelope so\n * napplets are never left hanging on a malformed request.\n *\n * @param options - Optional host callbacks for session lifecycle + state, plus\n * mediaSessionTarget and documentTarget for test injection, and an optional\n * hostBridge for native-OS media backend delegation.\n * @returns A ServiceHandler (with `destroy()`) to register with the runtime\n *\n * @example\n * ```ts\n * import { createMediaService } from '@kehto/services';\n *\n * const media = createMediaService();\n * runtime.registerService('media', media);\n * // Later, on shell teardown:\n * media.destroy();\n * ```\n */\nexport function createMediaService(options: MediaServiceOptions = {}): ServiceHandler & { destroy(): void } {\n const descriptor: ServiceDescriptor = {\n name: 'media',\n version: MEDIA_SERVICE_VERSION,\n description: options.hostBridge\n ? 'NIP-5D media NAP reference handler (host-bridge delegated)'\n : 'NIP-5D media NAP reference handler (navigator.mediaSession mirror)',\n };\n\n const bridge: HostMediaBridge = options.hostBridge\n ?? createBrowserMediaBridge({\n mediaSessionTarget: options.mediaSessionTarget,\n documentTarget: options.documentTarget,\n });\n const state = createMediaServiceState(options, bridge);\n\n const unsubscribeAction = bridge.onAction((sessionId, action, value) => {\n sendMediaCommand(state, sessionId, action, value);\n });\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n handleMediaMessage(state, windowId, message, send);\n },\n\n onWindowDestroyed(windowId: string): void {\n destroyWindowSessions(state, windowId);\n },\n\n destroy(): void {\n destroyMediaState(state, unsubscribeAction);\n },\n };\n}\n","/**\n * NAP-NOTIFY reference service.\n *\n * The service owns protocol correlation and per-window lifecycle while a host\n * backend owns presentation, permission, badges, and channels.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n NotifyActionMessage,\n NotifyBadgeMessage,\n NotifyChannelRegisterMessage,\n NotifyClickedMessage,\n NotifyControl,\n NotifyControlsMessage,\n NotifyDismissedMessage,\n NotifyDismissMessage,\n NotifyPermissionRequestMessage,\n NotifyPermissionResultMessage,\n NotifySendMessage,\n NotifySendResultMessage,\n} from '@napplet/nap/notify/types';\n\nconst NOTIFY_SERVICE_VERSION = '1.0.0';\n\n/** User-interaction events emitted by a host notification presentation. */\nexport type NotifyInteractionMessage =\n | NotifyActionMessage\n | NotifyClickedMessage\n | NotifyDismissedMessage;\n\n/** Host presentation request for one accepted notification. */\nexport interface NotifyPresentation {\n /** Window that requested the notification. */\n readonly windowId: string;\n /** Shell-assigned notification identifier. */\n readonly notificationId: string;\n /** Original, untrusted notification payload. */\n readonly message: NotifySendMessage;\n /** Route a host-side user interaction back to the requesting napplet. */\n readonly emit: (message: NotifyInteractionMessage) => void;\n}\n\n/** Host hooks required to turn NAP-NOTIFY messages into observable behavior. */\nexport interface NotifyServiceOptions {\n /** Generate a shell-assigned notification identifier. */\n generateId?: () => string;\n /** Render or otherwise deliver a notification. Missing backends fail closed. */\n present?: (presentation: NotifyPresentation) => void | Promise<void>;\n /** Remove an active notification from host presentation. */\n dismiss?: (windowId: string, notificationId: string) => void | Promise<void>;\n /** Display or clear the requesting napplet's badge count. */\n setBadge?: (windowId: string, count: number) => void | Promise<void>;\n /** Persist a channel registration in host-owned state. */\n registerChannel?: (windowId: string, channel: NotifyChannelRegisterMessage) => void | Promise<void>;\n /** Apply policy or prompt the user for notification permission. */\n requestPermission?: (windowId: string, channel?: string) => boolean | Promise<boolean>;\n /** Release host-owned state when the runtime destroys a napplet window. */\n destroyWindow?: (windowId: string) => void | Promise<void>;\n /** Notification controls implemented by the host backend. */\n controls?: readonly NotifyControl[];\n /** Observe asynchronous fire-and-forget backend failures. */\n onError?: (error: unknown) => void;\n}\n\nfunction errorText(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction forget(result: void | Promise<void>, onError?: (error: unknown) => void): void {\n void Promise.resolve(result).catch((error: unknown) => onError?.(error));\n}\n\n/**\n * Create a NAP-NOTIFY service backed by host-owned notification behavior.\n *\n * A backendless instance is safe to register for conformance testing: send\n * requests return an error and permission requests return `granted: false`\n * instead of manufacturing successful delivery.\n *\n * @param options - Host presentation, policy, and lifecycle hooks.\n * @returns Runtime service handler for the `notify` domain.\n *\n * @example\n * ```ts\n * const notify = createNotifyService({\n * present: ({ message }) => showToast(message.title, message.body),\n * requestPermission: () => promptUser(),\n * });\n * runtime.registerService('notify', notify);\n * ```\n */\nexport function createNotifyService(options: NotifyServiceOptions = {}): ServiceHandler {\n let counter = 0;\n const generateId = options.generateId ?? ((): string => `shell-${++counter}`);\n const active = new Map<string, Set<string>>();\n const controlsSent = new Set<string>();\n\n const descriptor: ServiceDescriptor = {\n name: 'notify',\n version: NOTIFY_SERVICE_VERSION,\n description: 'NAP-NOTIFY host-backed notification handler',\n };\n\n const pushControls = (windowId: string, send: (msg: NappletMessage) => void): void => {\n if (!options.controls || controlsSent.has(windowId)) return;\n controlsSent.add(windowId);\n const message: NotifyControlsMessage = { type: 'notify.controls', controls: [...options.controls] };\n send(message as NappletMessage);\n };\n\n const sendNotification = async (\n windowId: string,\n message: NotifySendMessage,\n send: (msg: NappletMessage) => void,\n ): Promise<void> => {\n if (!options.present) {\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n error: 'notification presentation unavailable',\n };\n send(result as NappletMessage);\n return;\n }\n\n const notificationId = generateId();\n const windowNotifications = active.get(windowId) ?? new Set<string>();\n windowNotifications.add(notificationId);\n active.set(windowId, windowNotifications);\n const ownsWindow = (): boolean => active.get(windowId) === windowNotifications;\n const ownsNotification = (): boolean => ownsWindow() && windowNotifications.has(notificationId);\n const emit = (interaction: NotifyInteractionMessage): void => {\n if (!ownsNotification()) return;\n if (interaction.type === 'notify.dismissed') windowNotifications.delete(notificationId);\n send(interaction as NappletMessage);\n };\n\n try {\n await options.present({ windowId, notificationId, message, emit });\n if (!ownsWindow()) {\n if (options.dismiss) forget(options.dismiss(windowId, notificationId), options.onError);\n return;\n }\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n notificationId,\n };\n send(result as NappletMessage);\n } catch (error) {\n if (!ownsWindow()) return;\n windowNotifications.delete(notificationId);\n if (windowNotifications.size === 0) active.delete(windowId);\n const result: NotifySendResultMessage = {\n type: 'notify.send.result',\n id: message.id,\n error: errorText(error),\n };\n send(result as NappletMessage);\n }\n };\n\n const requestPermission = async (\n windowId: string,\n message: NotifyPermissionRequestMessage,\n send: (msg: NappletMessage) => void,\n ): Promise<void> => {\n let granted = false;\n try {\n granted = options.requestPermission\n ? await options.requestPermission(windowId, message.channel)\n : false;\n } catch (error) {\n options.onError?.(error);\n }\n const result: NotifyPermissionResultMessage = {\n type: 'notify.permission.result',\n id: message.id,\n granted,\n };\n send(result as NappletMessage);\n };\n\n return {\n descriptor,\n\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n pushControls(windowId, send);\n switch (message.type) {\n case 'notify.send':\n void sendNotification(windowId, message as NotifySendMessage, send);\n return;\n case 'notify.dismiss': {\n const notificationId = (message as NotifyDismissMessage).notificationId;\n const windowNotifications = active.get(windowId);\n if (!windowNotifications?.delete(notificationId)) return;\n if (options.dismiss) forget(options.dismiss(windowId, notificationId), options.onError);\n return;\n }\n case 'notify.badge': {\n if (options.setBadge) {\n const count = Math.max(0, Math.floor((message as NotifyBadgeMessage).count));\n forget(options.setBadge(windowId, count), options.onError);\n }\n return;\n }\n case 'notify.channel.register':\n if (options.registerChannel) {\n forget(options.registerChannel(windowId, message as NotifyChannelRegisterMessage), options.onError);\n }\n return;\n case 'notify.permission.request':\n void requestPermission(windowId, message as NotifyPermissionRequestMessage, send);\n return;\n default: {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown notify method: ${message.type}`,\n } as NappletMessage);\n }\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const windowNotifications = active.get(windowId);\n active.delete(windowId);\n controlsSent.delete(windowId);\n if (options.dismiss && windowNotifications) {\n for (const notificationId of windowNotifications) {\n forget(options.dismiss(windowId, notificationId), options.onError);\n }\n }\n if (options.destroyWindow) forget(options.destroyWindow(windowId), options.onError);\n },\n };\n}\n","/**\n * theme-service.ts — NIP-5D theme NAP reference service.\n *\n * Handles the single napplet->shell request type from @napplet/nap/theme:\n * - `theme.get` -> `theme.get.result { theme }` (current theme)\n *\n * Exposes a host-facing `publishTheme(theme)` handle that:\n * 1. Replaces the service's internal current theme.\n * 2. Invokes `options.onBroadcast(envelope)` synchronously with a\n * `theme.changed` envelope so the shell adapter (Plan 13-02) can\n * fan-out the push to every registered napplet.\n * 3. Returns the envelope so callers can use it directly if they prefer.\n *\n * The default theme values are centralized here and mirrored in the runtime's\n * fallback path (`packages/runtime/src/runtime.ts`) — runtime does NOT import\n * from @kehto/services because services depends on runtime (one-way only).\n *\n * Host apps replace this via `runtime.registerService('theme', realHandler)`\n * when real CSS injection / storage / dark-mode logic is needed.\n *\n * @example\n * ```ts\n * import { createThemeService } from '@kehto/services';\n *\n * const theme = createThemeService({\n * onBroadcast: (envelope) => broadcastToAllNapplets(envelope),\n * });\n * runtime.registerService('theme', theme.handler);\n *\n * // Later, when the user flips dark/light mode:\n * theme.publishTheme(newTheme);\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from\n// @napplet/core v0.2.0+ (napplet phase-81). Re-exported from\n// @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n Theme,\n ThemeChangedMessage,\n ThemeGetResultMessage,\n} from '@napplet/nap/theme/types';\n\n/** Theme service version — follows semver. */\nconst THEME_SERVICE_VERSION = '1.0.0';\n\n/**\n * Canonical default theme values.\n *\n * Must stay synchronized with the runtime fallback constant at\n * `packages/runtime/src/runtime.ts` (THEME_FALLBACK_DEFAULT). The runtime\n * keeps a local copy instead of importing from @kehto/services to avoid a\n * runtime->services dependency (services depends on runtime; one-way).\n */\nconst DEFAULT_THEME: Theme = {\n colors: {\n background: '#0a0a0a',\n text: '#e0e0e0',\n primary: '#7aa2f7',\n },\n // fonts, background, title intentionally undefined — all optional per\n // @napplet/nap/theme Theme interface.\n};\n\nfunction isCompleteTheme(theme: Theme | undefined): theme is Theme {\n const colors = theme?.colors;\n return typeof colors?.background === 'string'\n && typeof colors.text === 'string'\n && typeof colors.primary === 'string';\n}\n\nfunction normalizeTheme(theme: Theme | undefined): Theme {\n return isCompleteTheme(theme) ? theme : DEFAULT_THEME;\n}\n\n/**\n * Configuration for `createThemeService`.\n *\n * @example\n * ```ts\n * const theme = createThemeService({\n * initialTheme: { colors: { background: '#fff', text: '#000', primary: '#00f' } },\n * onBroadcast: (envelope) => shellBridge.broadcastToAll(envelope),\n * });\n * ```\n */\nexport interface ThemeServiceOptions {\n /**\n * Override the default theme payload. If omitted, the service starts with\n * the canonical defaults (`#0a0a0a / #e0e0e0 / #7aa2f7`).\n */\n initialTheme?: Theme;\n\n /**\n * Called synchronously from `publishTheme(theme)` with a `theme.changed`\n * envelope. Intended for the shell adapter (Plan 13-02) to fan-out the\n * push to every registered napplet via the runtime's sendToNapplet\n * primitive.\n *\n * Keep this callback shape framework-agnostic — the service does NOT\n * import any shell / browser APIs.\n */\n onBroadcast?: (envelope: ThemeChangedMessage) => void;\n}\n\n/**\n * A theme service bundle — the ServiceHandler that handles `theme.*`\n * envelopes, the host-facing `publishTheme(theme)` handle for theme-change\n * broadcasts, and a `getCurrentTheme()` accessor for host-side reads.\n */\nexport interface ThemeService {\n /** Register this with the runtime via `runtime.registerService('theme', handler)`. */\n handler: ServiceHandler;\n\n /**\n * Publish a theme-change to the shell adapter. Updates the service's\n * internal current theme, invokes `options.onBroadcast` with a\n * `theme.changed` envelope, and returns the envelope.\n *\n * @param theme - The new theme payload\n * @returns A `theme.changed` envelope (same one passed to onBroadcast)\n */\n publishTheme: (theme: Theme) => ThemeChangedMessage;\n\n /** Return the current theme. Equivalent to the payload a napplet's `theme.get` would receive. */\n getCurrentTheme: () => Theme;\n}\n\n/**\n * Create a theme service that handles the NIP-5D `theme.*` NAP.\n *\n * Answers `theme.get` with the current theme (default or\n * `options.initialTheme`). Exposes `publishTheme(theme)` for the host app to\n * broadcast theme changes to every registered napplet — the shell adapter\n * (Plan 13-02) wires `onBroadcast` to `runtime.sendToNapplet` fan-out.\n *\n * @param options - Optional service configuration (see ThemeServiceOptions)\n * @returns A ThemeService bundle ready for `runtime.registerService('theme', service.handler)`\n *\n * @example\n * ```ts\n * import { createThemeService } from '@kehto/services';\n *\n * const theme = createThemeService();\n * runtime.registerService('theme', theme.handler);\n * theme.publishTheme({ colors: { background: '#fff', text: '#000', primary: '#00f' } });\n * ```\n */\nexport function createThemeService(options: ThemeServiceOptions = {}): ThemeService {\n let currentTheme: Theme = normalizeTheme(options.initialTheme);\n\n const descriptor: ServiceDescriptor = {\n name: 'theme',\n version: THEME_SERVICE_VERSION,\n description: 'NIP-5D theme NAP reference handler',\n };\n\n const handler: ServiceHandler = {\n descriptor,\n\n handleMessage(\n _windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n\n if (message.type === 'theme.get') {\n const result: ThemeGetResultMessage = {\n type: 'theme.get.result',\n id,\n theme: currentTheme,\n };\n send(result as NappletMessage);\n return;\n }\n\n return;\n },\n\n // Theme service has no per-window state to clean up.\n onWindowDestroyed(_windowId: string): void {\n /* no-op */\n },\n };\n\n function publishTheme(theme: Theme): ThemeChangedMessage {\n currentTheme = normalizeTheme(theme);\n const envelope: ThemeChangedMessage = { type: 'theme.changed', theme: currentTheme };\n options.onBroadcast?.(envelope);\n return envelope;\n }\n\n function getCurrentTheme(): Theme {\n return currentTheme;\n }\n\n return { handler, publishTheme, getCurrentTheme };\n}\n","import type {\n ConfigSchemaErrorCode,\n ConfigValues,\n NappletConfigSchema,\n} from '@napplet/nap/config/types';\n\n/** Result of validating a NAP-CONFIG Core Subset schema. */\nexport type ConfigSchemaValidation =\n | { ok: true }\n | { ok: false; code: ConfigSchemaErrorCode; error: string };\n\n/**\n * NAP-CONFIG's bounded JSON Schema vocabulary. Unknown standard keywords are\n * rejected because accepting a constraint that is not enforced would allow\n * invalid values to cross the host boundary. Unknown `x-napplet-*`\n * annotations remain opaque metadata as required by the draft.\n */\nconst CONFIG_TYPES = new Set(['string', 'number', 'integer', 'boolean', 'array', 'object']);\nconst CONFIG_SCHEMA_KEYS = new Set([\n '$schema', '$version', 'type', 'properties', 'required', 'items',\n 'additionalProperties', 'default', 'title', 'description', 'enum',\n 'enumDescriptions', 'minimum', 'maximum', 'minLength', 'maxLength',\n 'minItems', 'maxItems', 'format', 'deprecationMessage',\n 'markdownDescription',\n]);\nconst FORBIDDEN_SCHEMA_KEYS = new Set([\n '$ref', '$defs', 'definitions', 'pattern', 'patternProperties', 'propertyNames',\n 'dependencies', 'dependentSchemas', 'unevaluatedProperties', 'oneOf', 'anyOf',\n 'allOf', 'not', 'if', 'then', 'else',\n]);\n\n/** Validate the complete bounded NAP-CONFIG Core Subset recursively. */\nexport function validateConfigSchema(schema: unknown): ConfigSchemaValidation {\n if (!isRecord(schema)) {\n return { ok: false, code: 'invalid-schema', error: 'schema root must be an object' };\n }\n if (schema.type !== 'object') {\n return { ok: false, code: 'invalid-schema', error: 'schema root must have type: \"object\"' };\n }\n if (schema.$schema !== undefined) {\n if (typeof schema.$schema !== 'string' || !isSupportedSchemaDraft(schema.$schema)) {\n return { ok: false, code: 'unsupported-draft', error: 'schema draft is not supported' };\n }\n }\n if (schema.$version !== undefined && (!Number.isSafeInteger(schema.$version) || Number(schema.$version) < 0)) {\n return { ok: false, code: 'invalid-schema', error: '$version must be a non-negative integer' };\n }\n return validateSchemaNode(schema, 0, '$');\n}\n\nfunction validateSchemaNode(schema: Record<string, unknown>, depth: number, path: string): ConfigSchemaValidation {\n if (schema.type === 'object' && depth > 4) {\n return { ok: false, code: 'schema-too-deep', error: `${path} exceeds the four-level object depth limit` };\n }\n for (const key of Object.keys(schema)) {\n if (FORBIDDEN_SCHEMA_KEYS.has(key)) {\n const code: ConfigSchemaErrorCode = key === '$ref' || key === '$defs' || key === 'definitions'\n ? 'ref-not-allowed'\n : key === 'pattern'\n ? 'pattern-not-allowed'\n : 'invalid-schema';\n return { ok: false, code, error: `${key} is not permitted in the Core Subset at ${path}` };\n }\n if (!CONFIG_SCHEMA_KEYS.has(key) && !key.startsWith('x-napplet-')) {\n return { ok: false, code: 'invalid-schema', error: `${key} is outside the NAP-CONFIG Core Subset at ${path}` };\n }\n if (path !== '$' && (key === '$schema' || key === '$version')) {\n return { ok: false, code: 'invalid-schema', error: `${key} is only valid at the schema root` };\n }\n }\n if (typeof schema.type !== 'string' || !CONFIG_TYPES.has(schema.type)) {\n return { ok: false, code: 'invalid-schema', error: `${path} has an unsupported or missing type` };\n }\n if (schema['x-napplet-secret'] === true && Object.hasOwn(schema, 'default')) {\n return { ok: false, code: 'secret-with-default', error: `${path} combines x-napplet-secret with default` };\n }\n const annotations = validateAnnotations(schema, path);\n if (!annotations.ok) return annotations;\n if (schema.type === 'object') return validateObjectSchema(schema, depth, path);\n if (schema.type === 'array') {\n if (!isRecord(schema.items)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.items must be one homogeneous schema` };\n }\n const itemType = schema.items.type;\n if (itemType === 'object' || itemType === 'array') {\n return { ok: false, code: 'invalid-schema', error: `${path} arrays may contain primitives only` };\n }\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n const itemValidation = validateSchemaNode(schema.items, depth, `${path}[]`);\n if (!itemValidation.ok) return itemValidation;\n return validateDefault(schema, path);\n }\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n return validateDefault(schema, path);\n}\n\nfunction validateObjectSchema(schema: Record<string, unknown>, depth: number, path: string): ConfigSchemaValidation {\n const constraints = validateScalarConstraints(schema, path);\n if (!constraints.ok) return constraints;\n if (schema.properties !== undefined && !isRecord(schema.properties)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.properties must be an object` };\n }\n if (schema.required !== undefined && (\n !Array.isArray(schema.required)\n || schema.required.some((key) => typeof key !== 'string')\n || new Set(schema.required).size !== schema.required.length\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.required must contain unique strings` };\n }\n if (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== 'boolean') {\n return { ok: false, code: 'invalid-schema', error: `${path}.additionalProperties must be boolean` };\n }\n const properties = schema.properties ?? {};\n if (Array.isArray(schema.required) && schema.required.some((key) => !Object.hasOwn(properties, key))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.required references an undeclared property` };\n }\n for (const [key, child] of Object.entries(properties)) {\n if (!isRecord(child)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be an object schema` };\n }\n const validation = validateSchemaNode(child, child.type === 'object' ? depth + 1 : depth, `${path}.${key}`);\n if (!validation.ok) return validation;\n }\n return validateDefault(schema, path);\n}\n\nfunction validateScalarConstraints(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n const numericKeys = schema.type === 'number' || schema.type === 'integer' ? ['minimum', 'maximum'] : [];\n const countKeys = schema.type === 'string'\n ? ['minLength', 'maxLength']\n : schema.type === 'array'\n ? ['minItems', 'maxItems']\n : [];\n for (const key of ['minimum', 'maximum', 'minLength', 'maxLength', 'minItems', 'maxItems']) {\n if (schema[key] !== undefined && !numericKeys.includes(key) && !countKeys.includes(key)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} does not apply to ${String(schema.type)}` };\n }\n }\n for (const key of numericKeys) {\n if (schema[key] !== undefined && (typeof schema[key] !== 'number' || !Number.isFinite(schema[key]))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a finite number` };\n }\n }\n for (const key of countKeys) {\n if (schema[key] !== undefined && (!Number.isSafeInteger(schema[key]) || Number(schema[key]) < 0)) {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a non-negative integer` };\n }\n }\n if (typeof schema.minimum === 'number' && typeof schema.maximum === 'number' && schema.minimum > schema.maximum) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minimum cannot exceed maximum` };\n }\n if (typeof schema.minLength === 'number' && typeof schema.maxLength === 'number' && schema.minLength > schema.maxLength) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minLength cannot exceed maxLength` };\n }\n if (typeof schema.minItems === 'number' && typeof schema.maxItems === 'number' && schema.minItems > schema.maxItems) {\n return { ok: false, code: 'invalid-schema', error: `${path}.minItems cannot exceed maxItems` };\n }\n if (schema.enum !== undefined) {\n if (!Array.isArray(schema.enum) || schema.enum.length === 0) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum must be a non-empty array` };\n }\n if (schema.enum.some((value) => !isPrimitiveForType(value, String(schema.type)))) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum contains a value outside its declared type` };\n }\n if (new Set(schema.enum.map((value) => JSON.stringify(value))).size !== schema.enum.length) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enum values must be unique` };\n }\n }\n return { ok: true };\n}\n\nfunction validateAnnotations(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n for (const key of ['title', 'description', 'format', 'x-napplet-section', 'deprecationMessage', 'markdownDescription']) {\n if (schema[key] !== undefined && typeof schema[key] !== 'string') {\n return { ok: false, code: 'invalid-schema', error: `${path}.${key} must be a string` };\n }\n }\n if (schema['x-napplet-secret'] !== undefined && typeof schema['x-napplet-secret'] !== 'boolean') {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-secret must be boolean` };\n }\n if (schema['x-napplet-secret'] === true && schema.type !== 'string') {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-secret applies only to strings` };\n }\n if (schema['x-napplet-order'] !== undefined && (\n typeof schema['x-napplet-order'] !== 'number'\n || !Number.isFinite(schema['x-napplet-order'])\n || schema['x-napplet-order'] < 0\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.x-napplet-order must be a non-negative number` };\n }\n if (schema.enumDescriptions !== undefined && (\n !Array.isArray(schema.enumDescriptions)\n || schema.enumDescriptions.some((value) => typeof value !== 'string')\n || !Array.isArray(schema.enum)\n || schema.enumDescriptions.length !== schema.enum.length\n )) {\n return { ok: false, code: 'invalid-schema', error: `${path}.enumDescriptions must parallel enum` };\n }\n return { ok: true };\n}\n\nfunction validateDefault(schema: Record<string, unknown>, path: string): ConfigSchemaValidation {\n if (!Object.hasOwn(schema, 'default')) return { ok: true };\n if (!configValueConforms(schema, schema.default, path === '$')) {\n return { ok: false, code: 'invalid-schema', error: `${path}.default does not validate against its schema` };\n }\n return { ok: true };\n}\n\nexport function configValuesConform(schema: Record<string, unknown>, value: unknown): boolean {\n return configValueConforms(schema, value, true);\n}\n\nfunction configValueConforms(schema: Record<string, unknown>, value: unknown, topLevel: boolean): boolean {\n if (Array.isArray(schema.enum) && !schema.enum.some((item) => Object.is(item, value))) return false;\n if (schema.type === 'string') {\n return typeof value === 'string' && withinLength(value.length, schema.minLength, schema.maxLength);\n }\n if (schema.type === 'number') {\n return typeof value === 'number' && Number.isFinite(value) && withinLength(value, schema.minimum, schema.maximum);\n }\n if (schema.type === 'integer') {\n return Number.isSafeInteger(value) && withinLength(value as number, schema.minimum, schema.maximum);\n }\n if (schema.type === 'boolean') return typeof value === 'boolean';\n if (schema.type === 'array') {\n return Array.isArray(value)\n && isRecord(schema.items)\n && withinLength(value.length, schema.minItems, schema.maxItems)\n && value.every((item) => configValueConforms(schema.items as Record<string, unknown>, item, false));\n }\n if (schema.type === 'object') {\n if (!isRecord(value)) return false;\n const properties = isRecord(schema.properties) ? schema.properties : {};\n if (Array.isArray(schema.required) && schema.required.some((key) => typeof key === 'string' && !Object.hasOwn(value, key))) {\n return false;\n }\n return Object.entries(value).every(([key, child]) => {\n const childSchema = properties[key];\n return isRecord(childSchema)\n ? configValueConforms(childSchema, child, false)\n : allowsAdditionalProperties(schema, topLevel) && cloneJsonConfigValue(child) !== undefined;\n });\n }\n return false;\n}\n\nfunction allowsAdditionalProperties(schema: Record<string, unknown>, topLevel: boolean): boolean {\n return schema.additionalProperties === true\n || (!topLevel && schema.additionalProperties === undefined);\n}\n\nfunction isPrimitiveForType(value: unknown, type: string): boolean {\n if (type === 'string') return typeof value === 'string';\n if (type === 'number') return typeof value === 'number' && Number.isFinite(value);\n if (type === 'integer') return Number.isSafeInteger(value);\n if (type === 'boolean') return typeof value === 'boolean';\n return false;\n}\n\nfunction isSupportedSchemaDraft(value: string): boolean {\n return /^https?:\\/\\/json-schema\\.org\\/(?:draft-0?7\\/schema#?|draft\\/2019-09\\/schema#?|draft\\/2020-12\\/schema#?)$/u.test(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Apply schema defaults and drop values that do not validate. */\nexport function resolveConfigValues(schema: NappletConfigSchema, values: ConfigValues): ConfigValues {\n return resolveObjectValues(schema as Record<string, unknown>, isRecord(values) ? values : {}, {}, true) as ConfigValues;\n}\n\nfunction resolveObjectValues(\n schema: Record<string, unknown>,\n input: Record<string, unknown>,\n inheritedDefault: Record<string, unknown>,\n topLevel: boolean,\n): Record<string, unknown> {\n const output: Record<string, unknown> = {};\n const ownDefault = isRecord(schema.default) ? schema.default : {};\n const defaults = { ...inheritedDefault, ...ownDefault };\n for (const [key, childValue] of Object.entries(schema.properties ?? {})) {\n if (!isRecord(childValue)) continue;\n const candidate = Object.hasOwn(input, key) ? input[key] : defaults[key];\n const resolved = resolveConfigValue(childValue, candidate);\n if (resolved !== undefined) setConfigValue(output, key, resolved);\n }\n if (allowsAdditionalProperties(schema, topLevel)) {\n const properties = isRecord(schema.properties) ? schema.properties : {};\n for (const [key, candidate] of Object.entries({ ...defaults, ...input })) {\n if (Object.hasOwn(properties, key)) continue;\n const resolved = cloneJsonConfigValue(candidate);\n if (resolved !== undefined) setConfigValue(output, key, resolved);\n }\n }\n return output;\n}\n\nfunction resolveConfigValue(schema: Record<string, unknown>, candidate: unknown): unknown {\n const value = candidate === undefined ? schema.default : candidate;\n if (value === undefined) return undefined;\n const resolved = resolveTypedConfigValue(schema, value);\n if (resolved !== undefined) return resolved;\n return candidate !== undefined && schema.default !== undefined\n ? resolveTypedConfigValue(schema, schema.default)\n : undefined;\n}\n\nfunction resolveTypedConfigValue(schema: Record<string, unknown>, value: unknown): unknown {\n if (schema.enum && Array.isArray(schema.enum) && !schema.enum.some((item) => Object.is(item, value))) return undefined;\n switch (schema.type) {\n case 'object': {\n if (!isRecord(value)) return undefined;\n const resolved = resolveObjectValues(schema, value, {}, false);\n return Array.isArray(schema.required)\n && schema.required.some((key) => typeof key === 'string' && !Object.hasOwn(resolved, key))\n ? undefined\n : resolved;\n }\n case 'array': {\n if (!Array.isArray(value) || !isRecord(schema.items) || !withinLength(value.length, schema.minItems, schema.maxItems)) {\n return undefined;\n }\n const resolved = value.map((item) => resolveConfigValue(schema.items as Record<string, unknown>, item));\n if (resolved.some((item) => item === undefined)) return undefined;\n return resolved;\n }\n case 'string': return typeof value === 'string' && withinLength(value.length, schema.minLength, schema.maxLength) ? value : undefined;\n case 'number': return typeof value === 'number' && Number.isFinite(value) && withinLength(value, schema.minimum, schema.maximum) ? value : undefined;\n case 'integer': return Number.isSafeInteger(value) && withinLength(value as number, schema.minimum, schema.maximum) ? value : undefined;\n case 'boolean': return typeof value === 'boolean' ? value : undefined;\n default: return undefined;\n }\n}\n\nfunction cloneJsonConfigValue(value: unknown, seen = new Set<object>()): unknown {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;\n if (typeof value !== 'object' || seen.has(value)) return undefined;\n seen.add(value);\n try {\n if (Array.isArray(value)) {\n const output = value.map((item) => cloneJsonConfigValue(item, seen));\n return output.some((item) => item === undefined) ? undefined : output;\n }\n const entries: Array<[string, unknown]> = [];\n for (const [key, child] of Object.entries(value)) {\n const resolved = cloneJsonConfigValue(child, seen);\n if (resolved === undefined) return undefined;\n entries.push([key, resolved]);\n }\n return Object.fromEntries(entries);\n } finally {\n seen.delete(value);\n }\n}\n\nfunction setConfigValue(output: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(output, key, { value, enumerable: true, configurable: true, writable: true });\n}\n\nfunction withinLength(value: number, minimum: unknown, maximum: unknown): boolean {\n return (typeof minimum !== 'number' || value >= minimum) && (typeof maximum !== 'number' || value <= maximum);\n}\n","/**\n * config-service.ts — NAP-CONFIG reference service (9th NAP domain, v1.7 Phase 39).\n *\n * Shell-side reference implementation for the canonical NAP-CONFIG wire\n * protocol (`@napplet/nap/config`, published at `^0.3.0`). Handles the full\n * 8-message discriminated union: 5 napplet→shell request types + 3\n * shell→napplet result/push types.\n *\n * ──────────────────────────── SCOPE BOUNDARY (CONFIG-04) ─────────────────────────\n * NAP-CONFIG is **shell-managed per-napplet configuration**. Napplets observe\n * values via `config.get` (one-shot) or `config.subscribe` (snapshot + live\n * push). The shell is the **sole writer** — there is intentionally **NO**\n * `config.set` wire message. Napplets cannot mutate configuration values;\n * the shell owns persistence and the update flow.\n *\n * Do NOT use this service as a general key-value store. NAP-STORAGE\n * (`state:read` / `state:write`) remains the general KV surface. Using\n * NAP-CONFIG to store e.g. `{ lastScrollPosition: 420 }` is an anti-pattern\n * (H-07 in PITFALLS.md) — such state belongs in NAP-STORAGE.\n * ──────────────────────────────────────────────────────────────────────────────────\n *\n * Host integration: provide `getValues(windowId)` and `saveValues(windowId,\n * values)` backed by storage scoped to the source window's napplet identity.\n * The service validates and defaults every snapshot before delivery.\n *\n * Provide `openSettings` to render the shell-owned settings UI. Optional\n * hooks may add host policy around schema acceptance and lifecycle cleanup.\n *\n * @example\n * ```ts\n * import { createConfigService } from '@kehto/services';\n *\n * const configFixtures = new Map<string, ConfigValues>();\n * const config = createConfigService({\n * getValues: (windowId) => ({ ...configFixtures.get(windowId) }),\n * saveValues: (windowId, values) => configFixtures.set(windowId, values),\n * });\n * runtime.registerService('config', config.handler);\n *\n * // Later, when shell-side values change:\n * config.publishValues({ theme: 'light' }, 'window-1');\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\n// DRIFT-CORE-06 — Phase 11-deviation: ServiceDescriptor dropped from @napplet/core\n// v0.2.0+ (napplet phase-81). Re-exported from @kehto/runtime (canonical home after Phase 24 DRIFT-01).\nimport type { ServiceHandler } from '@kehto/runtime';\nimport type {\n ConfigGetMessage,\n ConfigRegisterSchemaMessage,\n ConfigOpenSettingsMessage,\n ConfigValues,\n ConfigValuesMessage,\n ConfigRegisterSchemaResultMessage,\n ConfigSchemaErrorCode,\n NappletConfigSchema,\n} from '@napplet/nap/config/types';\nimport {\n configValuesConform,\n resolveConfigValues,\n validateConfigSchema,\n type ConfigSchemaValidation,\n} from './config-schema.js';\n\nexport { resolveConfigValues, validateConfigSchema } from './config-schema.js';\nexport type { ConfigSchemaValidation } from './config-schema.js';\n\n/** Config service version — follows semver. */\nconst CONFIG_SERVICE_VERSION = '1.0.0';\n\n/**\n * Configuration options for `createConfigService` (options-as-bridge\n * per v1.6 Decision 18).\n *\n * @example\n * ```ts\n * const config = createConfigService({\n * getValues: (windowId) => loadScopedValues(windowId),\n * saveValues: (windowId, values) => saveScopedValues(windowId, values),\n * openSettings: (windowId, section, context) => showSettingsPanel(windowId, section, context),\n * });\n * ```\n */\nexport interface ConfigServiceOptions {\n /**\n * Returns the current configuration values snapshot.\n * Called on every `config.get` and at every `config.subscribe` initial push.\n * Implementations should return a fresh object (not a mutable reference).\n */\n getValues(windowId: string): ConfigValues;\n\n /** Persist one shell-validated full snapshot after settings UI commit. */\n saveValues?: (windowId: string, values: ConfigValues) => void;\n\n /**\n * Optional: receive notification when a napplet subscribes to config updates.\n * Fire-and-forget — the service tracks the subscription internally regardless.\n */\n onSubscribe?: (windowId: string) => void;\n\n /**\n * Optional: receive notification when a napplet unsubscribes.\n */\n onUnsubscribe?: (windowId: string) => void;\n\n /**\n * Optional: validate and store a napplet-provided schema.\n *\n * The reference implementation always runs its complete bounded Core Subset\n * validator first. This hook may add host policy such as version acceptance\n * or durable schema persistence, but cannot weaken protocol validation.\n *\n * Return shape mirrors `config.registerSchema.result` wire envelope\n * (minus the `id` — the dispatch layer correlates).\n */\n registerSchema?: (\n windowId: string,\n schema: NappletConfigSchema,\n version: number | undefined,\n ) => ConfigSchemaValidation;\n\n /**\n * Optional: open the shell-side settings UI for this napplet.\n * Fire-and-forget — no response envelope per the wire spec.\n * If omitted, `config.openSettings` is silently dropped. A conformant host\n * must supply this hook before advertising the CONFIG domain.\n */\n openSettings?: (\n windowId: string,\n section: string | undefined,\n context: ConfigSettingsContext,\n ) => void;\n\n /** Release host-owned transient UI or state for a destroyed window. */\n onWindowDestroyed?: (windowId: string) => void;\n}\n\n/** Shell-owned settings state and commit boundary passed to host UI code. */\nexport interface ConfigSettingsContext {\n /** Accepted schema for this live napplet window. */\n readonly schema: NappletConfigSchema;\n /** Current validated/defaulted values. */\n readonly values: ConfigValues;\n /** Validate, persist, and publish a full settings snapshot. */\n commit(values: ConfigValues): ConfigValues;\n}\n\n/**\n * NAP-CONFIG reference service bundle — `handler` to register with the\n * runtime, `publishValues` for the host app to push updates live to all\n * subscribed napplets.\n */\nexport interface ConfigService {\n /** Register this with the runtime via `runtime.registerService('config', handler)`. */\n handler: ServiceHandler;\n\n /**\n * Broadcast a new values snapshot to every napplet with an active\n * `config.subscribe`. Each subscriber receives a `config.values` envelope\n * with no `id` (push form per wire spec — absence of `id` distinguishes\n * push from correlated `config.get` response).\n *\n * @param values - The new configuration snapshot (full object, not a diff)\n * @param windowId - Optional source window scope. Omit only for a host-wide\n * update that should be independently validated for every subscriber.\n */\n publishValues(values: ConfigValues, windowId?: string): void;\n\n /** Return the accepted schema for one live window, or null. */\n getSchema(windowId: string): NappletConfigSchema | null;\n\n /** Return the current validated/defaulted values for one live window, or null. */\n getValues(windowId: string): ConfigValues | null;\n}\n\n/**\n * Create a NAP-CONFIG reference service.\n *\n * Shell-writes, napplet-reads. Handles the full `@napplet/nap/config` wire\n * protocol: `config.get` (correlated snapshot), `config.subscribe` /\n * `config.unsubscribe` (live push stream), `config.registerSchema` (optional\n * schema registration + Core Subset validation), `config.openSettings`\n * (optional UI deep-link, fire-and-forget).\n *\n * Returns a `ConfigService` bundle: `{ handler, publishValues }`.\n * Register `handler` with the runtime; call `publishValues(newValues)` from\n * the shell whenever config state changes.\n *\n * @param options - Host-supplied implementation hooks (options-as-bridge,\n * v1.6 Decision 18). `getValues` is required; all other fields are optional.\n * @returns A ConfigService bundle.\n *\n * @see ConfigServiceOptions for the options shape.\n * @see packages/services/src/theme-service.ts for the sibling pattern.\n * @see SCOPE BOUNDARY comment at the top of this file re: NAP-STORAGE separation.\n *\n * @example\n * ```ts\n * import { createConfigService } from '@kehto/services';\n *\n * const config = createConfigService({\n * getValues: (windowId) => loadScopedValues(windowId),\n * saveValues: (windowId, values) => saveScopedValues(windowId, values),\n * openSettings: (windowId, section, context) => openSettingsUI(section, context),\n * });\n * runtime.registerService('config', config.handler);\n *\n * // Push one externally-originated scoped update:\n * config.publishValues({ theme: 'light', density: 'compact' }, 'window-1');\n * ```\n */\nexport function createConfigService(options: ConfigServiceOptions): ConfigService {\n return new ConfigServiceController(options);\n}\n\ninterface RegisteredConfigSchema {\n readonly schema: NappletConfigSchema;\n readonly version?: number;\n}\n\nclass ConfigServiceController implements ConfigService {\n private readonly subscribers = new Map<string, (message: NappletMessage) => void>();\n private readonly schemas = new Map<string, RegisteredConfigSchema>();\n readonly handler: ServiceHandler;\n\n constructor(private readonly options: ConfigServiceOptions) {\n this.handler = {\n descriptor: {\n name: 'config',\n version: CONFIG_SERVICE_VERSION,\n description: 'NAP-CONFIG reference service — shell-writes, napplet-reads configuration',\n },\n handleMessage: (windowId, message, send) => this.handleMessage(windowId, message, send),\n onWindowDestroyed: (windowId) => this.destroyWindow(windowId),\n };\n }\n\n getSchema(windowId: string): NappletConfigSchema | null {\n const schema = this.schemas.get(windowId)?.schema;\n return schema ? structuredClone(schema) : null;\n }\n\n getValues(windowId: string): ConfigValues | null {\n const schema = this.schemas.get(windowId)?.schema;\n if (!schema) return null;\n const values = resolveConfigValues(schema, this.options.getValues(windowId));\n return configValuesConform(schema as Record<string, unknown>, values) ? values : null;\n }\n\n publishValues(values: ConfigValues, windowId?: string): void {\n if (windowId) {\n this.publishWindowValues(windowId, values);\n return;\n }\n for (const target of this.subscribers.keys()) this.publishWindowValues(target, values);\n }\n\n private handleMessage(\n windowId: string,\n message: NappletMessage,\n send: (message: NappletMessage) => void,\n ): void {\n switch (message.type) {\n case 'config.get':\n this.handleGet(windowId, message as ConfigGetMessage, send);\n return;\n case 'config.subscribe':\n this.handleSubscribe(windowId, send);\n return;\n case 'config.unsubscribe':\n this.subscribers.delete(windowId);\n this.options.onUnsubscribe?.(windowId);\n return;\n case 'config.registerSchema':\n this.handleRegisterSchema(windowId, message as ConfigRegisterSchemaMessage, send);\n return;\n case 'config.openSettings':\n this.handleOpenSettings(windowId, message as ConfigOpenSettingsMessage, send);\n return;\n default:\n return;\n }\n }\n\n private handleGet(\n windowId: string,\n message: ConfigGetMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const values = this.readValues(windowId, send);\n if (values === undefined) return;\n if (!values) {\n this.sendValuesUnavailable(send, windowId);\n return;\n }\n const reply: ConfigValuesMessage = {\n type: 'config.values',\n id: message.id,\n values,\n };\n send(reply as NappletMessage);\n }\n\n private handleSubscribe(\n windowId: string,\n send: (message: NappletMessage) => void,\n ): void {\n this.subscribers.set(windowId, send);\n const values = this.readValues(windowId, send);\n if (values === undefined) return;\n if (!values) {\n this.sendValuesUnavailable(send, windowId);\n return;\n }\n send({ type: 'config.values', values } as NappletMessage);\n this.options.onSubscribe?.(windowId);\n }\n\n private handleRegisterSchema(\n windowId: string,\n message: ConfigRegisterSchemaMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const nextVersion = message.version ?? readSchemaVersion(message.schema);\n let validation = this.validateRegistration(windowId, message, nextVersion);\n if (validation.ok) validation = this.persistRegisteredSchema(windowId, message.schema, nextVersion);\n const result: ConfigRegisterSchemaResultMessage = validation.ok\n ? { type: 'config.registerSchema.result', id: message.id, ok: true }\n : {\n type: 'config.registerSchema.result',\n id: message.id,\n ok: false,\n code: validation.code,\n error: validation.error,\n };\n send(result as NappletMessage);\n if (validation.ok) this.publishWindowValues(windowId);\n }\n\n private validateRegistration(\n windowId: string,\n message: ConfigRegisterSchemaMessage,\n nextVersion: number | undefined,\n ): ConfigSchemaValidation {\n let validation = validateConfigSchema(message.schema);\n if (validation.ok && message.version !== undefined && (!Number.isSafeInteger(message.version) || message.version < 0)) {\n validation = { ok: false, code: 'invalid-schema', error: 'version must be a non-negative integer' };\n }\n const priorVersion = this.schemas.get(windowId)?.version;\n if (validation.ok && priorVersion !== undefined && nextVersion !== undefined && nextVersion < priorVersion) {\n validation = { ok: false, code: 'version-conflict', error: 'schema version cannot move backwards' };\n }\n if (!validation.ok || !this.options.registerSchema) return validation;\n try {\n return this.options.registerSchema(windowId, message.schema, message.version);\n } catch (cause) {\n return {\n ok: false,\n code: 'invalid-schema',\n error: cause instanceof Error ? cause.message : 'host schema policy failed',\n };\n }\n }\n\n private persistRegisteredSchema(\n windowId: string,\n schema: NappletConfigSchema,\n version: number | undefined,\n ): ConfigSchemaValidation {\n try {\n const resolved = resolveConfigValues(schema, this.options.getValues(windowId));\n this.options.saveValues?.(windowId, resolved);\n this.schemas.set(windowId, {\n schema: structuredClone(schema),\n ...(version !== undefined ? { version } : {}),\n });\n return { ok: true };\n } catch (cause) {\n return {\n ok: false,\n code: 'invalid-schema',\n error: cause instanceof Error ? cause.message : 'host configuration persistence failed',\n };\n }\n }\n\n private handleOpenSettings(\n windowId: string,\n message: ConfigOpenSettingsMessage,\n send: (message: NappletMessage) => void,\n ): void {\n const record = this.schemas.get(windowId);\n if (!record) {\n sendSchemaError(send, 'no-schema', 'no configuration schema is registered');\n return;\n }\n let values: ConfigValues;\n try {\n values = resolveConfigValues(record.schema, this.options.getValues(windowId));\n } catch (cause) {\n sendSchemaError(\n send,\n 'invalid-schema',\n cause instanceof Error ? cause.message : 'host configuration read failed',\n );\n return;\n }\n const section = message.section && schemaHasSection(record.schema, message.section)\n ? message.section\n : undefined;\n this.options.openSettings?.(windowId, section, {\n schema: structuredClone(record.schema),\n values,\n commit: (next) => this.commitValues(windowId, next),\n });\n }\n\n private commitValues(windowId: string, values: ConfigValues): ConfigValues {\n const schema = this.schemas.get(windowId)?.schema;\n if (!schema) throw new Error('no configuration schema is registered');\n const resolved = resolveConfigValues(schema, values);\n if (!configValuesConform(schema as Record<string, unknown>, resolved)) {\n throw new Error('required configuration values are missing or invalid');\n }\n this.options.saveValues?.(windowId, resolved);\n this.publishWindowValues(windowId, resolved);\n return resolved;\n }\n\n private publishWindowValues(windowId: string, supplied?: ConfigValues): void {\n const send = this.subscribers.get(windowId);\n const schema = this.schemas.get(windowId)?.schema;\n if (!send || !schema) return;\n const values = supplied ? resolveConfigValues(schema, supplied) : this.getValues(windowId);\n if (!values || !configValuesConform(schema as Record<string, unknown>, values)) return;\n try {\n send({ type: 'config.values', values } as NappletMessage);\n } catch {\n this.subscribers.delete(windowId);\n }\n }\n\n private readValues(\n windowId: string,\n send: (message: NappletMessage) => void,\n ): ConfigValues | null | undefined {\n try {\n return this.getValues(windowId);\n } catch (cause) {\n sendSchemaError(\n send,\n 'invalid-schema',\n cause instanceof Error ? cause.message : 'host configuration read failed',\n );\n return undefined;\n }\n }\n\n private sendValuesUnavailable(\n send: (message: NappletMessage) => void,\n windowId: string,\n ): void {\n if (!this.schemas.has(windowId)) {\n sendSchemaError(send, 'no-schema', 'no configuration schema is registered');\n return;\n }\n sendSchemaError(send, 'invalid-schema', 'configuration requires valid values before delivery');\n }\n\n private destroyWindow(windowId: string): void {\n this.subscribers.delete(windowId);\n this.schemas.delete(windowId);\n this.options.onWindowDestroyed?.(windowId);\n }\n}\nfunction sendSchemaError(\n send: (message: NappletMessage) => void,\n code: ConfigSchemaErrorCode,\n error: string,\n): void {\n send({ type: 'config.schemaError', code, error } as NappletMessage);\n}\n\nfunction readSchemaVersion(schema: NappletConfigSchema): number | undefined {\n const version = (schema as Record<string, unknown>).$version;\n return Number.isSafeInteger(version) && Number(version) >= 0 ? Number(version) : undefined;\n}\n\nfunction schemaHasSection(schema: NappletConfigSchema, section: string): boolean {\n const properties = (schema as Record<string, unknown>).properties;\n if (!isRecord(properties)) return false;\n for (const child of Object.values(properties)) {\n if (!isRecord(child)) continue;\n if (child['x-napplet-section'] === section) return true;\n if (child.type === 'object' && schemaHasSection(child as NappletConfigSchema, section)) return true;\n }\n return false;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/**\n * resource-service.ts — NAP-RESOURCE reference service (10th NAP domain, v1.7 Phase 40).\n *\n * Shell-side reference implementation for the canonical NAP-RESOURCE wire\n * protocol (`internal-resource.ts` in @kehto/shell/src/types; kehto-internal\n * model per PROJECT.md Decision #31.\n * Handles:\n * Inbound: resource.info, resource.bytes, resource.bytesMany, resource.cancel\n * Outbound: resource.info.result, resource.info.error,\n * resource.bytes.result, resource.bytes.error,\n * resource.bytesMany.result, resource.bytesMany.error\n *\n * ──────────────────────── SCOPE BOUNDARY (RESOURCE-01) ────────────────────────\n * NAP-RESOURCE is a runtime-mediated byte resolver — read-only, atomic.\n *\n * Kehto owns neutral wire lifecycle, cancellation, result projection, and\n * policy tooling. The runtime supplies the resolver and decides whether to use\n * the origin-grant hooks, which schemes to resolve, and which limits to expose.\n * Advisory `resource.info` scheme disclosure never authorizes or blocks a byte\n * request.\n * ──────────────────────────────────────────────────────────────────────────────\n *\n * Runtime integration requires only `fetch`. A runtime MAY also install the\n * optional origin-policy adapter (`isOriginGranted`, `getConnectGrants`, and\n * `resolveIdentity` together).\n *\n * @example\n * ```ts\n * import { createResourceService } from '@kehto/services';\n *\n * const resourceSvc = createResourceService({\n * fetch: (url, init) => globalThis.fetch(url, init),\n * });\n * runtime.registerService('resource', resourceSvc);\n * ```\n *\n * @example\n * ```ts\n * const resourceSvc = createResourceService({\n * fetch: (url, init) => runtimeResources.resolve(url, init),\n * isOriginGranted: (origin, grants) => grants.includes(origin),\n * getConnectGrants: (dTag, hash) => myOriginGrantStore.getOrigins(dTag, hash),\n * resolveIdentity: (windowId) => sessionRegistry.getEntryByWindowId(windowId) ?? null,\n * });\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type {\n ResourceBytesRequest,\n ResourceInfo,\n} from '@napplet/nap/resource/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nexport type {\n ResourceBytesRequest,\n ResourceInfo,\n ResourceSchemeInfo,\n} from '@napplet/nap/resource/types';\n\n/** Resource service version — follows semver. */\nconst RESOURCE_SERVICE_VERSION = '1.0.0';\n\n/** Read-only metadata passed from the resource service to a runtime resolver. */\nexport interface ResourceFetchInit {\n /** Read-only HTTP method selected by the service. */\n method?: string;\n /** Optional runtime-owned request headers. */\n headers?: Record<string, string>;\n /** Request-scoped cancellation signal. */\n signal: AbortSignal;\n /** Authenticated source window, for runtime-private scoped policy state. */\n windowId?: string;\n /** Advisory Blossom server origins for this resource only. */\n servers?: readonly string[];\n}\n\n/** Context passed to a host-provided resource info resolver. */\nexport interface ResourceInfoContext {\n windowId: string;\n identity: { dTag: string; aggregateHash: string } | null;\n}\n\n/** Static or dynamic advisory resource info exposed through `resource.info`. */\nexport type ResourceInfoProvider =\n | ResourceInfo\n | ((context: ResourceInfoContext) => ResourceInfo | Promise<ResourceInfo>);\n\n/**\n * Options for `createResourceService` (options-as-bridge per v1.6 Decision 18).\n *\n * `fetch` is the only required field. Kehto does not select a runtime's scheme\n * or origin policy. The optional origin-policy fields form one adapter and must\n * therefore be supplied together when a runtime chooses to use it.\n */\nexport interface ResourceServiceOptions {\n /**\n * Runtime-supplied resource resolver. Receives the requested URL and returns\n * a byte-classified `Response`.\n *\n * The runtime owns scheme dispatch and every NAP-RESOURCE policy decision.\n * The returned `content-type` must describe the runtime-classified bytes, not\n * an untrusted upstream header. Throw `ResourceServiceError` to preserve a\n * canonical protocol error classification.\n *\n * @param url - The URL from the resource.bytes request\n * @param init - Read-only method, request-scoped AbortSignal, and optional advisory Blossom servers\n */\n fetch(\n url: string,\n init: ResourceFetchInit,\n ): Promise<Response>;\n\n /**\n * Returns true if `origin` is present in `grants` (the list returned by\n * `getConnectGrants` for the napplet's dTag + aggregateHash).\n * Optional: when omitted with `getConnectGrants`, Kehto delegates directly\n * to the runtime resolver without an origin-grant decision.\n *\n * The reference implementation is simply `grants.includes(origin)`. Host apps\n * may provide normalized-origin comparison if needed.\n *\n * @param origin - Parsed origin of the requested URL (scheme + host + port)\n * @param grants - Readonly list from getConnectGrants for this napplet identity\n */\n isOriginGranted?(origin: string, grants: readonly string[]): boolean;\n\n /**\n * Returns the list of allowed fetch origins for the given napplet identity.\n * Called on every `resource.bytes` request — must be synchronous and fast.\n * Optional and paired with `isOriginGranted` plus `resolveIdentity`.\n *\n * Host-supplied grant source (e.g. a static per-dTag allowlist map, or any\n * other host-controlled policy). Returns an empty array to deny all origins.\n *\n * @param dTag - The napplet's d-tag (from session registry)\n * @param aggregateHash - The napplet's aggregate hash (from session registry)\n */\n getConnectGrants?(dTag: string, aggregateHash: string): readonly string[];\n\n /**\n * Resolve a windowId to the napplet's identity (dTag + aggregateHash).\n * Returns null if the window is not in the session registry.\n *\n * Typically wraps `sessionRegistry.getEntryByWindowId(windowId)`.\n *\n * @param windowId - The iframe window identifier\n */\n resolveIdentity?(windowId: string): { dTag: string; aggregateHash: string } | null;\n\n /**\n * Advisory NAP-RESOURCE introspection exposed through `resource.info`.\n *\n * Provide a static snapshot or a resolver when the shell wants to disclose\n * configured schemes and coarse limits. Scheme disclosure is advisory and is\n * never used as fetch authorization. Omit to disclose no schemes or limits.\n */\n resourceInfo?: ResourceInfoProvider;\n}\n\n/**\n * Type alias for the ServiceHandler returned by `createResourceService`.\n * Exported for host apps that need to type-annotate the handler reference.\n */\nexport type ResourceService = ServiceHandler;\n\ninterface ResourceRequestState {\n inFlight: Map<string, { controller: AbortController; windowId: string; requestId: string }>;\n perWindow: Map<string, Set<string>>;\n}\n\nexport type ResourceErrorCode =\n | 'invalid-request'\n | 'not-found'\n | 'blocked-by-policy'\n | 'timeout'\n | 'too-large'\n | 'unsupported-scheme'\n | 'decode-failed'\n | 'network-error'\n | 'quota-exceeded';\n\n/** A runtime resolver failure with a stable NAP-RESOURCE error classification. */\nexport class ResourceServiceError extends Error {\n /**\n * @param code - Canonical NAP-RESOURCE error code.\n * @param message - Diagnostic detail safe to return to the napplet.\n */\n constructor(readonly code: ResourceErrorCode, message: string) {\n super(message);\n this.name = 'ResourceServiceError';\n }\n}\n\ntype ResourceFetchSuccess = {\n ok: true;\n url: string;\n blob: Blob;\n mime: string;\n};\n\ntype ResourceFetchFailure = {\n ok: false;\n url: string;\n error: ResourceErrorCode;\n message: string;\n};\n\ntype ResourceFetchItem = ResourceFetchSuccess | ResourceFetchFailure;\n\nconst DEFAULT_RESOURCE_INFO: ResourceInfo = {\n schemes: [],\n};\n\nfunction requestKey(windowId: string, requestId: string): string {\n return `${windowId}\\u0000${requestId}`;\n}\n\nfunction assertResourceOptions(options: ResourceServiceOptions): void {\n if (typeof options?.fetch !== 'function') {\n throw new Error(\n '[RESOURCE-01] createResourceService requires a runtime-provided fetch resolver.',\n );\n }\n const hasOriginPolicy =\n options.isOriginGranted !== undefined || options.getConnectGrants !== undefined;\n if (\n hasOriginPolicy && (\n typeof options.isOriginGranted !== 'function'\n || typeof options.getConnectGrants !== 'function'\n || typeof options.resolveIdentity !== 'function'\n )\n ) {\n throw new Error(\n '[RESOURCE-01] optional origin policy requires {isOriginGranted, getConnectGrants, resolveIdentity} together.',\n );\n }\n if (options.resolveIdentity !== undefined && typeof options.resolveIdentity !== 'function') {\n throw new Error('[RESOURCE-01] resolveIdentity must be a function when provided.');\n }\n}\n\nfunction trackRequest(\n state: ResourceRequestState,\n requestId: string,\n windowId: string,\n controller: AbortController,\n): void {\n const key = requestKey(windowId, requestId);\n state.inFlight.set(key, { controller, windowId, requestId });\n if (!state.perWindow.has(windowId)) {\n state.perWindow.set(windowId, new Set());\n }\n state.perWindow.get(windowId)!.add(key);\n}\n\nfunction untrackRequest(state: ResourceRequestState, windowId: string, requestId: string): void {\n const key = requestKey(windowId, requestId);\n const entry = state.inFlight.get(key);\n if (entry) {\n state.inFlight.delete(key);\n state.perWindow.get(entry.windowId)?.delete(key);\n }\n}\n\nfunction sendResourceError(\n send: (m: NappletMessage) => void,\n requestId: string,\n message: string,\n error: ResourceErrorCode,\n type: 'resource.bytes.error' | 'resource.bytesMany.error' = 'resource.bytes.error',\n): void {\n send({\n type,\n id: requestId,\n error,\n message,\n } as NappletMessage);\n}\n\nfunction sendBytesManyError(\n send: (m: NappletMessage) => void,\n requestId: string,\n message: string,\n error: ResourceErrorCode,\n): void {\n sendResourceError(send, requestId, message, error, 'resource.bytesMany.error');\n}\n\nfunction parseResourceUrl(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n}\n\nfunction responseMime(response: Response): string {\n return response.headers.get('content-type')?.split(';', 1)[0]?.trim().toLowerCase()\n || 'application/octet-stream';\n}\n\nfunction responseBlob(buffer: ArrayBuffer, mime: string): Blob {\n return new Blob([buffer], { type: mime });\n}\n\nfunction requestIdFromMessage(message: NappletMessage & { id?: unknown; requestId?: unknown }): string | null {\n if (typeof message.id === 'string' && message.id.length > 0) return message.id;\n if (typeof message.requestId === 'string' && message.requestId.length > 0) return message.requestId;\n return null;\n}\n\nfunction normalizeResourceInfo(info: ResourceInfo): ResourceInfo {\n return {\n schemes: Array.isArray(info.schemes)\n ? info.schemes\n .filter((scheme) => typeof scheme?.scheme === 'string')\n .map((scheme) => ({ scheme: scheme.scheme, enabled: scheme.enabled !== false }))\n : [],\n ...(typeof info.maxBytes === 'number' ? { maxBytes: info.maxBytes } : {}),\n ...(typeof info.maxUrls === 'number' ? { maxUrls: info.maxUrls } : {}),\n ...(typeof info.maxServers === 'number' ? { maxServers: info.maxServers } : {}),\n };\n}\n\nasync function resolveResourceInfo(\n options: ResourceServiceOptions,\n windowId: string,\n): Promise<ResourceInfo> {\n const configured = options.resourceInfo ?? DEFAULT_RESOURCE_INFO;\n const info = typeof configured === 'function'\n ? await configured({ windowId, identity: options.resolveIdentity?.(windowId) ?? null })\n : configured;\n return normalizeResourceInfo(info);\n}\n\nasync function handleInfo(\n options: ResourceServiceOptions,\n windowId: string,\n requestId: string,\n send: (m: NappletMessage) => void,\n): Promise<void> {\n try {\n const info = await resolveResourceInfo(options, windowId);\n send({\n type: 'resource.info.result',\n id: requestId,\n info,\n } as NappletMessage);\n } catch (err: unknown) {\n send({\n type: 'resource.info.error',\n id: requestId,\n error: 'unavailable',\n message: err instanceof Error ? err.message : String(err),\n } as NappletMessage);\n }\n}\n\nfunction resourceInvalidRequest(url: string, message: string): ResourceFetchFailure {\n return {\n ok: false,\n url,\n error: 'invalid-request',\n message,\n };\n}\n\nfunction classifyResourceFailure(error: unknown): { error: ResourceErrorCode; message: string } {\n return {\n error: error instanceof ResourceServiceError ? error.code : 'network-error',\n message: error instanceof Error ? error.message : String(error),\n };\n}\n\nasync function fetchResourceItem(\n options: ResourceServiceOptions,\n windowId: string,\n info: ResourceInfo,\n request: ResourceBytesRequest,\n signal: AbortSignal,\n): Promise<ResourceFetchItem> {\n const { url } = request;\n const parsedUrl = parseResourceUrl(url);\n if (!parsedUrl) return resourceInvalidRequest(url, `invalid URL: ${url}`);\n if (parsedUrl.origin !== 'null' && options.isOriginGranted && options.getConnectGrants) {\n const identity = options.resolveIdentity?.(windowId) ?? null;\n if (!identity) {\n return {\n ok: false,\n url,\n error: 'blocked-by-policy',\n message: 'napplet identity not resolvable for runtime origin policy',\n };\n }\n const origin = parsedUrl.origin;\n const grants = options.getConnectGrants(identity.dTag, identity.aggregateHash);\n if (!options.isOriginGranted(origin, grants)) {\n return {\n ok: false,\n url,\n error: 'blocked-by-policy',\n message: `origin ${origin} not granted by runtime policy`,\n };\n }\n }\n\n try {\n const response = await options.fetch(url, {\n method: 'GET',\n signal,\n windowId,\n ...(parsedUrl.protocol === 'blossom:' && request.servers !== undefined\n ? { servers: [...request.servers] }\n : {}),\n });\n const buffer = await response.arrayBuffer();\n if (info.maxBytes !== undefined && buffer.byteLength > info.maxBytes) {\n return { ok: false, url, error: 'too-large', message: `resource exceeds ${info.maxBytes} bytes` };\n }\n const mime = responseMime(response);\n return {\n ok: true,\n url,\n blob: responseBlob(buffer, mime),\n mime,\n };\n } catch (err: unknown) {\n const isAbort =\n signal.aborted ||\n (err instanceof Error && (err.name === 'AbortError' || err.name === 'DOMException'));\n return {\n ok: false,\n url,\n error: isAbort ? 'timeout' : err instanceof ResourceServiceError ? err.code : 'network-error',\n message: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nasync function handleBytes(\n options: ResourceServiceOptions,\n state: ResourceRequestState,\n windowId: string,\n msg: { requestId: string } & ResourceBytesRequest,\n send: (m: NappletMessage) => void,\n): Promise<void> {\n const { requestId, ...request } = msg;\n const controller = new AbortController();\n trackRequest(state, requestId, windowId, controller);\n\n try {\n const info = await resolveResourceInfo(options, windowId);\n const item = await fetchResourceItem(options, windowId, info, request, controller.signal);\n if (controller.signal.aborted) return;\n if (!item.ok) {\n sendResourceError(send, requestId, item.message, item.error);\n return;\n }\n send({\n type: 'resource.bytes.result',\n id: requestId,\n blob: item.blob,\n mime: item.mime,\n } as NappletMessage);\n } catch (error: unknown) {\n if (!controller.signal.aborted) {\n const failure = classifyResourceFailure(error);\n sendResourceError(send, requestId, failure.message, failure.error);\n }\n } finally {\n untrackRequest(state, windowId, requestId);\n }\n}\n\nasync function handleBytesMany(\n options: ResourceServiceOptions,\n state: ResourceRequestState,\n windowId: string,\n msg: { requestId: string; requests: readonly ResourceBytesRequest[] },\n send: (m: NappletMessage) => void,\n): Promise<void> {\n const { requestId, requests } = msg;\n if (!isResourceRequestList(requests)) {\n sendBytesManyError(send, requestId, 'resource.bytesMany requires a non-empty requests array', 'invalid-request');\n return;\n }\n\n let info: ResourceInfo;\n try {\n info = await resolveResourceInfo(options, windowId);\n } catch (error: unknown) {\n const failure = classifyResourceFailure(error);\n sendBytesManyError(send, requestId, failure.message, failure.error);\n return;\n }\n if (info.maxUrls !== undefined && requests.length > info.maxUrls) {\n sendBytesManyError(send, requestId, `resource.bytesMany exceeds ${info.maxUrls} URLs`, 'too-large');\n return;\n }\n\n const controller = new AbortController();\n trackRequest(state, requestId, windowId, controller);\n try {\n const items: Array<Record<string, unknown>> = [];\n for (const request of requests) {\n const item = await fetchResourceItem(options, windowId, info, request, controller.signal);\n if (controller.signal.aborted) return;\n if (item.ok) {\n items.push({\n url: item.url,\n ok: true,\n blob: item.blob,\n mime: item.mime,\n });\n } else {\n items.push({\n url: item.url,\n ok: false,\n error: item.error,\n message: item.message,\n });\n }\n }\n send({\n type: 'resource.bytesMany.result',\n id: requestId,\n items,\n } as NappletMessage);\n } catch (error: unknown) {\n if (!controller.signal.aborted) {\n const failure = classifyResourceFailure(error);\n sendBytesManyError(send, requestId, failure.message, failure.error);\n }\n } finally {\n untrackRequest(state, windowId, requestId);\n }\n}\n\nfunction hasValidServerHints(request: { servers?: readonly unknown[] }): boolean {\n return request.servers === undefined\n || (Array.isArray(request.servers) && request.servers.every((server) => typeof server === 'string'));\n}\n\nfunction isResourceRequestList(value: unknown): value is readonly ResourceBytesRequest[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((request) => (\n typeof request === 'object'\n && request !== null\n && typeof (request as ResourceBytesRequest).url === 'string'\n && hasValidServerHints(request as ResourceBytesRequest)\n ));\n}\n\nfunction handleCancel(state: ResourceRequestState, windowId: string, requestId: string): void {\n const entry = state.inFlight.get(requestKey(windowId, requestId));\n if (entry) {\n entry.controller.abort();\n }\n}\n\nfunction destroyWindowRequests(state: ResourceRequestState, windowId: string): void {\n const requestIds = state.perWindow.get(windowId);\n if (!requestIds) return;\n for (const key of requestIds) {\n const entry = state.inFlight.get(key);\n if (entry) {\n entry.controller.abort();\n state.inFlight.delete(key);\n }\n }\n state.perWindow.delete(windowId);\n}\n\n/**\n * Create a NAP-RESOURCE reference service.\n *\n * Implements the NAP-RESOURCE request/response protocol: `resource.info`,\n * `resource.bytes`, `resource.bytesMany`, `resource.cancel`, and their\n * result/error envelopes.\n *\n * The runtime resolver is validated at construction. Runtime implementers may\n * additionally install the origin-grant adapter; Kehto provides and enforces\n * the hook contract without selecting the grants or requiring that policy.\n *\n * Returns a `ServiceHandler` (no `publishValues`-style surface — resource has\n * no shell-initiated push beyond the response/error path).\n *\n * @param options - Runtime resolver plus optional runtime-owned policy helpers\n * @returns ServiceHandler to register via `runtime.registerService('resource', handler)`\n *\n * @example\n * ```ts\n * import { createResourceService } from '@kehto/services';\n *\n * const svc = createResourceService({\n * fetch: (url, init) => runtimeResources.resolve(url, init),\n * isOriginGranted: (origin, grants) => grants.includes(origin),\n * getConnectGrants: (dTag, hash) => myOriginGrantStore.getOrigins(dTag, hash),\n * resolveIdentity: (windowId) => sessionRegistry.getEntryByWindowId(windowId) ?? null,\n * });\n * runtime.registerService('resource', svc);\n * ```\n */\nexport function createResourceService(options: ResourceServiceOptions): ResourceService {\n assertResourceOptions(options);\n const state: ResourceRequestState = {\n inFlight: new Map(),\n perWindow: new Map<string, Set<string>>(),\n };\n\n const descriptor: ServiceDescriptor = {\n name: 'resource',\n version: RESOURCE_SERVICE_VERSION,\n description:\n 'NAP-RESOURCE service — runtime-resolved byte transport (RESOURCE-01..06)',\n };\n\n const handler: ServiceHandler = {\n descriptor,\n\n handleMessage(\n windowId: string,\n message: NappletMessage,\n send: (msg: NappletMessage) => void,\n ): void {\n switch (message.type) {\n case 'resource.bytes': {\n const m = message as NappletMessage & {\n id?: string;\n requestId?: string;\n url: string;\n servers?: readonly string[];\n };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n if (typeof m.url !== 'string' || !hasValidServerHints(m)) {\n sendResourceError(send, requestId, 'resource.bytes requires a URL and optional string servers', 'invalid-request');\n return;\n }\n handleBytes(options, state, windowId, {\n requestId,\n url: m.url,\n ...(m.servers !== undefined ? { servers: [...m.servers] } : {}),\n }, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.info': {\n const m = message as NappletMessage & { id?: string; requestId?: string };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n handleInfo(options, windowId, requestId, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.bytesMany': {\n const m = message as NappletMessage & {\n id?: string;\n requestId?: string;\n requests?: readonly ResourceBytesRequest[];\n };\n const requestId = requestIdFromMessage(m);\n if (!requestId) return;\n handleBytesMany(options, state, windowId, { requestId, requests: m.requests ?? [] }, send).catch(() => { /* errors surface via send() */ });\n return;\n }\n\n case 'resource.cancel': {\n const m = message as NappletMessage & { id?: string; requestId?: string };\n const requestId = requestIdFromMessage(m);\n if (requestId) handleCancel(state, windowId, requestId);\n return;\n }\n\n default:\n // Unknown resource.* message — silently ignored per NIP-5D.\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n destroyWindowRequests(state, windowId);\n },\n };\n\n return handler;\n}\n","/**\n * outbox-service.ts — NAP-OUTBOX (outbox-aware relay routing) reference service.\n *\n * Shell-side handler for the NAP-OUTBOX wire protocol. It is a pure envelope\n * router: it validates `outbox.*` envelopes, delegates the actual relay\n * discovery / routing / dedup / publish-fanout work to an injected\n * {@link OutboxRouter}, and posts the correlated result / lifecycle messages\n * (echoing the request `id` or `subId`) back to the napplet.\n *\n * The router is injected (options-as-bridge) so this service has no Nostr\n * dependency and is fully unit-testable. A concrete relay-pool-backed router\n * ships alongside as {@link createRelayPoolOutboxRouter}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: outbox.getEvent, outbox.query, outbox.subscribe, outbox.close,\n * outbox.publish, outbox.resolveRelays\n * Outbound: outbox.getEvent.result, outbox.query.result, outbox.event,\n * outbox.closed, outbox.publish.result,\n * outbox.resolveRelays.result\n *\n * The shell owns relay discovery, routing, fallback, deduplication, signature\n * validation, signing, and publish fanout policy — all of which live behind\n * the {@link OutboxRouter}. This service only marshals the wire protocol.\n *\n * @example\n * ```ts\n * import { createOutboxService, createRelayPoolOutboxRouter } from '@kehto/services';\n *\n * const router = createRelayPoolOutboxRouter({ relayPool, loadRelayLists, fallbackRelays });\n * runtime.registerService('outbox', createOutboxService({ router }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayEventResult, ServiceDescriptor, ServiceHandler, ServiceRuntimeContext } from '@kehto/runtime';\n\n/** Outbox service version — follows semver. */\nconst OUTBOX_SERVICE_VERSION = '1.0.0';\n\n/** Options for a single-event outbox lookup. */\nexport interface OutboxEventOptions {\n /** Author hint for routing through the author's outbox relays. */\n author?: string;\n /** Relay hints; treated as a hint subject to shell validation, not a bypass. */\n relays?: string[];\n /** Wall-clock budget for the lookup, in milliseconds. */\n timeoutMs?: number;\n}\n\n/** Options for a one-shot outbox query. */\nexport interface OutboxQueryOptions {\n /** Explicit author hints (augment/override authors derived from filters). */\n authors?: string[];\n /** Relay hints; treated as a hint subject to shell validation, not a bypass. */\n relays?: string[];\n /** Maximum events to collect. */\n limit?: number;\n /** Wall-clock budget for the query, in milliseconds. */\n timeoutMs?: number;\n}\n\n/** Options for a live outbox subscription. */\nexport interface OutboxSubscribeOptions extends OutboxQueryOptions {}\n\n/** Options for an outbox publish. */\nexport interface OutboxPublishOptions {\n /** Explicit relay URL fanout candidates, subject to shell validation. */\n relays?: string[];\n /** Include the shell user's NIP-65 write relays. Defaults to true. */\n toOutbox?: boolean;\n /** Recipient pubkeys whose NIP-65 read relays are required fanout targets. */\n toInboxes?: string[];\n}\n\n/** A read/write target for relay-plan resolution. */\nexport interface OutboxTarget {\n /** Authors to resolve relays for. */\n authors?: string[];\n /** Single pubkey to resolve relays for. */\n pubkey?: string;\n /** Whether the plan is for reading (their write relays) or writing (their read relays). */\n direction?: 'read' | 'write';\n}\n\n/** The relay plan the shell would use for a target. */\nexport interface OutboxRelayPlan {\n /** Resolved relay URLs. */\n relays: string[];\n /** Where the plan came from. */\n source: 'nip65' | 'cache' | 'policy' | 'fallback';\n /** Authors for which no relay list could be resolved. */\n missingAuthors?: string[];\n}\n\n/** Outcome of a single-event outbox lookup. */\nexport interface OutboxEventResult {\n /** The signature-validated event result matching the requested event id, when found. */\n result?: RelayEventResult;\n /** True when some relay lists or connections failed and the lookup was partial. */\n incomplete?: boolean;\n /** Error reason when the lookup could not complete or did not find the event. */\n error?: string;\n}\n\n/** Outcome of an outbox query, as returned by the {@link OutboxRouter}. */\nexport interface OutboxResult {\n /** Deduplicated, signature-validated event results. */\n events: RelayEventResult[];\n /** True when some relay lists or connections failed and results are partial. */\n incomplete?: boolean;\n /** Error reason when the query could not complete. */\n error?: string;\n}\n\n/** Outcome of an outbox publish, as returned by the {@link OutboxRouter}. */\nexport interface OutboxPublishResult {\n /** Whether the publish succeeded on at least the required relays. */\n ok: boolean;\n /** The signed event returned by the shell. */\n event?: NostrEvent;\n /** The published event id. */\n eventId?: string;\n /** Map of relay URL -> per-relay publish success. */\n relays?: Record<string, boolean>;\n /** Error reason when the publish failed. */\n error?: string;\n}\n\n/** Sink an {@link OutboxRouter} streams subscription lifecycle through. */\nexport interface OutboxSubscriptionSink {\n /** Deliver a matching event result. */\n event(result: RelayEventResult): void;\n /** Signal that the subscription was closed upstream; `reason` is optional. */\n closed(reason?: string): void;\n}\n\n/** Sink a bounded outbox query streams verified events through as they arrive. */\nexport interface OutboxQueryStreamSink {\n /** Deliver one deduplicated, signature-validated event result. */\n event(result: RelayEventResult): void;\n}\n\n/** Handle for a bounded streaming query and its compatibility aggregation. */\nexport interface OutboxQueryStream {\n /** Aggregated result, settled on all-relay EOSE or the overall deadline. */\n readonly result: Promise<OutboxResult>;\n /** Stop relay reads and settle {@link result} as incomplete. */\n close(): void;\n}\n\n/** Handle to a router-owned subscription. */\nexport interface OutboxRouterSubscription {\n /** Stop the subscription and release its relay connections. */\n close(): void;\n}\n\n/**\n * Abstract outbox router. Implementors own relay discovery (NIP-65 / NIP-66),\n * routing, fallback, deduplication, signature validation, signing, and publish\n * fanout. The service translates wire envelopes into these calls and back.\n */\nexport interface OutboxRouter {\n /** Fetch and validate one event by id through shell-owned outbox routing. */\n getEvent?(eventId: string, options?: OutboxEventOptions): Promise<OutboxEventResult>;\n /** Resolve relays, query them, dedup by id, validate signatures, collect events. */\n query(filters: NostrFilter[], options?: OutboxQueryOptions): Promise<OutboxResult>;\n /** Open a live outbox-aware subscription, streaming through `sink`. */\n subscribe(\n filters: NostrFilter[],\n options: OutboxSubscribeOptions | undefined,\n sink: OutboxSubscriptionSink,\n ): OutboxRouterSubscription;\n /** Sign `template` and fan it out to the relevant write/inbox relays. */\n publish(template: EventTemplate, options?: OutboxPublishOptions): Promise<OutboxPublishResult>;\n /** Return the relay plan the shell would use for a read/write target. */\n resolveRelays(target: OutboxTarget): Promise<OutboxRelayPlan>;\n}\n\n/** Outbox router with an incremental surface for bounded queries. */\nexport interface StreamingOutboxRouter extends OutboxRouter {\n /**\n * Start policy/fallback reads immediately, stream verified events through\n * `sink`, and attach author relays when concurrent discovery resolves.\n * `options.limit` applies to the final aggregate; the sink receives every\n * verified unique arrival until EOSE, close, or the overall deadline.\n */\n queryStream(\n filters: NostrFilter[],\n options: OutboxQueryOptions | undefined,\n sink: OutboxQueryStreamSink,\n ): OutboxQueryStream;\n}\n\n/** Options for {@link createOutboxService}. */\nexport interface OutboxServiceOptions {\n /** The outbox router the shell uses to reach relays. Required. */\n router: OutboxRouter;\n /**\n * Optionally selects a source-scoped router for event-returning reads.\n *\n * The selected router is used for `outbox.getEvent`, `outbox.query`, and\n * `outbox.subscribe`. A query-specific router takes precedence when both\n * hooks are supplied.\n *\n * @param windowId - The authenticated source window for the request.\n * @param context - Narrow runtime context for source-bound policy checks.\n * @returns Router used for that source's event-returning reads.\n */\n getReadRouter?(windowId: string, context: ServiceRuntimeContext | undefined): OutboxRouter;\n /**\n * Optionally selects a query-specific router for a live napplet source.\n *\n * @param windowId - The authenticated source window for the request.\n * @param context - Narrow runtime context for source-bound policy checks.\n * @returns Router used only for that source's one-shot outbox queries.\n */\n getQueryRouter?(windowId: string, context: ServiceRuntimeContext | undefined): OutboxRouter;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst OUTBOX_DESCRIPTOR: ServiceDescriptor = {\n name: 'outbox',\n version: OUTBOX_SERVICE_VERSION,\n description: 'NAP-OUTBOX outbox-aware relay routing — getEvent/query/subscribe/publish/resolveRelays',\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction stringArray(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const strings = value.filter((item): item is string => typeof item === 'string');\n return strings.length > 0 ? strings : undefined;\n}\n\nfunction uint(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\nfunction sanitizeEventOptions(raw: unknown): OutboxEventOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxEventOptions = {};\n if (typeof raw.author === 'string') out.author = raw.author;\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n const timeoutMs = uint(raw.timeoutMs);\n if (timeoutMs !== undefined) out.timeoutMs = timeoutMs;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeQueryOptions(raw: unknown): OutboxQueryOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxQueryOptions = {};\n const authors = stringArray(raw.authors);\n if (authors) out.authors = authors;\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n const limit = uint(raw.limit);\n if (limit !== undefined) out.limit = limit;\n const timeoutMs = uint(raw.timeoutMs);\n if (timeoutMs !== undefined) out.timeoutMs = timeoutMs;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nconst sanitizeSubscribeOptions = sanitizeQueryOptions;\n\nfunction sanitizePublishOptions(raw: unknown): OutboxPublishOptions | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxPublishOptions = {};\n const relays = stringArray(raw.relays);\n if (relays) out.relays = relays;\n if (typeof raw.toOutbox === 'boolean') out.toOutbox = raw.toOutbox;\n const toInboxes = stringArray(raw.toInboxes);\n if (toInboxes) out.toInboxes = toInboxes;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction sanitizeTarget(raw: unknown): OutboxTarget | undefined {\n if (!isRecord(raw)) return undefined;\n const out: OutboxTarget = {};\n const authors = stringArray(raw.authors);\n if (authors) out.authors = authors;\n if (typeof raw.pubkey === 'string') out.pubkey = raw.pubkey;\n if (raw.direction === 'read' || raw.direction === 'write') out.direction = raw.direction;\n return out;\n}\n\n/**\n * Normalize a wire `filters` field (a single NIP-01 filter or an array) into a\n * filter array. Returns `null` when the input is missing or has no usable\n * filter objects.\n */\nfunction normalizeFilters(raw: unknown): NostrFilter[] | null {\n if (Array.isArray(raw)) {\n const filters = raw.filter((f): f is NostrFilter => typeof f === 'object' && f !== null);\n return filters.length > 0 ? filters : null;\n }\n if (typeof raw === 'object' && raw !== null) return [raw as NostrFilter];\n return null;\n}\n\nfunction buildEventQuery(eventId: string, eventOptions?: OutboxEventOptions): { filter: NostrFilter; queryOptions: OutboxQueryOptions } {\n const filter: NostrFilter = { ids: [eventId] };\n const queryOptions: OutboxQueryOptions = { limit: 1 };\n if (typeof eventOptions?.author === 'string' && eventOptions.author.length > 0) {\n filter.authors = [eventOptions.author];\n queryOptions.authors = [eventOptions.author];\n }\n if (Array.isArray(eventOptions?.relays)) queryOptions.relays = eventOptions.relays;\n if (eventOptions?.timeoutMs !== undefined) queryOptions.timeoutMs = eventOptions.timeoutMs;\n return { filter, queryOptions };\n}\n\nfunction eventResultFromQuery(eventId: string, result: OutboxResult): OutboxEventResult {\n const relayResult = result.events.find((candidate) => candidate.event.id === eventId);\n const eventResult: OutboxEventResult = {};\n if (relayResult) eventResult.result = relayResult;\n if (!relayResult) eventResult.error = result.error ?? 'not found';\n else if (result.error !== undefined) eventResult.error = result.error;\n if (result.incomplete !== undefined) eventResult.incomplete = result.incomplete;\n return eventResult;\n}\n\nfunction fallbackGetEvent(router: OutboxRouter, eventId: string, eventOptions?: OutboxEventOptions): Promise<OutboxEventResult> {\n const { filter, queryOptions } = buildEventQuery(eventId, eventOptions);\n return router.query([filter], queryOptions).then((result) => eventResultFromQuery(eventId, result));\n}\n\nfunction sendEventResult(id: string, eventId: string, result: OutboxEventResult, send: Send): void {\n if (result.result && result.result.event.id !== eventId) {\n send({ type: 'outbox.getEvent.result', id, error: 'not found' } as NappletMessage);\n return;\n }\n send({\n type: 'outbox.getEvent.result',\n id,\n ...(result.result === undefined ? {} : { result: result.result }),\n ...(result.incomplete === undefined ? {} : { incomplete: result.incomplete }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage);\n}\n\nfunction handleGetEvent(router: OutboxRouter, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; eventId?: unknown; options?: unknown };\n const id = m.id ?? '';\n if (typeof m.eventId !== 'string' || m.eventId.length === 0) {\n send({ type: 'outbox.getEvent.result', id, error: 'invalid filter' } as NappletMessage);\n return;\n }\n const getEvent = router.getEvent ?? ((eventId, eventOptions) => fallbackGetEvent(router, eventId, eventOptions));\n void getEvent(m.eventId, sanitizeEventOptions(m.options))\n .then((result) => sendEventResult(id, m.eventId as string, result, send))\n .catch((err) =>\n send({ type: 'outbox.getEvent.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n}\n\n/**\n * Create the NAP-OUTBOX service handler.\n *\n * @param options - Must provide an {@link OutboxRouter}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('outbox', handler)`.\n * @throws If `options.router` is missing.\n */\nexport function createOutboxService(options: OutboxServiceOptions): ServiceHandler {\n if (!options || typeof options.router !== 'object' || options.router === null) {\n throw new Error('createOutboxService: options.router is required');\n }\n const { router, getReadRouter, getQueryRouter } = options;\n let runtimeContext: ServiceRuntimeContext | undefined;\n\n // Active subscriptions keyed by `windowId:subId` for lifecycle management.\n const subscriptions = new Map<string, OutboxRouterSubscription>();\n\n function handleQuery(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; filters?: unknown; options?: unknown };\n const id = m.id ?? '';\n const filters = normalizeFilters(m.filters);\n if (!filters) {\n send({ type: 'outbox.query.result', id, events: [], error: 'invalid filter' } as NappletMessage);\n return;\n }\n const queryRouter = getQueryRouter?.(windowId, runtimeContext)\n ?? getReadRouter?.(windowId, runtimeContext)\n ?? router;\n void queryRouter\n .query(filters, sanitizeQueryOptions(m.options))\n .then((result) =>\n send({\n type: 'outbox.query.result',\n id,\n events: result.events,\n ...(result.incomplete === undefined ? {} : { incomplete: result.incomplete }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage),\n )\n .catch((err) =>\n send({ type: 'outbox.query.result', id, events: [], error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleSubscribe(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { subId?: string; filters?: unknown; options?: unknown };\n const subId = m.subId;\n if (typeof subId !== 'string' || subId.length === 0) return;\n const subKey = `${windowId}:${subId}`;\n\n // Replace any existing subscription for this key.\n subscriptions.get(subKey)?.close();\n subscriptions.delete(subKey);\n\n const filters = normalizeFilters(m.filters);\n if (!filters) {\n send({ type: 'outbox.closed', subId, reason: 'invalid filter' } as NappletMessage);\n return;\n }\n\n const sink: OutboxSubscriptionSink = {\n event: (result) => send({ type: 'outbox.event', subId, result } as NappletMessage),\n closed: (reason) => {\n subscriptions.delete(subKey);\n send({ type: 'outbox.closed', subId, ...(reason === undefined ? {} : { reason }) } as NappletMessage);\n },\n };\n\n const readRouter = getReadRouter?.(windowId, runtimeContext) ?? router;\n subscriptions.set(subKey, readRouter.subscribe(filters, sanitizeSubscribeOptions(m.options), sink));\n }\n\n function handleClose(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { subId?: string };\n const subId = m.subId;\n if (typeof subId !== 'string') return;\n const subKey = `${windowId}:${subId}`;\n subscriptions.get(subKey)?.close();\n subscriptions.delete(subKey);\n send({ type: 'outbox.closed', subId } as NappletMessage);\n }\n\n function handlePublish(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; event?: EventTemplate; options?: unknown };\n const id = m.id ?? '';\n if (!m.event || typeof m.event !== 'object') {\n send({ type: 'outbox.publish.result', id, ok: false, error: 'invalid filter' } as NappletMessage);\n return;\n }\n void router\n .publish(m.event, sanitizePublishOptions(m.options))\n .then((result) =>\n send({\n type: 'outbox.publish.result',\n id,\n ok: result.ok,\n ...(result.event === undefined ? {} : { event: result.event }),\n ...(result.eventId === undefined ? {} : { eventId: result.eventId }),\n ...(result.relays === undefined ? {} : { relays: result.relays }),\n ...(result.error === undefined ? {} : { error: result.error }),\n } as NappletMessage),\n )\n .catch((err) =>\n send({ type: 'outbox.publish.result', id, ok: false, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleResolveRelays(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; target?: unknown };\n const id = m.id ?? '';\n const target = sanitizeTarget(m.target);\n if (!target) {\n send({ type: 'outbox.resolveRelays.result', id, error: 'invalid filter' } as NappletMessage);\n return;\n }\n void router\n .resolveRelays(target)\n .then((plan) => send({ type: 'outbox.resolveRelays.result', id, plan } as NappletMessage))\n .catch((err) =>\n send({ type: 'outbox.resolveRelays.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n return {\n descriptor: OUTBOX_DESCRIPTOR,\n onRegistered(context: ServiceRuntimeContext): void {\n runtimeContext = context;\n },\n onUnregistered(): void {\n runtimeContext = undefined;\n },\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'outbox.getEvent':\n handleGetEvent(getReadRouter?.(windowId, runtimeContext) ?? router, message, send);\n return;\n case 'outbox.query':\n handleQuery(windowId, message, send);\n return;\n case 'outbox.subscribe':\n handleSubscribe(windowId, message, send);\n return;\n case 'outbox.close':\n handleClose(windowId, message, send);\n return;\n case 'outbox.publish':\n handlePublish(message, send);\n return;\n case 'outbox.resolveRelays':\n handleResolveRelays(message, send);\n return;\n default:\n // Unknown outbox.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, sub] of subscriptions) {\n if (key.startsWith(prefix)) {\n sub.close();\n subscriptions.delete(key);\n }\n }\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'outbox request failed';\n}\n","/**\n * relay-pool-outbox-router.ts — concrete {@link StreamingOutboxRouter} backed by a relay pool.\n *\n * Implements the outbox-model routing that NAP-OUTBOX centralizes so napplets\n * don't each reinvent it: derive authors, resolve their NIP-65 relays, fan a\n * per-relay subscription out across the plan, deduplicate by event id (while\n * recording every relay an event was observed on), validate signatures, and —\n * for publish — sign the template and fan it out to the relevant write/inbox\n * relays.\n *\n * NIP-65 relay-list *fetching* is the host's concern (it may come from a\n * kind-10002 cache, a NIP-66 indexer via `@kehto/nip/66`, or a live query), so\n * it is injected via {@link RelayPoolOutboxRouterOptions.loadRelayLists}. The\n * relay pool, signer, and signature verifier are injected too — keeping this\n * router browser-agnostic and unit-testable with mocks.\n *\n * Relay-selection model (per the outbox model):\n * - reading an author's events → their **write** relays (where they publish)\n * - writing to reach an author → their **read** relays (their inbox)\n *\n * @example\n * ```ts\n * import { createOutboxService, createRelayPoolOutboxRouter } from '@kehto/services';\n *\n * const router = createRelayPoolOutboxRouter({\n * relayPool: myOutboxPool,\n * loadRelayLists: (pubkeys) => relayListCache.getMany(pubkeys),\n * fallbackRelays: ['wss://relay.damus.io', 'wss://nos.lol'],\n * signEvent: (tmpl) => signer.signEvent(tmpl),\n * verifyEvent: (ev) => verifyEvent(ev),\n * });\n * runtime.registerService('outbox', createOutboxService({ router }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NostrEvent, NostrFilter } from '@napplet/core';\nimport { createRelayEventResultWithHints } from '@kehto/runtime';\nimport type { RelayEventResult } from '@kehto/runtime';\nimport type {\n OutboxResult,\n OutboxPublishResult,\n OutboxRelayPlan,\n OutboxEventOptions,\n OutboxEventResult,\n OutboxQueryOptions,\n OutboxSubscribeOptions,\n OutboxPublishOptions,\n OutboxTarget,\n OutboxSubscriptionSink,\n OutboxRouterSubscription,\n OutboxQueryStream,\n OutboxQueryStreamSink,\n StreamingOutboxRouter,\n} from './outbox-service.js';\nimport type {\n DiscoveredReadRelays,\n LiveOutboxSubscription,\n OutboxCollector,\n PublishTargetResolution,\n RelayListEntry,\n RelayPoolOutboxRouterOptions,\n RequiredRelayResolution,\n RouterCtx,\n} from './relay-pool-outbox-types.js';\n\nexport type {\n OutboxRelayPool,\n RelayListEntry,\n RelayPoolOutboxRouterOptions,\n} from './relay-pool-outbox-types.js';\n\n// Timer globals available in all JS runtimes.\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\n/** Default per-query wall-clock budget when `options.timeoutMs` is unset. */\nconst DEFAULT_QUERY_TIMEOUT_MS = 4000;\n\n/** Default relay gate: only ws(s):// URLs are permitted. */\nfunction defaultRelayAllowed(url: string): boolean {\n return typeof url === 'string' && (url.startsWith('wss://') || url.startsWith('ws://'));\n}\n\n/** Collect a deduplicated author set from filters + explicit option hints. */\nfunction deriveAuthors(filters: NostrFilter[], optionAuthors?: string[]): string[] {\n const authors = new Set<string>();\n for (const filter of filters) {\n for (const author of filter.authors ?? []) authors.add(author);\n }\n for (const author of optionAuthors ?? []) authors.add(author);\n return [...authors];\n}\n\n/** Apply the relay gate to a candidate set, deduplicating. */\nfunction allowed(ctx: RouterCtx, urls: Iterable<string>): string[] {\n const out = new Set<string>();\n for (const url of urls) {\n if (ctx.isRelayAllowed(url)) out.add(url);\n }\n return [...out];\n}\n\nasync function resolveRequiredRelays(\n ctx: RouterCtx,\n pubkeys: string[],\n direction: 'read' | 'write',\n): Promise<RequiredRelayResolution> {\n const useWrite = direction === 'read';\n const relays = new Set<string>();\n const missingAuthors: string[] = [];\n const deniedAuthors: string[] = [];\n const uniquePubkeys = [...new Set(pubkeys)];\n\n if (uniquePubkeys.length === 0) return { relays: [], missingAuthors, deniedAuthors };\n\n const lists = await ctx.loadRelayLists(uniquePubkeys);\n for (const pubkey of uniquePubkeys) {\n const entry = lists.get(pubkey);\n const candidateRelays = entry ? (useWrite ? entry.write : entry.read) : undefined;\n if (!candidateRelays || candidateRelays.length === 0) {\n missingAuthors.push(pubkey);\n continue;\n }\n\n const allowedRelays = allowed(ctx, candidateRelays);\n if (allowedRelays.length === 0) {\n deniedAuthors.push(pubkey);\n continue;\n }\n\n for (const url of allowedRelays) relays.add(url);\n }\n\n return { relays: [...relays], missingAuthors, deniedAuthors };\n}\n\n/**\n * Resolve the relay plan for a set of pubkeys. Returns the allowed relay set,\n * its provenance, and any pubkeys whose relay list was unavailable.\n */\nasync function resolvePlan(\n ctx: RouterCtx,\n pubkeys: string[],\n direction: 'read' | 'write',\n relayHints?: string[],\n): Promise<OutboxRelayPlan> {\n const useWrite = direction === 'read';\n const discovered = new Set<string>();\n const missingAuthors: string[] = [];\n let sawNip65 = false;\n\n if (pubkeys.length > 0) {\n const lists = await ctx.loadRelayLists(pubkeys);\n for (const pubkey of pubkeys) {\n const entry = lists.get(pubkey);\n const relays = entry ? (useWrite ? entry.write : entry.read) : undefined;\n if (relays && relays.length > 0) {\n sawNip65 = true;\n for (const url of relays) discovered.add(url);\n } else {\n missingAuthors.push(pubkey);\n }\n }\n }\n\n const hintRelays = allowed(ctx, relayHints ?? []);\n let relays = allowed(ctx, discovered);\n let source: OutboxRelayPlan['source'];\n if (relays.length > 0) {\n source = sawNip65 ? 'nip65' : 'policy';\n } else {\n relays = allowed(ctx, ctx.fallbackRelays);\n source = relays.length > 0 ? 'fallback' : 'policy';\n }\n\n // Relay hints from the napplet augment the host-owned plan, subject to the relay gate.\n for (const url of hintRelays) {\n if (!relays.includes(url)) relays.push(url);\n }\n if (relays.length === 0) source = 'fallback';\n\n const plan: OutboxRelayPlan = { relays, source };\n if (missingAuthors.length > 0) plan.missingAuthors = missingAuthors;\n return plan;\n}\n\n/** Extract policy-allowed author write relays from already-loaded NIP-65 data. */\nfunction discoveredReadRelays(\n ctx: RouterCtx,\n authors: string[],\n lists: Map<string, RelayListEntry>,\n): DiscoveredReadRelays {\n const relays = new Set<string>();\n const missingAuthors: string[] = [];\n for (const author of new Set(authors)) {\n const writeRelays = lists.get(author)?.write;\n if (!writeRelays || writeRelays.length === 0) {\n missingAuthors.push(author);\n continue;\n }\n for (const url of allowed(ctx, writeRelays)) relays.add(url);\n }\n return { relays: [...relays], missingAuthors };\n}\n\n/** Pick immediate policy hints, falling back only when none survive validation. */\nfunction immediateReadRelays(ctx: RouterCtx, relayHints?: string[]): string[] {\n const hints = allowed(ctx, relayHints ?? []);\n return hints.length > 0 ? hints : allowed(ctx, ctx.fallbackRelays);\n}\n\n/** Record that `id` was observed on `relayUrl`. */\nfunction recordRelay(collector: OutboxCollector, id: string, relayUrl: string): void {\n let set = collector.relayMap.get(id);\n if (!set) { set = new Set<string>(); collector.relayMap.set(id, set); }\n set.add(relayUrl);\n}\n\n/** Materialize one event using every relay sighting known at call time. */\nfunction collectedEventResult(collector: OutboxCollector, event: NostrEvent): RelayEventResult {\n return createRelayEventResultWithHints(event, [...(collector.relayMap.get(event.id) ?? [])]);\n}\n\n/** Verify one event id once, then stream it and wake completion checks. */\nfunction admitEvent(\n ctx: RouterCtx,\n collector: OutboxCollector,\n event: NostrEvent,\n sink: OutboxQueryStreamSink,\n isOpen: () => boolean,\n onSettled: () => void,\n): void {\n if (collector.admitted.has(event.id)) return;\n collector.admitted.add(event.id);\n collector.pendingVerifications += 1;\n void ctx.verify(event).then((ok) => {\n if (!isOpen()) return;\n if (!ok) {\n collector.relayMap.delete(event.id);\n return;\n }\n collector.seen.set(event.id, event);\n try {\n sink.event(collectedEventResult(collector, event));\n } catch {\n // A consumer callback cannot poison relay collection or completion.\n }\n }).finally(() => {\n collector.pendingVerifications -= 1;\n onSettled();\n });\n}\n\n/** Build the compatibility aggregate without changing its existing limit rule. */\nfunction buildQueryResult(\n collector: OutboxCollector,\n options: OutboxQueryOptions | undefined,\n incomplete: boolean,\n subscribedAny: boolean,\n): OutboxResult {\n let events = [...collector.seen.values()].map((event) => collectedEventResult(collector, event));\n if (options?.limit !== undefined && events.length > options.limit) {\n events = [...events].sort((a, b) => b.event.created_at - a.event.created_at).slice(0, options.limit);\n }\n const result: OutboxResult = { events };\n if (incomplete || !subscribedAny) result.incomplete = true;\n if (!subscribedAny) result.error = 'relay list unavailable';\n return result;\n}\n\n/** Thin wrapper so callers read as a single-relay subscribe. */\nfunction relayPoolSubscribe(\n ctx: RouterCtx,\n filters: NostrFilter[],\n relayUrl: string,\n cb: (item: NostrEvent | 'EOSE') => void,\n): { unsubscribe(): void } {\n return ctx.relayPool.subscribe(filters, [relayUrl], cb);\n}\n\nfunction startQueryImpl(\n ctx: RouterCtx,\n filters: NostrFilter[],\n options: OutboxQueryOptions | undefined,\n sink: OutboxQueryStreamSink,\n): OutboxQueryStream {\n if (!ctx.relayPool.isAvailable()) {\n return {\n result: Promise.resolve({ events: [], incomplete: true, error: 'relay list unavailable' }),\n close(): void { /* no relay handles */ },\n };\n }\n\n const authors = deriveAuthors(filters, options?.authors);\n const collector: OutboxCollector = {\n seen: new Map(),\n admitted: new Set(),\n relayMap: new Map(),\n pendingVerifications: 0,\n };\n const handles = new Map<string, { unsubscribe(): void }>();\n const activeRelays = new Set<string>();\n const eoseRelays = new Set<string>();\n let discoveryPending = authors.length > 0;\n let incomplete = false;\n let subscribedAny = false;\n let finished = false;\n let resolveResult!: (result: OutboxResult) => void;\n const result = new Promise<OutboxResult>((resolve) => { resolveResult = resolve; });\n let timer: unknown;\n\n function closeHandles(): void {\n for (const handle of handles.values()) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n }\n handles.clear();\n }\n\n function finalize(forceIncomplete = false): void {\n if (finished) return;\n finished = true;\n incomplete ||= forceIncomplete;\n clearTimeout(timer);\n closeHandles();\n resolveResult(buildQueryResult(collector, options, incomplete, subscribedAny));\n }\n\n function maybeFinalize(): void {\n if (finished || discoveryPending || collector.pendingVerifications > 0) return;\n if (activeRelays.size === 0 || eoseRelays.size >= activeRelays.size) finalize();\n }\n\n function attachRelay(relayUrl: string): void {\n if (finished || activeRelays.has(relayUrl)) return;\n activeRelays.add(relayUrl);\n try {\n const handle = relayPoolSubscribe(ctx, filters, relayUrl, (item) => {\n if (finished) return;\n if (item === 'EOSE') {\n eoseRelays.add(relayUrl);\n maybeFinalize();\n return;\n }\n recordRelay(collector, item.id, relayUrl);\n admitEvent(ctx, collector, item, sink, () => !finished, maybeFinalize);\n });\n subscribedAny = true;\n if (finished) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n } else {\n handles.set(relayUrl, handle);\n }\n } catch {\n activeRelays.delete(relayUrl);\n incomplete = true;\n }\n }\n\n function attachMany(relayUrls: Iterable<string>): void {\n for (const relayUrl of relayUrls) attachRelay(relayUrl);\n }\n\n timer = setTimeout(() => finalize(true), options?.timeoutMs ?? ctx.defaultTimeoutMs);\n\n if (authors.length === 0) {\n discoveryPending = false;\n attachMany(allowed(ctx, [...ctx.fallbackRelays, ...(options?.relays ?? [])]));\n maybeFinalize();\n } else {\n let loaded: Promise<Map<string, RelayListEntry>> | Map<string, RelayListEntry>;\n try {\n loaded = ctx.loadRelayLists(authors);\n } catch {\n discoveryPending = false;\n incomplete = true;\n attachMany(immediateReadRelays(ctx, options?.relays));\n maybeFinalize();\n return { result, close: () => finalize(true) };\n }\n\n if (loaded instanceof Map) {\n const discovery = discoveredReadRelays(ctx, authors, loaded);\n discoveryPending = false;\n incomplete ||= discovery.missingAuthors.length > 0;\n const initial = allowed(ctx, [...(options?.relays ?? []), ...discovery.relays]);\n attachMany(initial.length > 0 ? initial : allowed(ctx, ctx.fallbackRelays));\n maybeFinalize();\n } else {\n attachMany(immediateReadRelays(ctx, options?.relays));\n void Promise.resolve(loaded).then((lists) => {\n if (finished) return;\n const discovery = discoveredReadRelays(ctx, authors, lists);\n discoveryPending = false;\n incomplete ||= discovery.missingAuthors.length > 0;\n attachMany(discovery.relays);\n maybeFinalize();\n }).catch(() => {\n if (finished) return;\n discoveryPending = false;\n incomplete = true;\n maybeFinalize();\n });\n }\n }\n\n return { result, close: () => finalize(true) };\n}\n\nfunction queryImpl(ctx: RouterCtx, filters: NostrFilter[], options?: OutboxQueryOptions): Promise<OutboxResult> {\n return startQueryImpl(ctx, filters, options, { event: () => { /* aggregate only */ } }).result;\n}\n\nasync function getEventImpl(ctx: RouterCtx, eventId: string, options?: OutboxEventOptions): Promise<OutboxEventResult> {\n if (typeof eventId !== 'string' || eventId.length === 0) {\n return { error: 'invalid filter' };\n }\n\n const filter: NostrFilter = { ids: [eventId] };\n const queryOptions: OutboxQueryOptions = { limit: 1 };\n if (typeof options?.author === 'string' && options.author.length > 0) {\n filter.authors = [options.author];\n queryOptions.authors = [options.author];\n }\n if (Array.isArray(options?.relays)) queryOptions.relays = options.relays;\n if (options?.timeoutMs !== undefined) queryOptions.timeoutMs = options.timeoutMs;\n\n const queryResult = await queryImpl(ctx, [filter], queryOptions);\n const eventResult = queryResult.events.find((candidate) => candidate.event.id === eventId);\n const result: OutboxEventResult = {};\n if (eventResult) result.result = eventResult;\n if (!eventResult) result.error = queryResult.error ?? 'not found';\n else if (queryResult.error !== undefined) result.error = queryResult.error;\n if (queryResult.incomplete !== undefined) result.incomplete = queryResult.incomplete;\n return result;\n}\n\nfunction closeLiveSub(sub: LiveOutboxSubscription): void {\n for (const handle of sub.handles.values()) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n }\n sub.handles.clear();\n}\n\n/** Wire one relay's subscription into an outbox subscription's event flow. */\nfunction attachLiveRelay(\n ctx: RouterCtx,\n sub: LiveOutboxSubscription,\n filters: NostrFilter[],\n relayUrl: string,\n sink: OutboxSubscriptionSink,\n): boolean {\n if (sub.closed || sub.relays.has(relayUrl)) return false;\n sub.relays.add(relayUrl);\n try {\n const handle = relayPoolSubscribe(ctx, filters, relayUrl, (item) => {\n if (sub.closed || item === 'EOSE' || sub.admitted.has(item.id)) return;\n sub.admitted.add(item.id);\n void ctx.verify(item).then((ok) => {\n if (!ok || sub.closed || sub.seen.has(item.id)) return;\n sub.seen.add(item.id);\n sink.event(createRelayEventResultWithHints(item, [relayUrl]));\n });\n });\n if (sub.closed) {\n try { handle.unsubscribe(); } catch { /* best-effort */ }\n } else {\n sub.handles.set(relayUrl, handle);\n }\n return true;\n } catch {\n sub.relays.delete(relayUrl);\n return false;\n }\n}\n\nfunction startSubscription(\n ctx: RouterCtx,\n filters: NostrFilter[],\n options: OutboxSubscribeOptions | undefined,\n sink: OutboxSubscriptionSink,\n): OutboxRouterSubscription {\n const authors = deriveAuthors(filters, options?.authors);\n const sub: LiveOutboxSubscription = {\n handles: new Map(),\n relays: new Set(),\n admitted: new Set(),\n seen: new Set(),\n closed: false,\n };\n let discoveryOpen = authors.length > 0;\n let discoveryTimer: unknown;\n\n function attachMany(relayUrls: Iterable<string>): void {\n for (const relayUrl of relayUrls) attachLiveRelay(ctx, sub, filters, relayUrl, sink);\n }\n\n function closeUnavailable(): void {\n if (sub.closed || sub.handles.size > 0) return;\n sub.closed = true;\n sink.closed('relay list unavailable');\n }\n\n if (!ctx.relayPool.isAvailable()) {\n closeUnavailable();\n } else if (authors.length === 0) {\n discoveryOpen = false;\n attachMany(immediateReadRelays(ctx, options?.relays));\n closeUnavailable();\n } else {\n let loaded: Promise<Map<string, RelayListEntry>> | Map<string, RelayListEntry>;\n try {\n loaded = ctx.loadRelayLists(authors);\n } catch {\n discoveryOpen = false;\n attachMany(immediateReadRelays(ctx, options?.relays));\n closeUnavailable();\n loaded = new Map();\n }\n\n if (discoveryOpen && loaded instanceof Map) {\n const discovery = discoveredReadRelays(ctx, authors, loaded);\n discoveryOpen = false;\n const initial = allowed(ctx, [...(options?.relays ?? []), ...discovery.relays]);\n attachMany(initial.length > 0 ? initial : allowed(ctx, ctx.fallbackRelays));\n closeUnavailable();\n } else if (discoveryOpen) {\n attachMany(immediateReadRelays(ctx, options?.relays));\n discoveryTimer = setTimeout(() => {\n discoveryOpen = false;\n closeUnavailable();\n }, options?.timeoutMs ?? ctx.defaultTimeoutMs);\n void Promise.resolve(loaded).then((lists) => {\n if (!discoveryOpen || sub.closed) return;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n attachMany(discoveredReadRelays(ctx, authors, lists).relays);\n closeUnavailable();\n }).catch(() => {\n if (!discoveryOpen || sub.closed) return;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n closeUnavailable();\n });\n }\n }\n\n return {\n close(): void {\n if (sub.closed) return;\n sub.closed = true;\n discoveryOpen = false;\n clearTimeout(discoveryTimer);\n closeLiveSub(sub);\n },\n };\n}\n\n/** Resolve the full write/inbox/hint relay set a publish should fan out to. */\nasync function resolvePublishTargets(\n ctx: RouterCtx,\n signed: NostrEvent,\n options?: OutboxPublishOptions,\n): Promise<PublishTargetResolution> {\n const targets = new Set<string>();\n const requiredTargets = new Set<string>();\n\n if (options?.toOutbox !== false) {\n // The author's own write relays (outbox model for the user's own event).\n const outboxPlan = await resolveRequiredRelays(ctx, [signed.pubkey], 'read');\n if (outboxPlan.missingAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'relay list unavailable' };\n if (outboxPlan.deniedAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'policy denied' };\n for (const url of outboxPlan.relays) {\n targets.add(url);\n requiredTargets.add(url);\n }\n }\n\n if (options?.toInboxes && options.toInboxes.length > 0) {\n const inboxPlan = await resolveRequiredRelays(ctx, options.toInboxes, 'write');\n if (inboxPlan.missingAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'relay list unavailable' };\n if (inboxPlan.deniedAuthors.length > 0) return { relayUrls: [], requiredRelayUrls: [], error: 'policy denied' };\n for (const url of inboxPlan.relays) {\n targets.add(url);\n requiredTargets.add(url);\n }\n }\n\n for (const url of options?.relays ?? []) targets.add(url);\n return {\n relayUrls: allowed(ctx, targets),\n requiredRelayUrls: allowed(ctx, requiredTargets),\n };\n}\n\nasync function publishImpl(ctx: RouterCtx, template: EventTemplate, options?: OutboxPublishOptions): Promise<OutboxPublishResult> {\n if (!ctx.signEvent) return { ok: false, error: 'publish denied' };\n if (!ctx.relayPool.isAvailable()) return { ok: false, error: 'relay list unavailable' };\n\n let signed: NostrEvent;\n try {\n signed = await ctx.signEvent(template);\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : 'sign failed' };\n }\n\n const publishTargets = await resolvePublishTargets(ctx, signed, options);\n if (publishTargets.error) {\n return { ok: false, event: signed, eventId: signed.id, error: publishTargets.error };\n }\n const relayUrls = publishTargets.relayUrls;\n if (relayUrls.length === 0) return { ok: false, event: signed, eventId: signed.id, error: 'relay list unavailable' };\n\n let relays: Record<string, boolean>;\n try {\n relays = normalizePublishResult(await ctx.relayPool.publish(signed, relayUrls), relayUrls);\n } catch (err) {\n return { ok: false, event: signed, eventId: signed.id, error: err instanceof Error ? err.message : 'publish failed' };\n }\n\n const ok = publishTargets.requiredRelayUrls.length > 0\n ? publishTargets.requiredRelayUrls.every((url) => relays[url] === true)\n : Object.values(relays).some(Boolean);\n const result: OutboxPublishResult = { ok, event: signed, eventId: signed.id, relays };\n if (!ok) result.error = 'publish denied';\n return result;\n}\n\n/**\n * Create a relay-pool-backed {@link StreamingOutboxRouter}.\n *\n * @param options - Relay pool, NIP-65 loader, fallback relays, and optional\n * signer / verifier / relay gate / timeout.\n * @returns A {@link StreamingOutboxRouter} for direct incremental reads and\n * compatibility use through {@link createOutboxService}.\n * @throws If `relayPool`, `loadRelayLists`, or `fallbackRelays` are missing.\n */\nexport function createRelayPoolOutboxRouter(options: RelayPoolOutboxRouterOptions): StreamingOutboxRouter {\n if (!options || typeof options.relayPool !== 'object' || options.relayPool === null) {\n throw new Error('createRelayPoolOutboxRouter: options.relayPool is required');\n }\n if (typeof options.loadRelayLists !== 'function') {\n throw new Error('createRelayPoolOutboxRouter: options.loadRelayLists is required');\n }\n if (!Array.isArray(options.fallbackRelays)) {\n throw new Error('createRelayPoolOutboxRouter: options.fallbackRelays is required');\n }\n\n const verifyEvent = options.verifyEvent;\n const ctx: RouterCtx = {\n relayPool: options.relayPool,\n loadRelayLists: options.loadRelayLists,\n fallbackRelays: options.fallbackRelays,\n signEvent: options.signEvent,\n isRelayAllowed: options.isRelayAllowed ?? defaultRelayAllowed,\n defaultTimeoutMs: options.defaultTimeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS,\n async verify(event: NostrEvent): Promise<boolean> {\n if (!verifyEvent) return true;\n try {\n return await verifyEvent(event);\n } catch {\n return false;\n }\n },\n };\n\n return {\n getEvent: (eventId, eventOptions) => getEventImpl(ctx, eventId, eventOptions),\n query: (filters, queryOptions) => queryImpl(ctx, filters, queryOptions),\n queryStream: (filters, queryOptions, sink) => startQueryImpl(ctx, filters, queryOptions, sink),\n subscribe: (filters, subscribeOptions, sink) => startSubscription(ctx, filters, subscribeOptions, sink),\n publish: (template, publishOptions) => publishImpl(ctx, template, publishOptions),\n resolveRelays: (target: OutboxTarget) => {\n const pubkeys = target.authors ?? (target.pubkey ? [target.pubkey] : []);\n return resolvePlan(ctx, pubkeys, target.direction ?? 'read');\n },\n };\n}\n\n/** Normalize a pool publish return into a per-relay success map. */\nfunction normalizePublishResult(\n res: Record<string, boolean> | void,\n relayUrls: string[],\n): Record<string, boolean> {\n const out: Record<string, boolean> = {};\n if (res && typeof res === 'object') {\n for (const url of relayUrls) out[url] = res[url] ?? false;\n } else {\n // void return → optimistic success on every targeted relay.\n for (const url of relayUrls) out[url] = true;\n }\n return out;\n}\n","/**\n * upload-service.ts — NAP-UPLOAD (shell-mediated file/blob upload) reference service.\n *\n * Shell-side handler for the NAP-UPLOAD wire protocol. It is a pure envelope\n * router: it validates `upload.*` envelopes, delegates the actual byte transfer\n * (server selection, rail authorization signing, the HTTP upload) to an injected\n * {@link Uploader}, and posts the correlated result / status messages back to the\n * napplet.\n *\n * The uploader is injected (options-as-bridge) so this service has no transport\n * or Nostr dependency and is fully unit-testable. NAP-UPLOAD is deliberately\n * abstract over the backend — the runtime decides *how* it uploads (NIP-96,\n * Blossom, …). A concrete HTTP-backed uploader ships alongside as\n * {@link createHttpUploader}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: upload.info, upload.upload, upload.status\n * Outbound: upload.info.result, upload.upload.result,\n * upload.status.result, upload.status.changed\n *\n * The service owns the `uploadId` (generated per request, scoped to the\n * requesting napplet), tracks the latest {@link UploadStatus} per upload for\n * `upload.status` queries, and cleans up on window teardown. The shell owns\n * consent, policy, server selection, signing, and the HTTP upload — all behind\n * the {@link Uploader}.\n *\n * @example\n * ```ts\n * import { createUploadService, createHttpUploader } from '@kehto/services';\n *\n * const uploader = createHttpUploader({ rails: { nip96: { servers } }, signEvent });\n * runtime.registerService('upload', createUploadService({ uploader }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\n/** Upload service version — follows semver. */\nconst UPLOAD_SERVICE_VERSION = '1.0.0';\n\n/**\n * Storage rail. `nip96` (NIP-96 HTTP file storage) and `blossom` (Blossom blob\n * storage) are the first concrete backends; the open string keeps the API\n * stable as shells add rails (torrents, usenet, …).\n */\nexport type UploadRail = 'nip96' | 'blossom' | (string & {});\n\n/** Lifecycle state of an upload. */\nexport type UploadState = 'pending' | 'uploading' | 'complete' | 'failed' | 'cancelled';\n\n/** Pixel dimensions of an uploaded image/video. */\nexport interface UploadDimensions {\n width: number;\n height: number;\n}\n\n/**\n * A napplet's upload request. `data` crosses the postMessage boundary by\n * structured clone — shells never require base64 encoding.\n */\nexport interface UploadRequest {\n /** Storage rail; omit to let the shell pick a configured default. */\n rail?: UploadRail;\n /** The bytes to upload. */\n data: ArrayBuffer | Blob;\n /** MIME type; inferred from `data` when omitted. */\n mimeType?: string;\n /** Suggested filename. */\n filename?: string;\n /** Alt text / description for the file event. */\n caption?: string;\n /** Request the server not re-encode the file (NIP-96 `no_transform`). */\n noTransform?: boolean;\n /** Rail-specific or shell-specific extra metadata. */\n metadata?: Record<string, unknown>;\n}\n\n/** A single Nostr tag (NIP-94 / imeta entries are arrays of strings). */\nexport type NostrTag = string[];\n\n/** The result of an upload. */\nexport interface UploadResult {\n /** Whether the upload succeeded (or is progressing) vs failed/cancelled. */\n ok: boolean;\n /** Shell-generated id, scoped to the requesting napplet. */\n uploadId: string;\n /** Current lifecycle state. */\n status: UploadState;\n /** The rail the shell used. */\n rail: UploadRail;\n /** Primary download URL. */\n url?: string;\n /** Mirrors / alternative server URLs. */\n fallbackUrls?: string[];\n /** Hash of the stored blob (NIP-94 `x`). */\n sha256?: string;\n /** Hash before server transforms (NIP-94 `ox`). */\n originalSha256?: string;\n /** Size in bytes. */\n size?: number;\n /** Stored MIME type. */\n mimeType?: string;\n /** Image/video dimensions when known. */\n dimensions?: UploadDimensions;\n /** Blurhash placeholder when known. */\n blurhash?: string;\n /** Ready-to-attach NIP-94 / imeta tags. */\n nip94?: NostrTag[];\n /** Error reason when the upload failed or was cancelled. */\n error?: string;\n}\n\n/** A status snapshot for an upload, including progress counters. */\nexport interface UploadStatus extends UploadResult {\n /** Bytes sent so far (while uploading). */\n bytesSent?: number;\n /** Total bytes to send. */\n bytesTotal?: number;\n /** Unix ms timestamp of this status. */\n updatedAt: number;\n}\n\n/** Runtime-disclosed support for one upload rail. */\nexport interface UploadRailInfo {\n rail: UploadRail;\n enabled: boolean;\n returns?: string[];\n}\n\n/** Advisory upload rails and coarse policy limits disclosed by the runtime. */\nexport interface UploadInfo {\n rails: UploadRailInfo[];\n maxBytes?: number;\n mimeTypes?: string[];\n}\n\n/** Context passed to a host-provided upload info resolver. */\nexport interface UploadInfoContext {\n windowId: string;\n}\n\n/** Static or dynamic advisory upload info exposed through `upload.info`. */\nexport type UploadInfoProvider =\n | UploadInfo\n | ((context: UploadInfoContext) => UploadInfo | Promise<UploadInfo>);\n\n/**\n * Context handed to an {@link Uploader} for a single upload. Carries the\n * service-owned `uploadId` and a sink for streaming progress / state changes.\n */\nexport interface UploaderContext {\n /** The service-generated upload id (authoritative; scoped to the napplet). */\n uploadId: string;\n /** The napplet window that requested the upload. */\n windowId: string;\n /**\n * Push a status update (progress, or a transition to complete/failed). The\n * service stamps `uploadId` and `updatedAt` before forwarding to the napplet\n * as `upload.status.changed`, and records it as the latest tracked status.\n */\n onStatus(status: UploadStatus): void;\n}\n\n/**\n * Abstract upload backend. Implementors own server selection, rail\n * authorization signing (NIP-98 for NIP-96, kind 24242 for Blossom), the HTTP\n * upload, and integrity-hash reporting. The service translates wire envelopes\n * into these calls and back. A concrete reference implementation ships as\n * {@link createHttpUploader}.\n */\nexport interface Uploader {\n /** Upload `request.data`, streaming progress through `ctx.onStatus`. */\n upload(request: UploadRequest, ctx: UploaderContext): Promise<UploadResult>;\n /** Optional: resolve the latest status for an upload the service is not tracking. */\n status?(uploadId: string): Promise<UploadStatus | undefined>;\n /** Optional: abort an in-flight upload (called on window teardown). */\n cancel?(uploadId: string): void;\n}\n\n/** Options for {@link createUploadService}. */\nexport interface UploadServiceOptions {\n /** The upload backend the shell uses. Required. */\n uploader: Uploader;\n /**\n * Advisory NAP-UPLOAD introspection exposed through `upload.info`.\n *\n * Provide a static snapshot or resolver when the shell wants to disclose\n * configured rails and coarse limits. Omit to expose no rails and no limits.\n */\n uploadInfo?: UploadInfoProvider;\n /** Generate an upload id; defaults to `crypto.randomUUID()`. */\n generateId?: () => string;\n /** Current time in unix ms; defaults to `Date.now()`. */\n now?: () => number;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst UPLOAD_DESCRIPTOR: ServiceDescriptor = {\n name: 'upload',\n version: UPLOAD_SERVICE_VERSION,\n description: 'NAP-UPLOAD shell-mediated file/blob upload — upload/status with progress pushes',\n};\n\nconst DEFAULT_UPLOAD_INFO: UploadInfo = {\n rails: [],\n};\n\n/** Per-upload tracking entry, keyed by `windowId:uploadId`. */\ninterface UploadEntry {\n uploadId: string;\n status?: UploadStatus;\n}\n\ndeclare const crypto: { randomUUID(): string };\n\n/**\n * Create the NAP-UPLOAD service handler.\n *\n * @param options - Must provide an {@link Uploader}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('upload', handler)`.\n * @throws If `options.uploader` is missing.\n */\nexport function createUploadService(options: UploadServiceOptions): ServiceHandler {\n if (!options || typeof options.uploader !== 'object' || options.uploader === null) {\n throw new Error('createUploadService: options.uploader is required');\n }\n const { uploader } = options;\n const uploadInfo = options.uploadInfo ?? DEFAULT_UPLOAD_INFO;\n const generateId = options.generateId ?? (() => crypto.randomUUID());\n const now = options.now ?? (() => Date.now());\n\n // Tracked uploads keyed by `windowId:uploadId` for status lookup + cleanup.\n const entries = new Map<string, UploadEntry>();\n\n function normalizeUploadInfo(info: UploadInfo): UploadInfo {\n return {\n rails: Array.isArray(info.rails)\n ? info.rails\n .filter((rail) => typeof rail?.rail === 'string')\n .map((rail) => ({\n rail: rail.rail,\n enabled: rail.enabled !== false,\n ...(Array.isArray(rail.returns) ? { returns: rail.returns.filter((value) => typeof value === 'string') } : {}),\n }))\n : [],\n ...(typeof info.maxBytes === 'number' ? { maxBytes: info.maxBytes } : {}),\n ...(Array.isArray(info.mimeTypes) ? { mimeTypes: info.mimeTypes.filter((value) => typeof value === 'string') } : {}),\n };\n }\n\n function handleInfo(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string };\n const id = m.id ?? '';\n void Promise.resolve()\n .then(() => (typeof uploadInfo === 'function' ? uploadInfo({ windowId }) : uploadInfo))\n .then((info) => {\n send({ type: 'upload.info.result', id, info: normalizeUploadInfo(info) } as NappletMessage);\n })\n .catch((err) => {\n send({ type: 'upload.info.result', id, error: toErrorMessage(err) } as NappletMessage);\n });\n }\n\n function handleUpload(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; request?: UploadRequest };\n const id = m.id ?? '';\n const request = m.request;\n if (!request || typeof request !== 'object' || request.data == null) {\n send({ type: 'upload.upload.result', id, error: 'invalid request' } as NappletMessage);\n return;\n }\n\n const uploadId = generateId();\n const key = `${windowId}:${uploadId}`;\n entries.set(key, { uploadId });\n\n const ctx: UploaderContext = {\n uploadId,\n windowId,\n onStatus: (status) => {\n const stamped: UploadStatus = { ...status, uploadId, updatedAt: status.updatedAt || now() };\n const entry = entries.get(key);\n if (entry) entry.status = stamped;\n send({ type: 'upload.status.changed', status: stamped } as NappletMessage);\n },\n };\n\n void uploader\n .upload(request, ctx)\n .then((result) => {\n const stamped: UploadResult = { ...result, uploadId };\n const entry = entries.get(key);\n if (entry) entry.status = { ...stamped, updatedAt: now() };\n send({ type: 'upload.upload.result', id, result: stamped } as NappletMessage);\n })\n .catch((err) => {\n entries.delete(key);\n send({ type: 'upload.upload.result', id, error: toErrorMessage(err) } as NappletMessage);\n });\n }\n\n function handleStatus(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; uploadId?: string };\n const id = m.id ?? '';\n const uploadId = m.uploadId;\n if (typeof uploadId !== 'string' || uploadId.length === 0) {\n send({ type: 'upload.status.result', id, error: 'invalid uploadId' } as NappletMessage);\n return;\n }\n\n const tracked = entries.get(`${windowId}:${uploadId}`)?.status;\n if (tracked) {\n send({ type: 'upload.status.result', id, status: tracked } as NappletMessage);\n return;\n }\n\n if (uploader.status) {\n void uploader\n .status(uploadId)\n .then((status) =>\n send(\n status\n ? ({ type: 'upload.status.result', id, status } as NappletMessage)\n : ({ type: 'upload.status.result', id, error: 'unknown upload' } as NappletMessage),\n ),\n )\n .catch((err) =>\n send({ type: 'upload.status.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n return;\n }\n\n send({ type: 'upload.status.result', id, error: 'unknown upload' } as NappletMessage);\n }\n\n return {\n descriptor: UPLOAD_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'upload.info':\n handleInfo(windowId, message, send);\n return;\n case 'upload.upload':\n handleUpload(windowId, message, send);\n return;\n case 'upload.status':\n handleStatus(windowId, message, send);\n return;\n default:\n // Unknown upload.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n const prefix = `${windowId}:`;\n for (const [key, entry] of entries) {\n if (key.startsWith(prefix)) {\n uploader.cancel?.(entry.uploadId);\n entries.delete(key);\n }\n }\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'upload request failed';\n}\n","/**\n * http-uploader.ts — NAP-UPLOAD concrete HTTP-backed {@link Uploader}.\n *\n * The reference upload backend for {@link createUploadService}. Implements two\n * storage rails over HTTP:\n *\n * - **NIP-96** — signs a NIP-98 (kind 27235) HTTP-auth event, POSTs the file as\n * `multipart/form-data`, and maps the returned NIP-94 event tags into an\n * {@link UploadResult}.\n * - **Blossom** — signs a kind 24242 authorization event, PUTs the raw bytes to\n * `<server>/upload`, and maps the returned blob descriptor.\n *\n * Signing (`signEvent`) and transport (`fetch`) are injected so the uploader\n * carries no Nostr or network dependency and is fully unit-testable. The shell\n * holds the signing key and never exposes it to napplets — the uploader only\n * receives a signing callback. Server URLs are shell configuration, not napplet\n * input: a napplet may *hint* a rail, but never a server.\n *\n * The configured server URL is used directly as the upload endpoint (the\n * NIP-96 `api_url` / Blossom base). Hosts that need `.well-known` discovery can\n * resolve it before constructing the uploader.\n *\n * @example\n * ```ts\n * const uploader = createHttpUploader({\n * rails: { nip96: { servers: ['https://nostr.build/api/v2/nip96/upload'] } },\n * signEvent: (tmpl) => signer.signEvent(tmpl),\n * });\n * runtime.registerService('upload', createUploadService({ uploader }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { EventTemplate, NostrEvent } from '@napplet/core';\nimport type {\n NostrTag,\n UploadDimensions,\n UploadRail,\n UploadRequest,\n UploadResult,\n Uploader,\n UploaderContext,\n} from './upload-service.js';\n\n/** NIP-98 HTTP-auth event kind. */\nconst KIND_NIP98 = 27235;\n/** Blossom authorization event kind. */\nconst KIND_BLOSSOM_AUTH = 24242;\n/** Blossom auth-event lifetime, in seconds. */\nconst BLOSSOM_AUTH_TTL_S = 3600;\n\n/** Per-rail server configuration. The first server is the primary endpoint. */\nexport interface RailServerConfig {\n /** Ordered server endpoint URLs; index 0 is primary. */\n servers: string[];\n}\n\n/** Storage rails this uploader can serve. */\nexport interface HttpUploaderRails {\n /** NIP-96 HTTP file storage. */\n nip96?: RailServerConfig;\n /** Blossom blob storage. */\n blossom?: RailServerConfig;\n}\n\n/** Signs an event template on the user's behalf (shell holds the key). */\nexport type SignEvent = (template: EventTemplate) => Promise<NostrEvent>;\n\n/** Options for {@link createHttpUploader}. */\nexport interface HttpUploaderOptions {\n /** Configured rails + their servers. */\n rails: HttpUploaderRails;\n /** Rail to use when a request omits one; defaults to the first configured rail. */\n defaultRail?: UploadRail;\n /** Signs NIP-98 / Blossom auth events. Required. */\n signEvent: SignEvent;\n /** Fetch implementation; defaults to the global `fetch`. */\n fetch?: typeof fetch;\n /** Hex SHA-256 of the payload bytes; defaults to Web Crypto. */\n digestSha256?: (bytes: Uint8Array) => Promise<string>;\n /** Unix *seconds* clock for event timestamps; defaults to `Date.now()/1000`. */\n now?: () => number;\n}\n\ndeclare const btoa: (data: string) => string;\ndeclare const crypto: { subtle: { digest(alg: string, data: ArrayBuffer): Promise<ArrayBuffer> } };\n\n/**\n * Create the reference HTTP {@link Uploader} (NIP-96 + Blossom rails).\n *\n * @param options - Rails, server config, and the injected `signEvent`.\n * @returns An {@link Uploader} for `createUploadService({ uploader })`.\n * @throws If `options.signEvent` is missing.\n */\nexport function createHttpUploader(options: HttpUploaderOptions): Uploader {\n if (!options || typeof options.signEvent !== 'function') {\n throw new Error('createHttpUploader: options.signEvent is required');\n }\n const rails = options.rails ?? {};\n const signEvent = options.signEvent;\n const fetchFn = options.fetch ?? fetch;\n const digest = options.digestSha256 ?? defaultDigestSha256;\n const nowS = options.now ?? (() => Math.floor(Date.now() / 1000));\n const defaultRail = options.defaultRail ?? firstConfiguredRail(rails);\n const inFlight = new Map<string, AbortController>();\n\n async function upload(request: UploadRequest, ctx: UploaderContext): Promise<UploadResult> {\n const rail = request.rail ?? defaultRail;\n const config = rail === 'nip96' ? rails.nip96 : rail === 'blossom' ? rails.blossom : undefined;\n if (rail !== 'nip96' && rail !== 'blossom') {\n return failed(ctx.uploadId, rail ?? 'unknown', 'unsupported rail');\n }\n\n const server = config?.servers?.[0];\n if (!server) {\n return failed(ctx.uploadId, rail, 'no server configured');\n }\n\n const controller = new AbortController();\n inFlight.set(ctx.uploadId, controller);\n let sha256: string | undefined;\n\n try {\n const bytes = await toBytes(request.data);\n sha256 = await digest(bytes);\n if (controller.signal.aborted) {\n return cancelled(ctx.uploadId, rail, sha256);\n }\n return rail === 'nip96'\n ? await uploadNip96({ request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller })\n : await uploadBlossom({ request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller });\n } catch (err) {\n if (controller.signal.aborted) {\n return cancelled(ctx.uploadId, rail, sha256);\n }\n return failed(ctx.uploadId, rail, toErrorMessage(err), sha256);\n } finally {\n inFlight.delete(ctx.uploadId);\n }\n }\n\n function cancel(uploadId: string): void {\n inFlight.get(uploadId)?.abort();\n }\n\n return { upload, cancel };\n}\n\ninterface RailUploadArgs {\n request: UploadRequest;\n ctx: UploaderContext;\n server: string;\n bytes: Uint8Array;\n sha256: string;\n signEvent: SignEvent;\n fetchFn: typeof fetch;\n nowS: () => number;\n controller: AbortController;\n}\n\nasync function uploadNip96(args: RailUploadArgs): Promise<UploadResult> {\n const { request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller } = args;\n\n const auth = await signEvent({\n kind: KIND_NIP98,\n created_at: nowS(),\n content: '',\n tags: [\n ['u', server],\n ['method', 'POST'],\n ['payload', sha256],\n ],\n });\n\n const form = new FormData();\n form.append('file', new Blob([bytesToArrayBuffer(bytes)], { type: request.mimeType }), request.filename ?? 'file');\n if (request.caption !== undefined) form.append('caption', request.caption);\n if (request.mimeType !== undefined) form.append('content_type', request.mimeType);\n if (request.noTransform) form.append('no_transform', 'true');\n\n emitUploading(ctx, 'nip96', bytes.byteLength, nowS);\n\n const res = await fetchFn(server, {\n method: 'POST',\n headers: { Authorization: nostrAuthHeader(auth) },\n body: form,\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return failed(ctx.uploadId, 'nip96', `server rejected (HTTP ${res.status})`, sha256);\n }\n\n const body = (await res.json()) as Nip96Response;\n if (body.status === 'error') {\n return failed(ctx.uploadId, 'nip96', body.message ?? 'upload failed', sha256);\n }\n\n const tags = body.nip94_event?.tags ?? [];\n return fromNip94Tags(ctx.uploadId, 'nip96', tags, bytes.byteLength, sha256);\n}\n\ninterface Nip96Response {\n status?: 'success' | 'error' | string;\n message?: string;\n nip94_event?: { tags?: string[][] };\n}\n\n/** Map NIP-94 / imeta tags into an {@link UploadResult}. */\nfunction fromNip94Tags(\n uploadId: string,\n rail: UploadRail,\n tags: string[][],\n fallbackSize: number,\n fallbackSha: string,\n): UploadResult {\n const get = (name: string): string | undefined => tags.find((t) => t[0] === name)?.[1];\n const url = get('url');\n const result: UploadResult = {\n ok: Boolean(url),\n uploadId,\n status: url ? 'complete' : 'failed',\n rail,\n sha256: get('x') ?? fallbackSha,\n nip94: tags as NostrTag[],\n };\n if (url) result.url = url;\n const ox = get('ox');\n if (ox) result.originalSha256 = ox;\n const size = get('size');\n result.size = size ? Number(size) : fallbackSize;\n const m = get('m');\n if (m) result.mimeType = m;\n const dim = parseDimensions(get('dim'));\n if (dim) result.dimensions = dim;\n const blurhash = get('blurhash');\n if (blurhash) result.blurhash = blurhash;\n if (!url) result.error = 'server returned no url';\n return result;\n}\n\nasync function uploadBlossom(args: RailUploadArgs): Promise<UploadResult> {\n const { request, ctx, server, bytes, sha256, signEvent, fetchFn, nowS, controller } = args;\n\n const auth = await signEvent({\n kind: KIND_BLOSSOM_AUTH,\n created_at: nowS(),\n content: `Upload ${request.filename ?? 'file'}`,\n tags: [\n ['t', 'upload'],\n ['x', sha256],\n ['expiration', String(nowS() + BLOSSOM_AUTH_TTL_S)],\n ],\n });\n\n const endpoint = `${trimTrailingSlash(server)}/upload`;\n const headers: Record<string, string> = { Authorization: nostrAuthHeader(auth) };\n if (request.mimeType) headers['Content-Type'] = request.mimeType;\n\n emitUploading(ctx, 'blossom', bytes.byteLength, nowS);\n\n const res = await fetchFn(endpoint, {\n method: 'PUT',\n headers,\n body: bytesToArrayBuffer(bytes),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return failed(ctx.uploadId, 'blossom', `server rejected (HTTP ${res.status})`, sha256);\n }\n\n const blob = (await res.json()) as BlossomDescriptor;\n if (typeof blob.url !== 'string' || blob.url.length === 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned no url', sha256);\n }\n if (typeof blob.sha256 !== 'string' || blob.sha256.length === 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned no sha256', sha256);\n }\n if (blob.sha256 !== sha256) {\n return failed(ctx.uploadId, 'blossom', 'server returned mismatched sha256', sha256);\n }\n if (!Number.isSafeInteger(blob.size) || (blob.size ?? -1) < 0) {\n return failed(ctx.uploadId, 'blossom', 'server returned invalid size', sha256);\n }\n if (blob.size !== bytes.byteLength) {\n return failed(ctx.uploadId, 'blossom', 'server returned mismatched size', sha256);\n }\n\n const mimeType = blob.type ?? request.mimeType;\n const nip94: NostrTag[] = [['url', blob.url]];\n if (mimeType) nip94.push(['m', mimeType]);\n nip94.push(['x', sha256], ['size', String(bytes.byteLength)]);\n\n const result: UploadResult = {\n ok: true,\n uploadId: ctx.uploadId,\n status: 'complete',\n rail: 'blossom',\n url: blob.url,\n sha256,\n size: bytes.byteLength,\n nip94,\n };\n if (mimeType) result.mimeType = mimeType;\n return result;\n}\n\ninterface BlossomDescriptor {\n url?: string;\n sha256?: string;\n size?: number;\n type?: string;\n}\n\nfunction failed(uploadId: string, rail: UploadRail, error: string, sha256?: string): UploadResult {\n return { ok: false, uploadId, status: 'failed', rail, error, ...(sha256 ? { sha256 } : {}) };\n}\n\nfunction cancelled(uploadId: string, rail: UploadRail, sha256?: string): UploadResult {\n return { ok: false, uploadId, status: 'cancelled', rail, error: 'user cancelled', ...(sha256 ? { sha256 } : {}) };\n}\n\nfunction emitUploading(ctx: UploaderContext, rail: UploadRail, bytesTotal: number, nowS: () => number): void {\n ctx.onStatus({\n ok: true,\n uploadId: ctx.uploadId,\n status: 'uploading',\n rail,\n bytesTotal,\n updatedAt: nowS() * 1_000,\n });\n}\n\nfunction firstConfiguredRail(rails: HttpUploaderRails): UploadRail | undefined {\n if (rails.nip96?.servers?.length) return 'nip96';\n if (rails.blossom?.servers?.length) return 'blossom';\n return undefined;\n}\n\nfunction nostrAuthHeader(event: NostrEvent): string {\n return `Nostr ${base64Utf8(JSON.stringify(event))}`;\n}\n\nfunction base64Utf8(s: string): string {\n // UTF-8 safe base64 (event content/tags may contain non-ASCII).\n return btoa(String.fromCharCode(...new TextEncoder().encode(s)));\n}\n\nfunction parseDimensions(dim: string | undefined): UploadDimensions | undefined {\n if (!dim) return undefined;\n const m = /^(\\d+)x(\\d+)$/.exec(dim);\n if (!m) return undefined;\n return { width: Number(m[1]), height: Number(m[2]) };\n}\n\nasync function toBytes(data: ArrayBuffer | Blob): Promise<Uint8Array> {\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n return new Uint8Array(await data.arrayBuffer());\n}\n\nfunction bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\nfunction trimTrailingSlash(url: string): string {\n return url.endsWith('/') ? url.slice(0, -1) : url;\n}\n\nasync function defaultDigestSha256(bytes: Uint8Array): Promise<string> {\n const buf = await crypto.subtle.digest('SHA-256', bytesToArrayBuffer(bytes));\n return Array.from(new Uint8Array(buf))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'upload failed';\n}\n","/**\n * intent-service.ts — NAP-INTENT (archetype intent dispatch) reference service.\n *\n * Shell-side handler for the NAP-INTENT wire protocol. It is a pure envelope\n * router: it validates `intent.*` envelopes, delegates archetype resolution,\n * default handling and target dispatch to an injected\n * {@link IntentResolver}, then posts correlated result and push messages back\n * to the napplet.\n *\n * The resolver is injected (options-as-bridge) so this service has no shell or\n * target-lifecycle dependency and is fully unit-testable. A concrete\n * catalog-backed resolver ships alongside as {@link createCatalogIntentResolver}.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: intent.invoke, intent.available, intent.handlers\n * Outbound: intent.invoke.result, intent.available.result,\n * intent.handlers.result, intent.changed\n *\n * The shell owns archetype→handler resolution, the user's default-handler\n * preference, chooser, and target lifecycle policy behind the\n * {@link IntentResolver}. This service only marshals the wire protocol and\n * fans `intent.changed` pushes out to eligible napplets.\n *\n * @example\n * ```ts\n * import { createIntentService, createCatalogIntentResolver } from '@kehto/services';\n *\n * const resolver = createCatalogIntentResolver({ loadCatalog, targets });\n * runtime.registerService('intent', createIntentService({ resolver }));\n * ```\n *\n * @packageDocumentation\n */\n\nimport type {\n IntentAvailability,\n IntentRequest,\n IntentResult,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n ServiceDescriptor,\n ServiceHandler,\n ServiceRuntimeContext,\n} from '@kehto/runtime';\n\n/** Intent service version — follows semver. */\nconst INTENT_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to {@link IntentResolver.invoke} for trust/attribution. */\nexport interface IntentResolverContext {\n /** Runtime-attested dTag of the napplet that issued the request. */\n sender: string;\n}\n\n/**\n * Abstract intent resolver. Implementors own the installed-napplet catalog,\n * archetype→handler resolution, the user's default-handler preference, the\n * chooser and target lifecycle/delivery policy. The\n * service translates wire envelopes into these calls and back.\n */\nexport interface IntentResolver {\n /**\n * Resolve and dispatch the request.\n *\n * @param request - Validated normalized intent request.\n * @param context - Runtime-attested source identity.\n * @returns The canonical structured dispatch result.\n */\n invoke(\n request: IntentRequest,\n context: IntentResolverContext,\n ): IntentResult | Promise<IntentResult>;\n /** Report whether the runtime can currently satisfy `archetype`, and how. */\n available(archetype: string): IntentAvailability | Promise<IntentAvailability>;\n /** Report availability for every archetype the runtime can currently satisfy. */\n handlers(): IntentAvailability[] | Promise<IntentAvailability[]>;\n /**\n * Register for availability changes (a napplet installed/removed, or a default\n * handler changed). The service forwards each change to served napplets as an\n * `intent.changed` push. Returns an unsubscribe handle. Resolvers whose\n * catalog never changes at runtime MAY omit this.\n */\n onChanged?(listener: (availability: IntentAvailability) => void): () => void;\n}\n\n/** Options for {@link createIntentService}. */\nexport interface IntentServiceOptions {\n /** The intent resolver the shell uses to route archetypes. Required. */\n resolver: IntentResolver;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst INTENT_DESCRIPTOR: ServiceDescriptor = {\n name: 'intent',\n version: INTENT_SERVICE_VERSION,\n description: 'NAP-INTENT archetype intent dispatch — invoke/available/handlers',\n};\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'intent request failed';\n}\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\ntype RequestValidation =\n | { request: IntentRequest }\n | { error: 'invalid request' | 'invalid convention' | 'invoke rejected' };\n\nfunction validateIntentRequest(value: unknown): RequestValidation {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return { error: 'invalid request' };\n }\n const request = value as Record<string, unknown>;\n if (\n typeof request.archetype !== 'string'\n || request.archetype.length === 0\n ) {\n return { error: 'invalid request' };\n }\n\n const action = request.action === undefined ? 'open' : request.action;\n if (typeof action !== 'string' || action.length === 0) {\n return { error: 'invalid request' };\n }\n if (\n request.convention !== undefined\n && (\n typeof request.convention !== 'string'\n || !/^napplet:[^/?#\\s]+\\/[^/?#\\s]+$/.test(request.convention)\n )\n ) {\n return { error: 'invalid convention' };\n }\n\n const allowedKeys = ['archetype', 'action', 'convention', 'payload', 'handler', 'behavior'];\n if (Object.keys(request).some((key) => !allowedKeys.includes(key))) {\n return { error: 'invoke rejected' };\n }\n\n if (\n hasOwn(request, 'handler')\n && request.handler !== undefined\n && (typeof request.handler !== 'string' || request.handler.length === 0)\n ) {\n return { error: 'invoke rejected' };\n }\n\n let behavior: IntentRequest['behavior'];\n if (hasOwn(request, 'behavior') && request.behavior !== undefined) {\n if (\n typeof request.behavior !== 'object'\n || request.behavior === null\n || Array.isArray(request.behavior)\n ) {\n return { error: 'invoke rejected' };\n }\n const record = request.behavior as Record<string, unknown>;\n if (\n Object.keys(record).some((key) =>\n key !== 'focus' && key !== 'newWindow' && key !== 'reuse')\n || (hasOwn(record, 'focus') && typeof record.focus !== 'boolean')\n || (hasOwn(record, 'newWindow') && typeof record.newWindow !== 'boolean')\n || (hasOwn(record, 'reuse') && typeof record.reuse !== 'boolean')\n ) {\n return { error: 'invoke rejected' };\n }\n behavior = {\n ...(record.focus === undefined ? {} : { focus: record.focus as boolean }),\n ...(record.newWindow === undefined\n ? {}\n : { newWindow: record.newWindow as boolean }),\n ...(record.reuse === undefined ? {} : { reuse: record.reuse as boolean }),\n };\n }\n\n return {\n request: {\n archetype: request.archetype,\n action,\n ...(typeof request.convention === 'string'\n ? { convention: request.convention }\n : {}),\n ...(hasOwn(request, 'payload') ? { payload: request.payload } : {}),\n ...(typeof request.handler === 'string' ? { handler: request.handler } : {}),\n ...(behavior === undefined ? {} : { behavior }),\n },\n };\n}\n\nfunction settleResolverCall<T>(\n call: () => T | Promise<T>,\n send: Send,\n resultType: string,\n id: string,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (error) {\n send({ type: resultType, id, error: toErrorMessage(error) } as NappletMessage);\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((error) => send({ type: resultType, id, error: toErrorMessage(error) } as NappletMessage));\n}\n\nfunction rejectedResult(\n request: Pick<IntentRequest, 'archetype' | 'action'>,\n error: string,\n): IntentResult {\n return {\n ok: false,\n archetype: request.archetype,\n action: request.action ?? 'open',\n handled: false,\n error,\n };\n}\n\nfunction rejectInvoke(\n send: Send,\n id: string,\n request: Pick<IntentRequest, 'archetype' | 'action'>,\n error = 'invoke rejected',\n): void {\n send({\n type: 'intent.invoke.result',\n id,\n result: rejectedResult(request, error),\n } as NappletMessage);\n}\n\n/**\n * Create the NAP-INTENT service handler.\n *\n * @param options - Must provide an {@link IntentResolver}.\n * @returns A `ServiceHandler` ready for `runtime.registerService('intent', handler)`.\n * @throws If `options.resolver` is missing.\n */\nexport function createIntentService(options: IntentServiceOptions): ServiceHandler {\n if (!options || typeof options.resolver !== 'object' || options.resolver === null) {\n throw new Error('createIntentService: options.resolver is required');\n }\n const { resolver } = options;\n let runtimeContext: ServiceRuntimeContext | undefined;\n let unsubscribeChanged: (() => void) | undefined;\n\n function handleInvoke(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; request?: unknown };\n const id = m.id ?? '';\n const validation = validateIntentRequest(m.request);\n if ('error' in validation) {\n const raw = typeof m.request === 'object' && m.request !== null\n ? m.request as Record<string, unknown>\n : {};\n rejectInvoke(\n send,\n id,\n {\n archetype: typeof raw.archetype === 'string' ? raw.archetype : '',\n action: typeof raw.action === 'string' ? raw.action : 'open',\n },\n validation.error,\n );\n return;\n }\n const sender = runtimeContext?.resolveDTag(windowId);\n if (!sender) {\n rejectInvoke(send, id, validation.request);\n return;\n }\n\n let pending: Promise<IntentResult>;\n try {\n pending = Promise.resolve(resolver.invoke(validation.request, { sender }));\n } catch {\n rejectInvoke(send, id, validation.request);\n return;\n }\n void pending.then(\n (result) => {\n send({\n type: 'intent.invoke.result',\n id,\n result,\n } as NappletMessage);\n },\n () => {\n rejectInvoke(send, id, validation.request);\n },\n ).catch(() => {\n // Adapter send failures must not manufacture a second source result.\n });\n }\n\n function handleAvailable(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; archetype?: unknown };\n const id = m.id ?? '';\n if (typeof m.archetype !== 'string' || m.archetype.length === 0) {\n send({ type: 'intent.available.result', id, error: 'invalid archetype' } as NappletMessage);\n return;\n }\n const archetype = m.archetype;\n settleResolverCall(\n () => resolver.available(archetype),\n send, 'intent.available.result', id,\n (availability) => ({ type: 'intent.available.result', id, availability } as NappletMessage),\n );\n }\n\n function handleHandlers(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string };\n const id = m.id ?? '';\n settleResolverCall(\n () => resolver.handlers(),\n send, 'intent.handlers.result', id,\n (handlers) => ({ type: 'intent.handlers.result', id, handlers } as NappletMessage),\n );\n }\n\n return {\n descriptor: INTENT_DESCRIPTOR,\n onRegistered(context): void {\n try {\n unsubscribeChanged?.();\n } catch {\n // Resolver cleanup is best-effort during replacement.\n }\n runtimeContext = context;\n unsubscribeChanged = resolver.onChanged?.((availability) => {\n const current = runtimeContext;\n if (!current) return;\n const message = { type: 'intent.changed', availability } as NappletMessage;\n for (const windowId of current.listWindowIds()) {\n current.sendToEligibleNapplet(windowId, message);\n }\n });\n },\n onUnregistered(): void {\n const unsubscribe = unsubscribeChanged;\n unsubscribeChanged = undefined;\n runtimeContext = undefined;\n try {\n unsubscribe?.();\n } catch {\n // Resolver cleanup cannot block runtime teardown.\n }\n },\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'intent.invoke':\n handleInvoke(windowId, message, send);\n return;\n case 'intent.available':\n handleAvailable(message, send);\n return;\n case 'intent.handlers':\n handleHandlers(message, send);\n return;\n default:\n // Unknown intent.* action — silently ignored (forward-compatible).\n return;\n }\n },\n };\n}\n","/**\n * catalog-intent-resolver.ts — NAP-INTENT concrete {@link IntentResolver}.\n *\n * A reference resolver backed by installed, verified NIP-5A manifest\n * archetype/convention tags plus host-supplied user policy. Catalog order and\n * payload contents never select a handler.\n *\n * Selection policy:\n * 1. Filter installed candidates by declared action and convention support.\n * 2. Require positive host authorization for an explicit handler dTag.\n * 3. Otherwise use an explicit chooser, a compatible user default, the sole\n * compatible candidate, or an injected chooser.\n * 4. Reject ambiguity when no chooser policy exists.\n *\n * The catalog, defaults, chooser, authorization hook, and target controller are\n * injected, so this resolver has no shell, manifest, or DOM dependency.\n *\n * @packageDocumentation\n */\n\nimport type {\n IntentAvailability,\n IntentBehavior,\n IntentCandidate,\n IntentRequest,\n IntentResult,\n} from '@napplet/core';\nimport type {\n IntentResolver,\n IntentResolverContext,\n} from './intent-service.js';\n\n/** The exact manifest-derived conventions a napplet fulfills for one archetype. */\nexport interface IntentArchetypeSupport {\n /** Verbs derived from this napplet's accepted conventions. */\n actions: string[];\n /** Stable queryless convention identities declared by the manifest. */\n conventions: string[];\n}\n\n/**\n * One installed napplet's intent surface, derived from its signed NIP-5A\n * manifest. Keyed by archetype slug so a single napplet can fulfill several\n * roles.\n */\nexport interface IntentCatalogEntry {\n /** The napplet's dTag. */\n dTag: string;\n /** Human-readable title from the manifest. */\n title?: string;\n /** Archetype slug to exact manifest-derived support. */\n archetypes: Record<string, IntentArchetypeSupport>;\n}\n\n/** Exact values dispatched to one selected intent target. */\nexport interface IntentDispatchParams {\n /** Selected target napplet dTag. */\n readonly handler: string;\n /** Runtime-attested source napplet dTag. */\n readonly sender: string;\n /** Requested target archetype. */\n readonly archetype: string;\n /** Requested action. */\n readonly action: string;\n /** Selected stable convention used to deliver the payload. */\n readonly convention: string;\n /** Opaque convention payload. */\n readonly payload?: unknown;\n /** Copied target lifecycle hints. */\n readonly behavior?: Readonly<IntentBehavior>;\n}\n\n/** Result returned by a host after creating/focusing and dispatching to a target. */\nexport interface IntentTargetDispatch {\n /** Shell-assigned target window identifier. */\n readonly windowId: string;\n}\n\n/** Host controller that owns target lifecycle and convention delivery policy. */\nexport interface IntentTargetController {\n /**\n * Create or focus the selected target, wait until it can receive the\n * convention, then enqueue delivery through the host's ordinary carrier.\n *\n * @param params - Immutable selected target and dispatch values.\n * @returns The created/focused target identity.\n */\n dispatch(\n params: IntentDispatchParams,\n ): IntentTargetDispatch | Promise<IntentTargetDispatch>;\n}\n\n/** Options for {@link createCatalogIntentResolver}. */\nexport interface CatalogIntentResolverOptions {\n /** Return the installed-napplet catalog sourced from signed manifests. */\n loadCatalog(): IntentCatalogEntry[] | Promise<IntentCatalogEntry[]>;\n /** Target controller that creates/readies the selected target and dispatches its convention. */\n targets: IntentTargetController;\n /**\n * Return the user's default handler dTag for an archetype.\n *\n * @param archetype - Normalized archetype slug.\n * @returns The user-selected default dTag, or `undefined`.\n */\n getDefaultHandler?(archetype: string): string | undefined;\n /**\n * Ask user policy to select one exact-compatible candidate.\n *\n * @param archetype - Normalized archetype slug.\n * @param candidates - Only candidates with an exact matching contract.\n * @param sender - Runtime-attested source napplet dTag.\n * @returns A candidate dTag, or `undefined` when the user cancels.\n */\n chooseHandler?(\n archetype: string,\n candidates: IntentCandidate[],\n sender: string,\n ): string | undefined | Promise<string | undefined>;\n /**\n * Authorize a caller's explicit handler dTag preference.\n *\n * @param sender - Runtime-attested source napplet dTag.\n * @param handler - Explicit requested handler dTag.\n * @param request - Normalized intent request.\n * @param candidate - Installed exact-compatible candidate.\n * @returns `true` only when explicit targeting is user-authorized.\n */\n authorizeExplicitHandler?(\n sender: string,\n handler: string,\n request: IntentRequest,\n candidate: IntentCandidate,\n ): boolean | Promise<boolean>;\n}\n\n/**\n * A {@link IntentResolver} backed by a catalog, with a host hook to announce\n * catalog/default changes.\n */\nexport interface CatalogIntentResolver extends IntentResolver {\n /**\n * Announce that the catalog or default handler for `archetype` changed.\n *\n * @param archetype - Changed archetype slug.\n * @returns Nothing.\n */\n notifyChanged(archetype: string): void;\n}\n\n/** Build the candidate list for an archetype, marking the user's default. */\nfunction candidatesFor(\n catalog: IntentCatalogEntry[],\n archetype: string,\n defaultHandler: string | undefined,\n): IntentCandidate[] {\n const candidates: IntentCandidate[] = [];\n for (const entry of catalog) {\n const support = entry.archetypes[archetype];\n if (!support) continue;\n candidates.push({\n dTag: entry.dTag,\n ...(entry.title === undefined ? {} : { title: entry.title }),\n actions: [...support.actions],\n conventions: [...support.conventions],\n ...(entry.dTag === defaultHandler ? { isDefault: true } : {}),\n });\n }\n return candidates;\n}\n\nfunction reject(request: IntentRequest, error: string): IntentResult {\n return {\n ok: false,\n archetype: request.archetype,\n action: request.action ?? 'open',\n handled: false,\n error,\n };\n}\n\ntype HandlerSelection =\n | { candidate: IntentCandidate }\n | { error: 'invoke rejected' | 'user cancelled' };\n\nasync function availabilityForCatalog(\n options: CatalogIntentResolverOptions,\n archetype: string,\n): Promise<IntentAvailability> {\n const catalog = await options.loadCatalog();\n const defaultHandler = options.getDefaultHandler?.(archetype);\n const candidates = candidatesFor(catalog, archetype, defaultHandler);\n return {\n archetype,\n available: candidates.length > 0,\n candidates,\n hasDefault: defaultHandler !== undefined\n && candidates.some((candidate) => candidate.dTag === defaultHandler),\n };\n}\n\nasync function chooseCompatible(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n candidates: IntentCandidate[],\n sender: string,\n): Promise<HandlerSelection> {\n if (!options.chooseHandler) {\n return request.handler === 'choose'\n ? { error: 'user cancelled' }\n : { error: 'invoke rejected' };\n }\n const picked = await options.chooseHandler(request.archetype, candidates, sender);\n if (picked === undefined) return { error: 'user cancelled' };\n const candidate = candidates.find((item) => item.dTag === picked);\n return candidate ? { candidate } : { error: 'invoke rejected' };\n}\n\nasync function pickHandler(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n compatible: IntentCandidate[],\n sender: string,\n): Promise<HandlerSelection> {\n const preference = request.handler;\n if (typeof preference === 'string' && preference !== 'default' && preference !== 'choose') {\n const candidate = compatible.find((item) => item.dTag === preference);\n if (!candidate || !options.authorizeExplicitHandler) return { error: 'invoke rejected' };\n let authorized = false;\n try {\n authorized = await options.authorizeExplicitHandler(\n sender,\n preference,\n request,\n candidate,\n );\n } catch {\n return { error: 'invoke rejected' };\n }\n return authorized ? { candidate } : { error: 'invoke rejected' };\n }\n\n if (preference === 'choose') {\n return chooseCompatible(options, request, compatible, sender);\n }\n\n const defaultCandidate = compatible.find((candidate) => candidate.isDefault === true);\n if (preference === 'default') {\n return defaultCandidate ? { candidate: defaultCandidate } : { error: 'invoke rejected' };\n }\n if (defaultCandidate) return { candidate: defaultCandidate };\n if (compatible.length === 1) return { candidate: compatible[0] };\n return chooseCompatible(options, request, compatible, sender);\n}\n\nasync function invokeFromCatalog(\n options: CatalogIntentResolverOptions,\n request: IntentRequest,\n context: IntentResolverContext,\n): Promise<IntentResult> {\n const catalog = await options.loadCatalog();\n const defaultHandler = options.getDefaultHandler?.(request.archetype);\n const candidates = candidatesFor(catalog, request.archetype, defaultHandler);\n if (candidates.length === 0) return reject(request, 'no handler');\n\n const action = request.action ?? 'open';\n const actionCompatible = candidates.filter((candidate) =>\n candidate.actions.includes(action));\n if (actionCompatible.length === 0) return reject(request, 'unsupported action');\n const compatible = request.convention === undefined\n ? actionCompatible\n : actionCompatible.filter((candidate) =>\n candidate.conventions.includes(request.convention as string));\n if (compatible.length === 0) return reject(request, 'unsupported convention');\n\n const sender = context.sender;\n if (typeof sender !== 'string' || sender.length === 0) {\n return reject(request, 'invoke rejected');\n }\n const selected = await pickHandler(options, request, compatible, sender);\n if ('error' in selected) return reject(request, selected.error);\n\n const convention = request.convention\n ?? selected.candidate.conventions.find((value) => {\n const match = /^napplet:[^/?#\\s]+\\/([^/?#\\s]+)$/.exec(value);\n return match?.[1] === action;\n });\n if (!convention) return reject(request, 'unsupported convention');\n const behavior = request.behavior === undefined\n ? undefined\n : Object.freeze({\n ...(request.behavior.focus === undefined ? {} : { focus: request.behavior.focus }),\n ...(request.behavior.newWindow === undefined\n ? {}\n : { newWindow: request.behavior.newWindow }),\n ...(request.behavior.reuse === undefined ? {} : { reuse: request.behavior.reuse }),\n });\n const params = Object.freeze({\n handler: selected.candidate.dTag,\n sender,\n archetype: request.archetype,\n action,\n convention,\n ...(request.payload === undefined ? {} : { payload: request.payload }),\n ...(behavior === undefined ? {} : { behavior }),\n }) satisfies IntentDispatchParams;\n\n let target: IntentTargetDispatch;\n try {\n target = await options.targets.dispatch(params);\n } catch {\n return reject(request, 'invoke failed');\n }\n if (!target || typeof target.windowId !== 'string' || target.windowId.length === 0) {\n return reject(request, 'invoke failed');\n }\n\n return {\n ok: true,\n archetype: request.archetype,\n action,\n handled: true,\n handler: selected.candidate.dTag,\n windowId: target.windowId,\n convention,\n };\n}\n\n/**\n * Create a catalog-backed NAP-INTENT resolver.\n *\n * @param options - Catalog loader and target controller plus optional user\n * default, chooser, and explicit-handler authorization hooks.\n * @returns A catalog-backed resolver.\n * @throws If required catalog or target-controller options are missing.\n *\n * @example\n * ```ts\n * const resolver = createCatalogIntentResolver({\n * loadCatalog: () => installedNapplets,\n * targets: { dispatch: (params) => openAndDispatch(params) },\n * getDefaultHandler: (archetype) => userDefaults[archetype],\n * });\n * ```\n */\nexport function createCatalogIntentResolver(options: CatalogIntentResolverOptions): CatalogIntentResolver {\n if (!options || typeof options.loadCatalog !== 'function') {\n throw new Error('createCatalogIntentResolver: options.loadCatalog is required');\n }\n if (!options.targets || typeof options.targets.dispatch !== 'function') {\n throw new Error('createCatalogIntentResolver: options.targets is required');\n }\n const listeners = new Set<(availability: IntentAvailability) => void>();\n const availabilityFor = (archetype: string): Promise<IntentAvailability> =>\n availabilityForCatalog(options, archetype);\n\n async function handlers(): Promise<IntentAvailability[]> {\n const catalog = await options.loadCatalog();\n const archetypes = new Set<string>();\n for (const entry of catalog) {\n for (const slug of Object.keys(entry.archetypes)) archetypes.add(slug);\n }\n return Promise.all([...archetypes].map((archetype) => availabilityFor(archetype)));\n }\n\n return {\n invoke: (request, context) => invokeFromCatalog(options, request, context),\n available: availabilityFor,\n handlers,\n onChanged(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n notifyChanged(archetype) {\n if (listeners.size === 0) return;\n void availabilityFor(archetype).then((availability) => {\n for (const listener of listeners) listener(availability);\n });\n },\n };\n}\n","/**\n * manifest-intent-catalog.ts — signed-manifest → NAP-INTENT catalog adapter.\n *\n * Adapts a resolved NIP-5A/5D napplet manifest's archetype tags into an\n * {@link IntentCatalogEntry} — the shape `createCatalogIntentResolver.loadCatalog`\n * consumes. This lets NAP-INTENT availability and handler candidacy flow from\n * verified manifest tags rather than host-injected catalog data.\n *\n * To avoid a `@kehto/services → @kehto/nip` dependency cycle (services must stay\n * dependency-light and `@kehto/nip` is a lower-level NIP utility), the adapter\n * takes a minimal STRUCTURAL input {@link ManifestArchetypeInput} that the\n * `@kehto/nip/5d` `NappletManifest` satisfies by duck typing — callers pass\n * `resolved.manifest` directly without any package coupling.\n *\n * @packageDocumentation\n */\n\nimport type { IntentArchetypeSupport, IntentCatalogEntry } from './catalog-intent-resolver.js';\n\n/**\n * The structural subset of `@kehto/nip/5d` `NappletManifest` the adapter needs.\n * Intentionally a duck-typed shape so the playground (or any caller) can pass a\n * resolved manifest without importing `@kehto/nip`.\n */\nexport interface ManifestArchetypeInput {\n /** The napplet's `d` identifier. */\n dTag: string;\n /** Optional human-readable title from the manifest. */\n title?: string;\n /**\n * Ordered convention contracts from the manifest's `archetype` tags.\n */\n archetypes: Array<{ slug: string; convention: string }>;\n}\n\nfunction actionFromConvention(convention: string): string {\n const match = /^napplet:([^/?#\\s]+)\\/([^/?#\\s]+)$/.exec(convention);\n if (!match) {\n throw new TypeError('manifest archetype convention is invalid');\n }\n return match[2];\n}\n\n/**\n * Map a resolved napplet manifest's archetype data into an\n * {@link IntentCatalogEntry}.\n *\n * Repeated slugs group into one support record; action and convention arrays\n * remain stable and deduplicated.\n *\n * @param manifest - A resolved manifest's structural archetype data.\n * @returns The `IntentCatalogEntry` for `createCatalogIntentResolver`.\n *\n * @example\n * ```ts\n * manifestToIntentCatalogEntry({\n * dTag: 'profile-viewer',\n * title: 'Profile',\n * archetypes: [{ slug: 'profile', convention: 'napplet:profile/open' }],\n * });\n * // → { dTag: 'profile-viewer', title: 'Profile',\n * // archetypes: { profile: {\n * // actions: ['open'],\n * // conventions: ['napplet:profile/open'],\n * // } } }\n * ```\n */\nexport function manifestToIntentCatalogEntry(manifest: ManifestArchetypeInput): IntentCatalogEntry {\n const archetypes: Record<string, IntentArchetypeSupport> = {};\n for (const { slug, convention } of manifest.archetypes) {\n if (!/^[a-z0-9][a-z0-9-]*$/.test(slug)) {\n throw new TypeError('manifest archetype slug is invalid');\n }\n // NAP-INTENT deliberately keeps routing archetypes and payload conventions\n // orthogonal, so the convention's URI archetype need not equal this slug.\n const action = actionFromConvention(convention);\n const support = archetypes[slug] ??= {\n actions: [],\n conventions: [],\n };\n if (!support.actions.includes(action)) support.actions.push(action);\n if (!support.conventions.includes(convention)) support.conventions.push(convention);\n }\n return {\n dTag: manifest.dTag,\n ...(manifest.title === undefined ? {} : { title: manifest.title }),\n archetypes,\n };\n}\n","/**\n * cvm-service.ts — NAP-CVM (ContextVM bridge) reference service.\n *\n * Shell-side handler for the NAP-CVM wire protocol. It is a pure envelope\n * router: it validates `cvm.*` envelopes, delegates the actual ContextVM /\n * MCP-over-Nostr work to an injected {@link CvmTransport}, and posts the\n * correlated `*.result` (echoing the request `id`) back to the napplet.\n *\n * The transport is injected (options-as-bridge) so this service has no Nostr\n * dependency and is fully unit-testable. A concrete ContextVM transport ships\n * separately at `@kehto/services/cvm-nostr-transport`.\n *\n * ──────────────────────────── Responsibilities ────────────────────────────\n * Inbound: cvm.discover, cvm.request, cvm.close\n * Outbound: cvm.discover.result, cvm.request.result, cvm.close.result,\n * cvm.event (server-pushed MCP notifications)\n *\n * MCP-level errors are returned inside `request.result.message.error`;\n * transport/shell-policy failures are returned in the envelope `error` field.\n *\n * `cvm.event` is fanned out to every window that holds an active session with\n * the originating server (a window opens a session by issuing a `cvm.request`\n * and closes it via `cvm.close` or window teardown).\n *\n * @example\n * ```ts\n * import { createCvmService } from '@kehto/services';\n * import { createNostrCvmTransport } from '@kehto/services/cvm-nostr-transport';\n *\n * const transport = createNostrCvmTransport({ defaultRelays: ['wss://relay.contextvm.org'] });\n * runtime.registerService('cvm', createCvmService({ transport }));\n * ```\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n CvmDiscoverQuery,\n CvmRequestOptions,\n CvmServer,\n CvmServerRef,\n McpMessage,\n} from './cvm-types.js';\n\n/** CVM service version — follows semver. */\nconst CVM_SERVICE_VERSION = '1.0.0';\n\n/**\n * Abstract ContextVM transport. Implementors own Nostr relay access, signing,\n * encryption (CEP-4 gift wrap), JSON-RPC correlation, and MCP initialization.\n */\nexport interface CvmTransport {\n /** Resolve public ContextVM server announcements matching the query. */\n discover(query?: CvmDiscoverQuery): Promise<CvmServer[]>;\n /** Send a raw MCP message to a server and resolve with the MCP response. */\n request(server: CvmServerRef, message: McpMessage, options?: CvmRequestOptions): Promise<McpMessage>;\n /** Release any session state held for a server (subscriptions, init cache). */\n close(server: CvmServerRef): Promise<void>;\n /**\n * Subscribe to server-pushed MCP messages not correlated to a single\n * request (e.g. notifications). Returns a handle whose `close()` detaches.\n */\n onEvent(handler: (server: CvmServerRef, message: McpMessage) => void): { close(): void };\n /** Release all transport resources when the owning service is disposed. */\n dispose?(): void;\n}\n\n/** Options for {@link createCvmService}. */\nexport interface CvmServiceOptions {\n /** The ContextVM transport the shell uses to reach servers. Required. */\n transport: CvmTransport;\n}\n\n/** The created CVM service, exposing the handler plus a disposal hook. */\nexport interface CvmService extends ServiceHandler {\n /** Detach the transport event subscription. Idempotent. */\n dispose(): void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst CVM_DESCRIPTOR: ServiceDescriptor = {\n name: 'cvm',\n version: CVM_SERVICE_VERSION,\n description: 'NAP-CVM ContextVM bridge — MCP over Nostr',\n};\n\n/**\n * Create the NAP-CVM service handler.\n *\n * @param options - Must provide a {@link CvmTransport}.\n * @returns A {@link CvmService} (a `ServiceHandler` with a `dispose()` hook).\n * @throws If `options.transport` is missing.\n */\nexport function createCvmService(options: CvmServiceOptions): CvmService {\n if (!options || typeof options.transport !== 'object' || options.transport === null) {\n throw new Error('createCvmService: options.transport is required');\n }\n const { transport } = options;\n\n // Per-window send callbacks, captured at request time for cvm.event fan-out.\n const sendByWindow = new Map<string, Send>();\n // serverPubkey -> set of windowIds with an active session.\n const windowsByServer = new Map<string, Set<string>>();\n\n function openSession(windowId: string, server: CvmServerRef, send: Send): void {\n sendByWindow.set(windowId, send);\n let windows = windowsByServer.get(server.pubkey);\n if (!windows) {\n windows = new Set<string>();\n windowsByServer.set(server.pubkey, windows);\n }\n windows.add(windowId);\n }\n\n function closeSession(windowId: string, serverPubkey: string): void {\n const windows = windowsByServer.get(serverPubkey);\n if (windows) {\n windows.delete(windowId);\n if (windows.size === 0) windowsByServer.delete(serverPubkey);\n }\n }\n\n const eventSub = transport.onEvent((server, message) => {\n const windows = windowsByServer.get(server.pubkey);\n if (!windows) return;\n for (const windowId of windows) {\n const send = sendByWindow.get(windowId);\n send?.({ type: 'cvm.event', server, message } as NappletMessage);\n }\n });\n\n function handleDiscover(msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; query?: CvmDiscoverQuery };\n const id = m.id ?? '';\n void transport\n .discover(m.query)\n .then((servers) => send({ type: 'cvm.discover.result', id, servers } as NappletMessage))\n .catch((err) =>\n send({ type: 'cvm.discover.result', id, servers: [], error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleRequest(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & {\n id?: string;\n server?: CvmServerRef;\n message?: McpMessage;\n options?: CvmRequestOptions;\n };\n const id = m.id ?? '';\n if (!m.server || typeof m.server.pubkey !== 'string' || m.server.pubkey.length === 0) {\n send({ type: 'cvm.request.result', id, error: 'server not found' } as NappletMessage);\n return;\n }\n if (!m.message || typeof m.message !== 'object') {\n send({ type: 'cvm.request.result', id, error: 'unsupported method' } as NappletMessage);\n return;\n }\n openSession(windowId, m.server, send);\n void transport\n .request(m.server, m.message, m.options)\n .then((message) => send({ type: 'cvm.request.result', id, message } as NappletMessage))\n .catch((err) =>\n send({ type: 'cvm.request.result', id, error: toErrorMessage(err) } as NappletMessage),\n );\n }\n\n function handleClose(windowId: string, msg: NappletMessage, send: Send): void {\n const m = msg as NappletMessage & { id?: string; server?: CvmServerRef };\n const id = m.id ?? '';\n if (!m.server || typeof m.server.pubkey !== 'string') {\n send({ type: 'cvm.close.result', id, error: 'server not found' } as NappletMessage);\n return;\n }\n closeSession(windowId, m.server.pubkey);\n void transport\n .close(m.server)\n .then(() => send({ type: 'cvm.close.result', id } as NappletMessage))\n .catch((err) => send({ type: 'cvm.close.result', id, error: toErrorMessage(err) } as NappletMessage));\n }\n\n return {\n descriptor: CVM_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n switch (message.type) {\n case 'cvm.discover':\n handleDiscover(message, send);\n return;\n case 'cvm.request':\n handleRequest(windowId, message, send);\n return;\n case 'cvm.close':\n handleClose(windowId, message, send);\n return;\n default:\n // Unknown cvm.* action — silently ignored (forward-compatible).\n return;\n }\n },\n onWindowDestroyed(windowId: string): void {\n sendByWindow.delete(windowId);\n for (const [pubkey, windows] of windowsByServer) {\n windows.delete(windowId);\n if (windows.size === 0) {\n windowsByServer.delete(pubkey);\n void transport.close({ pubkey }).catch(() => {});\n }\n }\n },\n dispose(): void {\n eventSub.close();\n transport.dispose?.();\n sendByWindow.clear();\n windowsByServer.clear();\n },\n };\n}\n\nfunction toErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return 'cvm request failed';\n}\n","/**\n * link-service.ts — NAP-LINK reference service.\n *\n * Shell-mediated user-visible navigation. The napplet requests `link.open`;\n * the shell validates policy and hands navigation to a host-owned context.\n */\n\nimport type { LinkOpenErrorCode, LinkOpenOptions, LinkOpenResult, NappletMessage } from '@napplet/core';\nimport type { LinkOpenMessage, LinkOpenResultMessage } from '@napplet/nap/link/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst LINK_SERVICE_VERSION = '1.0.0';\nconst DEFAULT_ALLOWED_PROTOCOLS = ['http:', 'https:'] as const;\n\n/**\n * Host context passed to {@link LinkServiceOptions.open}.\n */\nexport interface LinkOpenContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Parsed absolute URL after validation. */\n url: URL;\n /** Optional untrusted napplet display hints. */\n options?: LinkOpenOptions;\n}\n\n/**\n * Options for {@link createLinkService}.\n */\nexport interface LinkServiceOptions {\n /**\n * Protocols allowed before host policy runs. Defaults to `http:` and `https:`.\n */\n allowedProtocols?: readonly string[];\n /**\n * Host-owned opener. Return `{ status: \"opened\" }` after handing navigation\n * to the host context, or `{ status: \"denied\" }` to reject by policy/user.\n * When omitted, all otherwise-valid URLs are denied.\n */\n open?: (context: LinkOpenContext) => LinkOpenResult | Promise<LinkOpenResult>;\n}\n\nfunction denied(id: string, error: LinkOpenErrorCode): LinkOpenResultMessage {\n return { type: 'link.open.result', id, status: 'denied', error };\n}\n\nfunction parseLinkUrl(id: string, rawUrl: string): URL | LinkOpenResultMessage {\n try {\n return new URL(rawUrl);\n } catch {\n return denied(id, 'invalid-url');\n }\n}\n\nfunction isProtocolAllowed(url: URL, allowedProtocols: readonly string[]): boolean {\n return allowedProtocols.includes(url.protocol);\n}\n\nasync function handleOpen(\n options: LinkServiceOptions,\n windowId: string,\n message: LinkOpenMessage,\n send: (msg: NappletMessage) => void,\n): Promise<void> {\n const parsed = parseLinkUrl(message.id, message.url);\n if ('type' in parsed) {\n send(parsed as NappletMessage);\n return;\n }\n\n const allowedProtocols = options.allowedProtocols ?? DEFAULT_ALLOWED_PROTOCOLS;\n if (!isProtocolAllowed(parsed, allowedProtocols)) {\n send(denied(message.id, 'unsupported-scheme') as NappletMessage);\n return;\n }\n\n if (!options.open) {\n send(denied(message.id, 'blocked-by-policy') as NappletMessage);\n return;\n }\n\n try {\n const result = await options.open({ windowId, url: parsed, options: message.options });\n send({\n type: 'link.open.result',\n id: message.id,\n status: result.status,\n ...(result.status === 'denied' ? { error: 'blocked-by-policy' as LinkOpenErrorCode } : {}),\n } as NappletMessage);\n } catch {\n send(denied(message.id, 'blocked-by-policy') as NappletMessage);\n }\n}\n\n/**\n * Create the NAP-LINK reference service.\n *\n * @param options - Host policy and opener hooks for link.open requests.\n * @returns A runtime service handler for the `link` domain.\n *\n * @example\n * ```ts\n * const service = createLinkService({\n * open: ({ url }) => ({ status: url.protocol === 'https:' ? 'opened' : 'denied' }),\n * });\n * ```\n */\nexport function createLinkService(options: LinkServiceOptions = {}): ServiceHandler {\n const descriptor: ServiceDescriptor = {\n name: 'link',\n version: LINK_SERVICE_VERSION,\n description: 'NAP-LINK reference handler for shell-mediated link opening',\n };\n\n return {\n descriptor,\n handleMessage(windowId: string, message: NappletMessage, send: (msg: NappletMessage) => void): void {\n if (message.type === 'link.open') {\n void handleOpen(options, windowId, message as LinkOpenMessage, send);\n return;\n }\n\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown link method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n","/**\n * lists-service.ts — NAP-LISTS reference service.\n *\n * Shell-mediated NIP-51 list metadata and mutation hooks. The napplet requests\n * `lists.*`; the shell owns list lookup, validation, signing, publishing, and\n * private-item policy.\n */\n\nimport type {\n ListItem,\n ListMutationResult,\n ListOptions,\n ListRef,\n ListSupport,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n ListsAddMessage,\n ListsRemoveMessage,\n} from '@napplet/nap/lists/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst LISTS_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-LISTS hooks. */\nexport interface ListsServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n}\n\n/** Options for {@link createListsService}. */\nexport interface ListsServiceOptions {\n /** Host-owned supported list metadata. */\n supported?: (context: ListsServiceContext) => readonly ListSupport[] | Promise<readonly ListSupport[]>;\n /** Host-owned add mutation. */\n add?: (\n list: ListRef,\n items: readonly ListItem[],\n options: ListOptions | undefined,\n context: ListsServiceContext,\n ) => ListMutationResult | Promise<ListMutationResult>;\n /** Host-owned remove mutation. */\n remove?: (\n list: ListRef,\n items: readonly ListItem[],\n options: ListOptions | undefined,\n context: ListsServiceContext,\n ) => ListMutationResult | Promise<ListMutationResult>;\n}\n\ntype Send = (msg: NappletMessage) => void;\ntype MutationHook = NonNullable<ListsServiceOptions['add'] | ListsServiceOptions['remove']>;\n\nconst LISTS_DESCRIPTOR: ServiceDescriptor = {\n name: 'lists',\n version: LISTS_SERVICE_VERSION,\n description: 'NAP-LISTS reference handler for shell-mediated NIP-51 list mutations',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'lists request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'lists request failed'))));\n}\n\nfunction unsupportedMutation(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n ok: false,\n error: 'unsupported',\n reason: `${resultType.replace('.result', '')} unavailable`,\n supported: [],\n } as NappletMessage;\n}\n\nfunction settleMutation(\n hook: MutationHook | undefined,\n message: ListsAddMessage | ListsRemoveMessage,\n context: ListsServiceContext,\n send: Send,\n resultType: 'lists.add.result' | 'lists.remove.result',\n): void {\n if (!hook) {\n send(unsupportedMutation(resultType, message.id));\n return;\n }\n settle(\n () => hook(message.list, message.items, message.options, context),\n send,\n (error) => ({ type: resultType, id: message.id, ok: false, error: 'list-unavailable', reason: error } as NappletMessage),\n (result) => ({ type: resultType, id: message.id, ...result } as NappletMessage),\n );\n}\n\n/**\n * Create the NAP-LISTS reference service.\n *\n * @param options - Host list metadata and mutation hooks for lists.* requests.\n * @returns A runtime service handler for the `lists` domain.\n */\nexport function createListsService(options: ListsServiceOptions = {}): ServiceHandler {\n return {\n descriptor: LISTS_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = { windowId };\n\n if (message.type === 'lists.supported') {\n if (!options.supported) {\n send({ type: 'lists.supported.result', id, lists: [] } as NappletMessage);\n return;\n }\n settle(\n () => options.supported!(context),\n send,\n (error) => ({ type: 'lists.supported.result', id, error } as NappletMessage),\n (lists) => ({ type: 'lists.supported.result', id, lists: [...lists] } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'lists.add') {\n settleMutation(options.add, message as ListsAddMessage, context, send, 'lists.add.result');\n return;\n }\n\n if (message.type === 'lists.remove') {\n settleMutation(options.remove, message as ListsRemoveMessage, context, send, 'lists.remove.result');\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown lists method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n","/**\n * serial-service.ts — NAP-SERIAL reference service.\n *\n * Shell-mediated serial sessions. The napplet requests `serial.*`; the shell\n * owns device selection, permissions, session handles, reads, write ordering,\n * close events, and policy.\n */\n\nimport type {\n NappletMessage,\n SerialEvent,\n SerialOpenRequest,\n SerialOpenResult,\n} from '@napplet/core';\nimport type {\n SerialCloseMessage,\n SerialOpenMessage,\n SerialWriteMessage,\n} from '@napplet/nap/serial/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst SERIAL_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-SERIAL hooks. */\nexport interface SerialServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned serial event back to the requesting napplet. */\n emit(event: SerialEvent): void;\n}\n\n/** Options for {@link createSerialService}. */\nexport interface SerialServiceOptions {\n /** Host-owned serial open hook. */\n open?: (\n request: SerialOpenRequest,\n context: SerialServiceContext,\n ) => SerialOpenResult | Promise<SerialOpenResult>;\n /** Host-owned serial write hook. */\n write?: (\n sessionId: string,\n data: readonly number[],\n context: SerialServiceContext,\n ) => void | Promise<void>;\n /** Host-owned serial close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: SerialServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\ninterface SerialConnection {\n state: 'opening' | 'open' | 'closed';\n readonly bufferedEvents: SerialEvent[];\n}\n\ninterface SerialWindow {\n readonly connections: Map<string, SerialConnection>;\n readonly pendingConnections: Set<SerialConnection>;\n}\n\nconst SERIAL_DESCRIPTOR: ServiceDescriptor = {\n name: 'serial',\n version: SERIAL_SERVICE_VERSION,\n description: 'NAP-SERIAL reference handler for shell-mediated serial sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'serial request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'serial request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, emit: (event: SerialEvent) => void): SerialServiceContext {\n return {\n windowId,\n emit,\n };\n}\n\n/**\n * Create the NAP-SERIAL reference service.\n *\n * @param options - Host serial session hooks for serial.* requests.\n * @returns A runtime service handler for the `serial` domain.\n */\nexport function createSerialService(options: SerialServiceOptions = {}): ServiceHandler {\n const windows = new Map<string, SerialWindow>();\n\n const currentWindow = (windowId: string): SerialWindow => {\n const existing = windows.get(windowId);\n if (existing) return existing;\n const record: SerialWindow = {\n connections: new Map<string, SerialConnection>(),\n pendingConnections: new Set<SerialConnection>(),\n };\n windows.set(windowId, record);\n return record;\n };\n\n const emit = (\n windowId: string,\n record: SerialWindow,\n connection: SerialConnection | undefined,\n send: Send,\n event: SerialEvent,\n ): void => {\n if (!connection || windows.get(windowId) !== record || connection.state === 'closed') return;\n if (connection.state === 'opening') {\n connection.bufferedEvents.push(event);\n return;\n }\n send({ type: 'serial.event', event } as NappletMessage);\n };\n\n return {\n descriptor: SERIAL_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const record = currentWindow(windowId);\n const isCurrent = (): boolean => windows.get(windowId) === record;\n const currentSend: Send = (outbound) => {\n if (isCurrent()) send(outbound);\n };\n if (message.type === 'serial.open') {\n if (!options.open) {\n currentSend(unsupported('serial.open.result', id));\n return;\n }\n const serialMessage = message as SerialOpenMessage;\n const connection: SerialConnection = { state: 'opening', bufferedEvents: [] };\n record.pendingConnections.add(connection);\n const context = createContext(windowId, (event) => emit(windowId, record, connection, currentSend, event));\n let pending: Promise<SerialOpenResult>;\n try {\n pending = Promise.resolve(options.open(serialMessage.request, context));\n } catch (error) {\n record.pendingConnections.delete(connection);\n connection.state = 'closed';\n currentSend({\n type: 'serial.open.result',\n id,\n error: errorMessage(error, 'serial request failed'),\n } as NappletMessage);\n return;\n }\n void pending\n .then((result) => {\n record.pendingConnections.delete(connection);\n if (!isCurrent() || connection.state === 'closed') return;\n connection.state = 'open';\n record.connections.set(result.session.id, connection);\n currentSend({ type: 'serial.open.result', id, session: result.session } as NappletMessage);\n if (!isCurrent() || connection.state !== 'open') return;\n for (const event of connection.bufferedEvents) {\n if (!isCurrent() || connection.state !== 'open') break;\n currentSend({ type: 'serial.event', event } as NappletMessage);\n }\n connection.bufferedEvents.length = 0;\n })\n .catch((error) => {\n record.pendingConnections.delete(connection);\n connection.state = 'closed';\n currentSend({\n type: 'serial.open.result',\n id,\n error: errorMessage(error, 'serial request failed'),\n } as NappletMessage);\n });\n return;\n }\n\n if (message.type === 'serial.write') {\n if (!options.write) {\n currentSend(unsupported('serial.write.result', id));\n return;\n }\n const serialMessage = message as SerialWriteMessage;\n const context = createContext(windowId, (event) => emit(\n windowId,\n record,\n record.connections.get(serialMessage.sessionId),\n currentSend,\n event,\n ));\n settle(\n () => options.write!(serialMessage.sessionId, serialMessage.data, context),\n currentSend,\n (error) => ({ type: 'serial.write.result', id, error } as NappletMessage),\n () => ({ type: 'serial.write.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'serial.close') {\n if (!options.close) {\n currentSend(unsupported('serial.close.result', id));\n return;\n }\n const serialMessage = message as SerialCloseMessage;\n const connection = record.connections.get(serialMessage.sessionId);\n if (connection) {\n connection.state = 'closed';\n record.connections.delete(serialMessage.sessionId);\n }\n const context = createContext(windowId, (event) => emit(\n windowId,\n record,\n connection,\n currentSend,\n event,\n ));\n settle(\n () => options.close!(serialMessage.sessionId, serialMessage.reason, context),\n currentSend,\n (error) => ({ type: 'serial.close.result', id, error } as NappletMessage),\n () => ({ type: 'serial.close.result', id } as NappletMessage),\n );\n return;\n }\n\n currentSend({\n type: `${message.type}.error`,\n id,\n error: `Unknown serial method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n const record = windows.get(windowId);\n if (record) {\n for (const connection of record.connections.values()) connection.state = 'closed';\n for (const connection of record.pendingConnections) connection.state = 'closed';\n record.connections.clear();\n record.pendingConnections.clear();\n windows.delete(windowId);\n }\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * ble-service.ts — NAP-BLE reference service.\n *\n * Shell-mediated BLE/GATT sessions. The napplet requests `ble.*`; the shell\n * owns chooser UI, permissions, GATT handles, notifications, and policy.\n */\n\nimport type {\n BleAttribute,\n BleEvent,\n BleOpenRequest,\n BleOpenResult,\n BleService,\n BleWriteOptions,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n BleCloseMessage,\n BleOpenMessage,\n BleReadMessage,\n BleServicesMessage,\n BleSubscribeMessage,\n BleUnsubscribeMessage,\n BleWriteMessage,\n} from '@napplet/nap/ble/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst BLE_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-BLE hooks. */\nexport interface BleServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned BLE event back to the requesting napplet. */\n emit(event: BleEvent): void;\n}\n\n/** Options for {@link createBleService}. */\nexport interface BleServiceOptions {\n /** Host-owned BLE open hook. */\n open?: (\n request: BleOpenRequest,\n context: BleServiceContext,\n ) => BleOpenResult | Promise<BleOpenResult>;\n /** Host-owned GATT service listing hook. */\n services?: (\n sessionId: string,\n context: BleServiceContext,\n ) => BleService[] | Promise<BleService[]>;\n /** Host-owned GATT read hook. */\n read?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => number[] | Promise<number[]>;\n /** Host-owned GATT write hook. */\n write?: (\n sessionId: string,\n target: BleAttribute,\n data: readonly number[],\n options: BleWriteOptions | undefined,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned notification subscription hook. */\n subscribe?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned notification unsubscribe hook. */\n unsubscribe?: (\n sessionId: string,\n target: BleAttribute,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Host-owned BLE close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: BleServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst BLE_DESCRIPTOR: ServiceDescriptor = {\n name: 'ble',\n version: BLE_SERVICE_VERSION,\n description: 'NAP-BLE reference handler for shell-mediated BLE/GATT sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'ble request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'ble request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, send: Send): BleServiceContext {\n return {\n windowId,\n emit(event) {\n send({ type: 'ble.event', event } as NappletMessage);\n },\n };\n}\n\n/**\n * Create the NAP-BLE reference service.\n *\n * @param options - Host BLE session hooks for ble.* requests.\n * @returns A runtime service handler for the `ble` domain.\n */\nexport function createBleService(options: BleServiceOptions = {}): ServiceHandler {\n return {\n descriptor: BLE_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = createContext(windowId, send);\n\n if (message.type === 'ble.open') {\n if (!options.open) {\n send(unsupported('ble.open.result', id));\n return;\n }\n const bleMessage = message as BleOpenMessage;\n settle(\n () => options.open!(bleMessage.request, context),\n send,\n (error) => ({ type: 'ble.open.result', id, error } as NappletMessage),\n (result) => ({ type: 'ble.open.result', id, session: result.session } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.services') {\n if (!options.services) {\n send(unsupported('ble.services.result', id));\n return;\n }\n const bleMessage = message as BleServicesMessage;\n settle(\n () => options.services!(bleMessage.sessionId, context),\n send,\n (error) => ({ type: 'ble.services.result', id, error } as NappletMessage),\n (services) => ({ type: 'ble.services.result', id, services } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.read') {\n if (!options.read) {\n send(unsupported('ble.read.result', id));\n return;\n }\n const bleMessage = message as BleReadMessage;\n settle(\n () => options.read!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.read.result', id, error } as NappletMessage),\n (data) => ({ type: 'ble.read.result', id, data } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.write') {\n if (!options.write) {\n send(unsupported('ble.write.result', id));\n return;\n }\n const bleMessage = message as BleWriteMessage;\n settle(\n () => options.write!(bleMessage.sessionId, bleMessage.target, bleMessage.data, bleMessage.options, context),\n send,\n (error) => ({ type: 'ble.write.result', id, error } as NappletMessage),\n () => ({ type: 'ble.write.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.subscribe') {\n if (!options.subscribe) {\n send(unsupported('ble.subscribe.result', id));\n return;\n }\n const bleMessage = message as BleSubscribeMessage;\n settle(\n () => options.subscribe!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.subscribe.result', id, error } as NappletMessage),\n () => ({ type: 'ble.subscribe.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.unsubscribe') {\n if (!options.unsubscribe) {\n send(unsupported('ble.unsubscribe.result', id));\n return;\n }\n const bleMessage = message as BleUnsubscribeMessage;\n settle(\n () => options.unsubscribe!(bleMessage.sessionId, bleMessage.target, context),\n send,\n (error) => ({ type: 'ble.unsubscribe.result', id, error } as NappletMessage),\n () => ({ type: 'ble.unsubscribe.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'ble.close') {\n if (!options.close) {\n send(unsupported('ble.close.result', id));\n return;\n }\n const bleMessage = message as BleCloseMessage;\n settle(\n () => options.close!(bleMessage.sessionId, bleMessage.reason, context),\n send,\n (error) => ({ type: 'ble.close.result', id, error } as NappletMessage),\n () => ({ type: 'ble.close.result', id } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown ble method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * webrtc-service.ts — NAP-WEBRTC reference service.\n *\n * Shell-mediated WebRTC sessions. The napplet requests `webrtc.*`; the shell\n * owns signaling, signing/encryption, SDP, ICE, peer connections, and policy.\n */\n\nimport type {\n NappletMessage,\n WebrtcEvent,\n WebrtcOpenRequest,\n WebrtcOpenResult,\n} from '@napplet/core';\nimport type {\n WebrtcCloseMessage,\n WebrtcOpenMessage,\n WebrtcSendMessage,\n} from '@napplet/nap/webrtc/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\nconst WEBRTC_SERVICE_VERSION = '1.0.0';\n\n/** Context passed to host-provided NAP-WEBRTC hooks. */\nexport interface WebrtcServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n /** Emit a runtime-owned WebRTC event back to the requesting napplet. */\n emit(event: WebrtcEvent): void;\n}\n\n/** Options for {@link createWebrtcService}. */\nexport interface WebrtcServiceOptions {\n /** Host-owned WebRTC open hook. */\n open?: (\n request: WebrtcOpenRequest,\n context: WebrtcServiceContext,\n ) => WebrtcOpenResult | Promise<WebrtcOpenResult>;\n /** Host-owned WebRTC send hook. */\n send?: (\n sessionId: string,\n payload: unknown,\n context: WebrtcServiceContext,\n ) => void | Promise<void>;\n /** Host-owned WebRTC close hook. */\n close?: (\n sessionId: string,\n reason: string | undefined,\n context: WebrtcServiceContext,\n ) => void | Promise<void>;\n /** Optional host cleanup when a napplet window is destroyed. */\n destroyWindow?: (windowId: string) => void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst WEBRTC_DESCRIPTOR: ServiceDescriptor = {\n name: 'webrtc',\n version: WEBRTC_SERVICE_VERSION,\n description: 'NAP-WEBRTC reference handler for shell-mediated WebRTC sessions',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'webrtc request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'webrtc request failed'))));\n}\n\nfunction unsupported(resultType: string, id: string): NappletMessage {\n return {\n type: resultType,\n id,\n error: `${resultType.replace('.result', '')} unavailable`,\n } as NappletMessage;\n}\n\nfunction createContext(windowId: string, send: Send): WebrtcServiceContext {\n return {\n windowId,\n emit(event) {\n send({ type: 'webrtc.event', event } as NappletMessage);\n },\n };\n}\n\n/**\n * Create the NAP-WEBRTC reference service.\n *\n * @param options - Host WebRTC session hooks for webrtc.* requests.\n * @returns A runtime service handler for the `webrtc` domain.\n */\nexport function createWebrtcService(options: WebrtcServiceOptions = {}): ServiceHandler {\n return {\n descriptor: WEBRTC_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = createContext(windowId, send);\n\n if (message.type === 'webrtc.open') {\n if (!options.open) {\n send(unsupported('webrtc.open.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcOpenMessage;\n settle(\n () => options.open!(webrtcMessage.request, context),\n send,\n (error) => ({ type: 'webrtc.open.result', id, error } as NappletMessage),\n (result) => ({ type: 'webrtc.open.result', id, session: result.session } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'webrtc.send') {\n if (!options.send) {\n send(unsupported('webrtc.send.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcSendMessage;\n settle(\n () => options.send!(webrtcMessage.sessionId, webrtcMessage.payload, context),\n send,\n (error) => ({ type: 'webrtc.send.result', id, error } as NappletMessage),\n () => ({ type: 'webrtc.send.result', id } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'webrtc.close') {\n if (!options.close) {\n send(unsupported('webrtc.close.result', id));\n return;\n }\n const webrtcMessage = message as WebrtcCloseMessage;\n settle(\n () => options.close!(webrtcMessage.sessionId, webrtcMessage.reason, context),\n send,\n (error) => ({ type: 'webrtc.close.result', id, error } as NappletMessage),\n () => ({ type: 'webrtc.close.result', id } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown webrtc method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(windowId: string): void {\n options.destroyWindow?.(windowId);\n },\n };\n}\n","/**\n * common-service.ts — NAP-COMMON reference service.\n *\n * Shell-mediated public NIP-19 helpers and common social actions. The napplet\n * requests `common.*`; the shell owns identity, consent, event construction,\n * signing, publishing, relay access, and NIP-19 handling.\n */\n\nimport type {\n CommonActionResult,\n CommonFollowsResult,\n CommonNip19DecodeResult,\n CommonNip19EncodeInput,\n CommonNip19EncodeResult,\n CommonProfileResult,\n CommonProfileTarget,\n CommonReaction,\n CommonReportReason,\n CommonReportTarget,\n NappletMessage,\n} from '@napplet/core';\nimport type {\n CommonDecodeNip19Message,\n CommonEncodeNip19Message,\n CommonFollowMessage,\n CommonGetProfileMessage,\n CommonReactMessage,\n CommonReportMessage,\n CommonUnfollowMessage,\n} from '@napplet/nap/common/types';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport {\n decode,\n encodeBytes,\n naddrEncode,\n neventEncode,\n noteEncode,\n nprofileEncode,\n npubEncode,\n} from 'nostr-tools/nip19';\n\nconst COMMON_SERVICE_VERSION = '1.0.0';\nconst BECH32_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';\n\n/** Context passed to host-provided NAP-COMMON hooks. */\nexport interface CommonServiceContext {\n /** Window id of the requesting napplet. */\n windowId: string;\n}\n\n/** Options for {@link createCommonService}. */\nexport interface CommonServiceOptions {\n /** Host-owned profile lookup. */\n getProfile?: (target: CommonProfileTarget, context: CommonServiceContext) => CommonProfileResult | Promise<CommonProfileResult>;\n /** Host-owned current-user follows lookup. */\n follows?: (context: CommonServiceContext) => CommonFollowsResult | Promise<CommonFollowsResult>;\n /** Host-owned follow action. */\n follow?: (pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned unfollow action. */\n unfollow?: (pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned reaction action. */\n react?: (\n targetEventId: string,\n reaction: CommonReaction,\n customEmojiHref: string | undefined,\n context: CommonServiceContext,\n ) => CommonActionResult | Promise<CommonActionResult>;\n /** Host-owned report action. */\n report?: (\n target: CommonReportTarget,\n reason: CommonReportReason,\n text: string,\n context: CommonServiceContext,\n ) => CommonActionResult | Promise<CommonActionResult>;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst COMMON_DESCRIPTOR: ServiceDescriptor = {\n name: 'common',\n version: COMMON_SERVICE_VERSION,\n description: 'NAP-COMMON reference handler for shell-mediated social helpers',\n};\n\nfunction errorMessage(err: unknown, fallback: string): string {\n if (err instanceof Error) return err.message;\n if (typeof err === 'string') return err;\n return fallback;\n}\n\nfunction encodeNip19(input: CommonNip19EncodeInput): CommonNip19EncodeResult {\n try {\n switch (input.type) {\n case 'npub':\n return { ok: true, value: npubEncode(input.hex), nip19Type: input.type };\n case 'note':\n return { ok: true, value: noteEncode(input.hex), nip19Type: input.type };\n case 'nprofile':\n return { ok: true, value: nprofileEncode({ pubkey: input.pubkey, relays: input.relays }), nip19Type: input.type };\n case 'nevent':\n return {\n ok: true,\n value: neventEncode({ id: input.eventId, relays: input.relays, author: input.author, kind: input.kind }),\n nip19Type: input.type,\n };\n case 'naddr':\n return {\n ok: true,\n value: naddrEncode({ identifier: input.identifier, pubkey: input.pubkey, kind: input.kind, relays: input.relays }),\n nip19Type: input.type,\n };\n case 'nrelay':\n return { ok: true, value: encodeBytes('nrelay', new TextEncoder().encode(input.relay)), nip19Type: input.type };\n default:\n return { ok: false, error: 'unsupported NIP-19 type' };\n }\n } catch (err) {\n return { ok: false, error: errorMessage(err, 'NIP-19 encode failed') };\n }\n}\n\nfunction decodeNip19Value(value: string): CommonNip19DecodeResult {\n try {\n const decoded = decode(value);\n switch (decoded.type) {\n case 'npub':\n case 'note':\n return { ok: true, nip19Type: decoded.type, hex: decoded.data };\n case 'nprofile':\n return { ok: true, nip19Type: decoded.type, pubkey: decoded.data.pubkey, relays: decoded.data.relays };\n case 'nevent':\n return {\n ok: true,\n nip19Type: decoded.type,\n eventId: decoded.data.id,\n relays: decoded.data.relays,\n author: decoded.data.author,\n kind: decoded.data.kind,\n };\n case 'naddr':\n return {\n ok: true,\n nip19Type: decoded.type,\n identifier: decoded.data.identifier,\n pubkey: decoded.data.pubkey,\n kind: decoded.data.kind,\n relays: decoded.data.relays,\n };\n default:\n return { ok: false, error: 'unsupported NIP-19 type' };\n }\n } catch (err) {\n if (value.startsWith('nrelay1')) return decodeNrelay(value);\n return { ok: false, error: errorMessage(err, 'NIP-19 decode failed') };\n }\n}\n\nfunction decodeNrelay(value: string): CommonNip19DecodeResult {\n try {\n const separator = value.lastIndexOf('1');\n if (separator <= 0 || separator + 7 > value.length) return { ok: false, error: 'invalid nrelay value' };\n const dataPart = value.slice(separator + 1);\n const words = [...dataPart].map((char) => {\n const index = BECH32_CHARSET.indexOf(char);\n if (index === -1) throw new Error('invalid nrelay character');\n return index;\n });\n const payloadWords = words.slice(0, -6);\n const bytes = convertBits(payloadWords, 5, 8, false);\n return { ok: true, nip19Type: 'nrelay', relay: new TextDecoder().decode(Uint8Array.from(bytes)) };\n } catch (err) {\n return { ok: false, error: errorMessage(err, 'invalid nrelay value') };\n }\n}\n\nfunction convertBits(data: readonly number[], fromBits: number, toBits: number, pad: boolean): number[] {\n let acc = 0;\n let bits = 0;\n const result: number[] = [];\n const maxv = (1 << toBits) - 1;\n const maxAcc = (1 << (fromBits + toBits - 1)) - 1;\n for (const value of data) {\n if (value < 0 || value >> fromBits !== 0) throw new Error('invalid bech32 data');\n acc = ((acc << fromBits) | value) & maxAcc;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n result.push((acc >> bits) & maxv);\n }\n }\n if (pad) {\n if (bits > 0) result.push((acc << (toBits - bits)) & maxv);\n } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) !== 0) {\n throw new Error('invalid bech32 padding');\n }\n return result;\n}\n\nfunction settle<T>(\n call: () => T | Promise<T>,\n send: Send,\n okFalse: (error: string) => NappletMessage,\n onValue: (value: T) => NappletMessage,\n): void {\n let pending: Promise<T>;\n try {\n pending = Promise.resolve(call());\n } catch (err) {\n send(okFalse(errorMessage(err, 'common request failed')));\n return;\n }\n pending\n .then((value) => send(onValue(value)))\n .catch((err) => send(okFalse(errorMessage(err, 'common request failed'))));\n}\n\n/**\n * Create the NAP-COMMON reference service.\n *\n * @param options - Host profile/social hooks for common.* requests.\n * @returns A runtime service handler for the `common` domain.\n */\nexport function createCommonService(options: CommonServiceOptions = {}): ServiceHandler {\n return {\n descriptor: COMMON_DESCRIPTOR,\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const context = { windowId };\n\n if (message.type === 'common.encodeNip19') {\n const input = (message as CommonEncodeNip19Message).input;\n const result = input ? encodeNip19(input) : { ok: false, error: 'invalid input' };\n send({ type: 'common.encodeNip19.result', id, ...result } as NappletMessage);\n return;\n }\n\n if (message.type === 'common.decodeNip19') {\n const value = (message as CommonDecodeNip19Message).value;\n const result = typeof value === 'string' ? decodeNip19Value(value) : { ok: false, error: 'invalid value' };\n send({ type: 'common.decodeNip19.result', id, ...result } as NappletMessage);\n return;\n }\n\n if (message.type === 'common.getProfile') {\n const target = (message as CommonGetProfileMessage).target;\n if (!options.getProfile) {\n send({ type: 'common.getProfile.result', id, ok: false, pubkey: target ?? '', error: 'profile lookup unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.getProfile!(target, context),\n send,\n (error) => ({ type: 'common.getProfile.result', id, ok: false, pubkey: target ?? '', error } as NappletMessage),\n (result) => ({ type: 'common.getProfile.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.follows') {\n if (!options.follows) {\n send({ type: 'common.follows.result', id, ok: false, pubkeys: [], error: 'follows lookup unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.follows!(context),\n send,\n (error) => ({ type: 'common.follows.result', id, ok: false, pubkeys: [], error } as NappletMessage),\n (result) => ({ type: 'common.follows.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.follow') {\n const pubkeys = (message as CommonFollowMessage).pubkeys ?? [];\n settleAction(options.follow, pubkeys, context, send, 'common.follow.result', id, 'follow unavailable');\n return;\n }\n\n if (message.type === 'common.unfollow') {\n const pubkeys = (message as CommonUnfollowMessage).pubkeys ?? [];\n settleAction(options.unfollow, pubkeys, context, send, 'common.unfollow.result', id, 'unfollow unavailable');\n return;\n }\n\n if (message.type === 'common.react') {\n const m = message as CommonReactMessage;\n if (!options.react) {\n send({ type: 'common.react.result', id, ok: false, error: 'react unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.react!(m.targetEventId, m.reaction, m.customEmojiHref, context),\n send,\n (error) => ({ type: 'common.react.result', id, ok: false, error } as NappletMessage),\n (result) => ({ type: 'common.react.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n if (message.type === 'common.report') {\n const m = message as CommonReportMessage;\n if (!options.report) {\n send({ type: 'common.report.result', id, ok: false, error: 'report unavailable' } as NappletMessage);\n return;\n }\n settle(\n () => options.report!(m.target, m.reason, m.text, context),\n send,\n (error) => ({ type: 'common.report.result', id, ok: false, error } as NappletMessage),\n (result) => ({ type: 'common.report.result', id, ...result } as NappletMessage),\n );\n return;\n }\n\n send({\n type: `${message.type}.error`,\n id,\n error: `Unknown common method: ${message.type}`,\n } as NappletMessage);\n },\n onWindowDestroyed(_windowId: string): void {\n /* no per-window state */\n },\n };\n}\n\nfunction settleAction(\n action: ((pubkeys: string[], context: CommonServiceContext) => CommonActionResult | Promise<CommonActionResult>) | undefined,\n pubkeys: string[],\n context: CommonServiceContext,\n send: Send,\n resultType: string,\n id: string,\n unavailable: string,\n): void {\n if (!action) {\n send({ type: resultType, id, ok: false, error: unavailable } as NappletMessage);\n return;\n }\n settle(\n () => action(pubkeys, context),\n send,\n (error) => ({ type: resultType, id, ok: false, error } as NappletMessage),\n (result) => ({ type: resultType, id, ...result } as NappletMessage),\n );\n}\n","/**\n * dm-service.ts -- NAP-DM reference service.\n *\n * Routes `dm.*` envelopes to an injected adapter. The service owns request\n * correlation, per-window live subscription fan-out, and lifecycle cleanup;\n * adapters own protocol-specific transport/encryption/storage behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\nimport type {\n DmAdapter,\n DmConversationsMessage,\n DmMessagesMessage,\n DmSendMessage,\n DmSubscribeMessage,\n DmUnsubscribeMessage,\n} from './dm-types.js';\n\n/** Options for {@link createDmService}. */\nexport interface DmServiceOptions {\n /** Adapter that implements one or more chat backends under NAP-DM. */\n adapter: DmAdapter;\n}\n\n/** Created DM service. */\nexport interface DmService extends ServiceHandler {\n /** Close adapter resources. Idempotent when adapter close is idempotent. */\n dispose(): void;\n}\n\ntype Send = (msg: NappletMessage) => void;\n\nconst DM_DESCRIPTOR: ServiceDescriptor = {\n name: 'dm',\n version: '1.0.0',\n description: 'NAP-DM runtime-mediated direct messages',\n};\n\nfunction err(error: unknown, fallback = 'dm request failed'): string {\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n return fallback;\n}\n\n/**\n * Create a NAP-DM service handler.\n *\n * @param options - DM adapter to route requests through.\n * @returns Service handler for `runtime.registerService('dm', handler)`.\n */\nexport function createDmService(options: DmServiceOptions): DmService {\n if (!options || typeof options.adapter !== 'object' || options.adapter === null) {\n throw new Error('createDmService: options.adapter is required');\n }\n const { adapter } = options;\n const ownedSubscriptions = new Map<string, Set<string>>();\n\n function remember(windowId: string, subscriptionId: string): void {\n let subs = ownedSubscriptions.get(windowId);\n if (!subs) {\n subs = new Set();\n ownedSubscriptions.set(windowId, subs);\n }\n subs.add(subscriptionId);\n }\n\n function forget(windowId: string, subscriptionId: string): void {\n const subs = ownedSubscriptions.get(windowId);\n if (!subs) return;\n subs.delete(subscriptionId);\n if (subs.size === 0) ownedSubscriptions.delete(windowId);\n }\n\n return {\n descriptor: DM_DESCRIPTOR,\n\n handleMessage(windowId: string, message: NappletMessage, send: Send): void {\n const id = (message as { id?: unknown }).id;\n const requestId = typeof id === 'string' ? id : '';\n\n switch (message.type) {\n case 'dm.status':\n void Promise.resolve().then(() => adapter.status())\n .then((status) => send({ type: 'dm.status.result', id: requestId, ...status } as NappletMessage))\n .catch((error) => send({ type: 'dm.status.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n\n case 'dm.conversations':\n void Promise.resolve().then(() => adapter.conversations(message as DmConversationsMessage))\n .then((page) => send({ type: 'dm.conversations.result', id: requestId, ...page } as NappletMessage))\n .catch((error) =>\n send({ type: 'dm.conversations.result', id: requestId, error: err(error) } as NappletMessage),\n );\n return;\n\n case 'dm.messages':\n void Promise.resolve().then(() => adapter.messages(message as DmMessagesMessage))\n .then((page) => send({ type: 'dm.messages.result', id: requestId, ...page } as NappletMessage))\n .catch((error) =>\n send({ type: 'dm.messages.result', id: requestId, error: err(error) } as NappletMessage),\n );\n return;\n\n case 'dm.send':\n void Promise.resolve()\n .then(() => adapter.send(message as DmSendMessage))\n .then((result) => send({ type: 'dm.send.result', id: requestId, ...result } as NappletMessage))\n .catch((error) => send({ type: 'dm.send.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n\n case 'dm.subscribe': {\n let activeSubscriptionId = '';\n const pending: Parameters<Parameters<DmAdapter['subscribe']>[1]>[0][] = [];\n void Promise.resolve().then(() =>\n adapter.subscribe(message as DmSubscribeMessage, (dmMessage) => {\n if (activeSubscriptionId.length > 0) {\n send({ type: 'dm.message', subscriptionId: activeSubscriptionId, message: dmMessage } as NappletMessage);\n } else {\n pending.push(dmMessage);\n }\n }),\n )\n .then((subscription) => {\n remember(windowId, subscription.subscriptionId);\n activeSubscriptionId = subscription.subscriptionId;\n send({ type: 'dm.subscribe.result', id: requestId, ...subscription } as NappletMessage);\n for (const dmMessage of pending) {\n send({ type: 'dm.message', subscriptionId: activeSubscriptionId, message: dmMessage } as NappletMessage);\n }\n pending.length = 0;\n })\n .catch((error) => {\n send({ type: 'dm.subscribe.result', id: requestId, error: err(error) } as NappletMessage);\n });\n return;\n }\n\n case 'dm.unsubscribe': {\n const subId = (message as DmUnsubscribeMessage).subscriptionId;\n if (\n typeof subId !== 'string'\n || subId.length === 0\n || ownedSubscriptions.get(windowId)?.has(subId) !== true\n ) {\n send({ type: 'dm.unsubscribe.result', id: requestId, error: 'subscription not found' } as NappletMessage);\n return;\n }\n void Promise.resolve().then(() => adapter.unsubscribe(subId))\n .then((ok) => {\n forget(windowId, subId);\n send({ type: 'dm.unsubscribe.result', id: requestId, ...ok } as NappletMessage);\n })\n .catch((error) => send({ type: 'dm.unsubscribe.result', id: requestId, error: err(error) } as NappletMessage));\n return;\n }\n\n default:\n return;\n }\n },\n\n onWindowDestroyed(windowId: string): void {\n const subs = ownedSubscriptions.get(windowId);\n if (!subs) return;\n for (const subId of subs) void Promise.resolve(adapter.unsubscribe(subId)).catch(() => undefined);\n ownedSubscriptions.delete(windowId);\n },\n\n dispose(): void {\n adapter.close?.();\n ownedSubscriptions.clear();\n },\n };\n}\n","/**\n * dm-nip17-adapter.ts -- concrete NIP-17 adapter for NAP-DM.\n *\n * Uses nostr-tools NIP-17 gift wrapping and an injected relay pool. The owner\n * key is shell/runtime-owned; napplets only see normalized cleartext messages.\n */\n\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\nimport * as nip44 from 'nostr-tools/nip44';\nimport {\n getEventHash,\n getPublicKey,\n validateEvent,\n verifyEvent,\n} from 'nostr-tools/pure';\nimport * as nip17 from 'nostr-tools/nip17';\nimport type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\n\nconst NIP17_GIFT_WRAP_KIND = 1059;\nconst NIP59_SEAL_KIND = 13;\nlet subscriptionCounter = 0;\n\n/** Options for {@link createNip17DmAdapter}. */\nexport interface Nip17DmAdapterOptions {\n /** Shell-owned secret key used for NIP-17 seal creation/unwrapping. */\n ownerSecretKey: Uint8Array;\n /** Relay pool used for publish/subscribe. */\n relayPool: DmRelayPool;\n /** Default relays for NIP-17 DM traffic. */\n relays?: string[];\n /** Optional normalized message store. Defaults to an in-memory store. */\n store?: DmMemoryStore;\n /** Runtime policy invoked once before encrypted relay publication. */\n authorizeSend?(request: DmSendRequest): boolean | Promise<boolean>;\n}\n\ninterface LiveSub {\n handle?: { unsubscribe(): void };\n conversationId?: string;\n onMessage(message: DmMessage): void;\n}\n\nfunction tagsFor(tags: string[][], name: string): string[] {\n return tags.filter((tag) => tag[0] === name && typeof tag[1] === 'string').map((tag) => tag[1]);\n}\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nclass Nip17DmRuntime {\n private readonly ownerPubkey: string;\n private readonly store: DmMemoryStore;\n private readonly live = new Map<string, LiveSub>();\n private historyLoaded = false;\n private historyLoading: Promise<void> | null = null;\n\n constructor(private readonly options: Nip17DmAdapterOptions) {\n this.ownerPubkey = getPublicKey(options.ownerSecretKey);\n this.store = options.store ?? new DmMemoryStore();\n }\n\n adapter(): DmAdapter {\n return {\n status: () => this.status(),\n conversations: (query) => this.conversations(query),\n messages: (query) => this.messages(query),\n send: (request) => this.send(request),\n subscribe: (request, onMessage) => this.subscribe(request, onMessage),\n unsubscribe: (subscriptionId) => this.unsubscribe(subscriptionId),\n close: () => this.close(),\n };\n }\n\n private relaysFor(filters: NostrFilter[]): string[] {\n return this.options.relays && this.options.relays.length > 0\n ? this.options.relays\n : this.options.relayPool.selectRelayTier(filters);\n }\n\n private normalizeRumor(rumor: NostrEvent, status: DmMessage['status']): DmMessage | null {\n if (rumor.kind !== 14) return null;\n const participants = [...new Set([this.ownerPubkey, rumor.pubkey, ...tagsFor(rumor.tags, 'p')])];\n const conversationId = this.store.conversationIdFor(participants);\n return this.store.upsertMessage({\n id: rumor.id,\n conversationId,\n senderPubkey: rumor.pubkey,\n createdAt: rumor.created_at,\n content: rumor.content,\n status,\n }, participants);\n }\n\n private decryptEvent(event: NostrEvent): unknown {\n return JSON.parse(nip44.decrypt(\n event.content,\n nip44.getConversationKey(this.options.ownerSecretKey, event.pubkey),\n ));\n }\n\n private unwrapVerified(wrap: NostrEvent): NostrEvent | null {\n if (\n wrap.kind !== NIP17_GIFT_WRAP_KIND\n || !verifyEvent(wrap)\n || !wrap.tags.some((tag) => tag[0] === 'p' && tag[1] === this.ownerPubkey)\n ) return null;\n try {\n const seal = this.decryptEvent(wrap);\n if (\n !validateEvent(seal)\n || typeof (seal as { id?: unknown }).id !== 'string'\n || typeof (seal as { sig?: unknown }).sig !== 'string'\n || seal.kind !== NIP59_SEAL_KIND\n ) return null;\n const signedSeal = seal as NostrEvent;\n if (!verifyEvent(signedSeal)) return null;\n const rumor = this.decryptEvent(signedSeal);\n if (!validateEvent(rumor)) return null;\n const candidate = rumor as NostrEvent;\n if (\n candidate.kind !== 14\n || candidate.pubkey !== signedSeal.pubkey\n || typeof candidate.id !== 'string'\n || candidate.id !== getEventHash(candidate)\n ) return null;\n return candidate;\n } catch {\n return null;\n }\n }\n\n private ingestWrap(wrap: NostrEvent, status: DmMessage['status']): DmMessage | null {\n const rumor = this.unwrapVerified(wrap);\n return rumor ? this.normalizeRumor(rumor, status) : null;\n }\n\n private async loadHistory(): Promise<void> {\n if (this.historyLoaded || !this.options.relayPool.query) return;\n this.historyLoading ??= (async () => {\n const filters = [{ kinds: [NIP17_GIFT_WRAP_KIND], '#p': [this.ownerPubkey], limit: 1_000 }] as NostrFilter[];\n const wraps = await this.options.relayPool.query!(filters, this.relaysFor(filters));\n for (const wrap of wraps) this.ingestWrap(wrap, 'received');\n this.historyLoaded = true;\n })().finally(() => { this.historyLoading = null; });\n await this.historyLoading;\n }\n\n private status(): DmStatus {\n return {\n available: this.options.relayPool.isAvailable(),\n ownerPubkey: this.ownerPubkey,\n implementations: ['nip17'],\n capabilities: ['send', 'receive', 'subscribe', 'history'],\n };\n }\n\n private async conversations(query?: DmConversationQuery): Promise<DmConversationPage> {\n await this.loadHistory();\n return this.store.conversations(query);\n }\n\n private async messages(query: DmMessageQuery): Promise<DmMessagePage> {\n await this.loadHistory();\n return this.store.messages(query);\n }\n\n private async send(request: DmSendRequest): Promise<DmSendResult> {\n if (!this.options.relayPool.isAvailable()) throw new Error('relay unavailable');\n if (!Array.isArray(request.recipients) || request.recipients.length === 0) throw new Error('invalid recipient');\n if (request.recipients.some((recipient) => !/^[0-9a-f]{64}$/.test(recipient))) throw new Error('invalid recipient');\n if (typeof request.content !== 'string' || request.content.length === 0) throw new Error('content required');\n if (this.options.authorizeSend && !await this.options.authorizeSend(request)) throw new Error('forbidden');\n const recipients = [...new Set(request.recipients)];\n const wraps = nip17.wrapManyEvents(\n this.options.ownerSecretKey,\n recipients.map((publicKey) => ({ publicKey })),\n request.content,\n ) as NostrEvent[];\n for (const wrap of wraps) await Promise.resolve(this.options.relayPool.publish(wrap));\n const conversationId = request.conversationId ?? this.store.conversationIdFor([this.ownerPubkey, ...recipients]);\n const message = this.store.upsertMessage({\n id: request.clientMessageId ?? wraps[0]?.id ?? `dm-${nowSeconds()}`,\n conversationId,\n senderPubkey: this.ownerPubkey,\n createdAt: nowSeconds(),\n content: request.content,\n status: 'sent',\n }, [this.ownerPubkey, ...recipients]);\n return { ok: true, message };\n }\n\n private async subscribe(\n request: DmSubscribeRequest,\n onMessage: (message: DmMessage) => void,\n ): Promise<DmSubscription> {\n if (!this.options.relayPool.isAvailable()) throw new Error('relay unavailable');\n await this.loadHistory();\n const subscriptionId = `dm-nip17-${++subscriptionCounter}`;\n const filters = [{ kinds: [NIP17_GIFT_WRAP_KIND], '#p': [this.ownerPubkey] }] as NostrFilter[];\n const sub: LiveSub = { conversationId: request.conversationId, onMessage };\n this.live.set(subscriptionId, sub);\n try {\n const handle = this.options.relayPool.subscribe(filters, (item) => {\n if (item === 'EOSE') return;\n const message = this.ingestWrap(item, 'received');\n if (!message) return;\n const active = this.live.get(subscriptionId);\n if (active && (!active.conversationId || active.conversationId === message.conversationId)) active.onMessage(message);\n }, this.relaysFor(filters));\n if (this.live.get(subscriptionId) !== sub) {\n handle.unsubscribe();\n throw new Error('subscription cancelled');\n }\n sub.handle = handle;\n return { subscriptionId };\n } catch (error) {\n this.live.delete(subscriptionId);\n throw error;\n }\n }\n\n private unsubscribe(subscriptionId: string): { ok: boolean } {\n const sub = this.live.get(subscriptionId);\n if (!sub) return { ok: false };\n sub.handle?.unsubscribe();\n this.live.delete(subscriptionId);\n return { ok: true };\n }\n\n private close(): void {\n for (const sub of this.live.values()) sub.handle?.unsubscribe();\n this.live.clear();\n }\n}\n\n/**\n * Create a concrete NIP-17 NAP-DM adapter.\n *\n * @param options - Owner key, relay pool, relay hints, optional store.\n * @returns DM adapter for {@link createDmService}.\n */\nexport function createNip17DmAdapter(options: Nip17DmAdapterOptions): DmAdapter {\n return new Nip17DmRuntime(options).adapter();\n}\n","import type {\n DmConversation,\n DmConversationPage,\n DmConversationQuery,\n DmHexPubkey,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmPeer,\n} from './dm-types.js';\n\n/** In-memory normalized DM store. */\nexport class DmMemoryStore {\n private readonly messagesByConversation = new Map<string, DmMessage[]>();\n private readonly participantsByConversation = new Map<string, DmPeer[]>();\n\n conversationIdFor(participants: readonly DmHexPubkey[]): string {\n return `direct:${[...new Set(participants)].sort().join(',')}`;\n }\n\n upsertMessage(message: DmMessage, participants: readonly DmHexPubkey[]): DmMessage {\n const peers = [...new Set(participants)].sort().map((pubkey) => ({ pubkey }));\n this.participantsByConversation.set(message.conversationId, peers);\n const list = this.messagesByConversation.get(message.conversationId) ?? [];\n const existing = list.findIndex((entry) => entry.id === message.id);\n if (existing >= 0) list[existing] = message;\n else list.push(message);\n list.sort((a, b) => a.createdAt - b.createdAt);\n this.messagesByConversation.set(message.conversationId, list);\n return message;\n }\n\n conversations(query: DmConversationQuery = {}): DmConversationPage {\n const offset = query.cursor ? Number.parseInt(query.cursor, 10) || 0 : 0;\n const limit = query.limit ?? 50;\n const conversations: DmConversation[] = [...this.messagesByConversation.entries()]\n .map(([id, messages]) => {\n const latest = messages[messages.length - 1];\n return {\n id,\n kind: id.startsWith('group:') ? 'group' : 'direct',\n participants: this.participantsByConversation.get(id) ?? [],\n unread: messages.filter((message) => message.status === 'received').length,\n ...(latest ? { updatedAt: latest.createdAt } : {}),\n } satisfies DmConversation;\n })\n .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));\n const page = conversations.slice(offset, offset + limit);\n const next = offset + page.length < conversations.length ? String(offset + page.length) : undefined;\n return { conversations: page, ...(next ? { cursor: next } : {}) };\n }\n\n messages(query: DmMessageQuery): DmMessagePage {\n const offset = query.cursor ? Number.parseInt(query.cursor, 10) || 0 : 0;\n const limit = query.limit ?? 100;\n const all = this.messagesByConversation.get(query.conversationId) ?? [];\n const page = all.slice(offset, offset + limit);\n const next = offset + page.length < all.length ? String(offset + page.length) : undefined;\n return { messages: page, ...(next ? { cursor: next } : {}) };\n }\n}\n","/**\n * dm-ndr-adapter.ts -- Nostr Double Ratchet adapter for NAP-DM.\n *\n * Kehto accepts a structural NDR runtime instead of depending directly on the\n * moving nostr-double-ratchet package API. Hosts can pass NdrRuntime instances.\n */\n\nimport type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\nimport { finalizeEvent } from 'nostr-tools/pure';\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\n\n/** Minimal event shape returned by NDR runtimes for sent or received messages. */\nexport interface NdrRumorLike {\n id: string;\n pubkey?: string;\n created_at?: number;\n content?: string;\n}\n\n/** Minimal structural subset of nostr-double-ratchet NdrRuntime. */\nexport interface NdrRuntimeLike {\n getState?(): { ownerPubkey?: string | null; currentDevicePubkey?: string | null };\n initForOwner?(ownerPubkey: string): Promise<unknown>;\n setupUser?(pubkey: string, ownerPubkey?: string): Promise<void>;\n sendMessage(recipientPubkey: string, content: string, options?: unknown, ownerPubkey?: string): Promise<NdrRumorLike>;\n onSessionEvent(callback: (event: NdrRumorLike, from: string, meta?: unknown) => void): () => void;\n close?(): void;\n}\n\nexport interface NdrRelayTransport {\n nostrSubscribe(filter: NostrFilter, onEvent: (event: NostrEvent) => void): () => void;\n nostrFetch(filter: NostrFilter): Promise<NostrEvent[]>;\n nostrPublish(event: Partial<NostrEvent>, innerEventId?: string): Promise<NostrEvent>;\n}\n\nexport interface NdrRelayTransportOptions {\n relayPool: DmRelayPool;\n publishSecretKey?: Uint8Array;\n relays?: string[];\n fetchTimeoutMs?: number;\n}\n\n/** Options for {@link createNdrDmAdapter}. */\nexport interface NdrDmAdapterOptions {\n runtime: NdrRuntimeLike;\n ownerPubkey: string;\n store?: DmMemoryStore;\n available?: boolean;\n}\n\nlet ndrSubCounter = 0;\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nfunction relaysFor(options: NdrRelayTransportOptions, filter: NostrFilter): string[] {\n return options.relays && options.relays.length > 0\n ? options.relays\n : options.relayPool.selectRelayTier([filter]);\n}\n\nfunction signedEvent(event: Partial<NostrEvent>, secretKey?: Uint8Array): NostrEvent {\n if (\n typeof event.id === 'string' &&\n typeof event.pubkey === 'string' &&\n typeof event.sig === 'string' &&\n typeof event.kind === 'number' &&\n typeof event.created_at === 'number' &&\n Array.isArray(event.tags) &&\n typeof event.content === 'string'\n ) {\n return event as NostrEvent;\n }\n if (!secretKey) throw new Error('publishSecretKey required for unsigned NDR events');\n return finalizeEvent(\n {\n kind: event.kind ?? 1060,\n created_at: event.created_at ?? nowSeconds(),\n tags: event.tags ?? [],\n content: event.content ?? '',\n },\n secretKey,\n ) as NostrEvent;\n}\n\n/** Build the transport contract expected by nostr-double-ratchet over Kehto relay service hooks. */\nexport function createNdrRelayTransport(options: NdrRelayTransportOptions): NdrRelayTransport {\n return {\n nostrSubscribe(filter, onEvent) {\n const handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item !== 'EOSE') onEvent(item);\n },\n relaysFor(options, filter),\n );\n return () => handle.unsubscribe();\n },\n\n nostrFetch(filter) {\n const events: NostrEvent[] = [];\n return new Promise((resolve) => {\n let done = false;\n let handle: { unsubscribe(): void } | undefined;\n const finish = () => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n handle?.unsubscribe();\n resolve(events);\n };\n const timer = setTimeout(finish, options.fetchTimeoutMs ?? 500);\n handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') finish();\n else events.push(item);\n },\n relaysFor(options, filter),\n );\n });\n },\n\n async nostrPublish(event) {\n const signed = signedEvent(event, options.publishSecretKey);\n await Promise.resolve(options.relayPool.publish(signed));\n return signed;\n },\n };\n}\n\n/** Create an NDR-backed NAP-DM adapter. */\nexport function createNdrDmAdapter(options: NdrDmAdapterOptions): DmAdapter {\n const store = options.store ?? new DmMemoryStore();\n const live = new Map<string, { conversationId?: string; onMessage(message: DmMessage): void }>();\n const detach = options.runtime.onSessionEvent((event, from) => {\n const participants = [options.ownerPubkey, from];\n const conversationId = store.conversationIdFor(participants);\n const message = store.upsertMessage(\n {\n id: event.id,\n conversationId,\n senderPubkey: from,\n createdAt: event.created_at ?? Math.floor(Date.now() / 1000),\n content: event.content ?? '',\n status: 'received',\n },\n participants,\n );\n for (const sub of live.values()) {\n if (!sub.conversationId || sub.conversationId === message.conversationId) sub.onMessage(message);\n }\n });\n\n return {\n status(): DmStatus {\n return {\n available: options.available ?? true,\n ownerPubkey: options.ownerPubkey,\n implementations: ['ndr'],\n capabilities: ['send', 'receive', 'subscribe', 'history', 'double-ratchet'],\n };\n },\n\n conversations(query?: DmConversationQuery): DmConversationPage {\n return store.conversations(query);\n },\n\n messages(query: DmMessageQuery): DmMessagePage {\n return store.messages(query);\n },\n\n async send(request: DmSendRequest): Promise<DmSendResult> {\n const recipient = request.recipients[0];\n if (!recipient) throw new Error('recipient required');\n await options.runtime.setupUser?.(recipient, options.ownerPubkey);\n const rumor = await options.runtime.sendMessage(recipient, request.content, undefined, options.ownerPubkey);\n const message = store.upsertMessage(\n {\n id: request.clientMessageId ?? rumor.id,\n conversationId: request.conversationId ?? store.conversationIdFor([options.ownerPubkey, recipient]),\n senderPubkey: options.ownerPubkey,\n createdAt: rumor.created_at ?? Math.floor(Date.now() / 1000),\n content: request.content,\n status: 'sent',\n },\n [options.ownerPubkey, recipient],\n );\n return { ok: true, message };\n },\n\n subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): DmSubscription {\n const subscriptionId = `dm-ndr-${++ndrSubCounter}`;\n live.set(subscriptionId, { conversationId: request.conversationId, onMessage });\n return { subscriptionId };\n },\n\n unsubscribe(subscriptionId: string) {\n return { ok: live.delete(subscriptionId) };\n },\n\n close(): void {\n live.clear();\n detach();\n options.runtime.close?.();\n },\n };\n}\n","import type {\n DmAdapter,\n DmConversationPage,\n DmConversationQuery,\n DmMessage,\n DmMessagePage,\n DmMessageQuery,\n DmRelayPool,\n DmSendRequest,\n DmSendResult,\n DmStatus,\n DmSubscribeRequest,\n DmSubscription,\n} from './dm-types.js';\nimport { DmMemoryStore } from './dm-memory-store.js';\nimport { finalizeEvent, getPublicKey } from 'nostr-tools/pure';\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\n\ndeclare const btoa: (data: string) => string;\ndeclare const atob: (data: string) => string;\n\n/** Cordn DM client contract. */\nexport interface CordnDmClient {\n status?(): Promise<Partial<DmStatus>> | Partial<DmStatus>;\n conversations(query?: DmConversationQuery): Promise<DmConversationPage> | DmConversationPage;\n messages(query: DmMessageQuery): Promise<DmMessagePage> | DmMessagePage;\n send(request: DmSendRequest): Promise<DmSendResult>;\n subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): Promise<DmSubscription> | DmSubscription;\n unsubscribe(subscriptionId: string): Promise<{ ok: boolean }> | { ok: boolean };\n close?(): void;\n}\n\nexport interface CordnGroupMessage {\n cursor: number;\n gid: string;\n msg_64: string;\n at: number;\n}\n\nexport interface CordnCoordinatorSubscription {\n stream: AsyncIterable<CordnGroupMessage>;\n result: Promise<{ subscribed: true }>;\n abort(reason?: string): Promise<void>;\n}\n\nexport interface CordnCoordinatorClient {\n PostGroupMessage(input: { msg_64: string }): Promise<{ cursor: number; gid: string; at: number }>;\n FetchGroupMessages(input: { gid: string; after?: number }): Promise<{ messages: CordnGroupMessage[] }>;\n SubscribeGroupMessages(input: { gid: string; after?: number }): Promise<CordnCoordinatorSubscription>;\n close?(): void;\n}\n\nexport interface CordnCodecResult {\n msg_64: string;\n gid: string;\n}\n\nexport interface CordnRelayCoordinatorOptions {\n relayPool: DmRelayPool;\n ownerSecretKey: Uint8Array;\n relays?: string[];\n kind?: number;\n fetchTimeoutMs?: number;\n}\n\n/** Options for {@link createCordnDmAdapter}. */\nexport interface CordnDmAdapterOptions {\n client?: CordnDmClient;\n coordinator?: CordnCoordinatorClient;\n ownerPubkey?: string;\n defaultGroupId?: string;\n store?: DmMemoryStore;\n encodeMessage?(request: DmSendRequest, ownerPubkey?: string): CordnCodecResult;\n decodeMessage?(message: CordnGroupMessage, ownerPubkey?: string): DmMessage;\n}\n\nconst CORDN_RELAY_GROUP_MESSAGE_KIND = 30383;\nlet cordnSubCounter = 0;\n\nfunction nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nfunction encodeBase64(value: string): string {\n return btoa(String.fromCharCode(...new TextEncoder().encode(value)));\n}\n\nfunction decodeBase64(value: string): string {\n return new TextDecoder().decode(Uint8Array.from(atob(value), (char) => char.charCodeAt(0)));\n}\n\nfunction groupIdFor(request: DmSendRequest, defaultGroupId?: string): string {\n if (request.conversationId?.startsWith('group:')) return request.conversationId.slice('group:'.length);\n if (request.conversationId) return request.conversationId;\n if (defaultGroupId) return defaultGroupId;\n const firstRecipient = request.recipients[0];\n if (firstRecipient) return firstRecipient;\n throw new Error('group id required');\n}\n\nfunction defaultEncodeMessage(request: DmSendRequest, ownerPubkey?: string, defaultGroupId?: string): CordnCodecResult {\n const gid = groupIdFor(request, defaultGroupId);\n return {\n gid,\n msg_64: encodeBase64(JSON.stringify({\n gid,\n pubkey: ownerPubkey ?? '',\n created_at: nowSeconds(),\n kind: 9,\n tags: [],\n content: request.content,\n })),\n };\n}\n\nfunction defaultDecodeMessage(message: CordnGroupMessage, ownerPubkey?: string): DmMessage {\n let payload: { pubkey?: string; created_at?: number; content?: string } = {};\n try {\n payload = JSON.parse(decodeBase64(message.msg_64)) as typeof payload;\n } catch {\n payload = { content: decodeBase64(message.msg_64) };\n }\n return {\n id: `cordn:${message.gid}:${message.cursor}`,\n conversationId: `group:${message.gid}`,\n senderPubkey: payload.pubkey || ownerPubkey || '',\n createdAt: payload.created_at ?? message.at,\n content: payload.content ?? '',\n status: payload.pubkey && payload.pubkey === ownerPubkey ? 'sent' : 'received',\n };\n}\n\nfunction relayUrls(options: CordnRelayCoordinatorOptions, filter: NostrFilter): string[] {\n return options.relays && options.relays.length > 0\n ? options.relays\n : options.relayPool.selectRelayTier([filter]);\n}\n\nfunction parseRelayGroupMessage(event: NostrEvent): CordnGroupMessage | null {\n try {\n const parsed = JSON.parse(event.content) as CordnGroupMessage;\n if (typeof parsed.gid !== 'string' || typeof parsed.cursor !== 'number' || typeof parsed.msg_64 !== 'string') return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function createCordnRelayCoordinatorClient(options: CordnRelayCoordinatorOptions): CordnCoordinatorClient {\n const ownerPubkey = getPublicKey(options.ownerSecretKey);\n const kind = options.kind ?? CORDN_RELAY_GROUP_MESSAGE_KIND;\n const cursors = new Map<string, number>();\n\n async function fetchGroup(gid: string, after = 0): Promise<CordnGroupMessage[]> {\n const filter = { kinds: [kind], '#g': [gid] } as NostrFilter;\n const records: CordnGroupMessage[] = [];\n await new Promise<void>((resolve) => {\n let done = false;\n let handle: { unsubscribe(): void } | undefined;\n const finish = () => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n handle?.unsubscribe();\n resolve();\n };\n const timer = setTimeout(finish, options.fetchTimeoutMs ?? 500);\n handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') finish();\n else {\n const parsed = parseRelayGroupMessage(item);\n if (parsed && parsed.cursor > after) records.push(parsed);\n }\n },\n relayUrls(options, filter),\n );\n });\n return records.sort((a, b) => a.cursor - b.cursor);\n }\n\n return {\n async PostGroupMessage(input) {\n const decoded = JSON.parse(decodeBase64(input.msg_64)) as { gid?: string };\n const gid = decoded.gid;\n if (!gid) throw new Error('group id required');\n const cursor = (cursors.get(gid) ?? 0) + 1;\n cursors.set(gid, cursor);\n const at = nowSeconds();\n const record: CordnGroupMessage = { cursor, gid, msg_64: input.msg_64, at };\n const event = finalizeEvent(\n {\n kind,\n created_at: at,\n tags: [['g', gid], ['p', ownerPubkey]],\n content: JSON.stringify(record),\n },\n options.ownerSecretKey,\n ) as NostrEvent;\n await Promise.resolve(options.relayPool.publish(event));\n return { cursor, gid, at };\n },\n\n async FetchGroupMessages(input) {\n return { messages: await fetchGroup(input.gid, input.after ?? 0) };\n },\n\n async SubscribeGroupMessages(input) {\n const filter = { kinds: [kind], '#g': [input.gid] } as NostrFilter;\n const queue: CordnGroupMessage[] = [];\n const waiters: Array<(next: IteratorResult<CordnGroupMessage>) => void> = [];\n let closed = false;\n const push = (message: CordnGroupMessage) => {\n if (message.cursor <= (input.after ?? 0)) return;\n const waiter = waiters.shift();\n if (waiter) waiter({ value: message, done: false });\n else queue.push(message);\n };\n const handle = options.relayPool.subscribe(\n [filter],\n (item) => {\n if (item === 'EOSE') return;\n const message = parseRelayGroupMessage(item);\n if (message) push(message);\n },\n relayUrls(options, filter),\n );\n return {\n result: Promise.resolve({ subscribed: true }),\n stream: {\n [Symbol.asyncIterator]() {\n return {\n next() {\n const existing = queue.shift();\n if (existing) return Promise.resolve({ value: existing, done: false });\n if (closed) return Promise.resolve({ value: undefined, done: true } as IteratorResult<CordnGroupMessage>);\n return new Promise<IteratorResult<CordnGroupMessage>>((resolve) => waiters.push(resolve));\n },\n };\n },\n },\n async abort() {\n closed = true;\n handle.unsubscribe();\n for (const waiter of waiters.splice(0)) waiter({ value: undefined, done: true } as IteratorResult<CordnGroupMessage>);\n },\n };\n },\n };\n}\n\n/** Create a Cordn/ContextVM-backed NAP-DM adapter. */\nexport function createCordnDmAdapter(options: CordnDmAdapterOptions): DmAdapter {\n const store = options.store ?? new DmMemoryStore();\n const live = new Map<string, { abort(): Promise<void>; conversationId?: string }>();\n const ownerPubkey = options.ownerPubkey;\n\n if (!options.client && !options.coordinator) {\n throw new Error('createCordnDmAdapter: client or coordinator is required');\n }\n\n return {\n async status(): Promise<DmStatus> {\n const status = await options.client?.status?.();\n return {\n available: status?.available ?? true,\n ownerPubkey: status?.ownerPubkey ?? ownerPubkey,\n implementations: ['cordn', ...(status?.implementations ?? [])],\n capabilities: ['send', 'receive', 'subscribe', 'history', 'contextvm', ...(status?.capabilities ?? [])],\n };\n },\n conversations(query?: DmConversationQuery) {\n if (options.client) return options.client.conversations(query);\n return store.conversations(query);\n },\n async messages(query: DmMessageQuery): Promise<DmMessagePage> {\n if (options.client) return options.client.messages(query);\n const gid = query.conversationId.startsWith('group:') ? query.conversationId.slice('group:'.length) : query.conversationId;\n const fetched = await options.coordinator!.FetchGroupMessages({ gid });\n for (const record of fetched.messages) store.upsertMessage(\n (options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey),\n [ownerPubkey ?? '', gid],\n );\n return store.messages(query);\n },\n async send(request: DmSendRequest): Promise<DmSendResult> {\n if (options.client) return options.client.send(request);\n const encoded = (options.encodeMessage ?? ((req, pk) => defaultEncodeMessage(req, pk, options.defaultGroupId)))(request, ownerPubkey);\n const posted = await options.coordinator!.PostGroupMessage({ msg_64: encoded.msg_64 });\n const record: CordnGroupMessage = { ...posted, msg_64: encoded.msg_64 };\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', encoded.gid]);\n return { ok: true, message: { ...message, status: 'sent' } };\n },\n async subscribe(request: DmSubscribeRequest, onMessage: (message: DmMessage) => void): Promise<DmSubscription> {\n if (options.client) return options.client.subscribe(request, onMessage);\n const conversationId = request.conversationId ?? (options.defaultGroupId ? `group:${options.defaultGroupId}` : undefined);\n if (!conversationId) throw new Error('group id required');\n const gid = conversationId.startsWith('group:') ? conversationId.slice('group:'.length) : conversationId;\n const fetched = await options.coordinator!.FetchGroupMessages({ gid });\n let after = 0;\n for (const record of fetched.messages) {\n after = Math.max(after, record.cursor);\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', gid]);\n onMessage(message);\n }\n const subscription = await options.coordinator!.SubscribeGroupMessages({ gid, after });\n const subscriptionId = `dm-cordn-${++cordnSubCounter}`;\n live.set(subscriptionId, { abort: () => subscription.abort('dm.unsubscribe'), conversationId });\n void (async () => {\n for await (const record of subscription.stream) {\n const message = store.upsertMessage((options.decodeMessage ?? defaultDecodeMessage)(record, ownerPubkey), [ownerPubkey ?? '', gid]);\n onMessage(message);\n }\n })();\n return { subscriptionId };\n },\n unsubscribe(subscriptionId: string) {\n if (options.client) return options.client.unsubscribe(subscriptionId);\n const sub = live.get(subscriptionId);\n if (!sub) return { ok: false };\n live.delete(subscriptionId);\n void sub.abort();\n return { ok: true };\n },\n close(): void {\n for (const [id, sub] of live) {\n live.delete(id);\n void sub.abort();\n }\n options.client?.close?.();\n options.coordinator?.close?.();\n },\n };\n}\n","/**\n * NAP-FS service boundary for runtime-owned virtual filesystems.\n *\n * The service owns wire correlation and per-window watch identity. Backends own\n * virtual-path policy, authorization, persistence, picker mediation, and I/O.\n */\n\nimport type {\n FsChange,\n FsDirectoryEntry,\n FsError,\n FsInfo,\n FsMetadata,\n FsMkdirOptions,\n FsPickOptions,\n FsPickResult,\n FsReadOptions,\n FsReadResult,\n FsWatchOptions,\n FsWriteOptions,\n FsWriteResult,\n NappletMessage,\n} from '@napplet/core';\nimport type { ServiceDescriptor, ServiceHandler } from '@kehto/runtime';\n\n/** Change details emitted by a backend before the service adds its scoped watch id. */\nexport type FsBackendChange = Omit<FsChange, 'watchId'>;\n\n/** Active backend watch handle. */\nexport interface FsBackendWatch {\n /** Stop the backing watch. Must be idempotent. */\n close(): void | Promise<void>;\n}\n\n/** Filesystem operations supplied to the NAP-FS service. */\nexport interface FsBackend {\n info(windowId: string): FsInfo | Promise<FsInfo>;\n pickFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickFiles(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickDirectory(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n pickSaveFile(windowId: string, options?: FsPickOptions): FsPickResult | Promise<FsPickResult>;\n stat(windowId: string, path: string): FsMetadata | Promise<FsMetadata>;\n list(windowId: string, path: string): FsDirectoryEntry[] | Promise<FsDirectoryEntry[]>;\n read(windowId: string, path: string, options?: FsReadOptions): FsReadResult | Promise<FsReadResult>;\n write(windowId: string, path: string, data: string, options?: FsWriteOptions): FsWriteResult | Promise<FsWriteResult>;\n mkdir(windowId: string, path: string, options?: FsMkdirOptions): void | Promise<void>;\n remove(windowId: string, path: string, recursive?: boolean): void | Promise<void>;\n move(windowId: string, fromPath: string, toPath: string): void | Promise<void>;\n watch(\n windowId: string,\n path: string,\n options: FsWatchOptions | undefined,\n onChange: (change: FsBackendChange) => void,\n ): FsBackendWatch | Promise<FsBackendWatch>;\n /** Release session-scoped backend state for a destroyed runtime window. */\n onWindowDestroyed?(windowId: string): void;\n /** Release all backend resources. */\n close?(): void;\n}\n\n/** Error with a closed NAP-FS wire reason. */\nexport class FsServiceError extends Error {\n constructor(readonly code: FsError, message = code) {\n super(message);\n this.name = 'FsServiceError';\n }\n}\n\n/** Options for {@link createFsService}. */\nexport interface FsServiceOptions {\n backend: FsBackend;\n}\n\n/** Created NAP-FS service. */\nexport interface FsService extends ServiceHandler {\n /** Close watches and backend resources. */\n dispose(): void;\n}\n\ninterface OwnedWatch {\n readonly windowId: string;\n handle?: FsBackendWatch;\n}\n\nconst FS_DESCRIPTOR: ServiceDescriptor = {\n name: 'fs',\n version: '1.0.0',\n description: 'NAP-FS runtime-owned virtual filesystem',\n};\n\nlet watchCounter = 0;\n\nfunction fsError(error: unknown): FsError {\n return error instanceof FsServiceError ? error.code : 'io-error';\n}\n\n/**\n * Create a NAP-FS wire service over a runtime-owned backend.\n *\n * @param options - Filesystem backend.\n * @returns Service handler for `runtime.registerService('fs', handler)`.\n */\nexport function createFsService(options: FsServiceOptions): FsService {\n if (!options?.backend) throw new Error('createFsService: options.backend is required');\n const watches = new Map<string, OwnedWatch>();\n const watchesByWindow = new Map<string, Set<string>>();\n\n function ownWatch(watchId: string, owned: OwnedWatch): void {\n watches.set(watchId, owned);\n let ids = watchesByWindow.get(owned.windowId);\n if (!ids) {\n ids = new Set();\n watchesByWindow.set(owned.windowId, ids);\n }\n ids.add(watchId);\n }\n\n function forgetWatch(watchId: string): OwnedWatch | undefined {\n const owned = watches.get(watchId);\n if (!owned) return undefined;\n watches.delete(watchId);\n const ids = watchesByWindow.get(owned.windowId);\n ids?.delete(watchId);\n if (ids?.size === 0) watchesByWindow.delete(owned.windowId);\n return owned;\n }\n\n function result(\n send: (message: NappletMessage) => void,\n type: string,\n id: string,\n operation: () => unknown | Promise<unknown>,\n field?: string,\n ): void {\n void Promise.resolve()\n .then(operation)\n .then((value) => send({\n type,\n id,\n ...(field ? { [field]: value } : {}),\n } as NappletMessage))\n .catch((error) => send({ type, id, error: fsError(error) } as NappletMessage));\n }\n\n function closeWindowWatches(windowId: string): void {\n for (const watchId of watchesByWindow.get(windowId) ?? []) {\n const owned = forgetWatch(watchId);\n if (owned?.handle) void Promise.resolve(owned.handle.close()).catch(() => undefined);\n }\n }\n\n return {\n descriptor: FS_DESCRIPTOR,\n\n handleMessage(windowId, message, send): void {\n const input = message as NappletMessage & Record<string, unknown>;\n const id = typeof input.id === 'string' ? input.id : '';\n switch (input.type) {\n case 'fs.info':\n result(send, 'fs.info.result', id, () => options.backend.info(windowId), 'info');\n return;\n case 'fs.pickFile':\n result(send, 'fs.pickFile.result', id, () => options.backend.pickFile(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickFiles':\n result(send, 'fs.pickFiles.result', id, () => options.backend.pickFiles(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickDirectory':\n result(send, 'fs.pickDirectory.result', id, () => options.backend.pickDirectory(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.pickSaveFile':\n result(send, 'fs.pickSaveFile.result', id, () => options.backend.pickSaveFile(windowId, input.options as FsPickOptions | undefined), 'result');\n return;\n case 'fs.stat':\n result(send, 'fs.stat.result', id, () => options.backend.stat(windowId, input.path as string), 'metadata');\n return;\n case 'fs.list':\n result(send, 'fs.list.result', id, () => options.backend.list(windowId, input.path as string), 'entries');\n return;\n case 'fs.read':\n result(send, 'fs.read.result', id, () => options.backend.read(windowId, input.path as string, input.options as FsReadOptions | undefined), 'result');\n return;\n case 'fs.write':\n result(send, 'fs.write.result', id, () => options.backend.write(windowId, input.path as string, input.data as string, input.options as FsWriteOptions | undefined), 'result');\n return;\n case 'fs.mkdir':\n result(send, 'fs.mkdir.result', id, () => options.backend.mkdir(windowId, input.path as string, input.options as FsMkdirOptions | undefined));\n return;\n case 'fs.remove':\n result(send, 'fs.remove.result', id, () => options.backend.remove(windowId, input.path as string, input.recursive as boolean | undefined));\n return;\n case 'fs.move':\n result(send, 'fs.move.result', id, () => options.backend.move(windowId, input.fromPath as string, input.toPath as string));\n return;\n case 'fs.watch': {\n const watchId = `fs-watch-${++watchCounter}`;\n result(send, 'fs.watch.result', id, async () => {\n const owned: OwnedWatch = { windowId };\n ownWatch(watchId, owned);\n const handle = await options.backend.watch(\n windowId,\n input.path as string,\n input.options as FsWatchOptions | undefined,\n (change) => {\n if (watches.get(watchId) !== owned || owned.handle === undefined) return;\n send({ type: 'fs.changed', change: { ...change, watchId } } as NappletMessage);\n },\n );\n if (watches.get(watchId) !== owned) {\n await Promise.resolve(handle.close()).catch(() => undefined);\n throw new FsServiceError('cancelled');\n }\n owned.handle = handle;\n return watchId;\n }, 'watchId');\n return;\n }\n case 'fs.unwatch': {\n const watchId = typeof input.watchId === 'string' ? input.watchId : '';\n const owned = watches.get(watchId);\n if (!owned || owned.windowId !== windowId) {\n send({ type: 'fs.unwatch.result', id } as NappletMessage);\n return;\n }\n const forgotten = forgetWatch(watchId);\n result(send, 'fs.unwatch.result', id, () => forgotten?.handle?.close());\n return;\n }\n default:\n return;\n }\n },\n\n onWindowDestroyed(windowId): void {\n closeWindowWatches(windowId);\n options.backend.onWindowDestroyed?.(windowId);\n },\n\n dispose(): void {\n for (const windowId of watchesByWindow.keys()) closeWindowWatches(windowId);\n options.backend.close?.();\n },\n };\n}\n"],"mappings":"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","names":["result","err","relayUrls","createRelayEventResult","createRelayEventResultWithHints","deliver","result","relayUrls","result","err","result","generateId","result","constraints","result","isRecord","err","isRecord","result","err","createRelayEventResultWithHints","relays","result","relayUrls","err","verifyEvent","generateId","handleInfo","err","toErrorMessage","result","err","toErrorMessage","result","toErrorMessage","err","result","err","toErrorMessage","result","err","result","errorMessage","err","settle","result","errorMessage","err","settle","unsupported","createContext","result","errorMessage","err","settle","unsupported","createContext","result","errorMessage","err","result","settle","forget","result","nowSeconds","finalizeEvent","getPublicKey","nowSeconds","result"]}
|