Skip to content

Commit a1967fe

Browse files
authored
feat(solidstart): Serve the managed tunnel on SolidStart v1 (#25423)
This PR wires SolidStart's SDK tunnel by adding a nitro 2 plugin that adds a request hook to handle tunneled requests. The base path comes from `server.baseURL`, and the `tunnel-basepath` e2e variants cover dev and prod. For reviewers: if you need an overview of what this is, refer to #25199. closes #25465
1 parent 99314fa commit a1967fe

20 files changed

Lines changed: 304 additions & 16 deletions

File tree

‎dev-packages/e2e-tests/test-applications/solidstart/app.config.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ export default defineConfig(
55
withSentry(
66
{
77
middleware: './src/middleware.ts',
8+
// The base-path variant serves the app, and so the tunnel, under `/app`.
9+
...(process.env.E2E_TEST_BASE_PATH && { server: { baseURL: `${process.env.E2E_TEST_BASE_PATH}/` } }),
10+
vite: {
11+
define: {
12+
__E2E_TEST_TUNNEL__: JSON.stringify(!!process.env.E2E_TEST_TUNNEL),
13+
},
14+
},
815
},
916
{
1017
sentryUrl: 'http://localhost:3032',

‎dev-packages/e2e-tests/test-applications/solidstart/instrument.server.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ Sentry.init({
77
environment: 'qa', // dynamic sampling bias to keep transactions
88
tracesSampleRate: 1.0, // Capture 100% of the transactions
99
tunnel: 'http://localhost:3031/', // proxy server
10+
integrations: process.env.E2E_TEST_TUNNEL ? [Sentry.serveTunnelIntegration()] : [],
1011
debug: !!process.env.DEBUG,
1112
});

‎dev-packages/e2e-tests/test-applications/solidstart/package.json‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
"test:prod": "TEST_ENV=production playwright test",
1111
"test:build": "pnpm install && pnpm build",
1212
"test:assert": "node assert-build.ts && pnpm test:prod && pnpm test:dev",
13-
"test:dev": "TEST_ENV=development playwright test db"
13+
"test:dev": "TEST_ENV=development playwright test db",
14+
"test:build:tunnel-basepath": "pnpm install && E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app pnpm build",
15+
"test:assert:tunnel-basepath": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 TEST_ENV=production playwright test tunnel",
16+
"test:assert:tunnel-basepath-dev": "E2E_TEST_TUNNEL=1 E2E_TEST_BASE_PATH=/app E2E_TEST_DSN=http://public@localhost:3031/1337 TEST_ENV=development playwright test tunnel"
1417
},
1518
"type": "module",
1619
"dependencies": {
@@ -30,11 +33,25 @@
3033
"jsdom": "^24.0.0",
3134
"solid-js": "1.9.5",
3235
"typescript": "^5.4.5",
33-
"vinxi": "^0.4.0",
34-
"vite": "^5.4.11",
36+
"vinxi": "^0.5.11",
37+
"vite": "^6.4.4",
3538
"vite-plugin-solid": "^2.11.6"
3639
},
3740
"volta": {
3841
"extends": "../../package.json"
42+
},
43+
"sentryTest": {
44+
"variants": [
45+
{
46+
"build-command": "pnpm test:build:tunnel-basepath",
47+
"assert-command": "pnpm test:assert:tunnel-basepath",
48+
"label": "solidstart (tunnel-basepath)"
49+
},
50+
{
51+
"build-command": "pnpm install",
52+
"assert-command": "pnpm test:assert:tunnel-basepath-dev",
53+
"label": "solidstart (tunnel-basepath-dev)"
54+
}
55+
]
3956
}
4057
}

‎dev-packages/e2e-tests/test-applications/solidstart/src/entry-client.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import * as Sentry from '@sentry/solidstart';
44
import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter';
55
import { StartClient, mount } from '@solidjs/start/client';
66

7+
declare const __E2E_TEST_TUNNEL__: boolean;
8+
79
Sentry.init({
810
// We can't use env variables here, seems like they are stripped
911
// out in production builds.
10-
dsn: 'https://public@dsn.ingest.sentry.io/1337',
12+
dsn: __E2E_TEST_TUNNEL__ ? 'http://public@localhost:3031/1337' : 'https://public@dsn.ingest.sentry.io/1337',
1113
environment: 'qa', // dynamic sampling bias to keep transactions
1214
integrations: [solidRouterBrowserTracingIntegration()],
13-
tunnel: 'http://localhost:3031/', // proxy server
15+
tunnel: __E2E_TEST_TUNNEL__ ? true : 'http://localhost:3031/', // proxy server
1416
// Performance Monitoring
1517
tracesSampleRate: 1.0, // Capture 100% of the transactions
1618
debug: !!import.meta.env.DEBUG,

‎dev-packages/e2e-tests/test-applications/solidstart/src/instrument.server.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ Sentry.init({
55
environment: 'qa', // dynamic sampling bias to keep transactions
66
tracesSampleRate: 1.0, // Capture 100% of the transactions
77
tunnel: 'http://localhost:3031/', // proxy server
8+
integrations: process.env.E2E_TEST_TUNNEL ? [Sentry.serveTunnelIntegration()] : [],
89
debug: !!process.env.DEBUG,
910
});

‎dev-packages/e2e-tests/test-applications/solidstart/src/middleware.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,14 @@ import { sentryBeforeResponseMiddleware } from '@sentry/solidstart';
22
import { createMiddleware } from '@solidjs/start/middleware';
33

44
export default createMiddleware({
5+
onRequest: [
6+
// The tunnel variant checks that the managed tunnel answers before an auth middleware like this one.
7+
event => {
8+
const request = event.request;
9+
if (process.env.E2E_TEST_TUNNEL && request.method === 'POST' && request.headers.get('x-auth') !== 'ok') {
10+
return new Response('blocked by user auth', { status: 401 });
11+
}
12+
},
13+
],
514
onBeforeResponse: [sentryBeforeResponseMiddleware()],
615
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect, test } from '@playwright/test';
2+
import { isServerSpanAt, watchForTunnelSpan } from '@sentry-internal/test-utils';
3+
4+
test.skip(!process.env.E2E_TEST_TUNNEL, 'Only runs in the tunnel variant');
5+
6+
const APP_NAME = 'solidstart';
7+
const BASE_PATH = process.env.E2E_TEST_BASE_PATH ?? '';
8+
const TUNNEL_PATH = new RegExp(`^${BASE_PATH}/[a-z0-9]{1,7}$`);
9+
10+
test('answers the managed tunnel before the app middleware', async ({ page }) => {
11+
// The variant's auth middleware rejects every other POST.
12+
expect((await page.request.post(`${BASE_PATH}/`, { data: 'x' })).status()).toBe(401);
13+
14+
const raceTunnelSpan = watchForTunnelSpan(APP_NAME, isServerSpanAt(TUNNEL_PATH));
15+
const tunnelResponsePromise = page.waitForResponse(
16+
response => response.request().method() === 'POST' && TUNNEL_PATH.test(new URL(response.url()).pathname),
17+
);
18+
19+
await page.goto(`${BASE_PATH}/`);
20+
21+
expect((await tunnelResponsePromise).status()).toBe(200);
22+
expect(
23+
await raceTunnelSpan(isServerSpanAt(`${BASE_PATH}/users/6`), () => page.request.get(`${BASE_PATH}/users/6`)),
24+
).toBe('anchor');
25+
});

‎packages/solidstart/package.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@
8787
"@sentry/server-utils": "11.9.0",
8888
"@sentry/solid": "11.9.0",
8989
"@sentry/bundler-plugins": "11.9.0",
90-
"@sentry/conventions": "0.29.0"
90+
"@sentry/conventions": "0.29.0",
91+
"h3": "^1.15.10"
9192
},
9293
"devDependencies": {
9394
"@sentry-internal/rollup-utils": "11.9.0",
@@ -99,6 +100,7 @@
99100
"@testing-library/user-event": "^14.5.2",
100101
"@types/node": "^20.19.0",
101102
"nitropack": "^2.13.4",
103+
"node-mock-http": "^1.0.4",
102104
"solid-js": "^1.9.11",
103105
"vinxi": "^0.5.11",
104106
"vite": "^8.3.1",

‎packages/solidstart/rollup.npm.config.mjs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ export default makeNPMConfigVariants(
1313
'src/client/solidrouter.ts',
1414
'src/server/solidrouter.ts',
1515
'src/vite/index.ts',
16+
'src/nitro/tunnelPlugin.ts',
1617
],
1718
// prevent this internal code from ending up in our built package (this doesn't happen automatically because
1819
// the name doesn't match an SDK dependency)
1920
packageSpecificConfig: {
20-
external: ['solid-js/web', 'solid-js', '@sentry/solid', '@sentry/solid/solidrouter'],
21+
external: ['solid-js/web', 'solid-js', '@sentry/solid', '@sentry/solid/solidrouter', 'h3'],
2122
output: {
2223
dynamicImportInCjs: true,
2324
},

‎packages/solidstart/src/client/sdk.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Client, Integration } from '@sentry/core';
22
import { applySdkMetadata } from '@sentry/core';
33
import type { BrowserOptions } from '@sentry/solid';
4+
import { applyTunnelBasePath } from '../common/tunnel';
45
import {
56
browserTracingIntegration,
67
getDefaultIntegrations as getDefaultSolidIntegrations,
@@ -19,6 +20,7 @@ export function init(options: BrowserOptions): Client | undefined {
1920
...options,
2021
};
2122

23+
applyTunnelBasePath(opts);
2224
applySdkMetadata(opts, 'solidstart', ['solidstart', 'solid']);
2325

2426
return initSolidSDK(opts);

0 commit comments

Comments
 (0)