# Migration Plan ## Goal Refactor the Electron main-process architecture without changing product behavior. The plan is sequential and should be followed in order so future sessions can make progress without reinterpreting the architecture. ## Migration Principles - Preserve behavior before moving structure. - Add tests around risky lifecycle behavior before refactoring it. - Keep the NodeCG runtime model intact. - Keep Electron minimal. - Avoid introducing IPC, preload, or renderer code unless required by a concrete feature. - Prefer small pure functions for paths, URLs, update metadata, asset selection, and navigation policy. - Rewrite only the degraded areas: updater, lifecycle orchestration, and platform process shutdown. ## Phase 1: Freeze Existing Behavior Purpose: Protect current startup, shutdown, provisioning, navigation, and update behavior before extracting modules. Tasks: - Add tests for first-run runtime provisioning and relaunch behavior. - Add tests for loading window and main window ordering. - Add tests for update scheduling behavior. - Add tests for shutdown when NodeCG is running. - Add tests for shutdown when NodeCG is already stopped. - Add tests for double quit or repeated shutdown calls. - Add tests for macOS-style activation after windows are closed. Acceptance criteria: - Existing tests continue to pass. - New tests describe current behavior, not the desired future behavior. - No folder reorganization occurs in this phase. ## Phase 2: Extract Pure Path And URL Logic Purpose: Remove deterministic calculations from `main.ts` while keeping Electron side effects in the entrypoint. Target files: ```text src/main/app/paths.ts src/main/config/runtime-config.ts ``` Tasks: - Extract `userData` path calculation. - Extract managed runtime path calculation. - Extract NodeCG dashboard URL construction. - Extract runtime configuration parsing. - Keep Electron `app.setPath`, `app.setName`, and lock handling in bootstrap code. Acceptance criteria: - Extracted functions are pure. - Tests cover path and URL edge cases. - No Electron app object is required to test the extracted logic. ## Phase 3: Introduce ApplicationController Purpose: Make lifecycle explicit without redesigning the app. Target file: ```text src/main/app/application-controller.ts ``` Required states: ```text idle preparing starting ready stopping stopped failed ``` Responsibilities: - Prepare runtime. - Start NodeCG. - Wait for readiness. - Create or reuse windows through a window service. - Load dashboards only after NodeCG readiness. - Schedule update checks. - Handle activation safely. - Handle shutdown idempotently. Non-responsibilities: - Direct process-kill command construction. - Direct update metadata parsing. - Direct Electron `BrowserWindow` option construction. - Business logic inside preload or renderer code. Acceptance criteria: - `main.ts` becomes a thin bootstrap. - Activation uses readiness-aware startup behavior. - Shutdown is idempotent. - Tests cover state transitions and failure paths. ## Phase 4: Extract Shutdown Service Purpose: Make shutdown behavior predictable and easy to test. Target files: ```text src/main/app/shutdown-service.ts src/main/nodecg/nodecg-process-service.ts ``` Tasks: - Centralize app shutdown sequencing. - Ensure NodeCG is stopped before app exit completes. - Handle repeated shutdown requests. - Handle process already exited. - Preserve current quit behavior. Acceptance criteria: - Repeated quit calls do not duplicate process termination. - Tests cover `before-quit`, process exit before shutdown, and shutdown failure logging. ## Phase 5: Rewrite Updater In Small Modules Purpose: Replace the fragile updater internals while preserving user-visible behavior. Target files: ```text src/main/updates/update-service.ts src/main/updates/update-config.ts src/main/updates/update-download.ts src/main/updates/update-schema.ts ``` Tasks: - Define a runtime schema for update metadata. - Validate remote JSON before using it. - Validate update asset URLs. - Restrict protocols to `https:` unless an explicit local development mode exists. - Select platform assets through pure functions. - Download to a safe temporary path. - Finalize downloads atomically. - Keep dialog behavior outside fetch and download helpers. - Correct Spanish encoding issues. - Add tests for malformed JSON, missing assets, invalid URLs, failed downloads, and cancelled dialogs. Acceptance criteria: - Invalid update metadata fails closed. - Downloaded files cannot escape the intended temporary directory. - User-facing strings render correctly. - Existing update behavior is preserved where valid metadata is provided. ## Phase 6: Isolate Platform Process Termination Purpose: Keep OS-specific process-kill details outside NodeCG lifecycle logic. Target file: ```text src/main/nodecg/platform-process-killer.ts ``` Tasks: - Implement a small interface for terminating process trees. - Provide Windows and POSIX implementations. - Validate that Windows PIDs are numeric before command construction. - Avoid spreading platform conditionals through the process manager. - Test command selection and error handling. Acceptance criteria: - `taskkill` construction is isolated. - POSIX process termination is isolated. - Process manager tests no longer need to know platform command details. ## Phase 7: Harden Electron Window Policy Purpose: Make Electron browser security explicit and testable. Target files: ```text src/main/windows/window-service.ts src/main/windows/navigation-policy.ts ``` Tasks: - Explicitly set `webSecurity: true`. - Keep `nodeIntegration: false`. - Keep `contextIsolation: true`. - Keep `sandbox: true`. - Add a permission request handler that denies by default. - Define devtools policy by environment. - Keep navigation allowlist limited to approved local NodeCG origins. - Prevent unexpected new-window behavior. - Review CSP options for NodeCG-hosted content where feasible. Acceptance criteria: - BrowserWindow options are covered by tests. - Permission requests are denied unless explicitly allowed. - Navigation policy has tests for allowed and blocked origins. ## Phase 8: Normalize Scripts And Shared Constants Purpose: Reduce packaging fragility without changing the build system. Targets: ```text scripts/ src/main/config/ ``` Tasks: - Make repository layout assumptions explicit. - Validate required paths before build work starts. - Share package/runtime constants where reasonable. - Improve error messages for missing parent project or missing NodeCG runtime. - Keep scripts simple `.mjs` utilities. Acceptance criteria: - CI failures explain missing external dependencies clearly. - Local build behavior remains unchanged. - No new framework is introduced. ## Phase 9: Reorganize Folders Last Purpose: Move files only after behavior is protected and new ownership is clear. Target structure: ```text src/main/ app/ config/ windows/ nodecg/ updates/ logging/ src/shared/ ``` Tasks: - Move files into target folders after tests pass. - Update imports mechanically. - Avoid changing logic during moves. - Run typecheck, tests, and lint after each group of moves. Acceptance criteria: - File moves are behavior-neutral. - Test output remains unchanged. - Imports reflect the target architecture. ## Required Verification Per Phase Run the relevant subset while iterating, then run all checks before closing a phase: ```text npm run typecheck npm test npm run lint ``` ## Stop Conditions Stop and reassess if: - A change requires adding IPC without a product need. - A refactor changes the NodeCG runtime model. - Update validation requires a product or release-server decision. - CSP changes break NodeCG dashboards. - CI requires external repository layout decisions outside this package.