Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Nov 18, 2025

Summary by CodeRabbit

  • Chores

    • Reworked server start/request flow with a centralized middleware pipeline, request-scoped start options, and guaranteed router cleanup.
  • Bug Fixes

    • Stricter redirect validation and consistent serialized redirect responses.
    • Accept header validation for HTML requests.
    • Ensure injected-HTML listeners and stream cancellations/timeouts are cleaned up and errors return proper responses.
    • Route loaders now use router origin for API requests.
  • Refactor

    • On-demand server manifest loading and improved SSR state dehydration.
  • New Features

    • Added the /users path to prerendering configuration.
  • Tests

    • Removed prerendering tests/assertions for the users (and related deferred) routes.

@nx-cloud
Copy link

nx-cloud bot commented Nov 18, 2025

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 08391dd

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ❌ Failed 8m 46s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-19 00:27:10 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Reworks the server start flow to build a per-request router via getRouter, merge startOptions with serialization adapters (including ServerFunctionSerializationAdapter), run a middleware pipeline that prioritizes server functions, perform SSR dehydration and header assembly, validate/serialize redirects (x-tsr-redirect), centralize error handling, and reset router on completion.

Changes

Cohort / File(s) Change Summary
Start handler core
packages/start-server-core/src/createStartHandler.ts
Replace global fetch patching and inline router setup with request-scoped getRouter and requestStartOptions (merging startOptions and serialization adapters). Introduce runWithStartContext-based middleware pipeline, server-function-first handling, Accept header validation for HTML, on-demand start manifest loading, SSR dehydration and header assembly, x-tsr-redirect support and redirect validation, centralized error handling, and final router reset in finally.
SSR stream cancellation
packages/router-core/src/ssr/transformStreamWithRouter.ts
Allow passthrough stream to accept an optional onCancel hook, add cancel handling to the ReadableStream to mark destroyed and invoke onCancel, defer assignment and teardown of injected-HTML listener (stopListeningToInjectedHtml), wire final passthrough cancellation to stop listening and clear timeouts, and ensure cleanup runs in finally.
E2E route loaders (use router origin for API calls)
e2e/react-start/basic/src/routes/users.$userId.tsx, e2e/react-start/basic/src/routes/users.tsx, e2e/solid-start/basic/src/routes/users.$userId.tsx, e2e/solid-start/basic/src/routes/users.tsx
Import and use getRouterInstance/getRouter to obtain router and use router.options.origin as baseURL for Axios/API requests instead of relative URLs; minor await/order adjustments.
E2E Vite configs (prerender filter)
e2e/react-start/basic/vite.config.ts, e2e/solid-start/basic/vite.config.ts
Add /users to prerender filter entries and remove an inline comment; no runtime behavior change beyond filter update.
E2E prerendering tests
e2e/react-start/basic/tests/prerendering.spec.ts, e2e/solid-start/basic/tests/prerendering.spec.ts
Remove assertions and test blocks that expected prerendered output for users (and deferred) pages; reduce coverage of those prerender checks.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler as createStartHandler
    participant ReqOpts as requestStartOptions
    participant Router
    participant Context as runWithStartContext
    participant Middleware as Middleware Pipeline
    participant ServerFn as Server Functions
    participant RouterExec as executeRouter
    participant SSR as SSR Dehydrate

    Client->>Handler: incoming HTTP request
    Handler->>ReqOpts: compute requestStartOptions (merge adapters)
    Handler->>Router: getRouter(ReqOpts)
    Router-->>Handler: configured router

    rect rgb(245,250,255)
    Handler->>Context: runWithStartContext(request)
    Context->>Middleware: execute middleware chain
    Middleware->>ServerFn: attempt server-function handling first
    alt server-fn handled
        ServerFn-->>Middleware: Response
    else
        Middleware->>RouterExec: executeRouter -> route resolution
        RouterExec-->>Middleware: route result / redirect / error
    end
    end

    alt Response ready
        Middleware->>SSR: dehydrate SSR state + collect headers
        SSR-->>Handler: final body + headers
        Handler-->>Client: final response
    else Redirect / Error
        Middleware-->>Handler: redirect/error response
        Handler-->>Client: redirect/error
    end

    Note over Handler,Router: finally -> reset router / cleanup
Loading
sequenceDiagram
    participant Stream as PassthroughStream
    participant Listener as InjectedHTMLListener

    Stream->>Listener: subscribe (deferred assignment)
    Client->>Stream: consume
    alt Consumer cancels or times out
        Stream--xListener: onCancel -> stopListeningToInjectedHtml()
    end
    Listener-->>Stream: stop/cleanup invoked
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus on:
    • router lifecycle and final cleanup (resetting router state)
    • merging/deduplication of serialization adapters into requestStartOptions
    • middleware ordering and server-function-first behavior
    • redirect serialization, x-tsr-redirect header handling, and redirect validation
    • SSR dehydration, header collection, and response assembly
    • stream cancellation and injected-HTML listener teardown in transformStreamWithRouter

Possibly related PRs

Suggested reviewers

  • chorobin
  • tannerlinsley

Poem

🐇 I hopped through start and stream with care,
I stitched adapters, trimmed the snare,
When callers cancel, listeners sleep,
Redirects checked before they leap,
Router reset — I bound my lair.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title "fix: memory leaks" is vague and generic; it does not clearly specify which memory leaks are being fixed or what the primary change is. Consider a more specific title such as "fix: memory leaks in stream handling and router cleanup" to better convey the nature of the changes across the codebase.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01557d4 and 08391dd.

📒 Files selected for processing (2)
  • e2e/react-start/basic/tests/prerendering.spec.ts (0 hunks)
  • e2e/solid-start/basic/tests/prerendering.spec.ts (0 hunks)

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 18, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5896

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5896

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 01557d4

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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ca33d7 and d7735af.

📒 Files selected for processing (1)
  • packages/start-server-core/src/createStartHandler.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • packages/start-server-core/src/createStartHandler.ts
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.

Applied to files:

  • packages/start-server-core/src/createStartHandler.ts
🧬 Code graph analysis (1)
packages/start-server-core/src/createStartHandler.ts (5)
packages/router-core/src/ssr/ssr-server.ts (2)
  • getOrigin (227-239)
  • attachRouterServerSsrUtils (100-225)
packages/start-client-core/src/client/ServerFunctionSerializationAdapter.ts (1)
  • ServerFunctionSerializationAdapter (5-16)
packages/start-storage-context/src/async-local-storage.ts (1)
  • runWithStartContext (15-20)
packages/start-server-core/src/router-manifest.ts (1)
  • getStartManifest (10-63)
packages/start-client-core/src/createServerFn.ts (2)
  • flattenMiddlewares (582-606)
  • executeMiddleware (172-241)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (1)
packages/start-server-core/src/createStartHandler.ts (1)

83-125: Router lifecycle, SSR wiring, and redirect handling look solid.

The refactored flow—lazy getRouter initialisation with memory history, per-request runWithStartContext, SSR setup via attachRouterServerSsrUtils + dehydrate, and centralised redirect handling (including manual x-tsr-redirect responses and router.resolveRedirect) is coherent and addresses the previous global/patchy setup. The finally block that clears router.serverSsr and drops the local router reference is an appropriate cleanup step for avoiding per-request SSR state retention.

Also applies to: 137-234, 251-314

Comment on lines 127 to 135
const startOptions: AnyStartInstanceOptions =
(await (await getEntries()).startEntry.startInstance?.getOptions()) ||
({} as AnyStartInstanceOptions)
startOptions.serializationAdapters =
startOptions.serializationAdapters || []
// insert start specific default serialization adapters
startOptions.serializationAdapters.push(
ServerFunctionSerializationAdapter,
)
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 | 🔴 Critical

Avoid per-request growth of serializationAdapters (risk of memory leak).

startOptions.serializationAdapters.push(ServerFunctionSerializationAdapter) mutates the options object on every request. If startInstance.getOptions() returns a shared object (which is likely), this will append the same adapter indefinitely, growing the array and causing duplicated work.

Guard the push with a dedupe check so the adapter is only added once per options object:

