@kaito-http/core 3.0.0-beta.7 → 3.0.1

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.cjs ADDED
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ KaitoError: () => KaitoError,
24
+ KaitoRequest: () => KaitoRequest,
25
+ Router: () => Router,
26
+ WrappedError: () => WrappedError,
27
+ createKaitoHandler: () => createKaitoHandler,
28
+ createUtilities: () => createUtilities,
29
+ isNodeLikeDev: () => isNodeLikeDev,
30
+ parsable: () => parsable
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/error.ts
35
+ var WrappedError = class _WrappedError extends Error {
36
+ constructor(data) {
37
+ super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
38
+ this.data = data;
39
+ }
40
+ static maybe(maybeError) {
41
+ if (maybeError instanceof Error) {
42
+ return maybeError;
43
+ }
44
+ return _WrappedError.from(maybeError);
45
+ }
46
+ static from(data) {
47
+ return new _WrappedError(data);
48
+ }
49
+ };
50
+ var KaitoError = class extends Error {
51
+ constructor(status, message) {
52
+ super(message);
53
+ this.status = status;
54
+ }
55
+ };
56
+
57
+ // src/handler.ts
58
+ function createKaitoHandler(config) {
59
+ const handle = config.router.freeze(config);
60
+ return async (request) => {
61
+ if (config.before) {
62
+ const result = await config.before(request);
63
+ if (result instanceof Response) {
64
+ if (config.transform) {
65
+ const result2 = await config.transform(request, result);
66
+ if (result2 instanceof Response) return result;
67
+ }
68
+ return result;
69
+ }
70
+ }
71
+ const response = await handle(request);
72
+ if (config.transform) {
73
+ const result = await config.transform(request, response);
74
+ if (result instanceof Response) return result;
75
+ }
76
+ return response;
77
+ };
78
+ }
79
+
80
+ // src/request.ts
81
+ var KaitoRequest = class {
82
+ url;
83
+ _request;
84
+ constructor(url, request) {
85
+ this._request = request;
86
+ this.url = url;
87
+ }
88
+ get headers() {
89
+ return this._request.headers;
90
+ }
91
+ get method() {
92
+ return this._request.method;
93
+ }
94
+ async arrayBuffer() {
95
+ return this._request.arrayBuffer();
96
+ }
97
+ async blob() {
98
+ return this._request.blob();
99
+ }
100
+ async formData() {
101
+ return this._request.formData();
102
+ }
103
+ async bytes() {
104
+ const buffer = await this.arrayBuffer();
105
+ return new Uint8Array(buffer);
106
+ }
107
+ async json() {
108
+ return this._request.json();
109
+ }
110
+ async text() {
111
+ return this._request.text();
112
+ }
113
+ get request() {
114
+ return this._request;
115
+ }
116
+ };
117
+
118
+ // src/head.ts
119
+ var KaitoHead = class {
120
+ _headers;
121
+ _status;
122
+ constructor() {
123
+ this._headers = null;
124
+ this._status = 200;
125
+ }
126
+ get headers() {
127
+ if (this._headers === null) {
128
+ this._headers = new Headers();
129
+ }
130
+ return this._headers;
131
+ }
132
+ status(status) {
133
+ if (status === void 0) {
134
+ return this._status;
135
+ }
136
+ this._status = status;
137
+ return this;
138
+ }
139
+ /**
140
+ * Turn this KaitoHead instance into a Response instance
141
+ * @param body The Kaito JSON format to be sent as the response body
142
+ * @returns A Response instance, ready to be sent
143
+ */
144
+ toResponse(body) {
145
+ const init = {
146
+ status: this._status
147
+ };
148
+ if (this._headers) {
149
+ init.headers = this._headers;
150
+ }
151
+ return Response.json(body, init);
152
+ }
153
+ /**
154
+ * Whether this KaitoHead instance has been touched/modified
155
+ */
156
+ get touched() {
157
+ return this._status !== 200 || this._headers !== null;
158
+ }
159
+ };
160
+
161
+ // src/util.ts
162
+ var isNodeLikeDev = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.NODE_ENV === "development";
163
+ function createUtilities(getContext) {
164
+ return {
165
+ getContext,
166
+ router: () => Router.create()
167
+ };
168
+ }
169
+ function parsable(parse) {
170
+ return {
171
+ parse
172
+ };
173
+ }
174
+
175
+ // src/router/router.ts
176
+ var Router = class _Router {
177
+ state;
178
+ static create = () => new _Router({
179
+ through: async (context) => context,
180
+ routes: /* @__PURE__ */ new Set()
181
+ });
182
+ static parseQuery(schema, url) {
183
+ if (!schema) {
184
+ return {};
185
+ }
186
+ const result = {};
187
+ for (const key in schema) {
188
+ if (!schema.hasOwnProperty(key)) continue;
189
+ const value = url.searchParams.get(key);
190
+ result[key] = schema[key].parse(value);
191
+ }
192
+ return result;
193
+ }
194
+ constructor(options) {
195
+ this.state = options;
196
+ }
197
+ get routes() {
198
+ return this.state.routes;
199
+ }
200
+ add = (method, path, route) => {
201
+ const merged = {
202
+ // TODO: Ideally fix the typing here, but this will be replaced in Kaito v4 where all routes must return a Response (which we can type)
203
+ ...typeof route === "object" ? route : { run: route },
204
+ method,
205
+ path,
206
+ through: this.state.through
207
+ };
208
+ return new _Router({
209
+ ...this.state,
210
+ routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
211
+ });
212
+ };
213
+ merge = (pathPrefix, other) => {
214
+ const newRoutes = [...other.state.routes].map((route) => ({
215
+ ...route,
216
+ path: `${pathPrefix}${route.path}`
217
+ }));
218
+ return new _Router({
219
+ ...this.state,
220
+ routes: /* @__PURE__ */ new Set([...this.state.routes, ...newRoutes])
221
+ });
222
+ };
223
+ freeze = (server) => {
224
+ const routes = /* @__PURE__ */ new Map();
225
+ for (const route of this.state.routes) {
226
+ if (!routes.has(route.path)) {
227
+ routes.set(route.path, /* @__PURE__ */ new Map());
228
+ }
229
+ routes.get(route.path).set(route.method, route);
230
+ }
231
+ const findRoute = (method, path) => {
232
+ const params = {};
233
+ const pathParts = path.split("/").filter(Boolean);
234
+ for (const [routePath, methodHandlers] of routes) {
235
+ const routeParts = routePath.split("/").filter(Boolean);
236
+ if (routeParts.length !== pathParts.length) continue;
237
+ let matches = true;
238
+ for (let i = 0; i < routeParts.length; i++) {
239
+ const routePart = routeParts[i];
240
+ const pathPart = pathParts[i];
241
+ if (routePart && pathPart && routePart.startsWith(":")) {
242
+ params[routePart.slice(1)] = pathPart;
243
+ } else if (routePart !== pathPart) {
244
+ matches = false;
245
+ break;
246
+ }
247
+ }
248
+ if (matches) {
249
+ const route = methodHandlers.get(method);
250
+ if (route) return { route, params };
251
+ }
252
+ }
253
+ return { params };
254
+ };
255
+ return async (req) => {
256
+ const url = new URL(req.url);
257
+ const method = req.method;
258
+ const { route, params } = findRoute(method, url.pathname);
259
+ if (!route) {
260
+ const body = {
261
+ success: false,
262
+ data: null,
263
+ message: `Cannot ${method} ${url.pathname}`
264
+ };
265
+ return Response.json(body, { status: 404 });
266
+ }
267
+ const request = new KaitoRequest(url, req);
268
+ const head = new KaitoHead();
269
+ try {
270
+ const body = route.body ? await route.body.parse(await req.json()) : void 0;
271
+ const query = _Router.parseQuery(route.query, url);
272
+ const rootCtx = await server.getContext(request, head);
273
+ const ctx = await route.through(rootCtx);
274
+ const result = await route.run({
275
+ ctx,
276
+ body,
277
+ query,
278
+ params
279
+ });
280
+ if (result instanceof Response) {
281
+ if (isNodeLikeDev) {
282
+ if (head.touched) {
283
+ const msg = [
284
+ "Kaito detected that you used the KaitoHead object to modify the headers or status, but then returned a Response in the route",
285
+ "This is usually a mistake, as your Response object will override any changes you made to the headers or status code.",
286
+ "",
287
+ "This warning was shown because `process.env.NODE_ENV=development`"
288
+ ].join("\n");
289
+ console.warn(msg);
290
+ }
291
+ }
292
+ return result;
293
+ }
294
+ return head.toResponse({
295
+ success: true,
296
+ data: result,
297
+ message: "OK"
298
+ });
299
+ } catch (e) {
300
+ const error = WrappedError.maybe(e);
301
+ if (error instanceof KaitoError) {
302
+ return head.status(error.status).toResponse({
303
+ success: false,
304
+ data: null,
305
+ message: error.message
306
+ });
307
+ }
308
+ const { status, message } = await server.onError({ error, req: request }).catch(() => ({ status: 500, message: "Internal Server Error" }));
309
+ return head.status(status).toResponse({
310
+ success: false,
311
+ data: null,
312
+ message
313
+ });
314
+ }
315
+ };
316
+ };
317
+ method = (method) => (path, route) => {
318
+ return this.add(method, path, route);
319
+ };
320
+ get = this.method("GET");
321
+ post = this.method("POST");
322
+ put = this.method("PUT");
323
+ patch = this.method("PATCH");
324
+ delete = this.method("DELETE");
325
+ head = this.method("HEAD");
326
+ options = this.method("OPTIONS");
327
+ through = (through) => {
328
+ return new _Router({
329
+ ...this.state,
330
+ through: async (context) => through(await this.state.through(context))
331
+ });
332
+ };
333
+ };
334
+ // Annotate the CommonJS export names for ESM import in node:
335
+ 0 && (module.exports = {
336
+ KaitoError,
337
+ KaitoRequest,
338
+ Router,
339
+ WrappedError,
340
+ createKaitoHandler,
341
+ createUtilities,
342
+ isNodeLikeDev,
343
+ parsable
344
+ });
@@ -0,0 +1,255 @@
1
+ declare class WrappedError<T> extends Error {
2
+ readonly data: T;
3
+ static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
+ static from<T>(data: T): WrappedError<T>;
5
+ private constructor();
6
+ }
7
+ declare class KaitoError extends Error {
8
+ readonly status: number;
9
+ constructor(status: number, message: string);
10
+ }
11
+
12
+ declare class KaitoRequest {
13
+ readonly url: URL;
14
+ private readonly _request;
15
+ constructor(url: URL, request: Request);
16
+ get headers(): Headers;
17
+ get method(): string;
18
+ arrayBuffer(): Promise<ArrayBuffer>;
19
+ blob(): Promise<Blob>;
20
+ formData(): Promise<FormData>;
21
+ bytes(): Promise<Uint8Array>;
22
+ json(): Promise<unknown>;
23
+ text(): Promise<string>;
24
+ get request(): Request;
25
+ }
26
+
27
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
28
+
29
+ /**
30
+ * This class is merely a wrapper around a `Headers` object and a status code.
31
+ * It's used while the router is executing a route to store any mutations to the status
32
+ * code or headers that the developer may want to make.
33
+ *
34
+ * This exists because there's otherwise no way to indicate back to Kaito that
35
+ * the developer wants to change the status code or headers.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const response = new KaitoHead();
40
+ *
41
+ * response.status(200);
42
+ * response.headers.set('Content-Type', 'application/json');
43
+ *
44
+ * console.log(response.headers); // Headers {'content-type': 'application/json'}
45
+ * ```
46
+ */
47
+ declare class KaitoHead {
48
+ private _headers;
49
+ private _status;
50
+ constructor();
51
+ get headers(): Headers;
52
+ /**
53
+ * Gets the status code of this KaitoHead instance
54
+ * @returns The status code
55
+ */
56
+ status(): number;
57
+ /**
58
+ * Sets the status code of this KaitoHead instance
59
+ * @param status The status code to set
60
+ * @returns This KaitoHead instance
61
+ */
62
+ status(status: number): this;
63
+ /**
64
+ * Turn this KaitoHead instance into a Response instance
65
+ * @param body The Kaito JSON format to be sent as the response body
66
+ * @returns A Response instance, ready to be sent
67
+ */
68
+ toResponse<T>(body: APIResponse<T>): Response;
69
+ /**
70
+ * Whether this KaitoHead instance has been touched/modified
71
+ */
72
+ get touched(): boolean;
73
+ }
74
+
75
+ /**
76
+ * A helper to check if the environment is Node.js-like and the NODE_ENV is development
77
+ */
78
+ declare const isNodeLikeDev: boolean;
79
+ type ErroredAPIResponse = {
80
+ success: false;
81
+ data: null;
82
+ message: string;
83
+ };
84
+ type SuccessfulAPIResponse<T> = {
85
+ success: true;
86
+ data: T;
87
+ message: 'OK';
88
+ };
89
+ type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
90
+ type AnyResponse = APIResponse<unknown>;
91
+ type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
92
+ type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
93
+ [k in Param | keyof ExtractRouteParams<Rest>]: string;
94
+ } : T extends `${string}:${infer Param}` ? {
95
+ [k in Param]: string;
96
+ } : {};
97
+ /**
98
+ * A function that is called to get the context for a request.
99
+ *
100
+ * This is useful for things like authentication, to pass in a database connection, etc.
101
+ *
102
+ * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
103
+ *
104
+ * @param req - The kaito request object, which contains the request method, url, headers, etc
105
+ * @param head - The kaito head object, which contains getters and setters for headers and status
106
+ * @returns The context for your routes
107
+ */
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>;
140
+
141
+ type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
142
+ ctx: Context;
143
+ body: BodyOutput;
144
+ query: QueryOutput;
145
+ params: ExtractRouteParams<Path>;
146
+ };
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;
153
+ path: Path;
154
+ method: Method;
155
+ run(arg: RouteArgument<Path, ContextTo, {
156
+ [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
+ }, InferParsable<Body>['output']>): Promise<Result>;
158
+ };
159
+ type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
160
+
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;
162
+ type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
163
+ type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
164
+ routes: Set<Routes>;
165
+ through: (context: ContextFrom) => Promise<ContextTo>;
166
+ };
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> {
169
+ private readonly state;
170
+ static create: <Context>() => Router<Context, Context, never>;
171
+ private static parseQuery;
172
+ constructor(options: RouterState<R, ContextFrom, ContextTo>);
173
+ 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>;
177
+ 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"]>) => 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"]>) => 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"]>) => 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"]>) => 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"]>) => 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"]>) => 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"]>) => 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>;
186
+ }
187
+
188
+ type HandlerConfig<ContextFrom> = {
189
+ /**
190
+ * The root router to mount on this handler.
191
+ */
192
+ router: Router<ContextFrom, unknown, any>;
193
+ /**
194
+ * A function that is called to get the context for a request.
195
+ *
196
+ * This is useful for things like authentication, to pass in a database connection, etc.
197
+ *
198
+ * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
199
+ */
200
+ getContext: GetContext<ContextFrom>;
201
+ /**
202
+ * A function that is called when an error occurs inside a route handler.
203
+ *
204
+ * The result of this function is used to determine the response status and message, and is
205
+ * always sent to the client. You could include logic to check for production vs development
206
+ * environments here, and this would also be a good place to include error tracking
207
+ * like Sentry or Rollbar.
208
+ *
209
+ * @param arg - The error thrown, and the KaitoRequest instance
210
+ * @returns A KaitoError or an object with a status and message
211
+ */
212
+ onError: (arg: {
213
+ error: Error;
214
+ req: KaitoRequest;
215
+ }) => Promise<KaitoError | {
216
+ status: number;
217
+ message: string;
218
+ }>;
219
+ /**
220
+ * A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * before: async req => {
225
+ * if (req.method === 'OPTIONS') {
226
+ * return new Response(null, {status: 204});
227
+ * }
228
+ * }
229
+ * ```
230
+ */
231
+ before?: (req: Request) => Promise<Response | void | undefined>;
232
+ /**
233
+ * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
+ *
235
+ * You can also return a new response in this function, or just mutate the current one.
236
+ *
237
+ * This function WILL receive the result of `.before()` if you return a response from it. This means
238
+ * you only need to define headers in a single place.
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * transform: async (req, res) => {
243
+ * res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
244
+ * res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
245
+ * res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
246
+ * res.headers.set('Access-Control-Max-Age', '86400');
247
+ * res.headers.set('Access-Control-Allow-Credentials', 'true');
248
+ * }
249
+ * ```
250
+ */
251
+ transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
252
+ };
253
+ declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
254
+
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 };