@openclaw/discord 2026.6.33 → 2026.6.35

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 (179) hide show
  1. package/dist/action-runtime-api.js +1 -1
  2. package/dist/{api-CjnGBhM3.js → api-1GXlSrYP.js} +42 -21
  3. package/dist/api.js +14 -14
  4. package/dist/{approval-handler.runtime-BWPQrzpP.js → approval-handler.runtime-Dv4p7REU.js} +3 -3
  5. package/dist/{audit-BwJ_2OjL.js → audit-DEVi9nIs.js} +3 -3
  6. package/dist/{channel-Ddnonoy4.js → channel-CQIRiosn.js} +15 -15
  7. package/dist/{channel-actions-CwmIqnYN.js → channel-actions-JziPEcBU.js} +2 -2
  8. package/dist/{channel-actions.runtime-DvpTfaJ4.js → channel-actions.runtime-D7L47LY3.js} +5 -5
  9. package/dist/channel-plugin-api.js +1 -1
  10. package/dist/{channel.setup-glZ5Y98a.js → channel.setup-oMeR19Cc.js} +2 -2
  11. package/dist/{components-j6lTZBsT.js → components-_s5UPJqU.js} +1 -1
  12. package/dist/{conversation-identity-36FFBwm5.js → conversation-identity-B0k94-bz.js} +2 -2
  13. package/dist/{directory-config-BYMFzL29.js → directory-config-CD1svkyN.js} +1 -1
  14. package/dist/directory-contract-api.js +1 -1
  15. package/dist/{directory-live-9JtJCiu8.js → directory-live-DBTsSRxl.js} +5 -5
  16. package/dist/{handle-action.guild-admin-xxGYrp-I.js → handle-action.guild-admin-CYIFMxh-.js} +2 -2
  17. package/dist/index.js +1 -1
  18. package/dist/{manager.runtime-BK6cf2Zu.js → manager.runtime-BZVdGX2d.js} +17 -9
  19. package/dist/{message-handler-BqpJIavO.js → message-handler-DZd9Bpq8.js} +7 -6
  20. package/dist/{message-handler.preflight-C90MxCcv.js → message-handler.preflight-Axv9J9EO.js} +12 -11
  21. package/dist/{message-handler.process-CeQ7Bd_3.js → message-handler.process-CG0zFS-Z.js} +10 -10
  22. package/dist/{message-utils-CJeAoISH.js → message-utils-Dbuw27gd.js} +1 -1
  23. package/dist/{outbound-adapter-Dxs6iKEq.js → outbound-adapter-CKC_kmUN.js} +6 -6
  24. package/dist/pluralkit-CfTReLl3.js +40 -0
  25. package/dist/{probe-esE_Oqdm.js → probe-DcgZIpyF.js} +3 -2
  26. package/dist/{probe.runtime-C3Atgpvr.js → probe.runtime-CVwM9L5E.js} +1 -1
  27. package/dist/{provider-C7376XFH.js → provider-CwwSXIIj.js} +102 -37
  28. package/dist/{provider-session.runtime-CCdqogGf.js → provider-session.runtime-reBWkqHZ.js} +3 -3
  29. package/dist/provider.runtime-BsmTLYS_.js +2 -0
  30. package/dist/{resolve-allowlist-common-3gB2Gpmm.js → resolve-allowlist-common-D7bZqAw3.js} +3 -3
  31. package/dist/{resolve-channels-B3gQf8QL.js → resolve-channels--oE-RnO6.js} +6 -6
  32. package/dist/{resolve-users-BO10y5fJ.js → resolve-users-QXo8CXBU.js} +5 -5
  33. package/dist/{runtime-CFlR3L9X.js → runtime-CF7ramAs.js} +7 -7
  34. package/dist/runtime-api.actions.js +2 -2
  35. package/dist/runtime-api.js +19 -19
  36. package/dist/runtime-api.lookup.js +5 -5
  37. package/dist/runtime-api.monitor-CzF8MUl_.js +6 -0
  38. package/dist/runtime-api.monitor.js +5 -5
  39. package/dist/runtime-api.send.js +5 -5
  40. package/dist/runtime-api.threads.js +3 -3
  41. package/dist/{send-JHxfT4Pk.js → send-Bb295iHT.js} +5 -5
  42. package/dist/{send.components-CN35E2Ck.js → send.components-COkDUO3B.js} +4 -4
  43. package/dist/{send.outbound-B-cA1Esv.js → send.outbound-B_sk0hNe.js} +3 -3
  44. package/dist/{send.receipt-CGecJDin.js → send.receipt-Cli6_9Sn.js} +19 -7
  45. package/dist/{send.shared-vcbfThdy.js → send.shared-BCG7MWnP.js} +2 -2
  46. package/dist/session-binding-contract-api.js +1 -1
  47. package/dist/setup-plugin-api.js +1 -1
  48. package/dist/{subagent-hooks-20wXhVzZ.js → subagent-hooks-CtqoWK4x.js} +2 -2
  49. package/dist/subagent-hooks-api.js +1 -1
  50. package/dist/{system-events-DR27TE7p.js → system-events-BxGNk_wE.js} +1 -1
  51. package/dist/{target-resolver-CDi1gI90.js → target-resolver-BcoK6b0g.js} +2 -2
  52. package/dist/targets-Q-ZoP7K4.js +3 -0
  53. package/dist/{thread-bindings-DU4T6nDo.js → thread-bindings-DJ_HT6GJ.js} +4 -4
  54. package/dist/{thread-bindings.discord-api-CYkr-3Gj.js → thread-bindings.discord-api-OPyjvgyz.js} +4 -4
  55. package/dist/{thread-bindings.manager-BqWs1UE2.js → thread-bindings.manager-Cd2HAVw_.js} +3 -3
  56. package/dist/{transcripts-source-D-lBI5ZD.js → transcripts-source-CPf5gURy.js} +1 -1
  57. package/dist/transcripts-source-api.js +1 -1
  58. package/dist/{typing-xr05bL-r.js → typing-DY0cwLdg.js} +1 -1
  59. package/node_modules/undici/README.md +8 -2
  60. package/node_modules/undici/docs/docs/api/Agent.md +248 -44
  61. package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
  62. package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
  63. package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
  64. package/node_modules/undici/docs/docs/api/Client.md +386 -179
  65. package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
  66. package/node_modules/undici/docs/docs/api/Connector.md +118 -36
  67. package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
  68. package/node_modules/undici/docs/docs/api/Cookies.md +122 -71
  69. package/node_modules/undici/docs/docs/api/Debug.md +34 -18
  70. package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
  71. package/node_modules/undici/docs/docs/api/Dispatcher.md +485 -1102
  72. package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +102 -87
  73. package/node_modules/undici/docs/docs/api/Errors.md +503 -40
  74. package/node_modules/undici/docs/docs/api/EventSource.md +298 -32
  75. package/node_modules/undici/docs/docs/api/Fetch.md +680 -31
  76. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -98
  77. package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
  78. package/node_modules/undici/docs/docs/api/Interceptors.md +418 -0
  79. package/node_modules/undici/docs/docs/api/MockAgent.md +333 -314
  80. package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
  81. package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
  82. package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
  83. package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
  84. package/node_modules/undici/docs/docs/api/MockPool.md +321 -258
  85. package/node_modules/undici/docs/docs/api/Pool.md +235 -43
  86. package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
  87. package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
  88. package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -63
  89. package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -26
  90. package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -77
  91. package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
  92. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +264 -542
  93. package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +177 -196
  94. package/node_modules/undici/docs/docs/api/Util.md +68 -11
  95. package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
  96. package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
  97. package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
  98. package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
  99. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +6 -4
  100. package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
  101. package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
  102. package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
  103. package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
  104. package/node_modules/undici/docs/docs/{GettingStarted.md → getting-started.md} +22 -19
  105. package/node_modules/undici/docs/docs/index.md +784 -0
  106. package/node_modules/undici/docs/docs/site.json +125 -0
  107. package/node_modules/undici/docs/docs/type-map.json +79 -0
  108. package/node_modules/undici/lib/api/api-request.js +7 -1
  109. package/node_modules/undici/lib/api/readable.js +42 -7
  110. package/node_modules/undici/lib/cache/memory-cache-store.js +56 -11
  111. package/node_modules/undici/lib/cache/sqlite-cache-store.js +7 -1
  112. package/node_modules/undici/lib/core/connect.js +16 -2
  113. package/node_modules/undici/lib/core/errors.js +20 -0
  114. package/node_modules/undici/lib/core/request.js +14 -3
  115. package/node_modules/undici/lib/core/symbols.js +3 -0
  116. package/node_modules/undici/lib/core/util.js +30 -2
  117. package/node_modules/undici/lib/dispatcher/agent.js +10 -5
  118. package/node_modules/undici/lib/dispatcher/balanced-pool.js +7 -4
  119. package/node_modules/undici/lib/dispatcher/client-h1.js +74 -9
  120. package/node_modules/undici/lib/dispatcher/client-h2.js +483 -151
  121. package/node_modules/undici/lib/dispatcher/client.js +76 -30
  122. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +13 -1
  123. package/node_modules/undici/lib/dispatcher/dispatcher.js +15 -1
  124. package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +3 -0
  125. package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +37 -8
  126. package/node_modules/undici/lib/dispatcher/proxy-agent.js +58 -7
  127. package/node_modules/undici/lib/dispatcher/retry-agent.js +3 -0
  128. package/node_modules/undici/lib/dispatcher/round-robin-pool.js +1 -1
  129. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +106 -22
  130. package/node_modules/undici/lib/global.js +36 -16
  131. package/node_modules/undici/lib/handler/cache-handler.js +297 -65
  132. package/node_modules/undici/lib/handler/cache-revalidation-handler.js +13 -3
  133. package/node_modules/undici/lib/handler/decorator-handler.js +7 -1
  134. package/node_modules/undici/lib/handler/deduplication-handler.js +12 -6
  135. package/node_modules/undici/lib/handler/redirect-handler.js +26 -5
  136. package/node_modules/undici/lib/handler/retry-handler.js +238 -51
  137. package/node_modules/undici/lib/interceptor/cache.js +221 -58
  138. package/node_modules/undici/lib/interceptor/decompress.js +124 -15
  139. package/node_modules/undici/lib/interceptor/deduplicate.js +5 -4
  140. package/node_modules/undici/lib/interceptor/dns.js +7 -0
  141. package/node_modules/undici/lib/interceptor/dump.js +10 -23
  142. package/node_modules/undici/lib/mock/mock-agent.js +13 -1
  143. package/node_modules/undici/lib/mock/mock-interceptor.js +25 -7
  144. package/node_modules/undici/lib/mock/mock-utils.js +246 -27
  145. package/node_modules/undici/lib/util/cache.js +415 -71
  146. package/node_modules/undici/lib/util/date.js +23 -6
  147. package/node_modules/undici/lib/web/cookies/parse.js +150 -146
  148. package/node_modules/undici/lib/web/cookies/util.js +79 -9
  149. package/node_modules/undici/lib/web/eventsource/eventsource-stream.js +28 -1
  150. package/node_modules/undici/lib/web/eventsource/eventsource.js +13 -3
  151. package/node_modules/undici/lib/web/eventsource/util.js +1 -1
  152. package/node_modules/undici/lib/web/fetch/body.js +2 -1
  153. package/node_modules/undici/lib/web/fetch/constants.js +1 -1
  154. package/node_modules/undici/lib/web/fetch/index.js +17 -2
  155. package/node_modules/undici/lib/web/fetch/request.js +2 -3
  156. package/node_modules/undici/lib/web/fetch/response.js +1 -3
  157. package/node_modules/undici/lib/web/fetch/util.js +10 -2
  158. package/node_modules/undici/lib/web/webidl/index.js +3 -3
  159. package/node_modules/undici/lib/web/websocket/connection.js +1 -1
  160. package/node_modules/undici/lib/web/websocket/permessage-deflate.js +5 -0
  161. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -10
  162. package/node_modules/undici/lib/web/websocket/websocket.js +22 -0
  163. package/node_modules/undici/package.json +4 -4
  164. package/node_modules/undici/types/client.d.ts +53 -3
  165. package/node_modules/undici/types/cookies.d.ts +1 -1
  166. package/node_modules/undici/types/dispatcher.d.ts +3 -3
  167. package/node_modules/undici/types/errors.d.ts +10 -0
  168. package/node_modules/undici/types/handlers.d.ts +2 -0
  169. package/node_modules/undici/types/interceptors.d.ts +2 -0
  170. package/node_modules/undici/types/mock-agent.d.ts +11 -10
  171. package/node_modules/undici/types/mock-interceptor.d.ts +5 -1
  172. package/node_modules/undici/types/proxy-agent.d.ts +7 -0
  173. package/node_modules/undici/types/socks5-proxy-agent.d.ts +2 -0
  174. package/npm-shrinkwrap.json +7 -7
  175. package/package.json +6 -5
  176. package/dist/pluralkit-DI6RYaVj.js +0 -24
  177. package/dist/provider.runtime-D1m66kDS.js +0 -2
  178. package/dist/runtime-api.monitor-4oc58TSk.js +0 -6
  179. package/dist/targets-C6yd3y8R.js +0 -3
