Initial Commit
This commit is contained in:
74
packages/ui/react/react-auth/src/context/auth-context.md
Normal file
74
packages/ui/react/react-auth/src/context/auth-context.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# `auth-context`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`auth-context.tsx` exposes the React Context surface for the auth flow. It defines `AuthContext`, the `AuthProvider` component, and the `useAuth()` hook. The provider owns an `AuthClient` instance (either injected or constructed from `config`) and a state block of `{ user, session, isLoading, isAuthenticated, error }`. On mount (unless `skipInitialCheck` is set) it fetches the current session, normalises it through `transformSessionResponse`, and settles the state. It exposes eleven action callbacks (`signIn`, `signUp`, `signOut`, `updateProfile`, `changePassword`, `forgotPassword`, `resetPassword`, `verifyEmail`, `resendVerificationEmail`, `deleteAccount`, `refreshSession`) — every action wraps `client` calls with loading/error state mutations, re-fetches the session where appropriate, records the corresponding OTel counter, and re-throws on failure so callers can `catch` for form-level UI handling. `useAuth()` outside an `AuthProvider` throws `ReactAuthContextMissingError` so the failure is immediate and typed rather than a downstream `undefined` access.
|
||||
|
||||
## Features
|
||||
|
||||
- `AuthContext` + `AuthProvider` + `useAuth()` triple.
|
||||
- 11 typed action callbacks; each settles `isLoading` and surfaces `error` deterministically.
|
||||
- `skipInitialCheck` flag short-circuits the mount-time session fetch (test surface).
|
||||
- `onAuthStateChange` callback fires on every state transition (useful for analytics).
|
||||
- Re-throws action errors so form-level UI can render messages; never silently swallows.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```tsx
|
||||
import { AuthProvider, useAuth } from "@suaveplan/react-auth";
|
||||
|
||||
function Inside() {
|
||||
const { user, signOut } = useAuth();
|
||||
return user ? <button onClick={() => signOut()}>Sign out</button> : null;
|
||||
}
|
||||
|
||||
<AuthProvider config={{ baseURL: "https://auth.example.com" }}>
|
||||
<Inside />
|
||||
</AuthProvider>;
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Inject a pre-built client and observe state changes:
|
||||
|
||||
```tsx
|
||||
import {
|
||||
AuthProvider,
|
||||
createAuthClient,
|
||||
type AuthState,
|
||||
} from "@suaveplan/react-auth";
|
||||
|
||||
const client = createAuthClient({
|
||||
baseURL: "https://auth.example.com",
|
||||
siteDomain: "tenant-a",
|
||||
});
|
||||
|
||||
function onAuthStateChange(state: AuthState) {
|
||||
analytics.track("auth_state", { authenticated: state.isAuthenticated });
|
||||
}
|
||||
|
||||
<AuthProvider
|
||||
config={{ baseURL: "https://auth.example.com" }}
|
||||
client={client}
|
||||
onAuthStateChange={onAuthStateChange}
|
||||
>
|
||||
{/* ... */}
|
||||
</AuthProvider>;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface AuthState` — `{ user: User | null; session: Session | null; isLoading: boolean; isAuthenticated: boolean; error: Error | null }`.
|
||||
- `interface AuthContextValue extends AuthState` — adds `client: AuthClient` plus the 11 action callbacks.
|
||||
- `interface AuthProviderProps` — `{ children: ReactNode; config: AuthClientConfig; client?: AuthClient; onAuthStateChange?: (state: AuthState) => void; skipInitialCheck?: boolean }`.
|
||||
- `const AuthContext: React.Context<AuthContextValue | undefined>` — context handle.
|
||||
- `function AuthProvider(props: AuthProviderProps): ReactNode` — wraps children with the context.
|
||||
- `function useAuth(): AuthContextValue` — reads the context; throws `ReactAuthContextMissingError` when outside an `AuthProvider`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
State is consolidated in a single `useState` block so transitions are atomic — callers never see a half-updated value. Every action wraps its critical section in `try`/`catch`/`finally`-style logic but with explicit `setState` calls rather than a `finally` clause, because the success and failure branches set different sets of fields. Caught values pass through `toError(value)` which wraps non-`Error` throwables so the `error` field is always a real `Error` (this avoids violating biome's `useThrowOnlyError` rule when a downstream surface re-throws from the context). Session fetches go through `timedSessionFetch` which records the wall-clock duration into the `react_auth.session_fetch.duration_ms` histogram with a `success`/`failure` label. The `mounted` ref guards the initial-load effect from setting state after unmount; React 19's `StrictMode` double-mount runs the cleanup before the second mount, so the first effect's `mounted = false` is honoured.
|
||||
Reference in New Issue
Block a user