import { InputError } from '@backstage/errors'; const propsOfHumanDuration = [ "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds" ]; function readDurationFromConfig(config, options) { let root; let found = false; const result = {}; try { root = (options == null ? void 0 : options.key) ? config.getConfig(options.key) : config; for (const prop of propsOfHumanDuration) { const value = root.getOptionalNumber(prop); if (value !== void 0) { result[prop] = value; found = true; } } } catch (error) { throw new InputError(`Failed to read duration from config, ${error}`); } try { if (!found) { const good = propsOfHumanDuration.map((p) => `'${p}'`).join(", "); throw new Error(`Needs one or more of ${good}`); } const invalidProps = root.keys().filter((prop) => !propsOfHumanDuration.includes(prop)); if (invalidProps.length) { const what = invalidProps.length === 1 ? "property" : "properties"; const bad = invalidProps.map((p) => `'${p}'`).join(", "); const good = propsOfHumanDuration.map((p) => `'${p}'`).join(", "); throw new Error( `Unknown ${what} ${bad}; expected one or more of ${good}` ); } } catch (error) { let prefix = "Failed to read duration from config"; if (options == null ? void 0 : options.key) { prefix += ` at '${options.key}'`; } throw new InputError(`${prefix}, ${error}`); } return result; } var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; function isObject(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function cloneDeep(value) { if (typeof value !== "object" || value === null) { return value; } if (Array.isArray(value)) { return value.map(cloneDeep); } return Object.fromEntries( Object.entries(value).map(([k, v]) => [k, cloneDeep(v)]) ); } function merge(into, from) { if (into === null) { return void 0; } if (into === void 0) { return from === void 0 ? void 0 : merge(from); } if (typeof into !== "object" || Array.isArray(into)) { return into; } const fromObj = isObject(from) ? from : {}; const out = {}; for (const key of /* @__PURE__ */ new Set([...Object.keys(into), ...Object.keys(fromObj)])) { const val = merge(into[key], fromObj[key]); if (val !== void 0) { out[key] = val; } } return out; } function typeOf(value) { if (value === null) { return "null"; } else if (Array.isArray(value)) { return "array"; } const type = typeof value; if (type === "number" && isNaN(value)) { return "nan"; } if (type === "string" && value === "") { return "empty-string"; } return type; } const errors = { type(key, context, typeName, expected) { return `Invalid type in config for key '${key}' in '${context}', got ${typeName}, wanted ${expected}`; }, missing(key, context) { return `Missing required config value at '${key}' in '${context}'`; }, convert(key, context, expected) { return `Unable to convert config value for key '${key}' in '${context}' to a ${expected}`; } }; class ConfigReader { constructor(data, context = "mock-config", fallback, prefix = "") { this.data = data; this.context = context; this.fallback = fallback; this.prefix = prefix; /** * A set of key paths that where removed from the config due to not being visible. * * This was added as a mutable private member to avoid changes to the public API. * Its only purpose of this is to warn users of missing visibility when running * the frontend in development mode. */ __publicField(this, "filteredKeys"); __publicField(this, "notifiedFilteredKeys", /* @__PURE__ */ new Set()); } /** * Instantiates the config reader from a list of application config objects. */ static fromConfigs(configs) { if (configs.length === 0) { return new ConfigReader(void 0); } return configs.reduce( (previousReader, { data, context, filteredKeys, deprecatedKeys }) => { const reader = new ConfigReader(data, context, previousReader); reader.filteredKeys = filteredKeys; if (deprecatedKeys) { for (const { key, description } of deprecatedKeys) { console.warn( `The configuration key '${key}' of ${context} is deprecated and may be removed soon. ${description || ""}` ); } } return reader; }, void 0 ); } /** {@inheritdoc Config.has} */ has(key) { var _a, _b; const value = this.readValue(key); if (value === null) { return false; } if (value !== void 0) { return true; } return (_b = (_a = this.fallback) == null ? void 0 : _a.has(key)) != null ? _b : false; } /** {@inheritdoc Config.keys} */ keys() { var _a, _b; const localKeys = this.data ? Object.keys(this.data) : []; const fallbackKeys = (_b = (_a = this.fallback) == null ? void 0 : _a.keys()) != null ? _b : []; return [.../* @__PURE__ */ new Set([...localKeys, ...fallbackKeys])].filter( (k) => { var _a2; return ((_a2 = this.data) == null ? void 0 : _a2[k]) !== null; } ); } /** {@inheritdoc Config.get} */ get(key) { const value = this.getOptional(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key != null ? key : ""), this.context)); } return value; } /** {@inheritdoc Config.getOptional} */ getOptional(key) { var _a, _b; const value = cloneDeep(this.readValue(key)); const fallbackValue = (_a = this.fallback) == null ? void 0 : _a.getOptional(key); if (value === null) { return void 0; } if (value === void 0) { if (process.env.NODE_ENV === "development") { if (fallbackValue === void 0 && key) { const fullKey = this.fullKey(key); if (((_b = this.filteredKeys) == null ? void 0 : _b.includes(fullKey)) && !this.notifiedFilteredKeys.has(fullKey)) { this.notifiedFilteredKeys.add(fullKey); console.warn( `Failed to read configuration value at '${fullKey}' as it is not visible. See https://backstage.io/docs/conf/defining#visibility for instructions on how to make it visible.` ); } } } return merge(fallbackValue); } else if (fallbackValue === void 0) { return merge(value); } return merge(value, fallbackValue); } /** {@inheritdoc Config.getConfig} */ getConfig(key) { const value = this.getOptionalConfig(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalConfig} */ getOptionalConfig(key) { var _a; const value = this.readValue(key); const fallbackConfig = (_a = this.fallback) == null ? void 0 : _a.getOptionalConfig(key); if (isObject(value)) { return this.copy(value, key, fallbackConfig); } if (value === null) { return void 0; } if (value !== void 0) { throw new TypeError( errors.type(this.fullKey(key), this.context, typeOf(value), "object") ); } return fallbackConfig; } /** {@inheritdoc Config.getConfigArray} */ getConfigArray(key) { const value = this.getOptionalConfigArray(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalConfigArray} */ getOptionalConfigArray(key) { var _a; const configs = this.readConfigValue(key, (values) => { if (!Array.isArray(values)) { return { expected: "object-array" }; } for (const [index, value] of values.entries()) { if (!isObject(value)) { return { expected: "object-array", value, key: `${key}[${index}]` }; } } return true; }); if (!configs) { if (process.env.NODE_ENV === "development") { const fullKey = this.fullKey(key); if (((_a = this.filteredKeys) == null ? void 0 : _a.some((k) => k.startsWith(fullKey))) && !this.notifiedFilteredKeys.has(key)) { this.notifiedFilteredKeys.add(key); console.warn( `Failed to read configuration array at '${key}' as it does not have any visible elements. See https://backstage.io/docs/conf/defining#visibility for instructions on how to make it visible.` ); } } return void 0; } return configs.map((obj, index) => this.copy(obj, `${key}[${index}]`)); } /** {@inheritdoc Config.getNumber} */ getNumber(key) { const value = this.getOptionalNumber(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalNumber} */ getOptionalNumber(key) { const value = this.readConfigValue( key, (val) => typeof val === "number" || typeof val === "string" || { expected: "number" } ); if (typeof value === "number" || value === void 0) { return value; } const number = Number(value); if (!Number.isFinite(number)) { throw new Error( errors.convert(this.fullKey(key), this.context, "number") ); } return number; } /** {@inheritdoc Config.getBoolean} */ getBoolean(key) { const value = this.getOptionalBoolean(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalBoolean} */ getOptionalBoolean(key) { const value = this.readConfigValue( key, (val) => typeof val === "boolean" || typeof val === "number" || typeof val === "string" || { expected: "boolean" } ); if (typeof value === "boolean" || value === void 0) { return value; } const valueString = String(value).trim(); if (/^(?:y|yes|true|1|on)$/i.test(valueString)) { return true; } if (/^(?:n|no|false|0|off)$/i.test(valueString)) { return false; } throw new Error(errors.convert(this.fullKey(key), this.context, "boolean")); } /** {@inheritdoc Config.getString} */ getString(key) { const value = this.getOptionalString(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalString} */ getOptionalString(key) { return this.readConfigValue( key, (value) => typeof value === "string" && value !== "" || { expected: "string" } ); } /** {@inheritdoc Config.getStringArray} */ getStringArray(key) { const value = this.getOptionalStringArray(key); if (value === void 0) { throw new Error(errors.missing(this.fullKey(key), this.context)); } return value; } /** {@inheritdoc Config.getOptionalStringArray} */ getOptionalStringArray(key) { return this.readConfigValue(key, (values) => { if (!Array.isArray(values)) { return { expected: "string-array" }; } for (const [index, value] of values.entries()) { if (typeof value !== "string" || value === "") { return { expected: "string-array", value, key: `${key}[${index}]` }; } } return true; }); } fullKey(key) { return `${this.prefix}${this.prefix ? "." : ""}${key}`; } copy(data, key, fallback) { const reader = new ConfigReader( data, this.context, fallback, this.fullKey(key) ); reader.filteredKeys = this.filteredKeys; return reader; } readConfigValue(key, validate) { var _a, _b; const value = this.readValue(key); if (value === void 0) { if (process.env.NODE_ENV === "development") { const fullKey = this.fullKey(key); if (((_a = this.filteredKeys) == null ? void 0 : _a.includes(fullKey)) && !this.notifiedFilteredKeys.has(fullKey)) { this.notifiedFilteredKeys.add(fullKey); console.warn( `Failed to read configuration value at '${fullKey}' as it is not visible. See https://backstage.io/docs/conf/defining#visibility for instructions on how to make it visible.` ); } } return (_b = this.fallback) == null ? void 0 : _b.readConfigValue(key, validate); } if (value === null) { return void 0; } const result = validate(value); if (result !== true) { const { key: keyName = key, value: theValue = value, expected } = result; throw new TypeError( errors.type( this.fullKey(keyName), this.context, typeOf(theValue), expected ) ); } return value; } readValue(key) { const parts = key ? key.split(".") : []; for (const part of parts) { if (!CONFIG_KEY_PART_PATTERN.test(part)) { throw new TypeError(`Invalid config key '${key}'`); } } if (this.data === void 0) { return void 0; } let value = this.data; for (const [index, part] of parts.entries()) { if (isObject(value)) { value = value[part]; } else if (value !== void 0 && value !== null) { const badKey = this.fullKey(parts.slice(0, index).join(".")); throw new TypeError( errors.type(badKey, this.context, typeOf(value), "object") ); } } return value; } } export { ConfigReader, readDurationFromConfig }; //# sourceMappingURL=index.esm.js.map