Skip to content

Commit c68e630

Browse files
fix(v10/cloudflare): auto-instrument when wrangler main is a package (#25430)
Backport of: #25343 ## Differences to the original PR - `packages/cloudflare/src/vite/transform.ts`: on v10, a local class already wrapped by a named export keeps its binding (which now points at the wrapped class), so `export { Foo as default }` stays as written instead of being re-pointed at a wrapper name. - `packages/cloudflare/src/vite/autoInstrument.ts`, tests and the `cloudflare-autoinstrument-package-entry` e2e app: small adjustments to fit v10's code layout. Heads-up: this conflicts with #25407 (backport of #25345) in `packages/cloudflare/src/vite/transform.ts` and its test. Whichever lands second needs a rebase. This branch was prepared by the v10 backport automation, which was blocked at PR creation; it's opened manually now. <!-- junior-request-attribution:start --> via **Jan Peer Stöcklmair**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3A83fc948beed4fb12a54d41cb --> -- [View Junior Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A83fc948beed4fb12a54d41cb/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Nicolas Hrubec <nicolas.hrubec@outlook.com>
1 parent f3da3b6 commit c68e630

18 files changed

Lines changed: 695 additions & 50 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist
2+
.wrangler
3+
test-results
4+
pnpm-lock.yaml
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineCloudflareOptions } from '@sentry/cloudflare';
2+
3+
// The worker entry is a package, so the plugin reads this file from beside
4+
// the wrangler config.
5+
export default defineCloudflareOptions((env: { E2E_TEST_DSN: string }) => ({
6+
dsn: env.E2E_TEST_DSN,
7+
environment: 'qa',
8+
tunnel: 'http://localhost:3031/',
9+
tracesSampleRate: 1.0,
10+
}));
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "cloudflare-autoinstrument-package-entry",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"build": "vite build",
8+
"preview": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')",
9+
"test": "playwright test",
10+
"test:build": "pnpm install && pnpm build",
11+
"test:assert": "pnpm test"
12+
},
13+
"dependencies": {
14+
"@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz",
15+
"worker-entry": "file:../../test-applications/cloudflare-autoinstrument-package-entry/worker-entry"
16+
},
17+
"devDependencies": {
18+
"@cloudflare/vite-plugin": "^1.47.0",
19+
"@cloudflare/workers-types": "^5.20260727.1",
20+
"@playwright/test": "~1.56.0",
21+
"@sentry-internal/test-utils": "link:../../../test-utils",
22+
"vite": "^8.1.5",
23+
"wrangler": "^4.114.0"
24+
},
25+
"volta": {
26+
"node": "24.15.0",
27+
"extends": "../../package.json"
28+
}
29+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
export default getPlaywrightConfig(
4+
{
5+
startCommand: 'pnpm preview',
6+
port: 8787,
7+
},
8+
{
9+
workers: '100%',
10+
retries: 0,
11+
},
12+
);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { startEventProxyServer } from '@sentry-internal/test-utils';
2+
3+
startEventProxyServer({
4+
port: 3031,
5+
proxyServerName: 'cloudflare-autoinstrument-package-entry',
6+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
// The worker entry is a package with no Sentry calls. Any event that arrives
5+
// proves the plugin wrapped the module that wrangler's `main` resolved to.
6+
7+
test('wraps a package entry with withSentry (options from instrument.server.ts)', async ({ baseURL }) => {
8+
const transactionPromise = waitForTransaction('cloudflare-autoinstrument-package-entry', event => {
9+
return event.contexts?.trace?.op === 'http.server';
10+
});
11+
12+
const res = await fetch(baseURL!);
13+
expect(res.status).toBe(200);
14+
15+
const transaction = await transactionPromise;
16+
expect(transaction.contexts?.trace?.origin).toBe('auto.http.cloudflare');
17+
// Only `instrument.server.ts` sets this environment.
18+
expect(transaction.environment).toBe('qa');
19+
});
20+
21+
test('captures an error thrown by a package entry', async ({ baseURL }) => {
22+
const errorPromise = waitForError(
23+
'cloudflare-autoinstrument-package-entry',
24+
event => event.exception?.values?.[0]?.value === 'Thrown from a package entry',
25+
);
26+
27+
const res = await fetch(`${baseURL}/throw`);
28+
expect(res.status).toBe(500);
29+
30+
const error = await errorPromise;
31+
expect(error.environment).toBe('qa');
32+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { cloudflare } from '@cloudflare/vite-plugin';
2+
import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';
3+
import { defineConfig } from 'vite';
4+
5+
export default defineConfig({
6+
plugins: [
7+
cloudflare(),
8+
// On v10, auto-instrumentation is opt-in.
9+
sentryCloudflareVitePlugin({
10+
_experimental: {
11+
autoInstrumentation: true,
12+
},
13+
}),
14+
],
15+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Shaped like bundler output (e.g. `@astrojs/cloudflare`): the default export
2+
// is a specifier, not `export default <expr>`.
3+
var fetch_handler_default = {
4+
async fetch(request) {
5+
if (new URL(request.url).pathname === '/throw') {
6+
throw new Error('Thrown from a package entry');
7+
}
8+
return new Response('ok');
9+
},
10+
};
11+
export { fetch_handler_default as default };
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "worker-entry",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"exports": {
7+
"./fetch-handler": "./fetch-handler.js"
8+
}
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "node_modules/wrangler/config-schema.json",
3+
"name": "cloudflare-autoinstrument-package-entry",
4+
// A package export, not a file path. The Sentry plugin resolves it with Vite.
5+
"main": "worker-entry/fetch-handler",
6+
"compatibility_date": "2026-05-20",
7+
"compatibility_flags": ["nodejs_compat"],
8+
}

0 commit comments

Comments
 (0)