@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
|
@@ -4,25 +4,25 @@ import {HttpMethod} from '../src/route.js';
|
|
|
4
4
|
|
|
5
5
|
const router = new TrieRouter();
|
|
6
6
|
|
|
7
|
-
// регистрация
|
|
8
|
-
//
|
|
7
|
+
// регистрация маршрута для разбора
|
|
8
|
+
// передаваемого заголовка "Cookie"
|
|
9
9
|
router.defineRoute({
|
|
10
10
|
method: HttpMethod.GET,
|
|
11
|
-
path: '/
|
|
12
|
-
handler: ({
|
|
11
|
+
path: '/parseCookies',
|
|
12
|
+
handler: ({cookies}) => cookies,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
// и
|
|
15
|
+
// создание экземпляра HTTP сервера
|
|
16
|
+
// и подключение обработчика запросов
|
|
17
17
|
const server = new http.Server();
|
|
18
18
|
server.on('request', router.requestListener);
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// прослушивание входящих запросов
|
|
21
21
|
// на указанный адрес и порт
|
|
22
22
|
const port = 3000;
|
|
23
23
|
const host = '0.0.0.0';
|
|
24
24
|
server.listen(port, host, function () {
|
|
25
25
|
const cyan = '\x1b[36m%s\x1b[0m';
|
|
26
26
|
console.log(cyan, 'Server listening on port:', port);
|
|
27
|
-
console.log(cyan, 'Open in browser:', `http://${host}:${port}/
|
|
27
|
+
console.log(cyan, 'Open in browser:', `http://${host}:${port}/parseCookies`);
|
|
28
28
|
});
|
|
@@ -4,25 +4,29 @@ import {HttpMethod} from '../src/route.js';
|
|
|
4
4
|
|
|
5
5
|
const router = new TrieRouter();
|
|
6
6
|
|
|
7
|
-
// регистрация
|
|
8
|
-
//
|
|
7
|
+
// регистрация маршрута для разбора
|
|
8
|
+
// передаваемых параметров пути
|
|
9
9
|
router.defineRoute({
|
|
10
10
|
method: HttpMethod.GET,
|
|
11
|
-
path: '/
|
|
11
|
+
path: '/parseParams/:p1/:p2',
|
|
12
12
|
handler: ({params}) => params,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
// и
|
|
15
|
+
// создание экземпляра HTTP сервера
|
|
16
|
+
// и подключение обработчика запросов
|
|
17
17
|
const server = new http.Server();
|
|
18
18
|
server.on('request', router.requestListener);
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// прослушивание входящих запросов
|
|
21
21
|
// на указанный адрес и порт
|
|
22
22
|
const port = 3000;
|
|
23
23
|
const host = '0.0.0.0';
|
|
24
24
|
server.listen(port, host, function () {
|
|
25
25
|
const cyan = '\x1b[36m%s\x1b[0m';
|
|
26
26
|
console.log(cyan, 'Server listening on port:', port);
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(
|
|
28
|
+
cyan,
|
|
29
|
+
'Open in browser:',
|
|
30
|
+
`http://${host}:${port}/parseParams/foo/bar`,
|
|
31
|
+
);
|
|
28
32
|
});
|
|
@@ -4,25 +4,29 @@ import {HttpMethod} from '../src/route.js';
|
|
|
4
4
|
|
|
5
5
|
const router = new TrieRouter();
|
|
6
6
|
|
|
7
|
-
// регистрация
|
|
8
|
-
//
|
|
7
|
+
// регистрация маршрута для разбора
|
|
8
|
+
// передаваемых параметров запроса
|
|
9
9
|
router.defineRoute({
|
|
10
10
|
method: HttpMethod.GET,
|
|
11
|
-
path: '/
|
|
11
|
+
path: '/parseQuery',
|
|
12
12
|
handler: ({query}) => query,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
// и
|
|
15
|
+
// создание экземпляра HTTP сервера
|
|
16
|
+
// и подключение обработчика запросов
|
|
17
17
|
const server = new http.Server();
|
|
18
18
|
server.on('request', router.requestListener);
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// прослушивание входящих запросов
|
|
21
21
|
// на указанный адрес и порт
|
|
22
22
|
const port = 3000;
|
|
23
23
|
const host = '0.0.0.0';
|
|
24
24
|
server.listen(port, host, function () {
|
|
25
25
|
const cyan = '\x1b[36m%s\x1b[0m';
|
|
26
26
|
console.log(cyan, 'Server listening on port:', port);
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(
|
|
28
|
+
cyan,
|
|
29
|
+
'Open in browser:',
|
|
30
|
+
`http://${host}:${port}/parseQuery?foo=bar&baz=qux`,
|
|
31
|
+
);
|
|
28
32
|
});
|
|
@@ -4,7 +4,7 @@ import {HttpMethod} from '../src/route.js';
|
|
|
4
4
|
|
|
5
5
|
const router = new TrieRouter();
|
|
6
6
|
|
|
7
|
-
// регистрация
|
|
7
|
+
// регистрация маршрута для вывода
|
|
8
8
|
// времени работы сервера
|
|
9
9
|
router.defineRoute({
|
|
10
10
|
method: HttpMethod.GET,
|
|
@@ -24,12 +24,12 @@ router.defineRoute({
|
|
|
24
24
|
},
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
// и
|
|
27
|
+
// создание экземпляра HTTP сервера
|
|
28
|
+
// и подключение обработчика запросов
|
|
29
29
|
const server = new http.Server();
|
|
30
30
|
server.on('request', router.requestListener);
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// прослушивание входящих запросов
|
|
33
33
|
// на указанный адрес и порт
|
|
34
34
|
const port = 3000;
|
|
35
35
|
const host = '0.0.0.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/js-trie-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
|
|
5
5
|
"author": "Mikhail Evstropov <e22m4u@yandex.ru>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,30 +38,31 @@
|
|
|
38
38
|
"prepare": "husky"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"@e22m4u/js-debug": "~0.3.1",
|
|
41
42
|
"@e22m4u/js-format": "~0.2.0",
|
|
42
|
-
"@e22m4u/js-path-trie": "~0.0.
|
|
43
|
-
"@e22m4u/js-service": "~0.
|
|
43
|
+
"@e22m4u/js-path-trie": "~0.0.11",
|
|
44
|
+
"@e22m4u/js-service": "~0.4.0",
|
|
44
45
|
"debug": "~4.4.3",
|
|
45
46
|
"http-errors": "~2.0.0",
|
|
46
47
|
"statuses": "~2.0.2"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
|
-
"@commitlint/cli": "~
|
|
50
|
-
"@commitlint/config-conventional": "~
|
|
50
|
+
"@commitlint/cli": "~20.1.0",
|
|
51
|
+
"@commitlint/config-conventional": "~20.0.0",
|
|
51
52
|
"@eslint/js": "~9.36.0",
|
|
52
53
|
"@types/chai-as-promised": "~8.0.2",
|
|
53
54
|
"c8": "~10.1.3",
|
|
54
|
-
"chai": "~6.0
|
|
55
|
+
"chai": "~6.2.0",
|
|
55
56
|
"chai-as-promised": "~8.0.2",
|
|
56
57
|
"esbuild": "~0.25.10",
|
|
57
58
|
"eslint": "~9.36.0",
|
|
58
59
|
"eslint-config-prettier": "~10.1.8",
|
|
59
60
|
"eslint-plugin-chai-expect": "~3.1.0",
|
|
60
|
-
"eslint-plugin-jsdoc": "~60.
|
|
61
|
+
"eslint-plugin-jsdoc": "~60.5.0",
|
|
61
62
|
"eslint-plugin-mocha": "~11.1.0",
|
|
62
63
|
"globals": "~16.4.0",
|
|
63
64
|
"husky": "~9.1.7",
|
|
64
|
-
"mocha": "~11.7.
|
|
65
|
+
"mocha": "~11.7.3",
|
|
65
66
|
"prettier": "~3.6.2",
|
|
66
67
|
"rimraf": "~6.0.1",
|
|
67
68
|
"typescript": "~5.9.2"
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {Service} from '@e22m4u/js-service';
|
|
1
|
+
import {DebuggableService as BaseDebuggableService} from '@e22m4u/js-service';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Debuggable service.
|
|
6
5
|
*/
|
|
7
|
-
declare class DebuggableService extends
|
|
8
|
-
/**
|
|
9
|
-
* Debug.
|
|
10
|
-
*
|
|
11
|
-
* @protected
|
|
12
|
-
*/
|
|
13
|
-
protected debug: Debugger;
|
|
14
|
-
}
|
|
6
|
+
declare class DebuggableService extends BaseDebuggableService {}
|
|
@@ -1,28 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {toCamelCase} from './utils/index.js';
|
|
3
|
-
import {createDebugger} from './utils/index.js';
|
|
4
|
-
import {ServiceContainer} from '@e22m4u/js-service';
|
|
1
|
+
import {DebuggableService as BaseDebuggableService} from '@e22m4u/js-service';
|
|
5
2
|
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
4
|
+
* @typedef {import('@e22m4u/js-service').ServiceContainer} ServiceContainer
|
|
8
5
|
*/
|
|
9
|
-
export class DebuggableService extends Service {
|
|
10
|
-
/**
|
|
11
|
-
* Debug.
|
|
12
|
-
*
|
|
13
|
-
* @type {Function}
|
|
14
|
-
*/
|
|
15
|
-
debug;
|
|
16
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Module debug namespace.
|
|
9
|
+
*/
|
|
10
|
+
export const MODULE_DEBUG_NAMESPACE = 'jsTrieRouter';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Debuggable service.
|
|
14
|
+
*/
|
|
15
|
+
export class DebuggableService extends BaseDebuggableService {
|
|
17
16
|
/**
|
|
18
17
|
* Constructor.
|
|
19
18
|
*
|
|
20
19
|
* @param {ServiceContainer} container
|
|
21
20
|
*/
|
|
22
|
-
constructor(container) {
|
|
23
|
-
super(container
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
constructor(container = undefined) {
|
|
22
|
+
super(container, {
|
|
23
|
+
namespace: MODULE_DEBUG_NAMESPACE,
|
|
24
|
+
noEnvironmentNamespace: true,
|
|
25
|
+
});
|
|
27
26
|
}
|
|
28
27
|
}
|
|
@@ -24,14 +24,14 @@ export class HookInvoker extends DebuggableService {
|
|
|
24
24
|
throw new Errorf(
|
|
25
25
|
'The parameter "route" of ' +
|
|
26
26
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
27
|
-
'should be a Route instance, but %v given.',
|
|
27
|
+
'should be a Route instance, but %v was given.',
|
|
28
28
|
route,
|
|
29
29
|
);
|
|
30
30
|
if (!hookType || typeof hookType !== 'string')
|
|
31
31
|
throw new Errorf(
|
|
32
32
|
'The parameter "hookType" of ' +
|
|
33
33
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
34
|
-
'should be a non-empty String, but %v given.',
|
|
34
|
+
'should be a non-empty String, but %v was given.',
|
|
35
35
|
hookType,
|
|
36
36
|
);
|
|
37
37
|
if (!Object.values(HookType).includes(hookType))
|
|
@@ -45,7 +45,7 @@ export class HookInvoker extends DebuggableService {
|
|
|
45
45
|
throw new Errorf(
|
|
46
46
|
'The parameter "response" of ' +
|
|
47
47
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
48
|
-
'should be a ServerResponse instance, but %v given.',
|
|
48
|
+
'should be a ServerResponse instance, but %v was given.',
|
|
49
49
|
response,
|
|
50
50
|
);
|
|
51
51
|
}
|
|
@@ -82,7 +82,6 @@ export class HookInvoker extends DebuggableService {
|
|
|
82
82
|
// если ответ уже был отправлен,
|
|
83
83
|
// то останавливаем выполнение
|
|
84
84
|
if (isResponseSent(response)) {
|
|
85
|
-
result = response;
|
|
86
85
|
return;
|
|
87
86
|
}
|
|
88
87
|
// если предыдущий Promise вернул значение
|
|
@@ -18,7 +18,7 @@ describe('HookInvoker', function () {
|
|
|
18
18
|
format(
|
|
19
19
|
'The parameter "route" of ' +
|
|
20
20
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
21
|
-
'should be a Route instance, but %s given.',
|
|
21
|
+
'should be a Route instance, but %s was given.',
|
|
22
22
|
v,
|
|
23
23
|
);
|
|
24
24
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -54,7 +54,7 @@ describe('HookInvoker', function () {
|
|
|
54
54
|
format(
|
|
55
55
|
'The parameter "hookType" of ' +
|
|
56
56
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
57
|
-
'should be a non-empty String, but %s given.',
|
|
57
|
+
'should be a non-empty String, but %s was given.',
|
|
58
58
|
v,
|
|
59
59
|
);
|
|
60
60
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -98,7 +98,7 @@ describe('HookInvoker', function () {
|
|
|
98
98
|
format(
|
|
99
99
|
'The parameter "response" of ' +
|
|
100
100
|
'the HookInvoker.invokeAndContinueUntilValueReceived ' +
|
|
101
|
-
'should be a ServerResponse instance, but %s given.',
|
|
101
|
+
'should be a ServerResponse instance, but %s was given.',
|
|
102
102
|
v,
|
|
103
103
|
);
|
|
104
104
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {Errorf} from '@e22m4u/js-format';
|
|
2
|
-
import {DebuggableService} from '../debuggable-service.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Hook type.
|
|
@@ -17,7 +16,7 @@ export const HookType = {
|
|
|
17
16
|
/**
|
|
18
17
|
* Hook registry.
|
|
19
18
|
*/
|
|
20
|
-
export class HookRegistry
|
|
19
|
+
export class HookRegistry {
|
|
21
20
|
/**
|
|
22
21
|
* Hooks.
|
|
23
22
|
*
|
|
@@ -35,12 +34,12 @@ export class HookRegistry extends DebuggableService {
|
|
|
35
34
|
*/
|
|
36
35
|
addHook(type, hook) {
|
|
37
36
|
if (!type || typeof type !== 'string')
|
|
38
|
-
throw new Errorf('The hook type is required, but %v given.', type);
|
|
37
|
+
throw new Errorf('The hook type is required, but %v was given.', type);
|
|
39
38
|
if (!Object.values(HookType).includes(type))
|
|
40
39
|
throw new Errorf('The hook type %v is not supported.', type);
|
|
41
40
|
if (!hook || typeof hook !== 'function')
|
|
42
41
|
throw new Errorf(
|
|
43
|
-
'The hook %v should be a Function, but %v given.',
|
|
42
|
+
'The hook %v should be a Function, but %v was given.',
|
|
44
43
|
type,
|
|
45
44
|
hook,
|
|
46
45
|
);
|
|
@@ -59,12 +58,12 @@ export class HookRegistry extends DebuggableService {
|
|
|
59
58
|
*/
|
|
60
59
|
hasHook(type, hook) {
|
|
61
60
|
if (!type || typeof type !== 'string')
|
|
62
|
-
throw new Errorf('The hook type is required, but %v given.', type);
|
|
61
|
+
throw new Errorf('The hook type is required, but %v was given.', type);
|
|
63
62
|
if (!Object.values(HookType).includes(type))
|
|
64
63
|
throw new Errorf('The hook type %v is not supported.', type);
|
|
65
64
|
if (!hook || typeof hook !== 'function')
|
|
66
65
|
throw new Errorf(
|
|
67
|
-
'The hook %v should be a Function, but %v given.',
|
|
66
|
+
'The hook %v should be a Function, but %v was given.',
|
|
68
67
|
type,
|
|
69
68
|
hook,
|
|
70
69
|
);
|
|
@@ -80,7 +79,7 @@ export class HookRegistry extends DebuggableService {
|
|
|
80
79
|
*/
|
|
81
80
|
getHooks(type) {
|
|
82
81
|
if (!type || typeof type !== 'string')
|
|
83
|
-
throw new Errorf('The hook type is required, but %v given.', type);
|
|
82
|
+
throw new Errorf('The hook type is required, but %v was given.', type);
|
|
84
83
|
if (!Object.values(HookType).includes(type))
|
|
85
84
|
throw new Errorf('The hook type %v is not supported.', type);
|
|
86
85
|
return this._hooks.get(type) || [];
|
|
@@ -8,7 +8,8 @@ describe('HookRegistry', function () {
|
|
|
8
8
|
it('requires the parameter "type" to be a non-empty String', function () {
|
|
9
9
|
const s = new HookRegistry();
|
|
10
10
|
const throwable = v => () => s.addHook(v, () => undefined);
|
|
11
|
-
const error = v =>
|
|
11
|
+
const error = v =>
|
|
12
|
+
format('The hook type is required, but %s was given.', v);
|
|
12
13
|
expect(throwable('')).to.throw(error('""'));
|
|
13
14
|
expect(throwable(10)).to.throw(error('10'));
|
|
14
15
|
expect(throwable(0)).to.throw(error('0'));
|
|
@@ -26,7 +27,10 @@ describe('HookRegistry', function () {
|
|
|
26
27
|
const s = new HookRegistry();
|
|
27
28
|
const throwable = v => () => s.addHook(HookType.PRE_HANDLER, v);
|
|
28
29
|
const error = v =>
|
|
29
|
-
format(
|
|
30
|
+
format(
|
|
31
|
+
'The hook "preHandler" should be a Function, but %s was given.',
|
|
32
|
+
v,
|
|
33
|
+
);
|
|
30
34
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
31
35
|
expect(throwable('')).to.throw(error('""'));
|
|
32
36
|
expect(throwable(10)).to.throw(error('10'));
|
|
@@ -69,7 +73,8 @@ describe('HookRegistry', function () {
|
|
|
69
73
|
it('requires the parameter "type" to be a non-empty String', function () {
|
|
70
74
|
const s = new HookRegistry();
|
|
71
75
|
const throwable = v => () => s.hasHook(v, () => undefined);
|
|
72
|
-
const error = v =>
|
|
76
|
+
const error = v =>
|
|
77
|
+
format('The hook type is required, but %s was given.', v);
|
|
73
78
|
expect(throwable('')).to.throw(error('""'));
|
|
74
79
|
expect(throwable(10)).to.throw(error('10'));
|
|
75
80
|
expect(throwable(0)).to.throw(error('0'));
|
|
@@ -87,7 +92,10 @@ describe('HookRegistry', function () {
|
|
|
87
92
|
const s = new HookRegistry();
|
|
88
93
|
const throwable = v => () => s.hasHook(HookType.PRE_HANDLER, v);
|
|
89
94
|
const error = v =>
|
|
90
|
-
format(
|
|
95
|
+
format(
|
|
96
|
+
'The hook "preHandler" should be a Function, but %s was given.',
|
|
97
|
+
v,
|
|
98
|
+
);
|
|
91
99
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
92
100
|
expect(throwable('')).to.throw(error('""'));
|
|
93
101
|
expect(throwable(10)).to.throw(error('10'));
|
|
@@ -123,7 +131,8 @@ describe('HookRegistry', function () {
|
|
|
123
131
|
it('requires the parameter "type" to be a non-empty String', function () {
|
|
124
132
|
const s = new HookRegistry();
|
|
125
133
|
const throwable = v => () => s.getHooks(v);
|
|
126
|
-
const error = v =>
|
|
134
|
+
const error = v =>
|
|
135
|
+
format('The hook type is required, but %s was given.', v);
|
|
127
136
|
expect(throwable('')).to.throw(error('""'));
|
|
128
137
|
expect(throwable(10)).to.throw(error('10'));
|
|
129
138
|
expect(throwable(0)).to.throw(error('0'));
|
|
@@ -45,13 +45,13 @@ export class BodyParser extends DebuggableService {
|
|
|
45
45
|
if (!mediaType || typeof mediaType !== 'string')
|
|
46
46
|
throw new Errorf(
|
|
47
47
|
'The parameter "mediaType" of BodyParser.defineParser ' +
|
|
48
|
-
'should be a non-empty String, but %v given.',
|
|
48
|
+
'should be a non-empty String, but %v was given.',
|
|
49
49
|
mediaType,
|
|
50
50
|
);
|
|
51
51
|
if (!parser || typeof parser !== 'function')
|
|
52
52
|
throw new Errorf(
|
|
53
53
|
'The parameter "parser" of BodyParser.defineParser ' +
|
|
54
|
-
'should be a Function, but %v given.',
|
|
54
|
+
'should be a Function, but %v was given.',
|
|
55
55
|
parser,
|
|
56
56
|
);
|
|
57
57
|
this._parsers[mediaType] = parser;
|
|
@@ -68,7 +68,7 @@ export class BodyParser extends DebuggableService {
|
|
|
68
68
|
if (!mediaType || typeof mediaType !== 'string')
|
|
69
69
|
throw new Errorf(
|
|
70
70
|
'The parameter "mediaType" of BodyParser.hasParser ' +
|
|
71
|
-
'should be a non-empty String, but %v given.',
|
|
71
|
+
'should be a non-empty String, but %v was given.',
|
|
72
72
|
mediaType,
|
|
73
73
|
);
|
|
74
74
|
return Boolean(this._parsers[mediaType]);
|
|
@@ -84,7 +84,7 @@ export class BodyParser extends DebuggableService {
|
|
|
84
84
|
if (!mediaType || typeof mediaType !== 'string')
|
|
85
85
|
throw new Errorf(
|
|
86
86
|
'The parameter "mediaType" of BodyParser.deleteParser ' +
|
|
87
|
-
'should be a non-empty String, but %v given.',
|
|
87
|
+
'should be a non-empty String, but %v was given.',
|
|
88
88
|
mediaType,
|
|
89
89
|
);
|
|
90
90
|
const parser = this._parsers[mediaType];
|
|
@@ -100,8 +100,9 @@ export class BodyParser extends DebuggableService {
|
|
|
100
100
|
* @returns {Promise<*>|undefined}
|
|
101
101
|
*/
|
|
102
102
|
parse(req) {
|
|
103
|
+
const debug = this.getDebuggerFor(this.parse);
|
|
103
104
|
if (!METHODS_WITH_BODY.includes(req.method.toUpperCase())) {
|
|
104
|
-
|
|
105
|
+
debug(
|
|
105
106
|
'Body parsing was skipped for the %s request.',
|
|
106
107
|
req.method.toUpperCase(),
|
|
107
108
|
);
|
|
@@ -112,8 +113,8 @@ export class BodyParser extends DebuggableService {
|
|
|
112
113
|
'$1',
|
|
113
114
|
);
|
|
114
115
|
if (!contentType) {
|
|
115
|
-
|
|
116
|
-
'Body parsing was skipped because the request
|
|
116
|
+
debug(
|
|
117
|
+
'Body parsing was skipped because the request had no content type.',
|
|
117
118
|
);
|
|
118
119
|
return;
|
|
119
120
|
}
|
|
@@ -126,7 +127,7 @@ export class BodyParser extends DebuggableService {
|
|
|
126
127
|
const parser = this._parsers[mediaType];
|
|
127
128
|
if (!parser) {
|
|
128
129
|
if (UNPARSABLE_MEDIA_TYPES.includes(mediaType)) {
|
|
129
|
-
|
|
130
|
+
debug('Body parsing was skipped for %v.', mediaType);
|
|
130
131
|
return;
|
|
131
132
|
}
|
|
132
133
|
throw createError(
|
|
@@ -154,8 +155,6 @@ export function parseJsonBody(input) {
|
|
|
154
155
|
try {
|
|
155
156
|
return JSON.parse(input);
|
|
156
157
|
} catch (error) {
|
|
157
|
-
|
|
158
|
-
console.warn(error);
|
|
159
|
-
throw createError(HttpErrors.BadRequest, 'Unable to parse request body.');
|
|
158
|
+
throw createError(HttpErrors.BadRequest, error.message);
|
|
160
159
|
}
|
|
161
160
|
}
|
|
@@ -16,7 +16,7 @@ describe('BodyParser', function () {
|
|
|
16
16
|
const error = v =>
|
|
17
17
|
format(
|
|
18
18
|
'The parameter "mediaType" of BodyParser.defineParser ' +
|
|
19
|
-
'should be a non-empty String, but %s given.',
|
|
19
|
+
'should be a non-empty String, but %s was given.',
|
|
20
20
|
v,
|
|
21
21
|
);
|
|
22
22
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -38,7 +38,7 @@ describe('BodyParser', function () {
|
|
|
38
38
|
const error = v =>
|
|
39
39
|
format(
|
|
40
40
|
'The parameter "parser" of BodyParser.defineParser ' +
|
|
41
|
-
'should be a Function, but %s given.',
|
|
41
|
+
'should be a Function, but %s was given.',
|
|
42
42
|
v,
|
|
43
43
|
);
|
|
44
44
|
expect(throwable('str')).to.throw(error('"str"'));
|
|
@@ -76,7 +76,7 @@ describe('BodyParser', function () {
|
|
|
76
76
|
const error = v =>
|
|
77
77
|
format(
|
|
78
78
|
'The parameter "mediaType" of BodyParser.hasParser ' +
|
|
79
|
-
'should be a non-empty String, but %s given.',
|
|
79
|
+
'should be a non-empty String, but %s was given.',
|
|
80
80
|
v,
|
|
81
81
|
);
|
|
82
82
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -111,7 +111,7 @@ describe('BodyParser', function () {
|
|
|
111
111
|
const error = v =>
|
|
112
112
|
format(
|
|
113
113
|
'The parameter "mediaType" of BodyParser.deleteParser ' +
|
|
114
|
-
'should be a non-empty String, but %s given.',
|
|
114
|
+
'should be a non-empty String, but %s was given.',
|
|
115
115
|
v,
|
|
116
116
|
);
|
|
117
117
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {IncomingMessage} from 'http';
|
|
2
|
+
import {ParsedCookies} from '../utils/index.js';
|
|
3
|
+
import {DebuggableService} from '../debuggable-service.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cookies parser.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CookiesParser extends DebuggableService {
|
|
9
|
+
/**
|
|
10
|
+
* Parse.
|
|
11
|
+
*
|
|
12
|
+
* @param req
|
|
13
|
+
*/
|
|
14
|
+
parse(req: IncomingMessage): ParsedCookies;
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {parseCookies} from '../utils/index.js';
|
|
2
|
+
import {getRequestPathname} from '../utils/index.js';
|
|
3
|
+
import {DebuggableService} from '../debuggable-service.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cookies parser.
|
|
7
|
+
*/
|
|
8
|
+
export class CookiesParser extends DebuggableService {
|
|
9
|
+
/**
|
|
10
|
+
* Parse
|
|
11
|
+
*
|
|
12
|
+
* @param {import('http').IncomingMessage} req
|
|
13
|
+
* @returns {object}
|
|
14
|
+
*/
|
|
15
|
+
parse(req) {
|
|
16
|
+
const debug = this.getDebuggerFor(this.parse);
|
|
17
|
+
const cookiesString = req.headers['cookie'] || '';
|
|
18
|
+
const cookies = parseCookies(cookiesString);
|
|
19
|
+
const cookiesKeys = Object.keys(cookies);
|
|
20
|
+
if (cookiesKeys.length) {
|
|
21
|
+
cookiesKeys.forEach(key => {
|
|
22
|
+
debug('The cookie %v had the value %v.', key, cookies[key]);
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
debug(
|
|
26
|
+
'The request %s %v had no cookies.',
|
|
27
|
+
req.method,
|
|
28
|
+
getRequestPathname(req),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return cookies;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {expect} from '../chai.js';
|
|
2
|
-
import {
|
|
2
|
+
import {CookiesParser} from './cookies-parser.js';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
4
|
+
describe('CookiesParser', function () {
|
|
5
5
|
describe('parse', function () {
|
|
6
|
-
it('returns
|
|
7
|
-
const parser = new
|
|
6
|
+
it('returns parsed cookies as a plain object', function () {
|
|
7
|
+
const parser = new CookiesParser();
|
|
8
8
|
const value = 'pkg=math; equation=E%3Dmc%5E2';
|
|
9
9
|
const result = parser.parse({url: '', headers: {cookie: value}});
|
|
10
10
|
expect(result).to.have.property('pkg', 'math');
|
|
@@ -12,13 +12,13 @@ describe('CookieParser', function () {
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
it('returns an empty object if no cookies', function () {
|
|
15
|
-
const parser = new
|
|
15
|
+
const parser = new CookiesParser();
|
|
16
16
|
const result = parser.parse({url: '', headers: {}});
|
|
17
17
|
expect(result).to.be.eql({});
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
it('returns an empty object for an empty string', function () {
|
|
21
|
-
const parser = new
|
|
21
|
+
const parser = new CookiesParser();
|
|
22
22
|
const result = parser.parse({url: '', headers: {cookie: ''}});
|
|
23
23
|
expect(result).to.be.eql({});
|
|
24
24
|
});
|
package/src/parsers/index.d.ts
CHANGED
package/src/parsers/index.js
CHANGED
|
@@ -13,16 +13,17 @@ export class QueryParser extends DebuggableService {
|
|
|
13
13
|
* @returns {object}
|
|
14
14
|
*/
|
|
15
15
|
parse(req) {
|
|
16
|
+
const debug = this.getDebuggerFor(this.parse);
|
|
16
17
|
const queryStr = req.url.replace(/^[^?]*\??/, '');
|
|
17
18
|
const query = queryStr ? querystring.parse(queryStr) : {};
|
|
18
19
|
const queryKeys = Object.keys(query);
|
|
19
20
|
if (queryKeys.length) {
|
|
20
21
|
queryKeys.forEach(key => {
|
|
21
|
-
|
|
22
|
+
debug('The query parameter %v had the value %v.', key, query[key]);
|
|
22
23
|
});
|
|
23
24
|
} else {
|
|
24
|
-
|
|
25
|
-
'The request %s %v
|
|
25
|
+
debug(
|
|
26
|
+
'The request %s %v had no query parameters.',
|
|
26
27
|
req.method,
|
|
27
28
|
getRequestPathname(req),
|
|
28
29
|
);
|