Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Jan 24, 2026

Summary by CodeRabbit

  • New Features

    • Added a fast-path streaming mode for server-side rendering to bypass heavy buffering when serialization is complete.
  • Improvements

    • Production-only caching for route filtering to reduce work on repeated requests.
    • More robust streaming behavior: safer cleanup, better error handling, injection/barrier handling, and a lifetime safety timeout.
  • Tests

    • Added/updated end-to-end assertions to verify initial HTML contains the stream barrier and router bootstrap markers.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

📝 Walkthrough

Walkthrough

Adds e2e assertions for TSR markers and a stream barrier; introduces production-aware manifest filtering with caching and script-wrapping changes in ssr-server; and massively refactors transformStreamWithRouter with a fast-path, buffered injection, barrier detection, robust cleanup, and lifetime/error handling.

Changes

Cohort / File(s) Summary
E2E Streaming SSR Tests
e2e/react-start/streaming-ssr/tests/fast-serial.spec.ts, e2e/react-start/streaming-ssr/tests/sync-only.spec.ts
Adds assertions that initial HTML includes TSR markers ($_TSR, $_TSR.router, $_TSR.e()) and the $tsr-stream-barrier placeholder.
SSR Server Implementation
packages/router-core/src/ssr/ssr-server.ts
Adds production-aware manifest route filtering with an LRU/WeakMap cache, introduces TSR_PREFIX/P_PREFIX/P_SUFFIX wrappers for serialization, changes injected HTML buffering from array to string, and optimizes ScriptBuffer.takeAll.
Stream Transform Pipeline
packages/router-core/src/ssr/transformStreamWithRouter.ts
Adds fast-path passthrough when serialization is finished, full buffered streaming path with pendingRouterHtml/leftover handling, barrier and closing-tag detection across chunks, idempotent cleanup, lifetime timeout safety, and enhanced error handling.

Sequence Diagram(s)

sequenceDiagram
    participant AppStream as App Stream
    participant Transform as transformStreamWithRouter
    participant ServerSSR as SSR Server
    participant Consumer as Downstream

    alt Serialization finished & no buffered HTML (fast path)
        AppStream->>Transform: read chunks
        Transform->>Consumer: passthrough bytes
        Transform->>ServerSSR: minimal cleanup
    else Serialization in progress (buffered path)
        AppStream->>Transform: stream chunks
        Transform->>Transform: decode & accumulate leftover
        Transform->>ServerSSR: onInjectedHtml / onSerializationFinished events
        Transform->>Transform: detect $tsr-stream-barrier and closing tags
        Transform->>Consumer: flush buffered HTML + injected scripts
        Transform->>ServerSSR: flush scripts synchronously
        Transform->>Transform: perform idempotent cleanup
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ssr

Poem

🐰 I hopped through streams both fast and slow,
I nudged the barrier where the routers go,
I wrapped the scripts in tidy seams,
I buffered bytes and chased the beams,
Hop hop — SSR hums, ready to flow!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: streaming perf' directly describes the main changes across the pull request, which focus on refactoring streaming performance optimizations in SSR-related files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Jan 24, 2026

View your CI Pipeline Execution ↗ for commit 484be5e

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 12m 51s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 36s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-24 22:29:01 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 24, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6496

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@6496

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6496

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@6496

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@6496

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@6496

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@6496

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6496

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6496

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6496

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@6496

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@6496

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@6496

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@6496

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@6496

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@6496

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@6496

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@6496

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@6496

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@6496

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@6496

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@6496

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6496

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6496

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6496

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6496

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6496

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6496

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6496

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@6496

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6496

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6496

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6496

@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@6496

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@6496

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@6496

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6496

@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@6496

@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@6496

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6496

