@backstage/config-loader 0.8.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @backstage/config-loader
2
2
 
3
+ ## 0.9.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/config@0.1.12
9
+ - @backstage/errors@0.2.0
10
+
11
+ ## 0.9.1
12
+
13
+ ### Patch Changes
14
+
15
+ - 84663d59a3: Bump `typescript-json-schema` from `^0.51.0` to `^0.52.0`.
16
+
17
+ ## 0.9.0
18
+
19
+ ### Minor Changes
20
+
21
+ - f6722d2458: Removed deprecated option `env` from `LoadConfigOptions` and associated tests
22
+ - 67d6cb3c7e: Removed deprecated option `configPaths` as it has been superseded by `configTargets`
23
+
24
+ ### Patch Changes
25
+
26
+ - 1e7070443d: In case remote.reloadIntervalSeconds is passed, it must be a valid positive value
27
+
28
+ ## 0.8.1
29
+
30
+ ### Patch Changes
31
+
32
+ - b055a6addc: Align on usage of `cross-fetch` vs `node-fetch` in frontend vs backend packages, and remove some unnecessary imports of either one of them
33
+ - 4bea7b81d3: Uses key visibility as fallback in non-object arrays
34
+
3
35
  ## 0.8.0
4
36
 
5
37
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -64,7 +64,7 @@ function readEnvConfig(env) {
64
64
  }
65
65
  }
66
66
  }
67
- return data ? [{data, context: "env"}] : [];
67
+ return data ? [{ data, context: "env" }] : [];
68
68
  }
69
69
  function safeJsonParse(str) {
70
70
  try {
@@ -139,13 +139,13 @@ async function applyConfigTransforms(initialDir, input, transforms) {
139
139
 
140
140
  const includeFileParser = {
141
141
  ".json": async (content) => JSON.parse(content),
142
- ".yaml": async (content) => yaml__default['default'].parse(content),
143
- ".yml": async (content) => yaml__default['default'].parse(content)
142
+ ".yaml": async (content) => yaml__default["default"].parse(content),
143
+ ".yml": async (content) => yaml__default["default"].parse(content)
144
144
  };
145
145
  function createIncludeTransform(env, readFile, substitute) {
146
146
  return async (input, baseDir) => {
147
147
  if (!isObject(input)) {
148
- return {applied: false};
148
+ return { applied: false };
149
149
  }
150
150
  const [includeKey] = Object.keys(input).filter((key) => key.startsWith("$"));
151
151
  if (includeKey) {
@@ -153,7 +153,7 @@ function createIncludeTransform(env, readFile, substitute) {
153
153
  throw new Error(`include key ${includeKey} should not have adjacent keys`);
154
154
  }
155
155
  } else {
156
- return {applied: false};
156
+ return { applied: false };
157
157
  }
158
158
  const rawIncludedValue = input[includeKey];
159
159
  if (typeof rawIncludedValue !== "string") {
@@ -168,13 +168,13 @@ function createIncludeTransform(env, readFile, substitute) {
168
168
  case "$file":
169
169
  try {
170
170
  const value = await readFile(path.resolve(baseDir, includeValue));
171
- return {applied: true, value};
171
+ return { applied: true, value };
172
172
  } catch (error) {
173
173
  throw new Error(`failed to read file ${includeValue}, ${error}`);
174
174
  }
175
175
  case "$env":
176
176
  try {
177
- return {applied: true, value: await env(includeValue)};
177
+ return { applied: true, value: await env(includeValue) };
178
178
  } catch (error) {
179
179
  throw new Error(`failed to read env ${includeValue}, ${error}`);
180
180
  }
@@ -217,7 +217,7 @@ function createIncludeTransform(env, readFile, substitute) {
217
217
  function createSubstitutionTransform(env) {
218
218
  return async (input) => {
219
219
  if (typeof input !== "string") {
220
- return {applied: false};
220
+ return { applied: false };
221
221
  }
222
222
  const parts = input.split(/(\$?\$\{[^{}]*\})/);
223
223
  for (let i = 1; i < parts.length; i += 2) {
@@ -229,9 +229,9 @@ function createSubstitutionTransform(env) {
229
229
  }
230
230
  }
231
231
  if (parts.some((part) => part === void 0)) {
232
- return {applied: true, value: void 0};
232
+ return { applied: true, value: void 0 };
233
233
  }
234
- return {applied: true, value: parts.join("")};
234
+ return { applied: true, value: parts.join("") };
235
235
  };
236
236
  }
237
237
 
@@ -239,8 +239,8 @@ const CONFIG_VISIBILITIES = ["frontend", "backend", "secret"];
239
239
  const DEFAULT_CONFIG_VISIBILITY = "backend";
240
240
 
241
241
  function compileConfigSchemas(schemas) {
242
- const visibilityByDataPath = new Map();
243
- const ajv = new Ajv__default['default']({
242
+ const visibilityByDataPath = /* @__PURE__ */ new Map();
243
+ const ajv = new Ajv__default["default"]({
244
244
  allErrors: true,
245
245
  allowUnionTypes: true,
246
246
  schemas: {
@@ -274,8 +274,8 @@ function compileConfigSchemas(schemas) {
274
274
  }
275
275
  const merged = mergeConfigSchemas(schemas.map((_) => _.value));
276
276
  const validate = ajv.compile(merged);
277
- const visibilityBySchemaPath = new Map();
278
- traverse__default['default'](merged, (schema, path) => {
277
+ const visibilityBySchemaPath = /* @__PURE__ */ new Map();
278
+ traverse__default["default"](merged, (schema, path) => {
279
279
  if (schema.visibility && schema.visibility !== "backend") {
280
280
  visibilityBySchemaPath.set(path, schema.visibility);
281
281
  }
@@ -299,7 +299,7 @@ function compileConfigSchemas(schemas) {
299
299
  };
300
300
  }
301
301
  function mergeConfigSchemas(schemas) {
302
- const merged = mergeAllOf__default['default']({allOf: schemas}, {
302
+ const merged = mergeAllOf__default["default"]({ allOf: schemas }, {
303
303
  ignoreAdditionalProperties: true,
304
304
  resolvers: {
305
305
  visibility(values, path) {
@@ -323,18 +323,18 @@ const req = typeof __non_webpack_require__ === "undefined" ? require : __non_web
323
323
  async function collectConfigSchemas(packageNames, packagePaths) {
324
324
  const schemas = new Array();
325
325
  const tsSchemaPaths = new Array();
326
- const visitedPackageVersions = new Map();
327
- const currentDir = await fs__default['default'].realpath(process.cwd());
326
+ const visitedPackageVersions = /* @__PURE__ */ new Map();
327
+ const currentDir = await fs__default["default"].realpath(process.cwd());
328
328
  async function processItem(item) {
329
329
  var _a, _b, _c, _d;
330
330
  let pkgPath = item.packagePath;
331
331
  if (pkgPath) {
332
- const pkgExists = await fs__default['default'].pathExists(pkgPath);
332
+ const pkgExists = await fs__default["default"].pathExists(pkgPath);
333
333
  if (!pkgExists) {
334
334
  return;
335
335
  }
336
336
  } else if (item.name) {
337
- const {name, parentPath} = item;
337
+ const { name, parentPath } = item;
338
338
  try {
339
339
  pkgPath = req.resolve(`${name}/package.json`, parentPath && {
340
340
  paths: [parentPath]
@@ -345,13 +345,13 @@ async function collectConfigSchemas(packageNames, packagePaths) {
345
345
  if (!pkgPath) {
346
346
  return;
347
347
  }
348
- const pkg = await fs__default['default'].readJson(pkgPath);
348
+ const pkg = await fs__default["default"].readJson(pkgPath);
349
349
  let versions = visitedPackageVersions.get(pkg.name);
350
350
  if (versions == null ? void 0 : versions.has(pkg.version)) {
351
351
  return;
352
352
  }
353
353
  if (!versions) {
354
- versions = new Set();
354
+ versions = /* @__PURE__ */ new Set();
355
355
  visitedPackageVersions.set(pkg.name, versions);
356
356
  }
357
357
  versions.add(pkg.version);
@@ -377,7 +377,7 @@ async function collectConfigSchemas(packageNames, packagePaths) {
377
377
  tsSchemaPaths.push(path.relative(currentDir, path.resolve(path.dirname(pkgPath), pkg.configSchema)));
378
378
  } else {
379
379
  const path$1 = path.resolve(path.dirname(pkgPath), pkg.configSchema);
380
- const value = await fs__default['default'].readJson(path$1);
380
+ const value = await fs__default["default"].readJson(path$1);
381
381
  schemas.push({
382
382
  value,
383
383
  path: path.relative(currentDir, path$1)
@@ -390,11 +390,11 @@ async function collectConfigSchemas(packageNames, packagePaths) {
390
390
  });
391
391
  }
392
392
  }
393
- await Promise.all(depNames.map((depName) => processItem({name: depName, parentPath: pkgPath})));
393
+ await Promise.all(depNames.map((depName) => processItem({ name: depName, parentPath: pkgPath })));
394
394
  }
395
395
  await Promise.all([
396
- ...packageNames.map((name) => processItem({name, parentPath: currentDir})),
397
- ...packagePaths.map((path) => processItem({name: path, packagePath: path}))
396
+ ...packageNames.map((name) => processItem({ name, parentPath: currentDir })),
397
+ ...packagePaths.map((path) => processItem({ name: path, packagePath: path }))
398
398
  ]);
399
399
  const tsSchemas = compileTsSchemas(tsSchemaPaths);
400
400
  return schemas.concat(tsSchemas);
@@ -431,7 +431,7 @@ function compileTsSchemas(paths) {
431
431
  if (!value) {
432
432
  throw new Error(`Invalid schema in ${path$1}, missing Config export`);
433
433
  }
434
- return {path: path$1, value};
434
+ return { path: path$1, value };
435
435
  });
436
436
  return tsSchemas;
437
437
  }
@@ -446,7 +446,7 @@ function filterByVisibility(data, includeVisibilities, visibilityByDataPath, tra
446
446
  if (typeof jsonVal !== "object") {
447
447
  if (isVisible) {
448
448
  if (transformFunc) {
449
- return transformFunc(jsonVal, {visibility});
449
+ return transformFunc(jsonVal, { visibility });
450
450
  }
451
451
  return jsonVal;
452
452
  }
@@ -459,7 +459,12 @@ function filterByVisibility(data, includeVisibilities, visibilityByDataPath, tra
459
459
  } else if (Array.isArray(jsonVal)) {
460
460
  const arr = new Array();
461
461
  for (const [index, value] of jsonVal.entries()) {
462
- const out = transform(value, `${visibilityPath}/${index}`, `${filterPath}[${index}]`);
462
+ let path = visibilityPath;
463
+ const hasVisibilityInIndex = visibilityByDataPath.get(`${visibilityPath}/${index}`);
464
+ if (hasVisibilityInIndex || typeof value === "object") {
465
+ path = `${visibilityPath}/${index}`;
466
+ }
467
+ const out = transform(value, path, `${filterPath}[${index}]`);
463
468
  if (out !== void 0) {
464
469
  arr.push(out);
465
470
  }
@@ -517,7 +522,7 @@ function filterErrorsByVisibility(errors, includeVisibilities, visibilityByDataP
517
522
  }
518
523
 
519
524
  function errorsToError(errors) {
520
- const messages = errors.map(({dataPath, message, params}) => {
525
+ const messages = errors.map(({ dataPath, message, params }) => {
521
526
  const paramStr = Object.entries(params).map(([name, value]) => `${name}=${value}`).join(" ");
522
527
  return `Config ${message || ""} { ${paramStr} } at ${dataPath}`;
523
528
  });
@@ -531,7 +536,7 @@ async function loadConfigSchema(options) {
531
536
  if ("dependencies" in options) {
532
537
  schemas = await collectConfigSchemas(options.dependencies, (_a = options.packagePaths) != null ? _a : []);
533
538
  } else {
534
- const {serialized} = options;
539
+ const { serialized } = options;
535
540
  if ((serialized == null ? void 0 : serialized.backstageConfigSchemaVersion) !== 1) {
536
541
  throw new Error("Serialized configuration schema is invalid or has an invalid version number");
537
542
  }
@@ -539,7 +544,7 @@ async function loadConfigSchema(options) {
539
544
  }
540
545
  const validate = compileConfigSchemas(schemas);
541
546
  return {
542
- process(configs, {visibility, valueTransform, withFilteredKeys} = {}) {
547
+ process(configs, { visibility, valueTransform, withFilteredKeys } = {}) {
543
548
  const result = validate(configs);
544
549
  const visibleErrors = filterErrorsByVisibility(result.errors, visibility, result.visibilityByDataPath, result.visibilityBySchemaPath);
545
550
  if (visibleErrors.length > 0) {
@@ -547,12 +552,12 @@ async function loadConfigSchema(options) {
547
552
  }
548
553
  let processedConfigs = configs;
549
554
  if (visibility) {
550
- processedConfigs = processedConfigs.map(({data, context}) => ({
555
+ processedConfigs = processedConfigs.map(({ data, context }) => ({
551
556
  context,
552
557
  ...filterByVisibility(data, visibility, result.visibilityByDataPath, valueTransform, withFilteredKeys)
553
558
  }));
554
559
  } else if (valueTransform) {
555
- processedConfigs = processedConfigs.map(({data, context}) => ({
560
+ processedConfigs = processedConfigs.map(({ data, context }) => ({
556
561
  context,
557
562
  ...filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByDataPath, valueTransform, withFilteredKeys)
558
563
  }));
@@ -578,21 +583,20 @@ function isValidUrl(url) {
578
583
  }
579
584
 
580
585
  async function loadConfig(options) {
581
- const {configRoot, experimentalEnvFunc: envFunc, watch, remote} = options;
586
+ const { configRoot, experimentalEnvFunc: envFunc, watch, remote } = options;
582
587
  const configPaths = options.configTargets.slice().filter((e) => e.hasOwnProperty("path")).map((configTarget) => configTarget.path);
583
- options.configPaths.forEach((cp) => {
584
- if (!configPaths.includes(cp)) {
585
- configPaths.push(cp);
586
- }
587
- });
588
588
  const configUrls = options.configTargets.slice().filter((e) => e.hasOwnProperty("url")).map((configTarget) => configTarget.url);
589
- if (remote === void 0 && configUrls.length > 0) {
590
- throw new Error(`Remote config detected but this feature is turned off`);
589
+ if (remote === void 0) {
590
+ if (configUrls.length > 0) {
591
+ throw new Error(`Please make sure you are passing the remote option when loading remote configurations. See https://backstage.io/docs/conf/writing#configuration-files for detailed info.`);
592
+ }
593
+ } else if (remote.reloadIntervalSeconds <= 0) {
594
+ throw new Error(`Remote config must be contain a non zero reloadIntervalSeconds: <seconds> value`);
591
595
  }
592
596
  if (configPaths.length === 0 && configUrls.length === 0) {
593
597
  configPaths.push(path.resolve(configRoot, "app-config.yaml"));
594
598
  const localConfig = path.resolve(configRoot, "app-config.local.yaml");
595
- if (await fs__default['default'].pathExists(localConfig)) {
599
+ if (await fs__default["default"].pathExists(localConfig)) {
596
600
  configPaths.push(localConfig);
597
601
  }
598
602
  }
@@ -604,21 +608,21 @@ async function loadConfig(options) {
604
608
  throw new Error(`Config load path is not absolute: '${configPath}'`);
605
609
  }
606
610
  const dir = path.dirname(configPath);
607
- const readFile = (path$1) => fs__default['default'].readFile(path.resolve(dir, path$1), "utf8");
608
- const input = yaml__default['default'].parse(await readFile(configPath));
611
+ const readFile = (path$1) => fs__default["default"].readFile(path.resolve(dir, path$1), "utf8");
612
+ const input = yaml__default["default"].parse(await readFile(configPath));
609
613
  const substitutionTransform = createSubstitutionTransform(env);
610
614
  const data = await applyConfigTransforms(dir, input, [
611
615
  createIncludeTransform(env, readFile, substitutionTransform),
612
616
  substitutionTransform
613
617
  ]);
614
- configs.push({data, context: path.basename(configPath)});
618
+ configs.push({ data, context: path.basename(configPath) });
615
619
  }
616
620
  return configs;
617
621
  };
618
622
  const loadRemoteConfigFiles = async () => {
619
623
  const configs = [];
620
624
  const readConfigFromUrl = async (url) => {
621
- const response = await fetch__default['default'](url);
625
+ const response = await fetch__default["default"](url);
622
626
  if (!response.ok) {
623
627
  throw new Error(`Could not read config file at ${url}`);
624
628
  }
@@ -633,12 +637,12 @@ async function loadConfig(options) {
633
637
  if (!remoteConfigContent) {
634
638
  throw new Error(`Config is not valid`);
635
639
  }
636
- const configYaml = yaml__default['default'].parse(remoteConfigContent);
640
+ const configYaml = yaml__default["default"].parse(remoteConfigContent);
637
641
  const substitutionTransform = createSubstitutionTransform(env);
638
642
  const data = await applyConfigTransforms(configRoot, configYaml, [
639
643
  substitutionTransform
640
644
  ]);
641
- configs.push({data, context: configUrl});
645
+ configs.push({ data, context: configUrl });
642
646
  }
643
647
  return configs;
644
648
  };
@@ -658,7 +662,7 @@ async function loadConfig(options) {
658
662
  }
659
663
  const envConfigs = await readEnvConfig(process.env);
660
664
  const watchConfigFile = (watchProp) => {
661
- const watcher = chokidar__default['default'].watch(configPaths, {
665
+ const watcher = chokidar__default["default"].watch(configPaths, {
662
666
  usePolling: process.env.NODE_ENV === "test"
663
667
  });
664
668
  let currentSerializedConfig = JSON.stringify(fileConfigs);