hono 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +189 -125
  2. package/dist/compose.d.ts +2 -2
  3. package/dist/compose.js +20 -8
  4. package/dist/context.d.ts +4 -5
  5. package/dist/context.js +5 -17
  6. package/dist/hono.d.ts +51 -25
  7. package/dist/hono.js +106 -49
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +2 -1
  10. package/dist/middleware/basic-auth/index.js +11 -10
  11. package/dist/middleware/body-parse/index.d.ts +5 -0
  12. package/dist/middleware/cookie/index.d.ts +1 -3
  13. package/dist/middleware/graphql-server/parse-body.d.ts +1 -3
  14. package/dist/middleware/jwt/index.d.ts +6 -0
  15. package/dist/middleware/jwt/index.js +49 -0
  16. package/dist/middleware/logger/index.js +3 -5
  17. package/dist/middleware/mustache/index.js +3 -9
  18. package/dist/router/reg-exp-router/node.d.ts +3 -0
  19. package/dist/router/reg-exp-router/node.js +13 -7
  20. package/dist/router/reg-exp-router/router.d.ts +21 -2
  21. package/dist/router/reg-exp-router/router.js +300 -80
  22. package/dist/router/reg-exp-router/trie.d.ts +4 -0
  23. package/dist/router/reg-exp-router/trie.js +2 -2
  24. package/dist/router/trie-router/node.d.ts +4 -3
  25. package/dist/router/trie-router/node.js +123 -55
  26. package/dist/router/trie-router/router.d.ts +1 -1
  27. package/dist/router.d.ts +4 -3
  28. package/dist/router.js +5 -4
  29. package/dist/utils/body.js +2 -2
  30. package/dist/utils/buffer.d.ts +1 -0
  31. package/dist/utils/buffer.js +9 -1
  32. package/dist/utils/crypto.d.ts +0 -2
  33. package/dist/utils/crypto.js +1 -51
  34. package/dist/utils/encode.d.ts +7 -0
  35. package/dist/utils/encode.js +105 -0
  36. package/dist/utils/jwt/index.d.ts +1 -0
  37. package/dist/utils/jwt/index.js +27 -0
  38. package/dist/utils/jwt/jwt.d.ts +7 -0
  39. package/dist/utils/jwt/jwt.js +98 -0
  40. package/dist/utils/jwt/types.d.ts +20 -0
  41. package/dist/utils/jwt/types.js +44 -0
  42. package/dist/utils/url.js +4 -4
  43. package/package.json +29 -21
@@ -1,7 +1,26 @@
1
1
  import { Router, Result } from '../../router';
2
- declare type Route<T> = [string, T];
2
+ interface Hint {
3
+ components: string[];
4
+ regExpComponents: Array<true | string>;
5
+ componentsLength: number;
6
+ endWithWildcard: boolean;
7
+ paramIndexList: number[];
8
+ maybeHandler: boolean;
9
+ namedParams: [number, string, string][];
10
+ }
11
+ interface Route<T> {
12
+ method: string;
13
+ path: string;
14
+ hint: Hint;
15
+ handlers: T[];
16
+ middleware: T[];
17
+ paramAliasMap: Record<string, string[]>;
18
+ }
3
19
  export declare class RegExpRouter<T> extends Router<T> {
4
- routes?: Record<string, Route<T>[]>;
20
+ routeData?: {
21
+ routes: Route<T>[];
22
+ methods: Set<string>;
23
+ };
5
24
  add(method: string, path: string, handler: T): void;
6
25
  match(method: string, path: string): Result<T> | null;
7
26
  private buildAllMatchers;
@@ -3,112 +3,332 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RegExpRouter = void 0;
4
4
  const router_1 = require("../../router");
5
5
  const trie_1 = require("../../router/reg-exp-router/trie");
6
- const regExpMatchAll = new RegExp('');
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.routes = {};
136
+ this.routeData = { routes: [], methods: new Set() };
12
137
  }