-    startOptions.serializationAdapters =
-      startOptions.serializationAdapters || []
-    // insert start specific default serialization adapters
-    startOptions.serializationAdapters.push(
-      ServerFunctionSerializationAdapter,
-    )
+    startOptions.serializationAdapters =
+      startOptions.serializationAdapters || []
+    // insert start specific default serialization adapter once
+    if (
+      !startOptions.serializationAdapters.includes(
+        ServerFunctionSerializationAdapter,
+      )
+    ) {
+      startOptions.serializationAdapters.push(
+        ServerFunctionSerializationAdapter,
+      )
+    }
📝 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.

Suggested change
const startOptions: AnyStartInstanceOptions =
(await (await getEntries()).startEntry.startInstance?.getOptions()) ||
({} as AnyStartInstanceOptions)
startOptions.serializationAdapters =
startOptions.serializationAdapters || []
// insert start specific default serialization adapters
startOptions.serializationAdapters.push(
ServerFunctionSerializationAdapter,
)
const startOptions: AnyStartInstanceOptions =
(await (await getEntries()).startEntry.startInstance?.getOptions()) ||
({} as AnyStartInstanceOptions)
startOptions.serializationAdapters =
startOptions.serializationAdapters || []
// insert start specific default serialization adapter once
if (
!startOptions.serializationAdapters.includes(
ServerFunctionSerializationAdapter,
)
) {
startOptions.serializationAdapters.push(
ServerFunctionSerializationAdapter,
)
}
🤖 Prompt for AI Agents
In packages/start-server-core/src/createStartHandler.ts around lines 127 to 135,
the code mutates startOptions.serializationAdapters on every request which can
grow a shared array and leak memory; instead avoid mutating a potentially shared
options object by either deduplicating before pushing or by replacing the array
with a new one that includes the adapter only once: check if
startOptions.serializationAdapters already contains
ServerFunctionSerializationAdapter (by identity) and only add it if missing, or
assign startOptions.serializationAdapters =
[...(startOptions.serializationAdapters || []),
ServerFunctionSerializationAdapter] only when not present so you never append
duplicates to a shared array.

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

♻️ Duplicate comments (3)
packages/start-server-core/src/createStartHandler.ts (3)

348-350: Critical: Route middleware chain can terminate prematurely when .options.server is missing.

Similar to the request middleware issue, this maps d.options.server without filtering, which can produce undefined entries that prematurely stop middleware execution.

Apply this diff:

   const middlewares = flattenMiddlewares(
     matchedRoutes.flatMap((r) => r.options.server?.middleware).filter(Boolean),
-  ).map((d) => d.options.server)
+  )
+    .map((d) => d.options.server)
+    .filter(Boolean)

376-378: Critical: Handler middleware chain can terminate prematurely when .options.server is missing.

This middleware mapping also lacks filtering for undefined server handlers, creating the same chain termination risk.

Apply this diff:

           if (middleware && middleware.length) {
             middlewares.push(
-              ...flattenMiddlewares(middleware).map((d) => d.options.server),
+              ...flattenMiddlewares(middleware)
+                .map((d) => d.options.server)
+                .filter(Boolean),
             )
           }

240-243: Critical: Middleware chain can terminate prematurely when .options.server is missing.

The middleware pipeline maps through d.options.server without filtering out undefined entries. Since executeMiddleware (line 444) treats a falsy middleware as "end of chain" (if (!middleware) return ctx), any entry where options.server is undefined will stop the pipeline and prevent requestHandlerMiddleware from running.

Apply this diff:

     const flattenedMiddlewares = startOptions.requestMiddleware
       ? flattenMiddlewares(startOptions.requestMiddleware)
       : []
-    const middlewares = flattenedMiddlewares.map((d) => d.options.server)
+    const middlewares = flattenedMiddlewares
+      .map((d) => d.options.server)
+      .filter(Boolean)
🧹 Nitpick comments (1)
packages/start-server-core/src/createStartHandler.ts (1)

257-268: Consider extracting duplicate manual redirect handling.

The manual redirect response logic (lines 257-267 and 298-307) is duplicated. While the control flow is correct (handling already-resolved vs newly-resolved redirects), extracting this to a helper function would improve maintainability.

Example helper:

function createManualRedirectResponse(response: any) {
  return json(
    {
      ...response.options,
      isSerializedRedirect: true,
    },
    {
      headers: response.headers,
    },
  )
}

