refactor: remove root node and npm workflow deps

This commit is contained in:
2026-03-07 21:19:14 -08:00
parent f0418c6e56
commit d0c11d347b
32 changed files with 215 additions and 299 deletions

View File

@@ -32,10 +32,10 @@ test('buildConfigWarningSummary includes warnings with formatted values', () =>
test('buildConfigWarningNotificationBody includes concise warning details', () => {
const body = buildConfigWarningNotificationBody('/tmp/config.jsonc', [
{
path: 'ankiConnect.openRouter',
message: 'Deprecated key; use ankiConnect.ai instead.',
path: 'ankiConnect.ai',
message: 'Expected boolean.',
value: { enabled: true },
fallback: {},
fallback: false,
},
{
path: 'ankiConnect.isLapis.sentenceCardSentenceField',
@@ -47,7 +47,7 @@ test('buildConfigWarningNotificationBody includes concise warning details', () =
assert.match(body, /2 config validation issue\(s\) detected\./);
assert.match(body, /File: \/tmp\/config\.jsonc/);
assert.match(body, /1\. ankiConnect\.openRouter: Deprecated key; use ankiConnect\.ai instead\./);
assert.match(body, /1\. ankiConnect\.ai: Expected boolean\./);
assert.match(
body,
/2\. ankiConnect\.isLapis\.sentenceCardSentenceField: Deprecated key; sentence-card sentence field is fixed to Sentence\./,
@@ -81,8 +81,7 @@ test('buildConfigParseErrorDetails includes path error and restart guidance', ()
test('failStartupFromConfig invokes handlers and throws', () => {
const calls: string[] = [];
const previousExitCode = process.exitCode;
process.exitCode = 0;
const exitCodes: number[] = [];
assert.throws(
() =>
@@ -93,6 +92,9 @@ test('failStartupFromConfig invokes handlers and throws', () => {
showErrorBox: (title, details) => {
calls.push(`dialog:${title}:${details}`);
},
setExitCode: (code) => {
exitCodes.push(code);
},
quit: () => {
calls.push('quit');
},
@@ -100,8 +102,6 @@ test('failStartupFromConfig invokes handlers and throws', () => {
/bad value/,
);
assert.equal(process.exitCode, 1);
assert.deepEqual(exitCodes, [1]);
assert.deepEqual(calls, ['log:bad value', 'dialog:Config Error:bad value', 'quit']);
process.exitCode = previousExitCode;
});

View File

@@ -3,6 +3,7 @@ import type { ConfigValidationWarning } from '../types';
export type StartupFailureHandlers = {
logError: (details: string) => void;
showErrorBox: (title: string, details: string) => void;
setExitCode?: (code: number) => void;
quit: () => void;
};
@@ -98,7 +99,10 @@ export function failStartupFromConfig(
): never {
handlers.logError(details);
handlers.showErrorBox(title, details);
process.exitCode = 1;
handlers.setExitCode?.(1);
if (!handlers.setExitCode) {
process.exitCode = 1;
}
handlers.quit();
throw new Error(details);
}

View File

@@ -1,22 +1,12 @@
import test from 'node:test';
import assert from 'node:assert/strict';
async function loadRegistryOrSkip(t: test.TestContext) {
try {
return await import('./registry');
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes('node:sqlite')) {
t.skip('registry import requires node:sqlite support in this runtime');
return null;
}
throw error;
}
async function loadRegistry() {
return import('./registry');
}
test('createMainRuntimeRegistry exposes expected runtime domains', async (t) => {
const loaded = await loadRegistryOrSkip(t);
if (!loaded) return;
test('createMainRuntimeRegistry exposes expected runtime domains', async () => {
const loaded = await loadRegistry();
const { createMainRuntimeRegistry } = loaded;
const registry = createMainRuntimeRegistry();
@@ -30,9 +20,8 @@ test('createMainRuntimeRegistry exposes expected runtime domains', async (t) =>
assert.ok(registry.mining);
});
test('registry domains expose representative factories', async (t) => {
const loaded = await loadRegistryOrSkip(t);
if (!loaded) return;
test('registry domains expose representative factories', async () => {
const loaded = await loadRegistry();
const { createMainRuntimeRegistry } = loaded;
const registry = createMainRuntimeRegistry();

View File

@@ -17,6 +17,9 @@ export function createBuildReloadConfigMainDepsHandler(deps: ReloadConfigMainDep
logError: (details: string) => deps.failHandlers.logError(details),
showErrorBox: (title: string, details: string) =>
deps.failHandlers.showErrorBox(title, details),
setExitCode: deps.failHandlers.setExitCode
? (code: number) => deps.failHandlers.setExitCode?.(code)
: undefined,
quit: () => deps.failHandlers.quit(),
},
});
@@ -29,6 +32,9 @@ export function createBuildCriticalConfigErrorMainDepsHandler(deps: CriticalConf
logError: (details: string) => deps.failHandlers.logError(details),
showErrorBox: (title: string, details: string) =>
deps.failHandlers.showErrorBox(title, details),
setExitCode: deps.failHandlers.setExitCode
? (code: number) => deps.failHandlers.setExitCode?.(code)
: undefined,
quit: () => deps.failHandlers.quit(),
},
});

View File

@@ -55,8 +55,7 @@ test('createReloadConfigHandler runs success flow with warnings', async () => {
test('createReloadConfigHandler fails startup for parse errors', () => {
const calls: string[] = [];
const previousExitCode = process.exitCode;
process.exitCode = 0;
const exitCodes: number[] = [];
const reloadConfig = createReloadConfigHandler({
reloadConfigStrict: () => ({
@@ -74,12 +73,13 @@ test('createReloadConfigHandler fails startup for parse errors', () => {
failHandlers: {
logError: (details) => calls.push(`error:${details}`),
showErrorBox: (title, details) => calls.push(`dialog:${title}:${details}`),
setExitCode: (code) => exitCodes.push(code),
quit: () => calls.push('quit'),
},
});
assert.throws(() => reloadConfig(), /Failed to parse config file at:/);
assert.equal(process.exitCode, 1);
assert.deepEqual(exitCodes, [1]);
assert.ok(calls.some((entry) => entry.startsWith('error:Failed to parse config file at:')));
assert.ok(calls.some((entry) => entry.includes('/tmp/config.jsonc')));
assert.ok(calls.some((entry) => entry.includes('Error: unexpected token')));
@@ -91,20 +91,18 @@ test('createReloadConfigHandler fails startup for parse errors', () => {
);
assert.ok(calls.includes('quit'));
assert.equal(calls.includes('hotReload:start'), false);
process.exitCode = previousExitCode;
});
test('createCriticalConfigErrorHandler formats and fails', () => {
const calls: string[] = [];
const previousExitCode = process.exitCode;
process.exitCode = 0;
const exitCodes: number[] = [];
const handleCriticalErrors = createCriticalConfigErrorHandler({
getConfigPath: () => '/tmp/config.jsonc',
failHandlers: {
logError: (details) => calls.push(`error:${details}`),
showErrorBox: (title, details) => calls.push(`dialog:${title}:${details}`),
setExitCode: (code) => exitCodes.push(code),
quit: () => calls.push('quit'),
},
});
@@ -114,11 +112,9 @@ test('createCriticalConfigErrorHandler formats and fails', () => {
/Critical config validation failed/,
);
assert.equal(process.exitCode, 1);
assert.deepEqual(exitCodes, [1]);
assert.ok(calls.some((entry) => entry.includes('/tmp/config.jsonc')));
assert.ok(calls.some((entry) => entry.includes('1. foo invalid')));
assert.ok(calls.some((entry) => entry.includes('2. bar invalid')));
assert.ok(calls.includes('quit'));
process.exitCode = previousExitCode;
});

View File

@@ -31,6 +31,7 @@ export type ReloadConfigRuntimeDeps = {
failHandlers: {
logError: (details: string) => void;
showErrorBox: (title: string, details: string) => void;
setExitCode?: (code: number) => void;
quit: () => void;
};
};
@@ -40,6 +41,7 @@ export type CriticalConfigErrorRuntimeDeps = {
failHandlers: {
logError: (details: string) => void;
showErrorBox: (title: string, details: string) => void;
setExitCode?: (code: number) => void;
quit: () => void;
};
};