@kaito-http/core 2.1.0 → 2.2.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.
@@ -1,5 +1,6 @@
1
1
  export declare class WrappedError<T> extends Error {
2
2
  readonly data: T;
3
- static from<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
- constructor(data: T);
3
+ static maybe<T>(maybeError: T): (T & Error) | WrappedError<T>;
4
+ static from<T>(data: T): WrappedError<T>;
5
+ private constructor();
5
6
  }
@@ -13,135 +13,115 @@ export declare type ContextWithInput<Ctx, Input> = {
13
13
  input: Input;
14
14
  };
15
15
  declare type Values<T> = T[keyof T];
16
- declare type Proc<Ctx, Result, Input extends z.ZodTypeAny | Never = Never> = Readonly<{
16
+ export declare type Proc<Ctx, Result, Input extends z.ZodTypeAny | Never = Never> = Readonly<{
17
17
  input?: Input;
18
18
  run(arg: ContextWithInput<Ctx, Input extends ZodTypeAny ? z.infer<Input> : undefined>): Promise<Result>;
19
19
  }>;
20
- declare type ProcsInit<Ctx> = {
21
- [Key in string]: Proc<Ctx, unknown, z.ZodTypeAny> & {
22
- method: Method;
23
- name: Key;
24
- };
20
+ export interface RouterProc<Path extends string = string, M extends Method = Method> {
21
+ method: M;
22
+ path: Path;
23
+ pattern: RegExp;
24
+ }
25
+ export declare type AnyProcs<Ctx> = {
26
+ [Path in string]: Proc<Ctx, unknown, z.ZodTypeAny> & RouterProc<Path>;
25
27
  };
26
- declare type AnyRouter<Ctx> = Router<Ctx, ProcsInit<Ctx>>;
27
- export declare class Router<Ctx, Procs extends ProcsInit<Ctx>> {
28
+ export declare type AnyRouter<Ctx> = Router<Ctx, AnyProcs<Ctx>>;
29
+ export declare class Router<Ctx, Procs extends AnyProcs<Ctx>> {
28
30
  private readonly procs;
31
+ private readonly _procsArray;
32
+ /**
33
+ * Ensures that the path does not start or end with a slash.
34
+ * @param path
35
+ * @private
36
+ */
37
+ private static stripSlashes;
29
38
  constructor(procs: Procs);
30
39
  getProcs(): Procs;
40
+ find(method: Method, url: string): (Readonly<{
41
+ input?: z.ZodTypeAny | undefined;
42
+ run(arg: ContextWithInput<Ctx, any>): Promise<unknown>;
43
+ }> & RouterProc<string, Method>) | null;
31
44
  private readonly create;
32
- readonly merge: <Prefix extends string, NewCtx, NewProcs extends ProcsInit<NewCtx>>(prefix: Prefix, router: Router<NewCtx, NewProcs>) => Router<NewCtx & Ctx, Procs & { [Key in `${Prefix}${Extract<keyof NewProcs, string>}`]: Omit<NewProcs[Key extends `${Prefix}${infer Rest}` ? Rest : never], "name"> & {
33
- name: Key;
45
+ readonly merge: <Prefix extends string, NewCtx, NewProcs extends AnyProcs<NewCtx>>(prefix: Prefix, router: Router<NewCtx, NewProcs>) => Router<NewCtx & Ctx, Procs & { [P in `${Prefix}${Extract<keyof NewProcs, string>}`]: Omit<NewProcs[P extends `${Prefix}${infer Rest}` ? Rest : never], "path"> & {
46
+ path: P;
34
47
  }; }>;
35
- readonly get: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
48
+ readonly get: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
36
49
  input?: Input | undefined;
37
50
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
38
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
51
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
39
52
  input?: Input | undefined;
40
53
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
41
- }> & {
42
- method: "GET";
43
- name: Name;
44
- }>>;
45
- readonly post: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
54
+ }> & RouterProc<Path, "GET">>>;
55
+ readonly post: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
46
56
  input?: Input | undefined;
47
57
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
48
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
58
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
49
59
  input?: Input | undefined;
50
60
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
51
- }> & {
52
- method: "POST";
53
- name: Name;
54
- }>>;
55
- readonly put: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
61
+ }> & RouterProc<Path, "POST">>>;
62
+ readonly put: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
56
63
  input?: Input | undefined;
57
64
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
58
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
65
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
59
66
  input?: Input | undefined;
60
67
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
61
- }> & {
62
- method: "PUT";
63
- name: Name;
64
- }>>;
65
- readonly patch: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
68
+ }> & RouterProc<Path, "PUT">>>;
69
+ readonly patch: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
66
70
  input?: Input | undefined;
