@kaito-http/core 2.7.1 → 2.8.0

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.
@@ -14,13 +14,6 @@ export declare class Router<Context, R extends Routes> {
14
14
  static create: <Context_1>() => Router<Context_1, []>;
15
15
  private static handle;
16
16
  constructor(routes: R);
17
- /**
18
- * Adds a new route to the router
19
- * @param route The route specification to add to this router
20
- * @returns A new router with this route added
21
- * @deprecated Use `Router#add` instead
22
- */
23
- old_add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(route: Method extends "GET" ? Omit<Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>, "body"> : Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>) => Router<Context, [...R, Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>]>;
24
17
  /**
25
18
  * Adds a new route to the router
26
19
  * @param method The HTTP method to add a route for
@@ -29,7 +22,15 @@ export declare class Router<Context, R extends Routes> {
29
22
  * @returns A new router with this route added
30
23
  */
31
24
  add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(method: Method, path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | (Method extends "GET" ? Omit<Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>, "path" | "body" | "method"> : Omit<Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">)) => Router<Context, [...R, Route<Context, Result, Path, Method, Query, BodyOutput, BodyDef, BodyInput>]>;
32
- merge: <PathPrefix extends `/${string}`, OtherRoutes extends Routes>(pathPrefix: PathPrefix, other: Router<Context, OtherRoutes>) => Router<Context, [...R, ...PrefixRoutesPath<PathPrefix, OtherRoutes>]>;
33
- toFindMyWay: (server: ServerConfig<Context, any>) => fmw.Instance<fmw.HTTPVersion.V1>;
25
+ readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends Routes>(pathPrefix: PathPrefix, other: Router<Context, OtherRoutes>) => Router<Context, [...R, ...PrefixRoutesPath<PathPrefix, OtherRoutes>]>;
26
+ freeze: (server: ServerConfig<Context, any>) => fmw.Instance<fmw.HTTPVersion.V1>;
27
+ private readonly method;
28
+ get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "GET", Query, BodyOutput, BodyDef, BodyInput>, "path" | "body" | "method">) => Router<Context, [...R, Route<Context, Result, Path, "GET", Query, BodyOutput, BodyDef, BodyInput>]>;
29
+ post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "POST", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
30
+ put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "PUT", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
31
+ patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "PATCH", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
32
+ delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "DELETE", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
33
+ head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "HEAD", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
34
+ options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, BodyOutput = never, BodyDef extends z.ZodTypeDef = z.ZodTypeDef, BodyInput = BodyOutput>(path: Path, route: ((args: import("./route").RouteArgument<Path, Context, z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }> extends infer T extends object ? { [k in keyof T]: z.objectUtil.addQuestionMarks<{ [k_1 in keyof Query]: Query[k_1]["_output"]; }>[k]; } : never, BodyOutput>) => Promise<Result>) | Omit<Route<Context, Result, Path, "OPTIONS", Query, BodyOutput, BodyDef, BodyInput>, "path" | "method">) => Router<Context, [...R, Route<Context, Result, Path, never, Query, BodyOutput, BodyDef, BodyInput>]>;
34
35
  }
35
36
  export {};
@@ -1,6 +1,7 @@
1
1
  import type { HTTPMethod } from 'find-my-way';
2
2
  import type { KaitoRequest } from './req';
3
3
  import type { KaitoResponse } from './res';
4
+ import { Router } from './router';
4
5
  export type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
5
6
  [k in Param | keyof ExtractRouteParams<Rest>]: string;
6
7
  } : T extends `${string}:${infer Param}` ? {
@@ -8,7 +9,32 @@ export type ExtractRouteParams<T extends string> = string extends T ? Record<str
8
9
  } : {};
9
10
  export type KaitoMethod = HTTPMethod | '*';
10
11
  export type GetContext<Result> = (req: KaitoRequest, res: KaitoResponse) => Promise<Result>;
12
+ /**
13
+ * @deprecated use `createUtilities` instead
14
+ */
11
15
  export declare function createGetContext<Context>(callback: GetContext<Context>): GetContext<Context>;
16
+ /**
17
+ * A helper function to create typed necessary functions
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const {router, getContext} = createUtilities(async (req, res) => {
22
+ * // Return context here
23
+ * })
24
+ *
25
+ * const app = router().get('/', async () => "hello");
26
+ *
27
+ * const server = createServer({
28
+ * router: app,
29
+ * getContext,
30
+ * // ...
31
+ * });
32
+ * ```
33
+ */
34
+ export declare function createUtilities<Context>(getContext: GetContext<Context>): {
35
+ getContext: GetContext<Context>;
36
+ router: () => Router<Context, []>;
37
+ };
12
38
  export type InferContext<T> = T extends (req: KaitoRequest, res: KaitoResponse) => Promise<infer U> ? U : never;
13
39
  export declare function getLastEntryInMultiHeaderValue(headerValue: string | string[]): string;
14
40
  type RemoveEndSlashes<T extends string> = T extends `${infer U}/` ? U : T;
@@ -7,9 +7,9 @@ var contentType = require('content-type');
7
7
  var node_stream = require('node:stream');
8
8
  var consumers = require('node:stream/consumers');
9
9
  var getRawBody = require('raw-body');
10
- var cookie = require('cookie');
11
10
  var fmw = require('find-my-way');
12
11
  var zod = require('zod');
12
+ var cookie = require('cookie');
13
13
  var http = require('node:http');
14
14
 
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -103,104 +103,26 @@ function _asyncToGenerator(fn) {
103
103
  };
104
104
  }
105
105
 
