@backstage/config-loader 0.7.1 → 0.9.0
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 +46 -0
- package/dist/index.cjs.js +143 -47
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +39 -15
- package/dist/index.esm.js +130 -35
- package/dist/index.esm.js.map +1 -1
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @backstage/config-loader
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- f6722d2458: Removed deprecated option `env` from `LoadConfigOptions` and associated tests
|
|
8
|
+
- 67d6cb3c7e: Removed deprecated option `configPaths` as it has been superseded by `configTargets`
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- 1e7070443d: In case remote.reloadIntervalSeconds is passed, it must be a valid positive value
|
|
13
|
+
|
|
14
|
+
## 0.8.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 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
|
|
19
|
+
- 4bea7b81d3: Uses key visibility as fallback in non-object arrays
|
|
20
|
+
|
|
21
|
+
## 0.8.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 1e99c73c75: Update `loadConfig` to return `LoadConfigResult` instead of an array of `AppConfig`.
|
|
26
|
+
|
|
27
|
+
This function is primarily used internally by other config loaders like `loadBackendConfig` which means no changes are required for most users.
|
|
28
|
+
|
|
29
|
+
If you use `loadConfig` directly you will need to update your usage from:
|
|
30
|
+
|
|
31
|
+
```diff
|
|
32
|
+
- const appConfigs = await loadConfig(options)
|
|
33
|
+
+ const { appConfigs } = await loadConfig(options)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- 8809b6c0dd: Update the json-schema dependency version.
|
|
39
|
+
- Updated dependencies
|
|
40
|
+
- @backstage/cli-common@0.1.6
|
|
41
|
+
|
|
42
|
+
## 0.7.2
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- 0611f3b3e2: Reading app config from a remote server
|
|
47
|
+
- 26c5659c97: Bump msw to the same version as the rest
|
|
48
|
+
|
|
3
49
|
## 0.7.1
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -12,6 +12,7 @@ var config = require('@backstage/config');
|
|
|
12
12
|
var fs = require('fs-extra');
|
|
13
13
|
var typescriptJsonSchema = require('typescript-json-schema');
|
|
14
14
|
var chokidar = require('chokidar');
|
|
15
|
+
var fetch = require('node-fetch');
|
|
15
16
|
|
|
16
17
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
17
18
|
|
|
@@ -21,6 +22,7 @@ var mergeAllOf__default = /*#__PURE__*/_interopDefaultLegacy(mergeAllOf);
|
|
|
21
22
|
var traverse__default = /*#__PURE__*/_interopDefaultLegacy(traverse);
|
|
22
23
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
23
24
|
var chokidar__default = /*#__PURE__*/_interopDefaultLegacy(chokidar);
|
|
25
|
+
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
24
26
|
|
|
25
27
|
const ENV_PREFIX = "APP_CONFIG_";
|
|
26
28
|
const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i;
|
|
@@ -62,7 +64,7 @@ function readEnvConfig(env) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
|
-
return data ? [{data, context: "env"}] : [];
|
|
67
|
+
return data ? [{ data, context: "env" }] : [];
|
|
66
68
|
}
|
|
67
69
|
function safeJsonParse(str) {
|
|
68
70
|
try {
|
|
@@ -137,13 +139,13 @@ async function applyConfigTransforms(initialDir, input, transforms) {
|
|
|
137
139
|
|
|
138
140
|
const includeFileParser = {
|
|
139
141
|
".json": async (content) => JSON.parse(content),
|
|
140
|
-
".yaml": async (content) => yaml__default[
|
|
141
|
-
".yml": async (content) => yaml__default[
|
|
142
|
+
".yaml": async (content) => yaml__default["default"].parse(content),
|
|
143
|
+
".yml": async (content) => yaml__default["default"].parse(content)
|
|
142
144
|
};
|
|
143
145
|
function createIncludeTransform(env, readFile, substitute) {
|
|
144
146
|
return async (input, baseDir) => {
|
|
145
147
|
if (!isObject(input)) {
|
|
146
|
-
return {applied: false};
|
|
148
|
+
return { applied: false };
|
|
147
149
|
}
|
|
148
150
|
const [includeKey] = Object.keys(input).filter((key) => key.startsWith("$"));
|
|
149
151
|
if (includeKey) {
|
|
@@ -151,7 +153,7 @@ function createIncludeTransform(env, readFile, substitute) {
|
|
|
151
153
|
throw new Error(`include key ${includeKey} should not have adjacent keys`);
|
|
152
154
|
}
|
|
153
155
|
} else {
|
|
154
|
-
return {applied: false};
|
|
156
|
+
return { applied: false };
|
|
155
157
|
}
|
|
156
158
|
const rawIncludedValue = input[includeKey];
|
|
157
159
|
if (typeof rawIncludedValue !== "string") {
|
|
@@ -166,13 +168,13 @@ function createIncludeTransform(env, readFile, substitute) {
|
|
|
166
168
|
case "$file":
|
|
167
169
|
try {
|
|
168
170
|
const value = await readFile(path.resolve(baseDir, includeValue));
|
|
169
|
-
return {applied: true, value};
|
|
171
|
+
return { applied: true, value };
|
|
170
172
|
} catch (error) {
|
|
171
173
|
throw new Error(`failed to read file ${includeValue}, ${error}`);
|
|
172
174
|
}
|
|
173
175
|
case "$env":
|
|
174
176
|
try {
|
|
175
|
-
return {applied: true, value: await env(includeValue)};
|
|
177
|
+
return { applied: true, value: await env(includeValue) };
|
|
176
178
|
} catch (error) {
|
|
177
179
|
throw new Error(`failed to read env ${includeValue}, ${error}`);
|
|
178
180
|
}
|
|
@@ -215,7 +217,7 @@ function createIncludeTransform(env, readFile, substitute) {
|
|
|
215
217
|
function createSubstitutionTransform(env) {
|
|
216
218
|
return async (input) => {
|
|
217
219
|
if (typeof input !== "string") {
|
|
218
|
-
return {applied: false};
|
|
220
|
+
return { applied: false };
|
|
219
221
|
}
|
|
220
222
|
const parts = input.split(/(\$?\$\{[^{}]*\})/);
|
|
221
223
|
for (let i = 1; i < parts.length; i += 2) {
|
|
@@ -227,9 +229,9 @@ function createSubstitutionTransform(env) {
|
|
|
227
229
|
}
|
|
228
230
|
}
|
|
229
231
|
if (parts.some((part) => part === void 0)) {
|
|
230
|
-
return {applied: true, value: void 0};
|
|
232
|
+
return { applied: true, value: void 0 };
|
|
231
233
|
}
|
|
232
|
-
return {applied: true, value: parts.join("")};
|
|
234
|
+
return { applied: true, value: parts.join("") };
|
|
233
235
|
};
|
|
234
236
|
}
|
|
235
237
|
|
|
@@ -237,8 +239,8 @@ const CONFIG_VISIBILITIES = ["frontend", "backend", "secret"];
|
|
|
237
239
|
const DEFAULT_CONFIG_VISIBILITY = "backend";
|
|
238
240
|
|
|
239
241
|
function compileConfigSchemas(schemas) {
|
|
240
|
-
const visibilityByDataPath = new Map();
|
|
241
|
-
const ajv = new Ajv__default[
|
|
242
|
+
const visibilityByDataPath = /* @__PURE__ */ new Map();
|
|
243
|
+
const ajv = new Ajv__default["default"]({
|
|
242
244
|
allErrors: true,
|
|
243
245
|
allowUnionTypes: true,
|
|
244
246
|
schemas: {
|
|
@@ -272,8 +274,8 @@ function compileConfigSchemas(schemas) {
|
|
|
272
274
|
}
|
|
273
275
|
const merged = mergeConfigSchemas(schemas.map((_) => _.value));
|
|
274
276
|
const validate = ajv.compile(merged);
|
|
275
|
-
const visibilityBySchemaPath = new Map();
|
|
276
|
-
traverse__default[
|
|
277
|
+
const visibilityBySchemaPath = /* @__PURE__ */ new Map();
|
|
278
|
+
traverse__default["default"](merged, (schema, path) => {
|
|
277
279
|
if (schema.visibility && schema.visibility !== "backend") {
|
|
278
280
|
visibilityBySchemaPath.set(path, schema.visibility);
|
|
279
281
|
}
|
|
@@ -297,7 +299,7 @@ function compileConfigSchemas(schemas) {
|
|
|
297
299
|
};
|
|
298
300
|
}
|
|
299
301
|
function mergeConfigSchemas(schemas) {
|
|
300
|
-
const merged = mergeAllOf__default[
|
|
302
|
+
const merged = mergeAllOf__default["default"]({ allOf: schemas }, {
|
|
301
303
|
ignoreAdditionalProperties: true,
|
|
302
304
|
resolvers: {
|
|
303
305
|
visibility(values, path) {
|
|
@@ -321,18 +323,18 @@ const req = typeof __non_webpack_require__ === "undefined" ? require : __non_web
|
|
|
321
323
|
async function collectConfigSchemas(packageNames, packagePaths) {
|
|
322
324
|
const schemas = new Array();
|
|
323
325
|
const tsSchemaPaths = new Array();
|
|
324
|
-
const visitedPackageVersions = new Map();
|
|
325
|
-
const currentDir = await fs__default[
|
|
326
|
+
const visitedPackageVersions = /* @__PURE__ */ new Map();
|
|
327
|
+
const currentDir = await fs__default["default"].realpath(process.cwd());
|
|
326
328
|
async function processItem(item) {
|
|
327
329
|
var _a, _b, _c, _d;
|
|
328
330
|
let pkgPath = item.packagePath;
|
|
329
331
|
if (pkgPath) {
|
|
330
|
-
const pkgExists = await fs__default[
|
|
332
|
+
const pkgExists = await fs__default["default"].pathExists(pkgPath);
|
|
331
333
|
if (!pkgExists) {
|
|
332
334
|
return;
|
|
333
335
|
}
|
|
334
336
|
} else if (item.name) {
|
|
335
|
-
const {name, parentPath} = item;
|
|
337
|
+
const { name, parentPath } = item;
|
|
336
338
|
try {
|
|
337
339
|
pkgPath = req.resolve(`${name}/package.json`, parentPath && {
|
|
338
340
|
paths: [parentPath]
|
|
@@ -343,13 +345,13 @@ async function collectConfigSchemas(packageNames, packagePaths) {
|
|
|
343
345
|
if (!pkgPath) {
|
|
344
346
|
return;
|
|
345
347
|
}
|
|
346
|
-
const pkg = await fs__default[
|
|
348
|
+
const pkg = await fs__default["default"].readJson(pkgPath);
|
|
347
349
|
let versions = visitedPackageVersions.get(pkg.name);
|
|
348
350
|
if (versions == null ? void 0 : versions.has(pkg.version)) {
|
|
349
351
|
return;
|
|
350
352
|
}
|
|
351
353
|
if (!versions) {
|
|
352
|
-
versions = new Set();
|
|
354
|
+
versions = /* @__PURE__ */ new Set();
|
|
353
355
|
visitedPackageVersions.set(pkg.name, versions);
|
|
354
356
|
}
|
|
355
357
|
versions.add(pkg.version);
|
|
@@ -375,7 +377,7 @@ async function collectConfigSchemas(packageNames, packagePaths) {
|
|
|
375
377
|
tsSchemaPaths.push(path.relative(currentDir, path.resolve(path.dirname(pkgPath), pkg.configSchema)));
|
|
376
378
|
} else {
|
|
377
379
|
const path$1 = path.resolve(path.dirname(pkgPath), pkg.configSchema);
|
|
378
|
-
const value = await fs__default[
|
|
380
|
+
const value = await fs__default["default"].readJson(path$1);
|
|
379
381
|
schemas.push({
|
|
380
382
|
value,
|
|
381
383
|
path: path.relative(currentDir, path$1)
|
|
@@ -388,11 +390,11 @@ async function collectConfigSchemas(packageNames, packagePaths) {
|
|
|
388
390
|
});
|
|
389
391
|
}
|
|
390
392
|
}
|
|
391
|
-
await Promise.all(depNames.map((depName) => processItem({name: depName, parentPath: pkgPath})));
|
|
393
|
+
await Promise.all(depNames.map((depName) => processItem({ name: depName, parentPath: pkgPath })));
|
|
392
394
|
}
|
|
393
395
|
await Promise.all([
|
|
394
|
-
...packageNames.map((name) => processItem({name, parentPath: currentDir})),
|
|
395
|
-
...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 }))
|
|
396
398
|
]);
|
|
397
399
|
const tsSchemas = compileTsSchemas(tsSchemaPaths);
|
|
398
400
|
return schemas.concat(tsSchemas);
|
|
@@ -429,7 +431,7 @@ function compileTsSchemas(paths) {
|
|
|
429
431
|
if (!value) {
|
|
430
432
|
throw new Error(`Invalid schema in ${path$1}, missing Config export`);
|
|
431
433
|
}
|
|
432
|
-
return {path: path$1, value};
|
|
434
|
+
return { path: path$1, value };
|
|
433
435
|
});
|
|
434
436
|
return tsSchemas;
|
|
435
437
|
}
|
|
@@ -444,7 +446,7 @@ function filterByVisibility(data, includeVisibilities, visibilityByDataPath, tra
|
|
|
444
446
|
if (typeof jsonVal !== "object") {
|
|
445
447
|
if (isVisible) {
|
|
446
448
|
if (transformFunc) {
|
|
447
|
-
return transformFunc(jsonVal, {visibility});
|
|
449
|
+
return transformFunc(jsonVal, { visibility });
|
|
448
450
|
}
|
|
449
451
|
return jsonVal;
|
|
450
452
|
}
|
|
@@ -457,7 +459,12 @@ function filterByVisibility(data, includeVisibilities, visibilityByDataPath, tra
|
|
|
457
459
|
} else if (Array.isArray(jsonVal)) {
|
|
458
460
|
const arr = new Array();
|
|
459
461
|
for (const [index, value] of jsonVal.entries()) {
|
|
460
|
-
|
|
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}]`);
|
|
461
468
|
if (out !== void 0) {
|
|
462
469
|
arr.push(out);
|
|
463
470
|
}
|
|
@@ -515,7 +522,7 @@ function filterErrorsByVisibility(errors, includeVisibilities, visibilityByDataP
|
|
|
515
522
|
}
|
|
516
523
|
|
|
517
524
|
function errorsToError(errors) {
|
|
518
|
-
const messages = errors.map(({dataPath, message, params}) => {
|
|
525
|
+
const messages = errors.map(({ dataPath, message, params }) => {
|
|
519
526
|
const paramStr = Object.entries(params).map(([name, value]) => `${name}=${value}`).join(" ");
|
|
520
527
|
return `Config ${message || ""} { ${paramStr} } at ${dataPath}`;
|
|
521
528
|
});
|
|
@@ -529,7 +536,7 @@ async function loadConfigSchema(options) {
|
|
|
529
536
|
if ("dependencies" in options) {
|
|
530
537
|
schemas = await collectConfigSchemas(options.dependencies, (_a = options.packagePaths) != null ? _a : []);
|
|
531
538
|
} else {
|
|
532
|
-
const {serialized} = options;
|
|
539
|
+
const { serialized } = options;
|
|
533
540
|
if ((serialized == null ? void 0 : serialized.backstageConfigSchemaVersion) !== 1) {
|
|
534
541
|
throw new Error("Serialized configuration schema is invalid or has an invalid version number");
|
|
535
542
|
}
|
|
@@ -537,7 +544,7 @@ async function loadConfigSchema(options) {
|
|
|
537
544
|
}
|
|
538
545
|
const validate = compileConfigSchemas(schemas);
|
|
539
546
|
return {
|
|
540
|
-
process(configs, {visibility, valueTransform, withFilteredKeys} = {}) {
|
|
547
|
+
process(configs, { visibility, valueTransform, withFilteredKeys } = {}) {
|
|
541
548
|
const result = validate(configs);
|
|
542
549
|
const visibleErrors = filterErrorsByVisibility(result.errors, visibility, result.visibilityByDataPath, result.visibilityBySchemaPath);
|
|
543
550
|
if (visibleErrors.length > 0) {
|
|
@@ -545,12 +552,12 @@ async function loadConfigSchema(options) {
|
|
|
545
552
|
}
|
|
546
553
|
let processedConfigs = configs;
|
|
547
554
|
if (visibility) {
|
|
548
|
-
processedConfigs = processedConfigs.map(({data, context}) => ({
|
|
555
|
+
processedConfigs = processedConfigs.map(({ data, context }) => ({
|
|
549
556
|
context,
|
|
550
557
|
...filterByVisibility(data, visibility, result.visibilityByDataPath, valueTransform, withFilteredKeys)
|
|
551
558
|
}));
|
|
552
559
|
} else if (valueTransform) {
|
|
553
|
-
processedConfigs = processedConfigs.map(({data, context}) => ({
|
|
560
|
+
processedConfigs = processedConfigs.map(({ data, context }) => ({
|
|
554
561
|
context,
|
|
555
562
|
...filterByVisibility(data, Array.from(CONFIG_VISIBILITIES), result.visibilityByDataPath, valueTransform, withFilteredKeys)
|
|
556
563
|
}));
|
|
@@ -566,13 +573,30 @@ async function loadConfigSchema(options) {
|
|
|
566
573
|
};
|
|
567
574
|
}
|
|
568
575
|
|
|
576
|
+
function isValidUrl(url) {
|
|
577
|
+
try {
|
|
578
|
+
new URL(url);
|
|
579
|
+
return true;
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
569
585
|
async function loadConfig(options) {
|
|
570
|
-
const {configRoot, experimentalEnvFunc: envFunc, watch} = options;
|
|
571
|
-
const configPaths = options.
|
|
572
|
-
|
|
586
|
+
const { configRoot, experimentalEnvFunc: envFunc, watch, remote } = options;
|
|
587
|
+
const configPaths = options.configTargets.slice().filter((e) => e.hasOwnProperty("path")).map((configTarget) => configTarget.path);
|
|
588
|
+
const configUrls = options.configTargets.slice().filter((e) => e.hasOwnProperty("url")).map((configTarget) => configTarget.url);
|
|
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`);
|
|
595
|
+
}
|
|
596
|
+
if (configPaths.length === 0 && configUrls.length === 0) {
|
|
573
597
|
configPaths.push(path.resolve(configRoot, "app-config.yaml"));
|
|
574
598
|
const localConfig = path.resolve(configRoot, "app-config.local.yaml");
|
|
575
|
-
if (await fs__default[
|
|
599
|
+
if (await fs__default["default"].pathExists(localConfig)) {
|
|
576
600
|
configPaths.push(localConfig);
|
|
577
601
|
}
|
|
578
602
|
}
|
|
@@ -584,14 +608,41 @@ async function loadConfig(options) {
|
|
|
584
608
|
throw new Error(`Config load path is not absolute: '${configPath}'`);
|
|
585
609
|
}
|
|
586
610
|
const dir = path.dirname(configPath);
|
|
587
|
-
const readFile = (path$1) => fs__default[
|
|
588
|
-
const input = yaml__default[
|
|
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));
|
|
589
613
|
const substitutionTransform = createSubstitutionTransform(env);
|
|
590
614
|
const data = await applyConfigTransforms(dir, input, [
|
|
591
615
|
createIncludeTransform(env, readFile, substitutionTransform),
|
|
592
616
|
substitutionTransform
|
|
593
617
|
]);
|
|
594
|
-
configs.push({data, context: path.basename(configPath)});
|
|
618
|
+
configs.push({ data, context: path.basename(configPath) });
|
|
619
|
+
}
|
|
620
|
+
return configs;
|
|
621
|
+
};
|
|
622
|
+
const loadRemoteConfigFiles = async () => {
|
|
623
|
+
const configs = [];
|
|
624
|
+
const readConfigFromUrl = async (url) => {
|
|
625
|
+
const response = await fetch__default["default"](url);
|
|
626
|
+
if (!response.ok) {
|
|
627
|
+
throw new Error(`Could not read config file at ${url}`);
|
|
628
|
+
}
|
|
629
|
+
return await response.text();
|
|
630
|
+
};
|
|
631
|
+
for (let i = 0; i < configUrls.length; i++) {
|
|
632
|
+
const configUrl = configUrls[i];
|
|
633
|
+
if (!isValidUrl(configUrl)) {
|
|
634
|
+
throw new Error(`Config load path is not valid: '${configUrl}'`);
|
|
635
|
+
}
|
|
636
|
+
const remoteConfigContent = await readConfigFromUrl(configUrl);
|
|
637
|
+
if (!remoteConfigContent) {
|
|
638
|
+
throw new Error(`Config is not valid`);
|
|
639
|
+
}
|
|
640
|
+
const configYaml = yaml__default["default"].parse(remoteConfigContent);
|
|
641
|
+
const substitutionTransform = createSubstitutionTransform(env);
|
|
642
|
+
const data = await applyConfigTransforms(configRoot, configYaml, [
|
|
643
|
+
substitutionTransform
|
|
644
|
+
]);
|
|
645
|
+
configs.push({ data, context: configUrl });
|
|
595
646
|
}
|
|
596
647
|
return configs;
|
|
597
648
|
};
|
|
@@ -601,12 +652,20 @@ async function loadConfig(options) {
|
|
|
601
652
|
} catch (error) {
|
|
602
653
|
throw new errors.ForwardedError("Failed to read static configuration file", error);
|
|
603
654
|
}
|
|
655
|
+
let remoteConfigs = [];
|
|
656
|
+
if (remote) {
|
|
657
|
+
try {
|
|
658
|
+
remoteConfigs = await loadRemoteConfigFiles();
|
|
659
|
+
} catch (error) {
|
|
660
|
+
throw new errors.ForwardedError(`Failed to read remote configuration file`, error);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
604
663
|
const envConfigs = await readEnvConfig(process.env);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const watcher = chokidar__default['default'].watch(configPaths, {
|
|
664
|
+
const watchConfigFile = (watchProp) => {
|
|
665
|
+
const watcher = chokidar__default["default"].watch(configPaths, {
|
|
608
666
|
usePolling: process.env.NODE_ENV === "test"
|
|
609
667
|
});
|
|
668
|
+
let currentSerializedConfig = JSON.stringify(fileConfigs);
|
|
610
669
|
watcher.on("change", async () => {
|
|
611
670
|
try {
|
|
612
671
|
const newConfigs = await loadConfigFiles();
|
|
@@ -615,18 +674,55 @@ async function loadConfig(options) {
|
|
|
615
674
|
return;
|
|
616
675
|
}
|
|
617
676
|
currentSerializedConfig = newSerializedConfig;
|
|
618
|
-
|
|
677
|
+
watchProp.onChange([...remoteConfigs, ...newConfigs, ...envConfigs]);
|
|
619
678
|
} catch (error) {
|
|
620
679
|
console.error(`Failed to reload configuration files, ${error}`);
|
|
621
680
|
}
|
|
622
681
|
});
|
|
623
|
-
if (
|
|
624
|
-
|
|
682
|
+
if (watchProp.stopSignal) {
|
|
683
|
+
watchProp.stopSignal.then(() => {
|
|
625
684
|
watcher.close();
|
|
626
685
|
});
|
|
627
686
|
}
|
|
687
|
+
};
|
|
688
|
+
const watchRemoteConfig = (watchProp, remoteProp) => {
|
|
689
|
+
const hasConfigChanged = async (oldRemoteConfigs, newRemoteConfigs) => {
|
|
690
|
+
return JSON.stringify(oldRemoteConfigs) !== JSON.stringify(newRemoteConfigs);
|
|
691
|
+
};
|
|
692
|
+
let handle;
|
|
693
|
+
try {
|
|
694
|
+
handle = setInterval(async () => {
|
|
695
|
+
console.info(`Checking for config update`);
|
|
696
|
+
const newRemoteConfigs = await loadRemoteConfigFiles();
|
|
697
|
+
if (await hasConfigChanged(remoteConfigs, newRemoteConfigs)) {
|
|
698
|
+
remoteConfigs = newRemoteConfigs;
|
|
699
|
+
console.info(`Remote config change, reloading config ...`);
|
|
700
|
+
watchProp.onChange([...remoteConfigs, ...fileConfigs, ...envConfigs]);
|
|
701
|
+
console.info(`Remote config reloaded`);
|
|
702
|
+
}
|
|
703
|
+
}, remoteProp.reloadIntervalSeconds * 1e3);
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error(`Failed to reload configuration files, ${error}`);
|
|
706
|
+
}
|
|
707
|
+
if (watchProp.stopSignal) {
|
|
708
|
+
watchProp.stopSignal.then(() => {
|
|
709
|
+
if (handle !== void 0) {
|
|
710
|
+
console.info(`Stopping remote config watch`);
|
|
711
|
+
clearInterval(handle);
|
|
712
|
+
handle = void 0;
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
if (watch) {
|
|
718
|
+
watchConfigFile(watch);
|
|
719
|
+
}
|
|
720
|
+
if (watch && remote) {
|
|
721
|
+
watchRemoteConfig(watch, remote);
|
|
628
722
|
}
|
|
629
|
-
return
|
|
723
|
+
return {
|
|
724
|
+
appConfigs: remote ? [...remoteConfigs, ...fileConfigs, ...envConfigs] : [...fileConfigs, ...envConfigs]
|
|
725
|
+
};
|
|
630
726
|
}
|
|
631
727
|
|
|
632
728
|
exports.loadConfig = loadConfig;
|