fix: rename let app in firebaseConfig.js to stop shadowing window.app in inline event handlers#4
Open
DDancingDeath wants to merge 1 commit into
Open
fix: rename let app in firebaseConfig.js to stop shadowing window.app in inline event handlers#4DDancingDeath wants to merge 1 commit into
DDancingDeath wants to merge 1 commit into
Conversation
…pp in inline event handlers Found via Playwright interaction-driver in the staging clone repo (DDancingDeath/AadhatManagementApp-staging). Full RCA: STG-WALK-5 in that repo's docs/REVIEW_ISSUES.md. ## The bug firebaseConfig.js declared `let app = firebase.initializeApp(...)` at the top level of a classic <script>. In that context, `let app` creates a binding in the **global lexical environment** that lives "above" `window` in the scope chain inline event handlers see. So `onclick="app.X()"` in any HTML template resolves `app` to the **Firebase App instance** (which has no `.X` method) — even though `window.app.X` is defined and works fine from console / module code. The shadow is invisible from DevTools (`window.app` looks correct) so the bug is silent: every inline `onclick="app.foo()"` button throws `Cannot read properties of undefined (reading 'foo')` and does nothing. In the staging repo this took out the entire AI Assistant feature (every quick-question button + the Send button). Once the AI Assistant lands in prod (separate work), the same shadow would break it instantly. ## The fix 1. Rename `let app` -> `let firebaseApp` in firebaseConfig.js (4 lines touched). 16-line warning comment added above the declaration so future maintainers don't accidentally rename it back. 2. New `www/js/__tests__/firebase-app-shadow.test.js` Jest test that FAILS if any future commit reintroduces `let|const|var app` as a top-level binding in firebaseConfig.js. The test also asserts the Firebase init result is bound to a name other than `app`. ## What does NOT change - No HTML template was modified. All existing inline handlers in prod already use `window.app.nav.X()` form (see navigation.html), so this rename is a pure no-op for the running app — nothing was resolving against the shadowed binding. - No public API. `window.app` continues to be set by main.js exactly as before. The `auth`, `db`, `firebase` exports of firebaseConfig.js are unchanged. - No build, deploy, or service worker change. ## Testing - `npx jest`: 105/105 passing (was 103; +2 new regression tests) - Manual: opened the deployed staging URL with the same fix applied, every existing inline `window.app.nav.showTab(...)` continues to work; AI Assistant Send button — broken in staging by this exact bug — now sends and receives real replies. ## Why this is a separate PR (not bundled with the chat fix) The chat module itself doesn't exist in prod (WALK-1: AI Assistant non-functional). Porting the staging chat module is a much larger follow-up. This PR ships ONLY the firebaseConfig.js shadowing fix + its regression test, so the future chat port doesn't have to also debug a years-old SDK shadowing trap. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Renames
let app->let firebaseAppinwww/firebaseConfig.jsto stop a hidden global-lexical-scope shadow that breaks inline event handlers likeonclick="app.foo()".How it was found
Built a Playwright interaction-driver in the staging clone repo (DDancingDeath/AadhatManagementApp-staging) that clicks every interactive button on every tab — not just renders pages. Within minutes it surfaced that the entire AI Assistant tab in the staging clone was throwing
Cannot read properties of undefined (reading 'sendFromInput')on every Send-button click.Existing JSDOM tests passed because they only string-matched HTML; they never invoked any handler. So the bug was invisible from automated tests, invisible from DevTools (
window.applooked perfect), and looked fine to a casual visual smoke check (the chat tab visibly rendered).Root cause
firebaseConfig.jsis loaded as a classic<script>(not a module). In that context, a top-levellet appcreates a binding in the realm''s global lexical environment. That environment lives "above"windowin the scope chain that inline event handlers see. Soonclick="app.X()"resolvesappto this Firebase App instance — even thoughwindow.app.Xexists and works fine when called from the console or from module code.Result: any
onclick="app.foo()"button silently throws and does nothing.What this PR changes
www/firebaseConfig.jslet app->let firebaseApp(4 lines). Added a 16-line warning comment above the declaration so the trap is documented inline for future maintainers.www/js/__tests__/firebase-app-shadow.test.jslet|const|var appas a top-level binding in firebaseConfig.js; (2) sanity-checks the result offirebase.initializeApp(...)is bound to a different name (firebaseAppetc.).What this PR does NOT change
navigation.htmlalready useswindow.app.nav.X()form for every inline handler, so nothing in the running app currently resolves against the shadowed binding. This rename is a pure no-op for current behaviour. The fix is defence-in-depth — when the AI Assistant module (separate WALK-1 follow-up) lands in prod, the fix will already be in place.window.appcontinues to be set bymain.jsexactly as before. The exports offirebaseConfig.js(auth,db, etc.) are unchanged.Testing
npx jest— 105 passed / 105 (was 103; +2 new regression tests).Why a narrow PR
The chat module itself doesn''t exist in prod yet (tracked as WALK-1 — "AI Assistant non-functional"). Porting the staging chat module is a much larger follow-up. This PR ships only the SDK-level shadowing fix + its regression test, so the future chat port doesn''t also have to debug a years-old SDK trap.
Same bug class to watch out for
Any classic
<script>that doeslet foo = ...orconst foo = ...at top level is creating a global lexical binding that isn''t awindow.fooproperty but DOES shadowwindow.foofor inline event handlers. Other repo-wide identifiers worth checking:db,auth,firebase. (None are currently used in inline handlers, but worth grepping if more inline handlers get added.)