Skip to content

meta: Update changelog for 9.24.0 #16417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@

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

## 9.24.0

### Important Changes

- feat(angular): Bump `@sentry/angular` peer dependencies to add Angular 20 support ([#16414](https://github.com/getsentry/sentry-javascript/pull/16414))

This release adds support for Angular 20 to the Sentry Angular SDK `@sentry/angular`.

### Other Changes

- feat(browser): Add `unregisterOriginalCallbacks` option to `browserApiErrorsIntegration` ([#16412](https://github.com/getsentry/sentry-javascript/pull/16412))
- feat(core): Add user to logs ([#16399](https://github.com/getsentry/sentry-javascript/pull/16399))
- feat(core): Make sure Supabase db query insights are populated ([#16169](https://github.com/getsentry/sentry-javascript/pull/16169))

## 9.23.0

### Important changes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

const btn = document.getElementById('btn');

const myClickListener = () => {
// eslint-disable-next-line no-console
console.log('clicked');
};

btn.addEventListener('click', myClickListener);

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

btn.addEventListener('click', myClickListener);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<button id="btn">Click me</button>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../utils/fixtures';

/**
* This test demonstrates an unfortunate edge case with our EventTarget.addEventListener instrumentation.
* If a listener is registered before Sentry.init() and then again, the same listener is added
* after Sentry.init(), our `browserApiErrorsIntegration`'s instrumentation causes the listener to be
* added twice, while without the integration it would only be added and invoked once.
*
* Real-life example of such an issue:
* https://github.com/getsentry/sentry-javascript/issues/16398
*/
sentryTest(
'causes listeners to be invoked twice if registered before and after Sentry initialization',
async ({ getLocalTestUrl, page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
consoleLogs.push(msg.text());
});

await page.goto(await getLocalTestUrl({ testDir: __dirname }));

await page.waitForFunction('window.Sentry');

await page.locator('#btn').click();

expect(consoleLogs).toHaveLength(2);
expect(consoleLogs).toEqual(['clicked', 'clicked']);
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

const btn = document.getElementById('btn');

const myClickListener = () => {
// eslint-disable-next-line no-console
console.log('clicked');
};

btn.addEventListener('click', myClickListener);

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [
Sentry.browserApiErrorsIntegration({
unregisterOriginalCallbacks: true,
}),
],
});

btn.addEventListener('click', myClickListener);
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';

/**
* By setting `unregisterOriginalCallbacks` to `true`, we can avoid the issue of double-invocations
* (see other test for more details).
*/
sentryTest(
'causes listeners to be invoked twice if registered before and after Sentry initialization',
async ({ getLocalTestUrl, page }) => {
const consoleLogs: string[] = [];
page.on('console', msg => {
consoleLogs.push(msg.text());
});

await page.goto(await getLocalTestUrl({ testDir: __dirname }));

await page.waitForFunction('window.Sentry');

await page.locator('#btn').click();

expect(consoleLogs).toHaveLength(1);
expect(consoleLogs).toEqual(['clicked']);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -81,34 +81,40 @@ sentryTest('should capture Supabase authentication spans', async ({ getLocalTest
const url = await getLocalTestUrl({ testDir: __dirname });

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

expect(supabaseSpans).toHaveLength(2);
expect(supabaseSpans![0]).toMatchObject({
description: 'signInWithPassword',
description: 'auth signInWithPassword',
op: 'db',
parent_span_id: eventData.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: eventData.contexts?.trace?.trace_id,
status: 'ok',
data: expect.objectContaining({
'sentry.op': 'db.auth.signInWithPassword',
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
'db.operation': 'auth.signInWithPassword',
'db.system': 'postgresql',
}),
});

expect(supabaseSpans![1]).toMatchObject({
description: 'signOut',
description: 'auth signOut',
op: 'db',
parent_span_id: eventData.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: eventData.contexts?.trace?.trace_id,
status: 'ok',
data: expect.objectContaining({
'sentry.op': 'db.auth.signOut',
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
'db.operation': 'auth.signOut',
'db.system': 'postgresql',
}),
});
});
Expand All @@ -124,22 +130,25 @@ sentryTest('should capture Supabase authentication errors', async ({ getLocalTes

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

const supabaseSpans = transactionEvent.spans?.filter(({ op }) => op?.startsWith('db.auth'));
const supabaseSpans = transactionEvent.spans?.filter(({ op }) => op?.startsWith('db'));

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

expect(supabaseSpans).toHaveLength(2);
expect(supabaseSpans![0]).toMatchObject({
description: 'signInWithPassword',
description: 'auth signInWithPassword',
op: 'db',
parent_span_id: transactionEvent.contexts?.trace?.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: transactionEvent.contexts?.trace?.trace_id,
status: 'unknown_error',
data: expect.objectContaining({
'sentry.op': 'db.auth.signInWithPassword',
'sentry.op': 'db',
'sentry.origin': 'auto.db.supabase',
'db.operation': 'auth.signInWithPassword',
'db.system': 'postgresql',
}),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ sentryTest('should capture Supabase database operation breadcrumbs', async ({ ge
timestamp: expect.any(Number),
type: 'supabase',
category: 'db.insert',
message: 'from(todos)',
data: expect.any(Object),
message: 'insert(...) filter(columns, ) from(todos)',
data: expect.objectContaining({
query: expect.arrayContaining(['filter(columns, )']),
}),
});
});

Expand Down
22 changes: 11 additions & 11 deletions dev-packages/e2e-tests/test-applications/angular-20/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^20.0.0-rc.2",
"@angular/common": "^20.0.0-rc.2",
"@angular/compiler": "^20.0.0-rc.2",
"@angular/core": "^20.0.0-rc.2",
"@angular/forms": "^20.0.0-rc.2",
"@angular/platform-browser": "^20.0.0-rc.2",
"@angular/platform-browser-dynamic": "^20.0.0-rc.2",
"@angular/router": "^20.0.0-rc.2",
"@angular/animations": "^20.0.0",
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@angular/platform-browser-dynamic": "^20.0.0",
"@angular/router": "^20.0.0",
"@sentry/angular": "* || latest",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^20.0.0-rc.2",
"@angular/cli": "^20.0.0-rc.2",
"@angular/compiler-cli": "^20.0.0-rc.2",
"@angular-devkit/build-angular": "^20.0.0",
"@angular/cli": "^20.0.0",
"@angular/compiler-cli": "^20.0.0",
"@playwright/test": "~1.50.0",
"@sentry-internal/test-utils": "link:../../../test-utils",
"@types/jasmine": "~5.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import * as Sentry from '@sentry/remix';
import { StrictMode, startTransition, useEffect } from 'react';
import { hydrateRoot } from 'react-dom/client';

// Extend the Window interface to include ENV
declare global {
interface Window {
ENV: {
SENTRY_DSN: string;
[key: string]: unknown;
};
}
}

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: window.ENV.SENTRY_DSN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ function isBotRequest(userAgent: string | null) {
return isbotModule.isbot(userAgent);
}

// isbot < 3.8.0
if ('default' in isbotModule && typeof isbotModule.default === 'function') {
return isbotModule.default(userAgent);
}

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function ErrorBoundary() {
}

function App() {
const { ENV } = useLoaderData();
const { ENV } = useLoaderData() as { ENV: { SENTRY_DSN: string } };

return (
<html lang="en">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({ params: { id } }) => {
};

export default function LoaderError() {
const data = useLoaderData();
const data = useLoaderData() as { test?: string };

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"private": true,
"sideEffects": false,
"scripts": {
"build": "remix vite:build",
"build": "remix vite:build && pnpm typecheck",
"dev": "node ./server.mjs",
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"start": "cross-env NODE_ENV=production node ./server.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import * as Sentry from '@sentry/remix';
import { StrictMode, startTransition, useEffect } from 'react';
import { hydrateRoot } from 'react-dom/client';

// Extend the Window interface to include ENV
declare global {
interface Window {
ENV: {
SENTRY_DSN: string;
[key: string]: unknown;
};
}
}

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: window.ENV.SENTRY_DSN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ function isBotRequest(userAgent: string | null) {
return isbotModule.isbot(userAgent);
}

// isbot < 3.8.0
if ('default' in isbotModule && typeof isbotModule.default === 'function') {
return isbotModule.default(userAgent);
}

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function ErrorBoundary() {
}

function App() {
const { ENV } = useLoaderData();
const { ENV } = useLoaderData() as { ENV: { SENTRY_DSN: string } };

return (
<html lang="en">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const loader: LoaderFunction = async ({ params: { id } }) => {
};

export default function LoaderError() {
const data = useLoaderData();
const data = useLoaderData() as { test?: string };

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix vite:build",
"build": "remix vite:build && pnpm typecheck",
"dev": "node ./server.mjs",
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"start": "cross-env NODE_ENV=production node ./server.mjs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { waitForError } from '@sentry-internal/test-utils';

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

await page.goto('/loader-error');
Expand Down
Loading
Loading