repocairn 0.1.2 → 0.1.4

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 (233) hide show
  1. package/README.md +13 -2
  2. package/dist/graphify-source.d.ts +17 -0
  3. package/dist/graphify-source.js +87 -0
  4. package/dist/graphify-source.js.map +1 -0
  5. package/dist/index-command.js +3 -1
  6. package/dist/index-command.js.map +1 -1
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/indexer.d.ts +1 -0
  11. package/dist/indexer.js +2 -1
  12. package/dist/indexer.js.map +1 -1
  13. package/dist/signals.d.ts +13 -0
  14. package/dist/signals.js +84 -0
  15. package/dist/signals.js.map +1 -0
  16. package/dist/tree-sitter-extract.js +134 -1
  17. package/dist/tree-sitter-extract.js.map +1 -1
  18. package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  19. package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  20. package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  21. package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  22. package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  23. package/node_modules/eventsource-parser/dist/index.cjs +2 -2
  24. package/node_modules/eventsource-parser/dist/index.cjs.map +1 -1
  25. package/node_modules/eventsource-parser/dist/index.js +2 -2
  26. package/node_modules/eventsource-parser/dist/index.js.map +1 -1
  27. package/node_modules/eventsource-parser/package.json +1 -1
  28. package/node_modules/eventsource-parser/src/parse.ts +4 -2
  29. package/node_modules/express-rate-limit/dist/index.cjs +6 -1
  30. package/node_modules/express-rate-limit/dist/index.mjs +6 -1
  31. package/node_modules/express-rate-limit/package.json +6 -6
  32. package/node_modules/fast-uri/index.js +37 -1
  33. package/node_modules/fast-uri/package.json +1 -1
  34. package/node_modules/fast-uri/test/security.test.js +136 -0
  35. package/node_modules/hono/dist/cjs/client/client.js +3 -3
  36. package/node_modules/hono/dist/cjs/client/utils.js +1 -1
  37. package/node_modules/hono/dist/cjs/context.js +32 -13
  38. package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
  39. package/node_modules/hono/dist/cjs/hono-base.js +3 -2
  40. package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
  41. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
  42. package/node_modules/hono/dist/cjs/jsx/intrinsic-element/components.js +1 -1
  43. package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
  44. package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
  45. package/node_modules/hono/dist/cjs/middleware/cors/index.js +17 -14
  46. package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
  47. package/node_modules/hono/dist/cjs/middleware/etag/digest.js +47 -1
  48. package/node_modules/hono/dist/cjs/middleware/etag/index.js +6 -3
  49. package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
  50. package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
  51. package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
  52. package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
  53. package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
  54. package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +6 -3
  55. package/node_modules/hono/dist/cjs/request.js +6 -8
  56. package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
  57. package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
  58. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
  59. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
  60. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
  61. package/node_modules/hono/dist/cjs/router/trie-router/node.js +44 -68
  62. package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
  63. package/node_modules/hono/dist/cjs/router.js +1 -1
  64. package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
  65. package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
  66. package/node_modules/hono/dist/cjs/utils/stream.js +5 -2
  67. package/node_modules/hono/dist/cjs/utils/url.js +9 -9
  68. package/node_modules/hono/dist/client/client.js +3 -3
  69. package/node_modules/hono/dist/client/utils.js +1 -1
  70. package/node_modules/hono/dist/context.js +32 -13
  71. package/node_modules/hono/dist/helper/proxy/index.js +4 -0
  72. package/node_modules/hono/dist/hono-base.js +3 -2
  73. package/node_modules/hono/dist/jsx/base.js +27 -23
  74. package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
  75. package/node_modules/hono/dist/jsx/intrinsic-element/components.js +1 -1
  76. package/node_modules/hono/dist/middleware/cache/index.js +103 -8
  77. package/node_modules/hono/dist/middleware/compress/index.js +5 -0
  78. package/node_modules/hono/dist/middleware/cors/index.js +17 -14
  79. package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
  80. package/node_modules/hono/dist/middleware/etag/digest.js +47 -1
  81. package/node_modules/hono/dist/middleware/etag/index.js +6 -3
  82. package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
  83. package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
  84. package/node_modules/hono/dist/middleware/language/language.js +10 -6
  85. package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
  86. package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
  87. package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +6 -3
  88. package/node_modules/hono/dist/request.js +7 -9
  89. package/node_modules/hono/dist/router/linear-router/router.js +7 -2
  90. package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
  91. package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
  92. package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
  93. package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
  94. package/node_modules/hono/dist/router/trie-router/node.js +44 -68
  95. package/node_modules/hono/dist/router/trie-router/router.js +3 -11
  96. package/node_modules/hono/dist/router.js +1 -1
  97. package/node_modules/hono/dist/types/client/types.d.ts +1 -1
  98. package/node_modules/hono/dist/types/context.d.ts +4 -0
  99. package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
  100. package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
  101. package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
  102. package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
  103. package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
  104. package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
  105. package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
  106. package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
  107. package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
  108. package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  109. package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
  110. package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
  111. package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
  112. package/node_modules/hono/dist/types/middleware/secure-headers/permissions-policy.d.ts +3 -3
  113. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
  114. package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
  115. package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
  116. package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
  117. package/node_modules/hono/dist/types/router.d.ts +1 -1
  118. package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
  119. package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  120. package/node_modules/hono/dist/utils/cookie.js +4 -3
  121. package/node_modules/hono/dist/utils/ipaddr.js +5 -3
  122. package/node_modules/hono/dist/utils/stream.js +5 -2
  123. package/node_modules/hono/dist/utils/url.js +7 -8
  124. package/node_modules/hono/package.json +10 -3
  125. package/node_modules/ip-address/README.md +163 -149
  126. package/node_modules/ip-address/dist/address-error.d.ts +11 -0
  127. package/node_modules/ip-address/dist/address-error.js.map +1 -1
  128. package/node_modules/ip-address/dist/common.d.ts +6 -0
  129. package/node_modules/ip-address/dist/common.js +17 -1
  130. package/node_modules/ip-address/dist/common.js.map +1 -1
  131. package/node_modules/ip-address/dist/ipv4.d.ts +22 -6
  132. package/node_modules/ip-address/dist/ipv4.js +25 -18
  133. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  134. package/node_modules/ip-address/dist/ipv6.d.ts +19 -5
  135. package/node_modules/ip-address/dist/ipv6.js +61 -33
  136. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  137. package/node_modules/ip-address/package.json +5 -3
  138. package/node_modules/jose/README.md +13 -13
  139. package/node_modules/jose/dist/types/index.d.ts +6 -5
  140. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +17 -11
  141. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +8 -19
  142. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +17 -11
  143. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +8 -19
  144. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +18 -13
  145. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +21 -21
  146. package/node_modules/jose/dist/types/jwk/embedded.d.ts +8 -12
  147. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +0 -15
  148. package/node_modules/jose/dist/types/jwks/local.d.ts +12 -21
  149. package/node_modules/jose/dist/types/jwks/remote.d.ts +34 -165
  150. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +2 -13
  151. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +17 -13
  152. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +2 -13
  153. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +17 -13
  154. package/node_modules/jose/dist/types/jws/general/sign.d.ts +16 -17
  155. package/node_modules/jose/dist/types/jws/general/verify.d.ts +18 -15
  156. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +17 -11
  157. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +11 -34
  158. package/node_modules/jose/dist/types/jwt/sign.d.ts +2 -13
  159. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +4 -13
  160. package/node_modules/jose/dist/types/jwt/verify.d.ts +14 -13
  161. package/node_modules/jose/dist/types/key/export.d.ts +0 -14
  162. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +7 -14
  163. package/node_modules/jose/dist/types/key/generate_secret.d.ts +16 -14
  164. package/node_modules/jose/dist/types/key/import.d.ts +18 -28
  165. package/node_modules/jose/dist/types/types.d.ts +243 -224
  166. package/node_modules/jose/dist/types/util/base64url.d.ts +11 -4
  167. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +1 -9
  168. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +0 -8
  169. package/node_modules/jose/dist/types/util/errors.d.ts +128 -121
  170. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +4 -22
  171. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +5 -153
  172. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +16 -111
  173. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +23 -3
  174. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +70 -82
  175. package/node_modules/jose/dist/webapi/jwk/embedded.js +5 -3
  176. package/node_modules/jose/dist/webapi/jwks/local.js +32 -73
  177. package/node_modules/jose/dist/webapi/jwks/remote.js +19 -28
  178. package/node_modules/jose/dist/webapi/jws/compact/sign.js +5 -2
  179. package/node_modules/jose/dist/webapi/jws/compact/verify.js +4 -16
  180. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +7 -65
  181. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +3 -89
  182. package/node_modules/jose/dist/webapi/jws/general/sign.js +19 -15
  183. package/node_modules/jose/dist/webapi/jws/general/verify.js +29 -9
  184. package/node_modules/jose/dist/webapi/jwt/decrypt.js +5 -5
  185. package/node_modules/jose/dist/webapi/jwt/sign.js +2 -3
  186. package/node_modules/jose/dist/webapi/jwt/unsecured.js +4 -3
  187. package/node_modules/jose/dist/webapi/jwt/verify.js +6 -6
  188. package/node_modules/jose/dist/webapi/key/export.js +37 -4
  189. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +33 -82
  190. package/node_modules/jose/dist/webapi/key/generate_secret.js +2 -2
  191. package/node_modules/jose/dist/webapi/key/import.js +8 -8
  192. package/node_modules/jose/dist/webapi/lib/asn1.js +53 -122
  193. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +1 -0
  194. package/node_modules/jose/dist/webapi/lib/content_encryption.js +34 -109
  195. package/node_modules/jose/dist/webapi/lib/crypto_key.js +18 -126
  196. package/node_modules/jose/dist/webapi/lib/helpers.js +23 -0
  197. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +0 -1
  198. package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +107 -0
  199. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +186 -0
  200. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +114 -0
  201. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +8 -98
  202. package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +73 -0
  203. package/node_modules/jose/dist/webapi/lib/jws_sign.js +74 -0
  204. package/node_modules/jose/dist/webapi/lib/jws_verify.js +107 -0
  205. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +68 -109
  206. package/node_modules/jose/dist/webapi/lib/key.js +132 -0
  207. package/node_modules/jose/dist/webapi/lib/key_algorithm.js +10 -0
  208. package/node_modules/jose/dist/webapi/lib/key_descriptor.js +7 -0
  209. package/node_modules/jose/dist/webapi/lib/key_management.js +162 -92
  210. package/node_modules/jose/dist/webapi/lib/options.js +48 -0
  211. package/node_modules/jose/dist/webapi/lib/signing.js +14 -56
  212. package/node_modules/jose/dist/webapi/lib/type_checks.js +13 -18
  213. package/node_modules/jose/dist/webapi/util/base64url.js +13 -4
  214. package/node_modules/jose/dist/webapi/util/decode_jwt.js +2 -2
  215. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +5 -15
  216. package/node_modules/jose/dist/webapi/util/errors.js +1 -1
  217. package/node_modules/jose/package.json +2 -4
  218. package/node_modules/type-is/node_modules/content-type/README.md +2 -0
  219. package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  220. package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  221. package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  222. package/node_modules/type-is/node_modules/content-type/package.json +1 -1
  223. package/package.json +5 -3
  224. package/node_modules/jose/dist/webapi/lib/aesgcmkw.js +0 -15
  225. package/node_modules/jose/dist/webapi/lib/aeskw.js +0 -25
  226. package/node_modules/jose/dist/webapi/lib/check_key_type.js +0 -122
  227. package/node_modules/jose/dist/webapi/lib/ecdhes.js +0 -52
  228. package/node_modules/jose/dist/webapi/lib/key_to_jwk.js +0 -34
  229. package/node_modules/jose/dist/webapi/lib/normalize_key.js +0 -166
  230. package/node_modules/jose/dist/webapi/lib/pbes2kw.js +0 -42
  231. package/node_modules/jose/dist/webapi/lib/rsaes.js +0 -24
  232. package/node_modules/jose/dist/webapi/lib/validate_algorithms.js +0 -10
  233. package/node_modules/jose/dist/webapi/lib/validate_crit.js +0 -33
@@ -26,7 +26,12 @@ function normalize (uri, options) {
26
26
  */
27
27
  function resolve (baseURI, relativeURI, options) {
28
28
  const schemelessOptions = options ? Object.assign({ scheme: 'null' }, options) : { scheme: 'null' }
29
- const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true)
29
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions)
30
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions)
31
+ if (baseMalformed || relativeMalformed) {
32
+ throw new Error(baseParsed.error || relativeParsed.error || 'URI is malformed.')
33
+ }
34
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true)
30
35
  schemelessOptions.skipEscape = true
31
36
  return serialize(resolved, schemelessOptions)
32
37
  }
@@ -206,6 +211,15 @@ const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:
206
211
  // with or without a scheme prefix, for the literal-backslash rejection below.
207
212
  const AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/
208
213
 
214
+ // Captures the leading authority-introducer region after an optional scheme: a
215
+ // run of forward slashes, backslashes, and the characters the WHATWG URL parser
216
+ // removes before parsing (TAB U+0009, LF U+000A, CR U+000D). A valid introducer
217
+ // is exactly "//". Node treats "\" as "/" on special schemes and strips those
218
+ // characters first, so forms like "\\", "/\", "\/", "/<TAB>/", or a leading
219
+ // "<TAB>//" reach an authority in Node while fast-uri's URI_PARSE folds them into
220
+ // the path group (host confusion / SSRF / redirect bypass).
221
+ const AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/
222
+
209
223
  /**
210
224
  * @param {import('./types/index').URIComponent} parsed
211
225
  * @param {RegExpMatchArray} matches
@@ -265,6 +279,28 @@ function parseWithStatus (uri, opts) {
265
279
  malformedAuthorityOrPort = true
266
280
  }
267
281
 
282
+ // Reject a malformed or whitespace-smuggled authority introducer. fast-uri
283
+ // only recognizes a literal "//"; anything else in the leading separator run
284
+ // (a backslash, or a "//" that appears only after removing the TAB/LF/CR that
285
+ // Node strips) means the authority fast-uri parses differs from the one Node's
286
+ // URL resolves. Reject rather than rewrite, mirroring the literal-backslash
287
+ // guard above. Percent-encoded forms (%5C, %09) are untouched, valid data.
288
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION)
289
+ if (introducerMatch !== null) {
290
+ const region = introducerMatch[1]
291
+ const normalizedRegion = region.replace(/[\t\n\r]/g, '')
292
+ // Two or more leading separators introduce an authority.
293
+ if (normalizedRegion.length >= 2) {
294
+ if (normalizedRegion.slice(0, 2) !== '//') {
295
+ parsed.error = parsed.error || 'URI authority must not contain a literal backslash.'
296
+ malformedAuthorityOrPort = true
297
+ } else if (region.length !== normalizedRegion.length) {
298
+ parsed.error = parsed.error || 'URI authority introducer must not contain whitespace.'
299
+ malformedAuthorityOrPort = true
300
+ }
301
+ }
302
+ }
303
+
268
304
  const matches = uri.match(URI_PARSE)
269
305
 
270
306
  if (matches) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fast-uri",
3
3
  "description": "Dependency-free RFC 3986 URI toolbox",
4
- "version": "3.1.4",
4
+ "version": "3.1.5",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
7
7
  "types": "types/index.d.ts",
@@ -221,3 +221,139 @@ test('parse does not reject a literal backslash in the query or fragment', (t) =
221
221
  t.notOk(parsed.error, 'backslash in query/fragment does not mark the URI malformed')
222
222
  t.equal(parsed.host, 'host.example.com', 'host parsed normally')
223
223
  })
224
+
225
+ test('parse rejects a malformed authority introducer (\\\\, /\\, \\/) in place of //', (t) => {
226
+ // Regression: "\\", "/\\", "\\/" after the scheme colon are not valid authority
227
+ // introducers. Node's URL treats "\\" as interchangeable with "/" on special
228
+ // schemes, so "http:\\\\evil.com/path" would be parsed as host "evil.com" by
229
+ // Node, but fast-uri must reject it as malformed to prevent SSRF/redirect bypass.
230
+ const cases = [
231
+ 'http:\\\\evil.com/path',
232
+ 'http:/\\evil.com/path',
233
+ 'http:\\/evil.com/path',
234
+ 'ws:\\\\evil.com/chat',
235
+ 'wss:\\\\evil.com/chat',
236
+ 'ftp:\\\\evil.com/',
237
+ '\\\\evil.com/path'
238
+ ]
239
+
240
+ t.plan(cases.length)
241
+
242
+ cases.forEach((input) => {
243
+ t.equal(
244
+ fastURI.parse(input).error,
245
+ 'URI authority must not contain a literal backslash.',
246
+ input
247
+ )
248
+ })
249
+ })
250
+
251
+ test('normalize does not canonicalize a malformed-authority-introducer URI', (t) => {
252
+ const cases = [
253
+ 'http:\\\\evil.com/path',
254
+ 'http:/\\evil.com/path'
255
+ ]
256
+
257
+ t.plan(cases.length)
258
+
259
+ cases.forEach((input) => {
260
+ t.equal(fastURI.normalize(input), input, input)
261
+ })
262
+ })
263
+
264
+ test('equal returns false for malformed-authority-introducer URIs', (t) => {
265
+ const pairs = [
266
+ ['http:\\\\evil.com/path', 'http://evil.com/path'],
267
+ ['http:/\\evil.com/path', 'http://evil.com/path']
268
+ ]
269
+
270
+ t.plan(pairs.length)
271
+
272
+ pairs.forEach(([left, right]) => {
273
+ t.equal(fastURI.equal(left, right), false, `${left} != ${right}`)
274
+ })
275
+ })
276
+
277
+ test('resolve throws on malformed authority introducer', (t) => {
278
+ // resolve() returns a plain string with no error field, so the only safe
279
+ // behavior is to throw when either component has a malformed authority.
280
+ const pairs = [
281
+ ['https://allowed.com/', '\\\\evil.com/path'],
282
+ ['\\\\evil.com/path', 'https://allowed.com/'],
283
+ ['https://allowed.com/', 'http:/\\evil.com/path'],
284
+ ['https://allowed.com/', 'http:\\/evil.com/path']
285
+ ]
286
+
287
+ t.plan(pairs.length)
288
+
289
+ pairs.forEach(([base, rel]) => {
290
+ t.throws(
291
+ () => fastURI.resolve(base, rel),
292
+ /URI authority must not contain a literal backslash/,
293
+ `${base} + ${rel}`
294
+ )
295
+ })
296
+ })
297
+
298
+ test('parse rejects a whitespace-split authority introducer (TAB, LF, CR)', (t) => {
299
+ // The WHATWG URL parser removes TAB (U+0009), LF (U+000A) and CR (U+000D) from
300
+ // the input before parsing, so a stripped character wedged into the introducer
301
+ // ("/<TAB>\\", "/<TAB>/", or a leading "<TAB>//") reaches an authority in Node
302
+ // while fast-uri would otherwise fold it into the path. These must be rejected
303
+ // like the adjacent "\\", "/\\", "\\/" forms.
304
+ const cases = [
305
+ { input: '/\t\\evil.com/path', expectedError: 'URI authority must not contain a literal backslash.' },
306
+ { input: '/\t/evil.com/path', expectedError: 'URI authority introducer must not contain whitespace.' },
307
+ { input: '/\n\\evil.com/path', expectedError: 'URI authority must not contain a literal backslash.' },
308
+ { input: '/\r\\evil.com/path', expectedError: 'URI authority must not contain a literal backslash.' },
309
+ { input: '\t//evil.com/path', expectedError: 'URI authority introducer must not contain whitespace.' },
310
+ { input: '\t/\\evil.com/path', expectedError: 'URI authority must not contain a literal backslash.' },
311
+ { input: 'https:/\t/evil.com/path', expectedError: 'URI authority introducer must not contain whitespace.' }
312
+ ]
313
+
314
+ t.plan(cases.length)
315
+
316
+ cases.forEach(({ input, expectedError }) => {
317
+ t.equal(fastURI.parse(input).error, expectedError, JSON.stringify(input))
318
+ })
319
+ })
320
+
321
+ test('resolve throws on a whitespace-split authority introducer', (t) => {
322
+ const pairs = [
323
+ ['https://allowed.com/', '/\t\\evil.com/path'],
324
+ ['https://allowed.com/', '/\t/evil.com/path'],
325
+ ['https://allowed.com/', '/\n\\evil.com/path'],
326
+ ['/\t/evil.com/path', 'https://allowed.com/']
327
+ ]
328
+
329
+ t.plan(pairs.length)
330
+
331
+ pairs.forEach(([base, rel]) => {
332
+ t.throws(
333
+ () => fastURI.resolve(base, rel),
334
+ /URI authority (must not contain a literal backslash|introducer must not contain whitespace)/,
335
+ `${JSON.stringify(base)} + ${JSON.stringify(rel)}`
336
+ )
337
+ })
338
+ })
339
+
340
+ test('parse does not reject valid authority introducer patterns', (t) => {
341
+ // No false positives: "//" introducer and scheme-less "//" must be valid.
342
+ const cases = [
343
+ 'http://good.com/',
344
+ 'https://good.com/',
345
+ 'ws://good.com/chat',
346
+ 'wss://good.com/chat',
347
+ 'ftp://good.com/',
348
+ '//good.com/path',
349
+ '/absolute/path',
350
+ 'relative/path'
351
+ ]
352
+
353
+ t.plan(cases.length)
354
+
355
+ cases.forEach((input) => {
356
+ const parsed = fastURI.parse(input)
357
+ t.notOk(parsed.error, input)
358
+ })
359
+ })
@@ -74,7 +74,7 @@ class ClientRequestImpl {
74
74
  }
75
75
  this.rBody = form;
76
76
  }
77
- if (args.json) {
77
+ if (args.json !== void 0) {
78
78
  this.rBody = JSON.stringify(args.json);
79
79
  this.cType = "application/json";
80
80
  }
@@ -90,9 +90,9 @@ class ClientRequestImpl {
90
90
  if (args?.cookie) {
91
91
  const cookies = [];
92
92
  for (const [key, value] of Object.entries(args.cookie)) {
93
- cookies.push((0, import_cookie.serialize)(key, value, { path: "/" }));
93
+ cookies.push((0, import_cookie.serialize)(key, value));
94
94
  }
95
- headerValues["Cookie"] = cookies.join(",");
95
+ headerValues["Cookie"] = cookies.join("; ");
96
96
  }
97
97
  if (this.cType) {
98
98
  headerValues["Content-Type"] = this.cType;
@@ -37,7 +37,7 @@ const mergePath = (base, path) => {
37
37
  const replaceUrlParam = (urlString, params) => {
38
38
  for (const [k, v] of Object.entries(params)) {
39
39
  const reg = new RegExp("/:" + k + "(?:{[^/]+})?\\??(?=/|$)");
40
- urlString = urlString.replace(reg, v ? `/${v}` : "");
40
+ urlString = urlString.replace(reg, () => v ? `/${v}` : "");
41
41
  }
42
42
  return urlString;
43
43
  };
@@ -224,6 +224,10 @@ class Context {
224
224
  * c.header('X-Message', 'Hello!')
225
225
  * c.header('Content-Type', 'text/plain')
226
226
  *
227
+ * // Append multiple headers using the append option (e.g. Vary)
228
+ * c.header('Vary', 'Accept-Encoding', { append: true })
229
+ * c.header('Vary', 'User-Agent', { append: true })
230
+ *
227
231
  * return c.body('Thank you for coming')
228
232
  * })
229
233
  * ```
@@ -295,11 +299,11 @@ class Context {
295
299
  return Object.fromEntries(this.#var);
296
300
  }
297
301
  #newResponse(data, arg, headers) {
298
- const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();
299
- if (typeof arg === "object" && "headers" in arg) {
300
- const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
301
- for (const [key, value] of argHeaders) {
302
- if (key.toLowerCase() === "set-cookie") {
302
+ let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
303
+ if (typeof arg === "object" && arg.headers) {
304
+ responseHeaders ??= new Headers();
305
+ for (const [key, value] of new Headers(arg.headers)) {
306
+ if (key === "set-cookie") {
303
307
  responseHeaders.append(key, value);
304
308
  } else {
305
309
  responseHeaders.set(key, value);
@@ -307,19 +311,34 @@ class Context {
307
311
  }
308
312
  }
309
313
  if (headers) {
310
- for (const [k, v] of Object.entries(headers)) {
311
- if (typeof v === "string") {
312
- responseHeaders.set(k, v);
313
- } else {
314
- responseHeaders.delete(k);
315
- for (const v2 of v) {
316
- responseHeaders.append(k, v2);
314
+ if (!responseHeaders) {
315
+ let count = 0;
316
+ for (const k in headers) {
317
+ if (++count > 1 || typeof headers[k] !== "string") {
318
+ responseHeaders = new Headers();
319
+ break;
320
+ }
321
+ }
322
+ }
323
+ if (responseHeaders) {
324
+ for (const k in headers) {
325
+ const v = headers[k];
326
+ if (typeof v === "string") {
327
+ responseHeaders.set(k, v);
328
+ } else {
329
+ responseHeaders.delete(k);
330
+ for (const v2 of v) {
331
+ responseHeaders.append(k, v2);
332
+ }
317
333
  }
318
334
  }
319
335
  }
320
336
  }
321
337
  const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
322
- return createResponseInstance(data, { status, headers: responseHeaders });
338
+ return createResponseInstance(data, {
339
+ status,
340
+ headers: responseHeaders ?? headers
341
+ });
323
342
  }
324
343
  newResponse = (...args) => this.#newResponse(...args);
325
344
  /**
@@ -92,6 +92,10 @@ const proxy = async (input, proxyInit) => {
92
92
  req.headers.delete("accept-encoding");
93
93
  const res = await (customFetch || fetch)(req);
94
94
  const resHeaders = new Headers(res.headers);
95
+ const connectionValue = resHeaders.get("connection");
96
+ if (connectionValue) {
97
+ connectionValue.split(",").map((h) => h.trim()).filter((h) => ALLOWED_TOKEN_PATTERN.test(h)).forEach((h) => resHeaders.delete(h));
98
+ }
95
99
  hopByHopHeaders.forEach((header) => {
96
100
  resHeaders.delete(header);
97
101
  });
@@ -43,6 +43,7 @@ class Hono {
43
43
  delete;
44
44
  options;
45
45
  patch;
46
+ query;
46
47
  all;
47
48
  on;
48
49
  use;
@@ -342,8 +343,8 @@ class Hono {
342
343
  * @see {@link https://hono.dev/docs/api/hono#fetch}
343
344
  *
344
345
  * @param {Request} request - request Object of request
345
- * @param {Env} Env - env Object
346
- * @param {ExecutionContext} - context of execution
346
+ * @param {Env} env - env Object
347
+ * @param {ExecutionContext} executionCtx - context of execution
347
348
  * @returns {Response | Promise<Response>} response of request
348
349
  *
349
350
  */
@@ -100,6 +100,18 @@ const booleanAttributes = [
100
100
  "reversed",
101
101
  "selected"
102
102
  ];
103
+ const resolveFunctionComponentResult = (result, suspendedContext) => result.then((resolved) => {
104
+ if (!Array.isArray(resolved) && !(resolved instanceof JSXNode)) {
105
+ return resolved;
106
+ }
107
+ const children = Array.isArray(resolved) ? resolved : [resolved];
108
+ const render = () => {
109
+ const buffer = [""];
110
+ childrenToStringToBuffer(children, buffer);
111
+ return buffer.length === 1 ? (0, import_html.raw)(buffer[0], buffer.callbacks) : (0, import_html2.stringBufferToString)(buffer, buffer.callbacks);
112
+ };
113
+ return suspendedContext ? suspendedContext(render) : (0, import_context.runWithRenderContext)(render);
114
+ });
103
115
  const childrenToStringToBuffer = (children, buffer) => {
104
116
  for (let i = 0, len = children.length; i < len; i++) {
105
117
  const child = children[i];
@@ -109,9 +121,17 @@ const childrenToStringToBuffer = (children, buffer) => {
109
121
  continue;
110
122
  } else if (child instanceof JSXNode) {
111
123
  child.toStringToBuffer(buffer);
112
- } else if (typeof child === "number" || child.isEscaped) {
124
+ } else if (typeof child === "number") {
125
+ ;
126
+ buffer[0] += child;
127
+ } else if (child.isEscaped) {
113
128
  ;
114
129
  buffer[0] += child;
130
+ const callbacks = child.callbacks;
131
+ if (callbacks) {
132
+ buffer.callbacks ||= [];
133
+ buffer.callbacks.push(...callbacks);
134
+ }
115
135
  } else if (child instanceof Promise) {
116
136
  buffer.unshift("", child);
117
137
  } else {
@@ -125,7 +145,6 @@ class JSXNode {
125
145
  key;
126
146
  children;
127
147
  isEscaped = true;
128
- suspendedContext;
129
148
  constructor(tag, props, children) {
130
149
  if (typeof tag !== "function" && !(0, import_utils.isValidTagName)(tag)) {
131
150
  throw new Error(`Invalid JSX tag name: ${tag}`);
@@ -148,7 +167,7 @@ class JSXNode {
148
167
  this.toStringToBuffer(buffer);
149
168
  return buffer.length === 1 ? "callbacks" in buffer ? (0, import_html2.resolveCallbackSync)((0, import_html.raw)(buffer[0], buffer.callbacks)).toString() : buffer[0] : (0, import_html2.stringBufferToString)(buffer, buffer.callbacks);
150
169
  };
151
- return this.suspendedContext ? this.suspendedContext(render) : (0, import_context.runWithRenderContext)(render);
170
+ return (0, import_context.runWithRenderContext)(render);
152
171
  }
153
172
  toStringToBuffer(buffer) {
154
173
  const tag = this.tag;
@@ -222,21 +241,14 @@ class JSXFunctionNode extends JSXNode {
222
241
  return;
223
242
  } else if (res instanceof Promise) {
224
243
  if (import_context.globalContexts.length === 0) {
225
- buffer.unshift("", res);
244
+ buffer.unshift("", resolveFunctionComponentResult(res));
226
245
  } else {
227
- const suspendedContext = (0, import_context.captureRenderContext)();
228
- buffer.unshift(
229
- "",
230
- res.then((childRes) => {
231
- if (childRes instanceof JSXNode) {
232
- childRes.suspendedContext = suspendedContext;
233
- }
234
- return childRes;
235
- })
236
- );
246
+ buffer.unshift("", resolveFunctionComponentResult(res, (0, import_context.captureRenderContext)()));
237
247
  }
238
248
  } else if (res instanceof JSXNode) {
239
249
  res.toStringToBuffer(buffer);
250
+ } else if (Array.isArray(res)) {
251
+ childrenToStringToBuffer(res, buffer);
240
252
  } else if (typeof res === "number" || res.isEscaped) {
241
253
  buffer[0] += res;
242
254
  if (res.callbacks) {
@@ -315,15 +327,7 @@ const shallowEqual = (a, b) => {
315
327
  return true;
316
328
  };
317
329
  const memo = (component, propsAreEqual = shallowEqual) => {
318
- let computed = null;
319
- let prevProps = void 0;
320
- const wrapper = ((props) => {
321
- if (prevProps && !propsAreEqual(prevProps, props)) {
322
- computed = null;
323
- }
324
- prevProps = props;
325
- return computed ||= component(props);
326
- });
330
+ const wrapper = ((props) => component(props));
327
331
  wrapper[import_constants.DOM_MEMO] = propsAreEqual;
328
332
  wrapper[import_constants.DOM_RENDERER] = component;
329
333
  return wrapper;
@@ -263,7 +263,7 @@ const useCallback = (callback, deps) => {
263
263
  }
264
264
  return callback;
265
265
  };
266
- const useRef = (initialValue) => {
266
+ function useRef(initialValue) {
267
267
  const buildData = import_render.buildDataStack.at(-1);
268
268
  if (!buildData) {
269
269
  return { current: initialValue };
@@ -272,7 +272,7 @@ const useRef = (initialValue) => {
272
272
  const refArray = node[import_constants.DOM_STASH][1][STASH_REF] ||= [];
273
273
  const hookIndex = node[import_constants.DOM_STASH][0]++;
274
274
  return refArray[hookIndex] ||= { current: initialValue };
275
- };
275
+ }
276
276
  const use = (promise) => {
277
277
  const cachedRes = resolvedPromiseValueMap.get(promise);
278
278
  if (cachedRes) {
@@ -330,19 +330,22 @@ const useSyncExternalStore = (subscribe, getSnapshot, getServerSnapshot) => {
330
330
  }
331
331
  return getServerSnapshot();
332
332
  }
333
- const [serverSnapshotIsUsed] = useState(!!(buildData[0][4] && getServerSnapshot));
334
- const [state, setState] = useState(
335
- () => serverSnapshotIsUsed ? getServerSnapshot() : getSnapshot()
336
- );
333
+ const snapshot = buildData[0][4] && getServerSnapshot ? getServerSnapshot() : getSnapshot();
334
+ const [, setVersion] = useState(0);
335
+ const latestSnapshot = useRef([snapshot, getSnapshot]);
336
+ latestSnapshot.current = [snapshot, getSnapshot];
337
+ const unsubscribeRef = useRef(null);
337
338
  useEffect(() => {
338
- if (serverSnapshotIsUsed) {
339
- setState(getSnapshot());
339
+ const update2 = () => setVersion((version) => version + 1);
340
+ unsubscribeRef.current?.();
341
+ unsubscribeRef.current = subscribe(update2);
342
+ const [snapshot2, getSnapshot2] = latestSnapshot.current;
343
+ if (!Object.is(snapshot2, getSnapshot2())) {
344
+ update2();
340
345
  }
341
- return subscribe(() => {
342
- setState(getSnapshot());
343
- });
344
- }, []);
345
- return state;
346
+ }, [subscribe]);
347
+ useEffect(() => () => unsubscribeRef.current?.(), []);
348
+ return snapshot;
346
349
  };
347
350
  // Annotate the CommonJS export names for ESM import in node:
348
351
  0 && (module.exports = {
@@ -101,7 +101,7 @@ const documentMetadataTag = (tag, children, props, sort) => {
101
101
  const string = new import_base.JSXNode(tag, restProps, (0, import_children.toArray)(children || [])).toString();
102
102
  if (string instanceof Promise) {
103
103
  return string.then(
104
- (resString) => (0, import_html.raw)(string, [
104
+ (resString) => (0, import_html.raw)(resString, [
105
105
  ...resString.callbacks || [],
106
106
  insertIntoHead(tag, resString, restProps, precedence)
107
107
  ])
@@ -20,7 +20,21 @@ __export(cache_exports, {
20
20
  cache: () => cache
21
21
  });
22
22
  module.exports = __toCommonJS(cache_exports);
23
+ var import_request = require("../../request");
24
+ var import_crypto = require("../../utils/crypto");
23
25
  const defaultCacheableStatusCodes = [200];
26
+ const defaultMaxQueryBodySize = 64 * 1024;
27
+ const cacheKeyPath = "/.hono/cache";
28
+ const cacheKeyParameter = "__hono_cache_key";
29
+ const cacheMethodKeyParameter = "__hono_cache_method";
30
+ const queryDigestKeyParameter = "__hono_query_digest";
31
+ const cacheVaryKeyParameter = "__hono_cache_vary";
32
+ const queryRepresentationMetadataHeaders = [
33
+ "content-type",
34
+ "content-encoding",
35
+ "content-language",
36
+ "content-location"
37
+ ];
24
38
  const shouldSkipCacheControl = (cacheControl) => !!cacheControl && /(?:^|,\s*)(?:private|no-(?:store|cache))(?:\s*(?:=|,|$))/i.test(cacheControl);
25
39
  const parseVaryDirectives = (vary) => {
26
40
  if (vary == null) {
@@ -28,17 +42,86 @@ const parseVaryDirectives = (vary) => {
28
42
  }
29
43
  return (Array.isArray(vary) ? vary : vary.split(",")).map((directive) => directive.trim().toLowerCase()).filter(Boolean);
30
44
  };
45
+ const createCacheKey = (key, requestUrl, request, varyHeaders) => {
46
+ const url = new URL(cacheKeyPath, requestUrl);
47
+ url.searchParams.append(cacheKeyParameter, key.split("#", 1)[0]);
48
+ url.searchParams.append(cacheMethodKeyParameter, request.method);
49
+ if (request.method === "QUERY") {
50
+ url.searchParams.append(queryDigestKeyParameter, request.digest);
51
+ }
52
+ for (const header of varyHeaders) {
53
+ url.searchParams.append(cacheVaryKeyParameter, JSON.stringify(header));
54
+ }
55
+ return url.href;
56
+ };
31
57
  const shouldSkipCache = (res, optionsVaryDirectives, responseVary) => responseVary.length && (!optionsVaryDirectives || responseVary.some((name) => !optionsVaryDirectives.has(name))) || shouldSkipCacheControl(res.headers.get("Cache-Control")) || res.headers.has("Set-Cookie");
58
+ const reportCacheNotAvailable = (onCacheNotAvailable, reason) => {
59
+ if (onCacheNotAvailable === false) {
60
+ } else if (onCacheNotAvailable) {
61
+ onCacheNotAvailable(reason);
62
+ } else {
63
+ console.log(reason);
64
+ }
65
+ };
66
+ const createQueryDigest = async (c, maxQueryBodySize) => {
67
+ if (!globalThis.crypto?.subtle) {
68
+ return void 0;
69
+ }
70
+ if (c.req.raw.bodyUsed && Object.keys(c.req.bodyCache)[0] === "formData") {
71
+ return void 0;
72
+ }
73
+ try {
74
+ const requestHeaders = c.req.raw.headers;
75
+ const metadata = new TextEncoder().encode(
76
+ JSON.stringify(
77
+ queryRepresentationMetadataHeaders.map((header) => [header, requestHeaders.get(header)])
78
+ )
79
+ );
80
+ const body = (await (0, import_request.cloneRawRequest)(c.req)).body;
81
+ const chunks = [];
82
+ let bodySize = 0;
83
+ if (body) {
84
+ const reader = body.getReader();
85
+ for (; ; ) {
86
+ const { done, value } = await reader.read();
87
+ if (done) {
88
+ break;
89
+ }
90
+ bodySize += value.byteLength;
91
+ if (bodySize > maxQueryBodySize) {
92
+ void reader.cancel().catch(() => {
93
+ });
94
+ return void 0;
95
+ }
96
+ chunks.push(value);
97
+ }
98
+ }
99
+ const data = new Uint8Array(metadata.byteLength + bodySize);
100
+ data.set(metadata);
101
+ let offset = metadata.byteLength;
102
+ for (const chunk of chunks) {
103
+ data.set(chunk, offset);
104
+ offset += chunk.byteLength;
105
+ }
106
+ return await (0, import_crypto.sha256)(data) ?? void 0;
107
+ } catch {
108
+ return void 0;
109
+ }
110
+ };
32
111
  const cache = (options) => {
33
112
  if (!globalThis.caches) {
34
- if (options.onCacheNotAvailable === false) {
35
- } else if (options.onCacheNotAvailable) {
36
- options.onCacheNotAvailable();
37
- } else {
38
- console.log("Cache Middleware is not enabled because caches is not defined.");
39
- }
113
+ reportCacheNotAvailable(
114
+ options.onCacheNotAvailable,
115
+ "Cache Middleware is not enabled because caches is not defined."
116
+ );
40
117
  return async (_c, next) => await next();
41
118
  }
119
+ if (!globalThis.crypto?.subtle) {
120
+ reportCacheNotAvailable(
121
+ options.onCacheNotAvailable,
122
+ "Cache Middleware cannot cache QUERY requests because Web Crypto is not available."
123
+ );
124
+ }
42
125
  if (options.wait === void 0) {
43
126
  options.wait = false;
44
127
  }
@@ -53,6 +136,7 @@ const cache = (options) => {
53
136
  const cacheableStatusCodes = new Set(
54
137
  options.cacheableStatusCodes ?? defaultCacheableStatusCodes
55
138
  );
139
+ const maxQueryBodySize = options.maxQueryBodySize ?? defaultMaxQueryBodySize;
56
140
  const addHeader = (c, responseVary) => {
57
141
  if (cacheControlDirectives) {
58
142
  const existingDirectives = c.res.headers.get("Cache-Control")?.split(",").map((d) => d.trim().split("=", 1)[0].toLowerCase()) ?? [];
@@ -81,20 +165,31 @@ const cache = (options) => {
81
165
  }
82
166
  };
83
167
  return async function cache2(c, next) {
84
- if (c.req.method !== "GET" || c.req.raw.headers.has("Authorization")) {
168
+ if (c.req.method !== "GET" && c.req.method !== "QUERY" || c.req.raw.headers.has("Authorization")) {
85
169
  await next();
86
170
  return;
87
171
  }
172
+ let cacheKeyRequest = { method: "GET" };
173
+ if (c.req.method === "QUERY") {
174
+ const digest = await createQueryDigest(c, maxQueryBodySize);
175
+ if (digest === void 0) {
176
+ await next();
177
+ return;
178
+ }
179
+ cacheKeyRequest = { method: "QUERY", digest };
180
+ }
88
181
  let key = c.req.url;
89
182
  if (options.keyGenerator) {
90
183
  key = await options.keyGenerator(c);
91
184
  }
185
+ const varyHeaders = [];
92
186
  if (varyDirectives) {
93
187
  for (const directive of varyDirectives) {
94
188
  const value = c.req.raw.headers.get(directive) ?? "";
95
- key += `::${directive}=${encodeURIComponent(value)}`;
189
+ varyHeaders.push([directive, value]);
96
190
  }
97
191
  }
192
+ key = createCacheKey(key, c.req.url, cacheKeyRequest, varyHeaders);
98
193
  const cacheName = typeof options.cacheName === "function" ? await options.cacheName(c) : options.cacheName;
99
194
  const cache3 = await caches.open(cacheName);
100
195
  const response = await cache3.match(key);