Skip to content

[feat]: Add stable IDs for server functions#7693

Open
LadyBluenotes wants to merge 20 commits into
mainfrom
stable-serverfn-ids-2
Open

[feat]: Add stable IDs for server functions#7693
LadyBluenotes wants to merge 20 commits into
mainfrom
stable-serverfn-ids-2

Conversation

@LadyBluenotes

@LadyBluenotes LadyBluenotes commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features
    • Added optional support for manually providing a custom id when creating server functions for stable identifier-based runtime lookup, including through chaining and locally named re-exports.
  • Bug Fixes
    • Improved server-function ID reservation, deduping, and lifecycle across recompilations (including HMR) to prevent stale/duplicate IDs.
    • Detects and rejects computed or non-statically analyzable id shapes, and enforces collision rules for manual/manual and manual/generated cases.
  • Documentation
    • Added a guide for manual server function IDs, including collision behavior and security caveats.
  • Tests
    • Expanded type-level and compiler/runtime coverage for manual IDs, collisions, and invalidation/reuse behavior.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7380239-dbf3-4059-904c-b2d52fd82f48

📥 Commits

Reviewing files that changed from the base of the PR and between b96a0c6 and 148dfab.

📒 Files selected for processing (1)
  • packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts

📝 Walkthrough

Walkthrough

This PR adds optional id support to createServerFn and threads explicit server-function IDs through compiler reservation, invalidation cleanup, docs, and tests.

Changes

Manual server-function ID reservation

Layer / File(s) Summary
Public id option surface
packages/start-client-core/src/createServerFn.ts, packages/start-client-core/src/tests/createServerFn.test-d.ts, packages/start-plugin-core/src/start-compiler/types.ts
createServerFn option types gain optional id?: string, CompilationContext gains reserveFunctionId, and the type test asserts a createServerFn({ id }) call exposes handler.
Compiler reservation lifecycle
packages/start-plugin-core/src/start-compiler/compiler.ts
StartCompiler tracks reserved manual IDs, updates ID generation and deduplication, clears reservations on module invalidation, and exposes the new context handler.
Manual id extraction and handler wiring
packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
handleCreateServerFn extracts static id options, validates the format, and reserves explicit IDs when present.
ID behavior tests and docs
packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts, docs/start/framework/react/guide/server-functions.md
The plugin tests cover manual ID extraction, unsupported computed keys, generated ID collisions, reserved-ID reuse and release, dev-mode encoding, and the docs describe manual server-function IDs and collision rules.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through IDs and found a name,
createServerFn now plays the same.
Reserved and checked, then set in place,
HMR can clear its little trace.
I nibble tests and carrots with glee! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding stable/manual IDs for server functions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stable-serverfn-ids-2

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.

❤️ Share

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

@nx-cloud

nx-cloud Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 148dfab

Command Status Duration Result
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 17s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-26 14:08:49 UTC

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

1 package(s) bumped directly, 13 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/start-client-core 1.170.12 → 1.170.13 Changeset
@tanstack/react-start 1.168.26 → 1.168.27 Dependent
@tanstack/react-start-client 1.168.14 → 1.168.15 Dependent
@tanstack/react-start-rsc 0.1.25 → 0.1.26 Dependent
@tanstack/react-start-server 1.167.20 → 1.167.21 Dependent
@tanstack/solid-start 1.168.26 → 1.168.27 Dependent
@tanstack/solid-start-client 1.168.14 → 1.168.15 Dependent
@tanstack/solid-start-server 1.167.20 → 1.167.21 Dependent
@tanstack/start-plugin-core 1.171.18 → 1.171.19 Dependent
@tanstack/start-server-core 1.169.15 → 1.169.16 Dependent
@tanstack/start-static-server-functions 1.167.17 → 1.167.18 Dependent
@tanstack/vue-start 1.168.25 → 1.168.26 Dependent
@tanstack/vue-start-client 1.167.17 → 1.167.18 Dependent
@tanstack/vue-start-server 1.167.20 → 1.167.21 Dependent

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Benchmarks

  • Commit: 3a31bfbaf845
  • Measured at: 2026-06-26T14:09:20.129Z
  • Baseline source: history:ba52d2b8f9e9
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Initial gzip Raw Brotli Trend
react-router.minimal 87.33 KiB 0 B (0.00%) 87.19 KiB 273.80 KiB 76.00 KiB ▁▁▁▁▁▁▁████
react-router.full 91.06 KiB 0 B (0.00%) 90.92 KiB 285.70 KiB 79.10 KiB ▁▁▁▁▁▁▁████
solid-router.minimal 35.53 KiB 0 B (0.00%) 35.40 KiB 106.00 KiB 31.98 KiB ▁▁▁▁▁▁▁████
solid-router.full 40.58 KiB 0 B (0.00%) 40.46 KiB 121.22 KiB 36.50 KiB ▁▁▁▁▁▁▁████
vue-router.minimal 53.01 KiB 0 B (0.00%) 52.88 KiB 150.04 KiB 47.68 KiB ▁▁▁▁▁▁▁████
vue-router.full 58.99 KiB 0 B (0.00%) 58.86 KiB 168.80 KiB 52.88 KiB ▁▁▁▁▁▁▁████
react-start.minimal 101.99 KiB 0 B (0.00%) 101.85 KiB 322.23 KiB 88.23 KiB ▁▁▁▁▁▁▁▅▅▅█
react-start.deferred-hydration 102.72 KiB 0 B (0.00%) 101.87 KiB 323.61 KiB 88.88 KiB ▁▁▁▁▁▁▁▅▅▅█
react-start.full 105.40 KiB 0 B (0.00%) 105.26 KiB 332.16 KiB 91.16 KiB ▁▁▁▁▁▁▁▅▅▅█
react-start.rsbuild.minimal 99.69 KiB 0 B (0.00%) 99.52 KiB 316.66 KiB 85.87 KiB ▁▁▁▁▁▁▁▅▃▃█
react-start.rsbuild.minimal-iife 100.09 KiB 0 B (0.00%) 99.93 KiB 317.60 KiB 86.17 KiB ▁▁▁▁▁▁▁▅▃▃█
react-start.rsbuild.full 102.92 KiB 0 B (0.00%) 102.75 KiB 326.71 KiB 88.57 KiB ▁▁▁▁▁▁▁▅▄▄█
solid-start.minimal 49.67 KiB 0 B (0.00%) 49.54 KiB 152.18 KiB 43.85 KiB ▁▁▁▁▁▁▁▅▅▅█
solid-start.deferred-hydration 52.93 KiB 0 B (0.00%) 49.60 KiB 160.21 KiB 46.87 KiB ▁▁▁▁▁▁▁▅▅▅█
solid-start.full 55.49 KiB 0 B (0.00%) 55.36 KiB 169.22 KiB 48.92 KiB ▁▁▁▁▁▁▁▅▅▅█
vue-start.minimal 71.11 KiB 0 B (0.00%) 70.98 KiB 207.29 KiB 62.94 KiB ▁▁▁▁▁▁▁▅▅▅█
vue-start.full 75.09 KiB 0 B (0.00%) 74.96 KiB 219.93 KiB 66.46 KiB ▁▁▁▁▁▁▁▅▅▅█

Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better.

@pkg-pr-new

pkg-pr-new Bot commented Jun 25, 2026

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7693

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7693

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7693

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7693

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7693

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@7693

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7693

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7693

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7693

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7693

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7693

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7693

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7693

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7693

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7693

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7693

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7693

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7693

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7693

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7693

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7693

commit: 148dfab

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/start-plugin-core/src/start-compiler/compiler.ts`:
- Around line 674-689: The reserveFunctionId logic currently only checks
reserved/generated IDs and manual owners, so a manual server function ID can
still collide with a known server function discovered by getKnownServerFns().
Update reserveFunctionId in compiler.ts to also consult the known
server-function set before accepting opts.functionId, and reject any manual ID
that matches an existing server function ID so handleCreateServerFn never reuses
an unrelated knownFns entry.

In `@packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts`:
- Around line 184-215: Resolve static template-literal IDs in the createServerFn
ID extraction path: handle no-expression template literals the same way as
string literals so `resolveStaticString`-compatible values like `id:
\`get-user\`` and `const id = \`get-user\`; createServerFn({ id })` don’t fall
through to the generic error. Update the ID resolution logic in
`handleCreateServerFn` (especially the branches that currently check
`t.isStringLiteral`, `t.isIdentifier`, and `bindingInit`) to accept the static
template-literal case and still validate it against
`MANUAL_SERVER_FN_ID_PATTERN` before returning the resolved value.
- Around line 227-250: The helper getCreateServerFnCallExpression only matches
an innermost callee named createServerFn, so aliased or namespace-resolved
factory calls are missed and manual ids are skipped. Update the resolution logic
in getCreateServerFnCallExpression (and any related createServerFn candidate
handling in handleCreateServerFn.ts) to unwrap aliased/namespace call chains and
recognize the actual factory call regardless of local import name, so { id:
"..." } is preserved instead of falling back to generated IDs.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01e9cb30-43da-4380-bb1e-cd0ea1f81bcd

📥 Commits

Reviewing files that changed from the base of the PR and between 3d86aee and a8a2739.

📒 Files selected for processing (6)
  • packages/start-client-core/src/createServerFn.ts
  • packages/start-client-core/src/tests/createServerFn.test-d.ts
  • packages/start-plugin-core/src/start-compiler/compiler.ts
  • packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
  • packages/start-plugin-core/src/start-compiler/types.ts
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts

Comment thread packages/start-plugin-core/src/start-compiler/compiler.ts
Comment thread packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts Outdated
Comment thread packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts Outdated
@codspeed-hq

codspeed-hq Bot commented Jun 25, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 28.87%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 3 improved benchmarks
❌ 2 regressed benchmarks
✅ 139 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory mem server-fn-churn (solid) 394.4 KB 3,339.6 KB -88.19%
Memory mem streaming-peak chunked (vue) 11.9 MB 12.3 MB -3.19%
Memory mem aborted-requests (solid) 2.4 MB 1.9 MB +24.72%
Memory mem peak-large-page (solid) 3.9 MB 3.4 MB +14.54%
Memory mem aborted-requests (vue) 1,021 KB 915.8 KB +11.48%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing stable-serverfn-ids-2 (148dfab) with main (ba52d2b)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (bb2daa6) during the generation of this report, so ba52d2b was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

nx-cloud[bot]

This comment was marked as outdated.

@github-actions github-actions Bot added the documentation Everything documentation related label Jun 25, 2026
nx-cloud[bot]

This comment was marked as outdated.

nx-cloud[bot]

This comment was marked as outdated.

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nx Cloud is proposing a fix for your failed CI:

We fixed the ESLint @typescript-eslint/no-unnecessary-condition error by correcting the type and loop condition in the newly added getCreateServerFnCallExpression function. The currentCall variable was typed as t.CallExpression | undefined despite never being assigned undefined, and the while (currentCall) guard was therefore always truthy. Changing the type to t.CallExpression and the loop to while (true) accurately reflects the logic, where all exit paths are via explicit return statements.

Tip

We verified this fix by re-running @tanstack/start-plugin-core:test:eslint.

diff --git a/packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts b/packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
index 0d0bbb8a..e587a47e 100644
--- a/packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
+++ b/packages/start-plugin-core/src/start-compiler/handleCreateServerFn.ts
@@ -216,10 +216,10 @@ function extractManualServerFnId(
 function getCreateServerFnCallExpression(
   candidatePath: babel.NodePath<t.CallExpression>,
 ): t.CallExpression | undefined {
-  let currentCall: t.CallExpression | undefined = candidatePath.node
+  let currentCall: t.CallExpression = candidatePath.node
   let sawMethodChain = false
 
-  while (currentCall) {
+  for (;;) {
     const callee: t.CallExpression['callee'] = currentCall.callee
     if (!t.isMemberExpression(callee)) {
       return sawMethodChain ? currentCall : undefined
@@ -237,8 +237,6 @@ function getCreateServerFnCallExpression(
     sawMethodChain = true
     currentCall = innerCall
   }
-
-  return undefined
 }
 
 function resolveStaticString(

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally TbLk-kNAH

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

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.

1 participant