Skip to content

Commit aba909b

Browse files
authored
Merge pull request #15611 from getsentry/prepare-release/9.5.0
meta(changelog): Update changelog for 9.5.0
2 parents f11f7b1 + 79a1eef commit aba909b

File tree

32 files changed

+1452
-418
lines changed

32 files changed

+1452
-418
lines changed

CHANGELOG.md

+22
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@
1010

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

13+
## 9.5.0
14+
15+
### Important Changes
16+
17+
We found some issues with the new feedback screenshot annotation where screenshots are not being generated properly. Due to this issue, we are reverting the feature.
18+
19+
- Revert "feat(feedback) Allowing annotation via highlighting & masking ([#15484](https://github.com/getsentry/sentry-javascript/pull/15484))" (#15609)
20+
21+
### Other Changes
22+
23+
- Add cloudflare adapter detection and path generation ([#15603](https://github.com/getsentry/sentry-javascript/pull/15603))
24+
- deps(nextjs): Bump rollup to `4.34.9` ([#15589](https://github.com/getsentry/sentry-javascript/pull/15589))
25+
- feat(bun): Automatically add performance integrations ([#15586](https://github.com/getsentry/sentry-javascript/pull/15586))
26+
- feat(replay): Bump rrweb to 2.34.0 ([#15580](https://github.com/getsentry/sentry-javascript/pull/15580))
27+
- fix(browser): Call original function on early return from patched history API ([#15576](https://github.com/getsentry/sentry-javascript/pull/15576))
28+
- fix(nestjs): Copy metadata in custom decorators ([#15598](https://github.com/getsentry/sentry-javascript/pull/15598))
29+
- fix(react-router): Fix config type import ([#15583](https://github.com/getsentry/sentry-javascript/pull/15583))
30+
- fix(remix): Use correct types export for `@sentry/remix/cloudflare` ([#15599](https://github.com/getsentry/sentry-javascript/pull/15599))
31+
- fix(vue): Attach Pinia state only once per event ([#15588](https://github.com/getsentry/sentry-javascript/pull/15588))
32+
33+
Work in this release was contributed by @msurdi-a8c, @namoscato, and @rileyg98. Thank you for your contributions!
34+
1335
## 9.4.0
1436

1537
- feat(core): Add types for logs protocol and envelope ([#15530](https://github.com/getsentry/sentry-javascript/pull/15530))

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"dependencies": {
4242
"@babel/preset-typescript": "^7.16.7",
4343
"@playwright/test": "~1.50.0",
44-
"@sentry-internal/rrweb": "2.33.0",
44+
"@sentry-internal/rrweb": "2.34.0",
4545
"@sentry/browser": "9.4.0",
4646
"axios": "1.7.7",
4747
"babel-loader": "^8.2.2",

packages/browser-utils/src/instrument/history.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function instrumentHistory(): void {
5151
lastHref = to;
5252

5353
if (from === to) {
54-
return;
54+
return originalHistoryFunction.apply(this, args);
5555
}
5656

5757
const handlerData = { from, to } satisfies HandlerDataHistory;

packages/bun/src/sdk.ts

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as os from 'node:os';
22
import {
33
applySdkMetadata,
44
functionToStringIntegration,
5+
hasSpansEnabled,
56
inboundFiltersIntegration,
67
linkedErrorsIntegration,
78
requestDataIntegration,
@@ -11,6 +12,7 @@ import type { NodeClient } from '@sentry/node';
1112
import {
1213
consoleIntegration,
1314
contextLinesIntegration,
15+
getAutoPerformanceIntegrations,
1416
httpIntegration,
1517
init as initNode,
1618
modulesIntegration,
@@ -48,6 +50,7 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
4850
modulesIntegration(),
4951
// Bun Specific
5052
bunServerIntegration(),
53+
...(hasSpansEnabled(_options) ? getAutoPerformanceIntegrations() : []),
5154
];
5255
}
5356

packages/bun/test/init.test.ts

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { type Integration } from '@sentry/core';
2+
import * as sentryNode from '@sentry/node';
3+
import type { Mock } from 'bun:test';
4+
import { afterEach, beforeEach, describe, it, spyOn, mock, expect } from 'bun:test';
5+
import { getClient, init } from '../src';
6+
7+
const PUBLIC_DSN = 'https://username@domain/123';
8+
9+
class MockIntegration implements Integration {
10+
public name: string;
11+
public setupOnce: Mock<() => void>;
12+
public constructor(name: string) {
13+
this.name = name;
14+
this.setupOnce = mock(() => undefined);
15+
}
16+
}
17+
18+
describe('init()', () => {
19+
let mockAutoPerformanceIntegrations: Mock<() => Integration[]>;
20+
21+
beforeEach(() => {
22+
// @ts-expect-error weird
23+
mockAutoPerformanceIntegrations = spyOn(sentryNode, 'getAutoPerformanceIntegrations');
24+
});
25+
26+
afterEach(() => {
27+
mockAutoPerformanceIntegrations.mockRestore();
28+
});
29+
30+
describe('integrations', () => {
31+
it("doesn't install default integrations if told not to", () => {
32+
init({ dsn: PUBLIC_DSN, defaultIntegrations: false });
33+
34+
const client = getClient();
35+
36+
expect(client?.getOptions().integrations).toEqual([]);
37+
38+
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0);
39+
});
40+
41+
it('installs merged default integrations, with overrides provided through options', () => {
42+
const mockDefaultIntegrations = [
43+
new MockIntegration('Some mock integration 2.1'),
44+
new MockIntegration('Some mock integration 2.2'),
45+
];
46+
47+
const mockIntegrations = [
48+
new MockIntegration('Some mock integration 2.1'),
49+
new MockIntegration('Some mock integration 2.3'),
50+
];
51+
52+
init({ dsn: PUBLIC_DSN, integrations: mockIntegrations, defaultIntegrations: mockDefaultIntegrations });
53+
54+
expect(mockDefaultIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(0);
55+
expect(mockDefaultIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1);
56+
expect(mockIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1);
57+
expect(mockIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1);
58+
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0);
59+
});
60+
61+
it('installs integrations returned from a callback function', () => {
62+
const mockDefaultIntegrations = [
63+
new MockIntegration('Some mock integration 3.1'),
64+
new MockIntegration('Some mock integration 3.2'),
65+
];
66+
67+
const newIntegration = new MockIntegration('Some mock integration 3.3');
68+
69+
init({
70+
dsn: PUBLIC_DSN,
71+
defaultIntegrations: mockDefaultIntegrations,
72+
integrations: integrations => {
73+
const newIntegrations = [...integrations];
74+
newIntegrations[1] = newIntegration;
75+
return newIntegrations;
76+
},
77+
});
78+
79+
expect(mockDefaultIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1);
80+
expect(mockDefaultIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(0);
81+
expect(newIntegration.setupOnce).toHaveBeenCalledTimes(1);
82+
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0);
83+
});
84+
85+
it('installs performance default instrumentations if tracing is enabled', () => {
86+
const autoPerformanceIntegrations = [new MockIntegration('Performance integration')];
87+
mockAutoPerformanceIntegrations.mockImplementation(() => autoPerformanceIntegrations);
88+
89+
const mockIntegrations = [
90+
new MockIntegration('Some mock integration 4.1'),
91+
new MockIntegration('Some mock integration 4.3'),
92+
];
93+
94+
init({
95+
dsn: PUBLIC_DSN,
96+
integrations: mockIntegrations,
97+
tracesSampleRate: 1,
98+
});
99+
100+
expect(mockIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1);
101+
expect(mockIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1);
102+
expect(autoPerformanceIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1);
103+
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(1);
104+
105+
const integrations = getClient()?.getOptions().integrations;
106+
expect(integrations).toBeArray();
107+
expect(integrations?.map(({ name }) => name)).toContain('Performance integration');
108+
expect(integrations?.map(({ name }) => name)).toContain('Some mock integration 4.1');
109+
expect(integrations?.map(({ name }) => name)).toContain('Some mock integration 4.3');
110+
});
111+
});
112+
});

packages/core/src/types-hoist/feedback/config.ts

+9
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ export interface FeedbackGeneralConfiguration {
5757
name: string;
5858
};
5959

60+
/**
61+
* _experiments allows users to enable experimental or internal features.
62+
* We don't consider such features as part of the public API and hence we don't guarantee semver for them.
63+
* Experimental features can be added, changed or removed at any time.
64+
*
65+
* Default: undefined
66+
*/
67+
_experiments: Partial<{ annotations: boolean }>;
68+
6069
/**
6170
* Set an object that will be merged sent as tags data with the event.
6271
*/

packages/feedback/src/constants/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export const NAME_PLACEHOLDER = 'Your Name';
2020
export const NAME_LABEL = 'Name';
2121
export const SUCCESS_MESSAGE_TEXT = 'Thank you for your report!';
2222
export const IS_REQUIRED_LABEL = '(required)';
23-
export const ADD_SCREENSHOT_LABEL = 'Capture Screenshot';
24-
export const REMOVE_SCREENSHOT_LABEL = 'Remove Screenshot';
23+
export const ADD_SCREENSHOT_LABEL = 'Add a screenshot';
24+
export const REMOVE_SCREENSHOT_LABEL = 'Remove screenshot';
2525

2626
export const FEEDBACK_WIDGET_SOURCE = 'widget';
2727
export const FEEDBACK_API_SOURCE = 'api';

packages/feedback/src/core/integration.ts

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const buildFeedbackIntegration = ({
8484
email: 'email',
8585
name: 'username',
8686
},
87+
_experiments = {},
8788
tags,
8889
styleNonce,
8990
scriptNonce,
@@ -158,6 +159,8 @@ export const buildFeedbackIntegration = ({
158159
onSubmitError,
159160
onSubmitSuccess,
160161
onFormSubmitted,
162+
163+
_experiments,
161164
};
162165

163166
let _shadow: ShadowRoot | null = null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { VNode, h as hType } from 'preact';
2+
import type * as Hooks from 'preact/hooks';
3+
import { DOCUMENT } from '../../constants';
4+
5+
interface FactoryParams {
6+
h: typeof hType;
7+
}
8+
9+
export default function AnnotationsFactory({
10+
h, // eslint-disable-line @typescript-eslint/no-unused-vars
11+
}: FactoryParams) {
12+
return function Annotations({
13+
action,
14+
imageBuffer,
15+
annotatingRef,
16+
}: {
17+
action: 'crop' | 'annotate' | '';
18+
imageBuffer: HTMLCanvasElement;
19+
annotatingRef: Hooks.Ref<HTMLCanvasElement>;
20+
}): VNode {
21+
const onAnnotateStart = (): void => {
22+
if (action !== 'annotate') {
23+
return;
24+
}
25+
26+
const handleMouseMove = (moveEvent: MouseEvent): void => {
27+
const annotateCanvas = annotatingRef.current;
28+
if (annotateCanvas) {
29+
const rect = annotateCanvas.getBoundingClientRect();
30+
const x = moveEvent.clientX - rect.x;
31+
const y = moveEvent.clientY - rect.y;
32+
33+
const ctx = annotateCanvas.getContext('2d');
34+
if (ctx) {
35+
ctx.lineTo(x, y);
36+
ctx.stroke();
37+
ctx.beginPath();
38+
ctx.moveTo(x, y);
39+
}
40+
}
41+
};
42+
43+
const handleMouseUp = (): void => {
44+
const ctx = annotatingRef.current?.getContext('2d');
45+
if (ctx) {
46+
ctx.beginPath();
47+
}
48+
49+
// Add your apply annotation logic here
50+
applyAnnotation();
51+
52+
DOCUMENT.removeEventListener('mousemove', handleMouseMove);
53+
DOCUMENT.removeEventListener('mouseup', handleMouseUp);
54+
};
55+
56+
DOCUMENT.addEventListener('mousemove', handleMouseMove);
57+
DOCUMENT.addEventListener('mouseup', handleMouseUp);
58+
};
59+
60+
const applyAnnotation = (): void => {
61+
// Logic to apply the annotation
62+
const imageCtx = imageBuffer.getContext('2d');
63+
const annotateCanvas = annotatingRef.current;
64+
if (imageCtx && annotateCanvas) {
65+
imageCtx.drawImage(
66+
annotateCanvas,
67+
0,
68+
0,
69+
annotateCanvas.width,
70+
annotateCanvas.height,
71+
0,
72+
0,
73+
imageBuffer.width,
74+
imageBuffer.height,
75+
);
76+
77+
const annotateCtx = annotateCanvas.getContext('2d');
78+
if (annotateCtx) {
79+
annotateCtx.clearRect(0, 0, annotateCanvas.width, annotateCanvas.height);
80+
}
81+
}
82+
};
83+
return (
84+
<canvas
85+
class={`editor__annotation ${action === 'annotate' ? 'editor__annotation--active' : ''}`}
86+
onMouseDown={onAnnotateStart}
87+
ref={annotatingRef}
88+
></canvas>
89+
);
90+
};
91+
}

0 commit comments

Comments
 (0)