Uh oh!
There was an error while loading. Please reload this page.
perf(bundle): Reduce browser CDN bundle size by ~2% (-550 bytes gzipped) - #19833
perf(bundle): Reduce browser CDN bundle size by ~2% (-550 bytes gzipped)#19833HazAT wants to merge 0 commit into
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
size-limit report 📦
|
node-overhead report 🧳Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: No tests included for this feat PR
- Added 21 unit tests for the lazyLoadIntegration bundle name derivation logic covering standard integrations, hyphenated bundles, and AI integrations without Integration suffix.
Or push these changes by commenting:
@cursor push eb3a90c8c6
Preview (eb3a90c8c6)
diff --git a/packages/browser/test/utils/lazyLoadIntegration.test.ts b/packages/browser/test/utils/lazyLoadIntegration.test.ts--- a/packages/browser/test/utils/lazyLoadIntegration.test.ts+++ b/packages/browser/test/utils/lazyLoadIntegration.test.ts@@ -81,4 +81,81 @@
`https://browser.sentry-cdn.com/${SDK_VERSION}/httpclient.min.js`,
);
});
++ describe('bundle name derivation', () => {+ test.each([+ ['replayIntegration', 'replay'],+ ['feedbackIntegration', 'feedback'],+ ['captureConsoleIntegration', 'captureconsole'],+ ['contextLinesIntegration', 'contextlines'],+ ['linkedErrorsIntegration', 'linkederrors'],+ ['dedupeIntegration', 'dedupe'],+ ['extraErrorDataIntegration', 'extraerrordata'],+ ['graphqlClientIntegration', 'graphqlclient'],+ ['httpClientIntegration', 'httpclient'],+ ['reportingObserverIntegration', 'reportingobserver'],+ ['rewriteFramesIntegration', 'rewriteframes'],+ ['browserProfilingIntegration', 'browserprofiling'],+ ['moduleMetadataIntegration', 'modulemetadata'],+ ])('derives correct bundle name for %s', async (integrationName, expectedBundle) => {+ // @ts-expect-error For testing sake+ global.Sentry = {};++ try {+ // @ts-expect-error Dynamic integration name for testing+ await lazyLoadIntegration(integrationName);+ } catch {+ // skip - we just want to verify the script URL+ }++ expect(global.document.querySelector('script')?.src).toEqual(+ `https://browser.sentry-cdn.com/${SDK_VERSION}/${expectedBundle}.min.js`,+ );+ });++ test.each([+ ['replayCanvasIntegration', 'replay-canvas'],+ ['feedbackModalIntegration', 'feedback-modal'],+ ['feedbackScreenshotIntegration', 'feedback-screenshot'],+ ])('derives correct hyphenated bundle name for %s', async (integrationName, expectedBundle) => {+ // @ts-expect-error For testing sake+ global.Sentry = {};++ try {+ // @ts-expect-error Dynamic integration name for testing+ await lazyLoadIntegration(integrationName);+ } catch {+ // skip - we just want to verify the script URL+ }++ expect(global.document.querySelector('script')?.src).toEqual(+ `https://browser.sentry-cdn.com/${SDK_VERSION}/${expectedBundle}.min.js`,+ );+ });++ test.each([+ ['instrumentAnthropicAiClient', 'instrumentanthropicaiclient'],+ ['instrumentOpenAiClient', 'instrumentopenaiclient'],+ ['instrumentGoogleGenAIClient', 'instrumentgooglegenaiclient'],+ ['instrumentLangGraph', 'instrumentlanggraph'],+ ['createLangChainCallbackHandler', 'createlangchaincallbackhandler'],+ ])(+ 'derives correct bundle name for AI integrations without Integration suffix: %s',+ async (integrationName, expectedBundle) => {+ // @ts-expect-error For testing sake+ global.Sentry = {};++ try {+ // @ts-expect-error Dynamic integration name for testing+ await lazyLoadIntegration(integrationName);+ } catch {+ // skip - we just want to verify the script URL+ }++ expect(global.document.querySelector('script')?.src).toEqual(+ `https://browser.sentry-cdn.com/${SDK_VERSION}/${expectedBundle}.min.js`,+ );+ },+ );+ });
});This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Uh oh!
There was an error while loading. Please reload this page.
Bugbot Autofix prepared fixes for both issues found in the latest run.
Or push these changes by commenting: Preview (6862e0c2db)diff --git a/packages/core/src/api.ts b/packages/core/src/api.ts--- a/packages/core/src/api.ts+++ b/packages/core/src/api.ts@@ -53,25 +53,32 @@
}
const endpoint = `${getBaseApiEndpoint(dsn)}embed/error-page/`;
- const params = new URLSearchParams({ dsn: dsnToString(dsn) });+ let encodedOptions = `dsn=${dsnToString(dsn)}`;
for (const key in dialogOptions) {
- if (key === 'dsn' || key === 'onClose') {+ if (key === 'dsn') {
continue;
}
+ if (key === 'onClose') {+ continue;+ }+
if (key === 'user') {
const user = dialogOptions.user;
- if (user?.name) {- params.set('name', user.name);+ if (!user) {+ continue;
}
- if (user?.email) {- params.set('email', user.email);+ if (user.name) {+ encodedOptions += `&name=${encodeURIComponent(user.name)}`;
}
+ if (user.email) {+ encodedOptions += `&email=${encodeURIComponent(user.email)}`;+ }
} else {
- params.set(key, dialogOptions[key] as string);+ encodedOptions += `&${encodeURIComponent(key)}=${encodeURIComponent(dialogOptions[key] as string)}`;
}
}
- return `${endpoint}?${params.toString()}`;+ return `${endpoint}?${encodedOptions}`;
}
diff --git a/packages/core/src/utils/supports.ts b/packages/core/src/utils/supports.ts--- a/packages/core/src/utils/supports.ts+++ b/packages/core/src/utils/supports.ts@@ -108,7 +108,37 @@
return true;
}
- return _isFetchSupported();+ if (!_isFetchSupported()) {+ return false;+ }++ // Fast path to avoid DOM I/O+ // eslint-disable-next-line @typescript-eslint/unbound-method+ if (isNativeFunction(WINDOW.fetch)) {+ return true;+ }++ // window.fetch is implemented, but is polyfilled or already wrapped (e.g: by a chrome extension)+ // so create a "pure" iframe to see if that has native fetch+ let result = false;+ const doc = WINDOW.document;+ // eslint-disable-next-line deprecation/deprecation+ if (doc && typeof (doc.createElement as unknown) === 'function') {+ try {+ const sandbox = doc.createElement('iframe');+ sandbox.hidden = true;+ doc.head.appendChild(sandbox);+ if (sandbox.contentWindow?.fetch) {+ // eslint-disable-next-line @typescript-eslint/unbound-method+ result = isNativeFunction(sandbox.contentWindow.fetch);+ }+ doc.head.removeChild(sandbox);+ } catch (err) {+ DEBUG_BUILD && debug.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', err);+ }+ }++ return result;
}
/**This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard. |
There was a problem hiding this comment.
Nice! Cool to see some improvements, and at the same time very reassuring to see that we're already pretty good at writing size-optimal code (that being said, fully aware that we have some options to reduce more bytes with breaking changes).
Looks like some tests are failing though. Suggestion: Let's split this PR up into smaller chunks. Makes it easier to reason, detect which optimization is responsible for the test fails and also easier to revert individual changes.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Uh oh!
There was an error while loading. Please reload this page.
ef0e9f1 to
9f779f5Compare## Summary Small, safe simplifications across core utilities. Combined saves **~80 bytes gzipped**. ## Changes - **envelope.ts**: Slim `ITEM_TYPE_TO_DATA_CATEGORY_MAP` by removing 7 self-mapping entries (e.g. `session: "session"`). Falls back to the type name itself. - **object.ts**: Replace `getOwnProperties` manual `for...in` + `hasOwnProperty` loop with `Object.fromEntries(Object.entries(obj))`. Use shorthand `value` in `addNonEnumerableProperty`. - **baggage.ts**: Use `.startsWith()` instead of `.match(regex)` for sentry prefix check. - **browser.ts**: Inline `allowedAttrs` array literal directly in the `for...of` loop. - **eventFilters.ts**: Convert verbose `DEFAULT_IGNORE_ERRORS` string literals to shorter regex patterns with equivalent matching behavior (vv().getRestrictions, simulateEvent, solana, _AutofillCallbackHandler). All changes are behavior-preserving. Part of #19833. Co-Authored-By: Claude claude@anthropic.com --------- Co-authored-by: Lukas Stracke <lukas.stracke@sentry.io>
## Summary A collection of small, safe optimizations across the browser package. Combined saves **~60 bytes gzipped**. ## Changes UPDATE (@Lms24): Removed some initial changes, leaving them here for posterity | File | Change | Impact | |------|--------|--------| | ~`helpers.ts` + `stacktrace.ts`~ | ~Rename internal `sentryWrapped` → `sW` in wrap(). Update frame stripping regex to match both names.~ | ~10B gzip~ | | `breadcrumbs.ts` | Remove unused `breadcrumbData` variable from fetch handler | dead code | | `browserapierrors.ts` | Encode `DEFAULT_EVENT_TARGET` as `string.split(",")` instead of array literal | 51B raw | | `globalhandlers.ts` | Remove redundant intermediate variable aliases in `_enhanceEventWithInitialFrame` | cleaner code | | `detectBrowserExtension.ts` | Replace `array.some(startsWith)` with single regex test | ~3B gzip | | `eventbuilder.ts` | Simplify `getErrorPropertyFromObject` to `Object.values().find()` | ~9B gzip | | `lazyLoadIntegration.ts` | Derive CDN bundle filenames from ~integration names~ list of integration names instead of storing duplicate key-value pairs | ~30B gzip | ### lazyLoadIntegration detail The `LazyLoadableIntegrations` object stored 21 key-value pairs where values were mostly derivable from keys (strip `"Integration"`, lowercase). Replaced with: - An array of integration names (encoded as `string.split(",")`) - A derivation function - A 3-entry exceptions map for hyphenated names (`replay-canvas`, `feedback-modal`, `feedback-screenshot`) All changes are behavior-preserving. No public API modifications. Part of #19833. Co-Authored-By: Claude claude@anthropic.com --------- Co-authored-by: Lukas Stracke <lukas.stracke@sentry.io>


Summary
Reduces the base browser CDN bundle (
bundle.min.js) gzipped size by ~2% (~550 bytes gzipped / ~3.4 KB raw).These are safe, behavior-preserving changes found through systematic experimentation (50 iterations across 4 autoresearch sessions). No public API changes, no removed functionality.
Sub-PRs
This PR is split into focused sub-PRs for easier review:
What was NOT changed
unsafe_*terser options only apply to CDN.min.jsbundles (not npm ESM/CJS output)Co-Authored-By: Claude claude@anthropic.com