UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

304 lines 12.9 kB
import { type ContractLike, type HttpContractConfig, type ResolveContract, type StandardErrorResponseBody, type StandardSchemaV1 } from "../contracts/index.js"; import type { CallArgs, ClientConfig, EndpointResult, InferEndpointErrorResponse, InferEndpointErrorResponseByStatus, InferEndpointErrorStatus, InferSuccessResponse } from "./types.js"; /** * Source category for a `ContractError`. */ export type ContractErrorSource = "http" | "client" | "network" | "contract"; /** * Narrow a contract error union by source. */ export type ContractErrorWithSource<TError, TSource extends ContractErrorSource> = Extract<TError, { readonly source: TSource; }>; /** * Narrow a contract error union by HTTP status. */ export type ContractErrorWithStatus<TError, TStatus extends number> = Extract<TError, { readonly status: TStatus; }>; /** * Narrow a contract error union by Beignet error code. */ export type ContractErrorWithCode<TError, TCode extends string> = Extract<TError, { readonly code: TCode; }>; /** * Error for a non-2xx HTTP response. */ export type HttpContractError<TBody = unknown, TStatus extends number = number> = ContractError<TBody, TStatus, "http"> & { readonly source: "http"; readonly status: TStatus; readonly response: Response; }; /** * Error created by the client before a network request is made. */ export type ClientContractError = ContractError<undefined, undefined, "client"> & { readonly source: "client"; readonly status: undefined; readonly response: undefined; }; /** * Error created when the network request itself fails. */ export type NetworkContractError = ContractError<undefined, undefined, "network"> & { readonly source: "network"; readonly status: undefined; readonly response: undefined; }; /** * Error created when a response violates the contract. */ export type ResponseContractError<TBody = unknown, TStatus extends number | undefined = number | undefined> = ContractError<TBody, TStatus, "contract"> & { readonly source: "contract"; readonly status: TStatus; }; /** * Union of all Beignet client error variants. */ export type AnyContractError = HttpContractError | ClientContractError | NetworkContractError | ResponseContractError; type EndpointCatalogErrorDefinition<TContract extends HttpContractConfig> = TContract["metadata"] extends { errors: infer TErrors; } ? TErrors extends Record<string, { code: string; status: number; message: string; }> ? TErrors[keyof TErrors] : never : never; type InferErrorDefinitionDetails<TDef> = TDef extends { details: StandardSchemaV1; } ? StandardSchemaV1.InferOutput<TDef["details"]> : unknown; type StandardErrorBodyForDefinition<TDef extends { code: string; }> = StandardErrorResponseBody & { code: TDef["code"]; details?: InferErrorDefinitionDetails<TDef>; }; type EndpointCatalogContractError<TContract extends HttpContractConfig> = EndpointCatalogErrorDefinition<TContract> extends infer TDef ? TDef extends { code: string; status: number; } ? HttpContractError<StandardErrorBodyForDefinition<TDef>, TDef["status"]> & { readonly code: TDef["code"]; readonly details?: InferErrorDefinitionDetails<TDef>; } : never : never; /** * Infer route-owned error catalog codes declared by a contract. */ export type InferEndpointErrorCode<TContract extends HttpContractConfig> = EndpointCatalogErrorDefinition<TContract>["code"]; /** * Infer the full typed error union for a contract endpoint. */ export type InferEndpointContractError<TContract extends HttpContractConfig> = EndpointCatalogContractError<TContract> | { [TStatus in InferEndpointErrorStatus<TContract>]: HttpContractError<InferEndpointErrorResponseByStatus<TContract, TStatus>, TStatus>; }[InferEndpointErrorStatus<TContract>] | HttpContractError<InferEndpointErrorResponse<TContract>, number> | ClientContractError | NetworkContractError | ResponseContractError<InferEndpointErrorResponse<TContract>, number | undefined>; /** * Error thrown by Beignet contract clients. * * `source` distinguishes HTTP error responses, client-side request mistakes, * network failures, and contract drift such as response validation failures. */ export declare class ContractError<TBody = unknown, TStatus extends number | undefined = number | undefined, TSource extends ContractErrorSource = ContractErrorSource> extends Error { /** * Error source category. */ readonly source: TSource; /** * HTTP status when a response was available. */ readonly status: TStatus; /** * Stable error code. */ readonly code?: string; /** * Parsed response body when available. */ readonly body?: TBody; /** * Structured error details when available. */ readonly details?: unknown; /** * Native fetch response when available. */ readonly response?: Response; cause?: unknown; constructor(args: { source: TSource; status?: TStatus; code?: string; message: string; body?: TBody; details?: unknown; response?: Response; cause?: unknown; }); /** * Check whether this error has a specific HTTP status code. */ hasStatus<S extends number>(status: S): this is this & { readonly status: S; }; /** * Check whether this error came from a specific source. */ hasSource<S extends ContractErrorSource>(source: S): this is this & { readonly source: S; }; /** * Check whether this error has a specific error code. */ hasCode<C extends string>(code: C): this is this & { code: C; }; } /** * Type guard to check if an unknown error is a ContractError, * optionally narrowing by HTTP status code. * * @example * ```ts * try { await endpoint.call(...) } * catch (err) { * if (isContractError(err, 404)) { * // err.status is 404 * } * if (isContractError(err)) { * // err is ContractError * } * } * ``` */ export declare function isContractError(err: unknown): err is AnyContractError; export declare function isContractError<S extends number>(err: unknown, status: S): err is HttpContractError<unknown, S>; export declare function isContractError<TError extends AnyContractError, S extends number>(err: TError, criteria: { status: S; }): err is ContractErrorWithStatus<TError, S>; export declare function isContractError<TError extends AnyContractError, S extends ContractErrorSource>(err: TError, criteria: { source: S; }): err is ContractErrorWithSource<TError, S>; export declare function isContractError<TError extends ContractError, Status extends number, Source extends ContractErrorSource>(err: TError, criteria: { status: Status; source: Source; }): err is ContractErrorWithSource<ContractErrorWithStatus<TError, Status>, Source>; export declare function isContractError<TError extends ContractError, Code extends string>(err: TError, criteria: { code: Code; }): err is ContractErrorWithCode<TError, Code>; export declare function isContractError<TError extends ContractError, Status extends number, Code extends string>(err: TError, criteria: { status: Status; code: Code; }): err is ContractErrorWithCode<ContractErrorWithStatus<TError, Status>, Code>; export declare function isContractError<TError extends ContractError, Source extends ContractErrorSource, Code extends string>(err: TError, criteria: { source: Source; code: Code; }): err is ContractErrorWithCode<ContractErrorWithSource<TError, Source>, Code>; export declare function isContractError<TError extends ContractError, Status extends number, Source extends ContractErrorSource, Code extends string>(err: TError, criteria: { status: Status; source: Source; code: Code; }): err is ContractErrorWithCode<ContractErrorWithSource<ContractErrorWithStatus<TError, Status>, Source>, Code>; export declare function isContractError<S extends number>(err: unknown, criteria: { status: S; }): err is ContractError<unknown, S>; export declare function isContractError<S extends ContractErrorSource>(err: unknown, criteria: { source: S; }): err is ContractError<unknown, number | undefined, S>; export declare function isContractError<Status extends number, Source extends ContractErrorSource>(err: unknown, criteria: { status: Status; source: Source; }): err is ContractErrorWithSource<HttpContractError<unknown, Status>, Source>; export declare function isContractError<Code extends string>(err: unknown, criteria: { code: Code; }): err is ContractError<unknown, number | undefined> & { readonly code: Code; }; /** * Typed client endpoint for one contract. */ export declare class Endpoint<TContract extends HttpContractConfig, TProvidedHeaders extends string = never> { private contract; private config; constructor(contract: TContract, config: ClientConfig<TProvidedHeaders>); /** * Check whether an unknown error is a `ContractError` for this endpoint. */ isError(err: unknown): err is InferEndpointContractError<TContract>; isError<S extends InferEndpointErrorStatus<TContract>>(err: unknown, status: S): err is ContractErrorWithStatus<InferEndpointContractError<TContract>, S>; isError<S extends number>(err: unknown, status: S): err is HttpContractError<unknown, S>; isError<S extends InferEndpointErrorStatus<TContract>>(err: unknown, criteria: { status: S; }): err is ContractErrorWithStatus<InferEndpointContractError<TContract>, S>; isError<S extends ContractErrorSource>(err: unknown, criteria: { source: S; }): err is ContractErrorWithSource<InferEndpointContractError<TContract>, S>; isError<C extends InferEndpointErrorCode<TContract>>(err: unknown, criteria: { code: C; }): err is ContractErrorWithCode<InferEndpointContractError<TContract>, C>; isError<C extends string>(err: unknown, criteria: { code: C; }): err is InferEndpointContractError<TContract> & { readonly code: C; }; isError<Status extends InferEndpointErrorStatus<TContract>, Source extends ContractErrorSource>(err: unknown, criteria: { status: Status; source: Source; }): err is ContractErrorWithSource<ContractErrorWithStatus<InferEndpointContractError<TContract>, Status>, Source>; isError<Status extends InferEndpointErrorStatus<TContract>, C extends InferEndpointErrorCode<TContract>>(err: unknown, criteria: { status: Status; code: C; }): err is ContractErrorWithCode<ContractErrorWithStatus<InferEndpointContractError<TContract>, Status>, C>; isError<Source extends ContractErrorSource, C extends InferEndpointErrorCode<TContract>>(err: unknown, criteria: { source: Source; code: C; }): err is ContractErrorWithCode<ContractErrorWithSource<InferEndpointContractError<TContract>, Source>, C>; isError<Status extends InferEndpointErrorStatus<TContract>, Source extends ContractErrorSource, C extends InferEndpointErrorCode<TContract>>(err: unknown, criteria: { status: Status; source: Source; code: C; }): err is ContractErrorWithCode<ContractErrorWithSource<ContractErrorWithStatus<InferEndpointContractError<TContract>, Status>, Source>, C>; /** * Call the endpoint and return the parsed success body. * * Throws `ContractError` when the request fails or the response violates the * contract. */ call(...callArgs: CallArgs<TContract, TProvidedHeaders>): Promise<InferSuccessResponse<TContract>>; /** * Call the endpoint and return a typed result instead of throwing * `ContractError`. */ safeCall(...callArgs: CallArgs<TContract, TProvidedHeaders>): Promise<EndpointResult<TContract, InferEndpointContractError<TContract>>>; /** * Build the full URL with path and query parameters */ private buildUrl; private serializeQueryParam; private invalidQueryParam; /** * Build request headers */ private buildHeaders; /** * Create a contract error */ private createError; } /** * Client for making contract-based requests. */ export declare class Client<TProvidedHeaders extends string = never> { private config; constructor(config: ClientConfig<TProvidedHeaders>); /** * Create an endpoint wrapper for a contract. * * Accepts either a plain `HttpContractConfig` or a builder with a `.config` * property. */ endpoint<TContractLike extends ContractLike>(contract: TContractLike): Endpoint<ResolveContract<TContractLike>, TProvidedHeaders>; } /** * Create a configured Beignet client. */ export declare function createClient<const TProvidedHeaders extends string = never>(config?: ClientConfig<TProvidedHeaders>): Client<TProvidedHeaders>; export {}; //# sourceMappingURL=client.d.ts.map