@kaito-http/core 3.0.0-beta.10 → 3.0.0-beta.13

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
@@ -24,7 +24,6 @@ __export(index_exports, {
24
24
  KaitoRequest: () => KaitoRequest,
25
25
  Router: () => Router,
26
26
  WrappedError: () => WrappedError,
27
- apiresponse: () => apiresponse,
28
27
  createKaitoHandler: () => createKaitoHandler,
29
28
  createUtilities: () => createUtilities,
30
29
  parsable: () => parsable
@@ -56,53 +55,77 @@ var KaitoError = class extends Error {
56
55
 
57
56
  // src/request.ts
58
57
  var KaitoRequest = class {
59
- request;
60
- _url;
61
- constructor(request) {
62
- this.request = request;
58
+ url;
59
+ _request;
60
+ constructor(url, request) {
61
+ this._request = request;
62
+ this.url = url;
63
63
  }
64
64
  get headers() {
65
- return this.request.headers;
65
+ return this._request.headers;
66
66
  }
67
67
  get method() {
68
- return this.request.method;
69
- }
70
- get url() {
71
- return this.request.url;
72
- }
73
- parseURL() {
74
- if (!this._url) {
75
- this._url = new URL(this.url);
76
- }
77
- return this._url;
68
+ return this._request.method;
78
69
  }
79
70
  async arrayBuffer() {
80
- return this.request.arrayBuffer();
71
+ return this._request.arrayBuffer();
81
72
  }
82
73
  async blob() {
83
- return this.request.blob();
74
+ return this._request.blob();
84
75
  }
85
76
  async formData() {
86
- return this.request.formData();
77
+ return this._request.formData();
87
78
  }
88
79
  async bytes() {
89
80
  const buffer = await this.arrayBuffer();
90
81
  return new Uint8Array(buffer);
91
82
  }
92
83
  async json() {
93
- return this.request.json();
84
+ return this._request.json();
94
85
  }
95
86
  async text() {
96
- return this.request.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);
97
125
  }
98
126
  };
99
127
 
100
128
  // src/util.ts
101
- function apiresponse(status, response) {
102
- return Response.json(response, {
103
- status
104
- });
105
- }
106
129
  function createUtilities(getContext) {
107
130
  return {
108
131
  getContext,
@@ -141,6 +164,7 @@ var Router = class _Router {
141
164
  }
142
165
  add = (method, path, route) => {
143
166
  const merged = {
167
+ // TODO: Ideally fix the typing here, but this will be replaced in Kaito v4 where all routes must return a Response (which we can type)
144
168
  ...typeof route === "object" ? route : { run: route },
145
169
  method,
146
170
  path,
@@ -198,15 +222,17 @@ var Router = class _Router {
198
222
  const method = req.method;
199
223
  const { route, params } = findRoute(method, url.pathname);
200
224
  if (!route) {
201
- return apiresponse(404, {
225
+ const body = {
202
226
  success: false,
203
227
  data: null,
204
228
  message: `Cannot ${method} ${url.pathname}`
205
- });
229
+ };
230
+ return Response.json(body, { status: 404 });
206
231
  }
207
- const request = new KaitoRequest(req);
232
+ const request = new KaitoRequest(url, req);
233
+ const response = new KaitoResponse();
208
234
  try {
209
- const rootCtx = await server.getContext(request);
235
+ const rootCtx = await server.getContext(request, response);
210
236
  const ctx = await route.through(rootCtx);
211
237
  const body = route.body ? await route.body.parse(await req.json()) : void 0;
212
238
  const query = _Router.parseQuery(route.query, url);
@@ -216,7 +242,10 @@ var Router = class _Router {
216
242
  query,
217
243
  params
218
244
  });
219
- return apiresponse(200, {
245
+ if (result instanceof Response) {
246
+ return result;
247
+ }
248
+ return response.toResponse({
220
249
  success: true,
221
250
  data: result,
222
251
  message: "OK"
@@ -224,14 +253,14 @@ var Router = class _Router {
224
253
  } catch (e) {
225
254
  const error = WrappedError.maybe(e);
226
255
  if (error instanceof KaitoError) {
227
- return apiresponse(error.status, {
256
+ return response.status(error.status).toResponse({
228
257
  success: false,
229
258
  data: null,
230
259
  message: error.message
231
260
  });
232
261
  }
233
262
  const { status, message } = await server.onError({ error, req: request }).catch(() => ({ status: 500, message: "Internal Server Error" }));
234
- return apiresponse(status, {
263
+ return response.status(status).toResponse({
235
264
  success: false,
236
265
  data: null,
237
266
  message
@@ -283,7 +312,6 @@ function createKaitoHandler(config) {
283
312
  KaitoRequest,
284
313
  Router,
285
314
  WrappedError,
286
- apiresponse,
287
315
  createKaitoHandler,
288
316
  createUtilities,
289
317
  parsable
package/dist/index.d.cts CHANGED
@@ -10,23 +10,50 @@ declare class KaitoError extends Error {
10
10
  }
11
11
 
12
12
  declare class KaitoRequest {
13
- private readonly request;
14
- private _url;
15
- constructor(request: Request);
13
+ readonly url: URL;
14
+ private readonly _request;
15
+ constructor(url: URL, request: Request);
16
16
  get headers(): Headers;
17
17
  get method(): string;
18
- get url(): string;
19
- parseURL(): URL;
20
18
  arrayBuffer(): Promise<ArrayBuffer>;
21
19
  blob(): Promise<Blob>;
22
20
  formData(): Promise<FormData>;
23
21
  bytes(): Promise<Uint8Array>;
24
22
  json(): Promise<unknown>;
25
23
  text(): Promise<string>;
24
+ get request(): Request;
26
25
  }
27
26
 
28
27
  type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
29
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
+
30
57
  type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
31
58
  type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
32
59
  type ServerConfigWithBefore<BeforeAfterContext> = {
@@ -61,17 +88,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
61
88
  private static parseQuery;
62
89
  constructor(options: RouterState<R, ContextFrom, ContextTo>);
63
90
  get routes(): Set<R>;
64
- 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>>;
91
+ add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, Method, Query, Body>>;
65
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>>;
66
- freeze: (server: ServerConfig<ContextFrom, any>) => (req: Request) => Promise<Response>;
93
+ freeze: (server: Omit<ServerConfig<ContextFrom, any>, "router">) => (req: Request) => Promise<Response>;
67
94
  private readonly method;
68
- 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>>;
69
- 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>>;
70
- 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>>;
71
- 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>>;
72
- 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>>;
73
- 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>>;
74
- 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>>;
95
+ get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "GET", Query, Body>>;
96
+ post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "POST", Query, Body>>;
97
+ put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "PUT", Query, Body>>;
98
+ patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "PATCH", Query, Body>>;
99
+ delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "DELETE", Query, Body>>;
100
+ head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "HEAD", Query, Body>>;
101
+ options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "OPTIONS", Query, Body>>;
75
102
  through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
76
103
  }
77
104
 
@@ -87,13 +114,12 @@ type SuccessfulAPIResponse<T> = {
87
114
  };
88
115
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
89
116
  type AnyResponse = APIResponse<unknown>;
90
- declare function apiresponse<T>(status: number, response: APIResponse<T>): Response;
91
117
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
92
118
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
93
119
  } : T extends `${string}:${infer Param}` ? {
94
120
  [k in Param]: string;
95
121
  } : {};
96
- type GetContext<Result> = (req: KaitoRequest) => Promise<Result>;
122
+ type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
97
123
  /**
98
124
  * A helper function to create typed necessary functions
99
125
  *
@@ -146,4 +172,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
146
172
  };
147
173
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
148
174
 
149
- 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, apiresponse, createKaitoHandler, createUtilities, parsable };
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 };
package/dist/index.d.ts CHANGED
@@ -10,23 +10,50 @@ declare class KaitoError extends Error {
10
10
  }
11
11
 
12
12
  declare class KaitoRequest {
13
- private readonly request;
14
- private _url;
15
- constructor(request: Request);
13
+ readonly url: URL;
14
+ private readonly _request;
15
+ constructor(url: URL, request: Request);
16
16
  get headers(): Headers;
17
17
  get method(): string;
18
- get url(): string;
19
- parseURL(): URL;
20
18
  arrayBuffer(): Promise<ArrayBuffer>;
21
19
  blob(): Promise<Blob>;
22
20
  formData(): Promise<FormData>;
23
21
  bytes(): Promise<Uint8Array>;
24
22
  json(): Promise<unknown>;
25
23
  text(): Promise<string>;
24
+ get request(): Request;
26
25
  }
27
26
 
28
27
  type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
29
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
+
30
57
  type Before<BeforeAfterContext> = (req: Request) => Promise<BeforeAfterContext | Response>;
31
58
  type After<BeforeAfterContext> = (ctx: BeforeAfterContext, response: Response) => Promise<void>;
32
59
  type ServerConfigWithBefore<BeforeAfterContext> = {
@@ -61,17 +88,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
61
88
  private static parseQuery;
62
89
  constructor(options: RouterState<R, ContextFrom, ContextTo>);
63
90
  get routes(): Set<R>;
64
- 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>>;
91
+ add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, Method, Query, Body>>;
65
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>>;
66
- freeze: (server: ServerConfig<ContextFrom, any>) => (req: Request) => Promise<Response>;
93
+ freeze: (server: Omit<ServerConfig<ContextFrom, any>, "router">) => (req: Request) => Promise<Response>;
67
94
  private readonly method;
68
- 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>>;
69
- 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>>;
70
- 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>>;
71
- 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>>;
72
- 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>>;
73
- 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>>;
74
- 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>>;
95
+ get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "GET", Query, Body>>;
96
+ post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "POST", Query, Body>>;
97
+ put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "PUT", Query, Body>>;
98
+ patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "PATCH", Query, Body>>;
99
+ delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "DELETE", Query, Body>>;
100
+ head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "HEAD", Query, Body>>;
101
+ options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result extends Response ? unknown : Result, Path, "OPTIONS", Query, Body>>;
75
102
  through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
76
103
  }
77
104
 
@@ -87,13 +114,12 @@ type SuccessfulAPIResponse<T> = {
87
114
  };
88
115
  type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
89
116
  type AnyResponse = APIResponse<unknown>;
90
- declare function apiresponse<T>(status: number, response: APIResponse<T>): Response;
91
117
  type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
92
118
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
93
119
  } : T extends `${string}:${infer Param}` ? {
94
120
  [k in Param]: string;
95
121
  } : {};
96
- type GetContext<Result> = (req: KaitoRequest) => Promise<Result>;
122
+ type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
97
123
  /**
98
124
  * A helper function to create typed necessary functions
99
125
  *
@@ -146,4 +172,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
146
172
  };
147
173
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
148
174
 
149
- 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, apiresponse, createKaitoHandler, createUtilities, parsable };
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 };
package/dist/index.js CHANGED
@@ -23,53 +23,77 @@ var KaitoError = class extends Error {
23
23
 
24
24
  // src/request.ts
25
25
  var KaitoRequest = class {
26
- request;
27
- _url;
28
- constructor(request) {
29
- this.request = request;
26
+ url;
27
+ _request;
28
+ constructor(url, request) {
29
+ this._request = request;
30
+ this.url = url;
30
31
  }
31
32
  get headers() {
32
- return this.request.headers;
33
+ return this._request.headers;
33
34
  }
34
35
  get method() {
35
- return this.request.method;
36
- }
37
- get url() {
38
- return this.request.url;
39
- }
40
- parseURL() {
41
- if (!this._url) {
42
- this._url = new URL(this.url);
43
- }
44
- return this._url;
36
+ return this._request.method;
45
37
  }
46
38
  async arrayBuffer() {
47
- return this.request.arrayBuffer();
39
+ return this._request.arrayBuffer();
48
40
  }
49
41
  async blob() {
50
- return this.request.blob();
42
+ return this._request.blob();
51
43
  }
52
44
  async formData() {
53
- return this.request.formData();
45
+ return this._request.formData();
54
46
  }
55
47
  async bytes() {
56
48
  const buffer = await this.arrayBuffer();
57
49
  return new Uint8Array(buffer);
58
50
  }
59
51
  async json() {
60
- return this.request.json();
52
+ return this._request.json();
61
53
  }
62
54
  async text() {
63
- return this.request.text();
55
+ return this._request.text();
56
+ }
57
+ get request() {
58
+ return this._request;
59
+ }
60
+ };
61
+
62
+ // src/response.ts
63
+ var KaitoResponse = class {
64
+ _headers;
65
+ _status;
66
+ constructor() {
67
+ this._headers = null;
68
+ this._status = 200;
69
+ }
70
+ get headers() {
71
+ if (this._headers === null) {
72
+ this._headers = new Headers();
73
+ }
74
+ return this._headers;
75
+ }
76
+ status(status) {
77
+ this._status = status;
78
+ return this;
79
+ }
80
+ /**
81
+ * Turn this KaitoResponse instance into a Response instance
82
+ * @param body The Kaito JSON format to be sent as the response body
83
+ * @returns A Response instance, ready to be sent
84
+ */
85
+ toResponse(body) {
86
+ const init = {
87
+ status: this._status
88
+ };
89
+ if (this._headers) {
90
+ init.headers = this._headers;
91
+ }
92
+ return Response.json(body, init);
64
93
  }
65
94
  };
66
95
 
67
96
  // src/util.ts
68
- function apiresponse(status, response) {
69
- return Response.json(response, {
70
- status
71
- });
72
- }
73
97
  function createUtilities(getContext) {
74
98
  return {
75
99
  getContext,
@@ -108,6 +132,7 @@ var Router = class _Router {
108
132
  }
109
133
  add = (method, path, route) => {
110
134
  const merged = {
135
+ // TODO: Ideally fix the typing here, but this will be replaced in Kaito v4 where all routes must return a Response (which we can type)
111
136
  ...typeof route === "object" ? route : { run: route },
112
137
  method,
113
138
  path,
@@ -165,15 +190,17 @@ var Router = class _Router {
165
190
  const method = req.method;
166
191
  const { route, params } = findRoute(method, url.pathname);
167
192
  if (!route) {
168
- return apiresponse(404, {
193
+ const body = {
169
194
  success: false,
170
195
  data: null,
171
196
  message: `Cannot ${method} ${url.pathname}`
172
- });
197
+ };
198
+ return Response.json(body, { status: 404 });
173
199
  }
174
- const request = new KaitoRequest(req);
200
+ const request = new KaitoRequest(url, req);
201
+ const response = new KaitoResponse();
175
202
  try {
176
- const rootCtx = await server.getContext(request);
203
+ const rootCtx = await server.getContext(request, response);
177
204
  const ctx = await route.through(rootCtx);
178
205
  const body = route.body ? await route.body.parse(await req.json()) : void 0;
179
206
  const query = _Router.parseQuery(route.query, url);
@@ -183,7 +210,10 @@ var Router = class _Router {
183
210
  query,
184
211
  params
185
212
  });
186
- return apiresponse(200, {
213
+ if (result instanceof Response) {
214
+ return result;
215
+ }
216
+ return response.toResponse({
187
217
  success: true,
188
218
  data: result,
189
219
  message: "OK"
@@ -191,14 +221,14 @@ var Router = class _Router {
191
221
  } catch (e) {
192
222
  const error = WrappedError.maybe(e);
193
223
  if (error instanceof KaitoError) {
194
- return apiresponse(error.status, {
224
+ return response.status(error.status).toResponse({
195
225
  success: false,
196
226
  data: null,
197
227
  message: error.message
198
228
  });
199
229
  }
200
230
  const { status, message } = await server.onError({ error, req: request }).catch(() => ({ status: 500, message: "Internal Server Error" }));
201
- return apiresponse(status, {
231
+ return response.status(status).toResponse({
202
232
  success: false,
203
233
  data: null,
204
234
  message
@@ -249,7 +279,6 @@ export {
249
279
  KaitoRequest,
250
280
  Router,
251
281
  WrappedError,
252
- apiresponse,
253
282
  createKaitoHandler,
254
283
  createUtilities,
255
284
  parsable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "3.0.0-beta.10",
3
+ "version": "3.0.0-beta.13",
4
4
  "type": "module",
5
5
  "author": "Alistair Smith <hi@alistair.sh>",
6
6
  "description": "Functional HTTP Framework for TypeScript",