Skip to content

feat: add modern bundle flavor with native-API shims#1643

Open
tbrannam wants to merge 1 commit into
auth0:masterfrom
tbrannam:feat/modern-bundle-flavor
Open

feat: add modern bundle flavor with native-API shims#1643
tbrannam wants to merge 1 commit into
auth0:masterfrom
tbrannam:feat/modern-bundle-flavor

Conversation

@tbrannam

@tbrannam tbrannam commented May 14, 2026

Copy link
Copy Markdown

feat: add modern bundle flavor with native-API shims

Related

#1495 - cites a bundle saving opportunity that was rejected due to relying on new dependencies, this PR replicates the savings by instead leveraging Browser native functionality instead of introducing supply chain risks from other npm packages.

Summary

Adds a parallel auth0-js/modern entry point that's ~42% smaller raw / ~44% smaller gzipped than the default bundle, by aliasing five legacy npm dependencies to small shims over native browser APIs. The default bundle is byte-identical to before — existing consumers see no change.

Motivation

auth0-js v10 still ships an IE9-compatible bundle by default, which carries:

  • qs (the largest single dep)
  • superagent + its transitive deps (cookiejar, debug, fast-safe-stringify, mime, etc.)
  • es6-promise (Promise polyfill, unconditionally bundled via idtoken-verifier)
  • unfetch (XHR-based fetch fallback inside idtoken-verifier)
  • base64-js (used directly and via idtoken-verifier)

For consumers targeting evergreen browsers (no IE), every byte of these dependencies is replaceable with a thin wrapper around native APIs. This PR adds that option without removing the IE9-compatible path.

Approach

  1. Add a modern ESM build target (dist/auth0.modern.min.esm.js) to rollup.config.mjs. Target floor: Chrome 70 / Firefox 65 / Safari 12 / Edge 79 (late 2018).
  2. Introduce a MODERN_ALIASES map in rollup.config.mjs. The alias plugin redirects five package imports to local shim files when the modern build is active.
  3. Redirect idtoken-verifier to its unbundled source (node_modules/idtoken-verifier/src/index.js) — its published artifact pre-bundles the polyfills, so without this redirect the aliases for es6-promise/unfetch/base64-js have nothing to intercept.
  4. Expose via package.json exports as ./modern. Default import auth0 from 'auth0-js' continues to resolve to the legacy bundle.

Bundle sizes

Bundle Raw Gzipped
auth0.min.esm.js (legacy ESM, IE9+) 173,652 B 51.3 KB
auth0.modern.min.esm.js 100,807 B 28.9 KB
**Modern vs legacy(reduction) ** −42.0% −44.0%

Per-shim savings

Measured by toggling each MODERN_ALIASES entry off one at a time and rebuilding:

Shim Raw saved Gz saved Replaces
qs 39,723 B 12,285 B URLSearchParams
superagent 19,886 B 5,910 B fetch + AbortController
es6-promise 6,365 B 2,155 B native Promise (no-op shim)
base64-js 1,127 B 438 B btoa/atob + Uint8Array
unfetch 847 B 322 B native fetch
Total 67,948 B 21,110 B

The remaining ~5 KB of the legacy-vs-modern delta is from dropped Babel runtime helpers (_classCallCheck, _typeof, _createForOfIteratorHelper, etc.) that the IE9 build needs but the modern target doesn't.

Consumer API

Direct import

import auth0 from 'auth0-js/modern';

Same API surface — WebAuth, Authentication, Management exposed identically.

Bundler alias (transparent swap)

webpack:

resolve: { alias: { 'auth0-js$': 'auth0-js/modern' } }

Vite:

resolve: { alias: { 'auth0-js': 'auth0-js/modern' } }

esbuild:

build({ alias: { 'auth0-js': 'auth0-js/modern' } })

Browser support floor (modern bundle only)

Browser Minimum Released
Chrome / Chromium-Edge 70 Oct 2018
Firefox 65 Jan 2019
Safari 12 Sep 2018

Any earlier browser (including any IE) must use the default import path.

Behavior parity

The shims aim to be drop-in replacements. Where the original libraries had subtle behaviors, the shims preserve them:

  • qs.parse — duplicate flat keys and bracket-nested keys both collect into arrays under the base key. Matches qs's "non-string result fails strict-equality" property so parseHash's state check is unaffected. Parity test against real qs for duplicate-key shape.
  • superagent.retry(n) — retries on the same STATUS_CODES set superagent uses (408, 413, 429, 500, 502, 503, 504, 521, 522, 524) plus network errors. Immediate retry (no backoff, matching superagent).
  • superagent.abort() — silently suppresses the callback, matching superagent's xhr.abort() readystatechange path. The shim uses AbortController (fetch's only cancel mechanism).
  • base64-js.toByteArray — accepts URL-safe base64 (-/_) and missing trailing padding. Required because idtoken-verifier's decodeToHEX passes URL-safe JWT signature bytes directly through.
  • Response content-type matchingapplication/json and application/...+json vendor types both auto-parse as JSON (matches superagent's regex).

Validation

  • npm run smoke:modern passes — headless Chrome drives the full OIDC implicit (id_token) flow against the bundled oidc-provider, exercises every shim in the call path (qs for hash parsing, fetch shim for JWKS, base64-js shim for JWT verification), and asserts parseHash returns a valid id_token.

How to test locally

npm install
npm test                                # 698 unit tests, including 40 shim parity tests
npm run lint
npm run build
npm run test:es-check:es5               # legacy UMD ES5 compliance
npm run test:es-check:es2015:module     # legacy ESM ES2015 compliance
npm run test:es-check:es2019:modern     # modern ESM ES2019 compliance
npm run smoke:modern                    # headless Chrome end-to-end OIDC flow against the modern bundle

The smoke:modern script requires a chromedriver matching your installed Chrome major version (npm i -D chromedriver@<major> if you see "session not created").

Test plan checklist

  • Unit tests pass (npm test)
  • Lint clean (npm run lint)
  • All four es-check guards pass
  • npm run build produces all expected artifacts
  • npm run smoke:modern passes end-to-end against the local oidc-provider
  • (Reviewer) verify the modern bundle behaves correctly against a real Auth0 tenant
  • (Reviewer) confirm dist/auth0.modern.min.esm.js is appropriate to publish

@tbrannam tbrannam requested a review from a team as a code owner May 14, 2026 20:41
Adds a parallel `auth0-js/modern` ESM entry point that's ~42% smaller The default bundle is byte-identical to before; existing consumers see no change.

Exposed via package.json `exports`:

  import auth0 from 'auth0-js'         → legacy (default, unchanged)
  import auth0 from 'auth0-js/modern'  → modern bundle

Bundler users can transparently swap via alias (`auth0-js$` → `auth0-js/modern`
in webpack, `auth0-js` → `auth0-js/modern` in Vite/esbuild).
@tbrannam tbrannam force-pushed the feat/modern-bundle-flavor branch from aac9d09 to 9cd0ce2 Compare May 14, 2026 20:43
Comment thread src/web-auth/captcha.js
/**
* @typedef {import('../authentication').default} Authentication
*/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is inconsequential - it silences one of the two circular dependency warnings during builds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant