fix(xhr): flush final progress during the live loadend dispatch - #11121
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Greptile SummaryThis change delivers the final XHR download progress callback while the The focused XHR repro showed the previous revision lost the final Confidence Score: 5/5The PR is safe to merge; no blocking failure remains. The focused browser and XHR adapter checks passed, and the exercised completion, fallback, cancellation, and listener-exception paths behaved as intended.
What T-Rex did
Reviews (6): Last reviewed commit: "fix(xhr): preserve fallback download pro..." | Re-trigger Greptile |
c80b069 to
b54e649
Compare
Throttled progress deliveries replay events whose dispatch has already finished, so event.currentTarget is null and listeners reading incremental data from it lose every chunk delivered through the throttle timer or the loadend flush. Flush with the still-dispatching loadend event instead, so a final delivery with the complete transfer state always reaches the listener while the event has a live target. The reducer only treats a flush argument with a numeric loaded field as a fresh event; stream errors and abort reasons passed by other adapters keep replaying the last pending event.
The live loadend flush runs user code before settle(), so a throwing onDownloadProgress left the promise pending and cancellation listeners attached. Guard the flush and rethrow asynchronously, matching how listener errors already surface on the throttle timer path.
b54e649 to
693d477
Compare
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Summary
Axios 1.7.0 introduced throttled progress delivery. Timer-deferred XHR progress callbacks therefore receive an event whose DOM dispatch has already ended, so
event.currentTargetisnull. Consumers that incrementally readresponseTextthroughcurrentTargetcan miss every deferred chunk, reproducing #6796 most visibly in Firefox and Safari.This change force-delivers one final
onDownloadProgresscallback from the still-dispatching successful XHRloadendevent. That gives cumulative readers a live target and the complete response while preserving the established behavior of upload, stream-error, abort-reason, and failed-download flushes. XHR implementations withoutonloadendinstead replay their pending progress event through the legacy ready-state fallback.The forced delivery path is explicit rather than inferred from a flush argument's shape. A final callback can also throw or synchronously cancel the request without leaving settlement pending or causing the XHR handler to dereference a cleared request.
Playwright verification covers Chromium, Firefox, and WebKit. A
currentTarget-based reader that previously received only the initial part of a fast stream now recovers the complete payload.Linked issue
Closes #6796
Changes
lib/helpers/throttle.js: keeps ordinaryflushargument-agnostic and adds an explicitflushWithfunction for replacement arguments.lib/helpers/progressEventReducer.js: exposes that explicit forced-delivery function without inspecting stream errors or cancellation reasons.lib/adapters/xhr.js: force-delivers only the successful final download event, replays pending progress in the eventless ready-state fallback, stops cleanly after synchronous cancellation, and preserves prior failure/upload flush behavior.tests/browser/progress.browser.test.js: uses realEventTarget/ProgressEventsemantics and adds eight browser regression tests covering live final delivery, complete streamed reads, the ready-state fallback, cancellation, listener exceptions, status-zero failures, upload failures, and deferredevent.targetbehavior.tests/unit/helpers/progressEventReducer.test.jsandtests/unit/helpers/throttle.test.js: cover explicit forced delivery, pending-event replay, loaded-shaped errors, throwing getters, and the throttle tuple contract.PRE_RELEASE_CHANGELOG.mdandPRE_RELEASE_DOCS.md: record the download-only guarantee and retained failure semantics.Testing
npx eslint lib/adapters/xhr.js lib/helpers/progressEventReducer.js lib/helpers/throttle.jsnpm run test:vitest:unit— 58 files, 1,045 tests passednpm run test:vitest:browser:headless -- tests/browser/progress.browser.test.js— Chromium, Firefox, and WebKit; 42 tests passedChecklist
PRE_RELEASE_DOCS.md; no public type change🏄