@kaito-http/core 4.0.0-beta.1 → 4.0.0-beta.11

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
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  KaitoError: () => KaitoError,
24
+ KaitoHead: () => KaitoHead,
24
25
  KaitoRequest: () => KaitoRequest,
25
26
  Router: () => Router,
26
27
  WrappedError: () => WrappedError,
@@ -32,6 +33,7 @@ module.exports = __toCommonJS(index_exports);
32
33
  // src/router/router.ts
33
34
  var import_zod = require("zod");
34
35
  var import_zod_openapi = require("zod-openapi");
36
+ var import_extend = require("zod-openapi/extend");
35
37
 
36
38
  // src/error.ts
37
39
  var WrappedError = class _WrappedError extends Error {
@@ -145,11 +147,14 @@ var isNodeLikeDev = typeof process !== "undefined" && typeof process.env !== "un
145
147
  // src/router/router.ts
146
148
  var Router = class _Router {
147
149
  state;
148
- static create = (config) => new _Router({
149
- through: async (context) => context,
150
- routes: /* @__PURE__ */ new Set(),
151
- config
152
- });
150
+ static create = (config) => {
151
+ return new _Router({
152
+ through: async (context) => context,
153
+ routes: /* @__PURE__ */ new Set(),
154
+ config,
155
+ paramsSchema: null
156
+ });
157
+ };
153
158
  constructor(state) {
154
159
  this.state = state;
155
160
  }
@@ -161,17 +166,24 @@ var Router = class _Router {
161
166
  ...typeof route === "object" ? route : { run: route },
162
167
  method,
163
168
  path,
164
- through: this.state.through
169
+ router: this
165
170
  };
166
171
  return new _Router({
167
172
  ...this.state,
168
173
  routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
169
174
  });
170
175
  };
176
+ params = (spec) => new _Router({
177
+ ...this.state,
178
+ paramsSchema: import_zod.z.object(spec)
179
+ });
171
180
  merge = (pathPrefix, other) => {
172
181
  const newRoutes = [...other.state.routes].map((route) => ({
173
182
  ...route,
174
- path: `${pathPrefix}${route.path}`
183
+ // handle pathPrefix = / & route.path = / case causing //
184
+ // we intentionally are replacing on the joining path and not the pathPrefix, in case of
185
+ // /named -> merged to -> / causing /named/ not /named
186
+ path: `${pathPrefix}${route.path === "/" ? "" : route.path}`
175
187
  }));
176
188
  return new _Router({
177
189
  ...this.state,
@@ -225,10 +237,10 @@ var Router = class _Router {
225
237
  });
226
238
  }
227
239
  const findRoute = _Router.getFindRoute(methodToRoutesMap);
228
- const handle = async (req) => {
240
+ const handle = async (req, ...args) => {
229
241
  const url = new URL(req.url);
230
242
  const method = req.method;
231
- const { route, params } = findRoute(method, url.pathname);
243
+ const { route, params: rawParams } = findRoute(method, url.pathname);
232
244
  if (!route) {
233
245
  const body = {
234
246
  success: false,
@@ -242,7 +254,11 @@ var Router = class _Router {
242
254
  try {
243
255
  const body = route.body ? await route.body.parseAsync(await req.json()) : void 0;
244
256
  const query = route.fastQuerySchema ? await route.fastQuerySchema.parseAsync(url.searchParams) : {};
245
- const ctx = await route.through(await this.state.config.getContext?.(request, head) ?? null);
257
+ const params = route.router.state.paramsSchema ? route.router.state.paramsSchema.parse(rawParams) : rawParams;
258
+ const ctx = await route.router.state.through(
259
+ await this.state.config.getContext?.(request, head, ...args) ?? null,
260
+ params
261
+ );
246
262
  const result = await route.run({
247
263
  ctx,
248
264
  body,
@@ -301,7 +317,7 @@ var Router = class _Router {
301
317
  }
302
318
  }
303
319
  };
304
- return async (request) => {
320
+ return async (request, ...args) => {
305
321
  if (this.state.config.before) {
306
322
  const result = await this.state.config.before(request);
307
323
  if (result instanceof Response) {
@@ -314,7 +330,7 @@ var Router = class _Router {
314
330
  return result;
315
331
  }
316
332
  }
317
- const response = await handle(request);
333
+ const response = await handle(request, ...args);
318
334
  if (this.state.config.transform) {
319
335
  const transformed = await this.state.config.transform(request, response);
320
336
  if (transformed instanceof Response) {
@@ -329,23 +345,35 @@ var Router = class _Router {
329
345
  const paths = {};
330
346
  for (const route of this.state.routes) {
331
347
  const path = route.path;
348
+ if (!route.openapi) {
349
+ continue;
350
+ }
332
351
  const pathWithColonParamsReplaceWithCurlyBraces = path.replace(/:(\w+)/g, "{$1}");
333
352
  if (!paths[pathWithColonParamsReplaceWithCurlyBraces]) {
334
353
  paths[pathWithColonParamsReplaceWithCurlyBraces] = {};
335
354
  }
355
+ const content = route.openapi.body.type === "json" ? {
356
+ "application/json": {
357
+ schema: import_zod.z.object({
358
+ success: import_zod.z.literal(true).openapi({
359
+ type: "boolean",
360
+ enum: [true]
361
+ // Need this as zod-openapi doesn't properly work with literals
362
+ }),
363
+ data: route.openapi.body.schema
364
+ })
365
+ }
366
+ } : {
367
+ "text/event-stream": {
368
+ schema: route.openapi.body.schema
369
+ }
370
+ };
336
371
  const item = {
337
372
  description: route.openapi?.description ?? "Successful response",
338
373
  responses: {
339
374
  200: {
340
375
  description: route.openapi?.description ?? "Successful response",
341
- ...route.openapi ? {
342
- content: {
343
- [{
344
- json: "application/json",
345
- sse: "text/event-stream"
346
- }[route.openapi.body.type]]: { schema: route.openapi?.body.schema }
347
- }
348
- } : {}
376
+ content
349
377
  }
350
378
  }
351
379
  };
@@ -383,10 +411,10 @@ var Router = class _Router {
383
411
  };
384
412
  })
385
413
  });
386
- return this.add("GET", "/openapi.json", async () => Response.json(doc));
414
+ return this.get("/openapi.json", () => Response.json(doc));
387
415
  };
388
- method = (method) => (path, route) => {
389
- return this.add(method, path, route);
416
+ method = (method) => {
417
+ return (path, route) => this.add(method, path, route);
390
418
  };
391
419
  get = this.method("GET");
392
420
  post = this.method("POST");
@@ -398,18 +426,24 @@ var Router = class _Router {
398
426
  through = (through) => {
399
427
  return new _Router({
400
428
  ...this.state,
401
- through: async (context) => await through(await this.state.through(context))
429
+ through: async (context, params) => await through(await this.state.through(context, params), params)
402
430
  });
403
431
  };
404
432
  };
405
433
 
406
434
  // src/create.ts
407
435
  function create(config = {}) {
408
- return () => Router.create(config);
436
+ return Router.create(config);
409
437
  }
438
+ create.withInput = () => {
439
+ return {
440
+ create: (config = {}) => Router.create(config)
441
+ };
442
+ };
410
443
  // Annotate the CommonJS export names for ESM import in node:
411
444
  0 && (module.exports = {
412
445
  KaitoError,
446
+ KaitoHead,
413
447
  KaitoRequest,
414
448
  Router,
415
449
  WrappedError,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z, ZodTypeDef } from 'zod';
2
2
  import { KaitoSSEResponse } from './stream/stream.cjs';
3
3
 
4
4
  declare class WrappedError<T> extends Error {
@@ -27,8 +27,6 @@ declare class KaitoRequest {
27
27
  get request(): Request;
28
28
  }
29
29
 
30
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
31
-
32
30
  /**
33
31
  * This class is merely a wrapper around a `Headers` object and a status code.
34
32
  * It's used while the router is executing a route to store any mutations to the status
@@ -91,6 +89,10 @@ type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
91
89
  type AnyResponse = APIResponse<unknown>;
92
90
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
93
91
  type MaybePromise<T> = T | Promise<T>;
92
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
93
+ type NotReadonly<T> = {
94
+ -readonly [K in keyof T]: T[K];
95
+ };
94
96
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
95
97
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
96
98
  } : T extends `${string}:${infer Param}` ? {
@@ -107,18 +109,18 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
107
109
  * @param head - The kaito head object, which contains getters and setters for headers and status
108
110
  * @returns The context for your routes
109
111
  */
110
- type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
112
+ type GetContext<Result, WithArgument> = (req: KaitoRequest, head: KaitoHead, ...args: [WithArgument] extends [never] ? [] : [input: WithArgument]) => MaybePromise<Result>;
111
113
 
112
- type RouteRunData<Path extends string, Context, QueryOutput, BodyOutput> = {
114
+ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
113
115
  ctx: Context;
114
116
  body: BodyOutput;
115
117
  query: QueryOutput;
116
- params: ExtractRouteParams<Path>;
118
+ params: Params;
117
119
  };
118
120
  type AnyQuery = {
119
121
  [key in string]: any;
120
122
  };
121
- type Through<From, To> = (context: From) => Promise<To>;
123
+ type Through<From, To, RequiredParams extends Record<string, unknown>> = (context: From, params: RequiredParams) => Promise<To>;
122
124
  type SSEOutputSpec<Result> = {
123
125
  type: 'sse';
124
126
  schema: z.Schema<Result>;
@@ -133,8 +135,7 @@ type OutputSpec<Result> = {
133
135
  description?: string;
134
136
  body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
135
137
  };
136
- type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Query, Body> = {
137
- through: Through<unknown, ContextTo>;
138
+ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Record<string, unknown>, Method extends KaitoMethod, Query, Body> = {
138
139
  body?: z.Schema<Body>;
139
140
  query?: {
140
141
  [Key in keyof Query]: z.Schema<Query[Key]>;
@@ -142,35 +143,40 @@ type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Q
142
143
  path: Path;
143
144
  method: Method;
144
145
  openapi?: OutputSpec<NoInfer<Result>>;
145
- run(data: RouteRunData<Path, ContextTo, Query, Body>): Promise<Result> | Result;
146
+ router: Router<unknown, ContextTo, AdditionalParams, AnyRoute, any>;
147
+ run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
146
148
  };
147
- type AnyRoute = Route<any, any, any, any, any, any>;
149
+ type AnyRoute = Route<any, any, any, any, any, any, any>;
148
150
 
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;
151
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
150
152
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
151
- type RouterState<ContextFrom, ContextTo, Routes extends AnyRoute> = {
153
+ type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute, Input> = {
152
154
  routes: Set<Routes>;
153
- through: (context: unknown) => Promise<ContextTo>;
154
- config: KaitoConfig<ContextFrom>;
155
+ through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
156
+ config: KaitoConfig<ContextFrom, Input>;
157
+ paramsSchema: z.Schema<RequiredParams> | null;
155
158
  };
156
159
  /**
157
160
  * Accepts a router instance, and returns a union of all the routes in the router
158
161
  *
159
162
  * @example
160
163
  * ```ts
161
- * const app = router().get('/', () => 'Hello, world!');
164
+ * const app = router.get('/', () => 'Hello, world!');
162
165
  *
163
166
  * type Routes = InferRoutes<typeof app>;
164
167
  * ```
165
168
  */
166
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R extends AnyRoute> ? R : never;
167
- declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
169
+ type InferRoutes<R extends Router<any, any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute, any> ? R : never;
170
+ declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute, Input> {
168
171
  private readonly state;
169
- static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, never>;
170
- constructor(state: RouterState<ContextFrom, ContextTo, R>);
172
+ static create: <Context, Input_1 = never>(config: KaitoConfig<Context, Input_1>) => Router<Context, Context, {}, never, Input_1>;
173
+ protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R, Input>);
171
174
  get routes(): Set<R>;
172
175
  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>>;
176
+ params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute, infer Input> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
177
+ [Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
178
+ }) => Router<ContextFrom, ContextTo, NextParams, R, Input> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
179
+ readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes, Input>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>, Input>;
174
180
  protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
175
181
  route?: never;
176
182
  params?: never;
@@ -179,7 +185,7 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
179
185
  params: Record<string, string>;
180
186
  };
181
187
  private static buildQuerySchema;
182
- serve: () => (request: Request) => Promise<Response>;
188
+ serve: () => (request: Request, ...args: [Input] extends [never] ? [] : [input: Input]) => Promise<Response>;
183
189
  openapi: (highLevelSpec: {
184
190
  info: {
185
191
  version: string;
@@ -187,19 +193,19 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
187
193
  description?: string;
188
194
  };
189
195
  servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
190
- }) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", "GET", AnyQuery, unknown>>;
196
+ }) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>, Input>;
191
197
  private readonly method;
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>;
198
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, Input>;
199
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, Input>;
200
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, Input>;
201
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, Input>;
202
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, Input>;
203
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, Input>;
204
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, Input>;
205
+ through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R, Input>;
200
206
  }
201
207
 
202
- type KaitoConfig<ContextFrom> = {
208
+ type KaitoConfig<ContextFrom, WithArgument> = {
203
209
  /**
204
210
  * A function that is called to get the context for a request.
205
211
  *
@@ -207,7 +213,7 @@ type KaitoConfig<ContextFrom> = {
207
213
  *
208
214
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
209
215
  */
210
- getContext?: GetContext<ContextFrom>;
216
+ getContext?: GetContext<ContextFrom, WithArgument>;
211
217
  /**
212
218
  * A function that is called when an error occurs inside a route handler.
213
219
  *
@@ -258,13 +264,18 @@ type KaitoConfig<ContextFrom> = {
258
264
  transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
259
265
  };
260
266
  /**
261
- * Create a helper function for instantiating a Kaito router
267
+ * Helper function for instantiating a Kaito router
262
268
  *
263
269
  * This is the starting point for any Kaito application
264
270
  *
265
271
  * @param config - The configuration for the router
266
272
  * @returns A new Kaito router
267
273
  */
268
- declare function create<Context = null>(config?: KaitoConfig<Context>): () => Router<Context, Context, never>;
274
+ declare function create<Context = null>(config?: KaitoConfig<Context, never>): Router<Context, Context, {}, never, never>;
275
+ declare namespace create {
276
+ var withInput: <Input = never>() => {
277
+ create: <Context>(config?: KaitoConfig<Context, Input>) => Router<Context, Context, {}, never, Input>;
278
+ };
279
+ }
269
280
 
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 };
281
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z, ZodTypeDef } from 'zod';
2
2
  import { KaitoSSEResponse } from './stream/stream.js';
3
3
 
4
4
  declare class WrappedError<T> extends Error {
@@ -27,8 +27,6 @@ declare class KaitoRequest {
27
27
  get request(): Request;
28
28
  }
29
29
 
30
- type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
31
-
32
30
  /**
33
31
  * This class is merely a wrapper around a `Headers` object and a status code.
34
32
  * It's used while the router is executing a route to store any mutations to the status
@@ -91,6 +89,10 @@ type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
91
89
  type AnyResponse = APIResponse<unknown>;
92
90
  type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
93
91
  type MaybePromise<T> = T | Promise<T>;
92
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
93
+ type NotReadonly<T> = {
94
+ -readonly [K in keyof T]: T[K];
95
+ };
94
96
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
95
97
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
96
98
  } : T extends `${string}:${infer Param}` ? {
@@ -107,18 +109,18 @@ type ExtractRouteParams<T extends string> = string extends T ? Record<string, st
107
109
  * @param head - The kaito head object, which contains getters and setters for headers and status
108
110
  * @returns The context for your routes
109
111
  */
110
- type GetContext<Result> = (req: KaitoRequest, head: KaitoHead) => MaybePromise<Result>;
112
+ type GetContext<Result, WithArgument> = (req: KaitoRequest, head: KaitoHead, ...args: [WithArgument] extends [never] ? [] : [input: WithArgument]) => MaybePromise<Result>;
111
113
 
112
- type RouteRunData<Path extends string, Context, QueryOutput, BodyOutput> = {
114
+ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
113
115
  ctx: Context;
114
116
  body: BodyOutput;
115
117
  query: QueryOutput;
116
- params: ExtractRouteParams<Path>;
118
+ params: Params;
117
119
  };
118
120
  type AnyQuery = {
119
121
  [key in string]: any;
120
122
  };
121
- type Through<From, To> = (context: From) => Promise<To>;
123
+ type Through<From, To, RequiredParams extends Record<string, unknown>> = (context: From, params: RequiredParams) => Promise<To>;
122
124
  type SSEOutputSpec<Result> = {
123
125
  type: 'sse';
124
126
  schema: z.Schema<Result>;
@@ -133,8 +135,7 @@ type OutputSpec<Result> = {
133
135
  description?: string;
134
136
  body: NoInfer<Result extends KaitoSSEResponse<infer R> ? SSEOutputSpec<R> : JSONOutputSpec<Result>>;
135
137
  };
136
- type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Query, Body> = {
137
- through: Through<unknown, ContextTo>;
138
+ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Record<string, unknown>, Method extends KaitoMethod, Query, Body> = {
138
139
  body?: z.Schema<Body>;
139
140
  query?: {
140
141
  [Key in keyof Query]: z.Schema<Query[Key]>;
@@ -142,35 +143,40 @@ type Route<ContextTo, Result, Path extends string, Method extends KaitoMethod, Q
142
143
  path: Path;
143
144
  method: Method;
144
145
  openapi?: OutputSpec<NoInfer<Result>>;
145
- run(data: RouteRunData<Path, ContextTo, Query, Body>): Promise<Result> | Result;
146
+ router: Router<unknown, ContextTo, AdditionalParams, AnyRoute, any>;
147
+ run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
146
148
  };
147
- type AnyRoute = Route<any, any, any, any, any, any>;
149
+ type AnyRoute = Route<any, any, any, any, any, any, any>;
148
150
 
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;
151
+ type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
150
152
  type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
151
- type RouterState<ContextFrom, ContextTo, Routes extends AnyRoute> = {
153
+ type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute, Input> = {
152
154
  routes: Set<Routes>;
153
- through: (context: unknown) => Promise<ContextTo>;
154
- config: KaitoConfig<ContextFrom>;
155
+ through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
156
+ config: KaitoConfig<ContextFrom, Input>;
157
+ paramsSchema: z.Schema<RequiredParams> | null;
155
158
  };
156
159
  /**
157
160
  * Accepts a router instance, and returns a union of all the routes in the router
158
161
  *
159
162
  * @example
160
163
  * ```ts
161
- * const app = router().get('/', () => 'Hello, world!');
164
+ * const app = router.get('/', () => 'Hello, world!');
162
165
  *
163
166
  * type Routes = InferRoutes<typeof app>;
164
167
  * ```
165
168
  */
166
- type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R extends AnyRoute> ? R : never;
167
- declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
169
+ type InferRoutes<R extends Router<any, any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute, any> ? R : never;
170
+ declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute, Input> {
168
171
  private readonly state;
169
- static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, never>;
170
- constructor(state: RouterState<ContextFrom, ContextTo, R>);
172
+ static create: <Context, Input_1 = never>(config: KaitoConfig<Context, Input_1>) => Router<Context, Context, {}, never, Input_1>;
173
+ protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R, Input>);
171
174
  get routes(): Set<R>;
172
175
  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>>;
176
+ params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute, infer Input> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
177
+ [Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
178
+ }) => Router<ContextFrom, ContextTo, NextParams, R, Input> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
179
+ readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes, Input>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>, Input>;
174
180
  protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
175
181
  route?: never;
176
182
  params?: never;
@@ -179,7 +185,7 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
179
185
  params: Record<string, string>;
180
186
  };
181
187
  private static buildQuerySchema;
182
- serve: () => (request: Request) => Promise<Response>;
188
+ serve: () => (request: Request, ...args: [Input] extends [never] ? [] : [input: Input]) => Promise<Response>;
183
189
  openapi: (highLevelSpec: {
184
190
  info: {
185
191
  version: string;
@@ -187,19 +193,19 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
187
193
  description?: string;
188
194
  };
189
195
  servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
190
- }) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", "GET", AnyQuery, unknown>>;
196
+ }) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>, Input>;
191
197
  private readonly method;
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>;
198
+ get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, Input>;
199
+ post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, Input>;
200
+ put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, Input>;
201
+ patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, Input>;
202
+ delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, Input>;
203
+ head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, Input>;
204
+ options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, Input>;
205
+ through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R, Input>;
200
206
  }
201
207
 
202
- type KaitoConfig<ContextFrom> = {
208
+ type KaitoConfig<ContextFrom, WithArgument> = {
203
209
  /**
204
210
  * A function that is called to get the context for a request.
205
211
  *
@@ -207,7 +213,7 @@ type KaitoConfig<ContextFrom> = {
207
213
  *
208
214
  * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
209
215
  */
210
- getContext?: GetContext<ContextFrom>;
216
+ getContext?: GetContext<ContextFrom, WithArgument>;
211
217
  /**
212
218
  * A function that is called when an error occurs inside a route handler.
213
219
  *
@@ -258,13 +264,18 @@ type KaitoConfig<ContextFrom> = {
258
264
  transform?: (req: Request, res: Response) => MaybePromise<Response | void | undefined>;
259
265
  };
260
266
  /**
261
- * Create a helper function for instantiating a Kaito router
267
+ * Helper function for instantiating a Kaito router
262
268
  *
263
269
  * This is the starting point for any Kaito application
264
270
  *
265
271
  * @param config - The configuration for the router
266
272
  * @returns A new Kaito router
267
273
  */
268
- declare function create<Context = null>(config?: KaitoConfig<Context>): () => Router<Context, Context, never>;
274
+ declare function create<Context = null>(config?: KaitoConfig<Context, never>): Router<Context, Context, {}, never, never>;
275
+ declare namespace create {
276
+ var withInput: <Input = never>() => {
277
+ create: <Context>(config?: KaitoConfig<Context, Input>) => Router<Context, Context, {}, never, Input>;
278
+ };
279
+ }
269
280
 
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 };
281
+ export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };