Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
.output
.wrangler
.tanstack
src/routeTree.gen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "tanstackstart-react-cloudflare",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "wrangler dev --var E2E_TEST_DSN:$E2E_TEST_DSN --log-level=$(test $CI && echo 'none' || echo 'log')",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing quotes around --var value in preview script

Medium Severity

The preview script passes --var E2E_TEST_DSN:$E2E_TEST_DSN without quotes around the value. Every other Cloudflare test app in the repo (cloudflare-workers, cloudflare-local-workers, cloudflare-mcp, cloudflare-workersentrypoint, astro-6-cf-workers) uses the quoted form --var \"E2E_TEST_DSN:$E2E_TEST_DSN\". Since DSN values contain shell-sensitive characters like :// and @, the unquoted expansion could cause parsing issues depending on the shell environment in CI.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8dd6f21. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Preview bypasses built app

Medium Severity

The preview script starts wrangler dev, but this app is built through @cloudflare/vite-plugin. wrangler dev bypasses the Vite preview output that pnpm build creates, so Playwright can run a source Worker without the generated TanStack assets instead of the Cloudflare build under test.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d4d6179. Configure here.

"test": "playwright test",
"typecheck": "tsc --noEmit",
"test:build": "pnpm install && pnpm build",
"test:assert": "pnpm typecheck && pnpm test"
},
"dependencies": {
"@sentry/browser": "file:../../packed/sentry-browser-packed.tgz",
"@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz",
"@sentry/tanstackstart-react": "file:../../packed/sentry-tanstackstart-react-packed.tgz",
"@tanstack/react-start": "^1.136.0",
"@tanstack/react-router": "^1.136.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.35.0",
"@cloudflare/workers-types": "^4.20260504.0",
"@playwright/test": "~1.56.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^4.5.0",
"typescript": "^5.9.0",
"vite": "7.3.1",
"vite-tsconfig-paths": "^5.1.4",
"wrangler": "^4.68.1"
},
"volta": {
"node": "24.15.0",
"extends": "../../package.json"
},
"sentryTest": {
"optional": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';

export default getPlaywrightConfig({
startCommand: 'pnpm preview',
port: 8787,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface Env {
E2E_TEST_DSN: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as Sentry from '@sentry/browser';
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';

export const getRouter = () => {
const router = createRouter({
routeTree,
scrollRestoration: true,
});

if (!router.isServer) {
Sentry.init({
environment: 'qa',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 1.0,
release: 'e2e-test',
tunnel: 'http://localhost:3031/',
});
}

return router;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { ReactNode } from 'react';
import { Outlet, createRootRoute, HeadContent, Scripts } from '@tanstack/react-router';
import { getTraceData } from '@sentry/tanstackstart-react';

export const Route = createRootRoute({
head: () => {
const traceData = getTraceData();
const sentryMeta = Object.entries(traceData).map(([key, value]) => ({
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not sure if this is the best way in TanStack to add trace propagation, couldn't find anything specific in the docs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have never looked into this, does that work? If yes that's amazing then we can document how to set that up :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes it works - but I'm not sure if it is a hack of mine of the correct way of doing it. In any way, I can add the docs for it

name: key,
content: value,
}));

return {
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Cloudflare E2E Test',
},
...sentryMeta,
],
};
},
component: RootComponent,
});

function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
);
}

function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/api/error')({
server: {
handlers: {
GET: async () => {
throw new Error('Sentry API Route Test Error');
},
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createFileRoute } from '@tanstack/react-router';
import { flush } from '@sentry/cloudflare';

export const Route = createFileRoute('/api/flush')({
server: {
handlers: {
GET: async () => {
await flush();
return new Response('ok');
},
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createFileRoute } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';

const throwServerError = createServerFn().handler(async () => {
throw new Error('Sentry Server Function Test Error');
});

export const Route = createFileRoute('/')({
component: Home,
});

function Home() {
return (
<div>
<h1>TanStack Start Cloudflare E2E Test</h1>
<button
id="client-error-btn"
type="button"
onClick={() => {
throw new Error('Sentry Client Test Error');
}}
>
Break the client
</button>
<button
id="throw-server-fn-btn"
type="button"
onClick={async () => {
await throwServerError();
}}
>
Break server function
</button>
<button
id="api-error-btn"
type="button"
onClick={async () => {
await fetch('/api/error');
}}
>
Break API route
</button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createFileRoute } from '@tanstack/react-router';

export const Route = createFileRoute('/ssr-error')({
loader: () => {
throw new Error('Sentry SSR Test Error');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

l: this as well as the flush endpoint are not used I think? in other Tanstack Start apps we have a test that asserts that ssr errors are not captured (where this is used). fine to leave this out for this one I think but then we could trim down the application a bit (i.e. remove the flush endpoint and ssr error)

Copy link
Copy Markdown
Member Author

@JPeer264 JPeer264 May 11, 2026

Choose a reason for hiding this comment

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

Great catch. I removed the test because it originally didn't work because of the missing middleware. After readding the middleware I forgot to readd the test.

},
component: () => <div>SSR Error Page</div>,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as Sentry from '@sentry/cloudflare';
import { createFileRoute } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';

const testLog = createServerFn().handler(async () => {
console.log('Test log from server function');
return { message: 'Log created' };
});

const testNestedLog = createServerFn().handler(async () => {
await Sentry.startSpan({ name: 'testNestedLog' }, async () => {
await testLog();
});

console.log('Outer test log from server function');
return { message: 'Nested log created' };
});

export const Route = createFileRoute('/test-serverFn')({
component: TestServerFn,
});

function TestServerFn() {
return (
<div>
<h1>Test Server Function Page</h1>
<button
id="server-fn-btn"
type="button"
onClick={async () => {
await testLog();
}}
>
Call server function
</button>
<button
id="server-fn-nested-btn"
type="button"
onClick={async () => {
await testNestedLog();
}}
>
Call server function nested
</button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/cloudflare';
import { wrapFetchWithSentry } from '@sentry/tanstackstart-react';
import handler from '@tanstack/react-start/server-entry';

export default Sentry.withSentry(
(env: Env) => ({
dsn: env.E2E_TEST_DSN,
tunnel: 'http://localhost:3031/',
tracesSampleRate: 1.0,
environment: 'qa',
}),
// @ts-expect-error - handler is not typed as a Cloudflare handler
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is the type error

wrapFetchWithSentry(handler),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { sentryGlobalFunctionMiddleware, sentryGlobalRequestMiddleware } from '@sentry/tanstackstart-react';
import { createStart } from '@tanstack/react-start';

export const startInstance = createStart(() => {
return {
requestMiddleware: [sentryGlobalRequestMiddleware],
functionMiddleware: [sentryGlobalFunctionMiddleware],
};
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { startEventProxyServer } from '@sentry-internal/test-utils';

startEventProxyServer({
port: 3031,
proxyServerName: 'tanstackstart-react-cloudflare',
});
Loading
Loading