Skip to content

Commit 1e742be

Browse files
authored
Merge branch 'develop' into sig/client-reports-fix
2 parents db0b856 + ca02322 commit 1e742be

39 files changed

Lines changed: 1410 additions & 284 deletions

File tree

.size-limit.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = [
88
path: 'packages/browser/build/npm/esm/prod/index.js',
99
import: createImport('init'),
1010
gzip: true,
11-
limit: '25 KB',
11+
limit: '25.5 KB',
1212
},
1313
{
1414
name: '@sentry/browser - with treeshaking flags',
@@ -148,7 +148,7 @@ module.exports = [
148148
import: createImport('init', 'ErrorBoundary', 'reactRouterV6BrowserTracingIntegration'),
149149
ignore: ['react/jsx-runtime'],
150150
gzip: true,
151-
limit: '44 KB',
151+
limit: '44.5 KB',
152152
},
153153
// Vue SDK (ESM)
154154
{
@@ -171,20 +171,20 @@ module.exports = [
171171
path: 'packages/svelte/build/esm/index.js',
172172
import: createImport('init'),
173173
gzip: true,
174-
limit: '25 KB',
174+
limit: '25.5 KB',
175175
},
176176
// Browser CDN bundles
177177
{
178178
name: 'CDN Bundle',
179179
path: createCDNPath('bundle.min.js'),
180180
gzip: true,
181-
limit: '27.5 KB',
181+
limit: '28 KB',
182182
},
183183
{
184184
name: 'CDN Bundle (incl. Tracing)',
185185
path: createCDNPath('bundle.tracing.min.js'),
186186
gzip: true,
187-
limit: '42.5 KB',
187+
limit: '43 KB',
188188
},
189189
{
190190
name: 'CDN Bundle (incl. Tracing, Replay)',
@@ -234,7 +234,7 @@ module.exports = [
234234
import: createImport('init'),
235235
ignore: ['next/router', 'next/constants'],
236236
gzip: true,
237-
limit: '46.5 KB',
237+
limit: '47 KB',
238238
},
239239
// SvelteKit SDK (ESM)
240240
{
@@ -243,7 +243,7 @@ module.exports = [
243243
import: createImport('init'),
244244
ignore: ['$app/stores'],
245245
gzip: true,
246-
limit: '42.5 KB',
246+
limit: '43 KB',
247247
},
248248
// Node-Core SDK (ESM)
249249
{
@@ -261,7 +261,7 @@ module.exports = [
261261
import: createImport('init'),
262262
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
263263
gzip: true,
264-
limit: '162.5 KB',
264+
limit: '163 KB',
265265
},
266266
{
267267
name: '@sentry/node - without tracing',

CHANGELOG.md

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

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

7+
- **feat(tanstackstart-react): Add `wrapMiddlewaresWithSentry` for manual middleware instrumentation**
8+
9+
You can now wrap your middlewares using `wrapMiddlewaresWithSentry`, allowing you to trace middleware execution in your TanStack Start application.
10+
11+
```ts
12+
import { createMiddleware } from '@tanstack/react-start';
13+
import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';
14+
15+
const loggingMiddleware = createMiddleware({ type: 'function' }).server(async ({ next }) => {
16+
console.log('Request started');
17+
return next();
18+
});
19+
20+
export const [wrappedLoggingMiddleware] = wrapMiddlewaresWithSentry({ loggingMiddleware });
21+
```
22+
723
## 10.33.0
824

925
### Important Changes
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
enhanceFetchErrorMessages: false,
8+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
2+
3+
// Network error (e.g. ad-blocked, offline, page does not exist, ...)
4+
window.networkError = () => {
5+
fetch('http://sentry-test-external.io/does-not-exist');
6+
};
7+
8+
window.networkErrorSubdomain = () => {
9+
fetch('http://subdomain.sentry-test-external.io/does-not-exist');
10+
};
11+
12+
window.networkErrorWithPort = () => {
13+
fetch('http://sentry-test-external.io:3000/does-not-exist');
14+
};
15+
16+
// Invalid header also produces TypeError
17+
window.invalidHeaderName = () => {
18+
fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } });
19+
};
20+
21+
// Invalid header value also produces TypeError
22+
window.invalidHeaderValue = () => {
23+
fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] });
24+
};
25+
26+
// Invalid URL scheme
27+
window.invalidUrlScheme = () => {
28+
fetch('blub://sentry-test-external.io/invalid-scheme');
29+
};
30+
31+
// URL includes credentials
32+
window.credentialsInUrl = () => {
33+
fetch('https://user:password@sentry-test-external.io/credentials-in-url');
34+
};
35+
36+
// Invalid mode
37+
window.invalidMode = () => {
38+
fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' });
39+
};
40+
41+
// Invalid request method
42+
window.invalidMethod = () => {
43+
fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' });
44+
};
45+
46+
// No-cors mode with cors-required method
47+
window.noCorsMethod = () => {
48+
fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' });
49+
};
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { expect } from '@playwright/test';
2+
import { sentryTest } from '../../../utils/fixtures';
3+
import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers';
4+
5+
sentryTest(
6+
'enhanceFetchErrorMessages: false: enhances error for Sentry while preserving original',
7+
async ({ getLocalTestUrl, page, browserName }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
const reqPromise = waitForErrorRequest(page);
10+
const pageErrorPromise = new Promise<string>(resolve => {
11+
page.on('pageerror', error => {
12+
resolve(error.message);
13+
});
14+
});
15+
16+
await page.goto(url);
17+
await page.evaluate('networkError()');
18+
19+
const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
20+
const eventData = envelopeRequestParser(req);
21+
const originalErrorMap: Record<string, string> = {
22+
chromium: 'Failed to fetch',
23+
webkit: 'Load failed',
24+
firefox: 'NetworkError when attempting to fetch resource.',
25+
};
26+
27+
const originalError = originalErrorMap[browserName];
28+
29+
expect(pageErrorMessage).toContain(originalError);
30+
expect(pageErrorMessage).not.toContain('sentry-test-external.io');
31+
32+
expect(eventData.exception?.values).toHaveLength(1);
33+
expect(eventData.exception?.values?.[0]).toMatchObject({
34+
type: 'TypeError',
35+
value: originalError,
36+
mechanism: {
37+
handled: false,
38+
type: 'auto.browser.global_handlers.onunhandledrejection',
39+
},
40+
});
41+
},
42+
);
43+
44+
sentryTest(
45+
'enhanceFetchErrorMessages: false: enhances subdomain errors',
46+
async ({ getLocalTestUrl, page, browserName }) => {
47+
const url = await getLocalTestUrl({ testDir: __dirname });
48+
const reqPromise = waitForErrorRequest(page);
49+
const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));
50+
51+
await page.goto(url);
52+
await page.evaluate('networkErrorSubdomain()');
53+
54+
const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
55+
const eventData = envelopeRequestParser(req);
56+
57+
const originalErrorMap: Record<string, string> = {
58+
chromium: 'Failed to fetch',
59+
webkit: 'Load failed',
60+
firefox: 'NetworkError when attempting to fetch resource.',
61+
};
62+
63+
const originalError = originalErrorMap[browserName];
64+
65+
expect(pageErrorMessage).toContain(originalError);
66+
expect(pageErrorMessage).not.toContain('subdomain.sentry-test-external.io');
67+
expect(eventData.exception?.values).toHaveLength(1);
68+
expect(eventData.exception?.values?.[0]).toMatchObject({
69+
type: 'TypeError',
70+
value: originalError,
71+
mechanism: {
72+
handled: false,
73+
type: 'auto.browser.global_handlers.onunhandledrejection',
74+
},
75+
});
76+
},
77+
);
78+
79+
sentryTest(
80+
'enhanceFetchErrorMessages: false: includes port in hostname',
81+
async ({ getLocalTestUrl, page, browserName }) => {
82+
const url = await getLocalTestUrl({ testDir: __dirname });
83+
const reqPromise = waitForErrorRequest(page);
84+
85+
const pageErrorPromise = new Promise<string>(resolve => page.on('pageerror', error => resolve(error.message)));
86+
87+
await page.goto(url);
88+
await page.evaluate('networkErrorWithPort()');
89+
90+
const [req, pageErrorMessage] = await Promise.all([reqPromise, pageErrorPromise]);
91+
const eventData = envelopeRequestParser(req);
92+
93+
const originalErrorMap: Record<string, string> = {
94+
chromium: 'Failed to fetch',
95+
webkit: 'Load failed',
96+
firefox: 'NetworkError when attempting to fetch resource.',
97+
};
98+
99+
const originalError = originalErrorMap[browserName];
100+
101+
expect(pageErrorMessage).toContain(originalError);
102+
expect(pageErrorMessage).not.toContain('sentry-test-external.io:3000');
103+
expect(eventData.exception?.values).toHaveLength(1);
104+
expect(eventData.exception?.values?.[0]).toMatchObject({
105+
type: 'TypeError',
106+
value: originalError,
107+
mechanism: {
108+
handled: false,
109+
type: 'auto.browser.global_handlers.onunhandledrejection',
110+
},
111+
});
112+
},
113+
);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
enhanceFetchErrorMessages: 'report-only',
8+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
2+
3+
// Network error (e.g. ad-blocked, offline, page does not exist, ...)
4+
window.networkError = () => {
5+
fetch('http://sentry-test-external.io/does-not-exist');
6+
};
7+
8+
window.networkErrorSubdomain = () => {
9+
fetch('http://subdomain.sentry-test-external.io/does-not-exist');
10+
};
11+
12+
window.networkErrorWithPort = () => {
13+
fetch('http://sentry-test-external.io:3000/does-not-exist');
14+
};
15+
16+
// Invalid header also produces TypeError
17+
window.invalidHeaderName = () => {
18+
fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } });
19+
};
20+
21+
// Invalid header value also produces TypeError
22+
window.invalidHeaderValue = () => {
23+
fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] });
24+
};
25+
26+
// Invalid URL scheme
27+
window.invalidUrlScheme = () => {
28+
fetch('blub://sentry-test-external.io/invalid-scheme');
29+
};
30+
31+
// URL includes credentials
32+
window.credentialsInUrl = () => {
33+
fetch('https://user:password@sentry-test-external.io/credentials-in-url');
34+
};
35+
36+
// Invalid mode
37+
window.invalidMode = () => {
38+
fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' });
39+
};
40+
41+
// Invalid request method
42+
window.invalidMethod = () => {
43+
fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' });
44+
};
45+
46+
// No-cors mode with cors-required method
47+
window.noCorsMethod = () => {
48+
fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' });
49+
};

0 commit comments

Comments
 (0)