@suveren/gateway 0.6.5 → 0.7.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 (84) hide show
  1. package/bin/suveren-gateway.js +19 -2
  2. package/dist/control-plane/index.mjs +208 -34
  3. package/dist/ui/assets/index-CkWaRck1.css +1 -0
  4. package/dist/ui/assets/index-DHT9QCrE.js +110 -0
  5. package/dist/ui/favicon.svg +8 -0
  6. package/dist/ui/index.html +6 -2
  7. package/lib/autostart-templates.d.mts +2 -0
  8. package/lib/autostart-templates.mjs +16 -3
  9. package/node_modules/@hap/core/README.md +4 -4
  10. package/node_modules/@hap/core/package.json +1 -1
  11. package/node_modules/@hono/node-server/README.md +47 -1
  12. package/node_modules/@hono/node-server/dist/early-hints.cjs +35 -0
  13. package/node_modules/@hono/node-server/dist/early-hints.d.cts +16 -0
  14. package/node_modules/@hono/node-server/dist/early-hints.d.mts +16 -0
  15. package/node_modules/@hono/node-server/dist/early-hints.mjs +33 -0
  16. package/node_modules/@hono/node-server/dist/index.cjs +4 -1
  17. package/node_modules/@hono/node-server/dist/index.mjs +4 -1
  18. package/node_modules/@hono/node-server/package.json +14 -1
  19. package/node_modules/express-rate-limit/dist/index.cjs +6 -1
  20. package/node_modules/express-rate-limit/dist/index.mjs +6 -1
  21. package/node_modules/express-rate-limit/package.json +6 -6
  22. package/node_modules/hono/dist/cjs/context.js +28 -13
  23. package/node_modules/hono/dist/cjs/hono-base.js +3 -2
  24. package/node_modules/hono/dist/cjs/jsx/base.js +26 -14
  25. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +2 -2
  26. package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
  27. package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
  28. package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
  29. package/node_modules/hono/dist/cjs/middleware/etag/index.js +4 -1
  30. package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
  31. package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
  32. package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
  33. package/node_modules/hono/dist/cjs/request.js +6 -8
  34. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
  35. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
  36. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
  37. package/node_modules/hono/dist/cjs/router/trie-router/node.js +1 -1
  38. package/node_modules/hono/dist/cjs/router.js +1 -1
  39. package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
  40. package/node_modules/hono/dist/cjs/utils/stream.js +5 -2
  41. package/node_modules/hono/dist/cjs/utils/url.js +7 -7
  42. package/node_modules/hono/dist/context.js +28 -13
  43. package/node_modules/hono/dist/hono-base.js +3 -2
  44. package/node_modules/hono/dist/jsx/base.js +26 -14
  45. package/node_modules/hono/dist/jsx/hooks/index.js +2 -2
  46. package/node_modules/hono/dist/middleware/cache/index.js +103 -8
  47. package/node_modules/hono/dist/middleware/compress/index.js +5 -0
  48. package/node_modules/hono/dist/middleware/cors/index.js +1 -1
  49. package/node_modules/hono/dist/middleware/etag/index.js +4 -1
  50. package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
  51. package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
  52. package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
  53. package/node_modules/hono/dist/request.js +7 -9
  54. package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
  55. package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
  56. package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
  57. package/node_modules/hono/dist/router/trie-router/node.js +1 -1
  58. package/node_modules/hono/dist/router.js +1 -1
  59. package/node_modules/hono/dist/types/client/types.d.ts +1 -1
  60. package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
  61. package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
  62. package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
  63. package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
  64. package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
  65. package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
  66. package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
  67. package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
  68. package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
  69. package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  70. package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
  71. package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
  72. package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
  73. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
  74. package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
  75. package/node_modules/hono/dist/types/router.d.ts +1 -1
  76. package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
  77. package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  78. package/node_modules/hono/dist/utils/cookie.js +2 -2
  79. package/node_modules/hono/dist/utils/stream.js +5 -2
  80. package/node_modules/hono/dist/utils/url.js +5 -6
  81. package/node_modules/hono/package.json +9 -1
  82. package/package.json +1 -1
  83. package/dist/ui/assets/index-DIcuh0I4.css +0 -1
  84. package/dist/ui/assets/index-Dl5isaOV.js +0 -105
@@ -21,6 +21,7 @@ var selectEncoding = (header, candidates) => {
21
21
  }
