@kaito-http/core 3.0.0-beta.1 → 3.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 ADDED
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ KaitoError: () => KaitoError,
24
+ KaitoRequest: () => KaitoRequest,
25
+ Router: () => Router,
26
+ WrappedError: () => WrappedError,
27
+ createKaitoHandler: () => createKaitoHandler,
28
+ createUtilities: () => createUtilities,
29
+ parsable: () => parsable
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/error.ts
34
+ var WrappedError = class _WrappedError extends Error {
35
+ constructor(data) {
36
+ super("Something was thrown, but it was not an instance of Error, so a WrappedError was created.");
37
+ this.data = data;
38
+ }
39
+ static maybe(maybeError) {
40
+ if (maybeError instanceof Error) {
41
+ return maybeError;
42
+ }
43
+ return _WrappedError.from(maybeError);
44
+ }
45
+ static from(data) {
46
+ return new _WrappedError(data);
47
+ }
48
+ };
49
+ var KaitoError = class extends Error {
50
+ constructor(status, message) {
51
+ super(message);
52
+ this.status = status;
53
+ }
54
+ };
55
+
56
+ // src/request.ts
57
+ var KaitoRequest = class {
58
+ url;
59
+ _request;
60
+ constructor(url, request) {
61
+ this._request = request;
62
+ this.url = url;
63
+ }
64
+ get headers() {
65
+ return this._request.headers;
66
+ }
67
+ get method() {
68
+ return this._request.method;
69
+ }
70
+ async arrayBuffer() {
71
+ return this._request.arrayBuffer();
72
+ }
73
+ async blob() {
74
+ return this._request.blob();
75
+ }
76
+ async formData() {
77
+ return this._request.formData();
78
+ }
79
+ async bytes() {
80
+ const buffer = await this.arrayBuffer();
81
+ return new Uint8Array(buffer);
82
+ }
83
+ async json() {
84
+ return this._request.json();
85
+ }
86
+ async text() {
87
+ return this._request.text();
88
+ }
89
+ get request() {
90
+ return this._request;
91
+ }
92
+ };
93
+
94
+ // src/response.ts
95
+ var KaitoResponse = class {
96
+ _headers;
97
+ _status;
98
+ constructor() {
99
+ this._headers = null;
100
+ this._status = 200;
101
+ }
102
+ get headers() {
103
+ if (this._headers === null) {
104
+ this._headers = new Headers();
105
+ }
106
+ return this._headers;
107
+ }
108
+ status(status) {
109
+ this._status = status;
110
+ return this;
111
+ }
112
+ /**
113
+ * Turn this KaitoResponse instance into a Response instance
114
+ * @param body The Kaito JSON format to be sent as the response body
115
+ * @returns A Response instance, ready to be sent
116
+ */
117
+ toResponse(body) {
118
+ const init = {
119
+ status: this._status
120
+ };
121
+ if (this._headers) {
122
+ init.headers = this._headers;
123
+ }
124
+ return Response.json(body, init);
125
+ }
126
+ };
127
+
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
+ // src/router/router.ts
142
+ var Router = class _Router {
143
+ state;
144
+ static create = () => new _Router({
145
+ through: async (context) => context,
146
+ routes: /* @__PURE__ */ new Set()
147
+ });
148
+ static parseQuery(schema, url) {
149
+ if (!schema) {
150
+ return {};
151
+ }
152
+ const result = {};
153
+ for (const [key, parsable2] of Object.entries(schema)) {
154
+ const value = url.searchParams.get(key);
155
+ result[key] = parsable2.parse(value);
156
+ }
157
+ return result;
158
+ }
159
+ constructor(options) {
160
+ this.state = options;
161
+ }
162
+ get routes() {
163
+ return this.state.routes;
164
+ }
165
+ add = (method, path, route) => {
166
+ const merged = {
167
+ ...typeof route === "object" ? route : { run: route },
168
+ method,
169
+ path,
170
+ through: this.state.through
171
+ };
172
+ return new _Router({
173
+ ...this.state,
174
+ routes: /* @__PURE__ */ new Set([...this.state.routes, merged])
175
+ });
176
+ };
177
+ merge = (pathPrefix, other) => {
178
+ const newRoutes = [...other.state.routes].map((route) => ({
179
+ ...route,
180
+ path: `${pathPrefix}${route.path}`
181
+ }));
182
+ return new _Router({
183
+ ...this.state,
184
+ routes: /* @__PURE__ */ new Set([...this.state.routes, ...newRoutes])
185
+ });
186
+ };
187
+ freeze = (server) => {
188
+ const routes = /* @__PURE__ */ new Map();
189
+ for (const route of this.state.routes) {
190
+ if (!routes.has(route.path)) {
191
+ routes.set(route.path, /* @__PURE__ */ new Map());
192
+ }
193
+ routes.get(route.path).set(route.method, route);
194
+ }
195
+ const findRoute = (method, path) => {
196
+ const params = {};
197
+ const pathParts = path.split("/").filter(Boolean);
198
+ for (const [routePath, methodHandlers] of routes) {
199
+ const routeParts = routePath.split("/").filter(Boolean);
200
+ if (routeParts.length !== pathParts.length) continue;
201
+ let matches = true;
202
+ for (let i = 0; i < routeParts.length; i++) {
203
+ const routePart = routeParts[i];
204
+ const pathPart = pathParts[i];
205
+ if (routePart && pathPart && routePart.startsWith(":")) {
206
+ params[routePart.slice(1)] = pathPart;
207
+ } else if (routePart !== pathPart) {
208
+ matches = false;
209
+ break;
210
+ }
211
+ }
212
+ if (matches) {
213
+ const route = methodHandlers.get(method);
214
+ if (route) return { route, params };
215
+ }
216
+ }
217
+ return { params };
218
+ };
219
+ return async (req) => {
220
+ const url = new URL(req.url);
221
+ const method = req.method;
222
+ const { route, params } = findRoute(method, url.pathname);
223
+ if (!route) {
224
+ const body = {
225
+ success: false,
226
+ data: null,
227
+ message: `Cannot ${method} ${url.pathname}`
228
+ };
229
+ return Response.json(body, { status: 404 });
230
+ }
231
+ const request = new KaitoRequest(url, req);
232
+ const response = new KaitoResponse();
233
+ try {
234
+ const rootCtx = await server.getContext(request, response);
235
+ const ctx = await route.through(rootCtx);
236
+ const body = route.body ? await route.body.parse(await req.json()) : void 0;
237
+ const query = _Router.parseQuery(route.query, url);
238
+ const result = await route.run({
239
+ ctx,
240
+ body,
241
+ query,
242
+ params
243
+ });
244
+ if (result instanceof Response) {
245
+ return result;
246
+ }
247
+ return response.toResponse({
248
+ success: true,
249
+ data: result,
250
+ message: "OK"
251
+ });
252
+ } catch (e) {
253
+ const error = WrappedError.maybe(e);
254
+ if (error instanceof KaitoError) {
255
+ return response.status(error.status).toResponse({
256
+ success: false,
257
+ data: null,
258
+ message: error.message
259
+ });
260
+ }
261
+ const { status, message } = await server.onError({ error, req: request }).catch(() => ({ status: 500, message: "Internal Server Error" }));
262
+ return response.status(status).toResponse({
263
+ success: false,
264
+ data: null,
265
+ message
266
+ });
267
+ }
268
+ };
269
+ };
270
+ method = (method) => (path, route) => {
271
+ return this.add(method, path, route);
272
+ };
273
+ get = this.method("GET");
274
+ post = this.method("POST");
275
+ put = this.method("PUT");
276
+ patch = this.method("PATCH");
277
+ delete = this.method("DELETE");
278
+ head = this.method("HEAD");
279
+ options = this.method("OPTIONS");
280
+ through = (transform) => new _Router({
281
+ routes: this.state.routes,
282
+ through: async (context) => {
283
+ const fromCurrentRouter = await this.state.through(context);
284
+ return transform(fromCurrentRouter);
285
+ }
286
+ });
287
+ };
288
+
289
+ // src/server.ts
290
+ function createKaitoHandler(config) {
291
+ const match = config.router.freeze(config);
292
+ return async (request) => {
293
+ let before = void 0;
294
+ if (config.before) {
295
+ const result = await config.before(request);
296
+ if (result instanceof Response) {
297
+ return result;
298
+ }
299
+ before = result;
300
+ }
301
+ const response = await match(request);
302
+ if ("after" in config && config.after) {
303
+ await config.after(before, response);
304
+ }
305
+ return response;
306
+ };
307
+ }
308
+ // Annotate the CommonJS export names for ESM import in node:
309
+ 0 && (module.exports = {
310
+ KaitoError,
311
+ KaitoRequest,
312
+ Router,
313
+ WrappedError,
314
+ createKaitoHandler,
315
+ createUtilities,
316
+ parsable
317
+ });
@@ -0,0 +1,175 @@
1
+ declare class WrappedError<T> extends Error {
2
+ readonly data: T;
3
+ static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
+ static from<T>(data: T): WrappedError<T>;
5
+ private constructor();
6
+ }
7
+ declare class KaitoError extends Error {
8
+ readonly status: number;
9
+ constructor(status: number, message: string);
10
+ }
11
+
12
+ declare class KaitoRequest {
13
+ readonly url: URL;
14
+ private readonly _request;
15
+ constructor(url: URL, request: Request);
16
+ get headers(): Headers;
17
+ get method(): string;
18
+ arrayBuffer(): Promise<ArrayBuffer>;
19
+ blob(): Promise<Blob>;
20
+ formData(): Promise<FormData>;
21
+ bytes(): Promise<Uint8Array>;
22
+ json(): Promise<unknown>;
23
+ text(): Promise<string>;
24
+ get request(): Request;
25
+ }
26
+
27
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
28
+
29
+ /**
30
+ * This class is similar to a `Response` object from the Web Fetch API, but
31
+ * this with no body stream, and is only used for setting status codes/headers.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const response = new KaitoResponse();
36
+ *
37
+ * response.status = 200;
38
+ * response.header('Content-Type', 'application/json');
39
+ *
40
+ * console.log(response.headers); // Headers { 'content-type': 'application/json' }
41
+ * ```
42
+ */
43
+ declare class KaitoResponse {
44
+ private _headers;
45
+ private _status;
46
+ constructor();
47
+ get headers(): Headers;
48
+ status(status: number): this;
49
+ /**
50
+ * Turn this KaitoResponse instance into a Response instance
51
+ * @param body The Kaito JSON format to be sent as the response body
52
+ * @returns A Response instance, ready to be sent
53
+ */
54
+ toResponse<T>(body: APIResponse<T>): Response;
55
+ }
56
+
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> & {
66
+ router: Router<ContextFrom, unknown, any>;
67
+ getContext: GetContext<ContextFrom>;
68
+ onError(arg: {
69
+ error: Error;
70
+ req: KaitoRequest;
71
+ }): Promise<KaitoError | {
72
+ status: number;
73
+ message: string;
74
+ }>;
75
+ };
76
+ declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
77
+
78
+ 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
+ type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
80
+ type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
81
+ routes: Set<Routes>;
82
+ through: (context: ContextFrom) => Promise<ContextTo>;
83
+ };
84
+ type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
85
+ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
86
+ private readonly state;
87
+ static create: <Context>() => Router<Context, Context, never>;
88
+ private static parseQuery;
89
+ constructor(options: RouterState<R, ContextFrom, ContextTo>);
90
+ 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, Path, Method, Query, Body>>;
92
+ 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>;
94
+ 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, 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, 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, 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, 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, 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, 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, Path, "OPTIONS", Query, Body>>;
102
+ through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
103
+ }
104
+
105
+ type ErroredAPIResponse = {
106
+ success: false;
107
+ data: null;
108
+ message: string;
109
+ };
110
+ type SuccessfulAPIResponse<T> = {
111
+ success: true;
112
+ data: T;
113
+ message: 'OK';
114
+ };
115
+ type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
116
+ type AnyResponse = APIResponse<unknown>;
117
+ type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
118
+ [k in Param | keyof ExtractRouteParams<Rest>]: string;
119
+ } : T extends `${string}:${infer Param}` ? {
120
+ [k in Param]: string;
121
+ } : {};
122
+ type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
123
+ /**
124
+ * A helper function to create typed necessary functions
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const {router, getContext} = createUtilities(async (req, res) => {
129
+ * // Return context here
130
+ * })
131
+ *
132
+ * const app = router().get('/', async () => "hello");
133
+ *
134
+ * const server = createServer({
135
+ * router: app,
136
+ * getContext,
137
+ * // ...
138
+ * });
139
+ * ```
140
+ */
141
+ declare function createUtilities<Context>(getContext: GetContext<Context>): {
142
+ getContext: GetContext<Context>;
143
+ router: () => Router<Context, Context, never>;
144
+ };
145
+ interface Parsable<Output = any, Input = Output> {
146
+ _input: Input;
147
+ parse: (value: unknown) => Output;
148
+ }
149
+ type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
150
+ input: Input;
151
+ output: Output;
152
+ } : never;
153
+ declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
154
+
155
+ type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
156
+ ctx: Context;
157
+ body: BodyOutput;
158
+ query: QueryOutput;
159
+ params: ExtractRouteParams<Path>;
160
+ };
161
+ type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
162
+ type Through<From, To> = (context: From) => Promise<To>;
163
+ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
164
+ through: Through<ContextFrom, ContextTo>;
165
+ body?: Body;
166
+ query?: Query;
167
+ path: Path;
168
+ method: Method;
169
+ run(arg: RouteArgument<Path, ContextTo, {
170
+ [Key in keyof Query]: InferParsable<Query[Key]>['output'];
171
+ }, InferParsable<Body>['output']>): Promise<Result>;
172
+ };
173
+ type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
174
+
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 };
@@ -0,0 +1,175 @@
1
+ declare class WrappedError<T> extends Error {
2
+ readonly data: T;
3
+ static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
+ static from<T>(data: T): WrappedError<T>;
5
+ private constructor();
6
+ }
7
+ declare class KaitoError extends Error {
8
+ readonly status: number;
9
+ constructor(status: number, message: string);
10
+ }
11
+
12
+ declare class KaitoRequest {
13
+ readonly url: URL;
14
+ private readonly _request;
15
+ constructor(url: URL, request: Request);
16
+ get headers(): Headers;
17
+ get method(): string;
18
+ arrayBuffer(): Promise<ArrayBuffer>;
19
+ blob(): Promise<Blob>;
20
+ formData(): Promise<FormData>;
21
+ bytes(): Promise<Uint8Array>;
22
+ json(): Promise<unknown>;
23
+ text(): Promise<string>;
24
+ get request(): Request;
25
+ }
26
+
27
+ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
28
+
29
+ /**
30
+ * This class is similar to a `Response` object from the Web Fetch API, but
31
+ * this with no body stream, and is only used for setting status codes/headers.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const response = new KaitoResponse();
36
+ *
37
+ * response.status = 200;
38
+ * response.header('Content-Type', 'application/json');
39
+ *
40
+ * console.log(response.headers); // Headers { 'content-type': 'application/json' }
41
+ * ```
42
+ */
43
+ declare class KaitoResponse {
44
+ private _headers;
45
+ private _status;
46
+ constructor();
47
+ get headers(): Headers;
48
+ status(status: number): this;
49
+ /**
50
+ * Turn this KaitoResponse instance into a Response instance
51
+ * @param body The Kaito JSON format to be sent as the response body
52
+ * @returns A Response instance, ready to be sent
53
+ */
54
+ toResponse<T>(body: APIResponse<T>): Response;
55
+ }
56
+
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> & {
66
+ router: Router<ContextFrom, unknown, any>;
67
+ getContext: GetContext<ContextFrom>;
68
+ onError(arg: {
69
+ error: Error;
70
+ req: KaitoRequest;
71
+ }): Promise<KaitoError | {
72
+ status: number;
73
+ message: string;
74
+ }>;
75
+ };
76
+ declare function createKaitoHandler<Context, BeforeAfterContext = null>(config: ServerConfig<Context, BeforeAfterContext>): (request: Request) => Promise<Response>;
77
+
78
+ 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
+ type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
80
+ type RouterState<Routes extends AnyRoute, ContextFrom, ContextTo> = {
81
+ routes: Set<Routes>;
82
+ through: (context: ContextFrom) => Promise<ContextTo>;
83
+ };
84
+ type InferRoutes<R extends Router<any, any, any>> = R extends Router<any, any, infer R> ? R : never;
85
+ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
86
+ private readonly state;
87
+ static create: <Context>() => Router<Context, Context, never>;
88
+ private static parseQuery;
89
+ constructor(options: RouterState<R, ContextFrom, ContextTo>);
90
+ 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, Path, Method, Query, Body>>;
92
+ 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>;
94
+ 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, 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, 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, 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, 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, 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, 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, Path, "OPTIONS", Query, Body>>;
102
+ through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
103
+ }
104
+
105
+ type ErroredAPIResponse = {
106
+ success: false;
107
+ data: null;
108
+ message: string;
109
+ };
110
+ type SuccessfulAPIResponse<T> = {
111
+ success: true;
112
+ data: T;
113
+ message: 'OK';
114
+ };
115
+ type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
116
+ type AnyResponse = APIResponse<unknown>;
117
+ type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
118
+ [k in Param | keyof ExtractRouteParams<Rest>]: string;
119
+ } : T extends `${string}:${infer Param}` ? {
120
+ [k in Param]: string;
121
+ } : {};
122
+ type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
123
+ /**
124
+ * A helper function to create typed necessary functions
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const {router, getContext} = createUtilities(async (req, res) => {
129
+ * // Return context here
130
+ * })
131
+ *
132
+ * const app = router().get('/', async () => "hello");
133
+ *
134
+ * const server = createServer({
135
+ * router: app,
136
+ * getContext,
137
+ * // ...
138
+ * });
139
+ * ```
140
+ */
141
+ declare function createUtilities<Context>(getContext: GetContext<Context>): {
142
+ getContext: GetContext<Context>;
143
+ router: () => Router<Context, Context, never>;
144
+ };
145
+ interface Parsable<Output = any, Input = Output> {
146
+ _input: Input;
147
+ parse: (value: unknown) => Output;
148
+ }
149
+ type InferParsable<T> = T extends Parsable<infer Output, infer Input> ? {
150
+ input: Input;
151
+ output: Output;
152
+ } : never;
153
+ declare function parsable<T>(parse: (value: unknown) => T): Parsable<T, T>;
154
+
155
+ type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
156
+ ctx: Context;
157
+ body: BodyOutput;
158
+ query: QueryOutput;
159
+ params: ExtractRouteParams<Path>;
160
+ };
161
+ type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
162
+ type Through<From, To> = (context: From) => Promise<To>;
163
+ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition, Body extends Parsable> = {
164
+ through: Through<ContextFrom, ContextTo>;
165
+ body?: Body;
166
+ query?: Query;
167
+ path: Path;
168
+ method: Method;
169
+ run(arg: RouteArgument<Path, ContextTo, {
170
+ [Key in keyof Query]: InferParsable<Query[Key]>['output'];
171
+ }, InferParsable<Body>['output']>): Promise<Result>;
172
+ };
173
+ type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
174
+
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 };