Skip to content

Commit 29f6031

Browse files
authored
Merge pull request #10330 from getsentry/prepare-release/7.96.0
meta(changelog): Update changelog for 7.96.0
2 parents 72bde8d + bf84267 commit 29f6031

File tree

49 files changed

+861
-892
lines changed

Some content is hidden

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

49 files changed

+861
-892
lines changed

CHANGELOG.md

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

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

7+
## 7.96.0
8+
9+
### Important Changes
10+
11+
#### Deprecations
12+
13+
This release includes some deprecations for integrations in `@sentry/browser` and frontend framework SDKs
14+
(`@sentry/react`, `@sentry/vue`, etc.). Please take a look at our
15+
[migration guide](https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md) for more details.
16+
17+
- feat(browser): Export functional integrations & deprecate classes (#10267)
18+
19+
#### Web Vitals Fix for LCP and CLS
20+
21+
This release fixes an issue with the Web Vitals integration where LCP and CLS were not being captured correctly,
22+
increasing capture rate by 10-30% for some apps. LCP and CLS capturing issues were introduced with version `7.75.0`.
23+
24+
- fix(tracing): Ensure web vitals are correctly stopped/captured (#10323)
25+
26+
### Other Changes
27+
28+
- feat(react): Add `stripBasename` option for React Router 6. (#10314)
29+
- fix(node): Fix `node-cron` types and add test (#10315)
30+
- fix(node): Fix downleveled types entry point (#10321)
31+
- fix(node): LocalVariables integration should use setupOnce (#10307)
32+
- fix(replay): Fix type for options of replayIntegration (#10325)
33+
34+
Work in this release contributed by @Shubhdeep12. Thank you for your contribution!
35+
736
## 7.95.0
837

938
### Important Changes

MIGRATION.md

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,31 @@ integrations from the `Integrations.XXX` hash, is deprecated in favor of using t
3434

3535
The following list shows how integrations should be migrated:
3636

37-
| Old | New | Packages |
38-
| ------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
39-
| `new InboundFilters()` | `inboundFiltersIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
40-
| `new FunctionToString()` | `functionToStringIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
41-
| `new LinkedErrors()` | `linkedErrorsIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
42-
| `new ModuleMetadata()` | `moduleMetadataIntegration()` | `@sentry/core`, `@sentry/browser` |
43-
| `new RequestData()` | `requestDataIntegration()` | `@sentry/core`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
44-
| `new Wasm() ` | `wasmIntegration()` | `@sentry/wasm` |
45-
| `new Replay()` | `replayIntegration()` | `@sentry/browser` |
46-
| `new ReplayCanvas()` | `replayCanvasIntegration()` | `@sentry/browser` |
47-
| `new Feedback()` | `feedbackIntegration()` | `@sentry/browser` |
37+
| Old | New | Packages |
38+
| ------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------- |
39+
| `new InboundFilters()` | `inboundFiltersIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
40+
| `new FunctionToString()` | `functionToStringIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
41+
| `new LinkedErrors()` | `linkedErrorsIntegration()` | `@sentry/core`, `@sentry/browser`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
42+
| `new ModuleMetadata()` | `moduleMetadataIntegration()` | `@sentry/core`, `@sentry/browser` |
43+
| `new RequestData()` | `requestDataIntegration()` | `@sentry/core`, `@sentry/node`, `@sentry/deno`, `@sentry/bun`, `@sentry/vercel-edge` |
44+
| `new Wasm() ` | `wasmIntegration()` | `@sentry/wasm` |
45+
| `new Replay()` | `replayIntegration()` | `@sentry/browser` |
46+
| `new ReplayCanvas()` | `replayCanvasIntegration()` | `@sentry/browser` |
47+
| `new Feedback()` | `feedbackIntegration()` | `@sentry/browser` |
48+
| `new CaptureConsole()` | `captureConsoleIntegration()` | `@sentry/integrations` |
49+
| `new Debug()` | `debugIntegration()` | `@sentry/integrations` |
50+
| `new Dedupe()` | `dedupeIntegration()` | `@sentry/browser`, `@sentry/integrations`, `@sentry/deno` |
51+
| `new ExtraErrorData()` | `extraErrorDataIntegration()` | `@sentry/integrations` |
52+
| `new ReportingObserver()` | `reportingObserverIntegration()` | `@sentry/integrations` |
53+
| `new RewriteFrames()` | `rewriteFramesIntegration()` | `@sentry/integrations` |
54+
| `new SessionTiming()` | `sessionTimingIntegration()` | `@sentry/integrations` |
55+
| `new HttpClient()` | `httpClientIntegration()` | `@sentry/integrations` |
56+
| `new ContextLines()` | `contextLinesIntegration()` | `@sentry/browser` |
57+
| `new Breadcrumbs()` | `breadcrumbsIntegration()` | `@sentry/browser`, `@sentry/deno` |
58+
| `new GlobalHandlers()` | `globalHandlersIntegration()` | `@sentry/browser` |
59+
| `new HttpContext()` | `httpContextIntegration()` | `@sentry/browser` |
60+
| `new TryCatch()` | `browserApiErrorsIntegration()` | `@sentry/browser`, `@sentry/deno` |
61+
| `new VueIntegration()` | `vueIntegration()` | `@sentry/vue` |
4862

4963
## Deprecate `hub.bindClient()` and `makeMain()`
5064

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { addLcpInstrumentationHandler } from '@sentry-internal/tracing';
2+
3+
addLcpInstrumentationHandler(({ metric }) => {
4+
const entry = metric.entries[metric.entries.length - 1];
5+
window._LCP = entry.size;
6+
});
7+
8+
addLcpInstrumentationHandler(({ metric }) => {
9+
const entry = metric.entries[metric.entries.length - 1];
10+
window._LCP2 = entry.size;
11+
});
12+
13+
window.ADD_HANDLER = () => {
14+
addLcpInstrumentationHandler(({ metric }) => {
15+
const entry = metric.entries[metric.entries.length - 1];
16+
window._LCP3 = entry.size;
17+
});
18+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<div id="content"></div>
8+
<img src="https://example.com/path/to/image.png" />
9+
<button type="button">Test button</button>
10+
</body>
11+
</html>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Route } from '@playwright/test';
2+
import { expect } from '@playwright/test';
3+
import type { Event } from '@sentry/types';
4+
5+
import { sentryTest } from '../../../../utils/fixtures';
6+
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';
7+
8+
const bundle = process.env.PW_BUNDLE || '';
9+
10+
sentryTest(
11+
'should capture metrics for LCP instrumentation handlers',
12+
async ({ browserName, getLocalTestPath, page }) => {
13+
// This uses a utility that is not exported in CDN bundles
14+
if (shouldSkipTracingTest() || browserName !== 'chromium' || bundle.startsWith('bundle')) {
15+
sentryTest.skip();
16+
}
17+
18+
await page.route('**/path/to/image.png', (route: Route) =>
19+
route.fulfill({ path: `${__dirname}/assets/sentry-logo-600x179.png` }),
20+
);
21+
22+
const url = await getLocalTestPath({ testDir: __dirname });
23+
const [eventData] = await Promise.all([
24+
getFirstSentryEnvelopeRequest<Event>(page),
25+
page.goto(url),
26+
page.click('button'),
27+
]);
28+
29+
expect(eventData.measurements).toBeDefined();
30+
expect(eventData.measurements?.lcp?.value).toBeDefined();
31+
32+
expect(eventData.tags?.['lcp.element']).toBe('body > img');
33+
expect(eventData.tags?.['lcp.size']).toBe(107400);
34+
expect(eventData.tags?.['lcp.url']).toBe('https://example.com/path/to/image.png');
35+
36+
const lcp = await (await page.waitForFunction('window._LCP')).jsonValue();
37+
const lcp2 = await (await page.waitForFunction('window._LCP2')).jsonValue();
38+
const lcp3 = await page.evaluate('window._LCP3');
39+
40+
expect(lcp).toEqual(107400);
41+
expect(lcp2).toEqual(107400);
42+
// this has not been triggered yet
43+
expect(lcp3).toEqual(undefined);
44+
45+
// Adding a handler after LCP is completed still triggers the handler
46+
await page.evaluate('window.ADD_HANDLER()');
47+
const lcp3_2 = await (await page.waitForFunction('window._LCP3')).jsonValue();
48+
49+
expect(lcp3_2).toEqual(107400);
50+
},
51+
);

dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/fixtures/ReplayRecordingData.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ export const ReplayRecordingData = [
215215
description: 'largest-contentful-paint',
216216
startTimestamp: expect.any(Number),
217217
endTimestamp: expect.any(Number),
218-
data: { value: expect.any(Number), size: expect.any(Number) },
218+
data: {
219+
value: expect.any(Number),
220+
size: expect.any(Number),
221+
nodeId: 16,
222+
},
219223
},
220224
},
221225
},

dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ export const ReplayRecordingData = [
215215
description: 'largest-contentful-paint',
216216
startTimestamp: expect.any(Number),
217217
endTimestamp: expect.any(Number),
218-
data: { value: expect.any(Number), size: expect.any(Number) },
218+
data: {
219+
value: expect.any(Number),
220+
size: expect.any(Number),
221+
nodeId: 16,
222+
},
219223
},
220224
},
221225
},

dev-packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ export const ReplayRecordingData = [
215215
description: 'largest-contentful-paint',
216216
startTimestamp: expect.any(Number),
217217
endTimestamp: expect.any(Number),
218-
data: { value: expect.any(Number), size: expect.any(Number) },
218+
data: {
219+
value: expect.any(Number),
220+
size: expect.any(Number),
221+
nodeId: 16,
222+
},
219223
},
220224
},
221225
},

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
"apollo-server": "^3.11.1",
3737
"axios": "^0.27.2",
3838
"cors": "^2.8.5",
39+
"cron": "^3.1.6",
3940
"express": "^4.17.3",
4041
"graphql": "^16.3.0",
4142
"http-terminator": "^3.2.0",
4243
"mongodb": "^3.7.3",
44+
"mongoose": "^5.13.22",
4345
"mongodb-memory-server-global": "^7.6.3",
4446
"mysql": "^2.18.1",
4547
"nock": "^13.1.0",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/node';
2+
import { CronJob } from 'cron';
3+
4+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
5+
const CronJobWithCheckIn = Sentry.cron.instrumentCron(CronJob, 'my-cron-job');
6+
7+
setTimeout(() => {
8+
process.exit(0);
9+
}, 1_000);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';
2+
3+
afterAll(() => {
4+
cleanupChildProcesses();
5+
});
6+
7+
test('node-cron types should match', done => {
8+
createRunner(__dirname, 'scenario.ts').ensureNoErrorOutput().start(done);
9+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const Sentry = require('@sentry/node-experimental');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
});
10+
11+
// Stop the process from exiting before the transaction is sent
12+
setInterval(() => {}, 1000);
13+
14+
async function run() {
15+
const { ApolloServer, gql } = require('apollo-server');
16+
17+
await Sentry.startSpan(
18+
{
19+
name: 'Test Transaction',
20+
op: 'transaction',
21+
},
22+
async span => {
23+
const typeDefs = gql`type Query { hello: String }`;
24+
25+
const resolvers = {
26+
Query: {
27+
hello: () => {
28+
return 'Hello world!';
29+
},
30+
},
31+
};
32+
33+
const server = new ApolloServer({
34+
typeDefs,
35+
resolvers,
36+
});
37+
38+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
39+
await server.executeOperation({
40+
query: '{hello}',
41+
});
42+
43+
setTimeout(() => {
44+
span.end();
45+
server.stop();
46+
}, 500);
47+
},
48+
);
49+
}
50+
51+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
52+
run();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { conditionalTest } from '../../../utils';
2+
import { createRunner } from '../../../utils/runner';
3+
4+
conditionalTest({ min: 14 })('GraphQL/Apollo Tests', () => {
5+
const EXPECTED_TRANSACTION = {
6+
transaction: 'Test Transaction',
7+
spans: expect.arrayContaining([
8+
expect.objectContaining({
9+
data: {
10+
'graphql.operation.type': 'query',
11+
'graphql.source': '{hello}',
12+
'otel.kind': 'INTERNAL',
13+
'sentry.origin': 'auto.graphql.otel.graphql',
14+
},
15+
description: 'query',
16+
status: 'ok',
17+
origin: 'auto.graphql.otel.graphql',
18+
}),
19+
expect.objectContaining({
20+
data: {
21+
'graphql.field.name': 'hello',
22+
'graphql.field.path': 'hello',
23+
'graphql.field.type': 'String',
24+
'graphql.source': 'hello',
25+
'otel.kind': 'INTERNAL',
26+
'sentry.origin': 'manual',
27+
},
28+
description: 'graphql.resolve',
29+
status: 'ok',
30+
origin: 'manual',
31+
}),
32+
]),
33+
};
34+
35+
test('CJS - should instrument GraphQL queries used from Apollo Server.', done => {
36+
createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done);
37+
});
38+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node-experimental');
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
debug: true,
8+
tracesSampleRate: 1.0,
9+
transport: loggingTransport,
10+
});
11+
12+
// Stop the process from exiting before the transaction is sent
13+
setInterval(() => {}, 1000);
14+
15+
// Must be required after Sentry is initialized
16+
const mongoose = require('mongoose');
17+
18+
async function run() {
19+
await mongoose.connect(process.env.MONGO_URL || '');
20+
21+
const Schema = mongoose.Schema;
22+
23+
const BlogPostSchema = new Schema({
24+
title: String,
25+
body: String,
26+
date: Date,
27+
});
28+
29+
const BlogPost = mongoose.model('BlogPost', BlogPostSchema);
30+
31+
await Sentry.startSpan(
32+
{
33+
name: 'Test Transaction',
34+
op: 'transaction',
35+
},
36+
async () => {
37+
const post = new BlogPost();
38+
post.title = 'Test';
39+
post.body = 'Test body';
40+
post.date = new Date();
41+
42+
await post.save();
43+
44+
await BlogPost.findOne({});
45+
},
46+
);
47+
}
48+
49+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
50+
run();

0 commit comments

Comments
 (0)