import { regEx } from "../util/regex.js";
import { getConfigFileNames } from "./app-strings.js";
import { GlobalConfig } from "./global.js";
import { getRegexPredicate, isRegexMatch, matchRegexOrGlobList } from "../util/string-match.js";
import { parseUrl } from "../util/url.js";
import { compile } from "../util/template/index.js";
import { isCustomManager } from "../modules/manager/custom/index.js";
import { getExpression } from "../util/jsonata.js";
import { allManagersList, getManagerList } from "../modules/manager/index.js";
import { supportedDatasources } from "./presets/internal/merge-confidence.preset.js";
import { allowedStatusCheckStrings } from "./types.js";
import { getOptions } from "./options/index.js";
import { migrateConfig } from "./migration.js";
import { hasValidSchedule, hasValidTimezone } from "../workers/repository/update/branch/schedule.js";
import { resolveConfigPresets } from "./presets/index.js";
import { check } from "./validation-helpers/match-base-branches.js";
import { check as check$1 } from "./validation-helpers/regex-glob-matchers.js";
import { getParentName, isFalseGlobal, validateJSONataManagerFields, validateNumber, validatePlainObject, validateRegexManagerFields } from "./validation-helpers/utils.js";
import is, { isArray, isEmptyString, isNonEmptyArray, isNonEmptyString, isObject, isPlainObject, isString, isUndefined } from "@sindresorhus/is";
//#region lib/config/validation.ts
const options = getOptions();
let optionsInitialized = false;
let optionTypes;
let optionParents;
let optionGlobals;
let optionInherits;
let optionRegexOrGlob;
let optionAllowsNegativeIntegers;
const topLevelObjects = [...getManagerList(), "env"];
const ignoredNodes = [
	"$schema",
	"headers",
	"depType",
	"npmToken",
	"packageFile",
	"forkToken",
	"repository",
	"vulnerabilityAlertsOnly",
	"vulnerabilityAlert",
	"isVulnerabilityAlert",
	"vulnerabilityFixVersion",
	"copyLocalLibs",
	"prBody",
	"minimumConfidence"
];
const tzRe = regEx(/^:timezone\((.+)\)$/);
const rulesRe = regEx(/p.*Rules\[\d+\]$/);
function isIgnored(key) {
	return ignoredNodes.includes(key);
}
function getUnsupportedEnabledManagers(enabledManagers) {
	return enabledManagers.filter((manager) => !allManagersList.includes(manager.replace("custom.", "")));
}
function getDeprecationMessage(option) {
	const deprecatedOptions = {
		branchName: `Direct editing of branchName is now deprecated. Please edit branchPrefix, additionalBranchPrefix, or branchTopic instead`,
		commitMessage: `Direct editing of commitMessage is now deprecated. Please edit commitMessage's subcomponents instead.`,
		prTitle: `Direct editing of prTitle is now deprecated. Please edit commitMessage subcomponents instead as they will be passed through to prTitle.`
	};
	if (deprecatedOptions[option]) return deprecatedOptions[option];
	const found = options.find((o) => o.name === option);
	if (!found) return;
	if (!found.deprecationMsg) return;
	return `The '${option}' option is deprecated: ${found.deprecationMsg}`;
}
function isInhertConfigOption(key) {
	return optionInherits.has(key);
}
function isRegexOrGlobOption(key) {
	return optionRegexOrGlob.has(key);
}
function isGlobalOption(key) {
	return optionGlobals.has(key);
}
function initOptions() {
	if (optionsInitialized) return;
	optionParents = {};
	optionInherits = /* @__PURE__ */ new Set();
	optionTypes = {};
	optionRegexOrGlob = /* @__PURE__ */ new Set();
	optionGlobals = /* @__PURE__ */ new Set();
	optionAllowsNegativeIntegers = /* @__PURE__ */ new Set();
	for (const option of options) {
		optionTypes[option.name] = option.type;
		if (option.parents) optionParents[option.name] = option.parents;
		if (option.inheritConfigSupport) optionInherits.add(option.name);
		if (option.patternMatch) optionRegexOrGlob.add(option.name);
		if (option.globalOnly) optionGlobals.add(option.name);
		if (option.allowNegative) optionAllowsNegativeIntegers.add(option.name);
	}
	optionsInitialized = true;
}
async function validateConfig(configType, config, isPreset, parentPath) {
	initOptions();
	let errors = [];
	let warnings = [];
	for (const [key, val] of Object.entries(config)) {
		const currentPath = parentPath ? `${parentPath}.${key}` : key;
		/* v8 ignore next 7 -- TODO: add test */
		if (key === "__proto__") {
			errors.push({
				topic: "Config security error",
				message: "__proto__"
			});
			continue;
		}
		if (parentPath && parentPath !== "onboardingConfig" && topLevelObjects.includes(key)) errors.push({
			topic: "Configuration Error",
			message: `The "${key}" object can only be configured at the top level of a config but was found inside "${parentPath}"`
		});
		if (isGlobalOption(key)) {
			if (configType === "global") {
				await validateGlobalConfig(key, val, optionTypes[key], warnings, errors, currentPath, config);
				continue;
			} else if (!isFalseGlobal(key, parentPath) && !(configType === "inherit" && isInhertConfigOption(key))) {
				warnings.push({
					topic: "Configuration Error",
					message: `The "${key}" option is a global option reserved only for Renovate's global configuration and cannot be configured within a repository's config file.`
				});
				continue;
			}
		}
		if (key === "enabledManagers" && val) {
			const unsupportedManagers = getUnsupportedEnabledManagers(val);
			if (isNonEmptyArray(unsupportedManagers)) errors.push({
				topic: "Configuration Error",
				message: `The following managers configured in enabledManagers are not supported: "${unsupportedManagers.join(", ")}"`
			});
		}
		if (key === "registryUrls" && !parentPath && isNonEmptyArray(val)) warnings.push({
			topic: "Configuration Warning",
			message: "Setting `registryUrls` at the top level of your config will apply it to all managers and datasources, which can cause the wrong registry URL to be used for some packages. Use `registryUrls` inside `packageRules` to target specific managers or packages."
		});
		if (key === "defaultRegistryUrls" && !parentPath && isNonEmptyArray(val)) warnings.push({
			topic: "Configuration Warning",
			message: "Setting `defaultRegistryUrls` at the top level of your config will apply it to all managers and datasources, which can cause the wrong registry URL to be used for some packages. Use `defaultRegistryUrls` inside `packageRules` to target specific managers or packages."
		});
		if (!isIgnored(key) && !is.function(val)) {
			if (getDeprecationMessage(key)) warnings.push({
				topic: "Deprecation Warning",
				message: getDeprecationMessage(key)
			});
			if ((key.endsWith("Template") || [
				"branchName",
				"commitBody",
				"commitMessage",
				"prTitle",
				"semanticCommitScope"
			].includes(key)) && val) try {
				let res = compile(val.toString(), config, false);
				res = compile(res, config, false);
				compile(res, config, false);
			} catch {
				errors.push({
					topic: "Configuration Error",
					message: `Invalid template in config path: ${currentPath}`
				});
			}
			const parentName = getParentName(parentPath);
			if (!isPreset && optionParents[key] && !optionParents[key].includes(parentName)) {
				const message = `"${key}" can't be used in "${parentName}". Allowed objects: ${optionParents[key]?.toSorted().join(", ")}.`;
				warnings.push({
					topic: `${parentPath ? `${parentPath}.` : ""}${key}`,
					message
				});
			}
			if (!optionTypes[key]) errors.push({
				topic: "Configuration Error",
				message: `Invalid configuration option: ${currentPath}`
			});
			else if (key === "schedule") {
				const [validSchedule, errorMessage] = hasValidSchedule(val);
				if (!validSchedule) errors.push({
					topic: "Configuration Error",
					message: `Invalid ${currentPath}: \`${errorMessage}\``
				});
			} else if ([
				"allowedVersions",
				"matchCurrentVersion",
				"matchCurrentValue",
				"matchNewValue"
			].includes(key) && isRegexMatch(val)) {
				if (!getRegexPredicate(val)) errors.push({
					topic: "Configuration Error",
					message: `Invalid regExp for ${currentPath}: \`${val}\``
				});
			} else if (key === "timezone" && val !== null) {
				const [validTimezone, errorMessage] = hasValidTimezone(val);
				if (!validTimezone) errors.push({
					topic: "Configuration Error",
					message: `${currentPath}: ${errorMessage}`
				});
			} else if (val !== null) {
				const type = optionTypes[key];
				if (type === "boolean") {
					if (val !== true && val !== false) errors.push({
						topic: "Configuration Error",
						message: `Configuration option \`${currentPath}\` should be boolean. Found: ${JSON.stringify(val)} (${typeof val})`
					});
				} else if (type === "integer") {
					const allowsNegative = optionAllowsNegativeIntegers.has(key);
					errors.push(...validateNumber(key, val, allowsNegative, currentPath));
				} else if (type === "array" && val) if (isArray(val)) {
					for (const [subIndex, subval] of val.entries()) if (isObject(subval)) {
						const subValidation = await validateConfig(configType, subval, isPreset, `${currentPath}[${subIndex}]`);
						warnings = warnings.concat(subValidation.warnings);
						errors = errors.concat(subValidation.errors);
					}
					if (isRegexOrGlobOption(key)) errors.push(...check$1({
						val,
						currentPath
					}));
					if (key === "extends") for (const subval of val) if (isString(subval)) {
						if (configType !== "global" && subval.startsWith("global:")) errors.push({
							topic: "Configuration Error",
							message: `${currentPath}: you cannot extend from "global:" presets in a repository config's "extends"`
						});
						if (parentName === "packageRules" && subval.startsWith("group:")) warnings.push({
							topic: "Configuration Warning",
							message: `${currentPath}: you should not extend "group:" presets`
						});
						if (tzRe.test(subval)) {
							const [, timezone] = tzRe.exec(subval);
							const [validTimezone, errorMessage] = hasValidTimezone(timezone);
							if (!validTimezone) errors.push({
								topic: "Configuration Error",
								message: `${currentPath}: ${errorMessage}`
							});
						}
					} else errors.push({
						topic: "Configuration Error",
						message: `${currentPath}: preset value is not a string`
					});
					const selectors = [
						"matchFileNames",
						"matchLanguages",
						"matchCategories",
						"matchBaseBranches",
						"matchManagers",
						"matchDatasources",
						"matchDepTypes",
						"matchDepNames",
						"matchPackageNames",
						"matchCurrentValue",
						"matchCurrentVersion",
						"matchSourceUrls",
						"matchRegistryUrls",
						"matchUpdateTypes",
						"matchConfidence",
						"matchCurrentAge",
						"matchRepositories",
						"matchNewValue",
						"matchJsonata"
					];
					if (key === "packageRules") for (const [subIndex, packageRule] of val.entries()) if (isObject(packageRule)) {
						const { config: resolved } = await resolveConfigPresets(packageRule, config);
						const resolvedRule = migrateConfig({ packageRules: [resolved] }).migratedConfig.packageRules[0];
						warnings.push(...check({
							resolvedRule,
							currentPath: `${currentPath}[${subIndex}]`,
							baseBranchPatterns: config.baseBranchPatterns
						}));
						const selectorLength = Object.keys(resolvedRule).filter((ruleKey) => selectors.includes(ruleKey)).length;
						if (!selectorLength) {
							const message = `${currentPath}[${subIndex}]: Each packageRule must contain at least one match* or exclude* selector. Rule: ${JSON.stringify(packageRule)}`;
							errors.push({
								topic: "Configuration Error",
								message
							});
						}
						if (selectorLength === Object.keys(resolvedRule).length) {
							const message = `${currentPath}[${subIndex}]: Each packageRule must contain at least one non-match* or non-exclude* field. Rule: ${JSON.stringify(packageRule)}`;
							warnings.push({
								topic: "Configuration Error",
								message
							});
						}
						const preLookupOptions = [
							"allowedVersions",
							"extractVersion",
							"followTag",
							"ignoreDeps",
							"ignoreUnstable",
							"rangeStrategy",
							"registryUrls",
							"respectLatest",
							"rollbackPrs",
							"separateMajorMinor",
							"separateMinorPatch",
							"separateMultipleMajor",
							"separateMultipleMinor",
							"versioning"
						];
						if (isNonEmptyArray(resolvedRule.matchUpdateTypes)) {
							for (const option of preLookupOptions) if (resolvedRule[option] !== void 0) {
								const message = `${currentPath}[${subIndex}]: packageRules cannot combine both matchUpdateTypes and ${option}. Rule: ${JSON.stringify(packageRule)}`;
								errors.push({
									topic: "Configuration Error",
									message
								});
							}
						}
					} else errors.push({
						topic: "Configuration Error",
						message: `${currentPath} must contain JSON objects`
					});
					if (key === "customManagers") {
						const allowedKeys = [
							"customType",
							"description",
							"fileFormat",
							"managerFilePatterns",
							"matchStrings",
							"matchStringsStrategy",
							"depNameTemplate",
							"packageNameTemplate",
							"datasourceTemplate",
							"versioningTemplate",
							"registryUrlTemplate",
							"currentValueTemplate",
							"extractVersionTemplate",
							"autoReplaceStringTemplate",
							"depTypeTemplate"
						];
						for (const customManager of val) if (Object.keys(customManager).some((k) => !allowedKeys.includes(k))) {
							const disallowedKeys = Object.keys(customManager).filter((k) => !allowedKeys.includes(k));
							errors.push({
								topic: "Configuration Error",
								message: `Custom Manager contains disallowed fields: ${disallowedKeys.join(", ")}`
							});
						} else if (isNonEmptyString(customManager.customType) && isCustomManager(customManager.customType)) if (isNonEmptyArray(customManager.managerFilePatterns)) switch (customManager.customType) {
							case "regex":
								validateRegexManagerFields(customManager, currentPath, errors);
								break;
							case "jsonata":
								validateJSONataManagerFields(customManager, currentPath, errors);
								break;
						}
						else errors.push({
							topic: "Configuration Error",
							message: `Each Custom Manager must contain a non-empty managerFilePatterns array`
						});
						else if (isEmptyString(customManager.customType) || isUndefined(customManager.customType)) errors.push({
							topic: "Configuration Error",
							message: `Each Custom Manager must contain a non-empty customType string`
						});
						else errors.push({
							topic: "Configuration Error",
							message: `Invalid customType: ${customManager.customType}. Key is not a custom manager`
						});
					}
					if (["matchPackageNames", "matchDepNames"].includes(key)) {
						const startPattern = regEx(/!?\//);
						const endPattern = regEx(/\/g?i?$/);
						for (const pattern of val) if (startPattern.test(pattern) && endPattern.test(pattern)) try {
							regEx(pattern.replace(startPattern, "/"));
						} catch {
							errors.push({
								topic: "Configuration Error",
								message: `Invalid regExp for ${currentPath}: \`${pattern}\``
							});
						}
					}
					if (key === "baseBranchPatterns") {
						for (const baseBranchPattern of val) if (isRegexMatch(baseBranchPattern) && !getRegexPredicate(baseBranchPattern)) errors.push({
							topic: "Configuration Error",
							message: `Invalid regExp for ${currentPath}: \`${baseBranchPattern}\``
						});
					}
					if ((selectors.includes(key) || key === "matchCurrentVersion" || key === "matchCurrentValue") && !rulesRe.test(parentPath) && (isString(parentPath) || !isPreset)) errors.push({
						topic: "Configuration Error",
						message: `${currentPath}: ${key} should be inside a \`packageRule\` only`
					});
				} else errors.push({
					topic: "Configuration Error",
					message: `Configuration option \`${currentPath}\` should be a list (Array)`
				});
				else if (type === "string") {
					if (!isString(val)) errors.push({
						topic: "Configuration Error",
						message: `Configuration option \`${currentPath}\` should be a string`
					});
				} else if (type === "object" && currentPath !== "compatibility" && key !== "constraints") if (isPlainObject(val)) {
					if (key === "registryAliases") {
						const res = validatePlainObject(val);
						if (res !== true) errors.push({
							topic: "Configuration Error",
							message: `Invalid \`${currentPath}.${key}.${res}\` configuration: value is not a string`
						});
					} else if (key === "env") {
						const allowedEnvVars = configType === "global" ? config.allowedEnv ?? [] : GlobalConfig.get("allowedEnv", []);
						for (const [envVarName, envVarValue] of Object.entries(val)) {
							if (!isString(envVarValue)) errors.push({
								topic: "Configuration Error",
								message: `Invalid env variable value: \`${currentPath}.${envVarName}\` must be a string.`
							});
							if (!matchRegexOrGlobList(envVarName, allowedEnvVars)) errors.push({
								topic: "Configuration Error",
								message: `Env variable name \`${envVarName}\` is not allowed by this bot's \`allowedEnv\`.`
							});
						}
					} else if (key === "statusCheckNames") for (const [statusCheckKey, statusCheckValue] of Object.entries(val)) {
						if (!allowedStatusCheckStrings.includes(statusCheckKey)) errors.push({
							topic: "Configuration Error",
							message: `Invalid \`${currentPath}.${key}.${statusCheckKey}\` configuration: key is not allowed.`
						});
						if (!(isString(statusCheckValue) || null === statusCheckValue)) {
							errors.push({
								topic: "Configuration Error",
								message: `Invalid \`${currentPath}.${statusCheckKey}\` configuration: status check is not a string.`
							});
							continue;
						}
					}
					else if (key === "customDatasources") {
						const allowedKeys = [
							"description",
							"defaultRegistryUrlTemplate",
							"format",
							"transformTemplates"
						];
						for (const [customDatasourceName, customDatasourceValue] of Object.entries(val)) {
							if (!isPlainObject(customDatasourceValue)) {
								errors.push({
									topic: "Configuration Error",
									message: `Invalid \`${currentPath}.${customDatasourceName}\` configuration: customDatasource is not an object`
								});
								continue;
							}
							for (const [subKey, subValue] of Object.entries(customDatasourceValue)) if (!allowedKeys.includes(subKey)) errors.push({
								topic: "Configuration Error",
								message: `Invalid \`${currentPath}.${subKey}\` configuration: key is not allowed`
							});
							else if (subKey === "transformTemplates") {
								if (!isArray(subValue, isString)) errors.push({
									topic: "Configuration Error",
									message: `Invalid \`${currentPath}.${subKey}\` configuration: is not an array of string`
								});
							} else if (subKey === "description") {
								if (!(isString(subValue) || isArray(subValue, isString))) errors.push({
									topic: "Configuration Error",
									message: `Invalid \`${currentPath}.${subKey}\` configuration: is not an array of strings`
								});
							} else if (!isString(subValue)) errors.push({
								topic: "Configuration Error",
								message: `Invalid \`${currentPath}.${subKey}\` configuration: is a string`
							});
						}
					} else if (!options.filter((option) => option.freeChoice).map((option) => option.name).includes(key)) {
						const subValidation = await validateConfig(configType, val, isPreset, currentPath);
						warnings = warnings.concat(subValidation.warnings);
						errors = errors.concat(subValidation.errors);
					}
				} else errors.push({
					topic: "Configuration Error",
					message: `Configuration option \`${currentPath}\` should be a json object`
				});
			}
		}
		if (key === "hostRules" && isArray(val)) {
			const allowedHeaders = configType === "global" ? config.allowedHeaders ?? [] : GlobalConfig.get("allowedHeaders", []);
			for (const rule of val) {
				if (isNonEmptyString(rule.matchHost)) {
					if (rule.matchHost.includes("://")) {
						if (parseUrl(rule.matchHost) === null) errors.push({
							topic: "Configuration Error",
							message: `hostRules matchHost \`${rule.matchHost}\` is not a valid URL.`
						});
					}
				} else if (isEmptyString(rule.matchHost)) errors.push({
					topic: "Configuration Error",
					message: "Invalid value for hostRules matchHost. It cannot be an empty string."
				});
				if (!rule.headers) continue;
				for (const [header, value] of Object.entries(rule.headers)) {
					if (!isString(value)) errors.push({
						topic: "Configuration Error",
						message: `Invalid hostRules headers value configuration: header must be a string.`
					});
					if (!matchRegexOrGlobList(header, allowedHeaders)) errors.push({
						topic: "Configuration Error",
						message: `hostRules header \`${header}\` is not allowed by this bot's \`allowedHeaders\`.`
					});
				}
			}
		}
		if (key === "matchJsonata" && isArray(val, isString)) for (const expression of val) {
			const res = getExpression(expression);
			if (res instanceof Error) errors.push({
				topic: "Configuration Error",
				message: `Invalid JSONata expression for ${currentPath}: ${res.message}`
			});
		}
	}
	function sortAll(a, b) {
		if (a.topic === b.topic) return a.message > b.message ? 1 : -1;
		return a.topic > b.topic ? 1 : -1;
	}
	errors.sort(sortAll);
	warnings.sort(sortAll);
	return {
		errors,
		warnings
	};
}
/**
* Basic validation for global config options
*/
async function validateGlobalConfig(key, val, type, warnings, errors, currentPath, config) {
	/* v8 ignore next 5 -- not testable yet */
	if (getDeprecationMessage(key)) warnings.push({
		topic: "Deprecation Warning",
		message: getDeprecationMessage(key)
	});
	if (key === "binarySource" && val === "docker") warnings.push({
		topic: "Deprecation Warning",
		message: "Usage of `binarySource=docker` is deprecated, and will be removed in the future. Please migrate to `binarySource=install`. Feedback on the usage of `binarySource=docker` is welcome at https://github.com/renovatebot/renovate/discussions/40742"
	});
	if (val !== null) {
		// v8 ignore else -- TODO: add test #40625
		if (type === "string") if (isString(val)) {
			if (key === "onboardingConfigFileName" && !getPossibleConfigFileNames({
				configFileNames: config.configFileNames,
				platform: config.platform
			}).includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${getPossibleConfigFileNames({
					configFileNames: config.configFileNames,
					platform: config.platform
				}).join(", ")}.`
			});
			else if (key === "repositoryCache" && ![
				"enabled",
				"disabled",
				"reset"
			].includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${[
					"enabled",
					"disabled",
					"reset"
				].join(", ")}.`
			});
			else if (key === "dryRun" && ![
				"extract",
				"lookup",
				"full"
			].includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${[
					"extract",
					"lookup",
					"full"
				].join(", ")}.`
			});
			else if (key === "binarySource" && ![
				"docker",
				"global",
				"install",
				"hermit"
			].includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${[
					"docker",
					"global",
					"install",
					"hermit"
				].join(", ")}.`
			});
			else if (key === "requireConfig" && ![
				"required",
				"optional",
				"ignored"
			].includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${[
					"required",
					"optional",
					"ignored"
				].join(", ")}.`
			});
			else if (key === "gitUrl" && ![
				"default",
				"ssh",
				"endpoint"
			].includes(val)) warnings.push({
				topic: "Configuration Error",
				message: `Invalid value \`${val}\` for \`${currentPath}\`. The allowed values are ${[
					"default",
					"ssh",
					"endpoint"
				].join(", ")}.`
			});
			if (key === "reportType" && ["s3", "file"].includes(val) && !isString(config.reportPath)) errors.push({
				topic: "Configuration Error",
				message: `reportType '${val}' requires a configured reportPath`
			});
		} else warnings.push({
			topic: "Configuration Error",
			message: `Configuration option \`${currentPath}\` should be a string.`
		});
		else if (type === "integer") {
			const allowsNegative = optionAllowsNegativeIntegers.has(key);
			warnings.push(...validateNumber(key, val, allowsNegative, currentPath));
		} else if (type === "boolean") {
			if (val !== true && val !== false) warnings.push({
				topic: "Configuration Error",
				message: `Configuration option \`${currentPath}\` should be a boolean. Found: ${JSON.stringify(val)} (${typeof val}).`
			});
		} else if (type === "array") if (isArray(val)) {
			if (isRegexOrGlobOption(key)) warnings.push(...check$1({
				val,
				currentPath
			}));
			if (key === "gitNoVerify") {
				const allowedValues = ["commit", "push"];
				for (const value of val)
 // v8 ignore else -- TODO: add test #40625
				if (!allowedValues.includes(value)) warnings.push({
					topic: "Configuration Error",
					message: `Invalid value for \`${currentPath}\`. The allowed values are ${allowedValues.join(", ")}.`
				});
			}
			if (key === "mergeConfidenceDatasources") {
				const allowedValues = supportedDatasources;
				for (const value of val)
 // v8 ignore else -- TODO: add test #40625
				if (!allowedValues.includes(value)) warnings.push({
					topic: "Configuration Error",
					message: `Invalid value \`${value}\` for \`${currentPath}\`. The allowed values are ${allowedValues.join(", ")}.`
				});
			}
		} else warnings.push({
			topic: "Configuration Error",
			message: `Configuration option \`${currentPath}\` should be a list (Array).`
		});
		else if (type === "object") if (isPlainObject(val)) if (key === "onboardingConfig") {
			const subValidation = await validateConfig("repo", val);
			for (const warning of subValidation.warnings.concat(subValidation.errors)) warnings.push(warning);
		} else if (key === "force") {
			const subValidation = await validateConfig("global", val);
			for (const warning of subValidation.warnings.concat(subValidation.errors)) warnings.push(warning);
		} else if (key === "cacheTtlOverride") for (const [subKey, subValue] of Object.entries(val)) {
			const allowsNegative = optionAllowsNegativeIntegers.has(key);
			warnings.push(...validateNumber(key, subValue, allowsNegative, currentPath, subKey));
		}
		else {
			const res = validatePlainObject(val);
			if (res !== true) warnings.push({
				topic: "Configuration Error",
				message: `Invalid \`${currentPath}.${res}\` configuration: value must be a string.`
			});
		}
		else warnings.push({
			topic: "Configuration Error",
			message: `Configuration option \`${currentPath}\` should be a JSON object.`
		});
	}
}
function getPossibleConfigFileNames({ configFileNames, platform }) {
	const filenames = getConfigFileNames(platform);
	if (isNonEmptyArray(configFileNames)) return filenames.concat(configFileNames);
	return filenames;
}
//#endregion
export { validateConfig };

//# sourceMappingURL=validation.js.map