@trenskow/app 0.9.4 → 0.9.5

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.
@@ -17,7 +17,7 @@ import Request from './request.js';
17
17
  import Response from './response.js';
18
18
  import Endpoint from './endpoint.js';
19
19
 
20
- import { isObject } from './util/index.js';
20
+ import { isObject } from './util.js';
21
21
 
22
22
  export default class Application extends EventEmitter {
23
23
 
package/lib/endpoint.js CHANGED
@@ -10,47 +10,36 @@ import ApiError from '@trenskow/api-error';
10
10
 
11
11
  import Router from './router.js';
12
12
 
13
- import { isObject, matchPath, resolveInlineImport, Methods } from './util/index.js';
13
+ import { isObject, matchPath, resolveInlineImport, methods } from './util.js';
14
14
 
15
15
  export default class Endpoint extends Router {
16
16
 
17
17
  constructor() {
18
18
  super();
19
19
 
20
- Methods.all.forEach((method) => {
20
+ methods.concat(['all']).forEach((method) => {
21
21
 
22
- Object.defineProperty(this, method, {
23
- get: () => new Methods({
24
- existing: [method],
25
- additional: ['catchAll'],
26
- todo: (methods, ...handlers) => {
22
+ this[method] = (...handlers) => {
23
+ handlers = [].concat(...handlers);
24
+ return this._on(method, handlers);
25
+ };
27
26
 
28
- handlers = [].concat(...handlers);
29
-
30
- let match = 'direct';
31
-
32
- if (methods.includes('catchAll')) {
33
- match = 'indirect';
34
- methods = methods.filter((method) => method !== 'catchAll');
35
- }
36
-
37
- return this._on(methods, handlers, match);
38
-
39
- }
40
- })
41
- });
27
+ this[method].catchAll = (...handlers) => {
28
+ handlers = [].concat(...handlers);
29
+ return this._on(method, handlers, 'indirect');
30
+ };
42
31
 
43
32
  });
44
33
 
45
34
  }
46
35
 
47
- _on(methods, handlers, match) {
36
+ _on(method, handlers, match) {
48
37
 
49
- methods.forEach((method) => {
50
- if (!Methods.all.includes(method)) {
51
- throw new Error(`Method ${method} is unknown.`);
52
- }
53
- });
38
+ method = method.toLowerCase();
39
+
40
+ if (!methods.concat(['all']).includes(method)) {
41
+ throw new Error(`Method ${method} is unknown.`);
42
+ }
54
43
 
55
44
  match = match || 'direct';
56
45
 
@@ -67,16 +56,16 @@ export default class Endpoint extends Router {
67
56
  }
68
57
 
69
58
  const existing = this._layers.findIndex((layer) => {
70
- return (layer.handler === this._handleMethod && methods.some((method) => layer.methods.includes(method)));
59
+ return layer.method === method && layer.handler === this._handleMethod;
71
60
  });
72
61
 
73
62
  if (existing !== -1) {
74
- throw new Error('Endpoint already has a handler.');
63
+ throw new Error(`Endpoint already has a \`${method}\` handler.`);
75
64
  }
76
65
 
77
66
  this._layers.push({
78
67
  handler: this._handleMethod,
79
- methods,
68
+ method,
80
69
  handlers,
81
70
  match
82
71
  });
@@ -85,10 +74,6 @@ export default class Endpoint extends Router {
85
74
 
86
75
  }
87
76
 
88
- all(...handlers) {
89
- return this._on(Methods.all, handlers);
90
- }
91
-
92
77
  mount(path, endpoint) {
93
78
 
94
79
  if (isObject(path)) {
@@ -115,10 +100,10 @@ export default class Endpoint extends Router {
115
100
  }
116
101
 
117
102
  get mounts() {
118
- return new Proxy(this, {
119
- get: (target, path) => {
103
+ return new Proxy({}, {
104
+ get: (_, path) => {
120
105
  return (endpoint) => {
121
- return target.mount(path, endpoint);
106
+ return this.mount(path, endpoint);
122
107
  };
123
108
  }
124
109
  });
@@ -198,11 +183,11 @@ export default class Endpoint extends Router {
198
183
 
199
184
  const methods = this._layers
200
185
  .filter((layer) => layer.handler === this._handleMethod)
201
- .reduce((methods, layer) => methods.concat(layer.methods), [])
186
+ .map((layer) => layer.method.toUpperCase())
202
187
  .filter((value, index, array) => array.indexOf(value) === index);
203
188
 
204
189
  if (methods.length) {
205
- context.response.headers.allow = methods.map((method) => method.toUpperCase()).join(', ');
190
+ context.response.headers.allow = methods.join(', ');
206
191
  throw new ApiError.MethodNotAllowed();
207
192
  }
208
193
 
@@ -236,12 +221,12 @@ export default class Endpoint extends Router {
236
221
 
237
222
  let underlyingMethod = context.request.method.toLowerCase();
238
223
 
239
- if (underlyingMethod === 'head' && !this._layers.some((layer) => layer.methods.includes('head'))) {
224
+ if (underlyingMethod === 'head' && !this._layers.some((layer) => layer.method === 'head')) {
240
225
  underlyingMethod = 'get';
241
226
  }
242
227
 
243
228
  if (layer.match === 'direct' && !path.isLast) return await next();
244
- if (!layer.methods.includes(underlyingMethod)) return await next();
229
+ if (layer.method !== 'all' && layer.method !== underlyingMethod) return await next();
245
230
 
246
231
  for (let handler of layer.handlers) {
247
232
  const result = await handler(context);
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  //
2
2
  // index.js
3
3
  // @trenskow/app
4
- //
4
+ //
5
5
  // Created by Kristian Trenskow on 2021/11/07
6
6
  // For license see LICENSE.
7
- //
7
+ //
8
8
 
9
9
  import Router from './router.js';
10
10
  import Endpoint from './endpoint.js';
@@ -13,7 +13,7 @@ import Request from './request.js';
13
13
  import Response from './response.js';
14
14
  import ApiError from '@trenskow/api-error';
15
15
 
16
- import { isObject, matchPath, resolveInlineImport } from './util/index.js';
16
+ import { isObject, matchPath, resolveInlineImport } from './util.js';
17
17
 
18
18
  export default Application;
19
19
 
package/lib/router.js CHANGED
@@ -9,8 +9,10 @@
9
9
  import {
10
10
  isObject,
11
11
  resolveInlineImport,
12
- Methods
13
- } from './util/index.js';
12
+ methods,
13
+ methodsRead,
14
+ methodsWrite
15
+ } from './util.js';
14
16
 
15
17
  export default class Router {
16
18
 
@@ -18,9 +20,27 @@ export default class Router {
18
20
 
19
21
  this._layers = [];
20
22
 
23
+ methods.forEach((method) => {
24
+ return this.use[method] = (...handlers) => {
25
+ return this.#_use(handlers, method);
26
+ };
27
+ });
28
+
29
+ this.use.read = (...handlers) => {
30
+ methodsRead.forEach((method) => {
31
+ return this.#_use(handlers, method);
32
+ });
33
+ };
34
+
35
+ this.use.write = (...handlers) => {
36
+ methodsWrite.forEach((method) => {
37
+ return this.#_use(handlers, method);
38
+ });
39
+ };
40
+
21
41
  }
22
42
 
23
- #_use(handlers, methods) {
43
+ #_use(handlers, method) {
24
44
 
25
45
  handlers = [].concat(...handlers);
26
46
 
@@ -35,19 +55,15 @@ export default class Router {
35
55
  this._layers.push({
36
56
  handler: this._handleUse,
37
57
  handlers,
38
- methods
58
+ method
39
59
  });
40
60
 
41
61
  return this;
42
62
 
43
63
  }
44
64
 
45
- get use() {
46
- return new Methods({
47
- todo: (methods, ...handlers) => {
48
- return this.#_use(handlers, methods);
49
- }
50
- });
65
+ use(...handlers) {
66
+ return this.#_use(handlers, 'all');
51
67
  }
52
68
 
53
69
  mixin(router) {
@@ -111,7 +127,7 @@ export default class Router {
111
127
 
112
128
  const { request } = context;
113
129
 
114
- if (layer.methods.length === 0 || layer.methods.includes(request.method.toLowerCase())) {
130
+ if (layer.method === 'all' || request.method.toLowerCase() === layer.method) {
115
131
 
116
132
  for (let handler of layer.handlers) {
117
133
  await handler(context);
@@ -6,9 +6,13 @@
6
6
  // For license see LICENSE.
7
7
  //
8
8
 
9
+ import { METHODS } from 'http';
10
+
9
11
  import caseit from '@trenskow/caseit';
10
12
 
11
- import Methods from './methods.js';
13
+ const methods = METHODS.map((method) => method.toLowerCase());
14
+ const methodsWrite = ['post', 'put', 'patch', 'delete'];
15
+ const methodsRead = methods.filter((method) => !methodsWrite.includes(method));
12
16
 
13
17
  const isObject = (value) => value?.constructor === Object;
14
18
 
@@ -23,7 +27,9 @@ const resolveInlineImport = (value) => {
23
27
  };
24
28
 
25
29
  export {
26
- Methods,
30
+ methods,
31
+ methodsWrite,
32
+ methodsRead,
27
33
  isObject,
28
34
  matchPath,
29
35
  resolveInlineImport
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trenskow/app",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "A small HTTP router.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/test/index.js CHANGED
@@ -50,7 +50,9 @@ describe('Application', () => {
50
50
  it ('should respond with 405 when a route is configured, but request method is not specified,', async () => {
51
51
  app.root(
52
52
  new Endpoint()
53
- .post.put.delete(() => {})
53
+ .post(() => {})
54
+ .put(() => {})
55
+ .delete(() => {})
54
56
  );
55
57
  await request
56
58
  .get('/')
@@ -107,7 +109,7 @@ describe('Application', () => {
107
109
  new Endpoint()
108
110
  .get(() => 'Hello, World!')
109
111
  .get(() => 'Hello, World! (2)');
110
- }).to.throw('Endpoint already has a handler.');
112
+ }).to.throw('Endpoint already has a `get` handler.');
111
113
  });
112
114
 
113
115
  it ('should ignore GET method handler when path has been rewritten and respond with 200 and `Hello, World!`.', async () => {
@@ -281,7 +283,7 @@ describe('Application', () => {
281
283
 
282
284
  });
283
285
 
284
- it ('should respond with a value when using PUT on a all handler.', async () => {
286
+ it ('should respond with a value when using PUT on a catch-all method.', async () => {
285
287
 
286
288
  app.root(
287
289
  new Endpoint()
@@ -1,49 +0,0 @@
1
- //
2
- // is-object.js
3
- // @trenskow/app
4
- //
5
- // Created by Kristian Trenskow on 2025/01/02
6
- // For license see LICENSE.
7
- //
8
-
9
- import { METHODS } from 'http';
10
-
11
- const methods = METHODS.map((method) => method.toLowerCase());
12
-
13
- export default class Methods extends Function {
14
-
15
- static get all() {
16
- return methods;
17
- }
18
-
19
- constructor({ existing = [], additional = [], todo }) {
20
- super();
21
-
22
- if (!Array.isArray(existing)) {
23
- throw new Error('Existing must be an array.');
24
- }
25
-
26
- let methods = existing;
27
-
28
- return new Proxy(this, {
29
- get: (_ /* target */, property, receiver) => {
30
-
31
- if (property === 'all') {
32
- methods = Methods.all;
33
- } else if (Methods.all.concat(additional).includes(property)) {
34
- if (!methods.includes(property)) methods.push(property);
35
- } else {
36
- throw new Error(`Method ${property} is unknown.`);
37
- }
38
-
39
- return receiver;
40
-
41
- },
42
- apply: (_ /* target */, __ /* this */, args) => {
43
- return todo(methods, ...args);
44
- }
45
- });
46
-
47
- }
48
-
49
- }