@tanstack/cta-framework-react-cra 0.20.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/add-ons/db/assets/src/routes/demo.db-chat-api.ts +30 -26
  2. package/add-ons/mcp/assets/src/routes/api.mcp-todos.ts +29 -25
  3. package/add-ons/mcp/assets/src/routes/mcp.ts +22 -18
  4. package/add-ons/mcp/assets/src/utils/mcp-handler.ts +62 -60
  5. package/add-ons/neon/assets/src/routes/demo.neon.tsx +1 -1
  6. package/add-ons/oRPC/assets/src/routes/api.$.ts +12 -8
  7. package/add-ons/oRPC/assets/src/routes/api.rpc.$.ts +12 -8
  8. package/add-ons/start/assets/src/router.tsx.ejs +7 -14
  9. package/add-ons/start/assets/src/routes/api.demo-names.ts +11 -7
  10. package/add-ons/start/assets/src/routes/api.demo-tq-todos.ts.ejs +17 -13
  11. package/add-ons/start/assets/src/routes/demo.start.server-funcs.tsx +1 -1
  12. package/add-ons/start/assets/vite.config.ts.ejs +1 -3
  13. package/add-ons/start/package.json +1 -1
  14. package/add-ons/tRPC/assets/src/routes/api.trpc.$.tsx +8 -3
  15. package/examples/tanchat/assets/src/routes/api.demo-chat.ts +32 -28
  16. package/examples/tanchat/assets/src/routes/api.messages.ts +22 -17
  17. package/examples/tanchat/assets/src/routes/api.sse.ts +37 -33
  18. package/package.json +2 -2
  19. package/project/base/package.json +2 -2
  20. package/project/base/src/logo.svg +10 -42
  21. package/project/packages.json +2 -1
  22. package/tests/snapshots/react-cra/cr-js-form-npm.json +1 -1
  23. package/tests/snapshots/react-cra/cr-js-npm.json +1 -1
  24. package/tests/snapshots/react-cra/cr-ts-npm.json +1 -1
  25. package/tests/snapshots/react-cra/cr-ts-start-npm.json +5 -5
  26. package/tests/snapshots/react-cra/cr-ts-start-tanstack-query-npm.json +6 -6
  27. package/tests/snapshots/react-cra/fr-ts-biome-npm.json +1 -1
  28. package/tests/snapshots/react-cra/fr-ts-npm.json +1 -1
  29. package/tests/snapshots/react-cra/fr-ts-tw-npm.json +1 -1
@@ -1,4 +1,4 @@
1
- import { createServerFileRoute } from '@tanstack/react-start/server'
1
+ import { createFileRoute } from '@tanstack/react-router'
2
2
 
3
3
  import { createCollection, localOnlyCollectionOptions } from '@tanstack/db'
4
4
  import { z } from 'zod'
@@ -41,34 +41,38 @@ const sendMessage = (message: { user: string; text: string }) => {
41
41
  })
42
42
  }
43
43
 
44
- export const ServerRoute = createServerFileRoute('/demo/db-chat-api').methods({
45
- GET: () => {
46
- const stream = new ReadableStream({
47
- start(controller) {
48
- for (const [_id, message] of serverMessagesCollection.state) {
49
- controller.enqueue(JSON.stringify(message) + '\n')
50
- }
51
- serverMessagesCollection.subscribeChanges((changes) => {
52
- for (const change of changes) {
53
- if (change.type === 'insert') {
54
- controller.enqueue(JSON.stringify(change.value) + '\n')
44
+ export const Route = createFileRoute('/demo/db-chat-api')({
45
+ server: {
46
+ handlers: {
47
+ GET: () => {
48
+ const stream = new ReadableStream({
49
+ start(controller) {
50
+ for (const [_id, message] of serverMessagesCollection.state) {
51
+ controller.enqueue(JSON.stringify(message) + '\n')
55
52
  }
56
- }
53
+ serverMessagesCollection.subscribeChanges((changes) => {
54
+ for (const change of changes) {
55
+ if (change.type === 'insert') {
56
+ controller.enqueue(JSON.stringify(change.value) + '\n')
57
+ }
58
+ }
59
+ })
60
+ },
57
61
  })
58
- },
59
- })
60
62
 
61
- return new Response(stream, {
62
- headers: {
63
- 'Content-Type': 'application/x-ndjson',
63
+ return new Response(stream, {
64
+ headers: {
65
+ 'Content-Type': 'application/x-ndjson',
66
+ },
67
+ })
64
68
  },
65
- })
66
- },
67
- POST: async ({ request }) => {
68
- const message = IncomingMessageSchema.safeParse(await request.json())
69
- if (!message.success) {
70
- return new Response(message.error.message, { status: 400 })
71
- }
72
- sendMessage(message.data)
69
+ POST: async ({ request }) => {
70
+ const message = IncomingMessageSchema.safeParse(await request.json())
71
+ if (!message.success) {
72
+ return new Response(message.error.message, { status: 400 })
73
+ }
74
+ sendMessage(message.data)
75
+ },
76
+ },
73
77
  },
74
78
  })
@@ -1,29 +1,33 @@
1
- import { createServerFileRoute } from "@tanstack/react-start/server";
1
+ import { createFileRoute } from '@tanstack/react-router'
2
2
 
3
- import { addTodo, getTodos, subscribeToTodos } from "@/mcp-todos";
3
+ import { addTodo, getTodos, subscribeToTodos } from '@/mcp-todos'
4
4
 
5
- export const ServerRoute = createServerFileRoute("/api/mcp-todos").methods({
6
- GET: () => {
7
- const stream = new ReadableStream({
8
- start(controller) {
9
- setInterval(() => {
10
- controller.enqueue(`event: ping\n\n`);
11
- }, 1000);
12
- const unsubscribe = subscribeToTodos((todos) => {
13
- controller.enqueue(`data: ${JSON.stringify(todos)}\n\n`);
14
- });
15
- const todos = getTodos();
16
- controller.enqueue(`data: ${JSON.stringify(todos)}\n\n`);
17
- return () => unsubscribe();
5
+ export const Route = createFileRoute('/api/mcp-todos')({
6
+ server: {
7
+ handlers: {
8
+ GET: () => {
9
+ const stream = new ReadableStream({
10
+ start(controller) {
11
+ setInterval(() => {
12
+ controller.enqueue(`event: ping\n\n`)
13
+ }, 1000)
14
+ const unsubscribe = subscribeToTodos((todos) => {
15
+ controller.enqueue(`data: ${JSON.stringify(todos)}\n\n`)
16
+ })
17
+ const todos = getTodos()
18
+ controller.enqueue(`data: ${JSON.stringify(todos)}\n\n`)
19
+ return () => unsubscribe()
20
+ },
21
+ })
22
+ return new Response(stream, {
23
+ headers: { 'Content-Type': 'text/event-stream' },
24
+ })
18
25
  },
19
- });
20
- return new Response(stream, {
21
- headers: { "Content-Type": "text/event-stream" },
22
- });
23
- },
24
- POST: async ({ request }) => {
25
- const { title } = await request.json();
26
- addTodo(title);
27
- return Response.json(getTodos());
26
+ POST: async ({ request }) => {
27
+ const { title } = await request.json()
28
+ addTodo(title)
29
+ return Response.json(getTodos())
30
+ },
31
+ },
28
32
  },
29
- });
33
+ })
@@ -1,29 +1,29 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { createServerFileRoute } from "@tanstack/react-start/server";
3
- import z from "zod";
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
+ import { createFileRoute } from '@tanstack/react-router'
3
+ import z from 'zod'
4
4
 
5
- import { handleMcpRequest } from "@/utils/mcp-handler";
5
+ import { handleMcpRequest } from '@/utils/mcp-handler'
6
6
 
7
- import { addTodo } from "@/mcp-todos";
7
+ import { addTodo } from '@/mcp-todos'
8
8
 
9
9
  const server = new McpServer({
10
- name: "start-server",
11
- version: "1.0.0",
12
- });
10
+ name: 'start-server',
11
+ version: '1.0.0',
12
+ })
13
13
 
14
14
  server.registerTool(
15
- "addTodo",
15
+ 'addTodo',
16
16
  {
17
- title: "Tool to add a todo to a list of todos",
18
- description: "Add a todo to a list of todos",
17
+ title: 'Tool to add a todo to a list of todos',
18
+ description: 'Add a todo to a list of todos',
19
19
  inputSchema: {
20
- title: z.string().describe("The title of the todo"),
20
+ title: z.string().describe('The title of the todo'),
21
21
  },
22
22
  },
23
23
  ({ title }) => ({
24
- content: [{ type: "text", text: String(addTodo(title)) }],
25
- })
26
- );
24
+ content: [{ type: 'text', text: String(addTodo(title)) }],
25
+ }),
26
+ )
27
27
 
28
28
  // server.registerResource(
29
29
  // "counter-value",
@@ -44,6 +44,10 @@ server.registerTool(
44
44
  // }
45
45
  // );
46
46
 
47
- export const ServerRoute = createServerFileRoute("/mcp").methods({
48
- POST: async ({ request }) => handleMcpRequest(request, server),
49
- });
47
+ export const Route = createFileRoute('/mcp')({
48
+ server: {
49
+ handlers: {
50
+ POST: async ({ request }) => handleMcpRequest(request, server),
51
+ },
52
+ },
53
+ })
@@ -1,61 +1,63 @@
1
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
- import { getEvent } from "@tanstack/react-start/server";
3
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
-
5
- export async function handleMcpRequest(request: Request, server: McpServer) {
6
- const body = await request.json();
7
- const event = getEvent();
8
- const res = event.node.res;
9
- const req = event.node.req;
10
-
11
- return new Promise<Response>((resolve, reject) => {
12
- const transport = new StreamableHTTPServerTransport({
13
- sessionIdGenerator: undefined,
14
- });
15
-
16
- const cleanup = () => {
17
- transport.close();
18
- server.close();
19
- };
20
-
21
- let settled = false;
22
- const safeResolve = (response: Response) => {
23
- if (!settled) {
24
- settled = true;
25
- cleanup();
26
- resolve(response);
27
- }
28
- };
29
-
30
- const safeReject = (error: any) => {
31
- if (!settled) {
32
- settled = true;
33
- cleanup();
34
- reject(error);
35
- }
36
- };
37
-
38
- res.on("finish", () => safeResolve(new Response(null, { status: 200 })));
39
- res.on("close", () => safeResolve(new Response(null, { status: 200 })));
40
- res.on("error", safeReject);
41
-
42
- server
43
- .connect(transport)
44
- .then(() => transport.handleRequest(req, res, body))
45
- .catch((error) => {
46
- console.error("Transport error:", error);
47
- cleanup();
48
- if (!res.headersSent) {
49
- res.writeHead(500, { "Content-Type": "application/json" });
50
- res.end(
51
- JSON.stringify({
52
- jsonrpc: "2.0",
53
- error: { code: -32603, message: "Internal server error" },
54
- id: null,
55
- })
56
- );
57
- }
58
- safeReject(error);
59
- });
60
- });
1
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
2
+
3
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4
+ import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'
5
+
6
+ export async function handleMcpRequest(
7
+ request: Request,
8
+ server: McpServer,
9
+ ): Promise<Response> {
10
+ try {
11
+ const jsonRpcRequest = (await request.json()) as JSONRPCMessage
12
+
13
+ const [clientTransport, serverTransport] =
14
+ InMemoryTransport.createLinkedPair()
15
+
16
+ let responseData: JSONRPCMessage | null = null
17
+
18
+ clientTransport.onmessage = (message: JSONRPCMessage) => {
19
+ responseData = message
20
+ }
21
+
22
+ await server.connect(serverTransport)
23
+
24
+ await clientTransport.start()
25
+ await serverTransport.start()
26
+
27
+ await clientTransport.send(jsonRpcRequest)
28
+
29
+ await new Promise((resolve) => setTimeout(resolve, 10))
30
+
31
+ await clientTransport.close()
32
+ await serverTransport.close()
33
+
34
+ console.log('responseData', JSON.stringify(responseData, null, 2))
35
+
36
+ return Response.json(responseData, {
37
+ headers: {
38
+ 'Content-Type': 'application/json',
39
+ },
40
+ })
41
+ } catch (error) {
42
+ console.error('MCP handler error:', error)
43
+
44
+ // Return a JSON-RPC error response
45
+ return Response.json(
46
+ {
47
+ jsonrpc: '2.0',
48
+ error: {
49
+ code: -32603,
50
+ message: 'Internal server error',
51
+ data: error instanceof Error ? error.message : String(error),
52
+ },
53
+ id: null,
54
+ },
55
+ {
56
+ status: 500,
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ },
60
+ },
61
+ )
62
+ }
61
63
  }
@@ -19,7 +19,7 @@ const getTodos = createServerFn({
19
19
  const insertTodo = createServerFn({
20
20
  method: 'POST',
21
21
  })
22
- .validator((d: { title: string }) => d)
22
+ .inputValidator((d: { title: string }) => d)
23
23
  .handler(async ({ data }) => {
24
24
  const client = await getClient()
25
25
  if (!client) {
@@ -3,7 +3,7 @@ import '@/polyfill'
3
3
  import { OpenAPIHandler } from '@orpc/openapi/fetch'
4
4
  import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
5
5
  import { experimental_SmartCoercionPlugin as SmartCoercionPlugin } from '@orpc/json-schema'
6
- import { createServerFileRoute } from '@tanstack/react-start/server'
6
+ import { createFileRoute } from '@tanstack/react-router'
7
7
  import { onError } from '@orpc/server'
8
8
  import { OpenAPIReferencePlugin } from '@orpc/openapi/plugins'
9
9
 
@@ -63,11 +63,15 @@ async function handle({ request }: { request: Request }) {
63
63
  return response ?? new Response('Not Found', { status: 404 })
64
64
  }
65
65
 
66
- export const ServerRoute = createServerFileRoute('/api/$').methods({
67
- HEAD: handle,
68
- GET: handle,
69
- POST: handle,
70
- PUT: handle,
71
- PATCH: handle,
72
- DELETE: handle,
66
+ export const Route = createFileRoute('/api/$')({
67
+ server: {
68
+ handlers: {
69
+ HEAD: handle,
70
+ GET: handle,
71
+ POST: handle,
72
+ PUT: handle,
73
+ PATCH: handle,
74
+ DELETE: handle,
75
+ },
76
+ },
73
77
  })
@@ -1,7 +1,7 @@
1
1
  import '@/polyfill'
2
2
 
3
3
  import { RPCHandler } from '@orpc/server/fetch'
4
- import { createServerFileRoute } from '@tanstack/react-start/server'
4
+ import { createFileRoute } from '@tanstack/react-router'
5
5
  import router from '@/orpc/router'
6
6
 
7
7
  const handler = new RPCHandler(router)
@@ -15,11 +15,15 @@ async function handle({ request }: { request: Request }) {
15
15
  return response ?? new Response('Not Found', { status: 404 })
16
16
  }
17
17
 
18
- export const ServerRoute = createServerFileRoute('/api/rpc/$').methods({
19
- HEAD: handle,
20
- GET: handle,
21
- POST: handle,
22
- PUT: handle,
23
- PATCH: handle,
24
- DELETE: handle,
18
+ export const Route = createFileRoute('/api/rpc/$')({
19
+ server: {
20
+ handlers: {
21
+ HEAD: handle,
22
+ GET: handle,
23
+ POST: handle,
24
+ PUT: handle,
25
+ PATCH: handle,
26
+ DELETE: handle,
27
+ },
28
+ },
25
29
  })
@@ -1,17 +1,17 @@
1
- import { createRouter as createTanstackRouter } from '@tanstack/react-router'<% if (addOnEnabled['tanstack-query']) { %>
2
- import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
3
- import * as TanstackQuery from "./integrations/tanstack-query/root-provider";
4
- <% } %>
1
+ import { createRouter } from '@tanstack/react-router'<% if (addOnEnabled['tanstack-query']) { %>
2
+ import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
3
+ import * as TanstackQuery from "./integrations/tanstack-query/root-provider";
4
+ <% } %>
5
5
 
6
6
  // Import the generated route tree
7
7
  import { routeTree } from './routeTree.gen'
8
8
 
9
9
  // Create a new router instance
10
- export const createRouter = () => {
10
+ export const getRouter = () => {
11
11
  <% if (addOnEnabled['tanstack-query']) { %>
12
12
  const rqContext = TanstackQuery.getContext();
13
13
 
14
- const router = createTanstackRouter({
14
+ const router = createRouter({
15
15
  routeTree,
16
16
  context: { ...rqContext },
17
17
  defaultPreload: "intent",
@@ -29,17 +29,10 @@ export const createRouter = () => {
29
29
  return router;
30
30
 
31
31
  <% } else { %>
32
- return createTanstackRouter({
32
+ return createRouter({
33
33
  routeTree,
34
34
  scrollRestoration: true,
35
35
  defaultPreloadStaleTime: 0,
36
36
  })
37
37
  <% } %>
38
38
  }
39
-
40
- // Register the router instance for type safety
41
- declare module '@tanstack/react-router' {
42
- interface Register {
43
- router: ReturnType<typeof createRouter>
44
- }
45
- }
@@ -1,11 +1,15 @@
1
- import { createServerFileRoute } from '@tanstack/react-start/server'
1
+ import { createFileRoute } from '@tanstack/react-router'
2
2
 
3
- export const ServerRoute = createServerFileRoute().methods({
4
- GET: () => {
5
- return new Response(JSON.stringify(['Alice', 'Bob', 'Charlie']), {
6
- headers: {
7
- 'Content-Type': 'application/json',
3
+ export const Route = createFileRoute('/api/demo-names')({
4
+ server: {
5
+ handlers: {
6
+ GET: () => {
7
+ return new Response(JSON.stringify(['Alice', 'Bob', 'Charlie']), {
8
+ headers: {
9
+ 'Content-Type': 'application/json',
10
+ },
11
+ })
8
12
  },
9
- })
13
+ },
10
14
  },
11
15
  })
@@ -1,4 +1,4 @@
1
- <% if (!addOnEnabled['tanstack-query']) { ignoreFile() } %>import { createServerFileRoute } from '@tanstack/react-start/server'
1
+ <% if (!addOnEnabled['tanstack-query']) { ignoreFile() } %>import { createFileRoute } from '@tanstack/react-router'
2
2
 
3
3
  const todos = [
4
4
  {
@@ -15,17 +15,21 @@ const todos = [
15
15
  },
16
16
  ]
17
17
 
18
- export const ServerRoute = createServerFileRoute('/api/demo-tq-todos').methods({
19
- GET: () => {
20
- return Response.json(todos)
21
- },
22
- POST: async ({ request }) => {
23
- const name = await request.json()
24
- const todo = {
25
- id: todos.length + 1,
26
- name,
27
- }
28
- todos.push(todo)
29
- return Response.json(todo)
18
+ export const Route = createFileRoute('/api/demo-tq-todos')({
19
+ server: {
20
+ handlers: {
21
+ GET: () => {
22
+ return Response.json(todos)
23
+ },
24
+ POST: async ({ request }) => {
25
+ const name = await request.json()
26
+ const todo = {
27
+ id: todos.length + 1,
28
+ name,
29
+ }
30
+ todos.push(todo)
31
+ return Response.json(todo)
32
+ },
33
+ },
30
34
  },
31
35
  })
@@ -25,7 +25,7 @@ const getTodos = createServerFn({
25
25
  }).handler(async () => await readTodos())
26
26
 
27
27
  const addTodo = createServerFn({ method: 'POST' })
28
- .validator((d: string) => d)
28
+ .inputValidator((d: string) => d)
29
29
  .handler(async ({ data }) => {
30
30
  const todos = await readTodos()
31
31
  todos.push({ id: todos.length + 1, name: data })
@@ -24,9 +24,7 @@ const config = defineConfig({
24
24
  projects: ['./tsconfig.json'],
25
25
  }),
26
26
  <% if (tailwind) { %>tailwindcss(),<% } %>
27
- tanstackStart({
28
- customViteReactPlugin: true,
29
- }),
27
+ tanstackStart(),
30
28
  viteReact(),
31
29
  ],
32
30
  })
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "dependencies": {
8
8
  "@tanstack/react-router-ssr-query": "^1.131.7",
9
- "@tanstack/react-start": "^1.131.7",
9
+ "@tanstack/react-start": "^1.132.0",
10
10
  "vite-tsconfig-paths": "^5.1.4"
11
11
  }
12
12
  }
@@ -1,5 +1,6 @@
1
1
  import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
2
2
  import { trpcRouter } from '@/integrations/trpc/router'
3
+ import { createFileRoute } from '@tanstack/react-router'
3
4
 
4
5
  function handler({ request }: { request: Request }) {
5
6
  return fetchRequestHandler({
@@ -9,7 +10,11 @@ function handler({ request }: { request: Request }) {
9
10
  })
10
11
  }
11
12
 
12
- export const ServerRoute = createServerFileRoute('/api/trpc/$').methods({
13
- GET: handler,
14
- POST: handler,
13
+ export const Route = createFileRoute('/api/trpc/$')({
14
+ server: {
15
+ handlers: {
16
+ GET: handler,
17
+ POST: handler,
18
+ },
19
+ },
15
20
  })
@@ -1,4 +1,4 @@
1
- import { createServerFileRoute } from '@tanstack/react-start/server'
1
+ import { createFileRoute } from '@tanstack/react-router'
2
2
  import { anthropic } from '@ai-sdk/anthropic'
3
3
  import { convertToModelMessages, stepCountIs, streamText } from 'ai'
4
4
 
@@ -12,32 +12,36 @@ You can use the following tools to help the user:
12
12
  - recommendGuitar: Recommend a guitar to the user
13
13
  `
14
14
 
15
- export const ServerRoute = createServerFileRoute('/api/demo-chat').methods({
16
- POST: async ({ request }) => {
17
- try {
18
- const { messages } = await request.json()
19
-
20
- const tools = await getTools()
21
-
22
- const result = await streamText({
23
- model: anthropic('claude-3-5-sonnet-latest'),
24
- messages: convertToModelMessages(messages),
25
- temperature: 0.7,
26
- stopWhen: stepCountIs(5),
27
- system: SYSTEM_PROMPT,
28
- tools,
29
- })
30
-
31
- return result.toUIMessageStreamResponse()
32
- } catch (error) {
33
- console.error('Chat API error:', error)
34
- return new Response(
35
- JSON.stringify({ error: 'Failed to process chat request' }),
36
- {
37
- status: 500,
38
- headers: { 'Content-Type': 'application/json' },
39
- },
40
- )
41
- }
15
+ export const Route = createFileRoute('/api/demo-chat')({
16
+ server: {
17
+ handlers: {
18
+ POST: async ({ request }) => {
19
+ try {
20
+ const { messages } = await request.json()
21
+
22
+ const tools = await getTools()
23
+
24
+ const result = await streamText({
25
+ model: anthropic('claude-3-5-sonnet-latest'),
26
+ messages: convertToModelMessages(messages),
27
+ temperature: 0.7,
28
+ stopWhen: stepCountIs(5),
29
+ system: SYSTEM_PROMPT,
30
+ tools,
31
+ })
32
+
33
+ return result.toUIMessageStreamResponse()
34
+ } catch (error) {
35
+ console.error('Chat API error:', error)
36
+ return new Response(
37
+ JSON.stringify({ error: 'Failed to process chat request' }),
38
+ {
39
+ status: 500,
40
+ headers: { 'Content-Type': 'application/json' },
41
+ },
42
+ )
43
+ }
44
+ },
45
+ },
42
46
  },
43
47
  })