@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.
- package/lib/application.js +1 -1
- package/lib/endpoint.js +26 -41
- package/lib/index.js +3 -3
- package/lib/router.js +27 -11
- package/lib/{util/index.js → util.js} +8 -2
- package/package.json +1 -1
- package/test/index.js +5 -3
- package/lib/util/methods.js +0 -49
package/lib/application.js
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
20
|
+
methods.concat(['all']).forEach((method) => {
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
todo: (methods, ...handlers) => {
|
|
22
|
+
this[method] = (...handlers) => {
|
|
23
|
+
handlers = [].concat(...handlers);
|
|
24
|
+
return this._on(method, handlers);
|
|
25
|
+
};
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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(
|
|
36
|
+
_on(method, handlers, match) {
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
59
|
+
return layer.method === method && layer.handler === this._handleMethod;
|
|
71
60
|
});
|
|
72
61
|
|
|
73
62
|
if (existing !== -1) {
|
|
74
|
-
throw new Error(
|
|
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
|
-
|
|
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(
|
|
119
|
-
get: (
|
|
103
|
+
return new Proxy({}, {
|
|
104
|
+
get: (_, path) => {
|
|
120
105
|
return (endpoint) => {
|
|
121
|
-
return
|
|
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
|
-
.
|
|
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.
|
|
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.
|
|
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 (
|
|
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
|
|
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
|
-
|
|
13
|
-
|
|
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,
|
|
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
|
-
|
|
58
|
+
method
|
|
39
59
|
});
|
|
40
60
|
|
|
41
61
|
return this;
|
|
42
62
|
|
|
43
63
|
}
|
|
44
64
|
|
|
45
|
-
|
|
46
|
-
return
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
30
|
+
methods,
|
|
31
|
+
methodsWrite,
|
|
32
|
+
methodsRead,
|
|
27
33
|
isObject,
|
|
28
34
|
matchPath,
|
|
29
35
|
resolveInlineImport
|
package/package.json
CHANGED
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
|
|
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
|
|
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()
|
package/lib/util/methods.js
DELETED
|
@@ -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
|
-
}
|