Then replace both blocks with:

if (request.headers.get('x-tsr-redirect') === 'manual') {
  return createManualRedirectResponse(response)
}

Also applies to: 298-308

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7735af and a6494bd.

📒 Files selected for processing (1)
  • packages/start-server-core/src/createStartHandler.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.

Applied to files:

  • packages/start-server-core/src/createStartHandler.ts
🧬 Code graph analysis (1)
packages/start-server-core/src/createStartHandler.ts (6)
packages/router-core/src/ssr/ssr-server.ts (2)
  • getOrigin (227-239)
  • attachRouterServerSsrUtils (100-225)
packages/start-client-core/src/client/ServerFunctionSerializationAdapter.ts (1)
  • ServerFunctionSerializationAdapter (5-16)
packages/start-server-core/src/index.tsx (2)
  • HEADERS (16-16)
  • attachRouterServerSsrUtils (4-4)
packages/start-storage-context/src/async-local-storage.ts (1)
  • runWithStartContext (15-20)
packages/start-server-core/src/router-manifest.ts (1)
  • getStartManifest (10-63)
packages/start-client-core/src/createServerFn.ts (2)
  • flattenMiddlewares (582-606)
  • executeMiddleware (172-241)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (2)
packages/start-server-core/src/createStartHandler.ts (2)

94-102: Excellent fix for the serializationAdapters memory leak!

Creating a new array with the spread operator instead of mutating the shared startOptions.serializationAdapters properly addresses the memory leak concern from the previous review.


314-318: Good practice: router cleanup in finally block.

Ensuring router is set to null in the finally block helps prevent memory leaks by allowing garbage collection after each request completes or errors.

Comment on lines +179 to +186
return json(
{
error: 'Only HTML requests are supported here',
},
{
status: 500,
},
)
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

Use HTTP 406 instead of 500 for Accept header mismatch.

A 500 status indicates a server error, but rejecting an unsupported media type is a client request issue. HTTP 406 (Not Acceptable) is the standard status code when the server cannot satisfy the Accept header.

Apply this diff:

                 return json(
                   {
                     error: 'Only HTML requests are supported here',
                   },
                   {
-                    status: 500,
+                    status: 406,
                   },
                 )
📝 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.

Suggested change
return json(
{
error: 'Only HTML requests are supported here',
},
{
status: 500,
},
)
return json(
{
error: 'Only HTML requests are supported here',
},
{
status: 406,
},
)
🤖 Prompt for AI Agents
In packages/start-server-core/src/createStartHandler.ts around lines 179 to 186,
the response for requests that don't accept HTML currently returns HTTP 500;
change the response status to 406 (Not Acceptable) to reflect that the client's
Accept header cannot be satisfied. Update the json response call to use status:
406 and keep the error payload the same so the client receives the correct
semantic HTTP status for an unsupported media type.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6494bd and 7b2bbfa.

📒 Files selected for processing (1)
  • packages/router-core/src/ssr/transformStreamWithRouter.ts (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (3)
packages/router-core/src/ssr/transformStreamWithRouter.ts (3)

38-48: LGTM! Clean cancellation propagation.

The onCancel parameter and cancel() handler provide a proper hook for cleanup when the stream is canceled, marking the passthrough as destroyed and propagating the cancellation signal.


141-143: LGTM! Proper subscription cleanup.

Capturing the unsubscribe function in stopListeningToInjectedHtml enables proper cleanup of the event listener, preventing memory leaks from lingering subscriptions.


178-178: LGTM! Ensures cleanup on promise settlement.

The .finally() block ensures the subscription is cleaned up when the injected HTML promise settles, complementing the cancellation cleanup on line 106.

Note: This means cleanup can occur via two paths (cancellation or promise settlement), but the optional chaining and typical unsubscribe idempotency make this safe.

@schiller-manuel schiller-manuel merged commit 03595c8 into main Nov 19, 2025
3 of 5 checks passed
@schiller-manuel schiller-manuel deleted the fix-memory-leaks branch November 19, 2025 00:17
roduyemi pushed a commit to roduyemi/oss-router that referenced this pull request Nov 19, 2025
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