67
71
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
68
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
72
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
69
73
  input?: Input | undefined;
70
74
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
71
- }> & {
72
- method: "PATCH";
73
- name: Name;
74
- }>>;
75
- readonly delete: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
75
+ }> & RouterProc<Path, "PATCH">>>;
76
+ readonly delete: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
76
77
  input?: Input | undefined;
77
78
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
78
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
79
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
79
80
  input?: Input | undefined;
80
81
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
81
- }> & {
82
- method: "DELETE";
83
- name: Name;
84
- }>>;
85
- readonly head: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
82
+ }> & RouterProc<Path, "DELETE">>>;
83
+ readonly head: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
86
84
  input?: Input | undefined;
87
85
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
88
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
86
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
89
87
  input?: Input | undefined;
90
88
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
91
- }> & {
92
- method: "HEAD";
93
- name: Name;
94
- }>>;
95
- readonly options: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
89
+ }> & RouterProc<Path, "HEAD">>>;
90
+ readonly options: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
96
91
  input?: Input | undefined;
97
92
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
98
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
93
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
99
94
  input?: Input | undefined;
100
95
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
101
- }> & {
102
- method: "OPTIONS";
103
- name: Name;
104
- }>>;
105
- readonly connect: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
96
+ }> & RouterProc<Path, "OPTIONS">>>;
97
+ readonly connect: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
106
98
  input?: Input | undefined;
107
99
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
108
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
100
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
109
101
  input?: Input | undefined;
110
102
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
111
- }> & {
112
- method: "CONNECT";
113
- name: Name;
114
- }>>;
115
- readonly trace: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
103
+ }> & RouterProc<Path, "CONNECT">>>;
104
+ readonly trace: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
116
105
  input?: Input | undefined;
117
106
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
118
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
107
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
119
108
  input?: Input | undefined;
120
109
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
121
- }> & {
122
- method: "TRACE";
123
- name: Name;
124
- }>>;
125
- readonly acl: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
110
+ }> & RouterProc<Path, "TRACE">>>;
111
+ readonly acl: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
126
112
  input?: Input | undefined;
127
113
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
128
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
114
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
129
115
  input?: Input | undefined;
130
116
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
131
- }> & {
132
- method: "ACL";
133
- name: Name;
134
- }>>;
135
- readonly bind: <Name extends string, Result, Input extends z.ZodTypeAny>(name: Name, proc: Readonly<{
117
+ }> & RouterProc<Path, "ACL">>>;
118
+ readonly bind: <Path extends string, Result, Input extends z.ZodTypeAny>(path: Path, proc: Readonly<{
136
119
  input?: Input | undefined;
137
120
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
138
- }>) => Router<Ctx, Procs & Record<Name, Readonly<{
121
+ }>) => Router<Ctx, Procs & Record<Path, Readonly<{
139
122
  input?: Input | undefined;
140
123
  run(arg: ContextWithInput<Ctx, Input extends z.ZodTypeAny ? z.TypeOf<Input> : undefined>): Promise<Result>;
141
- }> & {
142
- method: "BIND";
143
- name: Name;
144
- }>>;
124
+ }> & RouterProc<Path, "BIND">>>;
145
125
  }
