Skip to content

Commit 12ac7ad

Browse files
Shawclaude
andcommitted
fix(cloud/frontend): repair /login render — broken inherits prebundle crashes React tree
The Cloud Tests > playwright job's 4 chronic failures (and the entire broken /login render in dev) trace to a single Vite/rolldown optimizeDeps issue with the `inherits` polyfill. Repro (verified locally with Chrome devtools): - /login mounted briefly, then crashed with TypeError: require_inherits_browser is not a function at .../elliptic-XXX.js - Result: blank black page, "Welcome back" heading and `you@example.com` placeholder never rendered. Root cause: 1. `inherits/inherits.js` (the npm main entry) does try { module.exports = require('util').inherits } catch { module.exports = require('./inherits_browser.js') } Vite's `util` alias is an empty shim, so the catch path runs — but rolldown's CommonJS prebundle for the elliptic graph hoists the consumer call sites *before* `require_inherits_browser`'s wrapper definition, throwing on first crypto access. 2. The same prebundle hoists `require_inherits` reference for nested elliptic submodules before its wrapper is wired up. Fix: - Resolve `inherits` directly to a self-contained `inherits.cjs` browser shim — bypasses the try/catch entirely so the catch path's require chain never enters the prebundle. - Add the entire crypto graph (elliptic + inherits + hash-base + create-hash + create-hmac + browserify-sign + secp256k1) to `optimizeDeps.include` so rolldown bundles them as a single deterministic chunk with wrappers at top-level. Verified: /login now renders the full Steward login section (Welcome back heading, email input, Passkey/Magic Link/Ethereum/ Solana buttons) with zero console errors. This unblocks the 4 chronic Cloud Tests > playwright failures: - auth-routing > login renders without page errors when returnTo is provided - public-pages > /login renders the Steward login page - public-pages > /login Terms link reaches React Router terms - steward-wallet-auth > Ethereum wallet sign-in signs through Steward and redirects to dashboard …and by extension NPM Release, which has been gated on Cloud Tests success. Replaces a dropped TypeScript shim with a CommonJS one because the file is consumed via `require()` from optimized-deps wrappers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a59f026 commit 12ac7ad

3 files changed

Lines changed: 66 additions & 49 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Browser-safe shim for the `inherits` npm package.
2+
//
3+
// The upstream `inherits.js` main entry tries `require('util').inherits`
4+
// first and falls back to `inherits_browser.js` inside a try/catch. Vite
5+
// aliases `util` to an empty shim, so the real path is the fallback —
6+
// but rolldown's optimizeDeps prebundle has trouble wiring that fallback
7+
// through the wrapped CommonJS module. Result: at runtime
8+
// `require_inherits_browser` is undefined and elliptic / hash-base /
9+
// create-hash crash, taking the React tree down with them on /login.
10+
//
11+
// This file is the body of `inherits_browser.js` exposed as a single CJS
12+
// module, so vite resolves to a known-good entry every time.
13+
14+
if (typeof Object.create === "function") {
15+
module.exports = function inherits(ctor, superCtor) {
16+
if (superCtor) {
17+
ctor.super_ = superCtor;
18+
ctor.prototype = Object.create(superCtor.prototype, {
19+
constructor: {
20+
value: ctor,
21+
enumerable: false,
22+
writable: true,
23+
configurable: true,
24+
},
25+
});
26+
}
27+
};
28+
} else {
29+
module.exports = function inherits(ctor, superCtor) {
30+
if (superCtor) {
31+
ctor.super_ = superCtor;
32+
var TempCtor = function () {};
33+
TempCtor.prototype = superCtor.prototype;
34+
ctor.prototype = new TempCtor();
35+
ctor.prototype.constructor = ctor;
36+
}
37+
};
38+
}

cloud/apps/frontend/src/shims/inherits.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

cloud/apps/frontend/vite.config.ts

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,20 +109,37 @@ export default defineConfig(({ mode }) => {
109109
// Avoid scanning the giant transitive graph from packages/lib at
110110
// dev-server boot.
111111
entries: ["src/main.tsx"],
112+
// Force-include the crypto graph so vite/rolldown's optimizer wires
113+
// every CommonJS `require_*` wrapper before any consumer call site.
114+
// Without this, the prebundle for elliptic/hash-base/create-hash
115+
// ends up referencing `require_inherits` before its wrapper is
116+
// defined (a known rolldown CJS hoisting issue), which crashes the
117+
// React tree on /login. Pre-bundling them as a unit forces the
118+
// wrappers into deterministic top-level position in the chunk.
119+
include: [
120+
"elliptic",
121+
"inherits",
122+
"hash-base",
123+
"create-hash",
124+
"create-hmac",
125+
"browserify-sign",
126+
"secp256k1",
127+
],
112128
},
113129
resolve: {
114130
alias: [
115-
// The `inherits` shim's main entry tries `require('util').inherits`
116-
// first, then falls back to `./inherits_browser.js`. Our `util`
117-
// alias is the empty shim, so the require'd module is `{}` and the
118-
// fallback path runs — but rolldown / esbuild's optimizeDeps
119-
// prebundle has trouble wiring that fallback through the wrapped
120-
// CommonJS module. The bundle ends up calling
121-
// `require_inherits_browser` before the wrapper is defined, which
122-
// throws inside elliptic / hash-base / create-hash and crashes the
123-
// React tree on /login. Resolve `inherits` to a self-contained
124-
// browser shim instead.
125-
{ find: /^inherits$/, replacement: r("./src/shims/inherits.ts") },
131+
// The upstream `inherits` package's main entry tries
132+
// `require('util').inherits` first and falls back to
133+
// `inherits_browser.js` inside a try/catch. Vite aliases `util`
134+
// to an empty shim, so the real path is the fallback — but
135+
// rolldown's CommonJS optimizeDeps prebundle ends up referencing
136+
// `require_inherits_browser` before its wrapper is hoisted, which
137+
// throws inside elliptic / hash-base / create-hash and crashes
138+
// the React tree on /login. Resolve `inherits` directly to a
139+
// browser-safe shim so the try/catch never runs at all. Pairs
140+
// with the `optimizeDeps.include` block above which forces the
141+
// crypto graph to bundle as a single deterministic chunk.
142+
{ find: /^inherits$/, replacement: r("./src/shims/inherits.cjs") },
126143
// Real Buffer polyfill — Solana wallet adapters, viem, ethers, base64
127144
// helpers all depend on Buffer. Stubbing it throws at runtime as soon
128145
// as any browser-reachable code path constructs a Buffer.

0 commit comments

Comments
 (0)