hono 4.12.9 → 4.12.18

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 (57) hide show
  1. package/dist/adapter/aws-lambda/handler.js +9 -2
  2. package/dist/cjs/adapter/aws-lambda/handler.js +9 -2
  3. package/dist/cjs/helper/css/common.js +56 -7
  4. package/dist/cjs/helper/css/index.js +11 -4
  5. package/dist/cjs/helper/ssg/ssg.js +12 -8
  6. package/dist/cjs/helper/ssg/utils.js +9 -0
  7. package/dist/cjs/jsx/base.js +7 -1
  8. package/dist/cjs/jsx/dom/css.js +11 -4
  9. package/dist/cjs/jsx/dom/render.js +39 -17
  10. package/dist/cjs/jsx/jsx-runtime.js +3 -0
  11. package/dist/cjs/jsx/utils.js +155 -5
  12. package/dist/cjs/middleware/body-limit/index.js +21 -30
  13. package/dist/cjs/middleware/cache/index.js +11 -3
  14. package/dist/cjs/middleware/compress/index.js +4 -0
  15. package/dist/cjs/middleware/cors/index.js +2 -5
  16. package/dist/cjs/middleware/ip-restriction/index.js +31 -9
  17. package/dist/cjs/middleware/method-override/index.js +1 -1
  18. package/dist/cjs/middleware/serve-static/index.js +1 -1
  19. package/dist/cjs/middleware/trailing-slash/index.js +2 -2
  20. package/dist/cjs/utils/cookie.js +26 -5
  21. package/dist/cjs/utils/ipaddr.js +10 -4
  22. package/dist/cjs/utils/jwt/jws.js +1 -1
  23. package/dist/cjs/utils/jwt/jwt.js +12 -6
  24. package/dist/helper/css/common.js +56 -7
  25. package/dist/helper/css/index.js +11 -4
  26. package/dist/helper/ssg/ssg.js +19 -9
  27. package/dist/helper/ssg/utils.js +8 -0
  28. package/dist/jsx/base.js +13 -2
  29. package/dist/jsx/dom/css.js +11 -4
  30. package/dist/jsx/dom/render.js +39 -17
  31. package/dist/jsx/jsx-runtime.js +4 -1
  32. package/dist/jsx/utils.js +153 -5
  33. package/dist/middleware/body-limit/index.js +21 -30
  34. package/dist/middleware/cache/index.js +11 -3
  35. package/dist/middleware/compress/index.js +4 -0
  36. package/dist/middleware/cors/index.js +2 -5
  37. package/dist/middleware/ip-restriction/index.js +34 -10
  38. package/dist/middleware/method-override/index.js +1 -1
  39. package/dist/middleware/serve-static/index.js +1 -1
  40. package/dist/middleware/trailing-slash/index.js +2 -2
  41. package/dist/tsconfig.build.tsbuildinfo +1 -1
  42. package/dist/types/helper/css/common.d.ts +20 -3
  43. package/dist/types/helper/css/index.d.ts +9 -2
  44. package/dist/types/helper/ssg/utils.d.ts +1 -0
  45. package/dist/types/jsx/dom/css.d.ts +8 -2
  46. package/dist/types/jsx/utils.d.ts +3 -1
  47. package/dist/types/middleware/cache/index.d.ts +2 -0
  48. package/dist/types/middleware/cors/index.d.ts +1 -1
  49. package/dist/types/middleware/ip-restriction/index.d.ts +38 -2
  50. package/dist/types/middleware/trailing-slash/index.d.ts +16 -0
  51. package/dist/types/types.d.ts +116 -116
  52. package/dist/types/utils/ipaddr.d.ts +12 -0
  53. package/dist/utils/cookie.js +26 -5
  54. package/dist/utils/ipaddr.js +7 -3
  55. package/dist/utils/jwt/jws.js +1 -1
  56. package/dist/utils/jwt/jwt.js +12 -6
  57. package/package.json +1 -1
