@kaito-http/core 3.0.3 → 4.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,7 +1,10 @@
1
+ import { z, ZodTypeDef } from 'zod';
2
+ import { KaitoSSEResponse } from './stream/stream.cjs';
3
+
1
4
  declare class WrappedError<T> extends Error {
2
- readonly data: T;
3
5
  static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
6
  static from<T>(data: T): WrappedError<T>;
7
+ readonly data: T;
5
8
  private constructor();
6
9
  }
7
10
  declare class KaitoError extends Error {
@@ -24,8 +27,6 @@ declare class KaitoRequest {
24
27
  get request(): Request;
25
28
  }
26
29
 
27
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
28
-
29
30
  /**
30
31
  * This class is merely a wrapper around a `Headers` object and a status code.
31
32
  * It's used while the router is executing a route to store any mutations to the status
@@ -45,8 +46,7 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
45
46
  * ```
46
47
  */
47
48
  declare class KaitoHead {
48
- private _headers;
49
- private _status;
49
+ #private;
50
50
  constructor();
51
51
  get headers(): Headers;
52
52
  /**
@@ -84,11 +84,15 @@ type ErroredAPIResponse = {
84
84
  type SuccessfulAPIResponse<T> = {
85
85
  success: true;
86
86
  data: T;
87
- message: 'OK';
88
87
  };
89
88
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
90
89
  type AnyResponse = APIResponse<unknown>;
91
90
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
91
+ type MaybePromise<T> = T | Promise<T>;
92
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
93
+ type NotReadonly<T> = {
94
+ -readonly [K in keyof T]: T[K];
95
+ };
92
96
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
93
97
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
94
98
  } : T extends `${string}:${infer Param}` ? {
@@ -105,91 +109,103 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
105
109
  * @param head - The kaito head object, which contains getters and setters for headers and status
106
110
  * @returns The context for your routes
107
111
  */
108
- type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => Promise<Result>;
109
- /**
110
- * A helper function to create typed necessary functions
111
- *
112
- * @example
113
- * ```ts
114
- * const {router, getContext} = createUtilities(async (req, res) => {
115
- * // Return context here
116
- * })
117
- *
118
- * const app = router().get('/', async () => "hello");
119
- *
120
- * const server = createKaitoHandler({
121
- * router: app,
122
- * getContext,
123
- * // ...
124
- * });
125
- * ```
126
- */
127
- declare function createUtilities<Context>(getContext: GetContext<Context>): {
128
- getContext: GetContext<Context>;
129
- router: () => Router<Context, Context, never>;
130
- };
131
- interface Parsable<Output = any, Input = Output> {
132
- _input: Input;
133
- parse: (value: unknown) => Output;
134
- }
135
- type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
136
- input: Input;
137
- output: Output;
138
- } : never;
139
- declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
112
+ type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
140
113
 
141
- type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
114
+ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
142
115
  ctx: Context;
143
116
  body: BodyOutput;
144
117
  query: QueryOutput;
145
- params: ExtractRouteParams<Path>;
118
+ params: Params;
119
+ };
120
+ type AnyQuery = {
121
+ [key in string]: any;
146
122
  };
