fix(router-core): avoid null bytes in dehydrated SSR match ids#7654
fix(router-core): avoid null bytes in dehydrated SSR match ids#7654VihaanAgarwal wants to merge 2 commits into
Conversation
dehydrateSsrMatchId replaced "/" with U+0000 so dehydrated ids would not look like crawlable URLs (TanStack#6739). U+0000 is forbidden in the HTML input stream though, so the inlined hydration payload tripped a control-character-in-input-stream parse error and failed markup validation. Encode with U+FFFD instead, which is valid in HTML and which hydrateSsrMatchId already decodes back to "/".
The codec keeps the U+0000 -> "/" decode branch so SSR payloads from a deploy before the U+FFFD switch still hydrate. That branch was the one path the suite didn't exercise, so add a test for it.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthrough
SSR Match-ID Null-Byte Fix
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
Summary
dehydrateSsrMatchIdencodes match IDs for the SSR hydration payload by replacing/with\0. That encoding came from #6739, so the dehydrated ids stop looking like relative URLs that crawlers pick up as phantom pages.The catch is that U+0000 is forbidden in the HTML input stream (the
control-character-in-input-streamparse error in the HTML spec). The dehydrated ids are inlined into the$tsr-stream-barrier<script>, so every SSR response ends up with raw null bytes and fails markup validation.validator.w3.orgreportsSaw U+0000 in stream(#7581). It only works in browsers today because the parser silently rewrites those null bytes to U+FFFD, which is exactly whyhydrateSsrMatchIdalready carries a� -> /fallback.This swaps the delimiter from
\0to�(U+FFFD REPLACEMENT CHARACTER):/, so the SSR dehydrated match IDs are URL-shaped strings — Google crawls them as phantom URLs #6739 crawler fix holdshydrateSsrMatchIdalready decodes� -> /, so the client side needs no changeThe existing
\0 -> /decode branch is left in place so any payload still carrying a null byte keeps round-tripping.Testing
Added a codec test asserting the dehydrated id has no C0 control characters. It fails on
mainand passes with this change.nx run @tanstack/router-core:test:unit(39 files, 1179 passed)nx run @tanstack/router-core:test:typesnx run @tanstack/router-core:test:eslintnx run @tanstack/router-core:buildFixes #7581
Summary by CodeRabbit
Bug Fixes
Tests