@e22m4u/js-trie-router 0.5.14 → 0.6.1

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.
Files changed (65) hide show
  1. package/dist/cjs/index.cjs +137 -119
  2. package/package.json +6 -6
  3. package/src/branch/merge-router-branch-definition.spec.js +32 -16
  4. package/src/branch/merge-router-branch-definitions.js +6 -3
  5. package/src/branch/router-branch.js +5 -5
  6. package/src/branch/router-branch.spec.js +81 -19
  7. package/src/branch/validate-router-branch-definition.js +16 -9
  8. package/src/branch/validate-router-branch-definition.spec.js +28 -19
  9. package/src/debuggable-service.spec.js +10 -2
  10. package/src/hooks/router-hook-invoker.js +5 -3
  11. package/src/hooks/router-hook-invoker.spec.js +19 -17
  12. package/src/hooks/router-hook-registry.spec.js +32 -18
  13. package/src/parsers/body-parser.d.ts +11 -4
  14. package/src/parsers/body-parser.js +28 -12
  15. package/src/parsers/body-parser.spec.js +136 -92
  16. package/src/parsers/cookies-parser.spec.js +3 -3
  17. package/src/parsers/query-parser.spec.js +3 -3
  18. package/src/parsers/request-parser.js +2 -2
  19. package/src/parsers/request-parser.spec.js +9 -9
  20. package/src/request-context.js +7 -8
  21. package/src/request-context.spec.js +17 -18
  22. package/src/route/route.spec.js +47 -36
  23. package/src/route/validate-route-definition.js +14 -6
  24. package/src/route/validate-route-definition.spec.js +32 -18
  25. package/src/route-registry.js +3 -3
  26. package/src/route-registry.spec.js +6 -6
  27. package/src/router-options.js +2 -2
  28. package/src/router-options.spec.js +6 -6
  29. package/src/senders/data-sender.spec.js +7 -7
  30. package/src/senders/error-sender.spec.js +3 -3
  31. package/src/trie-router.d.ts +6 -6
  32. package/src/trie-router.js +4 -4
  33. package/src/trie-router.spec.js +90 -23
  34. package/src/utils/clone-deep.spec.js +7 -7
  35. package/src/utils/create-cookie-string.js +1 -2
  36. package/src/utils/create-cookie-string.spec.js +4 -8
  37. package/src/utils/create-error.js +2 -4
  38. package/src/utils/create-error.spec.js +5 -13
  39. package/src/utils/create-request-mock.d.ts +5 -5
  40. package/src/utils/create-request-mock.js +74 -91
  41. package/src/utils/create-request-mock.spec.js +61 -98
  42. package/src/utils/create-response-mock.spec.js +13 -13
  43. package/src/utils/create-route-mock.spec.js +6 -6
  44. package/src/utils/fetch-request-body.js +3 -4
  45. package/src/utils/fetch-request-body.spec.js +22 -23
  46. package/src/utils/get-request-pathname.js +2 -2
  47. package/src/utils/get-request-pathname.spec.js +11 -4
  48. package/src/utils/index.d.ts +0 -1
  49. package/src/utils/index.js +0 -1
  50. package/src/utils/is-promise.spec.js +2 -2
  51. package/src/utils/is-readable-stream.spec.js +2 -2
  52. package/src/utils/is-response-sent.js +2 -2
  53. package/src/utils/is-response-sent.spec.js +5 -5
  54. package/src/utils/is-writable-stream.spec.js +2 -2
  55. package/src/utils/merge-deep.spec.js +2 -2
  56. package/src/utils/parse-content-type.js +1 -2
  57. package/src/utils/parse-content-type.spec.js +7 -11
  58. package/src/utils/parse-cookie-string.js +1 -2
  59. package/src/utils/parse-cookie-string.spec.js +2 -6
  60. package/src/utils/to-camel-case.js +1 -2
  61. package/src/utils/to-camel-case.spec.js +3 -7
  62. package/src/utils/to-pascal-case.spec.js +1 -1
  63. package/src/utils/normalize-path.d.ts +0 -12
  64. package/src/utils/normalize-path.js +0 -22
  65. package/src/utils/normalize-path.spec.js +0 -56
