/** * Tina4 API — Fetch wrapper with auth token management. * * Compatible with tina4-php and tina4-python backends: * - Sends Authorization: Bearer * - Reads FreshToken response header for token rotation * - Sends formToken in POST/PUT/PATCH/DELETE bodies */ export interface ApiConfig { baseUrl: string; auth: boolean; tokenKey: string; headers: Record; } export interface ApiResponse { status: number; data: T; ok: boolean; headers: Headers; /** @internal Used by debug tracker for request/response correlation. */ _requestId?: number; } export type RequestInterceptor = (config: RequestInit & { headers: Record; }) => (RequestInit & { headers: Record; }) | void; export type ResponseInterceptor = (response: ApiResponse) => ApiResponse | void; export interface RequestOptions { headers?: Record; params?: Record; } /** * HTTP client pre-configured for tina4-php / tina4-python backends. * * Features: * - Automatic `Authorization: Bearer ` header when `auth: true` * - Token rotation via `FreshToken` response header * - `formToken` injected into POST/PUT/PATCH/DELETE bodies for CSRF protection * - Per-request `headers` and `params` via `RequestOptions` * - Request/response interceptors * * @example * api.configure({ baseUrl: 'https://api.example.com', auth: true }); * * const users = await api.get('/users'); * const user = await api.get('/users', { params: { id: 42 } }); * await api.post('/users', { name: 'Alice' }); * await api.delete('/users/1'); */ export declare const api: { /** * Configure the API client. Call once at app startup. * * @example * api.configure({ * baseUrl: 'https://api.example.com', * auth: true, * tokenKey: 'my_token', // localStorage key (default: 'tina4_token') * headers: { 'X-App': '1' }, // default headers on every request * }); */ configure(c: Partial): void; /** * HTTP GET request. * @param path - API path relative to `baseUrl`. * @param options - `{ params, headers }` — query string params and per-request headers. * @example * const data = await api.get('/products', { params: { page: 2, limit: 20 } }); */ get(path: string, options?: RequestOptions): Promise; /** * HTTP POST request. * @param path - API path. * @param body - Request body (serialised as JSON). * @param options - `{ params, headers }`. * @example * await api.post('/users', { name: 'Alice', role: 'admin' }); */ post(path: string, body?: unknown, options?: RequestOptions): Promise; /** HTTP PUT — full replace. */ put(path: string, body?: unknown, options?: RequestOptions): Promise; /** HTTP PATCH — partial update. */ patch(path: string, body?: unknown, options?: RequestOptions): Promise; /** HTTP DELETE. */ delete(path: string, options?: RequestOptions): Promise; /** * Execute a GraphQL query or mutation. * * Sends a POST request with `{ query, variables }` body to the given path. * Returns `{ data, errors }` — throws if the HTTP request itself fails. * * @param path - GraphQL endpoint path (e.g. `/api/graphql`). * @param query - GraphQL query or mutation string. * @param variables - Optional variables object. * @param options - `{ params, headers }`. * * @example * const { data, errors } = await api.graphql('/api/graphql', * '{ products(limit: 10) { id name price } }' * ); * * @example * const { data } = await api.graphql('/api/graphql', * 'query ($term: String!) { search_products(term: $term) { id name } }', * { term: "widget" } * ); */ graphql(path: string, query: string, variables?: Record, options?: RequestOptions): Promise<{ data: T | null; errors?: Array<{ message: string; }>; }>; /** * Upload files via FormData (multipart/form-data). * * Unlike `post()`, this does NOT JSON-stringify the body or set * Content-Type — the browser sets the multipart boundary automatically. * Auth uses the Bearer token header (formToken cannot be injected into FormData). * * @param path - API path relative to `baseUrl`. * @param formData - A FormData instance containing files and fields. * @param options - `{ params, headers }` — query string params and per-request headers. * * @example * const form = new FormData(); * form.append('avatar', fileInput.files[0]); * form.append('name', 'Alice'); * const result = await api.upload('/users/avatar', form); */ upload(path: string, formData: FormData, options?: RequestOptions): Promise; /** * Register a request or response interceptor. * * @example * // Add a custom header to every request * api.intercept('request', (config) => { * config.headers['X-Client'] = 'my-app'; * }); * * // Redirect to login on 401 * api.intercept('response', (res) => { * if (res.status === 401) navigate('/login'); * }); */ intercept(type: "request" | "response", fn: RequestInterceptor | ResponseInterceptor): void; /** @internal Reset state (for tests). */ _reset(): void; }; //# sourceMappingURL=fetch.d.ts.map