learning_ai_common_plat/packages/api-client/src/client.ts
saravanakumardb1 90b9cf93d8 fix(common): configure ESLint 9 and fix lint issues
- Added @eslint/js dependency
- Updated eslint.config.js for ESLint 9 compatibility
- Added required globals (crypto, localStorage, React, etc.)
- Fixed unused imports and variables
- Disabled sort-imports temporarily
- Formatted all files with Prettier
2026-02-12 16:37:30 -08:00

89 lines
2.4 KiB
TypeScript

/**
* Configurable API client factory.
* Creates a fetch wrapper with base URL, auth token injection, and error handling.
*/
import type { ApiClient, ApiClientConfig, ApiResult } from './types.js';
/**
* Create an API client with a base URL and optional auth token.
*
* @example
* ```ts
* const api = createApiClient({
* baseUrl: "/api",
* getToken: () => localStorage.getItem("access_token"),
* });
*
* // Throws on error
* const users = await api.fetch<User[]>("/users");
*
* // Never throws
* const { data, error } = await api.safeFetch<User[]>("/users");
* ```
*/
export function createApiClient(config: ApiClientConfig): ApiClient {
const { baseUrl, getToken, defaultHeaders } = config;
function buildHeaders(options?: RequestInit): HeadersInit {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...defaultHeaders,
};
if (getToken) {
const token = getToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
}
if (options?.headers) {
const extra =
options.headers instanceof Headers
? Object.fromEntries(options.headers as any)
: Array.isArray(options.headers)
? Object.fromEntries(options.headers)
: (options.headers as Record<string, string>);
Object.assign(headers, extra);
}
return headers;
}
return {
async fetch<T>(path: string, options?: RequestInit): Promise<T> {
const res = await globalThis.fetch(`${baseUrl}${path}`, {
...options,
headers: buildHeaders(options),
});
if (!res.ok) {
const body = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(body.error || `HTTP ${res.status}`);
}
return res.json() as Promise<T>;
},
async safeFetch<T>(path: string, options?: RequestInit): Promise<ApiResult<T>> {
try {
const res = await globalThis.fetch(`${baseUrl}${path}`, {
...options,
headers: buildHeaders(options),
});
if (!res.ok) {
const body = await res.json().catch(() => ({ error: res.statusText }));
return { data: null, error: body.error || `HTTP ${res.status}` };
}
const data = (await res.json()) as T;
return { data, error: null };
} catch {
return { data: null, error: 'API unavailable' };
}
},
};
}