147
- type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
148
- type Through<From, To> = (context: From) => Promise<To>;
149
- type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
150
- through: Through<ContextFrom, ContextTo>;
151
- body?: Body;
152
- query?: Query;
123
+ type Through<From, To, RequiredParams extends Record<string, unknown>> = (context: From, params: RequiredParams) => Promise<To>;
124
+ type SSEOutputSpec<Result> = {
125
+ type: 'sse';
126
+ schema: z.Schema<Result>;
127
+ description?: string;
128
+ };
129
+ type JSONOutputSpec<Result> = {
130
+ type: 'json';
131
+ schema: z.Schema<Result>;
132
+ description?: string;
133
+ };
134
+ type OutputSpec<Result> = {
135
+ description?: string;
136
+ body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
137
+ };
138
+ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Record<string, unknown>, Method extends KaitoMethod, Query, Body> = {
139
+ body?: z.Schema<Body>;
140
+ query?: {
141
+ [Key in keyof Query]: z.Schema<Query[Key]>;
142
+ };
153
143
  path: Path;
154
144
  method: Method;
155
- run(arg: RouteArgument<Path, ContextTo, {
156
- [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result> | Result;
145
+ openapi?: OutputSpec<NoInfer<Result>>;
146
+ router: Router<unknown, ContextTo, AdditionalParams, AnyRoute>;
147
+ run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
158
148
  };
159
- type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
149
+ type AnyRoute = Route<any, any, any, any, any, any, any>;
160
150
 
161
- type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextFrom, infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextFrom, ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
151
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
162
152
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
163
- type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
153
+ type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute> = {
164
154
  routes: Set<Routes>;
165
- through: (context: ContextFrom) => Promise<ContextTo>;
155
+ through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
156
+ config: KaitoConfig<ContextFrom>;
157
+ paramsSchema: z.Schema<RequiredParams> | null;
166
158
  };
167
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
168
- declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
159
+ /**
160
+ * Accepts a router instance, and returns a union of all the routes in the router
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const app = router.get('/', () => 'Hello, world!');
165
+ *
166
+ * type Routes = InferRoutes<typeof app>;
167
+ * ```
168
+ */
169
+ type InferRoutes<R extends Router<any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute> ? R : never;
170
+ declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute> {
169
171
  private readonly state;
170
- static create: <Context>() => Router<Context, Context, never>;
171
- private static parseQuery;
172
- constructor(options: RouterState<R, ContextFrom, ContextTo>);
172
+ static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, {}, never>;
173
+ protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R>);
173
174
  get routes(): Set<R>;
174
- add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>>;
175
- readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
176
- freeze: (server: Omit<HandlerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
175
+ private add;
176
+ params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
177
+ [Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
178
+ }) => Router<ContextFrom, ContextTo, NextParams, R> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
179
+ readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
180
+ protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
181
+ route?: never;
182
+ params?: never;
183
+ } | {
184
+ route: R_1;
185
+ params: Record<string, string>;
186
+ };
187
+ private static buildQuerySchema;
188
+ serve: () => (request: Request) => Promise<Response>;
189
+ openapi: (highLevelSpec: {
190
+ info: {
191
+ version: string;
192
+ title: string;
193
+ description?: string;
194
+ };
195
+ servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
196
+ }) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>>;
177
197
  private readonly method;
178
- get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>>;
179
- post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>>;
180
- put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>>;
181
- patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>>;
182
- delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>>;
183
- head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>>;
184
- options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>>;
185
- through: <NextContext>(through: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
198
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>>;
199
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>>;
200
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>>;
201
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>>;
202
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>>;
203
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>>;
204
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>>;
205
+ through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R>;
186
206
  }
187
207
 
188
- type HandlerConfig<ContextFrom> = {
189
- /**
190
- * The root router to mount on this handler.
191
- */
192
- router: Router<ContextFrom, unknown, any>;
208
+ type KaitoConfig<ContextFrom> = {
193
209
  /**
194
210
  * A function that is called to get the context for a request.
195
211
  *
@@ -197,7 +213,7 @@ type HandlerConfig<ContextFrom> = {
197
213
  *
198
214
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
199
215
  */
200
- getContext: GetContext<ContextFrom>;
216
+ getContext?: GetContext<ContextFrom>;
201
217
  /**
202
218
  * A function that is called when an error occurs inside a route handler.
203
219
  *
@@ -209,10 +225,7 @@ type HandlerConfig<ContextFrom> = {
209
225
  * @param arg - The error thrown, and the KaitoRequest instance
210
226
  * @returns A KaitoError or an object with a status and message
211
227
  */
212
- onError: (arg: {
213
- error: Error;
214
- req: KaitoRequest;
215
- }) => Promise<KaitoError | {
228
+ onError?: (error: Error, req: KaitoRequest) => MaybePromise<KaitoError | {
216
229
  status: number;
217
230
  message: string;
218
231
  }>;
@@ -228,7 +241,7 @@ type HandlerConfig<ContextFrom> = {
228
241
  * }
229
242
  * ```
230
243
  */
231
- before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
244
+ before?: (req: Request) => MaybePromise<Response | void | undefined>;
232
245
  /**
233
246
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
247
  *
@@ -248,8 +261,16 @@ type HandlerConfig<ContextFrom> = {
248
261
  * }
249
262
  * ```
250
263
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
264
+ transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
252
265
  };
253
- declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
266
+ /**
267
+ * Create a helper function for instantiating a Kaito router
268
+ *
269
+ * This is the starting point for any Kaito application
270
+ *
271
+ * @param config - The configuration for the router
272
+ * @returns A new Kaito router
273
+ */
274
+ declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context, {}, never>;
254
275
 
255
- export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type HandlerConfig, type InferParsable, type InferRoutes, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, isNodeLikeDev, parsable };
276
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import { z, ZodTypeDef } from 'zod';
2
+ import { KaitoSSEResponse } from './stream/stream.js';
3
+
1
4
  declare class WrappedError<T> extends Error {
2
- readonly data: T;
3
5
  static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
6
  static from<T>(data: T): WrappedError<T>;
7
+ readonly data: T;
5
8
  private constructor();
6
9
  }
7
10
  declare class KaitoError extends Error {
@@ -24,8 +27,6 @@ declare class KaitoRequest {
24
27
  get request(): Request;
25
28
  }
26
29
 
27
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
28
-
29
30
  /**
30
31
  * This class is merely a wrapper around a `Headers` object and a status code.
31
32
  * It's used while the router is executing a route to store any mutations to the status
@@ -45,8 +46,7 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
45
46
  * ```
46
47
  */
47
48
  declare class KaitoHead {
48
- private _headers;
49
- private _status;
49
+ #private;
50
50
  constructor();
51
51
  get headers(): Headers;
52
52
  /**
@@ -84,11 +84,15 @@ type ErroredAPIResponse = {
84
84
  type SuccessfulAPIResponse<T> = {
85
85
  success: true;
86
86
  data: T;
87
- message: 'OK';
88
87
  };
89
88
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
90
89
  type AnyResponse = APIResponse<unknown>;
91
90
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
91
+ type MaybePromise<T> = T | Promise<T>;
92
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
93
+ type NotReadonly<T> = {
94
+ -readonly [K in keyof T]: T[K];
95
+ };
92
96
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
93
97
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
94
98
  } : T extends `${string}:${infer Param}` ? {
@@ -105,91 +109,103 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
105
109
  * @param head - The kaito head object, which contains getters and setters for headers and status
106
110
  * @returns The context for your routes
107
111
  */
108
- type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => Promise<Result>;
109
- /**
110
- * A helper function to create typed necessary functions
111
- *
112
- * @example
113
- * ```ts
114
- * const {router, getContext} = createUtilities(async (req, res) => {
115
- * // Return context here
116
- * })
117
- *
118
- * const app = router().get('/', async () => "hello");
119
- *
120
- * const server = createKaitoHandler({
121
- * router: app,
122
- * getContext,
123
- * // ...
124
- * });
125
- * ```
126
- */
127
- declare function createUtilities<Context>(getContext: GetContext<Context>): {
128
- getContext: GetContext<Context>;
129
- router: () => Router<Context, Context, never>;
130
- };
131
- interface Parsable<Output = any, Input = Output> {
132
- _input: Input;
133
- parse: (value: unknown) => Output;
134
- }
135
- type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
136
- input: Input;
137
- output: Output;
138
- } : never;
139
- declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
112
+ type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
140
113
 
141
- type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
114
+ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
142
115
  ctx: Context;
143
116
  body: BodyOutput;
144
117
  query: QueryOutput;
145
- params: ExtractRouteParams<Path>;
118
+ params: Params;
119
+ };
120
+ type AnyQuery = {
121
+ [key in string]: any;
146
122
  };
147
- type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
148
- type Through<From, To> = (context: From) => Promise<To>;
149
- type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
150
- through: Through<ContextFrom, ContextTo>;
151
- body?: Body;
152
- query?: Query;
123
+ type Through<From, To, RequiredParams extends Record<string, unknown>> = (context: From, params: RequiredParams) => Promise<To>;
124
+ type SSEOutputSpec<Result> = {
125
+ type: 'sse';
126
+ schema: z.Schema<Result>;
127
+ description?: string;
128
+ };
129
+ type JSONOutputSpec<Result> = {
130
+ type: 'json';
131
+ schema: z.Schema<Result>;
132
+ description?: string;
133
+ };
134
+ type OutputSpec<Result> = {
135
+ description?: string;
136
+ body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
137
+ };
138
+ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Record<string, unknown>, Method extends KaitoMethod, Query, Body> = {
139
+ body?: z.Schema<Body>;
140
+ query?: {
141
+ [Key in keyof Query]: z.Schema<Query[Key]>;
142
+ };
153
143
  path: Path;
154
144
  method: Method;
155
- run(arg: RouteArgument<Path, ContextTo, {
156
- [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result> | Result;
145
+ openapi?: OutputSpec<NoInfer<Result>>;
146
+ router: Router<unknown, ContextTo, AdditionalParams, AnyRoute>;
147
+ run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
158
148
  };
159
- type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
149
+ type AnyRoute = Route<any, any, any, any, any, any, any>;
160
150
 
161
- type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextFrom, infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextFrom, ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
151
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
162
152
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
163
- type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
153
+ type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute> = {
164
154
  routes: Set<Routes>;
165
- through: (context: ContextFrom) => Promise<ContextTo>;
155
+ through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
156
+ config: KaitoConfig<ContextFrom>;
157
+ paramsSchema: z.Schema<RequiredParams> | null;
166
158
  };
167
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
168
- declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
159
+ /**
160
+ * Accepts a router instance, and returns a union of all the routes in the router
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const app = router.get('/', () => 'Hello, world!');
165
+ *
166
+ * type Routes = InferRoutes<typeof app>;
167
+ * ```
168
+ */
169
+ type InferRoutes<R extends Router<any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute> ? R : never;
170
+ declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute> {
169
171
  private readonly state;
170
- static create: <Context>() => Router<Context, Context, never>;
171
- private static parseQuery;
172
- constructor(options: RouterState<R, ContextFrom, ContextTo>);
172
+ static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, {}, never>;
173
+ protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R>);
173
174
  get routes(): Set<R>;
174
- add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>>;
175
- readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
176
- freeze: (server: Omit<HandlerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
175
+ private add;
176
+ params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
177
+ [Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
178
+ }) => Router<ContextFrom, ContextTo, NextParams, R> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
179
+ readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
180
+ protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
181
+ route?: never;
182
+ params?: never;
183
+ } | {
184
+ route: R_1;
185
+ params: Record<string, string>;
186
+ };
187
+ private static buildQuerySchema;
188
+ serve: () => (request: Request) => Promise<Response>;
189
+ openapi: (highLevelSpec: {
190
+ info: {
191
+ version: string;
192
+ title: string;
193
+ description?: string;
194
+ };
195
+ servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
196
+ }) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>>;
177
197
  private readonly method;
178
- get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>>;
179
- post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>>;
180
- put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>>;
181
- patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>>;
182
- delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>>;
183
- head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>>;
184
- options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Result | Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>>;
185
- through: <NextContext>(through: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
198
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>>;
199
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>>;
200
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>>;
201
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>>;
202
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>>;
203
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>>;
204
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>>;
205
+ through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R>;
186
206
  }
187
207
 
188
- type HandlerConfig<ContextFrom> = {
189
- /**
190
- * The root router to mount on this handler.
191
- */
192
- router: Router<ContextFrom, unknown, any>;
208
+ type KaitoConfig<ContextFrom> = {
193
209
  /**
194
210
  * A function that is called to get the context for a request.
195
211
  *
@@ -197,7 +213,7 @@ type HandlerConfig<ContextFrom> = {
197
213
  *
198
214
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
199
215
  */
200
- getContext: GetContext<ContextFrom>;
216
+ getContext?: GetContext<ContextFrom>;
201
217
  /**
202
218
  * A function that is called when an error occurs inside a route handler.
203
219
  *
@@ -209,10 +225,7 @@ type HandlerConfig<ContextFrom> = {
209
225
  * @param arg - The error thrown, and the KaitoRequest instance
210
226
  * @returns A KaitoError or an object with a status and message
211
227
  */
212
- onError: (arg: {
213
- error: Error;
214
- req: KaitoRequest;
215
- }) => Promise<KaitoError | {
228
+ onError?: (error: Error, req: KaitoRequest) => MaybePromise<KaitoError | {
216
229
  status: number;
217
230
  message: string;
218
231
  }>;
@@ -228,7 +241,7 @@ type HandlerConfig<ContextFrom> = {
228
241
  * }
229
242
  * ```
230
243
  */
231
- before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
244
+ before?: (req: Request) => MaybePromise<Response | void | undefined>;
232
245
  /**
233
246
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
247
  *
@@ -248,8 +261,16 @@ type HandlerConfig<ContextFrom> = {
248
261
  * }
249
262
  * ```
250
263
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
264
+ transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
252
265
  };
253
- declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
266
+ /**
267
+ * Create a helper function for instantiating a Kaito router
268
+ *
269
+ * This is the starting point for any Kaito application
270
+ *
271
+ * @param config - The configuration for the router
272
+ * @returns A new Kaito router
273
+ */
274
+ declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context, {}, never>;
254
275
 
255
- export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type HandlerConfig, type InferParsable, type InferRoutes, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, isNodeLikeDev, parsable };
276
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };