export type AuthRequestMessage = | { type: "auth:get-state" } | { type: "auth:sign-in" } | { type: "auth:sign-out" } | { type: "auth:get-access-token" }; export interface AuthStateValue { accessTokenExpiresAt?: number | null; isAuthenticated: boolean; lastError?: string | null; resource?: string | null; scopes?: string[]; tokenAvailable?: boolean; userInfo?: { email?: string; name?: string; sub?: string; username?: string; } | null; } export type AuthResponseMessage = | { ok: true; type: "auth:state"; value: AuthStateValue } | { ok: true; type: "auth:token"; value: { accessToken: string } } | { ok: true; type: "auth:ack" } | { ok: false; type: "auth:error"; error: string }; const authRequestTypes = new Set([ "auth:get-state", "auth:sign-in", "auth:sign-out", "auth:get-access-token" ]); export function isAuthRequestMessage( value: unknown ): value is AuthRequestMessage { if (!value || typeof value !== "object") { return false; } const candidate = value as Partial; return typeof candidate.type === "string" && authRequestTypes.has(candidate.type); } export function isAuthResponseMessage( value: unknown ): value is AuthResponseMessage { if (!value || typeof value !== "object") { return false; } const candidate = value as Partial; if (candidate.ok === false) { return candidate.type === "auth:error" && typeof candidate.error === "string"; } if (candidate.ok !== true || typeof candidate.type !== "string") { return false; } if (candidate.type === "auth:ack") { return true; } if (candidate.type === "auth:token") { return Boolean( candidate.value && typeof candidate.value === "object" && typeof (candidate.value as { accessToken?: unknown }).accessToken === "string" ); } if (candidate.type === "auth:state") { return Boolean( candidate.value && typeof candidate.value === "object" && typeof (candidate.value as { isAuthenticated?: unknown }).isAuthenticated === "boolean" ); } return false; }