@openclaw/discord 2026.9.8 → 2026.10.1-beta.2
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.
- package/README.md +7 -0
- package/dist/.setup/{account-inspect-De8hOUxG.mjs → account-inspect-CjKqkDnH.mjs} +2 -2
- package/dist/.setup/{accounts-ChaMcixG.mjs → accounts-DXHCL8Zm.mjs} +9 -32
- package/dist/.setup/{active-turn-thread-route-fmHl1HNd.mjs → active-turn-thread-route-C4tp9noA.mjs} +5 -9
- package/dist/.setup/{activities-api-tvbsU2e3.mjs → activities-api-BL_hJ0JQ.mjs} +15 -18
- package/dist/.setup/{api-DVbJwZVt.mjs → api-_oznjffB.mjs} +1 -1
- package/dist/.setup/{approval-handler.runtime-DxCK2Rux.mjs → approval-handler.runtime-BilYSrRF.mjs} +22 -61
- package/dist/.setup/{approval-message-updates-BKFqavHZ.mjs → approval-message-updates-CgRwc2_Q.mjs} +2 -2
- package/dist/.setup/{audit-CLg9I71N.mjs → audit-DtvzxONM.mjs} +4 -4
- package/dist/.setup/{channel-access-C12aDZ0p.mjs → channel-access-BRp3Y-rF.mjs} +4 -17
- package/dist/.setup/{channel-type-DRCrEMss.mjs → channel-type-D1_b5UhU.mjs} +12 -16
- package/dist/.setup/{channel-DxlUVU9t.mjs → channel-z5lQMS_9.mjs} +79 -75
- package/dist/.setup/{channel.setup-CRo1GjV7.mjs → channel.setup-DBJtugJO.mjs} +14 -34
- package/dist/.setup/{components-Cq1PvIpN.mjs → components-CzXLR3QH.mjs} +56 -72
- package/dist/.setup/{config-schema-oxELUSDx.mjs → config-schema-CsemBDpS.mjs} +6 -17
- package/dist/.setup/{conversation-binding-route-Caz_XuFH.mjs → conversation-binding-route-Bi62ApSt.mjs} +1 -1
- package/dist/.setup/{directory-config-esqNn0Eo.mjs → directory-config-BH1LA-gi.mjs} +1 -1
- package/dist/.setup/{directory-live-DtKRHuGV.mjs → directory-live-C9xz7IPm.mjs} +10 -13
- package/dist/.setup/{discord-0xJYfdDD.mjs → discord-J7aRzq2Q.mjs} +101 -381
- package/dist/.setup/{doctor-BCl1H5tl.mjs → doctor-DxSAngJq.mjs} +31 -70
- package/dist/.setup/doctor-contract-Bg3NPAMw.mjs +137 -0
- package/dist/.setup/{handle-action-CUvQWnE7.mjs → handle-action-DlmFX-_G.mjs} +10 -11
- package/dist/.setup/{handle-action.guild-admin-Df-8gVle.mjs → handle-action.guild-admin-BnyqTczR.mjs} +67 -129
- package/dist/.setup/{live-policy-config-8Pv20CHc.mjs → live-policy-config-emmPNyh4.mjs} +1 -1
- package/dist/.setup/{message-handler-B-Jk4oDk.mjs → message-handler-BNToPZrx.mjs} +7 -6
- package/dist/.setup/{message-handler.preflight-v-__NSDq.mjs → message-handler.preflight-D0BcCyI7.mjs} +13 -23
- package/dist/.setup/{message-handler.preflight-pluralkit-CWLUBVcx.mjs → message-handler.preflight-pluralkit-D1g3eq0B.mjs} +5 -8
- package/dist/.setup/{message-handler.process-BNFFuChn.mjs → message-handler.process-CThRmCWK.mjs} +57 -96
- package/dist/.setup/{outbound-adapter-BobT-kse.mjs → outbound-adapter-BPq0c2wY.mjs} +11 -17
- package/dist/.setup/{outbound-session-route-zVGxxceg.mjs → outbound-session-route-DGPPKA0z.mjs} +21 -46
- package/dist/.setup/{presence-cache-Di3H3DIU.mjs → presence-cache-Dg3_dADn.mjs} +2 -4
- package/dist/.setup/{probe-LYrIHlBF.mjs → probe-DIRY8vh7.mjs} +17 -27
- package/dist/.setup/{provider-B_G5hASF.mjs → provider-D2OKMo0f.mjs} +273 -446
- package/dist/.setup/{provider-session.runtime-BYlTBFD2.mjs → provider-session.runtime-BupCTZQv.mjs} +3 -3
- package/dist/.setup/provider.runtime-BaW_JnfU.mjs +2 -0
- package/dist/.setup/{realtime-session.runtime-oUd9DOcz.mjs → realtime-session.runtime-D5RSC8Mm.mjs} +6 -8
- package/dist/.setup/{receive-recovery-CHPOsnLc.mjs → receive-recovery-GqF2PUN_.mjs} +1 -4
- package/dist/.setup/{resolve-allowlist-common-DPdRggu5.mjs → resolve-allowlist-common-DUYRrrVB.mjs} +5 -9
- package/dist/.setup/{resolve-channels-BFPNRmVv.mjs → resolve-channels--MUatisx.mjs} +3 -3
- package/dist/.setup/{resolve-users-0-aBuCy3.mjs → resolve-users-CxLcDmM0.mjs} +6 -9
- package/dist/.setup/{retry-pz_cSha2.mjs → retry-BKzO25k4.mjs} +27 -51
- package/dist/.setup/{runtime-sUksZ--Q.mjs → runtime-CerRBfaa.mjs} +2 -2
- package/dist/.setup/{runtime-FN000lxA.mjs → runtime-CwUA0RLr.mjs} +228 -255
- package/dist/.setup/{runtime-doctor-migrations-CshUGl36.mjs → runtime-doctor-migrations-BPV-R4G9.mjs} +6 -0
- package/dist/.setup/{security-audit-Cdaegq7C.mjs → security-audit-D42cp5N7.mjs} +21 -48
- package/dist/.setup/{send-CrfEI6Bs.mjs → send-2X7m2Zmw.mjs} +45 -58
- package/dist/.setup/{send.components-DxQAMfES.mjs → send.components-RqvgmCs3.mjs} +44 -90
- package/dist/.setup/{send.outbound-BISKnb-a.mjs → send.outbound-B4QHSEzm.mjs} +60 -98
- package/dist/.setup/{send.shared-DGFkbkQR.mjs → send.shared-BQIUh6Vn.mjs} +13 -20
- package/dist/.setup/{sender-identity-B9X5Vb9R.mjs → sender-identity-CooeYfAn.mjs} +1 -1
- package/dist/.setup/{session-key-normalization-wJgsKPNF.mjs → session-key-normalization-CCHGNSQw.mjs} +3 -9
- package/dist/.setup/setup-plugin-api-nn_giXvZ.mjs +3 -0
- package/dist/.setup/{shared-B3wZ4hN-.mjs → shared-CIJn6AEC.mjs} +7 -7
- package/dist/.setup/{subagent-hooks-nlUE9ow7.mjs → subagent-hooks-BYlVnxzy.mjs} +4 -9
- package/dist/.setup/{system-events-CdeZnvPV.mjs → system-events-CQ3if_pS.mjs} +2 -2
- package/dist/.setup/{target-resolver-BuLapkhy.mjs → target-resolver-Dzxf80Y3.mjs} +5 -13
- package/dist/.setup/targets-Lf8D55w-.mjs +3 -0
- package/dist/.setup/{thread-bindings-D7TfgjR_.mjs → thread-bindings-yNNlcUWE.mjs} +5 -6
- package/dist/.setup/{thread-bindings.manager-DN-NissX.mjs → thread-bindings.manager-plk0zJAW.mjs} +14 -20
- package/dist/.setup/{thread-bindings.session-updates-CshfKghV.mjs → thread-bindings.session-updates-C9aKtVs6.mjs} +295 -5
- package/dist/.setup/{timeouts-D86uWLt_.mjs → timeouts-pun7VJ02.mjs} +2 -15
- package/dist/.setup/{transcripts-source-p8EV179h.mjs → transcripts-source-CNWswry-.mjs} +31 -79
- package/dist/.setup/typing-CNlSKY5S.mjs +11 -0
- package/dist/.setup/{voice-runtime-B46rcwrG.mjs → voice-runtime-Bc75quiH.mjs} +62 -110
- package/dist/account-inspect-api.js +1 -1
- package/dist/action-runtime-api.js +1 -1
- package/dist/activities-api.js +1 -1
- package/dist/api.js +18 -18
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/control-ui/2afbdab1d3aa65a789d8a3eba666f4b1ec5df07073538fe30d6cf32093a1fb2d/index.js +1 -0
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +19 -128
- package/dist/index.js +1 -1
- package/dist/runtime-api.js +20 -21
- package/dist/security-audit-contract-api.js +1 -1
- package/dist/session-key-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/src/voice/audio-worker.runtime.js +6 -8
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/node_modules/libopus-wasm/CHANGELOG.md +8 -0
- package/node_modules/libopus-wasm/dist/discordjs.d.ts.map +1 -1
- package/node_modules/libopus-wasm/dist/discordjs.js +14 -2
- package/node_modules/libopus-wasm/dist/generated/libopus.generated.mjs +0 -0
- package/node_modules/libopus-wasm/dist/index.d.ts.map +1 -1
- package/node_modules/libopus-wasm/dist/index.js +55 -100
- package/node_modules/libopus-wasm/package.json +5 -5
- package/node_modules/undici/README.md +10 -1
- package/node_modules/undici/docs/docs/api/Agent.md +8 -8
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +9 -6
- package/node_modules/undici/docs/docs/api/Dispatcher.md +9 -0
- package/node_modules/undici/docs/docs/api/Errors.md +25 -0
- package/node_modules/undici/docs/docs/api/Fetch.md +10 -0
- package/node_modules/undici/docs/docs/api/Interceptors.md +4 -3
- package/node_modules/undici/docs/docs/api/Pool.md +5 -6
- package/node_modules/undici/docs/docs/index.md +10 -1
- package/node_modules/undici/lib/api/readable.js +1 -2
- package/node_modules/undici/lib/core/request.js +23 -2
- package/node_modules/undici/lib/core/util.js +0 -39
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -4
- package/node_modules/undici/lib/dispatcher/client-h2.js +52 -8
- package/node_modules/undici/lib/dispatcher/client.js +6 -1
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +21 -6
- package/node_modules/undici/lib/dispatcher/pool-base.js +63 -2
- package/node_modules/undici/lib/dispatcher/pool.js +90 -7
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +16 -3
- package/node_modules/undici/lib/handler/deduplication-handler.js +24 -11
- package/node_modules/undici/lib/handler/redirect-handler.js +5 -0
- package/node_modules/undici/lib/handler/retry-handler.js +72 -13
- package/node_modules/undici/lib/interceptor/cache.js +4 -1
- package/node_modules/undici/lib/interceptor/decompress.js +236 -69
- package/node_modules/undici/lib/web/cache/cache.js +17 -8
- package/node_modules/undici/lib/web/cache/cachestorage.js +11 -5
- package/node_modules/undici/lib/web/cookies/index.js +19 -7
- package/node_modules/undici/lib/web/cookies/util.js +3 -1
- package/node_modules/undici/lib/web/eventsource/eventsource.js +25 -1
- package/node_modules/undici/lib/web/fetch/body.js +22 -15
- package/node_modules/undici/lib/web/fetch/data-url.js +6 -6
- package/node_modules/undici/lib/web/fetch/formdata.js +30 -36
- package/node_modules/undici/lib/web/fetch/headers.js +34 -31
- package/node_modules/undici/lib/web/fetch/index.js +46 -25
- package/node_modules/undici/lib/web/fetch/request.js +74 -79
- package/node_modules/undici/lib/web/fetch/response.js +43 -44
- package/node_modules/undici/lib/web/fetch/util.js +7 -7
- package/node_modules/undici/lib/web/infra/index.js +16 -1
- package/node_modules/undici/lib/web/webidl/index.js +2 -14
- package/node_modules/undici/lib/web/websocket/connection.js +33 -12
- package/node_modules/undici/lib/web/websocket/events.js +43 -26
- package/node_modules/undici/lib/web/websocket/receiver.js +2 -0
- package/node_modules/undici/lib/web/websocket/stream/websocketerror.js +13 -10
- package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +19 -4
- package/node_modules/undici/lib/web/websocket/websocket.js +44 -41
- package/node_modules/undici/package.json +1 -1
- package/node_modules/undici/types/diagnostics-channel.d.ts +2 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/webidl.d.ts +13 -7
- package/openclaw.plugin.json +6 -0
- package/package.json +12 -7
- package/dist/.setup/doctor-contract-CvvgwrrG.mjs +0 -409
- package/dist/.setup/model-picker-preference-primitives-BHT1LkR_.mjs +0 -37
- package/dist/.setup/provider.runtime-CUE3UxER.mjs +0 -2
- package/dist/.setup/setup-plugin-api-BQUBxbhe.mjs +0 -3
- package/dist/.setup/targets-0KUcEIYx.mjs +0 -3
- package/dist/.setup/thread-bindings.state-g6v9zAym.mjs +0 -331
- package/dist/.setup/typing-BiHCzBRu.mjs +0 -15
|
@@ -47,34 +47,51 @@ function validatePartialResponseContentLength (headers, range, statusCode, retry
|
|
|
47
47
|
// so nothing outside the handler can trigger it.
|
|
48
48
|
class RetryController {
|
|
49
49
|
#onAbort
|
|
50
|
+
#paused = false
|
|
51
|
+
#target = null
|
|
50
52
|
|
|
51
53
|
constructor (onAbort) {
|
|
52
54
|
this.#onAbort = onAbort
|
|
53
|
-
this.target = null
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
set target (target) {
|
|
58
|
+
this.#target = target
|
|
59
|
+
if (this.#paused) {
|
|
60
|
+
target?.pause()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get target () { return this.#target }
|
|
65
|
+
|
|
66
|
+
pause () {
|
|
67
|
+
this.#paused = true
|
|
68
|
+
this.#target?.pause()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
resume () {
|
|
72
|
+
this.#paused = false
|
|
73
|
+
this.#target?.resume()
|
|
74
|
+
}
|
|
58
75
|
|
|
59
76
|
abort (reason) {
|
|
60
|
-
this
|
|
77
|
+
this.#target?.abort(reason)
|
|
61
78
|
this.#onAbort(reason)
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
get paused () { return this
|
|
65
|
-
get aborted () { return this
|
|
66
|
-
get reason () { return this
|
|
67
|
-
get rawHeaders () { return this
|
|
81
|
+
get paused () { return this.#paused || (this.#target?.paused ?? false) }
|
|
82
|
+
get aborted () { return this.#target?.aborted ?? false }
|
|
83
|
+
get reason () { return this.#target?.reason ?? null }
|
|
84
|
+
get rawHeaders () { return this.#target?.rawHeaders ?? null }
|
|
68
85
|
set rawHeaders (value) {
|
|
69
|
-
if (this
|
|
70
|
-
this
|
|
86
|
+
if (this.#target) {
|
|
87
|
+
this.#target.rawHeaders = value
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
get rawTrailers () { return this
|
|
91
|
+
get rawTrailers () { return this.#target?.rawTrailers ?? null }
|
|
75
92
|
set rawTrailers (value) {
|
|
76
|
-
if (this
|
|
77
|
-
this
|
|
93
|
+
if (this.#target) {
|
|
94
|
+
this.#target.rawTrailers = value
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
97
|
}
|
|
@@ -142,6 +159,12 @@ class RetryHandler {
|
|
|
142
159
|
// Backoff timer returned by the retry policy, so #onAbort can cancel it.
|
|
143
160
|
// Null for custom policies that do not return their timer.
|
|
144
161
|
this.retryTimer = null
|
|
162
|
+
// A response can complete while its controller is paused if the peer closes
|
|
163
|
+
// the connection. Hold its body until the retry policy decides whether to
|
|
164
|
+
// discard it for a retry or forward it as the final response.
|
|
165
|
+
this.pendingResponseData = null
|
|
166
|
+
this.pendingResponseTrailers = null
|
|
167
|
+
this.pendingResponseEnded = false
|
|
145
168
|
// Set once an abort during the backoff delivered the terminal error
|
|
146
169
|
// downstream; late policy callbacks and connection errors are then moot.
|
|
147
170
|
this.aborted = false
|
|
@@ -183,6 +206,13 @@ class RetryHandler {
|
|
|
183
206
|
this.retryPending = false
|
|
184
207
|
this.retryTimer = null
|
|
185
208
|
|
|
209
|
+
const pendingData = this.pendingResponseData
|
|
210
|
+
const pendingTrailers = this.pendingResponseTrailers
|
|
211
|
+
const pendingEnd = this.pendingResponseEnded
|
|
212
|
+
this.pendingResponseData = null
|
|
213
|
+
this.pendingResponseTrailers = null
|
|
214
|
+
this.pendingResponseEnded = false
|
|
215
|
+
|
|
186
216
|
if (passedErr) {
|
|
187
217
|
if (this.headersSent) {
|
|
188
218
|
// The downstream handler already received the response from an
|
|
@@ -193,6 +223,15 @@ class RetryHandler {
|
|
|
193
223
|
this.headersSent = true
|
|
194
224
|
this.checkpointResponseEnd(headers)
|
|
195
225
|
this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage)
|
|
226
|
+
controller.resume()
|
|
227
|
+
|
|
228
|
+
if (pendingEnd) {
|
|
229
|
+
for (const chunk of pendingData) {
|
|
230
|
+
this.onResponseData(controller, chunk)
|
|
231
|
+
}
|
|
232
|
+
this.onResponseEnd(controller, pendingTrailers)
|
|
233
|
+
}
|
|
234
|
+
return
|
|
196
235
|
}
|
|
197
236
|
controller.resume()
|
|
198
237
|
return
|
|
@@ -200,6 +239,9 @@ class RetryHandler {
|
|
|
200
239
|
|
|
201
240
|
this.error = err
|
|
202
241
|
controller.resume()
|
|
242
|
+
if (pendingEnd) {
|
|
243
|
+
this.onResponseEnd(controller, pendingTrailers)
|
|
244
|
+
}
|
|
203
245
|
}
|
|
204
246
|
|
|
205
247
|
// The pause()/resume() pair (here and in shouldRetry) acts on THIS
|
|
@@ -213,6 +255,9 @@ class RetryHandler {
|
|
|
213
255
|
// The default policy returns its backoff timer so an abort can cancel it;
|
|
214
256
|
// a custom policy may return anything (or nothing), which is ignored.
|
|
215
257
|
this.retryPending = true
|
|
258
|
+
this.pendingResponseData = []
|
|
259
|
+
this.pendingResponseTrailers = null
|
|
260
|
+
this.pendingResponseEnded = false
|
|
216
261
|
this.retryTimer = this.retryOpts.retry(
|
|
217
262
|
err,
|
|
218
263
|
{
|
|
@@ -464,6 +509,11 @@ class RetryHandler {
|
|
|
464
509
|
}
|
|
465
510
|
|
|
466
511
|
onResponseData (_controller, chunk) {
|
|
512
|
+
if (this.pendingResponseData !== null) {
|
|
513
|
+
this.pendingResponseData.push(chunk)
|
|
514
|
+
return
|
|
515
|
+
}
|
|
516
|
+
|
|
467
517
|
if (this.error) {
|
|
468
518
|
return
|
|
469
519
|
}
|
|
@@ -474,6 +524,12 @@ class RetryHandler {
|
|
|
474
524
|
}
|
|
475
525
|
|
|
476
526
|
onResponseEnd (_controller, trailers) {
|
|
527
|
+
if (this.pendingResponseData !== null) {
|
|
528
|
+
this.pendingResponseTrailers = trailers
|
|
529
|
+
this.pendingResponseEnded = true
|
|
530
|
+
return
|
|
531
|
+
}
|
|
532
|
+
|
|
477
533
|
if (this.error && this.retryOpts.throwOnError) {
|
|
478
534
|
throw this.error
|
|
479
535
|
}
|
|
@@ -587,6 +643,9 @@ class RetryHandler {
|
|
|
587
643
|
this.retryPending = false
|
|
588
644
|
clearTimeout(this.retryTimer)
|
|
589
645
|
this.retryTimer = null
|
|
646
|
+
this.pendingResponseData = null
|
|
647
|
+
this.pendingResponseTrailers = null
|
|
648
|
+
this.pendingResponseEnded = false
|
|
590
649
|
this.handler.onResponseError?.(this.controllerProxy, reason ?? new RequestAbortedError())
|
|
591
650
|
}
|
|
592
651
|
}
|
|
@@ -330,6 +330,8 @@ function sendCachedValue (handler, opts, result, age, context, isStale) {
|
|
|
330
330
|
assert(!stream.destroyed, 'stream should not be destroyed')
|
|
331
331
|
assert(!stream.readableDidRead, 'stream should not be readableDidRead')
|
|
332
332
|
|
|
333
|
+
let aborted = false
|
|
334
|
+
|
|
333
335
|
const controller = {
|
|
334
336
|
rawHeaders: [],
|
|
335
337
|
rawTrailers: [],
|
|
@@ -343,12 +345,13 @@ function sendCachedValue (handler, opts, result, age, context, isStale) {
|
|
|
343
345
|
return stream.isPaused()
|
|
344
346
|
},
|
|
345
347
|
get aborted () {
|
|
346
|
-
return
|
|
348
|
+
return aborted
|
|
347
349
|
},
|
|
348
350
|
get reason () {
|
|
349
351
|
return stream.errored
|
|
350
352
|
},
|
|
351
353
|
abort (reason) {
|
|
354
|
+
aborted = true
|
|
352
355
|
stream.destroy(reason ?? new AbortError())
|
|
353
356
|
}
|
|
354
357
|
}
|
|
@@ -9,8 +9,63 @@ const DecoratorHandler = require('../handler/decorator-handler')
|
|
|
9
9
|
/** @typedef {import('node:stream').Transform} Controller */
|
|
10
10
|
/** @typedef {Transform&import('node:zlib').Zlib} DecompressorStream */
|
|
11
11
|
|
|
12
|
+
class DecompressController {
|
|
13
|
+
#onPause
|
|
14
|
+
#onResume
|
|
15
|
+
#onAbort
|
|
16
|
+
#paused = false
|
|
17
|
+
|
|
18
|
+
constructor (onPause, onResume, onAbort) {
|
|
19
|
+
this.#onPause = onPause
|
|
20
|
+
this.#onResume = onResume
|
|
21
|
+
this.#onAbort = onAbort
|
|
22
|
+
this.target = null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pause () {
|
|
26
|
+
if (this.#paused) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.#paused = true
|
|
31
|
+
this.#onPause()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
resume () {
|
|
35
|
+
if (!this.#paused) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.#paused = false
|
|
40
|
+
this.#onResume()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
abort (reason) {
|
|
44
|
+
this.target?.abort(reason)
|
|
45
|
+
this.#onAbort(reason)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get paused () { return this.#paused }
|
|
49
|
+
get aborted () { return this.target?.aborted ?? false }
|
|
50
|
+
get reason () { return this.target?.reason ?? null }
|
|
51
|
+
get rawHeaders () { return this.target?.rawHeaders ?? null }
|
|
52
|
+
set rawHeaders (value) {
|
|
53
|
+
if (this.target) {
|
|
54
|
+
this.target.rawHeaders = value
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get rawTrailers () { return this.target?.rawTrailers ?? null }
|
|
59
|
+
set rawTrailers (value) {
|
|
60
|
+
if (this.target) {
|
|
61
|
+
this.target.rawTrailers = value
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
12
66
|
/** @type {Record<string, () => DecompressorStream>} */
|
|
13
67
|
const supportedEncodings = {
|
|
68
|
+
__proto__: null,
|
|
14
69
|
gzip: createGunzip,
|
|
15
70
|
'x-gzip': createGunzip,
|
|
16
71
|
br: createBrotliDecompress,
|
|
@@ -21,7 +76,7 @@ const supportedEncodings = {
|
|
|
21
76
|
}
|
|
22
77
|
|
|
23
78
|
const defaultSkipStatusCodes = /** @type {const} */ ([204, 304])
|
|
24
|
-
const defaultMaxSize =
|
|
79
|
+
const defaultMaxSize = 0
|
|
25
80
|
|
|
26
81
|
/**
|
|
27
82
|
* Limits the output of one stage in a decompression chain.
|
|
@@ -53,7 +108,7 @@ let warningEmitted = /** @type {boolean} */ (false)
|
|
|
53
108
|
* @typedef {Object} DecompressHandlerOptions
|
|
54
109
|
* @property {number[]|Readonly<number[]>} [skipStatusCodes=[204, 304]] - List of status codes to skip decompression for
|
|
55
110
|
* @property {boolean} [skipErrorResponses] - Whether to skip decompression for error responses (status codes >= 400)
|
|
56
|
-
* @property {number} [maxSize=
|
|
111
|
+
* @property {number} [maxSize=0] - Maximum decompressed response size in bytes. 0 disables the limit
|
|
57
112
|
*/
|
|
58
113
|
|
|
59
114
|
class DecompressHandler extends DecoratorHandler {
|
|
@@ -73,16 +128,130 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
73
128
|
#terminated = false
|
|
74
129
|
/** @type {boolean} */
|
|
75
130
|
#inputEnded = false
|
|
131
|
+
/** @type {boolean} */
|
|
132
|
+
#inputBackpressured = false
|
|
133
|
+
/** @type {boolean} */
|
|
134
|
+
#upstreamPaused = false
|
|
135
|
+
/** @type {boolean} */
|
|
136
|
+
#draining = false
|
|
137
|
+
/** @type {boolean} */
|
|
138
|
+
#drainRequested = false
|
|
139
|
+
/** @type {boolean} */
|
|
140
|
+
#completionPending = false
|
|
141
|
+
/** @type {DecompressorStream | undefined} */
|
|
142
|
+
#finalDecompressor
|
|
143
|
+
/** @type {DecompressController} */
|
|
144
|
+
#controller
|
|
76
145
|
|
|
77
146
|
constructor (handler, { skipStatusCodes = defaultSkipStatusCodes, skipErrorResponses = true, maxSize = defaultMaxSize } = {}) {
|
|
78
|
-
if (!Number.isSafeInteger(maxSize) || maxSize <
|
|
79
|
-
throw new InvalidArgumentError('maxSize must be a
|
|
147
|
+
if (!Number.isSafeInteger(maxSize) || maxSize < 0) {
|
|
148
|
+
throw new InvalidArgumentError('maxSize must be a non-negative integer')
|
|
80
149
|
}
|
|
81
150
|
|
|
82
151
|
super(handler)
|
|
83
152
|
this.#skipStatusCodes = skipStatusCodes
|
|
84
153
|
this.#skipErrorResponses = skipErrorResponses
|
|
85
154
|
this.#maxSize = maxSize
|
|
155
|
+
this.#controller = new DecompressController(
|
|
156
|
+
() => this.#onDownstreamPause(),
|
|
157
|
+
() => this.#onDownstreamResume(),
|
|
158
|
+
reason => {
|
|
159
|
+
if (this.#inputEnded && !this.#terminated) {
|
|
160
|
+
this.onResponseError(this.#controller, reason)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#onDownstreamPause () {
|
|
167
|
+
this.#pauseUpstream()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#onDownstreamResume () {
|
|
171
|
+
const drainWasDeferred = this.#draining
|
|
172
|
+
this.#drainOutput()
|
|
173
|
+
if (!drainWasDeferred) {
|
|
174
|
+
this.#resumeUpstreamIfNeeded()
|
|
175
|
+
this.#finishIfReady()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
#pauseUpstream () {
|
|
180
|
+
if (!this.#upstreamPaused && !this.#terminated) {
|
|
181
|
+
this.#upstreamPaused = true
|
|
182
|
+
this.#controller.target?.pause()
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#resumeUpstreamIfNeeded () {
|
|
187
|
+
if (this.#upstreamPaused && !this.#controller.paused && !this.#inputBackpressured) {
|
|
188
|
+
this.#upstreamPaused = false
|
|
189
|
+
if (!this.#inputEnded) {
|
|
190
|
+
this.#controller.target?.resume()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#drainOutput () {
|
|
196
|
+
if (this.#terminated || this.#controller.paused || !this.#finalDecompressor) {
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (this.#draining) {
|
|
201
|
+
this.#drainRequested = true
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.#draining = true
|
|
206
|
+
try {
|
|
207
|
+
do {
|
|
208
|
+
this.#drainRequested = false
|
|
209
|
+
let chunk
|
|
210
|
+
while (!this.#terminated && !this.#controller.paused && (chunk = this.#finalDecompressor.read()) !== null) {
|
|
211
|
+
if (this.#maxSize > 0) {
|
|
212
|
+
const decompressedSize = this.#decompressedSize + chunk.length
|
|
213
|
+
if (decompressedSize > this.#maxSize) {
|
|
214
|
+
this.#fail(new ResponseExceededMaxSizeError(
|
|
215
|
+
`Decompressed response size (${decompressedSize}) exceeded maxSize (${this.#maxSize})`
|
|
216
|
+
))
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.#decompressedSize = decompressedSize
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = super.onResponseData(this.#controller, chunk)
|
|
224
|
+
if (result === false && !this.#controller.paused) {
|
|
225
|
+
this.#controller.pause()
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} while (this.#drainRequested && !this.#terminated && !this.#controller.paused)
|
|
229
|
+
} finally {
|
|
230
|
+
this.#draining = false
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.#resumeUpstreamIfNeeded()
|
|
234
|
+
this.#finishIfReady()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
#finishIfReady () {
|
|
238
|
+
if (this.#terminated || !this.#completionPending || this.#controller.paused || this.#draining) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.#terminated = true
|
|
243
|
+
this.#cleanupDecompressors()
|
|
244
|
+
super.onResponseEnd(this.#controller, this.#trailers)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#onDecompressionEnd () {
|
|
248
|
+
if (this.#terminated) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.#completionPending = true
|
|
253
|
+
this.#drainOutput()
|
|
254
|
+
this.#finishIfReady()
|
|
86
255
|
}
|
|
87
256
|
|
|
88
257
|
/**
|
|
@@ -138,7 +307,7 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
138
307
|
const streams = []
|
|
139
308
|
for (let i = 0; i < decompressors.length; i++) {
|
|
140
309
|
streams.push(decompressors[i])
|
|
141
|
-
if (i < decompressors.length - 1) {
|
|
310
|
+
if (i < decompressors.length - 1 && this.#maxSize > 0) {
|
|
142
311
|
streams.push(createMaxSizeLimiter(this.#maxSize))
|
|
143
312
|
}
|
|
144
313
|
}
|
|
@@ -148,11 +317,10 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
148
317
|
|
|
149
318
|
/**
|
|
150
319
|
* Stops decompression and reports an error.
|
|
151
|
-
* @param {Controller} controller - The controller to coordinate with
|
|
152
320
|
* @param {Error} error - The decompression error
|
|
153
321
|
* @returns {void}
|
|
154
322
|
*/
|
|
155
|
-
#fail (
|
|
323
|
+
#fail (error) {
|
|
156
324
|
if (this.#terminated) {
|
|
157
325
|
return
|
|
158
326
|
}
|
|
@@ -160,75 +328,41 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
160
328
|
if (this.#inputEnded) {
|
|
161
329
|
// The request is already marked complete once the compressed input ends,
|
|
162
330
|
// so controller.abort() can no longer propagate decoder flush errors.
|
|
163
|
-
this.onResponseError(controller, error)
|
|
331
|
+
this.onResponseError(this.#controller, error)
|
|
164
332
|
} else {
|
|
165
|
-
controller.abort(error)
|
|
333
|
+
this.#controller.abort(error)
|
|
166
334
|
}
|
|
167
335
|
}
|
|
168
336
|
|
|
169
337
|
/**
|
|
170
|
-
* Sets up event handlers for
|
|
338
|
+
* Sets up event handlers for the final decompressor stream.
|
|
171
339
|
* @param {DecompressorStream} decompressor - The decompressor stream
|
|
172
|
-
* @param {Controller} controller - The controller to coordinate with
|
|
173
340
|
* @returns {void}
|
|
174
341
|
*/
|
|
175
|
-
#setupDecompressorEvents (decompressor
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
let chunk
|
|
182
|
-
while ((chunk = decompressor.read()) !== null) {
|
|
183
|
-
const decompressedSize = this.#decompressedSize + chunk.length
|
|
184
|
-
if (decompressedSize > this.#maxSize) {
|
|
185
|
-
this.#fail(controller, new ResponseExceededMaxSizeError(
|
|
186
|
-
`Decompressed response size (${decompressedSize}) exceeded maxSize (${this.#maxSize})`
|
|
187
|
-
))
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.#decompressedSize = decompressedSize
|
|
192
|
-
const result = super.onResponseData(controller, chunk)
|
|
193
|
-
if (result === false) {
|
|
194
|
-
break
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
decompressor.on('error', (error) => {
|
|
200
|
-
this.#fail(controller, error)
|
|
201
|
-
})
|
|
342
|
+
#setupDecompressorEvents (decompressor) {
|
|
343
|
+
this.#finalDecompressor = decompressor
|
|
344
|
+
decompressor.on('readable', () => this.#drainOutput())
|
|
345
|
+
decompressor.on('error', (error) => this.#fail(error))
|
|
202
346
|
}
|
|
203
347
|
|
|
204
348
|
/**
|
|
205
349
|
* Sets up event handling for a single decompressor
|
|
206
|
-
* @param {Controller} controller - The controller to handle events
|
|
207
350
|
* @returns {void}
|
|
208
351
|
*/
|
|
209
|
-
#setupSingleDecompressor (
|
|
352
|
+
#setupSingleDecompressor () {
|
|
210
353
|
const decompressor = this.#decompressors[0]
|
|
211
|
-
this.#setupDecompressorEvents(decompressor
|
|
354
|
+
this.#setupDecompressorEvents(decompressor)
|
|
212
355
|
|
|
213
|
-
decompressor.on('end', () =>
|
|
214
|
-
if (this.#terminated) {
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
this.#terminated = true
|
|
219
|
-
this.#cleanupDecompressors()
|
|
220
|
-
super.onResponseEnd(controller, this.#trailers)
|
|
221
|
-
})
|
|
356
|
+
decompressor.on('end', () => this.#onDecompressionEnd())
|
|
222
357
|
}
|
|
223
358
|
|
|
224
359
|
/**
|
|
225
360
|
* Sets up event handling for multiple chained decompressors using pipeline
|
|
226
|
-
* @param {Controller} controller - The controller to handle events
|
|
227
361
|
* @returns {void}
|
|
228
362
|
*/
|
|
229
|
-
#setupMultipleDecompressors (
|
|
363
|
+
#setupMultipleDecompressors () {
|
|
230
364
|
const lastDecompressor = this.#decompressors[this.#decompressors.length - 1]
|
|
231
|
-
this.#setupDecompressorEvents(lastDecompressor
|
|
365
|
+
this.#setupDecompressorEvents(lastDecompressor)
|
|
232
366
|
|
|
233
367
|
pipeline(this.#decompressors, (err) => {
|
|
234
368
|
if (this.#terminated) {
|
|
@@ -236,13 +370,26 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
236
370
|
}
|
|
237
371
|
|
|
238
372
|
if (err) {
|
|
239
|
-
this.#fail(
|
|
373
|
+
this.#fail(err)
|
|
240
374
|
return
|
|
241
375
|
}
|
|
242
376
|
|
|
243
|
-
this.#
|
|
244
|
-
|
|
245
|
-
|
|
377
|
+
this.#onDecompressionEnd()
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#setupInputBackpressure () {
|
|
382
|
+
const decompressor = this.#decompressors[0]
|
|
383
|
+
decompressor.on('drain', () => {
|
|
384
|
+
if (this.#terminated) {
|
|
385
|
+
return
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this.#inputBackpressured = false
|
|
389
|
+
if (!this.#controller.paused) {
|
|
390
|
+
this.#drainOutput()
|
|
391
|
+
this.#resumeUpstreamIfNeeded()
|
|
392
|
+
}
|
|
246
393
|
})
|
|
247
394
|
}
|
|
248
395
|
|
|
@@ -252,6 +399,16 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
252
399
|
*/
|
|
253
400
|
#cleanupDecompressors () {
|
|
254
401
|
this.#decompressors.length = 0
|
|
402
|
+
this.#finalDecompressor = undefined
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
onRequestStart (controller, context) {
|
|
406
|
+
this.#controller.target = controller
|
|
407
|
+
return super.onRequestStart(this.#controller, context)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
onRequestUpgrade (controller, statusCode, headers, socket) {
|
|
411
|
+
return super.onRequestUpgrade(this.#controller, statusCode, headers, socket)
|
|
255
412
|
}
|
|
256
413
|
|
|
257
414
|
/**
|
|
@@ -262,18 +419,24 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
262
419
|
* @returns {void}
|
|
263
420
|
*/
|
|
264
421
|
onResponseStart (controller, statusCode, headers, statusMessage) {
|
|
265
|
-
|
|
422
|
+
// Repeated field lines reach us as an array. RFC 9110 section 5.3 lets a
|
|
423
|
+
// recipient join them with commas, which yields the single-line form the
|
|
424
|
+
// decompression chain already handles.
|
|
425
|
+
const rawContentEncoding = headers['content-encoding']
|
|
426
|
+
const contentEncoding = Array.isArray(rawContentEncoding)
|
|
427
|
+
? rawContentEncoding.join(',')
|
|
428
|
+
: rawContentEncoding
|
|
266
429
|
|
|
267
430
|
// If content encoding is not supported or status code is in skip list
|
|
268
431
|
if (this.#shouldSkipDecompression(contentEncoding, statusCode)) {
|
|
269
|
-
return super.onResponseStart(controller, statusCode, headers, statusMessage)
|
|
432
|
+
return super.onResponseStart(this.#controller, statusCode, headers, statusMessage)
|
|
270
433
|
}
|
|
271
434
|
|
|
272
435
|
const decompressors = this.#createDecompressionChain(contentEncoding.toLowerCase())
|
|
273
436
|
|
|
274
437
|
if (decompressors.length === 0) {
|
|
275
438
|
this.#cleanupDecompressors()
|
|
276
|
-
return super.onResponseStart(controller, statusCode, headers, statusMessage)
|
|
439
|
+
return super.onResponseStart(this.#controller, statusCode, headers, statusMessage)
|
|
277
440
|
}
|
|
278
441
|
|
|
279
442
|
this.#decompressors = decompressors
|
|
@@ -281,8 +444,8 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
281
444
|
// Remove compression headers since we're decompressing
|
|
282
445
|
const { 'content-encoding': _, 'content-length': __, ...newHeaders } = headers
|
|
283
446
|
|
|
284
|
-
if (controller
|
|
285
|
-
const rawHeaders = controller.rawHeaders
|
|
447
|
+
if (this.#controller.rawHeaders) {
|
|
448
|
+
const rawHeaders = this.#controller.rawHeaders
|
|
286
449
|
|
|
287
450
|
if (Array.isArray(rawHeaders)) {
|
|
288
451
|
const filteredHeaders = []
|
|
@@ -308,13 +471,14 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
308
471
|
}
|
|
309
472
|
}
|
|
310
473
|
|
|
474
|
+
this.#setupInputBackpressure()
|
|
311
475
|
if (this.#decompressors.length === 1) {
|
|
312
|
-
this.#setupSingleDecompressor(
|
|
476
|
+
this.#setupSingleDecompressor()
|
|
313
477
|
} else {
|
|
314
|
-
this.#setupMultipleDecompressors(
|
|
478
|
+
this.#setupMultipleDecompressors()
|
|
315
479
|
}
|
|
316
480
|
|
|
317
|
-
return super.onResponseStart(controller, statusCode, newHeaders, statusMessage)
|
|
481
|
+
return super.onResponseStart(this.#controller, statusCode, newHeaders, statusMessage)
|
|
318
482
|
}
|
|
319
483
|
|
|
320
484
|
/**
|
|
@@ -324,10 +488,13 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
324
488
|
*/
|
|
325
489
|
onResponseData (controller, chunk) {
|
|
326
490
|
if (this.#decompressors.length > 0) {
|
|
327
|
-
this.#decompressors[0].write(chunk)
|
|
491
|
+
if (!this.#decompressors[0].write(chunk)) {
|
|
492
|
+
this.#inputBackpressured = true
|
|
493
|
+
this.#pauseUpstream()
|
|
494
|
+
}
|
|
328
495
|
return
|
|
329
496
|
}
|
|
330
|
-
super.onResponseData(controller, chunk)
|
|
497
|
+
return super.onResponseData(this.#controller, chunk)
|
|
331
498
|
}
|
|
332
499
|
|
|
333
500
|
/**
|
|
@@ -342,7 +509,7 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
342
509
|
this.#decompressors[0].end()
|
|
343
510
|
return
|
|
344
511
|
}
|
|
345
|
-
super.onResponseEnd(controller, trailers)
|
|
512
|
+
return super.onResponseEnd(this.#controller, trailers)
|
|
346
513
|
}
|
|
347
514
|
|
|
348
515
|
/**
|
|
@@ -360,7 +527,7 @@ class DecompressHandler extends DecoratorHandler {
|
|
|
360
527
|
decompressor.destroy()
|
|
361
528
|
}
|
|
362
529
|
this.#cleanupDecompressors()
|
|
363
|
-
super.onResponseError(controller, err)
|
|
530
|
+
super.onResponseError(this.#controller, err)
|
|
364
531
|
}
|
|
365
532
|
}
|
|
366
533
|
|