@backstage/config
Advanced tools
Comparing version 0.1.1-alpha.9 to 0.1.1-alpha.10
@@ -5,2 +5,7 @@ 'use strict'; | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var cloneDeep2 = _interopDefault(require('lodash/cloneDeep')); | ||
var mergeWith2 = _interopDefault(require('lodash/mergeWith')); | ||
const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; | ||
@@ -25,28 +30,77 @@ function isObject(value) { | ||
} | ||
const errors = { | ||
type(key, context, typeName, expected) { | ||
return `Invalid type in config for key '${key}' in '${context}', got ${typeName}, wanted ${expected}`; | ||
}, | ||
missing(key) { | ||
return `Missing required config value at '${key}'`; | ||
} | ||
}; | ||
class ConfigReader { | ||
constructor(data, fallback) { | ||
constructor(data, context = "empty-config", fallback, prefix = "") { | ||
this.data = data; | ||
this.context = context; | ||
this.fallback = fallback; | ||
this.prefix = prefix; | ||
} | ||
static fromConfigs(configs) { | ||
if (configs.length === 0) { | ||
return new ConfigReader({}); | ||
return new ConfigReader(void 0); | ||
} | ||
return configs.reduceRight((previousReader, nextConfig) => { | ||
return new ConfigReader(nextConfig, previousReader); | ||
return configs.reduceRight((previousReader, {data, context}) => { | ||
return new ConfigReader(data, context, previousReader); | ||
}, void 0); | ||
} | ||
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 [...new Set([...localKeys, ...fallbackKeys])]; | ||
} | ||
get(key) { | ||
const value = this.getOptional(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptional(key) { | ||
var _a; | ||
const value = this.readValue(key); | ||
const fallbackValue = (_a = this.fallback) == null ? void 0 : _a.getOptional(key); | ||
if (value === void 0) { | ||
return fallbackValue; | ||
} else if (fallbackValue === void 0) { | ||
return value; | ||
} | ||
return mergeWith2({}, {value: cloneDeep2(fallbackValue)}, {value}, (into, from) => !isObject(from) || !isObject(into) ? from : void 0).value; | ||
} | ||
getConfig(key) { | ||
const value = this.getOptionalConfig(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalConfig(key) { | ||
var _a; | ||
const value = this.readValue(key); | ||
const fallbackConfig = (_a = this.fallback) == null ? void 0 : _a.getConfig(key); | ||
const fallbackConfig = (_a = this.fallback) == null ? void 0 : _a.getOptionalConfig(key); | ||
const prefix = this.fullKey(key); | ||
if (isObject(value)) { | ||
return new ConfigReader(value, fallbackConfig); | ||
return new ConfigReader(value, this.context, fallbackConfig, prefix); | ||
} | ||
if (value !== void 0) { | ||
throw new TypeError(`Invalid type in config for key ${key}, got ${typeOf(value)}, wanted object`); | ||
throw new TypeError(errors.type(this.fullKey(key), this.context, typeOf(value), "object")); | ||
} | ||
return fallbackConfig != null ? fallbackConfig : ConfigReader.nullReader; | ||
return fallbackConfig; | ||
} | ||
getConfigArray(key) { | ||
const value = this.getOptionalConfigArray(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalConfigArray(key) { | ||
const configs = this.readConfigValue(key, (values) => { | ||
@@ -63,14 +117,45 @@ if (!Array.isArray(values)) { | ||
}); | ||
return (configs != null ? configs : []).map((obj) => new ConfigReader(obj)); | ||
if (!configs) { | ||
return void 0; | ||
} | ||
return configs.map((obj, index) => new ConfigReader(obj, this.context, void 0, this.fullKey(`${key}[${index}]`))); | ||
} | ||
getNumber(key) { | ||
const value = this.getOptionalNumber(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalNumber(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "number" || {expected: "number"}); | ||
} | ||
getBoolean(key) { | ||
const value = this.getOptionalBoolean(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalBoolean(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "boolean" || {expected: "boolean"}); | ||
} | ||
getString(key) { | ||
const value = this.getOptionalString(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalString(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "string" && value !== "" || {expected: "string"}); | ||
} | ||
getStringArray(key) { | ||
const value = this.getOptionalStringArray(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalStringArray(key) { | ||
return this.readConfigValue(key, (values) => { | ||
@@ -88,2 +173,5 @@ if (!Array.isArray(values)) { | ||
} | ||
fullKey(key) { | ||
return `${this.prefix}${this.prefix ? "." : ""}${key}`; | ||
} | ||
readConfigValue(key, validate) { | ||
@@ -99,4 +187,3 @@ var _a; | ||
const {key: keyName = key, value: theValue = value, expected} = result; | ||
const typeName = typeOf(theValue); | ||
throw new TypeError(`Invalid type in config for key ${keyName}, got ${typeName}, wanted ${expected}`); | ||
throw new TypeError(errors.type(this.fullKey(keyName), this.context, typeOf(theValue), expected)); | ||
} | ||
@@ -108,3 +195,2 @@ } | ||
const parts = key.split("."); | ||
let value = this.data; | ||
for (const part of parts) { | ||
@@ -114,6 +200,13 @@ if (!CONFIG_KEY_PART_PATTERN.test(part)) { | ||
} | ||
} | ||
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 { | ||
value = void 0; | ||
} else if (value !== void 0) { | ||
const badKey = this.fullKey(parts.slice(0, index).join(".")); | ||
throw new TypeError(errors.type(badKey, this.context, typeOf(value), "object")); | ||
} | ||
@@ -124,4 +217,3 @@ } | ||
} | ||
ConfigReader.nullReader = new ConfigReader({}); | ||
exports.ConfigReader = ConfigReader; |
declare type JsonObject = { | ||
[key in string]: JsonValue; | ||
[key in string]?: JsonValue; | ||
}; | ||
declare type JsonArray = JsonValue[]; | ||
declare type JsonValue = JsonObject | JsonArray | number | string | boolean | null; | ||
declare type AppConfig = JsonObject; | ||
declare type AppConfig = { | ||
context: string; | ||
data: JsonObject; | ||
}; | ||
declare type Config = { | ||
keys(): string[]; | ||
get(key: string): JsonValue; | ||
getOptional(key: string): JsonValue | undefined; | ||
getConfig(key: string): Config; | ||
getOptionalConfig(key: string): Config | undefined; | ||
getConfigArray(key: string): Config[]; | ||
getNumber(key: string): number | undefined; | ||
getBoolean(key: string): boolean | undefined; | ||
getString(key: string): string | undefined; | ||
getStringArray(key: string): string[] | undefined; | ||
getOptionalConfigArray(key: string): Config[] | undefined; | ||
getNumber(key: string): number; | ||
getOptionalNumber(key: string): number | undefined; | ||
getBoolean(key: string): boolean; | ||
getOptionalBoolean(key: string): boolean | undefined; | ||
getString(key: string): string; | ||
getOptionalString(key: string): string | undefined; | ||
getStringArray(key: string): string[]; | ||
getOptionalStringArray(key: string): string[] | undefined; | ||
}; | ||
@@ -18,12 +30,23 @@ | ||
private readonly data; | ||
private readonly context; | ||
private readonly fallback?; | ||
private static readonly nullReader; | ||
private readonly prefix; | ||
static fromConfigs(configs: AppConfig[]): ConfigReader; | ||
constructor(data: JsonObject, fallback?: ConfigReader | undefined); | ||
constructor(data: JsonObject | undefined, context?: string, fallback?: ConfigReader | undefined, prefix?: string); | ||
keys(): string[]; | ||
get(key: string): JsonValue; | ||
getOptional(key: string): JsonValue | undefined; | ||
getConfig(key: string): ConfigReader; | ||
getOptionalConfig(key: string): ConfigReader | undefined; | ||
getConfigArray(key: string): ConfigReader[]; | ||
getNumber(key: string): number | undefined; | ||
getBoolean(key: string): boolean | undefined; | ||
getString(key: string): string | undefined; | ||
getStringArray(key: string): string[] | undefined; | ||
getOptionalConfigArray(key: string): ConfigReader[] | undefined; | ||
getNumber(key: string): number; | ||
getOptionalNumber(key: string): number | undefined; | ||
getBoolean(key: string): boolean; | ||
getOptionalBoolean(key: string): boolean | undefined; | ||
getString(key: string): string; | ||
getOptionalString(key: string): string | undefined; | ||
getStringArray(key: string): string[]; | ||
getOptionalStringArray(key: string): string[] | undefined; | ||
private fullKey; | ||
private readConfigValue; | ||
@@ -30,0 +53,0 @@ private readValue; |
@@ -0,1 +1,4 @@ | ||
import cloneDeep2 from 'lodash/cloneDeep'; | ||
import mergeWith2 from 'lodash/mergeWith'; | ||
const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; | ||
@@ -20,28 +23,77 @@ function isObject(value) { | ||
} | ||
const errors = { | ||
type(key, context, typeName, expected) { | ||
return `Invalid type in config for key '${key}' in '${context}', got ${typeName}, wanted ${expected}`; | ||
}, | ||
missing(key) { | ||
return `Missing required config value at '${key}'`; | ||
} | ||
}; | ||
class ConfigReader { | ||
constructor(data, fallback) { | ||
constructor(data, context = "empty-config", fallback, prefix = "") { | ||
this.data = data; | ||
this.context = context; | ||
this.fallback = fallback; | ||
this.prefix = prefix; | ||
} | ||
static fromConfigs(configs) { | ||
if (configs.length === 0) { | ||
return new ConfigReader({}); | ||
return new ConfigReader(void 0); | ||
} | ||
return configs.reduceRight((previousReader, nextConfig) => { | ||
return new ConfigReader(nextConfig, previousReader); | ||
return configs.reduceRight((previousReader, {data, context}) => { | ||
return new ConfigReader(data, context, previousReader); | ||
}, void 0); | ||
} | ||
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 [...new Set([...localKeys, ...fallbackKeys])]; | ||
} | ||
get(key) { | ||
const value = this.getOptional(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptional(key) { | ||
var _a; | ||
const value = this.readValue(key); | ||
const fallbackValue = (_a = this.fallback) == null ? void 0 : _a.getOptional(key); | ||
if (value === void 0) { | ||
return fallbackValue; | ||
} else if (fallbackValue === void 0) { | ||
return value; | ||
} | ||
return mergeWith2({}, {value: cloneDeep2(fallbackValue)}, {value}, (into, from) => !isObject(from) || !isObject(into) ? from : void 0).value; | ||
} | ||
getConfig(key) { | ||
const value = this.getOptionalConfig(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalConfig(key) { | ||
var _a; | ||
const value = this.readValue(key); | ||
const fallbackConfig = (_a = this.fallback) == null ? void 0 : _a.getConfig(key); | ||
const fallbackConfig = (_a = this.fallback) == null ? void 0 : _a.getOptionalConfig(key); | ||
const prefix = this.fullKey(key); | ||
if (isObject(value)) { | ||
return new ConfigReader(value, fallbackConfig); | ||
return new ConfigReader(value, this.context, fallbackConfig, prefix); | ||
} | ||
if (value !== void 0) { | ||
throw new TypeError(`Invalid type in config for key ${key}, got ${typeOf(value)}, wanted object`); | ||
throw new TypeError(errors.type(this.fullKey(key), this.context, typeOf(value), "object")); | ||
} | ||
return fallbackConfig != null ? fallbackConfig : ConfigReader.nullReader; | ||
return fallbackConfig; | ||
} | ||
getConfigArray(key) { | ||
const value = this.getOptionalConfigArray(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalConfigArray(key) { | ||
const configs = this.readConfigValue(key, (values) => { | ||
@@ -58,14 +110,45 @@ if (!Array.isArray(values)) { | ||
}); | ||
return (configs != null ? configs : []).map((obj) => new ConfigReader(obj)); | ||
if (!configs) { | ||
return void 0; | ||
} | ||
return configs.map((obj, index) => new ConfigReader(obj, this.context, void 0, this.fullKey(`${key}[${index}]`))); | ||
} | ||
getNumber(key) { | ||
const value = this.getOptionalNumber(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalNumber(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "number" || {expected: "number"}); | ||
} | ||
getBoolean(key) { | ||
const value = this.getOptionalBoolean(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalBoolean(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "boolean" || {expected: "boolean"}); | ||
} | ||
getString(key) { | ||
const value = this.getOptionalString(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalString(key) { | ||
return this.readConfigValue(key, (value) => typeof value === "string" && value !== "" || {expected: "string"}); | ||
} | ||
getStringArray(key) { | ||
const value = this.getOptionalStringArray(key); | ||
if (value === void 0) { | ||
throw new Error(errors.missing(this.fullKey(key))); | ||
} | ||
return value; | ||
} | ||
getOptionalStringArray(key) { | ||
return this.readConfigValue(key, (values) => { | ||
@@ -83,2 +166,5 @@ if (!Array.isArray(values)) { | ||
} | ||
fullKey(key) { | ||
return `${this.prefix}${this.prefix ? "." : ""}${key}`; | ||
} | ||
readConfigValue(key, validate) { | ||
@@ -94,4 +180,3 @@ var _a; | ||
const {key: keyName = key, value: theValue = value, expected} = result; | ||
const typeName = typeOf(theValue); | ||
throw new TypeError(`Invalid type in config for key ${keyName}, got ${typeName}, wanted ${expected}`); | ||
throw new TypeError(errors.type(this.fullKey(keyName), this.context, typeOf(theValue), expected)); | ||
} | ||
@@ -103,3 +188,2 @@ } | ||
const parts = key.split("."); | ||
let value = this.data; | ||
for (const part of parts) { | ||
@@ -109,6 +193,13 @@ if (!CONFIG_KEY_PART_PATTERN.test(part)) { | ||
} | ||
} | ||
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 { | ||
value = void 0; | ||
} else if (value !== void 0) { | ||
const badKey = this.fullKey(parts.slice(0, index).join(".")); | ||
throw new TypeError(errors.type(badKey, this.context, typeOf(value), "object")); | ||
} | ||
@@ -119,4 +210,3 @@ } | ||
} | ||
ConfigReader.nullReader = new ConfigReader({}); | ||
export { ConfigReader }; |
{ | ||
"name": "@backstage/config", | ||
"description": "Config API used by Backstage core, backend, and CLI", | ||
"version": "0.1.1-alpha.9", | ||
"version": "0.1.1-alpha.10", | ||
"private": false, | ||
@@ -32,2 +32,5 @@ "publishConfig": { | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.15" | ||
}, | ||
"devDependencies": { | ||
@@ -40,4 +43,4 @@ "@types/jest": "^25.2.2", | ||
], | ||
"gitHead": "73898f3084733d5c88c5a65d9989838a86a861e5", | ||
"gitHead": "1253f82057eeb5f59b4e7c9a1f5a186a0b3c229f", | ||
"module": "dist/index.esm.js" | ||
} |
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
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
27792
462
1
137159
326
14
14
42
+ Addedlodash@^4.17.15
+ Addedlodash@4.17.21(transitive)