Refactor startup/logging service wiring and related test/config updates

This commit is contained in:
2026-02-15 21:02:54 -08:00
parent c6ac962f7a
commit bec69d1b71
41 changed files with 722 additions and 281 deletions

View File

@@ -36,6 +36,46 @@ test("parses jsonc and warns/falls back on invalid value", () => {
assert.ok(service.getWarnings().some((w) => w.path === "websocket.port"));
});
test("accepts valid logging.level", () => {
const dir = makeTempDir();
fs.writeFileSync(
path.join(dir, "config.jsonc"),
`{
"logging": {
"level": "warn"
}
}`,
"utf-8",
);
const service = new ConfigService(dir);
const config = service.getConfig();
assert.equal(config.logging.level, "warn");
});
test("falls back for invalid logging.level and reports warning", () => {
const dir = makeTempDir();
fs.writeFileSync(
path.join(dir, "config.jsonc"),
`{
"logging": {
"level": "trace"
}
}`,
"utf-8",
);
const service = new ConfigService(dir);
const config = service.getConfig();
const warnings = service.getWarnings();
assert.equal(config.logging.level, DEFAULT_CONFIG.logging.level);
assert.ok(
warnings.some((warning) => warning.path === "logging.level"),
);
});
test("parses invisible overlay config and new global shortcuts", () => {
const dir = makeTempDir();
fs.writeFileSync(
@@ -372,6 +412,7 @@ test("falls back to default when ankiConnect n+1 deck list is invalid", () => {
test("template generator includes known keys", () => {
const output = generateConfigTemplate(DEFAULT_CONFIG);
assert.match(output, /"ankiConnect":/);
assert.match(output, /"logging":/);
assert.match(output, /"websocket":/);
assert.match(output, /"youtubeSubgen":/);
assert.match(output, /"nPlusOne"\s*:\s*\{/);

View File

@@ -75,6 +75,9 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
enabled: "auto",
port: 6677,
},
logging: {
level: "info",
},
texthooker: {
openBrowser: true,
},
@@ -276,6 +279,13 @@ export const RUNTIME_OPTION_REGISTRY: RuntimeOptionRegistryEntry[] = [
];
export const CONFIG_OPTION_REGISTRY: ConfigOptionRegistryEntry[] = [
{
path: "logging.level",
kind: "enum",
enumValues: ["debug", "info", "warn", "error"],
defaultValue: DEFAULT_CONFIG.logging.level,
description: "Minimum log level for runtime logging.",
},
{
path: "websocket.enabled",
kind: "enum",
@@ -460,6 +470,14 @@ export const CONFIG_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
],
key: "websocket",
},
{
title: "Logging",
description: [
"Controls logging verbosity.",
"Set to debug for full runtime diagnostics.",
],
key: "logging",
},
{
title: "AnkiConnect Integration",
description: ["Automatic Anki updates and media generation options."],

View File

@@ -196,6 +196,20 @@ export class ConfigService {
}
}
if (isObject(src.logging)) {
const logLevel = asString(src.logging.level);
if (logLevel === "debug" || logLevel === "info" || logLevel === "warn" || logLevel === "error") {
resolved.logging.level = logLevel;
} else if (src.logging.level !== undefined) {
warn(
"logging.level",
src.logging.level,
resolved.logging.level,
"Expected debug, info, warn, or error.",
);
}
}
if (Array.isArray(src.keybindings)) {
resolved.keybindings = src.keybindings.filter(
(