/** * The MINIMAL user shape the runtime layer expects on the session — a * constraint, not a schema. The full user type is owned by the API: apps define * it there (with roles, organizations, memberships, …) and register it via * {@link AuthRegister}; packages only rely on the fields below. */ export type SessionUser = { id: string; name: string; email: string; }; /** * The user schema is owned by the API — this package never defines it. An app * registers its API-owned user type once, via declaration merging: * * ```ts * declare module "@bota-apps/types/auth" { * interface AuthRegister { * user: ApiUser; * } * } * ``` * * Every auth surface (`AuthState`, `useAuth`, `createAuthClient`, …) is then * typed with `ApiUser` app-wide, with no generics at call sites. Unregistered * (or registered with a type missing the expected fields), the surfaces fall * back to the minimal {@link SessionUser} constraint. The augmentation must * target `"@bota-apps/types/auth"` — the module that DECLARES the interface. */ export interface AuthRegister { } export type RegisteredAuthUser = AuthRegister extends { user: infer TUser extends SessionUser; } ? TUser : SessionUser; /** Session not resolved yet — initial ("unknown") or request in flight ("loading"). */ export type AuthPendingState = { status: "unknown" | "loading"; user: undefined; }; export type AuthenticatedState = { status: "authenticated"; user: TUser; }; /** The endpoint answered 401 — there is no session. */ export type UnauthenticatedState = { status: "unauthenticated"; user: undefined; }; /** The session request itself failed (transport/server) — auth is undetermined, not logged out. */ export type AuthErrorState = { status: "error"; user: undefined; error: Error; }; /** * Discriminated on `status`: narrowing the status narrows the payload — * `user` is a `TUser` exactly when `status === "authenticated"`, and `error` * exists exactly when `status === "error"`. Every member carries `user`, so * `state.user` reads as `TUser | undefined` without narrowing. */ export type AuthState = AuthPendingState | AuthenticatedState | UnauthenticatedState | AuthErrorState; export type AuthStatus = AuthState["status"]; export type AuthStore = { getState: () => AuthState; subscribe: (listener: () => void) => () => void; /** Idempotently resolves the session once via the endpoint's user request. */ ensureReady: () => Promise; /** Drops the cached resolution and re-resolves the session. */ refresh: () => Promise; /** * Re-targets the session at another organization on the host/BFF, then * re-resolves the user (whose organization-dependent fields reflect the new * tenant). */ switchOrganization: (organizationId: string) => Promise; logout: () => Promise; isAuthenticated: () => boolean; }; /** * The public auth surface: the external store plus the navigation helpers * (`requireSession`, `loginUrl`). Cookie-based throughout — the session lives * in an HttpOnly cookie owned by the host/BFF and there is no token to manage. */ export type AuthClient = AuthStore & { /** * Root-route guard. Resolves the session once; if there is none, hands off to * the host/BFF login endpoint and never resolves, so nothing renders before * the navigation happens. */ requireSession: () => Promise; /** Builds the host/BFF login URL, defaulting the return URL to the current location. */ loginUrl: (returnUrl?: string) => string; }; /** The HTTP paths the session endpoint talks to on the host/BFF. */ export type SessionPaths = { user: string; logout: string; login: string; switchOrganization: string; }; /** * The transport against the host/BFF. Resolves and tears down the session over * the same `/bff/*` contract the production backend exposes. */ export type SessionEndpoint = { /** Resolves the current user, or undefined when unauthenticated (401). */ getUser: () => Promise; logout: () => Promise; /** Re-targets the session at another organization. */ switchOrganization: (organizationId: string) => Promise; /** The resolved paths, so the client can build the login URL. */ paths: SessionPaths; }; //# sourceMappingURL=index.d.ts.map