/** * @napplet/core -- JSON envelope types for the napplet-shell wire protocol. * * Defines the base types for the JSON envelope wire format introduced * in NIP-5D v4. All messages between napplet and shell use a `type` * field as a discriminant in `domain.action` format. * * @example * ```ts * import type { NappletMessage, NapDomain } from '@napplet/core'; * import { NAP_DOMAINS } from '@napplet/core'; * ``` * * @packageDocumentation */ /** * Base interface for all JSON envelope messages exchanged between * napplet and shell. The `type` field is a string discriminant * in `domain.action` format (e.g., `"relay.subscribe"`, `"storage.get"`). * * Concrete message types extend this interface with domain-specific payload fields. * * @example * ```ts * const msg: NappletMessage = { type: 'relay.subscribe' }; * * // Concrete message with payload: * interface RelaySubscribe extends NappletMessage { * type: 'relay.subscribe'; * filters: NostrFilter[]; * } * ``` */ interface NappletMessage { /** Message type discriminant in "domain.action" format (e.g., "relay.subscribe", "storage.get") */ type: string; } /** * String literal union of the active NAP (Nostr Applet Protocol) domains. * Each domain corresponds to a capability namespace that a shell may support. * * | Domain | Scope | * |------------|----------------------------------------------------| * | `relay` | NIP-01 relay proxy (subscribe, publish) | * | `identity` | Read-only user identity queries | * | `storage` | Scoped key-value storage proxy | * | `inc` | Inter-napplet communication (INC peer bus) | * | `theme` | Theme tokens and appearance settings | * | `keys` | Keyboard forwarding and action keybindings | * | `media` | Media session control and playback | * | `notify` | Shell-rendered notifications | * | `config` | Per-napplet declarative configuration | * | `resource` | Byte-fetching primitive (URL → Blob) | * | `ble` | Runtime-mediated Bluetooth LE/GATT sessions | * | `webrtc` | Runtime-mediated WebRTC signaling and data sessions | * | `link` | Shell-mediated user-visible link opening | * | `count` | Runtime-mediated event counts | * | `lists` | Runtime-mediated NIP-51 list mutations | * | `serial` | Runtime-mediated serial device access | * | `fs` | Shell-mediated virtual filesystem access | * | `common` | Common social actions | * | `dm` | Runtime-mediated direct messages | * * @example * ```ts * const domain: NapDomain = 'relay'; * const isValid = NAP_DOMAINS.includes(domain); // true * ``` */ type NapDomain = 'relay' | 'identity' | 'storage' | 'inc' | 'theme' | 'keys' | 'media' | 'notify' | 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'ble' | 'webrtc' | 'link' | 'count' | 'lists' | 'serial' | 'fs' | 'common' | 'dm'; /** * Runtime-accessible constant array of all NAP domain names. * Useful for iteration, validation, and runtime injection configuration. * * @example * ```ts * const selected = NAP_DOMAINS.filter((domain) => domain !== 'ble'); * ``` */ declare const NAP_DOMAINS: readonly NapDomain[]; /** * @napplet/core -- NAP registration and message dispatch infrastructure. * * Provides a NAP-agnostic mechanism for NAP modules (relay, identity, storage, inc) * to register their domain string and a message handler function. Inbound messages * are dispatched to the correct NAP handler based on the domain prefix extracted * from `message.type` (the part before the first `.`). * * Use the {@link createDispatch} factory for isolated registries (testing, * multi-instance), or the module-level singleton exports ({@link registerNap}, * {@link dispatch}, {@link getRegisteredDomains}) for the common single-registry case. * * @example * ```ts * import { registerNap, dispatch } from '@napplet/core'; * * // NAP module registers its domain: * registerNap('relay', (msg) => { * console.log('relay handler received:', msg.type); * }); * * // Dispatch routes by domain prefix: * dispatch({ type: 'relay.subscribe' }); // => true, calls relay handler * dispatch({ type: 'identity.getPublicKey' }); // => false, no identity handler * ``` * * @packageDocumentation */ /** * Callback that a NAP module provides to handle messages in its domain. * * @param message - The envelope message whose `type` matched this handler's domain. * * @example * ```ts * const handler: NapHandler = (msg) => { * console.log('Received:', msg.type); * }; * ``` */ type NapHandler = (message: NappletMessage) => void; /** * Shape returned by {@link createDispatch}. Contains the three dispatch * operations backed by a shared, isolated handler registry. */ interface NapDispatch { /** Register a NAP domain handler. Throws if the domain is already registered. */ registerNap: (domain: string, handler: NapHandler) => void; /** Dispatch a message to the handler matching its domain prefix. Returns `true` if handled. */ dispatch: (message: NappletMessage) => boolean; /** Return all currently registered domain strings. */ getRegisteredDomains: () => string[]; } /** * Create an isolated NAP dispatch registry. * * Each call returns a fresh `{ registerNap, dispatch, getRegisteredDomains }` * backed by its own `Map`. Use this factory for * testability or when multiple independent dispatch registries are needed. * * @returns A fresh dispatch instance with its own handler map. * * @example * ```ts * import { createDispatch } from '@napplet/core'; * * const { registerNap, dispatch } = createDispatch(); * registerNap('relay', handleRelayMessage); * dispatch({ type: 'relay.subscribe' }); // true * ``` */ declare function createDispatch(): NapDispatch; /** * Register a handler for the given NAP domain on the default registry. * * @param domain - The domain string (e.g., `'relay'`, `'identity'`). * @param handler - Callback invoked for messages in this domain. * @throws {Error} If the domain is already registered. * * @example * ```ts * import { registerNap } from '@napplet/core'; * registerNap('relay', (msg) => console.log(msg)); * ``` */ declare const registerNap: NapDispatch['registerNap']; /** * Dispatch a message on the default registry. * * @param message - The envelope message to dispatch. * @returns `true` if a handler was found and called, `false` otherwise. * * @example * ```ts * import { dispatch } from '@napplet/core'; * dispatch({ type: 'relay.subscribe' }); // true if relay handler registered * ``` */ declare const dispatch: NapDispatch['dispatch']; /** * Return all registered domain strings from the default registry. * * @returns Array of domain strings. * * @example * ```ts * import { getRegisteredDomains } from '@napplet/core'; * getRegisteredDomains(); // ['relay', 'identity'] * ``` */ declare const getRegisteredDomains: NapDispatch['getRegisteredDomains']; /** * Read-only user identity queries: public key, profile, follows, relays, * lists, zaps, mutes, blocked, badges. All queries are strictly read-only -- * no signing, encryption, or decryption. * * @example * ```ts * // Get the user's public key: * const pubkey = await window.napplet.identity.getPublicKey(); * * // Get profile metadata: * const profile = await window.napplet.identity.getProfile(); * if (profile) console.log(profile.name); * * // Get follow list: * const follows = await window.napplet.identity.getFollows(); * ``` */ interface IdentityApi { /** Get the user's hex-encoded public key. Always succeeds. */ getPublicKey(): Promise; /** * Listen for shell-pushed user identity changes. * The callback receives a hex pubkey, or "" when no user/signer is connected. */ onChanged(handler: (pubkey: string) => void): Subscription; /** Get the user's relay list (NIP-65). */ getRelays(): Promise>; /** Get the user's profile metadata (kind 0). Returns null if not found. */ getProfile(): Promise<{ name?: string; displayName?: string; about?: string; picture?: string; banner?: string; nip05?: string; lud16?: string; website?: string; } | null>; /** Get the user's follow list (kind 3 contact list). */ getFollows(): Promise; /** Get entries from a user's categorized list. */ getList(listType: string): Promise; /** Get zap receipts sent to the user. */ getZaps(): Promise<{ eventId: string; sender: string; amount: number; content?: string; }[]>; /** Get the user's mute list (kind 10000). */ getMutes(): Promise; /** Get the user's block list. */ getBlocked(): Promise; /** Get badges awarded to the user (NIP-58). */ getBadges(): Promise<{ id: string; name?: string; description?: string; image?: string; thumbs?: string[]; awardedBy: string; }[]>; } /** * Read-only access to the shell's active theme (NAP-THEME). * * The shell owns theming; napplets read the current theme and react to * shell-pushed changes. The payload carries required colors plus optional * fonts, background media, and a title. * * @example * ```ts * const theme = await window.napplet.theme.get(); * document.body.style.background = theme.colors.background; * const sub = window.napplet.theme.onChanged((t) => applyTheme(t)); * ``` */ interface ThemeApi { /** Get the shell's current active theme. */ get(): Promise<{ colors: { background: string; text: string; primary: string; }; fonts?: { body?: { name: string; url: string; }; title?: { name: string; url: string; }; }; background?: { url: string; mode: string; mime: string; }; title?: string; }>; /** Listen for shell-pushed theme changes. */ onChanged(handler: (theme: { colors: { background: string; text: string; primary: string; }; fonts?: { body?: { name: string; url: string; }; title?: { name: string; url: string; }; }; background?: { url: string; mode: string; mime: string; }; title?: string; }) => void): Subscription; } /** * Per-napplet declarative configuration (NAP-CONFIG). * * Napplet declares a JSON Schema (typically at build time via * @napplet/vite-plugin's `configSchema` option, or at runtime via * `registerSchema`); shell renders the settings UI, validates values, * persists them scoped by `(dTag, aggregateHash)`, and delivers live * values via initial snapshot + push. Shell is the sole writer. * * @example * ```ts * // Register a schema with the shell: * await window.napplet.config.registerSchema({ * type: 'object', * properties: { theme: { type: 'string', enum: ['light', 'dark'], default: 'dark' } }, * }); * * // Subscribe to live values (first delivery is an immediate snapshot): * const sub = window.napplet.config.subscribe((values) => { * applyTheme(values.theme as string); * }); * * // Deep-link into shell-owned settings UI: * window.napplet.config.openSettings({ section: 'appearance' }); * ``` */ interface ConfigApi { /** * Register a napplet configuration schema with the shell at runtime. * Correlated via UUID; resolves on positive ACK, rejects with * `Error(code + ': ' + reason)` on shell rejection. * @param schema JSON Schema (draft-07+) describing the config surface. * @param version Optional `$version` migration hint. */ registerSchema(schema: Record, version?: number): Promise; /** * Snapshot current validated + defaulted config values. * Correlated via UUID; resolves on the matching `config.values` response. */ get(): Promise>; /** * Subscribe to live configuration updates. First delivery is an immediate * snapshot; subsequent deliveries fire whenever the shell commits a change. * Ref-counted: wire-level subscribe/unsubscribe only on 0->1 / 1->0 * local-subscriber transitions. * @param callback Invoked with the current config values on each push. * @returns A Subscription with `close()` to detach. */ subscribe(callback: (values: Record) => void): Subscription; /** * Request the shell open its settings UI for this napplet. * Fire-and-forget. The optional `section` deep-links to a named section * declared via the `x-napplet-section` extension somewhere in the schema. * @param options.section Optional section name to deep-link to. */ openSettings(options?: { section?: string; }): void; /** * Listen for schema-registration errors pushed by the shell (manifest * parse failure, `no-schema`, etc.). Uncorrelated fan-out. * @param callback Invoked with `{ code, error }` on each error push. * @returns A plain teardown function that detaches the listener. */ onSchemaError(callback: (err: { code: string; error: string; }) => void): () => void; /** * Readonly accessor for the currently-registered JSON Schema. * Updated on successful `registerSchema` responses. `null` until a schema is * registered. */ readonly schema: Record | null; } /** * Typed error vocabulary for resource fetch failures. */ type ResourceErrorCode = 'invalid-request' | 'not-found' | 'blocked-by-policy' | 'timeout' | 'too-large' | 'unsupported-scheme' | 'decode-failed' | 'network-error' | 'quota-exceeded'; /** Successful per-URL item returned by `resource.bytesMany`. */ interface ResourceBytesOkItem { url: string; ok: true; blob: Blob; mime: string; message?: string; } /** Failed per-URL item returned by `resource.bytesMany`. */ interface ResourceBytesErrorItem { url: string; ok: false; error: ResourceErrorCode; message?: string; blob?: never; } /** Ordered per-URL item returned by `resource.bytesMany`. */ type ResourceBytesItem = ResourceBytesOkItem | ResourceBytesErrorItem; /** Pre-resolved resource bytes carried by another NAP's event sidecar. */ interface ResourceSidecarEntry { url: string; blob: Blob; mime: string; } /** Runtime-disclosed support for one resource URL scheme. */ interface ResourceSchemeInfo { scheme: string; enabled: boolean; } /** Advisory resource capability and policy limits disclosed by the runtime. */ interface ResourceInfo { schemes: ResourceSchemeInfo[]; maxBytes?: number; maxUrls?: number; } /** * Browser-enforced resource fetching: napplets request bytes by URL, * shell fetches and returns a Blob. The strict-CSP iframe sandbox * blocks all napplet-side network access, so this is the canonical * (and only) byte-fetching primitive available inside a napplet. * * URL space is scheme-pluggable: shells register handlers per scheme. * Canonical schemes include `data:` (decoded in-shim, no round-trip), * `https:` (shell-side network with policy), `blossom:` (Blossom hash to * bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 single-hop * resolution). * * @example * ```ts * // Fetch raw bytes: * const blob = await window.napplet.resource.bytes('https://example.com/avatar.png'); * * // Fetch many resources in one envelope: * const items = await window.napplet.resource.bytesMany([ * 'https://example.com/avatar.png', * 'blossom:sha256:abc123...', * 'htree://example-root/path', * ]); * * // Get a managed object URL (revoke when done to free memory): * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:abc123...'); * imgEl.src = url; * imgEl.onload = () => revoke(); * ``` */ interface ResourceApi { /** * Inspect resource schemes and coarse policy limits the runtime is willing * to disclose. Advisory only; callers can use bytes/bytesMany without a * preflight. * @returns Promise resolving to the resource info snapshot. */ info(): Promise; /** * Fetch the bytes referenced by `url` through the shell's resource pipeline. * The shell selects a scheme handler, applies its resource policy * (private-IP blocks, size caps, timeouts, MIME classification), and * returns the bytes as a single Blob. No streaming, no chunking. * @param url URL identifying the resource (any registered scheme) * @returns Promise resolving to the fetched bytes as a Blob */ bytes(url: string, opts?: { signal?: AbortSignal; }): Promise; /** * Fetch the bytes referenced by many URLs through one shell envelope. * The returned items preserve input order and length. Failed URLs are * represented as `ok: false` items; successful siblings remain available. * @param urls Non-empty URL list * @param opts Optional AbortController signal * @returns Promise resolving to ordered per-URL result items */ bytesMany(urls: string[], opts?: { signal?: AbortSignal; }): Promise; /** * Convenience wrapper around `bytes(url)` that returns a managed * object URL plus a `revoke` function. Calling `revoke()` invokes * `URL.revokeObjectURL` exactly once to free the underlying Blob. * @param url URL identifying the resource * @returns Object containing the blob URL and a revoke function */ bytesAsObjectURL(url: string): { url: string; revoke: () => void; }; } /** * Standard NIP-01 nostr event. * @example * ```ts * const event: NostrEvent = { * id: '...', pubkey: '...', created_at: 1234567890, * kind: 1, tags: [['t', 'topic']], content: 'Hello', sig: '...', * }; * ``` */ interface NostrEvent { id: string; pubkey: string; created_at: number; kind: number; tags: string[][]; content: string; sig: string; } /** Metadata carried alongside a raw relay-read event result. */ interface RelayEventSidecar { /** Pre-resolved resource bytes under NAP-RESOURCE policy. */ resources?: ResourceSidecarEntry[]; /** Advisory relay URLs where the runtime observed the event or expects reads to work. */ relayHints?: string[]; } /** Raw event result returned by read-style relay surfaces. */ interface RelayEventResult { event: NostrEvent; sidecar?: RelayEventSidecar; } /** * NIP-01 subscription filter. * @example * ```ts * const filter: NostrFilter = { kinds: [1], authors: ['abc123...'], limit: 10 }; * ``` */ interface NostrFilter { ids?: string[]; authors?: string[]; kinds?: number[]; since?: number; until?: number; limit?: number; [key: `#${string}`]: string[] | undefined; } /** * Subscription handle returned by relay.subscribe() and inc.on(). * Call close() to unsubscribe and stop receiving events. * * @example * ```ts * const sub = window.napplet.relay.subscribe(filter, onEvent, onEose); * // Later: * sub.close(); * ``` */ interface Subscription { /** Close the subscription and stop receiving events. */ close(): void; } /** * Unsigned event template passed to relay.publish(). * The shell signs it before broadcasting. * * @example * ```ts * const signed = await window.napplet.relay.publish({ * kind: 1, * content: 'Hello Nostr!', * tags: [], * created_at: Math.floor(Date.now() / 1000), * }); * ``` */ interface EventTemplate { /** Nostr event kind number */ kind: number; /** Event content (typically plaintext or JSON string) */ content: string; /** Event tags (NIP-01 tag arrays) */ tags: string[][]; /** Unix timestamp (seconds since epoch) */ created_at: number; } /** A single Nostr tag (NIP-94 / imeta entries are arrays of strings). */ type NostrTag = string[]; /** The side that fetches/decodes media and emits authoritative playback state. */ type MediaPlaybackOwner = 'shell' | 'napplet'; /** Nostr event or address reference with optional relay hints. */ interface MediaNostrRef { eventId?: string; address?: string; relays?: string[]; } /** Source reference for shell-owned media playback or advisory source metadata. */ interface MediaSourceRef { url?: string; blossomHash?: string; nostr?: MediaNostrRef; mimeType?: string; } /** Artwork reference for media sessions. */ interface MediaArtwork { url?: string; hash?: string; } /** Media session metadata. */ interface MediaMetadata { title?: string; artist?: string; album?: string; artwork?: MediaArtwork; duration?: number; mediaType?: 'audio' | 'video'; } /** Link from a media session context to a related resource. */ interface MediaContextLink { rel: string; title?: string; nostr?: MediaNostrRef; } /** Optional UI, queue, and related-resource context for a media session. */ interface MediaSessionContext { label?: string; detail?: string; index?: number; total?: number; links?: MediaContextLink[]; } /** Media playback state. */ interface MediaState { status: 'playing' | 'paused' | 'stopped' | 'buffering'; position?: number; duration?: number; volume?: number; } /** Media action supported by a session or requested by a controller. */ type MediaAction = 'play' | 'pause' | 'stop' | 'next' | 'prev' | 'seek' | 'volume'; interface MediaSessionCreateBase { sessionId?: string; metadata?: MediaMetadata; context?: MediaSessionContext; capabilities?: MediaAction[]; autoplay?: boolean; live?: boolean; } /** Ownership-aware media session creation options. */ type MediaSessionCreate = (MediaSessionCreateBase & { owner: 'shell'; source: MediaSourceRef; }) | (MediaSessionCreateBase & { owner: 'napplet'; source?: MediaSourceRef; }); /** Result of a media session creation request. */ interface MediaSessionResult { sessionId?: string; owner?: MediaPlaybackOwner; error?: string; } /** * A single MCP JSON-RPC message exchanged with a ContextVM server (NAP-CVM). * The embedded `id` is the JSON-RPC correlation id, independent of the NIP-5D * envelope id used to correlate `cvm.request` with `cvm.request.result`. */ interface McpMessage { jsonrpc: '2.0'; id?: string | number; method?: string; params?: unknown; result?: unknown; error?: unknown; } /** An MCP tool definition, as returned by `tools/list`. */ interface McpTool { name: string; description?: string; inputSchema: { type: 'object'; properties?: Record; required?: string[]; }; } /** A content block inside an MCP tool result (text, image, resource, ...). */ interface McpContentBlock { type: string; text?: string; [key: string]: unknown; } /** The result of an MCP `tools/call`. */ interface McpToolResult { content: McpContentBlock[]; isError?: boolean; [key: string]: unknown; } /** An MCP resource descriptor, as returned by `resources/list`. */ interface McpResource { uri: string; name: string; title?: string; description?: string; mimeType?: string; size?: number; } /** Text contents of an MCP resource (`resources/read`). */ interface McpTextResourceContents { uri: string; mimeType?: string; text: string; } /** Binary contents of an MCP resource (`resources/read`); `blob` is base64-encoded. */ interface McpBlobResourceContents { uri: string; mimeType?: string; blob: string; } /** A single MCP resource content entry: either text or base64 blob. */ type McpResourceContent = McpTextResourceContents | McpBlobResourceContents; /** Identifies a ContextVM server by Nostr public key, with optional relay hints. */ interface CvmServerRef { pubkey: string; relays?: string[]; } /** Filter for ContextVM server discovery. */ interface CvmDiscoverQuery { search?: string; kinds?: number[]; relays?: string[]; limit?: number; } /** A discovered ContextVM server announcement. */ interface CvmServer extends CvmServerRef { name?: string; description?: string; capabilities?: string[]; paymentRequired?: boolean; } /** Per-request options for ContextVM operations. */ interface CvmRequestOptions { timeoutMs?: number; initialize?: boolean; payment?: 'deny' | 'prompt' | 'allow'; } /** JSON object passed to ContextVM tools. */ type JsonObject = Record; /** JSON Schema object advertised by a ContextVM registry tool. */ type JsonSchema = Record; /** Query for shell-curated ContextVM registry families. */ interface CvmRegistryQuery { search?: string; family?: string; schemaHash?: string; limit?: number; } /** Selection constraints for a ContextVM registry family. */ interface CvmRegistryOptions { schemaHash?: string; server?: CvmServerRef; } /** Call options for a shell-selected ContextVM registry tool. */ interface CvmRegistryCallOptions extends CvmRegistryOptions { timeoutMs?: number; initialize?: boolean; payment?: 'deny' | 'prompt' | 'allow'; cache?: 'default' | 'reload' | 'no-store'; } /** A tool advertised inside a shell-curated ContextVM registry family. */ interface CvmRegistryTool { name: string; description?: string; inputSchema: JsonSchema; outputSchema?: JsonSchema; schemaHash?: string; } /** A shell-curated ContextVM tool family and its candidate providers. */ interface CvmRegistryEntry { family: string; description?: string; schemaHash?: string; selected?: CvmServerRef; providers?: CvmServerRef[]; tools: CvmRegistryTool[]; } /** Options for a one-shot outbox query. */ interface OutboxQueryOptions { authors?: string[]; relays?: string[]; limit?: number; timeoutMs?: number; } /** Options for a single-event outbox lookup. */ interface OutboxEventOptions { author?: string; relays?: string[]; timeoutMs?: number; } /** Options for a live outbox subscription. */ interface OutboxSubscribeOptions extends OutboxQueryOptions { } /** Options for an outbox publish. */ interface OutboxPublishOptions { /** Explicit relay URL fanout candidates, subject to shell validation. */ relays?: string[]; /** Include the shell user's NIP-65 write relays. Defaults to true. */ toOutbox?: boolean; /** Recipient pubkeys whose NIP-65 read relays are required fanout targets. */ toInboxes?: string[]; } /** A read/write target for outbox relay-plan resolution. */ interface OutboxTarget { authors?: string[]; pubkey?: string; direction?: 'read' | 'write'; } /** The relay plan the shell would use for an outbox target. */ interface OutboxRelayPlan { relays: string[]; source: 'nip65' | 'cache' | 'policy' | 'fallback'; missingAuthors?: string[]; } /** The result of an outbox query. */ interface OutboxResult { events: RelayEventResult[]; incomplete?: boolean; error?: string; } /** The result of a single-event outbox lookup. */ interface OutboxEventResult { result?: RelayEventResult; incomplete?: boolean; error?: string; } /** The result of an outbox publish. */ interface OutboxPublishResult { ok: boolean; event?: NostrEvent; eventId?: string; relays?: Record; error?: string; } /** Handle for a live outbox subscription. */ interface OutboxSubscription { on(event: 'event', cb: (result: RelayEventResult) => void): void; on(event: 'closed', cb: (reason?: string) => void): void; close(): void; } /** Storage rail for shell-mediated uploads (NAP-UPLOAD). */ type UploadRail = 'nip96' | 'blossom' | (string & {}); /** Lifecycle state of an upload. */ type UploadState = 'pending' | 'uploading' | 'complete' | 'failed' | 'cancelled'; /** Runtime-disclosed support for one upload rail. */ interface UploadRailInfo { rail: UploadRail; enabled: boolean; returns?: string[]; } /** Advisory upload capability and policy limits disclosed by the runtime. */ interface UploadInfo { rails: UploadRailInfo[]; maxBytes?: number; mimeTypes?: string[]; } /** A napplet's upload request; `data` crosses the boundary by structured clone. */ interface UploadRequest { rail?: UploadRail; data: Blob | ArrayBuffer; mimeType?: string; filename?: string; caption?: string; noTransform?: boolean; metadata?: Record; } /** The result of an upload. */ interface UploadResult { ok: boolean; uploadId: string; status: UploadState; rail: UploadRail; url?: string; fallbackUrls?: string[]; sha256?: string; originalSha256?: string; size?: number; mimeType?: string; dimensions?: { width: number; height: number; }; blurhash?: string; nip94?: NostrTag[]; error?: string; } /** A status snapshot for an upload, including progress counters. */ interface UploadStatus extends UploadResult { bytesSent?: number; bytesTotal?: number; updatedAt: number; } /** How the shell should pick the handling napplet for an intent (NAP-INTENT). */ type IntentHandlerPreference = 'default' | 'choose' | (string & {}); /** Window and focus hints for an intent invocation. */ interface IntentBehavior { /** Focus the target surface. */ focus?: boolean; /** Request a new target window instead of reuse. */ newWindow?: boolean; /** Permit reuse of an existing matching window. */ reuse?: boolean; } /** Optional fields accepted by the `intent.open` convenience operation. */ interface IntentOpenOptions { /** Convention that shapes the opaque payload. */ convention?: string; /** Runtime-authorized handler selection preference. */ handler?: IntentHandlerPreference; /** Window and focus hints. */ behavior?: IntentBehavior; } /** A request to dispatch an action to a napplet archetype. */ interface IntentRequest extends IntentOpenOptions { /** Role slug used for handler resolution. */ archetype: string; /** Action to dispatch; defaults to `open`. */ action?: string; /** Opaque payload shaped by `convention` when present. */ payload?: unknown; } /** A napplet that can fulfill an archetype (from the manifest catalog). */ interface IntentCandidate { /** Napplet dTag. */ dTag: string; /** Optional human-readable handler label. */ title?: string; /** Actions supported by this candidate. */ actions: string[]; /** Payload conventions supported by this candidate. */ conventions: string[]; /** Whether this candidate is the current default. */ isDefault?: boolean; } /** Availability of an archetype, sourced from the installed-napplet catalog. */ interface IntentAvailability { /** Queried archetype. */ archetype: string; /** Whether at least one candidate is available. */ available: boolean; /** Candidate napplets. */ candidates: IntentCandidate[]; /** Whether the runtime has a default handler. */ hasDefault: boolean; } /** The result of an intent invocation. */ interface IntentResult { /** Whether dispatch completed. */ ok: boolean; /** Requested archetype. */ archetype: string; /** Dispatched action. */ action: string; /** Whether a handler accepted the dispatch. */ handled: boolean; /** dTag of the handling napplet. */ handler?: string; /** Runtime-assigned target window identifier. */ windowId?: string; /** Convention used for payload delivery. */ convention?: string; /** Failure reason. */ error?: string; } /** BLE UUID input accepted by NAP-BLE requests. */ type BleUuid = string | number; /** BLE session lifecycle state. */ type BleSessionState = 'opening' | 'open' | 'closed'; /** Web-Bluetooth-shaped device selection request. */ interface BleOpenRequest { filters?: BleDeviceFilter[]; exclusionFilters?: BleDeviceFilter[]; acceptAllDevices?: boolean; optionalServices?: BleUuid[]; label?: string; } /** Device filter for runtime-owned chooser/permission flows. */ interface BleDeviceFilter { services?: BleUuid[]; name?: string; namePrefix?: string; manufacturerData?: BleManufacturerDataFilter[]; serviceData?: BleServiceDataFilter[]; } /** Manufacturer data filter. Byte arrays are integer arrays in the range 0..255. */ interface BleManufacturerDataFilter { companyIdentifier: number; dataPrefix?: number[]; mask?: number[]; } /** Service data filter. Byte arrays are integer arrays in the range 0..255. */ interface BleServiceDataFilter { service: BleUuid; dataPrefix?: number[]; mask?: number[]; } /** Result of opening a BLE session. */ interface BleOpenResult { session: BleSession; } /** Runtime-scoped BLE session. */ interface BleSession { id: string; state: BleSessionState; device: BleDeviceInfo; } /** Redacted runtime-scoped device identity. */ interface BleDeviceInfo { id: string; name?: string; services?: string[]; } /** Exposed GATT service. */ interface BleService { uuid: string; characteristics: BleCharacteristic[]; } /** Exposed GATT characteristic. */ interface BleCharacteristic { uuid: string; properties: BleCharacteristicProperties; } /** Characteristic capabilities the runtime exposes. */ interface BleCharacteristicProperties { read?: boolean; write?: boolean; writeWithoutResponse?: boolean; notify?: boolean; indicate?: boolean; } /** Characteristic or descriptor target. */ interface BleAttribute { service: BleUuid; characteristic: BleUuid; descriptor?: BleUuid; } /** Write mode preference. */ interface BleWriteOptions { response?: 'with-response' | 'without-response' | 'auto'; } /** BLE runtime-pushed event. */ type BleEvent = BleStateEvent | BleNotificationEvent | BleClosedEvent; /** Session state update. */ interface BleStateEvent { type: 'state'; sessionId: string; state: BleSessionState; } /** Characteristic notification/indication payload. */ interface BleNotificationEvent { type: 'notification'; sessionId: string; target: BleAttribute; data: number[]; } /** Session closed update. */ interface BleClosedEvent { type: 'closed'; sessionId: string; reason?: string; } /** Runtime API mounted at `window.napplet.ble`. */ interface BleApi { open(request: BleOpenRequest): Promise; services(sessionId: string): Promise; read(sessionId: string, target: BleAttribute): Promise; write(sessionId: string, target: BleAttribute, data: number[], options?: BleWriteOptions): Promise; subscribe(sessionId: string, target: BleAttribute): Promise; unsubscribe(sessionId: string, target: BleAttribute): Promise; close(sessionId: string, reason?: string): Promise; onEvent(handler: (event: BleEvent) => void): Subscription; } /** Runtime-owned WebRTC session scope. */ type WebrtcScope = WebrtcDirectScope | WebrtcRoomScope; /** Direct peer session target. */ interface WebrtcDirectScope { type: 'direct'; pubkey: string; } /** Room session target. */ interface WebrtcRoomScope { type: 'room'; room: string; peers?: string[]; } /** Request to open a runtime-owned WebRTC session. */ interface WebrtcOpenRequest { scope: WebrtcScope; channel?: string; protocol?: string; } /** Result of opening a WebRTC session. */ interface WebrtcOpenResult { session: WebrtcSession; } /** WebRTC session lifecycle state. */ type WebrtcState = 'connecting' | 'open' | 'closed'; /** Runtime-scoped WebRTC session. */ interface WebrtcSession { id: string; scope: WebrtcScope; channel: string; protocol?: string; state: WebrtcState; } /** Runtime-pushed WebRTC session event. */ type WebrtcEvent = WebrtcStateEvent | WebrtcPeerEvent | WebrtcMessageEvent | WebrtcClosedEvent; /** Session state update. */ interface WebrtcStateEvent { type: 'state'; sessionId: string; state: WebrtcState; } /** Peer membership update. */ interface WebrtcPeerEvent { type: 'peer'; sessionId: string; pubkey: string; state: 'joined' | 'left'; } /** Opaque application payload received from a peer. */ interface WebrtcMessageEvent { type: 'message'; sessionId: string; from: string; payload: unknown; } /** Session closed update. */ interface WebrtcClosedEvent { type: 'closed'; sessionId: string; reason?: string; } /** Runtime API mounted at `window.napplet.webrtc`. */ interface WebrtcApi { open(request: WebrtcOpenRequest): Promise; send(sessionId: string, payload: unknown): Promise; close(sessionId: string, reason?: string): Promise; onEvent(handler: (event: WebrtcEvent) => void): Subscription; } /** Options for a shell-mediated link open request (NAP-LINK). */ interface LinkOpenOptions { /** Optional napplet-supplied prompt label. Shells must not treat it as trusted policy input. */ label?: string; } /** Result status for a shell-mediated link open request. */ type LinkOpenStatus = 'opened' | 'denied'; /** API result returned by `window.napplet.link.open()`. */ interface LinkOpenResult { /** Whether the shell accepted and handed off the navigation, or denied it. */ status: LinkOpenStatus; } /** Common denial reasons carried by `link.open.result` messages. */ type LinkOpenErrorCode = 'invalid-url' | 'unsupported-scheme' | 'blocked-by-policy' | 'user-denied' | (string & {}); /** Serial session state for runtime-mediated serial access (NAP-SERIAL). */ type SerialState = 'opening' | 'open' | 'closed'; /** Filter hint for the runtime-owned serial chooser. */ interface SerialPortFilter { usbVendorId?: number; usbProductId?: number; bluetoothServiceClassId?: string | number; } /** Runtime-owned serial open options. */ interface SerialOpenOptions { baudRate: number; dataBits?: 7 | 8; stopBits?: 1 | 2; parity?: 'none' | 'even' | 'odd'; bufferSize?: number; flowControl?: 'none' | 'hardware'; } /** A napplet request to select and open a serial session. */ interface SerialOpenRequest { filters?: SerialPortFilter[]; options: SerialOpenOptions; label?: string; } /** Redacted serial device metadata returned by the runtime. */ interface SerialPortInfo { usbVendorId?: number; usbProductId?: number; bluetoothServiceClassId?: string | number; displayName?: string; } /** Runtime-assigned serial session handle. */ interface SerialSession { id: string; state: SerialState; info?: SerialPortInfo; } /** Result of opening a serial session. */ interface SerialOpenResult { session: SerialSession; } /** Shell-pushed serial state, data, and close events. */ type SerialEvent = { type: 'state'; sessionId: string; state: SerialState; } | { type: 'data'; sessionId: string; data: number[]; } | { type: 'closed'; sessionId: string; reason?: string; }; /** * NAP-FS schema types for shell-mediated virtual filesystem access. * * Non-normative note -- the canonical definition lives in napplet/naps#88: * * * Byte transfer uses RFC 4648 standard padded base64 text on the JSON wire for * `fs.write.data` and `FsReadResult.data`; byte counts refer to decoded bytes. */ /** A coarse permission a runtime may advertise for a visible root or entry. */ type FsPermission = 'read' | 'write' | 'create' | 'delete' | 'list' | 'watch'; /** The kind of a filesystem entry. `unknown` grants no implied operation. */ type FsEntryKind = 'file' | 'directory' | 'unknown'; /** The kind of change reported by an advisory watch event. */ type FsChangeKind = 'created' | 'modified' | 'deleted' | 'moved' | 'unknown'; /** File write behavior. */ type FsWriteMode = 'replace' | 'append' | 'patch'; /** Closed set of NAP-FS error reasons. Never widen this to `string`. */ type FsError = 'not-found' | 'already-exists' | 'not-a-file' | 'not-a-directory' | 'invalid-path' | 'invalid-data' | 'permission-denied' | 'policy-denied' | 'quota-exceeded' | 'too-large' | 'unsupported' | 'conflict' | 'cancelled' | 'io-error'; /** A runtime-curated root visible to the napplet. Names and descriptions are safe-to-disclose labels, never host paths. */ interface FsRoot { /** Virtual absolute path of the root. */ path: string; /** Runtime-curated display label. */ name: string; /** Coarse advisory permissions for this root. */ permissions: FsPermission[]; /** Optional runtime-curated description. */ description?: string; } /** Runtime-advertised operational limits. Advisory discovery data, not authorization. */ interface FsLimits { /** Maximum bytes a single read may request. */ maxReadBytes: number; /** Maximum bytes a single write may carry. */ maxWriteBytes: number; /** Maximum concurrently active watches, when advertised. */ maxWatchCount?: number; /** Maximum concurrent in-flight requests, when advertised. */ maxInFlightRequests?: number; /** Maximum aggregate in-flight bytes, when advertised. */ maxInFlightBytes?: number; } /** Visible roots and runtime limits. Advisory discovery only -- never an authorization token. */ interface FsInfo { /** Roots visible to this napplet. */ roots: FsRoot[]; /** Runtime-advertised operational limits. */ limits: FsLimits; } /** Advisory picker filter. Runtimes and napplets must not treat it as content validation. */ interface FsAcceptRule { /** MIME type hint such as `text/plain`. */ mime?: string; /** Extension hint such as `.md`. */ extension?: string; } /** User-mediated picker options. Hints only -- never authority. */ interface FsPickOptions { /** Requested permission intent. The runtime decides the actual returned permissions. */ permissions?: FsPermission[]; /** Advisory UI filters only. */ accept?: FsAcceptRule[]; /** Suggested destination file name for save pickers. */ suggestedName?: string; /** Runtime-displayable description of the picker intent. */ description?: string; } /** A file or directory selected by runtime-mediated user choice. */ interface FsPickedEntry { /** Virtual absolute path exposed to this napplet. */ path: string; /** Selected entry kind. */ kind: 'file' | 'directory'; /** Entry name within its virtual parent. */ name: string; /** Permissions actually granted for the returned virtual path. */ permissions: FsPermission[]; /** Size in bytes, when the runtime discloses it. */ size?: number; /** Last-modified timestamp, when the runtime discloses it. */ modifiedAt?: number; } /** Result of a user-mediated picker request. Cancellation is an error, not an empty success. */ interface FsPickResult { /** Selected entries exposed as virtual filesystem paths. */ entries: FsPickedEntry[]; } /** Coarse metadata for a visible file or directory. Omits host-specific identifiers. */ interface FsMetadata { /** Virtual absolute path of the entry. */ path: string; /** The kind of entry. */ kind: FsEntryKind; /** Size in bytes, when the runtime discloses it. */ size?: number; /** Last-modified timestamp, when the runtime discloses it. */ modifiedAt?: number; /** Creation timestamp, when the runtime discloses it. */ createdAt?: number; /** Coarse advisory permissions for this entry. */ permissions?: FsPermission[]; /** Opaque write-precondition token. Compare only for equality; infer no ordering or content. */ revision?: string; } /** A direct child of a listed directory. Result ordering is unspecified. */ interface FsDirectoryEntry { /** Entry name within its parent directory. */ name: string; /** Virtual absolute path of the entry. */ path: string; /** The kind of entry. */ kind: FsEntryKind; /** Size in bytes, when the runtime discloses it. */ size?: number; /** Last-modified timestamp, when the runtime discloses it. */ modifiedAt?: number; } /** Options for reading bytes from a visible file. */ interface FsReadOptions { /** Starting byte offset. Defaults to 0. */ offset?: number; /** Requested decoded byte count. Defaults to the runtime maximum readable chunk. */ length?: number; } /** Result of reading bytes from a visible file. */ interface FsReadResult { /** Decoded file bytes encoded as RFC 4648 standard padded base64 text. */ data: string; /** Starting byte offset of this result. */ offset: number; /** Count of decoded bytes in `data`. */ bytesRead: number; /** Whether no more bytes are available after this result. */ eof: boolean; /** Total file size in bytes, when the runtime discloses it. */ size?: number; } /** Options for writing bytes to a visible file. */ interface FsWriteOptions { /** Write mode. Defaults to `replace`. */ mode?: FsWriteMode; /** Patch byte offset. Required for `patch`; invalid for `replace` and `append`. */ offset?: number; /** Opaque revision precondition. */ ifRevision?: string; /** Create-only precondition when true. */ ifAbsent?: boolean; } /** Result of writing bytes to a visible file. */ interface FsWriteResult { /** Count of decoded bytes committed. */ bytesWritten: number; /** Resulting file size in bytes, when the runtime discloses it. */ size?: number; } /** Options for directory creation. */ interface FsMkdirOptions { /** Create missing parents within the napplet's authorized view. */ recursive?: boolean; } /** Options for starting an advisory watch. */ interface FsWatchOptions { /** Watch visible descendants rather than only direct children. */ recursive?: boolean; } /** An advisory change signal. Events may be coalesced, duplicated, reordered, or dropped -- re-read after receiving one. */ interface FsChange { /** The watch that produced this event. */ watchId: string; /** Virtual absolute path the change applies to. */ path: string; /** The kind of change. */ kind: FsChangeKind; /** Previous path, when the change is a move. */ fromPath?: string; } /** NIP-51 list item kinds accepted by NAP-LISTS mutation requests. */ type ListItemType = 'pubkey' | 'event' | 'address' | 'hashtag' | 'word' | 'relay' | 'emoji' | 'server' | 'url' | 'group'; /** Public items are written to tags; private items are runtime-encrypted. */ type ListItemVisibility = 'public' | 'private'; /** Reference a NIP-51 list by exact kind or derived type name. */ type ListRef = { /** Direct NIP-51 event kind. */ kind: number; type?: never; /** NIP-51 `d` tag value for addressable sets. */ identifier?: string; } | { kind?: never; /** Type derived from the NIP-51 table name. */ type: string; /** NIP-51 `d` tag value for addressable sets. */ identifier?: string; }; /** A list item mutation intent. The runtime owns NIP-51 tag encoding. */ interface ListItem { /** Semantic item type; the runtime maps this to the selected list's NIP-51 tag. */ itemType: ListItemType; /** Item value: pubkey, event id, address, relay URL, word, etc. */ value: string; /** Optional relay hint. */ relay?: string; /** Optional user-visible label. */ label?: string; /** Whether the item should be public or private. Omitted means public for add. */ visibility?: ListItemVisibility; } /** Options for add/remove list mutation requests. */ interface ListOptions { /** Create a missing list when supported by the runtime. */ create?: boolean; /** Optional title metadata for created/addressable lists. */ title?: string; /** Optional description metadata for created/addressable lists. */ description?: string; /** Optional image metadata for created/addressable lists. */ image?: string; } /** One list kind/type supported by the runtime's policy and implementation. */ interface ListSupport { /** Direct NIP-51 event kind. */ kind: number; /** Type derived from the NIP-51 table name. */ type: string; /** Whether this list requires an addressable `identifier`. */ addressable: boolean; /** Item types the runtime accepts for this list. */ supportedItemTypes?: ListItemType[]; /** Whether private list items are supported for this list. */ privateItems?: boolean; } /** Common NAP-LISTS error codes. */ type ListErrorCode = 'unsupported-list' | 'unsupported-item' | 'invalid-list-ref' | 'ambiguous-list' | 'missing-identifier' | 'invalid-item' | 'not-signed-in' | 'list-not-found' | 'list-unavailable' | 'private-items-unsupported' | 'decrypt-failed' | 'user-denied' | 'publish-failed' | 'unsupported' | (string & {}); /** Result of a NAP-LISTS add/remove mutation. */ interface ListMutationResult { /** Whether the runtime completed the requested mutation. */ ok: boolean; /** Signed event id when available. */ eventId?: string; /** Optional redacted or complete signed event returned by the runtime. */ event?: Record; /** Number of items added. */ added?: number; /** Number of items removed. */ removed?: number; /** Number of no-op items skipped. */ skipped?: number; /** Machine-readable error code. */ error?: ListErrorCode; /** Human-readable failure reason. */ reason?: string; /** Supported candidates, especially for unsupported or ambiguous requests. */ supported?: ListSupport[]; } /** NIP-19 entity types NAP-COMMON exposes to napplets. */ type CommonNip19Type = 'npub' | 'note' | 'nprofile' | 'nevent' | 'naddr' | 'nrelay'; /** Hex-encoded public key. */ type CommonHexPubkey = string; /** Hex-encoded Nostr event id. */ type CommonNostrEventId = string; /** Input for `common.encodeNip19`. */ type CommonNip19EncodeInput = { type: 'npub' | 'note'; hex: string; } | { type: 'nprofile'; pubkey: CommonHexPubkey; relays?: string[]; } | { type: 'nevent'; eventId: CommonNostrEventId; relays?: string[]; author?: CommonHexPubkey; kind?: number; } | { type: 'naddr'; identifier: string; pubkey: CommonHexPubkey; kind: number; relays?: string[]; } | { type: 'nrelay'; relay: string; }; /** Result of `common.encodeNip19`. */ interface CommonNip19EncodeResult { /** Whether the value was encoded. */ ok: boolean; /** Encoded NIP-19 value. */ value?: string; /** Encoded NIP-19 entity type. */ nip19Type?: CommonNip19Type; /** Error reason when encoding failed. */ error?: string; } /** Result of `common.decodeNip19`. */ interface CommonNip19DecodeResult { /** Whether the value was decoded. */ ok: boolean; /** Decoded NIP-19 entity type. */ nip19Type?: CommonNip19Type; /** Hex field for `npub` and `note`. */ hex?: string; /** Profile/address public key. */ pubkey?: CommonHexPubkey; /** Event id for `nevent`. */ eventId?: CommonNostrEventId; /** Address identifier for `naddr`. */ identifier?: string; /** Relay hints carried by the value. */ relays?: string[]; /** Author hint for `nevent`. */ author?: CommonHexPubkey; /** Kind hint for `nevent` / `naddr`. */ kind?: number; /** Relay URL for `nrelay`. */ relay?: string; /** Error reason when decoding failed. */ error?: string; } /** Target accepted by `common.getProfile`: hex pubkey, npub, or nprofile. */ type CommonProfileTarget = string; /** Kind 0 metadata fields returned by `common.getProfile`. */ interface CommonProfileData { name?: string; displayName?: string; about?: string; picture?: string; banner?: string; nip05?: string; lud16?: string; website?: string; [key: string]: unknown; } /** Result of `common.getProfile`. */ interface CommonProfileResult { /** Whether the lookup completed. */ ok: boolean; /** Resolved profile public key. */ pubkey: CommonHexPubkey; /** Latest profile metadata, or null when none was found. */ profile?: CommonProfileData | null; /** Relay-owned kind 0 event result backing the profile data. */ result?: RelayEventResult; /** Error reason when lookup failed. */ error?: string; } /** Result of `common.follows`. */ interface CommonFollowsResult { /** Whether follows were resolved. */ ok: boolean; /** Followed public keys, normalized to hex. */ pubkeys: CommonHexPubkey[]; /** Error reason when follows could not be resolved. */ error?: string; } /** Result shared by modifying social actions. */ interface CommonActionResult { /** Whether the shell completed the action. */ ok: boolean; /** Published event id, when available. */ eventId?: CommonNostrEventId; /** Published event, when available. */ event?: NostrEvent; /** Error reason when the action failed or was denied. */ error?: string; } /** Reaction content accepted by `common.react`. */ type CommonReaction = '+' | '-' | (string & {}); /** NIP-56 report reason accepted by `common.report`. */ type CommonReportReason = 'nudity' | 'malware' | 'profanity' | 'illegal' | 'spam' | 'impersonation' | 'other'; /** Report a Nostr event. */ interface CommonEventReportTarget { type: 'event'; id: CommonNostrEventId; pubkey?: CommonHexPubkey; relay?: string; } /** Report a public key. */ interface CommonPubkeyReportTarget { type: 'pubkey'; pubkey: CommonHexPubkey | string; relay?: string; } /** Target accepted by `common.report`. */ type CommonReportTarget = CommonEventReportTarget | CommonPubkeyReportTarget; /** Hex-encoded Nostr public key. */ type DmHexPubkey = string; /** Unix timestamp in seconds. */ type DmTimestamp = number; /** Current runtime direct-message availability. */ interface DmStatus { available: boolean; ownerPubkey?: DmHexPubkey; implementations: string[]; capabilities: string[]; } /** Query parameters for normalized DM conversation summaries. */ interface DmConversationQuery { cursor?: string; limit?: number; } /** Public peer metadata safe for napplet display. */ interface DmPeer { pubkey: DmHexPubkey; label?: string; avatar?: string; } /** A normalized direct or group conversation summary. */ interface DmConversation { id: string; kind: 'direct' | 'group'; participants: DmPeer[]; subject?: string; unread: number; updatedAt?: DmTimestamp; } /** Page of normalized conversation summaries. */ interface DmConversationPage { conversations: DmConversation[]; cursor?: string; } /** Query parameters for message history within one conversation. */ interface DmMessageQuery { conversationId: string; cursor?: string; limit?: number; } /** Runtime-normalized delivery state for a DM message. */ type DmMessageStatus = 'sent' | 'delivered' | 'received' | 'failed'; /** Normalized cleartext message visible to the napplet by runtime policy. */ interface DmMessage { id: string; conversationId: string; senderPubkey: DmHexPubkey; createdAt: DmTimestamp; content: string; status: DmMessageStatus; } /** Page of normalized messages for one conversation. */ interface DmMessagePage { messages: DmMessage[]; cursor?: string; } /** Request to send a direct message. */ interface DmSendRequest { conversationId?: string; recipients: DmHexPubkey[]; content: string; clientMessageId?: string; } /** Result of a runtime-mediated send. */ interface DmSendResult { ok: boolean; message: DmMessage; } /** Request to start live DM delivery. */ interface DmSubscribeRequest { conversationId?: string; } /** Runtime-assigned live subscription identity. */ interface DmSubscription { subscriptionId: string; } /** Generic boolean acknowledgement used by `dm.unsubscribe`. */ interface DmOk { ok: boolean; } /** Error payload returned when a DM request cannot be fulfilled. */ interface DmError { error: string; } /** NAP-COUNT filter. */ type CountFilter = NostrFilter; /** Count query options. */ interface CountOptions { /** Exact-count preference. */ approximate?: boolean; /** HyperLogLog response preference. */ hll?: boolean; } /** Count query result. */ interface CountResult { /** Whether the query succeeded. */ ok: boolean; /** Aggregate matching-event count. */ count?: number; /** True when the count is approximate. */ approximate?: boolean; /** HyperLogLog value. */ hll?: string; /** Relays used for the query. */ relays?: string[]; /** Machine-readable error code. */ error?: string; /** Human-readable refusal or failure reason. */ reason?: string; } /** * NIP-01 relay operations: subscribe to events, publish events, one-shot queries. * Routes through the shell's relay pool via postMessage. */ interface RelayApi { /** * Open a live NIP-01 subscription through the shell's relay pool. * @param filters One or more NIP-01 subscription filters * @param onEvent Called for each matching event result * @param onEose Called when the shell signals end of stored events (EOSE) * @param options Optional: `{ relay, group }` for NIP-29 scoped relay subscriptions * @returns A Subscription handle with a `close()` method */ subscribe(filters: NostrFilter | NostrFilter[], onEvent: (result: RelayEventResult) => void, onEose: () => void, options?: { relay?: string; group?: string; }): Subscription; /** * Sign and publish a Nostr event through the shell. * @param template Unsigned event template * @param options Optional: `{ relay: true }` to publish via scoped relay * @returns The signed NostrEvent after successful publication */ publish(template: EventTemplate, options?: { relay?: boolean; }): Promise; /** * Publish an encrypted Nostr event through the shell. * The shell encrypts content, signs the event, and broadcasts it. * @param template Unsigned event template * @param recipient Hex-encoded recipient public key * @param encryption Encryption scheme: 'nip44' (default) or 'nip04' * @returns The signed encrypted NostrEvent after successful publication */ publishEncrypted(template: EventTemplate, recipient: string, encryption?: 'nip44' | 'nip04'): Promise; /** * One-shot query: subscribe, collect events until EOSE, then resolve. * @param filters NIP-01 subscription filters * @returns Promise resolving to array of matching event results */ query(filters: NostrFilter | NostrFilter[]): Promise; } /** * Inter-napplet pubsub: broadcast and receive INC events through the shell. */ interface IncApi { /** * Broadcast an INC message to other napplets via the shell. * @param topic An opaque stable topic or a convention URI such as * `napplet:profile/open?pubkey=abc123` * @param payload Optional opaque message payload */ emit(topic: string, payload?: unknown): void; /** * Subscribe to INC events on a specific topic. * @param topic The exact topic value to listen for * @param callback Called with one runtime-attested INC event * @returns A Subscription handle with a `close()` method */ on(topic: string, callback: (event: IncEvent) => void): Subscription; /** Point-to-point channel operations. */ channel: IncChannelApi; } /** A topic event delivered by the runtime. */ interface IncEvent { /** Exact subscribed topic. */ topic: string; /** Runtime-attested emitting napplet dTag. */ sender: string; /** Optional opaque payload. */ payload?: unknown; } /** A message delivered through an open INC channel. */ interface ChannelEvent { /** Shell-assigned opaque channel identifier. */ channelId: string; /** Runtime-attested sender dTag. */ sender: string; /** Optional opaque payload. */ payload?: unknown; } /** Terminal channel notification retained for late handlers. */ interface ChannelClosed { /** Shell-assigned opaque channel identifier. */ channelId: string; /** Optional runtime-supplied close reason. */ reason?: string; } /** Informational snapshot of an active channel. */ interface ChannelInfo { /** Shell-assigned opaque channel identifier. */ id: string; /** Peer napplet dTag. */ peer: string; } /** Symmetric handle exposed to both endpoints of an INC channel. */ interface ChannelHandle extends ChannelInfo { /** Send an opaque payload to the peer. */ emit(payload?: unknown): void; /** Receive peer events. */ on(callback: (event: ChannelEvent) => void): Subscription; /** Receive the retained terminal close record. */ onClosed(callback: (event: ChannelClosed) => void): Subscription; /** Close the channel for both endpoints. */ close(): void; } /** Point-to-point INC channel operations. */ interface IncChannelApi { /** Open a channel to a target napplet dTag. */ open(target: string): Promise; /** Receive inbound channel handles. */ onOpened(callback: (handle: ChannelHandle) => void): Subscription; /** List active inbound and outbound channels. */ list(): Promise; /** Send a payload to all open channel peers. */ broadcast(payload?: unknown): void; } /** * Per-instance napplet storage: identical surface to the shared {@link StorageApi} * methods, but every request is scoped to this napplet instance rather than * shared across all instances of the same napplet type. * * Reached via `window.napplet.storage.instance.*`. On the wire each call sets * `scope: "instance"`; the shared top-level methods omit `scope` entirely. * * Non-normative summary — defer to NAP-STORAGE (napplet/naps) for the * authoritative scope semantics. */ interface NappletInstanceStorage { /** * Retrieve a per-instance value by key. Returns null if the key does not exist. * @param key The storage key * @returns The stored string value, or null if not found */ getItem(key: string): Promise; /** * Store a per-instance key-value pair. * @param key The storage key * @param value The string value to store * @throws If the napplet exceeds its storage quota */ setItem(key: string, value: string): Promise; /** * Remove a per-instance key. * @param key The storage key to remove */ removeItem(key: string): Promise; /** * List all per-instance keys for this napplet instance. * @returns Array of storage key strings */ keys(): Promise; } /** * Napplet-scoped storage: async localStorage-like API proxied through the shell. * Each napplet's storage is isolated by identity — napplets cannot read each other's data. */ interface StorageApi { /** * Retrieve a stored value by key. Returns null if the key does not exist. * @param key The storage key * @returns The stored string value, or null if not found */ getItem(key: string): Promise; /** * Store a key-value pair. * @param key The storage key * @param value The string value to store * @throws If the napplet exceeds its storage quota */ setItem(key: string, value: string): Promise; /** * Remove a stored key. * @param key The storage key to remove */ removeItem(key: string): Promise; /** * List all keys stored by this napplet. * @returns Array of storage key strings */ keys(): Promise; /** * Per-instance storage: same surface as the shared methods above, but scoped * to this napplet instance. Sets `scope: "instance"` on the wire; the shared * top-level methods emit no `scope` field. * * Non-normative summary — defer to NAP-STORAGE (napplet/naps). */ instance: NappletInstanceStorage; } /** * Keyboard forwarding and action keybindings: register named actions the shell * can bind to keys, forward unbound keystrokes to the shell, listen for * shell-triggered actions locally. * * @example * ```ts * // Register an action the shell can bind to a key: * const result = await window.napplet.keys.registerAction({ * id: 'editor.save', label: 'Save', defaultKey: 'Ctrl+S', * }); * * // Listen for the bound key locally: * const sub = window.napplet.keys.onAction('editor.save', () => { * console.log('Save triggered!'); * }); * * // Unregister when no longer needed: * window.napplet.keys.unregisterAction('editor.save'); * ``` */ interface KeysApi { /** * Declare a named action that the shell can bind to a key. * The shell decides the actual binding; `defaultKey` is a hint only. * @param action The action to register (id, label, optional defaultKey) * @returns The assigned binding, if any */ registerAction(action: { id: string; label: string; defaultKey?: string; }): Promise<{ actionId: string; binding?: string; }>; /** * Remove a previously registered action. The shell removes any binding * and updates the suppress list. * @param actionId The action to unregister */ unregisterAction(actionId: string): void; /** * Register a local handler for when a bound key is pressed. * This is NOT a wire message — the shim intercepts the key locally * and invokes the callback with zero latency. * @param actionId The action to listen for * @param callback Called when the action is triggered * @returns A Subscription with `close()` to stop listening */ onAction(actionId: string, callback: () => void): Subscription; } /** * Media session control: create sessions, report state and metadata, * declare capabilities, receive commands from the shell. * * @example * ```ts * // Create a media session: * const { sessionId } = await window.napplet.media.createSession({ * owner: 'napplet', * metadata: { title: 'My Song', artist: 'The Artist' }, * }); * * // Report playback state: * window.napplet.media.reportState(sessionId, { * status: 'playing', position: 42.5, duration: 240, * }); * * // Listen for shell commands: * window.napplet.media.onCommand(sessionId, (action, value) => { * if (action === 'pause') player.pause(); * }); * ``` */ interface MediaApi { /** * Create a new media session with the shell. * @param options Ownership-aware session options. * @returns The shell result with canonical sessionId and owner, or error. */ createSession(options: MediaSessionCreate): Promise; /** * Update metadata for an existing session. Partial updates supported. * @param sessionId The session to update * @param metadata Partial metadata fields to update */ updateSession(sessionId: string, metadata: Partial): void; /** * Destroy a media session. * @param sessionId The session to destroy */ destroySession(sessionId: string): void; /** * Report current playback state for a session. * @param sessionId The session to report state for * @param state Current playback state */ reportState(sessionId: string, state: MediaState): void; /** * Declare which media actions the session currently supports. * @param sessionId The session to update capabilities for * @param actions Currently supported actions */ reportCapabilities(sessionId: string, actions: MediaAction[]): void; /** * Send a command to the current playback owner. * @param sessionId The session to control * @param action The media action to request * @param value Optional value for seek/volume */ sendCommand(sessionId: string, action: MediaAction, value?: number): void; /** * Listen for media commands from the shell. * @param sessionId The session to listen for commands on * @param callback Called with (action, value?) when a command is received * @returns A Subscription with `close()` to stop listening */ onCommand(sessionId: string, callback: (action: MediaAction, value?: number) => void): Subscription; /** * Listen for shell-reported playback state for shell-owned sessions. * @param sessionId The session to listen for state on * @param callback Called with playback state * @returns A Subscription with `close()` to stop listening */ onState(sessionId: string, callback: (state: MediaState) => void): Subscription; /** * Listen for shell-reported capabilities for shell-owned sessions. * @param sessionId The session to listen for capabilities on * @param callback Called with available actions * @returns A Subscription with `close()` to stop listening */ onCapabilities(sessionId: string, callback: (actions: MediaAction[]) => void): Subscription; /** * Listen for the shell's media control list. * @param sessionId The session to associate controls with * @param callback Called with the shell's supported controls * @returns A Subscription with `close()` to stop listening */ onControls(sessionId: string, callback: (controls: MediaAction[]) => void): Subscription; } /** * Shell-rendered notifications: send notifications, set badge counts, * register channels, request permission, listen for user interaction. * * @example * ```ts * // Send a notification: * const { notificationId } = await window.napplet.notify.send({ * title: 'New message', body: 'Alice: hey!', priority: 'normal', * }); * * // Set badge count: * window.napplet.notify.badge(3); * * // Listen for action clicks: * window.napplet.notify.onAction((notificationId, actionId) => { * if (actionId === 'reply') openReply(notificationId); * }); * ``` */ interface NotifyApi { /** * Send a notification to the shell. * @param notification Notification payload (title required) * @returns The shell-assigned notificationId */ send(notification: { title: string; body?: string; icon?: string; actions?: { id: string; label: string; }[]; channel?: string; priority?: 'low' | 'normal' | 'high' | 'urgent'; }): Promise<{ notificationId: string; }>; /** * Dismiss a notification by ID. Fire-and-forget. * @param notificationId The notification to dismiss */ dismiss(notificationId: string): void; /** * Set the badge count for this napplet. Pass 0 to clear. * @param count Badge count */ badge(count: number): void; /** * Register a notification channel for per-category user control. * @param channel Channel definition */ registerChannel(channel: { channelId: string; label: string; description?: string; defaultPriority?: 'low' | 'normal' | 'high' | 'urgent'; }): void; /** * Request permission to send notifications. * @param channel Optional channel to request permission for * @returns Whether permission was granted */ requestPermission(channel?: string): Promise<{ granted: boolean; }>; /** * Listen for action button clicks on notifications. * @param callback Called with (notificationId, actionId) * @returns A Subscription with `close()` to stop listening */ onAction(callback: (notificationId: string, actionId: string) => void): Subscription; /** * Listen for notification body clicks. * @param callback Called with (notificationId) * @returns A Subscription with `close()` to stop listening */ onClicked(callback: (notificationId: string) => void): Subscription; /** * Listen for notification dismissals. * @param callback Called with (notificationId, reason?) * @returns A Subscription with `close()` to stop listening */ onDismissed(callback: (notificationId: string, reason?: string) => void): Subscription; /** * Listen for the shell's notification capability list. * @param callback Called with supported controls * @returns A Subscription with `close()` to stop listening */ onControls(callback: (controls: ('toasts' | 'badges' | 'actions' | 'channels' | 'system')[]) => void): Subscription; } /** * Native ContextVM bridge (NAP-CVM): MCP-over-Nostr access mediated by the shell. * * ContextVM transports Model Context Protocol JSON-RPC over Nostr relays using * public-key server addressing and encrypted relay events. The shell owns all * transport details -- relay routing, signing, encryption, JSON-RPC correlation, * MCP initialization, per-napplet policy, and optional payment prompts. Napplets * supply a server identity (`pubkey` + optional relay hints) and the MCP * operation they want; they receive MCP results, never ContextVM private keys, * relay credentials, or direct socket access. * * @example * ```ts * if (window.napplet.cvm) { * const servers = await window.napplet.cvm.discover({ search: 'relay' }); * const tools = await window.napplet.cvm.listTools(servers[0]); * const result = await window.napplet.cvm.callTool(servers[0], tools[0].name, {}); * } * ``` */ interface CvmApi { /** * Discover public ContextVM servers known to the shell. * @param query Optional discovery filter (search, kinds, relays, limit) * @returns Promise resolving to the discovered servers */ discover(query?: CvmDiscoverQuery): Promise; /** * Send a raw MCP JSON-RPC message to a ContextVM server and resolve with the * matching MCP response. The shell wraps the message in ContextVM transport. * @param server Target ContextVM server * @param message MCP JSON-RPC message to deliver * @param options Optional per-request options * @returns Promise resolving to the MCP response message */ request(server: CvmServerRef, message: McpMessage, options?: CvmRequestOptions): Promise; /** * List the tools exposed by a ContextVM server (MCP `tools/list`). * @param server Target ContextVM server * @param options Optional per-request options */ listTools(server: CvmServerRef, options?: CvmRequestOptions): Promise; /** * Call a tool on a ContextVM server (MCP `tools/call`). * @param server Target ContextVM server * @param name Tool name * @param args Tool arguments * @param options Optional per-request options */ callTool(server: CvmServerRef, name: string, args?: Record, options?: CvmRequestOptions): Promise; /** * List the resources exposed by a ContextVM server (MCP `resources/list`). * @param server Target ContextVM server * @param options Optional per-request options */ listResources(server: CvmServerRef, options?: CvmRequestOptions): Promise; /** * Read a resource from a ContextVM server (MCP `resources/read`). * Resolves with the first content entry per the NAP-CVM API surface. * @param server Target ContextVM server * @param uri Resource URI * @param options Optional per-request options */ readResource(server: CvmServerRef, uri: string, options?: CvmRequestOptions): Promise; /** * Close shell-maintained session state for a server (subscriptions, cached * initialization state, pending correlation records). * @param server Server whose session should be torn down */ close(server: CvmServerRef): Promise; /** * Listen for server-pushed MCP messages (`cvm.event`) -- notifications and * unsolicited server messages not correlated to a single request. * @param callback Called with `(server, message)` for each server event * @returns A Subscription with `close()` to stop listening */ onEvent(callback: (server: CvmServerRef, message: McpMessage) => void): Subscription; /** * Shell-curated ContextVM tool families. The shell selects providers, * verifies schema hashes, applies cache/payment policy, and performs calls. */ registry: { /** * List registry families known to the shell. * @param query Optional family/search/schema filter */ list(query?: CvmRegistryQuery): Promise; /** * Test whether the shell can call a registry family. * @param family Registry family name * @param options Optional schema/provider constraints */ has(family: string, options?: CvmRegistryOptions): Promise; /** * Describe the shell-selected registry family entry. * @param family Registry family name * @param options Optional schema/provider constraints */ describe(family: string, options?: CvmRegistryOptions): Promise; /** * Call a tool on the shell-selected provider for a registry family. * @param family Registry family name * @param tool Tool name inside the family * @param args Tool arguments * @param options Optional schema/provider/cache/payment constraints */ call(family: string, tool: string, args?: JsonObject, options?: CvmRegistryCallOptions): Promise; }; } /** * Outbox-aware relay routing (NAP-OUTBOX): the napplet supplies Nostr filters * and intent; the shell discovers the correct relays (NIP-65 write/read relays, * fallbacks, relay intelligence), queries them, deduplicates events by id, * validates signatures, and streams updates. The shell owns relay discovery, * routing, fallback, deduplication, signing, and publish fanout policy. * * Use this instead of NAP-RELAY when relay selection is part of result * correctness (reading an author's notes from their write relays, publishing to * the user's write relays, fanning a directed event to recipient inbox relays). * * @example * ```ts * if (window.napplet.outbox) { * const { events } = await window.napplet.outbox.query( * [{ authors: ['ab12...'], kinds: [1], limit: 20 }], * { authors: ['ab12...'], timeoutMs: 3000 }, * ); * } * ``` */ interface OutboxApi { /** * Fetch one event by ID through shell-owned outbox routing. The shell validates * that any returned event matches the requested id and has a valid signature. * @param eventId Event id to fetch * @param options Optional author/relay hints and timeout * @returns Promise resolving to the outbox event result */ getEvent(eventId: string, options?: OutboxEventOptions): Promise; /** * Perform a one-shot outbox-aware query. The shell resolves relays, queries * them, deduplicates by event id, validates signatures, and returns * `RelayEventResult` records. Partial results carry `incomplete: true`; a * query-level failure arrives as inline `error`. * @param filters NIP-01 filter or filters * @param options Optional query options (authors, relays, limit, timeoutMs) * @returns Promise resolving to the outbox result */ query(filters: NostrFilter | NostrFilter[], options?: OutboxQueryOptions): Promise; /** * Open a live outbox-aware subscription. The shell may add/remove relay * connections as NIP-65 relay lists change and streams until `close()` or * `outbox.closed`. * @param filters NIP-01 filter or filters * @param options Optional subscribe options * @returns An OutboxSubscription handle with `on(...)` and `close()` */ subscribe(filters: NostrFilter | NostrFilter[], options?: OutboxSubscribeOptions): OutboxSubscription; /** * Publish a shell-signed event using outbox-aware relay fanout. * @param template Unsigned event template; the shell signs before fanout * @param options Optional publish fanout (`relays`, `toOutbox`, `toInboxes`); * `toOutbox` defaults to true when omitted * @returns Promise resolving to the outbox publish result */ publish(template: EventTemplate, options?: OutboxPublishOptions): Promise; /** * Resolve the relay plan the shell would use for a read/write target. * Useful for diagnostics/UI; prefer query/subscribe/publish for access. * @param target The read/write target (authors/pubkey, direction) * @returns Promise resolving to the relay plan */ resolveRelays(target: OutboxTarget): Promise; } /** * Shell-mediated file/blob upload (NAP-UPLOAD): the napplet hands the shell raw * bytes plus upload intent; the shell selects a storage server, signs the rail * authorization (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP * upload, and returns a stable URL plus NIP-94 integrity metadata. The shell is * the policy and consent boundary; napplets never receive signing keys, server * credentials, or direct network access. * * @example * ```ts * if (window.napplet.upload) { * const result = await window.napplet.upload.upload({ data: blob, filename: 'pic.png' }); * if (result.status === 'complete') attach(result.url, result.nip94); * } * ``` */ interface UploadApi { /** * Inspect upload rails and coarse policy limits the runtime is willing to * disclose. Advisory only; callers can upload without a preflight. * @returns Promise resolving to the upload info snapshot. */ info(): Promise; /** * Upload bytes. The shell handles consent, server selection, rail auth * signing, and the HTTP upload, then resolves with the initial result. * Large/async uploads resolve with `status: "uploading"` and report progress * via `onStatus`. Resolves with the result even on `ok: false` * (created-then-failed/cancelled); rejects only on a top-level error. * @param request The upload request (Blob/ArrayBuffer bytes + intent) * @returns Promise resolving to the initial upload result */ upload(request: UploadRequest): Promise; /** * Get the latest known status for a prior upload, including progress counters. * @param uploadId The shell-generated id from a prior upload * @returns Promise resolving to the latest status */ status(uploadId: string): Promise; /** * Register for shell-pushed status updates (progress, complete/failed). * @param handler Called with each new UploadStatus * @returns A Subscription with `close()` to stop listening */ onStatus(handler: (status: UploadStatus) => void): Subscription; } /** * Archetype intent dispatch (NAP-INTENT): invoke another napplet through an * authoritative convention URI without addressing it directly. The runtime * derives the archetype, action, queryless convention identity, and any * query-derived payload before resolving an installed, user-authorized handler. * @example * ```ts * if (window.napplet.intent) { * const { available } = await window.napplet.intent.available('note'); * if (available) { * await window.napplet.intent.open( * 'profile', * { pubkey: 'abc123' }, * { convention: 'napplet:profile/open' }, * ); * } * } * ``` */ interface IntentApi { /** * Dispatch an intent request by archetype. * @param request Archetype, optional action, convention, payload, and hints * @returns Promise resolving to the dispatch result */ invoke(request: IntentRequest): Promise; /** * Convenience sugar for `invoke({ archetype, action: "open", payload, ...opts })`. * @param archetype Role slug to open * @param payload Optional opaque payload * @param opts Optional convention, handler preference, and behavior hints * @returns Promise resolving to the dispatch result */ open(archetype: string, payload?: unknown, opts?: IntentOpenOptions): Promise; /** * Whether the runtime can currently satisfy `archetype`, with candidates and * the actions and conventions each supports. Sourced from the installed * catalog. * @param archetype Role slug to check * @returns Promise resolving to the archetype availability */ available(archetype: string): Promise; /** * Availability for every archetype the runtime can currently satisfy. * @returns Promise resolving to availability for each satisfiable archetype */ handlers(): Promise; /** * Register for shell-pushed availability updates (install/remove/default change). * @param handler Called with each updated IntentAvailability * @returns A Subscription with `close()` to stop listening */ onChanged(handler: (availability: IntentAvailability) => void): Subscription; } /** * Shell-mediated link opening (NAP-LINK): the napplet asks the shell to open an * external URL for the user. The shell owns navigation, policy, prompting, * opener isolation, and browser context. The napplet receives no network * access, opener authority, or fetched bytes. * * @example * ```ts * if (window.napplet.link) { * const result = await window.napplet.link.open('https://example.com/post/123', { label: 'Read post' }); * if (result.status === 'denied') showInlineFallback(); * } * ``` */ interface LinkApi { /** * Request that the shell open an external URL for the user. * @param url Absolute URL to open * @param options Optional prompt/display hints * @returns Promise resolving to the shell's open/deny status */ open(url: string, options?: LinkOpenOptions): Promise; } /** * Runtime-mediated event counts (NAP-COUNT): the napplet supplies one or more * NIP-01 filters, and the runtime returns aggregate count metadata without * sending matching event payloads. The runtime owns relay choice, NIP-45 COUNT * support, indexes, caches, approximation, and refusal policy. * * @example * ```ts * if (window.napplet.count) { * const { count } = await window.napplet.count.query({ kinds: [7], '#e': [eventId] }); * } * ``` */ interface CountApi { /** * Count events matching a non-empty NIP-01 filter array. * @param filters One NIP-01 filter or a non-empty array of filters * @param options Optional approximation and HyperLogLog hints * @returns Promise resolving to the runtime count result */ query(filters: CountFilter | CountFilter[], options?: CountOptions): Promise; } /** * Runtime-mediated NIP-51 list mutation (NAP-LISTS): the napplet names a list * and semantic items to add/remove; the runtime owns current-event lookup, * kind/type mapping, tag formatting, private item encryption, preservation, * signing, and publishing. * * @example * ```ts * if (window.napplet.lists) { * await window.napplet.lists.add({ type: 'mute-list' }, [ * { itemType: 'pubkey', value: 'abc123...' }, * ]); * } * ``` */ interface ListsApi { /** * Return the NIP-51 list kinds/types this runtime supports. * @returns Promise resolving to supported list descriptions */ supported(): Promise; /** * Add items to a runtime-supported NIP-51 list. * @param list List reference by kind or derived type * @param items Items to add * @param options Optional create/metadata hints * @returns Promise resolving to the mutation result */ add(list: ListRef, items: ListItem[], options?: ListOptions): Promise; /** * Remove items from a runtime-supported NIP-51 list. * @param list List reference by kind or derived type * @param items Items to remove * @param options Optional runtime hints * @returns Promise resolving to the mutation result */ remove(list: ListRef, items: ListItem[], options?: ListOptions): Promise; } /** * Common social actions (NAP-COMMON): shell-mediated NIP-19 helpers, profile * lookup, follows, and signed social actions. The shell owns identity, consent, * event construction, signing, publishing, relay access, and NIP-19 handling. * * @example * ```ts * if (window.napplet.common) { * const { pubkeys } = await window.napplet.common.follows(); * await window.napplet.common.react(noteId, '+'); * } * ``` */ interface CommonApi { /** * Encode a supported public NIP-19 value. `nsec` is intentionally unsupported. * @param input Structured NIP-19 encode input * @returns Promise resolving to the shell encode result */ encodeNip19(input: CommonNip19EncodeInput): Promise; /** * Decode a supported public NIP-19 value. `nsec` is intentionally unsupported. * @param value NIP-19 value to decode * @returns Promise resolving to normalized decoded fields */ decodeNip19(value: string): Promise; /** * Resolve a profile by hex pubkey, npub, or nprofile. * @param target Profile target * @returns Promise resolving to latest profile data when available */ getProfile(target: CommonProfileTarget): Promise; /** * Return the shell user's followed pubkeys as hex. * @returns Promise resolving to followed pubkeys */ follows(): Promise; /** * Ask the shell to follow one or more npub targets. * @param pubkeys Npub targets to follow * @returns Promise resolving to the action result */ follow(...pubkeys: string[]): Promise; /** * Ask the shell to unfollow one or more npub targets. * @param pubkeys Npub targets to unfollow * @returns Promise resolving to the action result */ unfollow(...pubkeys: string[]): Promise; /** * React to a native Nostr event. * @param targetEventId Event id to react to * @param reaction Reaction content * @param customEmojiHref Optional custom emoji URL * @returns Promise resolving to the action result */ react(targetEventId: string, reaction: CommonReaction, customEmojiHref?: string): Promise; /** * Report an event or pubkey with a NIP-56 reason. * @param target Structured report target * @param reason NIP-56 report reason * @param text Report text * @returns Promise resolving to the action result */ report(target: CommonReportTarget, reason: CommonReportReason, text: string): Promise; } /** * Runtime-mediated serial device access (NAP-SERIAL): the napplet asks the shell * to select and open a user-approved serial session, writes byte arrays to that * session, and receives shell-pushed state/data/close events. The shell owns * device selection, permissions, raw port handles, streams, OS paths, read loops, * and lifecycle policy. * * @example * ```ts * if (window.napplet.serial) { * const { session } = await window.napplet.serial.open({ options: { baudRate: 115200 } }); * await window.napplet.serial.write(session.id, [112, 105, 110, 103, 10]); * } * ``` */ interface SerialApi { /** * Ask the runtime to select and open a serial session. * @param request Filters, options, and optional chooser label * @returns Promise resolving to the runtime-assigned serial open result */ open(request: SerialOpenRequest): Promise; /** * Write bytes to an open serial session. * @param sessionId Runtime-assigned serial session id * @param data Byte values to write * @returns Promise resolving after the runtime acknowledges the write */ write(sessionId: string, data: Uint8Array | number[]): Promise; /** * Close an open serial session. * @param sessionId Runtime-assigned serial session id * @param reason Optional reason for the close request * @returns Promise resolving after the runtime acknowledges the close */ close(sessionId: string, reason?: string): Promise; /** * Register for shell-pushed serial events. * @param handler Called with each serial event * @returns A Subscription with `close()` to stop listening */ onEvent(handler: (event: SerialEvent) => void): Subscription; } /** * Shell-mediated virtual filesystem access (NAP-FS): the napplet sees only * virtual paths, directory entries, coarse metadata, user-mediated picker * results, and advisory change events. The runtime owns host paths, mounts, * backing store, policy, and authorization of every operation. * * `info()` is advisory discovery, not an authorization token -- permissions can * change mid-session and any operation can still fail, so handle rejections * even when `info()` advertised a matching permission. * * @example * ```ts * if (window.napplet.fs) { * const { roots } = await window.napplet.fs.info(); * const picked = await window.napplet.fs.pickFile({ accept: [{ extension: '.md' }] }); * const bytes = await window.napplet.fs.read(picked.entries[0].path); * await window.napplet.fs.write('/shared/copy.txt', bytes.data, { mode: 'replace' }); * const entries = await window.napplet.fs.list('/shared'); * const watchId = await window.napplet.fs.watch('/shared', { recursive: true }); * window.napplet.fs.onChanged((change) => refresh(change.path)); * } * ``` */ interface FsApi { /** * Discover visible roots, coarse root permissions, and runtime limits. * @returns Promise resolving to advisory filesystem discovery data */ info(): Promise; /** * Ask the runtime to let the user select one file. * @param options Optional picker hints * @returns Promise resolving to picked virtual filesystem paths */ pickFile(options?: FsPickOptions): Promise; /** * Ask the runtime to let the user select one or more files. * @param options Optional picker hints * @returns Promise resolving to picked virtual filesystem paths */ pickFiles(options?: FsPickOptions): Promise; /** * Ask the runtime to let the user select one directory. * @param options Optional picker hints * @returns Promise resolving to picked virtual filesystem paths */ pickDirectory(options?: FsPickOptions): Promise; /** * Ask the runtime to let the user select or name one file destination. * @param options Optional picker hints * @returns Promise resolving to picked virtual filesystem paths */ pickSaveFile(options?: FsPickOptions): Promise; /** * Read coarse metadata for a visible file or directory. * @param path Virtual absolute path of the entry * @returns Promise resolving to the entry metadata */ stat(path: string): Promise; /** * List the direct children of a visible directory. Ordering is unspecified. * @param path Virtual absolute path of the directory * @returns Promise resolving to the directory entries */ list(path: string): Promise; /** * Read bytes from a visible file. `data` is RFC 4648 standard padded base64 text. * @param path Virtual absolute path of the file * @param options Optional range read controls * @returns Promise resolving to the read result */ read(path: string, options?: FsReadOptions): Promise; /** * Write bytes to a visible file. `data` is RFC 4648 standard padded base64 text. * @param path Virtual absolute path of the file * @param data Decoded bytes encoded as standard padded base64 text * @param options Optional write mode and preconditions * @returns Promise resolving to the write result */ write(path: string, data: string, options?: FsWriteOptions): Promise; /** * Create a directory. * @param path Virtual absolute path of the directory to create * @param options Optional recursive parent creation * @returns Promise resolving once the runtime acknowledges the creation */ mkdir(path: string, options?: FsMkdirOptions): Promise; /** * Remove a file or directory. * @param path Virtual absolute path of the entry to remove * @param recursive Remove a non-empty directory and its authorized descendants * @returns Promise resolving once the runtime acknowledges the removal */ remove(path: string, recursive?: boolean): Promise; /** * Move or rename a file or directory. * @param fromPath Virtual absolute source path * @param toPath Virtual absolute destination path * @returns Promise resolving once the runtime acknowledges the move */ move(fromPath: string, toPath: string): Promise; /** * Start an advisory watch on a visible path. * @param path Virtual absolute path to watch * @param options Optional recursive descendant coverage * @returns Promise resolving to the runtime-generated watch id */ watch(path: string, options?: FsWatchOptions): Promise; /** * Stop a watch. Unknown ids may be treated as successful no-ops. * @param watchId Runtime-generated watch id * @returns Promise resolving once the runtime acknowledges the request */ unwatch(watchId: string): Promise; /** * Register for runtime-pushed filesystem change events. * @param handler Called with each advisory change * @returns A Subscription with `close()` to stop listening */ onChanged(handler: (change: FsChange) => void): Subscription; } /** * Runtime-mediated direct messages (NAP-DM): the napplet presents DM UI while * the shell owns signing, encryption, relay routing, storage, key/session * state, and policy. * * @example * ```ts * if (window.napplet.dm) { * const status = await window.napplet.dm.status(); * if (status.available) { * const { conversations } = await window.napplet.dm.conversations({ limit: 20 }); * const sub = await window.napplet.dm.subscribe({ conversationId: conversations[0]?.id }); * window.napplet.dm.onMessage((message) => render(message)); * await window.napplet.dm.unsubscribe(sub.subscriptionId); * } * } * ``` */ interface DmApi { /** * Get current DM availability and advisory runtime implementation labels. * @returns Promise resolving to the runtime DM status */ status(): Promise; /** * Fetch normalized conversation summaries visible to this napplet. * @param query Optional cursor and limit * @returns Promise resolving to a page of conversations */ conversations(query?: DmConversationQuery): Promise; /** * Fetch normalized message history for one conversation. * @param query Conversation id plus optional cursor and limit * @returns Promise resolving to a page of messages */ messages(query: DmMessageQuery): Promise; /** * Ask the runtime to send a direct message. * @param request Recipients, content, and optional conversation/client ids * @returns Promise resolving to the normalized sent message result */ send(request: DmSendRequest): Promise; /** * Start live delivery for one conversation or all visible conversations. * @param request Optional conversation scope * @returns Promise resolving to the runtime subscription id */ subscribe(request?: DmSubscribeRequest): Promise; /** * Stop a live delivery subscription. * @param subscriptionId Runtime subscription id from subscribe() * @returns Promise resolving to the runtime acknowledgement */ unsubscribe(subscriptionId: string): Promise; /** * Register for shell-pushed `dm.message` deliveries. * @param handler Called with each message and its runtime subscription id * @returns A Subscription with `close()` to stop listening */ onMessage(handler: (message: DmMessage, subscriptionId: string) => void): Subscription; } /** * The window.napplet global injected by a NIP-5D runtime. * * The published packages avoid global `Window` type mutation for JSR * compatibility. Consumers that access `window.napplet` directly can use this * interface in a local ambient declaration or cast: * ```ts * import type { NappletGlobal } from '@napplet/core'; * const napplet = (window as Window & { napplet: NappletGlobal }).napplet; * if (napplet.relay) await napplet.relay.query([{ kinds: [1], limit: 1 }]); * ``` * * Domain properties are optional. Presence means the runtime exposes that NAP * domain to the napplet; absence means the domain is unavailable. */ interface NappletGlobal { /** * NIP-01 relay operations: subscribe to events, publish events, one-shot queries. * Routes through the shell's relay pool via postMessage. */ relay?: RelayApi; /** * Inter-napplet topic and channel communication through the runtime. */ inc?: IncApi; /** * Napplet-scoped storage: async localStorage-like API proxied through the shell. * Each napplet's storage is isolated by identity — napplets cannot read each other's data. */ storage?: StorageApi; /** * Keyboard forwarding and action keybindings: register named actions the shell * can bind to keys, forward unbound keystrokes to the shell, listen for * shell-triggered actions locally. * * @example * ```ts * // Register an action the shell can bind to a key: * const result = await window.napplet.keys.registerAction({ * id: 'editor.save', label: 'Save', defaultKey: 'Ctrl+S', * }); * * // Listen for the bound key locally: * const sub = window.napplet.keys.onAction('editor.save', () => { * console.log('Save triggered!'); * }); * * // Unregister when no longer needed: * window.napplet.keys.unregisterAction('editor.save'); * ``` */ keys?: KeysApi; /** * Media session control: create sessions, report state and metadata, * declare capabilities, receive commands from the shell. * * @example * ```ts * // Create a media session: * const { sessionId } = await window.napplet.media.createSession({ * owner: 'napplet', * metadata: { title: 'My Song', artist: 'The Artist' }, * }); * * // Report playback state: * window.napplet.media.reportState(sessionId, { * status: 'playing', position: 42.5, duration: 240, * }); * * // Listen for shell commands: * window.napplet.media.onCommand(sessionId, (action, value) => { * if (action === 'pause') player.pause(); * }); * ``` */ media?: MediaApi; /** * Shell-rendered notifications: send notifications, set badge counts, * register channels, request permission, listen for user interaction. * * @example * ```ts * // Send a notification: * const { notificationId } = await window.napplet.notify.send({ * title: 'New message', body: 'Alice: hey!', priority: 'normal', * }); * * // Set badge count: * window.napplet.notify.badge(3); * * // Listen for action clicks: * window.napplet.notify.onAction((notificationId, actionId) => { * if (actionId === 'reply') openReply(notificationId); * }); * ``` */ notify?: NotifyApi; /** * Read-only user identity queries: public key, profile, follows, relays, * lists, zaps, mutes, blocked, badges. All queries are strictly read-only -- * no signing, encryption, or decryption. * * @example * ```ts * // Get the user's public key: * const pubkey = await window.napplet.identity.getPublicKey(); * * // Get profile metadata: * const profile = await window.napplet.identity.getProfile(); * if (profile) console.log(profile.name); * * // Get follow list: * const follows = await window.napplet.identity.getFollows(); * ``` */ identity?: IdentityApi; /** * Read-only access to the shell's active theme (NAP-THEME). * * The shell owns theming; napplets read the current theme and react to * shell-pushed changes. The payload carries required colors plus optional * fonts, background media, and a title. * * @example * ```ts * const theme = await window.napplet.theme.get(); * document.body.style.background = theme.colors.background; * const sub = window.napplet.theme.onChanged((t) => applyTheme(t)); * ``` */ theme?: ThemeApi; /** * Per-napplet declarative configuration (NAP-CONFIG). * * Napplet declares a JSON Schema (typically at build time via * @napplet/vite-plugin's `configSchema` option, or at runtime via * `registerSchema`); shell renders the settings UI, validates values, * persists them scoped by `(dTag, aggregateHash)`, and delivers live * values via initial snapshot + push. Shell is the sole writer. * * @example * ```ts * // Register a schema at runtime (escape hatch; prefer manifest-declared): * await window.napplet.config.registerSchema({ * type: 'object', * properties: { theme: { type: 'string', enum: ['light', 'dark'], default: 'dark' } }, * }); * * // Subscribe to live values (first delivery is an immediate snapshot): * const sub = window.napplet.config.subscribe((values) => { * applyTheme(values.theme as string); * }); * * // Deep-link into shell-owned settings UI: * window.napplet.config.openSettings({ section: 'appearance' }); * ``` */ config?: ConfigApi; /** * Browser-enforced resource fetching: napplets request bytes by URL, * shell fetches and returns a Blob. The strict-CSP iframe sandbox * blocks all napplet-side network access, so this is the canonical * (and only) byte-fetching primitive available inside a napplet. * * URL space is scheme-pluggable: shells register handlers per scheme. * Canonical schemes include `data:` (decoded in-shim, no round-trip), * `https:` (shell-side network with policy), `blossom:` (Blossom hash to * bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 * single-hop resolution). * * @example * ```ts * // Fetch raw bytes: * const blob = await window.napplet.resource.bytes('https://example.com/avatar.png'); * * // Fetch many resources in one envelope: * const items = await window.napplet.resource.bytesMany([ * 'https://example.com/avatar.png', * 'blossom:sha256:abc123...', * 'htree://example-root/path', * ]); * * // Get a managed object URL (revoke when done to free memory): * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:abc123...'); * imgEl.src = url; * imgEl.onload = () => revoke(); * ``` */ resource?: ResourceApi; /** * Native ContextVM bridge (NAP-CVM): MCP-over-Nostr access mediated by the shell. * * ContextVM transports Model Context Protocol JSON-RPC over Nostr relays using * public-key server addressing and encrypted relay events. The shell owns all * transport details -- relay routing, signing, encryption, JSON-RPC correlation, * MCP initialization, per-napplet policy, and optional payment prompts. Napplets * supply a server identity (`pubkey` + optional relay hints) and the MCP * operation they want; they receive MCP results, never ContextVM private keys, * relay credentials, or direct socket access. * * @example * ```ts * if (window.napplet.cvm) { * const servers = await window.napplet.cvm.discover({ search: 'relay' }); * const tools = await window.napplet.cvm.listTools(servers[0]); * const result = await window.napplet.cvm.callTool(servers[0], tools[0].name, {}); * } * ``` */ cvm?: CvmApi; /** * Outbox-aware relay routing (NAP-OUTBOX): the napplet supplies Nostr filters * and intent; the shell discovers the correct relays (NIP-65 write/read relays, * fallbacks, relay intelligence), queries them, deduplicates events by id, * validates signatures, and streams updates. The shell owns relay discovery, * routing, fallback, deduplication, signing, and publish fanout policy. * * Use this instead of NAP-RELAY when relay selection is part of result * correctness (reading an author's notes from their write relays, publishing to * the user's write relays, fanning a directed event to recipient inbox relays). * * @example * ```ts * if (window.napplet.outbox) { * const { events } = await window.napplet.outbox.query( * [{ authors: ['ab12...'], kinds: [1], limit: 20 }], * { authors: ['ab12...'], timeoutMs: 3000 }, * ); * } * ``` */ outbox?: OutboxApi; /** * Shell-mediated file/blob upload (NAP-UPLOAD): the napplet hands the shell raw * bytes plus upload intent; the shell selects a storage server, signs the rail * authorization (NIP-98 for NIP-96, kind 24242 for Blossom), performs the HTTP * upload, and returns a stable URL plus NIP-94 integrity metadata. The shell is * the policy and consent boundary; napplets never receive signing keys, server * credentials, or direct network access. * * @example * ```ts * if (window.napplet.upload) { * const result = await window.napplet.upload.upload({ data: blob, filename: 'pic.png' }); * if (result.status === 'complete') attach(result.url, result.nip94); * } * ``` */ upload?: UploadApi; /** * Archetype intent dispatch (NAP-INTENT): the runtime resolves a role to an * installed handler and owns target lifecycle and payload delivery. */ intent?: IntentApi; /** * Runtime-mediated Bluetooth LE/GATT sessions (NAP-BLE): the napplet asks * the shell to select a user-approved device, operate on exposed GATT * attributes, and receive state/notification/close events. The shell owns * chooser UI, permissions, device handles, GATT lifecycle, and policy. */ ble?: BleApi; /** * Runtime-mediated WebRTC sessions. The shell owns signaling transport, * signing/encryption, SDP, ICE, and RTCPeerConnection lifecycle. */ webrtc?: WebrtcApi; /** * Shell-mediated link opening (NAP-LINK): request user-visible navigation * without giving the napplet direct navigation authority, opener access, * network access, or fetched bytes. * * @example * ```ts * if (window.napplet.link) { * await window.napplet.link.open('https://example.com/post/123', { label: 'Read post' }); * } * ``` */ link?: LinkApi; /** * Runtime-mediated event counts (NAP-COUNT): request aggregate counts for * NIP-01 filters without receiving matching event payloads. The runtime owns * relay COUNT support, indexing, caching, approximation, relay disclosure, and * refusal policy. * * @example * ```ts * if (window.napplet.count) { * const result = await window.napplet.count.query({ kinds: [7], '#e': [eventId] }); * } * ``` */ count?: CountApi; /** * Runtime-mediated NIP-51 list mutation (NAP-LISTS): add or remove semantic * items from supported lists without requiring napplets to handle raw NIP-51 * tags, private item encryption, replaceable/addressable event preservation, * signing, or publishing. * * @example * ```ts * if (window.napplet.lists) { * await window.napplet.lists.add({ type: 'mute-list' }, [ * { itemType: 'pubkey', value: 'abc123...' }, * ]); * } * ``` */ lists?: ListsApi; /** * Common social actions (NAP-COMMON): shell-mediated NIP-19 helpers, profile * lookup, follows, and signed social actions. The shell owns identity, * consent, event construction, signing, publishing, relay access, and NIP-19 * handling. * * @example * ```ts * if (window.napplet.common) { * const { pubkeys } = await window.napplet.common.follows(); * await window.napplet.common.react(noteId, '+'); * } * ``` */ common?: CommonApi; /** * Runtime-mediated serial device access (NAP-SERIAL): the napplet asks the * shell to select and open a user-approved serial session, writes byte arrays, * and receives shell-pushed state/data/close events. The shell owns raw port * handles, streams, OS paths, permissions, read loops, and lifecycle policy. * * @example * ```ts * if (window.napplet.serial) { * const { session } = await window.napplet.serial.open({ options: { baudRate: 115200 } }); * await window.napplet.serial.write(session.id, [112, 105, 110, 103, 10]); * } * ``` */ serial?: SerialApi; /** * Shell-mediated virtual filesystem access (NAP-FS): the napplet sees only * virtual paths, directory entries, coarse metadata, base64-encoded file * bytes, user-mediated picker results, and advisory change events. The runtime * owns host paths, mounts, backing store, policy, and authorization of every * operation. * * @example * ```ts * if (window.napplet.fs) { * const picked = await window.napplet.fs.pickFile({ accept: [{ extension: '.md' }] }); * const bytes = await window.napplet.fs.read(picked.entries[0].path); * await window.napplet.fs.write('/shared/copy.md', bytes.data, { mode: 'replace' }); * const entries = await window.napplet.fs.list('/shared'); * await window.napplet.fs.mkdir('/shared/projects/new', { recursive: true }); * } * ``` */ fs?: FsApi; /** * Runtime-mediated direct messages (NAP-DM): napplets can request DM status, * conversations, history, send, and live delivery while the runtime owns * signing, encryption, relay routing, storage, key/session state, and policy. * * @example * ```ts * if (window.napplet.dm) { * const { conversations } = await window.napplet.dm.conversations({ limit: 20 }); * const live = await window.napplet.dm.subscribe({ conversationId: conversations[0]?.id }); * window.napplet.dm.onMessage((message) => render(message)); * await window.napplet.dm.unsubscribe(live.subscriptionId); * } * ``` */ dm?: DmApi; } /** * Built-in topic constants for the napplet INC event bus. * * @example * ```ts * emit(TOPICS.PROFILE_OPEN); * ``` */ declare const TOPICS: { readonly STREAM_CHANNEL_SWITCH: "stream:channel-switch"; readonly STREAM_CURRENT_CONTEXT_GET: "stream:current-context-get"; readonly STREAM_CURRENT_CONTEXT: "stream:current-context"; readonly NOTE_OPEN: "napplet:note/open"; readonly PROFILE_OPEN: "napplet:profile/open"; readonly DM_OPEN: "napplet:dm/open"; readonly KEYBINDS_GET: "keybinds:get-all"; readonly KEYBINDS_ALL: "keybinds:all"; readonly KEYBINDS_UPDATE: "keybinds:update"; readonly KEYBINDS_RESET: "keybinds:reset"; readonly KEYBINDS_CAPTURE_START: "keybinds:capture-start"; readonly KEYBINDS_CAPTURE_END: "keybinds:capture-end"; readonly WM_FOCUSED_WINDOW_CHANGED: "wm:focused-window-changed"; }; /** Key type for the TOPICS constant object. */ type TopicKey = keyof typeof TOPICS; /** Value type for the TOPICS constant object. */ type TopicValue = (typeof TOPICS)[TopicKey]; /** * @napplet/core -- iframe boundary helpers for clone-safe `postMessage`. * * Every NAP shim crosses the napplet ⇄ shell boundary by posting a JSON * envelope through `window.parent.postMessage(msg, '*')`, which **structured- * clones** its argument. Framework reactive values -- Svelte 5 `$state`, Vue * `reactive`, Solid stores -- are `Proxy` objects that are NOT structured- * cloneable, so `postMessage` throws a `DataCloneError`. In an async/Promise * path that throw becomes an unhandled rejection that gets silently swallowed, * and the envelope simply never crosses the boundary (napplet/web#67). * * These helpers make that failure mode disappear or surface loudly, depending * on the configured {@link CloneMode}: * * - **`'auto'` (default)** -- post the envelope as-is; only if the structured * clone fails, take a proxy-stripping {@link toCloneableSnapshot} and retry, * warning once per message type. Zero overhead on the happy path; reactive * napplets "just work"; a genuinely non-cloneable value (a function, etc.) * still throws a loud, actionable, synchronous error. * - **`'strict'`** -- never auto-recover; on `DataCloneError` throw the loud * actionable error immediately. Use this to catch accidental proxy leaks. * - **`'snapshot'`** -- always snapshot before posting (eager normalization). * * NOTE: this module references no browser globals. The post target is passed * in by the caller (NAP shims pass `window.parent`), so `@napplet/core` stays * DOM-free. These helpers are pure SDK plumbing: the bytes placed on the wire * are identical plain envelopes either way, so nothing here is protocol surface. * * @packageDocumentation */ /** * How {@link sendEnvelope} treats arguments that are not structured-cloneable. * * - `'auto'` -- post as-is, snapshot-and-retry on `DataCloneError` (default). * - `'strict'` -- throw a loud actionable error on `DataCloneError`. * - `'snapshot'` -- always snapshot before posting. */ type CloneMode = 'auto' | 'strict' | 'snapshot'; /** Minimal post target -- `window.parent` satisfies this without DOM types. */ interface PostMessageTarget { postMessage(message: unknown, targetOrigin: string): void; } /** * Set the global clone mode for {@link sendEnvelope}. * * @param mode - One of `'auto'` (default), `'strict'`, or `'snapshot'`. * * @example * ```ts * import { setCloneMode } from '@napplet/core'; * // Eagerly snapshot every outbound argument (framework-heavy napplets): * setCloneMode('snapshot'); * ``` */ declare function setCloneMode(mode: CloneMode): void; /** * Return the current clone mode. * * @returns The active {@link CloneMode}. */ declare function getCloneMode(): CloneMode; /** * Reset the once-per-type auto-recovery warning state. * * Mostly useful in tests and when a shell re-initializes the shim runtime. */ declare function clearCloneWarnings(): void; /** * Produce a structured-cloneable deep snapshot of `value`, stripping framework * reactive proxies (Svelte 5 `$state`, Vue `reactive`, Solid stores) into plain * objects and arrays. * * Unlike a `JSON` round-trip, this **preserves binary** (`Uint8Array`, * `ArrayBuffer`, typed arrays), `Date`, `RegExp`, `Map`, and `Set`, and handles * cyclic references. Functions and symbols are not representable and throw a * `TypeError` -- matching `structuredClone`, so genuinely non-cloneable input is * never silently masked. * * @typeParam T - The value type (preserved in the return type). * @param value - The value to snapshot. * @returns A plain, structured-cloneable copy of `value`. * @throws {TypeError} If `value` contains a function or symbol. * * @example * ```ts * import { toCloneableSnapshot } from '@napplet/core'; * // In a Svelte 5 napplet, filters/relays are $state proxies: * napplet.outbox.subscribe( * toCloneableSnapshot(filters), * { relays: toCloneableSnapshot(relays), timeoutMs: 3000 }, * ); * ``` */ declare function toCloneableSnapshot(value: T): T; /** * Post a JSON envelope to the shell across the iframe boundary, handling * non-structured-cloneable arguments per the active {@link CloneMode}. * * This is the single boundary chokepoint every NAP shim uses instead of calling * `target.postMessage(msg, '*')` directly, so that a `DataCloneError` is either * transparently recovered (`'auto'`/`'snapshot'`) or raised as a loud, * actionable, synchronous error (`'strict'`, or any mode when the value is * genuinely non-cloneable) rather than swallowed in an async path. * * @param target - The post target (NAP shims pass `window.parent`). * @param message - The envelope; must carry a `type` discriminator. * @param targetOrigin - The `postMessage` target origin (defaults to `'*'`). * @throws {Error} `NappletDataCloneError` if the message cannot be made cloneable. * * @example * ```ts * import { sendEnvelope } from '@napplet/core'; * sendEnvelope(window.parent, { type: 'outbox.subscribe', id, subId, filters }); * ``` */ declare function sendEnvelope(target: PostMessageTarget, message: T, targetOrigin?: string): void; export { type BleApi, type BleAttribute, type BleCharacteristic, type BleCharacteristicProperties, type BleClosedEvent, type BleDeviceFilter, type BleDeviceInfo, type BleEvent, type BleManufacturerDataFilter, type BleNotificationEvent, type BleOpenRequest, type BleOpenResult, type BleService, type BleServiceDataFilter, type BleSession, type BleSessionState, type BleStateEvent, type BleUuid, type BleWriteOptions, type ChannelClosed, type ChannelEvent, type ChannelHandle, type ChannelInfo, type CloneMode, type CommonActionResult, type CommonEventReportTarget, type CommonFollowsResult, type CommonHexPubkey, type CommonNip19DecodeResult, type CommonNip19EncodeInput, type CommonNip19EncodeResult, type CommonNip19Type, type CommonNostrEventId, type CommonProfileData, type CommonProfileResult, type CommonProfileTarget, type CommonPubkeyReportTarget, type CommonReaction, type CommonReportReason, type CommonReportTarget, type CountFilter, type CountOptions, type CountResult, type CvmDiscoverQuery, type CvmRegistryCallOptions, type CvmRegistryEntry, type CvmRegistryOptions, type CvmRegistryQuery, type CvmRegistryTool, type CvmRequestOptions, type CvmServer, type CvmServerRef, type DmConversation, type DmConversationPage, type DmConversationQuery, type DmError, type DmHexPubkey, type DmMessage, type DmMessagePage, type DmMessageQuery, type DmMessageStatus, type DmOk, type DmPeer, type DmSendRequest, type DmSendResult, type DmStatus, type DmSubscribeRequest, type DmSubscription, type DmTimestamp, type EventTemplate, type FsAcceptRule, type FsChange, type FsChangeKind, type FsDirectoryEntry, type FsEntryKind, type FsError, type FsInfo, type FsLimits, type FsMetadata, type FsMkdirOptions, type FsPermission, type FsPickOptions, type FsPickResult, type FsPickedEntry, type FsReadOptions, type FsReadResult, type FsRoot, type FsWatchOptions, type FsWriteMode, type FsWriteOptions, type FsWriteResult, type IncChannelApi, type IncEvent, type IntentAvailability, type IntentBehavior, type IntentCandidate, type IntentHandlerPreference, type IntentOpenOptions, type IntentRequest, type IntentResult, type JsonObject, type JsonSchema, type LinkOpenErrorCode, type LinkOpenOptions, type LinkOpenResult, type LinkOpenStatus, type ListErrorCode, type ListItem, type ListItemType, type ListItemVisibility, type ListMutationResult, type ListOptions, type ListRef, type ListSupport, type McpBlobResourceContents, type McpContentBlock, type McpMessage, type McpResource, type McpResourceContent, type McpTextResourceContents, type McpTool, type McpToolResult, type MediaAction, type MediaArtwork, type MediaContextLink, type MediaMetadata, type MediaNostrRef, type MediaPlaybackOwner, type MediaSessionContext, type MediaSessionCreate, type MediaSessionResult, type MediaSourceRef, type MediaState, NAP_DOMAINS, type NapDispatch, type NapDomain, type NapHandler, type NappletGlobal, type NappletInstanceStorage, type NappletMessage, type NostrEvent, type NostrFilter, type NostrTag, type OutboxEventOptions, type OutboxEventResult, type OutboxPublishOptions, type OutboxPublishResult, type OutboxQueryOptions, type OutboxRelayPlan, type OutboxResult, type OutboxSubscribeOptions, type OutboxSubscription, type OutboxTarget, type PostMessageTarget, type RelayEventResult, type RelayEventSidecar, type ResourceApi, type ResourceBytesErrorItem, type ResourceBytesItem, type ResourceBytesOkItem, type ResourceErrorCode, type ResourceInfo, type ResourceSchemeInfo, type ResourceSidecarEntry, type SerialEvent, type SerialOpenOptions, type SerialOpenRequest, type SerialOpenResult, type SerialPortFilter, type SerialPortInfo, type SerialSession, type SerialState, type Subscription, TOPICS, type TopicKey, type TopicValue, type UploadInfo, type UploadRail, type UploadRailInfo, type UploadRequest, type UploadResult, type UploadState, type UploadStatus, type WebrtcApi, type WebrtcClosedEvent, type WebrtcDirectScope, type WebrtcEvent, type WebrtcMessageEvent, type WebrtcOpenRequest, type WebrtcOpenResult, type WebrtcPeerEvent, type WebrtcRoomScope, type WebrtcScope, type WebrtcSession, type WebrtcState, type WebrtcStateEvent, clearCloneWarnings, createDispatch, dispatch, getCloneMode, getRegisteredDomains, registerNap, sendEnvelope, setCloneMode, toCloneableSnapshot };