export interface PairTokenRecord { token: string; expiresAt: number; used: boolean; } export interface MintResult { token: string; expiresAt: number; expiresInSeconds: number; } export interface ConsumeOk { ok: true; } export interface ConsumeErr { ok: false; reason: "unknown" | "expired" | "used"; } export type ConsumeResult = ConsumeOk | ConsumeErr; export declare class PairTokenStore { private current; private readonly ttlMs; private sweepTimer; constructor(opts?: { ttlSeconds?: number; autoSweep?: boolean; }); mint(): MintResult; /** * What `consume` would answer right now, WITHOUT spending the token. * * Exists so a caller can reject a bad token before doing any work, and still * spend the token only once the work has succeeded. A pair token is * single-use and lives 180 seconds, so spending it on a request that then * fails costs the user a whole new QR — and, worse, makes their retry * indistinguishable from an attacker replaying a photographed code, which is * the one signal `design.md` §2.6 designates as replay detection. * * **Named for its relationship to `consume`, deliberately.** It reserves * nothing and takes no lock, so two callers can both be told `{ ok: true }` * for the same token. A name like `verify` would read as a gate that had * decided something, and inviting a caller to act on this alone is precisely * the misuse the ordering it enables exists to prevent. A reader who sees * `wouldConsume` asks where the `consume` is, which is the right question. */ wouldConsume(token: string): ConsumeResult; consume(token: string): ConsumeResult; /** * The shared predicate behind `verify` and `consume`. * * One implementation on purpose: two copies of "is this token usable" is two * places for the expiry or single-use rule to drift, and a drift in this * direction fails open. */ private check; peek(): PairTokenRecord | null; clear(): void; sweep(): void; dispose(): void; } //# sourceMappingURL=pair-store.d.ts.map