diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 66b5de50861b..76bae75d5973 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -248,7 +248,8 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { if (isSSR && shouldAddMiddleware) { addMiddleware({ order: 'pre', - entrypoint: serverInitSnippet ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', + entrypoint: + serverInitSnippet && !isCloudflareWorkers ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', }); } }, diff --git a/packages/astro/src/server/sdk.ts b/packages/astro/src/server/sdk.ts index 557c1b7812d7..98910a1db2fd 100644 --- a/packages/astro/src/server/sdk.ts +++ b/packages/astro/src/server/sdk.ts @@ -4,11 +4,21 @@ import { applySdkMetadata } from '@sentry/core'; import type { NodeClient, NodeOptions } from '@sentry/node'; import { init as initNodeSdk } from '@sentry/node'; +declare const EdgeRuntime: string | undefined; +declare const Deno: unknown; +declare const Netlify: unknown; + /** * * @param options */ export function init(options: NodeOptions): NodeClient | undefined { + // With `edgeMiddleware` enabled, Vercel and Netlify bundle our middleware (which imports the + // server init) into a separate edge function, where the Node SDK can't run. + if (isEdgeRuntime()) { + return undefined; + } + const opts = { ...options, }; @@ -26,3 +36,8 @@ export function init(options: NodeOptions): NodeClient | undefined { return initNodeSdk(opts); } + +function isEdgeRuntime(): boolean { + // Netlify also defines the `Netlify` global in its Node functions, so only Deno + Netlify is its edge. + return typeof EdgeRuntime === 'string' || (typeof Netlify !== 'undefined' && typeof Deno !== 'undefined'); +} diff --git a/packages/astro/test/integration/index.files.test.ts b/packages/astro/test/integration/index.files.test.ts index 125333ede7be..05cf524543b8 100644 --- a/packages/astro/test/integration/index.files.test.ts +++ b/packages/astro/test/integration/index.files.test.ts @@ -37,4 +37,22 @@ describe('sentryAstro integration', () => { /^import ".*\/sentry.server.config.js"/, ); }); + + it('imports the default server init file from the middleware entrypoint', async () => { + const addMiddleware = vi.fn(); + const updateConfig = vi.fn(); + const setup = sentryAstro({}).hooks['astro:config:setup'] as (params: unknown) => Promise; + + await setup({ updateConfig, injectScript, addMiddleware, config: { output: 'server' } }); + + expect(addMiddleware).toHaveBeenCalledWith({ + order: 'pre', + entrypoint: 'virtual:@sentry/astro/middleware', + }); + + const plugin = updateConfig.mock.calls + .flatMap(([updatedConfig]) => updatedConfig.vite?.plugins ?? []) + .find((vitePlugin: { name?: string }) => vitePlugin?.name === 'sentry-astro-server-init'); + expect(plugin.load('\0virtual:@sentry/astro/server-init')).toMatch(/^import ".*\/sentry.server.config.js";$/); + }); }); diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 62481501bd23..870c28dc5b30 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -690,6 +690,24 @@ describe('sentryAstro integration', () => { }); }); + it('keeps the default middleware entrypoint for the Cloudflare Workers adapter', async () => { + const addMiddleware = vi.fn(); + const setup = sentryAstro({ serverInitPath: 'my-server-init-path.js' }).hooks['astro:config:setup'] as ( + params: unknown, + ) => Promise; + + await setup({ + ...baseConfigHookObject, + config: { output: 'server', adapter: { name: '@astrojs/cloudflare' } }, + addMiddleware, + updateConfig, + injectScript, + }); + + expect(addMiddleware).toHaveBeenCalledWith({ order: 'pre', entrypoint: '@sentry/astro/middleware' }); + expect(getServerInit(updateConfig)).toContain('my-server-init-path.js'); + }); + it('runs the server init once, from SSR pages and the middleware alike, so requests that render no page find a client', async () => { const integration = sentryAstro({}); const updateConfig = vi.fn(); diff --git a/packages/astro/test/server/sdk.test.ts b/packages/astro/test/server/sdk.test.ts index 6de7cab9f38e..e9d15f50cb7f 100644 --- a/packages/astro/test/server/sdk.test.ts +++ b/packages/astro/test/server/sdk.test.ts @@ -10,10 +10,42 @@ describe('Sentry server SDK', () => { describe('init', () => { afterEach(() => { vi.clearAllMocks(); + vi.unstubAllGlobals(); getMainCarrier().__SENTRY__ = undefined; }); + it("doesn't initialize the SDK in the Vercel Edge runtime", () => { + vi.stubGlobal('EdgeRuntime', 'edge-runtime'); + + expect(init({})).toBeUndefined(); + expect(nodeInit).not.toHaveBeenCalled(); + }); + + it("doesn't initialize the SDK in Netlify edge functions", () => { + vi.stubGlobal('Netlify', {}); + vi.stubGlobal('Deno', {}); + + expect(init({})).toBeUndefined(); + expect(nodeInit).not.toHaveBeenCalled(); + }); + + it('initializes the SDK in Netlify Node functions', () => { + vi.stubGlobal('Netlify', {}); + + init({}); + + expect(nodeInit).toHaveBeenCalledTimes(1); + }); + + it('initializes the SDK in Deno', () => { + vi.stubGlobal('Deno', {}); + + init({}); + + expect(nodeInit).toHaveBeenCalledTimes(1); + }); + it('adds Astro metadata to the SDK options', () => { expect(nodeInit).not.toHaveBeenCalled();