@backstage/config-loader
Advanced tools
Comparing version 0.0.0-nightly-202161521855 to 0.0.0-nightly-20217172188
# @backstage/config-loader | ||
## 0.0.0-nightly-202161521855 | ||
## 0.0.0-nightly-20217172188 | ||
### Patch Changes | ||
- 9b8cec063: Add support for config file watching through a new group of `watch` options to `loadConfig`. | ||
- Updated dependencies | ||
- @backstage/config@0.0.0-nightly-20217172188 | ||
## 0.6.6 | ||
### Patch Changes | ||
- e9d3983ee: Add option to populate the `filteredKeys` property when processing configuration with a schema. | ||
- Updated dependencies | ||
- @backstage/config@0.1.6 | ||
## 0.6.5 | ||
### Patch Changes | ||
- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. | ||
@@ -8,0 +24,0 @@ |
@@ -12,2 +12,3 @@ 'use strict'; | ||
var typescriptJsonSchema = require('typescript-json-schema'); | ||
var chokidar = require('chokidar'); | ||
@@ -20,2 +21,3 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); | ||
var chokidar__default = /*#__PURE__*/_interopDefaultLegacy(chokidar); | ||
@@ -407,7 +409,8 @@ const ENV_PREFIX = "APP_CONFIG_"; | ||
function filterByVisibility(data, includeVisibilities, visibilityByPath, transformFunc) { | ||
function filterByVisibility(data, includeVisibilities, visibilityByPath, transformFunc, withFilteredKeys) { | ||
var _a; | ||
function transform(jsonVal, path) { | ||
const filteredKeys = new Array(); | ||
function transform(jsonVal, visibilityPath, filterPath) { | ||
var _a2; | ||
const visibility = (_a2 = visibilityByPath.get(path)) != null ? _a2 : DEFAULT_CONFIG_VISIBILITY; | ||
const visibility = (_a2 = visibilityByPath.get(visibilityPath)) != null ? _a2 : DEFAULT_CONFIG_VISIBILITY; | ||
const isVisible = includeVisibilities.includes(visibility); | ||
@@ -421,2 +424,5 @@ if (typeof jsonVal !== "object") { | ||
} | ||
if (withFilteredKeys) { | ||
filteredKeys.push(filterPath); | ||
} | ||
return void 0; | ||
@@ -428,3 +434,3 @@ } else if (jsonVal === null) { | ||
for (const [index, value] of jsonVal.entries()) { | ||
const out = transform(value, `${path}/${index}`); | ||
const out = transform(value, `${visibilityPath}/${index}`, `${filterPath}[${index}]`); | ||
if (out !== void 0) { | ||
@@ -445,3 +451,3 @@ arr.push(out); | ||
} | ||
const out = transform(value, `${path}/${key}`); | ||
const out = transform(value, `${visibilityPath}/${key}`, filterPath ? `${filterPath}.${key}` : key); | ||
if (out !== void 0) { | ||
@@ -457,3 +463,6 @@ outObj[key] = out; | ||
} | ||
return (_a = transform(data, "")) != null ? _a : {}; | ||
return { | ||
filteredKeys: withFilteredKeys ? filteredKeys : void 0, | ||
data: (_a = transform(data, "", "")) != null ? _a : {} | ||
}; | ||
} | ||
@@ -474,3 +483,3 @@ | ||
return { | ||
process(configs, {visibility, valueTransform} = {}) { | ||
process(configs, {visibility, valueTransform, withFilteredKeys} = {}) { | ||
const result = validate(configs); | ||
@@ -486,3 +495,3 @@ if (result.errors) { | ||
context, | ||
data: filterByVisibility(data, visibility, result.visibilityByPath, valueTransform) | ||
...filterByVisibility(data, visibility, result.visibilityByPath, valueTransform, withFilteredKeys) | ||
})); | ||
@@ -492,3 +501,3 @@ } else if (valueTransform) { | ||
context, | ||
data: filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByPath, valueTransform) | ||
...filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByPath, valueTransform, withFilteredKeys) | ||
})); | ||
@@ -508,4 +517,3 @@ } | ||
async function loadConfig(options) { | ||
const configs = []; | ||
const {configRoot, experimentalEnvFunc: envFunc} = options; | ||
const {configRoot, experimentalEnvFunc: envFunc, watch} = options; | ||
const configPaths = options.configPaths.slice(); | ||
@@ -520,3 +528,4 @@ if (configPaths.length === 0) { | ||
const env = envFunc != null ? envFunc : async (name) => process.env[name]; | ||
try { | ||
const loadConfigFiles = async () => { | ||
const configs = []; | ||
for (const configPath of configPaths) { | ||
@@ -536,7 +545,36 @@ if (!path.isAbsolute(configPath)) { | ||
} | ||
return configs; | ||
}; | ||
let fileConfigs; | ||
try { | ||
fileConfigs = await loadConfigFiles(); | ||
} catch (error) { | ||
throw new Error(`Failed to read static configuration file, ${error.message}`); | ||
} | ||
configs.push(...readEnvConfig(process.env)); | ||
return configs; | ||
const envConfigs = await readEnvConfig(process.env); | ||
if (watch) { | ||
let currentSerializedConfig = JSON.stringify(fileConfigs); | ||
const watcher = chokidar__default['default'].watch(configPaths, { | ||
usePolling: process.env.NODE_ENV === "test" | ||
}); | ||
watcher.on("change", async () => { | ||
try { | ||
const newConfigs = await loadConfigFiles(); | ||
const newSerializedConfig = JSON.stringify(newConfigs); | ||
if (currentSerializedConfig === newSerializedConfig) { | ||
return; | ||
} | ||
currentSerializedConfig = newSerializedConfig; | ||
watch.onChange([...newConfigs, ...envConfigs]); | ||
} catch (error) { | ||
console.error(`Failed to reload configuration files, ${error}`); | ||
} | ||
}); | ||
if (watch.stopSignal) { | ||
watch.stopSignal.then(() => { | ||
watcher.close(); | ||
}); | ||
} | ||
} | ||
return [...fileConfigs, ...envConfigs]; | ||
} | ||
@@ -543,0 +581,0 @@ |
@@ -58,2 +58,8 @@ import { AppConfig, JsonObject } from '@backstage/config'; | ||
valueTransform?: TransformFunc<any>; | ||
/** | ||
* Whether or not to include the `filteredKeys` property in the output `AppConfig`s. | ||
* | ||
* Default: `false`. | ||
*/ | ||
withFilteredKeys?: boolean; | ||
}; | ||
@@ -95,2 +101,15 @@ /** | ||
experimentalEnvFunc?: EnvFunc; | ||
/** | ||
* An optional configuration that enables watching of config files. | ||
*/ | ||
watch?: { | ||
/** | ||
* A listener that is called when a config file is changed. | ||
*/ | ||
onChange: (configs: AppConfig[]) => void; | ||
/** | ||
* An optional signal that stops the watcher once the promise resolves. | ||
*/ | ||
stopSignal?: Promise<void>; | ||
}; | ||
}; | ||
@@ -97,0 +116,0 @@ declare function loadConfig(options: LoadConfigOptions): Promise<AppConfig[]>; |
@@ -8,2 +8,3 @@ import yaml from 'yaml'; | ||
import { getProgramFromFiles, generateSchema } from 'typescript-json-schema'; | ||
import chokidar from 'chokidar'; | ||
@@ -395,7 +396,8 @@ const ENV_PREFIX = "APP_CONFIG_"; | ||
function filterByVisibility(data, includeVisibilities, visibilityByPath, transformFunc) { | ||
function filterByVisibility(data, includeVisibilities, visibilityByPath, transformFunc, withFilteredKeys) { | ||
var _a; | ||
function transform(jsonVal, path) { | ||
const filteredKeys = new Array(); | ||
function transform(jsonVal, visibilityPath, filterPath) { | ||
var _a2; | ||
const visibility = (_a2 = visibilityByPath.get(path)) != null ? _a2 : DEFAULT_CONFIG_VISIBILITY; | ||
const visibility = (_a2 = visibilityByPath.get(visibilityPath)) != null ? _a2 : DEFAULT_CONFIG_VISIBILITY; | ||
const isVisible = includeVisibilities.includes(visibility); | ||
@@ -409,2 +411,5 @@ if (typeof jsonVal !== "object") { | ||
} | ||
if (withFilteredKeys) { | ||
filteredKeys.push(filterPath); | ||
} | ||
return void 0; | ||
@@ -416,3 +421,3 @@ } else if (jsonVal === null) { | ||
for (const [index, value] of jsonVal.entries()) { | ||
const out = transform(value, `${path}/${index}`); | ||
const out = transform(value, `${visibilityPath}/${index}`, `${filterPath}[${index}]`); | ||
if (out !== void 0) { | ||
@@ -433,3 +438,3 @@ arr.push(out); | ||
} | ||
const out = transform(value, `${path}/${key}`); | ||
const out = transform(value, `${visibilityPath}/${key}`, filterPath ? `${filterPath}.${key}` : key); | ||
if (out !== void 0) { | ||
@@ -445,3 +450,6 @@ outObj[key] = out; | ||
} | ||
return (_a = transform(data, "")) != null ? _a : {}; | ||
return { | ||
filteredKeys: withFilteredKeys ? filteredKeys : void 0, | ||
data: (_a = transform(data, "", "")) != null ? _a : {} | ||
}; | ||
} | ||
@@ -462,3 +470,3 @@ | ||
return { | ||
process(configs, {visibility, valueTransform} = {}) { | ||
process(configs, {visibility, valueTransform, withFilteredKeys} = {}) { | ||
const result = validate(configs); | ||
@@ -474,3 +482,3 @@ if (result.errors) { | ||
context, | ||
data: filterByVisibility(data, visibility, result.visibilityByPath, valueTransform) | ||
...filterByVisibility(data, visibility, result.visibilityByPath, valueTransform, withFilteredKeys) | ||
})); | ||
@@ -480,3 +488,3 @@ } else if (valueTransform) { | ||
context, | ||
data: filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByPath, valueTransform) | ||
...filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByPath, valueTransform, withFilteredKeys) | ||
})); | ||
@@ -496,4 +504,3 @@ } | ||
async function loadConfig(options) { | ||
const configs = []; | ||
const {configRoot, experimentalEnvFunc: envFunc} = options; | ||
const {configRoot, experimentalEnvFunc: envFunc, watch} = options; | ||
const configPaths = options.configPaths.slice(); | ||
@@ -508,3 +515,4 @@ if (configPaths.length === 0) { | ||
const env = envFunc != null ? envFunc : async (name) => process.env[name]; | ||
try { | ||
const loadConfigFiles = async () => { | ||
const configs = []; | ||
for (const configPath of configPaths) { | ||
@@ -524,7 +532,36 @@ if (!isAbsolute(configPath)) { | ||
} | ||
return configs; | ||
}; | ||
let fileConfigs; | ||
try { | ||
fileConfigs = await loadConfigFiles(); | ||
} catch (error) { | ||
throw new Error(`Failed to read static configuration file, ${error.message}`); | ||
} | ||
configs.push(...readEnvConfig(process.env)); | ||
return configs; | ||
const envConfigs = await readEnvConfig(process.env); | ||
if (watch) { | ||
let currentSerializedConfig = JSON.stringify(fileConfigs); | ||
const watcher = chokidar.watch(configPaths, { | ||
usePolling: process.env.NODE_ENV === "test" | ||
}); | ||
watcher.on("change", async () => { | ||
try { | ||
const newConfigs = await loadConfigFiles(); | ||
const newSerializedConfig = JSON.stringify(newConfigs); | ||
if (currentSerializedConfig === newSerializedConfig) { | ||
return; | ||
} | ||
currentSerializedConfig = newSerializedConfig; | ||
watch.onChange([...newConfigs, ...envConfigs]); | ||
} catch (error) { | ||
console.error(`Failed to reload configuration files, ${error}`); | ||
} | ||
}); | ||
if (watch.stopSignal) { | ||
watch.stopSignal.then(() => { | ||
watcher.close(); | ||
}); | ||
} | ||
} | ||
return [...fileConfigs, ...envConfigs]; | ||
} | ||
@@ -531,0 +568,0 @@ |
{ | ||
"name": "@backstage/config-loader", | ||
"description": "Config loading functionality used by Backstage backend, and CLI", | ||
"version": "0.0.0-nightly-202161521855", | ||
"version": "0.0.0-nightly-20217172188", | ||
"private": false, | ||
@@ -34,5 +34,6 @@ "publishConfig": { | ||
"@backstage/cli-common": "^0.1.1", | ||
"@backstage/config": "^0.1.5", | ||
"@backstage/config": "^0.0.0-nightly-20217172188", | ||
"@types/json-schema": "^7.0.6", | ||
"ajv": "^7.0.3", | ||
"chokidar": "^3.5.2", | ||
"fs-extra": "9.1.0", | ||
@@ -39,0 +40,0 @@ "json-schema": "^0.3.0", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
146933
1212
11
10
+ Addedchokidar@^3.5.2
+ Added@backstage/config@0.0.0-nightly-20250114022708(transitive)
+ Added@backstage/errors@0.0.0-nightly-20250114022708(transitive)
+ Added@backstage/types@0.0.0-nightly-20250114022708(transitive)
+ Addedanymatch@3.1.3(transitive)
+ Addedbinary-extensions@2.3.0(transitive)
+ Addedbraces@3.0.3(transitive)
+ Addedchokidar@3.6.0(transitive)
+ Addedfill-range@7.1.1(transitive)
+ Addedfsevents@2.3.3(transitive)
+ Addedglob-parent@5.1.2(transitive)
+ Addedis-binary-path@2.1.0(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-number@7.0.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addednormalize-path@3.0.0(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedreaddirp@3.6.0(transitive)
+ Addedserialize-error@8.1.0(transitive)
+ Addedto-regex-range@5.0.1(transitive)
+ Addedtype-fest@0.20.2(transitive)
- Removed@backstage/config@0.1.15(transitive)
- Removed@backstage/types@0.1.3(transitive)