import type { PushDeviceIdentifier } from "../interfaces/PushTokenAdapter"; import type { AccountEntry, AccountMap } from "../store/slices/accountsSlice"; /** * The rules a persisted account entry has to satisfy at EITHER layout, stated * once. Returns the entry with its identity settled, or `null` if it is not an * entry. * * WHAT MAKES AN ENTRY AN ENTRY. A refresh token and a user object: without the * token there is no session to preserve, and without the user there is nobody * to preserve it for. Both are required, and an entry missing either is * rejected rather than patched up — a half-entry that loads is worse than one * that never appears. * * IDENTITY MUST AGREE WITH THE KEY. An entry whose own `user.id` names someone * other than the account key it was filed under is REJECTED, not repaired. * Either half could be the truth and there is no way to tell which, so any * repair is a guess about whose credential this is — and guessing wrong files a * live session under the wrong account, which is the exact failure the * multi-account work exists to prevent. Dropping it costs one re-sign-in. * * An ABSENT id is not a conflict: every layout keys its accounts by `user.id`, * so the key is a sound identity for an entry that does not carry one. It is * filled in from the key, which is why every entry this returns is guaranteed * to agree with its key. * * `tokenExpiresAt` is normalized to a number, defaulting to `0` when the stored * value is not one. `0` reads as "expired", which renders a re-auth affordance * — the safe direction to be wrong in, since the alternative is claiming a * credential is live. Every other field is carried through untouched: this is a * gate on the fields the SDK itself depends on, not a whitelist of the account * shape, so a field added later survives a round trip without having to be * named here. */ export declare function readStoredAccountEntry(userId: string, value: unknown): AccountEntry | null; /** * The stored device identifier, or `null` if it is not one. * * Two valid shapes, native and web, mirroring `PushDeviceIdentifier`. The web * one is nested, and a HALF-formed subscription is the case that matters: an * object carrying an `endpoint` but no `keys` satisfies a cast and then throws * the moment anything reads `subscription.keys.p256dh` — which * `pushIdentifiersEqual` does, unguarded, on the STORED identifier. Each shape * is therefore accepted whole or not at all. * * Empty strings are rejected alongside missing ones. Neither the OS nor the * browser hands out an empty token, endpoint or key, and an identifier that * routes nowhere is worse than none: `null` leaves the re-acquisition paths * (`usePushRegistration`'s mount read, the rotation subscription) free to fetch * a real one, while a present-but-useless value looks to every one of them like * an identifier already in hand. */ export declare function readStoredDeviceIdentifier(value: unknown): PushDeviceIdentifier | null; /** * The four non-account fields every layout carries, each narrowed on its own. * * Concrete values, never absences: this is what a stored INDEX needs (Expo * writes all four unconditionally), and each default is the safe reading of a * field the bytes did not supply. `readStoredAccountMap` deliberately does NOT * use this — see the note there about preserving absence. */ export interface StoredMapFields { activeAccountId: string | null; signedOut: boolean; deviceIdentifier: PushDeviceIdentifier | null; pushIdentifierProbed: boolean; } export declare function readStoredMapFields(candidate: Record): StoredMapFields; /** * A whole persisted `AccountMap`, or `null` when the value is not one. * * The read path for the adapters that serialize the entire map as ONE stored * value (`react-js`'s `localStorage` entry, `react-native`'s Keychain * password). Expo's chunked layout composes the pieces above instead. * * WHAT MAKES A MAP A MAP: an object carrying an `accounts` object. Nothing * else is required, because nothing else can be missing in a way that makes the * value unrecognizable — every other field has a safe reading. A root that * fails this returns `null`, which is what these adapters already answered for * bytes they could not parse: the tolerant-read contract, unchanged. * * ONE BAD ENTRY COSTS ITS OWN ACCOUNT, NEVER THE MAP. Entries are validated * individually and a rejected one is simply absent from the result, so a single * corrupt credential does not sign every other account out. * * ABSENT OPTIONAL FIELDS STAY ABSENT. `signedOut`, `deviceIdentifier` and * `pushIdentifierProbed` are optional on `AccountMap` and documented to read a * particular way when missing; a map written before one of them existed must * come back missing it, not carrying a value the writer never chose. Present * but invalid is different — that degrades to the same safe default * `readStoredMapFields` applies. (This is also why that helper is not used * here: an index has to have all four, a map does not.) */ export declare function readStoredAccountMap(value: unknown): AccountMap | null;