first commit
This commit is contained in:
82
docs/ui/react/react-auth/src/client/auth-client.md
Normal file
82
docs/ui/react/react-auth/src/client/auth-client.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# `auth-client`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`auth-client.ts` ships a typed `AuthClient` class plus the `createAuthClient(config)` factory that wraps `better-auth/client`. The wrapper centralises three concerns the underlying client leaves to the caller: (1) consistent injection of an optional `x-site-domain` header across every request, used for multi-tenant routing on the auth server; (2) a structural surface that does not depend on the dynamic plugin generic graph emitted by `better-auth` (each method we call is typed as an optional callable so plugin presence is detected at runtime and reported as a no-op when absent rather than as a compile-time error); and (3) normalisation of the `listSessions` payload from native `Date` to ISO string for serialisation safety. Configuration is validated through Zod at the boundary and a `ReactAuthConfigInvalidError` is thrown with code `REACT_AUTH.CONFIG_INVALID` when the URL or site domain is malformed.
|
||||
|
||||
## Features
|
||||
|
||||
- `AuthClient` class + `createAuthClient(config)` factory.
|
||||
- Optional `siteDomain` propagates as the `x-site-domain` header on every call.
|
||||
- Optional `fetch` injection seam for testing (no `vi.mock` required).
|
||||
- Zod-validated config; invalid input throws `ReactAuthConfigInvalidError`.
|
||||
- `listSessions()` normalises `Date` fields to ISO strings.
|
||||
- Every method is plugin-aware: when the underlying client lacks the method it returns `undefined` (or `[]` for `listSessions`).
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createAuthClient } from "@suaveplan/react-auth";
|
||||
|
||||
const client = createAuthClient({
|
||||
baseURL: "https://auth.example.com",
|
||||
siteDomain: "tenant-a",
|
||||
});
|
||||
|
||||
await client.signIn({ email: "a@b.com", password: "secret" });
|
||||
const session = await client.getSession();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Inject a custom `fetch` (e.g. a recorder or a happy-path stub) without ever mocking the `better-auth` module:
|
||||
|
||||
```ts
|
||||
import { createAuthClient } from "@suaveplan/react-auth";
|
||||
|
||||
const captured: Request[] = [];
|
||||
const recordingFetch: typeof fetch = async (input, init) => {
|
||||
const req = new Request(input, init);
|
||||
captured.push(req);
|
||||
return new Response(JSON.stringify({ data: null }), { status: 200 });
|
||||
};
|
||||
|
||||
const client = createAuthClient({
|
||||
baseURL: "https://auth.example.com",
|
||||
siteDomain: "tenant-b",
|
||||
fetch: recordingFetch,
|
||||
});
|
||||
|
||||
await client.getSession();
|
||||
expect(captured[0].headers.get("x-site-domain")).toBe("tenant-b");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface AuthClientConfig` — `baseURL: string` (URL), `siteDomain?: string`, `fetch?: typeof fetch`.
|
||||
- `class AuthClient`
|
||||
- `constructor(config: AuthClientConfig)` — validates `config`, throws `ReactAuthConfigInvalidError` on failure.
|
||||
- `signIn(credentials: LoginCredentials): Promise<unknown>`
|
||||
- `signUp(data: RegisterData): Promise<unknown>`
|
||||
- `signOut(): Promise<unknown>`
|
||||
- `getSession(): Promise<unknown>`
|
||||
- `forgotPassword(email: string): Promise<unknown>`
|
||||
- `resetPassword(data: { token: string; password: string }): Promise<unknown>`
|
||||
- `changePassword(data: ChangePasswordData): Promise<unknown>`
|
||||
- `verifyEmail(token: string): Promise<unknown>`
|
||||
- `resendVerificationEmail(email: string): Promise<unknown>`
|
||||
- `updateProfile(data: UpdateProfileData): Promise<unknown>`
|
||||
- `deleteAccount(password: string): Promise<unknown>`
|
||||
- `listSessions(): Promise<Session[]>` — Date fields ISO-normalised.
|
||||
- `revokeSession(sessionId: string): Promise<unknown>`
|
||||
- `revokeAllSessions(): Promise<unknown>`
|
||||
- `getRawClient(): BetterAuthClientLike` — escape hatch for plugin-specific access.
|
||||
- `function createAuthClient(config: AuthClientConfig): AuthClient` — thin factory.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The client does not throw on missing plugin methods. The upstream package emitted a single `@ts-expect-error` to work around `forgetPassword` not being statically typed; the wrapper replaces that with a structural interface (`BetterAuthClientLike`) where every method is optional. This keeps the call sites compile-safe while also covering deployments where the auth server registers a different plugin set. `listSessions` returns `[]` when no `data` array is present so callers can render an empty state without branching on `undefined`. The `x-site-domain` header is computed per-call from `this.config.siteDomain` so that future hot-swaps of the client config (out of scope for Wave 1) automatically pick up the new value. Constructor-time validation uses Zod with `z.string().url()` for `baseURL` — failing fast catches misconfiguration during app boot rather than on first request.
|
||||
Reference in New Issue
Block a user