refactor: extract startup and initial args runtime wiring

This commit is contained in:
2026-02-20 03:14:47 -08:00
parent 6634ee7626
commit 2b70b54faf
7 changed files with 219 additions and 62 deletions

View File

@@ -0,0 +1,24 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { createInitialArgsRuntimeHandler } from './initial-args-runtime-handler';
test('initial args runtime handler composes main deps and runs initial command flow', () => {
const calls: string[] = [];
const handleInitialArgs = createInitialArgsRuntimeHandler({
getInitialArgs: () => ({ start: true } as never),
isBackgroundMode: () => true,
ensureTray: () => calls.push('tray'),
isTexthookerOnlyMode: () => false,
hasImmersionTracker: () => true,
getMpvClient: () => ({
connected: false,
connect: () => calls.push('connect'),
}),
logInfo: (message) => calls.push(`log:${message}`),
handleCliCommand: (_args, source) => calls.push(`cli:${source}`),
});
handleInitialArgs();
assert.deepEqual(calls, ['tray', 'log:Auto-connecting MPV client for immersion tracking', 'connect', 'cli:initial']);
});

View File

@@ -0,0 +1,10 @@
import { createHandleInitialArgsHandler } from './initial-args-handler';
import { createBuildHandleInitialArgsMainDepsHandler } from './initial-args-main-deps';
type InitialArgsMainDeps = Parameters<typeof createBuildHandleInitialArgsMainDepsHandler>[0];
export function createInitialArgsRuntimeHandler(deps: InitialArgsMainDeps) {
const buildHandleInitialArgsMainDepsHandler = createBuildHandleInitialArgsMainDepsHandler(deps);
const handleInitialArgsMainDeps = buildHandleInitialArgsMainDepsHandler();
return createHandleInitialArgsHandler(handleInitialArgsMainDeps);
}

View File

@@ -0,0 +1,72 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import type { CliArgs } from '../../cli/args';
import { createStartupRuntimeHandlers } from './startup-runtime-handlers';
test('startup runtime handlers compose lifecycle runner and startup bootstrap apply flow', () => {
const calls: string[] = [];
const appliedStates: Array<{ mode: string }> = [];
const runtime = createStartupRuntimeHandlers<
{ command: string },
{ mode: string },
{ startAppLifecycle: (args: CliArgs) => void }
>({
appLifecycleRuntimeRunnerMainDeps: {
app: { on: () => {} } as never,
platform: 'darwin',
shouldStartApp: () => true,
parseArgs: () => ({}) as never,
handleCliCommand: () => calls.push('handle-cli'),
printHelp: () => calls.push('help'),
logNoRunningInstance: () => calls.push('no-instance'),
onReady: async () => {
calls.push('ready');
},
onWillQuitCleanup: () => calls.push('cleanup'),
shouldRestoreWindowsOnActivate: () => true,
restoreWindowsOnActivate: () => calls.push('restore'),
shouldQuitOnWindowAllClosed: () => false,
},
createAppLifecycleRuntimeRunner: () => (args) => {
calls.push(`lifecycle:${args.command}`);
},
buildStartupBootstrapMainDeps: (startAppLifecycle) => ({
argv: ['node', 'main.js'],
parseArgs: () => ({ command: 'start' }) as never,
setLogLevel: () => {},
forceX11Backend: () => {},
enforceUnsupportedWaylandMode: () => {},
shouldStartApp: () => true,
getDefaultSocketPath: () => '/tmp/mpv.sock',
defaultTexthookerPort: 5174,
configDir: '/tmp/config',
defaultConfig: {} as never,
generateConfigTemplate: () => 'template',
generateDefaultConfigFile: async () => 0,
setExitCode: () => {},
quitApp: () => {},
logGenerateConfigError: () => {},
startAppLifecycle: (args) => {
const typedArgs = args as unknown as { command: string };
calls.push(`start-lifecycle:${typedArgs.command}`);
startAppLifecycle(typedArgs);
},
}),
createStartupBootstrapRuntimeDeps: (deps) => ({
startAppLifecycle: deps.startAppLifecycle,
}),
runStartupBootstrapRuntime: (deps) => {
deps.startAppLifecycle({ command: 'start' } as never);
return { mode: 'started' };
},
applyStartupState: (state) => {
appliedStates.push(state);
},
});
const state = runtime.runAndApplyStartupState();
assert.deepEqual(state, { mode: 'started' });
assert.deepEqual(appliedStates, [{ mode: 'started' }]);
assert.deepEqual(calls, ['start-lifecycle:start', 'lifecycle:start']);
});

View File

@@ -0,0 +1,55 @@
import { createBuildStartupBootstrapRuntimeFactoryDepsHandler } from './startup-bootstrap-deps-builder';
import { createBuildAppLifecycleRuntimeRunnerMainDepsHandler } from './startup-lifecycle-main-deps';
import { createBuildStartupBootstrapMainDepsHandler } from './startup-bootstrap-main-deps';
type AppLifecycleRuntimeRunnerMainDeps = Parameters<
typeof createBuildAppLifecycleRuntimeRunnerMainDepsHandler
>[0];
type StartupBootstrapMainDeps = Parameters<typeof createBuildStartupBootstrapMainDepsHandler>[0];
type StartupBootstrapRuntimeFactoryDeps = Parameters<
typeof createBuildStartupBootstrapRuntimeFactoryDepsHandler
>[0];
export function createStartupRuntimeHandlers<
TCliArgs,
TStartupState,
TStartupBootstrapRuntimeDeps = unknown,
>(deps: {
appLifecycleRuntimeRunnerMainDeps: AppLifecycleRuntimeRunnerMainDeps;
createAppLifecycleRuntimeRunner: (
params: AppLifecycleRuntimeRunnerMainDeps,
) => (args: TCliArgs) => void;
buildStartupBootstrapMainDeps: (
startAppLifecycle: (args: TCliArgs) => void,
) => StartupBootstrapMainDeps;
createStartupBootstrapRuntimeDeps: (
deps: StartupBootstrapRuntimeFactoryDeps,
) => TStartupBootstrapRuntimeDeps;
runStartupBootstrapRuntime: (deps: TStartupBootstrapRuntimeDeps) => TStartupState;
applyStartupState: (startupState: TStartupState) => void;
}) {
const appLifecycleRuntimeRunnerMainDeps =
createBuildAppLifecycleRuntimeRunnerMainDepsHandler(deps.appLifecycleRuntimeRunnerMainDeps)();
const appLifecycleRuntimeRunner = deps.createAppLifecycleRuntimeRunner(
appLifecycleRuntimeRunnerMainDeps,
);
const startupBootstrapMainDeps = createBuildStartupBootstrapMainDepsHandler(
deps.buildStartupBootstrapMainDeps(appLifecycleRuntimeRunner),
)();
const startupBootstrapRuntimeFactoryDeps =
createBuildStartupBootstrapRuntimeFactoryDepsHandler(startupBootstrapMainDeps)();
const runAndApplyStartupState = (): TStartupState => {
const startupState = deps.runStartupBootstrapRuntime(
deps.createStartupBootstrapRuntimeDeps(startupBootstrapRuntimeFactoryDeps),
);
deps.applyStartupState(startupState);
return startupState;
};
return {
appLifecycleRuntimeRunner,
runAndApplyStartupState,
};
}