UNPKG

@spfn/core

Version:

SPFN Framework Core - File-based routing, transactions, repository pattern

213 lines (209 loc) 5.91 kB
import '../auto-loader-JFaZ9gON.js'; import { a as RouteContract, I as InferContract } from '../types-DYueuoD6.js'; import 'hono'; import 'hono/utils/http-status'; import '@sinclair/typebox'; import '../error-handler-wjLL3v-a.js'; /** * Contract-Based API Client * * Type-safe HTTP client that works with RouteContract for full end-to-end type safety */ type RequestInterceptor = (url: string, init: RequestInit) => Promise<RequestInit> | RequestInit; interface ClientConfig { /** * API base URL (e.g., http://localhost:4000) */ baseUrl?: string; /** * Default headers to include in all requests */ headers?: Record<string, string>; /** * Request timeout in milliseconds */ timeout?: number; /** * Custom fetch implementation */ fetch?: typeof fetch; } interface CallOptions<TContract extends RouteContract> { params?: InferContract<TContract>['params']; query?: InferContract<TContract>['query']; body?: InferContract<TContract>['body']; headers?: Record<string, string>; baseUrl?: string; } /** * API Client Error */ declare class ApiClientError extends Error { readonly status: number; readonly url: string; readonly response?: unknown | undefined; readonly errorType?: "timeout" | "network" | "http" | undefined; constructor(message: string, status: number, url: string, response?: unknown | undefined, errorType?: "timeout" | "network" | "http" | undefined); } /** * Contract-based API Client */ declare class ContractClient { private readonly config; private readonly interceptors; constructor(config?: ClientConfig); /** * Add request interceptor */ use(interceptor: RequestInterceptor): void; /** * Make a type-safe API call using a contract * * @param contract - Route contract with absolute path * @param options - Call options (params, query, body, headers) */ call<TContract extends RouteContract>(contract: TContract, options?: CallOptions<TContract>): Promise<InferContract<TContract>['response']>; /** * Create a new client with merged configuration */ withConfig(config: Partial<ClientConfig>): ContractClient; private static buildUrl; private static buildQuery; private static getHttpMethod; private static isFormData; } /** * Create a new contract-based API client */ declare function createClient(config?: ClientConfig): ContractClient; /** * Configure the global client instance * * Call this in your app initialization to set default configuration * for all auto-generated API calls. * * @example * ```ts * // In app initialization (layout.tsx, _app.tsx, etc) * import { configureClient } from '@spfn/core/client'; * * configureClient({ * baseUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000', * timeout: 60000, * headers: { * 'X-App-Version': '1.0.0' * } * }); * * // Add interceptors * import { client } from '@spfn/core/client'; * client.use(async (url, init) => { * // Add auth header * return { * ...init, * headers: { * ...init.headers, * Authorization: `Bearer ${getToken()}` * } * }; * }); * ``` */ declare function configureClient(config: ClientConfig): void; /** * Global client singleton with Proxy * * This client can be configured using configureClient() before use. * Used by auto-generated API client code. */ declare const client: ContractClient; /** * Type guard for timeout errors * * @example * ```ts * try { * await api.users.getById({ params: { id: '123' } }); * } catch (error) { * if (isTimeoutError(error)) { * console.error('Request timed out, retrying...'); * // Implement retry logic * } * } * ``` */ declare function isTimeoutError(error: unknown): error is ApiClientError; /** * Type guard for network errors * * @example * ```ts * try { * await api.users.list(); * } catch (error) { * if (isNetworkError(error)) { * showOfflineMessage(); * } * } * ``` */ declare function isNetworkError(error: unknown): error is ApiClientError; /** * Type guard for HTTP errors (4xx, 5xx) * * @example * ```ts * try { * await api.users.create({ body: userData }); * } catch (error) { * if (isHttpError(error)) { * if (error.status === 401) { * redirectToLogin(); * } else if (error.status === 404) { * showNotFoundMessage(); * } * } * } * ``` */ declare function isHttpError(error: unknown): error is ApiClientError; /** * Check if error is a specific server error type * * @example * ```ts * try { * await api.workflows.getById({ params: { uuid: 'xxx' } }); * } catch (error) { * if (isServerError(error, 'NotFoundError')) { * showNotFoundMessage(); * } else if (isServerError(error, 'ValidationError')) { * showValidationErrors(getServerErrorDetails(error)); * } * } * ``` */ declare function isServerError(error: unknown, errorType: string): error is ApiClientError; /** * Get server error type from ApiClientError * * @example * ```ts * const errorType = getServerErrorType(error); * // 'NotFoundError', 'ValidationError', 'PaymentFailedError', etc. * ``` */ declare function getServerErrorType(error: ApiClientError): string | undefined; /** * Get server error details from ApiClientError * * @example * ```ts * if (isServerError(error, 'PaymentFailedError')) { * const details = getServerErrorDetails(error); * console.log('Payment ID:', details.paymentId); * } * ``` */ declare function getServerErrorDetails<T = any>(error: ApiClientError): T | undefined; export { ApiClientError, type CallOptions, type ClientConfig, ContractClient, type RequestInterceptor, client, configureClient, createClient, getServerErrorDetails, getServerErrorType, isHttpError, isNetworkError, isServerError, isTimeoutError };