@beignet/core
Version:
Core framework primitives for Beignet
139 lines • 6.43 kB
TypeScript
import type { ContractLike, ResolveContract } from "./contract-like.js";
import type { AddedCtxFromHooks, Handler, RouteHook } from "./http.js";
import type { AnyUseCaseLike, AnyUseCaseRouteDef, UseCaseRouteDef, ValidatedRouteInputs } from "./use-case-route.js";
/**
* Route registration that connects a contract to the handler implementing it.
*
* Most apps keep route definitions in `features/<feature>/routes.ts` and
* compose them with `defineRoutes(...)`.
*/
export type HandlerRouteDef<Ctx, CLike extends ContractLike = ContractLike, Hooks extends readonly RouteHook<Ctx, object>[] = readonly RouteHook<Ctx, object>[]> = {
/**
* Contract builder or plain contract config for this route.
*/
contract: CLike;
/**
* Route-scoped hooks that run after group hooks and before the handler.
*/
hooks?: Hooks;
/**
* Handler that implements the contract.
*/
handle: Handler<Ctx & AddedCtxFromHooks<Hooks>, ResolveContract<CLike>>;
useCase?: never;
input?: never;
status?: never;
};
/**
* Route registration for one contract.
*
* Routes either bind the contract directly to a use case (`{ contract,
* useCase }`) or implement a full handler (`{ contract, handle }`). The full
* handler form is the escape hatch for response headers, streaming, native
* `Response` values, and multi-status handling.
*/
export type RouteDef<Ctx, CLike extends ContractLike = ContractLike, Hooks extends readonly RouteHook<Ctx, object>[] = readonly RouteHook<Ctx, object>[]> = HandlerRouteDef<Ctx, CLike, Hooks> | AnyUseCaseRouteDef<Ctx, CLike, Hooks>;
type AnyRouteDef = RouteDef<any, any>;
type PlainRouteDef<Ctx> = RouteDef<Ctx, any, readonly []>;
type AnyContractRouteDef<Ctx> = RouteDef<Ctx, any>;
declare const ROUTE_GROUP_KIND = "beignet.route-group";
/**
* Named collection of related route registrations.
*
* Route groups colocate feature routes and can apply scoped route hooks to
* every route in the group. `defineRoutes(...)` flattens them before server
* registration while preserving those hooks.
*/
export type RouteGroup<Ctx, Routes extends readonly AnyRouteDef[] = readonly AnyRouteDef[]> = {
/**
* Internal marker used by `defineRoutes(...)`.
*/
kind: typeof ROUTE_GROUP_KIND;
/**
* Human-readable group name.
*/
name: string;
/**
* Hooks applied to every route in this group.
*/
hooks?: readonly RouteHook<Ctx, object>[];
/**
* Route definitions in this group.
*/
routes: Routes;
};
type RouteInput<Ctx> = AnyContractRouteDef<Ctx> | AnyRouteDef | RouteGroup<Ctx, readonly AnyRouteDef[]>;
type ContextualRouteInput<Ctx> = PlainRouteDef<Ctx> | RouteGroup<Ctx, readonly AnyRouteDef[]>;
/**
* App-bound builder for one route registration.
*/
export type RouteDefinitionBuilder<Ctx> = {
<CLike extends ContractLike, UC extends AnyUseCaseLike, const Hooks extends readonly RouteHook<Ctx, object>[] = readonly []>(route: UseCaseRouteDef<Ctx, CLike, UC, Hooks>): UseCaseRouteDef<Ctx, CLike, UC, Hooks>;
<CLike extends ContractLike, const Hooks extends readonly RouteHook<Ctx, object>[] = readonly []>(route: HandlerRouteDef<Ctx, CLike, Hooks>): HandlerRouteDef<Ctx, CLike, Hooks>;
};
/**
* App-bound builder for a named route group.
*/
export type RouteGroupBuilder<Ctx> = {
<const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [], const R extends readonly PlainRouteDef<Ctx & AddedCtxFromHooks<GroupHooks>>[] = readonly PlainRouteDef<Ctx & AddedCtxFromHooks<GroupHooks>>[]>(group: {
name: string;
hooks?: GroupHooks;
routes: R & ValidatedRouteInputs<Ctx & AddedCtxFromHooks<GroupHooks>, R>;
}): RouteGroup<Ctx, R>;
<const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [], const R extends readonly AnyRouteDef[] = readonly AnyRouteDef[]>(group: {
name: string;
hooks?: GroupHooks;
routes: R & ValidatedRouteInputs<Ctx & AddedCtxFromHooks<GroupHooks>, R>;
}): RouteGroup<Ctx, R>;
};
/**
* Route declaration builders bound to an application context type.
*/
export interface Routes<Ctx> {
/** Define one route registration. */
defineRoute: RouteDefinitionBuilder<Ctx>;
/** Define a named group of related route registrations. */
defineRouteGroup: RouteGroupBuilder<Ctx>;
}
type RoutesFromInput<Input> = Input extends RouteGroup<infer _Ctx, infer Routes> ? Routes : Input extends AnyRouteDef ? readonly [Input] : readonly [];
type FlattenRouteInputs<Inputs extends readonly unknown[]> = number extends Inputs["length"] ? readonly AnyRouteDef[] : Inputs extends readonly [infer First, ...infer Rest] ? readonly [...RoutesFromInput<First>, ...FlattenRouteInputs<Rest>] : readonly [];
type ContractsFromRouteList<Routes extends readonly RouteDef<any, any>[]> = {
readonly [Index in keyof Routes]: Routes[Index] extends RouteDef<any, infer CLike> ? ResolveContract<CLike> : never;
};
/**
* Define and flatten route registrations with strong type inference.
*
* Pass route definitions and route groups here before `createServer(...)`.
* Group entries are flattened so downstream tooling receives one route list.
*
* @example
* ```ts
* const routes = defineRoutes<AppContext>([
* { contract: listPosts, useCase: listPostsUseCase },
* ]);
* ```
*/
export declare function defineRoutes<Ctx, const R extends readonly ContextualRouteInput<Ctx>[] = readonly ContextualRouteInput<Ctx>[]>(routes: R & ValidatedRouteInputs<Ctx, R>): FlattenRouteInputs<R>;
export declare function defineRoutes<Ctx, const R extends readonly RouteInput<Ctx>[] = readonly RouteInput<Ctx>[]>(routes: R & ValidatedRouteInputs<Ctx, R>): FlattenRouteInputs<R>;
/**
* Extract contract configs from a route list.
*
* Use this to drive clients, OpenAPI, and docs from the same route list passed
* to `createServer(...)`.
*/
export declare function contractsFromRoutes<const R extends readonly RouteDef<any, any>[]>(routes: R): ContractsFromRouteList<R>;
/**
* Create route declaration builders bound to an application context type.
*
* Call this once in `lib/routes.ts`, then import the app-bound builders from
* feature route files.
*
* @example
* ```ts
* export const { defineRoute, defineRouteGroup } =
* createRoutes<AppContext>();
* ```
*/
export declare function createRoutes<Ctx>(): Routes<Ctx>;
export {};
//# sourceMappingURL=route-definitions.d.ts.map