@@ -22,8 +22,7 @@ __export(cache_exports, {
22
22
  module.exports = __toCommonJS(cache_exports);
23
23
  const defaultCacheableStatusCodes = [200];
24
24
  const shouldSkipCache = (res) => {
25
- const vary = res.headers.get("Vary");
26
- if (vary && vary.includes("*")) {
25
+ if (res.headers.has("Vary")) {
27
26
  return true;
28
27
  }
29
28
  const cacheControl = res.headers.get("Cache-Control");
@@ -37,7 +36,12 @@ const shouldSkipCache = (res) => {
37
36
  };
38
37
  const cache = (options) => {
39
38
  if (!globalThis.caches) {
40
- console.log("Cache Middleware is not enabled because caches is not defined.");
39
+ if (options.onCacheNotAvailable === false) {
40
+ } else if (options.onCacheNotAvailable) {
41
+ options.onCacheNotAvailable();
42
+ } else {
43
+ console.log("Cache Middleware is not enabled because caches is not defined.");
44
+ }
41
45
  return async (_c, next) => await next();
42
46
  }
43
47
  if (options.wait === void 0) {
@@ -79,6 +83,10 @@ const cache = (options) => {
79
83
  }
80
84
  };
81
85
  return async function cache2(c, next) {
86
+ if (c.req.method !== "GET" || c.req.raw.headers.has("Authorization")) {
87
+ await next();
88
+ return;
89
+ }
82
90
  let key = c.req.url;
83
91
  if (options.keyGenerator) {
84
92
  key = await options.keyGenerator(c);
@@ -45,6 +45,10 @@ const compress = (options) => {
45
45
  ctx.res = new Response(ctx.res.body.pipeThrough(stream), ctx.res);
46
46
  ctx.res.headers.delete("Content-Length");
47
47
  ctx.res.headers.set("Content-Encoding", encoding);
48
+ const etag = ctx.res.headers.get("ETag");
49
+ if (etag && !etag.startsWith("W/")) {
50
+ ctx.res.headers.set("ETag", `W/${etag}`);
51
+ }
48
52
  };
49
53
  };
50
54
  const shouldCompress = (res) => {
@@ -21,14 +21,11 @@ __export(cors_exports, {
21
21
  });
22
22
  module.exports = __toCommonJS(cors_exports);
23
23
  const cors = (options) => {
24
- const defaults = {
24
+ const opts = {
25
25
  origin: "*",
26
26
  allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
27
27
  allowHeaders: [],
28
- exposeHeaders: []
29
- };
30
- const opts = {
31
- ...defaults,
28
+ exposeHeaders: [],
32
29
  ...options
33
30
  };
34
31
  const findAllowOrigin = ((optsOrigin) => {
@@ -40,12 +40,17 @@ const buildMatcher = (rules) => {
40
40
  if (type2 === void 0) {
41
41
  throw new TypeError(`Invalid rule: ${rule}`);
42
42
  }
43
- const isIPv4 = type2 === "IPv4";
44
- const prefix = parseInt(separatedRule[1]);
43
+ let isIPv4 = type2 === "IPv4";
44
+ let prefix = parseInt(separatedRule[1]);
45
45
  if (isIPv4 ? prefix === 32 : prefix === 128) {
46
46
  rule = addrStr;
47
47
  } else {
48
- const addr = (isIPv4 ? import_ipaddr.convertIPv4ToBinary : import_ipaddr.convertIPv6ToBinary)(addrStr);
48
+ let addr = (isIPv4 ? import_ipaddr.convertIPv4ToBinary : import_ipaddr.convertIPv6ToBinary)(addrStr);
49
+ if (type2 === "IPv6" && (0, import_ipaddr.isIPv4MappedIPv6)(addr) && prefix >= 96) {
50
+ isIPv4 = true;
51
+ addr = (0, import_ipaddr.convertIPv4MappedIPv6ToIPv4)(addr);
52
+ prefix -= 96;
53
+ }
49
54
  const mask = (1n << BigInt(prefix)) - 1n << BigInt((isIPv4 ? 32 : 128) - prefix);
50
55
  cidrRules.push([isIPv4, addr & mask, mask]);
51
56
  continue;
@@ -55,21 +60,38 @@ const buildMatcher = (rules) => {
55
60
  if (type === void 0) {
56
61
  throw new TypeError(`Invalid rule: ${rule}`);
57
62
  }
58
- staticRules.add(
59
- type === "IPv4" ? rule : (0, import_ipaddr.convertIPv6BinaryToString)((0, import_ipaddr.convertIPv6ToBinary)(rule))
60
- // normalize IPv6 address (e.g. 0000:0000:0000:0000:0000:0000:0000:0001 => ::1)
61
- );
63
+ if (type === "IPv4") {
64
+ staticRules.add(rule);
65
+ staticRules.add(`::ffff:${rule}`);
66
+ } else {
67
+ const ipv6binary = (0, import_ipaddr.convertIPv6ToBinary)(rule);
68
+ const ipv6Addr = (0, import_ipaddr.convertIPv6BinaryToString)(ipv6binary);
69
+ staticRules.add(ipv6Addr);
70
+ if ((0, import_ipaddr.isIPv4MappedIPv6)(ipv6binary)) {
71
+ staticRules.add(ipv6Addr.substring(7));
72
+ }
73
+ }
62
74
  }
63
75
  }
64
76
  return (remote) => {
65
77
  if (staticRules.has(remote.addr)) {
66
78
  return true;
67
79
  }
80
+ const remoteAddr = remote.binaryAddr ||= (remote.isIPv4 ? import_ipaddr.convertIPv4ToBinary : import_ipaddr.convertIPv6ToBinary)(remote.addr);
81
+ const remoteIPv4Addr = remote.isIPv4 || (0, import_ipaddr.isIPv4MappedIPv6)(remoteAddr) ? remote.isIPv4 ? remoteAddr : (0, import_ipaddr.convertIPv4MappedIPv6ToIPv4)(remoteAddr) : void 0;
68
82
  for (const [isIPv4, addr, mask] of cidrRules) {
69
- if (isIPv4 !== remote.isIPv4) {
83
+ if (isIPv4) {
84
+ if (remoteIPv4Addr === void 0) {
85
+ continue;
86
+ }
87
+ if ((remoteIPv4Addr & mask) === addr) {
88
+ return true;
89
+ }
90
+ continue;
91
+ }
92
+ if (remote.isIPv4) {
70
93
  continue;
71
94
  }
72
- const remoteAddr = remote.binaryAddr ||= (isIPv4 ? import_ipaddr.convertIPv4ToBinary : import_ipaddr.convertIPv6ToBinary)(remote.addr);
73
95
  if ((remoteAddr & mask) === addr) {
74
96
  return true;
75
97
  }
@@ -49,7 +49,7 @@ const methodOverride = (options) => async function methodOverride2(c, next) {
49
49
  return app.fetch(request, c.env, getExecutionCtx(c));
50
50
  }
51
51
  }
52
- if (contentType === "application/x-www-form-urlencoded") {
52
+ if (contentType?.startsWith("application/x-www-form-urlencoded")) {
53
53
  const params = await (0, import_body.parseBody)(clonedRequest);
54
54
  const method = params[methodFormName];
55
55
  if (method) {
@@ -45,7 +45,7 @@ const serveStatic = (options) => {
45
45
  } else {
46
46
  try {
47
47
  filename = (0, import_url.tryDecodeURI)(c.req.path);
48
- if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) {
48
+ if (/(?:^|[\/\\])\.{1,2}(?:$|[\/\\])|[\/\\]{2,}/.test(filename)) {
49
49
  throw new Error();
50
50
  }
51
51
  } catch {
@@ -41,14 +41,14 @@ const trimTrailingSlash = (options) => {
41
41
  const appendTrailingSlash = (options) => {
42
42
  return async function appendTrailingSlash2(c, next) {
43
43
  if (options?.alwaysRedirect) {
44
- if ((c.req.method === "GET" || c.req.method === "HEAD") && c.req.path.at(-1) !== "/") {
44
+ if ((c.req.method === "GET" || c.req.method === "HEAD") && c.req.path.at(-1) !== "/" && !options.skip?.(c.req.path)) {
45
45
  const url = new URL(c.req.url);
46
46
  url.pathname += "/";
47
47
  return c.redirect(url.toString(), 301);
48
48
  }
49
49
  }
50
50
  await next();
51
- if (!options?.alwaysRedirect && c.res.status === 404 && (c.req.method === "GET" || c.req.method === "HEAD") && c.req.path.at(-1) !== "/") {
51
+ if (!options?.alwaysRedirect && c.res.status === 404 && (c.req.method === "GET" || c.req.method === "HEAD") && c.req.path.at(-1) !== "/" && !options?.skip?.(c.req.path)) {
52
52
  const url = new URL(c.req.url);
53
53
  url.pathname += "/";
54
54
  c.res = c.redirect(url.toString(), 301);
@@ -48,23 +48,41 @@ const verifySignature = async (base64Signature, value, secret) => {
48
48
  };
49
49
  const validCookieNameRegEx = /^[\w!#$%&'*.^`|~+-]+$/;
50
50
  const validCookieValueRegEx = /^[ !#-:<-[\]-~]*$/;
51
+ const trimCookieWhitespace = (value) => {
52
+ let start = 0;
53
+ let end = value.length;
54
+ while (start < end) {
55
+ const charCode = value.charCodeAt(start);
56
+ if (charCode !== 32 && charCode !== 9) {
57
+ break;
58
+ }
59
+ start++;
60
+ }
61
+ while (end > start) {
62
+ const charCode = value.charCodeAt(end - 1);
63
+ if (charCode !== 32 && charCode !== 9) {
64
+ break;
65
+ }
66
+ end--;
67
+ }
68
+ return start === 0 && end === value.length ? value : value.slice(start, end);
69
+ };
51
70
  const parse = (cookie, name) => {
52
71
  if (name && cookie.indexOf(name) === -1) {
53
72
  return {};
54
73
  }
55
- const pairs = cookie.trim().split(";");
74
+ const pairs = cookie.split(";");
56
75
  const parsedCookie = {};
57
- for (let pairStr of pairs) {
58
- pairStr = pairStr.trim();
76
+ for (const pairStr of pairs) {
59
77
  const valueStartPos = pairStr.indexOf("=");
60
78
  if (valueStartPos === -1) {
61
79
  continue;
62
80
  }
63
- const cookieName = pairStr.substring(0, valueStartPos).trim();
81
+ const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos));
64
82
  if (name && name !== cookieName || !validCookieNameRegEx.test(cookieName)) {
65
83
  continue;
66
84
  }
67
- let cookieValue = pairStr.substring(valueStartPos + 1).trim();
85
+ let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1));
68
86
  if (cookieValue.startsWith('"') && cookieValue.endsWith('"')) {
69
87
  cookieValue = cookieValue.slice(1, -1);
70
88
  }
@@ -96,6 +114,9 @@ const parseSigned = async (cookie, secret, name) => {
96
114
  return parsedCookie;
97
115
  };
98
116
  const _serialize = (name, value, opt = {}) => {
117
+ if (!validCookieNameRegEx.test(name)) {
118
+ throw new Error("Invalid cookie name");
119
+ }
99
120
  let cookie = `${name}=${value}`;
100
121
  if (name.startsWith("__Secure-") && !opt.secure) {
101
122
  throw new Error("__Secure- Cookie must have Secure attributes");
@@ -18,11 +18,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var ipaddr_exports = {};
19
19
  __export(ipaddr_exports, {
20
20
  convertIPv4BinaryToString: () => convertIPv4BinaryToString,
21
+ convertIPv4MappedIPv6ToIPv4: () => convertIPv4MappedIPv6ToIPv4,
21
22
  convertIPv4ToBinary: () => convertIPv4ToBinary,
22
23
  convertIPv6BinaryToString: () => convertIPv6BinaryToString,
23
24
  convertIPv6ToBinary: () => convertIPv6ToBinary,
24
25
  distinctRemoteAddr: () => distinctRemoteAddr,
25
- expandIPv6: () => expandIPv6
26
+ expandIPv6: () => expandIPv6,
27
+ isIPv4MappedIPv6: () => isIPv4MappedIPv6
26
28
  });
27
29
  module.exports = __toCommonJS(ipaddr_exports);
28
30
  const expandIPv6 = (ipV6) => {
@@ -81,9 +83,11 @@ const convertIPv4BinaryToString = (ipV4) => {
81
83
  }
82
84
  return sections.join(".");
83
85
  };
86
+ const isIPv4MappedIPv6 = (ipv6binary) => ipv6binary >> 32n === 0xffffn;
87
+ const convertIPv4MappedIPv6ToIPv4 = (ipv6binary) => ipv6binary & 0xffffffffn;
84
88
  const convertIPv6BinaryToString = (ipV6) => {
85
- if (ipV6 >> 32n === 0xffffn) {
86
- return `::ffff:${convertIPv4BinaryToString(ipV6 & 0xffffffffn)}`;
89
+ if (isIPv4MappedIPv6(ipV6)) {
90
+ return `::ffff:${convertIPv4BinaryToString(convertIPv4MappedIPv6ToIPv4(ipV6))}`;
87
91
  }
88
92
  const sections = [];
89
93
  for (let i = 0; i < 8; i++) {
@@ -121,9 +125,11 @@ const convertIPv6BinaryToString = (ipV6) => {
121
125
  // Annotate the CommonJS export names for ESM import in node:
122
126
  0 && (module.exports = {
123
127
  convertIPv4BinaryToString,
128
+ convertIPv4MappedIPv6ToIPv4,
124
129
  convertIPv4ToBinary,
125
130
  convertIPv6BinaryToString,
126
131
  convertIPv6ToBinary,
127
132
  distinctRemoteAddr,
128
- expandIPv6
133
+ expandIPv6,
134
+ isIPv4MappedIPv6
129
135
  });
@@ -36,7 +36,7 @@ async function verifying(publicKey, alg, signature, data) {
36
36
  return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);
37
37
  }
38
38
  function pemToBinary(pem) {
39
- return (0, import_encode.decodeBase64)(pem.replace(/-+(BEGIN|END).*/g, "").replace(/\s/g, ""));
39
+ return (0, import_encode.decodeBase64)(pem.replace(/-+(BEGIN|END).*?-+/g, "").replace(/\s/g, ""));
40
40
  }
41
41
  async function importPrivateKey(key, alg) {
42
42
  if (!crypto.subtle || !crypto.subtle.importKey) {
@@ -81,14 +81,20 @@ const verify = async (token, publicKey, algOrOptions) => {
81
81
  throw new import_types.JwtAlgorithmMismatch(alg, header.alg);
82
82
  }
83
83
  const now = Math.floor(Date.now() / 1e3);
84
- if (nbf && payload.nbf && payload.nbf > now) {
85
- throw new import_types.JwtTokenNotBefore(token);
84
+ if (nbf && payload.nbf !== void 0) {
85
+ if (typeof payload.nbf !== "number" || !Number.isFinite(payload.nbf) || payload.nbf > now) {
86
+ throw new import_types.JwtTokenNotBefore(token);
87
+ }
86
88
  }
87
- if (exp && payload.exp && payload.exp <= now) {
88
- throw new import_types.JwtTokenExpired(token);
89
+ if (exp && payload.exp !== void 0) {
90
+ if (typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || payload.exp <= now) {
91
+ throw new import_types.JwtTokenExpired(token);
92
+ }
89
93
  }
90
- if (iat && payload.iat && now < payload.iat) {
91
- throw new import_types.JwtTokenIssuedAt(now, payload.iat);
94
+ if (iat && payload.iat !== void 0) {
95
+ if (typeof payload.iat !== "number" || !Number.isFinite(payload.iat) || now < payload.iat) {
96
+ throw new import_types.JwtTokenIssuedAt(now, payload.iat);
97
+ }
92
98
  }
93
99
  if (iss) {
94
100
  if (!payload.iss) {
@@ -21,6 +21,23 @@ var toHash = (str) => {
21
21
  }
22
22
  return "css-" + out;
23
23
  };
24
+ var normalizeLabel = (label) => {
25
+ return label.trim().replace(/\s+/g, "-");
26
+ };
27
+ var isValidClassName = (name) => /^-?[_a-zA-Z][_a-zA-Z0-9-]*$/.test(name);
28
+ var RESERVED_KEYFRAME_NAMES = /* @__PURE__ */ new Set([
29
+ "default",
30
+ "inherit",
31
+ "initial",
32
+ "none",
33
+ "revert",
34
+ "revert-layer",
35
+ "unset"
36
+ ]);
37
+ var isValidKeyframeName = (name) => isValidClassName(name) && !RESERVED_KEYFRAME_NAMES.has(name.toLowerCase());
38
+ var defaultOnInvalidSlug = (slug) => {
39
+ console.warn(`Invalid slug: ${slug}`);
40
+ };
24
41
  var cssStringReStr = [
25
42
  '"(?:(?:\\\\[\\s\\S]|[^"\\\\])*)"',
26
43
  // double quoted string
@@ -107,13 +124,26 @@ var buildStyleString = (strings, values) => {
107
124
  }
108
125
  return [label, minify(styleString), selectors, externalClassNames];
109
126
  };
110
- var cssCommon = (strings, values) => {
127
+ var cssCommon = (strings, values, classNameSlug, onInvalidSlug) => {
111
128
  let [label, thisStyleString, selectors, externalClassNames] = buildStyleString(strings, values);
112
129
  const isPseudoGlobal = isPseudoGlobalSelectorRe.exec(thisStyleString);
113
130
  if (isPseudoGlobal) {
114
131
  thisStyleString = isPseudoGlobal[1];
115
132
  }
116
- const selector = (isPseudoGlobal ? PSEUDO_GLOBAL_SELECTOR : "") + toHash(label + thisStyleString);
133
+ const hash = toHash(label + thisStyleString);
134
+ let customSlug;
135
+ if (classNameSlug) {
136
+ const slug = classNameSlug(hash, normalizeLabel(label), thisStyleString);
137
+ if (slug) {
138
+ if (isValidClassName(slug)) {
139
+ customSlug = slug;
140
+ } else {
141
+ ;
142
+ (onInvalidSlug || defaultOnInvalidSlug)(slug);
143
+ }
144
+ }
145
+ }
146
+ const selector = (isPseudoGlobal ? PSEUDO_GLOBAL_SELECTOR : "") + (customSlug || hash);
117
147
  const className = (isPseudoGlobal ? selectors.map((s) => s[CLASS_NAME]) : [selector, ...externalClassNames]).join(" ");
118
148
  return {
119
149
  [SELECTOR]: selector,
@@ -138,24 +168,43 @@ var cxCommon = (args) => {
138
168
  }
139
169
  return args;
140
170
  };
141
- var keyframesCommon = (strings, ...values) => {
171
+ var keyframesCommon = (strings, values, classNameSlug, onInvalidSlug) => {
142
172
  const [label, styleString] = buildStyleString(strings, values);
173
+ const hash = toHash(label + styleString);
174
+ let customSlug;
175
+ if (classNameSlug) {
176
+ const slug = classNameSlug(hash, normalizeLabel(label), styleString);
177
+ if (slug) {
178
+ if (isValidKeyframeName(slug)) {
179
+ customSlug = slug;
180
+ } else {
181
+ ;
182
+ (onInvalidSlug || defaultOnInvalidSlug)(slug);
183
+ }
184
+ }
185
+ }
143
186
  return {
144
187
  [SELECTOR]: "",
145
- [CLASS_NAME]: `@keyframes ${toHash(label + styleString)}`,
188
+ [CLASS_NAME]: `@keyframes ${customSlug || hash}`,
146
189
  [STYLE_STRING]: styleString,
147
190
  [SELECTORS]: [],
148
191
  [EXTERNAL_CLASS_NAMES]: []
149
192
  };
150
193
  };
151
194
  var viewTransitionNameIndex = 0;
152
- var viewTransitionCommon = ((strings, values) => {
195
+ var viewTransitionCommon = ((strings, values, classNameSlug, onInvalidSlug) => {
153
196
  if (!strings) {
154
197
  strings = [`/* h-v-t ${viewTransitionNameIndex++} */`];
155
198
  }
156
- const content = Array.isArray(strings) ? cssCommon(strings, values) : strings;
199
+ const content = Array.isArray(strings) ? cssCommon(strings, values, classNameSlug, onInvalidSlug) : strings;
157
200
  const transitionName = content[CLASS_NAME];
158
- const res = cssCommon(["view-transition-name:", ""], [transitionName]);
201
+ const res = cssCommon(
202
+ ["view-transition-name:", ""],
203
+ // eslint-disable-line @typescript-eslint/no-explicit-any
204
+ [transitionName],
205
+ classNameSlug,
206
+ onInvalidSlug
207
+ );
159
208
  content[CLASS_NAME] = PSEUDO_GLOBAL_SELECTOR + content[CLASS_NAME];
160
209
  content[STYLE_STRING] = content[STYLE_STRING].replace(
161
210
  /(?<=::view-transition(?:[a-z-]*)\()(?=\))/g,
@@ -15,7 +15,11 @@ import {
15
15
  viewTransitionCommon
16
16
  } from "./common.js";
17
17
  import { rawCssString } from "./common.js";
18
- var createCssContext = ({ id }) => {
18
+ var createCssContext = ({
19
+ id,
20
+ classNameSlug,
21
+ onInvalidSlug
22
+ }) => {
19
23
  const [cssJsxDomObject, StyleRenderToDom] = createCssJsxDomObjects({ id });
20
24
  const contextMap = /* @__PURE__ */ new WeakMap();
21
25
  const nonceMap = /* @__PURE__ */ new WeakMap();
@@ -78,15 +82,18 @@ var createCssContext = ({ id }) => {
78
82
  return promise;
79
83
  };
80
84
  const css2 = (strings, ...values) => {
81
- return newCssClassNameObject(cssCommon(strings, values));
85
+ return newCssClassNameObject(cssCommon(strings, values, classNameSlug, onInvalidSlug));
82
86
  };
83
87
  const cx2 = (...args) => {
84
88
  args = cxCommon(args);
85
89
  return css2(Array(args.length).fill(""), ...args);
86
90
  };
87
- const keyframes2 = keyframesCommon;
91
+ const keyframes2 = (strings, ...values) => keyframesCommon(strings, values, classNameSlug, onInvalidSlug);
88
92
  const viewTransition2 = ((strings, ...values) => {
89
- return newCssClassNameObject(viewTransitionCommon(strings, values));
93
+ return newCssClassNameObject(
94
+ viewTransitionCommon(strings, values, classNameSlug, onInvalidSlug)
95
+ // eslint-disable-line @typescript-eslint/no-explicit-any
96
+ );
90
97
  });
91
98
  const Style2 = ({ children, nonce } = {}) => raw(
92
99
  `<style id="${id}"${nonce ? ` nonce="${nonce}"` : ""}>${children ? children[STYLE_STRING] : ""}</style>`,
@@ -4,22 +4,30 @@ import { createPool } from "../../utils/concurrent.js";
4
4
  import { getExtension } from "../../utils/mime.js";
5
5
  import { SSG_CONTEXT, X_HONO_DISABLE_SSG_HEADER_KEY } from "./middleware.js";
6
6
  import { defaultPlugin } from "./plugins.js";
7
- import { dirname, filterStaticGenerateRoutes, isDynamicRoute, joinPaths } from "./utils.js";
7
+ import {
8
+ dirname,
9
+ ensureWithinOutDir,
10
+ filterStaticGenerateRoutes,
11
+ isDynamicRoute,
12
+ joinPaths
13
+ } from "./utils.js";
8
14
  var DEFAULT_CONCURRENCY = 2;
9
15
  var DEFAULT_CONTENT_TYPE = "text/plain";
10
16
  var DEFAULT_OUTPUT_DIR = "./static";
11
17
  var generateFilePath = (routePath, outDir, mimeType, extensionMap) => {
12
18
  const extension = determineExtension(mimeType, extensionMap);
19
+ let filePath;
13
20
  if (routePath.endsWith(`.${extension}`)) {
14
- return joinPaths(outDir, routePath);
21
+ filePath = joinPaths(outDir, routePath);
22
+ } else if (routePath === "/") {
23
+ filePath = joinPaths(outDir, `index.${extension}`);
24
+ } else if (routePath.endsWith("/")) {
25
+ filePath = joinPaths(outDir, routePath, `index.${extension}`);
26
+ } else {
27
+ filePath = joinPaths(outDir, `${routePath}.${extension}`);
15
28
  }
16
- if (routePath === "/") {
17
- return joinPaths(outDir, `index.${extension}`);
18
- }
19
- if (routePath.endsWith("/")) {
20
- return joinPaths(outDir, routePath, `index.${extension}`);
21
- }
22
- return joinPaths(outDir, `${routePath}.${extension}`);
29
+ ensureWithinOutDir(outDir, filePath);
30
+ return filePath;
23
31
  };
24
32
  var parseResponseContent = async (response) => {
25
33
  const contentType = response.headers.get("Content-Type");
@@ -39,6 +47,8 @@ var defaultExtensionMap = {
39
47
  "text/html": "html",
40
48
  "text/xml": "xml",
41
49
  "application/xml": "xml",
50
+ "application/atom+xml": "xml",
51
+ "application/rss+xml": "xml",
42
52
  "application/yaml": "yaml"
43
53
  };
44
54
  var determineExtension = (mimeType, userExtensionMap) => {
@@ -51,8 +51,16 @@ var filterStaticGenerateRoutes = (hono) => {
51
51
  var isDynamicRoute = (path) => {
52
52
  return path.split("/").some((segment) => segment.startsWith(":") || segment.includes("*"));
53
53
  };
54
+ var ensureWithinOutDir = (outDir, filePath) => {
55
+ const normalizedOutDir = joinPaths("/", outDir);
56
+ const normalizedFilePath = joinPaths("/", filePath);
57
+ if (normalizedFilePath !== normalizedOutDir && !normalizedFilePath.startsWith(`${normalizedOutDir}/`)) {
58
+ throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`);
59
+ }
60
+ };
54
61
  export {
55
62
  dirname,
63
+ ensureWithinOutDir,
56
64
  filterStaticGenerateRoutes,
57
65
  isDynamicRoute,
58
66
  joinPaths
package/dist/jsx/base.js CHANGED
@@ -5,7 +5,12 @@ import { DOM_RENDERER, DOM_MEMO } from "./constants.js";
5
5
  import { createContext, globalContexts, useContext } from "./context.js";
6
6
  import { domRenderers } from "./intrinsic-element/common.js";
7
7
  import * as intrinsicElementTags from "./intrinsic-element/components.js";
8
- import { normalizeIntrinsicElementKey, styleObjectForEach } from "./utils.js";
8
+ import {
9
+ isValidAttributeName,
10
+ isValidTagName,
11
+ normalizeIntrinsicElementKey,
12
+ styleObjectForEach
13
+ } from "./utils.js";
9
14
  var nameSpaceContext = void 0;
10
15
  var getNameSpaceContext = () => nameSpaceContext;
11
16
  var toSVGAttributeName = (key) => /[A-Z]/.test(key) && // Presentation attributes are findable in style object. "clip-path", "font-size", "stroke-width", etc.
@@ -85,6 +90,9 @@ var JSXNode = class {
85
90
  isEscaped = true;
86
91
  localContexts;
87
92
  constructor(tag, props, children) {
93
+ if (typeof tag !== "function" && !isValidTagName(tag)) {
94
+ throw new Error(`Invalid JSX tag name: ${tag}`);
95
+ }
88
96
  this.tag = tag;
89
97
  this.props = props;
90
98
  this.children = children;
@@ -116,9 +124,12 @@ var JSXNode = class {
116
124
  const props = this.props;
117
125
  let { children } = this;
118
126
  buffer[0] += `<${tag}`;
119
- const normalizeKey = nameSpaceContext && useContext(nameSpaceContext) === "svg" ? (key) => toSVGAttributeName(normalizeIntrinsicElementKey(key)) : (key) => normalizeIntrinsicElementKey(key);
127
+ const normalizeKey = tag === "svg" || nameSpaceContext && useContext(nameSpaceContext) === "svg" ? (key) => toSVGAttributeName(normalizeIntrinsicElementKey(key)) : (key) => normalizeIntrinsicElementKey(key);
120
128
  for (let [key, v] of Object.entries(props)) {
121
129
  key = normalizeKey(key);
130
+ if (!isValidAttributeName(key)) {
131
+ continue;
132
+ }
122
133
  if (key === "children") {
123
134
  } else if (key === "style" && typeof v === "object") {
124
135
  let styleStr = "";
@@ -100,22 +100,29 @@ var createCssJsxDomObjects = ({ id }) => {
100
100
  });
101
101
  return [cssObject, Style2];
102
102
  };
103
- var createCssContext = ({ id }) => {
103
+ var createCssContext = ({
104
+ id,
105
+ classNameSlug,
106
+ onInvalidSlug
107
+ }) => {
104
108
  const [cssObject, Style2] = createCssJsxDomObjects({ id });
105
109
  const newCssClassNameObject = (cssClassName) => {
106
110
  cssClassName.toString = cssObject.toString;
107
111
  return cssClassName;
108
112
  };
109
113
  const css2 = (strings, ...values) => {
110
- return newCssClassNameObject(cssCommon(strings, values));
114
+ return newCssClassNameObject(cssCommon(strings, values, classNameSlug, onInvalidSlug));
111
115
  };
112
116
  const cx2 = (...args) => {
113
117
  args = cxCommon(args);
114
118
  return css2(Array(args.length).fill(""), ...args);
115
119
  };
116
- const keyframes2 = keyframesCommon;
120
+ const keyframes2 = (strings, ...values) => keyframesCommon(strings, values, classNameSlug, onInvalidSlug);
117
121
  const viewTransition2 = ((strings, ...values) => {
118
- return newCssClassNameObject(viewTransitionCommon(strings, values));
122
+ return newCssClassNameObject(
123
+ viewTransitionCommon(strings, values, classNameSlug, onInvalidSlug)
124
+ // eslint-disable-line @typescript-eslint/no-explicit-any
125
+ );
119
126
  });
120
127
  return {
121
128
  css: css2,