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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { readFileSync, writeFileSync } from 'node:fs';

// Prepend the Sentry server init to the built Nitro server entry so it runs at
// process boot — before the first request and before `node:http` is set up.
//
// TanStack Start lazy-loads the request-handler module (src/server.ts), so an
// `import` there only runs on the first request: too late for `Sentry.init()`
// to patch `node:http`, leaving the first request without an `http.server`
// transaction (its spans come in orphaned). This mirrors how `@sentry/nuxt`
// injects the server config into Nitro's entry, and removes the need for
// `node --import` (which isn't available on platforms like Vercel/Netlify).
const entryFile = '.output/server/index.mjs';
const topImport = "import './instrument.server.mjs';\n";

const contents = readFileSync(entryFile, 'utf8');

if (!contents.startsWith(topImport)) {
writeFileSync(entryFile, topImport + contents, 'utf8');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
db:
image: mysql:8.0
restart: always
container_name: e2e-tests-tanstackstart-react-orchestrion-mysql
# The `mysql` 2.x driver doesn't speak MySQL 8's default
# `caching_sha2_password` auth, so force the legacy plugin.
command: ['--default-authentication-plugin=mysql_native_password']
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: docker
healthcheck:
test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker']
interval: 2s
timeout: 3s
retries: 30
start_period: 10s

redis:
image: redis:7
restart: always
container_name: e2e-tests-tanstackstart-react-orchestrion-redis
ports:
- '6379:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 2s
timeout: 3s
retries: 30
start_period: 5s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalSetup() {
// Start MySQL and Redis via Docker Compose. `--wait` blocks until the
// healthchecks in docker-compose.yml pass, so the app can connect immediately.
execSync('docker compose up -d --wait', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalTeardown() {
execSync('docker compose down --volumes', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Sentry from '@sentry/tanstackstart-react';

// Opt into diagnostics-channel-based auto-instrumentation. This registers the
// channel subscribers (e.g. for `mysql` and `ioredis`) that turn the
// diagnostics-channel events — injected at build time by the orchestrion Vite
// plugin (see `vite.config.ts`) — into Sentry spans. Must run before
// `Sentry.init()`.
Sentry.experimentalUseDiagnosticsChannelInjection();

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.E2E_TEST_DSN,
tunnel: 'http://localhost:3031/', // proxy server
tracesSampleRate: 1,
transportOptions: {
// We expect the app to send a lot of events in a short time
bufferSize: 1000,
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "tanstackstart-react-orchestrion",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"build": "vite build && cp instrument.server.mjs .output/server && node add-instrumentation.mjs",
"start": "node .output/server/index.mjs",
"test": "playwright test",
"clean": "npx rimraf node_modules pnpm-lock.yaml",
"test:build": "pnpm install && pnpm build",
"test:build-latest": "pnpm add @tanstack/react-start@latest @tanstack/react-router@latest && pnpm install && pnpm build",
"test:assert": "pnpm test"
},
"//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels",
"dependencies": {
"@sentry/tanstackstart-react": "file:../../packed/sentry-tanstackstart-react-packed.tgz",
"@sentry/server-utils": "file:../../packed/sentry-server-utils-packed.tgz",
"@tanstack/react-start": "^1.136.0",
"@tanstack/react-router": "^1.136.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"nitro": "latest || *",
"ioredis": "5.10.1",
"mysql": "^2.18.1"
},
"devDependencies": {
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@types/node": "^24.10.0",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"typescript": "^5.9.0",
"vite": "7.3.2",
"vite-tsconfig-paths": "^5.1.4",
"@playwright/test": "~1.56.0",
"@sentry-internal/test-utils": "link:../../../test-utils"
},
"volta": {
"extends": "../../package.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getPlaywrightConfig } from '@sentry-internal/test-utils';

const config = getPlaywrightConfig({
startCommand: 'pnpm start',
port: 3000,
});

export default {
...config,
globalSetup: './global-setup.mjs',
globalTeardown: './global-teardown.mjs',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Imported first so Sentry.init() runs before any other import's side effects.
import './instrument.client';
import { StartClient } from '@tanstack/react-start/client';
import { StrictMode, startTransition } from 'react';
import { hydrateRoot } from 'react-dom/client';

startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<StartClient />
</StrictMode>,
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const __APP_DSN__: string;
declare const __APP_TUNNEL__: string | undefined;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/tanstackstart-react';

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: __APP_DSN__,
tracesSampleRate: 1.0,
release: 'e2e-test',
tunnel: __APP_TUNNEL__,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Sentry from '@sentry/tanstackstart-react';
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';

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

if (!router.isServer) {
Sentry.addIntegration(Sentry.tanstackRouterBrowserTracingIntegration(router));
}

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

export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Orchestrion',
},
],
}),
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,24 @@
import { createFileRoute } from '@tanstack/react-router';
import Redis from 'ioredis';

export const Route = createFileRoute('/api/db-ioredis')({
server: {
handlers: {
GET: async () => {
const redis = new Redis({
// Don't keep retrying forever if Redis goes away (e.g. on test teardown)
maxRetriesPerRequest: 1,
retryStrategy: () => null,
});

try {
await redis.set('test-key', 'test-value');
const value = await redis.get('test-key');
return Response.json({ value });
} finally {
redis.disconnect();
}
},
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createFileRoute } from '@tanstack/react-router';
import mysql from 'mysql';

const connection = mysql.createConnection({
user: 'root',
password: 'docker',
});

export const Route = createFileRoute('/api/db-mysql')({
server: {
handlers: {
GET: () => {
return new Promise<Response>(resolve => {
connection.query('SELECT 1 + 1 AS solution', () => {
connection.query('SELECT NOW()', ['1', '2'], () => {
resolve(Response.json({ status: 'ok' }));
});
});
});
},
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/react-router';

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

function Home() {
return <div>TanStack Start orchestrion e2e app</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { wrapFetchWithSentry } from '@sentry/tanstackstart-react';

import handler, { createServerEntry } from '@tanstack/react-start/server-entry';
import type { ServerEntry } from '@tanstack/react-start/server-entry';

const requestHandler: ServerEntry = wrapFetchWithSentry({
fetch(request: Request) {
return handler.fetch(request);
},
});

export default createServerEntry(requestHandler);
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-orchestrion',
});
Loading
Loading