UNPKG

chanfana

Version:

OpenAPI 3 and 3.1 schema generator and validator for Hono, itty-router and more!

1,186 lines (1,169 loc) 61.8 kB
import * as _asteasolutions_zod_to_openapi from '@asteasolutions/zod-to-openapi'; import { RouteConfig, ZodMediaTypeObject, OpenAPIRegistry } from '@asteasolutions/zod-to-openapi'; export { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import { Hono, Input } from 'hono'; import { Env, Schema, MergeSchemaPath, MergePath, BlankInput, HandlerResponse, H, ToSchema, TypedResponse } from 'hono/types'; import * as openapi3_ts_oas31 from 'openapi3-ts/oas31'; import { HeadersObject as HeadersObject$2, LinksObject as LinksObject$2 } from 'openapi3-ts/oas31'; import * as openapi3_ts_oas30 from 'openapi3-ts/oas30'; import { OpenAPIObject, HeadersObject as HeadersObject$1, LinksObject as LinksObject$1 } from 'openapi3-ts/oas30'; import { ZodObject, ZodType, z, ZodPipe } from 'zod'; type AnyZodObject = ZodObject<any, any>; type OrderByDirection = "asc" | "desc"; type Simplify<T> = { [KeyType in keyof T]: T[KeyType]; } & {}; type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <G>() => G extends B ? 1 : 2 ? true : false; type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : KeyType extends ExcludeType ? never : KeyType; type ExceptOptions = { requireExactProps?: boolean; }; type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = { requireExactProps: false; }> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType]; } & (Options["requireExactProps"] extends true ? Partial<Record<KeysType, never>> : {}); type SetOptional<BaseType, Keys extends keyof BaseType> = Simplify<Except<BaseType, Keys> & Partial<Pick<BaseType, Keys>>>; type SetRequired<BaseType, Keys extends keyof BaseType> = BaseType extends unknown ? Simplify<Except<BaseType, Keys> & Required<Pick<BaseType, Keys>>> : never; type OpenAPIObjectConfig = Omit<OpenAPIObject, "paths" | "components" | "webhooks">; type OpenAPIObjectConfigV31 = Omit<OpenAPIObject, "paths" | "components" | "webhooks">; type HeadersObject = HeadersObject$1 | HeadersObject$2; type LinksObject = LinksObject$1 | LinksObject$2; type ZodMediaType = "application/json" | "text/html" | "text/plain" | "application/xml" | (string & {}); type ZodContentObject = Partial<Record<ZodMediaType, ZodMediaTypeObject>>; interface ZodRequestBody { description?: string; content: ZodContentObject; required?: boolean; } interface ResponseConfig { description: string; headers?: AnyZodObject | HeadersObject; links?: LinksObject; content?: ZodContentObject; } type RouteParameter = AnyZodObject | ZodPipe<AnyZodObject, any> | undefined; interface RouterOptions { base?: string; schema?: Partial<OpenAPIObjectConfigV31 | OpenAPIObjectConfig>; docs_url?: string | null; redoc_url?: string | null; openapi_url?: string | null; raiseUnknownParameters?: boolean; generateOperationIds?: boolean; openapiVersion?: "3" | "3.1"; raiseOnError?: boolean; passthroughErrors?: boolean; /** * When enabled, response bodies are parsed through their Zod schema, * stripping unknown fields and validating required fields/types. * Validation failures result in a 500 error response and a console.error log. * Responses without a Zod schema are passed through unchanged. */ validateResponse?: boolean; } interface RouteOptions { router: any; raiseUnknownParameters: boolean; raiseOnError?: boolean; passthroughErrors?: boolean; validateResponse?: boolean; route: string; urlParams: Array<string>; } type RequestTypes = { body?: ZodRequestBody; params?: AnyZodObject; query?: AnyZodObject; cookies?: AnyZodObject; headers?: AnyZodObject | ZodType<unknown>[]; }; type OpenAPIRouteSchema = Simplify<Omit<RouteConfig, "responses" | "method" | "path" | "request"> & { request?: RequestTypes; responses?: { [statusCode: string]: ResponseConfig; }; "x-ignore"?: boolean; }>; type ValidatedData<S> = S extends OpenAPIRouteSchema ? { query: GetRequest<S> extends NonNullable<GetRequest<S>> ? GetOutput<GetRequest<S>, "query"> : undefined; params: GetRequest<S> extends NonNullable<GetRequest<S>> ? GetOutput<GetRequest<S>, "params"> : undefined; headers: GetRequest<S> extends NonNullable<GetRequest<S>> ? GetOutput<GetRequest<S>, "headers"> : undefined; body: GetRequest<S> extends NonNullable<GetRequest<S>> ? GetBody<GetPartBody<GetRequest<S>, "body">> : undefined; } : { query: undefined; params: undefined; headers: undefined; body: undefined; }; type GetRequest<T extends OpenAPIRouteSchema> = T["request"]; type GetOutput<T extends object | undefined, P extends keyof T> = T extends NonNullable<T> ? (T[P] extends AnyZodObject ? z.output<T[P]> : undefined) : undefined; type GetPartBody<T extends RequestTypes, P extends keyof T> = T[P] extends ZodRequestBody ? T[P] : undefined; type GetBody<T extends ZodRequestBody | undefined> = T extends NonNullable<T> ? T["content"]["application/json"] extends NonNullable<T["content"]["application/json"]> ? T["content"]["application/json"]["schema"] extends z.ZodType ? z.output<T["content"]["application/json"]["schema"]> : undefined : undefined : undefined; declare class OpenAPIRegistryMerger extends OpenAPIRegistry { _definitions: { route: { path: string; }; }[]; merge(registry: OpenAPIRegistryMerger, basePath?: string): void; } type OpenAPIRouterType<M> = { original: M; options: RouterOptions; registry: OpenAPIRegistryMerger; schema: any; }; /** * Valid HTTP methods for OpenAPI routes. * These are the standard methods supported by the OpenAPI specification. */ type HttpMethod = "get" | "head" | "post" | "put" | "delete" | "patch"; /** * Handles the generation of OpenAPI schema and serves the documentation UI. * * Paths defined with `x-ignore: true` in their `OpenAPIRouteSchema` * will be excluded from the generated OpenAPI specification by the CLI tool. */ declare class OpenAPIHandler { router: any; options: RouterOptions; registry: OpenAPIRegistryMerger; allowedMethods: string[]; /** * When true, the underlying router handles base path prefixing for route * registration (e.g. Hono's basePath()). Doc route paths will be registered * without the base prefix since the router adds it automatically. * The base is still used for schema generation and HTML references. * * This is a getter (not a field) so that subclass overrides take effect * even when accessed during the base class constructor (createDocsRoutes). */ protected get routerHandlesBasePrefix(): boolean; /** * Hook for adapters to wrap route handler functions. * Called for each OpenAPIRoute handler during route registration. * The base implementation returns the handler as-is. * Subclasses (e.g. HonoOpenAPIHandler) can override this to add * error conversion or other adapter-specific behavior. */ protected wrapHandler(handler: (...args: any[]) => Promise<Response>): (...args: any[]) => Promise<Response>; constructor(router: any, options?: RouterOptions); /** * Creates the documentation routes for Swagger UI, ReDoc, and OpenAPI JSON/YAML. * Respects the base path configuration for consistent URL generation. */ createDocsRoutes(): void; /** * Generates the OpenAPI schema document from registered routes. * @returns The complete OpenAPI specification object */ getGeneratedSchema(): openapi3_ts_oas30.OpenAPIObject | openapi3_ts_oas31.OpenAPIObject; /** * Registers a nested router and merges its OpenAPI registry. * @param params - Nested router parameters * @returns Array containing the nested router's fetch handler */ registerNestedRouter(params: { method: string; nestedRouter: any; path?: string; }): any[]; /** * Parses a route path, applying base path and converting to OpenAPI format. * @param path - The route path to parse * @returns The parsed and formatted path */ parseRoute(path: string): string; /** * Sanitizes an operationId to ensure it's valid for OpenAPI. * @param operationId - The raw operationId * @returns A sanitized operationId */ private sanitizeOperationId; /** * Registers a route with the OpenAPI registry. * @param params - Route registration parameters * @returns Array of wrapped handlers */ registerRoute(params: { method: string; path: string; handlers: any[]; doRegister?: boolean; }): any[]; /** * Handles common proxy properties for the wrapped router. * Provides access to isChanfana flag, original router, schema, and registry. */ handleCommonProxy(_target: any, prop: string, ..._args: any[]): any; /** * Gets the Request object from handler arguments. * Must be implemented by subclasses. * @param _args - Handler arguments */ getRequest(_args: any[]): Request; /** * Gets URL parameters from handler arguments. * Must be implemented by subclasses. * @param _args - Handler arguments */ getUrlParams(_args: any[]): Record<string, any>; /** * Gets environment bindings from handler arguments. * Must be implemented by subclasses. * @param _args - Handler arguments */ getBindings(_args: any[]): Record<string, any>; } /** * Base class for all OpenAPI route handlers. * Provides request validation, error handling, and response formatting. * * @template HandleArgs - Router handler arguments type */ declare class OpenAPIRoute<HandleArgs extends Array<object> = any> { /** * The main handler method to be implemented by subclasses. * @param _args - Handler arguments (context, request, etc. depending on router) * @returns Response object or plain object (will be auto-converted to JSON) */ handle(..._args: any[]): Response | Promise<Response> | object | Promise<object>; static isRoute: boolean; /** Args the execute() was called with */ args: HandleArgs; /** Cache for validated data - prevents re-validation on multiple calls */ validatedData: any; /** Cache for raw request data before Zod applies defaults/transformations */ unvalidatedData: any; /** Route configuration options */ params: RouteOptions; /** OpenAPI schema definition for this route */ schema: OpenAPIRouteSchema; constructor(params: RouteOptions); /** * Gets validated request data, validating the request if not already done. * Results are cached for subsequent calls. * * @returns Validated data including params, query, headers, and body */ getValidatedData<S = any>(): Promise<ValidatedData<S>>; /** * Gets raw request data before Zod validation/transformation. * Useful for checking which fields were actually sent in the request, * especially when using Zod 4 with optional fields that have defaults. * * @returns Raw request data object */ getUnvalidatedData(): Promise<any>; /** * Returns the OpenAPI schema for this route. * Override this method to customize schema properties. */ getSchema(): OpenAPIRouteSchema; /** * Returns the schema with Zod types, adding default response if not provided. * Note: This creates a shallow copy - nested objects are still references. */ getSchemaZod(): OpenAPIRouteSchema; /** * Hook to transform errors thrown during handle(). * Override this method to wrap, replace, or re-classify errors before * chanfana's default error formatting runs. * * The returned value is used for all subsequent error handling: * - If `raiseOnError` is true, the returned error is re-thrown (e.g. to Hono's onError). * - Otherwise, chanfana's `formatChanfanaError` is called on the returned error. * * @example * ```typescript * class MyRoute extends OpenAPIRoute { * protected handleError(error: unknown): unknown { * // Wrap ApiExceptions so they bypass chanfana's formatter * // and reach Hono's onError handler directly * if (error instanceof ApiException) { * return new MyCustomError(error); * } * return error; * } * } * ``` * * @param error - The caught error * @returns The error (possibly transformed) to be handled by chanfana. * Should be an Error instance. Returning non-Error values (null, strings, etc.) * may produce confusing stack traces if the error is ultimately re-thrown. */ protected handleError(error: unknown): unknown; /** * Main execution method called by the router. * Handles validation, error catching, and response formatting. * * Caches are reset on each execution to ensure request isolation. * * @param args - Handler arguments from the router * @returns Response object */ execute(...args: HandleArgs): Promise<any>; /** * Finds the Zod schema for a response with the given status code. * Falls back to the "default" response if no exact match is found. * @param statusCode - HTTP status code to look up * @returns Zod schema for the response body, or undefined if not found */ getResponseSchema(statusCode: number): z.ZodType | undefined; /** * Validates a response body against the response schema. * For plain objects, parses through Zod to strip unknown fields and validate types. * For Response objects with JSON content, clones the body, parses, and reconstructs * with corrected headers (Content-Length/Transfer-Encoding are removed). * Responses without a matching Zod schema (including non-JSON responses) are passed through unchanged. * * Note: Body-dependent headers such as ETag or Content-MD5 are preserved from the * original response and may become stale after fields are stripped or defaults applied. * * @param resp - The response from handle() * @returns The validated/stripped response * @throws ZodError if the response body fails schema validation * @throws SyntaxError if a Response claims application/json but the body is not valid JSON */ validateResponse(resp: any): Promise<any>; /** * Validates the incoming request against the schema. * @param request - The incoming Request object * @returns Validated and typed request data * @throws ZodError if validation fails */ validateRequest(request: Request): Promise<any>; } type MergeTypedResponse<T> = T extends Promise<infer T2> ? T2 extends TypedResponse ? T2 : TypedResponse : T extends TypedResponse ? T : TypedResponse; type HonoOpenAPIRouterType<E extends Env = Env, S extends Schema = {}, BasePath extends string = "/"> = OpenAPIRouterType<Hono<E, S, BasePath>> & { on(method: string, path: string, endpoint: typeof OpenAPIRoute<any>): Hono<E, S, BasePath>["on"]; on(method: string, path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["on"]; route<SubPath extends string, SubEnv extends Env, SubSchema extends Schema, SubBasePath extends string>(path: SubPath, app: HonoOpenAPIRouterType<SubEnv, SubSchema, SubBasePath>): HonoOpenAPIRouterType<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> | S, BasePath>; all<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"all", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; delete<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"delete", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; delete(path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["delete"]; get<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"get", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; get(path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["get"]; patch<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"patch", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; patch(path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["patch"]; post<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"post", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; post(path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["post"]; put<P extends string, I extends Input = BlankInput, R extends HandlerResponse<any> = any>(path: P, endpoint: typeof OpenAPIRoute<any> | H): HonoOpenAPIRouterType<E, S & ToSchema<"put", MergePath<BasePath, P>, I, MergeTypedResponse<R>>, BasePath>; put(path: string, router: Hono<E, S, BasePath>): Hono<E, S, BasePath>["put"]; } & Hono<E, S, BasePath>; declare class HonoOpenAPIHandler extends OpenAPIHandler { protected get routerHandlesBasePrefix(): boolean; /** * Wraps route handlers to catch chanfana errors (ZodError, ApiException) * and convert them to Hono HTTPException instances. This allows errors to * flow through Hono's onError handler while preserving chanfana's default * error response format via HTTPException.getResponse(). */ protected wrapHandler(handler: (...args: any[]) => Promise<Response>): (...args: any[]) => Promise<Response>; getRequest(args: any[]): any; getUrlParams(args: any[]): Record<string, any>; getBindings(args: any[]): Record<string, any>; } declare function fromHono<M extends Hono<E, S, BasePath>, E extends Env = M extends Hono<infer E, any, any> ? E : never, S extends Schema = M extends Hono<any, infer S, any> ? S : never, BasePath extends string = M extends Hono<any, any, infer BP> ? BP : never>(router: M, options?: RouterOptions): HonoOpenAPIRouterType<E, S, BasePath>; type IttyRouterOpenAPIRouterType<M> = OpenAPIRouterType<M> & { all(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["all"]; all(path: string, router: M): (M & any)["all"]; delete(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["delete"]; delete(path: string, router: M): (M & any)["delete"]; get(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["get"]; get(path: string, router: M): (M & any)["get"]; head(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["head"]; head(path: string, router: M): (M & any)["head"]; patch(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["patch"]; patch(path: string, router: M): (M & any)["patch"]; post(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["post"]; post(path: string, router: M): (M & any)["post"]; put(path: string, endpoint: typeof OpenAPIRoute<any>): (M & any)["put"]; put(path: string, router: M): (M & any)["put"]; }; declare class IttyRouterOpenAPIHandler extends OpenAPIHandler { getRequest(args: any[]): any; getUrlParams(args: any[]): Record<string, any>; getBindings(args: any[]): Record<string, any>; } declare function fromIttyRouter<M>(router: M, options?: RouterOptions): M & IttyRouterOpenAPIRouterType<M>; type JsonContent<T> = { content: { "application/json": { schema: z.ZodType<T>; }; }; }; declare const contentJson: <T>(schema: z.ZodType<T>) => JsonContent<T>; type FilterCondition = { field: string; operator: string; value: string | number | boolean | null; }; type ListFilters = { filters: Array<FilterCondition>; options: { page?: number; per_page?: number; order_by?: string; order_by_direction?: OrderByDirection; [key: string]: unknown; }; }; type Filters = { filters: Array<FilterCondition>; }; type UpdateFilters = { filters: Array<FilterCondition>; updatedData: Record<string, any>; }; type UpdatedData = { updatedData: Record<string, any>; }; type SerializerContext = { filters?: Array<FilterCondition>; options?: { page?: number; per_page?: number; order_by?: string; order_by_direction?: OrderByDirection; [key: string]: unknown; }; }; type Model = { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer?: (obj: object, context?: SerializerContext) => object; serializerSchema?: AnyZodObject; }; type ModelComplete = SetRequired<Model, "serializer" | "serializerSchema">; type MetaInput = { model: Model; fields?: AnyZodObject; pathParameters?: Array<string>; tags?: Array<string>; }; type Meta = { model: ModelComplete; fields: AnyZodObject; tags?: Array<string>; }; type O<M extends MetaInput> = z.infer<M["model"]["schema"]>; type ListResult<O> = { result: Array<O>; }; declare function MetaGenerator(meta: MetaInput): { fields: AnyZodObject; model: { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer: ((obj: object, context?: SerializerContext) => object) | ((obj: any, _context?: SerializerContext) => any); serializerSchema: AnyZodObject; }; pathParameters: string[] | null; tags: string[] | undefined; }; declare function metaSchemaProps(meta: MetaInput): Record<string, unknown>; type Logger = { log: (...args: any[]) => void; info: (...args: any[]) => void; warn: (...args: any[]) => void; error: (...args: any[]) => void; debug: (...args: any[]) => void; trace: (...args: any[]) => void; }; declare class CreateEndpoint<HandleArgs extends Array<object> = Array<object>> extends OpenAPIRoute<HandleArgs> { _meta: MetaInput; get meta(): { fields: AnyZodObject; model: { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer: ((obj: object, context?: SerializerContext) => object) | ((obj: any, _context?: SerializerContext) => any); serializerSchema: AnyZodObject; }; pathParameters: string[] | null; tags: string[] | undefined; }; getSchema(): { servers?: openapi3_ts_oas30.ServerObject[] | undefined; security?: openapi3_ts_oas30.SecurityRequirementObject[] | openapi3_ts_oas31.SecurityRequirementObject[] | undefined; tags?: string[] | undefined; externalDocs?: openapi3_ts_oas30.ExternalDocumentationObject | openapi3_ts_oas31.ExternalDocumentationObject | undefined; summary?: string | undefined; description?: string | undefined; operationId?: string | undefined; parameters?: (openapi3_ts_oas30.ParameterObject | openapi3_ts_oas30.ReferenceObject)[] | (openapi3_ts_oas31.ParameterObject | openapi3_ts_oas31.ReferenceObject)[] | undefined; requestBody?: openapi3_ts_oas30.ReferenceObject | openapi3_ts_oas31.ReferenceObject | openapi3_ts_oas30.RequestBodyObject | openapi3_ts_oas31.RequestBodyObject | undefined; callbacks?: openapi3_ts_oas30.CallbacksObject | openapi3_ts_oas31.CallbacksObject | undefined; deprecated?: boolean | undefined; request: RequestTypes | { body: ZodRequestBody | { content: { "application/json": { schema: z.ZodType<Record<string, unknown>, unknown, z.core.$ZodTypeInternals<Record<string, unknown>, unknown>>; }; }; }; params: AnyZodObject; query?: AnyZodObject; cookies?: AnyZodObject; headers?: AnyZodObject | z.ZodType<unknown>[]; }; responses: { [statusCode: string]: ResponseConfig; } | { "201": { description: string; headers?: AnyZodObject | (openapi3_ts_oas30.HeadersObject | openapi3_ts_oas31.HeadersObject); links?: openapi3_ts_oas30.LinksObject | openapi3_ts_oas31.LinksObject; content: Partial<Record<ZodMediaType, _asteasolutions_zod_to_openapi.ZodMediaTypeObject>> | { "application/json": { schema: z.ZodType<{ success: boolean; result: Record<string, unknown>; }, unknown, z.core.$ZodTypeInternals<{ success: boolean; result: Record<string, unknown>; }, unknown>>; }; }; }; }; "x-ignore"?: boolean | undefined; }; getObject(): Promise<O<typeof this._meta>>; before(data: O<typeof this._meta>): Promise<O<typeof this._meta>>; after(data: O<typeof this._meta>): Promise<O<typeof this._meta>>; create(data: O<typeof this._meta>): Promise<O<typeof this._meta>>; handle(..._args: HandleArgs): Promise<Response>; } /** * Base exception class for API errors. * Extend this class to create custom API exceptions with specific status codes and error codes. * * @example * ```typescript * throw new ApiException("Something went wrong"); * ``` */ declare class ApiException extends Error { isVisible: boolean; message: string; default_message: string; status: number; code: number; includesPath: boolean; constructor(message?: string); buildResponse(): { code: number; message: string; }[]; static schema(): { [x: number]: { content: { "application/json": { schema: z.ZodType<{ success: false; errors: { code: number; message: string; }[]; }, unknown, z.core.$ZodTypeInternals<{ success: false; errors: { code: number; message: string; }[]; }, unknown>>; }; }; description: string; }; }; } /** * Exception for input validation errors (400). * Used when request data fails Zod schema validation. * * @example * ```typescript * throw new InputValidationException("Invalid email format", ["body", "email"]); * ``` */ declare class InputValidationException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; path: null; includesPath: boolean; constructor(message?: string, path?: any); buildResponse(): { code: number; message: string; path: null; }[]; } /** * Exception that aggregates multiple API exceptions. * The highest status code among all errors will be used as the response status. * * @example * ```typescript * throw new MultiException([ * new InputValidationException("Invalid email", ["body", "email"]), * new InputValidationException("Invalid name", ["body", "name"]), * ]); * ``` */ declare class MultiException extends ApiException { isVisible: boolean; errors: Array<ApiException>; status: number; constructor(errors: Array<ApiException>); buildResponse(): { code: number; message: string; }[]; } /** * Exception for resource not found (404). * Used when the requested resource doesn't exist. * * @example * ```typescript * throw new NotFoundException("User not found"); * ``` */ declare class NotFoundException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for unauthorized access (401). * Used when authentication is required but not provided or invalid. */ declare class UnauthorizedException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for forbidden access (403). * Used when the user is authenticated but doesn't have permission. */ declare class ForbiddenException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for method not allowed (405). * Used when the HTTP method is not supported for the requested resource. */ declare class MethodNotAllowedException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for conflict errors (409). * Used when the request conflicts with the current state (e.g., duplicate resource). */ declare class ConflictException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for unprocessable entity (422). * Used when the request is well-formed but semantically incorrect. */ declare class UnprocessableEntityException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; includesPath: boolean; path: any; constructor(message?: string, path?: any); buildResponse(): { code: number; message: string; path: any; }[]; } /** * Exception for rate limiting (429). * Used when the user has sent too many requests in a given time period. */ declare class TooManyRequestsException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; retryAfter?: number; constructor(message?: string, retryAfter?: number); } /** * Exception for unexpected server errors (500). * Unlike the base ApiException (also 500, code 7000), this class defaults to isVisible=false, * meaning the error message is hidden from clients and replaced with "Internal Error". * Use this for errors that should be logged but not exposed to end users. * Use the base ApiException when the error message is safe to expose. */ declare class InternalServerErrorException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for bad gateway errors (502). * Used when an upstream server returns an invalid response. */ declare class BadGatewayException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for service unavailable (503). * Used when the server is temporarily unavailable (maintenance, overload). */ declare class ServiceUnavailableException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; retryAfter?: number; constructor(message?: string, retryAfter?: number); } /** * Exception for gateway timeout (504). * Used when an upstream server doesn't respond in time. */ declare class GatewayTimeoutException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; } /** * Exception for response validation errors (500). * Used when a handler's response doesn't match its declared Zod response schema. * This is a server-side bug (the handler produced invalid data), not a client error. * Error details are hidden from clients (isVisible=false) and logged via console.error. */ declare class ResponseValidationException extends ApiException { isVisible: boolean; default_message: string; status: number; code: number; constructor(message?: string, options?: ErrorOptions); } /** * Result from getSafeFilters - contains validated SQL conditions and parameters */ interface SafeFilters { /** Array of SQL condition strings (e.g., ["id = ?1", "name = ?2"]) */ conditions: string[]; /** Array of parameter values to bind to the prepared statement */ conditionsParams: (string | number | boolean | null)[]; } /** * Validates that a string is a safe SQL identifier (table name, column name). * Prevents SQL injection by only allowing alphanumeric characters and underscores. * * @param identifier - The identifier to validate * @param type - Type of identifier for error message (e.g., "table", "column") * @returns The validated identifier * @throws ApiException if the identifier is invalid */ declare function validateSqlIdentifier(identifier: string, type: string): string; /** * Validates a table name for safe use in SQL queries. * * @param tableName - The table name to validate * @returns The validated table name * @throws ApiException if the table name is invalid */ declare function validateTableName(tableName: string): string; /** * Validates a column name for safe use in SQL queries. * * @param columnName - The column name to validate * @param validColumns - Optional array of valid column names to check against * @returns The validated column name * @throws ApiException if the column name is invalid or not in the valid list */ declare function validateColumnName(columnName: string, validColumns?: string[]): string; /** * Validates and normalizes an ORDER BY direction. * * @param direction - The direction string to validate * @returns "asc" or "desc" */ declare function validateOrderDirection(direction: string | undefined): OrderByDirection; /** * Validates an ORDER BY column against a whitelist of allowed columns. * * @param column - The column name to order by * @param allowedColumns - Array of columns that are allowed for ordering * @param fallbackColumn - Column to use if the provided column is not allowed * @returns The validated column name or fallback */ declare function validateOrderByColumn(column: string | undefined, allowedColumns: string[], fallbackColumn: string): string; /** * Builds safe SQL filter conditions from an array of filter conditions. * Validates all column names against the provided schema columns. * * @param filters - Array of filter conditions * @param validColumns - Array of valid column names from the schema * @param startParamIndex - Starting index for parameter placeholders (default: 1) * @returns SafeFilters object with conditions and parameters * @throws ApiException if any column name is invalid or operator is not supported */ declare function buildSafeFilters(filters: FilterCondition[], validColumns: string[], startParamIndex?: number): SafeFilters; /** * Builds safe SQL filter conditions for primary key lookups only. * Filters out any conditions that are not on primary key columns. * * @param filters - Filters object containing filter conditions * @param primaryKeys - Array of primary key column names * @param validColumns - Array of valid column names from the schema * @param startParamIndex - Starting index for parameter placeholders (default: 1) * @returns SafeFilters object with conditions and parameters */ declare function buildPrimaryKeyFilters(filters: Filters, primaryKeys: string[], validColumns: string[], startParamIndex?: number): SafeFilters; /** * Gets a D1 database binding from the worker environment. * * @param getBindings - Function to get bindings from router args * @param args - Handler arguments * @param dbName - Name of the D1 binding * @returns D1Database instance * @throws ApiException if binding is not defined or is not a D1 binding */ declare function getD1Binding(getBindings: (args: any[]) => Record<string, any>, args: any[], dbName: string): D1Database; /** * Handles database errors and maps UNIQUE constraint violations to custom exceptions. * * @param error - The caught error * @param constraintsMessages - Map of constraint names to custom exceptions * @param logger - Optional logger for error tracking * @param operation - Description of the operation for logging * @throws The mapped InputValidationException or a sanitized ApiException */ declare function handleDbError(error: Error, constraintsMessages: Record<string, InputValidationException>, logger?: Logger, operation?: string): never; /** * Builds a safe WHERE clause from conditions array. * * @param conditions - Array of condition strings * @returns WHERE clause string or empty string if no conditions */ declare function buildWhereClause(conditions: string[]): string; /** * Builds a safe ORDER BY clause. * * @param column - Validated column name * @param direction - Validated direction ("asc" or "desc") * @returns ORDER BY clause string */ declare function buildOrderByClause(column: string, direction: OrderByDirection): string; /** * D1-specific CreateEndpoint implementation. * Provides automatic INSERT operations with SQL injection prevention. */ declare class D1CreateEndpoint<HandleArgs extends Array<object> = Array<object>> extends CreateEndpoint<HandleArgs> { /** Name of the D1 database binding in the worker environment. Defaults to "DB" */ dbName: string; /** Optional logger for debugging and error tracking */ logger?: Logger; /** Custom error messages for UNIQUE constraint violations. Keys are constraint names (e.g., "users.email") */ constraintsMessages: Record<string, InputValidationException>; /** * Gets the D1 database binding from the worker environment. * @returns D1Database instance * @throws ApiException if binding is not defined or is not a D1 binding */ getDBBinding(): D1Database; /** * Gets the list of valid column names from the model schema. * @returns Array of valid column names */ protected getValidColumns(): string[]; /** * Creates a new record in the database. * @param data - The validated data to insert * @returns The created record * @throws ApiException on database errors */ create(data: O<typeof this._meta>): Promise<O<typeof this._meta>>; } declare class DeleteEndpoint<HandleArgs extends Array<object> = Array<object>> extends OpenAPIRoute<HandleArgs> { _meta: MetaInput; get meta(): { fields: AnyZodObject; model: { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer: ((obj: object, context?: SerializerContext) => object) | ((obj: any, _context?: SerializerContext) => any); serializerSchema: AnyZodObject; }; pathParameters: string[] | null; tags: string[] | undefined; }; getSchema(): { servers?: openapi3_ts_oas30.ServerObject[] | undefined; security?: openapi3_ts_oas30.SecurityRequirementObject[] | openapi3_ts_oas31.SecurityRequirementObject[] | undefined; tags?: string[] | undefined; externalDocs?: openapi3_ts_oas30.ExternalDocumentationObject | openapi3_ts_oas31.ExternalDocumentationObject | undefined; summary?: string | undefined; description?: string | undefined; operationId?: string | undefined; parameters?: (openapi3_ts_oas30.ParameterObject | openapi3_ts_oas30.ReferenceObject)[] | (openapi3_ts_oas31.ParameterObject | openapi3_ts_oas31.ReferenceObject)[] | undefined; requestBody?: openapi3_ts_oas30.ReferenceObject | openapi3_ts_oas31.ReferenceObject | openapi3_ts_oas30.RequestBodyObject | openapi3_ts_oas31.RequestBodyObject | undefined; callbacks?: openapi3_ts_oas30.CallbacksObject | openapi3_ts_oas31.CallbacksObject | undefined; deprecated?: boolean | undefined; request: RequestTypes | { body: ZodRequestBody | { content: { "application/json": { schema: z.ZodType<{ [x: string]: any; }, unknown, z.core.$ZodTypeInternals<{ [x: string]: any; }, unknown>>; }; }; } | undefined; params: AnyZodObject; query?: AnyZodObject; cookies?: AnyZodObject; headers?: AnyZodObject | z.ZodType<unknown>[]; }; responses: { [statusCode: string]: ResponseConfig; } | { "200": { description: string; headers?: AnyZodObject | (openapi3_ts_oas30.HeadersObject | openapi3_ts_oas31.HeadersObject); links?: openapi3_ts_oas30.LinksObject | openapi3_ts_oas31.LinksObject; content: Partial<Record<ZodMediaType, _asteasolutions_zod_to_openapi.ZodMediaTypeObject>> | { "application/json": { schema: z.ZodType<{ success: boolean; result: Record<string, unknown>; }, unknown, z.core.$ZodTypeInternals<{ success: boolean; result: Record<string, unknown>; }, unknown>>; }; }; }; }; "x-ignore"?: boolean | undefined; }; getFilters(): Promise<Filters>; before(_oldObj: O<typeof this._meta>, filters: Filters): Promise<Filters>; after(data: O<typeof this._meta>): Promise<O<typeof this._meta>>; delete(_oldObj: O<typeof this._meta>, _filters: Filters): Promise<O<typeof this._meta> | null>; getObject(_filters: Filters): Promise<O<typeof this._meta> | null>; handle(..._args: HandleArgs): Promise<{ success: boolean; result: any; }>; } /** * D1-specific DeleteEndpoint implementation. * Provides automatic DELETE operations with SQL injection prevention. * Only allows deletion by primary key fields for safety. */ declare class D1DeleteEndpoint<HandleArgs extends Array<object> = Array<object>> extends DeleteEndpoint<HandleArgs> { /** Name of the D1 database binding in the worker environment. Defaults to "DB" */ dbName: string; /** Optional logger for debugging and error tracking */ logger?: Logger; /** Custom error messages for UNIQUE constraint violations. Keys are constraint names (e.g., "users.email") */ constraintsMessages: Record<string, InputValidationException>; /** * Gets the D1 database binding from the worker environment. * @returns D1Database instance * @throws ApiException if binding is not defined or is not a D1 binding */ getDBBinding(): D1Database; /** * Gets the list of valid column names from the model schema. * @returns Array of valid column names */ protected getValidColumns(): string[]; /** * Builds safe filters that only apply to primary keys. * This ensures that deletes can only target specific records by primary key. * @param filters - Filters object containing all filter conditions * @returns SafeFilters with validated conditions and parameters */ protected getSafeFilters(filters: Filters): SafeFilters; /** * Fetches the existing object before deletion. * @param filters - Filter conditions for finding the object * @returns The existing record or null if not found */ getObject(filters: Filters): Promise<O<typeof this._meta> | null>; /** * Deletes a record from the database. * @param oldObj - The existing record to delete * @param filters - Filter conditions for the deletion * @returns The deleted record or null if deletion failed * @throws ApiException on database errors */ delete(oldObj: O<typeof this._meta>, filters: Filters): Promise<O<typeof this._meta> | null>; } declare class ListEndpoint<HandleArgs extends Array<object> = Array<object>> extends OpenAPIRoute<HandleArgs> { _meta: MetaInput; get meta(): { fields: AnyZodObject; model: { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer: ((obj: object, context?: SerializerContext) => object) | ((obj: any, _context?: SerializerContext) => any); serializerSchema: AnyZodObject; }; pathParameters: string[] | null; tags: string[] | undefined; }; filterFields?: Array<string>; searchFields?: Array<string>; searchFieldName: string; pageFieldName: string; perPageFieldName: string; orderByFieldName: string; orderByDirectionFieldName: string; orderByFields: string[]; defaultOrderBy?: string; /** Default sort direction when order_by is used. Defaults to "asc". */ defaultOrderByDirection: OrderByDirection; get optionFields(): string[]; getSchema(): { servers?: openapi3_ts_oas30.ServerObject[] | undefined; security?: openapi3_ts_oas30.SecurityRequirementObject[] | openapi3_ts_oas31.SecurityRequirementObject[] | undefined; tags?: string[] | undefined; externalDocs?: openapi3_ts_oas30.ExternalDocumentationObject | openapi3_ts_oas31.ExternalDocumentationObject | undefined; summary?: string | undefined; description?: string | undefined; operationId?: string | undefined; parameters?: (openapi3_ts_oas30.ParameterObject | openapi3_ts_oas30.ReferenceObject)[] | (openapi3_ts_oas31.ParameterObject | openapi3_ts_oas31.ReferenceObject)[] | undefined; requestBody?: openapi3_ts_oas30.ReferenceObject | openapi3_ts_oas31.ReferenceObject | openapi3_ts_oas30.RequestBodyObject | openapi3_ts_oas31.RequestBodyObject | undefined; callbacks?: openapi3_ts_oas30.CallbacksObject | openapi3_ts_oas31.CallbacksObject | undefined; deprecated?: boolean | undefined; request: RequestTypes; responses: { [statusCode: string]: ResponseConfig; } | { "200": { description: string; headers?: AnyZodObject | (openapi3_ts_oas30.HeadersObject | openapi3_ts_oas31.HeadersObject); links?: openapi3_ts_oas30.LinksObject | openapi3_ts_oas31.LinksObject; content: Partial<Record<ZodMediaType, _asteasolutions_zod_to_openapi.ZodMediaTypeObject>> | { "application/json": { schema: z.ZodType<{ success: boolean; result: Record<string, unknown>[]; }, unknown, z.core.$ZodTypeInternals<{ success: boolean; result: Record<string, unknown>[]; }, unknown>>; }; }; }; }; "x-ignore"?: boolean | undefined; }; getFilters(): Promise<ListFilters>; before(filters: ListFilters): Promise<ListFilters>; after(data: ListResult<O<typeof this._meta>>): Promise<ListResult<O<typeof this._meta>>>; list(_filters: ListFilters): Promise<ListResult<O<typeof this._meta>>>; handle(..._args: HandleArgs): Promise<{ result: Record<string, unknown>[]; success: boolean; }>; } /** * D1-specific ListEndpoint implementation. * Provides automatic SELECT with pagination, filtering, and ordering. * Includes SQL injection prevention for all query components. */ declare class D1ListEndpoint<HandleArgs extends Array<object> = Array<object>> extends ListEndpoint<HandleArgs> { /** Name of the D1 database binding in the worker environment. Defaults to "DB" */ dbName: string; /** Optional logger for debugging and error tracking */ logger?: Logger; /** Maximum number of results per page. Override to change the limit. */ maxPerPage: number; /** * Gets the D1 database binding from the worker environment. * @returns D1Database instance * @throws ApiException if binding is not defined or is not a D1 binding */ getDBBinding(): D1Database; /** * Gets the list of valid column names from the model schema. * @returns Array of valid column names */ protected getValidColumns(): string[]; /** * Lists records with pagination, filtering, and ordering. * @param filters - Filter conditions and pagination options * @returns Object containing results and pagination info */ list(filters: { filters?: Array<{ field: string; operator: string; value: unknown; }>; options?: ListFilters["options"]; }): Promise<{ result: Record<string, unknown>[]; result_info: { count: number; page: number; per_page: number; total_count: number; }; }>; } declare class ReadEndpoint<HandleArgs extends Array<object> = Array<object>> extends OpenAPIRoute<HandleArgs> { _meta: MetaInput; get meta(): { fields: AnyZodObject; model: { tableName: string; schema: AnyZodObject; primaryKeys: Array<string>; serializer: ((obj: object, context?: SerializerContext) => object) | ((obj: any, _context?: SerializerContext) => any); serializerSchema: AnyZodObject; }; pathParameters: string[] | null; tags: string[] | undefined; }; getSchema(): { servers?: openapi3_ts_oas30.ServerObject[] | undefined; security?: openapi3_ts_oas30.SecurityRequirementObject[] | openapi3_ts_oas31.SecurityRequirementObject[] | undefined; tag