Skip to content

Commit 11d9c33

Browse files
authored
Merge branch 'develop' into develop
2 parents 5796a45 + a67d3ca commit 11d9c33

File tree

14 files changed

+715
-173
lines changed

14 files changed

+715
-173
lines changed

CHANGELOG.md

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,7 @@
1010

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

13-
Work in this release was contributed by @tjhiggins, @GrizliK1988, @davidturissini, @nwalters512, @aloisklink, @arturovt, @benjick, @maximepvrt, @mstrokin, and @kunal-511. Thank you for your contributions!
14-
15-
- **feat(solidstart)!: Default to `--import` setup and add `autoInjectServerSentry` ([#14862](https://github.com/getsentry/sentry-javascript/pull/14862))**
16-
17-
To enable the SolidStart SDK, wrap your SolidStart Config with `withSentry`. The `sentrySolidStartVite` plugin is now automatically
18-
added by `withSentry` and you can pass the Sentry build-time options like this:
19-
20-
```js
21-
import { defineConfig } from '@solidjs/start/config';
22-
import { withSentry } from '@sentry/solidstart';
23-
24-
export default defineConfig(
25-
withSentry(
26-
{
27-
/* Your SolidStart config options... */
28-
},
29-
{
30-
// Options for setting up source maps
31-
org: process.env.SENTRY_ORG,
32-
project: process.env.SENTRY_PROJECT,
33-
authToken: process.env.SENTRY_AUTH_TOKEN,
34-
},
35-
),
36-
);
37-
```
38-
39-
With the `withSentry` wrapper, the Sentry server config should not be added to the `public` directory anymore.
40-
Add the Sentry server config in `src/instrument.server.ts`. Then, the server config will be placed inside the server build output as `instrument.server.mjs`.
41-
42-
Now, there are two options to set up the SDK:
43-
44-
1. **(recommended)** Provide an `--import` CLI flag to the start command like this (path depends on your server setup):
45-
`node --import ./.output/server/instrument.server.mjs .output/server/index.mjs`
46-
2. Add `autoInjectServerSentry: 'top-level-import'` and the Sentry config will be imported at the top of the server entry (comes with tracing limitations)
47-
```js
48-
withSentry(
49-
{
50-
/* Your SolidStart config options... */
51-
},
52-
{
53-
// Optional: Install Sentry with a top-level import
54-
autoInjectServerSentry: 'top-level-import',
55-
},
56-
);
57-
```
13+
Work in this release was contributed by @tjhiggins, @GrizliK1988, @davidturissini, @nwalters512, @aloisklink, @arturovt, @benjick, @maximepvrt, @mstrokin, @kunal-511, @jahands, and @nathankleyn. Thank you for your contributions!
5814

5915
## 9.0.0-alpha.0
6016

docs/migration/v8-to-v9.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,22 @@ Sentry.init({
234234
- The `addNormalizedRequestDataToEvent` method has been removed. Use `httpRequestToRequestData` instead and put the resulting object directly on `event.request`.
235235
- A `sampleRand` field on `PropagationContext` is now required. This is relevant if you used `scope.setPropagationContext(...)`
236236
237+
### `@sentry/solidstart`
238+
239+
- The `sentrySolidStartVite` plugin is no longer exported. Instead, wrap the SolidStart config with `withSentry` and
240+
provide Sentry options as the second parameter.
241+
242+
```
243+
// app.config.ts
244+
import { defineConfig } from '@solidjs/start/config';
245+
import { withSentry } from '@sentry/solidstart';
246+
247+
export default defineConfig(withSentry(
248+
{ /* SolidStart config */ },
249+
{ /* Sentry build-time config (like project and org) */ })
250+
);
251+
```
252+
237253
#### Other/Internal Changes
238254
239255
The following changes are unlikely to affect users of the SDK. They are listed here only for completion sake, and to alert users that may be relying on internal behavior.

docs/pr-reviews.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22

33
Make sure to open PRs against `develop` branch.
44

5-
For feedback in PRs, we use the [LOGAF scale](https://blog.danlew.net/2020/04/15/the-logaf-scale/) to specify how
6-
important a comment is:
7-
8-
- `l`: low - nitpick. You may address this comment, but you don't have to.
9-
- `m`: medium - normal comment. Worth addressing and fixing.
10-
- `h`: high - Very important. We must not merge this PR without addressing this issue.
5+
For feedback in PRs, we use the [LOGAF scale](https://develop.sentry.dev/engineering-practices/code-review/#logaf-scale) to specify how
6+
important a comment is.
117

128
You only need one approval from a maintainer to be able to merge. For some PRs, asking specific or multiple people for
139
review might be adequate. You can either assign SDK team members directly (e.g. if you have some people in mind who are

packages/bun/src/integrations/bunserver.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,18 @@ export function instrumentBunServe(): void {
4747
Bun.serve = new Proxy(Bun.serve, {
4848
apply(serveTarget, serveThisArg, serveArgs: Parameters<typeof Bun.serve>) {
4949
instrumentBunServeOptions(serveArgs[0]);
50-
return serveTarget.apply(serveThisArg, serveArgs);
50+
const server: ReturnType<typeof Bun.serve> = serveTarget.apply(serveThisArg, serveArgs);
51+
52+
// A Bun server can be reloaded, re-wrap any fetch function passed to it
53+
// We can't use a Proxy for this as Bun does `instanceof` checks internally that fail if we
54+
// wrap the Server instance.
55+
const originalReload: typeof server.reload = server.reload.bind(server);
56+
server.reload = (serveOptions: Parameters<typeof Bun.serve>[0]) => {
57+
instrumentBunServeOptions(serveOptions);
58+
return originalReload(serveOptions);
59+
};
60+
61+
return server;
5162
},
5263
});
5364
}
Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,87 @@
1-
import { beforeAll, beforeEach, describe, expect, test } from 'bun:test';
1+
import { afterEach, beforeAll, beforeEach, describe, expect, test } from 'bun:test';
2+
import type { Span } from '@sentry/core';
23
import { getDynamicSamplingContextFromSpan, setCurrentClient, spanIsSampled, spanToJSON } from '@sentry/core';
34

45
import { BunClient } from '../../src/client';
56
import { instrumentBunServe } from '../../src/integrations/bunserver';
67
import { getDefaultBunClientOptions } from '../helpers';
78

8-
// Fun fact: Bun = 2 21 14 :)
9-
const DEFAULT_PORT = 22114;
10-
119
describe('Bun Serve Integration', () => {
1210
let client: BunClient;
11+
// Fun fact: Bun = 2 21 14 :)
12+
let port: number = 22114;
1313

1414
beforeAll(() => {
1515
instrumentBunServe();
1616
});
1717

1818
beforeEach(() => {
19-
const options = getDefaultBunClientOptions({ tracesSampleRate: 1, debug: true });
19+
const options = getDefaultBunClientOptions({ tracesSampleRate: 1 });
2020
client = new BunClient(options);
2121
setCurrentClient(client);
2222
client.init();
2323
});
2424

25+
afterEach(() => {
26+
// Don't reuse the port; Bun server stops lazily so tests may accidentally hit a server still closing from a
27+
// previous test
28+
port += 1;
29+
});
30+
2531
test('generates a transaction around a request', async () => {
32+
let generatedSpan: Span | undefined;
33+
2634
client.on('spanEnd', span => {
27-
expect(spanToJSON(span).status).toBe('ok');
28-
expect(spanToJSON(span).data?.['http.response.status_code']).toEqual(200);
29-
expect(spanToJSON(span).op).toEqual('http.server');
30-
expect(spanToJSON(span).description).toEqual('GET /');
35+
generatedSpan = span;
3136
});
3237

3338
const server = Bun.serve({
3439
async fetch(_req) {
3540
return new Response('Bun!');
3641
},
37-
port: DEFAULT_PORT,
42+
port,
3843
});
44+
await fetch(`http://localhost:${port}/`);
45+
server.stop();
3946

40-
await fetch('http://localhost:22114/');
47+
if (!generatedSpan) {
48+
throw 'No span was generated in the test';
49+
}
4150

42-
server.stop();
51+
expect(spanToJSON(generatedSpan).status).toBe('ok');
52+
expect(spanToJSON(generatedSpan).data?.['http.response.status_code']).toEqual(200);
53+
expect(spanToJSON(generatedSpan).op).toEqual('http.server');
54+
expect(spanToJSON(generatedSpan).description).toEqual('GET /');
4355
});
4456

4557
test('generates a post transaction', async () => {
58+
let generatedSpan: Span | undefined;
59+
4660
client.on('spanEnd', span => {
47-
expect(spanToJSON(span).status).toBe('ok');
48-
expect(spanToJSON(span).data?.['http.response.status_code']).toEqual(200);
49-
expect(spanToJSON(span).op).toEqual('http.server');
50-
expect(spanToJSON(span).description).toEqual('POST /');
61+
generatedSpan = span;
5162
});
5263

5364
const server = Bun.serve({
5465
async fetch(_req) {
5566
return new Response('Bun!');
5667
},
57-
port: DEFAULT_PORT,
68+
port,
5869
});
5970

60-
await fetch('http://localhost:22114/', {
71+
await fetch(`http://localhost:${port}/`, {
6172
method: 'POST',
6273
});
6374

6475
server.stop();
76+
77+
if (!generatedSpan) {
78+
throw 'No span was generated in the test';
79+
}
80+
81+
expect(spanToJSON(generatedSpan).status).toBe('ok');
82+
expect(spanToJSON(generatedSpan).data?.['http.response.status_code']).toEqual(200);
83+
expect(spanToJSON(generatedSpan).op).toEqual('http.server');
84+
expect(spanToJSON(generatedSpan).description).toEqual('POST /');
6585
});
6686

6787
test('continues a trace', async () => {
@@ -70,55 +90,93 @@ describe('Bun Serve Integration', () => {
7090
const PARENT_SAMPLED = '1';
7191

7292
const SENTRY_TRACE_HEADER = `${TRACE_ID}-${PARENT_SPAN_ID}-${PARENT_SAMPLED}`;
73-
const SENTRY_BAGGAGE_HEADER = 'sentry-version=1.0,sentry-environment=production';
93+
const SENTRY_BAGGAGE_HEADER = 'sentry-version=1.0,sentry-sample_rand=0.42,sentry-environment=production';
7494

75-
client.on('spanEnd', span => {
76-
expect(span.spanContext().traceId).toBe(TRACE_ID);
77-
expect(spanToJSON(span).parent_span_id).toBe(PARENT_SPAN_ID);
78-
expect(spanIsSampled(span)).toBe(true);
79-
expect(span.isRecording()).toBe(false);
95+
let generatedSpan: Span | undefined;
8096

81-
expect(getDynamicSamplingContextFromSpan(span)).toStrictEqual({
82-
version: '1.0',
83-
environment: 'production',
84-
});
97+
client.on('spanEnd', span => {
98+
generatedSpan = span;
8599
});
86100

87101
const server = Bun.serve({
88102
async fetch(_req) {
89103
return new Response('Bun!');
90104
},
91-
port: DEFAULT_PORT,
105+
port,
92106
});
93107

94-
await fetch('http://localhost:22114/', {
108+
await fetch(`http://localhost:${port}/`, {
95109
headers: { 'sentry-trace': SENTRY_TRACE_HEADER, baggage: SENTRY_BAGGAGE_HEADER },
96110
});
97111

98112
server.stop();
113+
114+
if (!generatedSpan) {
115+
throw 'No span was generated in the test';
116+
}
117+
118+
expect(generatedSpan.spanContext().traceId).toBe(TRACE_ID);
119+
expect(spanToJSON(generatedSpan).parent_span_id).toBe(PARENT_SPAN_ID);
120+
expect(spanIsSampled(generatedSpan)).toBe(true);
121+
expect(generatedSpan.isRecording()).toBe(false);
122+
123+
expect(getDynamicSamplingContextFromSpan(generatedSpan)).toStrictEqual({
124+
version: '1.0',
125+
sample_rand: '0.42',
126+
environment: 'production',
127+
});
99128
});
100129

101130
test('does not create transactions for OPTIONS or HEAD requests', async () => {
102-
client.on('spanEnd', () => {
103-
// This will never run, but we want to make sure it doesn't run.
104-
expect(false).toEqual(true);
131+
let generatedSpan: Span | undefined;
132+
133+
client.on('spanEnd', span => {
134+
generatedSpan = span;
105135
});
106136

107137
const server = Bun.serve({
108138
async fetch(_req) {
109139
return new Response('Bun!');
110140
},
111-
port: DEFAULT_PORT,
141+
port,
112142
});
113143

114-
await fetch('http://localhost:22114/', {
144+
await fetch(`http://localhost:${port}/`, {
115145
method: 'OPTIONS',
116146
});
117147

118-
await fetch('http://localhost:22114/', {
148+
await fetch(`http://localhost:${port}/`, {
119149
method: 'HEAD',
120150
});
121151

122152
server.stop();
153+
154+
expect(generatedSpan).toBeUndefined();
155+
});
156+
157+
test('intruments the server again if it is reloaded', async () => {
158+
let serverWasInstrumented = false;
159+
client.on('spanEnd', () => {
160+
serverWasInstrumented = true;
161+
});
162+
163+
const server = Bun.serve({
164+
async fetch(_req) {
165+
return new Response('Bun!');
166+
},
167+
port,
168+
});
169+
170+
server.reload({
171+
async fetch(_req) {
172+
return new Response('Reloaded Bun!');
173+
},
174+
});
175+
176+
await fetch(`http://localhost:${port}/`);
177+
178+
server.stop();
179+
180+
expect(serverWasInstrumented).toBeTrue();
123181
});
124182
});

packages/bun/test/sdk.test.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
import { expect, test } from 'bun:test';
1+
import { describe, expect, test } from 'bun:test';
22

33
import { init } from '../src/index';
44

5-
test("calling init shouldn't fail", () => {
6-
init({
5+
describe('Bun SDK', () => {
6+
const initOptions = {
77
dsn: 'https://[email protected]/0000000',
8+
tracesSampleRate: 1,
9+
};
10+
11+
test("calling init shouldn't fail", () => {
12+
expect(() => {
13+
init(initOptions);
14+
}).not.toThrow();
815
});
9-
expect(true).toBe(true);
10-
});
1116

12-
test('should return client from init', () => {
13-
expect(init({})).not.toBeUndefined();
17+
test('should return client from init', () => {
18+
expect(init(initOptions)).not.toBeUndefined();
19+
});
1420
});

packages/core/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,8 @@
6161
"volta": {
6262
"extends": "../../package.json"
6363
},
64-
"sideEffects": false
64+
"sideEffects": false,
65+
"devDependencies": {
66+
"zod": "^3.24.1"
67+
}
6568
}

0 commit comments

Comments
 (0)