@@ -3,16 +3,190 @@
3
3
  const {
4
4
  safeHTTPMethods,
5
5
  pathHasQueryOrFragment,
6
- hasSafeIterator
6
+ hasSafeIterator,
7
+ isValidHTTPToken
7
8
  } = require('../core/util')
8
9
 
9
10
  const { serializePathWithQuery } = require('../core/util')
11
+ const { kRequestOrigin } = require('../core/symbols')
12
+
13
+ const MAX_DELTA_SECONDS = 2147483647
14
+ const RESTRICTIVE_DIRECTIVE_NAMES = ['no-store', 'private', 'no-cache']
15
+ const kInvalidCacheControlDirectives = Symbol('invalid cache-control directives')
16
+
17
+ function trimOWS (value) {
18
+ return value.replace(/^[\t ]+|[\t ]+$/g, '')
19
+ }
20
+
21
+ function arrayIncludes (array, value) {
22
+ for (let i = 0; i < array.length; i++) {
23
+ if (array[i] === value) {
24
+ return true
25
+ }
26
+ }
27
+
28
+ return false
29
+ }
30
+
31
+ function trimOWSStart (value) {
32
+ return value.replace(/^[\t ]+/, '')
33
+ }
34
+
35
+ function trimOWSEnd (value) {
36
+ return value.replace(/[\t ]+$/, '')
37
+ }
38
+
39
+ function findUnescapedQuote (value, start) {
40
+ let escaped = false
41
+ for (let i = start; i < value.length; i++) {
42
+ if (escaped) {
43
+ escaped = false
44
+ } else if (value[i] === '\\') {
45
+ escaped = true
46
+ } else if (value[i] === '"') {
47
+ return i
48
+ }
49
+ }
50
+
51
+ return -1
52
+ }
53
+
54
+ function splitCacheControlHeaderValue (value) {
55
+ const directives = []
56
+ let start = 0
57
+ let quoteStart = -1
58
+ let inQuote = false
59
+ let escaped = false
60
+
61
+ for (let i = 0; i < value.length; i++) {
62
+ if (inQuote) {
63
+ if (escaped) {
64
+ escaped = false
65
+ } else if (value[i] === '\\') {
66
+ escaped = true
67
+ } else if (value[i] === '"') {
68
+ inQuote = false
69
+ quoteStart = -1
70
+ }
71
+ } else if (value[i] === '"') {
72
+ inQuote = true
73
+ quoteStart = i
74
+ } else if (value[i] === ',') {
75
+ directives.push({ value: value.substring(start, i), fromMalformedQuote: false })
76
+ start = i + 1
77
+ }
78
+ }
79
+
80
+ if (!inQuote) {
81
+ directives.push({ value: value.substring(start), fromMalformedQuote: false })
82
+ return directives
83
+ }
84
+
85
+ const tail = value.substring(start)
86
+ const quoteOffset = quoteStart - start
87
+ let tailStart = 0
88
+ for (let i = 0; i < tail.length; i++) {
89
+ if (tail[i] === ',') {
90
+ directives.push({
91
+ value: tail.substring(tailStart, i),
92
+ fromMalformedQuote: tailStart > quoteOffset
93
+ })
94
+ tailStart = i + 1
95
+ }
96
+ }
97
+
98
+ directives.push({
99
+ value: tail.substring(tailStart),
100
+ fromMalformedQuote: tailStart > quoteOffset
101
+ })
102
+ return directives
103
+ }
104
+
105
+ function markInvalidCacheControlDirective (directives, key) {
106
+ let invalidDirectives = directives[kInvalidCacheControlDirectives]
107
+
108
+ if (invalidDirectives === undefined) {
109
+ invalidDirectives = new Set()
110
+ Object.defineProperty(directives, kInvalidCacheControlDirectives, {
111
+ value: invalidDirectives
112
+ })
113
+ }
114
+
115
+ invalidDirectives.add(key)
116
+ }
117
+
118
+ function hasInvalidCacheControlDirective (directives, key) {
119
+ return directives[kInvalidCacheControlDirectives]?.has(key) === true
120
+ }
121
+
122
+ function getMalformedRestrictiveDirectiveName (key) {
123
+ for (const directiveName of RESTRICTIVE_DIRECTIVE_NAMES) {
124
+ if (
125
+ key.startsWith(directiveName) &&
126
+ key.length > directiveName.length &&
127
+ !isValidHTTPToken(key[directiveName.length])
128
+ ) {
129
+ return directiveName
130
+ }
131
+ }
132
+
133
+ let tokenOnlyKey = ''
134
+ let hasInvalidTokenChar = false
135
+ for (let i = 0; i < key.length; i++) {
136
+ if (isValidHTTPToken(key[i])) {
137
+ tokenOnlyKey += key[i]
138
+ } else {
139
+ hasInvalidTokenChar = true
140
+ }
141
+ }
142
+
143
+ if (hasInvalidTokenChar && arrayIncludes(RESTRICTIVE_DIRECTIVE_NAMES, tokenOnlyKey)) {
144
+ return tokenOnlyKey
145
+ }
146
+ }
10
147
 
