From 4351f855e06613e80d5a5e33d7e93d5fc5e6b41f Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 19 Oct 2026 14:06:59 +0000 Subject: [PATCH] fix(v10/astro): defer to `@sentry/cloudflare/vite` on Cloudflare Workers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of: #25347 Co-Authored-By: Jan VamPeer Stöcklmair --- .../astro-6-cf-workers/astro.config.mjs | 11 +++ .../astro-6-cf-workers/instrument.server.js | 9 ++ .../sentry.server.config.js | 13 --- .../worker-configuration.d.ts | 2 +- .../astro-6-cf-workers/wrangler.jsonc | 2 +- .../.gitignore | 4 - .../instrument.server.ts | 10 --- .../package.json | 29 ------ .../playwright.config.ts | 12 --- .../start-event-proxy.mjs | 6 -- .../tests/package-entry.test.ts | 32 ------- .../vite.config.ts | 15 ---- .../worker-entry/fetch-handler.js | 11 --- .../worker-entry/package.json | 9 -- .../wrangler.jsonc | 8 -- packages/astro/src/integration/cloudflare.ts | 10 ++- packages/astro/src/integration/index.ts | 35 +++++++- .../integration/cloudflareVitePlugin.test.ts | 48 ++++++++++ packages/astro/test/integration/index.test.ts | 89 +++++++++++++++++++ 19 files changed, 198 insertions(+), 157 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/astro-6-cf-workers/instrument.server.js delete mode 100644 dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.server.config.js delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/.gitignore delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/instrument.server.ts delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/playwright.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/tests/package-entry.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/vite.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/fetch-handler.js delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/wrangler.jsonc create mode 100644 packages/astro/test/integration/cloudflareVitePlugin.test.ts diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/astro.config.mjs index f19212596282..5eef35199932 100644 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/astro.config.mjs +++ b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/astro.config.mjs @@ -2,6 +2,7 @@ import sentry from '@sentry/astro'; // @ts-check import { defineConfig } from 'astro/config'; import cloudflare from '@astrojs/cloudflare'; +import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite'; // https://astro.build/config export default defineConfig({ @@ -18,4 +19,14 @@ export default defineConfig({ allowedDomains: [{ hostname: 'localhost' }], }, adapter: cloudflare(), + vite: { + plugins: [ + // On v10, auto-instrumentation is opt-in. + sentryCloudflareVitePlugin({ + _experimental: { + autoInstrumentation: true, + }, + }), + ], + }, }); diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/instrument.server.js b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/instrument.server.js new file mode 100644 index 000000000000..eeb325d68c3f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/instrument.server.js @@ -0,0 +1,9 @@ +import { defineCloudflareOptions } from '@sentry/cloudflare'; + +export default defineCloudflareOptions(env => ({ + dsn: env.E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', // proxy server + debug: true, +})); diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.server.config.js deleted file mode 100644 index 8f1a839bc05f..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/sentry.server.config.js +++ /dev/null @@ -1,13 +0,0 @@ -import * as Sentry from '@sentry/cloudflare'; -import handler from '@astrojs/cloudflare/entrypoints/server'; - -export default Sentry.withSentry( - env => ({ - dsn: env.E2E_TEST_DSN, - environment: 'qa', - tracesSampleRate: 1.0, - tunnel: 'http://localhost:3031/', // proxy server - debug: true, - }), - handler, -); diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/worker-configuration.d.ts b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/worker-configuration.d.ts index 03c757e9dcc2..96d062b4c7ac 100644 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/worker-configuration.d.ts +++ b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/worker-configuration.d.ts @@ -5,7 +5,7 @@ interface __BaseEnv_Env { } declare namespace Cloudflare { interface GlobalProps { - mainModule: typeof import('./sentry.server.config'); + mainModule: typeof import('@astrojs/cloudflare/entrypoints/server'); } interface Env extends __BaseEnv_Env {} } diff --git a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/wrangler.jsonc index aeabb81d7c07..96df92ce1118 100644 --- a/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/wrangler.jsonc +++ b/dev-packages/e2e-tests/test-applications/astro-6-cf-workers/wrangler.jsonc @@ -2,7 +2,7 @@ "compatibility_date": "2026-03-12", "compatibility_flags": ["global_fetch_strictly_public", "nodejs_als"], "name": "astro-6-cf-workers", - "main": "./sentry.server.config.js", + "main": "@astrojs/cloudflare/entrypoints/server", "assets": { "directory": "./dist", "binding": "ASSETS", diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/.gitignore b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/.gitignore deleted file mode 100644 index 3f64191fe034..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -.wrangler -test-results -pnpm-lock.yaml diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/instrument.server.ts b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/instrument.server.ts deleted file mode 100644 index 10d0f17fbcdb..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/instrument.server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineCloudflareOptions } from '@sentry/cloudflare'; - -// The worker entry is a package, so the plugin reads this file from beside -// the wrangler config. -export default defineCloudflareOptions((env: { E2E_TEST_DSN: string }) => ({ - dsn: env.E2E_TEST_DSN, - environment: 'qa', - tunnel: 'http://localhost:3031/', - tracesSampleRate: 1.0, -})); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/package.json deleted file mode 100644 index 63c24d1f32f2..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "cloudflare-autoinstrument-package-entry", - "version": "0.0.0", - "private": true, - "type": "module", - "scripts": { - "build": "vite build", - "preview": "wrangler dev --var \"E2E_TEST_DSN:$E2E_TEST_DSN\" --log-level=$(test $CI && echo 'none' || echo 'log')", - "test": "playwright test", - "test:build": "pnpm install && pnpm build", - "test:assert": "pnpm test" - }, - "dependencies": { - "@sentry/cloudflare": "file:../../packed/sentry-cloudflare-packed.tgz", - "worker-entry": "file:../../test-applications/cloudflare-autoinstrument-package-entry/worker-entry" - }, - "devDependencies": { - "@cloudflare/vite-plugin": "^1.47.0", - "@cloudflare/workers-types": "^5.20260727.1", - "@playwright/test": "~1.56.0", - "@sentry-internal/test-utils": "link:../../../test-utils", - "vite": "^8.1.5", - "wrangler": "^4.114.0" - }, - "volta": { - "node": "24.15.0", - "extends": "../../package.json" - } -} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/playwright.config.ts b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/playwright.config.ts deleted file mode 100644 index 9633de63b272..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/playwright.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -export default getPlaywrightConfig( - { - startCommand: 'pnpm preview', - port: 8787, - }, - { - workers: '100%', - retries: 0, - }, -); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/start-event-proxy.mjs deleted file mode 100644 index 553dac81dc3c..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'cloudflare-autoinstrument-package-entry', -}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/tests/package-entry.test.ts b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/tests/package-entry.test.ts deleted file mode 100644 index 770a32931013..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/tests/package-entry.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; - -// The worker entry is a package with no Sentry calls. Any event that arrives -// proves the plugin wrapped the module that wrangler's `main` resolved to. - -test('wraps a package entry with withSentry (options from instrument.server.ts)', async ({ baseURL }) => { - const transactionPromise = waitForTransaction('cloudflare-autoinstrument-package-entry', event => { - return event.contexts?.trace?.op === 'http.server'; - }); - - const res = await fetch(baseURL!); - expect(res.status).toBe(200); - - const transaction = await transactionPromise; - expect(transaction.contexts?.trace?.origin).toBe('auto.http.cloudflare'); - // Only `instrument.server.ts` sets this environment. - expect(transaction.environment).toBe('qa'); -}); - -test('captures an error thrown by a package entry', async ({ baseURL }) => { - const errorPromise = waitForError( - 'cloudflare-autoinstrument-package-entry', - event => event.exception?.values?.[0]?.value === 'Thrown from a package entry', - ); - - const res = await fetch(`${baseURL}/throw`); - expect(res.status).toBe(500); - - const error = await errorPromise; - expect(error.environment).toBe('qa'); -}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/vite.config.ts b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/vite.config.ts deleted file mode 100644 index 519dab4471c2..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/vite.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { cloudflare } from '@cloudflare/vite-plugin'; -import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - plugins: [ - cloudflare(), - // On v10, auto-instrumentation is opt-in. - sentryCloudflareVitePlugin({ - _experimental: { - autoInstrumentation: true, - }, - }), - ], -}); diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/fetch-handler.js b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/fetch-handler.js deleted file mode 100644 index 1d8a976634e6..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/fetch-handler.js +++ /dev/null @@ -1,11 +0,0 @@ -// Shaped like bundler output (e.g. `@astrojs/cloudflare`): the default export -// is a specifier, not `export default `. -var fetch_handler_default = { - async fetch(request) { - if (new URL(request.url).pathname === '/throw') { - throw new Error('Thrown from a package entry'); - } - return new Response('ok'); - }, -}; -export { fetch_handler_default as default }; diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/package.json deleted file mode 100644 index 08018b205e6e..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/worker-entry/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "worker-entry", - "version": "0.0.0", - "private": true, - "type": "module", - "exports": { - "./fetch-handler": "./fetch-handler.js" - } -} diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/wrangler.jsonc b/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/wrangler.jsonc deleted file mode 100644 index 4f9bc84616ee..000000000000 --- a/dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument-package-entry/wrangler.jsonc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "node_modules/wrangler/config-schema.json", - "name": "cloudflare-autoinstrument-package-entry", - // A package export, not a file path. The Sentry plugin resolves it with Vite. - "main": "worker-entry/fetch-handler", - "compatibility_date": "2026-05-20", - "compatibility_flags": ["nodejs_compat"], -} diff --git a/packages/astro/src/integration/cloudflare.ts b/packages/astro/src/integration/cloudflare.ts index c07f93ed16dc..d5447215dbf8 100644 --- a/packages/astro/src/integration/cloudflare.ts +++ b/packages/astro/src/integration/cloudflare.ts @@ -32,6 +32,10 @@ export function sentryCloudflareNodeWarningPlugin(): Plugin { }; } +// The Worker entry module: `@astrojs/cloudflare` v12 builds its own virtual entry, and v13+ builds +// through `@cloudflare/vite-plugin`, whose entry ends in `export default mod.default ?? {};`. +const WORKER_ENTRY_IDS = ['astrojs-ssr-virtual-entry', 'virtual:cloudflare/worker-entry']; + /** * A Vite plugin that ensures the Sentry server config is loaded at the * top level of the Cloudflare Worker entry module, rather than only being @@ -52,16 +56,14 @@ export function sentryCloudflareVitePlugin(): Plugin { enforce: 'post', transform(code, id) { - // Match the Astro SSR virtual entry — this becomes dist/_worker.js/index.js - // The resolved virtual module ID is `\0@astrojs-ssr-virtual-entry` - if (!id.includes('astrojs-ssr-virtual-entry')) { + if (!WORKER_ENTRY_IDS.some(entryId => id.includes(entryId))) { return undefined; } // In @astrojs/cloudflare v12, the virtual entry module structure is: // https://github.com/withastro/astro/blob/09bbdbb1e62c388eb405eeea03554c15e01f2957/packages/integrations/cloudflare/src/entrypoints/server.ts#L23 // We need to wrap `default` with `withSentry` before it's exported. - const defaultExportMatch = code.match(/export\s+default\s+([\w.]+)\s*;/); + const defaultExportMatch = code.match(/export\s+default\s+([^;]+?)\s*;/); if (!defaultExportMatch) { return undefined; diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 15b0f7c0c731..d509636443b5 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -166,6 +166,9 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { const isCloudflare = config?.adapter?.name?.startsWith('@astrojs/cloudflare'); const isCloudflareWorkers = isCloudflare && !isCloudflarePages(); + // With `_experimental.autoInstrumentation`, that plugin wraps the Worker entry, which inits + // the SDK from `instrument.server.*` or `env`, so our wrap and default init are not needed. + const usesCloudflareVitePlugin = isCloudflareWorkers && hasCloudflareVitePlugin(config); if (isCloudflare) { try { @@ -189,7 +192,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { // This ensures Sentry.init() runs in dev mode (where the Vite plugin doesn't fire) // and also serves as the fallback for non-Cloudflare adapters in production. injectScript('page-ssr', buildSdkInitFileImportSnippet(pathToServerInit)); - } else { + } else if (!usesCloudflareVitePlugin) { debug && logger.info('Using default server init.'); injectScript('page-ssr', buildServerSnippet(options || {})); } @@ -209,7 +212,9 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { debug && logger.info('Adding Cloudflare Vite plugin to wrap Worker entry with withSentry.'); updateConfig({ vite: { - plugins: [sentryCloudflareNodeWarningPlugin(), sentryCloudflareVitePlugin()], + plugins: usesCloudflareVitePlugin + ? [sentryCloudflareNodeWarningPlugin()] + : [sentryCloudflareNodeWarningPlugin(), sentryCloudflareVitePlugin()], }, }); } else if (isCloudflare) { @@ -250,6 +255,32 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { }; }; +// Of the sub-plugins `sentryCloudflareVitePlugin()` adds, only the one for +// `_experimental.autoInstrumentation`, which wraps the Worker, has this prefix. +const CLOUDFLARE_VITE_PLUGIN_NAME_PREFIX = 'sentry-cloudflare-'; + +/** + * Whether the user added `sentryCloudflareVitePlugin()` from `@sentry/cloudflare/vite`. It is + * detected by name because importing it here would load `@sentry/cloudflare` and `wrangler` for + * every Astro app. + */ +function hasCloudflareVitePlugin(config: AstroConfig): boolean { + const pending: unknown[] = [config.vite?.plugins]; + while (pending.length > 0) { + const plugin = pending.pop(); + if (Array.isArray(plugin)) { + pending.push(...plugin); + } else if ( + typeof plugin === 'object' && + plugin !== null && + String((plugin as { name?: unknown }).name).startsWith(CLOUDFLARE_VITE_PLUGIN_NAME_PREFIX) + ) { + return true; + } + } + return false; +} + const possibleFileExtensions = ['ts', 'js', 'tsx', 'jsx', 'mjs', 'cjs', 'mts']; function findDefaultSdkInitFile(type: 'server' | 'client'): string | undefined { diff --git a/packages/astro/test/integration/cloudflareVitePlugin.test.ts b/packages/astro/test/integration/cloudflareVitePlugin.test.ts new file mode 100644 index 000000000000..0464acd321e2 --- /dev/null +++ b/packages/astro/test/integration/cloudflareVitePlugin.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; +import { sentryCloudflareVitePlugin } from '../../src/integration/cloudflare'; + +function transform(code: string, id: string) { + const plugin = sentryCloudflareVitePlugin() as { transform: (code: string, id: string) => unknown }; + return plugin.transform(code, id); +} + +describe('sentryCloudflareVitePlugin', () => { + it('wraps the default export of the @astrojs/cloudflare v12 virtual entry', () => { + const code = ['const _exports = createExports(manifest);', 'export default _exports.default;'].join('\n'); + + expect(transform(code, '\0@astrojs-ssr-virtual-entry')).toEqual({ + code: [ + "import { withSentry } from '@sentry/cloudflare';", + 'const _exports = createExports(manifest);', + 'export default withSentry(() => undefined, _exports.default);', + ].join('\n'), + map: null, + }); + }); + + it('wraps the default export of the @cloudflare/vite-plugin worker entry', () => { + const code = [ + 'import { getExportTypes } from "virtual:cloudflare/export-types";', + 'import * as mod from "virtual:cloudflare/user-entry";', + 'export * from "virtual:cloudflare/user-entry";', + 'export default mod.default ?? {};', + 'if (import.meta.hot) { import.meta.hot.accept(); }', + ].join('\n'); + + expect(transform(code, '\0virtual:cloudflare/worker-entry')).toEqual({ + code: [ + "import { withSentry } from '@sentry/cloudflare';", + 'import { getExportTypes } from "virtual:cloudflare/export-types";', + 'import * as mod from "virtual:cloudflare/user-entry";', + 'export * from "virtual:cloudflare/user-entry";', + 'export default withSentry(() => undefined, mod.default ?? {});', + 'if (import.meta.hot) { import.meta.hot.accept(); }', + ].join('\n'), + map: null, + }); + }); + + it('leaves other modules alone', () => { + expect(transform('export default handler;', '/src/pages/index.astro')).toBeUndefined(); + }); +}); diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 3bc79d003a55..0d058e958ad0 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -353,6 +353,95 @@ describe('sentryAstro integration', () => { expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0); }); + // The plugin returns an array, so it arrives nested. + describe.each([ + { + setup: 'sentryCloudflareVitePlugin({ _experimental: { autoInstrumentation: true } })', + plugins: [{ name: 'sentry-cloudflare-auto-instrument' }], + }, + { + setup: + 'sentryCloudflareVitePlugin({ _experimental: { autoInstrumentation: true, useDiagnosticsChannelInjection: true } })', + plugins: [{ name: 'code-transformer' }, { name: 'sentry-cloudflare-auto-instrument' }], + }, + ])('with `$setup` from `@sentry/cloudflare/vite` in the vite config', ({ plugins }) => { + const cloudflareConfig = { + ...config, + adapter: { name: '@astrojs/cloudflare' }, + vite: { plugins: [plugins] }, + } as AstroConfig; + + it('leaves the Worker wrap to that plugin', async () => { + const integration = sentryAstro({}); + + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config: cloudflareConfig, + }); + + expect(updateConfig).toHaveBeenCalledWith({ + vite: { plugins: [expect.objectContaining({ name: 'sentry-astro-cloudflare-suppress-node-warnings' })] }, + }); + expect(updateConfig).not.toHaveBeenCalledWith({ + vite: { plugins: expect.arrayContaining([expect.objectContaining({ name: 'sentry-astro-cloudflare' })]) }, + }); + }); + + it('does not inject the default server init', async () => { + const integration = sentryAstro({}); + + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config: cloudflareConfig, + }); + + expect(injectScript).not.toHaveBeenCalledWith('page-ssr', expect.anything()); + }); + + it('still injects a server init file', async () => { + const integration = sentryAstro({ serverInitPath: 'my-server-init-path.js' }); + + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config: cloudflareConfig, + }); + + expect(injectScript).toHaveBeenCalledWith('page-ssr', expect.stringContaining('my-server-init-path.js')); + }); + }); + + // Without `_experimental.autoInstrumentation`, the plugin does not wrap the Worker on v10. + it('keeps its own Worker wrap when `@sentry/cloudflare/vite` only adds the orchestrion transform', async () => { + const integration = sentryAstro({}); + const cloudflareConfig = { + ...config, + adapter: { name: '@astrojs/cloudflare' }, + vite: { plugins: [[{ name: 'code-transformer' }]] }, + } as AstroConfig; + + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config: cloudflareConfig, + }); + + expect(updateConfig).toHaveBeenCalledWith({ + vite: { plugins: expect.arrayContaining([expect.objectContaining({ name: 'sentry-astro-cloudflare' })]) }, + }); + expect(injectScript).toHaveBeenCalledWith('page-ssr', expect.anything()); + }); + it.each([{}, { enabled: true }])('injects client and server init scripts', async options => { const integration = sentryAstro(options);