@tanstack/cta-framework-react-cra 0.34.13 → 0.34.15
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.
- package/add-ons/sentry/assets/instrument.server.mjs +7 -0
- package/add-ons/sentry/assets/src/routes/demo/sentry.testing.tsx +198 -116
- package/add-ons/sentry/package.json +6 -1
- package/add-ons/start/assets/src/router.tsx.ejs +18 -7
- package/add-ons/start/assets/vite.config.ts.ejs +0 -12
- package/dist/checksum.js +1 -1
- package/dist/types/checksum.d.ts +1 -1
- package/package.json +2 -2
- package/src/checksum.ts +1 -1
- package/tests/snapshots/react-cra/cr-ts-start-npm.json +1 -1
- package/add-ons/sentry/assets/src/app/global-middleware.ts +0 -11
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Sentry from '@sentry/tanstackstart-react'
|
|
2
|
+
Sentry.init({
|
|
3
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
4
|
+
// Adds request headers and IP for users, for more info visit:
|
|
5
|
+
// https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/configuration/options/#sendDefaultPii
|
|
6
|
+
sendDefaultPii: true,
|
|
7
|
+
})
|
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
import * as fs from 'node:fs/promises'
|
|
10
10
|
import { createFileRoute } from '@tanstack/react-router'
|
|
11
11
|
import { createServerFn } from '@tanstack/react-start'
|
|
12
|
-
import * as Sentry from
|
|
12
|
+
import * as Sentry from '@sentry/tanstackstart-react'
|
|
13
13
|
import { useState, useEffect, useRef } from 'react'
|
|
14
14
|
|
|
15
15
|
export const Route = createFileRoute('/demo/sentry/testing')({
|
|
16
16
|
component: RouteComponent,
|
|
17
|
+
errorComponent: ({ error }) => {
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
Sentry.captureException(error)
|
|
20
|
+
}, [error])
|
|
21
|
+
return <div>Error: {error.message}</div>
|
|
22
|
+
},
|
|
17
23
|
})
|
|
18
24
|
|
|
19
25
|
// Server function that will error
|
|
@@ -21,9 +27,9 @@ const badServerFunc = createServerFn({
|
|
|
21
27
|
method: 'GET',
|
|
22
28
|
}).handler(async () => {
|
|
23
29
|
return await Sentry.startSpan(
|
|
24
|
-
{
|
|
30
|
+
{
|
|
25
31
|
name: 'Reading non-existent file',
|
|
26
|
-
op: 'file.read'
|
|
32
|
+
op: 'file.read',
|
|
27
33
|
},
|
|
28
34
|
async () => {
|
|
29
35
|
try {
|
|
@@ -33,7 +39,7 @@ const badServerFunc = createServerFn({
|
|
|
33
39
|
Sentry.captureException(error)
|
|
34
40
|
throw error
|
|
35
41
|
}
|
|
36
|
-
}
|
|
42
|
+
},
|
|
37
43
|
)
|
|
38
44
|
})
|
|
39
45
|
|
|
@@ -42,14 +48,14 @@ const goodServerFunc = createServerFn({
|
|
|
42
48
|
method: 'GET',
|
|
43
49
|
}).handler(async () => {
|
|
44
50
|
return await Sentry.startSpan(
|
|
45
|
-
{
|
|
51
|
+
{
|
|
46
52
|
name: 'Successful server operation',
|
|
47
|
-
op: 'demo.success'
|
|
53
|
+
op: 'demo.success',
|
|
48
54
|
},
|
|
49
55
|
async () => {
|
|
50
|
-
await new Promise(resolve => setTimeout(resolve, 500))
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
51
57
|
return { success: true }
|
|
52
|
-
}
|
|
58
|
+
},
|
|
53
59
|
)
|
|
54
60
|
})
|
|
55
61
|
|
|
@@ -62,127 +68,134 @@ function RouteComponent() {
|
|
|
62
68
|
const [replayEvents, setReplayEvents] = useState<string[]>([])
|
|
63
69
|
const [copiedSpan, setCopiedSpan] = useState<string | null>(null)
|
|
64
70
|
const startTimeRef = useRef<string>('')
|
|
65
|
-
|
|
71
|
+
|
|
66
72
|
useEffect(() => {
|
|
67
73
|
// Set initial timestamp only once on client
|
|
68
74
|
if (!startTimeRef.current) {
|
|
69
75
|
startTimeRef.current = new Date().toISOString()
|
|
70
76
|
}
|
|
71
|
-
|
|
77
|
+
|
|
72
78
|
if (demoStep > 0) {
|
|
73
|
-
const secondsElapsed = (
|
|
74
|
-
|
|
79
|
+
const secondsElapsed = (
|
|
80
|
+
(new Date().getTime() - new Date(startTimeRef.current).getTime()) /
|
|
81
|
+
1000
|
|
82
|
+
).toFixed(1)
|
|
83
|
+
setReplayEvents((prev) => [
|
|
84
|
+
...prev,
|
|
85
|
+
`Step ${demoStep}: +${secondsElapsed}s`,
|
|
86
|
+
])
|
|
75
87
|
}
|
|
76
88
|
}, [demoStep])
|
|
77
89
|
|
|
78
90
|
const handleCopy = (operation: string) => {
|
|
79
|
-
navigator.clipboard.writeText(operation)
|
|
80
|
-
setCopiedSpan(operation)
|
|
81
|
-
setTimeout(() => setCopiedSpan(null), 2000)
|
|
91
|
+
navigator.clipboard.writeText(operation)
|
|
92
|
+
setCopiedSpan(operation)
|
|
93
|
+
setTimeout(() => setCopiedSpan(null), 2000)
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
const handleClientError = async () => {
|
|
85
|
-
setIsLoading(prev => ({ ...prev, clientError: true }))
|
|
86
|
-
setHasError(prev => ({ ...prev, clientError: false }))
|
|
87
|
-
setShowTrace(prev => ({ ...prev, clientError: true }))
|
|
88
|
-
|
|
97
|
+
setIsLoading((prev) => ({ ...prev, clientError: true }))
|
|
98
|
+
setHasError((prev) => ({ ...prev, clientError: false }))
|
|
99
|
+
setShowTrace((prev) => ({ ...prev, clientError: true }))
|
|
100
|
+
|
|
89
101
|
try {
|
|
90
102
|
await Sentry.startSpan(
|
|
91
|
-
{
|
|
103
|
+
{
|
|
92
104
|
name: 'Client Error Flow Demo',
|
|
93
105
|
op: 'demo.client-error-flow',
|
|
94
|
-
},
|
|
106
|
+
},
|
|
95
107
|
async () => {
|
|
96
|
-
Sentry.setContext(
|
|
97
|
-
feature:
|
|
98
|
-
triggered_at: new Date().toISOString()
|
|
99
|
-
})
|
|
100
|
-
|
|
108
|
+
Sentry.setContext('demo', {
|
|
109
|
+
feature: 'client-error-demo',
|
|
110
|
+
triggered_at: new Date().toISOString(),
|
|
111
|
+
})
|
|
112
|
+
|
|
101
113
|
// Simulate a client-side error
|
|
102
114
|
throw new Error('Client-side error demonstration')
|
|
103
|
-
}
|
|
115
|
+
},
|
|
104
116
|
)
|
|
105
117
|
} catch (error) {
|
|
106
|
-
setHasError(prev => ({ ...prev, clientError: true }))
|
|
107
|
-
setSpanOps(prev => ({ ...prev, clientError: 'demo.client-error-flow' }))
|
|
118
|
+
setHasError((prev) => ({ ...prev, clientError: true }))
|
|
119
|
+
setSpanOps((prev) => ({ ...prev, clientError: 'demo.client-error-flow' }))
|
|
108
120
|
Sentry.captureException(error)
|
|
109
121
|
} finally {
|
|
110
|
-
setIsLoading(prev => ({ ...prev, clientError: false }))
|
|
122
|
+
setIsLoading((prev) => ({ ...prev, clientError: false }))
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
|
|
114
126
|
const handleServerError = async () => {
|
|
115
|
-
setIsLoading(prev => ({ ...prev, serverError: true }))
|
|
116
|
-
setHasError(prev => ({ ...prev, serverError: false }))
|
|
117
|
-
setShowTrace(prev => ({ ...prev, serverError: true }))
|
|
118
|
-
|
|
127
|
+
setIsLoading((prev) => ({ ...prev, serverError: true }))
|
|
128
|
+
setHasError((prev) => ({ ...prev, serverError: false }))
|
|
129
|
+
setShowTrace((prev) => ({ ...prev, serverError: true }))
|
|
130
|
+
|
|
119
131
|
try {
|
|
120
132
|
await Sentry.startSpan(
|
|
121
|
-
{
|
|
133
|
+
{
|
|
122
134
|
name: 'Server Error Flow Demo',
|
|
123
135
|
op: 'demo.server-error-flow',
|
|
124
|
-
},
|
|
136
|
+
},
|
|
125
137
|
async () => {
|
|
126
|
-
Sentry.setContext(
|
|
127
|
-
feature:
|
|
128
|
-
triggered_at: new Date().toISOString()
|
|
129
|
-
})
|
|
130
|
-
|
|
138
|
+
Sentry.setContext('demo', {
|
|
139
|
+
feature: 'server-error-demo',
|
|
140
|
+
triggered_at: new Date().toISOString(),
|
|
141
|
+
})
|
|
142
|
+
|
|
131
143
|
await badServerFunc()
|
|
132
|
-
}
|
|
144
|
+
},
|
|
133
145
|
)
|
|
134
146
|
} catch (error) {
|
|
135
|
-
setHasError(prev => ({ ...prev, serverError: true }))
|
|
136
|
-
setSpanOps(prev => ({ ...prev, serverError: 'demo.server-error-flow' }))
|
|
147
|
+
setHasError((prev) => ({ ...prev, serverError: true }))
|
|
148
|
+
setSpanOps((prev) => ({ ...prev, serverError: 'demo.server-error-flow' }))
|
|
137
149
|
Sentry.captureException(error)
|
|
138
150
|
} finally {
|
|
139
|
-
setIsLoading(prev => ({ ...prev, serverError: false }))
|
|
151
|
+
setIsLoading((prev) => ({ ...prev, serverError: false }))
|
|
140
152
|
}
|
|
141
153
|
}
|
|
142
154
|
|
|
143
155
|
const handleClientTrace = async () => {
|
|
144
|
-
setIsLoading(prev => ({ ...prev, client: true }))
|
|
145
|
-
setShowTrace(prev => ({ ...prev, client: true }))
|
|
146
|
-
|
|
156
|
+
setIsLoading((prev) => ({ ...prev, client: true }))
|
|
157
|
+
setShowTrace((prev) => ({ ...prev, client: true }))
|
|
158
|
+
|
|
147
159
|
await Sentry.startSpan(
|
|
148
|
-
{
|
|
160
|
+
{
|
|
149
161
|
name: 'Client Operation',
|
|
150
162
|
op: 'demo.client',
|
|
151
|
-
},
|
|
163
|
+
},
|
|
152
164
|
async () => {
|
|
153
165
|
// Simulate some client-side work
|
|
154
|
-
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
155
|
-
}
|
|
166
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
167
|
+
},
|
|
156
168
|
)
|
|
157
|
-
|
|
158
|
-
setSpanOps(prev => ({ ...prev, client: 'demo.client' }))
|
|
159
|
-
setIsLoading(prev => ({ ...prev, client: false }))
|
|
169
|
+
|
|
170
|
+
setSpanOps((prev) => ({ ...prev, client: 'demo.client' }))
|
|
171
|
+
setIsLoading((prev) => ({ ...prev, client: false }))
|
|
160
172
|
}
|
|
161
173
|
|
|
162
174
|
const handleServerTrace = async () => {
|
|
163
|
-
setIsLoading(prev => ({ ...prev, server: true }))
|
|
164
|
-
setShowTrace(prev => ({ ...prev, server: true }))
|
|
165
|
-
|
|
175
|
+
setIsLoading((prev) => ({ ...prev, server: true }))
|
|
176
|
+
setShowTrace((prev) => ({ ...prev, server: true }))
|
|
177
|
+
|
|
166
178
|
try {
|
|
167
179
|
await Sentry.startSpan(
|
|
168
|
-
{
|
|
180
|
+
{
|
|
169
181
|
name: 'Server Operation',
|
|
170
182
|
op: 'demo.server',
|
|
171
|
-
},
|
|
183
|
+
},
|
|
172
184
|
async () => {
|
|
173
185
|
await goodServerFunc()
|
|
174
|
-
}
|
|
186
|
+
},
|
|
175
187
|
)
|
|
176
|
-
setSpanOps(prev => ({ ...prev, server: 'demo.server' }))
|
|
188
|
+
setSpanOps((prev) => ({ ...prev, server: 'demo.server' }))
|
|
177
189
|
} finally {
|
|
178
|
-
setIsLoading(prev => ({ ...prev, server: false }))
|
|
190
|
+
setIsLoading((prev) => ({ ...prev, server: false }))
|
|
179
191
|
}
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
return (
|
|
183
195
|
<>
|
|
184
|
-
<style
|
|
185
|
-
|
|
196
|
+
<style
|
|
197
|
+
dangerouslySetInnerHTML={{
|
|
198
|
+
__html: `
|
|
186
199
|
@keyframes fadeOut {
|
|
187
200
|
from { opacity: 1; transform: translateY(0); }
|
|
188
201
|
to { opacity: 0; transform: translateY(-10px); }
|
|
@@ -190,19 +203,23 @@ function RouteComponent() {
|
|
|
190
203
|
.animate-fade-out {
|
|
191
204
|
animation: fadeOut 2s ease-out forwards;
|
|
192
205
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
`,
|
|
207
|
+
}}
|
|
208
|
+
/>
|
|
209
|
+
<div
|
|
210
|
+
className="min-h-[calc(100vh-32px)] text-white p-8"
|
|
211
|
+
style={{
|
|
212
|
+
backgroundImage:
|
|
213
|
+
'radial-gradient(41.11% 49.93% at 50% 49.93%, #8d5494 0%, #563275 52.26%, #1f1633 100%)',
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
198
216
|
<div className="max-w-7xl mx-auto">
|
|
199
217
|
{/* Header */}
|
|
200
218
|
<div className="text-center mb-12">
|
|
201
|
-
<h1 className="text-8xl font-bold mb-4 text-white">
|
|
202
|
-
Sentry
|
|
203
|
-
</h1>
|
|
219
|
+
<h1 className="text-8xl font-bold mb-4 text-white">Sentry</h1>
|
|
204
220
|
<p className="text-4xl font-semibold text-white">
|
|
205
|
-
Code <span className="inline-block -rotate-9">breaks</span>, fix
|
|
221
|
+
Code <span className="inline-block -rotate-9">breaks</span>, fix
|
|
222
|
+
it faster
|
|
206
223
|
</p>
|
|
207
224
|
</div>
|
|
208
225
|
|
|
@@ -212,24 +229,35 @@ function RouteComponent() {
|
|
|
212
229
|
<div className="bg-[#1C2333] rounded-lg border border-gray-800 p-6">
|
|
213
230
|
<div className="space-y-4 text-gray-300">
|
|
214
231
|
<p>
|
|
215
|
-
The Sentry integration monitors this application across all
|
|
232
|
+
The Sentry integration monitors this application across all
|
|
233
|
+
routes; not just this one (we care about all tabs) using our{' '}
|
|
234
|
+
<code>@sentry/react</code> and <code>@sentry/node</code>{' '}
|
|
235
|
+
packages.
|
|
216
236
|
</p>
|
|
217
237
|
<div className="grid grid-cols-4 gap-4">
|
|
218
238
|
<div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
|
|
219
239
|
<div className="font-bold mb-1">Error Monitoring</div>
|
|
220
|
-
<div className="text-sm text-gray-400">
|
|
240
|
+
<div className="text-sm text-gray-400">
|
|
241
|
+
across client side and server functions
|
|
242
|
+
</div>
|
|
221
243
|
</div>
|
|
222
244
|
<div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
|
|
223
245
|
<div className="font-bold mb-1">Tracing and Spans</div>
|
|
224
|
-
<div className="text-sm text-gray-400">
|
|
246
|
+
<div className="text-sm text-gray-400">
|
|
247
|
+
for client and server side performance
|
|
248
|
+
</div>
|
|
225
249
|
</div>
|
|
226
250
|
<div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
|
|
227
251
|
<div className="font-bold mb-1">Session replay</div>
|
|
228
|
-
<div className="text-sm text-gray-400">
|
|
252
|
+
<div className="text-sm text-gray-400">
|
|
253
|
+
real user session playback
|
|
254
|
+
</div>
|
|
229
255
|
</div>
|
|
230
256
|
<div className="bg-[#2D3555] rounded-lg p-4 border border-gray-700 hover:border-purple-500/50 transition-colors">
|
|
231
257
|
<div className="font-bold mb-1">Real-time alerts</div>
|
|
232
|
-
<div className="text-sm text-gray-400">
|
|
258
|
+
<div className="text-sm text-gray-400">
|
|
259
|
+
because sleep is overrated anyway
|
|
260
|
+
</div>
|
|
233
261
|
</div>
|
|
234
262
|
</div>
|
|
235
263
|
</div>
|
|
@@ -239,26 +267,31 @@ function RouteComponent() {
|
|
|
239
267
|
<div className="grid grid-cols-2 gap-8">
|
|
240
268
|
{/* Client Side Testing */}
|
|
241
269
|
<div className="bg-[#1C2333] rounded-lg p-6 border border-gray-800">
|
|
242
|
-
<h2 className="text-xl font-semibold text-white mb-6">
|
|
270
|
+
<h2 className="text-xl font-semibold text-white mb-6">
|
|
271
|
+
Client-Side Testing
|
|
272
|
+
</h2>
|
|
243
273
|
<div className="space-y-6">
|
|
244
274
|
<div>
|
|
245
275
|
<button
|
|
246
276
|
type="button"
|
|
247
277
|
onClick={() => {
|
|
248
|
-
setDemoStep(prev => prev + 1)
|
|
278
|
+
setDemoStep((prev) => prev + 1)
|
|
249
279
|
handleClientError()
|
|
250
280
|
}}
|
|
251
281
|
className="w-full text-white rounded-md p-4 relative overflow-hidden group"
|
|
252
282
|
style={{
|
|
253
|
-
background:
|
|
283
|
+
background:
|
|
284
|
+
'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
|
|
254
285
|
backgroundPosition: '2% 0',
|
|
255
|
-
backgroundSize: '250% 100%'
|
|
286
|
+
backgroundSize: '250% 100%',
|
|
256
287
|
}}
|
|
257
288
|
>
|
|
258
289
|
<div className="absolute inset-0 bg-gradient-to-r from-red-500/10 to-orange-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
259
290
|
<div className="relative">
|
|
260
291
|
<div className="flex items-center mb-2">
|
|
261
|
-
<span className="font-medium">
|
|
292
|
+
<span className="font-medium">
|
|
293
|
+
Trigger Client-Side Error
|
|
294
|
+
</span>
|
|
262
295
|
</div>
|
|
263
296
|
</div>
|
|
264
297
|
</button>
|
|
@@ -266,9 +299,17 @@ function RouteComponent() {
|
|
|
266
299
|
<div className="mt-4 space-y-2">
|
|
267
300
|
<div className="bg-red-900/20 border border-red-500/50 rounded-lg p-2">
|
|
268
301
|
<div className="flex items-center text-red-400 text-sm">
|
|
269
|
-
<svg
|
|
270
|
-
|
|
271
|
-
|
|
302
|
+
<svg
|
|
303
|
+
className="w-4 h-4 mr-2"
|
|
304
|
+
fill="none"
|
|
305
|
+
strokeLinecap="round"
|
|
306
|
+
strokeLinejoin="round"
|
|
307
|
+
strokeWidth="2"
|
|
308
|
+
viewBox="0 0 24 24"
|
|
309
|
+
stroke="currentColor"
|
|
310
|
+
>
|
|
311
|
+
<title>Red Warning Sign</title>
|
|
312
|
+
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
272
313
|
</svg>
|
|
273
314
|
Client-side error captured and traced
|
|
274
315
|
</div>
|
|
@@ -282,8 +323,12 @@ function RouteComponent() {
|
|
|
282
323
|
onClick={() => handleCopy(spanOps.clientError)}
|
|
283
324
|
title="Click to copy operation name"
|
|
284
325
|
>
|
|
285
|
-
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
286
|
-
|
|
326
|
+
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
327
|
+
span.op
|
|
328
|
+
</span>
|
|
329
|
+
<code className="text-purple-400 text-sm font-mono">
|
|
330
|
+
{spanOps.clientError}
|
|
331
|
+
</code>
|
|
287
332
|
</button>
|
|
288
333
|
{copiedSpan === spanOps.clientError && (
|
|
289
334
|
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
|
|
@@ -301,32 +346,39 @@ function RouteComponent() {
|
|
|
301
346
|
<button
|
|
302
347
|
type="button"
|
|
303
348
|
onClick={() => {
|
|
304
|
-
setDemoStep(prev => prev + 1)
|
|
349
|
+
setDemoStep((prev) => prev + 1)
|
|
305
350
|
handleClientTrace()
|
|
306
351
|
}}
|
|
307
352
|
className="w-full text-white rounded-md p-4 relative overflow-hidden group"
|
|
308
353
|
style={{
|
|
309
|
-
background:
|
|
354
|
+
background:
|
|
355
|
+
'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
|
|
310
356
|
backgroundPosition: '2% 0',
|
|
311
|
-
backgroundSize: '250% 100%'
|
|
357
|
+
backgroundSize: '250% 100%',
|
|
312
358
|
}}
|
|
313
359
|
>
|
|
314
360
|
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
315
361
|
<div className="relative">
|
|
316
362
|
<div className="flex items-center mb-2">
|
|
317
|
-
<span className="font-medium">
|
|
363
|
+
<span className="font-medium">
|
|
364
|
+
Test Client-Side Span
|
|
365
|
+
</span>
|
|
318
366
|
</div>
|
|
319
367
|
</div>
|
|
320
368
|
</button>
|
|
321
369
|
{showTrace.client && (
|
|
322
370
|
<div className="mt-4 space-y-2">
|
|
323
371
|
<div className="flex items-center">
|
|
324
|
-
<div
|
|
372
|
+
<div
|
|
373
|
+
className={`w-3 h-3 rounded-full ${isLoading.client ? 'bg-blue-500 animate-pulse' : 'bg-green-500'}`}
|
|
374
|
+
/>
|
|
325
375
|
<div className="ml-2 flex-1">
|
|
326
376
|
<div className="h-1.5 bg-[#2D3555] rounded">
|
|
327
|
-
<div
|
|
377
|
+
<div
|
|
328
378
|
className="h-full bg-blue-500 rounded transition-all duration-500"
|
|
329
|
-
style={{
|
|
379
|
+
style={{
|
|
380
|
+
width: isLoading.client ? '60%' : '100%',
|
|
381
|
+
}}
|
|
330
382
|
/>
|
|
331
383
|
</div>
|
|
332
384
|
</div>
|
|
@@ -341,8 +393,12 @@ function RouteComponent() {
|
|
|
341
393
|
onClick={() => handleCopy(spanOps.client)}
|
|
342
394
|
title="Click to copy operation name"
|
|
343
395
|
>
|
|
344
|
-
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
345
|
-
|
|
396
|
+
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
397
|
+
span.op
|
|
398
|
+
</span>
|
|
399
|
+
<code className="text-purple-400 text-sm font-mono">
|
|
400
|
+
{spanOps.client}
|
|
401
|
+
</code>
|
|
346
402
|
</button>
|
|
347
403
|
{copiedSpan === spanOps.client && (
|
|
348
404
|
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
|
|
@@ -361,26 +417,31 @@ function RouteComponent() {
|
|
|
361
417
|
|
|
362
418
|
{/* Server Side Testing */}
|
|
363
419
|
<div className="bg-[#1C2333] rounded-lg p-6 border border-gray-800">
|
|
364
|
-
<h2 className="text-xl font-semibold text-white mb-6">
|
|
420
|
+
<h2 className="text-xl font-semibold text-white mb-6">
|
|
421
|
+
Server-Side Testing
|
|
422
|
+
</h2>
|
|
365
423
|
<div className="space-y-6">
|
|
366
424
|
<div>
|
|
367
425
|
<button
|
|
368
426
|
type="button"
|
|
369
427
|
onClick={() => {
|
|
370
|
-
setDemoStep(prev => prev + 1)
|
|
428
|
+
setDemoStep((prev) => prev + 1)
|
|
371
429
|
handleServerError()
|
|
372
430
|
}}
|
|
373
431
|
className="w-full text-white rounded-md p-4 relative overflow-hidden group"
|
|
374
432
|
style={{
|
|
375
|
-
background:
|
|
433
|
+
background:
|
|
434
|
+
'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
|
|
376
435
|
backgroundPosition: '2% 0',
|
|
377
|
-
backgroundSize: '250% 100%'
|
|
436
|
+
backgroundSize: '250% 100%',
|
|
378
437
|
}}
|
|
379
438
|
>
|
|
380
439
|
<div className="absolute inset-0 bg-gradient-to-r from-red-500/10 to-orange-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
381
440
|
<div className="relative">
|
|
382
441
|
<div className="flex items-center mb-2">
|
|
383
|
-
<span className="font-medium">
|
|
442
|
+
<span className="font-medium">
|
|
443
|
+
Trigger Server Error
|
|
444
|
+
</span>
|
|
384
445
|
</div>
|
|
385
446
|
</div>
|
|
386
447
|
</button>
|
|
@@ -388,7 +449,15 @@ function RouteComponent() {
|
|
|
388
449
|
<div className="mt-4 space-y-2">
|
|
389
450
|
<div className="bg-red-900/20 border border-red-500/50 rounded-lg p-3">
|
|
390
451
|
<div className="flex items-center text-red-400 text-sm">
|
|
391
|
-
<svg
|
|
452
|
+
<svg
|
|
453
|
+
className="w-4 h-4 mr-2"
|
|
454
|
+
fill="none"
|
|
455
|
+
strokeLinecap="round"
|
|
456
|
+
strokeLinejoin="round"
|
|
457
|
+
strokeWidth="2"
|
|
458
|
+
viewBox="0 0 24 24"
|
|
459
|
+
stroke="currentColor"
|
|
460
|
+
>
|
|
392
461
|
<title>Red Warning Sign</title>
|
|
393
462
|
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
394
463
|
</svg>
|
|
@@ -398,14 +467,18 @@ function RouteComponent() {
|
|
|
398
467
|
<div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
|
|
399
468
|
<div className="flex items-center justify-between">
|
|
400
469
|
<div className="relative">
|
|
401
|
-
<button
|
|
470
|
+
<button
|
|
402
471
|
type="button"
|
|
403
472
|
className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.serverError ? 'scale-95' : ''}`}
|
|
404
473
|
onClick={() => handleCopy(spanOps.serverError)}
|
|
405
474
|
title="Click to copy operation name"
|
|
406
475
|
>
|
|
407
|
-
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
408
|
-
|
|
476
|
+
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
477
|
+
span.op
|
|
478
|
+
</span>
|
|
479
|
+
<code className="text-purple-400 text-sm font-mono">
|
|
480
|
+
{spanOps.serverError}
|
|
481
|
+
</code>
|
|
409
482
|
</button>
|
|
410
483
|
{copiedSpan === spanOps.serverError && (
|
|
411
484
|
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
|
|
@@ -423,14 +496,15 @@ function RouteComponent() {
|
|
|
423
496
|
<button
|
|
424
497
|
type="button"
|
|
425
498
|
onClick={() => {
|
|
426
|
-
setDemoStep(prev => prev + 1)
|
|
499
|
+
setDemoStep((prev) => prev + 1)
|
|
427
500
|
handleServerTrace()
|
|
428
501
|
}}
|
|
429
502
|
className="w-full text-white rounded-md p-4 relative overflow-hidden group"
|
|
430
503
|
style={{
|
|
431
|
-
background:
|
|
504
|
+
background:
|
|
505
|
+
'linear-gradient(120deg, #c83852, #b44092 25%, #6a5fc1 50%, #452650 55%, #452650)',
|
|
432
506
|
backgroundPosition: '2% 0',
|
|
433
|
-
backgroundSize: '250% 100%'
|
|
507
|
+
backgroundSize: '250% 100%',
|
|
434
508
|
}}
|
|
435
509
|
>
|
|
436
510
|
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-purple-500/10 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
@@ -443,12 +517,16 @@ function RouteComponent() {
|
|
|
443
517
|
{showTrace.server && (
|
|
444
518
|
<div className="mt-4 space-y-2">
|
|
445
519
|
<div className="flex items-center">
|
|
446
|
-
<div
|
|
520
|
+
<div
|
|
521
|
+
className={`w-3 h-3 rounded-full ${isLoading.server ? 'bg-purple-500 animate-pulse' : 'bg-green-500'}`}
|
|
522
|
+
/>
|
|
447
523
|
<div className="ml-2 flex-1">
|
|
448
524
|
<div className="h-1.5 bg-[#2D3555] rounded">
|
|
449
|
-
<div
|
|
525
|
+
<div
|
|
450
526
|
className="h-full bg-purple-500 rounded transition-all duration-500"
|
|
451
|
-
style={{
|
|
527
|
+
style={{
|
|
528
|
+
width: isLoading.server ? '60%' : '100%',
|
|
529
|
+
}}
|
|
452
530
|
/>
|
|
453
531
|
</div>
|
|
454
532
|
</div>
|
|
@@ -457,14 +535,18 @@ function RouteComponent() {
|
|
|
457
535
|
<div className="bg-purple-900/20 border border-purple-500/50 rounded-lg p-3">
|
|
458
536
|
<div className="flex items-center justify-between">
|
|
459
537
|
<div className="relative">
|
|
460
|
-
<button
|
|
538
|
+
<button
|
|
461
539
|
type="button"
|
|
462
540
|
className={`inline-flex items-center bg-purple-900/40 px-3 py-1.5 rounded-lg border border-purple-500/50 cursor-pointer hover:bg-purple-900/60 transition-all ${copiedSpan === spanOps.server ? 'scale-95' : ''}`}
|
|
463
541
|
onClick={() => handleCopy(spanOps.server)}
|
|
464
542
|
title="Click to copy operation name"
|
|
465
543
|
>
|
|
466
|
-
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
467
|
-
|
|
544
|
+
<span className="text-purple-300 text-sm font-medium mr-2">
|
|
545
|
+
span.op
|
|
546
|
+
</span>
|
|
547
|
+
<code className="text-purple-400 text-sm font-mono">
|
|
548
|
+
{spanOps.server}
|
|
549
|
+
</code>
|
|
468
550
|
</button>
|
|
469
551
|
{copiedSpan === spanOps.server && (
|
|
470
552
|
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-green-500/90 text-white text-xs px-2 py-1 rounded animate-fade-out">
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"scripts": {
|
|
3
|
+
"build": "vite build && cp instrument.server.mjs .output/server",
|
|
4
|
+
"dev": "NODE_OPTIONS='--import ./instrument.server.mjs' vite dev --port 3000",
|
|
5
|
+
"start": "node --import ./.output/server/instrument.server.mjs .output/server/index.mjs"
|
|
6
|
+
},
|
|
2
7
|
"dependencies": {
|
|
3
|
-
"@sentry/tanstackstart-react": "^10.
|
|
8
|
+
"@sentry/tanstackstart-react": "^10.22.0"
|
|
4
9
|
}
|
|
5
10
|
}
|
|
@@ -2,13 +2,16 @@ import { createRouter } from '@tanstack/react-router'<% if (addOnEnabled['tansta
|
|
|
2
2
|
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
|
|
3
3
|
import * as TanstackQuery from "./integrations/tanstack-query/root-provider";
|
|
4
4
|
<% } %>
|
|
5
|
+
<% if (addOnEnabled.sentry) { %>
|
|
6
|
+
import * as Sentry from "@sentry/tanstackstart-react";
|
|
7
|
+
<% } %>
|
|
5
8
|
|
|
6
9
|
// Import the generated route tree
|
|
7
10
|
import { routeTree } from './routeTree.gen'
|
|
8
11
|
|
|
9
12
|
// Create a new router instance
|
|
10
13
|
export const getRouter = () => {
|
|
11
|
-
|
|
14
|
+
<% if (addOnEnabled['tanstack-query']) { %>
|
|
12
15
|
const rqContext = TanstackQuery.getContext();
|
|
13
16
|
|
|
14
17
|
const router = createRouter({
|
|
@@ -25,14 +28,22 @@ export const getRouter = () => {
|
|
|
25
28
|
})
|
|
26
29
|
|
|
27
30
|
setupRouterSsrQueryIntegration({router, queryClient: rqContext.queryClient})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<% } else { %>
|
|
32
|
-
return createRouter({
|
|
31
|
+
<% } else { %>
|
|
32
|
+
const router = createRouter({
|
|
33
33
|
routeTree,
|
|
34
34
|
scrollRestoration: true,
|
|
35
35
|
defaultPreloadStaleTime: 0,
|
|
36
36
|
})
|
|
37
|
-
|
|
37
|
+
<% } %>
|
|
38
|
+
|
|
39
|
+
<% if (addOnEnabled.sentry) { %>
|
|
40
|
+
if (!router.isServer) {
|
|
41
|
+
Sentry.init({
|
|
42
|
+
dsn: import.meta.env.VITE_SENTRY_DSN,
|
|
43
|
+
integrations: [],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
<% } %>
|
|
47
|
+
|
|
48
|
+
return router;
|
|
38
49
|
}
|
|
@@ -3,8 +3,6 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite';
|
|
|
3
3
|
import viteReact from '@vitejs/plugin-react'
|
|
4
4
|
import viteTsConfigPaths from 'vite-tsconfig-paths'<% if (tailwind) { %>
|
|
5
5
|
import tailwindcss from "@tailwindcss/vite"
|
|
6
|
-
<% } %><% if (addOnEnabled.sentry) { %>
|
|
7
|
-
import { wrapVinxiConfigWithSentry } from "@sentry/tanstackstart-react";
|
|
8
6
|
<% } %><% for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { %><%- integrationImportContent(integration) %>
|
|
9
7
|
<% } %>
|
|
10
8
|
|
|
@@ -24,14 +22,4 @@ const config = defineConfig({
|
|
|
24
22
|
],
|
|
25
23
|
})
|
|
26
24
|
|
|
27
|
-
<% if (addOnEnabled.sentry) { %>
|
|
28
|
-
export default wrapVinxiConfigWithSentry(config, {
|
|
29
|
-
org: process.env.VITE_SENTRY_ORG,
|
|
30
|
-
project: process.env.VITE_SENTRY_PROJECT,
|
|
31
|
-
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
32
|
-
// Only print logs for uploading source maps in CI
|
|
33
|
-
// Set to `true` to suppress logs
|
|
34
|
-
silent: !process.env.CI,
|
|
35
|
-
});<% } else { %>
|
|
36
25
|
export default config
|
|
37
|
-
<% } %>
|
package/dist/checksum.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// This file is auto-generated. Do not edit manually.
|
|
2
2
|
// Generated from add-ons, examples, hosts, project, and toolchains directories
|
|
3
|
-
export const contentChecksum = '
|
|
3
|
+
export const contentChecksum = 'b8aed4bd3da4fd99c4df983c4bf13aaeabc3f044035f5b2849a4075ccb244ce6';
|
package/dist/types/checksum.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const contentChecksum = "
|
|
1
|
+
export declare const contentChecksum = "b8aed4bd3da4fd99c4df983c4bf13aaeabc3f044035f5b2849a4075ccb244ce6";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/cta-framework-react-cra",
|
|
3
|
-
"version": "0.34.
|
|
3
|
+
"version": "0.34.15",
|
|
4
4
|
"description": "CTA Framework for React (Create React App)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "Jack Herrington <jherr@pobox.com>",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@tanstack/cta-engine": "0.34.
|
|
26
|
+
"@tanstack/cta-engine": "0.34.15"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.6.0",
|
package/src/checksum.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// This file is auto-generated. Do not edit manually.
|
|
2
2
|
// Generated from add-ons, examples, hosts, project, and toolchains directories
|
|
3
|
-
export const contentChecksum = '
|
|
3
|
+
export const contentChecksum = 'b8aed4bd3da4fd99c4df983c4bf13aaeabc3f044035f5b2849a4075ccb244ce6'
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"/public/robots.txt": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n",
|
|
8
8
|
"/src/components/Header.tsx": "import { Link } from '@tanstack/react-router'\n\nimport { useState } from 'react'\nimport {\n ChevronDown,\n ChevronRight,\n Home,\n Menu,\n Network,\n SquareFunction,\n StickyNote,\n X,\n} from 'lucide-react'\n\nexport default function Header() {\n const [isOpen, setIsOpen] = useState(false)\n const [groupedExpanded, setGroupedExpanded] = useState<\n Record<string, boolean>\n >({})\n\n return (\n <>\n <header className=\"p-4 flex items-center bg-gray-800 text-white shadow-lg\">\n <button\n onClick={() => setIsOpen(true)}\n className=\"p-2 hover:bg-gray-700 rounded-lg transition-colors\"\n aria-label=\"Open menu\"\n >\n <Menu size={24} />\n </button>\n <h1 className=\"ml-4 text-xl font-semibold\">\n <Link to=\"/\">\n <img\n src=\"/tanstack-word-logo-white.svg\"\n alt=\"TanStack Logo\"\n className=\"h-10\"\n />\n </Link>\n </h1>\n </header>\n\n <aside\n className={`fixed top-0 left-0 h-full w-80 bg-gray-900 text-white shadow-2xl z-50 transform transition-transform duration-300 ease-in-out flex flex-col ${\n isOpen ? 'translate-x-0' : '-translate-x-full'\n }`}\n >\n <div className=\"flex items-center justify-between p-4 border-b border-gray-700\">\n <h2 className=\"text-xl font-bold\">Navigation</h2>\n <button\n onClick={() => setIsOpen(false)}\n className=\"p-2 hover:bg-gray-800 rounded-lg transition-colors\"\n aria-label=\"Close menu\"\n >\n <X size={24} />\n </button>\n </div>\n\n <nav className=\"flex-1 p-4 overflow-y-auto\">\n <Link\n to=\"/\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <Home size={20} />\n <span className=\"font-medium\">Home</span>\n </Link>\n\n {/* Demo Links Start */}\n\n <Link\n to=\"/demo/start/server-funcs\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <SquareFunction size={20} />\n <span className=\"font-medium\">Start - Server Functions</span>\n </Link>\n\n <Link\n to=\"/demo/start/api-request\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <Network size={20} />\n <span className=\"font-medium\">Start - API Request</span>\n </Link>\n\n <div className=\"flex flex-row justify-between\">\n <Link\n to=\"/demo/start/ssr\"\n onClick={() => setIsOpen(false)}\n className=\"flex-1 flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex-1 flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <StickyNote size={20} />\n <span className=\"font-medium\">Start - SSR Demos</span>\n </Link>\n <button\n className=\"p-2 hover:bg-gray-800 rounded-lg transition-colors\"\n onClick={() =>\n setGroupedExpanded((prev) => ({\n ...prev,\n StartSSRDemo: !prev.StartSSRDemo,\n }))\n }\n >\n {groupedExpanded.StartSSRDemo ? (\n <ChevronDown size={20} />\n ) : (\n <ChevronRight size={20} />\n )}\n </button>\n </div>\n {groupedExpanded.StartSSRDemo && (\n <div className=\"flex flex-col ml-4\">\n <Link\n to=\"/demo/start/ssr/spa-mode\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <StickyNote size={20} />\n <span className=\"font-medium\">SPA Mode</span>\n </Link>\n\n <Link\n to=\"/demo/start/ssr/full-ssr\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <StickyNote size={20} />\n <span className=\"font-medium\">Full SSR</span>\n </Link>\n\n <Link\n to=\"/demo/start/ssr/data-only\"\n onClick={() => setIsOpen(false)}\n className=\"flex items-center gap-3 p-3 rounded-lg hover:bg-gray-800 transition-colors mb-2\"\n activeProps={{\n className:\n 'flex items-center gap-3 p-3 rounded-lg bg-cyan-600 hover:bg-cyan-700 transition-colors mb-2',\n }}\n >\n <StickyNote size={20} />\n <span className=\"font-medium\">Data Only</span>\n </Link>\n </div>\n )}\n\n {/* Demo Links End */}\n </nav>\n </aside>\n </>\n )\n}\n",
|
|
9
9
|
"/src/data/demo.punk-songs.ts": "import { createServerFn } from '@tanstack/react-start'\n\nexport const getPunkSongs = createServerFn({\n method: 'GET',\n}).handler(async () => [\n { id: 1, name: 'Teenage Dirtbag', artist: 'Wheatus' },\n { id: 2, name: 'Smells Like Teen Spirit', artist: 'Nirvana' },\n { id: 3, name: 'The Middle', artist: 'Jimmy Eat World' },\n { id: 4, name: 'My Own Worst Enemy', artist: 'Lit' },\n { id: 5, name: 'Fat Lip', artist: 'Sum 41' },\n { id: 6, name: 'All the Small Things', artist: 'blink-182' },\n { id: 7, name: 'Beverly Hills', artist: 'Weezer' },\n])\n",
|
|
10
|
-
"/src/router.tsx": "import { createRouter } from '@tanstack/react-router'\n\n// Import the generated route tree\nimport { routeTree } from './routeTree.gen'\n\n// Create a new router instance\nexport const getRouter = () => {\n
|
|
10
|
+
"/src/router.tsx": "import { createRouter } from '@tanstack/react-router'\n\n// Import the generated route tree\nimport { routeTree } from './routeTree.gen'\n\n// Create a new router instance\nexport const getRouter = () => {\n const router = createRouter({\n routeTree,\n scrollRestoration: true,\n defaultPreloadStaleTime: 0,\n })\n\n return router\n}\n",
|
|
11
11
|
"/src/routes/__root.tsx": "import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'\nimport { TanStackDevtools } from '@tanstack/react-devtools'\n\nimport Header from '../components/Header'\n\nimport appCss from '../styles.css?url'\n\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n {\n rel: 'stylesheet',\n href: appCss,\n },\n ],\n }),\n\n shellComponent: RootDocument,\n})\n\nfunction RootDocument({ children }: { children: React.ReactNode }) {\n return (\n <html lang=\"en\">\n <head>\n <HeadContent />\n </head>\n <body>\n <Header />\n {children}\n <TanStackDevtools\n config={{\n position: 'bottom-right',\n }}\n plugins={[\n {\n name: 'Tanstack Router',\n render: <TanStackRouterDevtoolsPanel />,\n },\n ]}\n />\n <Scripts />\n </body>\n </html>\n )\n}\n",
|
|
12
12
|
"/src/routes/demo/api.names.ts": "import { createFileRoute } from '@tanstack/react-router'\nimport { json } from '@tanstack/react-start'\n\nexport const Route = createFileRoute('/demo/api/names')({\n server: {\n handlers: {\n GET: () => json(['Alice', 'Bob', 'Charlie']),\n },\n },\n})\n",
|
|
13
13
|
"/src/routes/demo/start.api-request.tsx": "import { useEffect, useState } from 'react'\n\nimport { createFileRoute } from '@tanstack/react-router'\n\nfunction getNames() {\n return fetch('/demo/api/names').then((res) => res.json() as Promise<string[]>)\n}\n\nexport const Route = createFileRoute('/demo/start/api-request')({\n component: Home,\n})\n\nfunction Home() {\n const [names, setNames] = useState<Array<string>>([])\n\n useEffect(() => {\n getNames().then(setNames)\n }, [])\n\n return (\n <div\n className=\"flex items-center justify-center min-h-screen p-4 text-white\"\n style={{\n backgroundColor: '#000',\n backgroundImage:\n 'radial-gradient(ellipse 60% 60% at 0% 100%, #444 0%, #222 60%, #000 100%)',\n }}\n >\n <div className=\"w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10\">\n <h1 className=\"text-2xl mb-4\">Start API Request Demo - Names List</h1>\n <ul className=\"mb-4 space-y-2\">\n {names.map((name) => (\n <li\n key={name}\n className=\"bg-white/10 border border-white/20 rounded-lg p-3 backdrop-blur-sm shadow-md\"\n >\n <span className=\"text-lg text-white\">{name}</span>\n </li>\n ))}\n </ul>\n </div>\n </div>\n )\n}\n",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as Sentry from "@sentry/tanstackstart-react";
|
|
2
|
-
import {
|
|
3
|
-
createMiddleware,
|
|
4
|
-
registerGlobalMiddleware,
|
|
5
|
-
} from "@tanstack/react-start";
|
|
6
|
-
|
|
7
|
-
registerGlobalMiddleware({
|
|
8
|
-
middleware: [
|
|
9
|
-
createMiddleware().server(Sentry.sentryGlobalServerMiddlewareHandler()),
|
|
10
|
-
],
|
|
11
|
-
});
|