feat: add Undici adapter (follow-up)#10893
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
7 issues found across 8 files
Confidence score: 2/5
- There is a high-confidence runtime risk in
package.json: declaringundicias optional conflicts with static imports in the adapter path, which can trigger module resolution failures in Node.js at runtime. lib/adapters/undici.jshas concrete behavior regressions: basic auth URLs can fail with undiciRequest, and the mixed static/dynamic loading plus per-requestnew Agent()adds both correctness and performance risk.- Test reliability is also shaky in
tests/unit/adapters/undici.test.jsbecause undefined helper references can causeReferenceErrorbefore adapter logic is exercised, reducing confidence in coverage of these changes. - Pay close attention to
package.json,lib/adapters/undici.js,tests/unit/adapters/undici.test.js- runtime dependency resolution, auth handling, and test validity need fixes before merge.
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/undici.js">
<violation number="1" location="lib/adapters/undici.js:2">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**
Contradictory static and dynamic `undici` loading creates impossible fallback logic. The file static-imports `{ Agent, interceptors } from 'undici'` at module load time, so the module will already fail to load if `undici` is unavailable. Despite this, a runtime `try/catch` around `await import('undici')` assigns `undici = null` on failure, but the code then unconditionally accesses `undici.fetch`, `undici.request`, `undici.Request`, and `undici.Response` — making the null fallback non-functional and unreachable. This is unnecessary defensive handling for an impossible edge case.</violation>
<violation number="2" location="lib/adapters/undici.js:33">
P2: Per-request `new Agent()` causes avoidable connection pool churn and resource overhead</violation>
<violation number="3" location="lib/adapters/undici.js:105">
P1: Basic auth fails because undici `Request` rejects URLs containing credentials. The adapter should sanitize URL credentials before request construction.</violation>
</file>
<file name="tests/unit/adapters/undici.test.js">
<violation number="1" location="tests/unit/adapters/undici.test.js:589">
P2: Abort-normalization test references helpers that are not defined or imported in this file, so it will fail with ReferenceError before exercising the adapter.</violation>
<violation number="2" location="tests/unit/adapters/undici.test.js:686">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**
The test claims to validate AxiosError/ERR_NETWORK handling, but it only checks the nested cause code and never verifies the Axios error shape it names.</violation>
</file>
<file name="package.json">
<violation number="1" location="package.json:53">
P2: Missing `exports` entry for new undici adapter subpath</violation>
<violation number="2" location="package.json:170">
P0: Optional peer dependency `undici` will cause runtime module resolution failures in Node.js because `lib/adapters/undici.js` statically imports `undici` at the top level, and `lib/adapters/adapters.js` statically imports the undici adapter module. This means any Node.js user importing axios without `undici` installed will get `Cannot find module 'undici'` at startup.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| "selfsigned": "^5.5.0", | ||
| "stream-throttle": "^0.1.3", | ||
| "typescript": "^5.9.3", | ||
| "undici": "^8.2.0", |
There was a problem hiding this comment.
P0: Optional peer dependency undici will cause runtime module resolution failures in Node.js because lib/adapters/undici.js statically imports undici at the top level, and lib/adapters/adapters.js statically imports the undici adapter module. This means any Node.js user importing axios without undici installed will get Cannot find module 'undici' at startup.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At package.json, line 170:
<comment>Optional peer dependency `undici` will cause runtime module resolution failures in Node.js because `lib/adapters/undici.js` statically imports `undici` at the top level, and `lib/adapters/adapters.js` statically imports the undici adapter module. This means any Node.js user importing axios without `undici` installed will get `Cannot find module 'undici'` at startup.</comment>
<file context>
@@ -165,8 +167,17 @@
"selfsigned": "^5.5.0",
"stream-throttle": "^0.1.3",
"typescript": "^5.9.3",
+ "undici": "^8.2.0",
"vitest": "^4.1.5"
},
</file context>
| formData: () => asResponse().formData() | ||
| }; | ||
| }, | ||
| Request: undici.Request, |
There was a problem hiding this comment.
P1: Basic auth fails because undici Request rejects URLs containing credentials. The adapter should sanitize URL credentials before request construction.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/adapters/undici.js, line 105:
<comment>Basic auth fails because undici `Request` rejects URLs containing credentials. The adapter should sanitize URL credentials before request construction.</comment>
<file context>
@@ -0,0 +1,114 @@
+ formData: () => asResponse().formData()
+ };
+ },
+ Request: undici.Request,
+ Response: undici.Response
+ }
</file context>
| @@ -50,12 +50,14 @@ | |||
| "browser": { | |||
There was a problem hiding this comment.
P2: Missing exports entry for new undici adapter subpath
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At package.json, line 53:
<comment>Missing `exports` entry for new undici adapter subpath</comment>
<file context>
@@ -50,12 +50,14 @@
"browser": {
"./dist/node/axios.cjs": "./dist/browser/axios.cjs",
"./lib/adapters/http.js": "./lib/helpers/null.js",
+ "./lib/adapters/undici.js": "./lib/helpers/null.js",
"./lib/platform/node/index.js": "./lib/platform/browser/index.js",
"./lib/platform/node/classes/FormData.js": "./lib/helpers/null.js"
</file context>
There was a problem hiding this comment.
I don’t understand the issue here? This should be nullable since it’s not available in browser?
|
|
||
| it('should not classify a user-initiated abort as a timeout', async () => { | ||
| const safariFetch = (url, init) => { | ||
| const signal = getFetchSignal(url, init); |
There was a problem hiding this comment.
P2: Abort-normalization test references helpers that are not defined or imported in this file, so it will fail with ReferenceError before exercising the adapter.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/unit/adapters/undici.test.js, line 589:
<comment>Abort-normalization test references helpers that are not defined or imported in this file, so it will fail with ReferenceError before exercising the adapter.</comment>
<file context>
@@ -0,0 +1,1034 @@
+
+ it('should not classify a user-initiated abort as a timeout', async () => {
+ const safariFetch = (url, init) => {
+ const signal = getFetchSignal(url, init);
+
+ return new Promise((_resolve, reject) => {
</file context>
| } | ||
| }); | ||
|
|
||
| it('should remove manually set Content-Type without boundary for FormData', async () => { |
There was a problem hiding this comment.
This one fails, currently don’t know why
| } | ||
| }); | ||
|
|
||
| it('should preserve Content-Type if it already has boundary', async () => { |
There was a problem hiding this comment.
This one fails, currently don’t know why
| }); | ||
| }); | ||
|
|
||
| it('should support basic auth', async () => { |
There was a problem hiding this comment.
This one fails probably because Request is initialized with auth information which is forbidden. Error is TypeError: Request cannot be constructed from a URL that includes credentials: http://foo@localhost:8010/
This should be probbably be handled at adapter level to omit that information and pass it as request initialization options.
I’m curious why this doesn’t fail in fetch adapter test with native Request.
There was a problem hiding this comment.
This is actually tested wrong infetch adapter test suite: https://github.com/axios/axios/blob/v1.x/tests/unit/adapters/fetch.test.js#L468
It uses default Axios instead instead of Fetch adapter instance so it always passes.
I went ahead and created PR with adjusted test and new tests based on http adapter and URL parsing for fetch adapter case. This should also fix Undici test.
| formData: () => asResponse().formData() | ||
| }; | ||
| }, | ||
| Request: undici.Request, |
There was a problem hiding this comment.
I think we could squeeze even more performance by avoiding creating Request/Response instances for undici.request path since it produces basic response?
|
@niksy I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 8 files
Confidence score: 3/5
- There is a concrete regression risk in
lib/adapters/adapters.js: registeringundicias a direct adapter function instead of a lazy resolver object can change adapter fallback behavior at runtime. - Given the medium severity (6/10) and solid confidence (7/10), this is more than a cosmetic issue and could affect users depending on fallback adapter selection.
- Pay close attention to
lib/adapters/adapters.js- ensureundicifollows the same lazy resolver pattern asfetchso fallback semantics remain intact.
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/adapters.js">
<violation number="1" location="lib/adapters/adapters.js:23">
P2: `undici` is registered as a direct adapter function instead of a lazy resolver object like `fetch`, breaking adapter fallback semantics</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| fetch: { | ||
| get: fetchAdapter.getFetch, | ||
| }, | ||
| undici: undiciAdapter |
There was a problem hiding this comment.
P2: undici is registered as a direct adapter function instead of a lazy resolver object like fetch, breaking adapter fallback semantics
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/adapters/adapters.js, line 23:
<comment>`undici` is registered as a direct adapter function instead of a lazy resolver object like `fetch`, breaking adapter fallback semantics</comment>
<file context>
@@ -19,6 +20,7 @@ const knownAdapters = {
fetch: {
get: fetchAdapter.getFetch,
},
+ undici: undiciAdapter
};
</file context>
| undici: undiciAdapter | |
| undici: { | |
| get: (config) => undiciAdapter.get?.(config) ?? undiciAdapter | |
| } |
There was a problem hiding this comment.
Is this relevant? Undici adapter uses Fetch adapter getFetch method underneath.
| i = 1; | ||
| } | ||
|
|
||
| // option is ignored in undici.fetch |
There was a problem hiding this comment.
I’m not happy about this. It basically means we have different behavior based on requested response type. How should we handle this?
|
@niksy can you please look at resolving the comments if they have been fixed or leave a comment as to why you disagree |
|
@jasonsaayman will do! I’m running same benchmark as in original PR (https://github.com/nodejs/undici/tree/main/benchmarks), but unfortunately I’m not seeing any of the large gains from Should we also take a look at this before merging this PR? Does it make sense to contact someone from Undici to see if there is something we could do to improve our implementation? |
|
@niksy I will see if I can get some eyes on this from a Node maintainer. |
Summary
This PR is a follow-up to #6915, with up-to-date changes and minimal implementation using custom Fetch adapter.
Test suite is adapted from original PR and adjusted with latest fetch test suite changes, but there are 3 tests that are currently failing. I’ve marked them with comments so we can easily see and discuss potential changes.
This is the content diff between two test suites: https://www.diffchecker.com/hXpATVrd/
I’ve tried to omit what isn’t necessary for Undici test, but I don’t know if I missed something important.
Linked issue
Issue #10885 tracks all the necessary changes.
Checklist
index.d.tsandindex.d.cts)Summary by cubic
Adds a Node-only
undiciadapter that aligns Axios behavior with Undici’s request/fetch pipeline for redirects, timeouts, progress, streaming, and size limits. Redirects are enforced via Undici’s global dispatcher with consistent error mapping across bothundici.requestandundici.fetch.Description
lib/adapters/undici.js, registered it asundiciinlib/adapters/adapters.js; README and types include'undici'.undici; Node-only by aliasing to./lib/helpers/null.jsin browser/RN builds.undici.request; switches toundici.fetchonly when needed (progress,maxContentLength, orresponseType:stream/response/formdata/blob).maxRedirects; normalizes “too many redirects” toAxiosError.ERR_FR_TOO_MANY_REDIRECTSfor both paths.ETIMEDOUT; maps DNSENOTFOUNDtoAxiosError: Network Errorwithcause.maxBodyLength/maxContentLength(including chunked anddata:URLs); sanitizes headers; auto-sets FormDataContent-Type; sets defaultUser-Agenttoaxios/<version>without overriding user-provided values.undici@^8(Node >= 22.19); added as dev dep for tests.Docs
Add a new
/docs/page for theundiciadapter covering: install (npm i undici), usage (adapter: 'undici'), Node-only scope and minimum Node version, lazy import behavior, whenrequestvsfetchis used, redirect/timeout normalization, progress/streaming, size limits, and default/overrideableUser-Agent.Testing
Added
tests/unit/adapters/undici.test.js:Content-Type; default/customUser-Agent.ETIMEDOUT) and cancel vs timeout classification.maxBodyLength,maxContentLength(including chunked anddata:URLs).maxRedirectsvia Undici’s global dispatcher interceptor and consistentERR_FR_TOO_MANY_REDIRECTSmapping for thefetchpath.Semantic version impact
Minor: adds a new adapter and type literal (
'undici') without breaking existing APIs.Written for commit f14bbf2. Summary will update on new commits.