export type SessionState = | "authenticated" | "unauthenticated" | "recovery-pending" | "integration-failed"; export type SessionGateway = Readonly<{ getState(): SessionState; subscribe(listener: () => void): () => void; beginSignIn(returnTo?: string): Promise; signOut(): Promise; /** * LEG-01. Recovery is part of a request's lifetime, so it receives the same * context a credential attach does. The context is optional for one release * to keep existing owners working; the transport races the signal either way, * and a recovery that answers after the request already ended is observed but * never turned into a user-visible sign-out. */ recover( context?: CredentialOperationContext, ): Promise<"restored" | "no-session">; }>; export type CredentialRequestBinding = Readonly<{ origin: string; method: string; operationId: string; }>; export type CredentialPatch = Readonly<{ headers: Readonly>; }>; /** * ยง8.5. The transport lifetime handed to a credential owner. A cooperative * owner abandons its own work on abort; a non-cooperative one is still bounded * because the transport races the same signal. */ export type CredentialOperationContext = Readonly<{ signal: AbortSignal; deadlineAtMonotonicMs: number; }>; export type CredentialAttacher = Readonly<{ credentialPatch( binding: CredentialRequestBinding, context?: CredentialOperationContext, ): Promise; onUnauthenticated(): void; }>; export type AuthSessionPort = SessionGateway & CredentialAttacher;