@trenskow/app 0.8.61 → 0.9.1
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/README.md +3 -1
- package/lib/endpoint.js +8 -9
- package/lib/router.js +25 -6
- package/lib/util.js +7 -2
- package/package.json +2 -1
- package/test/index.js +41 -19
package/.vscode/launch.json
CHANGED
package/README.md
CHANGED
|
@@ -313,6 +313,8 @@ Whenever a function (such as [`.mount`](#mount) or [`.parameter`](#parameters) o
|
|
|
313
313
|
|
|
314
314
|
A router is the same as above, except it only supports [`.use`](#use).
|
|
315
315
|
|
|
316
|
+
> `.use` also has method specific variants such as `use.get`, `use.post`, `use.put`, `use.delete`, etc...
|
|
317
|
+
|
|
316
318
|
###### When using routers
|
|
317
319
|
|
|
318
320
|
As above, whenever a function takes a router as a parameter, it can be provided in any of the following ways.
|
|
@@ -707,7 +709,7 @@ The constructor takes no parameters.
|
|
|
707
709
|
|
|
708
710
|
#### Instance methods
|
|
709
711
|
|
|
710
|
-
##### `use`
|
|
712
|
+
##### `use`, `use.get`, `use.post`, `use.put`, `use.delete`, etc...
|
|
711
713
|
|
|
712
714
|
This method is like the [HTTP method handlers](#get-post-put-delete-etc) of [`Endpoint`](#endpoint-2), except it is called with all HTTP methods and the return value is ignored. Routing continues after handler returns.
|
|
713
715
|
|
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.1",
|
|
4
4
|
"description": "A small HTTP router.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
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",
|
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(
|