hono 4.12.10 → 4.12.12

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.
@@ -57,6 +57,23 @@ const toHash = (str) => {
57
57
  }
58
58
  return "css-" + out;
59
59
  };
60
+ const normalizeLabel = (label) => {
61
+ return label.trim().replace(/\s+/g, "-");
62
+ };
63
+ const isValidClassName = (name) => /^-?[_a-zA-Z][_a-zA-Z0-9-]*$/.test(name);
64
+ const RESERVED_KEYFRAME_NAMES = /* @__PURE__ */ new Set([
65
+ "default",
66
+ "inherit",
67
+ "initial",
68
+ "none",
69
+ "revert",
70
+ "revert-layer",
71
+ "unset"
72
+ ]);
73
+ const isValidKeyframeName = (name) => isValidClassName(name) && !RESERVED_KEYFRAME_NAMES.has(name.toLowerCase());
74
+ const defaultOnInvalidSlug = (slug) => {
75
+ console.warn(`Invalid slug: ${slug}`);
76
+ };
60
77
  const cssStringReStr = [
61
78
  '"(?:(?:\\\\[\\s\\S]|[^"\\\\])*)"',
62
79
  // double quoted string
@@ -143,13 +160,26 @@ const buildStyleString = (strings, values) => {
143
160
  }
144
161
  return [label, minify(styleString), selectors, externalClassNames];
145
162
  };
