Skip to content

@cacheable/net - fix: send FormData/Blob correctly using the runtime's own fetch#1636

Merged
jaredwray merged 3 commits into
mainfrom
claude/fix-formdata-coercion-C9bty
May 13, 2026
Merged

@cacheable/net - fix: send FormData/Blob correctly using the runtime's own fetch#1636
jaredwray merged 3 commits into
mainfrom
claude/fix-formdata-coercion-C9bty

Conversation

@jaredwray
Copy link
Copy Markdown
Owner

Summary

@cacheable/net@2.0.7 silently coerced FormData to its toString() value ("[object FormData]") and sent it with Content-Type: text/plain;charset=UTF-8. Native fetch handles the same input correctly with multipart/form-data; boundary=….

Root cause

fetch.ts imported fetch from the standalone undici@^7 package. Node 18+ ships its own bundled undici (6.x in our test env) that produces globalThis.FormData, globalThis.Blob, globalThis.File, globalThis.ReadableStream. The standalone undici@7's fetch checks value instanceof <its own FormData>. The user-constructed new FormData() is from Node's bundled realm, so that check fails and the body falls through to a string coercion — "[object FormData]" — with text/plain.

Fix

Use globalThis.fetch internally. Now the fetch implementation and the body classes (FormData, Blob, File, URLSearchParams, ReadableStream) come from the same realm, and every BodyInit shape is recognized correctly:

Body Before After
FormData text/plain + "[object FormData]" multipart/form-data; boundary=… + real multipart
FormData (with File) text/plain + "[object FormData]" multipart/form-data with filename="…"
URLSearchParams (worked) application/x-www-form-urlencoded;charset=UTF-8
Blob (worked) declared type preserved
string, JSON object (worked) unchanged

undici's RequestInit and Response types are still imported as type-only so the public API surface is unchanged.

Test plan

  • Local-server regression tests assert the wire-level Content-Type and body for FormData, FormData containing File, URLSearchParams, Blob, and a JSON object across post, patch, del
  • All 7 new tests pass; type-check clean; pnpm build clean (CJS + ESM + DTS)
  • No regressions in the existing FormData/URLSearchParams/Blob coverage
  • CI: full test suite against the mockhttp service

https://claude.ai/code/session_01XVcwdY82D89NiYwinHSBvA


Generated by Claude Code

…he runtime's own fetch

The package imported fetch from a standalone undici version whose
FormData/Blob/ReadableStream classes differ from Node's bundled
globals. Its instanceof checks rejected the user-constructed globals
and the body fell through to string coercion, so requests went out as
"[object FormData]" with Content-Type: text/plain instead of
multipart/form-data.

Switch the internal fetch to globalThis.fetch so the body classes and
fetch implementation share a realm, and add a local-server regression
test that asserts the wire-level body and content-type for FormData,
FormData containing a File, URLSearchParams, Blob, and JSON across
post/patch/del.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request switches from using a standalone undici fetch to the runtime's global fetch to ensure compatibility with global body classes like FormData, preventing incorrect coercion to strings. It also adds a suite of regression tests using a local HTTP server to verify correct body and header handling for various data types. Feedback suggests binding the captured fetch to globalThis for improved context safety and expanding the input type to support URL objects.

Comment thread packages/net/src/fetch.ts Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e61e79a) to head (da0b404).

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #1636   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           26        26           
  Lines         2496      2497    +1     
  Branches       554       555    +1     
=========================================
+ Hits          2496      2497    +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jaredwray jaredwray merged commit 7cbe243 into main May 13, 2026
10 checks passed
@jaredwray jaredwray deleted the claude/fix-formdata-coercion-C9bty branch May 13, 2026 01:14
@jaredwray jaredwray mentioned this pull request May 16, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants