diff --git a/.size-limit.js b/.size-limit.js index b4819fc83126..2d07afde52ab 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -5,14 +5,14 @@ module.exports = [ // Browser SDK (ESM) { name: '@sentry/browser', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init'), gzip: true, limit: '25 KB', }, { name: '@sentry/browser - with treeshaking flags', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init'), gzip: true, limit: '24.1 KB', @@ -35,28 +35,28 @@ module.exports = [ }, { name: '@sentry/browser (incl. Tracing)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration'), gzip: true, limit: '41.3 KB', }, { name: '@sentry/browser (incl. Tracing, Profiling)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'browserProfilingIntegration'), gzip: true, limit: '48 KB', }, { name: '@sentry/browser (incl. Tracing, Replay)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, limit: '80 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, limit: '75 KB', @@ -79,35 +79,35 @@ module.exports = [ }, { name: '@sentry/browser (incl. Tracing, Replay with Canvas)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'), gzip: true, limit: '85 KB', }, { name: '@sentry/browser (incl. Tracing, Replay, Feedback)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'), gzip: true, limit: '97 KB', }, { name: '@sentry/browser (incl. Feedback)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'feedbackIntegration'), gzip: true, limit: '42 KB', }, { name: '@sentry/browser (incl. sendFeedback)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'sendFeedback'), gzip: true, limit: '30 KB', }, { name: '@sentry/browser (incl. FeedbackAsync)', - path: 'packages/browser/build/npm/esm/index.js', + path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'feedbackAsyncIntegration'), gzip: true, limit: '35 KB', diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f98b440d47d..d5cf4d9b810f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 10.25.0 + +- feat(browser): Include Spotlight in development bundles ([#18078](https://github.com/getsentry/sentry-javascript/pull/18078)) +- feat(cloudflare): Add metrics exports ([#18147](https://github.com/getsentry/sentry-javascript/pull/18147)) +- feat(core): Truncate request string inputs in OpenAI integration ([#18136](https://github.com/getsentry/sentry-javascript/pull/18136)) +- feat(metrics): Add missing metric node exports ([#18149](https://github.com/getsentry/sentry-javascript/pull/18149)) +- feat(node): Add `maxCacheKeyLength` to Redis integration (remove truncation) ([#18045](https://github.com/getsentry/sentry-javascript/pull/18045)) +- feat(vercel-edge): Add metrics export ([#18148](https://github.com/getsentry/sentry-javascript/pull/18148)) +- fix(core): Only consider exception mechanism when updating session status from event with exceptions ([#18137](https://github.com/getsentry/sentry-javascript/pull/18137)) +- ref(browser): Remove truncation when not needed ([#18051](https://github.com/getsentry/sentry-javascript/pull/18051)) + +
+ Internal Changes + +- chore(build): Fix incorrect versions after merge ([#18154](https://github.com/getsentry/sentry-javascript/pull/18154)) +
+ ## 10.24.0 ### Important Changes diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 0a90b5e2be23..6e3ef99aa7ea 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -46,8 +46,8 @@ const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record = { const BUNDLE_PATHS: Record> = { browser: { - cjs: 'build/npm/cjs/index.js', - esm: 'build/npm/esm/index.js', + cjs: 'build/npm/cjs/prod/index.js', + esm: 'build/npm/esm/prod/index.js', bundle: 'build/bundles/bundle.js', bundle_min: 'build/bundles/bundle.min.js', bundle_replay: 'build/bundles/bundle.replay.js', @@ -67,8 +67,8 @@ const BUNDLE_PATHS: Record> = { loader_tracing_replay: 'build/bundles/bundle.tracing.replay.debug.min.js', }, integrations: { - cjs: 'build/npm/cjs/index.js', - esm: 'build/npm/esm/index.js', + cjs: 'build/npm/cjs/prod/index.js', + esm: 'build/npm/esm/prod/index.js', bundle: 'build/bundles/[INTEGRATION_NAME].js', bundle_min: 'build/bundles/[INTEGRATION_NAME].min.js', }, @@ -77,8 +77,8 @@ const BUNDLE_PATHS: Record> = { bundle_min: 'build/bundles/[INTEGRATION_NAME].min.js', }, wasm: { - cjs: 'build/npm/cjs/index.js', - esm: 'build/npm/esm/index.js', + cjs: 'build/npm/cjs/prod/index.js', + esm: 'build/npm/esm/prod/index.js', bundle: 'build/bundles/wasm.js', bundle_min: 'build/bundles/wasm.min.js', }, diff --git a/dev-packages/bundler-tests/.eslintrc.js b/dev-packages/bundler-tests/.eslintrc.js new file mode 100644 index 000000000000..a65cf78e0b57 --- /dev/null +++ b/dev-packages/bundler-tests/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: ['../../.eslintrc.js'], + parserOptions: { + sourceType: 'module', + }, +}; diff --git a/dev-packages/bundler-tests/fixtures/basic/index.html b/dev-packages/bundler-tests/fixtures/basic/index.html new file mode 100644 index 000000000000..40e5dbc0642e --- /dev/null +++ b/dev-packages/bundler-tests/fixtures/basic/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/dev-packages/bundler-tests/fixtures/basic/index.js b/dev-packages/bundler-tests/fixtures/basic/index.js new file mode 100644 index 000000000000..f3d47c97f7a2 --- /dev/null +++ b/dev-packages/bundler-tests/fixtures/basic/index.js @@ -0,0 +1,5 @@ +import { init } from '@sentry/browser'; + +init({ + dsn: 'https://00000000000000000000000000000000@o000000.ingest.sentry.io/0000000', +}); diff --git a/dev-packages/bundler-tests/package.json b/dev-packages/bundler-tests/package.json new file mode 100644 index 000000000000..1e61939cac5f --- /dev/null +++ b/dev-packages/bundler-tests/package.json @@ -0,0 +1,26 @@ +{ + "name": "@sentry-internal/bundler-tests", + "version": "10.24.0", + "description": "Bundler tests for Sentry Browser SDK", + "repository": "git://github.com/getsentry/sentry-javascript.git", + "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bundler-tests", + "author": "Sentry", + "license": "MIT", + "private": true, + "main": "./index.mjs", + "scripts": { + "test": "vitest run" + }, + "dependencies": { + "@sentry/browser": "10.24.0", + "webpack": "^5.0.0", + "rollup": "^4.0.0", + "vite": "^5.0.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "vitest": "^3.2.4" + }, + "volta": { + "extends": "../../package.json" + }, + "type": "module" +} diff --git a/dev-packages/bundler-tests/tests/bundling.test.ts b/dev-packages/bundler-tests/tests/bundling.test.ts new file mode 100644 index 000000000000..2cc8113ca83b --- /dev/null +++ b/dev-packages/bundler-tests/tests/bundling.test.ts @@ -0,0 +1,144 @@ +import { describe, expect, beforeAll, test } from 'vitest'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +import webpack from 'webpack'; +import { rollup } from 'rollup'; +import { build as viteBuild } from 'vite'; +import nodeResolve from '@rollup/plugin-node-resolve'; + +// Helper functions +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +function distDir(name: string): string { + const dir = path.join(__dirname, '..', 'dist', name); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + return dir; +} + +function rimraf(dir: string): void { + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); + } +} + +function readAllJs(outDir: string): string { + let contents = ''; + const stack = [outDir]; + while (stack.length) { + const current = stack.pop()!; + for (const entry of fs.readdirSync(current)) { + const full = path.join(current, entry); + const stat = fs.statSync(full); + if (stat.isDirectory()) { + stack.push(full); + } else if (entry.endsWith('.js') || entry.endsWith('.mjs')) { + contents += fs.readFileSync(full, 'utf8'); + } + } + } + return contents; +} + +function fixtureEntry(name: string): string { + return path.resolve(__dirname, '..', 'fixtures', name, 'index.js'); +} + +function rootDir(): string { + return path.join(__dirname, '../../..'); +} + +const SPOTLIGHT_URL = 'localhost:8969'; + +type BundleMode = 'development' | 'production'; + +function bundleWithWebpack(mode: BundleMode): Promise { + return new Promise((resolve, reject) => { + const outDir = distDir(`webpack-${mode}`); + rimraf(outDir); + const compiler = webpack({ + mode, + entry: fixtureEntry('basic'), + output: { path: outDir, filename: 'bundle.js' }, + }); + compiler?.run((err: Error | null | undefined, stats: webpack.Stats | undefined) => { + try { + if (err) throw err; + if (stats?.hasErrors()) { + throw new Error(stats.toString('errors-only')); + } + resolve(readAllJs(outDir)); + } catch (e) { + reject(e); + } finally { + compiler.close(() => {}); + } + }); + }); +} + +async function bundleWithRollup(mode: BundleMode): Promise { + const outDir = distDir(`rollup-${mode}`); + rimraf(outDir); + + const bundle = await rollup({ + input: fixtureEntry('basic'), + plugins: [ + nodeResolve({ + // There should really be a default where these get specified automatically + exportConditions: [mode === 'production' ? 'production' : 'development'], + }), + ], + }); + await bundle.write({ dir: outDir, format: 'esm' }); + await bundle.close(); + return readAllJs(outDir); +} + +async function bundleWithVite(mode: BundleMode): Promise { + const outDir = distDir(`vite-${mode}`); + rimraf(outDir); + + // In Vitest, NODE_ENV is always 'test', so we need to override it here + const prev = process.env.NODE_ENV; + process.env.NODE_ENV = mode; + + await viteBuild({ + mode, + root: path.dirname(fixtureEntry('basic')), + build: { outDir, minify: mode === 'production' }, + }); + + process.env.NODE_ENV = prev; + + return readAllJs(outDir); +} + +describe('spotlight', () => { + beforeAll(() => { + const distRoot = path.join(rootDir(), 'dist'); + rimraf(distRoot); + }); + + const cases: [string, (mode: BundleMode) => Promise][] = [ + ['webpack', bundleWithWebpack], + ['rollup', bundleWithRollup], + ['vite', bundleWithVite], + ]; + + for (const [name, bundler] of cases) { + test(`${name} development bundle contains spotlight`, async () => { + const code = await bundler('development'); + expect(code).toContain(SPOTLIGHT_URL); + }); + + test(`${name} production bundle does not contain spotlight`, async () => { + const code = await bundler('production'); + expect(code).not.toContain(SPOTLIGHT_URL); + }); + } +}); diff --git a/dev-packages/bundler-tests/vitest.config.mjs b/dev-packages/bundler-tests/vitest.config.mjs new file mode 100644 index 000000000000..8baf9e1fbf68 --- /dev/null +++ b/dev-packages/bundler-tests/vitest.config.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['tests/**/*.test.*s'], + timeout: 10000, + hookTimeout: 10000, + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts b/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts index ee4b7ac35421..17c6f714c499 100644 --- a/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts +++ b/dev-packages/e2e-tests/test-applications/node-exports-test-app/scripts/consistentExports.ts @@ -41,8 +41,6 @@ const DEPENDENTS: Dependent[] = [ ignoreExports: [ // Not needed for Astro 'setupFastifyErrorHandler', - // Todo(metrics): Add metrics exports for beta - 'metrics', ], }, { @@ -56,8 +54,6 @@ const DEPENDENTS: Dependent[] = [ 'childProcessIntegration', 'systemErrorIntegration', 'pinoIntegration', - // Todo(metrics): Add metrics exports for beta - 'metrics', ], }, { @@ -79,8 +75,6 @@ const DEPENDENTS: Dependent[] = [ ignoreExports: [ // Not needed for Serverless 'setupFastifyErrorHandler', - // Todo(metrics): Add metrics exports for beta - 'metrics', ], }, { @@ -90,8 +84,6 @@ const DEPENDENTS: Dependent[] = [ ignoreExports: [ // Not needed for Serverless 'setupFastifyErrorHandler', - // Todo(metrics): Add metrics exports for beta - 'metrics', ], }, { diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-completions.mjs similarity index 97% rename from dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation.mjs rename to dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-completions.mjs index 5623d3763657..96684ed9ec4f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-completions.mjs @@ -12,7 +12,7 @@ class MockOpenAI { await new Promise(resolve => setTimeout(resolve, 10)); return { - id: 'chatcmpl-truncation-test', + id: 'chatcmpl-completions-truncation-test', object: 'chat.completion', created: 1677652288, model: params.model, diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-responses.mjs b/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-responses.mjs new file mode 100644 index 000000000000..aebd3341eb33 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/openai/scenario-message-truncation-responses.mjs @@ -0,0 +1,93 @@ +import { instrumentOpenAiClient } from '@sentry/core'; +import * as Sentry from '@sentry/node'; + +class MockOpenAI { + constructor(config) { + this.apiKey = config.apiKey; + + this.responses = { + create: async params => { + // Simulate processing time + await new Promise(resolve => setTimeout(resolve, 10)); + + return { + id: 'chatcmpl-responses-truncation-test', + object: 'response', + created_at: 1677652288, + status: 'completed', + error: null, + incomplete_details: null, + instructions: null, + max_output_tokens: null, + model: params.model, + output: [ + { + type: 'message', + id: 'message-123', + status: 'completed', + role: 'assistant', + content: [ + { + type: 'output_text', + text: 'Response to truncated messages', + annotations: [], + }, + ], + }, + ], + parallel_tool_calls: true, + previous_response_id: null, + reasoning: { + effort: null, + summary: null, + }, + store: true, + temperature: params.temperature, + text: { + format: { + type: 'text', + }, + }, + tool_choice: 'auto', + tools: [], + top_p: 1.0, + truncation: 'disabled', + usage: { + input_tokens: 10, + input_tokens_details: { + cached_tokens: 0, + }, + output_tokens: 15, + output_tokens_details: { + reasoning_tokens: 0, + }, + total_tokens: 25, + }, + user: null, + metadata: {}, + }; + }, + }; + } +} + +async function run() { + await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { + const mockClient = new MockOpenAI({ + apiKey: 'mock-api-key', + }); + + const client = instrumentOpenAiClient(mockClient); + + // Create 1 large message that gets truncated to fit within the 20KB limit + const largeContent = 'A'.repeat(25000) + 'B'.repeat(25000); // ~50KB gets truncated to include only As + + await client.responses.create({ + model: 'gpt-3.5-turbo', + input: largeContent, + temperature: 0.7, + }); + }); +} + +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/openai/test.ts b/dev-packages/node-integration-tests/suites/tracing/openai/test.ts index 218e3c7ee61f..5cbb27df73bf 100644 --- a/dev-packages/node-integration-tests/suites/tracing/openai/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/openai/test.ts @@ -400,7 +400,7 @@ describe('OpenAI integration', () => { createEsmAndCjsTests( __dirname, - 'scenario-message-truncation.mjs', + 'scenario-message-truncation-completions.mjs', 'instrument-with-pii.mjs', (createRunner, test) => { test('truncates messages when they exceed byte limit - keeps only last message and crops it', async () => { @@ -433,4 +433,40 @@ describe('OpenAI integration', () => { }); }, ); + + createEsmAndCjsTests( + __dirname, + 'scenario-message-truncation-responses.mjs', + 'instrument-with-pii.mjs', + (createRunner, test) => { + test('truncates string inputs when they exceed byte limit', async () => { + await createRunner() + .ignore('event') + .expect({ + transaction: { + transaction: 'main', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + 'gen_ai.operation.name': 'responses', + 'sentry.op': 'gen_ai.responses', + 'sentry.origin': 'auto.ai.openai', + 'gen_ai.system': 'openai', + 'gen_ai.request.model': 'gpt-3.5-turbo', + // Messages should be present and should include truncated string input (contains only As) + 'gen_ai.request.messages': expect.stringMatching(/^A+$/), + }), + description: 'responses gpt-3.5-turbo', + op: 'gen_ai.responses', + origin: 'auto.ai.openai', + status: 'ok', + }), + ]), + }, + }) + .start() + .completed(); + }); + }, + ); }); diff --git a/dev-packages/rollup-utils/bundleHelpers.mjs b/dev-packages/rollup-utils/bundleHelpers.mjs index b353eebaa214..8dd2ebd21999 100644 --- a/dev-packages/rollup-utils/bundleHelpers.mjs +++ b/dev-packages/rollup-utils/bundleHelpers.mjs @@ -11,7 +11,6 @@ import { makeCleanupPlugin, makeCommonJSPlugin, makeIsDebugBuildPlugin, - makeJsonPlugin, makeLicensePlugin, makeNodeResolvePlugin, makeRrwebBuildPlugin, @@ -20,6 +19,7 @@ import { makeTerserPlugin, } from './plugins/index.mjs'; import { mergePlugins } from './utils.mjs'; +import { makeProductionReplacePlugin } from './plugins/npmPlugins.mjs'; const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js']; @@ -35,14 +35,13 @@ export function makeBaseBundleConfig(options) { excludeIframe: false, excludeShadowDom: false, }); + const productionReplacePlugin = makeProductionReplacePlugin(); // The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it // will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included // at all, and without `transformMixedEsModules`, they're only included if they're imported, not if they're required.) const commonJSPlugin = makeCommonJSPlugin({ transformMixedEsModules: true }); - const jsonPlugin = makeJsonPlugin(); - // used by `@sentry/browser` const standAloneBundleConfig = { output: { @@ -119,7 +118,7 @@ export function makeBaseBundleConfig(options) { strict: false, esModule: false, }, - plugins: [sucrasePlugin, nodeResolvePlugin, cleanupPlugin], + plugins: [productionReplacePlugin, sucrasePlugin, nodeResolvePlugin, cleanupPlugin], treeshake: 'smallest', }; diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index cff113d622d6..d5f7428b992d 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -16,6 +16,7 @@ import { makeCleanupPlugin, makeDebugBuildStatementReplacePlugin, makeNodeResolvePlugin, + makeProductionReplacePlugin, makeRrwebBuildPlugin, makeSucrasePlugin, } from './plugins/index.mjs'; @@ -114,22 +115,47 @@ export function makeBaseNPMConfig(options = {}) { } export function makeNPMConfigVariants(baseConfig, options = {}) { - const { emitEsm = true, emitCjs = true } = options; + const { emitEsm = true, emitCjs = true, splitDevProd = false } = options; const variantSpecificConfigs = []; if (emitCjs) { - variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } }); + if (splitDevProd) { + variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs/dev') } }); + variantSpecificConfigs.push({ + output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs/prod') }, + plugins: [makeProductionReplacePlugin()], + }); + } else { + variantSpecificConfigs.push({ output: { format: 'cjs', dir: path.join(baseConfig.output.dir, 'cjs') } }); + } } if (emitEsm) { - variantSpecificConfigs.push({ - output: { - format: 'esm', - dir: path.join(baseConfig.output.dir, 'esm'), - plugins: [makePackageNodeEsm()], - }, - }); + if (splitDevProd) { + variantSpecificConfigs.push({ + output: { + format: 'esm', + dir: path.join(baseConfig.output.dir, 'esm/dev'), + plugins: [makePackageNodeEsm()], + }, + }); + variantSpecificConfigs.push({ + output: { + format: 'esm', + dir: path.join(baseConfig.output.dir, 'esm/prod'), + plugins: [makeProductionReplacePlugin(), makePackageNodeEsm()], + }, + }); + } else { + variantSpecificConfigs.push({ + output: { + format: 'esm', + dir: path.join(baseConfig.output.dir, 'esm'), + plugins: [makePackageNodeEsm()], + }, + }); + } } return variantSpecificConfigs.map(variant => deepMerge(baseConfig, variant)); diff --git a/dev-packages/rollup-utils/plugins/npmPlugins.mjs b/dev-packages/rollup-utils/plugins/npmPlugins.mjs index 8c7e0ff10a80..7f08873f1c80 100644 --- a/dev-packages/rollup-utils/plugins/npmPlugins.mjs +++ b/dev-packages/rollup-utils/plugins/npmPlugins.mjs @@ -125,6 +125,24 @@ export function makeDebugBuildStatementReplacePlugin() { }); } +export function makeProductionReplacePlugin() { + const pattern = /\/\* rollup-include-development-only \*\/[\s\S]*?\/\* rollup-include-development-only-end \*\/\s*/g; + + function stripDevBlocks(code) { + if (!code) return null; + if (!code.includes('rollup-include-development-only')) return null; + const replaced = code.replace(pattern, ''); + return { code: replaced, map: null }; + } + + return { + name: 'remove-dev-mode-blocks', + renderChunk(code) { + return stripDevBlocks(code); + }, + }; +} + /** * Creates a plugin to replace build flags of rrweb with either a constant (if passed true/false) or with a safe statement that: * a) evaluates to `true` diff --git a/dev-packages/rollup-utils/utils.mjs b/dev-packages/rollup-utils/utils.mjs index 94b8c4483c23..b687ff9993c4 100644 --- a/dev-packages/rollup-utils/utils.mjs +++ b/dev-packages/rollup-utils/utils.mjs @@ -21,7 +21,16 @@ export function mergePlugins(pluginsA, pluginsB) { // here. // Additionally, the excludeReplay plugin must run before TS/Sucrase so that we can eliminate the replay code // before anything is type-checked (TS-only) and transpiled. - const order = ['excludeReplay', 'typescript', 'sucrase', '...', 'terser', 'license', 'output-base64-worker-script']; + const order = [ + 'remove-dev-mode-blocks', + 'excludeReplay', + 'typescript', + 'sucrase', + '...', + 'terser', + 'license', + 'output-base64-worker-script', + ]; const sortKeyA = order.includes(a.name) ? a.name : '...'; const sortKeyB = order.includes(b.name) ? b.name : '...'; diff --git a/package.json b/package.json index 1fd6eb062564..298e17031240 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "dev-packages/clear-cache-gh-action", "dev-packages/external-contributor-gh-action", "dev-packages/rollup-utils", - "dev-packages/node-overhead-gh-action" + "dev-packages/node-overhead-gh-action", + "dev-packages/bundler-tests" ], "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 69ca79e04a17..1774f597af43 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -162,6 +162,7 @@ export { statsigIntegration, unleashIntegration, growthbookIntegration, + metrics, } from '@sentry/node'; export { init } from './server/sdk'; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index da0393d9b0e9..586babab40ee 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -148,6 +148,7 @@ export { statsigIntegration, unleashIntegration, growthbookIntegration, + metrics, } from '@sentry/node'; export { diff --git a/packages/browser-utils/src/metrics/lcp.ts b/packages/browser-utils/src/metrics/lcp.ts index 5f84f1782041..a6410ac08580 100644 --- a/packages/browser-utils/src/metrics/lcp.ts +++ b/packages/browser-utils/src/metrics/lcp.ts @@ -77,8 +77,7 @@ export function _sendStandaloneLcpSpan( entry.element && (attributes['lcp.element'] = htmlTreeAsString(entry.element)); entry.id && (attributes['lcp.id'] = entry.id); - // Trim URL to the first 200 characters. - entry.url && (attributes['lcp.url'] = entry.url.trim().slice(0, 200)); + entry.url && (attributes['lcp.url'] = entry.url); // loadTime is the time of LCP that's related to receiving the LCP element response.. entry.loadTime != null && (attributes['lcp.loadTime'] = entry.loadTime); diff --git a/packages/browser/package.json b/packages/browser/package.json index 5c07b30eaab8..9ac303dbbd95 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -12,19 +12,24 @@ "files": [ "/build/npm" ], - "main": "build/npm/cjs/index.js", - "module": "build/npm/esm/index.js", + "main": "build/npm/cjs/prod/index.js", + "module": "build/npm/esm/prod/index.js", "types": "build/npm/types/index.d.ts", "exports": { "./package.json": "./package.json", ".": { - "import": { - "types": "./build/npm/types/index.d.ts", - "default": "./build/npm/esm/index.js" + "types": "./build/npm/types/index.d.ts", + "development": { + "import": "./build/npm/esm/dev/index.js", + "require": "./build/npm/cjs/dev/index.js" }, - "require": { - "types": "./build/npm/types/index.d.ts", - "default": "./build/npm/cjs/index.js" + "production": { + "import": "./build/npm/esm/prod/index.js", + "require": "./build/npm/cjs/prod/index.js" + }, + "default": { + "import": "./build/npm/esm/prod/index.js", + "require": "./build/npm/cjs/prod/index.js" } } }, @@ -67,7 +72,7 @@ "clean": "rimraf build coverage .rpt2_cache sentry-browser-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "lint:es-compatibility": "es-check es2020 ./build/{bundles,npm/cjs}/*.js && es-check es2020 ./build/npm/esm/*.js --module", + "lint:es-compatibility": "es-check es2020 ./build/{bundles,npm/cjs/prod}/*.js && es-check es2020 ./build/npm/esm/prod/*.js --module", "size:check": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES2017: \",$1,\"kB\";}'", "test": "vitest run", "test:watch": "vitest --watch", diff --git a/packages/browser/rollup.npm.config.mjs b/packages/browser/rollup.npm.config.mjs index 00251eea81fd..40e3cbff5906 100644 --- a/packages/browser/rollup.npm.config.mjs +++ b/packages/browser/rollup.npm.config.mjs @@ -16,4 +16,5 @@ export default makeNPMConfigVariants( }, }, }), + { splitDevProd: true }, ); diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index dddaa440b198..ea55174f340c 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -63,6 +63,18 @@ type BrowserSpecificOptions = BrowserClientReplayOptions & * @default false */ propagateTraceparent?: boolean; + + /** + * If you use Spotlight by Sentry during development, use + * this option to forward captured Sentry events to Spotlight. + * + * Either set it to true, or provide a specific Spotlight Sidecar URL. + * + * More details: https://spotlightjs.com/ + * + * IMPORTANT: Only set this option to `true` while developing, not in production! + */ + spotlight?: boolean | string; }; /** * Configuration options for the Sentry Browser SDK. diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index aa7b2fa9e412..6bada802b98e 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -217,5 +217,5 @@ function getFilenameFromUrl(url: string | undefined): string | undefined { return ``; } - return url.slice(0, 1024); + return url; // it's fine to not truncate it as it's not put in a regex (https://codeql.github.com/codeql-query-help/javascript/js-polynomial-redos) } diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index 7b5d67c636ae..800c1b701352 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -15,6 +15,7 @@ import { browserSessionIntegration } from './integrations/browsersession'; import { globalHandlersIntegration } from './integrations/globalhandlers'; import { httpContextIntegration } from './integrations/httpcontext'; import { linkedErrorsIntegration } from './integrations/linkederrors'; +import { spotlightBrowserIntegration } from './integrations/spotlight'; import { defaultStackParser } from './stack-parsers'; import { makeFetchTransport } from './transports/fetch'; import { checkAndWarnIfIsEmbeddedBrowserExtension } from './utils/detectBrowserExtension'; @@ -90,14 +91,26 @@ export function init(options: BrowserOptions = {}): Client | undefined { const shouldDisableBecauseIsBrowserExtenstion = !options.skipBrowserExtensionCheck && checkAndWarnIfIsEmbeddedBrowserExtension(); + let defaultIntegrations = + options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations; + + /* rollup-include-development-only */ + if (options.spotlight) { + if (!defaultIntegrations) { + defaultIntegrations = []; + } + const args = typeof options.spotlight === 'string' ? { sidecarUrl: options.spotlight } : undefined; + defaultIntegrations.push(spotlightBrowserIntegration(args)); + } + /* rollup-include-development-only-end */ + const clientOptions: BrowserClientOptions = { ...options, enabled: shouldDisableBecauseIsBrowserExtenstion ? false : options.enabled, stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser), integrations: getIntegrationsToSetup({ integrations: options.integrations, - defaultIntegrations: - options.defaultIntegrations == null ? getDefaultIntegrations(options) : options.defaultIntegrations, + defaultIntegrations, }), transport: options.transport || makeFetchTransport, }; diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index 33af15790191..813e087dc2d2 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -16,6 +16,7 @@ export type { Thread, User, FeatureFlagsIntegration, + Metric, } from '@sentry/core'; export { @@ -164,6 +165,7 @@ export { OpenFeatureIntegrationHook, statsigIntegration, unleashIntegration, + metrics, } from '@sentry/node'; export { diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index a6aa7ffc8d9a..36de54816030 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -16,6 +16,7 @@ export type { Stacktrace, Thread, User, + Metric, } from '@sentry/core'; export type { CloudflareOptions } from './client'; @@ -100,6 +101,7 @@ export { featureFlagsIntegration, growthbookIntegration, logger, + metrics, } from '@sentry/core'; export { withSentry } from './handler'; diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index c3ff126732f8..53e0328965a4 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1037,16 +1037,18 @@ export abstract class Client { /** Updates existing session based on the provided event */ protected _updateSessionFromEvent(session: Session, event: Event): void { + // initially, set `crashed` based on the event level and update from exceptions if there are any later on let crashed = event.level === 'fatal'; let errored = false; const exceptions = event.exception?.values; if (exceptions) { errored = true; + // reset crashed to false if there are exceptions, to ensure `mechanism.handled` is respected. + crashed = false; for (const ex of exceptions) { - const mechanism = ex.mechanism; - if (mechanism?.handled === false) { + if (ex.mechanism?.handled === false) { crashed = true; break; } diff --git a/packages/core/src/utils/ai/messageTruncation.ts b/packages/core/src/utils/ai/messageTruncation.ts index 64d186f927b8..945761f6220c 100644 --- a/packages/core/src/utils/ai/messageTruncation.ts +++ b/packages/core/src/utils/ai/messageTruncation.ts @@ -294,3 +294,13 @@ export function truncateMessagesByBytes(messages: unknown[], maxBytes: number): export function truncateGenAiMessages(messages: unknown[]): unknown[] { return truncateMessagesByBytes(messages, DEFAULT_GEN_AI_MESSAGES_BYTE_LIMIT); } + +/** + * Truncate GenAI string input using the default byte limit. + * + * @param input - The string to truncate + * @returns Truncated string + */ +export function truncateGenAiStringInput(input: string): string { + return truncateTextByBytes(input, DEFAULT_GEN_AI_MESSAGES_BYTE_LIMIT); +} diff --git a/packages/core/src/utils/ai/utils.ts b/packages/core/src/utils/ai/utils.ts index 00e147a16e5f..4a7a14eea554 100644 --- a/packages/core/src/utils/ai/utils.ts +++ b/packages/core/src/utils/ai/utils.ts @@ -7,7 +7,7 @@ import { GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, } from './gen-ai-attributes'; -import { truncateGenAiMessages } from './messageTruncation'; +import { truncateGenAiMessages, truncateGenAiStringInput } from './messageTruncation'; /** * Maps AI method paths to Sentry operation name */ @@ -95,7 +95,7 @@ export function setTokenUsageAttributes( export function getTruncatedJsonString(value: T | T[]): string { if (typeof value === 'string') { // Some values are already JSON strings, so we don't need to duplicate the JSON parsing - return value; + return truncateGenAiStringInput(value); } if (Array.isArray(value)) { // truncateGenAiMessages returns an array of strings, so we need to stringify it diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index db25793ccf7b..acb4197cf4cf 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1,4 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import type { SeverityLevel } from '../../src'; import { addBreadcrumb, dsnToString, @@ -2308,6 +2309,108 @@ describe('Client', () => { }); }); + describe('_updateSessionFromEvent()', () => { + describe('event has no exceptions', () => { + it('sets status to crashed if level is fatal', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level: 'fatal' }); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + duration: expect.any(Number), + errors: 1, + init: false, + sid: expect.any(String), + started: expect.any(Number), + status: 'crashed', + timestamp: expect.any(Number), + }); + }); + + it.each(['error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status to ok if level is %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureEvent({ message: 'test', level }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + }); + + describe('event has exceptions', () => { + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status ok for handled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('ok'); + }, + ); + + it.each(['fatal', 'error', 'warning', 'log', 'info', 'debug'] as const)( + 'sets status crashed for unhandled exceptions and ignores event level %s', + (level: SeverityLevel) => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + client.captureException(new Error('test'), { captureContext: { level }, mechanism: { handled: false } }); + + const updatedSession = client.session; + + expect(updatedSession?.status).toEqual('crashed'); + }, + ); + + it('sets status crashed if at least one exception is unhandled', () => { + const client = new TestClient(getDefaultTestClientOptions()); + const session = makeSession(); + getCurrentScope().setSession(session); + + const event: Event = { + exception: { + values: [ + { + mechanism: { type: 'generic', handled: true }, + }, + { + mechanism: { type: 'generic', handled: false }, + }, + { + mechanism: { type: 'generic', handled: true }, + }, + ], + }, + }; + + client.captureEvent(event); + + const updatedSession = client.session; + + expect(updatedSession).toMatchObject({ + status: 'crashed', + errors: 1, // an event with multiple exceptions still counts as one error in the session + }); + }); + }); + }); + describe('recordDroppedEvent()/_clearOutcomes()', () => { test('records and returns outcomes', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 02e55c45a7ba..3aa7da1cbab9 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -148,6 +148,7 @@ export { OpenFeatureIntegrationHook, statsigIntegration, unleashIntegration, + metrics, } from '@sentry/node'; export { diff --git a/packages/node/src/integrations/tracing/redis.ts b/packages/node/src/integrations/tracing/redis.ts index 8376c99c1998..f8be12352ae0 100644 --- a/packages/node/src/integrations/tracing/redis.ts +++ b/packages/node/src/integrations/tracing/redis.ts @@ -24,14 +24,34 @@ import { } from '../../utils/redisCache'; interface RedisOptions { + /** + * Define cache prefixes for cache keys that should be captured as a cache span. + * + * Setting this to, for example, `['user:']` will capture cache keys that start with `user:`. + */ cachePrefixes?: string[]; + /** + * Maximum length of the cache key added to the span description. If the key exceeds this length, it will be truncated. + * + * Passing `0` will use the full cache key without truncation. + * + * By default, the full cache key is used. + */ + maxCacheKeyLength?: number; } const INTEGRATION_NAME = 'Redis'; -let _redisOptions: RedisOptions = {}; +/* Only exported for testing purposes */ +export let _redisOptions: RedisOptions = {}; -const cacheResponseHook: RedisResponseCustomAttributeFunction = (span: Span, redisCommand, cmdArgs, response) => { +/* Only exported for testing purposes */ +export const cacheResponseHook: RedisResponseCustomAttributeFunction = ( + span: Span, + redisCommand, + cmdArgs, + response, +) => { span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.redis'); const safeKey = getCacheKeySafely(redisCommand, cmdArgs); @@ -70,9 +90,12 @@ const cacheResponseHook: RedisResponseCustomAttributeFunction = (span: Span, red [SEMANTIC_ATTRIBUTE_CACHE_KEY]: safeKey, }); + // todo: change to string[] once EAP supports it const spanDescription = safeKey.join(', '); - span.updateName(truncate(spanDescription, 1024)); + span.updateName( + _redisOptions.maxCacheKeyLength ? truncate(spanDescription, _redisOptions.maxCacheKeyLength) : spanDescription, + ); }; const instrumentIORedis = generateInstrumentOnce(`${INTEGRATION_NAME}.IORedis`, () => { diff --git a/packages/node/test/integrations/tracing/redis.test.ts b/packages/node/test/integrations/tracing/redis.test.ts index 38a5b80eb759..ae5b879c0b8e 100644 --- a/packages/node/test/integrations/tracing/redis.test.ts +++ b/packages/node/test/integrations/tracing/redis.test.ts @@ -1,4 +1,5 @@ -import { describe, expect, it } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { _redisOptions, cacheResponseHook } from '../../../src/integrations/tracing/redis'; import { calculateCacheItemSize, GET_COMMANDS, @@ -8,6 +9,82 @@ import { } from '../../../src/utils/redisCache'; describe('Redis', () => { + describe('cacheResponseHook', () => { + let mockSpan: any; + let originalRedisOptions: any; + + beforeEach(() => { + mockSpan = { + setAttribute: vi.fn(), + setAttributes: vi.fn(), + updateName: vi.fn(), + spanContext: () => ({ spanId: 'test-span-id', traceId: 'test-trace-id' }), + }; + + originalRedisOptions = { ..._redisOptions }; + }); + + afterEach(() => { + vi.restoreAllMocks(); + // Reset redis options by clearing all properties first, then restoring original ones + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + Object.keys(_redisOptions).forEach(key => delete (_redisOptions as any)[key]); + Object.assign(_redisOptions, originalRedisOptions); + }); + + describe('early returns', () => { + it.each([ + { desc: 'no args', cmd: 'get', args: [], response: 'test' }, + { desc: 'unsupported command', cmd: 'exists', args: ['key'], response: 'test' }, + { desc: 'no cache prefixes', cmd: 'get', args: ['key'], response: 'test', options: {} }, + { desc: 'non-matching prefix', cmd: 'get', args: ['key'], response: 'test', options: { cachePrefixes: ['c'] } }, + ])('should always set sentry.origin but return early when $desc', ({ cmd, args, response, options = {} }) => { + Object.assign(_redisOptions, options); + + cacheResponseHook(mockSpan, cmd, args, response); + + expect(mockSpan.setAttribute).toHaveBeenCalledWith('sentry.origin', 'auto.db.otel.redis'); + expect(mockSpan.setAttributes).not.toHaveBeenCalled(); + expect(mockSpan.updateName).not.toHaveBeenCalled(); + }); + }); + + describe('span name truncation', () => { + beforeEach(() => { + Object.assign(_redisOptions, { cachePrefixes: ['cache:'] }); + }); + + it('should not truncate span name when maxCacheKeyLength is not set', () => { + cacheResponseHook( + mockSpan, + 'mget', + ['cache:very-long-key-name', 'cache:very-long-key-name-2', 'cache:very-long-key-name-3'], + 'value', + ); + + expect(mockSpan.updateName).toHaveBeenCalledWith( + 'cache:very-long-key-name, cache:very-long-key-name-2, cache:very-long-key-name-3', + ); + }); + + it('should truncate span name when maxCacheKeyLength is set', () => { + Object.assign(_redisOptions, { maxCacheKeyLength: 10 }); + + cacheResponseHook(mockSpan, 'get', ['cache:very-long-key-name'], 'value'); + + expect(mockSpan.updateName).toHaveBeenCalledWith('cache:very...'); + }); + + it('should truncate multiple keys joined with commas', () => { + Object.assign(_redisOptions, { maxCacheKeyLength: 20 }); + + cacheResponseHook(mockSpan, 'mget', ['cache:key1', 'cache:key2', 'cache:key3'], ['val1', 'val2', 'val3']); + + expect(mockSpan.updateName).toHaveBeenCalledWith('cache:key1, cache:ke...'); + }); + }); + }); + describe('getCacheKeySafely (single arg)', () => { it('should return an empty string if there are no command arguments', () => { const result = getCacheKeySafely('get', []); @@ -26,7 +103,7 @@ describe('Redis', () => { expect(result).toStrictEqual(['key1']); }); - it('should return only the key for multiple arguments', () => { + it('should return only the first key for commands that only accept a singe key (get)', () => { const cmdArgs = ['key1', 'the-value']; const result = getCacheKeySafely('get', cmdArgs); expect(result).toStrictEqual(['key1']); diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 0613d6397f96..88177f11354a 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -128,6 +128,7 @@ export { createConsolaReporter, createSentryWinstonTransport, vercelAIIntegration, + metrics, } from '@sentry/node'; // We can still leave this for the carrier init and type exports diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 7a73234f535e..4f5017bb8f6c 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -16,6 +16,7 @@ export type { Stacktrace, Thread, User, + Metric, } from '@sentry/core'; export type { VercelEdgeOptions } from './types'; @@ -98,6 +99,7 @@ export { createLangChainCallbackHandler, featureFlagsIntegration, logger, + metrics, } from '@sentry/core'; export { VercelEdgeClient } from './client'; diff --git a/scripts/ci-unit-tests.ts b/scripts/ci-unit-tests.ts index 2802bde62fa6..2b6468c3e4ce 100644 --- a/scripts/ci-unit-tests.ts +++ b/scripts/ci-unit-tests.ts @@ -23,6 +23,7 @@ const BROWSER_TEST_PACKAGES = [ '@sentry-internal/replay-canvas', '@sentry-internal/replay-worker', '@sentry-internal/feedback', + '@sentry-internal/bundler-tests', '@sentry/wasm', ]; diff --git a/yarn.lock b/yarn.lock index a49764ebb218..99e434f5efff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6824,105 +6824,115 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz#f768e3b2b0e6b55c595d7a053652c06413713983" - integrity sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w== - -"@rollup/rollup-android-arm64@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz#40379fd5501cfdfd7d8f86dfa1d3ce8d3a609493" - integrity sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ== - -"@rollup/rollup-darwin-arm64@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz#972c227bc89fe8a38a3f0c493e1966900e4e1ff7" - integrity sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg== - -"@rollup/rollup-darwin-x64@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz#96c919dcb87a5aa7dec5f7f77d90de881e578fdd" - integrity sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw== - -"@rollup/rollup-freebsd-arm64@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz#d199d8eaef830179c0c95b7a6e5455e893d1102c" - integrity sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA== - -"@rollup/rollup-freebsd-x64@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz#cab01f9e06ca756c1fabe87d64825ae016af4713" - integrity sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw== - -"@rollup/rollup-linux-arm-gnueabihf@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz#f6f1c42036dba0e58dc2315305429beff0d02c78" - integrity sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ== - -"@rollup/rollup-linux-arm-musleabihf@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz#1157e98e740facf858993fb51431dce3a4a96239" - integrity sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw== - -"@rollup/rollup-linux-arm64-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz#b39db73f8a4c22e7db31a4f3fd45170105f33265" - integrity sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ== - -"@rollup/rollup-linux-arm64-musl@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz#4043398049fe4449c1485312d1ae9ad8af4056dd" - integrity sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g== - -"@rollup/rollup-linux-loongarch64-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz#855a80e7e86490da15a85dcce247dbc25265bc08" - integrity sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew== - -"@rollup/rollup-linux-powerpc64le-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz#8cf843cb7ab1d42e1dda680937cf0a2db6d59047" - integrity sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA== - -"@rollup/rollup-linux-riscv64-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz#287c085472976c8711f16700326f736a527f2f38" - integrity sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw== - -"@rollup/rollup-linux-riscv64-musl@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz#095ad5e53a54ba475979f1b3226b92440c95c892" - integrity sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg== - -"@rollup/rollup-linux-s390x-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz#a3dec8281d8f2aef1703e48ebc65d29fe847933c" - integrity sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw== - -"@rollup/rollup-linux-x64-gnu@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz#4b211e6fd57edd6a134740f4f8e8ea61972ff2c5" - integrity sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw== - -"@rollup/rollup-linux-x64-musl@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz#3ecbf8e21b4157e57bb15dc6837b6db851f9a336" - integrity sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g== - -"@rollup/rollup-win32-arm64-msvc@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz#d4aae38465b2ad200557b53c8c817266a3ddbfd0" - integrity sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg== - -"@rollup/rollup-win32-ia32-msvc@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz#0258e8ca052abd48b23fd6113360fa0cd1ec3e23" - integrity sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A== - -"@rollup/rollup-win32-x64-msvc@4.44.1": - version "4.44.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz#1c982f6a5044ffc2a35cd754a0951bdcb44d5ba0" - integrity sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug== +"@rollup/rollup-android-arm-eabi@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz#0f44a2f8668ed87b040b6fe659358ac9239da4db" + integrity sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ== + +"@rollup/rollup-android-arm64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz#25b9a01deef6518a948431564c987bcb205274f5" + integrity sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA== + +"@rollup/rollup-darwin-arm64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz#8a102869c88f3780c7d5e6776afd3f19084ecd7f" + integrity sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA== + +"@rollup/rollup-darwin-x64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz#8e526417cd6f54daf1d0c04cf361160216581956" + integrity sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA== + +"@rollup/rollup-freebsd-arm64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz#0e7027054493f3409b1f219a3eac5efd128ef899" + integrity sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA== + +"@rollup/rollup-freebsd-x64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz#72b204a920139e9ec3d331bd9cfd9a0c248ccb10" + integrity sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz#ab1b522ebe5b7e06c99504cc38f6cd8b808ba41c" + integrity sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ== + +"@rollup/rollup-linux-arm-musleabihf@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz#f8cc30b638f1ee7e3d18eac24af47ea29d9beb00" + integrity sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ== + +"@rollup/rollup-linux-arm64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz#7af37a9e85f25db59dc8214172907b7e146c12cc" + integrity sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg== + +"@rollup/rollup-linux-arm64-musl@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz#a623eb0d3617c03b7a73716eb85c6e37b776f7e0" + integrity sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q== + +"@rollup/rollup-linux-loong64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz#76ea038b549c5c6c5f0d062942627c4066642ee2" + integrity sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA== + +"@rollup/rollup-linux-ppc64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz#d9a4c3f0a3492bc78f6fdfe8131ac61c7359ccd5" + integrity sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw== + +"@rollup/rollup-linux-riscv64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz#87ab033eebd1a9a1dd7b60509f6333ec1f82d994" + integrity sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw== + +"@rollup/rollup-linux-riscv64-musl@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz#bda3eb67e1c993c1ba12bc9c2f694e7703958d9f" + integrity sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg== + +"@rollup/rollup-linux-s390x-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz#f7bc10fbe096ab44694233dc42a2291ed5453d4b" + integrity sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ== + +"@rollup/rollup-linux-x64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz#a151cb1234cc9b2cf5e8cfc02aa91436b8f9e278" + integrity sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q== + +"@rollup/rollup-linux-x64-musl@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz#7859e196501cc3b3062d45d2776cfb4d2f3a9350" + integrity sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg== + +"@rollup/rollup-openharmony-arm64@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz#85d0df7233734df31e547c1e647d2a5300b3bf30" + integrity sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw== + +"@rollup/rollup-win32-arm64-msvc@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz#e62357d00458db17277b88adbf690bb855cac937" + integrity sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w== + +"@rollup/rollup-win32-ia32-msvc@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz#fc7cd40f44834a703c1f1c3fe8bcc27ce476cd50" + integrity sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg== + +"@rollup/rollup-win32-x64-gnu@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz#1a22acfc93c64a64a48c42672e857ee51774d0d3" + integrity sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ== + +"@rollup/rollup-win32-x64-msvc@4.52.5": + version "4.52.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz#1657f56326bbe0ac80eedc9f9c18fc1ddd24e107" + integrity sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg== "@schematics/angular@14.2.13": version "14.2.13" @@ -27073,33 +27083,35 @@ rollup@^3.27.1, rollup@^3.28.1: optionalDependencies: fsevents "~2.3.2" -rollup@^4.20.0, rollup@^4.34.9, rollup@^4.35.0, rollup@^4.44.0: - version "4.44.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.44.1.tgz#641723932894e7acbe6052aea34b8e72ef8b7c8f" - integrity sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg== +rollup@^4.0.0, rollup@^4.20.0, rollup@^4.34.9, rollup@^4.35.0, rollup@^4.44.0: + version "4.52.5" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.52.5.tgz#96982cdcaedcdd51b12359981f240f94304ec235" + integrity sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.44.1" - "@rollup/rollup-android-arm64" "4.44.1" - "@rollup/rollup-darwin-arm64" "4.44.1" - "@rollup/rollup-darwin-x64" "4.44.1" - "@rollup/rollup-freebsd-arm64" "4.44.1" - "@rollup/rollup-freebsd-x64" "4.44.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.44.1" - "@rollup/rollup-linux-arm-musleabihf" "4.44.1" - "@rollup/rollup-linux-arm64-gnu" "4.44.1" - "@rollup/rollup-linux-arm64-musl" "4.44.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.44.1" - "@rollup/rollup-linux-powerpc64le-gnu" "4.44.1" - "@rollup/rollup-linux-riscv64-gnu" "4.44.1" - "@rollup/rollup-linux-riscv64-musl" "4.44.1" - "@rollup/rollup-linux-s390x-gnu" "4.44.1" - "@rollup/rollup-linux-x64-gnu" "4.44.1" - "@rollup/rollup-linux-x64-musl" "4.44.1" - "@rollup/rollup-win32-arm64-msvc" "4.44.1" - "@rollup/rollup-win32-ia32-msvc" "4.44.1" - "@rollup/rollup-win32-x64-msvc" "4.44.1" + "@rollup/rollup-android-arm-eabi" "4.52.5" + "@rollup/rollup-android-arm64" "4.52.5" + "@rollup/rollup-darwin-arm64" "4.52.5" + "@rollup/rollup-darwin-x64" "4.52.5" + "@rollup/rollup-freebsd-arm64" "4.52.5" + "@rollup/rollup-freebsd-x64" "4.52.5" + "@rollup/rollup-linux-arm-gnueabihf" "4.52.5" + "@rollup/rollup-linux-arm-musleabihf" "4.52.5" + "@rollup/rollup-linux-arm64-gnu" "4.52.5" + "@rollup/rollup-linux-arm64-musl" "4.52.5" + "@rollup/rollup-linux-loong64-gnu" "4.52.5" + "@rollup/rollup-linux-ppc64-gnu" "4.52.5" + "@rollup/rollup-linux-riscv64-gnu" "4.52.5" + "@rollup/rollup-linux-riscv64-musl" "4.52.5" + "@rollup/rollup-linux-s390x-gnu" "4.52.5" + "@rollup/rollup-linux-x64-gnu" "4.52.5" + "@rollup/rollup-linux-x64-musl" "4.52.5" + "@rollup/rollup-openharmony-arm64" "4.52.5" + "@rollup/rollup-win32-arm64-msvc" "4.52.5" + "@rollup/rollup-win32-ia32-msvc" "4.52.5" + "@rollup/rollup-win32-x64-gnu" "4.52.5" + "@rollup/rollup-win32-x64-msvc" "4.52.5" fsevents "~2.3.2" router@^2.2.0: