@e22m4u/js-trie-router 0.7.7 → 0.7.8
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 +36 -0
- package/dist/cjs/index.cjs +0 -19
- package/package.json +1 -1
- package/src/hooks/router-hook-registry.d.ts +85 -9
- package/src/senders/router-error-sender.js +0 -19
- package/src/trie-router.d.ts +47 -1
- package/src/utils/create-request-mock.spec.js +5 -5
- package/src/utils/create-response-mock.spec.js +1 -1
- package/src/utils/fetch-request-body.d.ts +1 -1
- package/src/utils/fetch-request-body.spec.js +1 -1
package/README.md
CHANGED
|
@@ -1035,6 +1035,42 @@ router.defineRoute({
|
|
|
1035
1035
|
}
|
|
1036
1036
|
```
|
|
1037
1037
|
|
|
1038
|
+
### Перехват и логирование ошибок
|
|
1039
|
+
|
|
1040
|
+
По умолчанию маршрутизатор не выводит информацию об ошибках в консоль,
|
|
1041
|
+
чтобы не нарушать формат логов приложения и не допускать утечки чувствительных
|
|
1042
|
+
данных (например, токенов из заголовков запроса).
|
|
1043
|
+
|
|
1044
|
+
Если требуется реализовать собственное логирование ошибок, то можно
|
|
1045
|
+
переопределить встроенный сервис `RouterErrorSender`. Для этого потребуется
|
|
1046
|
+
унаследовать класс данного сервиса и подменить стандартную реализацию
|
|
1047
|
+
в контейнере маршрутизатора.
|
|
1048
|
+
|
|
1049
|
+
```js
|
|
1050
|
+
import {TrieRouter, RouterErrorSender} from '@e22m4u/js-trie-router';
|
|
1051
|
+
|
|
1052
|
+
// создание собственного сервиса обработки ошибок
|
|
1053
|
+
class CustomErrorSender extends RouterErrorSender {
|
|
1054
|
+
send(request, response, error) {
|
|
1055
|
+
// логирование ошибки удобным способом
|
|
1056
|
+
console.error(`[Error on ${request.method} ${request.url}]:`, error);
|
|
1057
|
+
// вызов родительского метода для стандартной
|
|
1058
|
+
// отправки JSON-ответа с ошибкой клиенту
|
|
1059
|
+
super.send(request, response, error);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
const router = new TrieRouter();
|
|
1064
|
+
// подмена стандартного сервиса новым экземпляром,
|
|
1065
|
+
// передавая текущий контейнер в конструктор (обязательно)
|
|
1066
|
+
router.setService(RouterErrorSender, new CustomErrorSender(router.container));
|
|
1067
|
+
// ... регистрация маршрутов и запуск сервера
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
*При необходимости можно переопределить метод `send404(request, response)`,
|
|
1071
|
+
чтобы отслеживать запросы к несуществующим маршрутам или изменять формат
|
|
1072
|
+
ответа.*
|
|
1073
|
+
|
|
1038
1074
|
## Отладка
|
|
1039
1075
|
|
|
1040
1076
|
Установка переменной `DEBUG` включает вывод логов.
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -2460,7 +2460,6 @@ var RouterDataSender = class extends DebuggableService {
|
|
|
2460
2460
|
};
|
|
2461
2461
|
|
|
2462
2462
|
// src/senders/router-error-sender.js
|
|
2463
|
-
var import_util = require("util");
|
|
2464
2463
|
var import_statuses = __toESM(require("statuses"), 1);
|
|
2465
2464
|
var EXPOSED_ERROR_PROPERTIES = ["code", "details"];
|
|
2466
2465
|
var RouterErrorSender = class extends DebuggableService {
|
|
@@ -2497,24 +2496,6 @@ var RouterErrorSender = class extends DebuggableService {
|
|
|
2497
2496
|
body.error[name] = safeError[name];
|
|
2498
2497
|
}
|
|
2499
2498
|
});
|
|
2500
|
-
const requestData = {
|
|
2501
|
-
url: request.url,
|
|
2502
|
-
method: request.method,
|
|
2503
|
-
headers: request.headers
|
|
2504
|
-
};
|
|
2505
|
-
const inspectOptions = {
|
|
2506
|
-
showHidden: false,
|
|
2507
|
-
depth: null,
|
|
2508
|
-
colors: true,
|
|
2509
|
-
compact: false
|
|
2510
|
-
};
|
|
2511
|
-
console.warn((0, import_util.inspect)(requestData, inspectOptions));
|
|
2512
|
-
console.warn((0, import_util.inspect)(body, inspectOptions));
|
|
2513
|
-
if (error.stack) {
|
|
2514
|
-
console.log(error.stack);
|
|
2515
|
-
} else {
|
|
2516
|
-
console.error(error);
|
|
2517
|
-
}
|
|
2518
2499
|
response.statusCode = statusCode;
|
|
2519
2500
|
response.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
2520
2501
|
response.end(JSON.stringify(body, null, 2), "utf-8");
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {Callable} from '../types.js';
|
|
2
1
|
import {RouteDefinition} from '../route/index.js';
|
|
3
2
|
import {ServiceContainer} from '@e22m4u/js-service';
|
|
4
3
|
import {RequestContext} from '../request-context.js';
|
|
4
|
+
import {Callable, ValueOrPromise} from '../types.js';
|
|
5
|
+
import {IncomingMessage, ServerResponse} from 'http';
|
|
5
6
|
import {DebuggableService} from '../debuggable-service.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -30,6 +31,23 @@ export const ROUTER_HOOK_TYPES: RouterHookType[];
|
|
|
30
31
|
*/
|
|
31
32
|
export type RouterHook = Callable;
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* On defined route hook.
|
|
36
|
+
*/
|
|
37
|
+
export type OnDefineRouteHook = (
|
|
38
|
+
routeDef: RouteDefinition,
|
|
39
|
+
container: ServiceContainer,
|
|
40
|
+
) => RouteDefinition | undefined;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* On request hook.
|
|
44
|
+
*/
|
|
45
|
+
export type OnRequestHook = (
|
|
46
|
+
request: IncomingMessage,
|
|
47
|
+
response: ServerResponse,
|
|
48
|
+
container: ServiceContainer,
|
|
49
|
+
) => ValueOrPromise<boolean | undefined>;
|
|
50
|
+
|
|
33
51
|
/**
|
|
34
52
|
* Pre handler hook.
|
|
35
53
|
*/
|
|
@@ -40,18 +58,48 @@ export type PreHandlerHook = (ctx: RequestContext) => unknown;
|
|
|
40
58
|
*/
|
|
41
59
|
export type PostHandlerHook = (ctx: RequestContext, data: unknown) => unknown;
|
|
42
60
|
|
|
43
|
-
/**
|
|
44
|
-
* On defined route hook.
|
|
45
|
-
*/
|
|
46
|
-
export type OnDefineRouteHook = (
|
|
47
|
-
routeDef: RouteDefinition,
|
|
48
|
-
container: ServiceContainer,
|
|
49
|
-
) => RouteDefinition | undefined;
|
|
50
|
-
|
|
51
61
|
/**
|
|
52
62
|
* Router hook registry.
|
|
53
63
|
*/
|
|
54
64
|
export declare class RouterHookRegistry extends DebuggableService {
|
|
65
|
+
/**
|
|
66
|
+
* Add hook (overload for "onDefineRoute" hook).
|
|
67
|
+
*
|
|
68
|
+
* @param type
|
|
69
|
+
* @param hook
|
|
70
|
+
*/
|
|
71
|
+
addHook(
|
|
72
|
+
type: typeof RouterHookType.ON_DEFINE_ROUTE,
|
|
73
|
+
hook: OnDefineRouteHook,
|
|
74
|
+
): this;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Add hook (overload for "onRequest" hook).
|
|
78
|
+
*
|
|
79
|
+
* @param type
|
|
80
|
+
* @param hook
|
|
81
|
+
*/
|
|
82
|
+
addHook(type: typeof RouterHookType.ON_REQUEST, hook: OnRequestHook): this;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Add hook (overload for "preHandler" hook).
|
|
86
|
+
*
|
|
87
|
+
* @param type
|
|
88
|
+
* @param hook
|
|
89
|
+
*/
|
|
90
|
+
addHook(type: typeof RouterHookType.PRE_HANDLER, hook: PreHandlerHook): this;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Add hook (overload for "postHandler" hook).
|
|
94
|
+
*
|
|
95
|
+
* @param type
|
|
96
|
+
* @param hook
|
|
97
|
+
*/
|
|
98
|
+
addHook(
|
|
99
|
+
type: typeof RouterHookType.POST_HANDLER,
|
|
100
|
+
hook: PostHandlerHook,
|
|
101
|
+
): this;
|
|
102
|
+
|
|
55
103
|
/**
|
|
56
104
|
* Add hook.
|
|
57
105
|
*
|
|
@@ -68,6 +116,34 @@ export declare class RouterHookRegistry extends DebuggableService {
|
|
|
68
116
|
*/
|
|
69
117
|
hasHook(type: RouterHookType, hook: RouterHook): boolean;
|
|
70
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Get hooks (overload for "onDefineRoute").
|
|
121
|
+
*
|
|
122
|
+
* @param type
|
|
123
|
+
*/
|
|
124
|
+
getHooks(type: typeof RouterHookType.ON_DEFINE_ROUTE): OnDefineRouteHook[];
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get hooks (overload for "onRequest").
|
|
128
|
+
*
|
|
129
|
+
* @param type
|
|
130
|
+
*/
|
|
131
|
+
getHooks(type: typeof RouterHookType.ON_REQUEST): OnRequestHook[];
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get hooks (overload for "preHandler").
|
|
135
|
+
*
|
|
136
|
+
* @param type
|
|
137
|
+
*/
|
|
138
|
+
getHooks(type: typeof RouterHookType.PRE_HANDLER): PreHandlerHook[];
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get hooks (overload for "postHandler").
|
|
142
|
+
*
|
|
143
|
+
* @param type
|
|
144
|
+
*/
|
|
145
|
+
getHooks(type: typeof RouterHookType.POST_HANDLER): PostHandlerHook[];
|
|
146
|
+
|
|
71
147
|
/**
|
|
72
148
|
* Get hooks.
|
|
73
149
|
*
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {inspect} from 'util';
|
|
2
1
|
import getStatusMessage from 'statuses';
|
|
3
2
|
import {getRequestPathname} from '../utils/index.js';
|
|
4
3
|
import {DebuggableService} from '../debuggable-service.js';
|
|
@@ -44,24 +43,6 @@ export class RouterErrorSender extends DebuggableService {
|
|
|
44
43
|
body.error[name] = safeError[name];
|
|
45
44
|
}
|
|
46
45
|
});
|
|
47
|
-
const requestData = {
|
|
48
|
-
url: request.url,
|
|
49
|
-
method: request.method,
|
|
50
|
-
headers: request.headers,
|
|
51
|
-
};
|
|
52
|
-
const inspectOptions = {
|
|
53
|
-
showHidden: false,
|
|
54
|
-
depth: null,
|
|
55
|
-
colors: true,
|
|
56
|
-
compact: false,
|
|
57
|
-
};
|
|
58
|
-
console.warn(inspect(requestData, inspectOptions));
|
|
59
|
-
console.warn(inspect(body, inspectOptions));
|
|
60
|
-
if (error.stack) {
|
|
61
|
-
console.log(error.stack);
|
|
62
|
-
} else {
|
|
63
|
-
console.error(error);
|
|
64
|
-
}
|
|
65
46
|
response.statusCode = statusCode;
|
|
66
47
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
67
48
|
response.end(JSON.stringify(body, null, 2), 'utf-8');
|
package/src/trie-router.d.ts
CHANGED
|
@@ -3,10 +3,18 @@ import {RouteDefinition} from './route/index.js';
|
|
|
3
3
|
import {ServiceContainer} from '@e22m4u/js-service';
|
|
4
4
|
import {IncomingMessage, ServerResponse} from 'http';
|
|
5
5
|
import {DebuggableService} from './debuggable-service.js';
|
|
6
|
-
import {RouterHook, RouterHookType} from './hooks/index.js';
|
|
7
6
|
import {TrieRouterOptionsInput} from './trie-router-options.js';
|
|
8
7
|
import {RouterBranch, RouterBranchDefinition} from './branch/index.js';
|
|
9
8
|
|
|
9
|
+
import {
|
|
10
|
+
RouterHook,
|
|
11
|
+
OnRequestHook,
|
|
12
|
+
RouterHookType,
|
|
13
|
+
PreHandlerHook,
|
|
14
|
+
PostHandlerHook,
|
|
15
|
+
OnDefineRouteHook,
|
|
16
|
+
} from './hooks/index.js';
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* Trie router.
|
|
12
20
|
*/
|
|
@@ -105,6 +113,44 @@ export declare class TrieRouter extends DebuggableService {
|
|
|
105
113
|
response: ServerResponse,
|
|
106
114
|
): Promise<void>;
|
|
107
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Add hook (overload for "onDefineRoute" hook).
|
|
118
|
+
*
|
|
119
|
+
* @param type
|
|
120
|
+
* @param hook
|
|
121
|
+
*/
|
|
122
|
+
addHook(
|
|
123
|
+
type: typeof RouterHookType.ON_DEFINE_ROUTE,
|
|
124
|
+
hook: OnDefineRouteHook,
|
|
125
|
+
): this;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Add hook (overload for "onRequest" hook).
|
|
129
|
+
*
|
|
130
|
+
* @param type
|
|
131
|
+
* @param hook
|
|
132
|
+
*/
|
|
133
|
+
addHook(type: typeof RouterHookType.ON_REQUEST, hook: OnRequestHook): this;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Add hook (overload for "preHandler" hook).
|
|
137
|
+
*
|
|
138
|
+
* @param type
|
|
139
|
+
* @param hook
|
|
140
|
+
*/
|
|
141
|
+
addHook(type: typeof RouterHookType.PRE_HANDLER, hook: PreHandlerHook): this;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Add hook (overload for "postHandler" hook).
|
|
145
|
+
*
|
|
146
|
+
* @param type
|
|
147
|
+
* @param hook
|
|
148
|
+
*/
|
|
149
|
+
addHook(
|
|
150
|
+
type: typeof RouterHookType.POST_HANDLER,
|
|
151
|
+
hook: PostHandlerHook,
|
|
152
|
+
): this;
|
|
153
|
+
|
|
108
154
|
/**
|
|
109
155
|
* Add hook.
|
|
110
156
|
*
|
|
@@ -7,7 +7,7 @@ import {createRequestMock} from './create-request-mock.js';
|
|
|
7
7
|
import {CHARACTER_ENCODING_LIST} from './fetch-request-body.js';
|
|
8
8
|
|
|
9
9
|
describe('createRequestMock', function () {
|
|
10
|
-
it('should require the
|
|
10
|
+
it('should require the parameter "options" to be an Object', function () {
|
|
11
11
|
const throwable = v => () => createRequestMock(v);
|
|
12
12
|
const error = v =>
|
|
13
13
|
format('Parameter "options" must be an Object, but %s was given.', v);
|
|
@@ -496,24 +496,24 @@ describe('createRequestMock', function () {
|
|
|
496
496
|
expect(data).to.be.eql(body);
|
|
497
497
|
});
|
|
498
498
|
|
|
499
|
-
it('should pass a value
|
|
499
|
+
it('should pass a value from the "url" option to the request url', function () {
|
|
500
500
|
const req = createRequestMock({url: '/test'});
|
|
501
501
|
expect(req.url).to.be.eq('/test');
|
|
502
502
|
});
|
|
503
503
|
|
|
504
|
-
it('should pass a value
|
|
504
|
+
it('should pass a value from the "path" option to the request url', function () {
|
|
505
505
|
const req = createRequestMock({path: '/test'});
|
|
506
506
|
expect(req.url).to.be.eq('/test');
|
|
507
507
|
});
|
|
508
508
|
|
|
509
|
-
it('should pass a string
|
|
509
|
+
it('should pass a string from the "query" option to the request url', async function () {
|
|
510
510
|
const req1 = createRequestMock({query: 'p1=foo&p2=bar'});
|
|
511
511
|
const req2 = createRequestMock({query: '?p1=foo&p2=bar'});
|
|
512
512
|
expect(req1.url).to.be.eq('/?p1=foo&p2=bar');
|
|
513
513
|
expect(req2.url).to.be.eq('/?p1=foo&p2=bar');
|
|
514
514
|
});
|
|
515
515
|
|
|
516
|
-
it('should pass an object
|
|
516
|
+
it('should pass an object from the "query" option to the request url', async function () {
|
|
517
517
|
const req = createRequestMock({query: {foo: 'bar', baz: 'qux'}});
|
|
518
518
|
expect(req.url).to.be.eq('/?foo=bar&baz=qux');
|
|
519
519
|
});
|
|
@@ -139,7 +139,7 @@ describe('createResponseMock', function () {
|
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
describe('Stream', function () {
|
|
142
|
-
it('should set the property "
|
|
142
|
+
it('should set the property "headersSent" to true when the stream ends', function () {
|
|
143
143
|
const res = createResponseMock();
|
|
144
144
|
expect(res.headersSent).to.be.false;
|
|
145
145
|
res.end('test');
|
|
@@ -123,7 +123,7 @@ describe('fetchRequestBody', function () {
|
|
|
123
123
|
);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
it('should not throw an error if the body length
|
|
126
|
+
it('should not throw an error if the body length matches the header', async function () {
|
|
127
127
|
const body = 'Lorem Ipsum is simply dummy text.';
|
|
128
128
|
const contentLength = String(Buffer.from(body).byteLength);
|
|
129
129
|
const req = createRequestMock({
|