From 792ccc3c09295298b0811fb5ed735f92ad9025e5 Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 3 Aug 2026 13:39:14 +0000 Subject: [PATCH] feat: Improve location wording for config errors --- src/backend/scrobblers/ScrobbleClients.ts | 19 +++++++++---------- src/backend/sources/ScrobbleSources.ts | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/backend/scrobblers/ScrobbleClients.ts b/src/backend/scrobblers/ScrobbleClients.ts index 4c04df04..be39182f 100644 --- a/src/backend/scrobblers/ScrobbleClients.ts +++ b/src/backend/scrobblers/ScrobbleClients.ts @@ -15,11 +15,11 @@ import clone from 'clone'; import { stripIndents } from 'common-tags'; import { normalizeStr, type StringNormalizationOptions } from '../utils/StringUtils.ts'; import { prettifyError, ZodError } from 'zod'; -import { commonComponentEnvConfigToConfigPrimitives, generateCommonComponentEnvConfigSchema, transformPresetEnv, type CommonConfigPrimitives } from '../common/infrastructure/config/common.ts'; +import { commonComponentEnvConfigToConfigPrimitives, generateCommonComponentEnvConfigSchema, generateConfigLocation, transformPresetEnv, type CommonConfigPrimitives, type UnparsedConfig } from '../common/infrastructure/config/common.ts'; import type { CommonClientConfig } from '../common/infrastructure/config/client/index.ts'; import { getClientEnvSchema, validateClientAIOJson, validateClientJson, type ClientTypeConfigMap } from '../common/infrastructure/config/client/clientsMap.ts'; -type UnparsedConfig = {config: object, type: ClientType, source?: 'file' | 'aio' | 'env', pos: string}; +type UnparsedClientConfig = UnparsedConfig; type CommonParsedConfig = (CommonClientConfig & {source: string}); @@ -97,7 +97,7 @@ export default class ScrobbleClients { } buildClientsFromConfig = async () => { - const unparsedConfigs: UnparsedConfig[] = []; + const unparsedConfigs: UnparsedClientConfig[] = []; let configFile; try { @@ -175,7 +175,7 @@ export default class ScrobbleClients { } else if (rawClientConfigs === null) { this.logger.warn(`${clientType}.json contained no data`); } else if (typeof rawClientConfigs === 'object') { - clientUnparsedConfigs.push({ config: rawClientConfigs, type: clientType, source: 'file', pos: `object` }) + clientUnparsedConfigs.push({ config: rawClientConfigs, type: clientType, source: 'file', pos: `1` }) } else { this.logger.error(`All top level data from ${clientType}.json must be an object or an array of objects, will not parse configs from file`); } @@ -195,7 +195,6 @@ export default class ScrobbleClients { for (const entry of clientUnparsedConfigs) { let parsedConfig: CommonParsedConfig; try { - const sourceStr = `${entry.source} ${entry.pos}`; switch (entry.source) { case 'env': { const envSchema = await getClientEnvSchema(clientType); @@ -209,7 +208,7 @@ export default class ScrobbleClients { name: `${clientType} - ${entry.source}${entry.pos !== '' ? ` - ${entry.pos}` : ''} `, ...primitives, data, - source: sourceStr, + source: generateConfigLocation('client', entry), options: { ...options, ...(transformOptions ?? {}) @@ -219,18 +218,18 @@ export default class ScrobbleClients { case 'file': case 'aio': { if ('configureAs' in entry.config && entry.config.configureAs === 'source') { - this.logger.debug(`Skipping ${clientType} Config ${entry.source} ${entry.pos} because it is configured as a Source`); + this.logger.debug(`Skipping ${generateConfigLocation('client', entry)} because it is configured as a Source`); continue; } const parsed = entry.source === 'file' ? (await validateClientJson(entry.type, entry.config)) : (await validateClientAIOJson(entry.type, entry.config)); parsedConfig = { ...parsed, - source: sourceStr + source: generateConfigLocation('client', entry) } } break; } } catch (e) { - const msg = `Failed to validate ${clientType} Config ${entry.source} ${entry.pos}`; + const msg = `Failed to validate ${generateConfigLocation('client', entry)}`; if (e instanceof ZodError) { this.logger.error(`${msg}:\n${prettifyError(e)}`); } else { @@ -267,7 +266,7 @@ export default class ScrobbleClients { newClient.logger.info(`Client added from ${s.source}`); this.clients.push(newClient); } catch (e) { - this.logger.error(new Error(`Client from ${s.source} was not added due to unrecoverable errors`, { cause: e })); + this.logger.error(new Error(`${s.source} was not added due to unrecoverable errors`, { cause: e })); } } } diff --git a/src/backend/sources/ScrobbleSources.ts b/src/backend/sources/ScrobbleSources.ts index 153ab7c4..7181053e 100644 --- a/src/backend/sources/ScrobbleSources.ts +++ b/src/backend/sources/ScrobbleSources.ts @@ -14,11 +14,11 @@ import { nonEmptyStringOrDefault } from '../../core/StringUtils.ts'; import type {CommonSourceConfig, CommonSourceOptions} from '../common/infrastructure/config/source/index.ts'; import type {ExternalMetadataTerm, PlayTransformHooks} from '../../core/Transform.ts'; import { prettifyError, ZodError } from 'zod'; -import { commonComponentEnvConfigToConfigPrimitives, generateCommonComponentEnvConfigSchema, type CommonConfigPrimitives } from '../common/infrastructure/config/common.ts'; +import { commonComponentEnvConfigToConfigPrimitives, generateCommonComponentEnvConfigSchema, generateConfigLocation, type CommonConfigPrimitives, type UnparsedConfig } from '../common/infrastructure/config/common.ts'; import { getSourceEnvSchema, validateSourceAIOJson, validateSourceJson } from '../common/infrastructure/config/source/sourcesMap.ts'; import type { SourceTypeConfigMap } from "../common/infrastructure/config/source/sourcesMap.ts"; -type UnparsedConfig = {config: object, type: SourceType, source?: 'file' | 'aio' | 'env', pos: string}; +type UnparsedSourceConfig = UnparsedConfig; type CommonParsedConfig = (CommonSourceConfig & {source: string}); @@ -115,7 +115,7 @@ export default class ScrobbleSources { } buildSourcesFromConfig = async () => { - const unparsedConfigs: UnparsedConfig[] = []; + const unparsedConfigs: UnparsedSourceConfig[] = []; let configFile; try { @@ -195,7 +195,7 @@ export default class ScrobbleSources { } else if (rawConfigs === null) { this.logger.warn(`${configType}.json contained no data`); } else if (typeof rawConfigs === 'object') { - sourceUnparsedConfigs.push({ config: rawConfigs, type: configType, source: 'file', pos: `object` }) + sourceUnparsedConfigs.push({ config: rawConfigs, type: configType, source: 'file', pos: `1` }) } else { this.logger.error(`All top level data from ${configType}.json must be an object or an array of objects, will not parse configs from file`); } @@ -215,7 +215,6 @@ export default class ScrobbleSources { for (const entry of sourceUnparsedConfigs) { let parsedConfig: CommonParsedConfig; try { - const sourceStr = `${entry.source} ${entry.pos}`; switch (entry.source) { case 'env': { const envSchema = await getSourceEnvSchema(configType); @@ -229,7 +228,7 @@ export default class ScrobbleSources { name: `${configType} - ${entry.source}${entry.pos !== '' ? ` - ${entry.pos}` : ''} `, ...primitives, data, - source: sourceStr, + source: generateConfigLocation('source', entry), options: { ...options, ...(transformOptions ?? {}) @@ -241,18 +240,18 @@ export default class ScrobbleSources { if (('configureAs' in entry.config && entry.config.configureAs === 'client') // @ts-expect-error could be a client type || (clientTypes.includes(entry.type) && entry.config.configureAs !== 'source')) { - this.logger.debug(`Skipping ${configType} Config ${entry.source} ${entry.pos} because it is configured as a Client`); + this.logger.debug(`Skipping ${generateConfigLocation('source', entry)} because it is configured as a Client`); continue; } const parsed = entry.source === 'file' ? (await validateSourceJson(entry.type, entry.config)) : (await validateSourceAIOJson(entry.type, entry.config)); parsedConfig = { ...parsed, - source: sourceStr + source: generateConfigLocation('source', entry) } } break; } } catch (e) { - const msg = `Failed to validate ${configType} Config ${entry.source} ${entry.pos}`; + const msg = `Failed to validate ${generateConfigLocation('source', entry)}`; if (e instanceof ZodError) { this.logger.error(`${msg}:\n${prettifyError(e)}`); } else { @@ -288,7 +287,7 @@ export default class ScrobbleSources { newComponent.logger.info(`Source added from ${s.source}`); this.sources.push(newComponent); } catch (e) { - this.logger.error(new Error(`Source from ${s.source} was not added due to unrecoverable errors`, { cause: e })); + this.logger.error(new Error(`${s.source} was not added due to unrecoverable errors`, { cause: e })); } } } -- 2.51.2