hono 1.1.1 → 1.2.2
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 +126 -117
- package/dist/compose.d.ts +2 -2
- package/dist/compose.js +20 -8
- package/dist/compose.test.d.ts +1 -0
- package/dist/compose.test.js +511 -0
- package/dist/context.d.ts +8 -1
- package/dist/context.js +27 -37
- package/dist/context.test.d.ts +1 -0
- package/dist/context.test.js +127 -0
- package/dist/hono.d.ts +44 -21
- package/dist/hono.js +74 -43
- package/dist/hono.test.d.ts +1 -0
- package/dist/hono.test.js +592 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/middleware/basic-auth/index.js +2 -2
- package/dist/middleware/basic-auth/index.test.d.ts +1 -0
- package/dist/middleware/basic-auth/index.test.js +119 -0
- package/dist/middleware/body-parse/index.test.d.ts +1 -0
- package/dist/middleware/body-parse/index.test.js +59 -0
- package/dist/middleware/cookie/index.d.ts +1 -1
- package/dist/middleware/cookie/index.test.d.ts +1 -0
- package/dist/middleware/cookie/index.test.js +54 -0
- package/dist/middleware/cors/index.test.d.ts +1 -0
- package/dist/middleware/cors/index.test.js +59 -0
- package/dist/middleware/etag/index.test.d.ts +1 -0
- package/dist/middleware/etag/index.test.js +45 -0
- package/dist/middleware/graphql-server/index.js +1 -1
- package/dist/middleware/graphql-server/index.test.d.ts +1 -0
- package/dist/middleware/graphql-server/index.test.js +480 -0
- package/dist/middleware/graphql-server/parse-body.test.d.ts +1 -0
- package/dist/middleware/graphql-server/parse-body.test.js +57 -0
- package/dist/middleware/jwt/index.js +9 -11
- package/dist/middleware/jwt/index.test.d.ts +1 -0
- package/dist/middleware/jwt/index.test.js +51 -0
- package/dist/middleware/logger/index.test.d.ts +1 -0
- package/dist/middleware/logger/index.test.js +49 -0
- package/dist/middleware/mustache/index.test.d.ts +1 -0
- package/dist/middleware/mustache/index.test.js +49 -0
- package/dist/middleware/powered-by/index.test.d.ts +1 -0
- package/dist/middleware/powered-by/index.test.js +15 -0
- package/dist/middleware/pretty-json/index.test.d.ts +1 -0
- package/dist/middleware/pretty-json/index.test.js +28 -0
- package/dist/middleware/serve-static/index.test.d.ts +1 -0
- package/dist/middleware/serve-static/index.test.js +58 -0
- package/dist/router/reg-exp-router/index.d.ts +1 -1
- package/dist/router/reg-exp-router/index.js +1 -1
- package/dist/router/reg-exp-router/node.d.ts +3 -0
- package/dist/router/reg-exp-router/node.js +13 -7
- package/dist/router/reg-exp-router/router.d.ts +21 -2
- package/dist/router/reg-exp-router/router.js +301 -81
- package/dist/router/reg-exp-router/router.test.d.ts +1 -0
- package/dist/router/reg-exp-router/router.test.js +212 -0
- package/dist/router/reg-exp-router/trie.d.ts +7 -3
- package/dist/router/reg-exp-router/trie.js +3 -3
- package/dist/router/trie-router/index.d.ts +1 -1
- package/dist/router/trie-router/index.js +1 -1
- package/dist/router/trie-router/node.d.ts +4 -3
- package/dist/router/trie-router/node.js +123 -55
- package/dist/router/trie-router/node.test.d.ts +1 -0
- package/dist/router/trie-router/node.test.js +351 -0
- package/dist/router/trie-router/router.d.ts +2 -2
- package/dist/router/trie-router/router.js +1 -1
- package/dist/router/trie-router/router.test.d.ts +1 -0
- package/dist/router/trie-router/router.test.js +98 -0
- package/dist/router.d.ts +4 -3
- package/dist/router.js +5 -4
- package/dist/utils/body.js +2 -2
- package/dist/utils/body.test.d.ts +1 -0
- package/dist/utils/body.test.js +45 -0
- package/dist/utils/buffer.js +1 -1
- package/dist/utils/buffer.test.d.ts +1 -0
- package/dist/utils/buffer.test.js +36 -0
- package/dist/utils/cloudflare.test.d.ts +1 -0
- package/dist/utils/cloudflare.test.js +42 -0
- package/dist/utils/crypto.test.d.ts +1 -0
- package/dist/utils/crypto.test.js +15 -0
- package/dist/utils/encode.test.d.ts +1 -0
- package/dist/utils/encode.test.js +54 -0
- package/dist/utils/http-status.test.d.ts +1 -0
- package/dist/utils/http-status.test.js +8 -0
- package/dist/utils/jwt/jwt.test.d.ts +1 -0
- package/dist/utils/jwt/jwt.test.js +171 -0
- package/dist/utils/jwt/types.test.d.ts +1 -0
- package/dist/utils/jwt/types.test.js +12 -0
- package/dist/utils/mime.test.d.ts +1 -0
- package/dist/utils/mime.test.js +13 -0
- package/dist/utils/url.js +4 -4
- package/dist/utils/url.test.d.ts +1 -0
- package/dist/utils/url.test.js +87 -0
- package/package.json +3 -4
|
@@ -2,113 +2,333 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RegExpRouter = void 0;
|
|
4
4
|
const router_1 = require("../../router");
|
|
5
|
-
const trie_1 = require("
|
|
6
|
-
const regExpMatchAll = new RegExp('');
|
|
5
|
+
const trie_1 = require("./trie");
|
|
7
6
|
const emptyParam = {};
|
|
7
|
+
const nullMatcher = [/^$/, []];
|
|
8
|
+
function initHint(path) {
|
|
9
|
+
const components = path.match(/\/(?::\w+{[^}]+}|[^\/]*)/g) || [];
|
|
10
|
+
let componentsLength = components.length;
|
|
11
|
+
const paramIndexList = [];
|
|
12
|
+
const regExpComponents = [];
|
|
13
|
+
const namedParams = [];
|
|
14
|
+
for (let i = 0, len = components.length; i < len; i++) {
|
|
15
|
+
if (i === len - 1 && components[i] === '/*') {
|
|
16
|
+
componentsLength--;
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const m = components[i].match(/^\/:(\w+)({[^}]+})?/);
|
|
20
|
+
if (m) {
|
|
21
|
+
namedParams.push([i, m[1], m[2] || '[^/]+']);
|
|
22
|
+
regExpComponents[i] = m[2] || true;
|
|
23
|
+
}
|
|
24
|
+
else if (components[i] === '/*') {
|
|
25
|
+
regExpComponents[i] = true;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
regExpComponents[i] = components[i];
|
|
29
|
+
}
|
|
30
|
+
if (/\/(?::|\*)/.test(components[i])) {
|
|
31
|
+
paramIndexList.push(i);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
components,
|
|
36
|
+
regExpComponents,
|
|
37
|
+
componentsLength,
|
|
38
|
+
endWithWildcard: path.endsWith('*'),
|
|
39
|
+
paramIndexList,
|
|
40
|
+
namedParams,
|
|
41
|
+
maybeHandler: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function compareRoute(a, b) {
|
|
45
|
+
if (a.path === '*') {
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
let i = 0;
|
|
49
|
+
const len = a.hint.regExpComponents.length;
|
|
50
|
+
for (; i < len; i++) {
|
|
51
|
+
if (a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) {
|
|
52
|
+
if (a.hint.regExpComponents[i] === true) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// may be ambiguous
|
|
59
|
+
for (; i < len; i++) {
|
|
60
|
+
if (a.hint.regExpComponents[i] !== true &&
|
|
61
|
+
a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) {
|
|
62
|
+
return 2;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return i === b.hint.regExpComponents.length || a.hint.endWithWildcard ? 1 : 0;
|
|
66
|
+
}
|
|
67
|
+
function buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous = false) {
|
|
68
|
+
const trie = new trie_1.Trie({ reverse: hasAmbiguous });
|
|
69
|
+
const handlers = [];
|
|
70
|
+
if (routes.length === 0) {
|
|
71
|
+
return nullMatcher;
|
|
72
|
+
}
|
|
73
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
74
|
+
const paramMap = trie.insert(routes[i].path, i);
|
|
75
|
+
handlers[i] = [
|
|
76
|
+
[...routes[i].middleware, ...routes[i].handlers],
|
|
77
|
+
Object.keys(paramMap).length !== 0 ? paramMap : null,
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
81
|
+
for (let i = 0, len = handlers.length; i < len; i++) {
|
|
82
|
+
const paramMap = handlers[i][1];
|
|
83
|
+
if (paramMap) {
|
|
84
|
+
for (let j = 0, len = paramMap.length; j < len; j++) {
|
|
85
|
+
paramMap[j][1] = paramReplacementMap[paramMap[j][1]];
|
|
86
|
+
const aliasTo = routes[i].paramAliasMap[paramMap[j][0]];
|
|
87
|
+
if (aliasTo) {
|
|
88
|
+
for (let k = 0, len = aliasTo.length; k < len; k++) {
|
|
89
|
+
paramMap.push([aliasTo[k], paramMap[j][1]]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const handlerMap = [];
|
|
96
|
+
// using `in` because indexReplacementMap is a sparse array
|
|
97
|
+
for (const i in indexReplacementMap) {
|
|
98
|
+
handlerMap[i] = handlers[indexReplacementMap[i]];
|
|
99
|
+
}
|
|
100
|
+
return [regexp, handlerMap];
|
|
101
|
+
}
|
|
102
|
+
function verifyDuplicateParam(routes) {
|
|
103
|
+
const nameMap = {};
|
|
104
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
105
|
+
const route = routes[i];
|
|
106
|
+
for (let k = 0, len = route.hint.namedParams.length; k < len; k++) {
|
|
107
|
+
const [index, name] = route.hint.namedParams[k];
|
|
108
|
+
if (name in nameMap && index !== nameMap[name]) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
nameMap[name] = index;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const paramAliasMap = route.paramAliasMap;
|
|
116
|
+
const paramAliasMapKeys = Object.keys(paramAliasMap);
|
|
117
|
+
for (let k = 0, len = paramAliasMapKeys.length; k < len; k++) {
|
|
118
|
+
const aliasFrom = paramAliasMapKeys[k];
|
|
119
|
+
for (let l = 0, len = paramAliasMap[aliasFrom].length; l < len; l++) {
|
|
120
|
+
const aliasTo = paramAliasMap[aliasFrom][l];
|
|
121
|
+
const index = nameMap[aliasFrom];
|
|
122
|
+
if (aliasTo in nameMap && index !== nameMap[aliasTo]) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
nameMap[aliasTo] = index;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
8
133
|
class RegExpRouter extends router_1.Router {
|
|
9
134
|
constructor() {
|
|
10
135
|
super(...arguments);
|
|
11
|
-
this.
|
|
136
|
+
this.routeData = { routes: [], methods: new Set() };
|
|
12
137
|
}
|
|
13
138
|
add(method, path, handler) {
|
|
14
|
-
|
|
15
|
-
if (!this.routes) {
|
|
139
|
+
if (!this.routeData) {
|
|
16
140
|
throw new Error('Can not add a route since the matcher is already built.');
|
|
17
141
|
}
|
|
18
|
-
|
|
19
|
-
|
|
142
|
+
const { routes, methods } = this.routeData;
|
|
143
|
+
if (path === '/*') {
|
|
144
|
+
path = '*';
|
|
145
|
+
}
|
|
146
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
147
|
+
if (routes[i].method === method && routes[i].path === path) {
|
|
148
|
+
routes[i].handlers.push(handler);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
methods.add(method);
|
|
153
|
+
routes.push({
|
|
154
|
+
method,
|
|
155
|
+
path,
|
|
156
|
+
handlers: [handler],
|
|
157
|
+
hint: initHint(path),
|
|
158
|
+
middleware: [],
|
|
159
|
+
paramAliasMap: {},
|
|
160
|
+
});
|
|
20
161
|
}
|
|
21
162
|
match(method, path) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (regexp === regExpMatchAll) {
|
|
31
|
-
match = () => result;
|
|
163
|
+
const [primaryMatchers, secondaryMatchers, hasAmbiguous] = this.buildAllMatchers();
|
|
164
|
+
this.match = hasAmbiguous
|
|
165
|
+
? (method, path) => {
|
|
166
|
+
const matcher = primaryMatchers[method] || primaryMatchers[router_1.METHOD_NAME_ALL];
|
|
167
|
+
let match = path.match(matcher[0]);
|
|
168
|
+
if (!match) {
|
|
169
|
+
// do not support secondary matchers here.
|
|
170
|
+
return null;
|
|
32
171
|
}
|
|
33
|
-
|
|
34
|
-
|
|
172
|
+
const params = {};
|
|
173
|
+
const handlers = new Set();
|
|
174
|
+
let regExpSrc = matcher[0].source;
|
|
175
|
+
while (match) {
|
|
176
|
+
let index = match.indexOf('', 1);
|
|
177
|
+
for (;;) {
|
|
178
|
+
const [handler, paramMap] = matcher[1][index];
|
|
179
|
+
if (paramMap) {
|
|
180
|
+
for (let i = 0, len = paramMap.length; i < len; i++) {
|
|
181
|
+
params[paramMap[i][0]] = match[paramMap[i][1]];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (let i = 0, len = handler.length; i < len; i++) {
|
|
185
|
+
handlers.add(handler[i]);
|
|
186
|
+
}
|
|
187
|
+
const newIndex = match.indexOf('', index + 1);
|
|
188
|
+
if (newIndex === -1) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
index = newIndex;
|
|
192
|
+
}
|
|
193
|
+
regExpSrc = regExpSrc.replace(new RegExp(`((?:(?:\\(\\?:|.)*?\\([^)]*\\)){${index - 1}}.*?)\\(\\)`), '$1(^)');
|
|
194
|
+
match = path.match(new RegExp(regExpSrc));
|
|
35
195
|
}
|
|
196
|
+
return new router_1.Result([...handlers.values()], params);
|
|
36
197
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
198
|
+
: (method, path) => {
|
|
199
|
+
let matcher = primaryMatchers[method] || primaryMatchers[router_1.METHOD_NAME_ALL];
|
|
200
|
+
let match = path.match(matcher[0]);
|
|
201
|
+
if (!match) {
|
|
202
|
+
const matchers = secondaryMatchers[method] || secondaryMatchers[router_1.METHOD_NAME_ALL];
|
|
203
|
+
for (let i = 0, len = matchers.length; i < len && !match; i++) {
|
|
204
|
+
matcher = matchers[i];
|
|
205
|
+
match = path.match(matcher[0]);
|
|
206
|
+
}
|
|
207
|
+
if (!match) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const index = match.indexOf('', 1);
|
|
212
|
+
const [handler, paramMap] = matcher[1][index];
|
|
213
|
+
if (!paramMap) {
|
|
214
|
+
return new router_1.Result(handler, emptyParam);
|
|
215
|
+
}
|
|
216
|
+
const params = {};
|
|
217
|
+
for (let i = 0, len = paramMap.length; i < len; i++) {
|
|
218
|
+
params[paramMap[i][0]] = match[paramMap[i][1]];
|
|
219
|
+
}
|
|
220
|
+
return new router_1.Result(handler, params);
|
|
221
|
+
};
|
|
222
|
+
return this.match(method, path);
|
|
223
|
+
}
|
|
224
|
+
buildAllMatchers() {
|
|
225
|
+
this.routeData.routes.sort(({ hint: a }, { hint: b }) => {
|
|
226
|
+
if (a.componentsLength !== b.componentsLength) {
|
|
227
|
+
return a.componentsLength - b.componentsLength;
|
|
46
228
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
229
|
+
for (let i = 0, len = Math.min(a.paramIndexList.length, b.paramIndexList.length) + 1; i < len; i++) {
|
|
230
|
+
if (a.paramIndexList[i] !== b.paramIndexList[i]) {
|
|
231
|
+
if (a.paramIndexList[i] === undefined) {
|
|
232
|
+
return -1;
|
|
233
|
+
}
|
|
234
|
+
else if (b.paramIndexList[i] === undefined) {
|
|
235
|
+
return 1;
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
return a.paramIndexList[i] - b.paramIndexList[i];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
51
241
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
params[paramMap[i][0]] = match[paramMap[i][1]];
|
|
242
|
+
if (a.endWithWildcard !== b.endWithWildcard) {
|
|
243
|
+
return a.endWithWildcard ? -1 : 1;
|
|
55
244
|
}
|
|
56
|
-
return
|
|
245
|
+
return 0;
|
|
57
246
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
247
|
+
const primaryMatchers = {};
|
|
248
|
+
const secondaryMatchers = {};
|
|
249
|
+
let hasAmbiguous = false;
|
|
250
|
+
this.routeData.methods.forEach((method) => {
|
|
251
|
+
let _hasAmbiguous;
|
|
252
|
+
[primaryMatchers[method], secondaryMatchers[method], _hasAmbiguous] =
|
|
253
|
+
this.buildMatcher(method);
|
|
254
|
+
hasAmbiguous = hasAmbiguous || _hasAmbiguous;
|
|
65
255
|
});
|
|
66
|
-
|
|
67
|
-
|
|
256
|
+
primaryMatchers[router_1.METHOD_NAME_ALL] || (primaryMatchers[router_1.METHOD_NAME_ALL] = nullMatcher);
|
|
257
|
+
secondaryMatchers[router_1.METHOD_NAME_ALL] || (secondaryMatchers[router_1.METHOD_NAME_ALL] = []);
|
|
258
|
+
delete this.routeData; // to reduce memory usage
|
|
259
|
+
return [primaryMatchers, secondaryMatchers, hasAmbiguous];
|
|
68
260
|
}
|
|
69
261
|
buildMatcher(method) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const targetMethods = [method];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
262
|
+
var _a, _b;
|
|
263
|
+
let hasAmbiguous = false;
|
|
264
|
+
const targetMethods = new Set([method, router_1.METHOD_NAME_ALL]);
|
|
265
|
+
const routes = this.routeData.routes.filter(({ method }) => targetMethods.has(method));
|
|
266
|
+
// Reset temporary data per method
|
|
267
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
268
|
+
routes[i].middleware = [];
|
|
269
|
+
routes[i].paramAliasMap = {};
|
|
270
|
+
}
|
|
271
|
+
// preprocess routes
|
|
272
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
273
|
+
for (let j = i + 1; j < len; j++) {
|
|
274
|
+
const compareResult = compareRoute(routes[i], routes[j]);
|
|
275
|
+
// i includes j
|
|
276
|
+
if (compareResult === 1) {
|
|
277
|
+
const components = routes[j].hint.components;
|
|
278
|
+
const namedParams = routes[i].hint.namedParams;
|
|
279
|
+
for (let k = 0, len = namedParams.length; k < len; k++) {
|
|
280
|
+
const c = components[namedParams[k][0]];
|
|
281
|
+
const m = c.match(/^\/:(\w+)({[^}]+})?/);
|
|
282
|
+
if (m && namedParams[k][1] === m[1]) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (m) {
|
|
286
|
+
(_a = routes[j].paramAliasMap)[_b = m[1]] || (_a[_b] = []);
|
|
287
|
+
routes[j].paramAliasMap[m[1]].push(namedParams[k][1]);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
components[namedParams[k][0]] = `/:${namedParams[k][1]}{${c.substring(1)}}`;
|
|
291
|
+
routes[j].hint.namedParams.push([
|
|
292
|
+
namedParams[k][0],
|
|
293
|
+
namedParams[k][1],
|
|
294
|
+
c.substring(1),
|
|
295
|
+
]);
|
|
296
|
+
routes[j].path = components.join('');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
routes[j].middleware.push(...routes[i].handlers);
|
|
300
|
+
routes[i].hint.maybeHandler = false;
|
|
301
|
+
}
|
|
302
|
+
else if (compareResult === 2) {
|
|
303
|
+
// ambiguous
|
|
304
|
+
hasAmbiguous = true;
|
|
305
|
+
if (!verifyDuplicateParam([routes[i], routes[j]])) {
|
|
306
|
+
throw new Error('Duplicate param name');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
83
309
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
const tmp = routes[0][0].endsWith('*')
|
|
87
|
-
? routes[0][0].replace(/\/\*$/, '(?:$|/)') // /path/to/* => /path/to(?:$|/)
|
|
88
|
-
: `${routes[0][0]}$`; // /path/to/action => /path/to/action$
|
|
89
|
-
const regExpStr = `^${tmp.replace(/\*/g, '[^/]+')}`; // /prefix/*/path/to => /prefix/[^/]+/path/to
|
|
90
|
-
return [new RegExp(regExpStr), [[routes[0][1], null]]];
|
|
310
|
+
if (!verifyDuplicateParam([routes[i]])) {
|
|
311
|
+
throw new Error('Duplicate param name');
|
|
91
312
|
}
|
|
92
313
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
handlers[i] = [routes[i][1], Object.keys(paramMap).length !== 0 ? paramMap : null];
|
|
314
|
+
if (hasAmbiguous) {
|
|
315
|
+
return [buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous), [], hasAmbiguous];
|
|
96
316
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
317
|
+
const primaryRoutes = [];
|
|
318
|
+
const secondaryRoutes = [];
|
|
319
|
+
for (let i = 0, len = routes.length; i < len; i++) {
|
|
320
|
+
if (routes[i].hint.maybeHandler || !routes[i].hint.endWithWildcard) {
|
|
321
|
+
primaryRoutes.push(routes[i]);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
secondaryRoutes.push(routes[i]);
|
|
104
325
|
}
|
|
105
326
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return [regexp, handlerMap];
|
|
327
|
+
return [
|
|
328
|
+
buildMatcherFromPreprocessedRoutes(primaryRoutes, hasAmbiguous),
|
|
329
|
+
[buildMatcherFromPreprocessedRoutes(secondaryRoutes, hasAmbiguous)],
|
|
330
|
+
hasAmbiguous,
|
|
331
|
+
];
|
|
112
332
|
}
|
|
113
333
|
}
|
|
114
334
|
exports.RegExpRouter = RegExpRouter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const router_1 = require("../../router");
|
|
4
|
+
const router_2 = require("./router");
|
|
5
|
+
describe('Basic Usage', () => {
|
|
6
|
+
const router = new router_2.RegExpRouter();
|
|
7
|
+
router.add('GET', '/hello', 'get hello');
|
|
8
|
+
router.add('POST', '/hello', 'post hello');
|
|
9
|
+
it('get, post hello', async () => {
|
|
10
|
+
let res = router.match('GET', '/hello');
|
|
11
|
+
expect(res).not.toBeNull();
|
|
12
|
+
expect(res.handlers).toEqual(['get hello']);
|
|
13
|
+
res = router.match('POST', '/hello');
|
|
14
|
+
expect(res).not.toBeNull();
|
|
15
|
+
expect(res.handlers).toEqual(['post hello']);
|
|
16
|
+
res = router.match('PUT', '/hello');
|
|
17
|
+
expect(res).toBeNull();
|
|
18
|
+
res = router.match('GET', '/');
|
|
19
|
+
expect(res).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('Complex', () => {
|
|
23
|
+
let router;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
router = new router_2.RegExpRouter();
|
|
26
|
+
});
|
|
27
|
+
it('Named Param', async () => {
|
|
28
|
+
router.add('GET', '/entry/:id', 'get entry');
|
|
29
|
+
const res = router.match('GET', '/entry/123');
|
|
30
|
+
expect(res).not.toBeNull();
|
|
31
|
+
expect(res.handlers).toEqual(['get entry']);
|
|
32
|
+
expect(res.params['id']).toBe('123');
|
|
33
|
+
});
|
|
34
|
+
it('Wildcard', async () => {
|
|
35
|
+
router.add('GET', '/wild/*/card', 'get wildcard');
|
|
36
|
+
const res = router.match('GET', '/wild/xxx/card');
|
|
37
|
+
expect(res).not.toBeNull();
|
|
38
|
+
expect(res.handlers).toEqual(['get wildcard']);
|
|
39
|
+
});
|
|
40
|
+
it('Default', async () => {
|
|
41
|
+
router.add('GET', '/api/abc', 'get api');
|
|
42
|
+
router.add('GET', '/api/*', 'fallback');
|
|
43
|
+
let res = router.match('GET', '/api/abc');
|
|
44
|
+
expect(res).not.toBeNull();
|
|
45
|
+
expect(res.handlers).toEqual(['fallback', 'get api']);
|
|
46
|
+
res = router.match('GET', '/api/def');
|
|
47
|
+
expect(res).not.toBeNull();
|
|
48
|
+
expect(res.handlers).toEqual(['fallback']);
|
|
49
|
+
});
|
|
50
|
+
it('Regexp', async () => {
|
|
51
|
+
router.add('GET', '/post/:date{[0-9]+}/:title{[a-z]+}', 'get post');
|
|
52
|
+
let res = router.match('GET', '/post/20210101/hello');
|
|
53
|
+
expect(res).not.toBeNull();
|
|
54
|
+
expect(res.handlers).toEqual(['get post']);
|
|
55
|
+
expect(res.params['date']).toBe('20210101');
|
|
56
|
+
expect(res.params['title']).toBe('hello');
|
|
57
|
+
res = router.match('GET', '/post/onetwothree');
|
|
58
|
+
expect(res).toBeNull();
|
|
59
|
+
res = router.match('GET', '/post/123/123');
|
|
60
|
+
expect(res).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('Order independent', () => {
|
|
64
|
+
let router;
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
router = new router_2.RegExpRouter();
|
|
67
|
+
});
|
|
68
|
+
it('abstract -> concrete', async () => {
|
|
69
|
+
router.add('GET', '/:type/:action', 'foo');
|
|
70
|
+
router.add('GET', '/posts/:id', 'bar');
|
|
71
|
+
const res = router.match('GET', '/posts/123');
|
|
72
|
+
expect(res).not.toBeNull();
|
|
73
|
+
expect(res.handlers).toEqual(['foo', 'bar']);
|
|
74
|
+
});
|
|
75
|
+
it('concrete -> abstract', async () => {
|
|
76
|
+
router.add('GET', '/posts/:id', 'bar');
|
|
77
|
+
router.add('GET', '/:type/:action', 'foo');
|
|
78
|
+
const res = router.match('GET', '/posts/123');
|
|
79
|
+
expect(res).not.toBeNull();
|
|
80
|
+
expect(res.handlers).toEqual(['foo', 'bar']);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('Optimization for METHOD_NAME_OF_ALL', () => {
|
|
84
|
+
let router;
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
router = new router_2.RegExpRouter();
|
|
87
|
+
});
|
|
88
|
+
it('Apply to all requests', async () => {
|
|
89
|
+
router.add(router_1.METHOD_NAME_ALL, '*', 'OK');
|
|
90
|
+
const res = router.match('GET', '/entry/123');
|
|
91
|
+
expect(res).not.toBeNull();
|
|
92
|
+
expect(res.handlers).toEqual(['OK']);
|
|
93
|
+
expect(res.params).toMatchObject({});
|
|
94
|
+
});
|
|
95
|
+
it('Apply to all requests under a specific path', async () => {
|
|
96
|
+
router.add(router_1.METHOD_NAME_ALL, '/path/to/*', 'OK');
|
|
97
|
+
let res = router.match('GET', '/entry/123');
|
|
98
|
+
expect(res).toBeNull();
|
|
99
|
+
res = router.match('GET', '/path/to/entry/123');
|
|
100
|
+
expect(res).not.toBeNull();
|
|
101
|
+
expect(res.handlers).toEqual(['OK']);
|
|
102
|
+
expect(res.params).toMatchObject({});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('Multi match', () => {
|
|
106
|
+
describe('Blog', () => {
|
|
107
|
+
const router = new router_2.RegExpRouter();
|
|
108
|
+
router.add('ALL', '*', 'middleware a');
|
|
109
|
+
router.add('GET', '*', 'middleware b');
|
|
110
|
+
router.add('GET', '/entry', 'get entries');
|
|
111
|
+
router.add('POST', '/entry/*', 'middleware c');
|
|
112
|
+
router.add('POST', '/entry', 'post entry');
|
|
113
|
+
router.add('GET', '/entry/:id', 'get entry');
|
|
114
|
+
router.add('GET', '/entry/:id/comment/:comment_id', 'get comment');
|
|
115
|
+
it('GET /', async () => {
|
|
116
|
+
const res = router.match('GET', '/');
|
|
117
|
+
expect(res).not.toBeNull();
|
|
118
|
+
expect(res.handlers).toEqual(['middleware a', 'middleware b']);
|
|
119
|
+
});
|
|
120
|
+
it('GET /entry/123', async () => {
|
|
121
|
+
const res = router.match('GET', '/entry/123');
|
|
122
|
+
expect(res).not.toBeNull();
|
|
123
|
+
expect(res.handlers).toEqual(['middleware a', 'middleware b', 'get entry']);
|
|
124
|
+
expect(res.params['id']).toBe('123');
|
|
125
|
+
});
|
|
126
|
+
it('GET /entry/123/comment/456', async () => {
|
|
127
|
+
const res = router.match('GET', '/entry/123/comment/456');
|
|
128
|
+
expect(res).not.toBeNull();
|
|
129
|
+
expect(res.handlers).toEqual(['middleware a', 'middleware b', 'get comment']);
|
|
130
|
+
expect(res.params['id']).toBe('123');
|
|
131
|
+
expect(res.params['comment_id']).toBe('456');
|
|
132
|
+
});
|
|
133
|
+
it('POST /entry', async () => {
|
|
134
|
+
const res = router.match('POST', '/entry');
|
|
135
|
+
expect(res).not.toBeNull();
|
|
136
|
+
expect(res.handlers).toEqual(['middleware a', 'middleware c', 'post entry']);
|
|
137
|
+
});
|
|
138
|
+
it('DELETE /entry', async () => {
|
|
139
|
+
const res = router.match('DELETE', '/entry');
|
|
140
|
+
expect(res).not.toBeNull();
|
|
141
|
+
expect(res.handlers).toEqual(['middleware a']);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('Ambiguous', () => {
|
|
145
|
+
const router = new router_2.RegExpRouter();
|
|
146
|
+
router.add('GET', '/:user/entries', 'get user entries');
|
|
147
|
+
router.add('GET', '/entry/:name', 'get entry');
|
|
148
|
+
it('GET /entry/entry', async () => {
|
|
149
|
+
const res = router.match('GET', '/entry/entries');
|
|
150
|
+
expect(res).not.toBeNull();
|
|
151
|
+
expect(res.handlers).toEqual(['get user entries', 'get entry']);
|
|
152
|
+
expect(res.params['user']).toBe('entry');
|
|
153
|
+
expect(res.params['name']).toBe('entries');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('Multiple handlers', () => {
|
|
157
|
+
const router = new router_2.RegExpRouter();
|
|
158
|
+
router.add('GET', '/:type/:id', ':type');
|
|
159
|
+
router.add('GET', '/:class/:id', ':class');
|
|
160
|
+
router.add('GET', '/:model/:id', ':model');
|
|
161
|
+
router.add('GET', '/entry/:id', 'entry');
|
|
162
|
+
it('GET /entry/123', async () => {
|
|
163
|
+
const res = router.match('GET', '/entry/123');
|
|
164
|
+
expect(res).not.toBeNull();
|
|
165
|
+
expect(res.handlers).toEqual([':type', ':class', ':model', 'entry']);
|
|
166
|
+
expect(res.params['type']).toBe('entry');
|
|
167
|
+
expect(res.params['class']).toBe('entry');
|
|
168
|
+
expect(res.params['model']).toBe('entry');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
describe('Check for duplicate parameter names', () => {
|
|
173
|
+
it('self', () => {
|
|
174
|
+
const router = new router_2.RegExpRouter();
|
|
175
|
+
router.add('GET', '/:id/:id', 'get');
|
|
176
|
+
expect(() => {
|
|
177
|
+
router.match('GET', '/'); // check parameter names on the first `match` call
|
|
178
|
+
}).toThrowError(/Duplicate param name/);
|
|
179
|
+
});
|
|
180
|
+
it('parent', () => {
|
|
181
|
+
const router = new router_2.RegExpRouter();
|
|
182
|
+
router.add('GET', '/:id/:action', 'foo');
|
|
183
|
+
router.add('GET', '/posts/:id', 'bar');
|
|
184
|
+
expect(() => {
|
|
185
|
+
router.match('GET', '/');
|
|
186
|
+
}).toThrowError(/Duplicate param name/);
|
|
187
|
+
});
|
|
188
|
+
it('child', () => {
|
|
189
|
+
const router = new router_2.RegExpRouter();
|
|
190
|
+
router.add('GET', '/posts/:id', 'foo');
|
|
191
|
+
router.add('GET', '/:id/:action', 'bar');
|
|
192
|
+
expect(() => {
|
|
193
|
+
router.match('GET', '/');
|
|
194
|
+
}).toThrowError(/Duplicate param name/);
|
|
195
|
+
});
|
|
196
|
+
it('hierarchy', () => {
|
|
197
|
+
const router = new router_2.RegExpRouter();
|
|
198
|
+
router.add('GET', '/posts/:id/comments/:comment_id', 'foo');
|
|
199
|
+
router.add('GET', '/posts/:id', 'bar');
|
|
200
|
+
expect(() => {
|
|
201
|
+
router.match('GET', '/');
|
|
202
|
+
}).not.toThrow();
|
|
203
|
+
});
|
|
204
|
+
it('different regular expression', () => {
|
|
205
|
+
const router = new router_2.RegExpRouter();
|
|
206
|
+
router.add('GET', '/:id/:action{create|update}', 'foo');
|
|
207
|
+
router.add('GET', '/:id/:action{delete}', 'bar');
|
|
208
|
+
expect(() => {
|
|
209
|
+
router.match('GET', '/');
|
|
210
|
+
}).not.toThrow();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type { ParamMap, Context } from '
|
|
2
|
-
import { Node } from '
|
|
3
|
-
export type { ParamMap } from '
|
|
1
|
+
import type { ParamMap, Context } from './node';
|
|
2
|
+
import { Node } from './node';
|
|
3
|
+
export type { ParamMap } from './node';
|
|
4
4
|
export declare type ReplacementMap = number[];
|
|
5
|
+
interface InitOptions {
|
|
6
|
+
reverse: boolean;
|
|
7
|
+
}
|
|
5
8
|
export declare class Trie {
|
|
6
9
|
context: Context;
|
|
7
10
|
root: Node;
|
|
11
|
+
constructor({ reverse }?: InitOptions);
|
|
8
12
|
insert(path: string, index: number): ParamMap;
|
|
9
13
|
buildRegExp(): [RegExp, ReplacementMap, ReplacementMap];
|
|
10
14
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Trie = void 0;
|
|
4
|
-
const node_1 = require("
|
|
4
|
+
const node_1 = require("./node");
|
|
5
5
|
class Trie {
|
|
6
|
-
constructor() {
|
|
6
|
+
constructor({ reverse } = { reverse: false }) {
|
|
7
7
|
this.context = { varIndex: 0 };
|
|
8
|
-
this.root = new node_1.Node();
|
|
8
|
+
this.root = new node_1.Node({ reverse });
|
|
9
9
|
}
|
|
10
10
|
insert(path, index) {
|
|
11
11
|
const paramMap = [];
|