@e22m4u/js-trie-router 0.7.6 → 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/build-cjs.js +1 -1
- package/dist/cjs/index.cjs +62 -91
- package/package.json +8 -8
- package/src/route/route-registry.d.ts +0 -7
- package/src/route/route-registry.js +1 -31
- package/src/route/route-registry.spec.js +0 -92
- package/src/route/route.d.ts +1 -0
- package/src/route/route.js +2 -0
- package/src/trie-router.js +2 -23
- package/src/trie-router.spec.js +0 -58
package/build-cjs.js
CHANGED
package/dist/cjs/index.cjs
CHANGED
|
@@ -86,7 +86,10 @@ var import_js_debug = require("@e22m4u/js-debug");
|
|
|
86
86
|
// src/debuggable-service.js
|
|
87
87
|
var import_js_service = require("@e22m4u/js-service");
|
|
88
88
|
var MODULE_DEBUG_NAMESPACE = "jsTrieRouter";
|
|
89
|
-
var
|
|
89
|
+
var DebuggableService = class extends import_js_service.DebuggableService {
|
|
90
|
+
static {
|
|
91
|
+
__name(this, "DebuggableService");
|
|
92
|
+
}
|
|
90
93
|
/**
|
|
91
94
|
* Constructor.
|
|
92
95
|
*
|
|
@@ -99,8 +102,6 @@ var _DebuggableService = class _DebuggableService extends import_js_service.Debu
|
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
};
|
|
102
|
-
__name(_DebuggableService, "DebuggableService");
|
|
103
|
-
var DebuggableService = _DebuggableService;
|
|
104
105
|
|
|
105
106
|
// src/utils/clone-deep.js
|
|
106
107
|
function cloneDeep(value) {
|
|
@@ -953,7 +954,10 @@ __name(getRequestPathname, "getRequestPathname");
|
|
|
953
954
|
// src/request-context.js
|
|
954
955
|
var import_js_format11 = require("@e22m4u/js-format");
|
|
955
956
|
var import_js_service2 = require("@e22m4u/js-service");
|
|
956
|
-
var
|
|
957
|
+
var RequestContext = class {
|
|
958
|
+
static {
|
|
959
|
+
__name(this, "RequestContext");
|
|
960
|
+
}
|
|
957
961
|
/**
|
|
958
962
|
* Service container.
|
|
959
963
|
*
|
|
@@ -1139,8 +1143,6 @@ var _RequestContext = class _RequestContext {
|
|
|
1139
1143
|
this._route = route;
|
|
1140
1144
|
}
|
|
1141
1145
|
};
|
|
1142
|
-
__name(_RequestContext, "RequestContext");
|
|
1143
|
-
var RequestContext = _RequestContext;
|
|
1144
1146
|
|
|
1145
1147
|
// src/hooks/router-hook-invoker.js
|
|
1146
1148
|
var import_js_format13 = require("@e22m4u/js-format");
|
|
@@ -1154,7 +1156,10 @@ var RouterHookType = {
|
|
|
1154
1156
|
POST_HANDLER: "postHandler"
|
|
1155
1157
|
};
|
|
1156
1158
|
var ROUTER_HOOK_TYPES = Object.values(RouterHookType);
|
|
1157
|
-
var
|
|
1159
|
+
var RouterHookRegistry = class {
|
|
1160
|
+
static {
|
|
1161
|
+
__name(this, "RouterHookRegistry");
|
|
1162
|
+
}
|
|
1158
1163
|
/**
|
|
1159
1164
|
* Hooks.
|
|
1160
1165
|
*
|
|
@@ -1237,11 +1242,12 @@ var _RouterHookRegistry = class _RouterHookRegistry {
|
|
|
1237
1242
|
return this._hooks.get(type) || [];
|
|
1238
1243
|
}
|
|
1239
1244
|
};
|
|
1240
|
-
__name(_RouterHookRegistry, "RouterHookRegistry");
|
|
1241
|
-
var RouterHookRegistry = _RouterHookRegistry;
|
|
1242
1245
|
|
|
1243
1246
|
// src/hooks/router-hook-invoker.js
|
|
1244
|
-
var
|
|
1247
|
+
var RouterHookInvoker = class extends DebuggableService {
|
|
1248
|
+
static {
|
|
1249
|
+
__name(this, "RouterHookInvoker");
|
|
1250
|
+
}
|
|
1245
1251
|
/**
|
|
1246
1252
|
* Invoke on-request hooks.
|
|
1247
1253
|
*
|
|
@@ -1502,8 +1508,6 @@ var _RouterHookInvoker = class _RouterHookInvoker extends DebuggableService {
|
|
|
1502
1508
|
return currentData;
|
|
1503
1509
|
}
|
|
1504
1510
|
};
|
|
1505
|
-
__name(_RouterHookInvoker, "RouterHookInvoker");
|
|
1506
|
-
var RouterHookInvoker = _RouterHookInvoker;
|
|
1507
1511
|
|
|
1508
1512
|
// src/route/validate-route-definition.js
|
|
1509
1513
|
var import_js_format14 = require("@e22m4u/js-format");
|
|
@@ -1586,6 +1590,7 @@ __name(validateRouteDefinition, "validateRouteDefinition");
|
|
|
1586
1590
|
// src/route/route.js
|
|
1587
1591
|
var HttpMethod = {
|
|
1588
1592
|
GET: "GET",
|
|
1593
|
+
HEAD: "HEAD",
|
|
1589
1594
|
POST: "POST",
|
|
1590
1595
|
PUT: "PUT",
|
|
1591
1596
|
PATCH: "PATCH",
|
|
@@ -1593,7 +1598,10 @@ var HttpMethod = {
|
|
|
1593
1598
|
OPTIONS: "OPTIONS"
|
|
1594
1599
|
};
|
|
1595
1600
|
var DEFAULT_META = Object.freeze({});
|
|
1596
|
-
var
|
|
1601
|
+
var Route = class extends import_js_debug.Debuggable {
|
|
1602
|
+
static {
|
|
1603
|
+
__name(this, "Route");
|
|
1604
|
+
}
|
|
1597
1605
|
/**
|
|
1598
1606
|
* Definition.
|
|
1599
1607
|
*
|
|
@@ -1695,14 +1703,15 @@ var _Route = class _Route extends import_js_debug.Debuggable {
|
|
|
1695
1703
|
return this.handler(context);
|
|
1696
1704
|
}
|
|
1697
1705
|
};
|
|
1698
|
-
__name(_Route, "Route");
|
|
1699
|
-
var Route = _Route;
|
|
1700
1706
|
|
|
1701
1707
|
// src/route/route-registry.js
|
|
1702
1708
|
var import_js_path_trie = require("@e22m4u/js-path-trie");
|
|
1703
1709
|
var import_js_service3 = require("@e22m4u/js-service");
|
|
1704
1710
|
var import_js_format15 = require("@e22m4u/js-format");
|
|
1705
|
-
var
|
|
1711
|
+
var RouteRegistry = class extends DebuggableService {
|
|
1712
|
+
static {
|
|
1713
|
+
__name(this, "RouteRegistry");
|
|
1714
|
+
}
|
|
1706
1715
|
/**
|
|
1707
1716
|
* Constructor.
|
|
1708
1717
|
*
|
|
@@ -1792,38 +1801,7 @@ var _RouteRegistry = class _RouteRegistry extends DebuggableService {
|
|
|
1792
1801
|
requestPath
|
|
1793
1802
|
);
|
|
1794
1803
|
}
|
|
1795
|
-
/**
|
|
1796
|
-
* Get allowed methods for request path.
|
|
1797
|
-
*
|
|
1798
|
-
* @param {string} requestPath
|
|
1799
|
-
* @returns {string[]}
|
|
1800
|
-
*/
|
|
1801
|
-
getAllowedMethodsForRequestPath(requestPath) {
|
|
1802
|
-
if (typeof requestPath !== "string") {
|
|
1803
|
-
throw new import_js_format15.InvalidArgumentError(
|
|
1804
|
-
'Parameter "requestPath" must be a String, but %v was given.',
|
|
1805
|
-
requestPath
|
|
1806
|
-
);
|
|
1807
|
-
}
|
|
1808
|
-
const debug = this.getDebuggerFor(this.getAllowedMethodsForRequestPath);
|
|
1809
|
-
const allowedMethods = [];
|
|
1810
|
-
for (const method of Object.values(HttpMethod)) {
|
|
1811
|
-
const rawTriePath = `${method}/${requestPath}`;
|
|
1812
|
-
const triePath = rawTriePath.replace(/\/+/g, "/");
|
|
1813
|
-
if (this._trie.match(triePath)) {
|
|
1814
|
-
allowedMethods.push(method);
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
if (allowedMethods.length) {
|
|
1818
|
-
debug("Allowed methods for %v are: %l.", requestPath, allowedMethods);
|
|
1819
|
-
} else {
|
|
1820
|
-
debug("Path %v does not have allowed methods.", requestPath);
|
|
1821
|
-
}
|
|
1822
|
-
return allowedMethods;
|
|
1823
|
-
}
|
|
1824
1804
|
};
|
|
1825
|
-
__name(_RouteRegistry, "RouteRegistry");
|
|
1826
|
-
var RouteRegistry = _RouteRegistry;
|
|
1827
1805
|
|
|
1828
1806
|
// src/branch/router-branch.js
|
|
1829
1807
|
var import_js_format17 = require("@e22m4u/js-format");
|
|
@@ -1934,7 +1912,10 @@ function mergeRouterBranchDefinitions(firstDef, secondDef) {
|
|
|
1934
1912
|
__name(mergeRouterBranchDefinitions, "mergeRouterBranchDefinitions");
|
|
1935
1913
|
|
|
1936
1914
|
// src/branch/router-branch.js
|
|
1937
|
-
var
|
|
1915
|
+
var RouterBranch = class _RouterBranch extends DebuggableService {
|
|
1916
|
+
static {
|
|
1917
|
+
__name(this, "RouterBranch");
|
|
1918
|
+
}
|
|
1938
1919
|
/**
|
|
1939
1920
|
* Router.
|
|
1940
1921
|
*
|
|
@@ -2053,8 +2034,6 @@ var _RouterBranch = class _RouterBranch extends DebuggableService {
|
|
|
2053
2034
|
return new _RouterBranch(this._router, branchDef, this);
|
|
2054
2035
|
}
|
|
2055
2036
|
};
|
|
2056
|
-
__name(_RouterBranch, "RouterBranch");
|
|
2057
|
-
var RouterBranch = _RouterBranch;
|
|
2058
2037
|
|
|
2059
2038
|
// src/parsers/request-parser.js
|
|
2060
2039
|
var import_http3 = require("http");
|
|
@@ -2066,7 +2045,10 @@ var import_js_format19 = require("@e22m4u/js-format");
|
|
|
2066
2045
|
|
|
2067
2046
|
// src/trie-router-options.js
|
|
2068
2047
|
var import_js_format18 = require("@e22m4u/js-format");
|
|
2069
|
-
var
|
|
2048
|
+
var TrieRouterOptions = class {
|
|
2049
|
+
static {
|
|
2050
|
+
__name(this, "TrieRouterOptions");
|
|
2051
|
+
}
|
|
2070
2052
|
/**
|
|
2071
2053
|
* Request body bytes limit.
|
|
2072
2054
|
*
|
|
@@ -2141,11 +2123,12 @@ var _TrieRouterOptions = class _TrieRouterOptions {
|
|
|
2141
2123
|
}
|
|
2142
2124
|
}
|
|
2143
2125
|
};
|
|
2144
|
-
__name(_TrieRouterOptions, "TrieRouterOptions");
|
|
2145
|
-
var TrieRouterOptions = _TrieRouterOptions;
|
|
2146
2126
|
|
|
2147
2127
|
// src/parsers/request-body-parser.js
|
|
2148
|
-
var
|
|
2128
|
+
var RequestBodyParser = class extends DebuggableService {
|
|
2129
|
+
static {
|
|
2130
|
+
__name(this, "RequestBodyParser");
|
|
2131
|
+
}
|
|
2149
2132
|
/**
|
|
2150
2133
|
* Parsers.
|
|
2151
2134
|
*
|
|
@@ -2288,8 +2271,6 @@ var _RequestBodyParser = class _RequestBodyParser extends DebuggableService {
|
|
|
2288
2271
|
});
|
|
2289
2272
|
}
|
|
2290
2273
|
};
|
|
2291
|
-
__name(_RequestBodyParser, "RequestBodyParser");
|
|
2292
|
-
var RequestBodyParser = _RequestBodyParser;
|
|
2293
2274
|
function parseJsonBody(input) {
|
|
2294
2275
|
if (typeof input !== "string") {
|
|
2295
2276
|
return void 0;
|
|
@@ -2304,7 +2285,10 @@ __name(parseJsonBody, "parseJsonBody");
|
|
|
2304
2285
|
|
|
2305
2286
|
// src/parsers/request-query-parser.js
|
|
2306
2287
|
var import_querystring2 = __toESM(require("querystring"), 1);
|
|
2307
|
-
var
|
|
2288
|
+
var RequestQueryParser = class extends DebuggableService {
|
|
2289
|
+
static {
|
|
2290
|
+
__name(this, "RequestQueryParser");
|
|
2291
|
+
}
|
|
2308
2292
|
/**
|
|
2309
2293
|
* Parse
|
|
2310
2294
|
*
|
|
@@ -2330,11 +2314,12 @@ var _RequestQueryParser = class _RequestQueryParser extends DebuggableService {
|
|
|
2330
2314
|
return query;
|
|
2331
2315
|
}
|
|
2332
2316
|
};
|
|
2333
|
-
__name(_RequestQueryParser, "RequestQueryParser");
|
|
2334
|
-
var RequestQueryParser = _RequestQueryParser;
|
|
2335
2317
|
|
|
2336
2318
|
// src/parsers/request-cookies-parser.js
|
|
2337
|
-
var
|
|
2319
|
+
var RequestCookiesParser = class extends DebuggableService {
|
|
2320
|
+
static {
|
|
2321
|
+
__name(this, "RequestCookiesParser");
|
|
2322
|
+
}
|
|
2338
2323
|
/**
|
|
2339
2324
|
* Parse
|
|
2340
2325
|
*
|
|
@@ -2360,11 +2345,12 @@ var _RequestCookiesParser = class _RequestCookiesParser extends DebuggableServic
|
|
|
2360
2345
|
return cookies;
|
|
2361
2346
|
}
|
|
2362
2347
|
};
|
|
2363
|
-
__name(_RequestCookiesParser, "RequestCookiesParser");
|
|
2364
|
-
var RequestCookiesParser = _RequestCookiesParser;
|
|
2365
2348
|
|
|
2366
2349
|
// src/parsers/request-parser.js
|
|
2367
|
-
var
|
|
2350
|
+
var RequestParser = class extends DebuggableService {
|
|
2351
|
+
static {
|
|
2352
|
+
__name(this, "RequestParser");
|
|
2353
|
+
}
|
|
2368
2354
|
/**
|
|
2369
2355
|
* Parse.
|
|
2370
2356
|
*
|
|
@@ -2402,15 +2388,16 @@ var _RequestParser = class _RequestParser extends DebuggableService {
|
|
|
2402
2388
|
return promises.length ? Promise.all(promises).then(() => data) : data;
|
|
2403
2389
|
}
|
|
2404
2390
|
};
|
|
2405
|
-
__name(_RequestParser, "RequestParser");
|
|
2406
|
-
var RequestParser = _RequestParser;
|
|
2407
2391
|
|
|
2408
2392
|
// src/trie-router.js
|
|
2409
2393
|
var import_http4 = require("http");
|
|
2410
2394
|
|
|
2411
2395
|
// src/senders/router-data-sender.js
|
|
2412
2396
|
var import_js_format21 = require("@e22m4u/js-format");
|
|
2413
|
-
var
|
|
2397
|
+
var RouterDataSender = class extends DebuggableService {
|
|
2398
|
+
static {
|
|
2399
|
+
__name(this, "RouterDataSender");
|
|
2400
|
+
}
|
|
2414
2401
|
/**
|
|
2415
2402
|
* Send.
|
|
2416
2403
|
*
|
|
@@ -2471,14 +2458,15 @@ var _RouterDataSender = class _RouterDataSender extends DebuggableService {
|
|
|
2471
2458
|
debug(debugMsg);
|
|
2472
2459
|
}
|
|
2473
2460
|
};
|
|
2474
|
-
__name(_RouterDataSender, "RouterDataSender");
|
|
2475
|
-
var RouterDataSender = _RouterDataSender;
|
|
2476
2461
|
|
|
2477
2462
|
// src/senders/router-error-sender.js
|
|
2478
2463
|
var import_util = require("util");
|
|
2479
2464
|
var import_statuses = __toESM(require("statuses"), 1);
|
|
2480
2465
|
var EXPOSED_ERROR_PROPERTIES = ["code", "details"];
|
|
2481
|
-
var
|
|
2466
|
+
var RouterErrorSender = class extends DebuggableService {
|
|
2467
|
+
static {
|
|
2468
|
+
__name(this, "RouterErrorSender");
|
|
2469
|
+
}
|
|
2482
2470
|
/**
|
|
2483
2471
|
* Handle.
|
|
2484
2472
|
*
|
|
@@ -2556,12 +2544,13 @@ var _RouterErrorSender = class _RouterErrorSender extends DebuggableService {
|
|
|
2556
2544
|
);
|
|
2557
2545
|
}
|
|
2558
2546
|
};
|
|
2559
|
-
__name(_RouterErrorSender, "RouterErrorSender");
|
|
2560
|
-
var RouterErrorSender = _RouterErrorSender;
|
|
2561
2547
|
|
|
2562
2548
|
// src/trie-router.js
|
|
2563
2549
|
var import_js_service4 = require("@e22m4u/js-service");
|
|
2564
|
-
var
|
|
2550
|
+
var TrieRouter = class extends DebuggableService {
|
|
2551
|
+
static {
|
|
2552
|
+
__name(this, "TrieRouter");
|
|
2553
|
+
}
|
|
2565
2554
|
/**
|
|
2566
2555
|
* Constructor.
|
|
2567
2556
|
*
|
|
@@ -2616,7 +2605,7 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
|
|
|
2616
2605
|
* Example:
|
|
2617
2606
|
* ```js
|
|
2618
2607
|
* const router = new TrieRouter();
|
|
2619
|
-
* const apiBranch = router.createBranch({path: 'api'});
|
|
2608
|
+
* const apiBranch = router.createBranch({path: '/api'});
|
|
2620
2609
|
*
|
|
2621
2610
|
* // GET /api/hello
|
|
2622
2611
|
* apiBranch.defineRoute({
|
|
@@ -2683,22 +2672,6 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
|
|
|
2683
2672
|
}
|
|
2684
2673
|
const resolved = this.getService(RouteRegistry).matchRouteByRequest(request);
|
|
2685
2674
|
if (!resolved) {
|
|
2686
|
-
if (request.method.toUpperCase() === HttpMethod.OPTIONS) {
|
|
2687
|
-
const allowedMethods = this.getService(RouteRegistry).getAllowedMethodsForRequestPath(
|
|
2688
|
-
requestPath
|
|
2689
|
-
);
|
|
2690
|
-
if (allowedMethods.length > 0) {
|
|
2691
|
-
debug("Auto-handling OPTIONS request.");
|
|
2692
|
-
if (!allowedMethods.includes("OPTIONS")) {
|
|
2693
|
-
allowedMethods.push("OPTIONS");
|
|
2694
|
-
}
|
|
2695
|
-
const allowHeader = allowedMethods.join(", ");
|
|
2696
|
-
response.statusCode = 204;
|
|
2697
|
-
response.setHeader("Allow", allowHeader);
|
|
2698
|
-
response.end();
|
|
2699
|
-
return;
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
2675
|
debug(
|
|
2703
2676
|
"No route found for the request %s %v.",
|
|
2704
2677
|
request.method,
|
|
@@ -2784,8 +2757,6 @@ var _TrieRouter = class _TrieRouter extends DebuggableService {
|
|
|
2784
2757
|
return this.getService(RouterHookRegistry).hasHook(type, hook);
|
|
2785
2758
|
}
|
|
2786
2759
|
};
|
|
2787
|
-
__name(_TrieRouter, "TrieRouter");
|
|
2788
|
-
var TrieRouter = _TrieRouter;
|
|
2789
2760
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2790
2761
|
0 && (module.exports = {
|
|
2791
2762
|
CHARACTER_ENCODING_LIST,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/js-trie-router",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "HTTP маршрутизатор для Node.js на основе префиксного дерева",
|
|
5
5
|
"author": "Mikhail Evstropov <e22m4u@yandex.ru>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"require": "./dist/cjs/index.cjs"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
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.
|
|
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.
|
|
51
|
-
"@commitlint/config-conventional": "~20.4.
|
|
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": "~
|
|
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.
|
|
64
|
+
"eslint-plugin-jsdoc": "~62.7.1",
|
|
65
65
|
"eslint-plugin-mocha": "~11.2.0",
|
|
66
|
-
"globals": "~17.
|
|
66
|
+
"globals": "~17.4.0",
|
|
67
67
|
"husky": "~9.1.7",
|
|
68
68
|
"mocha": "~11.7.5",
|
|
69
69
|
"prettier": "~3.8.1",
|
|
@@ -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 {
|
|
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
|
});
|
package/src/route/route.d.ts
CHANGED
package/src/route/route.js
CHANGED
|
@@ -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',
|
package/src/trie-router.js
CHANGED
|
@@ -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,
|
package/src/trie-router.spec.js
CHANGED
|
@@ -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 () {
|