Files
scoreko-electron-dev/docs/refactor/MIGRATION_PLAN.md
T

7.7 KiB

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:

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:

src/main/app/application-controller.ts

Required states:

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:

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:

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:

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:

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:

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:

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:

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.