diff --git a/.size-limit.js b/.size-limit.js index 2d07afde52ab..4e929875dad5 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -240,7 +240,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '158 KB', + limit: '160 KB', }, { name: '@sentry/node - without tracing', diff --git a/CHANGELOG.md b/CHANGELOG.md index d5cf4d9b810f..09030a4dc82a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +- fix(node): Fix Spotlight configuration precedence to match specification (#18195) ## 10.25.0 diff --git a/packages/node-core/src/sdk/index.ts b/packages/node-core/src/sdk/index.ts index d53f5d4faefb..0814ab401535 100644 --- a/packages/node-core/src/sdk/index.ts +++ b/packages/node-core/src/sdk/index.ts @@ -182,8 +182,24 @@ function getClientOptions( getDefaultIntegrationsImpl: (options: Options) => Integration[], ): NodeClientOptions { const release = getRelease(options.release); - const spotlight = - options.spotlight ?? envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }) ?? process.env.SENTRY_SPOTLIGHT; + + // Parse spotlight configuration with proper precedence per spec + let spotlight: boolean | string | undefined; + if (options.spotlight === false) { + spotlight = false; + } else if (typeof options.spotlight === 'string') { + spotlight = options.spotlight; + } else { + // options.spotlight is true or undefined + const envBool = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }); + const envUrl = envBool === null && process.env.SENTRY_SPOTLIGHT ? process.env.SENTRY_SPOTLIGHT : undefined; + + spotlight = + options.spotlight === true + ? (envUrl ?? true) // true: use env URL if present, otherwise true + : (envBool ?? envUrl); // undefined: use env var (bool or URL) + } + const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); const mergedOptions = { diff --git a/packages/node-core/test/sdk/init.test.ts b/packages/node-core/test/sdk/init.test.ts index 4262bffb7cda..1332b89abcd6 100644 --- a/packages/node-core/test/sdk/init.test.ts +++ b/packages/node-core/test/sdk/init.test.ts @@ -216,6 +216,114 @@ describe('init()', () => { }), ); }); + + describe('spotlight configuration', () => { + afterEach(() => { + delete process.env.SENTRY_SPOTLIGHT; + }); + + it('enables spotlight with default URL from `SENTRY_SPOTLIGHT` env variable (truthy value)', () => { + process.env.SENTRY_SPOTLIGHT = 'true'; + + const client = init({ dsn: PUBLIC_DSN }); + + expect(client?.getOptions().spotlight).toBe(true); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('disables spotlight from `SENTRY_SPOTLIGHT` env variable (falsy value)', () => { + process.env.SENTRY_SPOTLIGHT = 'false'; + + const client = init({ dsn: PUBLIC_DSN }); + + expect(client?.getOptions().spotlight).toBe(false); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(false); + }); + + it('enables spotlight with custom URL from `SENTRY_SPOTLIGHT` env variable', () => { + process.env.SENTRY_SPOTLIGHT = 'http://localhost:3000/stream'; + + const client = init({ dsn: PUBLIC_DSN }); + + expect(client?.getOptions().spotlight).toBe('http://localhost:3000/stream'); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('enables spotlight with default URL from config `true`', () => { + const client = init({ dsn: PUBLIC_DSN, spotlight: true }); + + expect(client?.getOptions().spotlight).toBe(true); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('disables spotlight from config `false`', () => { + const client = init({ dsn: PUBLIC_DSN, spotlight: false }); + + expect(client?.getOptions().spotlight).toBe(false); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(false); + }); + + it('enables spotlight with custom URL from config', () => { + const client = init({ dsn: PUBLIC_DSN, spotlight: 'http://custom:8888/stream' }); + + expect(client?.getOptions().spotlight).toBe('http://custom:8888/stream'); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('config `false` overrides `SENTRY_SPOTLIGHT` env variable URL', () => { + process.env.SENTRY_SPOTLIGHT = 'http://localhost:3000/stream'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: false }); + + expect(client?.getOptions().spotlight).toBe(false); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(false); + }); + + it('config `false` overrides `SENTRY_SPOTLIGHT` env variable truthy value', () => { + process.env.SENTRY_SPOTLIGHT = 'true'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: false }); + + expect(client?.getOptions().spotlight).toBe(false); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(false); + }); + + it('config `false` with `SENTRY_SPOTLIGHT` env variable falsy value keeps spotlight disabled', () => { + process.env.SENTRY_SPOTLIGHT = 'false'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: false }); + + expect(client?.getOptions().spotlight).toBe(false); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(false); + }); + + it('config URL overrides `SENTRY_SPOTLIGHT` env variable URL', () => { + process.env.SENTRY_SPOTLIGHT = 'http://env:3000/stream'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: 'http://config:8888/stream' }); + + expect(client?.getOptions().spotlight).toBe('http://config:8888/stream'); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('config `true` with env var URL uses env var URL', () => { + process.env.SENTRY_SPOTLIGHT = 'http://localhost:3000/stream'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: true }); + + expect(client?.getOptions().spotlight).toBe('http://localhost:3000/stream'); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + + it('config `true` with env var truthy value uses default URL', () => { + process.env.SENTRY_SPOTLIGHT = 'true'; + + const client = init({ dsn: PUBLIC_DSN, spotlight: true }); + + expect(client?.getOptions().spotlight).toBe(true); + expect(client?.getOptions().integrations.some(integration => integration.name === 'Spotlight')).toBe(true); + }); + }); }); });