/** * Apna SDK wire protocol. * * The shared client <-> host message contract that every transport construct * (channels, bridge, host) builds on. This file is intentionally * dependency-free and runtime-light: types, a couple of const tag literals, * and small type guards only. No `post-robot`, no React, no DOM access. * * Both ends of the bridge MUST agree on this file — a change here is a change * to the protocol and must be mirrored in the host implementation. */ /** Protocol version tag stamped on every envelope. Bump on breaking changes. */ export declare const APNA_PROTOCOL: "apna/1"; export declare type ApnaProtocol = typeof APNA_PROTOCOL; /** * Whether a capability needs user consent before its handler runs. * - `open` — public/read-only data, no prompt (e.g. `nostr.query`). * - `gated` — needs a permission grant (e.g. `nostr.signEvent`). */ export declare type Gating = 'open' | 'gated'; /** * A capability the host implements, as advertised in the handshake. * `capability` is the fully-qualified, versioned capability string, e.g. * `nostr.query`, `social.v1.publishNote`, `identity.v1.me`. */ export interface CapabilityDescriptor { capability: string; gating: Gating; } export declare const MessageType: { readonly HandshakeInit: "handshake:init"; readonly HandshakeAck: "handshake:ack"; readonly RpcRequest: "rpc:request"; readonly RpcResponse: "rpc:response"; readonly StreamStart: "stream:start"; readonly StreamEvent: "stream:event"; readonly StreamStop: "stream:stop"; readonly PermissionRequest: "permission:request"; readonly PermissionResponse: "permission:response"; readonly Event: "event"; }; export declare type MessageType = typeof MessageType[keyof typeof MessageType]; /** Names of typed events the host can push to a mini-app. */ export declare const EventName: { /** FAB "Customise Mode" toggle — mini-app highlights customizable components. */ readonly CustomiseToggleHighlight: "customise:toggleHighlight"; /** Active user profile changed in the host. */ readonly ProfileSwitched: "profile:switched"; /** Updated design-component (Module Federation) selections for this app. */ readonly DesignSelections: "design:selections"; /** Host resolved light/dark theme changed. */ readonly ThemeChanged: "theme:changed"; /** A permission grant for this app changed (granted/revoked). */ readonly PermissionsChanged: "permissions:changed"; }; export declare type EventName = typeof EventName[keyof typeof EventName]; /** Host-resolved theme state sent with `theme:changed`. */ export declare type HostResolvedTheme = 'light' | 'dark'; /** Payload for `theme:changed` host -> app events. */ export interface HostThemePayload { theme: HostResolvedTheme; } /** Fields stamped on every Apna message so a channel can filter foreign traffic. */ interface BaseEnvelope { protocol: ApnaProtocol; type: MessageType; } /** client -> host: open a session. */ export interface HandshakeInit extends BaseEnvelope { type: typeof MessageType.HandshakeInit; /** Stable id of the mini-app (its publisher-rooted identity / app id). */ appId: string; /** Per-running-instance id, minted by the client. Echoed on every message. */ instanceId: string; /** SDK version of the mini-app, for host-side compatibility decisions. */ sdkVersion: string; } /** host -> client: session accepted, capabilities negotiated. */ export interface HandshakeAck extends BaseEnvelope { type: typeof MessageType.HandshakeAck; /** Echoed back so the client can confirm the channel binding. */ instanceId: string; /** Everything this host implements, with gating class per capability. */ capabilities: CapabilityDescriptor[]; /** Optional cached-read HTTP base, e.g. `https://host/api/nostr`. */ httpEndpoint?: string; /** Optional Module Federation `remoteEntry.js` URL for host design components. */ designRemote?: string; } /** client -> host: invoke a capability. */ export interface RpcRequest extends BaseEnvelope { type: typeof MessageType.RpcRequest; /** Correlation id, unique per request on a given bridge. */ id: number; instanceId: string; /** Fully-qualified capability string, e.g. `social.v1.publishNote`. */ capability: string; args: unknown[]; } /** Structured error returned on a failed rpc / permission response. */ export interface RpcError { /** * Machine-readable error code. Known codes: * - `permission-denied` — a gated capability was not granted. * - `not-implemented` — the host does not advertise this capability. * - `handler-error` — the capability handler threw. * - `timeout` — no response before the bridge timeout. */ code: string; message: string; } /** host -> client: result of an `rpc:request`. */ export interface RpcResponse extends BaseEnvelope { type: typeof MessageType.RpcResponse; /** Matches the originating `RpcRequest.id`. */ id: number; ok: boolean; /** Present when `ok` is true. */ value?: unknown; /** Present when `ok` is false. `error.code` may be `permission-denied`. */ error?: RpcError; } /** client -> host: start a streaming capability subscription. */ export interface StreamStart extends BaseEnvelope { type: typeof MessageType.StreamStart; /** Correlation id, unique per stream on a given bridge. */ id: number; instanceId: string; /** Fully-qualified streaming capability string, e.g. `nostr.subscribe`. */ capability: string; args: unknown[]; } /** host -> client: one event emitted by a started stream. */ export interface StreamEvent extends BaseEnvelope { type: typeof MessageType.StreamEvent; /** Matches the originating `StreamStart.id`. */ id: number; instanceId: string; payload?: unknown; } /** either side -> peer: stop a started stream. */ export interface StreamStop extends BaseEnvelope { type: typeof MessageType.StreamStop; /** Matches the originating `StreamStart.id`. */ id: number; instanceId: string; /** Present when the stream ended because of an error. */ error?: RpcError; } /** client -> host: proactively ask for one or more gated capabilities upfront. */ export interface PermissionRequest extends BaseEnvelope { type: typeof MessageType.PermissionRequest; id: number; instanceId: string; /** Fully-qualified capability strings the mini-app wants granted. */ capabilities: string[]; } /** A single decision in a permission response. */ export interface PermissionGrant { capability: string; decision: 'allow' | 'deny'; scope: 'always' | 'once' | 'session'; } /** host -> client: result of a `permission:request`. */ export interface PermissionResponse extends BaseEnvelope { type: typeof MessageType.PermissionResponse; /** Matches the originating `PermissionRequest.id`. */ id: number; ok: boolean; grants?: PermissionGrant[]; error?: RpcError; } /** host -> app: a typed push event (no response expected). */ export interface EventMessage extends BaseEnvelope { type: typeof MessageType.Event; instanceId: string; event: EventName; payload?: unknown; } /** The discriminated union of every message that can cross the bridge. */ export declare type ApnaMessage = HandshakeInit | HandshakeAck | RpcRequest | RpcResponse | StreamStart | StreamEvent | StreamStop | PermissionRequest | PermissionResponse | EventMessage; /** True if `value` looks like a well-formed Apna protocol message. */ export declare function isApnaMessage(value: unknown): value is ApnaMessage; /** Narrow an `ApnaMessage` to a specific `type`. */ export declare function isMessageOfType(msg: ApnaMessage, type: T): msg is Extract; export {};