@kaito-http/core 3.0.2 → 4.0.0-beta.2

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 } 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,7 +27,7 @@ declare class KaitoRequest {
24
27
  get request(): Request;
25
28
  }
26
29
 
27
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
30
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
28
31
 
29
32
  /**
30
33
  * This class is merely a wrapper around a `Headers` object and a status code.
@@ -45,8 +48,7 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
45
48
  * ```
46
49
  */
47
50
  declare class KaitoHead {
48
- private _headers;
49
- private _status;
51
+ #private;
50
52
  constructor();
51
53
  get headers(): Headers;
52
54
  /**
@@ -84,11 +86,11 @@ type ErroredAPIResponse = {
84
86
  type SuccessfulAPIResponse<T> = {
85
87
  success: true;
86
88
  data: T;
87
- message: 'OK';
88
89
  };
89
90
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
90
91
  type AnyResponse = APIResponse<unknown>;
91
92
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
93
+ type MaybePromise<T> = T | Promise<T>;
92
94
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
93
95
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
94
96
  } : T extends `${string}:${infer Param}` ? {
@@ -105,91 +107,99 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
105
107
  * @param head - The kaito head object, which contains getters and setters for headers and status
106
108
  * @returns The context for your routes
107
109
  */
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>;
110
+ type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
140
111
 
141
- type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
112
+ type RouteRunData<Path extends string, Context, QueryOutput, BodyOutput> = {
142
113
  ctx: Context;
143
114
  body: BodyOutput;
144
115
  query: QueryOutput;
145
116
  params: ExtractRouteParams<Path>;
146
117
  };
147
- type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
118
+ type AnyQuery = {
119
+ [key in string]: any;
120
+ };
148
121
  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;
122
+ type SSEOutputSpec<Result> = {
123
+ type: 'sse';
124
+ schema: z.Schema<Result>;
125
+ description?: string;
126
+ };
127
+ type JSONOutputSpec<Result> = {
128
+ type: 'json';
129
+ schema: z.Schema<Result>;
130
+ description?: string;
131
+ };
132
+ type OutputSpec<Result> = {
133
+ description?: string;
134
+ body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
135
+ };
136
+ type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Query, Body> = {
137
+ through: Through<unknown, ContextTo>;
138
+ body?: z.Schema<Body>;
139
+ query?: {
140
+ [Key in keyof Query]: z.Schema<Query[Key]>;
141
+ };
153
142
  path: Path;
154
143
  method: Method;
155
- run(arg: RouteArgument<Path, ContextTo, {
156
- [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result> | Result;
144
+ openapi?: OutputSpec<NoInfer<Result>>;
145
+ run(data: RouteRunData<Path, ContextTo, Query, Body>): Promise<Result> | Result;
158
146
  };
159
- type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
147
+ type AnyRoute = Route<any, any, any, any, any, any>;
160
148
 
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;
149
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
162
150
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
163
- type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
151
+ type RouterState<ContextFrom, ContextTo, Routes extends AnyRoute> = {
164
152
  routes: Set<Routes>;
165
- through: (context: ContextFrom) => Promise<ContextTo>;
153
+ through: (context: unknown) => Promise<ContextTo>;
154
+ config: KaitoConfig<ContextFrom>;
166
155
  };
167
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
156
+ /**
157
+ * Accepts a router instance, and returns a union of all the routes in the router
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * const app = router().get('/', () => 'Hello, world!');
162
+ *
163
+ * type Routes = InferRoutes<typeof app>;
164
+ * ```
165
+ */
166
+ type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R extends AnyRoute> ? R : never;
168
167
  declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
169
168
  private readonly state;
170
- static create: <Context>() => Router<Context, Context, never>;
171
- private static parseQuery;
172
- constructor(options: RouterState<R, ContextFrom, ContextTo>);
169
+ static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, never>;
170
+ constructor(state: RouterState<ContextFrom, ContextTo, R>);
173
171
  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>;
172
+ private add;
173
+ readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
174
+ protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
175
+ route?: never;
176
+ params?: never;
177
+ } | {
178
+ route: R_1;
179
+ params: Record<string, string>;
180
+ };
181
+ private static buildQuerySchema;
182
+ serve: () => (request: Request) => Promise<Response>;
183
+ openapi: (highLevelSpec: {
184
+ info: {
185
+ version: string;
186
+ title: string;
187
+ description?: string;
188
+ };
189
+ servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
190
+ }) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", "GET", AnyQuery, unknown>>;
177
191
  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>;
192
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "GET", Query, Body>>;
193
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "POST", Query, Body>>;
194
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "PUT", Query, Body>>;
195
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "PATCH", Query, Body>>;
196
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "DELETE", Query, Body>>;
197
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "HEAD", Query, Body>>;
198
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "OPTIONS", Query, Body>>;
199
+ through: <NextContext>(through: (context: ContextTo) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, R>;
186
200
  }
187
201
 
188
- type HandlerConfig<ContextFrom> = {
189
- /**
190
- * The root router to mount on this handler.
191
- */
192
- router: Router<ContextFrom, unknown, any>;
202
+ type KaitoConfig<ContextFrom> = {
193
203
  /**
194
204
  * A function that is called to get the context for a request.
195
205
  *
@@ -197,7 +207,7 @@ type HandlerConfig<ContextFrom> = {
197
207
  *
198
208
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
199
209
  */
200
- getContext: GetContext<ContextFrom>;
210
+ getContext?: GetContext<ContextFrom>;
201
211
  /**
202
212
  * A function that is called when an error occurs inside a route handler.
203
213
  *
@@ -209,10 +219,7 @@ type HandlerConfig<ContextFrom> = {
209
219
  * @param arg - The error thrown, and the KaitoRequest instance
210
220
  * @returns A KaitoError or an object with a status and message
211
221
  */
212
- onError: (arg: {
213
- error: Error;
214
- req: KaitoRequest;
215
- }) => Promise<KaitoError | {
222
+ onError?: (error: Error, req: KaitoRequest) => MaybePromise<KaitoError | {
216
223
  status: number;
217
224
  message: string;
218
225
  }>;
@@ -228,7 +235,7 @@ type HandlerConfig<ContextFrom> = {
228
235
  * }
229
236
  * ```
230
237
  */
231
- before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
238
+ before?: (req: Request) => MaybePromise<Response | void | undefined>;
232
239
  /**
233
240
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
241
  *
@@ -248,8 +255,16 @@ type HandlerConfig<ContextFrom> = {
248
255
  * }
249
256
  * ```
250
257
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
258
+ transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
252
259
  };
253
- declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
260
+ /**
261
+ * Create a helper function for instantiating a Kaito router
262
+ *
263
+ * This is the starting point for any Kaito application
264
+ *
265
+ * @param config - The configuration for the router
266
+ * @returns A new Kaito router
267
+ */
268
+ declare function create<Context = null>(config?: KaitoConfig<Context>): () => Router<Context, Context, never>;
254
269
 
255
- export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type HandlerConfig, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, isNodeLikeDev, parsable };
270
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, 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 } 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,7 +27,7 @@ declare class KaitoRequest {
24
27
  get request(): Request;
25
28
  }
26
29
 
27
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
30
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
28
31
 
29
32
  /**
30
33
  * This class is merely a wrapper around a `Headers` object and a status code.
@@ -45,8 +48,7 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
45
48
  * ```
46
49
  */
47
50
  declare class KaitoHead {
48
- private _headers;
49
- private _status;
51
+ #private;
50
52
  constructor();
51
53
  get headers(): Headers;
52
54
  /**
@@ -84,11 +86,11 @@ type ErroredAPIResponse = {
84
86
  type SuccessfulAPIResponse<T> = {
85
87
  success: true;
86
88
  data: T;
87
- message: 'OK';
88
89
  };
89
90
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
90
91
  type AnyResponse = APIResponse<unknown>;
91
92
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
93
+ type MaybePromise<T> = T | Promise<T>;
92
94
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
93
95
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
94
96
  } : T extends `${string}:${infer Param}` ? {
@@ -105,91 +107,99 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
105
107
  * @param head - The kaito head object, which contains getters and setters for headers and status
106
108
  * @returns The context for your routes
107
109
  */
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>;
110
+ type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
140
111
 
141
- type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
112
+ type RouteRunData<Path extends string, Context, QueryOutput, BodyOutput> = {
142
113
  ctx: Context;
143
114
  body: BodyOutput;
144
115
  query: QueryOutput;
145
116
  params: ExtractRouteParams<Path>;
146
117
  };
147
- type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
118
+ type AnyQuery = {
119
+ [key in string]: any;
120
+ };
148
121
  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;
122
+ type SSEOutputSpec<Result> = {
123
+ type: 'sse';
124
+ schema: z.Schema<Result>;
125
+ description?: string;
126
+ };
127
+ type JSONOutputSpec<Result> = {
128
+ type: 'json';
129
+ schema: z.Schema<Result>;
130
+ description?: string;
131
+ };
132
+ type OutputSpec<Result> = {
133
+ description?: string;
134
+ body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
135
+ };
136
+ type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Query, Body> = {
137
+ through: Through<unknown, ContextTo>;
138
+ body?: z.Schema<Body>;
139
+ query?: {
140
+ [Key in keyof Query]: z.Schema<Query[Key]>;
141
+ };
153
142
  path: Path;
154
143
  method: Method;
155
- run(arg: RouteArgument<Path, ContextTo, {
156
- [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result> | Result;
144
+ openapi?: OutputSpec<NoInfer<Result>>;
145
+ run(data: RouteRunData<Path, ContextTo, Query, Body>): Promise<Result> | Result;
158
146
  };
159
- type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
147
+ type AnyRoute = Route<any, any, any, any, any, any>;
160
148
 
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;
149
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
162
150
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
163
- type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
151
+ type RouterState<ContextFrom, ContextTo, Routes extends AnyRoute> = {
164
152
  routes: Set<Routes>;
165
- through: (context: ContextFrom) => Promise<ContextTo>;
153
+ through: (context: unknown) => Promise<ContextTo>;
154
+ config: KaitoConfig<ContextFrom>;
166
155
  };
167
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
156
+ /**
157
+ * Accepts a router instance, and returns a union of all the routes in the router
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * const app = router().get('/', () => 'Hello, world!');
162
+ *
163
+ * type Routes = InferRoutes<typeof app>;
164
+ * ```
165
+ */
166
+ type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R extends AnyRoute> ? R : never;
168
167
  declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
169
168
  private readonly state;
170
- static create: <Context>() => Router<Context, Context, never>;
171
- private static parseQuery;
172
- constructor(options: RouterState<R, ContextFrom, ContextTo>);
169
+ static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, never>;
170
+ constructor(state: RouterState<ContextFrom, ContextTo, R>);
173
171
  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>;
172
+ private add;
173
+ readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
174
+ protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
175
+ route?: never;
176
+ params?: never;
177
+ } | {
178
+ route: R_1;
179
+ params: Record<string, string>;
180
+ };
181
+ private static buildQuerySchema;
182
+ serve: () => (request: Request) => Promise<Response>;
183
+ openapi: (highLevelSpec: {
184
+ info: {
185
+ version: string;
186
+ title: string;
187
+ description?: string;
188
+ };
189
+ servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
190
+ }) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", "GET", AnyQuery, unknown>>;
177
191
  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>;
192
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "GET", Query, Body>>;
193
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "POST", Query, Body>>;
194
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "PUT", Query, Body>>;
195
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "PATCH", Query, Body>>;
196
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "DELETE", Query, Body>>;
197
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "HEAD", Query, Body>>;
198
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<Path, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Result, Path, "OPTIONS", Query, Body>>;
199
+ through: <NextContext>(through: (context: ContextTo) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, R>;
186
200
  }
187
201
 
188
- type HandlerConfig<ContextFrom> = {
189
- /**
190
- * The root router to mount on this handler.
191
- */
192
- router: Router<ContextFrom, unknown, any>;
202
+ type KaitoConfig<ContextFrom> = {
193
203
  /**
194
204
  * A function that is called to get the context for a request.
195
205
  *
@@ -197,7 +207,7 @@ type HandlerConfig<ContextFrom> = {
197
207
  *
198
208
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
199
209
  */
200
- getContext: GetContext<ContextFrom>;
210
+ getContext?: GetContext<ContextFrom>;
201
211
  /**
202
212
  * A function that is called when an error occurs inside a route handler.
203
213
  *
@@ -209,10 +219,7 @@ type HandlerConfig<ContextFrom> = {
209
219
  * @param arg - The error thrown, and the KaitoRequest instance
210
220
  * @returns A KaitoError or an object with a status and message
211
221
  */
212
- onError: (arg: {
213
- error: Error;
214
- req: KaitoRequest;
215
- }) => Promise<KaitoError | {
222
+ onError?: (error: Error, req: KaitoRequest) => MaybePromise<KaitoError | {
216
223
  status: number;
217
224
  message: string;
218
225
  }>;
@@ -228,7 +235,7 @@ type HandlerConfig<ContextFrom> = {
228
235
  * }
229
236
  * ```
230
237
  */
231
- before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
238
+ before?: (req: Request) => MaybePromise<Response | void | undefined>;
232
239
  /**
233
240
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
241
  *
@@ -248,8 +255,16 @@ type HandlerConfig<ContextFrom> = {
248
255
  * }
249
256
  * ```
250
257
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
258
+ transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
252
259
  };
253
- declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
260
+ /**
261
+ * Create a helper function for instantiating a Kaito router
262
+ *
263
+ * This is the starting point for any Kaito application
264
+ *
265
+ * @param config - The configuration for the router
266
+ * @returns A new Kaito router
267
+ */
268
+ declare function create<Context = null>(config?: KaitoConfig<Context>): () => Router<Context, Context, never>;
254
269
 
255
- export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type HandlerConfig, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, isNodeLikeDev, parsable };
270
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };