dsh-model-proxy-switch 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -2
  3. package/client.js +473 -0
  4. package/cordis.patch.yml +7 -0
  5. package/icon.svg +13 -0
  6. package/index.js +341 -0
  7. package/locale/en.json +25 -0
  8. package/locale/zh.json +25 -0
  9. package/package.json +55 -4
  10. package/vendor/http-proxy.js +542 -0
  11. package/vendor/node_modules/undici/LICENSE +21 -0
  12. package/vendor/node_modules/undici/README.md +741 -0
  13. package/vendor/node_modules/undici/docs/docs/api/Agent.md +84 -0
  14. package/vendor/node_modules/undici/docs/docs/api/BalancedPool.md +99 -0
  15. package/vendor/node_modules/undici/docs/docs/api/CacheStorage.md +30 -0
  16. package/vendor/node_modules/undici/docs/docs/api/CacheStore.md +164 -0
  17. package/vendor/node_modules/undici/docs/docs/api/Client.md +290 -0
  18. package/vendor/node_modules/undici/docs/docs/api/ClientStats.md +27 -0
  19. package/vendor/node_modules/undici/docs/docs/api/Connector.md +115 -0
  20. package/vendor/node_modules/undici/docs/docs/api/ContentType.md +57 -0
  21. package/vendor/node_modules/undici/docs/docs/api/Cookies.md +128 -0
  22. package/vendor/node_modules/undici/docs/docs/api/Debug.md +62 -0
  23. package/vendor/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +317 -0
  24. package/vendor/node_modules/undici/docs/docs/api/Dispatcher.md +1393 -0
  25. package/vendor/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +159 -0
  26. package/vendor/node_modules/undici/docs/docs/api/Errors.md +49 -0
  27. package/vendor/node_modules/undici/docs/docs/api/EventSource.md +45 -0
  28. package/vendor/node_modules/undici/docs/docs/api/Fetch.md +60 -0
  29. package/vendor/node_modules/undici/docs/docs/api/GlobalInstallation.md +139 -0
  30. package/vendor/node_modules/undici/docs/docs/api/H2CClient.md +263 -0
  31. package/vendor/node_modules/undici/docs/docs/api/MockAgent.md +603 -0
  32. package/vendor/node_modules/undici/docs/docs/api/MockCallHistory.md +197 -0
  33. package/vendor/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +43 -0
  34. package/vendor/node_modules/undici/docs/docs/api/MockClient.md +81 -0
  35. package/vendor/node_modules/undici/docs/docs/api/MockErrors.md +12 -0
  36. package/vendor/node_modules/undici/docs/docs/api/MockPool.md +555 -0
  37. package/vendor/node_modules/undici/docs/docs/api/Pool.md +84 -0
  38. package/vendor/node_modules/undici/docs/docs/api/PoolStats.md +35 -0
  39. package/vendor/node_modules/undici/docs/docs/api/ProxyAgent.md +229 -0
  40. package/vendor/node_modules/undici/docs/docs/api/RedirectHandler.md +93 -0
  41. package/vendor/node_modules/undici/docs/docs/api/RetryAgent.md +50 -0
  42. package/vendor/node_modules/undici/docs/docs/api/RetryHandler.md +118 -0
  43. package/vendor/node_modules/undici/docs/docs/api/RoundRobinPool.md +145 -0
  44. package/vendor/node_modules/undici/docs/docs/api/SnapshotAgent.md +616 -0
  45. package/vendor/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +275 -0
  46. package/vendor/node_modules/undici/docs/docs/api/Util.md +25 -0
  47. package/vendor/node_modules/undici/docs/docs/api/WebSocket.md +141 -0
  48. package/vendor/node_modules/undici/docs/docs/api/api-lifecycle.md +91 -0
  49. package/vendor/node_modules/undici/docs/docs/best-practices/client-certificate.md +64 -0
  50. package/vendor/node_modules/undici/docs/docs/best-practices/crawling.md +58 -0
  51. package/vendor/node_modules/undici/docs/docs/best-practices/mocking-request.md +190 -0
  52. package/vendor/node_modules/undici/docs/docs/best-practices/proxy.md +127 -0
  53. package/vendor/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +224 -0
  54. package/vendor/node_modules/undici/docs/docs/best-practices/writing-tests.md +63 -0
  55. package/vendor/node_modules/undici/index-fetch.js +65 -0
  56. package/vendor/node_modules/undici/index.d.ts +3 -0
  57. package/vendor/node_modules/undici/index.js +234 -0
  58. package/vendor/node_modules/undici/lib/api/abort-signal.js +59 -0
  59. package/vendor/node_modules/undici/lib/api/api-connect.js +110 -0
  60. package/vendor/node_modules/undici/lib/api/api-pipeline.js +252 -0
  61. package/vendor/node_modules/undici/lib/api/api-request.js +214 -0
  62. package/vendor/node_modules/undici/lib/api/api-stream.js +209 -0
  63. package/vendor/node_modules/undici/lib/api/api-upgrade.js +111 -0
  64. package/vendor/node_modules/undici/lib/api/index.js +7 -0
  65. package/vendor/node_modules/undici/lib/api/readable.js +580 -0
  66. package/vendor/node_modules/undici/lib/cache/memory-cache-store.js +279 -0
  67. package/vendor/node_modules/undici/lib/cache/sqlite-cache-store.js +467 -0
  68. package/vendor/node_modules/undici/lib/core/connect.js +159 -0
  69. package/vendor/node_modules/undici/lib/core/constants.js +143 -0
  70. package/vendor/node_modules/undici/lib/core/diagnostics.js +227 -0
  71. package/vendor/node_modules/undici/lib/core/errors.js +477 -0
  72. package/vendor/node_modules/undici/lib/core/request.js +485 -0
  73. package/vendor/node_modules/undici/lib/core/socks5-client.js +422 -0
  74. package/vendor/node_modules/undici/lib/core/socks5-utils.js +212 -0
  75. package/vendor/node_modules/undici/lib/core/symbols.js +76 -0
  76. package/vendor/node_modules/undici/lib/core/tree.js +160 -0
  77. package/vendor/node_modules/undici/lib/core/util.js +992 -0
  78. package/vendor/node_modules/undici/lib/dispatcher/agent.js +162 -0
  79. package/vendor/node_modules/undici/lib/dispatcher/balanced-pool.js +221 -0
  80. package/vendor/node_modules/undici/lib/dispatcher/client-h1.js +1744 -0
  81. package/vendor/node_modules/undici/lib/dispatcher/client-h2.js +1056 -0
  82. package/vendor/node_modules/undici/lib/dispatcher/client.js +669 -0
  83. package/vendor/node_modules/undici/lib/dispatcher/dispatcher-base.js +196 -0
  84. package/vendor/node_modules/undici/lib/dispatcher/dispatcher.js +48 -0
  85. package/vendor/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +146 -0
  86. package/vendor/node_modules/undici/lib/dispatcher/fixed-queue.js +135 -0
  87. package/vendor/node_modules/undici/lib/dispatcher/h2c-client.js +51 -0
  88. package/vendor/node_modules/undici/lib/dispatcher/pool-base.js +214 -0
  89. package/vendor/node_modules/undici/lib/dispatcher/pool.js +118 -0
  90. package/vendor/node_modules/undici/lib/dispatcher/proxy-agent.js +319 -0
  91. package/vendor/node_modules/undici/lib/dispatcher/retry-agent.js +35 -0
  92. package/vendor/node_modules/undici/lib/dispatcher/round-robin-pool.js +137 -0
  93. package/vendor/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +260 -0
  94. package/vendor/node_modules/undici/lib/encoding/index.js +33 -0
  95. package/vendor/node_modules/undici/lib/global.js +59 -0
  96. package/vendor/node_modules/undici/lib/handler/cache-handler.js +824 -0
  97. package/vendor/node_modules/undici/lib/handler/cache-revalidation-handler.js +124 -0
  98. package/vendor/node_modules/undici/lib/handler/decorator-handler.js +67 -0
  99. package/vendor/node_modules/undici/lib/handler/deduplication-handler.js +504 -0
  100. package/vendor/node_modules/undici/lib/handler/redirect-handler.js +245 -0
  101. package/vendor/node_modules/undici/lib/handler/retry-handler.js +504 -0
  102. package/vendor/node_modules/undici/lib/handler/unwrap-handler.js +106 -0
  103. package/vendor/node_modules/undici/lib/handler/wrap-handler.js +105 -0
  104. package/vendor/node_modules/undici/lib/interceptor/cache.js +621 -0
  105. package/vendor/node_modules/undici/lib/interceptor/decompress.js +561 -0
  106. package/vendor/node_modules/undici/lib/interceptor/deduplicate.js +117 -0
  107. package/vendor/node_modules/undici/lib/interceptor/dns.js +571 -0
  108. package/vendor/node_modules/undici/lib/interceptor/dump.js +99 -0
  109. package/vendor/node_modules/undici/lib/interceptor/redirect.js +21 -0
  110. package/vendor/node_modules/undici/lib/interceptor/response-error.js +95 -0
  111. package/vendor/node_modules/undici/lib/interceptor/retry.js +19 -0
  112. package/vendor/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  113. package/vendor/node_modules/undici/lib/llhttp/constants.d.ts +195 -0
  114. package/vendor/node_modules/undici/lib/llhttp/constants.js +531 -0
  115. package/vendor/node_modules/undici/lib/llhttp/llhttp-wasm.js +15 -0
  116. package/vendor/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +15 -0
  117. package/vendor/node_modules/undici/lib/llhttp/utils.d.ts +2 -0
  118. package/vendor/node_modules/undici/lib/llhttp/utils.js +12 -0
  119. package/vendor/node_modules/undici/lib/mock/mock-agent.js +232 -0
  120. package/vendor/node_modules/undici/lib/mock/mock-call-history.js +248 -0
  121. package/vendor/node_modules/undici/lib/mock/mock-client.js +68 -0
  122. package/vendor/node_modules/undici/lib/mock/mock-errors.js +29 -0
  123. package/vendor/node_modules/undici/lib/mock/mock-interceptor.js +209 -0
  124. package/vendor/node_modules/undici/lib/mock/mock-pool.js +68 -0
  125. package/vendor/node_modules/undici/lib/mock/mock-symbols.js +32 -0
  126. package/vendor/node_modules/undici/lib/mock/mock-utils.js +486 -0
  127. package/vendor/node_modules/undici/lib/mock/pending-interceptors-formatter.js +43 -0
  128. package/vendor/node_modules/undici/lib/mock/snapshot-agent.js +353 -0
  129. package/vendor/node_modules/undici/lib/mock/snapshot-recorder.js +588 -0
  130. package/vendor/node_modules/undici/lib/mock/snapshot-utils.js +158 -0
  131. package/vendor/node_modules/undici/lib/util/cache.js +676 -0
  132. package/vendor/node_modules/undici/lib/util/date.js +670 -0
  133. package/vendor/node_modules/undici/lib/util/promise.js +28 -0
  134. package/vendor/node_modules/undici/lib/util/runtime-features.js +124 -0
  135. package/vendor/node_modules/undici/lib/util/stats.js +32 -0
  136. package/vendor/node_modules/undici/lib/util/timers.js +425 -0
  137. package/vendor/node_modules/undici/lib/web/cache/cache.js +864 -0
  138. package/vendor/node_modules/undici/lib/web/cache/cachestorage.js +152 -0
  139. package/vendor/node_modules/undici/lib/web/cache/util.js +45 -0
  140. package/vendor/node_modules/undici/lib/web/cookies/constants.js +12 -0
  141. package/vendor/node_modules/undici/lib/web/cookies/index.js +201 -0
  142. package/vendor/node_modules/undici/lib/web/cookies/parse.js +315 -0
  143. package/vendor/node_modules/undici/lib/web/cookies/util.js +371 -0
  144. package/vendor/node_modules/undici/lib/web/eventsource/eventsource-stream.js +575 -0
  145. package/vendor/node_modules/undici/lib/web/eventsource/eventsource.js +504 -0
  146. package/vendor/node_modules/undici/lib/web/eventsource/util.js +29 -0
  147. package/vendor/node_modules/undici/lib/web/fetch/LICENSE +21 -0
  148. package/vendor/node_modules/undici/lib/web/fetch/body.js +509 -0
  149. package/vendor/node_modules/undici/lib/web/fetch/constants.js +131 -0
  150. package/vendor/node_modules/undici/lib/web/fetch/data-url.js +596 -0
  151. package/vendor/node_modules/undici/lib/web/fetch/formdata-parser.js +586 -0
  152. package/vendor/node_modules/undici/lib/web/fetch/formdata.js +259 -0
  153. package/vendor/node_modules/undici/lib/web/fetch/global.js +40 -0
  154. package/vendor/node_modules/undici/lib/web/fetch/headers.js +719 -0
  155. package/vendor/node_modules/undici/lib/web/fetch/index.js +2413 -0
  156. package/vendor/node_modules/undici/lib/web/fetch/request.js +1115 -0
  157. package/vendor/node_modules/undici/lib/web/fetch/response.js +641 -0
  158. package/vendor/node_modules/undici/lib/web/fetch/util.js +1522 -0
  159. package/vendor/node_modules/undici/lib/web/infra/index.js +243 -0
  160. package/vendor/node_modules/undici/lib/web/subresource-integrity/Readme.md +9 -0
  161. package/vendor/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js +307 -0
  162. package/vendor/node_modules/undici/lib/web/webidl/index.js +1006 -0
  163. package/vendor/node_modules/undici/lib/web/websocket/connection.js +339 -0
  164. package/vendor/node_modules/undici/lib/web/websocket/constants.js +126 -0
  165. package/vendor/node_modules/undici/lib/web/websocket/events.js +331 -0
  166. package/vendor/node_modules/undici/lib/web/websocket/frame.js +133 -0
  167. package/vendor/node_modules/undici/lib/web/websocket/permessage-deflate.js +105 -0
  168. package/vendor/node_modules/undici/lib/web/websocket/receiver.js +520 -0
  169. package/vendor/node_modules/undici/lib/web/websocket/sender.js +109 -0
  170. package/vendor/node_modules/undici/lib/web/websocket/stream/websocketerror.js +104 -0
  171. package/vendor/node_modules/undici/lib/web/websocket/stream/websocketstream.js +496 -0
  172. package/vendor/node_modules/undici/lib/web/websocket/util.js +347 -0
  173. package/vendor/node_modules/undici/lib/web/websocket/websocket.js +758 -0
  174. package/vendor/node_modules/undici/package.json +152 -0
  175. package/vendor/node_modules/undici/scripts/strip-comments.js +10 -0
  176. package/vendor/node_modules/undici/types/README.md +6 -0
  177. package/vendor/node_modules/undici/types/agent.d.ts +32 -0
  178. package/vendor/node_modules/undici/types/api.d.ts +43 -0
  179. package/vendor/node_modules/undici/types/balanced-pool.d.ts +30 -0
  180. package/vendor/node_modules/undici/types/cache-interceptor.d.ts +179 -0
  181. package/vendor/node_modules/undici/types/cache.d.ts +36 -0
  182. package/vendor/node_modules/undici/types/client-stats.d.ts +15 -0
  183. package/vendor/node_modules/undici/types/client.d.ts +153 -0
  184. package/vendor/node_modules/undici/types/connector.d.ts +36 -0
  185. package/vendor/node_modules/undici/types/content-type.d.ts +21 -0
  186. package/vendor/node_modules/undici/types/cookies.d.ts +30 -0
  187. package/vendor/node_modules/undici/types/diagnostics-channel.d.ts +74 -0
  188. package/vendor/node_modules/undici/types/dispatcher.d.ts +275 -0
  189. package/vendor/node_modules/undici/types/env-http-proxy-agent.d.ts +22 -0
  190. package/vendor/node_modules/undici/types/errors.d.ts +177 -0
  191. package/vendor/node_modules/undici/types/eventsource.d.ts +66 -0
  192. package/vendor/node_modules/undici/types/fetch.d.ts +231 -0
  193. package/vendor/node_modules/undici/types/formdata.d.ts +114 -0
  194. package/vendor/node_modules/undici/types/global-dispatcher.d.ts +9 -0
  195. package/vendor/node_modules/undici/types/global-origin.d.ts +7 -0
  196. package/vendor/node_modules/undici/types/h2c-client.d.ts +73 -0
  197. package/vendor/node_modules/undici/types/handlers.d.ts +14 -0
  198. package/vendor/node_modules/undici/types/header.d.ts +160 -0
  199. package/vendor/node_modules/undici/types/index.d.ts +91 -0
  200. package/vendor/node_modules/undici/types/interceptors.d.ts +82 -0
  201. package/vendor/node_modules/undici/types/mock-agent.d.ts +68 -0
  202. package/vendor/node_modules/undici/types/mock-call-history.d.ts +111 -0
  203. package/vendor/node_modules/undici/types/mock-client.d.ts +27 -0
  204. package/vendor/node_modules/undici/types/mock-errors.d.ts +12 -0
  205. package/vendor/node_modules/undici/types/mock-interceptor.d.ts +94 -0
  206. package/vendor/node_modules/undici/types/mock-pool.d.ts +27 -0
  207. package/vendor/node_modules/undici/types/patch.d.ts +29 -0
  208. package/vendor/node_modules/undici/types/pool-stats.d.ts +19 -0
  209. package/vendor/node_modules/undici/types/pool.d.ts +41 -0
  210. package/vendor/node_modules/undici/types/proxy-agent.d.ts +29 -0
  211. package/vendor/node_modules/undici/types/readable.d.ts +68 -0
  212. package/vendor/node_modules/undici/types/retry-agent.d.ts +8 -0
  213. package/vendor/node_modules/undici/types/retry-handler.d.ts +125 -0
  214. package/vendor/node_modules/undici/types/round-robin-pool.d.ts +41 -0
  215. package/vendor/node_modules/undici/types/snapshot-agent.d.ts +109 -0
  216. package/vendor/node_modules/undici/types/socks5-proxy-agent.d.ts +25 -0
  217. package/vendor/node_modules/undici/types/util.d.ts +18 -0
  218. package/vendor/node_modules/undici/types/utility.d.ts +7 -0
  219. package/vendor/node_modules/undici/types/webidl.d.ts +347 -0
  220. package/vendor/node_modules/undici/types/websocket.d.ts +188 -0
@@ -0,0 +1,824 @@
1
+ 'use strict'
2
+
3
+ const util = require('../core/util')
4
+ const { kRedirectCacheControl } = require('../core/symbols')
5
+ const {
6
+ parseCacheControlHeader,
7
+ hasInvalidCacheControlDirective,
8
+ parseVaryHeader,
9
+ hasVaryStar,
10
+ isInvalidOrWildcardVaryHeader,
11
+ isEtagUsable
12
+ } = require('../util/cache')
13
+ const { parseHttpDate } = require('../util/date.js')
14
+
15
+ function noop () {}
16
+
17
+ /**
18
+ * @param {string | URL} a
19
+ * @param {string | URL} b
20
+ * @returns {boolean}
21
+ */
22
+ function sameOrigin (a, b) {
23
+ try {
24
+ return new URL(String(a)).origin === new URL(String(b)).origin
25
+ } catch {
26
+ return String(a) === String(b)
27
+ }
28
+ }
29
+
30
+ // Status codes that we can use some heuristics on to cache
31
+ const HEURISTICALLY_CACHEABLE_STATUS_CODES = [
32
+ 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501
33
+ ]
34
+
35
+ // Status codes which semantic is not handled by the cache
36
+ // https://datatracker.ietf.org/doc/html/rfc9111#section-3
37
+ // This list should not grow beyond 206 unless the RFC is updated
38
+ // by a newer one including more. Please introduce another list if
39
+ // implementing caching of responses with the 'must-understand' directive.
40
+ const NOT_UNDERSTOOD_STATUS_CODES = [
41
+ 206
42
+ ]
43
+
44
+ const MAX_RESPONSE_AGE = 2147483647000
45
+
46
+ function trimOWS (value) {
47
+ return value.replace(/^[\t ]+|[\t ]+$/g, '')
48
+ }
49
+
50
+ function arrayIncludes (array, value) {
51
+ for (let i = 0; i < array.length; i++) {
52
+ if (array[i] === value) {
53
+ return true
54
+ }
55
+ }
56
+
57
+ return false
58
+ }
59
+
60
+ function appendConnectionHeaderTokens (headersToRemove, connectionHeader) {
61
+ const values = Array.isArray(connectionHeader) ? connectionHeader : [connectionHeader]
62
+
63
+ for (let i = 0; i < values.length; i++) {
64
+ const tokens = values[i].split(',')
65
+ for (let j = 0; j < tokens.length; j++) {
66
+ headersToRemove.push(trimOWS(tokens[j]).toLowerCase())
67
+ }
68
+ }
69
+ }
70
+
71
+ function getSameOriginPath (cacheKey, location) {
72
+ if (typeof location !== 'string') {
73
+ return undefined
74
+ }
75
+
76
+ let originUrl
77
+ let requestUrl
78
+ let locationUrl
79
+ try {
80
+ originUrl = new URL(cacheKey.origin)
81
+ requestUrl = new URL(cacheKey.path, originUrl)
82
+ locationUrl = new URL(location, requestUrl)
83
+ } catch {
84
+ return undefined
85
+ }
86
+
87
+ if (locationUrl.origin !== originUrl.origin) {
88
+ return undefined
89
+ }
90
+
91
+ return locationUrl.pathname + locationUrl.search
92
+ }
93
+
94
+ function deleteCachedUri (store, cacheKey, path) {
95
+ deleteCachedValue(store, {
96
+ ...cacheKey,
97
+ path
98
+ })
99
+
100
+ for (let i = 0; i < util.safeHTTPMethods.length; i++) {
101
+ const method = util.safeHTTPMethods[i]
102
+ if (method !== cacheKey.method) {
103
+ deleteCachedValue(store, {
104
+ ...cacheKey,
105
+ method,
106
+ path
107
+ })
108
+ }
109
+ }
110
+ }
111
+
112
+ function deleteLocationTargets (store, cacheKey, headerValue) {
113
+ if (headerValue === undefined) {
114
+ return
115
+ }
116
+
117
+ const values = Array.isArray(headerValue) ? headerValue : [headerValue]
118
+ for (let i = 0; i < values.length; i++) {
119
+ const path = getSameOriginPath(cacheKey, values[i])
120
+ if (path !== undefined) {
121
+ deleteCachedUri(store, cacheKey, path)
122
+ }
123
+ }
124
+ }
125
+
126
+ function invalidateUnsafeRequest (store, cacheKey, resHeaders) {
127
+ deleteCachedUri(store, cacheKey, cacheKey.path)
128
+ deleteLocationTargets(store, cacheKey, resHeaders.location)
129
+ deleteLocationTargets(store, cacheKey, resHeaders['content-location'])
130
+ }
131
+
132
+ /**
133
+ * @typedef {import('../../types/dispatcher.d.ts').default.DispatchHandler} DispatchHandler
134
+ *
135
+ * @implements {DispatchHandler}
136
+ */
137
+ class CacheHandler {
138
+ /**
139
+ * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey}
140
+ */
141
+ #cacheKey
142
+
143
+ /**
144
+ * @type {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions['type']}
145
+ */
146
+ #cacheType
147
+
148
+ /**
149
+ * @type {number | undefined}
150
+ */
151
+ #cacheByDefault
152
+
153
+ /**
154
+ * @type {import('../../types/cache-interceptor.d.ts').default.CacheStore}
155
+ */
156
+ #store
157
+
158
+ /**
159
+ * @type {import('../../types/dispatcher.d.ts').default.DispatchHandler}
160
+ */
161
+ #handler
162
+
163
+ /**
164
+ * @type {import('node:stream').Writable | undefined}
165
+ */
166
+ #writeStream
167
+
168
+ /**
169
+ * The URLs visited while following redirects, shared by reference from the
170
+ * redirect handler through the request context. Carries the suppressed hops'
171
+ * cache-control under {@link kRedirectCacheControl}.
172
+ * @type {URL[] | undefined}
173
+ */
174
+ #history
175
+
176
+ /**
177
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheHandlerOptions} opts
178
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
179
+ * @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
180
+ */
181
+ constructor ({ store, type, cacheByDefault }, cacheKey, handler) {
182
+ this.#store = store
183
+ this.#cacheType = type
184
+ this.#cacheByDefault = cacheByDefault
185
+ this.#cacheKey = cacheKey
186
+ this.#handler = handler
187
+ }
188
+
189
+ onRequestStart (controller, context) {
190
+ this.#writeStream?.destroy()
191
+ this.#writeStream = undefined
192
+ // The redirect handler mutates this shared history as redirects are
193
+ // followed, allowing the cache to identify the response's actual origin.
194
+ this.#history = context?.history
195
+ this.#handler.onRequestStart?.(controller, context)
196
+ }
197
+
198
+ onRequestUpgrade (controller, statusCode, headers, socket) {
199
+ this.#handler.onRequestUpgrade?.(controller, statusCode, headers, socket)
200
+ }
201
+
202
+ /**
203
+ * @param {import('../../types/dispatcher.d.ts').default.DispatchController} controller
204
+ * @param {number} statusCode
205
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
206
+ * @param {string} statusMessage
207
+ */
208
+ onResponseStart (
209
+ controller,
210
+ statusCode,
211
+ resHeaders,
212
+ statusMessage
213
+ ) {
214
+ const downstreamOnHeaders = () =>
215
+ this.#handler.onResponseStart?.(
216
+ controller,
217
+ statusCode,
218
+ resHeaders,
219
+ statusMessage
220
+ )
221
+ const handler = this
222
+
223
+ if (
224
+ !arrayIncludes(util.safeHTTPMethods, this.#cacheKey.method) &&
225
+ statusCode >= 200 &&
226
+ statusCode <= 399
227
+ ) {
228
+ // Successful response to an unsafe method, delete it from cache
229
+ // https://www.rfc-editor.org/rfc/rfc9111.html#name-invalidating-stored-response
230
+ invalidateUnsafeRequest(this.#store, this.#cacheKey, resHeaders)
231
+ return downstreamOnHeaders()
232
+ }
233
+
234
+ // Never store a response produced by a different origin under the original
235
+ // request's cache key.
236
+ if (this.#history && this.#history.length > 0) {
237
+ const finalUrl = this.#history[this.#history.length - 1]
238
+ if (!sameOrigin(finalUrl.origin, this.#cacheKey.origin)) {
239
+ return downstreamOnHeaders()
240
+ }
241
+ }
242
+
243
+ // Preserve restrictive cache policy from suppressed redirect responses.
244
+ const redirectCacheControl = this.#history?.[kRedirectCacheControl]
245
+ if (redirectCacheControl) {
246
+ for (const hopCacheControl of redirectCacheControl) {
247
+ const hopDirectives = hopCacheControl ? parseCacheControlHeader(hopCacheControl) : {}
248
+ if (
249
+ hopDirectives['no-store'] === true ||
250
+ (this.#cacheType === 'shared' && hopDirectives.private === true)
251
+ ) {
252
+ return downstreamOnHeaders()
253
+ }
254
+ }
255
+ }
256
+
257
+ const cacheControlHeader = resHeaders['cache-control']
258
+ const cacheControlDirectives = cacheControlHeader ? parseCacheControlHeader(cacheControlHeader) : {}
259
+
260
+ if (revalidationResponseDisallowsCachedReuse(this.#cacheType, resHeaders, cacheControlDirectives)) {
261
+ deleteCachedValue(this.#store, this.#cacheKey)
262
+ return downstreamOnHeaders()
263
+ }
264
+
265
+ const heuristicallyCacheable = resHeaders['last-modified'] && arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode)
266
+ if (
267
+ !cacheControlHeader &&
268
+ !resHeaders['expires'] &&
269
+ !heuristicallyCacheable &&
270
+ !this.#cacheByDefault
271
+ ) {
272
+ if (statusCode === 304 && resHeaders.vary && isInvalidOrWildcardVaryHeader(resHeaders.vary)) {
273
+ deleteCachedValue(this.#store, this.#cacheKey)
274
+ }
275
+
276
+ // Don't have anything to tell us this response is cachable and we're not
277
+ // caching by default
278
+ return downstreamOnHeaders()
279
+ }
280
+
281
+ if (!canCacheResponse(this.#cacheType, this.#cacheKey.method, statusCode, resHeaders, cacheControlDirectives, this.#cacheKey.headers)) {
282
+ if (statusCode === 304 && (cacheControlHeader || revalidationResponseDisallowsCachedReuse(this.#cacheType, resHeaders, cacheControlDirectives))) {
283
+ deleteCachedValue(this.#store, this.#cacheKey)
284
+ }
285
+
286
+ return downstreamOnHeaders()
287
+ }
288
+
289
+ const now = Date.now()
290
+ const resAge = Object.hasOwn(resHeaders, 'age') ? getAge(resHeaders.age) : undefined
291
+ if (resAge !== undefined && resAge >= MAX_RESPONSE_AGE) {
292
+ // Response considered stale
293
+ deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey)
294
+ return downstreamOnHeaders()
295
+ }
296
+
297
+ const resDate = Object.hasOwn(resHeaders, 'date') ? getDate(resHeaders.date) : undefined
298
+ if (resDate === null) {
299
+ deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey)
300
+ return downstreamOnHeaders()
301
+ }
302
+
303
+ const apparentAge = resDate ? Math.max(0, now - resDate.getTime()) : 0
304
+ const currentAge = Math.max(apparentAge, resAge ?? 0)
305
+
306
+ const staleAt =
307
+ determineStaleAt(this.#cacheType, now, resAge, resHeaders, resDate, cacheControlDirectives) ??
308
+ this.#cacheByDefault
309
+ if (staleAt === undefined || currentAge >= staleAt) {
310
+ if (cacheControlHeader || staleAt !== undefined) {
311
+ deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey)
312
+ }
313
+
314
+ return downstreamOnHeaders()
315
+ }
316
+
317
+ const baseTime = now - currentAge
318
+ const absoluteStaleAt = staleAt + baseTime
319
+ if (now >= absoluteStaleAt) {
320
+ // Response is already stale
321
+ deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey)
322
+ return downstreamOnHeaders()
323
+ }
324
+
325
+ let varyDirectives
326
+ if (this.#cacheKey.headers && resHeaders.vary) {
327
+ varyDirectives = parseVaryHeader(resHeaders.vary, this.#cacheKey.headers)
328
+ if (!varyDirectives) {
329
+ // Parse error
330
+ return downstreamOnHeaders()
331
+ }
332
+ }
333
+
334
+ const cachedAt = baseTime
335
+ const deleteAt = determineDeleteAt(baseTime, now, cacheControlDirectives, absoluteStaleAt)
336
+ const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives)
337
+
338
+ /**
339
+ * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
340
+ */
341
+ const value = {
342
+ statusCode,
343
+ statusMessage,
344
+ headers: strippedHeaders,
345
+ vary: varyDirectives,
346
+ cacheControlDirectives,
347
+ cachedAt,
348
+ staleAt: absoluteStaleAt,
349
+ deleteAt
350
+ }
351
+
352
+ // Not modified, re-use the cached value
353
+ // https://www.rfc-editor.org/rfc/rfc9111.html#name-handling-304-not-modified
354
+ if (statusCode === 304) {
355
+ const handle304 = (cachedValue) => {
356
+ if (!cachedValue) {
357
+ // Do not create a new cache entry, as a 304 won't have a body - so cannot be cached.
358
+ return downstreamOnHeaders()
359
+ }
360
+
361
+ // Re-use the cached value: statuscode, statusmessage, headers and body
362
+ value.statusCode = cachedValue.statusCode
363
+ value.statusMessage = cachedValue.statusMessage
364
+ value.etag = cachedValue.etag
365
+ value.vary = varyDirectives ?? cachedValue.vary
366
+ value.headers = { ...cachedValue.headers, ...strippedHeaders }
367
+
368
+ downstreamOnHeaders()
369
+
370
+ this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
371
+
372
+ if (!this.#writeStream || !cachedValue?.body) {
373
+ return
374
+ }
375
+
376
+ if (typeof cachedValue.body.values === 'function') {
377
+ const bodyIterator = cachedValue.body.values()
378
+
379
+ const streamCachedBody = () => {
380
+ for (const chunk of bodyIterator) {
381
+ const full = this.#writeStream.write(chunk) === false
382
+ this.#handler.onResponseData?.(controller, chunk)
383
+ // when stream is full stop writing until we get a 'drain' event
384
+ if (full) {
385
+ break
386
+ }
387
+ }
388
+ }
389
+
390
+ this.#writeStream
391
+ .on('error', function () {
392
+ handler.#writeStream = undefined
393
+ handler.#store.delete(handler.#cacheKey)
394
+ })
395
+ .on('drain', () => {
396
+ streamCachedBody()
397
+ })
398
+ .on('close', function () {
399
+ if (handler.#writeStream === this) {
400
+ handler.#writeStream = undefined
401
+ }
402
+ })
403
+
404
+ streamCachedBody()
405
+ } else if (typeof cachedValue.body.on === 'function') {
406
+ // Readable stream body (e.g. from async/remote cache stores)
407
+ cachedValue.body
408
+ .on('data', (chunk) => {
409
+ this.#writeStream.write(chunk)
410
+ this.#handler.onResponseData?.(controller, chunk)
411
+ })
412
+ .on('end', () => {
413
+ this.#writeStream.end()
414
+ })
415
+ .on('error', () => {
416
+ this.#writeStream = undefined
417
+ this.#store.delete(this.#cacheKey)
418
+ })
419
+
420
+ this.#writeStream
421
+ .on('error', function () {
422
+ handler.#writeStream = undefined
423
+ handler.#store.delete(handler.#cacheKey)
424
+ })
425
+ .on('close', function () {
426
+ if (handler.#writeStream === this) {
427
+ handler.#writeStream = undefined
428
+ }
429
+ })
430
+ }
431
+ }
432
+
433
+ /**
434
+ * @type {import('../../types/cache-interceptor.d.ts').default.CacheValue}
435
+ */
436
+ const result = this.#store.get(this.#cacheKey)
437
+ if (result && typeof result.then === 'function') {
438
+ result.then(handle304)
439
+ } else {
440
+ handle304(result)
441
+ }
442
+ } else {
443
+ if (typeof resHeaders.etag === 'string' && isEtagUsable(resHeaders.etag)) {
444
+ value.etag = resHeaders.etag
445
+ }
446
+
447
+ this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value)
448
+
449
+ if (!this.#writeStream) {
450
+ return downstreamOnHeaders()
451
+ }
452
+
453
+ this.#writeStream
454
+ .on('drain', () => controller.resume())
455
+ .on('error', function () {
456
+ // TODO (fix): Make error somehow observable?
457
+ handler.#writeStream = undefined
458
+
459
+ // Delete the value in case the cache store is holding onto state from
460
+ // the call to createWriteStream
461
+ handler.#store.delete(handler.#cacheKey)
462
+ })
463
+ .on('close', function () {
464
+ if (handler.#writeStream === this) {
465
+ handler.#writeStream = undefined
466
+ }
467
+
468
+ // TODO (fix): Should we resume even if was paused downstream?
469
+ controller.resume()
470
+ })
471
+
472
+ downstreamOnHeaders()
473
+ }
474
+ }
475
+
476
+ onResponseData (controller, chunk) {
477
+ if (this.#writeStream?.write(chunk) === false) {
478
+ controller.pause()
479
+ }
480
+
481
+ this.#handler.onResponseData?.(controller, chunk)
482
+ }
483
+
484
+ onResponseEnd (controller, trailers) {
485
+ this.#writeStream?.end()
486
+ this.#handler.onResponseEnd?.(controller, trailers)
487
+ }
488
+
489
+ onResponseError (controller, err) {
490
+ this.#writeStream?.destroy(err)
491
+ this.#writeStream = undefined
492
+ this.#handler.onResponseError?.(controller, err)
493
+ }
494
+ }
495
+
496
+ /**
497
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheStore} store
498
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
499
+ */
500
+ function deleteCachedValue (store, cacheKey) {
501
+ try {
502
+ store.delete(cacheKey)?.catch?.(noop)
503
+ } catch {
504
+ // Fail silently
505
+ }
506
+ }
507
+
508
+ function deleteCachedValueIfNotModified (statusCode, store, cacheKey) {
509
+ if (statusCode === 304) {
510
+ deleteCachedValue(store, cacheKey)
511
+ }
512
+ }
513
+
514
+ /**
515
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
516
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
517
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
518
+ * @returns {boolean}
519
+ */
520
+ function revalidationResponseDisallowsCachedReuse (cacheType, resHeaders, cacheControlDirectives) {
521
+ return cacheControlDirectives['no-store'] === true ||
522
+ (cacheType === 'shared' && (
523
+ cacheControlDirectives.private === true ||
524
+ Object.hasOwn(resHeaders, 'set-cookie')
525
+ )) ||
526
+ (resHeaders.vary ? isInvalidOrWildcardVaryHeader(resHeaders.vary) : false)
527
+ }
528
+
529
+ /**
530
+ * @see https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
531
+ *
532
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
533
+ * @param {string} method
534
+ * @param {number} statusCode
535
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
536
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
537
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} [reqHeaders]
538
+ */
539
+ function canCacheResponse (cacheType, method, statusCode, resHeaders, cacheControlDirectives, reqHeaders) {
540
+ if (!arrayIncludes(util.safeHTTPMethods, method)) {
541
+ return false
542
+ }
543
+ // Status code must be final and understood.
544
+ if (statusCode < 200 || arrayIncludes(NOT_UNDERSTOOD_STATUS_CODES, statusCode)) {
545
+ return false
546
+ }
547
+ // Responses with neither status codes that are heuristically cacheable, nor "explicit enough" caching
548
+ // directives, are not cacheable. "Explicit enough": see https://www.rfc-editor.org/rfc/rfc9111.html#section-3
549
+ if (!arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode) && !resHeaders['expires'] &&
550
+ !cacheControlDirectives.public &&
551
+ cacheControlDirectives['max-age'] === undefined &&
552
+ // RFC 9111: a private response directive, if the cache is not shared
553
+ !(cacheControlDirectives.private && cacheType === 'private') &&
554
+ !(cacheControlDirectives['s-maxage'] !== undefined && cacheType === 'shared')
555
+ ) {
556
+ return false
557
+ }
558
+
559
+ if (cacheControlDirectives['no-store']) {
560
+ return false
561
+ }
562
+
563
+ if (cacheType === 'shared' && (
564
+ cacheControlDirectives.private === true ||
565
+ Object.hasOwn(resHeaders, 'set-cookie')
566
+ )) {
567
+ return false
568
+ }
569
+
570
+ // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1-5
571
+ if (resHeaders.vary && hasVaryStar(resHeaders.vary)) {
572
+ return false
573
+ }
574
+
575
+ // https://www.rfc-editor.org/rfc/rfc9111.html#name-storing-responses-to-authen
576
+ if (reqHeaders != null && Object.hasOwn(reqHeaders, 'authorization')) {
577
+ if (
578
+ !cacheControlDirectives.public &&
579
+ !cacheControlDirectives['s-maxage'] &&
580
+ !cacheControlDirectives['must-revalidate']
581
+ ) {
582
+ return false
583
+ }
584
+
585
+ if (typeof reqHeaders.authorization !== 'string') {
586
+ return false
587
+ }
588
+
589
+ if (
590
+ Array.isArray(cacheControlDirectives['no-cache']) &&
591
+ arrayIncludes(cacheControlDirectives['no-cache'], 'authorization')
592
+ ) {
593
+ return false
594
+ }
595
+
596
+ if (
597
+ Array.isArray(cacheControlDirectives['private']) &&
598
+ arrayIncludes(cacheControlDirectives['private'], 'authorization')
599
+ ) {
600
+ return false
601
+ }
602
+ }
603
+
604
+ return true
605
+ }
606
+
607
+ /**
608
+ * @param {string | string[]} dateHeader
609
+ * @returns {Date | null | undefined}
610
+ */
611
+ function getDate (dateHeader) {
612
+ let dateValue = dateHeader
613
+ if (Array.isArray(dateValue)) {
614
+ if (dateValue.length !== 1) {
615
+ return null
616
+ }
617
+
618
+ dateValue = dateValue[0]
619
+ }
620
+
621
+ if (typeof dateValue !== 'string') {
622
+ return null
623
+ }
624
+
625
+ return parseHttpDate(dateValue)
626
+ }
627
+
628
+ /**
629
+ * @param {string | string[]} ageHeader
630
+ * @returns {number | undefined}
631
+ */
632
+ function getAge (ageHeader) {
633
+ let ageValue = ageHeader
634
+ if (Array.isArray(ageValue)) {
635
+ if (ageValue.length !== 1) {
636
+ return MAX_RESPONSE_AGE
637
+ }
638
+
639
+ ageValue = ageValue[0]
640
+ }
641
+
642
+ if (typeof ageValue !== 'string' || !/^[\t ]*[0-9]+[\t ]*$/.test(ageValue)) {
643
+ return MAX_RESPONSE_AGE
644
+ }
645
+
646
+ const age = BigInt(ageValue.replace(/^[\t ]+|[\t ]+$/g, ''))
647
+ if (age >= BigInt(MAX_RESPONSE_AGE / 1000)) {
648
+ return MAX_RESPONSE_AGE
649
+ }
650
+
651
+ return Number(age) * 1000
652
+ }
653
+
654
+ /**
655
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheOptions['type']} cacheType
656
+ * @param {number} now
657
+ * @param {number | undefined} age
658
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
659
+ * @param {Date | undefined} responseDate
660
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
661
+ *
662
+ * @returns {number | undefined} time that the value is stale at in seconds or undefined if it shouldn't be cached
663
+ */
664
+ function determineStaleAt (cacheType, now, age, resHeaders, responseDate, cacheControlDirectives) {
665
+ if (cacheType === 'shared') {
666
+ // Prioritize s-maxage since we're a shared cache
667
+ // s-maxage > max-age > Expire
668
+ // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10-3
669
+ if (hasInvalidCacheControlDirective(cacheControlDirectives, 's-maxage')) {
670
+ return 0
671
+ }
672
+
673
+ const sMaxAge = cacheControlDirectives['s-maxage']
674
+ if (sMaxAge !== undefined) {
675
+ return sMaxAge * 1000
676
+ }
677
+ }
678
+
679
+ if (hasInvalidCacheControlDirective(cacheControlDirectives, 'max-age')) {
680
+ return 0
681
+ }
682
+
683
+ const maxAge = cacheControlDirectives['max-age']
684
+ if (maxAge !== undefined) {
685
+ return maxAge * 1000
686
+ }
687
+
688
+ if (Object.hasOwn(resHeaders, 'expires')) {
689
+ // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
690
+ if (typeof resHeaders.expires !== 'string') {
691
+ return 0
692
+ }
693
+
694
+ const expiresDate = parseHttpDate(resHeaders.expires)
695
+ if (!expiresDate) {
696
+ return 0
697
+ }
698
+
699
+ if (now >= expiresDate.getTime()) {
700
+ return 0
701
+ }
702
+
703
+ if (responseDate) {
704
+ if (responseDate >= expiresDate) {
705
+ return 0
706
+ }
707
+
708
+ const freshnessLifetime = expiresDate.getTime() - responseDate.getTime()
709
+ if (age !== undefined && age >= freshnessLifetime) {
710
+ return 0
711
+ }
712
+
713
+ return freshnessLifetime
714
+ }
715
+
716
+ return expiresDate.getTime() - now
717
+ }
718
+
719
+ if (typeof resHeaders['last-modified'] === 'string') {
720
+ // https://www.rfc-editor.org/rfc/rfc9111.html#name-calculating-heuristic-fresh
721
+ const lastModified = parseHttpDate(resHeaders['last-modified'])
722
+ if (lastModified) {
723
+ if (lastModified.getTime() >= now) {
724
+ return undefined
725
+ }
726
+
727
+ const responseAge = now - lastModified.getTime()
728
+
729
+ return responseAge * 0.1
730
+ }
731
+ }
732
+
733
+ if (cacheControlDirectives.immutable) {
734
+ // https://www.rfc-editor.org/rfc/rfc8246.html#section-2.2
735
+ return 31536000000
736
+ }
737
+
738
+ return undefined
739
+ }
740
+
741
+ /**
742
+ * @param {number} baseTime
743
+ * @param {number} cachedAt
744
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
745
+ * @param {number} staleAt
746
+ */
747
+ function determineDeleteAt (baseTime, cachedAt, cacheControlDirectives, staleAt) {
748
+ let staleWhileRevalidate = -Infinity
749
+ let staleIfError = -Infinity
750
+ let immutable = -Infinity
751
+
752
+ if (cacheControlDirectives['stale-while-revalidate']) {
753
+ staleWhileRevalidate = staleAt + (cacheControlDirectives['stale-while-revalidate'] * 1000)
754
+ }
755
+
756
+ if (cacheControlDirectives['stale-if-error']) {
757
+ staleIfError = staleAt + (cacheControlDirectives['stale-if-error'] * 1000)
758
+ }
759
+
760
+ if (cacheControlDirectives.immutable && staleWhileRevalidate === -Infinity && staleIfError === -Infinity) {
761
+ immutable = cachedAt + 31536000000
762
+ }
763
+
764
+ // When no stale directives or immutable flag, add a revalidation buffer
765
+ // equal to the freshness lifetime so the entry survives past staleAt long
766
+ // enough to be revalidated instead of silently disappearing.
767
+ //
768
+ // Response Date headers only have second precision, so baseTime can trail the
769
+ // actual cache insertion time by up to ~1s. Pad the buffer by that bounded
770
+ // skew so short-lived entries do not disappear exactly when they should be
771
+ // revalidated.
772
+ if (staleWhileRevalidate === -Infinity && staleIfError === -Infinity && immutable === -Infinity) {
773
+ const freshnessLifetime = staleAt - baseTime
774
+ const datePrecisionPadding = Math.min(Math.max(cachedAt - baseTime, 0), 1000)
775
+ return staleAt + freshnessLifetime + datePrecisionPadding
776
+ }
777
+
778
+ return Math.max(staleAt, staleWhileRevalidate, staleIfError, immutable)
779
+ }
780
+
781
+ /**
782
+ * Strips headers required to be removed in cached responses
783
+ * @param {import('../../types/header.d.ts').IncomingHttpHeaders} resHeaders
784
+ * @param {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives} cacheControlDirectives
785
+ * @returns {Record<string, string | string []>}
786
+ */
787
+ function stripNecessaryHeaders (resHeaders, cacheControlDirectives) {
788
+ const headersToRemove = [
789
+ 'connection',
790
+ 'proxy-authenticate',
791
+ 'proxy-authentication-info',
792
+ 'proxy-authorization',
793
+ 'proxy-connection',
794
+ 'te',
795
+ 'transfer-encoding',
796
+ 'upgrade',
797
+ // We'll add age back when serving it
798
+ 'age'
799
+ ]
800
+
801
+ if (resHeaders['connection']) {
802
+ appendConnectionHeaderTokens(headersToRemove, resHeaders['connection'])
803
+ }
804
+
805
+ if (Array.isArray(cacheControlDirectives['no-cache'])) {
806
+ headersToRemove.push(...cacheControlDirectives['no-cache'])
807
+ }
808
+
809
+ if (Array.isArray(cacheControlDirectives['private'])) {
810
+ headersToRemove.push(...cacheControlDirectives['private'])
811
+ }
812
+
813
+ let strippedHeaders
814
+ for (const headerName of headersToRemove) {
815
+ if (Object.hasOwn(resHeaders, headerName)) {
816
+ strippedHeaders ??= { ...resHeaders }
817
+ delete strippedHeaders[headerName]
818
+ }
819
+ }
820
+
821
+ return strippedHeaders ?? resHeaders
822
+ }
823
+
824
+ module.exports = CacheHandler