106
- function createGetContext(callback) {
107
- return callback;
108
- }
109
- function getLastEntryInMultiHeaderValue(headerValue) {
110
- var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
111
- var lastIndex = normalized.lastIndexOf(',');
112
- return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
113
- }
114
- function getBody(_x) {
115
- return _getBody.apply(this, arguments);
116
- }
117
- function _getBody() {
118
- _getBody = _asyncToGenerator(function* (req) {
119
- if (!req.headers['content-type']) {
120
- return null;
121
- }
122
- var buffer = yield getRawBody__default["default"](req.raw);
123
- var {
124
- type
125
- } = contentType.parse(req.headers['content-type']);
126
- switch (type) {
127
- case 'application/json':
128
- {
129
- return consumers.json(node_stream.Readable.from(buffer));
130
- }
131
- default:
132
- {
133
- if (process.env.NODE_ENV === 'development') {
134
- console.warn('[kaito] Unsupported content type:', type);
135
- console.warn('[kaito] This message is only shown in development mode.');
136
- }
137
- return null;
138
- }
139
- }
140
- });
141
- return _getBody.apply(this, arguments);
142
- }
143
-
144
- class KaitoRequest {
145
- constructor(raw) {
146
- _defineProperty(this, "_url", null);
147
- this.raw = raw;
148
- }
149
-
150
- /**
151
- * The full URL of the request, including the protocol, hostname, and path.
152
- * Note: does not include the query string or hash
153
- */
154
- get fullURL() {
155
- var _this$raw$url;
156
- return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
157
- }
158
-
159
- /**
160
- * A new URL instance for the full URL of the request.
161
- */
162
- get url() {
163
- if (this._url) {
164
- return this._url;
165
- }
166
- this._url = new URL(this.fullURL);
167
- return this._url;
168
- }
169
-
170
- /**
171
- * The HTTP method of the request.
172
- */
173
- get method() {
174
- if (!this.raw.method) {
175
- throw new Error('Request method is not defined, somehow...');
176
- }
177
- return this.raw.method;
178
- }
179
-
180
- /**
181
- * The protocol of the request, either `http` or `https`.
182
- */
183
- get protocol() {
184
- if (this.raw.socket instanceof node_tls.TLSSocket) {
185
- return this.raw.socket.encrypted ? 'https' : 'http';
186
- }
187
- return 'http';
188
- }
189
-
190
- /**
191
- * The request headers
192
- */
193
- get headers() {
194
- return this.raw.headers;
106
+ function ownKeys(object, enumerableOnly) {
107
+ var keys = Object.keys(object);
108
+ if (Object.getOwnPropertySymbols) {
109
+ var symbols = Object.getOwnPropertySymbols(object);
110
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
111
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
112
+ })), keys.push.apply(keys, symbols);
195
113
  }
196
-
197
- /**
198
- * The hostname of the request.
199
- */
200
- get hostname() {
201
- var _this$raw$headers$hos, _this$raw$headers$Au;
202
- return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
114
+ return keys;
115
+ }
116
+ function _objectSpread2(target) {
117
+ for (var i = 1; i < arguments.length; i++) {
118
+ var source = null != arguments[i] ? arguments[i] : {};
119
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
120
+ _defineProperty(target, key, source[key]);
121
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
122
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
123
+ });
203
124
  }
125
+ return target;
204
126
  }
205
127
 
206
128
  class KaitoResponse {
@@ -255,31 +177,19 @@ class KaitoResponse {
255
177
  }
256
178
  }
257
179
 