22
22
  return best?.encoding;
23
23
  };
24
+ var varyAcceptEncodingRegExp = /(?:^|,)\s*accept-encoding\s*(?:,|$)/i;
24
25
  var compress = (options) => {
25
26
  const threshold = options?.threshold ?? 1024;
26
27
  const candidates = options?.encoding ? [options.encoding] : ENCODING_TYPES;
@@ -44,6 +45,10 @@ var compress = (options) => {
44
45
  !shouldTransform(ctx.res)) {
45
46
  return;
46
47
  }
48
+ const current = ctx.res.headers.get("Vary");
49
+ if (current !== "*" && !(current && varyAcceptEncodingRegExp.test(current))) {
50
+ ctx.header("Vary", current ? `${current}, Accept-Encoding` : "Accept-Encoding");
51
+ }
47
52
  const accepted = ctx.req.header("Accept-Encoding");
48
53
  const encoding = selectEncoding(accepted, candidates);
49
54
  if (!encoding || !ctx.res.body) {
@@ -2,7 +2,7 @@
2
2
  var cors = (options) => {
3
3
  const opts = {
4
4
  origin: "*",
5
- allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
5
+ allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "QUERY"],
6
6
  allowHeaders: [],
7
7
  exposeHeaders: [],
8
8
  ...options
@@ -32,6 +32,9 @@ var etag = (options) => {
32
32
  return async function etag2(c, next) {
33
33
  const ifNoneMatch = c.req.header("If-None-Match") ?? null;
34
34
  await next();
35
+ if (!(c.req.method === "GET" || c.req.method === "HEAD" || c.req.method === "QUERY") || !c.res.ok) {
36
+ return;
37
+ }
35
38
  const res = c.res;
36
39
  let etag3 = res.headers.get("ETag");
37
40
  if (!etag3) {
@@ -48,7 +51,7 @@ var etag = (options) => {
48
51
  }
49
52
  etag3 = weak ? `W/"${hash}"` : `"${hash}"`;
50
53
  }
51
- const matched = ifNoneMatch === "*" ? (c.req.method === "GET" || c.req.method === "HEAD") && res.ok : etagMatches(etag3, ifNoneMatch);
54
+ const matched = ifNoneMatch === "*" || etagMatches(etag3, ifNoneMatch);
52
55
  if (matched) {
53
56
  c.res = new Response(null, {
54
57
  status: 304,
@@ -24,7 +24,8 @@ var jwk = (options, init) => {
24
24
  res: unauthorizedResponse({
25
25
  ctx,
26
26
  error: "invalid_request",
27
- errDescription
27
+ errDescription,
28
+ realm: options.realm
28
29
  })
29
30
  });
30
31
  } else {
@@ -62,7 +63,8 @@ var jwk = (options, init) => {
62
63
  res: unauthorizedResponse({
63
64
  ctx,
64
65
  error: "invalid_request",
65
- errDescription
66
+ errDescription,
67
+ realm: options.realm
66
68
  })
67
69
  });
68
70
  }
@@ -89,7 +91,8 @@ var jwk = (options, init) => {
89
91
  ctx,
90
92
  error: "invalid_token",
91
93
  statusText: "Unauthorized",
92
- errDescription: "token verification failure"
94
+ errDescription: "token verification failure",
95
+ realm: options.realm
93
96
  }),
94
97
  cause
95
98
  });
@@ -99,11 +102,13 @@ var jwk = (options, init) => {
99
102
  };
100
103
  };
101
104
  function unauthorizedResponse(opts) {
105
+ const realm = (opts.realm ?? opts.ctx.req.url).replace(/"/g, '\\"');
106
+ const errDescription = opts.errDescription.replace(/"/g, '\\"');
102
107
  return new Response("Unauthorized", {
103
108
  status: 401,
104
109
  statusText: opts.statusText,
105
110
  headers: {
106
- "WWW-Authenticate": `Bearer realm="${opts.ctx.req.url}",error="${opts.error}",error_description="${opts.errDescription}"`
111
+ "WWW-Authenticate": `Bearer realm="${realm}",error="${opts.error}",error_description="${errDescription}"`
107
112
  }
108
113
  });
109
114
  }
@@ -27,7 +27,8 @@ var jwt = (options) => {
27
27
  res: unauthorizedResponse({
28
28
  ctx,
29
29
  error: "invalid_request",
30
- errDescription
30
+ errDescription,
31
+ realm: options.realm
31
32
  })
32
33
  });
33
34
  } else {
@@ -62,7 +63,8 @@ var jwt = (options) => {
62
63
  res: unauthorizedResponse({
63
64
  ctx,
64
65
  error: "invalid_request",
65
- errDescription
66
+ errDescription,
67
+ realm: options.realm
66
68
  })
67
69
  });
68
70
  }
