Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/router/integrations/query.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ title: TanStack Query Integration
The TanStack query integration is a separate package that you need to install:

```sh
npm install -D @tanstack/react-router-ssr-query
npm install @tanstack/react-router-ssr-query
# or
pnpm add -D @tanstack/react-router-ssr-query
pnpm add @tanstack/react-router-ssr-query
# or
yarn add -D @tanstack/react-router-ssr-query
yarn add @tanstack/react-router-ssr-query
# or
bun add -D @tanstack/react-router-ssr-query
bun add @tanstack/react-router-ssr-query
```

## Setup
Expand All @@ -38,7 +38,7 @@ import { createRouter } from '@tanstack/react-router'
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
import { routeTree } from './routeTree.gen'

export function createAppRouter() {
export function getRouter() {
const queryClient = new QueryClient()
const router = createRouter({
routeTree,
Expand Down
16 changes: 9 additions & 7 deletions docs/start/framework/react/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { redirect } from '@tanstack/react-router'

// Login server function
export const loginFn = createServerFn({ method: 'POST' })
.validator((data: { email: string; password: string }) => data)
.inputValidator((data: { email: string; password: string }) => data)
.handler(async ({ data }) => {
// Verify credentials (replace with your auth logic)
const user = await authenticateUser(data.email, data.password)
Expand Down Expand Up @@ -218,7 +218,9 @@ import { createServerFn } from '@tanstack/react-start'

// User registration
export const registerFn = createServerFn({ method: 'POST' })
.validator((data: { email: string; password: string; name: string }) => data)
.inputValidator(
(data: { email: string; password: string; name: string }) => data,
)
.handler(async ({ data }) => {
// Check if user exists
const existingUser = await getUserByEmail(data.email)
Expand Down Expand Up @@ -300,7 +302,7 @@ export const authProviders = {
}

export const initiateOAuthFn = createServerFn({ method: 'POST' })
.validator((data: { provider: 'google' | 'github' }) => data)
.inputValidator((data: { provider: 'google' | 'github' }) => data)
.handler(async ({ data }) => {
const provider = authProviders[data.provider]
const state = generateRandomState()
Expand All @@ -321,7 +323,7 @@ export const initiateOAuthFn = createServerFn({ method: 'POST' })
```tsx
// Password reset request
export const requestPasswordResetFn = createServerFn({ method: 'POST' })
.validator((data: { email: string }) => data)
.inputValidator((data: { email: string }) => data)
.handler(async ({ data }) => {
const user = await getUserByEmail(data.email)
if (!user) {
Expand All @@ -340,7 +342,7 @@ export const requestPasswordResetFn = createServerFn({ method: 'POST' })

// Password reset confirmation
export const resetPasswordFn = createServerFn({ method: 'POST' })
.validator((data: { token: string; newPassword: string }) => data)
.inputValidator((data: { token: string; newPassword: string }) => data)
.handler(async ({ data }) => {
const resetToken = await getPasswordResetToken(data.token)

Expand Down Expand Up @@ -421,7 +423,7 @@ const loginSchema = z.object({
})

export const loginFn = createServerFn({ method: 'POST' })
.validator((data) => loginSchema.parse(data))
.inputValidator((data) => loginSchema.parse(data))
.handler(async ({ data }) => {
// data is now validated
})
Expand Down Expand Up @@ -517,7 +519,7 @@ function LoginForm() {

```tsx
export const loginFn = createServerFn({ method: 'POST' })
.validator(
.inputValidator(
(data: { email: string; password: string; rememberMe?: boolean }) => data,
)
.handler(async ({ data }) => {
Expand Down
4 changes: 2 additions & 2 deletions docs/start/framework/react/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Add logging to your server functions to track execution, performance, and errors
import { createServerFn } from '@tanstack/react-start'

const getUser = createServerFn({ method: 'GET' })
.validator((id: string) => id)
.inputValidator((id: string) => id)
.handler(async ({ data: id }) => {
const startTime = Date.now()

Expand Down Expand Up @@ -518,7 +518,7 @@ import { trace, SpanStatusCode } from '@opentelemetry/api'
const tracer = trace.getTracer('tanstack-start')

const getUserWithTracing = createServerFn({ method: 'GET' })
.validator((id: string) => id)
.inputValidator((id: string) => id)
.handler(async ({ data: id }) => {
return tracer.startActiveSpan('get-user', async (span) => {
span.setAttributes({
Expand Down
3 changes: 1 addition & 2 deletions packages/react-start/src/default-entry/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import type { Register } from '@tanstack/react-start'
import type { Register } from '@tanstack/react-router'
import type { RequestHandler } from '@tanstack/react-start/server'

const fetch = createStartHandler(defaultStreamHandler)

export default {
// Providing `RequestHandler` from `@tanstack/react-start/server` is required so that the output types don't import it from `@tanstack/start-server-core`

// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
fetch: fetch as RequestHandler<Register>,
}
2 changes: 1 addition & 1 deletion packages/react-start/src/default-entry/start.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default {}
export const startInstance = undefined
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

Typing issue: exported startInstance is undefined, breaking downstream type narrowing.

With export const startInstance = undefined, the type is undefined, making if (startInstance) { await startInstance.getOptions() } unreachable/invalid in consumers (e.g., hydrateStart.ts). Export a union with a minimal shape to satisfy usage.

-export const startInstance = undefined
+export type StartInstanceShim = {
+  getOptions: () => Promise<unknown>
+}
+export const startInstance: StartInstanceShim | undefined = undefined
📝 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
export const startInstance = undefined
export type StartInstanceShim = {
getOptions: () => Promise<unknown>
}
export const startInstance: StartInstanceShim | undefined = undefined
🤖 Prompt for AI Agents
In packages/react-start/src/default-entry/start.ts around line 1, the file
currently exports startInstance as the literal value undefined which gives it
the type undefined and breaks downstream type-narrowing; change this to export a
typed union so consumers can narrow safely: declare a minimal interface/type
that matches expected usage (at least a getOptions method returning a Promise or
appropriate signature used by hydrateStart.ts), then export startInstance with
that interface unioned with undefined (e.g., MyStartInstance | undefined) and
initialize it to undefined so runtime value remains the same but TypeScript sees
the correct shape for conditional checks.

2 changes: 1 addition & 1 deletion packages/react-start/vite.config.server-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default tanstackViteConfig({
srcDir: './src/default-entry',
exclude: ['./src/default-entry/client.tsx'],
entry: ['./src/default-entry/server.ts'],
externalDeps: ['@tanstack/react-start/server', '#tanstack-start-entry'],
externalDeps: ['@tanstack/react-start/server'],
outDir: './dist/default-entry',
cjs: false,
})
2 changes: 1 addition & 1 deletion packages/router-plugin/src/core/code-splitter/compilers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export function compileCodeSplitReferenceRoute(
(prop) => {
if (t.isObjectProperty(prop)) {
if (t.isIdentifier(prop.key)) {
if (opts.deleteNodes?.has(prop.key.name as any)) {
if (opts.deleteNodes!.has(prop.key.name as any)) {
return false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ export const unpluginRouterCodeSplitterFactory: UnpluginFactory<
targetFramework: userConfig.target,
filename: id,
id,
deleteNodes: new Set(userConfig.codeSplittingOptions?.deleteNodes),
deleteNodes: userConfig.codeSplittingOptions?.deleteNodes
? new Set(userConfig.codeSplittingOptions.deleteNodes)
: undefined,
addHmr:
(userConfig.codeSplittingOptions?.addHmr ?? true) && !isProduction,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/solid-start/src/default-entry/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ const fetch = createStartHandler(defaultStreamHandler)

export default {
// Providing `RequestHandler` from `@tanstack/solid-start/server` is required so that the output types don't import it from `@tanstack/start-server-core`

// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
fetch: fetch as RequestHandler<Register>,
}
2 changes: 1 addition & 1 deletion packages/solid-start/src/default-entry/start.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default {}
export const startInstance = undefined
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

Same typing issue as React: startInstance must not be undefined-only.

Expose a union with a minimal method contract so downstream getOptions() compiles.

-export const startInstance = undefined
+export type StartInstanceShim = {
+  getOptions: () => Promise<unknown>
+}
+export const startInstance: StartInstanceShim | undefined = undefined
📝 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
export const startInstance = undefined
export type StartInstanceShim = {
getOptions: () => Promise<unknown>
}
export const startInstance: StartInstanceShim | undefined = undefined
🤖 Prompt for AI Agents
In packages/solid-start/src/default-entry/start.ts around line 1, the exported
startInstance is currently undefined-only which breaks downstream typing for
getOptions; change the export to a union type exposing a minimal method contract
(e.g. an object shape that includes the getOptions signature used downstream) so
startInstance can be either that object or undefined, update the exported
declaration accordingly, and ensure the declared getOptions parameter and return
types match the expectations in downstream code so compilation succeeds.

2 changes: 1 addition & 1 deletion packages/solid-start/vite.config.server-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default tanstackViteConfig({
srcDir: './src/default-entry',
exclude: ['./src/default-entry/client.tsx'],
entry: ['./src/default-entry/server.ts'],
externalDeps: ['@tanstack/solid-start/server', '#tanstack-start-entry'],
externalDeps: ['@tanstack/solid-start/server'],
outDir: './dist/default-entry',
cjs: false,
})
6 changes: 3 additions & 3 deletions packages/start-client-core/src/client/hydrateStart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { hydrate } from '@tanstack/router-core/ssr/client'
import { ServerFunctionSerializationAdapter } from './ServerFunctionSerializationAdapter'
import type { AnyStartInstanceOptions } from '../createStart'
import type { AnyRouter, AnySerializationAdapter } from '@tanstack/router-core'
import * as startEntry from '#tanstack-start-entry'
import { startInstance } from '#tanstack-start-entry'
import { getRouter } from '#tanstack-router-entry'

export async function hydrateStart(): Promise<AnyRouter> {
const router = await getRouter()

let serializationAdapters: Array<AnySerializationAdapter>
if (startEntry.startInstance) {
const startOptions = await startEntry.startInstance.getOptions()
if (startInstance) {
const startOptions = await startInstance.getOptions()
startOptions.serializationAdapters =
startOptions.serializationAdapters ?? []
window.__TSS_START_OPTIONS__ = startOptions as AnyStartInstanceOptions
Expand Down
1 change: 1 addition & 0 deletions packages/start-plugin-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@babel/code-frame": "7.26.2",
"@babel/core": "^7.26.8",
"@babel/types": "^7.26.8",
"@rolldown/pluginutils": "1.0.0-beta.40",
"@tanstack/router-core": "workspace:*",
"@tanstack/router-generator": "workspace:*",
"@tanstack/router-plugin": "workspace:*",
Expand Down
44 changes: 20 additions & 24 deletions packages/start-plugin-core/src/start-compiler-plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { logDiff } from '@tanstack/router-utils'
import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
import { normalizePath } from 'vite'
import path from 'pathe'
import { makeIdFiltersToMatchWithQuery } from '@rolldown/pluginutils'
import { VITE_ENVIRONMENT_NAMES } from '../constants'
import { compileStartOutputFactory } from './compilers'
import { transformFuncs } from './constants'
Expand All @@ -27,7 +28,6 @@ const require = createRequire(import.meta.url)
function resolveRuntimeFiles(opts: { package: string; files: Array<string> }) {
const pkgRoot = resolvePackage(opts.package)
const basePath = path.join(pkgRoot, 'dist', 'esm')

return opts.files.map((file) => normalizePath(path.join(basePath, file)))
}

Expand Down Expand Up @@ -60,29 +60,25 @@ export function startCompilerPlugin(
// we do not want to include them in the transformation
// however, those packages (especially start-client-core ATM) also USE these functions
// (namely `createIsomorphicFn` in `packages/start-client-core/src/getRouterInstance.ts`) and thus need to be transformed
...resolveRuntimeFiles({
package: '@tanstack/start-client-core',
files: [
'index.js',
'createIsomorphicFn.js',
'envOnly.js',
'createServerFn.js',
'createMiddleware.js',
'serverFnFetcher.js',
],
}),
...resolveRuntimeFiles({
package: '@tanstack/start-server-core',
files: [
'index.js',
'server-functions-handler.js',
'serverRoute.js',
],
}),
...resolveRuntimeFiles({
package: `@tanstack/${framework}-start-client`,
files: ['index.js'],
}),
...makeIdFiltersToMatchWithQuery([
...resolveRuntimeFiles({
package: '@tanstack/start-client-core',
files: [
'index.js',
'createIsomorphicFn.js',
'envOnly.js',
'serverFnFetcher.js',
],
}),
...resolveRuntimeFiles({
package: '@tanstack/start-server-core',
files: ['index.js', 'server-functions-handler.js'],
}),
...resolveRuntimeFiles({
package: `@tanstack/${framework}-start-client`,
files: ['index.js'],
}),
]),
],
},
},
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading