Compare commits

..
Author SHA1 Message Date
byrongamatosandClaude Opus 4.8 69002c1e20 refactor(app): make app.js's window contract explicit — 66 names (R3a)
app.js is a classic script, so each of its 385 top-level `function foo()` decls
is implicitly a property of `window`. As an ES module it will not be — module
scope is not global scope — and every name reached from outside this file would
silently vanish. This adds the explicit `window.*` assignments BEFORE the flip.

Provably a NO-OP: all 66 are top-level function declarations, so while app.js is
still a classic script `Object.assign(window, {...})` only re-assigns what
`window` already has. That is what makes it safe to land on its own, ahead of
the flip that needs it.

The consumers are wider than the inline handlers in index.html:
  - inline on*= handlers in static/v3/index.html
  - on*= handlers app.js BUILDS inside template literals (goFavPage,
    updatePlugin, hideScanBanner, ...) — they resolve against window at CLICK
    time, but live in a JS string, so scanning the HTML alone never finds them
  - static/v3/*.js (showScreen alone has 17 consumers), capabilities
  - feedback-desktop and the external plugin repos — easy to miss, they live in
    other repos and no core test covers them
  - capabilities/visualization.js reads window.setViz behind a `typeof` guard,
    so losing it DEGRADES IN SILENCE rather than throwing

Constitution II names window.playSong / window.showScreen / window.feedBack as
the public extension contract, so this is an obligation, not a convenience.

FOUR names are invisible to every static tool. app.js:2156-2157 picks the
handler NAME at runtime —
    const letterFn = favoritesOnly ? 'filterFavTreeLetter' : 'filterTreeLetter';
— and interpolates it into `onclick="${letterFn}('A')"`. The names exist only
inside string literals, so ESLint, no-undef, and any grep for `onclick="fn` all
miss them. They are the library A-Z rail and its pagination: drop one and those
buttons throw at click time and nowhere else.

New tests/js/window_contract.test.js scrapes the HTML's handlers AND app.js's
template-literal handlers, and pins the 4 runtime-composed names by hand.
Verified to BITE: dropping showScreen, goTreePage, or setViz each fails it with
the right message.

On-device: 28 A-Z rail buttons render with their real onclick sources
(filterTreeLetter('A'), ...) and 8/8 execute with no ReferenceError; all 66
names resolve on window in the browser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 17:01:39 +02:00
+12 -12
View File
@@ -119,19 +119,19 @@
script logs anything; load it as early as possible. See script logs anything; load it as early as possible. See
docs/diagnostics-bundle-spec.md (feedBack#166). --> docs/diagnostics-bundle-spec.md (feedBack#166). -->
<script defer src="/static/diagnostics.js"></script> <script defer src="/static/diagnostics.js"></script>
<script type="module" src="/static/capabilities.js"></script> <script defer src="/static/capabilities.js"></script>
<script type="module" src="/static/capabilities/library.js"></script> <script defer src="/static/capabilities/library.js"></script>
<script type="module" src="/static/capabilities/tuning.js"></script> <script defer src="/static/capabilities/tuning.js"></script>
<script type="module" src="/static/capabilities/working-tuning.js"></script> <script defer src="/static/capabilities/working-tuning.js"></script>
<script type="module" src="/static/capabilities/audio-session.js"></script> <script defer src="/static/capabilities/audio-session.js"></script>
<script type="module" src="/static/capabilities/audio-effects.js"></script> <script defer src="/static/capabilities/audio-effects.js"></script>
<script type="module" src="/static/capabilities/playback.js"></script> <script defer src="/static/capabilities/playback.js"></script>
<!-- fee[dB]ack v0.3.0: ui.library-card-injection capability (plugin card actions). --> <!-- fee[dB]ack v0.3.0: ui.library-card-injection capability (plugin card actions). -->
<script type="module" src="/static/capabilities/library-card-actions.js"></script> <script defer src="/static/capabilities/library-card-actions.js"></script>
<script type="module" src="/static/capabilities/visualization.js"></script> <script defer src="/static/capabilities/visualization.js"></script>
<script type="module" src="/static/capabilities/note-detection.js"></script> <script defer src="/static/capabilities/note-detection.js"></script>
<script type="module" src="/static/capabilities/midi-input.js"></script> <script defer src="/static/capabilities/midi-input.js"></script>
<script type="module" src="/static/capabilities/interface-scale.js"></script> <script defer src="/static/capabilities/interface-scale.js"></script>
</head> </head>
<body class="h-screen flex overflow-hidden bg-fb-sidebar text-fb-text font-display"> <body class="h-screen flex overflow-hidden bg-fb-sidebar text-fb-text font-display">