11
148
  /**
12
149
  * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts
13
150
  */
14
- function makeCacheKey (opts) {
15
- if (!opts.origin) {
151
+ function getRequestOrigin (opts) {
152
+ const origin = opts[kRequestOrigin] === undefined
153
+ ? opts.origin
154
+ : opts[kRequestOrigin]
155
+ return typeof origin === 'string' || origin instanceof URL
156
+ ? origin
157
+ : null
158
+ }
159
+
160
+ /**
161
+ * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts
162
+ * @param {string|null|undefined} interceptorOrigin
163
+ */
164
+ function getInterceptorOrigin (opts, interceptorOrigin) {
165
+ const requestOrigin = getRequestOrigin(opts)
166
+ if (interceptorOrigin === undefined) {
167
+ return requestOrigin
168
+ }
169
+ if (interceptorOrigin === null) {
170
+ return null
171
+ }
172
+ if (requestOrigin) {
173
+ try {
174
+ if (new URL(requestOrigin).origin !== interceptorOrigin) {
175
+ return null
176
+ }
177
+ } catch {
178
+ return interceptorOrigin
179
+ }
180
+ }
181
+ return interceptorOrigin
182
+ }
183
+
184
+ /**
185
+ * @param {import('../../types/dispatcher.d.ts').default.DispatchOptions} opts
186
+ * @param {string|URL|null} [origin]
187
+ */
188
+ function makeCacheKey (opts, origin = getRequestOrigin(opts)) {
189
+ if (!origin) {
16
190
  throw new Error('opts.origin is undefined')
17
191
  }
18
192
 
@@ -23,13 +197,27 @@ function makeCacheKey (opts) {
23
197
  }
24
198
 
25
199
  return {
26
- origin: opts.origin.toString(),
200
+ origin: origin.toString(),
27
201
  method: opts.method,
28
202
  path: fullPath,
29
203
  headers: opts.headers
30
204
  }
31
205
  }
32
206
 
207
+ function appendHeader (headers, key, val) {
208
+ const headerName = key.toLowerCase()
209
+ const current = headers[headerName]
210
+ const values = Array.isArray(val) ? val : [val]
211
+
212
+ if (current === undefined) {
213
+ headers[headerName] = Array.isArray(val) ? val.slice() : val
214
+ } else if (Array.isArray(current)) {
215
+ current.push(...values)
216
+ } else {
217
+ headers[headerName] = [current, ...values]
218
+ }
219
+ }
220
+
33
221
  /**
34
222
  * @param {Record<string, string[] | string>}
35
223
  * @returns {Record<string, string[] | string>}
@@ -42,19 +230,61 @@ function normalizeHeaders (opts) {
42
230
  headers = {}
43
231
 
44
232
  if (hasSafeIterator(opts.headers)) {
45
- for (const x of opts.headers) {
46
- if (!Array.isArray(x)) {
47
- throw new Error('opts.headers is not a valid header map')
233
+ if (Array.isArray(opts.headers)) {
234
+ // Array format: could be flat alternating [k, v, k, v, ...]
235
+ // or array-of-pairs [[k, v], ...]
236
+ const first = opts.headers[0]
237
+ if (Array.isArray(first)) {
238
+ for (const x of opts.headers) {
239
+ if (!Array.isArray(x)) {
240
+ throw new Error('opts.headers is not a valid header map')
241
+ }
242
+ const [key, val] = x
243
+ if (typeof key !== 'string' || typeof val !== 'string') {
244
+ throw new Error('opts.headers is not a valid header map')
245
+ }
246
+ appendHeader(headers, key, val)
247
+ }
248
+ } else {
249
+ // Flat alternating array [k, v, k, v, ...]
250
+ const len = opts.headers.length
251
+ if (len % 2 !== 0) {
252
+ throw new Error('opts.headers is not a valid header map')
253
+ }
254
+ for (let i = 0; i < len; i += 2) {
255
+ const key = opts.headers[i]
256
+ const val = opts.headers[i + 1]
257
+ if (typeof key !== 'string' || (typeof val !== 'string' && !Array.isArray(val))) {
258
+ throw new Error('opts.headers is not a valid header map')
259
+ }
260
+ if (typeof val === 'string') {
261
+ appendHeader(headers, key, val)
262
+ } else {
263
+ const mapped = []
264
+ for (let j = 0; j < val.length; j++) {
265
+ const v = val[j]
266
+ mapped.push(typeof v === 'string' ? v : v.toString('latin1'))
267
+ }
268
+ appendHeader(headers, key, mapped)
269
+ }
270
+ }
48
271
  }
49
- const [key, val] = x
50
- if (typeof key !== 'string' || typeof val !== 'string') {
51
- throw new Error('opts.headers is not a valid header map')
272
+ } else {
273
+ // Non-array iterable (e.g. Map) — use original iteration logic
274
+ for (const x of opts.headers) {
275
+ if (!Array.isArray(x)) {
276
+ throw new Error('opts.headers is not a valid header map')
277
+ }
278
+ const [key, val] = x
279
+ if (typeof key !== 'string' || typeof val !== 'string') {
280
+ throw new Error('opts.headers is not a valid header map')
281
+ }
282
+ appendHeader(headers, key, val)
52
283
  }
53
- headers[key.toLowerCase()] = val
54
284
  }
55
285
  } else {
56
286
  for (const key of Object.keys(opts.headers)) {
57
- headers[key.toLowerCase()] = opts.headers[key]
287
+ appendHeader(headers, key, opts.headers[key])
58
288
  }
59
289
  }
60
290
  } else {
@@ -126,29 +356,37 @@ function parseCacheControlHeader (header) {
126
356
  * @type {import('../../types/cache-interceptor.d.ts').default.CacheControlDirectives}
127
357
  */
128
358
  const output = {}
359
+ const invalidNumericDirectives = new Set()
360
+ const invalidNoArgumentDirectives = new Set()
129
361
 
130
- let directives
131
- if (Array.isArray(header)) {
132
- directives = []
133
-
134
- for (const directive of header) {
135
- directives.push(...directive.split(','))
136
- }
137
- } else {
138
- directives = header.split(',')
139
- }
362
+ const directives = splitCacheControlHeaderValue(Array.isArray(header) ? header.join(',') : header)
140
363
 
141
364
  for (let i = 0; i < directives.length; i++) {
142
- const directive = directives[i].toLowerCase()
365
+ const directiveRecord = directives[i]
366
+ const directive = directiveRecord.value.toLowerCase()
367
+ const fromMalformedQuote = directiveRecord.fromMalformedQuote
143
368
  const keyValueDelimiter = directive.indexOf('=')
144
369
 
145
370
  let key
146
371
  let value
372
+ let keyHasTrailingWhitespace = false
373
+ let valueHasLeadingWhitespace = false
147
374
  if (keyValueDelimiter !== -1) {
148
- key = directive.substring(0, keyValueDelimiter).trimStart()
149
- value = directive.substring(keyValueDelimiter + 1)
375
+ const rawKey = directive.substring(0, keyValueDelimiter)
376
+ const rawValue = directive.substring(keyValueDelimiter + 1)
377
+
378
+ keyHasTrailingWhitespace = trimOWSEnd(rawKey) !== rawKey
379
+ valueHasLeadingWhitespace = trimOWSStart(rawValue) !== rawValue
380
+ key = trimOWS(rawKey)
381
+ value = trimOWSStart(rawValue)
150
382
  } else {
151
- key = directive.trim()
383
+ key = trimOWS(directive)
384
+ }
385
+
386
+ const malformedRestrictiveDirectiveName = getMalformedRestrictiveDirectiveName(key)
387
+ if (malformedRestrictiveDirectiveName !== undefined) {
388
+ output[malformedRestrictiveDirectiveName] = true
389
+ continue
152
390
  }
153
391
 
154
392
  switch (key) {
@@ -158,7 +396,14 @@ function parseCacheControlHeader (header) {
158
396
  case 's-maxage':
159
397
  case 'stale-while-revalidate':
160
398
  case 'stale-if-error': {
161
- if (value === undefined || value[0] === ' ') {
399
+ if (fromMalformedQuote || invalidNumericDirectives.has(key)) {
400
+ continue
401
+ }
402
+
403
+ if (value === undefined || keyHasTrailingWhitespace || valueHasLeadingWhitespace) {
404
+ delete output[key]
405
+ invalidNumericDirectives.add(key)
406
+ markInvalidCacheControlDirective(output, key)
162
407
  continue
163
408
  }
164
409
 
@@ -170,22 +415,37 @@ function parseCacheControlHeader (header) {
170
415
  value = value.substring(1, value.length - 1)
171
416
  }
172
417
 
173
- const parsedValue = parseInt(value, 10)
174
- // eslint-disable-next-line no-self-compare
175
- if (parsedValue !== parsedValue) {
418
+ if (!/^[0-9]+$/.test(value)) {
419
+ delete output[key]
420
+ invalidNumericDirectives.add(key)
421
+ markInvalidCacheControlDirective(output, key)
176
422
  continue
177
423
  }
178
424
 
179
- if (key === 'max-age' && key in output && output[key] >= parsedValue) {
180
- continue
181
- }
425
+ const parsedValue = Math.min(parseInt(value, 10), MAX_DELTA_SECONDS)
182
426
 
183
- output[key] = parsedValue
427
+ if (key === 'min-fresh') {
428
+ if (!(key in output) || output[key] < parsedValue) {
429
+ output[key] = parsedValue
430
+ }
431
+ } else if (!(key in output) || output[key] > parsedValue) {
432
+ output[key] = parsedValue
433
+ }
184
434
 
185
435
  break
186
436
  }
187
437
  case 'private':
188
438
  case 'no-cache': {
439
+ if (fromMalformedQuote) {
440
+ output[key] = true
441
+ break
442
+ }
443
+
444
+ if (value !== undefined && value.length === 0) {
445
+ output[key] = true
446
+ break
447
+ }
448
+
189
449
  if (value) {
190
450
  // The private and no-cache directives can be unqualified (aka just
191
451
  // `private` or `no-cache`) or qualified (w/ a value). When they're
@@ -193,45 +453,64 @@ function parseCacheControlHeader (header) {
193
453
  // `no-cache="header1"`, or `no-cache="header1, header2"`
194
454
  // If we're given multiple headers, the comma messes us up since
195
455
  // we split the full header by commas. So, let's loop through the
196
- // remaining parts in front of us until we find one that ends in a
197
- // quote. We can then just splice all of the parts in between the
198
- // starting quote and the ending quote out of the directives array
199
- // and continue parsing like normal.
456
+ // remaining parts in front of us until we find one that contains a
457
+ // closing quote. We can then skip the consumed quoted-list fragments and
458
+ // continue parsing like normal.
200
459
  // https://www.rfc-editor.org/rfc/rfc9111.html#name-no-cache-2
201
460
  if (value[0] === '"') {
202
461
  // Something like `no-cache="some-header"` OR `no-cache="some-header, another-header"`.
462
+ value = trimOWSEnd(value)
203
463
 
204
- // Add the first header on and cut off the leading quote
205
- const headers = [value.substring(1)]
464
+ let fieldList = ''
465
+ let lastQuotedPart = i
466
+ let foundEndingQuote = false
467
+ const closingQuote = findUnescapedQuote(value, 1)
206
468
 
207
- let foundEndingQuote = value[value.length - 1] === '"'
208
- if (!foundEndingQuote) {
469
+ if (closingQuote !== -1) {
470
+ fieldList = value.substring(1, closingQuote)
471
+ foundEndingQuote = true
472
+ } else {
209
473
  // Something like `no-cache="some-header, another-header"`
210
474
  // This can still be something invalid, e.g. `no-cache="some-header, ...`
475
+ const fieldListParts = [value.substring(1)]
476
+
211
477
  for (let j = i + 1; j < directives.length; j++) {
212
- const nextPart = directives[j]
213
- const nextPartLength = nextPart.length
478
+ const nextPart = trimOWS(directives[j].value)
479
+ const closingQuote = findUnescapedQuote(nextPart, 0)
214
480
 
215
- headers.push(nextPart.trim())
481
+ lastQuotedPart = j
216
482
 
217
- if (nextPartLength !== 0 && nextPart[nextPartLength - 1] === '"') {
483
+ if (closingQuote !== -1) {
484
+ fieldListParts.push(nextPart.substring(0, closingQuote))
218
485
  foundEndingQuote = true
219
486
  break
220
487
  }
488
+
489
+ fieldListParts.push(nextPart)
221
490
  }
491
+
492
+ fieldList = fieldListParts.join(',')
222
493
  }
223
494
 
224
- if (foundEndingQuote) {
225
- let lastHeader = headers[headers.length - 1]
226
- if (lastHeader[lastHeader.length - 1] === '"') {
227
- lastHeader = lastHeader.substring(0, lastHeader.length - 1)
228
- headers[headers.length - 1] = lastHeader
229
- }
495
+ if (!foundEndingQuote) {
496
+ output[key] = true
497
+ break
498
+ }
230
499
 
231
- for (let j = 0; j < headers.length; j++) {
232
- headers[j] = headers[j].trim()
500
+ i = lastQuotedPart
501
+
502
+ const headers = fieldList.split(',')
503
+ let validFieldNames = true
504
+ for (let j = 0; j < headers.length; j++) {
505
+ headers[j] = trimOWS(headers[j])
506
+ if (!isValidHTTPToken(headers[j])) {
507
+ validFieldNames = false
233
508
  }
509
+ }
234
510
 
511
+ if (!validFieldNames) {
512
+ output[key] = true
513
+ } else if (output[key] !== true) {
235
514
  if (key in output) {
236
515
  output[key] = output[key].concat(headers)
237
516
  } else {
@@ -239,13 +518,17 @@ function parseCacheControlHeader (header) {
239
518
  }
240
519
  }
241
520
  } else {
242
- // Something like `no-cache="some-header"`
243
- const fieldName = value.trim()
521
+ // Something like `no-cache=some-header`
522
+ const fieldName = trimOWS(value)
244
523
 
245
- if (key in output) {
246
- output[key] = output[key].concat(fieldName)
247
- } else {
248
- output[key] = [fieldName]
524
+ if (!isValidHTTPToken(fieldName)) {
525
+ output[key] = true
526
+ } else if (output[key] !== true) {
527
+ if (key in output) {
528
+ output[key] = output[key].concat(fieldName)
529
+ } else {
530
+ output[key] = [fieldName]
531
+ }
249
532
  }
250
533
  }
251
534
 
@@ -254,19 +537,27 @@ function parseCacheControlHeader (header) {
254
537
  }
255
538
  // eslint-disable-next-line no-fallthrough
256
539
  case 'public':
257
- case 'no-store':
258
540
  case 'must-revalidate':
259
541
  case 'proxy-revalidate':
260
542
  case 'immutable':
261
543
  case 'no-transform':
262
544
  case 'must-understand':
263
545
  case 'only-if-cached':
264
- if (value) {
546
+ if (fromMalformedQuote || invalidNoArgumentDirectives.has(key)) {
547
+ continue
548
+ }
549
+
550
+ if (value !== undefined) {
265
551
  // These are qualified (something like `public=...`) when they aren't
266
- // allowed to be, skip
552
+ // allowed to be, skip all instances of the malformed directive.
553
+ delete output[key]
554
+ invalidNoArgumentDirectives.add(key)
267
555
  continue
268
556
  }
269
557
 
558
+ output[key] = true
559
+ break
560
+ case 'no-store':
270
561
  output[key] = true
271
562
  break
272
563
  default:
@@ -278,31 +569,79 @@ function parseCacheControlHeader (header) {
278
569
  return output
279
570
  }
280
571
 
572
+ /**
573
+ * @param {string | string[]} varyHeader Vary header from the server
574
+ * @returns {string[]}
575
+ */
576
+ function splitVaryHeader (varyHeader) {
577
+ const values = Array.isArray(varyHeader) ? varyHeader : [varyHeader]
578
+ const output = []
579
+
580
+ for (let i = 0; i < values.length; i++) {
581
+ const parts = values[i].split(',')
582
+ for (let j = 0; j < parts.length; j++) {
583
+ output.push(parts[j])
584
+ }
585
+ }
586
+
587
+ return output
588
+ }
589
+
590
+ /**
591
+ * @param {string | string[]} varyHeader Vary header from the server
592
+ * @returns {boolean}
593
+ */
594
+ function hasVaryStar (varyHeader) {
595
+ const values = splitVaryHeader(varyHeader)
596
+ for (let i = 0; i < values.length; i++) {
597
+ if (trimOWS(values[i]).indexOf('*') !== -1) {
598
+ return true
599
+ }
600
+ }
601
+
602
+ return false
603
+ }
604
+
281
605
  /**
282
606
  * @param {string | string[]} varyHeader Vary header from the server
283
607
  * @param {Record<string, string | string[]>} headers Request headers
284
- * @returns {Record<string, string | string[]>}
608
+ * @returns {Record<string, string | string[] | null> | undefined}
285
609
  */
286
610
  function parseVaryHeader (varyHeader, headers) {
287
- if (typeof varyHeader === 'string' && varyHeader.includes('*')) {
611
+ if (hasVaryStar(varyHeader)) {
288
612
  return headers
289
613
  }
290
614
 
291
615
  const output = /** @type {Record<string, string | string[] | null>} */ ({})
292
616
 
293
- const varyingHeaders = typeof varyHeader === 'string'
294
- ? varyHeader.split(',')
295
- : varyHeader
617
+ const varyingHeaders = splitVaryHeader(varyHeader)
296
618
 
297
619
  for (const header of varyingHeaders) {
298
- const trimmedHeader = header.trim().toLowerCase()
620
+ const trimmedHeader = trimOWS(header).toLowerCase()
621
+
622
+ if (trimmedHeader.length === 0) {
623
+ continue
624
+ }
625
+
626
+ if (!isValidHTTPToken(trimmedHeader)) {
627
+ return undefined
628
+ }
299
629
 
300
- output[trimmedHeader] = headers[trimmedHeader] ?? null
630
+ const headerValue = headers[trimmedHeader]
631
+ output[trimmedHeader] = Array.isArray(headerValue) ? headerValue.slice() : headerValue ?? null
301
632
  }
302
633
 
303
634
  return output
304
635
  }
305
636
 
637
+ /**
638
+ * @param {string | string[]} varyHeader Vary header from the server
639
+ * @returns {boolean}
640
+ */
641
+ function isInvalidOrWildcardVaryHeader (varyHeader) {
642
+ return hasVaryStar(varyHeader) || parseVaryHeader(varyHeader, {}) === undefined
643
+ }
644
+
306
645
  /**
307
646
  * Note: this deviates from the spec a little. Empty etags ("", W/"") are valid,
308
647
  * however, including them in cached resposnes serves little to no purpose.
@@ -366,7 +705,7 @@ function assertCacheMethods (methods, name = 'CacheMethods') {
366
705
  }
367
706
 
368
707
  for (const method of methods) {
369
- if (!safeHTTPMethods.includes(method)) {
708
+ if (!arrayIncludes(safeHTTPMethods, method)) {
370
709
  throw new TypeError(`element of ${name}-array needs to be one of following values: ${safeHTTPMethods.join(', ')}, got ${method}`)
371
710
  }
372
711
  }
@@ -401,12 +740,17 @@ function makeDeduplicationKey (cacheKey, excludeHeaders) {
401
740
  }
402
741
 
403
742
  module.exports = {
743
+ getInterceptorOrigin,
744
+ getRequestOrigin,
404
745
  makeCacheKey,
405
746
  normalizeHeaders,
406
747
  assertCacheKey,
407
748
  assertCacheValue,
408
749
  parseCacheControlHeader,
750
+ hasInvalidCacheControlDirective,
409
751
  parseVaryHeader,
752
+ hasVaryStar,
753
+ isInvalidOrWildcardVaryHeader,
410
754
  isEtagUsable,
411
755
  assertCacheMethods,
412
756
  assertCacheStore,