@kaito-http/core 4.0.0-beta.4 → 4.0.0-beta.6
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 +37 -16
- package/dist/index.d.cts +31 -26
- package/dist/index.d.ts +31 -26
- package/dist/index.js +40 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
33
33
|
// src/router/router.ts
|
|
34
34
|
var import_zod = require("zod");
|
|
35
35
|
var import_zod_openapi = require("zod-openapi");
|
|
36
|
+
var import_extend = require("zod-openapi/extend");
|
|
36
37
|
|
|
37
38
|
// src/error.ts
|
|
38
39
|
var WrappedError = class _WrappedError extends Error {
|
|
@@ -149,7 +150,8 @@ var Router = class _Router {
|
|
|
149
150
|
static create = (config) => new _Router({
|
|
150
151
|
through: async (context) => context,
|
|
151
152
|
routes: /* @__PURE__ */ new Set(),
|
|
152
|
-
config
|
|
153
|
+
config,
|
|
154
|
+
paramsSchema: null
|
|
153
155
|
});
|
|
154
156
|
constructor(state) {
|
|
155
157
|
this.state = state;
|
|
@@ -162,14 +164,17 @@ var Router = class _Router {
|
|
|
162
164
|
...typeof route === "object" ? route : { run: route },
|
|
163
165
|
method,
|
|
164
166
|
path,
|
|
165
|
-
|
|
167
|
+
router: this
|
|
166
168
|
};
|
|
167
169
|
return new _Router({
|
|
168
170
|
...this.state,
|
|
169
171
|
routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
|
|
170
172
|
});
|
|
171
173
|
};
|
|
172
|
-
params = () => new _Router(
|
|
174
|
+
params = (spec) => new _Router({
|
|
175
|
+
...this.state,
|
|
176
|
+
paramsSchema: import_zod.z.object(spec)
|
|
177
|
+
});
|
|
173
178
|
merge = (pathPrefix, other) => {
|
|
174
179
|
const newRoutes = [...other.state.routes].map((route) => ({
|
|
175
180
|
...route,
|
|
@@ -230,7 +235,7 @@ var Router = class _Router {
|
|
|
230
235
|
const handle = async (req) => {
|
|
231
236
|
const url = new URL(req.url);
|
|
232
237
|
const method = req.method;
|
|
233
|
-
const { route, params } = findRoute(method, url.pathname);
|
|
238
|
+
const { route, params: rawParams } = findRoute(method, url.pathname);
|
|
234
239
|
if (!route) {
|
|
235
240
|
const body = {
|
|
236
241
|
success: false,
|
|
@@ -244,7 +249,11 @@ var Router = class _Router {
|
|
|
244
249
|
try {
|
|
245
250
|
const body = route.body ? await route.body.parseAsync(await req.json()) : void 0;
|
|
246
251
|
const query = route.fastQuerySchema ? await route.fastQuerySchema.parseAsync(url.searchParams) : {};
|
|
247
|
-
const
|
|
252
|
+
const params = route.router.state.paramsSchema ? route.router.state.paramsSchema.parse(rawParams) : rawParams;
|
|
253
|
+
const ctx = await route.router.state.through(
|
|
254
|
+
await this.state.config.getContext?.(request, head) ?? null,
|
|
255
|
+
params
|
|
256
|
+
);
|
|
248
257
|
const result = await route.run({
|
|
249
258
|
ctx,
|
|
250
259
|
body,
|
|
@@ -331,23 +340,35 @@ var Router = class _Router {
|
|
|
331
340
|
const paths = {};
|
|
332
341
|
for (const route of this.state.routes) {
|
|
333
342
|
const path = route.path;
|
|
343
|
+
if (!route.openapi) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
334
346
|
const pathWithColonParamsReplaceWithCurlyBraces = path.replace(/:(\w+)/g, "{$1}");
|
|
335
347
|
if (!paths[pathWithColonParamsReplaceWithCurlyBraces]) {
|
|
336
348
|
paths[pathWithColonParamsReplaceWithCurlyBraces] = {};
|
|
337
349
|
}
|
|
350
|
+
const content = route.openapi.body.type === "json" ? {
|
|
351
|
+
"application/json": {
|
|
352
|
+
schema: import_zod.z.object({
|
|
353
|
+
success: import_zod.z.literal(true).openapi({
|
|
354
|
+
type: "boolean",
|
|
355
|
+
enum: [true]
|
|
356
|
+
// Need this as zod-openapi doesn't properly work with literals
|
|
357
|
+
}),
|
|
358
|
+
data: route.openapi.body.schema
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
} : {
|
|
362
|
+
"text/event-stream": {
|
|
363
|
+
schema: route.openapi.body.schema
|
|
364
|
+
}
|
|
365
|
+
};
|
|
338
366
|
const item = {
|
|
339
367
|
description: route.openapi?.description ?? "Successful response",
|
|
340
368
|
responses: {
|
|
341
|
-
|
|
369
|
+
default: {
|
|
342
370
|
description: route.openapi?.description ?? "Successful response",
|
|
343
|
-
|
|
344
|
-
content: {
|
|
345
|
-
[{
|
|
346
|
-
json: "application/json",
|
|
347
|
-
sse: "text/event-stream"
|
|
348
|
-
}[route.openapi.body.type]]: { schema: route.openapi?.body.schema }
|
|
349
|
-
}
|
|
350
|
-
} : {}
|
|
371
|
+
content
|
|
351
372
|
}
|
|
352
373
|
}
|
|
353
374
|
};
|
|
@@ -385,7 +406,7 @@ var Router = class _Router {
|
|
|
385
406
|
};
|
|
386
407
|
})
|
|
387
408
|
});
|
|
388
|
-
return this.
|
|
409
|
+
return this.get("/openapi.json", () => Response.json(doc));
|
|
389
410
|
};
|
|
390
411
|
method = (method) => {
|
|
391
412
|
return (path, route) => this.add(method, path, route);
|
|
@@ -400,7 +421,7 @@ var Router = class _Router {
|
|
|
400
421
|
through = (through) => {
|
|
401
422
|
return new _Router({
|
|
402
423
|
...this.state,
|
|
403
|
-
through: async (context) => await through(await this.state.through(context))
|
|
424
|
+
through: async (context, params) => await through(await this.state.through(context, params), params)
|
|
404
425
|
});
|
|
405
426
|
};
|
|
406
427
|
};
|
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';
|
|
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}` ? {
|
|
@@ -118,7 +120,7 @@ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
|
|
|
118
120
|
type AnyQuery = {
|
|
119
121
|
[key in string]: any;
|
|
120
122
|
};
|
|
121
|
-
type Through<From, 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, AdditionalParams extends Record<string,
|
|
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,36 +143,40 @@ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Reco
|
|
|
142
143
|
path: Path;
|
|
143
144
|
method: Method;
|
|
144
145
|
openapi?: OutputSpec<NoInfer<Result>>;
|
|
146
|
+
router: Router<unknown, ContextTo, AdditionalParams, AnyRoute>;
|
|
145
147
|
run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
|
|
146
148
|
};
|
|
147
149
|
type AnyRoute = Route<any, any, any, any, any, any, any>;
|
|
148
150
|
|
|
149
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}`, 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> = {
|
|
152
154
|
routes: Set<Routes>;
|
|
153
|
-
through: (context: unknown) => Promise<ContextTo>;
|
|
155
|
+
through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
|
|
154
156
|
config: KaitoConfig<ContextFrom>;
|
|
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
|
|
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, any>> = R extends Router<any, any, infer R extends AnyRoute
|
|
167
|
-
declare class Router<ContextFrom, ContextTo,
|
|
169
|
+
type InferRoutes<R extends Router<any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute> ? R : never;
|
|
170
|
+
declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute> {
|
|
168
171
|
private readonly state;
|
|
169
|
-
static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context,
|
|
170
|
-
|
|
172
|
+
static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, {}, never>;
|
|
173
|
+
protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R>);
|
|
171
174
|
get routes(): Set<R>;
|
|
172
175
|
private add;
|
|
173
|
-
params: this extends Router<infer ContextFrom, infer ContextTo, infer
|
|
174
|
-
|
|
176
|
+
params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
|
|
177
|
+
[Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
|
|
178
|
+
}) => Router<ContextFrom, ContextTo, NextParams, R> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
|
|
179
|
+
readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
|
|
175
180
|
protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
|
|
176
181
|
route?: never;
|
|
177
182
|
params?: never;
|
|
@@ -188,16 +193,16 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute, RequiredParams
|
|
|
188
193
|
description?: string;
|
|
189
194
|
};
|
|
190
195
|
servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
|
|
191
|
-
}) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET",
|
|
196
|
+
}) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>>;
|
|
192
197
|
private readonly method;
|
|
193
|
-
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" | "
|
|
194
|
-
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" | "
|
|
195
|
-
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" | "
|
|
196
|
-
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" | "
|
|
197
|
-
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" | "
|
|
198
|
-
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" | "
|
|
199
|
-
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" | "
|
|
200
|
-
through: <NextContext>(through: (context: ContextTo) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext,
|
|
198
|
+
get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>>;
|
|
199
|
+
post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>>;
|
|
200
|
+
put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>>;
|
|
201
|
+
patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>>;
|
|
202
|
+
delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>>;
|
|
203
|
+
head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>>;
|
|
204
|
+
options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>>;
|
|
205
|
+
through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R>;
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
type KaitoConfig<ContextFrom> = {
|
|
@@ -266,6 +271,6 @@ type KaitoConfig<ContextFrom> = {
|
|
|
266
271
|
* @param config - The configuration for the router
|
|
267
272
|
* @returns A new Kaito router
|
|
268
273
|
*/
|
|
269
|
-
declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context,
|
|
274
|
+
declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context, {}, never>;
|
|
270
275
|
|
|
271
|
-
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 OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
|
|
276
|
+
export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,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';
|
|
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}` ? {
|
|
@@ -118,7 +120,7 @@ type RouteRunData<Params, Context, QueryOutput, BodyOutput> = {
|
|
|
118
120
|
type AnyQuery = {
|
|
119
121
|
[key in string]: any;
|
|
120
122
|
};
|
|
121
|
-
type Through<From, 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, AdditionalParams extends Record<string,
|
|
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,36 +143,40 @@ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Reco
|
|
|
142
143
|
path: Path;
|
|
143
144
|
method: Method;
|
|
144
145
|
openapi?: OutputSpec<NoInfer<Result>>;
|
|
146
|
+
router: Router<unknown, ContextTo, AdditionalParams, AnyRoute>;
|
|
145
147
|
run(data: RouteRunData<ExtractRouteParams<Path> & AdditionalParams, ContextTo, Query, Body>): Promise<Result> | Result;
|
|
146
148
|
};
|
|
147
149
|
type AnyRoute = Route<any, any, any, any, any, any, any>;
|
|
148
150
|
|
|
149
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}`, 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> = {
|
|
152
154
|
routes: Set<Routes>;
|
|
153
|
-
through: (context: unknown) => Promise<ContextTo>;
|
|
155
|
+
through: (context: unknown, params: RequiredParams) => Promise<ContextTo>;
|
|
154
156
|
config: KaitoConfig<ContextFrom>;
|
|
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
|
|
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, any>> = R extends Router<any, any, infer R extends AnyRoute
|
|
167
|
-
declare class Router<ContextFrom, ContextTo,
|
|
169
|
+
type InferRoutes<R extends Router<any, any, any, any>> = R extends Router<any, any, any, infer R extends AnyRoute> ? R : never;
|
|
170
|
+
declare class Router<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, R extends AnyRoute> {
|
|
168
171
|
private readonly state;
|
|
169
|
-
static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context,
|
|
170
|
-
|
|
172
|
+
static create: <Context>(config: KaitoConfig<Context>) => Router<Context, Context, {}, never>;
|
|
173
|
+
protected constructor(state: RouterState<ContextFrom, ContextTo, RequiredParams, R>);
|
|
171
174
|
get routes(): Set<R>;
|
|
172
175
|
private add;
|
|
173
|
-
params: this extends Router<infer ContextFrom, infer ContextTo, infer
|
|
174
|
-
|
|
176
|
+
params: this extends Router<infer ContextFrom, infer ContextTo, infer Params extends Record<string, unknown>, infer R extends AnyRoute> ? [keyof Params] extends [never] ? <NextParams extends Record<string, unknown> = {}>(spec: {
|
|
177
|
+
[Key in keyof NextParams]: z.ZodType<NextParams[Key], ZodTypeDef, string>;
|
|
178
|
+
}) => Router<ContextFrom, ContextTo, NextParams, R> : 'You cannot define params() on a router that has already had params defined, as routes that already consume params can break.' : never;
|
|
179
|
+
readonly merge: <PathPrefix extends `/${string}`, NextRequiredParams extends Record<string, unknown>, OtherRoutes extends AnyRoute>(pathPrefix: keyof NextRequiredParams extends keyof ExtractRouteParams<PathPrefix> | keyof RequiredParams ? PathPrefix : `Missing ${Exclude<Extract<keyof NextRequiredParams, string>, keyof RequiredParams>}${string}`, other: Router<ContextFrom, unknown, NextRequiredParams, OtherRoutes>) => Router<ContextFrom, ContextTo, RequiredParams, Extract<R | PrefixRoutesPath<PathPrefix, Extract<OtherRoutes, AnyRoute>>, AnyRoute>>;
|
|
175
180
|
protected static getFindRoute: <R_1>(routes: Map<KaitoMethod, Map<string, R_1>>) => (method: KaitoMethod, path: string) => {
|
|
176
181
|
route?: never;
|
|
177
182
|
params?: never;
|
|
@@ -188,16 +193,16 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute, RequiredParams
|
|
|
188
193
|
description?: string;
|
|
189
194
|
};
|
|
190
195
|
servers?: Partial<Record<(`https://` | `http://`) | ({} & string), string>>;
|
|
191
|
-
}) => Router<ContextFrom, ContextTo, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET",
|
|
196
|
+
}) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Response, "/openapi.json", RequiredParams, "GET", {}, never>>;
|
|
192
197
|
private readonly method;
|
|
193
|
-
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" | "
|
|
194
|
-
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" | "
|
|
195
|
-
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" | "
|
|
196
|
-
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" | "
|
|
197
|
-
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" | "
|
|
198
|
-
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" | "
|
|
199
|
-
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" | "
|
|
200
|
-
through: <NextContext>(through: (context: ContextTo) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext,
|
|
198
|
+
get: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>, "body" | "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "GET", Query, Body>>;
|
|
199
|
+
post: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "POST", Query, Body>>;
|
|
200
|
+
put: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PUT", Query, Body>>;
|
|
201
|
+
patch: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "PATCH", Query, Body>>;
|
|
202
|
+
delete: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "DELETE", Query, Body>>;
|
|
203
|
+
head: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "HEAD", Query, Body>>;
|
|
204
|
+
options: <Result, Path extends string, Query extends AnyQuery = {}, Body = never>(path: Path, route: ((data: RouteRunData<ExtractRouteParams<Path> & RequiredParams, ContextTo, Query, Body>) => Result | Promise<Result>) | Omit<Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>, "path" | "method" | "router">) => Router<ContextFrom, ContextTo, RequiredParams, R | Route<ContextTo, Result, Path, RequiredParams, "OPTIONS", Query, Body>>;
|
|
205
|
+
through: <NextContext>(through: (context: ContextTo, params: RequiredParams) => MaybePromise<NextContext>) => Router<ContextFrom, NextContext, RequiredParams, R>;
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
type KaitoConfig<ContextFrom> = {
|
|
@@ -266,6 +271,6 @@ type KaitoConfig<ContextFrom> = {
|
|
|
266
271
|
* @param config - The configuration for the router
|
|
267
272
|
* @returns A new Kaito router
|
|
268
273
|
*/
|
|
269
|
-
declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context,
|
|
274
|
+
declare function create<Context = null>(config?: KaitoConfig<Context>): Router<Context, Context, {}, never>;
|
|
270
275
|
|
|
271
|
-
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 OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
|
|
276
|
+
export { type APIResponse, type AnyQuery, type AnyResponse, type AnyRoute, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferRoutes, type JSONOutputSpec, type KaitoConfig, KaitoError, KaitoHead, type KaitoMethod, KaitoRequest, type MakeOptional, type MaybePromise, type NotReadonly, type OutputSpec, type Route, type RouteRunData, Router, type RouterState, type SSEOutputSpec, type SuccessfulAPIResponse, type Through, WrappedError, create, isNodeLikeDev };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/router/router.ts
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createDocument
|
|
5
|
+
} from "zod-openapi";
|
|
6
|
+
import "zod-openapi/extend";
|
|
4
7
|
|
|
5
8
|
// src/error.ts
|
|
6
9
|
var WrappedError = class _WrappedError extends Error {
|
|
@@ -117,7 +120,8 @@ var Router = class _Router {
|
|
|
117
120
|
static create = (config) => new _Router({
|
|
118
121
|
through: async (context) => context,
|
|
119
122
|
routes: /* @__PURE__ */ new Set(),
|
|
120
|
-
config
|
|
123
|
+
config,
|
|
124
|
+
paramsSchema: null
|
|
121
125
|
});
|
|
122
126
|
constructor(state) {
|
|
123
127
|
this.state = state;
|
|
@@ -130,14 +134,17 @@ var Router = class _Router {
|
|
|
130
134
|
...typeof route === "object" ? route : { run: route },
|
|
131
135
|
method,
|
|
132
136
|
path,
|
|
133
|
-
|
|
137
|
+
router: this
|
|
134
138
|
};
|
|
135
139
|
return new _Router({
|
|
136
140
|
...this.state,
|
|
137
141
|
routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
|
|
138
142
|
});
|
|
139
143
|
};
|
|
140
|
-
params = () => new _Router(
|
|
144
|
+
params = (spec) => new _Router({
|
|
145
|
+
...this.state,
|
|
146
|
+
paramsSchema: z.object(spec)
|
|
147
|
+
});
|
|
141
148
|
merge = (pathPrefix, other) => {
|
|
142
149
|
const newRoutes = [...other.state.routes].map((route) => ({
|
|
143
150
|
...route,
|
|
@@ -198,7 +205,7 @@ var Router = class _Router {
|
|
|
198
205
|
const handle = async (req) => {
|
|
199
206
|
const url = new URL(req.url);
|
|
200
207
|
const method = req.method;
|
|
201
|
-
const { route, params } = findRoute(method, url.pathname);
|
|
208
|
+
const { route, params: rawParams } = findRoute(method, url.pathname);
|
|
202
209
|
if (!route) {
|
|
203
210
|
const body = {
|
|
204
211
|
success: false,
|
|
@@ -212,7 +219,11 @@ var Router = class _Router {
|
|
|
212
219
|
try {
|
|
213
220
|
const body = route.body ? await route.body.parseAsync(await req.json()) : void 0;
|
|
214
221
|
const query = route.fastQuerySchema ? await route.fastQuerySchema.parseAsync(url.searchParams) : {};
|
|
215
|
-
const
|
|
222
|
+
const params = route.router.state.paramsSchema ? route.router.state.paramsSchema.parse(rawParams) : rawParams;
|
|
223
|
+
const ctx = await route.router.state.through(
|
|
224
|
+
await this.state.config.getContext?.(request, head) ?? null,
|
|
225
|
+
params
|
|
226
|
+
);
|
|
216
227
|
const result = await route.run({
|
|
217
228
|
ctx,
|
|
218
229
|
body,
|
|
@@ -299,23 +310,35 @@ var Router = class _Router {
|
|
|
299
310
|
const paths = {};
|
|
300
311
|
for (const route of this.state.routes) {
|
|
301
312
|
const path = route.path;
|
|
313
|
+
if (!route.openapi) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
302
316
|
const pathWithColonParamsReplaceWithCurlyBraces = path.replace(/:(\w+)/g, "{$1}");
|
|
303
317
|
if (!paths[pathWithColonParamsReplaceWithCurlyBraces]) {
|
|
304
318
|
paths[pathWithColonParamsReplaceWithCurlyBraces] = {};
|
|
305
319
|
}
|
|
320
|
+
const content = route.openapi.body.type === "json" ? {
|
|
321
|
+
"application/json": {
|
|
322
|
+
schema: z.object({
|
|
323
|
+
success: z.literal(true).openapi({
|
|
324
|
+
type: "boolean",
|
|
325
|
+
enum: [true]
|
|
326
|
+
// Need this as zod-openapi doesn't properly work with literals
|
|
327
|
+
}),
|
|
328
|
+
data: route.openapi.body.schema
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
} : {
|
|
332
|
+
"text/event-stream": {
|
|
333
|
+
schema: route.openapi.body.schema
|
|
334
|
+
}
|
|
335
|
+
};
|
|
306
336
|
const item = {
|
|
307
337
|
description: route.openapi?.description ?? "Successful response",
|
|
308
338
|
responses: {
|
|
309
|
-
|
|
339
|
+
default: {
|
|
310
340
|
description: route.openapi?.description ?? "Successful response",
|
|
311
|
-
|
|
312
|
-
content: {
|
|
313
|
-
[{
|
|
314
|
-
json: "application/json",
|
|
315
|
-
sse: "text/event-stream"
|
|
316
|
-
}[route.openapi.body.type]]: { schema: route.openapi?.body.schema }
|
|
317
|
-
}
|
|
318
|
-
} : {}
|
|
341
|
+
content
|
|
319
342
|
}
|
|
320
343
|
}
|
|
321
344
|
};
|
|
@@ -353,7 +376,7 @@ var Router = class _Router {
|
|
|
353
376
|
};
|
|
354
377
|
})
|
|
355
378
|
});
|
|
356
|
-
return this.
|
|
379
|
+
return this.get("/openapi.json", () => Response.json(doc));
|
|
357
380
|
};
|
|
358
381
|
method = (method) => {
|
|
359
382
|
return (path, route) => this.add(method, path, route);
|
|
@@ -368,7 +391,7 @@ var Router = class _Router {
|
|
|
368
391
|
through = (through) => {
|
|
369
392
|
return new _Router({
|
|
370
393
|
...this.state,
|
|
371
|
-
through: async (context) => await through(await this.state.through(context))
|
|
394
|
+
through: async (context, params) => await through(await this.state.through(context, params), params)
|
|
372
395
|
});
|
|
373
396
|
};
|
|
374
397
|
};
|