@@ -65,7 +65,6 @@ __export(index_exports, {
65
65
  isResponseSent: () => isResponseSent,
66
66
  isWritableStream: () => isWritableStream,
67
67
  mergeDeep: () => mergeDeep,
68
- normalizePath: () => normalizePath,
69
68
  parseContentType: () => parseContentType,
70
69
  parseCookieString: () => parseCookieString,
71
70
  parseJsonBody: () => parseJsonBody,
@@ -167,13 +166,13 @@ var import_js_format = require("@e22m4u/js-format");
167
166
  function createError(errorCtor, message, ...args) {
168
167
  if (typeof errorCtor !== "function") {
169
168
  throw new import_js_format.InvalidArgumentError(
170
- 'The first parameter of "createError" must be a constructor, but %v was given.',
169
+ 'Parameter "errorCtor" must be a Function, but %v was given.',
171
170
  errorCtor
172
171
  );
173
172
  }
174
173
  if (message != null && typeof message !== "string") {
175
174
  throw new import_js_format.InvalidArgumentError(
176
- 'The second parameter of "createError" must be a String, but %v was given.',
175
+ 'Parameter "message" must be a String, but %v was given.',
177
176
  message
178
177
  );
179
178
  }
@@ -190,7 +189,7 @@ var import_js_format2 = require("@e22m4u/js-format");
190
189
  function toCamelCase(input) {
191
190
  if (typeof input !== "string") {
192
191
  throw new import_js_format2.InvalidArgumentError(
193
- 'The first parameter of "toCamelCase" must be a String, but %v was given.',
192
+ 'Parameter "input" must be a String, but %v was given.',
194
193
  input
195
194
  );
196
195
  }
@@ -207,22 +206,12 @@ function toPascalCase(input) {
207
206
  }
208
207
  __name(toPascalCase, "toPascalCase");
209
208
 
210
- // src/utils/normalize-path.js
211
- function normalizePath(value, noStartingSlash = false) {
212
- if (typeof value !== "string") {
213
- return "/";
214
- }
215
- const res = value.trim().replace(/\/+/g, "/").replace(/(^\/|\/$)/g, "");
216
- return noStartingSlash ? res : "/" + res;
217
- }
218
- __name(normalizePath, "normalizePath");
219
-
220
209
  // src/utils/is-response-sent.js
221
210
  var import_js_format3 = require("@e22m4u/js-format");
222
211
  function isResponseSent(response) {
223
212
  if (!response || typeof response !== "object" || Array.isArray(response) || typeof response.headersSent !== "boolean") {
224
213
  throw new import_js_format3.InvalidArgumentError(
225
- 'The first parameter of "isResponseSent" must be an instance of ServerResponse, but %v was given.',
214
+ 'Parameter "response" must be an instance of ServerResponse, but %v was given.',
226
215
  response
227
216
  );
228
217
  }
@@ -254,7 +243,7 @@ var import_js_format4 = require("@e22m4u/js-format");
254
243
  function parseContentType(input) {
255
244
  if (typeof input !== "string") {
256
245
  throw new import_js_format4.InvalidArgumentError(
257
- "The first parameter of `parseContentType` must be a String, but %v was given.",
246
+ 'Parameter "input" must be a String, but %v was given.',
258
247
  input
259
248
  );
260
249
  }
@@ -300,13 +289,13 @@ var CHARACTER_ENCODING_LIST = [
300
289
  function fetchRequestBody(request, bodyBytesLimit = 0) {
301
290
  if (!(request instanceof import_http.IncomingMessage)) {
302
291
  throw new import_js_format5.InvalidArgumentError(
303
- 'The first parameter of "fetchRequestBody" must be an IncomingMessage instance, but %v was given.',
292
+ 'Parameter "request" must be an instance of IncomingMessage, but %v was given.',
304
293
  request
305
294
  );
306
295
  }
307
296
  if (typeof bodyBytesLimit !== "number") {
308
297
  throw new import_js_format5.InvalidArgumentError(
309
- 'The parameter "bodyBytesLimit" of "fetchRequestBody" must be a Number, but %v was given.',
298
+ 'Parameter "bodyBytesLimit" must be a Number, but %v was given.',
310
299
  bodyBytesLimit
311
300
  );
312
301
  }
@@ -393,7 +382,7 @@ var import_js_format6 = require("@e22m4u/js-format");
393
382
  function parseCookieString(input) {
394
383
  if (typeof input !== "string") {
395
384
  throw new import_js_format6.InvalidArgumentError(
396
- 'The first parameter of "parseCookieString" must be a String, but %v was given.',
385
+ 'Parameter "input" must be a String, but %v was given.',
397
386
  input
398
387
  );
399
388
  }
@@ -421,7 +410,7 @@ var import_js_format7 = require("@e22m4u/js-format");
421
410
  function createCookieString(data) {
422
411
  if (!data || typeof data !== "object" || Array.isArray(data)) {
423
412
  throw new import_js_format7.InvalidArgumentError(
424
- 'The first parameter of "createCookieString" must be an Object, but %v was given.',
413
+ "Cookie data must be an Object, but %v was given.",
425
414
  data
426
415
  );
427
416
  }
@@ -441,111 +430,111 @@ function createCookieString(data) {
441
430
  __name(createCookieString, "createCookieString");
442
431
 
443
432
  // src/utils/create-request-mock.js
444
- function createRequestMock(patch) {
445
- if (patch != null && typeof patch !== "object" || Array.isArray(patch)) {
433
+ function createRequestMock(options) {
434
+ if (options != null && typeof options !== "object" || Array.isArray(options)) {
446
435
  throw new import_js_format8.InvalidArgumentError(
447
- 'The first parameter of "createRequestMock" must be an Object, but %v was given.',
448
- patch
436
+ 'Parameter "options" must be an Object, but %v was given.',
437
+ options
449
438
  );
450
439
  }
451
- patch = patch || {};
452
- if (patch.host != null && typeof patch.host !== "string") {
440
+ options = options || {};
441
+ if (options.host != null && typeof options.host !== "string") {
453
442
  throw new import_js_format8.InvalidArgumentError(
454
- 'The parameter "host" of "createRequestMock" must be a String, but %v was given.',
455
- patch.host
443
+ 'Option "host" must be a String, but %v was given.',
444
+ options.host
456
445
  );
457
446
  }
458
- if (patch.method != null && typeof patch.method !== "string") {
447
+ if (options.method != null && typeof options.method !== "string") {
459
448
  throw new import_js_format8.InvalidArgumentError(
460
- 'The parameter "method" of "createRequestMock" must be a String, but %v was given.',
461
- patch.method
449
+ 'Option "method" must be a String, but %v was given.',
450
+ options.method
462
451
  );
463
452
  }
464
- if (patch.secure != null && typeof patch.secure !== "boolean") {
453
+ if (options.secure != null && typeof options.secure !== "boolean") {
465
454
  throw new import_js_format8.InvalidArgumentError(
466
- 'The parameter "secure" of "createRequestMock" must be a Boolean, but %v was given.',
467
- patch.secure
455
+ 'Option "secure" must be a Boolean, but %v was given.',
456
+ options.secure
468
457
  );
469
458
  }
470
- if (patch.path != null && typeof patch.path !== "string") {
459
+ if (options.path != null && typeof options.path !== "string") {
471
460
  throw new import_js_format8.InvalidArgumentError(
472
- 'The parameter "path" of "createRequestMock" must be a String, but %v was given.',
473
- patch.path
461
+ 'Option "path" must be a String, but %v was given.',
462
+ options.path
474
463
  );
475
464
  }
476
- if (patch.query != null && typeof patch.query !== "object" && typeof patch.query !== "string" || Array.isArray(patch.query)) {
465
+ if (options.query != null && typeof options.query !== "object" && typeof options.query !== "string" || Array.isArray(options.query)) {
477
466
  throw new import_js_format8.InvalidArgumentError(
478
- 'The parameter "query" of "createRequestMock" must be a String or Object, but %v was given.',
479
- patch.query
467
+ 'Option "query" must be a String or Object, but %v was given.',
468
+ options.query
480
469
  );
481
470
  }
482
- if (patch.cookies != null && typeof patch.cookies !== "string" && typeof patch.cookies !== "object" || Array.isArray(patch.cookies)) {
471
+ if (options.cookies != null && typeof options.cookies !== "string" && typeof options.cookies !== "object" || Array.isArray(options.cookies)) {
483
472
  throw new import_js_format8.InvalidArgumentError(
484
- 'The parameter "cookies" of "createRequestMock" must be a String or Object, but %v was given.',
485
- patch.cookies
473
+ 'Option "cookies" must be a String or Object, but %v was given.',
474
+ options.cookies
486
475
  );
487
476
  }
488
- if (patch.headers != null && typeof patch.headers !== "object" || Array.isArray(patch.headers)) {
477
+ if (options.headers != null && typeof options.headers !== "object" || Array.isArray(options.headers)) {
489
478
  throw new import_js_format8.InvalidArgumentError(
490
- 'The parameter "headers" of "createRequestMock" must be an Object, but %v was given.',
491
- patch.headers
479
+ 'Option "headers" must be an Object, but %v was given.',
480
+ options.headers
492
481
  );
493
482
  }
494
- if (patch.stream != null && !isReadableStream(patch.stream)) {
483
+ if (options.stream != null && !isReadableStream(options.stream)) {
495
484
  throw new import_js_format8.InvalidArgumentError(
496
- 'The parameter "stream" of "createRequestMock" must be a Stream, but %v was given.',
497
- patch.stream
485
+ 'Option "stream" must be a Stream, but %v was given.',
486
+ options.stream
498
487
  );
499
488
  }
500
- if (patch.encoding != null) {
501
- if (typeof patch.encoding !== "string") {
489
+ if (options.encoding != null) {
490
+ if (typeof options.encoding !== "string") {
502
491
  throw new import_js_format8.InvalidArgumentError(
503
- 'The parameter "encoding" of "createRequestMock" must be a String, but %v was given.',
504
- patch.encoding
492
+ 'Option "encoding" must be a String, but %v was given.',
493
+ options.encoding
505
494
  );
506
495
  }
507
- if (!CHARACTER_ENCODING_LIST.includes(patch.encoding)) {
496
+ if (!CHARACTER_ENCODING_LIST.includes(options.encoding)) {
508
497
  throw new import_js_format8.InvalidArgumentError(
509
498
  "Character encoding %v is not supported.",
510
- patch.encoding
499
+ options.encoding
511
500
  );
512
501
  }
513
502
  }
514
- if (patch.stream) {
515
- if (patch.secure != null) {
503
+ if (options.stream) {
504
+ if (options.secure != null) {
516
505
  throw new import_js_format8.InvalidArgumentError(
517
- 'The "createRequestMock" does not allow specifying the "stream" and "secure" options simultaneously.'
506
+ 'The "stream" and "secure" options cannot be used together.'
518
507
  );
519
508
  }
520
- if (patch.body != null) {
509
+ if (options.body != null) {
521
510
  throw new import_js_format8.InvalidArgumentError(
522
- 'The "createRequestMock" does not allow specifying the "stream" and "body" options simultaneously.'
511
+ 'The "stream" and "body" options cannot be used together.'
523
512
  );
524
513
  }
525
- if (patch.encoding != null) {
514
+ if (options.encoding != null) {
526
515
  throw new import_js_format8.InvalidArgumentError(
527
- 'The "createRequestMock" does not allow specifying the "stream" and "encoding" options simultaneously.'
516
+ 'The "stream" and "encoding" options cannot be used together.'
528
517
  );
529
518
  }
530
519
  }
531
- const request = patch.stream || createRequestStream(patch.secure, patch.body, patch.encoding);
532
- request.url = createRequestUrl(patch.path || "/", patch.query);
520
+ const request = options.stream || createRequestStream(options.secure, options.body, options.encoding);
521
+ request.url = createRequestUrl(options.path || "/", options.query);
533
522
  request.headers = createRequestHeaders(
534
- patch.host,
535
- patch.secure,
536
- patch.body,
537
- patch.cookies,
538
- patch.encoding,
539
- patch.headers
523
+ options.host,
524
+ options.secure,
525
+ options.body,
526
+ options.cookies,
527
+ options.encoding,
528
+ options.headers
540
529
  );
541
- request.method = (patch.method || "get").toUpperCase();
530
+ request.method = (options.method || "get").toUpperCase();
542
531
  return request;
543
532
  }
544
533
  __name(createRequestMock, "createRequestMock");
545
534
  function createRequestStream(secure, body, encoding) {
546
535
  if (encoding != null && typeof encoding !== "string") {
547
536
  throw new import_js_format8.InvalidArgumentError(
548
- 'The parameter "encoding" of "createRequestStream" must be a String, but %v was given.',
537
+ 'Parameter "encoding" must be a String, but %v was given.',
549
538
  encoding
550
539
  );
551
540
  }
@@ -571,13 +560,13 @@ __name(createRequestStream, "createRequestStream");
571
560
  function createRequestUrl(path, query) {
572
561
  if (typeof path !== "string") {
573
562
  throw new import_js_format8.InvalidArgumentError(
574
- 'The parameter "path" of "createRequestUrl" must be a String, but %v was given.',
563
+ 'Parameter "path" must be a String, but %v was given.',
575
564
  path
576
565
  );
577
566
  }
578
567
  if (query != null && typeof query !== "string" && typeof query !== "object" || Array.isArray(query)) {
579
568
  throw new import_js_format8.InvalidArgumentError(
580
- 'The parameter "query" of "createRequestUrl" must be a String or Object, but %v was given.',
569
+ 'Parameter "query" must be a String or Object, but %v was given.',
581
570
  query
582
571
  );
583
572
  }
@@ -596,34 +585,34 @@ __name(createRequestUrl, "createRequestUrl");
596
585
  function createRequestHeaders(host, secure, body, cookies, encoding, headers) {
597
586
  if (host != null && typeof host !== "string") {
598
587
  throw new import_js_format8.InvalidArgumentError(
599
- 'The parameter "host" of "createRequestHeaders" a non-empty String, but %v was given.',
588
+ 'Parameter "host" must be a non-empty String, but %v was given.',
600
589
  host
601
590
  );
602
591
  }
603
592
  host = host || "localhost";
604
593
  if (secure != null && typeof secure !== "boolean") {
605
594
  throw new import_js_format8.InvalidArgumentError(
606
- 'The parameter "secure" of "createRequestHeaders" must be a String, but %v was given.',
595
+ 'Parameter "secure" must be a String, but %v was given.',
607
596
  secure
608
597
  );
609
598
  }
610
599
  secure = Boolean(secure);
611
600
  if (cookies != null && typeof cookies !== "object" && typeof cookies !== "string" || Array.isArray(cookies)) {
612
601
  throw new import_js_format8.InvalidArgumentError(
613
- 'The parameter "cookies" of "createRequestHeaders" must be a String or Object, but %v was given.',
602
+ 'Parameter "cookies" must be a String or an Object, but %v was given.',
614
603
  cookies
615
604
  );
616
605
  }
617
606
  if (headers != null && typeof headers !== "object" || Array.isArray(headers)) {
618
607
  throw new import_js_format8.InvalidArgumentError(
619
- 'The parameter "headers" of "createRequestHeaders" must be an Object, but %v was given.',
608
+ 'Parameter "headers" must be an Object, but %v was given.',
620
609
  headers
621
610
  );
622
611
  }
623
612
  headers = headers || {};
624
613
  if (encoding != null && typeof encoding !== "string") {
625
614
  throw new import_js_format8.InvalidArgumentError(
626
- 'The parameter "encoding" of "createRequestHeaders" must be a String, but %v was given.',
615
+ 'Parameter "encoding" must be a String, but %v was given.',
627
616
  encoding
628
617
  );
629
618
  }
@@ -779,7 +768,7 @@ var import_js_format9 = require("@e22m4u/js-format");
779
768
  function getRequestPathname(request) {
780
769
  if (!request || typeof request !== "object" || Array.isArray(request) || typeof request.url !== "string") {
781
770
  throw new import_js_format9.InvalidArgumentError(
782
- 'The first parameter of "getRequestPathname" must be an instance of IncomingMessage, but %v was given.',
771
+ 'Parameter "request" must be an instance of IncomingMessage, but %v was given.',
783
772
  request
784
773
  );
785
774
  }
@@ -902,7 +891,7 @@ var _RouterHookInvoker = class _RouterHookInvoker extends DebuggableService {
902
891
  invokeAndContinueUntilValueReceived(route, hookType, response, ...args) {
903
892
  if (!route || !(route instanceof Route)) {
904
893
  throw new import_js_format11.InvalidArgumentError(
905
- 'Parameter "route" must be a Route instance, but %v was given.',
894
+ 'Parameter "route" must be an instance of Route, but %v was given.',
906
895
  route
907
896
  );
908
897
  }
@@ -920,7 +909,7 @@ var _RouterHookInvoker = class _RouterHookInvoker extends DebuggableService {
920
909
  }
921
910
  if (!response || typeof response !== "object" || Array.isArray(response) || typeof response.headersSent !== "boolean") {
922
911
  throw new import_js_format11.InvalidArgumentError(
923
- 'Parameter "response" must be a ServerResponse instance, but %v was given.',
912
+ 'Parameter "response" must be an instance of ServerResponse, but %v was given.',
924
913
  response
925
914
  );
926
915
  }
@@ -1006,9 +995,15 @@ function validateRouteDefinition(routeDef) {
1006
995
  routeDef.method
1007
996
  );
1008
997
  }
1009
- if (!routeDef.path || typeof routeDef.path !== "string") {
998
+ if (typeof routeDef.path !== "string") {
1010
999
  throw new import_js_format12.InvalidArgumentError(
1011
- 'Option "path" must be a non-empty String, but %v was given.',
1000
+ 'Option "path" must be a String, but %v was given.',
1001
+ routeDef.path
1002
+ );
1003
+ }
1004
+ if (!routeDef.path.startsWith("/")) {
1005
+ throw new import_js_format12.InvalidArgumentError(
1006
+ 'Option "path" must start with "/", but %v was given.',
1012
1007
  routeDef.path
1013
1008
  );
1014
1009
  }
@@ -1023,7 +1018,7 @@ function validateRouteDefinition(routeDef) {
1023
1018
  routeDef.preHandler.forEach((preHandler) => {
1024
1019
  if (typeof preHandler !== "function") {
1025
1020
  throw new import_js_format12.InvalidArgumentError(
1026
- "Route pre-handler must be a Function, but %v was given.",
1021
+ 'Hook "preHandler" must be a Function, but %v was given.',
1027
1022
  preHandler
1028
1023
  );
1029
1024
  }
@@ -1040,7 +1035,7 @@ function validateRouteDefinition(routeDef) {
1040
1035
  routeDef.postHandler.forEach((postHandler) => {
1041
1036
  if (typeof postHandler !== "function") {
1042
1037
  throw new import_js_format12.InvalidArgumentError(
1043
- "Route post-handler must be a Function, but %v was given.",
1038
+ 'Hook "postHandler" must be a Function, but %v was given.',
1044
1039
  postHandler
1045
1040
  );
1046
1041
  }
@@ -1208,7 +1203,7 @@ var _RouterOptions = class _RouterOptions extends DebuggableService {
1208
1203
  setRequestBodyBytesLimit(input) {
1209
1204
  if (typeof input !== "number" || input < 0) {
1210
1205
  throw new import_js_format13.InvalidArgumentError(
1211
- 'The option "requestBodyBytesLimit" must be a positive Number or 0, but %v was given.',
1206
+ 'Option "requestBodyBytesLimit" must be a positive Number or 0, but %v was given.',
1212
1207
  input
1213
1208
  );
1214
1209
  }
@@ -1243,13 +1238,13 @@ var _BodyParser = class _BodyParser extends DebuggableService {
1243
1238
  defineParser(mediaType, parser) {
1244
1239
  if (!mediaType || typeof mediaType !== "string") {
1245
1240
  throw new import_js_format14.InvalidArgumentError(
1246
- 'The parameter "mediaType" of BodyParser.defineParser must be a non-empty String, but %v was given.',
1241
+ 'Parameter "mediaType" must be a non-empty String, but %v was given.',
1247
1242
  mediaType
1248
1243
  );
1249
1244
  }
1250
1245
  if (!parser || typeof parser !== "function") {
1251
1246
  throw new import_js_format14.InvalidArgumentError(
1252
- 'The parameter "parser" of BodyParser.defineParser must be a Function, but %v was given.',
1247
+ 'Parameter "parser" must be a Function, but %v was given.',
1253
1248
  parser
1254
1249
  );
1255
1250
  }
@@ -1265,29 +1260,44 @@ var _BodyParser = class _BodyParser extends DebuggableService {
1265
1260
  hasParser(mediaType) {
1266
1261
  if (!mediaType || typeof mediaType !== "string") {
1267
1262
  throw new import_js_format14.InvalidArgumentError(
1268
- 'The parameter "mediaType" of BodyParser.hasParser must be a non-empty String, but %v was given.',
1263
+ 'Parameter "mediaType" must be a non-empty String, but %v was given.',
1269
1264
  mediaType
1270
1265
  );
1271
1266
  }
1272
1267
  return Boolean(this._parsers[mediaType]);
1273
1268
  }
1274
1269
  /**
1275
- * Delete parser.
1270
+ * Get parser.
1276
1271
  *
1277
1272
  * @param {string} mediaType
1278
- * @returns {this}
1273
+ * @returns {Function}
1279
1274
  */
1280
- deleteParser(mediaType) {
1275
+ getParser(mediaType) {
1281
1276
  if (!mediaType || typeof mediaType !== "string") {
1282
1277
  throw new import_js_format14.InvalidArgumentError(
1283
- 'The parameter "mediaType" of BodyParser.deleteParser must be a non-empty String, but %v was given.',
1278
+ 'Parameter "mediaType" must be a non-empty String, but %v was given.',
1284
1279
  mediaType
1285
1280
  );
1286
1281
  }
1287
1282
  const parser = this._parsers[mediaType];
1288
1283
  if (!parser) {
1289
1284
  throw new import_js_format14.InvalidArgumentError(
1290
- "The parser of %v is not found.",
1285
+ "Media type %v does not have a parser.",
1286
+ mediaType
1287
+ );
1288
+ }
1289
+ return parser;
1290
+ }
1291
+ /**
1292
+ * Remove parser.
1293
+ *
1294
+ * @param {string} mediaType
1295
+ * @returns {this}
1296
+ */
1297
+ removeParser(mediaType) {
1298
+ if (!mediaType || typeof mediaType !== "string") {
1299
+ throw new import_js_format14.InvalidArgumentError(
1300
+ 'Parameter "mediaType" must be a non-empty String, but %v was given.',
1291
1301
  mediaType
1292
1302
  );
1293
1303
  }
@@ -1442,7 +1452,7 @@ var _RequestParser = class _RequestParser extends DebuggableService {
1442
1452
  parse(request) {
1443
1453
  if (!(request instanceof import_http3.IncomingMessage)) {
1444
1454
  throw new import_js_format15.InvalidArgumentError(
1445
- "The first parameter of RequestParser.parse must be an instance of IncomingMessage, but %v was given.",
1455
+ 'Parameter "request" must be an instance of IncomingMessage, but %v was given.',
1446
1456
  request
1447
1457
  );
1448
1458
  }
@@ -1497,7 +1507,7 @@ var _RouteRegistry = class _RouteRegistry extends DebuggableService {
1497
1507
  const debug = this.getDebuggerFor(this.defineRoute);
1498
1508
  if (!routeDef || typeof routeDef !== "object" || Array.isArray(routeDef)) {
1499
1509
  throw new import_js_format16.InvalidArgumentError(
1500
- "The route definition must be an Object, but %v was given.",
1510
+ "Route definition must be an Object, but %v was given.",
1501
1511
  routeDef
1502
1512
  );
1503
1513
  }
@@ -1721,28 +1731,28 @@ var _RequestContext = class _RequestContext {
1721
1731
  constructor(container, request, response, route) {
1722
1732
  if (!(0, import_js_service3.isServiceContainer)(container)) {
1723
1733
  throw new import_js_format17.InvalidArgumentError(
1724
- 'The parameter "container" of RequestContext.constructor must be an instance of ServiceContainer, but %v was given.',
1734
+ 'Parameter "container" must be an instance of ServiceContainer, but %v was given.',
1725
1735
  container
1726
1736
  );
1727
1737
  }
1728
1738
  this._container = container;
1729
1739
  if (!request || typeof request !== "object" || Array.isArray(request) || !isReadableStream(request)) {
1730
1740
  throw new import_js_format17.InvalidArgumentError(
1731
- 'The parameter "request" of RequestContext.constructor must be an instance of IncomingMessage, but %v was given.',
1741
+ 'Parameter "request" must be an instance of IncomingMessage, but %v was given.',
1732
1742
  request
1733
1743
  );
1734
1744
  }
1735
1745
  this._request = request;
1736
1746
  if (!response || typeof response !== "object" || Array.isArray(response) || !isWritableStream(response)) {
1737
1747
  throw new import_js_format17.InvalidArgumentError(
1738
- 'The parameter "response" of RequestContext.constructor must be an instance of ServerResponse, but %v was given.',
1748
+ 'Parameter "response" must be an instance of ServerResponse, but %v was given.',
1739
1749
  response
1740
1750
  );
1741
1751
  }
1742
1752
  this._response = response;
1743
1753
  if (!(route instanceof Route)) {
1744
1754
  throw new import_js_format17.InvalidArgumentError(
1745
- 'The parameter "route" of RequestContext.constructor must be an instance of Route, but %v was given.',
1755
+ 'Parameter "route" must be an instance of Route, but %v was given.',
1746
1756
  route
1747
1757
  );
1748
1758
  }
@@ -1774,16 +1784,22 @@ function validateRouterBranchDefinition(branchDef) {
1774
1784
  branchDef.method
1775
1785
  );
1776
1786
  }
1777
- if (!branchDef.path || typeof branchDef.path !== "string") {
1787
+ if (branchDef.handler !== void 0) {
1788
+ throw new import_js_format18.InvalidArgumentError(
1789
+ 'Option "handler" is not supported for the router branch, but %v was given.',
1790
+ branchDef.handler
1791
+ );
1792
+ }
1793
+ if (typeof branchDef.path !== "string") {
1778
1794
  throw new import_js_format18.InvalidArgumentError(
1779
- 'Option "path" must be a non-empty String, but %v was given.',
1795
+ 'Option "path" must be a String, but %v was given.',
1780
1796
  branchDef.path
1781
1797
  );
1782
1798
  }
1783
- if (branchDef.handler !== void 0) {
1799
+ if (!branchDef.path.startsWith("/")) {
1784
1800
  throw new import_js_format18.InvalidArgumentError(
1785
- 'Option "handler" is not supported for the router branch, but %v was given.',
1786
- branchDef.handler
1801
+ 'Option "path" must start with "/", but %v was given.',
1802
+ branchDef.path
1787
1803
  );
1788
1804
  }
1789
1805
  if (branchDef.preHandler !== void 0) {
@@ -1791,7 +1807,7 @@ function validateRouterBranchDefinition(branchDef) {
1791
1807
  branchDef.preHandler.forEach((preHandler) => {
1792
1808
  if (typeof preHandler !== "function") {
1793
1809
  throw new import_js_format18.InvalidArgumentError(
1794
- "Route pre-handler must be a Function, but %v was given.",
1810
+ 'Hook "preHandler" must be a Function, but %v was given.',
1795
1811
  preHandler
1796
1812
  );
1797
1813
  }
@@ -1808,7 +1824,7 @@ function validateRouterBranchDefinition(branchDef) {
1808
1824
  branchDef.postHandler.forEach((postHandler) => {
1809
1825
  if (typeof postHandler !== "function") {
1810
1826
  throw new import_js_format18.InvalidArgumentError(
1811
- "Route post-handler must be a Function, but %v was given.",
1827
+ 'Hook "postHandler" must be a Function, but %v was given.',
1812
1828
  postHandler
1813
1829
  );
1814
1830
  }
@@ -1836,8 +1852,11 @@ function mergeRouterBranchDefinitions(firstDef, secondDef) {
1836
1852
  validateRouterBranchDefinition(firstDef);
1837
1853
  validateRouterBranchDefinition(secondDef);
1838
1854
  const mergedDef = {};
1839
- const path = (firstDef.path || "") + "/" + (secondDef.path || "");
1840
- mergedDef.path = normalizePath(path);
1855
+ let fullPath = "/" + (firstDef.path || "");
1856
+ if (secondDef.path && secondDef.path !== "/") {
1857
+ fullPath += "/" + secondDef.path;
1858
+ }
1859
+ mergedDef.path = fullPath.replace(/\/+/g, "/");
1841
1860
  if (firstDef.preHandler || secondDef.preHandler) {
1842
1861
  mergedDef.preHandler = [firstDef.preHandler, secondDef.preHandler].flat().filter(Boolean);
1843
1862
  }
@@ -1920,17 +1939,17 @@ var _RouterBranch = class _RouterBranch extends DebuggableService {
1920
1939
  * @param {RouterBranch} [parentBranch]
1921
1940
  */
1922
1941
  constructor(router, branchDef, parentBranch) {
1923
- super(router.container);
1924
1942
  if (!(router instanceof TrieRouter)) {
1925
1943
  throw new import_js_format19.InvalidArgumentError(
1926
- 'Parameter "router" must be a TrieRouter instance, but %v was given.',
1944
+ 'Parameter "router" must be an instance of TrieRouter, but %v was given.',
1927
1945
  router
1928
1946
  );
1929
1947
  }
1948
+ super(router.container);
1930
1949
  this._router = router;
1931
1950
  if (parentBranch !== void 0 && !(parentBranch instanceof _RouterBranch)) {
1932
1951
  throw new import_js_format19.InvalidArgumentError(
1933
- 'Parameter "parentBranch" must be a RouterBranch instance, but %v was given.',
1952
+ 'Parameter "parentBranch" must be an instance of RouterBranch, but %v was given.',
1934
1953
  parentBranch
1935
1954
  );
1936
1955
  }
@@ -1945,7 +1964,7 @@ var _RouterBranch = class _RouterBranch extends DebuggableService {
1945
1964
  validateRouterBranchDefinition(branchDef);
1946
1965
  this._definition = cloneDeep(branchDef);
1947
1966
  }
1948
- this.ctorDebug("Created a branch %v.", normalizePath(branchDef.path, true));
1967
+ this.ctorDebug("Created a branch %v.", branchDef.path);
1949
1968
  this.ctorDebug("Branch path is %v.", this._definition.path);
1950
1969
  }
1951
1970
  /**
@@ -2138,7 +2157,7 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
2138
2157
  * ```
2139
2158
  * const router = new TrieRouter();
2140
2159
  * router.defineRoute({
2141
- * method: HttpMethod.GET, // Request method.
2160
+ * method: HttpMethod.GET, // Request method.
2142
2161
  * path: '/', // Path template.
2143
2162
  * handler: ctx => 'Hello world!', // Request handler.
2144
2163
  * });
@@ -2150,9 +2169,9 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
2150
2169
  * router.defineRoute({
2151
2170
  * method: HttpMethod.POST, // Request method.
2152
2171
  * path: '/users/:id', // The path template may have parameters.
2153
- * preHandler(ctx) { ... }, // The "preHandler" executes before a route handler.
2154
- * handler(ctx) { ... }, // Request handler function.
2155
- * postHandler(ctx, data) { ... }, // The "postHandler" executes after a route handler.
2172
+ * preHandler(ctx) { ... }, // The hook "preHandler" executes before a route handler.
2173
+ * handler(ctx) { ... }, // Route handler function.
2174
+ * postHandler(ctx, data) { ... }, // The hook "postHandler" executes after a route handler.
2156
2175
  * });
2157
2176
  * ```
2158
2177
  *
@@ -2338,7 +2357,6 @@ var TrieRouter = _TrieRouter;
2338
2357
  isResponseSent,
2339
2358
  isWritableStream,
2340
2359
  mergeDeep,
2341
- normalizePath,
2342
2360
  parseContentType,
2343
2361
  parseCookieString,
2344
2362
  parseJsonBody,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-trie-router",
3
- "version": "0.5.14",
3
+ "version": "0.6.1",
4
4
  "description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
5
5
  "author": "Mikhail Evstropov <e22m4u@yandex.ru>",
6
6
  "license": "MIT",
@@ -39,8 +39,8 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@e22m4u/js-debug": "~0.4.1",
42
- "@e22m4u/js-format": "~0.3.2",
43
- "@e22m4u/js-path-trie": "~0.1.1",
42
+ "@e22m4u/js-format": "~0.4.0",
43
+ "@e22m4u/js-path-trie": "~0.2.0",
44
44
  "@e22m4u/js-service": "~0.5.1",
45
45
  "debug": "~4.4.3",
46
46
  "http-errors": "~2.0.1",
@@ -56,18 +56,18 @@
56
56
  "c8": "~10.1.3",
57
57
  "chai": "~6.2.2",
58
58
  "chai-as-promised": "~8.0.2",
59
- "esbuild": "~0.27.2",
59
+ "esbuild": "~0.27.3",
60
60
  "eslint": "~9.39.2",
61
61
  "eslint-config-prettier": "~10.1.8",
62
62
  "eslint-plugin-chai-expect": "~3.1.0",
63
63
  "eslint-plugin-import": "~2.32.0",
64
- "eslint-plugin-jsdoc": "~62.5.2",
64
+ "eslint-plugin-jsdoc": "~62.6.0",
65
65
  "eslint-plugin-mocha": "~11.2.0",
66
66
  "globals": "~17.3.0",
67
67
  "husky": "~9.1.7",
68
68
  "mocha": "~11.7.5",
69
69
  "prettier": "~3.8.1",
70
- "rimraf": "~6.1.2",
70
+ "rimraf": "~6.1.3",
71
71
  "typescript": "~5.9.3"
72
72
  }
73
73
  }