@e22m4u/js-trie-router 0.7.5 → 0.7.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 CHANGED
@@ -16,6 +16,7 @@ HTTP маршрутизатор для Node.js на основе
16
16
  ## Содержание
17
17
 
18
18
  - [Установка](#установка)
19
+ - [Расширения](#расширения)
19
20
  - [Использование](#использование)
20
21
  - [Параметры маршрутизатора](#параметры-маршрутизатора)
21
22
  - [Контекст запроса](#контекст-запроса)
@@ -54,6 +55,15 @@ import {TrieRouter} from '@e22m4u/js-trie-router';
54
55
  const {TrieRouter} = require('@e22m4u/js-trie-router');
55
56
  ```
56
57
 
58
+ ## Расширения
59
+
60
+ Расширение функционала выполняется с помощью NPM модулей.
61
+
62
+ | модуль | описание |
63
+ |------------------------------------------------------------------------------------------------|------------------------------------------------|
64
+ | [@e22m4u/js-trie-router-cors](https://www.npmjs.com/package/@e22m4u/js-trie-router-cors) | Модуль поддержки CORS (кросс-доменные запросы) |
65
+ | [@e22m4u/js-trie-router-openapi](https://www.npmjs.com/package/@e22m4u/js-trie-router-openapi) | Генерация OpenAPI документа и валидация данных |
66
+
57
67
  ## Использование
58
68
 
59
69
  Базовый пример создания экземпляра роутера, объявления маршрута
package/build-cjs.js CHANGED
@@ -6,7 +6,7 @@ await esbuild.build({
6
6
  outfile: 'dist/cjs/index.cjs',
7
7
  format: 'cjs',
8
8
  platform: 'node',
9
- target: ['node16'],
9
+ target: ['node18'],
10
10
  bundle: true,
11
11
  keepNames: true,
12
12
  external: [
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  RequestQueryParser: () => RequestQueryParser,
44
44
  Route: () => Route,
45
45
  RouteRegistry: () => RouteRegistry,
46
+ RouterBranch: () => RouterBranch,
46
47
  RouterDataSender: () => RouterDataSender,
47
48
  RouterErrorSender: () => RouterErrorSender,
48
49
  RouterHookInvoker: () => RouterHookInvoker,
@@ -65,12 +66,14 @@ __export(index_exports, {
65
66
  isResponseSent: () => isResponseSent,
66
67
  isWritableStream: () => isWritableStream,
67
68
  mergeDeep: () => mergeDeep,
69
+ mergeRouterBranchDefinitions: () => mergeRouterBranchDefinitions,
68
70
  parseContentType: () => parseContentType,
69
71
  parseCookieString: () => parseCookieString,
70
72
  parseJsonBody: () => parseJsonBody,
71
73
  toCamelCase: () => toCamelCase,
72
74
  toPascalCase: () => toPascalCase,
73
- validateRouteDefinition: () => validateRouteDefinition
75
+ validateRouteDefinition: () => validateRouteDefinition,
76
+ validateRouterBranchDefinition: () => validateRouterBranchDefinition
74
77
  });
75
78
  module.exports = __toCommonJS(index_exports);
76
79
 
@@ -83,7 +86,10 @@ var import_js_debug = require("@e22m4u/js-debug");
83
86
  // src/debuggable-service.js
84
87
  var import_js_service = require("@e22m4u/js-service");
85
88
  var MODULE_DEBUG_NAMESPACE = "jsTrieRouter";
86
- var _DebuggableService = class _DebuggableService extends import_js_service.DebuggableService {
89
+ var DebuggableService = class extends import_js_service.DebuggableService {
90
+ static {
91
+ __name(this, "DebuggableService");
92
+ }
87
93
  /**
88
94
  * Constructor.
89
95
  *
@@ -96,8 +102,6 @@ var _DebuggableService = class _DebuggableService extends import_js_service.Debu
96
102
  });
97
103
  }
98
104
  };
99
- __name(_DebuggableService, "DebuggableService");
100
- var DebuggableService = _DebuggableService;
101
105
 
102
106
  // src/utils/clone-deep.js
103
107
  function cloneDeep(value) {
@@ -950,7 +954,10 @@ __name(getRequestPathname, "getRequestPathname");
950
954
  // src/request-context.js
951
955
  var import_js_format11 = require("@e22m4u/js-format");
952
956
  var import_js_service2 = require("@e22m4u/js-service");
953
- var _RequestContext = class _RequestContext {
957
+ var RequestContext = class {
958
+ static {
959
+ __name(this, "RequestContext");
960
+ }
954
961
  /**
955
962
  * Service container.
956
963
  *
@@ -1136,8 +1143,6 @@ var _RequestContext = class _RequestContext {
1136
1143
  this._route = route;
1137
1144
  }
1138
1145
  };
1139
- __name(_RequestContext, "RequestContext");
1140
- var RequestContext = _RequestContext;
1141
1146
 
1142
1147
  // src/hooks/router-hook-invoker.js
1143
1148
  var import_js_format13 = require("@e22m4u/js-format");
@@ -1151,7 +1156,10 @@ var RouterHookType = {
1151
1156
  POST_HANDLER: "postHandler"
1152
1157
  };
1153
1158
  var ROUTER_HOOK_TYPES = Object.values(RouterHookType);
1154
- var _RouterHookRegistry = class _RouterHookRegistry {
1159
+ var RouterHookRegistry = class {
1160
+ static {
1161
+ __name(this, "RouterHookRegistry");
1162
+ }
1155
1163
  /**
1156
1164
  * Hooks.
1157
1165
  *
@@ -1234,11 +1242,12 @@ var _RouterHookRegistry = class _RouterHookRegistry {
1234
1242
  return this._hooks.get(type) || [];
1235
1243
  }
1236
1244
  };
1237
- __name(_RouterHookRegistry, "RouterHookRegistry");
1238
- var RouterHookRegistry = _RouterHookRegistry;
1239
1245
 
1240
1246
  // src/hooks/router-hook-invoker.js
1241
- var _RouterHookInvoker = class _RouterHookInvoker extends DebuggableService {
1247
+ var RouterHookInvoker = class extends DebuggableService {
1248
+ static {
1249
+ __name(this, "RouterHookInvoker");
1250
+ }
1242
1251
  /**
1243
1252
  * Invoke on-request hooks.
1244
1253
  *
@@ -1499,8 +1508,6 @@ var _RouterHookInvoker = class _RouterHookInvoker extends DebuggableService {
1499
1508
  return currentData;
1500
1509
  }
1501
1510
  };
1502
- __name(_RouterHookInvoker, "RouterHookInvoker");
1503
- var RouterHookInvoker = _RouterHookInvoker;
1504
1511
 
1505
1512
  // src/route/validate-route-definition.js
1506
1513
  var import_js_format14 = require("@e22m4u/js-format");
@@ -1583,6 +1590,7 @@ __name(validateRouteDefinition, "validateRouteDefinition");
1583
1590
  // src/route/route.js
1584
1591
  var HttpMethod = {
1585
1592
  GET: "GET",
1593
+ HEAD: "HEAD",
1586
1594
  POST: "POST",
1587
1595
  PUT: "PUT",
1588
1596
  PATCH: "PATCH",
@@ -1590,7 +1598,10 @@ var HttpMethod = {
1590
1598
  OPTIONS: "OPTIONS"
1591
1599
  };
1592
1600
  var DEFAULT_META = Object.freeze({});
1593
- var _Route = class _Route extends import_js_debug.Debuggable {
1601
+ var Route = class extends import_js_debug.Debuggable {
1602
+ static {
1603
+ __name(this, "Route");
1604
+ }
1594
1605
  /**
1595
1606
  * Definition.
1596
1607
  *
@@ -1692,14 +1703,15 @@ var _Route = class _Route extends import_js_debug.Debuggable {
1692
1703
  return this.handler(context);
1693
1704
  }
1694
1705
  };
1695
- __name(_Route, "Route");
1696
- var Route = _Route;
1697
1706
 
1698
1707
  // src/route/route-registry.js
1699
1708
  var import_js_path_trie = require("@e22m4u/js-path-trie");
1700
1709
  var import_js_service3 = require("@e22m4u/js-service");
1701
1710
  var import_js_format15 = require("@e22m4u/js-format");
1702
- var _RouteRegistry = class _RouteRegistry extends DebuggableService {
1711
+ var RouteRegistry = class extends DebuggableService {
1712
+ static {
1713
+ __name(this, "RouteRegistry");
1714
+ }
1703
1715
  /**
1704
1716
  * Constructor.
1705
1717
  *
@@ -1789,38 +1801,7 @@ var _RouteRegistry = class _RouteRegistry extends DebuggableService {
1789
1801
  requestPath
1790
1802
  );
1791
1803
  }
1792
- /**
1793
- * Get allowed methods for request path.
1794
- *
1795
- * @param {string} requestPath
1796
- * @returns {string[]}
1797
- */
1798
- getAllowedMethodsForRequestPath(requestPath) {
1799
- if (typeof requestPath !== "string") {
1800
- throw new import_js_format15.InvalidArgumentError(
1801
- 'Parameter "requestPath" must be a String, but %v was given.',
1802
- requestPath
1803
- );
1804
- }
1805
- const debug = this.getDebuggerFor(this.getAllowedMethodsForRequestPath);
1806
- const allowedMethods = [];
1807
- for (const method of Object.values(HttpMethod)) {
1808
- const rawTriePath = `${method}/${requestPath}`;
1809
- const triePath = rawTriePath.replace(/\/+/g, "/");
1810
- if (this._trie.match(triePath)) {
1811
- allowedMethods.push(method);
1812
- }
1813
- }
1814
- if (allowedMethods.length) {
1815
- debug("Allowed methods for %v are: %l.", requestPath, allowedMethods);
1816
- } else {
1817
- debug("Path %v does not have allowed methods.", requestPath);
1818
- }
1819
- return allowedMethods;
1820
- }
1821
1804
  };
1822
- __name(_RouteRegistry, "RouteRegistry");
1823
- var RouteRegistry = _RouteRegistry;
1824
1805
 
1825
1806
  // src/branch/router-branch.js
1826
1807
  var import_js_format17 = require("@e22m4u/js-format");
@@ -1931,7 +1912,10 @@ function mergeRouterBranchDefinitions(firstDef, secondDef) {
1931
1912
  __name(mergeRouterBranchDefinitions, "mergeRouterBranchDefinitions");
1932
1913
 
1933
1914
  // src/branch/router-branch.js
1934
- var _RouterBranch = class _RouterBranch extends DebuggableService {
1915
+ var RouterBranch = class _RouterBranch extends DebuggableService {
1916
+ static {
1917
+ __name(this, "RouterBranch");
1918
+ }
1935
1919
  /**
1936
1920
  * Router.
1937
1921
  *
@@ -2050,8 +2034,6 @@ var _RouterBranch = class _RouterBranch extends DebuggableService {
2050
2034
  return new _RouterBranch(this._router, branchDef, this);
2051
2035
  }
2052
2036
  };
2053
- __name(_RouterBranch, "RouterBranch");
2054
- var RouterBranch = _RouterBranch;
2055
2037
 
2056
2038
  // src/parsers/request-parser.js
2057
2039
  var import_http3 = require("http");
@@ -2063,7 +2045,10 @@ var import_js_format19 = require("@e22m4u/js-format");
2063
2045
 
2064
2046
  // src/trie-router-options.js
2065
2047
  var import_js_format18 = require("@e22m4u/js-format");
2066
- var _TrieRouterOptions = class _TrieRouterOptions {
2048
+ var TrieRouterOptions = class {
2049
+ static {
2050
+ __name(this, "TrieRouterOptions");
2051
+ }
2067
2052
  /**
2068
2053
  * Request body bytes limit.
2069
2054
  *
@@ -2138,11 +2123,12 @@ var _TrieRouterOptions = class _TrieRouterOptions {
2138
2123
  }
2139
2124
  }
2140
2125
  };
2141
- __name(_TrieRouterOptions, "TrieRouterOptions");
2142
- var TrieRouterOptions = _TrieRouterOptions;
2143
2126
 
2144
2127
  // src/parsers/request-body-parser.js
2145
- var _RequestBodyParser = class _RequestBodyParser extends DebuggableService {
2128
+ var RequestBodyParser = class extends DebuggableService {
2129
+ static {
2130
+ __name(this, "RequestBodyParser");
2131
+ }
2146
2132
  /**
2147
2133
  * Parsers.
2148
2134
  *
@@ -2285,8 +2271,6 @@ var _RequestBodyParser = class _RequestBodyParser extends DebuggableService {
2285
2271
  });
2286
2272
  }
2287
2273
  };
2288
- __name(_RequestBodyParser, "RequestBodyParser");
2289
- var RequestBodyParser = _RequestBodyParser;
2290
2274
  function parseJsonBody(input) {
2291
2275
  if (typeof input !== "string") {
2292
2276
  return void 0;
@@ -2301,7 +2285,10 @@ __name(parseJsonBody, "parseJsonBody");
2301
2285
 
2302
2286
  // src/parsers/request-query-parser.js
2303
2287
  var import_querystring2 = __toESM(require("querystring"), 1);
2304
- var _RequestQueryParser = class _RequestQueryParser extends DebuggableService {
2288
+ var RequestQueryParser = class extends DebuggableService {
2289
+ static {
2290
+ __name(this, "RequestQueryParser");
2291
+ }
2305
2292
  /**
2306
2293
  * Parse
2307
2294
  *
@@ -2327,11 +2314,12 @@ var _RequestQueryParser = class _RequestQueryParser extends DebuggableService {
2327
2314
  return query;
2328
2315
  }
2329
2316
  };
2330
- __name(_RequestQueryParser, "RequestQueryParser");
2331
- var RequestQueryParser = _RequestQueryParser;
2332
2317
 
2333
2318
  // src/parsers/request-cookies-parser.js
2334
- var _RequestCookiesParser = class _RequestCookiesParser extends DebuggableService {
2319
+ var RequestCookiesParser = class extends DebuggableService {
2320
+ static {
2321
+ __name(this, "RequestCookiesParser");
2322
+ }
2335
2323
  /**
2336
2324
  * Parse
2337
2325
  *
@@ -2357,11 +2345,12 @@ var _RequestCookiesParser = class _RequestCookiesParser extends DebuggableServic
2357
2345
  return cookies;
2358
2346
  }
2359
2347
  };
2360
- __name(_RequestCookiesParser, "RequestCookiesParser");
2361
- var RequestCookiesParser = _RequestCookiesParser;
2362
2348
 
2363
2349
  // src/parsers/request-parser.js
2364
- var _RequestParser = class _RequestParser extends DebuggableService {
2350
+ var RequestParser = class extends DebuggableService {
2351
+ static {
2352
+ __name(this, "RequestParser");
2353
+ }
2365
2354
  /**
2366
2355
  * Parse.
2367
2356
  *
@@ -2399,15 +2388,16 @@ var _RequestParser = class _RequestParser extends DebuggableService {
2399
2388
  return promises.length ? Promise.all(promises).then(() => data) : data;
2400
2389
  }
2401
2390
  };
2402
- __name(_RequestParser, "RequestParser");
2403
- var RequestParser = _RequestParser;
2404
2391
 
2405
2392
  // src/trie-router.js
2406
2393
  var import_http4 = require("http");
2407
2394
 
2408
2395
  // src/senders/router-data-sender.js
2409
2396
  var import_js_format21 = require("@e22m4u/js-format");
2410
- var _RouterDataSender = class _RouterDataSender extends DebuggableService {
2397
+ var RouterDataSender = class extends DebuggableService {
2398
+ static {
2399
+ __name(this, "RouterDataSender");
2400
+ }
2411
2401
  /**
2412
2402
  * Send.
2413
2403
  *
@@ -2428,8 +2418,8 @@ var _RouterDataSender = class _RouterDataSender extends DebuggableService {
2428
2418
  return;
2429
2419
  }
2430
2420
  if (isReadableStream(data)) {
2431
- if (!response.getHeader("content-type")) {
2432
- response.setHeader("content-type", "application/octet-stream");
2421
+ if (!response.getHeader("Content-Type")) {
2422
+ response.setHeader("Content-Type", "application/octet-stream");
2433
2423
  }
2434
2424
  data.pipe(response);
2435
2425
  debug("Sending response with a Stream.");
@@ -2441,13 +2431,13 @@ var _RouterDataSender = class _RouterDataSender extends DebuggableService {
2441
2431
  case "boolean":
2442
2432
  case "object":
2443
2433
  if (Buffer.isBuffer(data)) {
2444
- if (!response.getHeader("content-type")) {
2445
- response.setHeader("content-type", "application/octet-stream");
2434
+ if (!response.getHeader("Content-Type")) {
2435
+ response.setHeader("Content-Type", "application/octet-stream");
2446
2436
  }
2447
2437
  debugMsg = "Buffer has been sent as binary data.";
2448
2438
  } else {
2449
- if (!response.getHeader("content-type")) {
2450
- response.setHeader("content-type", "application/json");
2439
+ if (!response.getHeader("Content-Type")) {
2440
+ response.setHeader("Content-Type", "application/json");
2451
2441
  }
2452
2442
  debugMsg = (0, import_js_format21.format)(
2453
2443
  "%v has been sent as JSON.",
@@ -2457,8 +2447,8 @@ var _RouterDataSender = class _RouterDataSender extends DebuggableService {
2457
2447
  }
2458
2448
  break;
2459
2449
  default:
2460
- if (!response.getHeader("content-type")) {
2461
- response.setHeader("content-type", "text/plain");
2450
+ if (!response.getHeader("Content-Type")) {
2451
+ response.setHeader("Content-Type", "text/plain");
2462
2452
  }
2463
2453
  debugMsg = "Response data has been sent as plain text.";
2464
2454
  data = String(data);
@@ -2468,14 +2458,15 @@ var _RouterDataSender = class _RouterDataSender extends DebuggableService {
2468
2458
  debug(debugMsg);
2469
2459
  }
2470
2460
  };
2471
- __name(_RouterDataSender, "RouterDataSender");
2472
- var RouterDataSender = _RouterDataSender;
2473
2461
 
2474
2462
  // src/senders/router-error-sender.js
2475
2463
  var import_util = require("util");
2476
2464
  var import_statuses = __toESM(require("statuses"), 1);
2477
2465
  var EXPOSED_ERROR_PROPERTIES = ["code", "details"];
2478
- var _RouterErrorSender = class _RouterErrorSender extends DebuggableService {
2466
+ var RouterErrorSender = class extends DebuggableService {
2467
+ static {
2468
+ __name(this, "RouterErrorSender");
2469
+ }
2479
2470
  /**
2480
2471
  * Handle.
2481
2472
  *
@@ -2525,7 +2516,7 @@ var _RouterErrorSender = class _RouterErrorSender extends DebuggableService {
2525
2516
  console.error(error);
2526
2517
  }
2527
2518
  response.statusCode = statusCode;
2528
- response.setHeader("content-type", "application/json; charset=utf-8");
2519
+ response.setHeader("Content-Type", "application/json; charset=utf-8");
2529
2520
  response.end(JSON.stringify(body, null, 2), "utf-8");
2530
2521
  debug(
2531
2522
  "%s error has been sent for the request %s %v.",
@@ -2544,7 +2535,7 @@ var _RouterErrorSender = class _RouterErrorSender extends DebuggableService {
2544
2535
  send404(request, response) {
2545
2536
  const debug = this.getDebuggerFor(this.send404);
2546
2537
  response.statusCode = 404;
2547
- response.setHeader("content-type", "text/plain; charset=utf-8");
2538
+ response.setHeader("Content-Type", "text/plain; charset=utf-8");
2548
2539
  response.end("404 Not Found", "utf-8");
2549
2540
  debug(
2550
2541
  "404 error has been sent for the request %s %v.",
@@ -2553,12 +2544,13 @@ var _RouterErrorSender = class _RouterErrorSender extends DebuggableService {
2553
2544
  );
2554
2545
  }
2555
2546
  };
2556
- __name(_RouterErrorSender, "RouterErrorSender");
2557
- var RouterErrorSender = _RouterErrorSender;
2558
2547
 
2559
2548
  // src/trie-router.js
2560
2549
  var import_js_service4 = require("@e22m4u/js-service");
2561
- var _TrieRouter = class _TrieRouter extends DebuggableService {
2550
+ var TrieRouter = class extends DebuggableService {
2551
+ static {
2552
+ __name(this, "TrieRouter");
2553
+ }
2562
2554
  /**
2563
2555
  * Constructor.
2564
2556
  *
@@ -2613,7 +2605,7 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
2613
2605
  * Example:
2614
2606
  * ```js
2615
2607
  * const router = new TrieRouter();
2616
- * const apiBranch = router.createBranch({path: 'api'});
2608
+ * const apiBranch = router.createBranch({path: '/api'});
2617
2609
  *
2618
2610
  * // GET /api/hello
2619
2611
  * apiBranch.defineRoute({
@@ -2680,22 +2672,6 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
2680
2672
  }
2681
2673
  const resolved = this.getService(RouteRegistry).matchRouteByRequest(request);
2682
2674
  if (!resolved) {
2683
- if (request.method.toUpperCase() === HttpMethod.OPTIONS) {
2684
- const allowedMethods = this.getService(RouteRegistry).getAllowedMethodsForRequestPath(
2685
- requestPath
2686
- );
2687
- if (allowedMethods.length > 0) {
2688
- debug("Auto-handling OPTIONS request.");
2689
- if (!allowedMethods.includes("OPTIONS")) {
2690
- allowedMethods.push("OPTIONS");
2691
- }
2692
- const allowHeader = allowedMethods.join(", ");
2693
- response.statusCode = 204;
2694
- response.setHeader("Allow", allowHeader);
2695
- response.end();
2696
- return;
2697
- }
2698
- }
2699
2675
  debug(
2700
2676
  "No route found for the request %s %v.",
2701
2677
  request.method,
@@ -2781,8 +2757,6 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
2781
2757
  return this.getService(RouterHookRegistry).hasHook(type, hook);
2782
2758
  }
2783
2759
  };
2784
- __name(_TrieRouter, "TrieRouter");
2785
- var TrieRouter = _TrieRouter;
2786
2760
  // Annotate the CommonJS export names for ESM import in node:
2787
2761
  0 && (module.exports = {
2788
2762
  CHARACTER_ENCODING_LIST,
@@ -2797,6 +2771,7 @@ var TrieRouter = _TrieRouter;
2797
2771
  RequestQueryParser,
2798
2772
  Route,
2799
2773
  RouteRegistry,
2774
+ RouterBranch,
2800
2775
  RouterDataSender,
2801
2776
  RouterErrorSender,
2802
2777
  RouterHookInvoker,
@@ -2819,10 +2794,12 @@ var TrieRouter = _TrieRouter;
2819
2794
  isResponseSent,
2820
2795
  isWritableStream,
2821
2796
  mergeDeep,
2797
+ mergeRouterBranchDefinitions,
2822
2798
  parseContentType,
2823
2799
  parseCookieString,
2824
2800
  parseJsonBody,
2825
2801
  toCamelCase,
2826
2802
  toPascalCase,
2827
- validateRouteDefinition
2803
+ validateRouteDefinition,
2804
+ validateRouterBranchDefinition
2828
2805
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-trie-router",
3
- "version": "0.7.5",
3
+ "version": "0.7.7",
4
4
  "description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
5
5
  "author": "Mikhail Evstropov <e22m4u@yandex.ru>",
6
6
  "license": "MIT",
@@ -11,10 +11,10 @@
11
11
  "server",
12
12
  "nodejs"
13
13
  ],
14
- "homepage": "https://gitrepos.ru/e22m4u/js-trie-router",
14
+ "homepage": "https://gitverse.ru/e22m4u/js-trie-router",
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git+https://gitrepos.ru/e22m4u/js-trie-router.git"
17
+ "url": "git+https://gitverse.ru/e22m4u/js-trie-router.git"
18
18
  },
19
19
  "type": "module",
20
20
  "types": "./src/index.d.ts",
@@ -26,7 +26,7 @@
26
26
  "require": "./dist/cjs/index.cjs"
27
27
  },
28
28
  "engines": {
29
- "node": ">=16"
29
+ "node": ">=18"
30
30
  },
31
31
  "scripts": {
32
32
  "lint": "tsc && eslint ./src",
@@ -41,19 +41,19 @@
41
41
  "@e22m4u/js-debug": "~0.4.1",
42
42
  "@e22m4u/js-format": "~0.4.0",
43
43
  "@e22m4u/js-path-trie": "~0.2.0",
44
- "@e22m4u/js-service": "~0.5.1",
44
+ "@e22m4u/js-service": "~0.6.1",
45
45
  "debug": "~4.4.3",
46
46
  "http-errors": "~2.0.1",
47
47
  "statuses": "~2.0.2"
48
48
  },
49
49
  "devDependencies": {
50
- "@commitlint/cli": "~20.4.2",
51
- "@commitlint/config-conventional": "~20.4.2",
50
+ "@commitlint/cli": "~20.4.3",
51
+ "@commitlint/config-conventional": "~20.4.3",
52
52
  "@eslint/js": "~9.39.2",
53
53
  "@types/chai": "~5.2.3",
54
54
  "@types/chai-as-promised": "~8.0.2",
55
55
  "@types/mocha": "~10.0.10",
56
- "c8": "~10.1.3",
56
+ "c8": "~11.0.0",
57
57
  "chai": "~6.2.2",
58
58
  "chai-as-promised": "~8.0.2",
59
59
  "esbuild": "~0.27.3",
@@ -61,9 +61,9 @@
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.7.0",
64
+ "eslint-plugin-jsdoc": "~62.7.1",
65
65
  "eslint-plugin-mocha": "~11.2.0",
66
- "globals": "~17.3.0",
66
+ "globals": "~17.4.0",
67
67
  "husky": "~9.1.7",
68
68
  "mocha": "~11.7.5",
69
69
  "prettier": "~3.8.1",
package/src/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './route/index.js';
3
3
  export * from './utils/index.js';
4
4
  export * from './hooks/index.js';
5
5
  export * from './trie-router.js';
6
+ export * from './branch/index.js';
6
7
  export * from './parsers/index.js';
7
8
  export * from './senders/index.js';
8
9
  export * from './request-context.js';
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ export * from './route/index.js';
3
3
  export * from './utils/index.js';
4
4
  export * from './hooks/index.js';
5
5
  export * from './trie-router.js';
6
+ export * from './branch/index.js';
6
7
  export * from './parsers/index.js';
7
8
  export * from './senders/index.js';
8
9
  export * from './request-context.js';
@@ -36,11 +36,4 @@ export declare class RouteRegistry extends DebuggableService {
36
36
  * @param request
37
37
  */
38
38
  matchRouteByRequest(request: IncomingMessage): ResolvedRoute | undefined;
39
-
40
- /**
41
- * Get allowed methods for request path.
42
- *
43
- * @param requestPath
44
- */
45
- getAllowedMethodsForRequestPath(requestPath: string): string[];
46
39
  }
@@ -1,4 +1,4 @@
1
- import {HttpMethod, Route} from './route.js';
1
+ import {Route} from './route.js';
2
2
  import {PathTrie} from '@e22m4u/js-path-trie';
3
3
  import {ServiceContainer} from '@e22m4u/js-service';
4
4
  import {getRequestPathname} from '../utils/index.js';
@@ -124,34 +124,4 @@ export class RouteRegistry extends DebuggableService {
124
124
  requestPath,
125
125
  );
126
126
  }
127
-
128
- /**
129
- * Get allowed methods for request path.
130
- *
131
- * @param {string} requestPath
132
- * @returns {string[]}
133
- */
134
- getAllowedMethodsForRequestPath(requestPath) {
135
- if (typeof requestPath !== 'string') {
136
- throw new InvalidArgumentError(
137
- 'Parameter "requestPath" must be a String, but %v was given.',
138
- requestPath,
139
- );
140
- }
141
- const debug = this.getDebuggerFor(this.getAllowedMethodsForRequestPath);
142
- const allowedMethods = [];
143
- for (const method of Object.values(HttpMethod)) {
144
- const rawTriePath = `${method}/${requestPath}`;
145
- const triePath = rawTriePath.replace(/\/+/g, '/');
146
- if (this._trie.match(triePath)) {
147
- allowedMethods.push(method);
148
- }
149
- }
150
- if (allowedMethods.length) {
151
- debug('Allowed methods for %v are: %l.', requestPath, allowedMethods);
152
- } else {
153
- debug('Path %v does not have allowed methods.', requestPath);
154
- }
155
- return allowedMethods;
156
- }
157
127
  }
@@ -2,7 +2,6 @@ import {expect} from 'chai';
2
2
  import {format} from '@e22m4u/js-format';
3
3
  import {Route, HttpMethod} from './route.js';
4
4
  import {RouteRegistry} from './route-registry.js';
5
- import {ServiceContainer} from '@e22m4u/js-service';
6
5
  import {RouterHookRegistry, RouterHookType} from '../hooks/index.js';
7
6
 
8
7
  describe('RouteRegistry', function () {
@@ -153,95 +152,4 @@ describe('RouteRegistry', function () {
153
152
  expect(res.params).to.be.eql({p1: 'baz', p2: 'qux'});
154
153
  });
155
154
  });
156
-
157
- describe('getAllowedMethodsForRequestPath', function () {
158
- it('should require the parameter "requestPath" to be a String', function () {
159
- const S = new RouteRegistry();
160
- const throwable = v => () => S.getAllowedMethodsForRequestPath(v);
161
- const error = v =>
162
- format(
163
- 'Parameter "requestPath" must be a String, but %s was given.',
164
- v,
165
- );
166
- expect(throwable(10)).to.throw(error('10'));
167
- expect(throwable(0)).to.throw(error('0'));
168
- expect(throwable(true)).to.throw(error('true'));
169
- expect(throwable(false)).to.throw(error('false'));
170
- expect(throwable([])).to.throw(error('Array'));
171
- expect(throwable({})).to.throw(error('Object'));
172
- expect(throwable(undefined)).to.throw(error('undefined'));
173
- expect(throwable(null)).to.throw(error('null'));
174
- expect(throwable(() => undefined)).to.throw(error('Function'));
175
- throwable('str')();
176
- throwable('')();
177
- });
178
-
179
- it('should return an empty array if no routes match the path', function () {
180
- const S = new RouteRegistry(new ServiceContainer());
181
- S.defineRoute({
182
- method: HttpMethod.GET,
183
- path: '/foo',
184
- handler: () => undefined,
185
- });
186
- const res = S.getAllowedMethodsForRequestPath('/bar');
187
- expect(res).to.be.eql([]);
188
- });
189
-
190
- it('should return an array with a single method if only one matches', function () {
191
- const S = new RouteRegistry(new ServiceContainer());
192
- S.defineRoute({
193
- method: HttpMethod.POST,
194
- path: '/foo',
195
- handler: () => undefined,
196
- });
197
- const res = S.getAllowedMethodsForRequestPath('/foo');
198
- expect(res).to.be.eql([HttpMethod.POST]);
199
- });
200
-
201
- it('should return an array with multiple methods if several routes match the path', function () {
202
- const S = new RouteRegistry(new ServiceContainer());
203
- const handler = () => undefined;
204
- S.defineRoute({method: HttpMethod.GET, path: '/foo', handler});
205
- S.defineRoute({method: HttpMethod.POST, path: '/foo', handler});
206
- S.defineRoute({method: HttpMethod.DELETE, path: '/foo', handler});
207
- const res = S.getAllowedMethodsForRequestPath('/foo');
208
- expect(res).to.be.eql([
209
- HttpMethod.GET,
210
- HttpMethod.POST,
211
- HttpMethod.DELETE,
212
- ]);
213
- });
214
-
215
- it('should correctly resolve allowed methods for paths with parameters', function () {
216
- const S = new RouteRegistry(new ServiceContainer());
217
- const handler = () => undefined;
218
- S.defineRoute({method: HttpMethod.GET, path: '/users/:id', handler});
219
- S.defineRoute({method: HttpMethod.PUT, path: '/users/:id', handler});
220
- S.defineRoute({method: HttpMethod.POST, path: '/users', handler});
221
- const res = S.getAllowedMethodsForRequestPath('/users/123');
222
- expect(res).to.be.eql([HttpMethod.GET, HttpMethod.PUT]);
223
- });
224
-
225
- it('should distinguish between paths with and without trailing slash', function () {
226
- const S = new RouteRegistry(new ServiceContainer());
227
- const handler = () => undefined;
228
- S.defineRoute({method: HttpMethod.GET, path: '/foo', handler});
229
- S.defineRoute({method: HttpMethod.POST, path: '/foo/', handler});
230
- const res1 = S.getAllowedMethodsForRequestPath('/foo');
231
- expect(res1).to.be.eql([HttpMethod.GET]);
232
- const res2 = S.getAllowedMethodsForRequestPath('/foo/');
233
- expect(res2).to.be.eql([HttpMethod.POST]);
234
- });
235
-
236
- it('should check for the explicitly defined OPTIONS method', function () {
237
- const S = new RouteRegistry(new ServiceContainer());
238
- S.defineRoute({
239
- method: HttpMethod.OPTIONS,
240
- path: '/api',
241
- handler: () => undefined,
242
- });
243
- const res = S.getAllowedMethodsForRequestPath('/api');
244
- expect(res).to.be.eql([HttpMethod.OPTIONS]);
245
- });
246
- });
247
155
  });
@@ -7,6 +7,7 @@ import {RouterHookRegistry} from '../hooks/index.js';
7
7
  */
8
8
  export declare const HttpMethod: {
9
9
  GET: 'GET';
10
+ HEAD: 'HEAD';
10
11
  POST: 'POST';
11
12
  PUT: 'PUT';
12
13
  PATCH: 'PATCH';
@@ -24,6 +24,7 @@ import {validateRouteDefinition} from './validate-route-definition.js';
24
24
  *
25
25
  * @type {{
26
26
  * GET: 'GET',
27
+ * HEAD: 'HEAD',
27
28
  * POST: 'POST',
28
29
  * PUT: 'PUT',
29
30
  * PATCH: 'PATCH',
@@ -33,6 +34,7 @@ import {validateRouteDefinition} from './validate-route-definition.js';
33
34
  */
34
35
  export const HttpMethod = {
35
36
  GET: 'GET',
37
+ HEAD: 'HEAD',
36
38
  POST: 'POST',
37
39
  PUT: 'PUT',
38
40
  PATCH: 'PATCH',
@@ -33,10 +33,10 @@ export class RouterDataSender extends DebuggableService {
33
33
  // если ответ контроллера является стримом,
34
34
  // то поток отправляет бинарные данные
35
35
  if (isReadableStream(data)) {
36
- // если заголовок "content-type" не определен ранее,
36
+ // если заголовок "Content-Type" не определен ранее,
37
37
  // то устанавливается заголовок потоковых данных
38
- if (!response.getHeader('content-type')) {
39
- response.setHeader('content-type', 'application/octet-stream');
38
+ if (!response.getHeader('Content-Type')) {
39
+ response.setHeader('Content-Type', 'application/octet-stream');
40
40
  }
41
41
  data.pipe(response);
42
42
  debug('Sending response with a Stream.');
@@ -49,21 +49,21 @@ export class RouterDataSender extends DebuggableService {
49
49
  case 'number':
50
50
  case 'boolean':
51
51
  case 'object':
52
- // для бинарных данных предусмотрен специальный "content-type",
52
+ // для бинарных данных предусмотрен специальный "Content-Type",
53
53
  // который устанавливается автоматически, если не был определен
54
54
  // ранее (к примеру, в обработчике маршрута)
55
55
  if (Buffer.isBuffer(data)) {
56
- if (!response.getHeader('content-type')) {
57
- response.setHeader('content-type', 'application/octet-stream');
56
+ if (!response.getHeader('Content-Type')) {
57
+ response.setHeader('Content-Type', 'application/octet-stream');
58
58
  }
59
59
  debugMsg = 'Buffer has been sent as binary data.';
60
60
  }
61
61
  // объекты, массивы, числа и логические значения
62
62
  // отправляются в виде JSON строки, с соответствующим
63
- // заголовком "content-type" (если не был определен)
63
+ // заголовком "Content-Type" (если не был определен)
64
64
  else {
65
- if (!response.getHeader('content-type')) {
66
- response.setHeader('content-type', 'application/json');
65
+ if (!response.getHeader('Content-Type')) {
66
+ response.setHeader('Content-Type', 'application/json');
67
67
  }
68
68
  debugMsg = format(
69
69
  '%v has been sent as JSON.',
@@ -73,8 +73,8 @@ export class RouterDataSender extends DebuggableService {
73
73
  }
74
74
  break;
75
75
  default:
76
- if (!response.getHeader('content-type')) {
77
- response.setHeader('content-type', 'text/plain');
76
+ if (!response.getHeader('Content-Type')) {
77
+ response.setHeader('Content-Type', 'text/plain');
78
78
  }
79
79
  debugMsg = 'Response data has been sent as plain text.';
80
80
  data = String(data);
@@ -80,7 +80,7 @@ describe('RouterDataSender', function () {
80
80
  writable._final = function (callback) {
81
81
  const sentData = Buffer.concat(chunks).toString('utf-8');
82
82
  expect(sentData).to.be.eq(data);
83
- const ct = res.getHeader('content-type');
83
+ const ct = res.getHeader('Content-Type');
84
84
  expect(ct).to.be.eq('application/octet-stream');
85
85
  callback();
86
86
  done();
@@ -90,7 +90,7 @@ describe('RouterDataSender', function () {
90
90
  S.send(res, stream);
91
91
  });
92
92
 
93
- it('should allow override the "content-type" header for a readable stream', function (done) {
93
+ it('should allow override the "Content-Type" header for a readable stream', function (done) {
94
94
  const data = 'text';
95
95
  const stream = new Readable();
96
96
  stream._read = () => {};
@@ -98,7 +98,7 @@ describe('RouterDataSender', function () {
98
98
  stream.push(null);
99
99
  const res = createResponseMock();
100
100
  const contentType = 'custom/type';
101
- res.setHeader('content-type', contentType);
101
+ res.setHeader('Content-Type', contentType);
102
102
  const writable = new Writable();
103
103
  const chunks = [];
104
104
  writable._write = function (chunk, encoding, done) {
@@ -108,7 +108,7 @@ describe('RouterDataSender', function () {
108
108
  writable._final = function (callback) {
109
109
  const sentData = Buffer.concat(chunks).toString('utf-8');
110
110
  expect(sentData).to.be.eq(data);
111
- const ct = res.getHeader('content-type');
111
+ const ct = res.getHeader('Content-Type');
112
112
  expect(ct).to.be.eq(contentType);
113
113
  callback();
114
114
  done();
@@ -131,7 +131,7 @@ describe('RouterDataSender', function () {
131
131
  const sentJson = Buffer.concat(chunks).toString('utf-8');
132
132
  const sentData = JSON.parse(sentJson);
133
133
  expect(sentData).to.be.eql(data);
134
- const ct = res.getHeader('content-type');
134
+ const ct = res.getHeader('Content-Type');
135
135
  expect(ct).to.be.eq('application/json');
136
136
  callback();
137
137
  done();
@@ -141,11 +141,11 @@ describe('RouterDataSender', function () {
141
141
  S.send(res, data);
142
142
  });
143
143
 
144
- it('should allow override the "content-type" header for a number value', function (done) {
144
+ it('should allow override the "Content-Type" header for a number value', function (done) {
145
145
  const data = 10;
146
146
  const res = createResponseMock();
147
147
  const contentType = 'custom/type';
148
- res.setHeader('content-type', contentType);
148
+ res.setHeader('Content-Type', contentType);
149
149
  const writable = new Writable();
150
150
  const chunks = [];
151
151
  writable._write = function (chunk, encoding, done) {
@@ -156,7 +156,7 @@ describe('RouterDataSender', function () {
156
156
  const sentJson = Buffer.concat(chunks).toString('utf-8');
157
157
  const sentData = JSON.parse(sentJson);
158
158
  expect(sentData).to.be.eql(data);
159
- const ct = res.getHeader('content-type');
159
+ const ct = res.getHeader('Content-Type');
160
160
  expect(ct).to.be.eq(contentType);
161
161
  callback();
162
162
  done();
@@ -179,7 +179,7 @@ describe('RouterDataSender', function () {
179
179
  const sentJson = Buffer.concat(chunks).toString('utf-8');
180
180
  const sentData = JSON.parse(sentJson);
181
181
  expect(sentData).to.be.eql(data);
182
- const ct = res.getHeader('content-type');
182
+ const ct = res.getHeader('Content-Type');
183
183
  expect(ct).to.be.eq('application/json');
184
184
  callback();
185
185
  done();
@@ -189,11 +189,11 @@ describe('RouterDataSender', function () {
189
189
  S.send(res, data);
190
190
  });
191
191
 
192
- it('should allow override the "content-type" header for a boolean value', function (done) {
192
+ it('should allow override the "Content-Type" header for a boolean value', function (done) {
193
193
  const data = true;
194
194
  const res = createResponseMock();
195
195
  const contentType = 'custom/type';
196
- res.setHeader('content-type', contentType);
196
+ res.setHeader('Content-Type', contentType);
197
197
  const writable = new Writable();
198
198
  const chunks = [];
199
199
  writable._write = function (chunk, encoding, done) {
@@ -204,7 +204,7 @@ describe('RouterDataSender', function () {
204
204
  const sentJson = Buffer.concat(chunks).toString('utf-8');
205
205
  const sentData = JSON.parse(sentJson);
206
206
  expect(sentData).to.be.eql(data);
207
- const ct = res.getHeader('content-type');
207
+ const ct = res.getHeader('Content-Type');
208
208
  expect(ct).to.be.eq(contentType);
209
209
  callback();
210
210
  done();
@@ -226,7 +226,7 @@ describe('RouterDataSender', function () {
226
226
  writable._final = function (callback) {
227
227
  const sentData = Buffer.concat(chunks);
228
228
  expect(sentData).to.be.eql(sentData);
229
- const ct = res.getHeader('content-type');
229
+ const ct = res.getHeader('Content-Type');
230
230
  expect(ct).to.be.eq('application/octet-stream');
231
231
  callback();
232
232
  done();
@@ -236,11 +236,11 @@ describe('RouterDataSender', function () {
236
236
  S.send(res, data);
237
237
  });
238
238
 
239
- it('should allow override the "content-type" header for a Buffer', function (done) {
239
+ it('should allow override the "Content-Type" header for a Buffer', function (done) {
240
240
  const data = Buffer.from('text');
241
241
  const res = createResponseMock();
242
242
  const contentType = 'custom/type';
243
- res.setHeader('content-type', contentType);
243
+ res.setHeader('Content-Type', contentType);
244
244
  const writable = new Writable();
245
245
  const chunks = [];
246
246
  writable._write = function (chunk, encoding, done) {
@@ -250,7 +250,7 @@ describe('RouterDataSender', function () {
250
250
  writable._final = function (callback) {
251
251
  const sentData = Buffer.concat(chunks);
252
252
  expect(sentData).to.be.eql(sentData);
253
- const ct = res.getHeader('content-type');
253
+ const ct = res.getHeader('Content-Type');
254
254
  expect(ct).to.be.eq(contentType);
255
255
  callback();
256
256
  done();
@@ -273,7 +273,7 @@ describe('RouterDataSender', function () {
273
273
  const sentJson = Buffer.concat(chunks).toString('utf-8');
274
274
  const sentData = JSON.parse(sentJson);
275
275
  expect(sentData).to.be.eql(data);
276
- const ct = res.getHeader('content-type');
276
+ const ct = res.getHeader('Content-Type');
277
277
  expect(ct).to.be.eq('application/json');
278
278
  callback();
279
279
  done();
@@ -283,11 +283,11 @@ describe('RouterDataSender', function () {
283
283
  S.send(res, data);
284
284
  });
285
285
 
286
- it('should allow override the "content-type" header for an object value', function (done) {
286
+ it('should allow override the "Content-Type" header for an object value', function (done) {
287
287
  const data = {foo: 'bar'};
288
288
  const res = createResponseMock();
289
289
  const contentType = 'custom/type';
290
- res.setHeader('content-type', contentType);
290
+ res.setHeader('Content-Type', contentType);
291
291
  const writable = new Writable();
292
292
  const chunks = [];
293
293
  writable._write = function (chunk, encoding, done) {
@@ -298,7 +298,7 @@ describe('RouterDataSender', function () {
298
298
  const sentJson = Buffer.concat(chunks).toString('utf-8');
299
299
  const sentData = JSON.parse(sentJson);
300
300
  expect(sentData).to.be.eql(data);
301
- const ct = res.getHeader('content-type');
301
+ const ct = res.getHeader('Content-Type');
302
302
  expect(ct).to.be.eq(contentType);
303
303
  callback();
304
304
  done();
@@ -320,7 +320,7 @@ describe('RouterDataSender', function () {
320
320
  writable._final = function (callback) {
321
321
  const sentData = Buffer.concat(chunks).toString('utf-8');
322
322
  expect(sentData).to.be.eq(data);
323
- const ct = res.getHeader('content-type');
323
+ const ct = res.getHeader('Content-Type');
324
324
  expect(ct).to.be.eq('text/plain');
325
325
  callback();
326
326
  done();
@@ -330,11 +330,11 @@ describe('RouterDataSender', function () {
330
330
  S.send(res, data);
331
331
  });
332
332
 
333
- it('should allow override the "content-type" header for a string value', function (done) {
333
+ it('should allow override the "Content-Type" header for a string value', function (done) {
334
334
  const data = 'text';
335
335
  const res = createResponseMock();
336
336
  const contentType = 'custom/type';
337
- res.setHeader('content-type', contentType);
337
+ res.setHeader('Content-Type', contentType);
338
338
  const writable = new Writable();
339
339
  const chunks = [];
340
340
  writable._write = function (chunk, encoding, done) {
@@ -344,7 +344,7 @@ describe('RouterDataSender', function () {
344
344
  writable._final = function (callback) {
345
345
  const sentData = Buffer.concat(chunks).toString('utf-8');
346
346
  expect(sentData).to.be.eq(data);
347
- const ct = res.getHeader('content-type');
347
+ const ct = res.getHeader('Content-Type');
348
348
  expect(ct).to.be.eq(contentType);
349
349
  callback();
350
350
  done();
@@ -63,7 +63,7 @@ export class RouterErrorSender extends DebuggableService {
63
63
  console.error(error);
64
64
  }
65
65
  response.statusCode = statusCode;
66
- response.setHeader('content-type', 'application/json; charset=utf-8');
66
+ response.setHeader('Content-Type', 'application/json; charset=utf-8');
67
67
  response.end(JSON.stringify(body, null, 2), 'utf-8');
68
68
  debug(
69
69
  '%s error has been sent for the request %s %v.',
@@ -83,7 +83,7 @@ export class RouterErrorSender extends DebuggableService {
83
83
  send404(request, response) {
84
84
  const debug = this.getDebuggerFor(this.send404);
85
85
  response.statusCode = 404;
86
- response.setHeader('content-type', 'text/plain; charset=utf-8');
86
+ response.setHeader('Content-Type', 'text/plain; charset=utf-8');
87
87
  response.end('404 Not Found', 'utf-8');
88
88
  debug(
89
89
  '404 error has been sent for the request %s %v.',
@@ -25,7 +25,7 @@ describe('RouterErrorSender', function () {
25
25
  const data = JSON.parse(json);
26
26
  expect(data).to.be.eql({error: {message: 'Unauthorized'}});
27
27
  expect(res.statusCode).to.be.eq(401);
28
- const ct = res.getHeader('content-type');
28
+ const ct = res.getHeader('Content-Type');
29
29
  expect(ct).to.be.eq('application/json; charset=utf-8');
30
30
  callback();
31
31
  done();
@@ -57,7 +57,7 @@ describe('RouterErrorSender', function () {
57
57
  expect(data.error).not.to.have.property('shouldNotBeExposedProp');
58
58
  expect(data).to.be.eql(expectedData);
59
59
  expect(res.statusCode).to.be.eq(401);
60
- const ct = res.getHeader('content-type');
60
+ const ct = res.getHeader('Content-Type');
61
61
  expect(ct).to.be.eq('application/json; charset=utf-8');
62
62
  callback();
63
63
  done();
@@ -82,7 +82,7 @@ describe('RouterErrorSender', function () {
82
82
  const body = Buffer.concat(chunks).toString('utf-8');
83
83
  expect(body).to.be.eql('404 Not Found');
84
84
  expect(res.statusCode).to.be.eq(404);
85
- const ct = res.getHeader('content-type');
85
+ const ct = res.getHeader('Content-Type');
86
86
  expect(ct).to.be.eq('text/plain; charset=utf-8');
87
87
  callback();
88
88
  done();
@@ -1,10 +1,10 @@
1
1
  import {RouterBranch} from './branch/index.js';
2
+ import {RouteRegistry} from './route/index.js';
2
3
  import {RequestParser} from './parsers/index.js';
3
4
  import {RequestContext} from './request-context.js';
4
5
  import {ServerResponse, IncomingMessage} from 'http';
5
6
  import {DebuggableService} from './debuggable-service.js';
6
7
  import {TrieRouterOptions} from './trie-router-options.js';
7
- import {HttpMethod, RouteRegistry} from './route/index.js';
8
8
  import {RouterDataSender, RouterErrorSender} from './senders/index.js';
9
9
  import {isServiceContainer, ServiceContainer} from '@e22m4u/js-service';
10
10
  import {isPromise, isResponseSent, getRequestPathname} from './utils/index.js';
@@ -83,7 +83,7 @@ export class TrieRouter extends DebuggableService {
83
83
  * Example:
84
84
  * ```js
85
85
  * const router = new TrieRouter();
86
- * const apiBranch = router.createBranch({path: 'api'});
86
+ * const apiBranch = router.createBranch({path: '/api'});
87
87
  *
88
88
  * // GET /api/hello
89
89
  * apiBranch.defineRoute({
@@ -165,27 +165,6 @@ export class TrieRouter extends DebuggableService {
165
165
  const resolved =
166
166
  this.getService(RouteRegistry).matchRouteByRequest(request);
167
167
  if (!resolved) {
168
- // обработка метода OPTIONS выполняется автоматически
169
- // перед отправкой ошибки 404, если для пути запроса
170
- // имеются другие методы, то вместо ошибки 404 будет
171
- // отправлен ответ с "Allow*" заголовками
172
- if (request.method.toUpperCase() === HttpMethod.OPTIONS) {
173
- const allowedMethods =
174
- this.getService(RouteRegistry).getAllowedMethodsForRequestPath(
175
- requestPath,
176
- );
177
- if (allowedMethods.length > 0) {
178
- debug('Auto-handling OPTIONS request.');
179
- if (!allowedMethods.includes('OPTIONS')) {
180
- allowedMethods.push('OPTIONS');
181
- }
182
- const allowHeader = allowedMethods.join(', ');
183
- response.statusCode = 204;
184
- response.setHeader('Allow', allowHeader);
185
- response.end();
186
- return;
187
- }
188
- }
189
168
  debug(
190
169
  'No route found for the request %s %v.',
191
170
  request.method,
@@ -1625,64 +1625,6 @@ describe('TrieRouter', function () {
1625
1625
  });
1626
1626
  });
1627
1627
  });
1628
-
1629
- describe('OPTIONS method handling', function () {
1630
- it('should automatically return 204 with specific headers for an unhandled OPTIONS request', async function () {
1631
- const router = new TrieRouter();
1632
- router.defineRoute({
1633
- method: HttpMethod.GET,
1634
- path: '/api/resource',
1635
- handler: () => 'OK',
1636
- });
1637
- router.defineRoute({
1638
- method: HttpMethod.POST,
1639
- path: '/api/resource',
1640
- handler: () => 'OK',
1641
- });
1642
- const req = createRequestMock({
1643
- method: HttpMethod.OPTIONS,
1644
- path: '/api/resource',
1645
- });
1646
- const res = createResponseMock();
1647
- await router.handleRequest(req, res);
1648
- expect(res.statusCode).to.be.eq(204);
1649
- expect(res.getHeader('Allow')).to.be.eq('GET, POST, OPTIONS');
1650
- });
1651
-
1652
- it('should execute a custom OPTIONS handler when it is explicitly defined', async function () {
1653
- const router = new TrieRouter();
1654
- let customOptionsCalled = false;
1655
- router.defineRoute({
1656
- method: HttpMethod.OPTIONS,
1657
- path: '/api/resource',
1658
- handler: () => {
1659
- customOptionsCalled = true;
1660
- return 'Custom OPTIONS response';
1661
- },
1662
- });
1663
- const req = createRequestMock({
1664
- method: HttpMethod.OPTIONS,
1665
- path: '/api/resource',
1666
- });
1667
- const res = createResponseMock();
1668
- await router.handleRequest(req, res);
1669
- const body = await res.getBody();
1670
- expect(customOptionsCalled).to.be.true;
1671
- expect(res.statusCode).to.be.eq(200);
1672
- expect(body).to.be.eq('Custom OPTIONS response');
1673
- });
1674
-
1675
- it('should return 404 for an OPTIONS request if the path does not exist for any method', async function () {
1676
- const router = new TrieRouter();
1677
- const req = createRequestMock({
1678
- method: HttpMethod.OPTIONS,
1679
- path: '/unknown',
1680
- });
1681
- const res = createResponseMock();
1682
- await router.handleRequest(req, res);
1683
- expect(res.statusCode).to.be.eq(404);
1684
- });
1685
- });
1686
1628
  });
1687
1629
 
1688
1630
  describe('addHook', function () {