@kaito-http/core 3.0.0-beta.16 → 3.0.0-beta.21

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 CHANGED
@@ -106,6 +106,9 @@ var KaitoResponse = class {
106
106
  return this._headers;
107
107
  }
108
108
  status(status) {
109
+ if (status === void 0) {
110
+ return this._status;
111
+ }
109
112
  this._status = status;
110
113
  return this;
111
114
  }
@@ -125,19 +128,6 @@ var KaitoResponse = class {
125
128
  }
126
129
  };
127
130
 
128
- // src/util.ts
129
- function createUtilities(getContext) {
130
- return {
131
- getContext,
132
- router: () => Router.create()
133
- };
134
- }
135
- function parsable(parse) {
136
- return {
137
- parse
138
- };
139
- }
140
-
141
131
  // src/router/router.ts
142
132
  var Router = class _Router {
143
133
  state;
@@ -150,9 +140,10 @@ var Router = class _Router {
150
140
  return {};
151
141
  }
152
142
  const result = {};
153
- for (const [key, parsable2] of Object.entries(schema)) {
143
+ for (const key in schema) {
144
+ if (!schema.hasOwnProperty(key)) continue;
154
145
  const value = url.searchParams.get(key);
155
- result[key] = parsable2.parse(value);
146
+ result[key] = schema[key].parse(value);
156
147
  }
157
148
  return result;
158
149
  }
@@ -232,10 +223,10 @@ var Router = class _Router {
232
223
  const request = new KaitoRequest(url, req);
233
224
  const response = new KaitoResponse();
234
225
  try {
235
- const rootCtx = await server.getContext(request, response);
236
- const ctx = await route.through(rootCtx);
237
226
  const body = route.body ? await route.body.parse(await req.json()) : void 0;
238
227
  const query = _Router.parseQuery(route.query, url);
228
+ const rootCtx = await server.getContext(request, response);
229
+ const ctx = await route.through(rootCtx);
239
230
  const result = await route.run({
240
231
  ctx,
241
232
  body,
@@ -289,23 +280,33 @@ var Router = class _Router {
289
280
 
290
281
  // src/server.ts
291
282
  function createKaitoHandler(config) {
292
- const match = config.router.freeze(config);
283
+ const handle = config.router.freeze(config);
293
284
  return async (request) => {
294
- let before = void 0;
295
285
  if (config.before) {
296
286
  const result = await config.before(request);
297
- if (result instanceof Response) {
298
- return result;
299
- }
300
- before = result;
287
+ if (result instanceof Response) return result;
301
288
  }
302
- const response = await match(request);
303
- if ("after" in config && config.after) {
304
- await config.after(before, response);
289
+ const response = await handle(request);
290
+ if (config.transform) {
291
+ const result = await config.transform(request, response);
292
+ if (result instanceof Response) return result;
305
293
  }
306
294
  return response;
307
295
  };
308
296
  }
297
+
298
+ // src/util.ts
299
+ function createUtilities(getContext) {
300
+ return {
301
+ getContext,
302
+ router: () => Router.create()
303
+ };
304
+ }
305
+ function parsable(parse) {
306
+ return {
307
+ parse
308
+ };
309
+ }
309
310
  // Annotate the CommonJS export names for ESM import in node:
310
311
  0 && (module.exports = {
311
312
  KaitoError,
package/dist/index.d.cts CHANGED
@@ -34,10 +34,10 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
34
34
  * ```ts
35
35
  * const response = new KaitoResponse();
36
36
  *
37
- * response.status = 200;
38
- * response.header('Content-Type', 'application/json');
37
+ * response.status(200);
38
+ * response.headers.set('Content-Type', 'application/json');
39
39
  *
40
- * console.log(response.headers); // Headers { 'content-type': 'application/json' }
40
+ * console.log(response.headers); // Headers {'content-type': 'application/json'}
41
41
  * ```
42
42
  */
43
43
  declare class KaitoResponse {
@@ -45,6 +45,16 @@ declare class KaitoResponse {
45
45
  private _status;
46
46
  constructor();
47
47
  get headers(): Headers;
48
+ /**
49
+ * Gets the status code of this KaitoResponse instance
50
+ * @returns The status code
51
+ */
52
+ status(): number;
53
+ /**
54
+ * Sets the status code of this KaitoResponse instance
55
+ * @param status The status code to set
56
+ * @returns This KaitoResponse instance
57
+ */
48
58
  status(status: number): this;
49
59
  /**
50
60
  * Turn this KaitoResponse instance into a Response instance
@@ -54,26 +64,70 @@ declare class KaitoResponse {
54
64
  toResponse<T>(body: APIResponse<T>): Response;
55
65
  }
56
66
 
57
- type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
58
- type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
59
- type ServerConfigWithBefore<BeforeAfterContext> = {
60
- before: Before<BeforeAfterContext>;
61
- after?: After<BeforeAfterContext>;
62
- } | {
63
- before?: undefined;
64
- };
65
- type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
67
+ type Before = (req: Request) => Promise<Response | void | undefined>;
68
+ type ServerConfig<ContextFrom> = {
69
+ /**
70
+ * The root router to mount on this server.
71
+ */
66
72
  router: Router<ContextFrom, unknown, any>;
73
+ /**
74
+ * A function that is called to get the context for a request.
75
+ *
76
+ * This is useful for things like authentication, to pass in a database connection, etc.
77
+ *
78
+ * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
79
+ */
67
80
  getContext: GetContext<ContextFrom>;
68
- onError(arg: {
81
+ /**
82
+ * A function that is called when an error occurs inside a route handler.
83
+ *
84
+ * The result of this function is used to determine the response status and message, and is
85
+ * always sent to the client. You could include logic to check for production vs development
86
+ * environments here, and this would also be a good place to include error tracking
87
+ * like Sentry or Rollbar.
88
+ *
89
+ * @param arg - The error and the request
90
+ * @returns A KaitoError or an object with a status and message
91
+ */
92
+ onError: (arg: {
69
93
  error: Error;
70
94
  req: KaitoRequest;
71
- }): Promise<KaitoError | {
95
+ }) => Promise<KaitoError | {
72
96
  status: number;
73
97
  message: string;
74
98
  }>;
99
+ /**
100
+ * A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * before: async req => {
105
+ * if (req.method === 'OPTIONS') {
106
+ * return new Response(null, {status: 204});
107
+ * }
108
+ * }
109
+ * ```
110
+ */
111
+ before?: Before;
112
+ /**
113
+ * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
114
+ *
115
+ * You can also return a new response in this function, or just mutate the current one.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * transform: async (req, res) => {
120
+ * res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
121
+ * res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
122
+ * res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
123
+ * res.headers.set('Access-Control-Max-Age', '86400');
124
+ * res.headers.set('Access-Control-Allow-Credentials', 'true');
125
+ * }
126
+ * ```
127
+ */
128
+ transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
75
129
  };
76
- declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
130
+ declare function createKaitoHandler<Context>(config: ServerConfig<Context>): (request: Request) => Promise<Response>;
77
131
 
78
132
  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;
79
133
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
@@ -88,17 +142,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
88
142
  private static parseQuery;
89
143
  constructor(options: RouterState<R, ContextFrom, ContextTo>);
90
144
  get routes(): Set<R>;
91
- 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 extends Response ? unknown : Result, Path, Method, Query, Body>>;
145
+ 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>>;
92
146
  readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
93
- freeze: (server: Omit<ServerConfig<ContextFrom, any>, "router">) => (req: Request) => Promise<Response>;
147
+ freeze: (server: Omit<ServerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
94
148
  private readonly method;
95
- 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 extends Response ? unknown : Result, Path, "GET", Query, Body>>;
96
- 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 extends Response ? unknown : Result, Path, "POST", Query, Body>>;
97
- 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 extends Response ? unknown : Result, Path, "PUT", Query, Body>>;
98
- 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 extends Response ? unknown : Result, Path, "PATCH", Query, Body>>;
99
- 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 extends Response ? unknown : Result, Path, "DELETE", Query, Body>>;
100
- 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 extends Response ? unknown : Result, Path, "HEAD", Query, Body>>;
101
- 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 extends Response ? unknown : Result, Path, "OPTIONS", Query, Body>>;
149
+ 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>>;
150
+ 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>>;
151
+ 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>>;
152
+ 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>>;
153
+ 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>>;
154
+ 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>>;
155
+ 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>>;
102
156
  through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
103
157
  }
104
158
 
@@ -114,6 +168,7 @@ type SuccessfulAPIResponse<T> = {
114
168
  };
115
169
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
116
170
  type AnyResponse = APIResponse<unknown>;
171
+ type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
117
172
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
118
173
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
119
174
  } : T extends `${string}:${infer Param}` ? {
@@ -172,4 +227,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
172
227
  };
173
228
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
174
229
 
175
- export { type APIResponse, type After, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type ServerConfigWithBefore, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
230
+ export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
package/dist/index.d.ts CHANGED
@@ -34,10 +34,10 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
34
34
  * ```ts
35
35
  * const response = new KaitoResponse();
36
36
  *
37
- * response.status = 200;
38
- * response.header('Content-Type', 'application/json');
37
+ * response.status(200);
38
+ * response.headers.set('Content-Type', 'application/json');
39
39
  *
40
- * console.log(response.headers); // Headers { 'content-type': 'application/json' }
40
+ * console.log(response.headers); // Headers {'content-type': 'application/json'}
41
41
  * ```
42
42
  */
43
43
  declare class KaitoResponse {
@@ -45,6 +45,16 @@ declare class KaitoResponse {
45
45
  private _status;
46
46
  constructor();
47
47
  get headers(): Headers;
48
+ /**
49
+ * Gets the status code of this KaitoResponse instance
50
+ * @returns The status code
51
+ */
52
+ status(): number;
53
+ /**
54
+ * Sets the status code of this KaitoResponse instance
55
+ * @param status The status code to set
56
+ * @returns This KaitoResponse instance
57
+ */
48
58
  status(status: number): this;
49
59
  /**
50
60
  * Turn this KaitoResponse instance into a Response instance
@@ -54,26 +64,70 @@ declare class KaitoResponse {
54
64
  toResponse<T>(body: APIResponse<T>): Response;
55
65
  }
56
66
 
57
- type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
58
- type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
59
- type ServerConfigWithBefore<BeforeAfterContext> = {
60
- before: Before<BeforeAfterContext>;
61
- after?: After<BeforeAfterContext>;
62
- } | {
63
- before?: undefined;
64
- };
65
- type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
67
+ type Before = (req: Request) => Promise<Response | void | undefined>;
68
+ type ServerConfig<ContextFrom> = {
69
+ /**
70
+ * The root router to mount on this server.
71
+ */
66
72
  router: Router<ContextFrom, unknown, any>;
73
+ /**
74
+ * A function that is called to get the context for a request.
75
+ *
76
+ * This is useful for things like authentication, to pass in a database connection, etc.
77
+ *
78
+ * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
79
+ */
67
80
  getContext: GetContext<ContextFrom>;
68
- onError(arg: {
81
+ /**
82
+ * A function that is called when an error occurs inside a route handler.
83
+ *
84
+ * The result of this function is used to determine the response status and message, and is
85
+ * always sent to the client. You could include logic to check for production vs development
86
+ * environments here, and this would also be a good place to include error tracking
87
+ * like Sentry or Rollbar.
88
+ *
89
+ * @param arg - The error and the request
90
+ * @returns A KaitoError or an object with a status and message
91
+ */
92
+ onError: (arg: {
69
93
  error: Error;
70
94
  req: KaitoRequest;
71
- }): Promise<KaitoError | {
95
+ }) => Promise<KaitoError | {
72
96
  status: number;
73
97
  message: string;
74
98
  }>;
99
+ /**
100
+ * A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * before: async req => {
105
+ * if (req.method === 'OPTIONS') {
106
+ * return new Response(null, {status: 204});
107
+ * }
108
+ * }
109
+ * ```
110
+ */
111
+ before?: Before;
112
+ /**
113
+ * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
114
+ *
115
+ * You can also return a new response in this function, or just mutate the current one.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * transform: async (req, res) => {
120
+ * res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
121
+ * res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
122
+ * res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
123
+ * res.headers.set('Access-Control-Max-Age', '86400');
124
+ * res.headers.set('Access-Control-Allow-Credentials', 'true');
125
+ * }
126
+ * ```
127
+ */
128
+ transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
75
129
  };
76
- declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
130
+ declare function createKaitoHandler<Context>(config: ServerConfig<Context>): (request: Request) => Promise<Response>;
77
131
 
78
132
  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;
79
133
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
@@ -88,17 +142,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
88
142
  private static parseQuery;
89
143
  constructor(options: RouterState<R, ContextFrom, ContextTo>);
90
144
  get routes(): Set<R>;
91
- 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 extends Response ? unknown : Result, Path, Method, Query, Body>>;
145
+ 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>>;
92
146
  readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
93
- freeze: (server: Omit<ServerConfig<ContextFrom, any>, "router">) => (req: Request) => Promise<Response>;
147
+ freeze: (server: Omit<ServerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
94
148
  private readonly method;
95
- 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 extends Response ? unknown : Result, Path, "GET", Query, Body>>;
96
- 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 extends Response ? unknown : Result, Path, "POST", Query, Body>>;
97
- 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 extends Response ? unknown : Result, Path, "PUT", Query, Body>>;
98
- 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 extends Response ? unknown : Result, Path, "PATCH", Query, Body>>;
99
- 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 extends Response ? unknown : Result, Path, "DELETE", Query, Body>>;
100
- 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 extends Response ? unknown : Result, Path, "HEAD", Query, Body>>;
101
- 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 extends Response ? unknown : Result, Path, "OPTIONS", Query, Body>>;
149
+ 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>>;
150
+ 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>>;
151
+ 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>>;
152
+ 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>>;
153
+ 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>>;
154
+ 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>>;
155
+ 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>>;
102
156
  through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
103
157
  }
104
158
 
@@ -114,6 +168,7 @@ type SuccessfulAPIResponse<T> = {
114
168
  };
115
169
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
116
170
  type AnyResponse = APIResponse<unknown>;
171
+ type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
117
172
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
118
173
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
119
174
  } : T extends `${string}:${infer Param}` ? {
@@ -172,4 +227,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
172
227
  };
173
228
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
174
229
 
175
- export { type APIResponse, type After, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type ServerConfigWithBefore, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
230
+ export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
package/dist/index.js CHANGED
@@ -74,6 +74,9 @@ var KaitoResponse = class {
74
74
  return this._headers;
75
75
  }
76
76
  status(status) {
77
+ if (status === void 0) {
78
+ return this._status;
79
+ }
77
80
  this._status = status;
78
81
  return this;
79
82
  }
@@ -93,19 +96,6 @@ var KaitoResponse = class {
93
96
  }
94
97
  };
95
98
 
96
- // src/util.ts
97
- function createUtilities(getContext) {
98
- return {
99
- getContext,
100
- router: () => Router.create()
101
- };
102
- }
103
- function parsable(parse) {
104
- return {
105
- parse
106
- };
107
- }
108
-
109
99
  // src/router/router.ts
110
100
  var Router = class _Router {
111
101
  state;
@@ -118,9 +108,10 @@ var Router = class _Router {
118
108
  return {};
119
109
  }
120
110
  const result = {};
121
- for (const [key, parsable2] of Object.entries(schema)) {
111
+ for (const key in schema) {
112
+ if (!schema.hasOwnProperty(key)) continue;
122
113
  const value = url.searchParams.get(key);
123
- result[key] = parsable2.parse(value);
114
+ result[key] = schema[key].parse(value);
124
115
  }
125
116
  return result;
126
117
  }
@@ -200,10 +191,10 @@ var Router = class _Router {
200
191
  const request = new KaitoRequest(url, req);
201
192
  const response = new KaitoResponse();
202
193
  try {
203
- const rootCtx = await server.getContext(request, response);
204
- const ctx = await route.through(rootCtx);
205
194
  const body = route.body ? await route.body.parse(await req.json()) : void 0;
206
195
  const query = _Router.parseQuery(route.query, url);
196
+ const rootCtx = await server.getContext(request, response);
197
+ const ctx = await route.through(rootCtx);
207
198
  const result = await route.run({
208
199
  ctx,
209
200
  body,
@@ -257,23 +248,33 @@ var Router = class _Router {
257
248
 
258
249
  // src/server.ts
259
250
  function createKaitoHandler(config) {
260
- const match = config.router.freeze(config);
251
+ const handle = config.router.freeze(config);
261
252
  return async (request) => {
262
- let before = void 0;
263
253
  if (config.before) {
264
254
  const result = await config.before(request);
265
- if (result instanceof Response) {
266
- return result;
267
- }
268
- before = result;
255
+ if (result instanceof Response) return result;
269
256
  }
270
- const response = await match(request);
271
- if ("after" in config && config.after) {
272
- await config.after(before, response);
257
+ const response = await handle(request);
258
+ if (config.transform) {
259
+ const result = await config.transform(request, response);
260
+ if (result instanceof Response) return result;
273
261
  }
274
262
  return response;
275
263
  };
276
264
  }
265
+
266
+ // src/util.ts
267
+ function createUtilities(getContext) {
268
+ return {
269
+ getContext,
270
+ router: () => Router.create()
271
+ };
272
+ }
273
+ function parsable(parse) {
274
+ return {
275
+ parse
276
+ };
277
+ }
277
278
  export {
278
279
  KaitoError,
279
280
  KaitoRequest,