import { type PairingEmoji, type PairingEnvelope } from "@gryt/crypto"; import type { MlsOwnDeviceAdd } from "../mls/interfaces.js"; import type { HistoryArchive, HistoryNotedMessage, HistoryProgress, OwnDeviceAdder, PairingClock, PairingDeviceInfo, PairingEndReason, PairingFetch } from "./interfaces.js"; import type { PairingRelay } from "./relayClient.js"; export type ApproverState = { phase: "idle"; } | { phase: "claiming"; } | { phase: "waiting"; } | { phase: "confirming"; device: PairingDeviceInfo; /** Where the relay puts N, and A, from their IPs. Null when it can't say. */ location: string | null; yourLocation: string | null; emoji: readonly PairingEmoji[]; deadline: number; } | { phase: "signing_in"; device: PairingDeviceInfo; } /** No extension on this Keycloak: the app opens `url`, Keycloak's own device page, for the same code. */ | { phase: "browser"; device: PairingDeviceInfo; url: string; } | { phase: "waiting_ready"; device: PairingDeviceInfo; } | { phase: "adding"; device: PairingDeviceInfo; host: string; done: number; total: number; } /** N is in every conversation; the history's tail is still going. Progress is on `history`. */ | { phase: "sending"; device: PairingDeviceInfo; added: Record; } | { phase: "done"; device: PairingDeviceInfo; added: Record; } | { phase: "ended"; reason: PairingEndReason; }; export interface ApproverOptions { relay: PairingRelay; /** A's own relay origin. A QR naming any other is refused. */ relayOrigin: string; /** For the extension call. */ fetch: PairingFetch; devices: OwnDeviceAdder; clock?: PairingClock; approvalMs?: number; /** A's archive. Without it, no history goes across. */ history?: HistoryArchive; /** How long after the adds A keeps collecting old-epoch messages for the last tail. */ lateWindowMs?: number; } export interface ApproverPairing { readonly state: ApproverState; subscribe(listener: (state: ApproverState) => void): () => void; /** Null until Approve, or with no archive. */ readonly history: HistoryProgress | null; subscribeHistory(listener: (progress: HistoryProgress) => void): () => void; /** Every MLS message A archives while this runs, received or sent, for the tail. */ noteMessage(message: HistoryNotedMessage): void; /** A scanned QR, or a typed code. */ claim(input: { qr: string; } | { code: string; }): void; /** For an account, `accessToken` refreshes first: the extension refuses a token over 60 seconds old. */ approve(envelope: PairingEnvelope, accessToken?: () => Promise): void; deny(): Promise; mismatch(): Promise; cancel(): Promise; } /** A: claims the session, shows N and the emoji, and on Approve seals the envelope, signs N in and adds it. */ export declare function createApproverPairing(options: ApproverOptions): ApproverPairing;