Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d1646c8
Merge pull request #17896 from getsentry/master
github-actions[bot] Oct 9, 2025
4c85627
fix(nextjs): Fix createRouteManifest with basePath (#17838)
seoyeon9888 Oct 13, 2025
baf4ff8
chore: Add external contributor to CHANGELOG.md (#17915)
HazAT Oct 13, 2025
7fc2858
feat(nextjs): Prepare for next 16 bundler default (#17868)
chargome Oct 13, 2025
3b04938
test(nextjs): Update next 15 tests (#17919)
chargome Oct 14, 2025
fd8bcbd
feat(nextjs): Support native debugIds in turbopack (#17853)
chargome Oct 14, 2025
da08d49
chore(nextjs): Add Next.js 16 peer dependency (#17925)
chargome Oct 14, 2025
6b83234
test(nextjs): Add next@16 e2e test (#17922)
chargome Oct 14, 2025
6229224
Merge pull request #17848 from getsentry/rolaabuhasna/js-945-expose-a…
RulaKhaled Oct 14, 2025
245e91b
feat(flags): Add Growthbook integration (#17440)
madhuchavva Oct 14, 2025
ac57cec
ref(core): Add weight tracking logic to browser logs/metrics (#17901)
AbhiPrasad Oct 14, 2025
ed98c1f
feat(node): Capture `pino` logger name (#17930)
timfish Oct 14, 2025
4e988d5
chore: Add external contributor to CHANGELOG.md (#17928)
HazAT Oct 14, 2025
c68674a
fix(browser): Ignore React 19.2+ component render measure entries (#1…
Lms24 Oct 15, 2025
e4e272b
feat(solid): Add support for TanStack Router Solid (#17735)
thedanchez Oct 15, 2025
fdbce16
chore(ci): Update Next.js canary testing (#17939)
chargome Oct 15, 2025
e2fe686
chore: Bump size limit (#17941)
chargome Oct 15, 2025
05122e0
chore: Add external contributor to CHANGELOG.md (#17940)
HazAT Oct 15, 2025
fc64c47
fix(react): Add `POP` guard for long-running `pageload` spans (#17867)
onurtemizkan Oct 15, 2025
e557d38
fix(tracemetrics): Send boolean for internal replay attribute (#17908)
chargome Oct 15, 2025
baa7a06
meta(changelog): Update changelog for 10.20.0
chargome Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,8 @@ jobs:
build-command: 'test:build-canary'
label: 'create-react-app (canary)'
- test-application: 'nextjs-app-dir'
build-command: 'test:build-canary'
label: 'nextjs-app-dir (canary)'
- test-application: 'nextjs-app-dir'
build-command: 'test:build-latest'
label: 'nextjs-app-dir (latest)'
build-command: 'test:build-15'
label: 'nextjs-app-dir (next@15)'
- test-application: 'nextjs-13'
build-command: 'test:build-latest'
label: 'nextjs-13 (latest)'
Expand All @@ -90,12 +87,15 @@ jobs:
- test-application: 'nextjs-14'
build-command: 'test:build-latest'
label: 'nextjs-14 (latest)'
- test-application: 'nextjs-15'
build-command: 'test:build-canary'
label: 'nextjs-15 (canary)'
- test-application: 'nextjs-15'
build-command: 'test:build-latest'
label: 'nextjs-15 (latest)'
- test-application: 'nextjs-16'
build-command: 'test:build-canary'
label: 'nextjs-16 (canary)'
- test-application: 'nextjs-16'
build-command: 'test:build-canary-webpack'
label: 'nextjs-16 (canary-webpack)'
- test-application: 'nextjs-turbo'
build-command: 'test:build-canary'
label: 'nextjs-turbo (canary)'
Expand Down
12 changes: 6 additions & 6 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/index.js',
import: createImport('init', 'browserTracingIntegration'),
gzip: true,
limit: '40.7 KB',
limit: '41 KB',
},
{
name: '@sentry/browser (incl. Tracing, Replay)',
Expand Down Expand Up @@ -82,7 +82,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/index.js',
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'),
gzip: true,
limit: '96 KB',
limit: '97 KB',
},
{
name: '@sentry/browser (incl. Feedback)',
Expand All @@ -103,7 +103,7 @@ module.exports = [
path: 'packages/browser/build/npm/esm/index.js',
import: createImport('init', 'feedbackAsyncIntegration'),
gzip: true,
limit: '34 KB',
limit: '35 KB',
},
// React SDK (ESM)
{
Expand All @@ -128,7 +128,7 @@ module.exports = [
path: 'packages/vue/build/esm/index.js',
import: createImport('init'),
gzip: true,
limit: '29 KB',
limit: '30 KB',
},
{
name: '@sentry/vue (incl. Tracing)',
Expand Down Expand Up @@ -183,7 +183,7 @@ module.exports = [
path: createCDNPath('bundle.tracing.min.js'),
gzip: false,
brotli: false,
limit: '123 KB',
limit: '124 KB',
},
{
name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed',
Expand Down Expand Up @@ -215,7 +215,7 @@ module.exports = [
import: createImport('init'),
ignore: ['$app/stores'],
gzip: true,
limit: '41 KB',
limit: '42 KB',
},
// Node-Core SDK (ESM)
{
Expand Down
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

## 10.20.0

### Important Changes

- **feat(flags): Add Growthbook integration ([#17440](https://github.com/getsentry/sentry-javascript/pull/17440))**

Adds a new Growthbook integration for feature flag support.

- **feat(solid): Add support for TanStack Router Solid ([#17735](https://github.com/getsentry/sentry-javascript/pull/17735))**

Adds support for TanStack Router in the Solid SDK, enabling better routing instrumentation for Solid applications.

- **feat(nextjs): Support native debugIds in turbopack ([#17853](https://github.com/getsentry/sentry-javascript/pull/17853))**

Adds support for native Debug IDs in Turbopack, improving source map resolution and error tracking for Next.js applications using Turbopack. Native Debug ID generation will be enabled automatically for compatible versions.

### Other Changes

- feat(nextjs): Prepare for next 16 bundler default ([#17868](https://github.com/getsentry/sentry-javascript/pull/17868))
- feat(node): Capture `pino` logger name ([#17930](https://github.com/getsentry/sentry-javascript/pull/17930))
- fix(browser): Ignore React 19.2+ component render measure entries ([#17905](https://github.com/getsentry/sentry-javascript/pull/17905))
- fix(nextjs): Fix createRouteManifest with basePath ([#17838](https://github.com/getsentry/sentry-javascript/pull/17838))
- fix(react): Add `POP` guard for long-running `pageload` spans ([#17867](https://github.com/getsentry/sentry-javascript/pull/17867))
- fix(tracemetrics): Send boolean for internal replay attribute ([#17908](https://github.com/getsentry/sentry-javascript/pull/17908))
- ref(core): Add weight tracking logic to browser logs/metrics ([#17901](https://github.com/getsentry/sentry-javascript/pull/17901))

<details>
<summary> <strong>Internal Changes</strong> </summary>
- chore(nextjs): Add Next.js 16 peer dependency ([#17925](https://github.com/getsentry/sentry-javascript/pull/17925))
- chore(ci): Update Next.js canary testing ([#17939](https://github.com/getsentry/sentry-javascript/pull/17939))
- chore: Bump size limit ([#17941](https://github.com/getsentry/sentry-javascript/pull/17941))
- test(nextjs): Add next@16 e2e test ([#17922](https://github.com/getsentry/sentry-javascript/pull/17922))
- test(nextjs): Update next 15 tests ([#17919](https://github.com/getsentry/sentry-javascript/pull/17919))
- chore: Add external contributor to CHANGELOG.md ([#17915](https://github.com/getsentry/sentry-javascript/pull/17915))
- chore: Add external contributor to CHANGELOG.md ([#17928](https://github.com/getsentry/sentry-javascript/pull/17928))
- chore: Add external contributor to CHANGELOG.md ([#17940](https://github.com/getsentry/sentry-javascript/pull/17940))
</details>

Work in this release was contributed by @seoyeon9888, @madhuchavva and @thedanchez . Thank you for your contributions!

## 10.19.0

- feat(tracemetrics): Add trace metrics behind an experiments flag ([#17883](https://github.com/getsentry/sentry-javascript/pull/17883))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { expect } from '@playwright/test';
import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core';
import { sentryTest } from '../../../../../../utils/fixtures';
import {
envelopeRequestParser,
shouldSkipFeatureFlagsTest,
waitForErrorRequest,
} from '../../../../../../utils/helpers';

sentryTest('GrowthBook onError: basic eviction/update and no async tasks', async ({ getLocalTestUrl, page }) => {
if (shouldSkipFeatureFlagsTest()) {
sentryTest.skip();
}

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'test-id' }) });
});

const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
await page.goto(url);

await page.evaluate(bufferSize => {
const gb = new (window as any).GrowthBook();

for (let i = 1; i <= bufferSize; i++) {
gb.isOn(`feat${i}`);
}

gb.__setOn(`feat${bufferSize + 1}`, true);
gb.isOn(`feat${bufferSize + 1}`); // eviction

gb.__setOn('feat3', true);
gb.isOn('feat3'); // update

// Test getFeatureValue with boolean values (should be captured)
gb.__setFeatureValue('bool-feat', true);
gb.getFeatureValue('bool-feat', false);

// Test getFeatureValue with non-boolean values (should be ignored)
gb.__setFeatureValue('string-feat', 'hello');
gb.getFeatureValue('string-feat', 'default');
gb.__setFeatureValue('number-feat', 42);
gb.getFeatureValue('number-feat', 0);
}, FLAG_BUFFER_SIZE);

const reqPromise = waitForErrorRequest(page);
await page.locator('#error').click();
const req = await reqPromise;
const event = envelopeRequestParser(req);

const values = event.contexts?.flags?.values || [];

// After the sequence of operations:
// 1. feat1-feat100 are added (100 items)
// 2. feat101 is added, evicts feat1 (100 items: feat2-feat100, feat101)
// 3. feat3 is updated to true, moves to end (100 items: feat2, feat4-feat100, feat101, feat3)
// 4. bool-feat is added, evicts feat2 (100 items: feat4-feat100, feat101, feat3, bool-feat)

const expectedFlags = [];
for (let i = 4; i <= FLAG_BUFFER_SIZE; i++) {
expectedFlags.push({ flag: `feat${i}`, result: false });
}
expectedFlags.push({ flag: `feat${FLAG_BUFFER_SIZE + 1}`, result: true });
expectedFlags.push({ flag: 'feat3', result: true });
expectedFlags.push({ flag: 'bool-feat', result: true }); // Only boolean getFeatureValue should be captured

expect(values).toEqual(expectedFlags);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as Sentry from '@sentry/browser';

// Minimal mock GrowthBook class for tests
window.GrowthBook = class {
constructor() {
this._onFlags = Object.create(null);
this._featureValues = Object.create(null);
}

isOn(featureKey) {
return !!this._onFlags[featureKey];
}

getFeatureValue(featureKey, defaultValue) {
return Object.prototype.hasOwnProperty.call(this._featureValues, featureKey)
? this._featureValues[featureKey]
: defaultValue;
}

// Helpers for tests
__setOn(featureKey, value) {
this._onFlags[featureKey] = !!value;
}

__setFeatureValue(featureKey, value) {
this._featureValues[featureKey] = value;
}
};

window.Sentry = Sentry;
window.sentryGrowthBookIntegration = Sentry.growthbookIntegration({ growthbookClass: window.GrowthBook });

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
sampleRate: 1.0,
integrations: [window.sentryGrowthBookIntegration],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
document.getElementById('error').addEventListener('click', () => {
throw new Error('Button triggered error');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="error">Throw Error</button>
</body>
<script src="./subject.js"></script>
</html>


Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { expect } from '@playwright/test';
import type { Scope } from '@sentry/browser';
import { sentryTest } from '../../../../../../utils/fixtures';
import {
envelopeRequestParser,
shouldSkipFeatureFlagsTest,
waitForErrorRequest,
} from '../../../../../../utils/helpers';

sentryTest('GrowthBook onError: forked scopes are isolated', async ({ getLocalTestUrl, page }) => {
if (shouldSkipFeatureFlagsTest()) {
sentryTest.skip();
}

await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'test-id' }) });
});

const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
await page.goto(url);

const forkedReqPromise = waitForErrorRequest(page, event => !!event.tags?.isForked === true);
const mainReqPromise = waitForErrorRequest(page, event => !!event.tags?.isForked === false);

await page.evaluate(() => {
const Sentry = (window as any).Sentry;
const errorButton = document.querySelector('#error') as HTMLButtonElement;
const gb = new (window as any).GrowthBook();

gb.__setOn('shared', true);
gb.__setOn('main', true);

gb.isOn('shared');

Sentry.withScope((scope: Scope) => {
gb.__setOn('forked', true);
gb.__setOn('shared', false);
gb.isOn('forked');
gb.isOn('shared');
scope.setTag('isForked', true);
errorButton.click();
});

gb.isOn('main');
Sentry.getCurrentScope().setTag('isForked', false);
errorButton.click();
return true;
});

const forkedReq = await forkedReqPromise;
const forkedEvent = envelopeRequestParser(forkedReq);

const mainReq = await mainReqPromise;
const mainEvent = envelopeRequestParser(mainReq);

expect(forkedEvent.contexts?.flags?.values).toEqual([
{ flag: 'forked', result: true },
{ flag: 'shared', result: false },
]);

expect(mainEvent.contexts?.flags?.values).toEqual([
{ flag: 'shared', result: true },
{ flag: 'main', result: true },
]);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as Sentry from '@sentry/browser';

window.GrowthBook = class {
constructor() {
this._onFlags = Object.create(null);
this._featureValues = Object.create(null);
}

isOn(featureKey) {
return !!this._onFlags[featureKey];
}

getFeatureValue(featureKey, defaultValue) {
return Object.prototype.hasOwnProperty.call(this._featureValues, featureKey)
? this._featureValues[featureKey]
: defaultValue;
}

__setOn(featureKey, value) {
this._onFlags[featureKey] = !!value;
}

__setFeatureValue(featureKey, value) {
this._featureValues[featureKey] = value;
}
};

window.Sentry = Sentry;
window.sentryGrowthBookIntegration = Sentry.growthbookIntegration({ growthbookClass: window.GrowthBook });

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
sampleRate: 1.0,
tracesSampleRate: 1.0,
integrations: [
window.sentryGrowthBookIntegration,
Sentry.browserTracingIntegration({ instrumentNavigation: false, instrumentPageLoad: false }),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const btnStartSpan = document.getElementById('btnStartSpan');
const btnEndSpan = document.getElementById('btnEndSpan');
const btnStartNestedSpan = document.getElementById('btnStartNestedSpan');
const btnEndNestedSpan = document.getElementById('btnEndNestedSpan');

window.withNestedSpans = callback => {
window.Sentry.startSpan({ name: 'test-root-span' }, rootSpan => {
window.traceId = rootSpan.spanContext().traceId;

window.Sentry.startSpan({ name: 'test-span' }, _span => {
window.Sentry.startSpan({ name: 'test-nested-span' }, _nestedSpan => {
callback();
});
});
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="btnStartSpan">Start Span</button>
<button id="btnEndSpan">End Span</button>
<button id="btnStartNestedSpan">Start Nested Span</button>
<button id="btnEndNestedSpan">End Nested Span</button>
</body>
<script src="./subject.js"></script>
</html>


Loading
Loading