Skip to content

Commit 3657c56

Browse files
authored
Merge pull request #16417 from getsentry/prepare-release/9.24.0
meta: Update changelog for 9.24.0
2 parents 3b06e0b + d8f3f8f commit 3657c56

File tree

66 files changed

+3818
-167
lines changed

Some content is hidden

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

66 files changed

+3818
-167
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@
1010

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

13+
## 9.24.0
14+
15+
### Important Changes
16+
17+
- feat(angular): Bump `@sentry/angular` peer dependencies to add Angular 20 support ([#16414](https://github.com/getsentry/sentry-javascript/pull/16414))
18+
19+
This release adds support for Angular 20 to the Sentry Angular SDK `@sentry/angular`.
20+
21+
### Other Changes
22+
23+
- feat(browser): Add `unregisterOriginalCallbacks` option to `browserApiErrorsIntegration` ([#16412](https://github.com/getsentry/sentry-javascript/pull/16412))
24+
- feat(core): Add user to logs ([#16399](https://github.com/getsentry/sentry-javascript/pull/16399))
25+
- feat(core): Make sure Supabase db query insights are populated ([#16169](https://github.com/getsentry/sentry-javascript/pull/16169))
26+
1327
## 9.23.0
1428

1529
### Important changes
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
const btn = document.getElementById('btn');
6+
7+
const myClickListener = () => {
8+
// eslint-disable-next-line no-console
9+
console.log('clicked');
10+
};
11+
12+
btn.addEventListener('click', myClickListener);
13+
14+
Sentry.init({
15+
dsn: 'https://[email protected]/1337',
16+
});
17+
18+
btn.addEventListener('click', myClickListener);
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="btn">Click me</button>
8+
</body>
9+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../utils/fixtures';
3+
4+
/**
5+
* This test demonstrates an unfortunate edge case with our EventTarget.addEventListener instrumentation.
6+
* If a listener is registered before Sentry.init() and then again, the same listener is added
7+
* after Sentry.init(), our `browserApiErrorsIntegration`'s instrumentation causes the listener to be
8+
* added twice, while without the integration it would only be added and invoked once.
9+
*
10+
* Real-life example of such an issue:
11+
* https://github.com/getsentry/sentry-javascript/issues/16398
12+
*/
13+
sentryTest(
14+
'causes listeners to be invoked twice if registered before and after Sentry initialization',
15+
async ({ getLocalTestUrl, page }) => {
16+
const consoleLogs: string[] = [];
17+
page.on('console', msg => {
18+
consoleLogs.push(msg.text());
19+
});
20+
21+
await page.goto(await getLocalTestUrl({ testDir: __dirname }));
22+
23+
await page.waitForFunction('window.Sentry');
24+
25+
await page.locator('#btn').click();
26+
27+
expect(consoleLogs).toHaveLength(2);
28+
expect(consoleLogs).toEqual(['clicked', 'clicked']);
29+
},
30+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
const btn = document.getElementById('btn');
6+
7+
const myClickListener = () => {
8+
// eslint-disable-next-line no-console
9+
console.log('clicked');
10+
};
11+
12+
btn.addEventListener('click', myClickListener);
13+
14+
Sentry.init({
15+
dsn: 'https://[email protected]/1337',
16+
integrations: [
17+
Sentry.browserApiErrorsIntegration({
18+
unregisterOriginalCallbacks: true,
19+
}),
20+
],
21+
});
22+
23+
btn.addEventListener('click', myClickListener);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../../utils/fixtures';
3+
4+
/**
5+
* By setting `unregisterOriginalCallbacks` to `true`, we can avoid the issue of double-invocations
6+
* (see other test for more details).
7+
*/
8+
sentryTest(
9+
'causes listeners to be invoked twice if registered before and after Sentry initialization',
10+
async ({ getLocalTestUrl, page }) => {
11+
const consoleLogs: string[] = [];
12+
page.on('console', msg => {
13+
consoleLogs.push(msg.text());
14+
});
15+
16+
await page.goto(await getLocalTestUrl({ testDir: __dirname }));
17+
18+
await page.waitForFunction('window.Sentry');
19+
20+
await page.locator('#btn').click();
21+
22+
expect(consoleLogs).toHaveLength(1);
23+
expect(consoleLogs).toEqual(['clicked']);
24+
},
25+
);

dev-packages/browser-integration-tests/suites/integrations/supabase/auth/test.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,34 +81,40 @@ sentryTest('should capture Supabase authentication spans', async ({ getLocalTest
8181
const url = await getLocalTestUrl({ testDir: __dirname });
8282

8383
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
84-
const supabaseSpans = eventData.spans?.filter(({ op }) => op?.startsWith('db.auth'));
84+
const supabaseSpans = eventData.spans?.filter(({ op }) => op?.startsWith('db'));
8585

8686
expect(supabaseSpans).toHaveLength(2);
8787
expect(supabaseSpans![0]).toMatchObject({
88-
description: 'signInWithPassword',
88+
description: 'auth signInWithPassword',
89+
op: 'db',
8990
parent_span_id: eventData.contexts?.trace?.span_id,
9091
span_id: expect.any(String),
9192
start_timestamp: expect.any(Number),
9293
timestamp: expect.any(Number),
9394
trace_id: eventData.contexts?.trace?.trace_id,
9495
status: 'ok',
9596
data: expect.objectContaining({
96-
'sentry.op': 'db.auth.signInWithPassword',
97+
'sentry.op': 'db',
9798
'sentry.origin': 'auto.db.supabase',
99+
'db.operation': 'auth.signInWithPassword',
100+
'db.system': 'postgresql',
98101
}),
99102
});
100103

101104
expect(supabaseSpans![1]).toMatchObject({
102-
description: 'signOut',
105+
description: 'auth signOut',
106+
op: 'db',
103107
parent_span_id: eventData.contexts?.trace?.span_id,
104108
span_id: expect.any(String),
105109
start_timestamp: expect.any(Number),
106110
timestamp: expect.any(Number),
107111
trace_id: eventData.contexts?.trace?.trace_id,
108112
status: 'ok',
109113
data: expect.objectContaining({
110-
'sentry.op': 'db.auth.signOut',
114+
'sentry.op': 'db',
111115
'sentry.origin': 'auto.db.supabase',
116+
'db.operation': 'auth.signOut',
117+
'db.system': 'postgresql',
112118
}),
113119
});
114120
});
@@ -124,22 +130,25 @@ sentryTest('should capture Supabase authentication errors', async ({ getLocalTes
124130

125131
const [errorEvent, transactionEvent] = await getMultipleSentryEnvelopeRequests<Event>(page, 2, { url });
126132

127-
const supabaseSpans = transactionEvent.spans?.filter(({ op }) => op?.startsWith('db.auth'));
133+
const supabaseSpans = transactionEvent.spans?.filter(({ op }) => op?.startsWith('db'));
128134

129135
expect(errorEvent.exception?.values?.[0].value).toBe('Invalid email or password');
130136

131137
expect(supabaseSpans).toHaveLength(2);
132138
expect(supabaseSpans![0]).toMatchObject({
133-
description: 'signInWithPassword',
139+
description: 'auth signInWithPassword',
140+
op: 'db',
134141
parent_span_id: transactionEvent.contexts?.trace?.span_id,
135142
span_id: expect.any(String),
136143
start_timestamp: expect.any(Number),
137144
timestamp: expect.any(Number),
138145
trace_id: transactionEvent.contexts?.trace?.trace_id,
139146
status: 'unknown_error',
140147
data: expect.objectContaining({
141-
'sentry.op': 'db.auth.signInWithPassword',
148+
'sentry.op': 'db',
142149
'sentry.origin': 'auto.db.supabase',
150+
'db.operation': 'auth.signInWithPassword',
151+
'db.system': 'postgresql',
143152
}),
144153
});
145154
});

dev-packages/browser-integration-tests/suites/integrations/supabase/db-operations/test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ sentryTest('should capture Supabase database operation breadcrumbs', async ({ ge
4444
timestamp: expect.any(Number),
4545
type: 'supabase',
4646
category: 'db.insert',
47-
message: 'from(todos)',
48-
data: expect.any(Object),
47+
message: 'insert(...) filter(columns, ) from(todos)',
48+
data: expect.objectContaining({
49+
query: expect.arrayContaining(['filter(columns, )']),
50+
}),
4951
});
5052
});
5153

dev-packages/e2e-tests/test-applications/angular-20/package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@
1515
},
1616
"private": true,
1717
"dependencies": {
18-
"@angular/animations": "^20.0.0-rc.2",
19-
"@angular/common": "^20.0.0-rc.2",
20-
"@angular/compiler": "^20.0.0-rc.2",
21-
"@angular/core": "^20.0.0-rc.2",
22-
"@angular/forms": "^20.0.0-rc.2",
23-
"@angular/platform-browser": "^20.0.0-rc.2",
24-
"@angular/platform-browser-dynamic": "^20.0.0-rc.2",
25-
"@angular/router": "^20.0.0-rc.2",
18+
"@angular/animations": "^20.0.0",
19+
"@angular/common": "^20.0.0",
20+
"@angular/compiler": "^20.0.0",
21+
"@angular/core": "^20.0.0",
22+
"@angular/forms": "^20.0.0",
23+
"@angular/platform-browser": "^20.0.0",
24+
"@angular/platform-browser-dynamic": "^20.0.0",
25+
"@angular/router": "^20.0.0",
2626
"@sentry/angular": "* || latest",
2727
"rxjs": "~7.8.0",
2828
"tslib": "^2.3.0",
2929
"zone.js": "~0.15.0"
3030
},
3131
"devDependencies": {
32-
"@angular-devkit/build-angular": "^20.0.0-rc.2",
33-
"@angular/cli": "^20.0.0-rc.2",
34-
"@angular/compiler-cli": "^20.0.0-rc.2",
32+
"@angular-devkit/build-angular": "^20.0.0",
33+
"@angular/cli": "^20.0.0",
34+
"@angular/compiler-cli": "^20.0.0",
3535
"@playwright/test": "~1.50.0",
3636
"@sentry-internal/test-utils": "link:../../../test-utils",
3737
"@types/jasmine": "~5.1.0",

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/app/entry.client.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import * as Sentry from '@sentry/remix';
33
import { StrictMode, startTransition, useEffect } from 'react';
44
import { hydrateRoot } from 'react-dom/client';
55

6+
// Extend the Window interface to include ENV
7+
declare global {
8+
interface Window {
9+
ENV: {
10+
SENTRY_DSN: string;
11+
[key: string]: unknown;
12+
};
13+
}
14+
}
15+
616
Sentry.init({
717
environment: 'qa', // dynamic sampling bias to keep transactions
818
dsn: window.ENV.SENTRY_DSN,

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/app/entry.server.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ function isBotRequest(userAgent: string | null) {
4040
return isbotModule.isbot(userAgent);
4141
}
4242

43-
// isbot < 3.8.0
44-
if ('default' in isbotModule && typeof isbotModule.default === 'function') {
45-
return isbotModule.default(userAgent);
46-
}
47-
4843
return false;
4944
}
5045

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/app/root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function ErrorBoundary() {
5252
}
5353

5454
function App() {
55-
const { ENV } = useLoaderData();
55+
const { ENV } = useLoaderData() as { ENV: { SENTRY_DSN: string } };
5656

5757
return (
5858
<html lang="en">

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/app/routes/navigate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({ params: { id } }) => {
1010
};
1111

1212
export default function LoaderError() {
13-
const data = useLoaderData();
13+
const data = useLoaderData() as { test?: string };
1414

1515
return (
1616
<div>

dev-packages/e2e-tests/test-applications/create-remix-app-express-vite-dev/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"sideEffects": false,
44
"scripts": {
5-
"build": "remix vite:build",
5+
"build": "remix vite:build && pnpm typecheck",
66
"dev": "node ./server.mjs",
77
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
88
"start": "cross-env NODE_ENV=production node ./server.mjs",

dev-packages/e2e-tests/test-applications/create-remix-app-express/app/entry.client.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import * as Sentry from '@sentry/remix';
33
import { StrictMode, startTransition, useEffect } from 'react';
44
import { hydrateRoot } from 'react-dom/client';
55

6+
// Extend the Window interface to include ENV
7+
declare global {
8+
interface Window {
9+
ENV: {
10+
SENTRY_DSN: string;
11+
[key: string]: unknown;
12+
};
13+
}
14+
}
15+
616
Sentry.init({
717
environment: 'qa', // dynamic sampling bias to keep transactions
818
dsn: window.ENV.SENTRY_DSN,

dev-packages/e2e-tests/test-applications/create-remix-app-express/app/entry.server.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ function isBotRequest(userAgent: string | null) {
4040
return isbotModule.isbot(userAgent);
4141
}
4242

43-
// isbot < 3.8.0
44-
if ('default' in isbotModule && typeof isbotModule.default === 'function') {
45-
return isbotModule.default(userAgent);
46-
}
47-
4843
return false;
4944
}
5045

dev-packages/e2e-tests/test-applications/create-remix-app-express/app/root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function ErrorBoundary() {
5252
}
5353

5454
function App() {
55-
const { ENV } = useLoaderData();
55+
const { ENV } = useLoaderData() as { ENV: { SENTRY_DSN: string } };
5656

5757
return (
5858
<html lang="en">

dev-packages/e2e-tests/test-applications/create-remix-app-express/app/routes/navigate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({ params: { id } }) => {
1010
};
1111

1212
export default function LoaderError() {
13-
const data = useLoaderData();
13+
const data = useLoaderData() as { test?: string };
1414

1515
return (
1616
<div>

dev-packages/e2e-tests/test-applications/create-remix-app-express/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"sideEffects": false,
44
"type": "module",
55
"scripts": {
6-
"build": "remix vite:build",
6+
"build": "remix vite:build && pnpm typecheck",
77
"dev": "node ./server.mjs",
88
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
99
"start": "cross-env NODE_ENV=production node ./server.mjs",

dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-errors.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { waitForError } from '@sentry-internal/test-utils';
33

44
test('Sends a loader error to Sentry', async ({ page }) => {
55
const loaderErrorPromise = waitForError('create-remix-app-express', errorEvent => {
6-
return errorEvent.exception.values[0].value === 'Loader Error';
6+
return errorEvent?.exception?.values?.[0]?.value === 'Loader Error';
77
});
88

99
await page.goto('/loader-error');

0 commit comments

Comments
 (0)