@@ -83,7 +85,8 @@ var jwt = (options) => {
83
85
  ctx,
84
86
  error: "invalid_token",
85
87
  statusText: "Unauthorized",
86
- errDescription: "token verification failure"
88
+ errDescription: "token verification failure",
89
+ realm: options.realm
87
90
  }),
88
91
  cause
89
92
  });
@@ -93,11 +96,13 @@ var jwt = (options) => {
93
96
  };
94
97
  };
95
98
  function unauthorizedResponse(opts) {
99
+ const realm = (opts.realm ?? opts.ctx.req.url).replace(/"/g, '\\"');
100
+ const errDescription = opts.errDescription.replace(/"/g, '\\"');
96
101
  return new Response("Unauthorized", {
97
102
  status: 401,
98
103
  statusText: opts.statusText,
99
104
  headers: {
100
- "WWW-Authenticate": `Bearer realm="${opts.ctx.req.url}",error="${opts.error}",error_description="${opts.errDescription}"`
105
+ "WWW-Authenticate": `Bearer realm="${realm}",error="${opts.error}",error_description="${errDescription}"`
101
106
  }
102
107
  });
103
108
  }
@@ -0,0 +1,68 @@
1
+ // src/middleware/method-not-allowed/index.ts
2
+ import { basePath, matchedRoutes } from "../../helper/route/index.js";
3
+ import { METHOD_NAME_ALL } from "../../router.js";
4
+ import { TrieRouter } from "../../router/trie-router/index.js";
5
+ var methodNotAllowed = (options) => {
6
+ let methodRouter;
7
+ return async function methodNotAllowed2(c, next) {
8
+ const routeIndex = c.req.routeIndex;
9
+ await next();
10
+ if (c.res.status !== 404) {
11
+ return;
12
+ }
13
+ if (c.error) {
14
+ return;
15
+ }
16
+ if (!methodRouter) {
17
+ const methodsByPath = /* @__PURE__ */ new Map();
18
+ for (const route of options.app.routes) {
19
+ if (route.method === METHOD_NAME_ALL || route.method === "HEAD") {
20
+ continue;
21
+ }
22
+ const methods2 = methodsByPath.get(route.path) ?? /* @__PURE__ */ new Set();
23
+ methods2.add(route.method);
24
+ if (route.method === "GET") {
25
+ methods2.add("HEAD");
26
+ }
27
+ methodsByPath.set(route.path, methods2);
28
+ }
29
+ methodRouter = new TrieRouter();
30
+ for (const [path, methods2] of methodsByPath) {
31
+ methodRouter.add(METHOD_NAME_ALL, path, [...methods2]);
32
+ }
33
+ }
34
+ let requestPath = c.req.path;
35
+ const routes = matchedRoutes(c);
36
+ const currentRoute = routes[routeIndex];
37
+ const sourceRoute = options.app.routes.find((route) => route.handler === methodNotAllowed2);
38
+ if (currentRoute && sourceRoute) {
39
+ const currentBasePathParts = basePath(c, routeIndex).split("/").filter(Boolean);
40
+ const sourceBasePathLength = sourceRoute.basePath.split("/").filter(Boolean).length;
41
+ const mountBasePathParts = currentBasePathParts.slice(
42
+ 0,
43
+ currentBasePathParts.length - sourceBasePathLength
44
+ );
45
+ if (mountBasePathParts.length > 0) {
46
+ const mountBasePath = `/${mountBasePathParts.join("/")}`;
47
+ requestPath = c.req.path.slice(mountBasePath.length) || "/";
48
+ }
49
+ }
50
+ const allowedMethods = /* @__PURE__ */ new Set();
51
+ for (const [methods2] of methodRouter.match(METHOD_NAME_ALL, requestPath)[0]) {
52
+ for (const method of methods2) {
53
+ allowedMethods.add(method);
54
+ }
55
+ }
56
+ if (allowedMethods.size === 0 || allowedMethods.has(c.req.method)) {
57
+ return;
58
+ }
59
+ c.res.headers.delete("Allow");
60
+ c.res.headers.delete("Content-Length");
61
+ const methods = [...allowedMethods];
62
+ const allow = methods.join(", ");
63
+ c.res = options.onMethodNotAllowed ? await options.onMethodNotAllowed(c, methods) : c.text("Method Not Allowed", 405, { Allow: allow });
64
+ };
65
+ };
66
+ export {
67
+ methodNotAllowed
68
+ };
@@ -2,8 +2,7 @@
2
2
  import { HTTPException } from "./http-exception.js";
3
3
  import { GET_MATCH_RESULT } from "./request/constants.js";
4
4
  import { parseBody } from "./utils/body.js";
5
- import { decodeURIComponent_, getQueryParam, getQueryParams, tryDecode } from "./utils/url.js";
6
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
5
+ import { getQueryParam, getQueryParams, tryDecodeURIComponent } from "./utils/url.js";
7
6
  var HonoRequest = class {
8
7
  /**
9
8
  * `.raw` can get the raw Request object.
@@ -42,7 +41,6 @@ var HonoRequest = class {
42
41
  this.raw = request;
43
42
  this.path = path;
44
43
  this.#matchResult = matchResult;
45
- this.#validatedData = {};
46
44
  }
47
45
  param(key) {
48
46
  return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
@@ -50,7 +48,7 @@ var HonoRequest = class {
50
48
  #getDecodedParam(key) {
51
49
  const paramKey = this.#matchResult[0][this.routeIndex][1][key];
52
50
  const param = this.#getParamValue(paramKey);
53
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
51
+ return param && tryDecodeURIComponent(param);
54
52
  }
55
53
  #getAllDecodedParams() {
56
54
  const decoded = {};
@@ -58,7 +56,7 @@ var HonoRequest = class {
58
56
  for (const key of keys) {
59
57
  const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
60
58
  if (value !== void 0) {
61
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
59
+ decoded[key] = tryDecodeURIComponent(value);
62
60
  }
63
61
  }
64
62
  return decoded;
@@ -91,8 +89,7 @@ var HonoRequest = class {
91
89
  if (cachedBody) {
92
90
  return cachedBody;
93
91
  }
94
- const anyCachedKey = Object.keys(bodyCache)[0];
95
- if (anyCachedKey) {
92
+ for (const anyCachedKey in bodyCache) {
96
93
  return bodyCache[anyCachedKey].then((body) => {
97
94
  if (anyCachedKey === "json") {
98
95
  body = JSON.stringify(body);
@@ -195,10 +192,11 @@ var HonoRequest = class {
195
192
  * @param data - The validated data to add.
196
193
  */
197
194
  addValidatedData(target, data) {
198
- this.#validatedData[target] = data;
195
+ ;
196
+ (this.#validatedData ??= {})[target] = data;
199
197
  }
200
198
  valid(target) {
201
- return this.#validatedData[target];
199
+ return this.#validatedData?.[target];
202
200
  }
203
201
  /**
204
202
  * `.url()` can get the request url strings.
@@ -12,7 +12,7 @@ function compareKey(a, b) {
12
12
  return 1;
13
13
  }
14
14
  if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
15
- return 1;
15
+ return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
16
16
  } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
17
17
  return -1;
18
18
  }
@@ -24,76 +24,75 @@ function compareKey(a, b) {
24
24
  return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
25
25
  }
26
26
  var Node = class _Node {
27
+ // handler index of a dynamic path, or -1 for a static path terminal
27
28
  #index;
28
29
  #varIndex;
29
30
  #children = /* @__PURE__ */ Object.create(null);
30
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
31
- if (tokens.length === 0) {
32
- if (this.#index !== void 0) {
33
- throw PATH_ERROR;
34
- }
35
- if (pathErrorCheckOnly) {
36
- return;
37
- }
38
- this.#index = index;
39
- return;
40
- }
41
- const [token, ...restTokens] = tokens;
42
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
43
- let node;
44
- if (pattern) {
45
- const name = pattern[1];
46
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
47
- if (name && pattern[2]) {
48
- if (regexpStr === ".*") {
49
- throw PATH_ERROR;
50
- }
51
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
52
- if (/\((?!\?:)/.test(regexpStr)) {
53
- throw PATH_ERROR;
31
+ insert(tokens, index, paramMap, context, isStatic) {
32
+ let node = this;
33
+ for (let i = 0, len = tokens.length; i < len; i++) {
34
+ const token = tokens[i];
35
+ const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
36
+ let nextNode;
37
+ if (pattern) {
38
+ const name = pattern[1];
39
+ let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
40
+ if (name && pattern[2]) {
41
+ if (regexpStr === ".*") {
42
+ throw PATH_ERROR;
43
+ }
44
+ regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
45
+ if (/\((?!\?:)/.test(regexpStr)) {
46
+ throw PATH_ERROR;
47
+ }
48
+ if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
49
+ throw PATH_ERROR;
50
+ }
54
51
  }
55
- }
56
- node = this.#children[regexpStr];
57
- if (!node) {
58
- if (Object.keys(this.#children).some(
59
- (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
60
- )) {
61
- throw PATH_ERROR;
52
+ nextNode = node.#children[regexpStr];
53
+ if (!nextNode) {
54
+ if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
55
+ for (const k in node.#children) {
56
+ if (
57
+ // a single-char pattern coexists with single-char literals as a literal does
58
+ (regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
59
+ ) {
60
+ throw PATH_ERROR;
61
+ }
62
+ }
63
+ }
64
+ nextNode = node.#children[regexpStr] = new _Node();
62
65
  }
63
- if (pathErrorCheckOnly) {
64
- return;
65
- }
66
- node = this.#children[regexpStr] = new _Node();
67
66
  if (name !== "") {
68
- node.#varIndex = context.varIndex++;
67
+ nextNode.#varIndex ??= context.varIndex++;
68
+ paramMap.push([name, nextNode.#varIndex]);
69
69
  }
70
- }
71
- if (!pathErrorCheckOnly && name !== "") {
72
- paramMap.push([name, node.#varIndex]);
73
- }
74
- } else {
75
- node = this.#children[token];
76
- if (!node) {
77
- if (Object.keys(this.#children).some(
78
- (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
79
- )) {
80
- throw PATH_ERROR;
70
+ } else {
71
+ nextNode = node.#children[token];
72
+ if (!nextNode) {
73
+ for (const k in node.#children) {
74
+ if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
75
+ throw PATH_ERROR;
76
+ }
77
+ }
78
+ nextNode = node.#children[token] = new _Node();
81
79
  }
82
- if (pathErrorCheckOnly) {
83
- return;
84
- }
85
- node = this.#children[token] = new _Node();
86
80
  }
81
+ node = nextNode;
82
+ }
83
+ if (node.#index !== void 0) {
84
+ throw PATH_ERROR;
87
85
  }
88
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
86
+ node.#index = isStatic ? -1 : index;
89
87
  }
90
88
  buildRegExpStr() {
91
89
  const childKeys = Object.keys(this.#children).sort(compareKey);
92
90
  const strList = childKeys.map((k) => {
93
91
  const c = this.#children[k];
94
- return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
95
- });
96
- if (typeof this.#index === "number") {
92
+ const childStr = c.buildRegExpStr();
93
+ return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
94
+ }).filter(Boolean);
95
+ if (typeof this.#index === "number" && this.#index !== -1) {
97
96
  strList.unshift(`#${this.#index}`);
98
97
  }
99
98
  if (strList.length === 0) {
@@ -8,7 +8,6 @@ import { checkOptionalParameter } from "../../utils/url.js";
8
8
  import { match, emptyParam } from "./matcher.js";
9
9
  import { PATH_ERROR } from "./node.js";
10
10
  import { Trie } from "./trie.js";
11
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
12
11
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
13
12
  function buildWildcardRegExp(path) {
14
13
  return wildcardRegExpCache[path] ??= new RegExp(
@@ -21,63 +20,6 @@ function buildWildcardRegExp(path) {
21
20
  function clearWildcardRegExpCache() {
22
21
  wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
23
22
  }
24
- function buildMatcherFromPreprocessedRoutes(routes) {
25
- const trie = new Trie();
26
- const handlerData = [];
27
- if (routes.length === 0) {
28
- return nullMatcher;
29
- }
30
- const routesWithStaticPathFlag = routes.map(
31
- (route) => [!/\*|\/:/.test(route[0]), ...route]
32
- ).sort(
33
- ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
34
- );
35
- const staticMap = /* @__PURE__ */ Object.create(null);
36
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
37
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
38
- if (pathErrorCheckOnly) {
39
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
40
- } else {
41
- j++;
42
- }
43
- let paramAssoc;
44
- try {
45
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
46
- } catch (e) {
47
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
48
- }
49
- if (pathErrorCheckOnly) {
50
- continue;
51
- }
52
- handlerData[j] = handlers.map(([h, paramCount]) => {
53
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
54
- paramCount -= 1;
55
- for (; paramCount >= 0; paramCount--) {
56
- const [key, value] = paramAssoc[paramCount];
57
- paramIndexMap[key] = value;
58
- }
59
- return [h, paramIndexMap];
60
- });
61
- }
62
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
63
- for (let i = 0, len = handlerData.length; i < len; i++) {
64
- for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
65
- const map = handlerData[i][j]?.[1];
66
- if (!map) {
67
- continue;
68
- }
69
- const keys = Object.keys(map);
70
- for (let k = 0, len3 = keys.length; k < len3; k++) {
71
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
72
- }
73
- }
74
- }
75
- const handlerMap = [];
76
- for (const i in indexReplacementMap) {
77
- handlerMap[i] = handlerData[indexReplacementMap[i]];
78
- }
79
- return [regexp, handlerMap, staticMap];
80
- }
81
23
  function findMiddleware(middleware, path) {
82
24
  if (!middleware) {
83
25
  return void 0;
@@ -93,9 +35,18 @@ var RegExpRouter = class {
93
35
  name = "RegExpRouter";
94
36
  #middleware;
95
37
  #routes;
38
+ #tries;
96
39
  constructor() {
97
40
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
98
41
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
42
+ this.#tries = { [METHOD_NAME_ALL]: new Trie() };
43
+ }
44
+ #insertPath(method, path) {
45
+ try {
46
+ this.#tries[method].insert(path, !/\*|\/:/.test(path));
47
+ } catch (e) {
48
+ throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
49
+ }
99
50
  }
100
51
  add(method, path, handler) {
101
52
  const middleware = this.#middleware;
@@ -104,11 +55,12 @@ var RegExpRouter = class {
104
55
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
105
56
  }
106
57
  if (!middleware[method]) {
107
- ;
58
+ this.#tries[method] = new Trie();
108
59
  [middleware, routes].forEach((handlerMap) => {
109
60
  handlerMap[method] = /* @__PURE__ */ Object.create(null);
110
61
  Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
111
62
  handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
63
+ this.#insertPath(method, p);
112
64
  });
113
65
  });
114
66
  }
@@ -118,13 +70,12 @@ var RegExpRouter = class {
118
70
  const paramCount = (path.match(/\/:/g) || []).length;
119
71
  if (/\*$/.test(path)) {
120
72
  const re = buildWildcardRegExp(path);
121
- if (method === METHOD_NAME_ALL) {
122
- Object.keys(middleware).forEach((m) => {
123
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
124
- });
125
- } else {
126
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
127
- }
73
+ Object.keys(middleware).forEach((m) => {
74
+ if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) {
75
+ this.#insertPath(m, path);
76
+ middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
77
+ }
78
+ });
128
79
  Object.keys(middleware).forEach((m) => {
129
80
  if (method === METHOD_NAME_ALL || method === m) {
130
81
  Object.keys(middleware[m]).forEach((p) => {
@@ -146,9 +97,12 @@ var RegExpRouter = class {
146
97
  const path2 = paths[i];
147
98
  Object.keys(routes).forEach((m) => {
148
99
  if (method === METHOD_NAME_ALL || method === m) {
149
- routes[m][path2] ||= [
150
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
151
- ];
100
+ if (!routes[m][path2]) {
101
+ this.#insertPath(m, path2);
102
+ routes[m][path2] = [
103
+ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
104
+ ];
105
+ }
152
106
  routes[m][path2].push([handler, paramCount - len + i + 1]);
153
107
  }
154
108
  });
@@ -160,29 +114,54 @@ var RegExpRouter = class {
160
114
  Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
161
115
  matchers[method] ||= this.#buildMatcher(method);
162
116
  });
163
- this.#middleware = this.#routes = void 0;
117
+ this.#middleware = this.#routes = this.#tries = void 0;
164
118
  clearWildcardRegExpCache();
165
119
  return matchers;
166
120
  }
167
121
  #buildMatcher(method) {
168
- const routes = [];
169
- let hasOwnRoute = method === METHOD_NAME_ALL;
170
- [this.#middleware, this.#routes].forEach((r) => {
171
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
172
- if (ownRoute.length !== 0) {
173
- hasOwnRoute ||= true;
174
- routes.push(...ownRoute);
175
- } else if (method !== METHOD_NAME_ALL) {
176
- routes.push(
177
- ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
178
- );
122
+ const middleware = this.#middleware[method];
123
+ const routes = this.#routes[method];
124
+ const trie = this.#tries[method];
125
+ const staticMap = /* @__PURE__ */ Object.create(null);
126
+ const handlerData = [];
127
+ [middleware, routes].forEach((r) => {
128
+ for (const path in r) {
129
+ const handlers = r[path];
130
+ const pathData = trie.paths[path];
131
+ if (!pathData) {
132
+ staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
133
+ continue;
134
+ }
135
+ const paramAssoc = pathData[1];
136
+ handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
137
+ const paramIndexMap = /* @__PURE__ */ Object.create(null);
138
+ paramCount -= 1;
139
+ for (; paramCount >= 0; paramCount--) {
140
+ const [key, value] = paramAssoc[paramCount];
141
+ paramIndexMap[key] = value;
142
+ }
143
+ return [h, paramIndexMap];
144
+ });
179
145
  }
180
146
  });
181
- if (!hasOwnRoute) {
182
- return null;
183
- } else {
184
- return buildMatcherFromPreprocessedRoutes(routes);
147
+ const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
148
+ for (let i = 0, len = handlerData.length; i < len; i++) {
149
+ for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
150
+ const map = handlerData[i][j]?.[1];
151
+ if (!map) {
152
+ continue;
153
+ }
154
+ const keys = Object.keys(map);
155
+ for (let k = 0, len3 = keys.length; k < len3; k++) {
156
+ map[keys[k]] = paramReplacementMap[map[keys[k]]];
157
+ }
158
+ }
159
+ }
160
+ const handlerMap = [];
161
+ for (const i in indexReplacementMap) {
162
+ handlerMap[i] = handlerData[indexReplacementMap[i]];
185
163
  }
164
+ return [regexp, handlerMap, staticMap];
186
165
  }
187
166
  };
188
167
  export {
@@ -3,12 +3,20 @@ import { Node } from "./node.js";
3
3
  var Trie = class {
4
4
  #context = { varIndex: 0 };
5
5
  #root = new Node();
6
- insert(path, index, pathErrorCheckOnly) {
6
+ #index = 0;
7
+ // dynamic path -> [handler index, param assoc]; static paths are not registered
8
+ paths = /* @__PURE__ */ Object.create(null);
9
+ insert(path, isStatic) {
10
+ if (isStatic) {
11
+ this.#root.insert(path.split(""), 0, [], this.#context, true);
12
+ return;
13
+ }
7
14
  const paramAssoc = [];
8
15
  const groups = [];
16
+ let markedPath = path;
9
17
  for (let i = 0; ; ) {
10
18
  let replaced = false;
11
- path = path.replace(/\{[^}]+\}/g, (m) => {
19
+ markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
12
20
  const mark = `@\\${i}`;
13
21
  groups[i] = [mark, m];
14
22
  i++;
@@ -19,7 +27,7 @@ var Trie = class {
19
27
  break;
20
28
  }
21
29
  }
22
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
30
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
23
31
  for (let i = groups.length - 1; i >= 0; i--) {
24
32
  const [mark] = groups[i];
25
33
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -29,8 +37,8 @@ var Trie = class {
29
37
  }
30
38
  }
31
39
  }
32
- this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
33
- return paramAssoc;
40
+ this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
41
+ this.paths[path] = [this.#index++, paramAssoc];
34
42
  }
35
43
  buildRegExp() {
36
44
  let regexp = this.#root.buildRegExpStr();
@@ -145,7 +145,7 @@ var Node = class _Node {
145
145
  }
146
146
  if (hasChildren(child.#children)) {
147
147
  child.#params = params;
148
- const componentCount = m[0].match(/\//)?.length ?? 0;
148
+ const componentCount = m[0].match(/\//g)?.length ?? 0;
149
149
  const targetCurNodes = curNodesQueue[componentCount] ||= [];
150
150
  targetCurNodes.push(child);
151
151
  }