@e22m4u/js-trie-router 0.2.0 → 0.3.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/README.md +3 -3
- package/dist/cjs/index.cjs +225 -213
- package/examples/{cookie-parsing-example.js → cookies-parsing-example.js} +8 -8
- package/examples/params-parsing-example.js +11 -7
- package/examples/query-parsing-example.js +11 -7
- package/examples/uptime-example.js +4 -4
- package/package.json +9 -8
- package/src/debuggable-service.d.ts +2 -10
- package/src/debuggable-service.js +16 -17
- package/src/hooks/hook-invoker.js +3 -4
- package/src/hooks/hook-invoker.spec.js +3 -3
- package/src/hooks/hook-registry.d.ts +1 -1
- package/src/hooks/hook-registry.js +6 -7
- package/src/hooks/hook-registry.spec.js +14 -5
- package/src/parsers/body-parser.js +10 -11
- package/src/parsers/body-parser.spec.js +4 -4
- package/src/parsers/cookies-parser.d.ts +15 -0
- package/src/parsers/cookies-parser.js +33 -0
- package/src/parsers/{cookie-parser.spec.js → cookies-parser.spec.js} +6 -6
- package/src/parsers/index.d.ts +1 -1
- package/src/parsers/index.js +1 -1
- package/src/parsers/query-parser.js +4 -3
- package/src/parsers/request-parser.d.ts +2 -2
- package/src/parsers/request-parser.js +7 -7
- package/src/parsers/request-parser.spec.js +12 -12
- package/src/request-context.d.ts +3 -3
- package/src/request-context.js +5 -5
- package/src/request-context.spec.js +3 -3
- package/src/route-registry.js +14 -14
- package/src/route-registry.spec.js +4 -1
- package/src/route.d.ts +12 -7
- package/src/route.js +16 -14
- package/src/route.spec.js +13 -7
- package/src/router-options.js +1 -1
- package/src/router-options.spec.js +1 -1
- package/src/senders/data-sender.js +6 -5
- package/src/senders/error-sender.js +11 -4
- package/src/trie-router.js +46 -38
- package/src/trie-router.spec.js +54 -3
- package/src/utils/create-cookies-string.d.ts +6 -0
- package/src/utils/{create-cookie-string.js → create-cookies-string.js} +4 -4
- package/src/utils/{create-cookie-string.spec.js → create-cookies-string.spec.js} +7 -7
- package/src/utils/create-debugger.js +1 -1
- package/src/utils/create-debugger.spec.js +1 -1
- package/src/utils/create-error.js +2 -2
- package/src/utils/create-error.spec.js +2 -2
- package/src/utils/create-request-mock.d.ts +1 -1
- package/src/utils/create-request-mock.js +47 -43
- package/src/utils/create-request-mock.spec.js +18 -18
- package/src/utils/fetch-request-body.d.ts +11 -2
- package/src/utils/fetch-request-body.js +10 -17
- package/src/utils/fetch-request-body.spec.js +8 -9
- package/src/utils/get-request-pathname.js +1 -1
- package/src/utils/get-request-pathname.spec.js +1 -1
- package/src/utils/index.d.ts +2 -2
- package/src/utils/index.js +2 -2
- package/src/utils/is-readable-stream.js +1 -2
- package/src/utils/is-response-sent.js +1 -1
- package/src/utils/is-response-sent.spec.js +1 -1
- package/src/utils/parse-content-type.js +1 -1
- package/src/utils/parse-cookies.d.ts +19 -0
- package/src/utils/{parse-cookie.js → parse-cookies.js} +8 -5
- package/src/utils/{parse-cookie.spec.js → parse-cookies.spec.js} +13 -8
- package/src/utils/to-camel-case.js +1 -1
- package/src/utils/to-camel-case.spec.js +1 -1
- package/src/parsers/cookie-parser.d.ts +0 -15
- package/src/parsers/cookie-parser.js +0 -32
- package/src/utils/create-cookie-string.d.ts +0 -6
- package/src/utils/parse-cookie.d.ts +0 -19
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {IncomingMessage} from 'http';
|
|
2
2
|
import {ValueOrPromise} from '../types.js';
|
|
3
3
|
import {ParsedQuery} from './query-parser.js';
|
|
4
|
-
import {
|
|
4
|
+
import {ParsedCookies} from '../utils/index.js';
|
|
5
5
|
import {DebuggableService} from '../debuggable-service.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -16,7 +16,7 @@ export type ParsedHeaders = {
|
|
|
16
16
|
*/
|
|
17
17
|
type ParsedRequestData = {
|
|
18
18
|
query: ParsedQuery;
|
|
19
|
-
|
|
19
|
+
cookies: ParsedCookies;
|
|
20
20
|
body: unknown;
|
|
21
21
|
headers: ParsedHeaders;
|
|
22
22
|
};
|
|
@@ -3,7 +3,7 @@ import {Errorf} from '@e22m4u/js-format';
|
|
|
3
3
|
import {isPromise} from '../utils/index.js';
|
|
4
4
|
import {BodyParser} from './body-parser.js';
|
|
5
5
|
import {QueryParser} from './query-parser.js';
|
|
6
|
-
import {
|
|
6
|
+
import {CookiesParser} from './cookies-parser.js';
|
|
7
7
|
import {DebuggableService} from '../debuggable-service.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -20,7 +20,7 @@ export class RequestParser extends DebuggableService {
|
|
|
20
20
|
if (!(req instanceof IncomingMessage))
|
|
21
21
|
throw new Errorf(
|
|
22
22
|
'The first argument of RequestParser.parse should be ' +
|
|
23
|
-
'an instance of IncomingMessage, but %v given.',
|
|
23
|
+
'an instance of IncomingMessage, but %v was given.',
|
|
24
24
|
req,
|
|
25
25
|
);
|
|
26
26
|
const data = {};
|
|
@@ -39,11 +39,11 @@ export class RequestParser extends DebuggableService {
|
|
|
39
39
|
// данные заголовка "cookie" с проверкой
|
|
40
40
|
// значения на Promise, и разрываем
|
|
41
41
|
// "eventLoop" при необходимости
|
|
42
|
-
const
|
|
43
|
-
if (isPromise(
|
|
44
|
-
promises.push(
|
|
42
|
+
const parsedCookies = this.getService(CookiesParser).parse(req);
|
|
43
|
+
if (isPromise(parsedCookies)) {
|
|
44
|
+
promises.push(parsedCookies.then(v => (data.cookies = v)));
|
|
45
45
|
} else {
|
|
46
|
-
data.
|
|
46
|
+
data.cookies = parsedCookies;
|
|
47
47
|
}
|
|
48
48
|
// аналогично предыдущей операции, разбираем
|
|
49
49
|
// тело запроса с проверкой результата
|
|
@@ -56,7 +56,7 @@ export class RequestParser extends DebuggableService {
|
|
|
56
56
|
}
|
|
57
57
|
// что бы предотвратить модификацию
|
|
58
58
|
// заголовков, возвращаем их копию
|
|
59
|
-
data.headers =
|
|
59
|
+
data.headers = Object.assign({}, req.headers);
|
|
60
60
|
// если имеются асинхронные операции, то результат
|
|
61
61
|
// будет обернут в Promise, в противном случае
|
|
62
62
|
// данные возвращаются сразу
|
|
@@ -12,7 +12,7 @@ describe('RequestParser', function () {
|
|
|
12
12
|
const error = v =>
|
|
13
13
|
format(
|
|
14
14
|
'The first argument of RequestParser.parse should be ' +
|
|
15
|
-
'an instance of IncomingMessage, but %s given.',
|
|
15
|
+
'an instance of IncomingMessage, but %s was given.',
|
|
16
16
|
v,
|
|
17
17
|
);
|
|
18
18
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -29,13 +29,13 @@ describe('RequestParser', function () {
|
|
|
29
29
|
throwable(createRequestMock())();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
it('returns the result object if no request body
|
|
32
|
+
it('returns the result object if no request body', function () {
|
|
33
33
|
const s = new RequestParser();
|
|
34
34
|
const req = createRequestMock();
|
|
35
35
|
const res = s.parse(req);
|
|
36
36
|
expect(res).to.be.eql({
|
|
37
37
|
query: {},
|
|
38
|
-
|
|
38
|
+
cookies: {},
|
|
39
39
|
body: undefined,
|
|
40
40
|
headers: {host: 'localhost'},
|
|
41
41
|
});
|
|
@@ -54,7 +54,7 @@ describe('RequestParser', function () {
|
|
|
54
54
|
const res = await promise;
|
|
55
55
|
expect(res).to.be.eql({
|
|
56
56
|
query: {},
|
|
57
|
-
|
|
57
|
+
cookies: {},
|
|
58
58
|
body,
|
|
59
59
|
headers: {
|
|
60
60
|
host: 'localhost',
|
|
@@ -64,25 +64,25 @@ describe('RequestParser', function () {
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it('returns the
|
|
67
|
+
it('returns the result object with the parsed query', function () {
|
|
68
68
|
const s = new RequestParser();
|
|
69
69
|
const req = createRequestMock({path: '/path?p1=foo&p2=bar'});
|
|
70
70
|
const res = s.parse(req);
|
|
71
71
|
expect(res).to.be.eql({
|
|
72
72
|
query: {p1: 'foo', p2: 'bar'},
|
|
73
|
-
|
|
73
|
+
cookies: {},
|
|
74
74
|
body: undefined,
|
|
75
75
|
headers: {host: 'localhost'},
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
-
it('returns the
|
|
79
|
+
it('returns the result object with the parsed cookies', function () {
|
|
80
80
|
const s = new RequestParser();
|
|
81
81
|
const req = createRequestMock({headers: {cookie: 'p1=foo; p2=bar;'}});
|
|
82
82
|
const res = s.parse(req);
|
|
83
83
|
expect(res).to.be.eql({
|
|
84
84
|
query: {},
|
|
85
|
-
|
|
85
|
+
cookies: {p1: 'foo', p2: 'bar'},
|
|
86
86
|
body: undefined,
|
|
87
87
|
headers: {
|
|
88
88
|
host: 'localhost',
|
|
@@ -91,7 +91,7 @@ describe('RequestParser', function () {
|
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('returns the parsed body of the media type "text/plain"
|
|
94
|
+
it('returns the result object with the parsed body of the media type "text/plain"', async function () {
|
|
95
95
|
const s = new RequestParser();
|
|
96
96
|
const body = 'Lorem Ipsum is simply dummy text.';
|
|
97
97
|
const req = createRequestMock({
|
|
@@ -102,7 +102,7 @@ describe('RequestParser', function () {
|
|
|
102
102
|
const res = await s.parse(req);
|
|
103
103
|
expect(res).to.be.eql({
|
|
104
104
|
query: {},
|
|
105
|
-
|
|
105
|
+
cookies: {},
|
|
106
106
|
body,
|
|
107
107
|
headers: {
|
|
108
108
|
host: 'localhost',
|
|
@@ -112,7 +112,7 @@ describe('RequestParser', function () {
|
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
it('returns the parsed body of the media type "application/json"
|
|
115
|
+
it('returns the result object with the parsed body of the media type "application/json"', async function () {
|
|
116
116
|
const s = new RequestParser();
|
|
117
117
|
const body = {foo: 'bar', baz: 'qux'};
|
|
118
118
|
const json = JSON.stringify(body);
|
|
@@ -124,7 +124,7 @@ describe('RequestParser', function () {
|
|
|
124
124
|
const res = await s.parse(req);
|
|
125
125
|
expect(res).to.be.eql({
|
|
126
126
|
query: {},
|
|
127
|
-
|
|
127
|
+
cookies: {},
|
|
128
128
|
body,
|
|
129
129
|
headers: {
|
|
130
130
|
host: 'localhost',
|
package/src/request-context.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ServerResponse} from 'http';
|
|
2
2
|
import {IncomingMessage} from 'http';
|
|
3
|
-
import {ParsedCookie} from './utils/index.js';
|
|
4
3
|
import {ParsedQuery} from './parsers/index.js';
|
|
4
|
+
import {ParsedCookies} from './utils/index.js';
|
|
5
5
|
import {ParsedHeaders} from './parsers/index.js';
|
|
6
6
|
import {ServiceContainer} from '@e22m4u/js-service';
|
|
7
7
|
|
|
@@ -47,9 +47,9 @@ export declare class RequestContext {
|
|
|
47
47
|
headers: ParsedHeaders;
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* Cookies.
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
cookies: ParsedCookies;
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Body.
|
package/src/request-context.js
CHANGED
|
@@ -52,11 +52,11 @@ export class RequestContext {
|
|
|
52
52
|
headers = {};
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* Parsed
|
|
55
|
+
* Parsed cookies.
|
|
56
56
|
*
|
|
57
57
|
* @type {object}
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
cookies = {};
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Parsed body.
|
|
@@ -113,7 +113,7 @@ export class RequestContext {
|
|
|
113
113
|
if (!isServiceContainer(container))
|
|
114
114
|
throw new Errorf(
|
|
115
115
|
'The parameter "container" of RequestContext.constructor ' +
|
|
116
|
-
'should be an instance of ServiceContainer, but %v given.',
|
|
116
|
+
'should be an instance of ServiceContainer, but %v was given.',
|
|
117
117
|
container,
|
|
118
118
|
);
|
|
119
119
|
this.container = container;
|
|
@@ -125,7 +125,7 @@ export class RequestContext {
|
|
|
125
125
|
) {
|
|
126
126
|
throw new Errorf(
|
|
127
127
|
'The parameter "request" of RequestContext.constructor ' +
|
|
128
|
-
'should be an instance of IncomingMessage, but %v given.',
|
|
128
|
+
'should be an instance of IncomingMessage, but %v was given.',
|
|
129
129
|
request,
|
|
130
130
|
);
|
|
131
131
|
}
|
|
@@ -138,7 +138,7 @@ export class RequestContext {
|
|
|
138
138
|
) {
|
|
139
139
|
throw new Errorf(
|
|
140
140
|
'The parameter "response" of RequestContext.constructor ' +
|
|
141
|
-
'should be an instance of ServerResponse, but %v given.',
|
|
141
|
+
'should be an instance of ServerResponse, but %v was given.',
|
|
142
142
|
response,
|
|
143
143
|
);
|
|
144
144
|
}
|
|
@@ -14,7 +14,7 @@ describe('RequestContext', function () {
|
|
|
14
14
|
const error = v =>
|
|
15
15
|
format(
|
|
16
16
|
'The parameter "container" of RequestContext.constructor ' +
|
|
17
|
-
'should be an instance of ServiceContainer, but %s given.',
|
|
17
|
+
'should be an instance of ServiceContainer, but %s was given.',
|
|
18
18
|
v,
|
|
19
19
|
);
|
|
20
20
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -37,7 +37,7 @@ describe('RequestContext', function () {
|
|
|
37
37
|
const error = v =>
|
|
38
38
|
format(
|
|
39
39
|
'The parameter "request" of RequestContext.constructor ' +
|
|
40
|
-
'should be an instance of IncomingMessage, but %s given.',
|
|
40
|
+
'should be an instance of IncomingMessage, but %s was given.',
|
|
41
41
|
v,
|
|
42
42
|
);
|
|
43
43
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -60,7 +60,7 @@ describe('RequestContext', function () {
|
|
|
60
60
|
const error = v =>
|
|
61
61
|
format(
|
|
62
62
|
'The parameter "response" of RequestContext.constructor ' +
|
|
63
|
-
'should be an instance of ServerResponse, but %s given.',
|
|
63
|
+
'should be an instance of ServerResponse, but %s was given.',
|
|
64
64
|
v,
|
|
65
65
|
);
|
|
66
66
|
expect(throwable('str')).to.throw(error('"str"'));
|
package/src/route-registry.js
CHANGED
|
@@ -32,16 +32,17 @@ export class RouteRegistry extends DebuggableService {
|
|
|
32
32
|
* @returns {Route}
|
|
33
33
|
*/
|
|
34
34
|
defineRoute(routeDef) {
|
|
35
|
+
const debug = this.getDebuggerFor(this.defineRoute);
|
|
35
36
|
if (!routeDef || typeof routeDef !== 'object' || Array.isArray(routeDef))
|
|
36
37
|
throw new Errorf(
|
|
37
|
-
'The route definition should be an Object, but %v given.',
|
|
38
|
+
'The route definition should be an Object, but %v was given.',
|
|
38
39
|
routeDef,
|
|
39
40
|
);
|
|
40
41
|
const route = new Route(routeDef);
|
|
41
42
|
const triePath = `${route.method}/${route.path}`;
|
|
42
43
|
this._trie.add(triePath, route);
|
|
43
|
-
|
|
44
|
-
'The route %s %v
|
|
44
|
+
debug(
|
|
45
|
+
'The route %s %v was registered.',
|
|
45
46
|
route.method.toUpperCase(),
|
|
46
47
|
route.path,
|
|
47
48
|
);
|
|
@@ -55,9 +56,10 @@ export class RouteRegistry extends DebuggableService {
|
|
|
55
56
|
* @returns {ResolvedRoute|undefined}
|
|
56
57
|
*/
|
|
57
58
|
matchRouteByRequest(req) {
|
|
59
|
+
const debug = this.getDebuggerFor(this.matchRouteByRequest);
|
|
58
60
|
const requestPath = (req.url || '/').replace(/\?.*$/, '');
|
|
59
|
-
|
|
60
|
-
'Matching %s %v
|
|
61
|
+
debug(
|
|
62
|
+
'Matching routes with the request %s %v.',
|
|
61
63
|
req.method.toUpperCase(),
|
|
62
64
|
requestPath,
|
|
63
65
|
);
|
|
@@ -65,28 +67,26 @@ export class RouteRegistry extends DebuggableService {
|
|
|
65
67
|
const resolved = this._trie.match(triePath);
|
|
66
68
|
if (resolved) {
|
|
67
69
|
const route = resolved.value;
|
|
68
|
-
|
|
69
|
-
'The
|
|
70
|
-
req.method.toUpperCase(),
|
|
71
|
-
requestPath,
|
|
70
|
+
debug(
|
|
71
|
+
'The route %s %v was matched.',
|
|
72
72
|
route.method.toUpperCase(),
|
|
73
73
|
route.path,
|
|
74
74
|
);
|
|
75
75
|
const paramNames = Object.keys(resolved.params);
|
|
76
|
-
if (paramNames) {
|
|
76
|
+
if (paramNames.length) {
|
|
77
77
|
paramNames.forEach(name => {
|
|
78
|
-
|
|
79
|
-
'The path parameter %v
|
|
78
|
+
debug(
|
|
79
|
+
'The path parameter %v had the value %v.',
|
|
80
80
|
name,
|
|
81
81
|
resolved.params[name],
|
|
82
82
|
);
|
|
83
83
|
});
|
|
84
84
|
} else {
|
|
85
|
-
|
|
85
|
+
debug('No path parameters found.');
|
|
86
86
|
}
|
|
87
87
|
return {route, params: resolved.params};
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
debug(
|
|
90
90
|
'No matched route for the request %s %v.',
|
|
91
91
|
req.method.toUpperCase(),
|
|
92
92
|
requestPath,
|
|
@@ -11,7 +11,10 @@ describe('RouteRegistry', function () {
|
|
|
11
11
|
const s = new RouteRegistry();
|
|
12
12
|
const throwable = v => () => s.defineRoute(v);
|
|
13
13
|
const error = v =>
|
|
14
|
-
format(
|
|
14
|
+
format(
|
|
15
|
+
'The route definition should be an Object, but %s was given.',
|
|
16
|
+
v,
|
|
17
|
+
);
|
|
15
18
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
16
19
|
expect(throwable('')).to.throw(error('""'));
|
|
17
20
|
expect(throwable(10)).to.throw(error('10'));
|
package/src/route.d.ts
CHANGED
|
@@ -5,13 +5,18 @@ import {RequestContext} from './request-context.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* Http method.
|
|
7
7
|
*/
|
|
8
|
-
export
|
|
9
|
-
GET
|
|
10
|
-
POST
|
|
11
|
-
PUT
|
|
12
|
-
PATCH
|
|
13
|
-
DELETE
|
|
14
|
-
}
|
|
8
|
+
export declare const HttpMethod: {
|
|
9
|
+
GET: 'GET';
|
|
10
|
+
POST: 'POST';
|
|
11
|
+
PUT: 'PUT';
|
|
12
|
+
PATCH: 'PATCH';
|
|
13
|
+
DELETE: 'DELETE';
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Type of HttpMethod.
|
|
18
|
+
*/
|
|
19
|
+
export type HttpMethod = (typeof HttpMethod)[keyof typeof HttpMethod];
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Route handler.
|
package/src/route.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {Errorf} from '@e22m4u/js-format';
|
|
2
2
|
import {HookType} from './hooks/index.js';
|
|
3
|
+
import {Debuggable} from '@e22m4u/js-debug';
|
|
3
4
|
import {HookRegistry} from './hooks/index.js';
|
|
4
|
-
import {createDebugger} from './utils/index.js';
|
|
5
5
|
import {getRequestPathname} from './utils/index.js';
|
|
6
|
+
import {MODULE_DEBUG_NAMESPACE} from './debuggable-service.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('./request-context.js').RequestContext} RequestContext
|
|
@@ -37,17 +38,10 @@ export const HttpMethod = {
|
|
|
37
38
|
DELETE: 'DELETE',
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
/**
|
|
41
|
-
* Debugger.
|
|
42
|
-
*
|
|
43
|
-
* @type {Function}
|
|
44
|
-
*/
|
|
45
|
-
const debug = createDebugger('route');
|
|
46
|
-
|
|
47
41
|
/**
|
|
48
42
|
* Route.
|
|
49
43
|
*/
|
|
50
|
-
export class Route {
|
|
44
|
+
export class Route extends Debuggable {
|
|
51
45
|
/**
|
|
52
46
|
* Method.
|
|
53
47
|
*
|
|
@@ -122,29 +116,35 @@ export class Route {
|
|
|
122
116
|
* @param {RouteDefinition} routeDef
|
|
123
117
|
*/
|
|
124
118
|
constructor(routeDef) {
|
|
119
|
+
super({
|
|
120
|
+
namespace: MODULE_DEBUG_NAMESPACE,
|
|
121
|
+
noEnvironmentNamespace: true,
|
|
122
|
+
noInstantiationMessage: true,
|
|
123
|
+
});
|
|
125
124
|
if (!routeDef || typeof routeDef !== 'object' || Array.isArray(routeDef))
|
|
126
125
|
throw new Errorf(
|
|
127
|
-
'The first parameter of Route.
|
|
128
|
-
'should be an Object, but %v given.',
|
|
126
|
+
'The first parameter of Route.constructor ' +
|
|
127
|
+
'should be an Object, but %v was given.',
|
|
129
128
|
routeDef,
|
|
130
129
|
);
|
|
131
130
|
if (!routeDef.method || typeof routeDef.method !== 'string')
|
|
132
131
|
throw new Errorf(
|
|
133
132
|
'The option "method" of the Route should be ' +
|
|
134
|
-
'a non-empty String, but %v given.',
|
|
133
|
+
'a non-empty String, but %v was given.',
|
|
135
134
|
routeDef.method,
|
|
136
135
|
);
|
|
137
136
|
this._method = routeDef.method.toUpperCase();
|
|
138
137
|
if (typeof routeDef.path !== 'string')
|
|
139
138
|
throw new Errorf(
|
|
140
|
-
'The option "path" of the Route should be ' +
|
|
139
|
+
'The option "path" of the Route should be ' +
|
|
140
|
+
'a String, but %v was given.',
|
|
141
141
|
routeDef.path,
|
|
142
142
|
);
|
|
143
143
|
this._path = routeDef.path;
|
|
144
144
|
if (typeof routeDef.handler !== 'function')
|
|
145
145
|
throw new Errorf(
|
|
146
146
|
'The option "handler" of the Route should be ' +
|
|
147
|
-
'a Function, but %v given.',
|
|
147
|
+
'a Function, but %v was given.',
|
|
148
148
|
routeDef.handler,
|
|
149
149
|
);
|
|
150
150
|
this._handler = routeDef.handler;
|
|
@@ -164,6 +164,7 @@ export class Route {
|
|
|
164
164
|
this._hookRegistry.addHook(HookType.POST_HANDLER, hook);
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
|
+
this.ctorDebug('A new route %s %v was created.', this._method, this._path);
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
@@ -173,6 +174,7 @@ export class Route {
|
|
|
173
174
|
* @returns {*}
|
|
174
175
|
*/
|
|
175
176
|
handle(context) {
|
|
177
|
+
const debug = this.getDebuggerFor(this.handle);
|
|
176
178
|
const requestPath = getRequestPathname(context.req);
|
|
177
179
|
debug(
|
|
178
180
|
'Invoking the Route handler for the request %s %v.',
|
package/src/route.spec.js
CHANGED
|
@@ -14,8 +14,8 @@ describe('Route', function () {
|
|
|
14
14
|
const throwable = v => () => new Route(v);
|
|
15
15
|
const error = v =>
|
|
16
16
|
format(
|
|
17
|
-
'The first parameter of Route.
|
|
18
|
-
'should be an Object, but %s given.',
|
|
17
|
+
'The first parameter of Route.constructor ' +
|
|
18
|
+
'should be an Object, but %s was given.',
|
|
19
19
|
v,
|
|
20
20
|
);
|
|
21
21
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -45,7 +45,7 @@ describe('Route', function () {
|
|
|
45
45
|
const error = v =>
|
|
46
46
|
format(
|
|
47
47
|
'The option "method" of the Route should be ' +
|
|
48
|
-
'a non-empty String, but %s given.',
|
|
48
|
+
'a non-empty String, but %s was given.',
|
|
49
49
|
v,
|
|
50
50
|
);
|
|
51
51
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -71,7 +71,7 @@ describe('Route', function () {
|
|
|
71
71
|
const error = v =>
|
|
72
72
|
format(
|
|
73
73
|
'The option "path" of the Route should be ' +
|
|
74
|
-
'a String, but %s given.',
|
|
74
|
+
'a String, but %s was given.',
|
|
75
75
|
v,
|
|
76
76
|
);
|
|
77
77
|
expect(throwable(10)).to.throw(error('10'));
|
|
@@ -97,7 +97,7 @@ describe('Route', function () {
|
|
|
97
97
|
const error = v =>
|
|
98
98
|
format(
|
|
99
99
|
'The option "handler" of the Route should be ' +
|
|
100
|
-
'a Function, but %s given.',
|
|
100
|
+
'a Function, but %s was given.',
|
|
101
101
|
v,
|
|
102
102
|
);
|
|
103
103
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -122,7 +122,10 @@ describe('Route', function () {
|
|
|
122
122
|
handler: () => undefined,
|
|
123
123
|
});
|
|
124
124
|
const error = v =>
|
|
125
|
-
format(
|
|
125
|
+
format(
|
|
126
|
+
'The hook "preHandler" should be a Function, but %s was given.',
|
|
127
|
+
v,
|
|
128
|
+
);
|
|
126
129
|
expect(throwable1('str')).to.throw(error('"str"'));
|
|
127
130
|
expect(throwable1('')).to.throw(error('""'));
|
|
128
131
|
expect(throwable1(10)).to.throw(error('10'));
|
|
@@ -163,7 +166,10 @@ describe('Route', function () {
|
|
|
163
166
|
postHandler: v,
|
|
164
167
|
});
|
|
165
168
|
const error = v =>
|
|
166
|
-
format(
|
|
169
|
+
format(
|
|
170
|
+
'The hook "postHandler" should be a Function, but %s was given.',
|
|
171
|
+
v,
|
|
172
|
+
);
|
|
167
173
|
expect(throwable1('str')).to.throw(error('"str"'));
|
|
168
174
|
expect(throwable1('')).to.throw(error('""'));
|
|
169
175
|
expect(throwable1(10)).to.throw(error('10'));
|
package/src/router-options.js
CHANGED
|
@@ -32,7 +32,7 @@ export class RouterOptions extends DebuggableService {
|
|
|
32
32
|
if (typeof input !== 'number' || input < 0)
|
|
33
33
|
throw new Errorf(
|
|
34
34
|
'The option "requestBodyBytesLimit" must be ' +
|
|
35
|
-
'a positive Number or 0, but %v given.',
|
|
35
|
+
'a positive Number or 0, but %v was given.',
|
|
36
36
|
input,
|
|
37
37
|
);
|
|
38
38
|
this._requestBodyBytesLimit = input;
|
|
@@ -25,7 +25,7 @@ describe('RouterOptions', function () {
|
|
|
25
25
|
const error = v =>
|
|
26
26
|
format(
|
|
27
27
|
'The option "requestBodyBytesLimit" must be ' +
|
|
28
|
-
'a positive Number or 0, but %s given.',
|
|
28
|
+
'a positive Number or 0, but %s was given.',
|
|
29
29
|
v,
|
|
30
30
|
);
|
|
31
31
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -14,14 +14,15 @@ export class DataSender extends DebuggableService {
|
|
|
14
14
|
* @returns {undefined}
|
|
15
15
|
*/
|
|
16
16
|
send(res, data) {
|
|
17
|
+
const debug = this.getDebuggerFor(this.send);
|
|
17
18
|
// если ответ контроллера является объектом
|
|
18
19
|
// ServerResponse, или имеются отправленные
|
|
19
20
|
// заголовки, то считаем, что контроллер
|
|
20
21
|
// уже отправил ответ самостоятельно
|
|
21
22
|
if (data === res || res.headersSent) {
|
|
22
|
-
|
|
23
|
+
debug(
|
|
23
24
|
'Response sending was skipped because ' +
|
|
24
|
-
'its headers where sent already
|
|
25
|
+
'its headers where sent already.',
|
|
25
26
|
);
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
@@ -30,7 +31,7 @@ export class DataSender extends DebuggableService {
|
|
|
30
31
|
if (data == null) {
|
|
31
32
|
res.statusCode = 204;
|
|
32
33
|
res.end();
|
|
33
|
-
|
|
34
|
+
debug('The empty response was sent.');
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
// если ответ контроллера является стримом,
|
|
@@ -38,7 +39,7 @@ export class DataSender extends DebuggableService {
|
|
|
38
39
|
if (isReadableStream(data)) {
|
|
39
40
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
40
41
|
data.pipe(res);
|
|
41
|
-
|
|
42
|
+
debug('The stream response was sent.');
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
// подготовка данных перед отправкой, и установка
|
|
@@ -67,6 +68,6 @@ export class DataSender extends DebuggableService {
|
|
|
67
68
|
}
|
|
68
69
|
// отправка подготовленных данных
|
|
69
70
|
res.end(data);
|
|
70
|
-
|
|
71
|
+
debug(debugMsg);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -23,6 +23,7 @@ export class ErrorSender extends DebuggableService {
|
|
|
23
23
|
* @returns {undefined}
|
|
24
24
|
*/
|
|
25
25
|
send(req, res, error) {
|
|
26
|
+
const debug = this.getDebuggerFor(this.send);
|
|
26
27
|
let safeError = {};
|
|
27
28
|
if (error) {
|
|
28
29
|
if (typeof error === 'object') {
|
|
@@ -54,11 +55,16 @@ export class ErrorSender extends DebuggableService {
|
|
|
54
55
|
};
|
|
55
56
|
console.warn(inspect(requestData, inspectOptions));
|
|
56
57
|
console.warn(inspect(body, inspectOptions));
|
|
58
|
+
if (error.stack) {
|
|
59
|
+
console.log(error.stack);
|
|
60
|
+
} else {
|
|
61
|
+
console.error(error);
|
|
62
|
+
}
|
|
57
63
|
res.statusCode = statusCode;
|
|
58
64
|
res.setHeader('content-type', 'application/json; charset=utf-8');
|
|
59
65
|
res.end(JSON.stringify(body, null, 2), 'utf-8');
|
|
60
|
-
|
|
61
|
-
'The %s error
|
|
66
|
+
debug(
|
|
67
|
+
'The %s error was sent for the request %s %v.',
|
|
62
68
|
statusCode,
|
|
63
69
|
req.method,
|
|
64
70
|
getRequestPathname(req),
|
|
@@ -73,11 +79,12 @@ export class ErrorSender extends DebuggableService {
|
|
|
73
79
|
* @returns {undefined}
|
|
74
80
|
*/
|
|
75
81
|
send404(req, res) {
|
|
82
|
+
const debug = this.getDebuggerFor(this.send404);
|
|
76
83
|
res.statusCode = 404;
|
|
77
84
|
res.setHeader('content-type', 'text/plain; charset=utf-8');
|
|
78
85
|
res.end('404 Not Found', 'utf-8');
|
|
79
|
-
|
|
80
|
-
'The 404 error
|
|
86
|
+
debug(
|
|
87
|
+
'The 404 error was sent for the request %s %v.',
|
|
81
88
|
req.method,
|
|
82
89
|
getRequestPathname(req),
|
|
83
90
|
);
|