fix(client-js,react): support HeadersInit for custom client headers#15366
fix(client-js,react): support HeadersInit for custom client headers#15366aryanbarde80 wants to merge 2 commits intomastra-ai:mainfrom
Conversation
🦋 Changeset detectedLatest commit: ff0c3e1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Thank you for your contribution! Please ensure that your PR fixes an existing issue and that you have linked it in the description (e.g. with We use CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback. Addressing CodeRabbit's feedback will greatly increase the chances of your PR being merged. We appreciate your understanding and cooperation in helping us maintain high code quality standards. Comment |
WalkthroughExpands Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client-sdks/client-js/src/resources/base.ts`:
- Around line 18-20: The current Array.isArray(headers) branch uses
Object.fromEntries(headers) which drops duplicate header keys; instead construct
a Headers instance from the tuple array (new Headers(headers)) and use its
entries() to build the resulting plain object so duplicate names are combined
per the Fetch spec (comma+space). Update the branch that handles
Array.isArray(headers) to create a Headers from the tuples and then produce the
output object from Headers.entries() rather than calling Object.fromEntries on
the raw tuples; reference the Array.isArray(headers) check, Headers, and
Object.fromEntries when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b4a2e8ad-1244-46c3-a256-e73e844c0a1e
📒 Files selected for processing (6)
.changeset/slow-snails-breathe.mdclient-sdks/client-js/README.mdclient-sdks/client-js/src/resources/base.test.tsclient-sdks/client-js/src/resources/base.tsclient-sdks/client-js/src/types.tsclient-sdks/react/src/mastra-client-context.tsx
| if (Array.isArray(headers)) { | ||
| return Object.fromEntries(headers); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In the Fetch standard (or MDN), when constructing new Headers([['accept','application/json'], ['accept','text/plain']]), what does headers.get('accept') return, and how are duplicate tuple header names handled?
💡 Result:
When constructing new Headers([['accept','application/json'], ['accept','text/plain']]), headers.get('accept') returns "application/json, text/plain". Duplicate tuple header names are handled by treating the Headers object as a multimap that allows multiple values per name. The constructor processes the sequence of tuples by appending each (name, value) pair to the internal header list using the append algorithm, which adds each new value as a separate header entry. The get(name) method then returns all values for that name (case-insensitive match), joined by ", " (comma followed by space), in the order they were added. This behavior aligns with HTTP semantics where multiple headers with the same name are combined into a single comma-separated list for most headers (except Set-Cookie, which has special handling via getSetCookie). MDN confirms get returns all values as a single string when multiple are present, with an example for Accept-Encoding showing "deflate, gzip". The Fetch spec's header list supports duplicates internally, and get combines them.
Citations:
- 1: https://developer.mozilla.org/docs/web/api/headers
- 2: https://developer.mozilla.org/en-US/docs/Web/API/Headers
- 3: https://fetch.spec.wintercg.org/
- 4: https://developer.mozilla.org/en-US/docs/Web/API/Headers/getAll
- 5: https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers
🏁 Script executed:
# Look at the full context of the normalizeHeaders function
cat -n client-sdks/client-js/src/resources/base.ts | head -30Repository: mastra-ai/mastra
Length of output: 1008
🏁 Script executed:
# Search for how normalizeHeaders is used downstream
rg -A 5 -B 2 "normalizeHeaders" client-sdks/client-js/src/resources/base.tsRepository: mastra-ai/mastra
Length of output: 573
Normalize duplicate tuple headers per HTTP specification.
At line 19, Object.fromEntries(headers) drops repeated header names (last-write-wins), misaligning with how the Headers instanceof branch handles them. The Fetch standard combines duplicate header names with ", " (comma-space). Use new Headers(headers).entries() to maintain consistency.
Proposed fix
if (Array.isArray(headers)) {
- return Object.fromEntries(headers);
+ return Object.fromEntries(new Headers(headers).entries());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (Array.isArray(headers)) { | |
| return Object.fromEntries(headers); | |
| } | |
| if (Array.isArray(headers)) { | |
| return Object.fromEntries(new Headers(headers).entries()); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client-sdks/client-js/src/resources/base.ts` around lines 18 - 20, The
current Array.isArray(headers) branch uses Object.fromEntries(headers) which
drops duplicate header keys; instead construct a Headers instance from the tuple
array (new Headers(headers)) and use its entries() to build the resulting plain
object so duplicate names are combined per the Fetch spec (comma+space). Update
the branch that handles Array.isArray(headers) to create a Headers from the
tuples and then produce the output object from Headers.entries() rather than
calling Object.fromEntries on the raw tuples; reference the
Array.isArray(headers) check, Headers, and Object.fromEntries when making the
change.
Description
Allow client SDK custom headers to use the full fetch
HeadersInitshapes instead of only plain string maps.This updates both typing and runtime behavior so callers can pass:
HeadersinstancesIt also fixes the React provider merge path so local dev header injection still works with non-object header inputs.
Related Issue(s)
Fixes the client SDK custom headers typing/runtime issue.
Type of Change
Checklist
ELI5
The PR lets developers pass custom HTTP headers to the Mastra SDK in more flexible ways—not just as a simple list of pairs, but also as JavaScript objects or arrays. It also fixes a bug where the React version couldn't properly add its own dev headers when developers used these flexible formats.
Changes
Core Type Updates
ClientHeaderstype alias equal toHeadersInitinclient-sdks/client-js/src/types.tsClientOptions.headersandRequestOptions.headersfromRecord<string, string>toClientHeaders, enabling support for:Headersinstances[key, value][]Header Normalization
normalizeHeaders()helper function inclient-sdks/client-js/src/resources/base.tsto convert anyHeadersInitform into aRecord<string, string>BaseResource.request()to use this helper when merging client-level and request-level headerscontent-type: application/jsonheader setting for POST/PUT/PATCH/DELETE requests withoutFormDataReact Provider Fix
MastraClientProviderprops type to acceptClientHeadersinstead ofRecord<string, string>Headersinstance, then normalizing back to object formTesting & Documentation
BaseResourcewithHeadersinstance in client optionsHeadersInitsupport for theheadersconfiguration optionImpact