Skip to content

meta(changelog): Update changelog for 9.14.0 #16115

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 20 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8682df0
test(node): Increase timeout of anr tests (#16076)
mydea Apr 16, 2025
056a865
ci: Fix issue labelling with multiple labels (#16072)
mydea Apr 16, 2025
9f9a6c1
ci: Finally really fix issue labels (#16080)
mydea Apr 16, 2025
e066a4b
ci: Finally fix it for real real (#16082)
mydea Apr 16, 2025
4e82018
Merge pull request #16083 from getsentry/master
github-actions[bot] Apr 16, 2025
3bc1923
ci: Fix additional label (#16085)
mydea Apr 16, 2025
f1b8291
build(deps): Bump astro from 4.16.1 to 4.16.18 in /dev-packages/e2e-t…
dependabot[bot] Apr 16, 2025
87e5f8b
test(nuxt): Add tests for trace baggage (#16046)
s1gr1d Apr 17, 2025
263eeee
feat: Add Supabase Integration (#15719)
onurtemizkan Apr 17, 2025
5cd3457
feat(nuxt): Log when adding HTML trace meta tags (#16044)
s1gr1d Apr 17, 2025
e4f4597
feat(react-router): Trace propagation (#16070)
chargome Apr 22, 2025
1d10238
ref(node): Log when incoming request bodies are being captured (#16104)
mydea Apr 22, 2025
8026b85
build(deps): Bump fastify from 5.0.0 to 5.3.2 in /dev-packages/e2e-te…
dependabot[bot] Apr 22, 2025
447e62e
feat(deps): Bump @prisma/instrumentation from 6.5.0 to 6.6.0 (#16102)
dependabot[bot] Apr 22, 2025
88ba26b
fix(node): Make body capturing more robust (#16105)
lforst Apr 23, 2025
a3d0224
ref(core): Remove internal `utils-hoist` re-export (#16114)
mydea Apr 23, 2025
7438608
feat(nextjs): Improve server component data (#15996)
lforst Apr 23, 2025
20ce849
feat(nestjs): Gracefully handle RPC scenarios in `SentryGlobalFilter`…
chargome Apr 23, 2025
749eda9
meta(changelog): Update changelog for 9.14.0
chargome Apr 23, 2025
7c0365c
rm ref pr
chargome Apr 23, 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
48 changes: 37 additions & 11 deletions .github/workflows/issue-package-label.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
"@sentry.bun": {
"label": "Bun"
},
"@sentry.cloudflare - hono": {
"@sentry.cloudflare.-.hono": {
"label": "Hono"
},
"@sentry.cloudflare": {
Expand All @@ -68,20 +68,20 @@ jobs:
"@sentry.nextjs": {
"label": "Next.js"
},
"@sentry.node - express": {
"@sentry.node.-.express": {
"label": "Express"
},
"@sentry.node - fastify": {
"@sentry.node.-.fastify": {
"label": "Fastify"
},
"@sentry.node - koa": {
"@sentry.node.-.koa": {
"label": "Koa"
},
"@sentry.node - hapi": {
"label": "Hapi
"@sentry.node.-.hapi": {
"label": "Hapi"
},
"@sentry.node - connect": {
"label": "Connect
"@sentry.node.-.connect": {
"label": "Connect"
},
"@sentry.node": {
"label": "Node.js"
Expand All @@ -90,7 +90,7 @@ jobs:
"label": "Nuxt"
},
"@sentry.react-router": {
"label": "React Router Framework "
"label": "React Router Framework"
},
"@sentry.react": {
"label": "React"
Expand Down Expand Up @@ -120,10 +120,10 @@ jobs:
"label": "WASM"
},
"Sentry.Browser.Loader": {
"label": "Browser\nLoader Script"
"label": "Browser"
},
"Sentry.Browser.CDN.bundle": {
"label": "Browser\nCDN Bundle"
"label": "Browser"
}
}
export_to: output
Expand All @@ -134,3 +134,29 @@ jobs:
uses: actions-ecosystem/action-add-labels@v1
with:
labels: ${{ steps.packageLabel.outputs.label }}

- name: Map additional to issue label
# https://github.com/kanga333/variable-mapper
uses: kanga333/variable-mapper@v0.3.0
id: additionalLabel
if: steps.packageName.outputs.match != ''
with:
key: '${{ steps.packageName.outputs.group1 }}'
# Note: Since this is handled as a regex, and JSON parse wrangles slashes /, we just use `.` instead
map: |
{
"Sentry.Browser.Loader": {
"label": "Loader Script"
},
"Sentry.Browser.CDN.bundle": {
"label": "CDN Bundle"
}
}
export_to: output

- name: Add additional label if applicable
# Note: We only add the label if the issue is still open
if: steps.additionalLabel.outputs.label != ''
uses: actions-ecosystem/action-add-labels@v1
with:
labels: ${{ steps.additionalLabel.outputs.label }}
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@

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

## 9.14.0

### Important Changes

- **feat: Add Supabase Integration ([#15719](https://github.com/getsentry/sentry-javascript/pull/15719))**

This PR adds Supabase integration to `@sentry/core`, allowing automatic instrumentation of Supabase client operations (database queries and authentication) for performance monitoring and error tracking.

- **feat(nestjs): Gracefully handle RPC scenarios in `SentryGlobalFilter` ([#16066](https://github.com/getsentry/sentry-javascript/pull/16066))**

This PR adds better RPC exception handling to `@sentry/nestjs`, preventing application crashes while still capturing errors and warning users when a dedicated filter is needed. The implementation gracefully handles the 'rpc' context type in `SentryGlobalFilter` to improve reliability in hybrid applications.

- **feat(react-router): Trace propagation ([#16070](https://github.com/getsentry/sentry-javascript/pull/16070))**

This PR adds trace propagation to `@sentry/react-router` by providing utilities to inject trace meta tags into HTML headers and offering a pre-built Sentry-instrumented request handler, improving distributed tracing capabilities across page loads.

### Other Changes

- feat(deps): Bump @prisma/instrumentation from 6.5.0 to 6.6.0 ([#16102](https://github.com/getsentry/sentry-javascript/pull/16102))
- feat(nextjs): Improve server component data ([#15996](https://github.com/getsentry/sentry-javascript/pull/15996))
- feat(nuxt): Log when adding HTML trace meta tags ([#16044](https://github.com/getsentry/sentry-javascript/pull/16044))
- fix(node): Make body capturing more robust ([#16105](https://github.com/getsentry/sentry-javascript/pull/16105))
- ref(node): Log when incoming request bodies are being captured ([#16104](https://github.com/getsentry/sentry-javascript/pull/16104))

## 9.13.0

### Important Changes
Expand Down
1 change: 1 addition & 0 deletions dev-packages/browser-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@playwright/test": "~1.50.0",
"@sentry-internal/rrweb": "2.34.0",
"@sentry/browser": "9.13.0",
"@supabase/supabase-js": "2.49.3",
"axios": "1.8.2",
"babel-loader": "^8.2.2",
"fflate": "0.8.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as Sentry from '@sentry/browser';

import { createClient } from '@supabase/supabase-js';
window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key');

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate authentication operations
async function performAuthenticationOperations() {
await supabaseClient.auth.signInWithPassword({
email: 'test@example.com',
password: 'test-password',
});

await supabaseClient.auth.signOut();
}

performAuthenticationOperations();
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';

import { sentryTest } from '../../../../utils/fixtures';
import {
getFirstSentryEnvelopeRequest,
getMultipleSentryEnvelopeRequests,
shouldSkipTracingTest,
} from '../../../../utils/helpers';

async function mockSupabaseAuthRoutesSuccess(page: Page) {
await page.route('**/auth/v1/token?grant_type=password**', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
token_type: 'bearer',
expires_in: 3600,
}),
headers: {
'Content-Type': 'application/json',
},
});
});

await page.route('**/auth/v1/logout**', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
message: 'Logged out',
}),
headers: {
'Content-Type': 'application/json',
},
});
});
}

async function mockSupabaseAuthRoutesFailure(page: Page) {
await page.route('**/auth/v1/token?grant_type=password**', route => {
return route.fulfill({
status: 400,
body: JSON.stringify({
error_description: 'Invalid email or password',
error: 'invalid_grant',
}),
headers: {
'Content-Type': 'application/json',
},
});
});

await page.route('**/auth/v1/logout**', route => {
return route.fulfill({
status: 400,
body: JSON.stringify({
error_description: 'Invalid refresh token',
error: 'invalid_grant',
}),
headers: {
'Content-Type': 'application/json',
},
});
});
}


const bundle = process.env.PW_BUNDLE || '';
// We only want to run this in non-CDN bundle mode
if (bundle.startsWith('bundle')) {
sentryTest.skip();
}

sentryTest('should capture Supabase authentication spans', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
return;
}

await mockSupabaseAuthRoutesSuccess(page);

const url = await getLocalTestUrl({ testDir: __dirname });

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

expect(supabaseSpans).toHaveLength(2);
expect(supabaseSpans![0]).toMatchObject({
description: 'signInWithPassword',
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.origin': 'auto.db.supabase',
}),
});

expect(supabaseSpans![1]).toMatchObject({
description: 'signOut',
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.origin': 'auto.db.supabase',
}),
});
});

sentryTest('should capture Supabase authentication errors', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
return;
}

await mockSupabaseAuthRoutesFailure(page);

const url = await getLocalTestUrl({ testDir: __dirname });

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

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

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

expect(supabaseSpans).toHaveLength(2);
expect(supabaseSpans![0]).toMatchObject({
description: 'signInWithPassword',
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.origin': 'auto.db.supabase',
}),
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as Sentry from '@sentry/browser';

import { createClient } from '@supabase/supabase-js';
window.Sentry = Sentry;

const supabaseClient = createClient('https://test.supabase.co', 'test-key');

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.supabaseIntegration({ supabaseClient })],
tracesSampleRate: 1.0,
});

// Simulate database operations
async function performDatabaseOperations() {
try {
await supabaseClient.from('todos').insert([{ title: 'Test Todo' }]);

await supabaseClient.from('todos').select('*');

// Trigger an error to capture the breadcrumbs
throw new Error('Test Error');
} catch (error) {
Sentry.captureException(error);
}
}

performDatabaseOperations();
Loading
Loading