@e22m4u/js-trie-router 0.3.6 → 0.3.7
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 +11 -10
- package/dist/cjs/index.cjs +49 -34
- package/package.json +2 -2
- package/src/hooks/hook-invoker.js +68 -41
- package/src/hooks/hook-invoker.spec.js +27 -2
- package/src/route.d.ts +3 -1
- package/src/route.spec.js +4 -5
package/README.md
CHANGED
|
@@ -101,16 +101,17 @@ router.defineRoute({
|
|
|
101
101
|
handler(ctx) {
|
|
102
102
|
// GET /users/10?include=city
|
|
103
103
|
// Cookie: foo=bar; baz=qux;
|
|
104
|
-
console.log(ctx.req);
|
|
105
|
-
console.log(ctx.res);
|
|
106
|
-
console.log(ctx.params);
|
|
107
|
-
console.log(ctx.query);
|
|
108
|
-
console.log(ctx.headers);
|
|
109
|
-
console.log(ctx.cookies);
|
|
110
|
-
console.log(ctx.method);
|
|
111
|
-
console.log(ctx.path);
|
|
112
|
-
console.log(ctx.pathname);
|
|
113
|
-
console.log(ctx.meta);
|
|
104
|
+
console.log(ctx.req); // IncomingMessage
|
|
105
|
+
console.log(ctx.res); // ServerResponse
|
|
106
|
+
console.log(ctx.params); // {id: 10}
|
|
107
|
+
console.log(ctx.query); // {include: 'city'}
|
|
108
|
+
console.log(ctx.headers); // {cookie: 'foo=bar; baz=qux;'}
|
|
109
|
+
console.log(ctx.cookies); // {foo: 'bar', baz: 'qux'}
|
|
110
|
+
console.log(ctx.method); // "GET"
|
|
111
|
+
console.log(ctx.path); // "/users/10?include=city"
|
|
112
|
+
console.log(ctx.pathname); // "/users/10"
|
|
113
|
+
console.log(ctx.meta); // {prop: 'value'}
|
|
114
|
+
console.log(ctx.container); // ServiceContainer
|
|
114
115
|
// ...
|
|
115
116
|
},
|
|
116
117
|
});
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -76,6 +76,25 @@ var import_js_debug = require("@e22m4u/js-debug");
|
|
|
76
76
|
// src/hooks/hook-invoker.js
|
|
77
77
|
var import_js_format13 = require("@e22m4u/js-format");
|
|
78
78
|
|
|
79
|
+
// src/debuggable-service.js
|
|
80
|
+
var import_js_service = require("@e22m4u/js-service");
|
|
81
|
+
var MODULE_DEBUG_NAMESPACE = "jsTrieRouter";
|
|
82
|
+
var _DebuggableService = class _DebuggableService extends import_js_service.DebuggableService {
|
|
83
|
+
/**
|
|
84
|
+
* Constructor.
|
|
85
|
+
*
|
|
86
|
+
* @param {ServiceContainer} container
|
|
87
|
+
*/
|
|
88
|
+
constructor(container = void 0) {
|
|
89
|
+
super(container, {
|
|
90
|
+
namespace: MODULE_DEBUG_NAMESPACE,
|
|
91
|
+
noEnvironmentNamespace: true
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
__name(_DebuggableService, "DebuggableService");
|
|
96
|
+
var DebuggableService = _DebuggableService;
|
|
97
|
+
|
|
79
98
|
// src/utils/clone-deep.js
|
|
80
99
|
function cloneDeep(value) {
|
|
81
100
|
if (value == null || typeof value !== "object") {
|
|
@@ -739,25 +758,6 @@ var _HookRegistry = class _HookRegistry {
|
|
|
739
758
|
__name(_HookRegistry, "HookRegistry");
|
|
740
759
|
var HookRegistry = _HookRegistry;
|
|
741
760
|
|
|
742
|
-
// src/debuggable-service.js
|
|
743
|
-
var import_js_service = require("@e22m4u/js-service");
|
|
744
|
-
var MODULE_DEBUG_NAMESPACE = "jsTrieRouter";
|
|
745
|
-
var _DebuggableService = class _DebuggableService extends import_js_service.DebuggableService {
|
|
746
|
-
/**
|
|
747
|
-
* Constructor.
|
|
748
|
-
*
|
|
749
|
-
* @param {ServiceContainer} container
|
|
750
|
-
*/
|
|
751
|
-
constructor(container = void 0) {
|
|
752
|
-
super(container, {
|
|
753
|
-
namespace: MODULE_DEBUG_NAMESPACE,
|
|
754
|
-
noEnvironmentNamespace: true
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
__name(_DebuggableService, "DebuggableService");
|
|
759
|
-
var DebuggableService = _DebuggableService;
|
|
760
|
-
|
|
761
761
|
// src/hooks/hook-invoker.js
|
|
762
762
|
var _HookInvoker = class _HookInvoker extends DebuggableService {
|
|
763
763
|
/**
|
|
@@ -788,31 +788,46 @@ var _HookInvoker = class _HookInvoker extends DebuggableService {
|
|
|
788
788
|
response
|
|
789
789
|
);
|
|
790
790
|
}
|
|
791
|
+
if (isResponseSent(response)) {
|
|
792
|
+
return response;
|
|
793
|
+
}
|
|
791
794
|
const hooks = [
|
|
792
795
|
...this.getService(HookRegistry).getHooks(hookType),
|
|
793
796
|
...route.hookRegistry.getHooks(hookType)
|
|
794
797
|
];
|
|
795
798
|
let result = void 0;
|
|
796
|
-
for (
|
|
799
|
+
for (let i = 0; i < hooks.length; i++) {
|
|
800
|
+
const hook = hooks[i];
|
|
801
|
+
result = hook(...args);
|
|
797
802
|
if (isResponseSent(response)) {
|
|
798
|
-
|
|
799
|
-
break;
|
|
803
|
+
return response;
|
|
800
804
|
}
|
|
801
|
-
if (result
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
805
|
+
if (result != null) {
|
|
806
|
+
if (isPromise(result)) {
|
|
807
|
+
return (async () => {
|
|
808
|
+
let asyncResult = await result;
|
|
809
|
+
if (isResponseSent(response)) {
|
|
810
|
+
return response;
|
|
811
|
+
}
|
|
812
|
+
if (asyncResult != null) {
|
|
813
|
+
return asyncResult;
|
|
814
|
+
}
|
|
815
|
+
for (let j = i + 1; j < hooks.length; j++) {
|
|
816
|
+
asyncResult = await hooks[j](...args);
|
|
817
|
+
if (isResponseSent(response)) {
|
|
818
|
+
return response;
|
|
819
|
+
}
|
|
820
|
+
if (asyncResult != null) {
|
|
821
|
+
return asyncResult;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
806
824
|
return;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
});
|
|
811
|
-
} else {
|
|
812
|
-
break;
|
|
825
|
+
})();
|
|
826
|
+
}
|
|
827
|
+
return result;
|
|
813
828
|
}
|
|
814
829
|
}
|
|
815
|
-
return
|
|
830
|
+
return;
|
|
816
831
|
}
|
|
817
832
|
};
|
|
818
833
|
__name(_HookInvoker, "HookInvoker");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/js-trie-router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
|
|
5
5
|
"author": "Mikhail Evstropov <e22m4u@yandex.ru>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"eslint": "~9.39.1",
|
|
59
59
|
"eslint-config-prettier": "~10.1.8",
|
|
60
60
|
"eslint-plugin-chai-expect": "~3.1.0",
|
|
61
|
-
"eslint-plugin-jsdoc": "~61.
|
|
61
|
+
"eslint-plugin-jsdoc": "~61.2.0",
|
|
62
62
|
"eslint-plugin-mocha": "~11.2.0",
|
|
63
63
|
"globals": "~16.5.0",
|
|
64
64
|
"husky": "~9.1.7",
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {Route} from '../route.js';
|
|
2
2
|
import {Errorf} from '@e22m4u/js-format';
|
|
3
|
-
import {isPromise} from '../utils/index.js';
|
|
4
|
-
import {HookRegistry} from './hook-registry.js';
|
|
5
|
-
import {isResponseSent} from '../utils/index.js';
|
|
6
|
-
import {RouterHookType} from './hook-registry.js';
|
|
7
3
|
import {DebuggableService} from '../debuggable-service.js';
|
|
4
|
+
import {isPromise, isResponseSent} from '../utils/index.js';
|
|
5
|
+
import {HookRegistry, RouterHookType} from './hook-registry.js';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* Hook invoker.
|
|
@@ -49,6 +47,11 @@ export class HookInvoker extends DebuggableService {
|
|
|
49
47
|
response,
|
|
50
48
|
);
|
|
51
49
|
}
|
|
50
|
+
// если ответ уже отправлен,
|
|
51
|
+
// то возвращается ServerResponse
|
|
52
|
+
if (isResponseSent(response)) {
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
52
55
|
// так как хуки роута выполняются
|
|
53
56
|
// после глобальных, то объединяем
|
|
54
57
|
// их в данной последовательности
|
|
@@ -56,48 +59,72 @@ export class HookInvoker extends DebuggableService {
|
|
|
56
59
|
...this.getService(HookRegistry).getHooks(hookType),
|
|
57
60
|
...route.hookRegistry.getHooks(hookType),
|
|
58
61
|
];
|
|
59
|
-
// последовательный вызов хуков будет прерван,
|
|
60
|
-
// если один из них вернет значение (или Promise)
|
|
61
|
-
// отличное от "undefined" и "null"
|
|
62
62
|
let result = undefined;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// итерация по хукам выполняется по индексу,
|
|
64
|
+
// чтобы знать, с какого места продолжать
|
|
65
|
+
// в асинхронном режиме
|
|
66
|
+
for (let i = 0; i < hooks.length; i++) {
|
|
67
|
+
const hook = hooks[i];
|
|
68
|
+
// вызов хука выполняется
|
|
69
|
+
// в синхронном режиме
|
|
70
|
+
result = hook(...args);
|
|
71
|
+
// если ответ уже отправлен,
|
|
72
|
+
// то возвращается ServerResponse
|
|
66
73
|
if (isResponseSent(response)) {
|
|
67
|
-
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
// если выполняется первый хук, или предыдущий
|
|
71
|
-
// хук вернул пустое значение, то выполняем
|
|
72
|
-
// следующий, записывая возвращаемое
|
|
73
|
-
// значение в результат
|
|
74
|
-
if (result == null) {
|
|
75
|
-
result = hook(...args);
|
|
74
|
+
return response;
|
|
76
75
|
}
|
|
77
|
-
// если
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
// если синхронный вызов хука вернул значение отличное
|
|
77
|
+
// от undefined и null, то требуется проверить данное
|
|
78
|
+
// значение для коррекции режима вызова оставшихся хуков
|
|
79
|
+
if (result != null) {
|
|
80
|
+
// если синхронный вызов хука вернул Promise, то дальнейшее
|
|
81
|
+
// выполнение переключается в асинхронный режим, начиная
|
|
82
|
+
// с индекса следующего хука
|
|
83
|
+
if (isPromise(result)) {
|
|
84
|
+
return (async () => {
|
|
85
|
+
// ожидание Promise, который был получен
|
|
86
|
+
// на предыдущем шаге (в синхронном режиме)
|
|
87
|
+
let asyncResult = await result;
|
|
88
|
+
// если ответ уже отправлен,
|
|
89
|
+
// то возвращается ServerResponse
|
|
90
|
+
if (isResponseSent(response)) {
|
|
91
|
+
return response;
|
|
92
|
+
}
|
|
93
|
+
// если Promise разрешился значением отличным
|
|
94
|
+
// от undefined и null, то данное значение
|
|
95
|
+
// возвращается в качестве результата
|
|
96
|
+
if (asyncResult != null) {
|
|
97
|
+
return asyncResult;
|
|
98
|
+
}
|
|
99
|
+
// продолжение вызова хуков начиная
|
|
100
|
+
// со следующего индекса (асинхронно)
|
|
101
|
+
for (let j = i + 1; j < hooks.length; j++) {
|
|
102
|
+
// с этого момента все синхронные
|
|
103
|
+
// хуки выполняются как асинхронные
|
|
104
|
+
asyncResult = await hooks[j](...args);
|
|
105
|
+
// если ответ уже отправлен,
|
|
106
|
+
// то возвращается ServerResponse
|
|
107
|
+
if (isResponseSent(response)) {
|
|
108
|
+
return response;
|
|
109
|
+
}
|
|
110
|
+
// если хук вернул значение отличное
|
|
111
|
+
// от undefined и null, то данное значение
|
|
112
|
+
// возвращается в качестве результата
|
|
113
|
+
if (asyncResult != null) {
|
|
114
|
+
return asyncResult;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
85
117
|
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
// если предыдущий хук вернул значение
|
|
95
|
-
// отличное от "undefined" и "null",
|
|
96
|
-
// то завершаем обход
|
|
97
|
-
else {
|
|
98
|
-
break;
|
|
118
|
+
})();
|
|
119
|
+
}
|
|
120
|
+
// если синхронный хук вернул значение отличное
|
|
121
|
+
// от undefined и null, то данное значение
|
|
122
|
+
// возвращается в качестве результата
|
|
123
|
+
return result;
|
|
99
124
|
}
|
|
100
125
|
}
|
|
101
|
-
|
|
126
|
+
// все хуки были синхронными
|
|
127
|
+
// и не вернули значения
|
|
128
|
+
return;
|
|
102
129
|
}
|
|
103
130
|
}
|
|
@@ -234,7 +234,32 @@ describe('HookInvoker', function () {
|
|
|
234
234
|
]);
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
it('
|
|
237
|
+
it('returns the given response and should not call hooks if the response is already sent', function () {
|
|
238
|
+
const s = new HookInvoker();
|
|
239
|
+
const res = createResponseMock();
|
|
240
|
+
res._headersSent = true;
|
|
241
|
+
s.getService(HookRegistry).addHook(RouterHookType.PRE_HANDLER, () => {
|
|
242
|
+
throw new Error('Should not be called');
|
|
243
|
+
});
|
|
244
|
+
const route = new Route({
|
|
245
|
+
method: HttpMethod.GET,
|
|
246
|
+
path: '/',
|
|
247
|
+
preHandler: [
|
|
248
|
+
() => {
|
|
249
|
+
throw new Error('Should not be called');
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
handler: () => undefined,
|
|
253
|
+
});
|
|
254
|
+
const result = s.invokeAndContinueUntilValueReceived(
|
|
255
|
+
route,
|
|
256
|
+
RouterHookType.PRE_HANDLER,
|
|
257
|
+
res,
|
|
258
|
+
);
|
|
259
|
+
expect(result).to.be.eq(res);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('stops global hooks invocation and returns the given response if it is already sent', function () {
|
|
238
263
|
const s = new HookInvoker();
|
|
239
264
|
const order = [];
|
|
240
265
|
const res = createResponseMock();
|
|
@@ -270,7 +295,7 @@ describe('HookInvoker', function () {
|
|
|
270
295
|
expect(order).to.be.eql(['globalHook1', 'globalHook2']);
|
|
271
296
|
});
|
|
272
297
|
|
|
273
|
-
it('stops route hooks invocation and returns the given response if it
|
|
298
|
+
it('stops route hooks invocation and returns the given response if it is already sent', function () {
|
|
274
299
|
const s = new HookInvoker();
|
|
275
300
|
const order = [];
|
|
276
301
|
const res = createResponseMock();
|
package/src/route.d.ts
CHANGED
package/src/route.spec.js
CHANGED
|
@@ -370,14 +370,13 @@ describe('Route', function () {
|
|
|
370
370
|
|
|
371
371
|
describe('handle', function () {
|
|
372
372
|
it('invokes the handler with the given RequestContext and return its result', function () {
|
|
373
|
-
const handler = ctx => {
|
|
374
|
-
expect(ctx).to.be.instanceof(RequestContext);
|
|
375
|
-
return 'OK';
|
|
376
|
-
};
|
|
377
373
|
const route = new Route({
|
|
378
374
|
method: HttpMethod.GET,
|
|
379
375
|
path: '/',
|
|
380
|
-
handler
|
|
376
|
+
handler(ctx) {
|
|
377
|
+
expect(ctx).to.be.instanceof(RequestContext);
|
|
378
|
+
return 'OK';
|
|
379
|
+
},
|
|
381
380
|
});
|
|
382
381
|
const req = createRequestMock();
|
|
383
382
|
const res = createResponseMock();
|