@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 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` включает вывод логов.
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-trie-router",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
5
5
  "author": "Mikhail Evstropov <e22m4u@yandex.ru>",
6
6
  "license": "MIT",
@@ -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');
@@ -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 option "options" to be an Object', function () {
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 form the "url" option to the request url', function () {
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 form the "path" option to the request url', function () {
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 form the "query" option to the request url', async function () {
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 form the "query" option to the request url', async function () {
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 "headerSent" to true when the stream ends', function () {
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');
@@ -23,4 +23,4 @@ export const CHARACTER_ENCODING_LIST: (
23
23
  export declare function fetchRequestBody(
24
24
  request: IncomingMessage,
25
25
  bodyBytesLimit?: number,
26
- ): Promise<string>;
26
+ ): Promise<string | undefined>;
@@ -123,7 +123,7 @@ describe('fetchRequestBody', function () {
123
123
  );
124
124
  });
125
125
 
126
- it('should not throw an error if the body length does match with the header', async function () {
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({