13
138
  add(method, path, handler) {
14
- var _a;
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
- (_a = this.routes)[method] || (_a[method] = []);
19
- this.routes[method].push([path, handler]);
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 matchers = this.buildAllMatchers();
23
- let match;
24
- // Optimization for middleware
25
- const methods = Object.keys(matchers);
26
- if (methods.length === 1 && methods[0] === router_1.METHOD_NAME_OF_ALL) {
27
- const [regexp, handlers] = matchers[router_1.METHOD_NAME_OF_ALL];
28
- if (handlers.length === 1) {
29
- const result = new router_1.Result(handlers[0][0], emptyParam);
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
- else if (handlers.length === 1 && !handlers[0][1]) {
34
- match = (_, path) => (regexp.test(path) ? result : null);
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
- match || (match = (method, path) => {
39
- const matcher = matchers[method] || matchers[router_1.METHOD_NAME_OF_ALL];
40
- if (!matcher) {
41
- return null;
42
- }
43
- const match = path.match(matcher[0]);
44
- if (!match) {
45
- return null;
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
- const index = match.indexOf('', 1);
48
- const [handler, paramMap] = matcher[1][index];
49
- if (!paramMap) {
50
- return new router_1.Result(handler, emptyParam);
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
- const params = {};
53
- for (let i = 0; i < paramMap.length; i++) {
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 new router_1.Result(handler, params);
245
+ return 0;
57
246
  });
58
- this.match = match;
59
- return this.match(method, path);
60
- }
61
- buildAllMatchers() {
62
- const matchers = {};
63
- Object.keys(this.routes).forEach((method) => {
64
- matchers[method] = this.buildMatcher(method);
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
- delete this.routes; // to reduce memory usage
67
- return matchers;
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
- const trie = new trie_1.Trie();
71
- const handlers = [];
72
- const targetMethods = [method];
73
- if (method !== router_1.METHOD_NAME_OF_ALL) {
74
- targetMethods.unshift(router_1.METHOD_NAME_OF_ALL);
75
- }
76
- const routes = targetMethods.flatMap((method) => this.routes[method] || []);
77
- if (routes.length === 0) {
78
- return null;
79
- }
80
- if (method === router_1.METHOD_NAME_OF_ALL) {
81
- if (routes.length === 1 && routes[0][0] === '*') {
82
- return [regExpMatchAll, [[routes[0][1], null]]];
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 (routes.length === 1 && !routes[0][0].match(/:/)) {
85
- // there is only one route and no capture
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
- for (let i = 0; i < routes.length; i++) {
94
- const paramMap = trie.insert(routes[i][0], i);
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 [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
98
- for (let i = 0; i < handlers.length; i++) {
99
- const paramMap = handlers[i][1];
100
- if (paramMap) {
101
- for (let i = 0; i < paramMap.length; i++) {
102
- paramMap[i][1] = paramReplacementMap[paramMap[i][1]];
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
- const handlerMap = [];
107
- // using `in` because indexReplacementMap is a sparse array
108
- for (const i in indexReplacementMap) {
109
- handlerMap[i] = handlers[indexReplacementMap[i]];
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;
@@ -2,9 +2,13 @@ import type { ParamMap, Context } from '../../router/reg-exp-router/node';
2
2
  import { Node } from '../../router/reg-exp-router/node';
3
3
  export type { ParamMap } from '../../router/reg-exp-router/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
  }
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Trie = void 0;
4
4
  const node_1 = require("../../router/reg-exp-router/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 = [];
@@ -1,12 +1,13 @@
1
1
  import { Result } from '../../router';
2
2
  import type { Pattern } from '../../utils/url';
3
3
  export declare class Node<T> {
4
- method: Record<string, T>;
5
- handler: T;
4
+ methods: Record<string, T>[];
5
+ handlers: T[];
6
6
  children: Record<string, Node<T>>;
7
- middlewares: [];
8
7
  patterns: Pattern[];
9
8
  constructor(method?: string, handler?: T, children?: Record<string, Node<T>>);
10
9
  insert(method: string, path: string, handler: T): Node<T>;
10
+ private getHandlers;
11
+ private next;
11
12
  search(method: string, path: string): Result<T>;
12
13
  }