146
126
  export declare class KaitoError extends Error {
147
127
  readonly status: number;
@@ -151,8 +131,8 @@ export declare class KaitoError extends Error {
151
131
  export declare function createRouter<Ctx>(): Router<Ctx, {}>;
152
132
  export declare type InferAPIResponseType<R extends AnyRouter<unknown>, M extends Method, Path extends Extract<Values<ReturnType<R['getProcs']>>, {
153
133
  method: M;
154
- }>['name']> = ReturnType<ReturnType<R['getProcs']>[Path]['run']> extends Promise<infer V> ? V : never;
155
- export declare function createServer<Ctx, R extends Router<Ctx, ProcsInit<Ctx>>>(config: {
134
+ }>['path']> = ReturnType<ReturnType<R['getProcs']>[Path]['run']> extends Promise<infer V> ? V : never;
135
+ export declare function createServer<Ctx, R extends Router<Ctx, AnyProcs<Ctx>>>(config: {
156
136
  getContext: GetContext<Ctx>;
157
137
  router: R;
158
138
  onError(error: {
@@ -101,12 +101,16 @@ function _objectSpread2(target) {
101
101
  }
102
102
 
103
103
  class WrappedError extends Error {
104
- static from(maybeError) {
104
+ static maybe(maybeError) {
105
105
  if (maybeError instanceof Error) {
106
106
  return maybeError;
107
107
  }
108
108
 
109
- return new WrappedError(maybeError);
109
+ return WrappedError.from(maybeError);
110
+ }
111
+
112
+ static from(data) {
113
+ return new WrappedError(data);
110
114
  }
111
115
 
112
116
  constructor(data) {
@@ -227,22 +231,45 @@ function createGetContext(getContext) {
227
231
  return getContext;
228
232
  }
229
233
  class Router {
234
+ /**
235
+ * Ensures that the path does not start or end with a slash.
236
+ * @param path
237
+ * @private
238
+ */
239
+ static stripSlashes(path) {
240
+ if (path.startsWith('/')) {
241
+ path = path.slice(1);
242
+ }
243
+
244
+ if (path.endsWith('/')) {
245
+ path = path.slice(-1);
246
+ }
247
+
248
+ return path;
249
+ }
250
+
230
251
  constructor(procs) {
231
- _defineProperty(this, "create", method => (name, proc) => {
232
- return new Router(_objectSpread2(_objectSpread2({}, this.procs), {}, {
233
- [name]: _objectSpread2(_objectSpread2({}, proc), {}, {
252
+ _defineProperty(this, "create", method => (path, proc) => {
253
+ var stripped = Router.stripSlashes(path);
254
+ var pattern = new RegExp("^/".concat(stripped, "/?$"), 'i');
255
+
256
+ var merged = _objectSpread2(_objectSpread2({}, this.procs), {}, {
257
+ [path]: _objectSpread2(_objectSpread2({}, proc), {}, {
234
258
  method,
235
- name
259
+ path,
260
+ pattern
236
261
  })
237
- }));
262
+ });
263
+
264
+ return new Router(merged);
238
265
  });
239
266
 
240
267
  _defineProperty(this, "merge", (prefix, router) => {
241
268
  var newProcs = Object.entries(router.getProcs()).reduce((all, entry) => {
242
- var [name, proc] = entry;
269
+ var [path, proc] = entry;
243
270
  return _objectSpread2(_objectSpread2({}, all), {}, {
244
- ["".concat(prefix).concat(name)]: _objectSpread2(_objectSpread2({}, proc), {}, {
245
- name: "".concat(prefix).concat(name)
271
+ ["".concat(prefix).concat(path)]: _objectSpread2(_objectSpread2({}, proc), {}, {
272
+ path: "".concat(prefix).concat(path)
246
273
  })
247
274
  });
248
275
  }, {});
@@ -275,12 +302,27 @@ class Router {
275
302
  _defineProperty(this, "bind", this.create('BIND'));
276
303
 
277
304
  this.procs = procs;
305
+ this._procsArray = Object.values(procs);
278
306
  }
279
307
 
280
308
  getProcs() {
281
309
  return this.procs;
282
310
  }
283
311
 
312
+ find(method, url) {
313
+ for (var proc of this._procsArray) {
314
+ if (proc.method !== method) {
315
+ continue;
316
+ }
317
+
318
+ if (proc.pattern.test(url)) {
319
+ return proc;
320
+ }
321
+ }
322
+
323
+ return null;
324
+ }
325
+
284
326
  }
285
327
  class KaitoError extends Error {
286
328
  constructor(status, message, cause) {
@@ -302,8 +344,7 @@ function createServer(config) {
302
344
  }
303
345
  };
304
346
 
305
- var tree = config.router.getProcs();
306
- var server = http__default["default"].createServer( /*#__PURE__*/function () {
347
+ return http__default["default"].createServer( /*#__PURE__*/function () {
307
348
  var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse) {
308
349
  var start = Date.now();
309
350
  var req = new KaitoRequest(incomingMessage);
@@ -312,7 +353,7 @@ function createServer(config) {
312
353
  try {
313
354
  var _handler$input, _yield$getInput;
314
355
 
315
- var handler = tree[req.url.pathname];
356
+ var handler = config.router.find(req.method, req.url.pathname);
316
357
 
317
358
  if (!handler) {
318
359
  throw new KaitoError(404, "Cannot ".concat(req.method, " this route."));
@@ -343,7 +384,7 @@ function createServer(config) {
343
384
  status: _status,
344
385
  message: _message
345
386
  } = yield config.onError({
346
- error: WrappedError.from(error),
387
+ error: WrappedError.maybe(error),
347
388
  req,
348
389
  res
349
390
  }).catch(() => ({
@@ -365,7 +406,6 @@ function createServer(config) {
365
406
  return _ref.apply(this, arguments);
366
407
  };
367
408
  }());
368
- return server;
369
409
  }
370
410
 
371
411
  exports.KaitoError = KaitoError;
@@ -101,12 +101,16 @@ function _objectSpread2(target) {
101
101
  }
102
102
 
103
103
  class WrappedError extends Error {
104
- static from(maybeError) {
104
+ static maybe(maybeError) {
105
105
  if (maybeError instanceof Error) {
106
106
  return maybeError;
107
107
  }
108
108
 
109
- return new WrappedError(maybeError);
109
+ return WrappedError.from(maybeError);
110
+ }
111
+
112
+ static from(data) {
113
+ return new WrappedError(data);
110
114
  }
111
115
 
112
116
  constructor(data) {
@@ -227,22 +231,45 @@ function createGetContext(getContext) {
227
231
  return getContext;
228
232
  }
229
233
  class Router {
234
+ /**
235
+ * Ensures that the path does not start or end with a slash.
236
+ * @param path
237
+ * @private
238
+ */
239
+ static stripSlashes(path) {
240
+ if (path.startsWith('/')) {
241
+ path = path.slice(1);
242
+ }
243
+
244
+ if (path.endsWith('/')) {
245
+ path = path.slice(-1);
246
+ }
247
+
248
+ return path;
249
+ }
250
+
230
251
  constructor(procs) {
231
- _defineProperty(this, "create", method => (name, proc) => {
232
- return new Router(_objectSpread2(_objectSpread2({}, this.procs), {}, {
233
- [name]: _objectSpread2(_objectSpread2({}, proc), {}, {
252
+ _defineProperty(this, "create", method => (path, proc) => {
253
+ var stripped = Router.stripSlashes(path);
254
+ var pattern = new RegExp("^/".concat(stripped, "/?$"), 'i');
255
+
256
+ var merged = _objectSpread2(_objectSpread2({}, this.procs), {}, {
257
+ [path]: _objectSpread2(_objectSpread2({}, proc), {}, {
234
258
  method,
235
- name
259
+ path,
260
+ pattern
236
261
  })
237
- }));
262
+ });
263
+
264
+ return new Router(merged);
238
265
  });
239
266
 
240
267
  _defineProperty(this, "merge", (prefix, router) => {
241
268
  var newProcs = Object.entries(router.getProcs()).reduce((all, entry) => {
242
- var [name, proc] = entry;
269
+ var [path, proc] = entry;
243
270
  return _objectSpread2(_objectSpread2({}, all), {}, {
244
- ["".concat(prefix).concat(name)]: _objectSpread2(_objectSpread2({}, proc), {}, {
245
- name: "".concat(prefix).concat(name)
271
+ ["".concat(prefix).concat(path)]: _objectSpread2(_objectSpread2({}, proc), {}, {
272
+ path: "".concat(prefix).concat(path)
246
273
  })
247
274
  });
248
275
  }, {});
@@ -275,12 +302,27 @@ class Router {
275
302
  _defineProperty(this, "bind", this.create('BIND'));
276
303
 
277
304
  this.procs = procs;
305
+ this._procsArray = Object.values(procs);
278
306
  }
279
307
 
280
308
  getProcs() {
281
309
  return this.procs;
282
310
  }
283
311
 
312
+ find(method, url) {
313
+ for (var proc of this._procsArray) {
314
+ if (proc.method !== method) {
315
+ continue;
316
+ }
317
+
318
+ if (proc.pattern.test(url)) {
319
+ return proc;
320
+ }
321
+ }
322
+
323
+ return null;
324
+ }
325
+
284
326
  }
285
327
  class KaitoError extends Error {
286
328
  constructor(status, message, cause) {
@@ -302,8 +344,7 @@ function createServer(config) {
302
344
  }
303
345
  };
304
346
 
305
- var tree = config.router.getProcs();
306
- var server = http__default["default"].createServer( /*#__PURE__*/function () {
347
+ return http__default["default"].createServer( /*#__PURE__*/function () {
307
348
  var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse) {
308
349
  var start = Date.now();
309
350
  var req = new KaitoRequest(incomingMessage);
@@ -312,7 +353,7 @@ function createServer(config) {
312
353
  try {
313
354
  var _handler$input, _yield$getInput;
314
355
 
315
- var handler = tree[req.url.pathname];
356
+ var handler = config.router.find(req.method, req.url.pathname);
316
357
 
317
358
  if (!handler) {
318
359
  throw new KaitoError(404, "Cannot ".concat(req.method, " this route."));
@@ -343,7 +384,7 @@ function createServer(config) {
343
384
  status: _status,
344
385
  message: _message
345
386
  } = yield config.onError({
346
- error: WrappedError.from(error),
387
+ error: WrappedError.maybe(error),
347
388
  req,
348
389
  res
349
390
  }).catch(() => ({
@@ -365,7 +406,6 @@ function createServer(config) {
365
406
  return _ref.apply(this, arguments);
366
407
  };
367
408
  }());
368
- return server;
369
409
  }
370
410
 
371
411
  exports.KaitoError = KaitoError;
@@ -92,12 +92,16 @@ function _objectSpread2(target) {
92
92
  }
93
93
 
94
94
  class WrappedError extends Error {
95
- static from(maybeError) {
95
+ static maybe(maybeError) {
96
96
  if (maybeError instanceof Error) {
97
97
  return maybeError;
98
98
  }
99
99
 
100
- return new WrappedError(maybeError);
100
+ return WrappedError.from(maybeError);
101
+ }
102
+
103
+ static from(data) {
104
+ return new WrappedError(data);
101
105
  }
102
106
 
103
107
  constructor(data) {
@@ -218,22 +222,45 @@ function createGetContext(getContext) {
218
222
  return getContext;
219
223
  }
220
224
  class Router {
225
+ /**
226
+ * Ensures that the path does not start or end with a slash.
227
+ * @param path
228
+ * @private
229
+ */
230
+ static stripSlashes(path) {
231
+ if (path.startsWith('/')) {
232
+ path = path.slice(1);
233
+ }
234
+
235
+ if (path.endsWith('/')) {
236
+ path = path.slice(-1);
237
+ }
238
+
239
+ return path;
240
+ }
241
+
221
242
  constructor(procs) {
222
- _defineProperty(this, "create", method => (name, proc) => {
223
- return new Router(_objectSpread2(_objectSpread2({}, this.procs), {}, {
224
- [name]: _objectSpread2(_objectSpread2({}, proc), {}, {
243
+ _defineProperty(this, "create", method => (path, proc) => {
244
+ var stripped = Router.stripSlashes(path);
245
+ var pattern = new RegExp("^/".concat(stripped, "/?$"), 'i');
246
+
247
+ var merged = _objectSpread2(_objectSpread2({}, this.procs), {}, {
248
+ [path]: _objectSpread2(_objectSpread2({}, proc), {}, {
225
249
  method,
226
- name
250
+ path,
251
+ pattern
227
252
  })
228
- }));
253
+ });
254
+
255
+ return new Router(merged);
229
256
  });
230
257
 
231
258
  _defineProperty(this, "merge", (prefix, router) => {
232
259
  var newProcs = Object.entries(router.getProcs()).reduce((all, entry) => {
233
- var [name, proc] = entry;
260
+ var [path, proc] = entry;
234
261
  return _objectSpread2(_objectSpread2({}, all), {}, {
235
- ["".concat(prefix).concat(name)]: _objectSpread2(_objectSpread2({}, proc), {}, {
236
- name: "".concat(prefix).concat(name)
262
+ ["".concat(prefix).concat(path)]: _objectSpread2(_objectSpread2({}, proc), {}, {
263
+ path: "".concat(prefix).concat(path)
237
264
  })
238
265
  });
239
266
  }, {});
@@ -266,12 +293,27 @@ class Router {
266
293
  _defineProperty(this, "bind", this.create('BIND'));
267
294
 
268
295
  this.procs = procs;
296
+ this._procsArray = Object.values(procs);
269
297
  }
270
298
 
271
299
  getProcs() {
272
300
  return this.procs;
273
301
  }
274
302
 
303
+ find(method, url) {
304
+ for (var proc of this._procsArray) {
305
+ if (proc.method !== method) {
306
+ continue;
307
+ }
308
+
309
+ if (proc.pattern.test(url)) {
310
+ return proc;
311
+ }
312
+ }
313
+
314
+ return null;
315
+ }
316
+
275
317
  }
276
318
  class KaitoError extends Error {
277
319
  constructor(status, message, cause) {
@@ -293,8 +335,7 @@ function createServer(config) {
293
335
  }
294
336
  };
295
337
 
296
- var tree = config.router.getProcs();
297
- var server = http.createServer( /*#__PURE__*/function () {
338
+ return http.createServer( /*#__PURE__*/function () {
298
339
  var _ref = _asyncToGenerator(function* (incomingMessage, serverResponse) {
299
340
  var start = Date.now();
300
341
  var req = new KaitoRequest(incomingMessage);
@@ -303,7 +344,7 @@ function createServer(config) {
303
344
  try {
304
345
  var _handler$input, _yield$getInput;
305
346
 
306
- var handler = tree[req.url.pathname];
347
+ var handler = config.router.find(req.method, req.url.pathname);
307
348
 
308
349
  if (!handler) {
309
350
  throw new KaitoError(404, "Cannot ".concat(req.method, " this route."));
@@ -334,7 +375,7 @@ function createServer(config) {
334
375
  status: _status,
335
376
  message: _message
336
377
  } = yield config.onError({
337
- error: WrappedError.from(error),
378
+ error: WrappedError.maybe(error),
338
379
  req,
339
380
  res
340
381
  }).catch(() => ({
@@ -356,7 +397,6 @@ function createServer(config) {
356
397
  return _ref.apply(this, arguments);
357
398
  };
358
399
  }());
359
- return server;
360
400
  }
361
401
 
362
402
  export { KaitoError, Router, createGetContext, createRouter, createServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "2.1.0",
3
+ "version": "2.2.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>",