mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a) (#893)
* refactor(app): carve resume-session out of app.js (R3a)
static/js/resume-session.js (157) — the snapshot taken when you leave a song and the
pill that offers it back. Bodies VERBATIM. app.js 7,727 → 7,601.
Fifth slice out of the strongly-connected core. ONE hook (playSong) + a
currentFilename getter.
S.pendingResume JOINS THE CONTAINER — on demand, exactly as intended. app.js WRITES
it (playSong({ resume }) arms it; the song:ready listener consumes it) while this
module reads it, so it cannot be a plain export: an imported binding is read-only.
Same reason isPlaying is there. The container grows one field per carve that needs
it, never speculatively.
THE CONTRACT TEST CAUGHT THE MISSING HOOK, again on a path nothing executes:
"playSong is read by a module but never wired by app.js — it would throw at runtime".
Second time it has caught a real wiring gap the moment it appeared.
A REAL TRAP, worth remembering: I first did the S.pendingResume rewrite by feeding
acorn's identifier RANGES from node into python, and it corrupted the file
(`_pS.pendingResume null;`). **Acorn's offsets are UTF-16 code units; Python's string
indices are code points.** static/app.js contains emoji, so every offset past one
drifts. Do an AST-driven rewrite in the SAME language that produced the offsets.
`node --check` caught it; a silent version of that bug is very easy to imagine.
VERIFIED. A/B against origin/main in two browsers, real song: the window API
(resumeLastSession / _snapshotResumeSession / _readResumeSession /
_clearResumeSession), snapshot, read-back, and clear — IDENTICAL, zero page errors.
HONEST LIMIT: my probe never got the snapshot to actually PERSIST (there is a guard
beyond the 3s minimum position that a scripted playSong does not satisfy), so that
path is verified only as identical-to-main, not as observed-working. The real
coverage is tests/browser/resume-session.spec.ts, which drives the flow properly.
Zero harnesses broke. pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean),
tailwind clean, Codex 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a)
static/js/juce-audio.js (994) — bodies VERBATIM. app.js 7,603 → 6,643.
THE LARGEST SINGLE SLICE of the whole carve phase: 960 lines, ~13% of what was left.
Three self-installing IIFEs:
_installJuceEngineRoutingWatcher (444) routes a song to the JUCE engine or HTML5 as
the desktop output enters/leaves exclusive/ASIO
_installRendererBusFeeder (337) feeds the highway renderer bus from whichever
transport is actually running
_installJuceAudioElementShim (156) patches audio.play/pause so the rest of the app
keeps talking to the <audio> element while JUCE
owns the transport
They EXPORT NOTHING — all three publish through `window.*` (_juceMode,
_reevaluateJuceRouting, _reevaluateRendererBus, …). So app.js needs only a
side-effect import plus the one binding it actually uses
(_resetJuceAudioShimChain, which the shim IIFE assigns).
THE ORDERING QUESTION, CHECKED RATHER THAN ASSUMED. Importing this module runs the
IIFEs EARLIER than before: imports evaluate ahead of app.js's body, and therefore
ahead of configureHost(). A hook read at IIFE-execution time would THROW. So I walked
the AST at IIFE-body depth to see what they actually touch when they run: nothing but
listener registration, and `audio.play`/`audio.pause` patching — and `audio` is itself
an imported module now. Verified in the browser: both are patched on the carved build
exactly as on main, which proves the shim installs correctly at its new, earlier point.
(Had I got this wrong, host.js throws loudly rather than silently misbehaving — which
is the whole reason it has no no-op defaults.)
VERIFIED. A/B against origin/main in two browsers: the entire window.* surface the
IIFEs publish (_juceMode, _juceOutputIsExclusive, _reevaluateJuceRouting,
_reevaluateRendererBus, _clearJuceRerouteMemo), audio.play/pause patched, a real song
loading and togglePlay driving the public mirror — IDENTICAL, zero page errors.
Harnesses: juce_engine_reroute (19 tests) + renderer_bus_feeder (13) slice the IIFEs by
signature — retargeted, and each sandbox gains a `host` object routed at its EXISTING
stubs so every assertion holds unchanged. test_plugin_runtime_idempotence is SPLIT: 3 of
its 4 source-asserts stayed in app.js, the sm.emit('song:resume') one moved.
pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean), tailwind clean, Codex 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
dc429ecd16
commit
8d0e270345
@@ -1,4 +1,4 @@
|
||||
// Behavioral tests for the JUCE engine-reroute watcher in static/app.js.
|
||||
// Behavioral tests for the JUCE engine-reroute watcher in static/js/juce-audio.js.
|
||||
//
|
||||
// The watcher (an IIFE, `_installJuceEngineRoutingWatcher`) migrates a loaded
|
||||
// song between the HTML5 <audio> element and the native JUCE backing transport
|
||||
@@ -14,14 +14,15 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const APP_JS = path.join(__dirname, '..', '..', 'static', 'app.js');
|
||||
// The JUCE audio shims were carved out of app.js into their own module (R3a).
|
||||
const APP_JS = path.join(__dirname, '..', '..', 'static', 'js', 'juce-audio.js');
|
||||
|
||||
// Brace-balanced extraction of the watcher IIFE, starting at its `(function`
|
||||
// and ending after the matching `})();`.
|
||||
function extractWatcherIIFE(src) {
|
||||
const marker = '(function _installJuceEngineRoutingWatcher() {';
|
||||
const start = src.indexOf(marker);
|
||||
assert.ok(start !== -1, 'watcher IIFE not found in app.js');
|
||||
assert.ok(start !== -1, 'watcher IIFE not found in static/js/juce-audio.js');
|
||||
const openBrace = src.indexOf('{', start);
|
||||
let depth = 1;
|
||||
let i = openBrace + 1;
|
||||
@@ -100,6 +101,17 @@ function makeSandbox({ isAudioRunning, loadBackingTrack, outputType = 'Windows A
|
||||
|
||||
const src = fs.readFileSync(APP_JS, 'utf8');
|
||||
const iife = extractWatcherIIFE(src);
|
||||
// The shims reach back into app.js through the host seam (static/js/host.js).
|
||||
// Route it at the SAME stubs this sandbox already had — a fresh `() => {}` would
|
||||
// swallow the calls and the assertions below would pass vacuously.
|
||||
sandbox.host = {
|
||||
jucePlayer: () => sandbox.jucePlayer,
|
||||
playSong: (...a) => (sandbox.playSong ? sandbox.playSong(...a) : undefined),
|
||||
_audioSeek: (...a) => (sandbox._audioSeek ? sandbox._audioSeek(...a) : Promise.resolve({ completed: true })),
|
||||
setPlayButtonState: (...a) => (sandbox.setPlayButtonState ? sandbox.setPlayButtonState(...a) : undefined),
|
||||
_songEventPayload: (...a) => (sandbox._songEventPayload ? sandbox._songEventPayload(...a) : ({})),
|
||||
showScreen: (...a) => (sandbox.showScreen ? sandbox.showScreen(...a) : undefined),
|
||||
};
|
||||
vm.createContext(sandbox);
|
||||
vm.runInContext(iife, sandbox);
|
||||
return sandbox;
|
||||
|
||||
Reference in New Issue
Block a user