146
- const cssCommon = (strings, values) => {
163
+ const cssCommon = (strings, values, classNameSlug, onInvalidSlug) => {
147
164
  let [label, thisStyleString, selectors, externalClassNames] = buildStyleString(strings, values);
148
165
  const isPseudoGlobal = isPseudoGlobalSelectorRe.exec(thisStyleString);
149
166
  if (isPseudoGlobal) {
150
167
  thisStyleString = isPseudoGlobal[1];
151
168
  }
152
- const selector = (isPseudoGlobal ? PSEUDO_GLOBAL_SELECTOR : "") + toHash(label + thisStyleString);
169
+ const hash = toHash(label + thisStyleString);
170
+ let customSlug;
171
+ if (classNameSlug) {
172
+ const slug = classNameSlug(hash, normalizeLabel(label), thisStyleString);
173
+ if (slug) {
174
+ if (isValidClassName(slug)) {
175
+ customSlug = slug;
176
+ } else {
177
+ ;
178
+ (onInvalidSlug || defaultOnInvalidSlug)(slug);
179
+ }
180
+ }
181
+ }
182
+ const selector = (isPseudoGlobal ? PSEUDO_GLOBAL_SELECTOR : "") + (customSlug || hash);
153
183
  const className = (isPseudoGlobal ? selectors.map((s) => s[CLASS_NAME]) : [selector, ...externalClassNames]).join(" ");
154
184
  return {
155
185
  [SELECTOR]: selector,
@@ -174,24 +204,43 @@ const cxCommon = (args) => {
174
204
  }
175
205
  return args;
176
206
  };
177
- const keyframesCommon = (strings, ...values) => {
207
+ const keyframesCommon = (strings, values, classNameSlug, onInvalidSlug) => {
178
208
  const [label, styleString] = buildStyleString(strings, values);
209
+ const hash = toHash(label + styleString);
210
+ let customSlug;
211
+ if (classNameSlug) {
212
+ const slug = classNameSlug(hash, normalizeLabel(label), styleString);
213
+ if (slug) {
214
+ if (isValidKeyframeName(slug)) {
215
+ customSlug = slug;
216
+ } else {
217
+ ;
218
+ (onInvalidSlug || defaultOnInvalidSlug)(slug);
219
+ }
220
+ }
221
+ }
179
222
  return {
180
223
  [SELECTOR]: "",
181
- [CLASS_NAME]: `@keyframes ${toHash(label + styleString)}`,
224
+ [CLASS_NAME]: `@keyframes ${customSlug || hash}`,
182
225
  [STYLE_STRING]: styleString,
183
226
  [SELECTORS]: [],
184
227
  [EXTERNAL_CLASS_NAMES]: []
185
228
  };
186
229
  };
187
230
  let viewTransitionNameIndex = 0;
188
- const viewTransitionCommon = ((strings, values) => {
231
+ const viewTransitionCommon = ((strings, values, classNameSlug, onInvalidSlug) => {
189
232
  if (!strings) {
190
233
  strings = [`/* h-v-t ${viewTransitionNameIndex++} */`];
191
234
  }
192
- const content = Array.isArray(strings) ? cssCommon(strings, values) : strings;
235
+ const content = Array.isArray(strings) ? cssCommon(strings, values, classNameSlug, onInvalidSlug) : strings;
193
236
  const transitionName = content[CLASS_NAME];
194
- const res = cssCommon(["view-transition-name:", ""], [transitionName]);
237
+ const res = cssCommon(
238
+ ["view-transition-name:", ""],
239
+ // eslint-disable-line @typescript-eslint/no-explicit-any
240
+ [transitionName],
241
+ classNameSlug,
242
+ onInvalidSlug
243
+ );
195
244
  content[CLASS_NAME] = PSEUDO_GLOBAL_SELECTOR + content[CLASS_NAME];
196
245
  content[STYLE_STRING] = content[STYLE_STRING].replace(
197
246
  /(?<=::view-transition(?:[a-z-]*)\()(?=\))/g,
@@ -31,7 +31,11 @@ var import_constants = require("../../jsx/constants");
31
31
  var import_css = require("../../jsx/dom/css");
32
32
  var import_common = require("./common");
33
33
  var import_common2 = require("./common");
34
- const createCssContext = ({ id }) => {
34
+ const createCssContext = ({
35
+ id,
36
+ classNameSlug,
37
+ onInvalidSlug
38
+ }) => {
35
39
  const [cssJsxDomObject, StyleRenderToDom] = (0, import_css.createCssJsxDomObjects)({ id });
36
40
  const contextMap = /* @__PURE__ */ new WeakMap();
37
41
  const nonceMap = /* @__PURE__ */ new WeakMap();
@@ -94,15 +98,18 @@ const createCssContext = ({ id }) => {
94
98
  return promise;
95
99
  };
96
100
  const css2 = (strings, ...values) => {
97
- return newCssClassNameObject((0, import_common.cssCommon)(strings, values));
101
+ return newCssClassNameObject((0, import_common.cssCommon)(strings, values, classNameSlug, onInvalidSlug));
98
102
  };
99
103
  const cx2 = (...args) => {
100
104
  args = (0, import_common.cxCommon)(args);
101
105
  return css2(Array(args.length).fill(""), ...args);
102
106
  };
103
- const keyframes2 = import_common.keyframesCommon;
107
+ const keyframes2 = (strings, ...values) => (0, import_common.keyframesCommon)(strings, values, classNameSlug, onInvalidSlug);
104
108
  const viewTransition2 = ((strings, ...values) => {
105
- return newCssClassNameObject((0, import_common.viewTransitionCommon)(strings, values));
109
+ return newCssClassNameObject(
110
+ (0, import_common.viewTransitionCommon)(strings, values, classNameSlug, onInvalidSlug)
111
+ // eslint-disable-line @typescript-eslint/no-explicit-any
112
+ );
106
113
  });
107
114
  const Style2 = ({ children, nonce } = {}) => (0, import_html.raw)(
108
115
  `<style id="${id}"${nonce ? ` nonce="${nonce}"` : ""}>${children ? children[import_common.STYLE_STRING] : ""}</style>`,
@@ -38,16 +38,18 @@ const DEFAULT_CONTENT_TYPE = "text/plain";
38
38
  const DEFAULT_OUTPUT_DIR = "./static";
39
39
  const generateFilePath = (routePath, outDir, mimeType, extensionMap) => {
40
40
  const extension = determineExtension(mimeType, extensionMap);
41
+ let filePath;
41
42
  if (routePath.endsWith(`.${extension}`)) {
42
- return (0, import_utils2.joinPaths)(outDir, routePath);
43
+ filePath = (0, import_utils2.joinPaths)(outDir, routePath);
44
+ } else if (routePath === "/") {
45
+ filePath = (0, import_utils2.joinPaths)(outDir, `index.${extension}`);
46
+ } else if (routePath.endsWith("/")) {
47
+ filePath = (0, import_utils2.joinPaths)(outDir, routePath, `index.${extension}`);
48
+ } else {
49
+ filePath = (0, import_utils2.joinPaths)(outDir, `${routePath}.${extension}`);
43
50
  }
44
- if (routePath === "/") {
45
- return (0, import_utils2.joinPaths)(outDir, `index.${extension}`);
46
- }
47
- if (routePath.endsWith("/")) {
48
- return (0, import_utils2.joinPaths)(outDir, routePath, `index.${extension}`);
49
- }
50
- return (0, import_utils2.joinPaths)(outDir, `${routePath}.${extension}`);
51
+ (0, import_utils2.ensureWithinOutDir)(outDir, filePath);
52
+ return filePath;
51
53
  };
52
54
  const parseResponseContent = async (response) => {
53
55
  const contentType = response.headers.get("Content-Type");
@@ -18,6 +18,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var utils_exports = {};
19
19
  __export(utils_exports, {
20
20
  dirname: () => dirname,
21
+ ensureWithinOutDir: () => ensureWithinOutDir,
21
22
  filterStaticGenerateRoutes: () => filterStaticGenerateRoutes,
22
23
  isDynamicRoute: () => isDynamicRoute,
23
24
  joinPaths: () => joinPaths
@@ -75,9 +76,17 @@ const filterStaticGenerateRoutes = (hono) => {
75
76
  const isDynamicRoute = (path) => {
76
77
  return path.split("/").some((segment) => segment.startsWith(":") || segment.includes("*"));
77
78
  };
79
+ const ensureWithinOutDir = (outDir, filePath) => {
80
+ const normalizedOutDir = joinPaths("/", outDir);
81
+ const normalizedFilePath = joinPaths("/", filePath);
82
+ if (normalizedFilePath !== normalizedOutDir && !normalizedFilePath.startsWith(`${normalizedOutDir}/`)) {
83
+ throw new Error(`Path traversal detected: "${filePath}" is outside the output directory`);
84
+ }
85
+ };
78
86
  // Annotate the CommonJS export names for ESM import in node:
79
87
  0 && (module.exports = {
80
88
  dirname,
89
+ ensureWithinOutDir,
81
90
  filterStaticGenerateRoutes,
82
91
  isDynamicRoute,
83
92
  joinPaths
@@ -117,22 +117,29 @@ const createCssJsxDomObjects = ({ id }) => {
117
117
  });
118
118
  return [cssObject, Style2];
119
119
  };
120
- const createCssContext = ({ id }) => {
120
+ const createCssContext = ({
121
+ id,
122
+ classNameSlug,
123
+ onInvalidSlug
124
+ }) => {
121
125
  const [cssObject, Style2] = createCssJsxDomObjects({ id });
122
126
  const newCssClassNameObject = (cssClassName) => {
123
127
  cssClassName.toString = cssObject.toString;
124
128
  return cssClassName;
125
129
  };
126
130
  const css2 = (strings, ...values) => {
127
- return newCssClassNameObject((0, import_common.cssCommon)(strings, values));
131
+ return newCssClassNameObject((0, import_common.cssCommon)(strings, values, classNameSlug, onInvalidSlug));
128
132
  };
129
133
  const cx2 = (...args) => {
130
134
  args = (0, import_common.cxCommon)(args);
131
135
  return css2(Array(args.length).fill(""), ...args);
132
136
  };
133
- const keyframes2 = import_common.keyframesCommon;
137
+ const keyframes2 = (strings, ...values) => (0, import_common.keyframesCommon)(strings, values, classNameSlug, onInvalidSlug);
134
138
  const viewTransition2 = ((strings, ...values) => {
135
- return newCssClassNameObject((0, import_common.viewTransitionCommon)(strings, values));
139
+ return newCssClassNameObject(
140
+ (0, import_common.viewTransitionCommon)(strings, values, classNameSlug, onInvalidSlug)
141
+ // eslint-disable-line @typescript-eslint/no-explicit-any
142
+ );
136
143
  });
137
144
  return {
138
145
  css: css2,
@@ -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
  }
@@ -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 {
@@ -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
  });
@@ -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");
@@ -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
@@ -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,