258
- function ownKeys(object, enumerableOnly) {
259
- var keys = Object.keys(object);
260
- if (Object.getOwnPropertySymbols) {
261
- var symbols = Object.getOwnPropertySymbols(object);
262
- enumerableOnly && (symbols = symbols.filter(function (sym) {
263
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
264
- })), keys.push.apply(keys, symbols);
265
- }
266
- return keys;
267
- }
268
- function _objectSpread2(target) {
269
- for (var i = 1; i < arguments.length; i++) {
270
- var source = null != arguments[i] ? arguments[i] : {};
271
- i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
272
- _defineProperty(target, key, source[key]);
273
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
274
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
275
- });
180
+ var getSend = res => (status, response) => {
181
+ if (res.raw.headersSent) {
182
+ return;
276
183
  }
277
- return target;
278
- }
279
-
184
+ res.status(status).json(response);
185
+ };
280
186
  class Router {
281
- static handle(server, route, options) {
187
+ static handle(
188
+ // Allow for any server to be passed
189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ server, route, options) {
282
191
  return _asyncToGenerator(function* () {
192
+ var send = getSend(options.res);
283
193
  try {
284
194
  var _yield$route$body$par, _route$body;
285
195
  var ctx = yield server.getContext(options.req, options.res);
@@ -291,7 +201,13 @@ class Router {
291
201
  query,
292
202
  params: options.params
293
203
  });
294
- options.res.status(200).json({
204
+ if (options.res.raw.headersSent) {
205
+ return {
206
+ success: true,
207
+ data: result
208
+ };
209
+ }
210
+ send(200, {
295
211
  success: true,
296
212
  data: result,
297
213
  message: 'OK'
@@ -303,7 +219,7 @@ class Router {
303
219
  } catch (e) {
304
220
  var error = WrappedError.maybe(e);
305
221
  if (error instanceof KaitoError) {
306
- options.res.status(error.status).json({
222
+ send(error.status, {
307
223
  success: false,
308
224
  data: null,
309
225
  message: error.message
@@ -321,7 +237,7 @@ class Router {
321
237
  status: 500,
322
238
  message: 'Internal Server Error'
323
239
  }));
324
- options.res.status(status).json({
240
+ send(status, {
325
241
  success: false,
326
242
  data: null,
327
243
  message
@@ -337,7 +253,6 @@ class Router {
337
253
  })();
338
254
  }
339
255
  constructor(routes) {
340
- _defineProperty(this, "old_add", route => new Router([...this.routes, route]));
341
256
  _defineProperty(this, "add", (method, path, route) => {
342
257
  var merged = _objectSpread2(_objectSpread2({}, typeof route === 'object' ? route : {
343
258
  run: route
@@ -353,7 +268,7 @@ class Router {
353
268
  }));
354
269
  return new Router([...this.routes, ...newRoutes]);
355
270
  });
356
- _defineProperty(this, "toFindMyWay", server => {
271
+ _defineProperty(this, "freeze", server => {
357
272
  var instance = fmw__default["default"]({
358
273
  ignoreTrailingSlash: true,
359
274
  defaultRoute(req, serverResponse) {
@@ -361,7 +276,7 @@ class Router {
361
276
  var _req$url;
362
277
  var res = new KaitoResponse(serverResponse);
363
278
  var message = "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/');
364
- res.status(404).json({
279
+ getSend(res)(404, {
365
280
  success: false,
366
281
  data: null,
367
282
  message
@@ -403,21 +318,158 @@ class Router {
403
318
  }
404
319
  return instance;
405
320
  });
321
+ _defineProperty(this, "method", method => (path, route) => this.add(method, path, route));
322
+ _defineProperty(this, "get", this.method('GET'));
323
+ _defineProperty(this, "post", this.method('POST'));
324
+ _defineProperty(this, "put", this.method('PUT'));
325
+ _defineProperty(this, "patch", this.method('PATCH'));
326
+ _defineProperty(this, "delete", this.method('DELETE'));
327
+ _defineProperty(this, "head", this.method('HEAD'));
328
+ _defineProperty(this, "options", this.method('OPTIONS'));
406
329
  this.routes = routes;
407
330
  }
408
331
 
409
332
  /**
410
333
  * Adds a new route to the router
334
+ * @param method The HTTP method to add a route for
335
+ * @param path The path to add a route for
411
336
  * @param route The route specification to add to this router
412
337
  * @returns A new router with this route added
413
- * @deprecated Use `Router#add` instead
414
338
  */
415
339
  }
416
340
  _defineProperty(Router, "create", () => new Router([]));
417
341
 
342
+ /**
343
+ * @deprecated use `createUtilities` instead
344
+ */
345
+ function createGetContext(callback) {
346
+ return callback;
347
+ }
348
+
349
+ /**
350
+ * A helper function to create typed necessary functions
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * const {router, getContext} = createUtilities(async (req, res) => {
355
+ * // Return context here
356
+ * })
357
+ *
358
+ * const app = router().get('/', async () => "hello");
359
+ *
360
+ * const server = createServer({
361
+ * router: app,
362
+ * getContext,
363
+ * // ...
364
+ * });
365
+ * ```
366
+ */
367
+ function createUtilities(getContext) {
368
+ return {
369
+ getContext,
370
+ router: () => Router.create()
371
+ };
372
+ }
373
+ function getLastEntryInMultiHeaderValue(headerValue) {
374
+ var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
375
+ var lastIndex = normalized.lastIndexOf(',');
376
+ return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
377
+ }
378
+ function getBody(_x) {
379
+ return _getBody.apply(this, arguments);
380
+ }
381
+ function _getBody() {
382
+ _getBody = _asyncToGenerator(function* (req) {
383
+ if (!req.headers['content-type']) {
384
+ return null;
385
+ }
386
+ var buffer = yield getRawBody__default["default"](req.raw);
387
+ var {
388
+ type
389
+ } = contentType.parse(req.headers['content-type']);
390
+ switch (type) {
391
+ case 'application/json':
392
+ {
393
+ return consumers.json(node_stream.Readable.from(buffer));
394
+ }
395
+ default:
396
+ {
397
+ if (process.env.NODE_ENV === 'development') {
398
+ console.warn('[kaito] Unsupported content type:', type);
399
+ console.warn('[kaito] This message is only shown in development mode.');
400
+ }
401
+ return null;
402
+ }
403
+ }
404
+ });
405
+ return _getBody.apply(this, arguments);
406
+ }
407
+
408
+ class KaitoRequest {
409
+ constructor(raw) {
410
+ _defineProperty(this, "_url", null);
411
+ this.raw = raw;
412
+ }
413
+
414
+ /**
415
+ * The full URL of the request, including the protocol, hostname, and path.
416
+ * Note: does not include the query string or hash
417
+ */
418
+ get fullURL() {
419
+ var _this$raw$url;
420
+ return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
421
+ }
422
+
423
+ /**
424
+ * A new URL instance for the full URL of the request.
425
+ */
426
+ get url() {
427
+ if (this._url) {
428
+ return this._url;
429
+ }
430
+ this._url = new URL(this.fullURL);
431
+ return this._url;
432
+ }
433
+
434
+ /**
435
+ * The HTTP method of the request.
436
+ */
437
+ get method() {
438
+ if (!this.raw.method) {
439
+ throw new Error('Request method is not defined, somehow...');
440
+ }
441
+ return this.raw.method;
442
+ }
443
+
444
+ /**
445
+ * The protocol of the request, either `http` or `https`.
446
+ */
447
+ get protocol() {
448
+ if (this.raw.socket instanceof node_tls.TLSSocket) {
449
+ return this.raw.socket.encrypted ? 'https' : 'http';
450
+ }
451
+ return 'http';
452
+ }
453
+
454
+ /**
455
+ * The request headers
456
+ */
457
+ get headers() {
458
+ return this.raw.headers;
459
+ }
460
+
461
+ /**
462
+ * The hostname of the request.
463
+ */
464
+ get hostname() {
465
+ var _this$raw$headers$hos, _this$raw$headers$Au;
466
+ return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
467
+ }
468
+ }
469
+
418
470
  function createFMWServer(config) {
419
471
  var _config$rawRoutes;
420
- var fmw = config.router.toFindMyWay(config);
472
+ var router = config.router.freeze(config);
421
473
  var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
422
474
  for (var method in rawRoutes) {
423
475
  if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
@@ -429,10 +481,10 @@ function createFMWServer(config) {
429
481
  }
430
482
  for (var route of routes) {
431
483
  if (method === '*') {
432
- fmw.all(route.path, route.handler);
484
+ router.all(route.path, route.handler);
433
485
  continue;
434
486
  }
435
- fmw[method.toLowerCase()](route.path, route.handler);
487
+ router[method.toLowerCase()](route.path, route.handler);
436
488
  }
437
489
  }
438
490
  var server = http__namespace.createServer( /*#__PURE__*/function () {
@@ -448,7 +500,7 @@ function createFMWServer(config) {
448
500
  if (res.headersSent) {
449
501
  return;
450
502
  }
451
- var result = yield fmw.lookup(req, res);
503
+ var result = yield router.lookup(req, res);
452
504
  if ('after' in config && config.after) {
453
505
  yield config.after(before, result);
454
506
  }
@@ -459,7 +511,7 @@ function createFMWServer(config) {
459
511
  }());
460
512
  return {
461
513
  server,
462
- fmw
514
+ fmw: router
463
515
  };
464
516
  }
465
517
  function createServer(config) {
@@ -474,5 +526,6 @@ exports.WrappedError = WrappedError;
474
526
  exports.createFMWServer = createFMWServer;
475
527
  exports.createGetContext = createGetContext;
476
528
  exports.createServer = createServer;
529
+ exports.createUtilities = createUtilities;
477
530
  exports.getBody = getBody;
478
531
  exports.getLastEntryInMultiHeaderValue = getLastEntryInMultiHeaderValue;
@@ -7,9 +7,9 @@ var contentType = require('content-type');
7
7
  var node_stream = require('node:stream');
8
8
  var consumers = require('node:stream/consumers');
9
9
  var getRawBody = require('raw-body');
10
- var cookie = require('cookie');
11
10
  var fmw = require('find-my-way');
12
11
  var zod = require('zod');
12
+ var cookie = require('cookie');
13
13
  var http = require('node:http');
14
14
 
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -103,100 +103,26 @@ function _asyncToGenerator(fn) {
103
103
  };
104
104
  }
105
105
 
106
- function createGetContext(callback) {
107
- return callback;
108
- }
109
- function getLastEntryInMultiHeaderValue(headerValue) {
110
- var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
111
- var lastIndex = normalized.lastIndexOf(',');
112
- return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
113
- }
114
- function getBody(_x) {
115
- return _getBody.apply(this, arguments);
116
- }
117
- function _getBody() {
118
- _getBody = _asyncToGenerator(function* (req) {
119
- if (!req.headers['content-type']) {
120
- return null;
121
- }
122
- var buffer = yield getRawBody__default["default"](req.raw);
123
- var {
124
- type
125
- } = contentType.parse(req.headers['content-type']);
126
- switch (type) {
127
- case 'application/json':
128
- {
129
- return consumers.json(node_stream.Readable.from(buffer));
130
- }
131
- default:
132
- {
133
- return null;
134
- }
135
- }
136
- });
137
- return _getBody.apply(this, arguments);
138
- }
139
-
140
- class KaitoRequest {
141
- constructor(raw) {
142
- _defineProperty(this, "_url", null);
143
- this.raw = raw;
144
- }
145
-
146
- /**
147
- * The full URL of the request, including the protocol, hostname, and path.
148
- * Note: does not include the query string or hash
149
- */
150
- get fullURL() {
151
- var _this$raw$url;
152
- return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
153
- }
154
-
155
- /**
156
- * A new URL instance for the full URL of the request.
157
- */
158
- get url() {
159
- if (this._url) {
160
- return this._url;
161
- }
162
- this._url = new URL(this.fullURL);
163
- return this._url;
164
- }
165
-
166
- /**
167
- * The HTTP method of the request.
168
- */
169
- get method() {
170
- if (!this.raw.method) {
171
- throw new Error('Request method is not defined, somehow...');
172
- }
173
- return this.raw.method;
174
- }
175
-
176
- /**
177
- * The protocol of the request, either `http` or `https`.
178
- */
179
- get protocol() {
180
- if (this.raw.socket instanceof node_tls.TLSSocket) {
181
- return this.raw.socket.encrypted ? 'https' : 'http';
182
- }
183
- return 'http';
184
- }
185
-
186
- /**
187
- * The request headers
188
- */
189
- get headers() {
190
- return this.raw.headers;
106
+ function ownKeys(object, enumerableOnly) {
107
+ var keys = Object.keys(object);
108
+ if (Object.getOwnPropertySymbols) {
109
+ var symbols = Object.getOwnPropertySymbols(object);
110
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
111
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
112
+ })), keys.push.apply(keys, symbols);
191
113
  }
192
-
193
- /**
194
- * The hostname of the request.
195
- */
196
- get hostname() {
197
- var _this$raw$headers$hos, _this$raw$headers$Au;
198
- return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
114
+ return keys;
115
+ }
116
+ function _objectSpread2(target) {
117
+ for (var i = 1; i < arguments.length; i++) {
118
+ var source = null != arguments[i] ? arguments[i] : {};
119
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
120
+ _defineProperty(target, key, source[key]);
121
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
122
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
123
+ });
199
124
  }
125
+ return target;
200
126
  }
201
127
 
202
128
  class KaitoResponse {
@@ -251,31 +177,19 @@ class KaitoResponse {
251
177
  }
252
178
  }
253
179
 
254
- function ownKeys(object, enumerableOnly) {
255
- var keys = Object.keys(object);
256
- if (Object.getOwnPropertySymbols) {
257
- var symbols = Object.getOwnPropertySymbols(object);
258
- enumerableOnly && (symbols = symbols.filter(function (sym) {
259
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
260
- })), keys.push.apply(keys, symbols);
261
- }
262
- return keys;
263
- }
264
- function _objectSpread2(target) {
265
- for (var i = 1; i < arguments.length; i++) {
266
- var source = null != arguments[i] ? arguments[i] : {};
267
- i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
268
- _defineProperty(target, key, source[key]);
269
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
270
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
271
- });
180
+ var getSend = res => (status, response) => {
181
+ if (res.raw.headersSent) {
182
+ return;
272
183
  }
273
- return target;
274
- }
275
-
184
+ res.status(status).json(response);
185
+ };
276
186
  class Router {
277
- static handle(server, route, options) {
187
+ static handle(
188
+ // Allow for any server to be passed
189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ server, route, options) {
278
191
  return _asyncToGenerator(function* () {
192
+ var send = getSend(options.res);
279
193
  try {
280
194
  var _yield$route$body$par, _route$body;
281
195
  var ctx = yield server.getContext(options.req, options.res);
@@ -287,7 +201,13 @@ class Router {
287
201
  query,
288
202
  params: options.params
289
203
  });
290
- options.res.status(200).json({
204
+ if (options.res.raw.headersSent) {
205
+ return {
206
+ success: true,
207
+ data: result
208
+ };
209
+ }
210
+ send(200, {
291
211
  success: true,
292
212
  data: result,
293
213
  message: 'OK'
@@ -299,7 +219,7 @@ class Router {
299
219
  } catch (e) {
300
220
  var error = WrappedError.maybe(e);
301
221
  if (error instanceof KaitoError) {
302
- options.res.status(error.status).json({
222
+ send(error.status, {
303
223
  success: false,
304
224
  data: null,
305
225
  message: error.message
@@ -317,7 +237,7 @@ class Router {
317
237
  status: 500,
318
238
  message: 'Internal Server Error'
319
239
  }));
320
- options.res.status(status).json({
240
+ send(status, {
321
241
  success: false,
322
242
  data: null,
323
243
  message
@@ -333,7 +253,6 @@ class Router {
333
253
  })();
334
254
  }
335
255
  constructor(routes) {
336
- _defineProperty(this, "old_add", route => new Router([...this.routes, route]));
337
256
  _defineProperty(this, "add", (method, path, route) => {
338
257
  var merged = _objectSpread2(_objectSpread2({}, typeof route === 'object' ? route : {
339
258
  run: route
@@ -349,7 +268,7 @@ class Router {
349
268
  }));
350
269
  return new Router([...this.routes, ...newRoutes]);
351
270
  });
352
- _defineProperty(this, "toFindMyWay", server => {
271
+ _defineProperty(this, "freeze", server => {
353
272
  var instance = fmw__default["default"]({
354
273
  ignoreTrailingSlash: true,
355
274
  defaultRoute(req, serverResponse) {
@@ -357,7 +276,7 @@ class Router {
357
276
  var _req$url;
358
277
  var res = new KaitoResponse(serverResponse);
359
278
  var message = "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/');
360
- res.status(404).json({
279
+ getSend(res)(404, {
361
280
  success: false,
362
281
  data: null,
363
282
  message
@@ -399,21 +318,154 @@ class Router {
399
318
  }
400
319
  return instance;
401
320
  });
321
+ _defineProperty(this, "method", method => (path, route) => this.add(method, path, route));
322
+ _defineProperty(this, "get", this.method('GET'));
323
+ _defineProperty(this, "post", this.method('POST'));
324
+ _defineProperty(this, "put", this.method('PUT'));
325
+ _defineProperty(this, "patch", this.method('PATCH'));
326
+ _defineProperty(this, "delete", this.method('DELETE'));
327
+ _defineProperty(this, "head", this.method('HEAD'));
328
+ _defineProperty(this, "options", this.method('OPTIONS'));
402
329
  this.routes = routes;
403
330
  }
404
331
 
405
332
  /**
406
333
  * Adds a new route to the router
334
+ * @param method The HTTP method to add a route for
335
+ * @param path The path to add a route for
407
336
  * @param route The route specification to add to this router
408
337
  * @returns A new router with this route added
409
- * @deprecated Use `Router#add` instead
410
338
  */
411
339
  }
412
340
  _defineProperty(Router, "create", () => new Router([]));
413
341
 
342
+ /**
343
+ * @deprecated use `createUtilities` instead
344
+ */
345
+ function createGetContext(callback) {
346
+ return callback;
347
+ }
348
+
349
+ /**
350
+ * A helper function to create typed necessary functions
351
+ *
352
+ * @example
353
+ * ```ts
354
+ * const {router, getContext} = createUtilities(async (req, res) => {
355
+ * // Return context here
356
+ * })
357
+ *
358
+ * const app = router().get('/', async () => "hello");
359
+ *
360
+ * const server = createServer({
361
+ * router: app,
362
+ * getContext,
363
+ * // ...
364
+ * });
365
+ * ```
366
+ */
367
+ function createUtilities(getContext) {
368
+ return {
369
+ getContext,
370
+ router: () => Router.create()
371
+ };
372
+ }
373
+ function getLastEntryInMultiHeaderValue(headerValue) {
374
+ var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
375
+ var lastIndex = normalized.lastIndexOf(',');
376
+ return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
377
+ }
378
+ function getBody(_x) {
379
+ return _getBody.apply(this, arguments);
380
+ }
381
+ function _getBody() {
382
+ _getBody = _asyncToGenerator(function* (req) {
383
+ if (!req.headers['content-type']) {
384
+ return null;
385
+ }
386
+ var buffer = yield getRawBody__default["default"](req.raw);
387
+ var {
388
+ type
389
+ } = contentType.parse(req.headers['content-type']);
390
+ switch (type) {
391
+ case 'application/json':
392
+ {
393
+ return consumers.json(node_stream.Readable.from(buffer));
394
+ }
395
+ default:
396
+ {
397
+ return null;
398
+ }
399
+ }
400
+ });
401
+ return _getBody.apply(this, arguments);
402
+ }
403
+
404
+ class KaitoRequest {
405
+ constructor(raw) {
406
+ _defineProperty(this, "_url", null);
407
+ this.raw = raw;
408
+ }
409
+
410
+ /**
411
+ * The full URL of the request, including the protocol, hostname, and path.
412
+ * Note: does not include the query string or hash
413
+ */
414
+ get fullURL() {
415
+ var _this$raw$url;
416
+ return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
417
+ }
418
+
419
+ /**
420
+ * A new URL instance for the full URL of the request.
421
+ */
422
+ get url() {
423
+ if (this._url) {
424
+ return this._url;
425
+ }
426
+ this._url = new URL(this.fullURL);
427
+ return this._url;
428
+ }
429
+
430
+ /**
431
+ * The HTTP method of the request.
432
+ */
433
+ get method() {
434
+ if (!this.raw.method) {
435
+ throw new Error('Request method is not defined, somehow...');
436
+ }
437
+ return this.raw.method;
438
+ }
439
+
440
+ /**
441
+ * The protocol of the request, either `http` or `https`.
442
+ */
443
+ get protocol() {
444
+ if (this.raw.socket instanceof node_tls.TLSSocket) {
445
+ return this.raw.socket.encrypted ? 'https' : 'http';
446
+ }
447
+ return 'http';
448
+ }
449
+
450
+ /**
451
+ * The request headers
452
+ */
453
+ get headers() {
454
+ return this.raw.headers;
455
+ }
456
+
457
+ /**
458
+ * The hostname of the request.
459
+ */
460
+ get hostname() {
461
+ var _this$raw$headers$hos, _this$raw$headers$Au;
462
+ return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
463
+ }
464
+ }
465
+
414
466
  function createFMWServer(config) {
415
467
  var _config$rawRoutes;
416
- var fmw = config.router.toFindMyWay(config);
468
+ var router = config.router.freeze(config);
417
469
  var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
418
470
  for (var method in rawRoutes) {
419
471
  if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
@@ -425,10 +477,10 @@ function createFMWServer(config) {
425
477
  }
426
478
  for (var route of routes) {
427
479
  if (method === '*') {
428
- fmw.all(route.path, route.handler);
480
+ router.all(route.path, route.handler);
429
481
  continue;
430
482
  }
431
- fmw[method.toLowerCase()](route.path, route.handler);
483
+ router[method.toLowerCase()](route.path, route.handler);
432
484
  }
433
485
  }
434
486
  var server = http__namespace.createServer( /*#__PURE__*/function () {
@@ -444,7 +496,7 @@ function createFMWServer(config) {
444
496
  if (res.headersSent) {
445
497
  return;
446
498
  }
447
- var result = yield fmw.lookup(req, res);
499
+ var result = yield router.lookup(req, res);
448
500
  if ('after' in config && config.after) {
449
501
  yield config.after(before, result);
450
502
  }
@@ -455,7 +507,7 @@ function createFMWServer(config) {
455
507
  }());
456
508
  return {
457
509
  server,
458
- fmw
510
+ fmw: router
459
511
  };
460
512
  }
461
513
  function createServer(config) {
@@ -470,5 +522,6 @@ exports.WrappedError = WrappedError;
470
522
  exports.createFMWServer = createFMWServer;
471
523
  exports.createGetContext = createGetContext;
472
524
  exports.createServer = createServer;
525
+ exports.createUtilities = createUtilities;
473
526
  exports.getBody = getBody;
474
527
  exports.getLastEntryInMultiHeaderValue = getLastEntryInMultiHeaderValue;
@@ -3,9 +3,9 @@ import { parse } from 'content-type';
3
3
  import { Readable } from 'node:stream';
4
4
  import { json } from 'node:stream/consumers';
5
5
  import getRawBody from 'raw-body';
6
- import { serialize } from 'cookie';
7
6
  import fmw from 'find-my-way';
8
7
  import { z } from 'zod';
8
+ import { serialize } from 'cookie';
9
9
  import * as http from 'node:http';
10
10
 
11
11
  class WrappedError extends Error {
@@ -75,104 +75,26 @@ function _asyncToGenerator(fn) {
75
75
  };
76
76
  }
77
77
 
78
- function createGetContext(callback) {
79
- return callback;
80
- }
81
- function getLastEntryInMultiHeaderValue(headerValue) {
82
- var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
83
- var lastIndex = normalized.lastIndexOf(',');
84
- return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
85
- }
86
- function getBody(_x) {
87
- return _getBody.apply(this, arguments);
88
- }
89
- function _getBody() {
90
- _getBody = _asyncToGenerator(function* (req) {
91
- if (!req.headers['content-type']) {
92
- return null;
93
- }
94
- var buffer = yield getRawBody(req.raw);
95
- var {
96
- type
97
- } = parse(req.headers['content-type']);
98
- switch (type) {
99
- case 'application/json':
100
- {
101
- return json(Readable.from(buffer));
102
- }
103
- default:
104
- {
105
- if (process.env.NODE_ENV === 'development') {
106
- console.warn('[kaito] Unsupported content type:', type);
107
- console.warn('[kaito] This message is only shown in development mode.');
108
- }
109
- return null;
110
- }
111
- }
112
- });
113
- return _getBody.apply(this, arguments);
114
- }
115
-
116
- class KaitoRequest {
117
- constructor(raw) {
118
- _defineProperty(this, "_url", null);
119
- this.raw = raw;
120
- }
121
-
122
- /**
123
- * The full URL of the request, including the protocol, hostname, and path.
124
- * Note: does not include the query string or hash
125
- */
126
- get fullURL() {
127
- var _this$raw$url;
128
- return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
129
- }
130
-
131
- /**
132
- * A new URL instance for the full URL of the request.
133
- */
134
- get url() {
135
- if (this._url) {
136
- return this._url;
137
- }
138
- this._url = new URL(this.fullURL);
139
- return this._url;
140
- }
141
-
142
- /**
143
- * The HTTP method of the request.
144
- */
145
- get method() {
146
- if (!this.raw.method) {
147
- throw new Error('Request method is not defined, somehow...');
148
- }
149
- return this.raw.method;
150
- }
151
-
152
- /**
153
- * The protocol of the request, either `http` or `https`.
154
- */
155
- get protocol() {
156
- if (this.raw.socket instanceof TLSSocket) {
157
- return this.raw.socket.encrypted ? 'https' : 'http';
158
- }
159
- return 'http';
160
- }
161
-
162
- /**
163
- * The request headers
164
- */
165
- get headers() {
166
- return this.raw.headers;
78
+ function ownKeys(object, enumerableOnly) {
79
+ var keys = Object.keys(object);
80
+ if (Object.getOwnPropertySymbols) {
81
+ var symbols = Object.getOwnPropertySymbols(object);
82
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
83
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
84
+ })), keys.push.apply(keys, symbols);
167
85
  }
168
-
169
- /**
170
- * The hostname of the request.
171
- */
172
- get hostname() {
173
- var _this$raw$headers$hos, _this$raw$headers$Au;
174
- return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
86
+ return keys;
87
+ }
88
+ function _objectSpread2(target) {
89
+ for (var i = 1; i < arguments.length; i++) {
90
+ var source = null != arguments[i] ? arguments[i] : {};
91
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
92
+ _defineProperty(target, key, source[key]);
93
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
94
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
95
+ });
175
96
  }
97
+ return target;
176
98
  }
177
99
 
178
100
  class KaitoResponse {
@@ -227,31 +149,19 @@ class KaitoResponse {
227
149
  }
228
150
  }
229
151
 
230
- function ownKeys(object, enumerableOnly) {
231
- var keys = Object.keys(object);
232
- if (Object.getOwnPropertySymbols) {
233
- var symbols = Object.getOwnPropertySymbols(object);
234
- enumerableOnly && (symbols = symbols.filter(function (sym) {
235
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
236
- })), keys.push.apply(keys, symbols);
237
- }
238
- return keys;
239
- }
240
- function _objectSpread2(target) {
241
- for (var i = 1; i < arguments.length; i++) {
242
- var source = null != arguments[i] ? arguments[i] : {};
243
- i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
244
- _defineProperty(target, key, source[key]);
245
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
246
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
247
- });
152
+ var getSend = res => (status, response) => {
153
+ if (res.raw.headersSent) {
154
+ return;
248
155
  }
249
- return target;
250
- }
251
-
156
+ res.status(status).json(response);
157
+ };
252
158
  class Router {
253
- static handle(server, route, options) {
159
+ static handle(
160
+ // Allow for any server to be passed
161
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
+ server, route, options) {
254
163
  return _asyncToGenerator(function* () {
164
+ var send = getSend(options.res);
255
165
  try {
256
166
  var _yield$route$body$par, _route$body;
257
167
  var ctx = yield server.getContext(options.req, options.res);
@@ -263,7 +173,13 @@ class Router {
263
173
  query,
264
174
  params: options.params
265
175
  });
266
- options.res.status(200).json({
176
+ if (options.res.raw.headersSent) {
177
+ return {
178
+ success: true,
179
+ data: result
180
+ };
181
+ }
182
+ send(200, {
267
183
  success: true,
268
184
  data: result,
269
185
  message: 'OK'
@@ -275,7 +191,7 @@ class Router {
275
191
  } catch (e) {
276
192
  var error = WrappedError.maybe(e);
277
193
  if (error instanceof KaitoError) {
278
- options.res.status(error.status).json({
194
+ send(error.status, {
279
195
  success: false,
280
196
  data: null,
281
197
  message: error.message
@@ -293,7 +209,7 @@ class Router {
293
209
  status: 500,
294
210
  message: 'Internal Server Error'
295
211
  }));
296
- options.res.status(status).json({
212
+ send(status, {
297
213
  success: false,
298
214
  data: null,
299
215
  message
@@ -309,7 +225,6 @@ class Router {
309
225
  })();
310
226
  }
311
227
  constructor(routes) {
312
- _defineProperty(this, "old_add", route => new Router([...this.routes, route]));
313
228
  _defineProperty(this, "add", (method, path, route) => {
314
229
  var merged = _objectSpread2(_objectSpread2({}, typeof route === 'object' ? route : {
315
230
  run: route
@@ -325,7 +240,7 @@ class Router {
325
240
  }));
326
241
  return new Router([...this.routes, ...newRoutes]);
327
242
  });
328
- _defineProperty(this, "toFindMyWay", server => {
243
+ _defineProperty(this, "freeze", server => {
329
244
  var instance = fmw({
330
245
  ignoreTrailingSlash: true,
331
246
  defaultRoute(req, serverResponse) {
@@ -333,7 +248,7 @@ class Router {
333
248
  var _req$url;
334
249
  var res = new KaitoResponse(serverResponse);
335
250
  var message = "Cannot ".concat(req.method, " ").concat((_req$url = req.url) !== null && _req$url !== void 0 ? _req$url : '/');
336
- res.status(404).json({
251
+ getSend(res)(404, {
337
252
  success: false,
338
253
  data: null,
339
254
  message
@@ -375,21 +290,158 @@ class Router {
375
290
  }
376
291
  return instance;
377
292
  });
293
+ _defineProperty(this, "method", method => (path, route) => this.add(method, path, route));
294
+ _defineProperty(this, "get", this.method('GET'));
295
+ _defineProperty(this, "post", this.method('POST'));
296
+ _defineProperty(this, "put", this.method('PUT'));
297
+ _defineProperty(this, "patch", this.method('PATCH'));
298
+ _defineProperty(this, "delete", this.method('DELETE'));
299
+ _defineProperty(this, "head", this.method('HEAD'));
300
+ _defineProperty(this, "options", this.method('OPTIONS'));
378
301
  this.routes = routes;
379
302
  }
380
303
 
381
304
  /**
382
305
  * Adds a new route to the router
306
+ * @param method The HTTP method to add a route for
307
+ * @param path The path to add a route for
383
308
  * @param route The route specification to add to this router
384
309
  * @returns A new router with this route added
385
- * @deprecated Use `Router#add` instead
386
310
  */
387
311
  }
388
312
  _defineProperty(Router, "create", () => new Router([]));
389
313
 
314
+ /**
315
+ * @deprecated use `createUtilities` instead
316
+ */
317
+ function createGetContext(callback) {
318
+ return callback;
319
+ }
320
+
321
+ /**
322
+ * A helper function to create typed necessary functions
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * const {router, getContext} = createUtilities(async (req, res) => {
327
+ * // Return context here
328
+ * })
329
+ *
330
+ * const app = router().get('/', async () => "hello");
331
+ *
332
+ * const server = createServer({
333
+ * router: app,
334
+ * getContext,
335
+ * // ...
336
+ * });
337
+ * ```
338
+ */
339
+ function createUtilities(getContext) {
340
+ return {
341
+ getContext,
342
+ router: () => Router.create()
343
+ };
344
+ }
345
+ function getLastEntryInMultiHeaderValue(headerValue) {
346
+ var normalized = Array.isArray(headerValue) ? headerValue.join(',') : headerValue;
347
+ var lastIndex = normalized.lastIndexOf(',');
348
+ return lastIndex === -1 ? normalized.trim() : normalized.slice(lastIndex + 1).trim();
349
+ }
350
+ function getBody(_x) {
351
+ return _getBody.apply(this, arguments);
352
+ }
353
+ function _getBody() {
354
+ _getBody = _asyncToGenerator(function* (req) {
355
+ if (!req.headers['content-type']) {
356
+ return null;
357
+ }
358
+ var buffer = yield getRawBody(req.raw);
359
+ var {
360
+ type
361
+ } = parse(req.headers['content-type']);
362
+ switch (type) {
363
+ case 'application/json':
364
+ {
365
+ return json(Readable.from(buffer));
366
+ }
367
+ default:
368
+ {
369
+ if (process.env.NODE_ENV === 'development') {
370
+ console.warn('[kaito] Unsupported content type:', type);
371
+ console.warn('[kaito] This message is only shown in development mode.');
372
+ }
373
+ return null;
374
+ }
375
+ }
376
+ });
377
+ return _getBody.apply(this, arguments);
378
+ }
379
+
380
+ class KaitoRequest {
381
+ constructor(raw) {
382
+ _defineProperty(this, "_url", null);
383
+ this.raw = raw;
384
+ }
385
+
386
+ /**
387
+ * The full URL of the request, including the protocol, hostname, and path.
388
+ * Note: does not include the query string or hash
389
+ */
390
+ get fullURL() {
391
+ var _this$raw$url;
392
+ return "".concat(this.protocol, "://").concat(this.hostname).concat((_this$raw$url = this.raw.url) !== null && _this$raw$url !== void 0 ? _this$raw$url : '');
393
+ }
394
+
395
+ /**
396
+ * A new URL instance for the full URL of the request.
397
+ */
398
+ get url() {
399
+ if (this._url) {
400
+ return this._url;
401
+ }
402
+ this._url = new URL(this.fullURL);
403
+ return this._url;
404
+ }
405
+
406
+ /**
407
+ * The HTTP method of the request.
408
+ */
409
+ get method() {
410
+ if (!this.raw.method) {
411
+ throw new Error('Request method is not defined, somehow...');
412
+ }
413
+ return this.raw.method;
414
+ }
415
+
416
+ /**
417
+ * The protocol of the request, either `http` or `https`.
418
+ */
419
+ get protocol() {
420
+ if (this.raw.socket instanceof TLSSocket) {
421
+ return this.raw.socket.encrypted ? 'https' : 'http';
422
+ }
423
+ return 'http';
424
+ }
425
+
426
+ /**
427
+ * The request headers
428
+ */
429
+ get headers() {
430
+ return this.raw.headers;
431
+ }
432
+
433
+ /**
434
+ * The hostname of the request.
435
+ */
436
+ get hostname() {
437
+ var _this$raw$headers$hos, _this$raw$headers$Au;
438
+ return (_this$raw$headers$hos = this.raw.headers.host) !== null && _this$raw$headers$hos !== void 0 ? _this$raw$headers$hos : getLastEntryInMultiHeaderValue((_this$raw$headers$Au = this.raw.headers[':authority']) !== null && _this$raw$headers$Au !== void 0 ? _this$raw$headers$Au : []);
439
+ }
440
+ }
441
+
390
442
  function createFMWServer(config) {
391
443
  var _config$rawRoutes;
392
- var fmw = config.router.toFindMyWay(config);
444
+ var router = config.router.freeze(config);
393
445
  var rawRoutes = (_config$rawRoutes = config.rawRoutes) !== null && _config$rawRoutes !== void 0 ? _config$rawRoutes : {};
394
446
  for (var method in rawRoutes) {
395
447
  if (!Object.prototype.hasOwnProperty.call(rawRoutes, method)) {
@@ -401,10 +453,10 @@ function createFMWServer(config) {
401
453
  }
402
454
  for (var route of routes) {
403
455
  if (method === '*') {
404
- fmw.all(route.path, route.handler);
456
+ router.all(route.path, route.handler);
405
457
  continue;
406
458
  }
407
- fmw[method.toLowerCase()](route.path, route.handler);
459
+ router[method.toLowerCase()](route.path, route.handler);
408
460
  }
409
461
  }
410
462
  var server = http.createServer( /*#__PURE__*/function () {
@@ -420,7 +472,7 @@ function createFMWServer(config) {
420
472
  if (res.headersSent) {
421
473
  return;
422
474
  }
423
- var result = yield fmw.lookup(req, res);
475
+ var result = yield router.lookup(req, res);
424
476
  if ('after' in config && config.after) {
425
477
  yield config.after(before, result);
426
478
  }
@@ -431,11 +483,11 @@ function createFMWServer(config) {
431
483
  }());
432
484
  return {
433
485
  server,
434
- fmw
486
+ fmw: router
435
487
  };
436
488
  }
437
489
  function createServer(config) {
438
490
  return createFMWServer(config).server;
439
491
  }
440
492
 
441
- export { KaitoError, KaitoRequest, KaitoResponse, Router, WrappedError, createFMWServer, createGetContext, createServer, getBody, getLastEntryInMultiHeaderValue };
493
+ export { KaitoError, KaitoRequest, KaitoResponse, Router, WrappedError, createFMWServer, createGetContext, createServer, createUtilities, getBody, getLastEntryInMultiHeaderValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "2.7.1",
3
+ "version": "2.8.0",
4
4
  "description": "Functional HTTP Framework for TypeScript",
5
5
  "repository": "https://github.com/kaito-http/kaito",
6
6
  "author": "Alistair Smith <hi@alistair.sh>",