← Context/ plans
Fix Env Aliasing That Drops PUBLIC_ Vars in the Developers Menu
Assigning import.meta.env to a variable defeats rsbuild's static replacement, so two production URLs quietly resolve to localhost.
Why care?
PUBLIC_ID_BASE=https://id.didi.sh is set on the shell Railway service and correctly baked
into the production bundle everywhere else. In one component it silently resolves to
http://localhost:4000 anyway.
Small blast radius — the Developers menu only — but the shape of the bug is the dangerous part: setting the Railway variable does nothing, and nothing warns you. Anyone debugging this by checking the service variables will conclude it's configured correctly, because it is.
Evidence
shell/src/DevelopersMenu.svelte:32-34:
const env = (import.meta as { env?: Record<string, string> }).env ?? {};
const DESIGN_PORTAL = env.PUBLIC_DESIGN_PORTAL_URL || 'http://localhost:3020';
const ID_BASE = env.PUBLIC_ID_BASE || 'http://localhost:4000';What rsbuild emitted into the deployed bundle:
r = { MODE:"production", DEV:!1, PROD:!0, SSR:!1, BASE_URL:"/", ASSET_PREFIX:"" }
o = r.PUBLIC_DESIGN_PORTAL_URL || "http://localhost:3020"
n = r.PUBLIC_ID_BASE || "http://localhost:4000"No PUBLIC_* keys survive. Assigning import.meta.env to a variable makes rsbuild
substitute the base env object wholesale, and the per-variable static replacement never
happens — so every PUBLIC_* read off that alias falls through to its default.
Contrast packages/workspace/src/ws-url.ts:29, which reads the member directly in a single
expression and inlined correctly:
function ee(){ let e = "wss://ws.augment.didi.sh/ws"; return typeof e=="string" && e.length>0 ? e : Q }Same repo, same build, opposite outcomes — the only difference is the alias.
There is an irony worth preserving: the comment directly above the broken lines warns about a
related env-var footgun (empty string vs undefined, and why ?? alone is insufficient).
The author was thinking about exactly this class of bug and was bitten by an adjacent one.
The plan
Read each member directly in
DevelopersMenu.svelte— no intermediateenvobject:TSconst DESIGN_PORTAL = (import.meta as { env?: Record<string, string> }).env?.PUBLIC_DESIGN_PORTAL_URL || 'http://localhost:3020'; const ID_BASE = (import.meta as { env?: Record<string, string> }).env?.PUBLIC_ID_BASE || 'http://localhost:4000';Sweep for the same shape repo-wide. Any
= import.meta.envassignment, destructure, or spread is suspect:BASHgrep -rnE '(const|let|var)\s+\w+\s*=\s*\(?import\.meta' --include='*.ts' --include='*.svelte' apps shell packages grep -rnE 'import\.meta[^.]*\.env\s*[;,)}]' --include='*.ts' --include='*.svelte' apps shell packagesDecide
PUBLIC_DESIGN_PORTAL_URL. It is read but set nowhere, and no design-system service is deployed. Either deploy thedocs-portalapp and set the variable, or drop the read and let the menu omit the entry in production. Do not leave a live link tolocalhost:3020in a production menu.Add a reminder. This is exactly the shape
context-v/reminders/exists for — a short, sharp correction born from a mistake the toolchain invites. One paragraph: readimport.meta.env.PUBLIC_Xdirectly; never alias the env object.Rebuild and redeploy
shell.PUBLIC_*is baked at build time.
Verification
After redeploy,
grep -c 'localhost:4000' index.*.json the served bundle is 0 for the Developers-menu pathThe menu's id-service link points at
https://id.didi.shThe repo-wide sweep returns no remaining aliased reads
A reminder exists in
context-v/reminders/
Risks and notes
Low urgency, low risk — this is polish, not a broken surface. Sequence it behind the watchdog and the stale-sources correctness bug.
Verify against the emitted bundle, not the source. The whole defect is that correct- looking source compiles wrong; only the built output proves the fix.
Check whether remotes share the pattern before assuming shell-only. Each remote is built independently, so the same alias in a remote would fail the same way.
References
shell/src/DevelopersMenu.svelte— lines 32-34packages/workspace/src/ws-url.ts— the correct pattern, same reposhell/rsbuild.config.ts— the remotes block, and its comment on the adjacent footgunCommit
61c0e2c— the prior fix that centralised WS endpoint resolution