fix: XHR adapter/avoid main-thread jank#10831
Conversation
There was a problem hiding this comment.
2 issues found across 4 files
Confidence score: 3/5
- There is a concrete regression risk in
lib/adapters/xhr.js: usingPromise.resolve(config)can treat a config object with its own callablethenas a thenable, which may hijack or block request dispatch. - The
lib/helpers/cookies.jsissue is low severity, but duplicated parsing/decoding inreadAsyncvsreadcould drift over time and cause inconsistent sync/async XSRF cookie behavior. - Given one medium-severity, high-confidence behavior risk (6/10, confidence 9/10), this looks like some merge risk rather than a merge-blocker.
- Pay close attention to
lib/adapters/xhr.jsandlib/helpers/cookies.js- thenable coercion in config resolution and sync/async cookie parsing consistency.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/helpers/cookies.js">
<violation number="1" location="lib/helpers/cookies.js:37">
P3: Duplicate cookie parsing/decoding logic in `readAsync` can drift from `read`, creating inconsistent sync/async XSRF cookie handling.</violation>
</file>
<file name="lib/adapters/xhr.js">
<violation number="1" location="lib/adapters/xhr.js:18">
P2: Wrapping the resolved config in `Promise.resolve()` can treat a config object with an own callable `then` as a thenable and hijack or block request dispatch.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/adapters/xhr.js">
<violation number="1" location="lib/adapters/xhr.js:18">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**
Behavior change in the XHR adapter is shipped without visible regression-test coverage.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Thanks for the patch @John-pillo1124.
This is the right idea, but it doesn't match the constraints from #10826, and there are a few things to address before this can go in.
The fetch adapter changes need to come out:
lib/adapters/fetch.js is explicitly out of scope in the issue. Fetch is already async, so awaiting resolveConfig saves no jank; it just adds a microtask hop on every fetch request for no benefit. Revert this file entirely.
The XHR adapter is a near-total re-indent at +197/-179:
The issue is specific: do this without re-indenting the body. Wrapping everything in a handleConfig callback throws away git blame on the whole file. The shape we want is roughly:
async function (config) {
const _config = await resolveConfig(config);
return new Promise(function dispatchXhrRequest(resolve, reject) {
// existing body unchanged, _config in closure
});
}No tests:
The issue calls this out: we need to mock window.cookieStore and verify the async branch actually runs, since asserting via document.cookie co-stores with cookieStore and doesn't differentiate. At minimum, I want to see:
- cookieStore mock present, XHR request goes out with the XSRF header populated from the raw
cookieStorevalue (no URI decoding). - cookieStore undefined, the existing
document.cookiepath is unchanged. - non-standard-env shim's new
readAsync()returnsPromise.resolve(null)(Web Worker / React Native fallback). cookieStore.getrejection falls through tothis.read(name)cleanly.
That fourth one matters because the .catch(() => this.read(name)) in readAsync swallows every error type silently. That fallback should be deliberate and covered.
resolveConfig is now polymorphic;
It returns the config synchronously on one branch and a Promise of the config on the other, depending on whether window.cookieStore is detected at runtime. That is a foot-gun for any future caller.
- Keep
resolveConfigsynchronous and move the async cookieStore read into the XHR adapter directly, callingcookies.readAsync(xsrfCookieName)and setting the header there. This better matches the "XHR-only" framing in the issue.
A few non-blocking nits while you are in there:
lib/helpers/cookies.js:36thetypeof document === 'undefined'guard inside the standard-browser-env branch is dead code. The export is already gated onplatform.hasStandardBrowserEnv, which meansdocumentexists. Drop it.- Trailing whitespace on several of the new lines (
lib/helpers/cookies.js:36,lib/adapters/fetch.js:154, etc.) will likely fail lint once the full CI matrix runs. - The commit subject
fix: XHR adapter/avoid main-thread jankuses/as a scope separator. Conventional commits wantfix(xhr): avoid main-thread jankor similar. Squash the second fix-up commit into the first as well. - I would also like to see a microbenchmark or trace in the PR description showing the actual jank reduction. The original issue cites the WHATWG discussion as motivation, but doesn't quantify. 1000 sequential same-origin XHR requests with XSRF, before vs after a long task, would be enough to justify the change concretely.
|
Hi, When window.cookieStore exists, resolveConfig() returns a Promise and the XHR adapter defers request.open/send until it resolves, so axios() no longer synchronously dispatches the XHR. This breaks in-repo browser tests (and potentially user code) that assumes the request has been sent immediately after calling axios(). Severity: action required | Category: correctness How to fix: Avoid async XHR dispatch Agent prompt to fix - you can give this to your LLM of choice:
We noticed a couple of other issues in this PR as well - happy to share if helpful. Qodo code review - free for open-source. |
Summary
Implemented asynchronous XSRF cookie reading using the Cookie Store API when available to avoid main-thread jank from synchronous
document.cookieaccess in axios HTTP client.Related issue
fixes: #10826
Problem
document.cookiesynchronously, which blocks the main threaddocument.cookieAPI is a known performance bottleneckSolution
Implemented a jank-avoiding read path for XSRF cookies that:
window.cookieStoreat runtime, no new config optionscookieStore.get()withoutdecodeURIComponentBenefits