UNPKG

@beignet/core

Version:

Core framework primitives for Beignet

156 lines 7.84 kB
import type { StandardSchemaV1 } from "@standard-schema/spec"; import { type ContractDeprecationMeta } from "./lifecycle.js"; import type { OpenAPIOperationMeta } from "./openapi-meta.js"; import type { BodyHttpMethod, ContractErrorResponses, ContractHeaderSchemas, ContractMeta, ContractResponses, HttpContractConfig, HttpMethod, MergeContractMeta, MergedContractErrorResponses, OmitMetaKeys, ResponsesFromErrorDefinitions, StandardSchema } from "./types.js"; /** * Fluent builder for one HTTP contract. * * A contract describes the transport boundary for one endpoint: method, path, * request schemas, response schemas, metadata, and route-owned catalog errors. * Builder methods are immutable; each call returns a new builder with narrower * types. */ export declare class ContractBuilder<TMethod extends HttpMethod, TPathParams extends StandardSchema | null, TQuery extends StandardSchema | null, TBody extends StandardSchema | null, THeaders extends ContractHeaderSchemas, TResponses extends ContractResponses, TMeta extends ContractMeta, TPath extends string = string> { readonly kind: "http"; readonly name: string; readonly namespace?: string; readonly localName: string; readonly method: TMethod; private readonly _path; private readonly _pathParams; private readonly _query; private readonly _body; private readonly _headers; private readonly _responses; private readonly _meta; constructor(config: HttpContractConfig<TMethod, TPathParams, TQuery, TBody, TResponses, TMeta, TPath, THeaders>); /** * Request and response schemas attached to this contract. * * Server adapters use these for validation. Client and frontend integrations * use them for local validation and type inference. */ get schema(): { pathParams: TPathParams; query: TQuery; headers: THeaders; body: TBody; responses: TResponses; }; /** * Response schemas keyed by HTTP status code. */ get responseSchemas(): TResponses; /** * URL path template for this contract. */ get path(): TPath; /** * Metadata consumed by hooks, OpenAPI generation, and app conventions. */ get metadata(): TMeta; /** * Plain contract config consumed by framework internals and integrations. */ get config(): HttpContractConfig<TMethod, TPathParams, TQuery, TBody, TResponses, TMeta, TPath, THeaders>; /** * Attach a schema for dynamic path parameters. * * The schema validates parameters parsed from path templates such as * `/posts/:id` or `/posts/[id]`. */ pathParams<TNewPathParams extends StandardSchemaV1>(schema: TNewPathParams): ContractBuilder<TMethod, TNewPathParams, TQuery, TBody, THeaders, TResponses, TMeta, TPath>; /** * Attach a schema for query parameters. */ query<TNewQuery extends StandardSchemaV1>(schema: TNewQuery): ContractBuilder<TMethod, TPathParams, TNewQuery, TBody, THeaders, TResponses, TMeta, TPath>; /** * Attach a schema for a JSON request body. * * This method is only available on POST, PUT, and PATCH contracts. */ body<TNewBody extends StandardSchemaV1>(this: ContractBuilder<BodyHttpMethod, StandardSchema | null, StandardSchema | null, StandardSchema | null, ContractHeaderSchemas, ContractResponses, ContractMeta, TPath>, schema: TNewBody): ContractBuilder<TMethod, TPathParams, TQuery, TNewBody, THeaders, TResponses, TMeta, TPath>; /** * Attach a request header schema. * * Multiple schemas are evaluated in declaration order and their parsed * outputs are merged. This lets a contract inherit shared group headers and * still declare route-specific headers. */ headers<TNewHeaders extends StandardSchemaV1>(schema: TNewHeaders): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders extends readonly StandardSchema[] ? readonly [...THeaders, TNewHeaders] : THeaders extends StandardSchema ? readonly [THeaders, TNewHeaders] : readonly [TNewHeaders], TResponses, TMeta, TPath>; /** * Add or replace route-owned response schemas by status code. * * These schemas describe business responses returned by route handlers. * Framework-owned errors such as request validation failures do not need to be * declared here. * * Use `null` for void/empty responses such as 204 No Content. */ responses<TNewResponses extends ContractResponses>(responseSchemas: TNewResponses): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders, Omit<TResponses, keyof TNewResponses> & TNewResponses, TMeta, TPath>; /** * Declare route-owned application errors from an error catalog. * * Catalog errors use Beignet's standard error response envelope and remain * distinguishable from framework-owned errors. Declarations merge with * previously declared catalog errors, including shared group errors; later * declarations win when the same catalog key is declared twice. Use * `.responses()` when a route needs a custom error response body instead of * catalog semantics. */ errors<TErrorDefs extends ContractErrorResponses>(errorDefs: TErrorDefs): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders, Omit<TResponses, keyof ResponsesFromErrorDefinitions<TErrorDefs>> & ResponsesFromErrorDefinitions<TErrorDefs>, OmitMetaKeys<TMeta, "errors"> & { errors: MergedContractErrorResponses<TMeta, TErrorDefs>; }, TPath>; /** * Merge metadata into this contract. * * Hooks and tooling can read metadata for concerns such as auth, rate limits, * idempotency, OpenAPI, or app-specific conventions. */ meta<TNewMeta extends ContractMeta>(newMeta: TNewMeta): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders, TResponses, MergeContractMeta<TMeta, TNewMeta>, TPath>; /** * Mark this contract as deprecated for external clients. */ deprecated<const TDeprecation extends ContractDeprecationMeta>(deprecation: TDeprecation): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders, TResponses, MergeContractMeta<TMeta, { deprecation: TDeprecation; }>, TPath>; /** * Merge OpenAPI operation metadata into this contract. */ openapi<TPatch extends Partial<OpenAPIOperationMeta>>(patch: TPatch): ContractBuilder<TMethod, TPathParams, TQuery, TBody, THeaders, TResponses, MergeContractMeta<TMeta, { openapi: TPatch; }>, TPath>; } /** * Options for creating one contract with `defineContract(...)`. */ export type DefineContractOptions<TMethod extends HttpMethod = HttpMethod, TPath extends string = string> = { /** HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) */ method: TMethod; /** URL path template (e.g., "/api/users/:id") */ path: TPath; /** Optional contract name (auto-generated from method + path if not provided) */ name?: string; }; /** * Create a new HTTP contract builder. * * Most apps prefer `defineContractGroup().namespace(...).prefix(...)` for * related feature contracts. Use this lower-level factory when a standalone * contract is clearer. * * @example * ```ts * const getTodo = defineContract({ * method: "GET", * path: "/api/todos/:id", * }) * .pathParams(z.object({ id: z.string() })) * .responses({ 200: TodoSchema }); * ``` * * @param options - HTTP method, path template, and optional contract name. * @returns A fluent contract builder. */ export declare function defineContract<TMethod extends HttpMethod, const TPath extends string>(options: DefineContractOptions<TMethod, TPath>): ContractBuilder<TMethod, null, null, null, null, Record<never, never>, ContractMeta, TPath>; //# sourceMappingURL=contract-builder.d.ts.map