/** * In-memory cache of gate passwords the server has already accepted. * * WHY THIS IS NOT sessionStorage (security audit 2026-08-14, F36). * * `CollectionView` used to persist the plaintext collection password to * `sessionStorage` under `micdrop:authedCollections:list`. This widget is a * light-DOM embed: it runs on the VENUE'S OWN ORIGIN, alongside whatever else * that page loads — tag managers, RUM agents, session-replay recorders, * chat widgets. `sessionStorage` is readable by every one of them, and it kept * the password readable for the whole tab session, long after the visitor * typed it. Gate passwords are stored plaintext at rest server-side and are * SHARED BY EVERY ATTENDEE of a private/pre-sale event, so one harvested value * defeats the gate for everyone. * * A module-scoped Map keeps the same UX for the case that actually matters — * the widget is a SPA, so navigating collection → event → back re-mounts the * component and the unlock still survives — while removing the cross-script * read surface and the persistence. The trade is deliberate and narrow: a hard * page RELOAD now re-prompts, where before it did not. * * The server remains the authority either way. A cached password that no * longer matches comes back as `passwordRequired` and the caller re-prompts, * so nothing here can reveal gated content on its own. */ /** Which gate a cached password belongs to. Keys are namespaced by scope so an * unlocked collection never satisfies the per-event gate (or vice versa). */ export type GateScope = 'collection' | 'event'; /** Remember a password the server just accepted. No-op for an empty password. */ export declare function rememberGatePassword(scope: GateScope, id: string | number, password?: string): void; /** Recall a previously accepted password for this page load, if any. */ export declare function recallGatePassword(scope: GateScope, id: string | number): string | undefined; /** Drop a cached password the server has since rejected (rotated / revoked). */ export declare function forgetGatePassword(scope: GateScope, id: string | number): void; /** * Record that a gate has been unlocked for this page load. * * This is the ONLY store of "already authenticated" for the event/collection * gates. It is deliberately NOT web storage: * * - a blocked store (sandboxed iframe, Safari private mode, a partitioned * third-party context) made the old `sessionStorage.getItem` READ throw, and * that throw propagated out of the `isLocked` derivation and out of the * correct-password handler — so on a venue's own domain a buyer could type * the RIGHT password and still be told nothing happened. A Map cannot throw, * so a blocked store now degrades to "re-prompt on the next mount", which is * the honest fallback. * - it is unreachable from other scripts on the host page, closing the forge * surface described on LEGACY_STORAGE_KEYS. * * The server stays the authority: an unlock recorded here only suppresses the * prompt, and any payload that still arrives with `passwordRequired` re-locks. */ export declare function rememberGateUnlock(scope: GateScope, id: string | number): void; /** Whether this page load has already cleared that gate. Never throws. */ export declare function recallGateUnlock(scope: GateScope, id: string | number): boolean; /** * Test hook: wipe the whole cache between cases. * @internal test-only reset of the module cache. */ export declare function clearGatePasswordCache(): void;