commit: 484be5e

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Comment on lines +179 to +187
;(async () => {
const reader = appStream.getReader()
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
if (cleanedUp || isStreamClosed) return
controller?.enqueue(value as unknown as Uint8Array)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's look at the file structure and line count
wc -l packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Read the relevant section of the file to understand context
sed -n '170,200p' packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 841


🏁 Script executed:

#!/bin/bash
# Find all call sites for transformStreamWithRouter and related functions
rg -n --type=ts --type=tsx 'transformStreamWithRouter|transformReadableStreamWithRouter|transformPipeableStreamWithRouter' --max-count=50

Repository: TanStack/router

Length of output: 86


🏁 Script executed:

#!/bin/bash
# Find all call sites - try without tsx type specification
rg -n 'transformStreamWithRouter|transformReadableStreamWithRouter|transformPipeableStreamWithRouter' --max-count=50

Repository: TanStack/router

Length of output: 2469


🏁 Script executed:

#!/bin/bash
# Look at the beginning of the file to understand imports and the function signature
head -80 packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 2455


🏁 Script executed:

#!/bin/bash
# Check where appStream comes from - look for the function definition
sed -n '1,120p' packages/router-core/src/ssr/transformStreamWithRouter.ts | tail -100

Repository: TanStack/router

Length of output: 3260


🏁 Script executed:

#!/bin/bash
# Continue reading the transformStreamWithRouter function, especially around the fast path
sed -n '100,250p' packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 4515


🏁 Script executed:

#!/antml:bin/bash
# Check the call sites to understand what stream types are being passed
grep -A5 'transformReadableStreamWithRouter\|transformPipeableStreamWithRouter' packages/react-router/src/ssr/renderRouterToStream.tsx | head -40

Repository: TanStack/router

Length of output: 739


🏁 Script executed:

#!/bin/bash
# Check what ReadableStream type is being used and its chunk types
grep -B5 -A10 'appStream\.getReader\|appStream\.read' packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 819


🏁 Script executed:

#!/bin/bash
# Search for tests related to transformStreamWithRouter to see if non-Uint8Array types are tested
rg -n 'transformStreamWithRouter|getReader|\.read\(' --type=ts packages/router-core/src/ssr -A3 | head -80

Repository: TanStack/router

Length of output: 4794


🏁 Script executed:

#!/bin/bash
# Check if there are any test files for this module
find . -name '*transform*stream*' -o -name '*ssr*test*' | grep -i test

Repository: TanStack/router

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Look for how renderToReadableStream or renderToPipeableStream work - these feed the appStream
rg -n 'renderToReadableStream|renderToPipeableStream|toWeb\(' packages/react-router/src/ssr -B2 -A2 | head -60

Repository: TanStack/router

Length of output: 2940


🏁 Script executed:

#!/bin/bash
# Check the ReadableStream generic type in the slow path more carefully
sed -n '245,400p' packages/router-core/src/ssr/transformStreamWithRouter.ts | grep -A10 "const stream = new ReadableStream"

Repository: TanStack/router

Length of output: 249


🏁 Script executed:

#!/antml:bin/bash
# Search for RawStream usage to see if it handles different chunk types
sed -n '190,220p' packages/router-core/src/ssr/serializer/RawStream.ts

Repository: TanStack/router

Length of output: 933


🏁 Script executed:

#!/bin/bash
# Check the slow path implementation to see exactly how it handles value type
sed -n '384,410p' packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 919


🏁 Script executed:

#!/antml:bin/bash
# Verify the ReadableStream input type annotation - no generic parameter?
sed -n '100,110p' packages/router-core/src/ssr/transformStreamWithRouter.ts

Repository: TanStack/router

Length of output: 475


Validate fast-path chunk types to match output stream contract.
The fast-path passthrough enqueues chunks with an unsafe as unknown as Uint8Array cast, while the slow path explicitly guards with instanceof Uint8Array. Since the output stream is declared as ReadableStream<Uint8Array>, the fast path should validate incoming chunk types to ensure type safety consistency.

🔧 Proposed fix
-          controller?.enqueue(value as unknown as Uint8Array)
+          if (typeof value === 'string') {
+            controller?.enqueue(textEncoder.encode(value))
+          } else if (value instanceof Uint8Array) {
+            controller?.enqueue(value)
+          } else if (value instanceof ArrayBuffer) {
+            controller?.enqueue(new Uint8Array(value))
+          } else {
+            controller?.enqueue(textEncoder.encode(String(value)))
+          }

@schiller-manuel schiller-manuel merged commit ba47d4d into main Jan 24, 2026
6 checks passed
@schiller-manuel schiller-manuel deleted the streaming-perf branch January 24, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants