Skip to content

Commit e80eb02

Browse files
authored
Merge branch 'develop' into billy/replay-575-replay-w-linked-errors-from-prior-week
2 parents 2499b42 + ac57cec commit e80eb02

File tree

251 files changed

+6967
-619
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

251 files changed

+6967
-619
lines changed

.cursor/rules/publishing_release.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ The release process is outlined in [publishing-a-release.md](mdc:docs/publishing
1414

1515
1. Make sure you are on the latest version of the `develop` branch. To confirm this, run `git pull origin develop` to get the latest changes from the repo.
1616
2. Run `yarn changelog` on the `develop` branch and copy the output. You can use `yarn changelog | pbcopy` to copy the output of `yarn changelog` into your clipboard.
17-
3. Decide on a version for the release based on [semver](mdc:https://semver.org). The version should be decided based on what is in included in the release. For example, if the release includes a new feature, we should increment the minor version. If it includes only bug fixes, we should increment the patch version.
17+
3. Decide on a version for the release based on [semver](mdc:https://semver.org). The version should be decided based on what is in included in the release. For example, if the release includes a new feature, we should increment the minor version. If it includes only bug fixes, we should increment the patch version. You can find the latest version in [CHANGELOG.md](mdc:CHANGELOG.md) at the very top.
1818
4. Create a branch `prepare-release/VERSION`, eg. `prepare-release/8.1.0`, off `develop`.
1919
5. Update [CHANGELOG.md](mdc:CHANGELOG.md) to add an entry for the next release number and a list of changes since the last release from the output of `yarn changelog`. See the `Updating the Changelog` section in [publishing-a-release.md](mdc:docs/publishing-a-release.md) for more details. If you remove changelog entries because they are not applicable, please let the user know.
2020
6. Commit the changes to [CHANGELOG.md](mdc:CHANGELOG.md) with `meta(changelog): Update changelog for VERSION` where `VERSION` is the version of the release, e.g. `meta(changelog): Update changelog for 8.1.0`

.size-limit.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ module.exports = [
3838
path: 'packages/browser/build/npm/esm/index.js',
3939
import: createImport('init', 'browserTracingIntegration'),
4040
gzip: true,
41-
limit: '40.7 KB',
41+
limit: '41 KB',
4242
},
4343
{
4444
name: '@sentry/browser (incl. Tracing, Replay)',
@@ -82,7 +82,7 @@ module.exports = [
8282
path: 'packages/browser/build/npm/esm/index.js',
8383
import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'),
8484
gzip: true,
85-
limit: '96 KB',
85+
limit: '97 KB',
8686
},
8787
{
8888
name: '@sentry/browser (incl. Feedback)',
@@ -96,14 +96,14 @@ module.exports = [
9696
path: 'packages/browser/build/npm/esm/index.js',
9797
import: createImport('init', 'sendFeedback'),
9898
gzip: true,
99-
limit: '29 KB',
99+
limit: '30 KB',
100100
},
101101
{
102102
name: '@sentry/browser (incl. FeedbackAsync)',
103103
path: 'packages/browser/build/npm/esm/index.js',
104104
import: createImport('init', 'feedbackAsyncIntegration'),
105105
gzip: true,
106-
limit: '34 KB',
106+
limit: '35 KB',
107107
},
108108
// React SDK (ESM)
109109
{
@@ -128,7 +128,7 @@ module.exports = [
128128
path: 'packages/vue/build/esm/index.js',
129129
import: createImport('init'),
130130
gzip: true,
131-
limit: '29 KB',
131+
limit: '30 KB',
132132
},
133133
{
134134
name: '@sentry/vue (incl. Tracing)',
@@ -150,13 +150,13 @@ module.exports = [
150150
name: 'CDN Bundle',
151151
path: createCDNPath('bundle.min.js'),
152152
gzip: true,
153-
limit: '26 KB',
153+
limit: '27 KB',
154154
},
155155
{
156156
name: 'CDN Bundle (incl. Tracing)',
157157
path: createCDNPath('bundle.tracing.min.js'),
158158
gzip: true,
159-
limit: '41 KB',
159+
limit: '42 KB',
160160
},
161161
{
162162
name: 'CDN Bundle (incl. Tracing, Replay)',
@@ -183,7 +183,7 @@ module.exports = [
183183
path: createCDNPath('bundle.tracing.min.js'),
184184
gzip: false,
185185
brotli: false,
186-
limit: '120 KB',
186+
limit: '123 KB',
187187
},
188188
{
189189
name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed',
@@ -215,7 +215,7 @@ module.exports = [
215215
import: createImport('init'),
216216
ignore: ['$app/stores'],
217217
gzip: true,
218-
limit: '41 KB',
218+
limit: '42 KB',
219219
},
220220
// Node-Core SDK (ESM)
221221
{

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@
44

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

7+
Work in this release was contributed by @seoyeon9888. Thank you for your contribution!
8+
9+
## 10.19.0
10+
11+
- feat(tracemetrics): Add trace metrics behind an experiments flag ([#17883](https://github.com/getsentry/sentry-javascript/pull/17883))
12+
13+
<details>
14+
<summary> <strong>Internal Changes</strong> </summary>
15+
16+
- chore: add info latest release for the cursor release command ([#17876](https://github.com/getsentry/sentry-javascript/pull/17876))
17+
18+
</details>
19+
720
## 10.18.0
821

922
### Important Changes

dev-packages/browser-integration-tests/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sentry-internal/browser-integration-tests",
3-
"version": "10.18.0",
3+
"version": "10.19.0",
44
"main": "index.js",
55
"license": "MIT",
66
"engines": {
@@ -43,7 +43,7 @@
4343
"@babel/preset-typescript": "^7.16.7",
4444
"@playwright/test": "~1.53.2",
4545
"@sentry-internal/rrweb": "2.34.0",
46-
"@sentry/browser": "10.18.0",
46+
"@sentry/browser": "10.19.0",
4747
"@supabase/supabase-js": "2.49.3",
4848
"axios": "^1.12.2",
4949
"babel-loader": "^8.2.2",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { expect } from '@playwright/test';
2+
import { _INTERNAL_FLAG_BUFFER_SIZE as FLAG_BUFFER_SIZE } from '@sentry/core';
3+
import { sentryTest } from '../../../../../../utils/fixtures';
4+
import {
5+
envelopeRequestParser,
6+
shouldSkipFeatureFlagsTest,
7+
waitForErrorRequest,
8+
} from '../../../../../../utils/helpers';
9+
10+
sentryTest('GrowthBook onError: basic eviction/update and no async tasks', async ({ getLocalTestUrl, page }) => {
11+
if (shouldSkipFeatureFlagsTest()) {
12+
sentryTest.skip();
13+
}
14+
15+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
16+
return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'test-id' }) });
17+
});
18+
19+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
20+
await page.goto(url);
21+
22+
await page.evaluate(bufferSize => {
23+
const gb = new (window as any).GrowthBook();
24+
25+
for (let i = 1; i <= bufferSize; i++) {
26+
gb.isOn(`feat${i}`);
27+
}
28+
29+
gb.__setOn(`feat${bufferSize + 1}`, true);
30+
gb.isOn(`feat${bufferSize + 1}`); // eviction
31+
32+
gb.__setOn('feat3', true);
33+
gb.isOn('feat3'); // update
34+
35+
// Test getFeatureValue with boolean values (should be captured)
36+
gb.__setFeatureValue('bool-feat', true);
37+
gb.getFeatureValue('bool-feat', false);
38+
39+
// Test getFeatureValue with non-boolean values (should be ignored)
40+
gb.__setFeatureValue('string-feat', 'hello');
41+
gb.getFeatureValue('string-feat', 'default');
42+
gb.__setFeatureValue('number-feat', 42);
43+
gb.getFeatureValue('number-feat', 0);
44+
}, FLAG_BUFFER_SIZE);
45+
46+
const reqPromise = waitForErrorRequest(page);
47+
await page.locator('#error').click();
48+
const req = await reqPromise;
49+
const event = envelopeRequestParser(req);
50+
51+
const values = event.contexts?.flags?.values || [];
52+
53+
// After the sequence of operations:
54+
// 1. feat1-feat100 are added (100 items)
55+
// 2. feat101 is added, evicts feat1 (100 items: feat2-feat100, feat101)
56+
// 3. feat3 is updated to true, moves to end (100 items: feat2, feat4-feat100, feat101, feat3)
57+
// 4. bool-feat is added, evicts feat2 (100 items: feat4-feat100, feat101, feat3, bool-feat)
58+
59+
const expectedFlags = [];
60+
for (let i = 4; i <= FLAG_BUFFER_SIZE; i++) {
61+
expectedFlags.push({ flag: `feat${i}`, result: false });
62+
}
63+
expectedFlags.push({ flag: `feat${FLAG_BUFFER_SIZE + 1}`, result: true });
64+
expectedFlags.push({ flag: 'feat3', result: true });
65+
expectedFlags.push({ flag: 'bool-feat', result: true }); // Only boolean getFeatureValue should be captured
66+
67+
expect(values).toEqual(expectedFlags);
68+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
// Minimal mock GrowthBook class for tests
4+
window.GrowthBook = class {
5+
constructor() {
6+
this._onFlags = Object.create(null);
7+
this._featureValues = Object.create(null);
8+
}
9+
10+
isOn(featureKey) {
11+
return !!this._onFlags[featureKey];
12+
}
13+
14+
getFeatureValue(featureKey, defaultValue) {
15+
return Object.prototype.hasOwnProperty.call(this._featureValues, featureKey)
16+
? this._featureValues[featureKey]
17+
: defaultValue;
18+
}
19+
20+
// Helpers for tests
21+
__setOn(featureKey, value) {
22+
this._onFlags[featureKey] = !!value;
23+
}
24+
25+
__setFeatureValue(featureKey, value) {
26+
this._featureValues[featureKey] = value;
27+
}
28+
};
29+
30+
window.Sentry = Sentry;
31+
window.sentryGrowthBookIntegration = Sentry.growthbookIntegration({ growthbookClass: window.GrowthBook });
32+
33+
Sentry.init({
34+
dsn: 'https://[email protected]/1337',
35+
sampleRate: 1.0,
36+
integrations: [window.sentryGrowthBookIntegration],
37+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
document.getElementById('error').addEventListener('click', () => {
2+
throw new Error('Button triggered error');
3+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<button id="error">Throw Error</button>
8+
</body>
9+
<script src="./subject.js"></script>
10+
</html>
11+
12+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { expect } from '@playwright/test';
2+
import type { Scope } from '@sentry/browser';
3+
import { sentryTest } from '../../../../../../utils/fixtures';
4+
import {
5+
envelopeRequestParser,
6+
shouldSkipFeatureFlagsTest,
7+
waitForErrorRequest,
8+
} from '../../../../../../utils/helpers';
9+
10+
sentryTest('GrowthBook onError: forked scopes are isolated', async ({ getLocalTestUrl, page }) => {
11+
if (shouldSkipFeatureFlagsTest()) {
12+
sentryTest.skip();
13+
}
14+
15+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
16+
return route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: 'test-id' }) });
17+
});
18+
19+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
20+
await page.goto(url);
21+
22+
const forkedReqPromise = waitForErrorRequest(page, event => !!event.tags?.isForked === true);
23+
const mainReqPromise = waitForErrorRequest(page, event => !!event.tags?.isForked === false);
24+
25+
await page.evaluate(() => {
26+
const Sentry = (window as any).Sentry;
27+
const errorButton = document.querySelector('#error') as HTMLButtonElement;
28+
const gb = new (window as any).GrowthBook();
29+
30+
gb.__setOn('shared', true);
31+
gb.__setOn('main', true);
32+
33+
gb.isOn('shared');
34+
35+
Sentry.withScope((scope: Scope) => {
36+
gb.__setOn('forked', true);
37+
gb.__setOn('shared', false);
38+
gb.isOn('forked');
39+
gb.isOn('shared');
40+
scope.setTag('isForked', true);
41+
errorButton.click();
42+
});
43+
44+
gb.isOn('main');
45+
Sentry.getCurrentScope().setTag('isForked', false);
46+
errorButton.click();
47+
return true;
48+
});
49+
50+
const forkedReq = await forkedReqPromise;
51+
const forkedEvent = envelopeRequestParser(forkedReq);
52+
53+
const mainReq = await mainReqPromise;
54+
const mainEvent = envelopeRequestParser(mainReq);
55+
56+
expect(forkedEvent.contexts?.flags?.values).toEqual([
57+
{ flag: 'forked', result: true },
58+
{ flag: 'shared', result: false },
59+
]);
60+
61+
expect(mainEvent.contexts?.flags?.values).toEqual([
62+
{ flag: 'shared', result: true },
63+
{ flag: 'main', result: true },
64+
]);
65+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.GrowthBook = class {
4+
constructor() {
5+
this._onFlags = Object.create(null);
6+
this._featureValues = Object.create(null);
7+
}
8+
9+
isOn(featureKey) {
10+
return !!this._onFlags[featureKey];
11+
}
12+
13+
getFeatureValue(featureKey, defaultValue) {
14+
return Object.prototype.hasOwnProperty.call(this._featureValues, featureKey)
15+
? this._featureValues[featureKey]
16+
: defaultValue;
17+
}
18+
19+
__setOn(featureKey, value) {
20+
this._onFlags[featureKey] = !!value;
21+
}
22+
23+
__setFeatureValue(featureKey, value) {
24+
this._featureValues[featureKey] = value;
25+
}
26+
};
27+
28+
window.Sentry = Sentry;
29+
window.sentryGrowthBookIntegration = Sentry.growthbookIntegration({ growthbookClass: window.GrowthBook });
30+
31+
Sentry.init({
32+
dsn: 'https://[email protected]/1337',
33+
sampleRate: 1.0,
34+
tracesSampleRate: 1.0,
35+
integrations: [
36+
window.sentryGrowthBookIntegration,
37+
Sentry.browserTracingIntegration({ instrumentNavigation: false, instrumentPageLoad: false }),
38+
],
39+
});

0 commit comments

Comments
 (0)