Merged
Conversation
Add the 5-file phases/ directory that lifts the ~1100-line async bootstrap IIFE out of engine.ts into ordered phase modules. No call site change yet — engine.ts still owns the IIFE body verbatim; this commit only introduces the phase modules and the orchestrator test. - phases/bootstrap.ts — Phase signature, BootstrapDeps, runBootstrapPhases. - phases/initGpu.ts — GPU device + every renderer + point-source slot registry loop. Stashes IIFE-locals on a `phaseLocals` carrier for later phases. - phases/wireSlots.ts — sidecar slots (filaments, famous-meta, pgc-aliases), load-progress emitter, thumbnail subsystem, parallel multi-survey load, synthetic fallback. - phases/wireInput.ts — pick renderer, camera, orbit controls, click handlers, input bindings, ready-status callback, settings seed. - phases/startLoop.ts — RunFrameDeps assembly, frameRef.current assignment, first scheduler.requestRender(). - tests/services/engine/phases/bootstrap.test.ts — orchestrator-level unit test: phases run in declared order, first rejection short-circuits, state writes propagate. Phase content is lifted verbatim from engine.ts's IIFE — no behavioural changes. The orchestrator wraps four awaits; engine.ts will collapse to a single `await runBootstrapPhases(state, deps)` in a follow-up commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…estrator call
The 1100-line async bootstrap IIFE in engine.ts is replaced with a
single `await runBootstrapPhases(state, bootstrapDeps)` call. Phase
content (GPU init, slot wiring, input wiring, loop start) lives in
`phases/*.ts` after the previous commit. This commit only changes the
call site:
- Replace `let frame` with `const frameRef: { current }` so `startLoop`
can write across the module boundary.
- Replace `let detachControls` with `const detachControlsRef`; same
rationale, `wireInput` writes to it.
- Add `const handleRef`; engine.ts assigns `handleRef.current = handle`
after the public handle literal evaluates so `wireInput`'s
onDoubleClick closure can resolve `handle.focusOn(...)` lazily.
- Build `bootstrapDeps` once before the IIFE; the IIFE body becomes
three lines (initializing → await runBootstrapPhases → catch+error).
- Update `destroy()` to read through `detachControlsRef.current`.
- Drop now-unused imports (PointRenderer, createPickRenderer,
createPostProcess, attachOrbitControls, every gpu/renderer
constructor, every fetcher, AssetSlot factory, etc.).
- Update the module-header docstring to list the new `phases/*` layout.
engine.ts goes from 1880 → 1121 lines (-759). No behavioural change:
the orchestrator preserves the single-try/catch contract the IIFE had,
phase ordering matches the original sequential blocks, and observable
callbacks fire in the same order.
Test suite: 931 passing (+6 from Phase 5's bootstrap orchestrator
test added in the prior commit).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
skymap | 33c9029 | Commit Preview URL Branch Preview URL |
May 08 2026, 12:58 AM |
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
Phase 5 of the engine internal restructure (Spec B). Lifts the
~1100-line async bootstrap IIFE out of
engine.tsinto 5 orderedphase modules under
src/services/engine/phases/:phases/initGpu.ts— GPU device + every renderer (point, pick,milky-way, filament, quad, disk, procedural-disk) + post-process +
the 5 point-source asset slots from the registry.
phases/wireSlots.ts— sidecar slots (filaments, famous-meta,pgc-aliases), load-progress emitter, thumbnail subsystem, parallel
multi-survey load with synthetic fallback.
phases/wireInput.ts— pick renderer, camera + bbox auto-framing,orbit controls + click handlers, input bindings, ready-status
callback, settings-callback fan-out.
phases/startLoop.ts—RunFrameDepsassembly,frameRef.currentassignment, first
scheduler.requestRender().phases/bootstrap.ts— orchestrator (runBootstrapPhases),BootstrapDepsshape, sharedPhasesignature.The IIFE in
engine.tscollapses to:engine.tsgoes from 1880 → 1121 lines (-759, ~40%). Nobehavioural change — phase content is lifted verbatim; only closure
references rewritten as
state.*/deps.*. Mutable closurecaptures (
frame,detachControls,handle) flow through{current}ref boxes (same pattern Phase 3 introduced for
lastReportedFps).Test plan
npx tsc --noEmitclean (both src + tools).npx vitest run— 931 / 931 passing (+6 from new bootstraporchestrator test).
tests/services/engine/phases/bootstrap.test.ts):- phases run in declared order;
- first rejection short-circuits (initGpu/wireSlots/wireInput
throw → later phases NOT called);
- state writes from earlier phases visible to later phases.
initializing → loading → readyfire in order, click + dblclickwork, settings panel echoes, destroy() detaches orbit controls.
Phase boundary table
initGpuawait initGpu(canvas)wireSlotswireInputstartLoopDeviations from the plan
firstReadySourceflows throughphaseLocals, notstate.The pre-Phase-5 IIFE captured
firstReadySourceas a closure localwritten by the all-arrivals subscriber and read by the
cb.onStatusChange({kind:'ready', source})call later in the sameIIFE. With the IIFE split into phases, that capture crosses a phase
boundary (
wireSlotswrites;wireInputreads). Promoting it toEngineStatewould expand the type for one consumer; threading itthrough
phaseLocalskeeps the orchestrator-internal carrier.PhaseLocalsis a phase-internal carrier onBootstrapDeps.The pre-Phase-5 IIFE used its own closure scope to thread the GPU
device, context, and IIFE-local renderers between sequential
blocks. Phases preserve this with a
deps.phaseLocalsfield(typed) that
initGpuwrites andwireSlots/startLoopread.state.gpuonly carries the renderersdestroy()releases(renderer, postProcess, filamentRenderer, pickRenderer); the rest
live on
phaseLocalsto keepEngineStatenarrow.Anything surprising
handle.focusOn(lastClickedInfo)call insidewireInput'sonDoubleClick handler captures the public handle, which is
declared after the bootstrap IIFE in
engine.ts. We thread itthrough
deps.handleRef: { current: EngineHandle | null };engine.ts assigns
handleRef.current = handleafter the handleliteral evaluates. By the time a user can physically double-click,
orbit controls are wired and
handleRefis non-null. Same{current}ref pattern asframeRef/detachControlsRef.if (state.sources.clouds.size === 0) returnearly-return mid-IIFE is preserved by having
wireInputandstartLoopboth check the same condition and bail. The enginethen sits in 'loading' state with nothing wired — identical to the
pre-Phase-5 behaviour.
🤖 Generated with Claude Code