@backstage/config
Advanced tools
Comparing version 1.2.0 to 1.3.0
# @backstage/config | ||
## 1.3.0 | ||
### Minor Changes | ||
- d52d7f9: Make `readDurationFromConfig` support both ISO and ms formats as well, to make it easier to enter time as an end user | ||
### Patch Changes | ||
- Updated dependencies | ||
- @backstage/types@1.2.0 | ||
- @backstage/errors@1.2.5 | ||
## 1.2.0 | ||
@@ -4,0 +16,0 @@ |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var readDurationFromConfig = require('./readDurationFromConfig.cjs.js'); | ||
var reader = require('./reader.cjs.js'); | ||
var errors$1 = require('@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 errors$1.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 errors$1.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; | ||
} | ||
} | ||
exports.ConfigReader = ConfigReader; | ||
exports.readDurationFromConfig = readDurationFromConfig; | ||
exports.readDurationFromConfig = readDurationFromConfig.readDurationFromConfig; | ||
exports.ConfigReader = reader.ConfigReader; | ||
//# sourceMappingURL=index.cjs.js.map |
@@ -1,2 +0,2 @@ | ||
import { JsonPrimitive as JsonPrimitive$1, JsonObject as JsonObject$1, JsonArray as JsonArray$1, JsonValue as JsonValue$1, HumanDuration } from '@backstage/types'; | ||
import { JsonArray as JsonArray$1, JsonObject as JsonObject$1, JsonPrimitive as JsonPrimitive$1, JsonValue as JsonValue$1, HumanDuration } from '@backstage/types'; | ||
import { Config as Config$1 } from '@backstage/config'; | ||
@@ -34,3 +34,3 @@ | ||
/** | ||
* Reads a duration from a config object. | ||
* Reads a duration from config. | ||
* | ||
@@ -40,2 +40,13 @@ * @public | ||
* | ||
* The supported formats are: | ||
* | ||
* - A string in the format of '1d', '2 seconds' etc. as supported by the `ms` | ||
* library. | ||
* - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'. | ||
* - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`. | ||
* | ||
* The string forms are naturally only supported if the `options.key` argument | ||
* is passed, since a `Config` argument always represents an object by its | ||
* nature. | ||
* | ||
* This does not support optionality; if you want to support optional durations, | ||
@@ -46,4 +57,4 @@ * you need to first check the presence of the target with `config.has(...)` and | ||
* @param config - A configuration object | ||
* @param key - If specified, read the duration from the given subkey | ||
* under the config object | ||
* @param key - If specified, read the duration from the given subkey under the | ||
* config object | ||
* @returns A duration object | ||
@@ -237,2 +248,2 @@ */ | ||
export { AppConfig, Config, ConfigReader, JsonArray, JsonObject, JsonPrimitive, JsonValue, readDurationFromConfig }; | ||
export { type AppConfig, type Config, ConfigReader, type JsonArray, type JsonObject, type JsonPrimitive, type JsonValue, readDurationFromConfig }; |
@@ -1,438 +0,3 @@ | ||
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 }; | ||
export { readDurationFromConfig } from './readDurationFromConfig.esm.js'; | ||
export { ConfigReader } from './reader.esm.js'; | ||
//# sourceMappingURL=index.esm.js.map |
{ | ||
"name": "@backstage/config", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Config API used by Backstage core, backend, and CLI", | ||
@@ -39,10 +39,18 @@ "backstage": { | ||
"dependencies": { | ||
"@backstage/errors": "^1.2.4", | ||
"@backstage/types": "^1.1.1" | ||
"@backstage/errors": "^1.2.5", | ||
"@backstage/types": "^1.2.0", | ||
"ms": "^2.1.3" | ||
}, | ||
"devDependencies": { | ||
"@backstage/cli": "^0.26.0", | ||
"@backstage/test-utils": "^1.5.1" | ||
"@backstage/cli": "^0.29.0", | ||
"@backstage/test-utils": "^1.7.1" | ||
}, | ||
"typesVersions": { | ||
"*": { | ||
"index": [ | ||
"dist/index.d.ts" | ||
] | ||
} | ||
}, | ||
"module": "dist/index.esm.js" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 4 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 2 instances in 1 package
140854
16
1369
0
327
15
15
43
3
4
1
+ Addedms@^2.1.3
+ Addedms@2.1.3(transitive)
Updated@backstage/errors@^1.2.5
Updated@backstage/types@^1.2.0