@trenskow/app 0.8.60 → 0.9.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.
- package/.vscode/launch.json +3 -1
- package/lib/endpoint.js +8 -9
- package/lib/router.js +25 -6
- package/lib/util.js +7 -2
- package/package.json +3 -2
- package/test/index.js +41 -19
package/.vscode/launch.json
CHANGED
package/lib/endpoint.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
//
|
|
2
2
|
// endpoint.js
|
|
3
3
|
// @trenskow/app
|
|
4
|
-
//
|
|
4
|
+
//
|
|
5
5
|
// Created by Kristian Trenskow on 2021/11/08
|
|
6
6
|
// For license see LICENSE.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
import { METHODS } from 'http';
|
|
7
|
+
//
|
|
10
8
|
|
|
11
9
|
import ApiError from '@trenskow/api-error';
|
|
12
10
|
|
|
13
11
|
import Router from './router.js';
|
|
14
12
|
|
|
15
|
-
import { isObject, matchPath, resolveInlineImport } from './util.js';
|
|
16
|
-
|
|
17
|
-
const methods = METHODS.map((method) => method.toLowerCase());
|
|
13
|
+
import { isObject, matchPath, resolveInlineImport, methods } from './util.js';
|
|
18
14
|
|
|
19
15
|
export default class Endpoint extends Router {
|
|
20
16
|
|
|
@@ -59,9 +55,12 @@ export default class Endpoint extends Router {
|
|
|
59
55
|
throw new Error('Handler must be a function.');
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
const existing = this._layers.findIndex((layer) =>
|
|
58
|
+
const existing = this._layers.findIndex((layer) => {
|
|
59
|
+
return layer.method === method && layer.handler === this._handleMethod;
|
|
60
|
+
});
|
|
61
|
+
|
|
63
62
|
if (existing !== -1) {
|
|
64
|
-
|
|
63
|
+
throw new Error(`Endpoint already has a \`${method}\` handler.`);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
this._layers.push({
|
package/lib/router.js
CHANGED
|
@@ -6,15 +6,23 @@
|
|
|
6
6
|
// For license see LICENSE.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
|
-
import { isObject, resolveInlineImport } from './util.js';
|
|
9
|
+
import { isObject, resolveInlineImport, methods } from './util.js';
|
|
10
10
|
|
|
11
11
|
export default class Router {
|
|
12
12
|
|
|
13
13
|
constructor() {
|
|
14
|
+
|
|
14
15
|
this._layers = [];
|
|
16
|
+
|
|
17
|
+
methods.forEach((method) => {
|
|
18
|
+
return this.use[method] = (...handlers) => {
|
|
19
|
+
return this.#_use(handlers, method);
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
#_use(handlers, method) {
|
|
18
26
|
|
|
19
27
|
handlers = [].concat(...handlers);
|
|
20
28
|
|
|
@@ -28,13 +36,18 @@ export default class Router {
|
|
|
28
36
|
|
|
29
37
|
this._layers.push({
|
|
30
38
|
handler: this._handleUse,
|
|
31
|
-
handlers
|
|
39
|
+
handlers,
|
|
40
|
+
method
|
|
32
41
|
});
|
|
33
42
|
|
|
34
43
|
return this;
|
|
35
44
|
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
use(...handlers) {
|
|
48
|
+
return this.#_use(handlers, 'all');
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
mixin(router) {
|
|
39
52
|
|
|
40
53
|
if (isObject(router)) {
|
|
@@ -94,9 +107,15 @@ export default class Router {
|
|
|
94
107
|
|
|
95
108
|
async _handleUse(layer, _, context, next) {
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
const { request } = context;
|
|
111
|
+
|
|
112
|
+
if (layer.method === 'all' || request.method.toLowerCase() === layer.method) {
|
|
113
|
+
|
|
114
|
+
for (let handler of layer.handlers) {
|
|
115
|
+
await handler(context);
|
|
116
|
+
if (context.state !== 'routing') return;
|
|
117
|
+
}
|
|
118
|
+
|
|
100
119
|
}
|
|
101
120
|
|
|
102
121
|
return await next();
|
package/lib/util.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
//
|
|
2
2
|
// is-object.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
|
+
|
|
9
|
+
import { METHODS } from 'http';
|
|
8
10
|
|
|
9
11
|
import caseit from '@trenskow/caseit';
|
|
10
12
|
|
|
13
|
+
const methods = METHODS.map((method) => method.toLowerCase());
|
|
14
|
+
|
|
11
15
|
const isObject = (value) => value?.constructor === Object;
|
|
12
16
|
|
|
13
17
|
const matchPath = (path1, path2, { application: { path: { matchMode }} }) => {
|
|
@@ -21,6 +25,7 @@ const resolveInlineImport = (value) => {
|
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
export {
|
|
28
|
+
methods,
|
|
24
29
|
isObject,
|
|
25
30
|
matchPath,
|
|
26
31
|
resolveInlineImport
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trenskow/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A small HTTP router.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -27,13 +27,14 @@
|
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@eslint/eslintrc": "^3.2.0",
|
|
29
29
|
"@eslint/js": "^9.13.0",
|
|
30
|
+
"chai": "^5.1.2",
|
|
30
31
|
"eslint": "^9.17.0",
|
|
31
32
|
"globals": "^15.14.0",
|
|
32
33
|
"mocha": "^11.0.1",
|
|
33
34
|
"supertest": "^7.0.0"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
|
-
"@trenskow/api-error": "^2.5.
|
|
37
|
+
"@trenskow/api-error": "^2.5.16",
|
|
37
38
|
"@trenskow/caseit": "^1.4.6"
|
|
38
39
|
}
|
|
39
40
|
}
|
package/test/index.js
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
import supertest from 'supertest';
|
|
10
|
-
import {
|
|
10
|
+
import { expect } from 'chai';
|
|
11
11
|
|
|
12
|
-
import Application from '../lib/index.js';
|
|
12
|
+
import { Application, Endpoint, Router } from '../lib/index.js';
|
|
13
13
|
|
|
14
14
|
process.on('unhandledRejection', (error) => {
|
|
15
15
|
console.error(error.stack);
|
|
16
|
-
process.
|
|
16
|
+
process.eit (1);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
let app;
|
|
@@ -104,22 +104,12 @@ describe('Application', () => {
|
|
|
104
104
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
it ('should
|
|
108
|
-
|
|
109
|
-
app.root(
|
|
107
|
+
it ('should throw an error if endpoint already has a handler for method.', async () => {
|
|
108
|
+
expect(() => {
|
|
110
109
|
new Endpoint()
|
|
111
|
-
.get(() => '
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
() => 'Hello, World!',
|
|
115
|
-
() => { /* Ignore this */ })
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
await request
|
|
119
|
-
.get('/')
|
|
120
|
-
.expect('Content-Type', 'text/plain; charset=utf-8')
|
|
121
|
-
.expect(200, 'Hello, World!');
|
|
122
|
-
|
|
110
|
+
.get(() => 'Hello, World!')
|
|
111
|
+
.get(() => 'Hello, World! (2)');
|
|
112
|
+
}).to.throw('Endpoint already has a `get` handler.');
|
|
123
113
|
});
|
|
124
114
|
|
|
125
115
|
it ('should ignore GET method handler when path has been rewritten and respond with 200 and `Hello, World!`.', async () => {
|
|
@@ -244,7 +234,7 @@ describe('Application', () => {
|
|
|
244
234
|
|
|
245
235
|
});
|
|
246
236
|
|
|
247
|
-
it ('should respond with a
|
|
237
|
+
it ('should respond with a value generated in a transform.', async () => {
|
|
248
238
|
|
|
249
239
|
app.root(
|
|
250
240
|
new Endpoint()
|
|
@@ -261,6 +251,38 @@ describe('Application', () => {
|
|
|
261
251
|
|
|
262
252
|
});
|
|
263
253
|
|
|
254
|
+
it ('should respond with an ignored value generated in a specific transform.', async () => {
|
|
255
|
+
|
|
256
|
+
app.root(
|
|
257
|
+
new Endpoint()
|
|
258
|
+
.use(({ parameters }) => parameters.value = 'my-value')
|
|
259
|
+
.use.post(({ parameters }) => parameters.value = 'my-value-post')
|
|
260
|
+
.get(({ parameters: { value }}) => value)
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
await request
|
|
264
|
+
.get('/')
|
|
265
|
+
.expect('Content-Type', 'text/plain; charset=utf-8')
|
|
266
|
+
.expect(200, 'my-value');
|
|
267
|
+
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it ('should respond with a value generated in a specific transform.', async () => {
|
|
271
|
+
|
|
272
|
+
app.root(
|
|
273
|
+
new Endpoint()
|
|
274
|
+
.use(({ parameters }) => parameters.value = 'my-value')
|
|
275
|
+
.use.get(({ parameters }) => parameters.value = 'my-value-get')
|
|
276
|
+
.get(({ parameters: { value }}) => value)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
await request
|
|
280
|
+
.get('/')
|
|
281
|
+
.expect('Content-Type', 'text/plain; charset=utf-8')
|
|
282
|
+
.expect(200, 'my-value-get');
|
|
283
|
+
|
|
284
|
+
});
|
|
285
|
+
|
|
264
286
|
it ('should respond with a value when using PUT on a catch-all method.', async () => {
|
|
265
287
|
|
|
266
288
|
app.root(
|