Skip to content

Commit 8979782

Browse files
authored
Merge branch 'v8' into v8-upgrade-otel-deps
2 parents 18a272f + 42969d7 commit 8979782

File tree

147 files changed

+4790
-303
lines changed

Some content is hidden

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

147 files changed

+4790
-303
lines changed

CHANGELOG.md

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,104 @@
1010

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

13+
## 8.52.0
14+
15+
### Important Changes
16+
17+
- **feat(solidstart): Add `withSentry` wrapper for SolidStart config ([#15135](https://github.com/getsentry/sentry-javascript/pull/15135))**
18+
19+
To enable the SolidStart SDK, wrap your SolidStart Config with `withSentry`. The `sentrySolidStartVite` plugin is now automatically
20+
added by `withSentry` and you can pass the Sentry build-time options like this:
21+
22+
```js
23+
import { defineConfig } from '@solidjs/start/config';
24+
import { withSentry } from '@sentry/solidstart';
25+
26+
export default defineConfig(
27+
withSentry(
28+
{
29+
/* Your SolidStart config options... */
30+
},
31+
{
32+
// Options for setting up source maps
33+
org: process.env.SENTRY_ORG,
34+
project: process.env.SENTRY_PROJECT,
35+
authToken: process.env.SENTRY_AUTH_TOKEN,
36+
},
37+
),
38+
);
39+
```
40+
41+
With the `withSentry` wrapper, the Sentry server config should not be added to the `public` directory anymore.
42+
Add the Sentry server config in `src/instrument.server.ts`. Then, the server config will be placed inside the server build output as `instrument.server.mjs`.
43+
44+
Now, there are two options to set up the SDK:
45+
46+
1. **(recommended)** Provide an `--import` CLI flag to the start command like this (path depends on your server setup):
47+
`node --import ./.output/server/instrument.server.mjs .output/server/index.mjs`
48+
2. Add `autoInjectServerSentry: 'top-level-import'` and the Sentry config will be imported at the top of the server entry (comes with tracing limitations)
49+
```js
50+
withSentry(
51+
{
52+
/* Your SolidStart config options... */
53+
},
54+
{
55+
// Optional: Install Sentry with a top-level import
56+
autoInjectServerSentry: 'top-level-import',
57+
},
58+
);
59+
```
60+
61+
### Other Changes
62+
63+
- feat(v8/core): Add client outcomes for breadcrumbs buffer ([#15149](https://github.com/getsentry/sentry-javascript/pull/15149))
64+
- feat(v8/core): Improve error formatting in ZodErrors integration ([#15155](https://github.com/getsentry/sentry-javascript/pull/15155))
65+
- fix(v8/bun): Ensure instrumentation of `Bun.serve` survives a server reload ([#15157](https://github.com/getsentry/sentry-javascript/pull/15157))
66+
- fix(v8/core): Pass `module` into `loadModule` ([#15139](https://github.com/getsentry/sentry-javascript/pull/15139)) (#15166)
67+
68+
Work in this release was contributed by @jahands, @jrandolf, and @nathankleyn. Thank you for your contributions!
69+
70+
## 8.51.0
71+
72+
### Important Changes
73+
74+
- **feat(v8/node): Add `prismaInstrumentation` option to Prisma integration as escape hatch for all Prisma versions ([#15128](https://github.com/getsentry/sentry-javascript/pull/15128))**
75+
76+
This release adds a compatibility API to add support for Prisma version 6.
77+
To capture performance data for Prisma version 6:
78+
79+
1. Install the `@prisma/instrumentation` package on version 6.
80+
1. Pass a `new PrismaInstrumentation()` instance as exported from `@prisma/instrumentation` to the `prismaInstrumentation` option:
81+
82+
```js
83+
import { PrismaInstrumentation } from '@prisma/instrumentation';
84+
85+
Sentry.init({
86+
integrations: [
87+
prismaIntegration({
88+
// Override the default instrumentation that Sentry uses
89+
prismaInstrumentation: new PrismaInstrumentation(),
90+
}),
91+
],
92+
});
93+
```
94+
95+
The passed instrumentation instance will override the default instrumentation instance the integration would use, while the `prismaIntegration` will still ensure data compatibility for the various Prisma versions.
96+
97+
1. Remove the `previewFeatures = ["tracing"]` option from the client generator block of your Prisma schema.
98+
99+
### Other Changes
100+
101+
- feat(v8/browser): Add `multiplexedtransport.js` CDN bundle ([#15046](https://github.com/getsentry/sentry-javascript/pull/15046))
102+
- feat(v8/browser): Add Unleash integration ([#14948](https://github.com/getsentry/sentry-javascript/pull/14948))
103+
- feat(v8/deno): Deprecate Deno SDK as published on deno.land ([#15121](https://github.com/getsentry/sentry-javascript/pull/15121))
104+
- feat(v8/sveltekit): Deprecate `fetchProxyScriptNonce` option ([#15011](https://github.com/getsentry/sentry-javascript/pull/15011))
105+
- fix(v8/aws-lambda): Avoid overwriting root span name ([#15054](https://github.com/getsentry/sentry-javascript/pull/15054))
106+
- fix(v8/core): `fatal` events should set session as crashed ([#15073](https://github.com/getsentry/sentry-javascript/pull/15073))
107+
- fix(v8/node/nestjs): Use method on current fastify request ([#15104](https://github.com/getsentry/sentry-javascript/pull/15104))
108+
109+
Work in this release was contributed by @tjhiggins, and @nwalters512. Thank you for your contributions!
110+
13111
## 8.50.0
14112

15113
- feat(v8/react): Add support for React Router `createMemoryRouter` ([#14985](https://github.com/getsentry/sentry-javascript/pull/14985))
@@ -24,7 +122,7 @@
24122
- fix(v8/sveltekit): Ensure source maps deletion is called after source ma… ([#14963](https://github.com/getsentry/sentry-javascript/pull/14963))
25123
- fix(v8/vue): Re-throw error when no errorHandler exists ([#14943](https://github.com/getsentry/sentry-javascript/pull/14943))
26124
27-
Work in this release was contributed by @HHK1 and @mstrokin. Thank you for your contribution!
125+
Work in this release was contributed by @HHK1 and @mstrokin. Thank you for your contributions!
28126
29127
## 8.48.0
30128

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": "8.50.0",
3+
"version": "8.52.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.44.1",
4545
"@sentry-internal/rrweb": "2.31.0",
46-
"@sentry/browser": "8.50.0",
46+
"@sentry/browser": "8.52.0",
4747
"axios": "1.7.7",
4848
"babel-loader": "^8.2.2",
4949
"html-webpack-plugin": "^5.5.0",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.UnleashClient = class {
4+
isEnabled(x) {
5+
return x;
6+
}
7+
};
8+
9+
window.Sentry = Sentry;
10+
window.sentryUnleashIntegration = Sentry.unleashIntegration({ unleashClientClass: window.UnleashClient });
11+
12+
Sentry.init({
13+
dsn: 'https://[email protected]/1337',
14+
sampleRate: 1.0,
15+
integrations: [window.sentryUnleashIntegration],
16+
debug: true, // Required to test logging.
17+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../../../utils/fixtures';
4+
5+
import { shouldSkipFeatureFlagsTest } from '../../../../../utils/helpers';
6+
7+
sentryTest('Logs and returns if isEnabled does not match expected signature', async ({ getLocalTestUrl, page }) => {
8+
if (shouldSkipFeatureFlagsTest()) {
9+
sentryTest.skip();
10+
}
11+
const bundleKey = process.env.PW_BUNDLE || '';
12+
const hasDebug = !bundleKey.includes('_min');
13+
14+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
15+
return route.fulfill({
16+
status: 200,
17+
contentType: 'application/json',
18+
body: JSON.stringify({ id: 'test-id' }),
19+
});
20+
});
21+
22+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
23+
await page.goto(url);
24+
25+
const errorLogs: string[] = [];
26+
page.on('console', msg => {
27+
if (msg.type() == 'error') {
28+
errorLogs.push(msg.text());
29+
}
30+
});
31+
32+
const results = await page.evaluate(() => {
33+
const unleash = new (window as any).UnleashClient();
34+
const res1 = unleash.isEnabled('my-feature');
35+
const res2 = unleash.isEnabled(999);
36+
const res3 = unleash.isEnabled({});
37+
return [res1, res2, res3];
38+
});
39+
40+
// Test that the expected results are still returned. Note isEnabled is identity function for this test.
41+
expect(results).toEqual(['my-feature', 999, {}]);
42+
43+
// Expected error logs.
44+
if (hasDebug) {
45+
expect(errorLogs).toEqual(
46+
expect.arrayContaining([
47+
expect.stringContaining(
48+
'[Feature Flags] UnleashClient.isEnabled does not match expected signature. arg0: my-feature (string), result: my-feature (string)',
49+
),
50+
expect.stringContaining(
51+
'[Feature Flags] UnleashClient.isEnabled does not match expected signature. arg0: 999 (number), result: 999 (number)',
52+
),
53+
expect.stringContaining(
54+
'[Feature Flags] UnleashClient.isEnabled does not match expected signature. arg0: [object Object] (object), result: [object Object] (object)',
55+
),
56+
]),
57+
);
58+
}
59+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../../../utils/fixtures';
4+
5+
import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest } from '../../../../../utils/helpers';
6+
7+
const FLAG_BUFFER_SIZE = 100; // Corresponds to constant in featureFlags.ts, in browser utils.
8+
9+
sentryTest('Basic test with eviction, update, and no async tasks', async ({ getLocalTestUrl, page }) => {
10+
if (shouldSkipFeatureFlagsTest()) {
11+
sentryTest.skip();
12+
}
13+
14+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
15+
return route.fulfill({
16+
status: 200,
17+
contentType: 'application/json',
18+
body: JSON.stringify({ id: 'test-id' }),
19+
});
20+
});
21+
22+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
23+
await page.goto(url);
24+
25+
await page.evaluate(bufferSize => {
26+
const client = new (window as any).UnleashClient();
27+
28+
client.isEnabled('feat1');
29+
client.isEnabled('strFeat');
30+
client.isEnabled('noPayloadFeat');
31+
client.isEnabled('jsonFeat');
32+
client.isEnabled('noVariantFeat');
33+
client.isEnabled('disabledFeat');
34+
35+
for (let i = 7; i <= bufferSize; i++) {
36+
client.isEnabled(`feat${i}`);
37+
}
38+
client.isEnabled(`feat${bufferSize + 1}`); // eviction
39+
client.isEnabled('noPayloadFeat'); // update (move to tail)
40+
}, FLAG_BUFFER_SIZE);
41+
42+
const reqPromise = waitForErrorRequest(page);
43+
await page.locator('#error').click();
44+
const req = await reqPromise;
45+
const event = envelopeRequestParser(req);
46+
47+
const expectedFlags = [{ flag: 'strFeat', result: true }];
48+
expectedFlags.push({ flag: 'jsonFeat', result: true });
49+
expectedFlags.push({ flag: 'noVariantFeat', result: true });
50+
expectedFlags.push({ flag: 'disabledFeat', result: false });
51+
for (let i = 7; i <= FLAG_BUFFER_SIZE; i++) {
52+
expectedFlags.push({ flag: `feat${i}`, result: false });
53+
}
54+
expectedFlags.push({ flag: `feat${FLAG_BUFFER_SIZE + 1}`, result: false });
55+
expectedFlags.push({ flag: 'noPayloadFeat', result: true });
56+
57+
expect(event.contexts?.flags?.values).toEqual(expectedFlags);
58+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.UnleashClient = class {
4+
constructor() {
5+
this._featureToVariant = {
6+
strFeat: { name: 'variant1', enabled: true, feature_enabled: true, payload: { type: 'string', value: 'test' } },
7+
noPayloadFeat: { name: 'eu-west', enabled: true, feature_enabled: true },
8+
jsonFeat: {
9+
name: 'paid-orgs',
10+
enabled: true,
11+
feature_enabled: true,
12+
payload: {
13+
type: 'json',
14+
value: '{"foo": {"bar": "baz"}, "hello": [1, 2, 3]}',
15+
},
16+
},
17+
18+
// Enabled feature with no configured variants.
19+
noVariantFeat: { name: 'disabled', enabled: false, feature_enabled: true },
20+
21+
// Disabled feature.
22+
disabledFeat: { name: 'disabled', enabled: false, feature_enabled: false },
23+
};
24+
25+
// Variant returned for features that don't exist.
26+
// `feature_enabled` may be defined in prod, but we want to test the undefined case.
27+
this._fallbackVariant = {
28+
name: 'disabled',
29+
enabled: false,
30+
};
31+
}
32+
33+
isEnabled(toggleName) {
34+
const variant = this._featureToVariant[toggleName] || this._fallbackVariant;
35+
return variant.feature_enabled || false;
36+
}
37+
38+
getVariant(toggleName) {
39+
return this._featureToVariant[toggleName] || this._fallbackVariant;
40+
}
41+
};
42+
43+
window.Sentry = Sentry;
44+
window.sentryUnleashIntegration = Sentry.unleashIntegration({ unleashClientClass: window.UnleashClient });
45+
46+
Sentry.init({
47+
dsn: 'https://[email protected]/1337',
48+
sampleRate: 1.0,
49+
integrations: [window.sentryUnleashIntegration],
50+
});
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
</html>
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+
3+
import { sentryTest } from '../../../../../utils/fixtures';
4+
5+
import { envelopeRequestParser, shouldSkipFeatureFlagsTest, waitForErrorRequest } from '../../../../../utils/helpers';
6+
7+
import type { Scope } from '@sentry/browser';
8+
9+
sentryTest('Flag evaluations in forked scopes are stored separately.', async ({ getLocalTestUrl, page }) => {
10+
if (shouldSkipFeatureFlagsTest()) {
11+
sentryTest.skip();
12+
}
13+
14+
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
15+
return route.fulfill({
16+
status: 200,
17+
contentType: 'application/json',
18+
body: JSON.stringify({ id: 'test-id' }),
19+
});
20+
});
21+
22+
const url = await getLocalTestUrl({ testDir: __dirname, skipDsnRouteHandler: true });
23+
await page.goto(url);
24+
25+
const forkedReqPromise = waitForErrorRequest(page, event => !!event.tags && event.tags.isForked === true);
26+
const mainReqPromise = waitForErrorRequest(page, event => !!event.tags && event.tags.isForked === false);
27+
28+
await page.evaluate(() => {
29+
const Sentry = (window as any).Sentry;
30+
const errorButton = document.querySelector('#error') as HTMLButtonElement;
31+
const unleash = new (window as any).UnleashClient();
32+
33+
unleash.isEnabled('strFeat');
34+
35+
Sentry.withScope((scope: Scope) => {
36+
unleash.isEnabled('disabledFeat');
37+
unleash.isEnabled('strFeat');
38+
scope.setTag('isForked', true);
39+
if (errorButton) {
40+
errorButton.click();
41+
}
42+
});
43+
44+
unleash.isEnabled('noPayloadFeat');
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: 'disabledFeat', result: false },
58+
{ flag: 'strFeat', result: true },
59+
]);
60+
61+
expect(mainEvent.contexts?.flags?.values).toEqual([
62+
{ flag: 'strFeat', result: true },
63+
{ flag: 'noPayloadFeat', result: true },
64+
]);
65+
});

0 commit comments

Comments
 (0)