fix(plugins): restore window.esc for out-of-tree plugins - #986
Merged
Conversation
app.js exported `esc` as an implicit global back when it was a classic script. a9fce29 made it an ES module and 14b4058 carved `esc` into js/dom.js; the window re-export list was rebuilt without it. Out-of-tree plugins load screen.js as a classic script and call `esc()` bare, so nothing in-tree catches the break: no-undef, a call-graph scan and a grep all pass while the plugin throws in the field. The MIDI plugin builds its device list with esc() inside the same try block that catches requestMIDIAccess() failures, so the ReferenceError surfaced to testers as "MIDI Access denied esc is not defined" — access had actually been granted. Pin the whole plugin-facing global surface by name, mirroring tests/test_plugin_context_contract.py. Verified both ways against a running app: without the fix the spec fails with "missing or not functions: esc". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe window export contract now includes ChangesPlugin global contract
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
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.
What testers saw
MIDI access was never denied. It was granted. The error message is two unrelated things glued together.
plugins/midi/screen.jsbuilds its device dropdown withesc(o.name), and the call sits inside the try block guardingrequestMIDIAccess():So a
ReferenceErrorfrom the device list gets reported by the handler meant for permission failures. Testers with working hardware and granted permission still see "access denied".Root cause
esc()was a top-level function instatic/app.jsback when app.js was a classic script, which made it an implicit global any plugin could call.a9fce29made app.js an ES module → top-level names became module-scoped.14b4058carvedescout intojs/dom.js.Object.assign(window, {...})block that hands globals back to plugins was rebuilt withoutesc.In-tree plugins were unaffected because they each define a local
esc(career,achievements,input_setup, …). Out-of-tree plugins rely on the host's.Why fix it here and not in the MIDI plugin
Four bundled plugins call
esc()bare with no local definition —midi,multiplayer,nam_tone,practice. Only MIDI was reported. One entry in the export list fixes all four; patching MIDI alone leaves three broken.The guard
tests/test_plugin_context_contract.pyalready pins the server-side plugin context by name, and its docstring says exactly why:That lesson was written down but only applied to the server side. This adds the frontend twin — a literal-list pin of the plugin-facing window surface, same reasoning, same no-derived-list rule. Relevant with the module-refactor epic still open (#879, #901–#904, #917).
Verification
Both directions, against a running app in real Chromium:
window globals plugins depend on are missing or not functions: esc— the tester's bug, reproduced../static:/app/staticlive-reload mount indocker-compose.ymldoes not propagate edits — the container snapshots at boot. My first run passed without the fix because it was serving a stale file;md5sumon disk vs. in-container confirmed the drift. Every result above requireddocker compose restart web. Worth knowing for anyone trusting that mount.npm run lint→ 0 errors (12 pre-existing warnings)npm run test:js→ 1099/1099codex execpreflight → NO ISSUESNot in this PR
resources/slopsmith/.catchreports any exception as "access denied". Moving_updateMidiDevices()outside the try would stop future bugs wearing a permissions costume — that's an out-of-tree repo, so it's a separate change.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
escutility for browser plugins.Tests