diff --git a/packages/server-functions-plugin/src/index.ts b/packages/server-functions-plugin/src/index.ts
index fb7e265f52c..0d81ec67f87 100644
--- a/packages/server-functions-plugin/src/index.ts
+++ b/packages/server-functions-plugin/src/index.ts
@@ -1,7 +1,8 @@
///
import crypto from 'node:crypto'
+import assert from 'node:assert'
import { TanStackDirectiveFunctionsPluginEnv } from '@tanstack/directive-functions-plugin'
-import type { DevEnvironment, Plugin } from 'vite'
+import type { Plugin } from 'vite'
import type {
DirectiveFn,
GenerateFunctionIdFn,
@@ -39,16 +40,26 @@ const debug =
process.env.TSR_VITE_DEBUG &&
['true', 'server-functions-plugin'].includes(process.env.TSR_VITE_DEBUG)
+const validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`
+
+function parseIdQuery(id: string): {
+ filename: string
+ query: {
+ [k: string]: string
+ }
+} {
+ if (!id.includes('?')) return { filename: id, query: {} }
+ const [filename, rawQuery] = id.split(`?`, 2) as [string, string]
+ const query = Object.fromEntries(new URLSearchParams(rawQuery))
+ return { filename, query }
+}
+
export function TanStackServerFnPlugin(
opts: TanStackServerFnPluginOpts,
): Array {
const directiveFnsById: Record = {}
- let serverDevEnv: DevEnvironment | undefined
const onDirectiveFnsById = (d: Record) => {
- if (serverDevEnv) {
- return
- }
if (debug) {
console.info(`onDirectiveFnsById received: `, d)
}
@@ -152,6 +163,24 @@ export function TanStackServerFnPlugin(
provider: opts.provider,
callers: opts.callers,
}),
+ {
+ name: 'tanstack-start-server-fn-vite-plugin-validate-serverfn-id',
+ apply: 'serve',
+ load: {
+ filter: {
+ id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),
+ },
+ handler(id) {
+ const parsed = parseIdQuery(id)
+ assert(parsed)
+ assert(parsed.query.id)
+ if (directiveFnsById[parsed.query.id]) {
+ return `export {}`
+ }
+ this.error(`Invalid server function ID: ${parsed.query.id}`)
+ },
+ },
+ },
{
// On the server, we need to be able to read the server-function manifest from the client build.
// This is likely used in the handler for server functions, so we can find the server function
@@ -191,6 +220,8 @@ export function TanStackServerFnPlugin(
if (this.environment.mode !== 'build') {
const mod = `
export async function getServerFnById(id) {
+ const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id
+ await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)
const decoded = Buffer.from(id, 'base64url').toString('utf8')
const devServerFn = JSON.parse(decoded)
const mod = await import(/* @vite-ignore */ devServerFn.file)