@travetto/config 6.0.1 → 7.0.0-rc.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/README.md +4 -4
- package/package.json +3 -3
- package/src/decorator.ts +19 -18
- package/src/parser/parser.ts +2 -2
- package/src/service.ts +12 -12
- package/src/source/override.ts +3 -15
- package/src/util.ts +44 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @travetto/config
|
|
|
13
13
|
yarn add @travetto/config
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The config module provides support for loading application config on startup. Configuration values support the common [YAML](https://en.wikipedia.org/wiki/YAML) constructs as defined in [yaml](https://github.com/eemeli/yaml). Additionally, the configuration is built upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") module, to enforce type correctness, and allow for validation of configuration as an entrypoint into the application. Given that all [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L13) classes are [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#
|
|
16
|
+
The config module provides support for loading application config on startup. Configuration values support the common [YAML](https://en.wikipedia.org/wiki/YAML) constructs as defined in [yaml](https://github.com/eemeli/yaml). Additionally, the configuration is built upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") module, to enforce type correctness, and allow for validation of configuration as an entrypoint into the application. Given that all [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L13) classes are [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L19)-based classes, all the standard [@Schema](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/schema.ts#L19) and [@Field](https://github.com/travetto/travetto/tree/main/module/schema/src/decorator/field.ts#L21) functionality applies.
|
|
17
17
|
|
|
18
18
|
## Resolution
|
|
19
19
|
The configuration information is comprised of:
|
|
@@ -24,8 +24,8 @@ Config loading follows a defined resolution path, below is the order in increasi
|
|
|
24
24
|
1. `resources/application.<ext>` - Priority `100` - Load the default `application.<ext>` if available.
|
|
25
25
|
1. `resources/{env}.<ext>` - Priority `200` - Load environment specific profile configurations as defined by the values of `process.env.TRV_ENV`.
|
|
26
26
|
1. `resources/*.<ext>` - Priority `300` - Load profile specific configurations as defined by the values in `process.env.TRV_PROFILES`
|
|
27
|
-
1. [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#
|
|
28
|
-
1. [OverrideConfigSource](https://github.com/travetto/travetto/tree/main/module/config/src/source/override.ts#
|
|
27
|
+
1. [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L15) [ConfigSource](https://github.com/travetto/travetto/tree/main/module/config/src/source/types.ts#L11) - Priority `???` - These are custom config sources provided by the module, and are able to define their own priorities
|
|
28
|
+
1. [OverrideConfigSource](https://github.com/travetto/travetto/tree/main/module/config/src/source/override.ts#L9) - Priority `999` - This is for [@EnvVar](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L37) overrides, and is at the top priority for all built-in config sources.
|
|
29
29
|
|
|
30
30
|
By default all configuration data is inert, and will only be applied when constructing an instance of a configuration class.
|
|
31
31
|
|
|
@@ -176,7 +176,7 @@ At startup, the [ConfigurationService](https://github.com/travetto/travetto/tree
|
|
|
176
176
|
The [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#L24) service provides injectable access to all of the loaded configuration. For simplicity, a decorator, [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L13) allows for classes to automatically be bound with config information on post construction via the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") module. The decorator will install a `postConstruct` method if not already defined, that performs the binding of configuration. This is due to the fact that we cannot rewrite the constructor, and order of operation matters.
|
|
177
177
|
|
|
178
178
|
### Environment Variables
|
|
179
|
-
Additionally there are times in which you may want to also support configuration via environment variables. [EnvVar](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#
|
|
179
|
+
Additionally there are times in which you may want to also support configuration via environment variables. [@EnvVar](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L37) supports override configuration values when environment variables are present.
|
|
180
180
|
|
|
181
181
|
The decorator takes in a namespace, of what part of the resolved configuration you want to bind to your class. Given the following class:
|
|
182
182
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc.0",
|
|
4
4
|
"description": "Configuration support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yaml",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"directory": "module/config"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/di": "^
|
|
30
|
-
"@travetto/schema": "^
|
|
29
|
+
"@travetto/di": "^7.0.0-rc.0",
|
|
30
|
+
"@travetto/schema": "^7.0.0-rc.0",
|
|
31
31
|
"yaml": "^2.8.1"
|
|
32
32
|
},
|
|
33
33
|
"travetto": {
|
package/src/decorator.ts
CHANGED
|
@@ -1,40 +1,41 @@
|
|
|
1
|
-
import { Class, ClassInstance } from '@travetto/runtime';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Class, ClassInstance, getClass } from '@travetto/runtime';
|
|
2
|
+
import { DependencyRegistryIndex } from '@travetto/di';
|
|
3
|
+
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import { OverrideConfig, OverrideConfigSymbol } from './source/override.ts';
|
|
6
5
|
import { ConfigurationService, ConfigBaseType } from './service.ts';
|
|
6
|
+
import { ConfigOverrideUtil } from './util.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Indicates that the given class should be populated with the configured fields, on instantiation
|
|
10
10
|
* @augments `@travetto/schema:Schema`
|
|
11
|
-
* @
|
|
11
|
+
* @kind decorator
|
|
12
12
|
*/
|
|
13
13
|
export function Config(ns: string) {
|
|
14
|
-
return <T extends Class>(
|
|
15
|
-
const og: Function = target.prototype.postConstruct;
|
|
14
|
+
return <T extends Class>(cls: T): T => {
|
|
16
15
|
// Declare as part of global config
|
|
17
|
-
|
|
18
|
-
const env = SchemaRegistry.getOrCreatePendingMetadata<OverrideConfig>(target, OverrideConfigSymbol, { ns, fields: {} });
|
|
19
|
-
env.ns = ns;
|
|
16
|
+
SchemaRegistryIndex.getForRegister(cls).register({ interfaces: [ConfigBaseType] });
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
ConfigOverrideUtil.setOverrideConfig(cls, ns);
|
|
19
|
+
|
|
20
|
+
DependencyRegistryIndex.getForRegister(cls).registerClass();
|
|
21
|
+
|
|
22
|
+
const og: Function = cls.prototype.postConstruct;
|
|
23
|
+
cls.prototype.postConstruct = async function (): Promise<void> {
|
|
22
24
|
// Apply config
|
|
23
|
-
const cfg = await
|
|
24
|
-
await cfg.bindTo(
|
|
25
|
+
const cfg = await DependencyRegistryIndex.getInstance(ConfigurationService);
|
|
26
|
+
await cfg.bindTo(cls, this, ns);
|
|
25
27
|
await og?.call(this);
|
|
26
28
|
};
|
|
27
|
-
return
|
|
29
|
+
return cls;
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Allows for binding specific fields to environment variables as a top-level override
|
|
35
|
+
* @kind decorator
|
|
33
36
|
*/
|
|
34
37
|
export function EnvVar(name: string, ...others: string[]) {
|
|
35
|
-
return (
|
|
36
|
-
|
|
37
|
-
env.fields[prop] = (): string | undefined =>
|
|
38
|
-
process.env[[name, ...others].find(x => !!process.env[x])!];
|
|
38
|
+
return (instance: ClassInstance, property: string): void => {
|
|
39
|
+
ConfigOverrideUtil.setOverrideConfigField(getClass(instance), property, [name, ...others]);
|
|
39
40
|
};
|
|
40
41
|
}
|
package/src/parser/parser.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { DependencyRegistryIndex, Injectable } from '@travetto/di';
|
|
5
5
|
import { AppError, toConcrete } from '@travetto/runtime';
|
|
6
6
|
|
|
7
7
|
import { ConfigData, ConfigParser } from './types.ts';
|
|
@@ -13,7 +13,7 @@ export class ParserManager {
|
|
|
13
13
|
#parsers: Record<string, ConfigParser>;
|
|
14
14
|
|
|
15
15
|
async postConstruct(): Promise<void> {
|
|
16
|
-
const parsers = await
|
|
16
|
+
const parsers = await DependencyRegistryIndex.getInstances(toConcrete<ConfigParser>());
|
|
17
17
|
|
|
18
18
|
// Register parsers
|
|
19
19
|
this.#parsers = Object.fromEntries(parsers.flatMap(p => p.ext.map(e => [e, p])));
|
package/src/service.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
|
|
3
|
-
import { AppError, toConcrete, castTo, Class,
|
|
4
|
-
import {
|
|
5
|
-
import { BindUtil, DataUtil,
|
|
3
|
+
import { AppError, toConcrete, castTo, Class, Env, Runtime, RuntimeResources, getClass } from '@travetto/runtime';
|
|
4
|
+
import { DependencyRegistryIndex, getDefaultQualifier, Injectable } from '@travetto/di';
|
|
5
|
+
import { BindUtil, DataUtil, SchemaRegistryIndex, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
6
6
|
|
|
7
7
|
import { ParserManager } from './parser/parser.ts';
|
|
8
8
|
import { ConfigData } from './parser/types.ts';
|
|
@@ -50,13 +50,13 @@ export class ConfigurationService {
|
|
|
50
50
|
* - If of the same priority, then alpha sort on the source
|
|
51
51
|
*/
|
|
52
52
|
async postConstruct(): Promise<void> {
|
|
53
|
-
const providers = await
|
|
53
|
+
const providers = await DependencyRegistryIndex.getCandidates(toConcrete<ConfigSource>());
|
|
54
54
|
|
|
55
55
|
const configs = await Promise.all(
|
|
56
|
-
providers.map(async (el) => await
|
|
56
|
+
providers.map(async (el) => await DependencyRegistryIndex.getInstance(el.candidateType, el.qualifier))
|
|
57
57
|
);
|
|
58
58
|
|
|
59
|
-
const parser = await
|
|
59
|
+
const parser = await DependencyRegistryIndex.getInstance(ParserManager);
|
|
60
60
|
|
|
61
61
|
const possible = await Promise.all([
|
|
62
62
|
new FileConfigSource(parser),
|
|
@@ -93,22 +93,22 @@ export class ConfigurationService {
|
|
|
93
93
|
* - Will not show fields marked as secret
|
|
94
94
|
*/
|
|
95
95
|
async exportActive(): Promise<{ sources: ConfigSpecSimple[], active: ConfigData }> {
|
|
96
|
-
const configTargets = await
|
|
96
|
+
const configTargets = await DependencyRegistryIndex.getCandidates(ConfigBaseType);
|
|
97
97
|
const configs = await Promise.all(
|
|
98
98
|
configTargets
|
|
99
|
-
.filter(el => el.qualifier ===
|
|
99
|
+
.filter(el => el.qualifier === getDefaultQualifier(el.class)) // Is self targeting?
|
|
100
100
|
.toSorted((a, b) => a.class.name.localeCompare(b.class.name))
|
|
101
101
|
.map(async el => {
|
|
102
|
-
const inst = await
|
|
102
|
+
const inst = await DependencyRegistryIndex.getInstance(el.class, el.qualifier);
|
|
103
103
|
return [el, inst] as const;
|
|
104
104
|
})
|
|
105
105
|
);
|
|
106
106
|
const out: Record<string, ConfigData> = {};
|
|
107
107
|
for (const [el, inst] of configs) {
|
|
108
108
|
const data = BindUtil.bindSchemaToObject<ConfigData>(
|
|
109
|
-
inst
|
|
109
|
+
getClass(inst), {}, inst, { filterInput: f => !('secret' in f) || !f.secret, filterValue: v => v !== undefined }
|
|
110
110
|
);
|
|
111
|
-
out[el.
|
|
111
|
+
out[el.candidateType.name] = DataUtil.filterByKeys(data, this.#secrets);
|
|
112
112
|
}
|
|
113
113
|
return { sources: this.#specs, active: out };
|
|
114
114
|
}
|
|
@@ -118,7 +118,7 @@ export class ConfigurationService {
|
|
|
118
118
|
*/
|
|
119
119
|
async bindTo<T>(cls: Class<T>, item: T, namespace: string, validate = true): Promise<T> {
|
|
120
120
|
const classId = cls.Ⲑid;
|
|
121
|
-
if (!
|
|
121
|
+
if (!SchemaRegistryIndex.has(cls)) {
|
|
122
122
|
throw new AppError(`${classId} is not a valid schema class, config is not supported`);
|
|
123
123
|
}
|
|
124
124
|
BindUtil.bindSchemaToObject(cls, item, this.#get(namespace));
|
package/src/source/override.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import { SchemaRegistry } from '@travetto/schema';
|
|
2
|
-
|
|
3
1
|
import { ConfigData } from '../parser/types.ts';
|
|
4
2
|
import { ConfigSource, ConfigSpec } from './types.ts';
|
|
5
|
-
|
|
6
|
-
export const OverrideConfigSymbol = Symbol.for('@travetto/config:overrides');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Configuration Override
|
|
10
|
-
*/
|
|
11
|
-
export type OverrideConfig = {
|
|
12
|
-
ns: string;
|
|
13
|
-
fields: Record<string, () => (unknown | undefined)>;
|
|
14
|
-
};
|
|
3
|
+
import { ConfigOverrideUtil } from '../util.ts';
|
|
15
4
|
|
|
16
5
|
/**
|
|
17
6
|
* Overridable config source, provides ability to override field level values, currently used by
|
|
@@ -20,12 +9,11 @@ export type OverrideConfig = {
|
|
|
20
9
|
export class OverrideConfigSource implements ConfigSource {
|
|
21
10
|
#build(): ConfigData {
|
|
22
11
|
const out: ConfigData = {};
|
|
23
|
-
for (const
|
|
24
|
-
const { ns, fields = {} } = SchemaRegistry.getMetadata<OverrideConfig>(cls, OverrideConfigSymbol) ?? {};
|
|
12
|
+
for (const { namespace, fields } of ConfigOverrideUtil.getAllOverrideConfigs()) {
|
|
25
13
|
for (const [key, value] of Object.entries(fields)) {
|
|
26
14
|
const val = value();
|
|
27
15
|
if (val !== undefined && val !== '') {
|
|
28
|
-
out[`${
|
|
16
|
+
out[`${namespace}.${key}`] = val;
|
|
29
17
|
}
|
|
30
18
|
}
|
|
31
19
|
}
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Any, asFull, Class } from '@travetto/runtime';
|
|
2
|
+
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
3
|
+
|
|
4
|
+
export const OverrideConfigSymbol = Symbol.for('@travetto/config:overrides');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration Override
|
|
8
|
+
*/
|
|
9
|
+
export type OverrideConfig = {
|
|
10
|
+
namespace?: string;
|
|
11
|
+
fields?: Record<string, () => (unknown | undefined)>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Utility for managing override configuration via SchemaRegistryIndex
|
|
16
|
+
*/
|
|
17
|
+
export class ConfigOverrideUtil {
|
|
18
|
+
static getOverrideConfig(cls: Class<Any>): OverrideConfig | undefined {
|
|
19
|
+
return SchemaRegistryIndex.get(cls).getMetadata<OverrideConfig>(OverrideConfigSymbol);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static getAllOverrideConfigs(): Required<OverrideConfig>[] {
|
|
23
|
+
const out: Required<OverrideConfig>[] = [];
|
|
24
|
+
for (const cls of SchemaRegistryIndex.getClasses()) {
|
|
25
|
+
const cfg = this.getOverrideConfig(cls);
|
|
26
|
+
if (cfg && cfg.fields && cfg.namespace) {
|
|
27
|
+
out.push(asFull(cfg));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static setOverrideConfigField(cls: Class<Any>, field: string, names: string[]): void {
|
|
34
|
+
const env = SchemaRegistryIndex.getForRegister(cls)
|
|
35
|
+
.registerMetadata<OverrideConfig>(OverrideConfigSymbol, {});
|
|
36
|
+
|
|
37
|
+
(env.fields ??= {})[field] = (): string | undefined =>
|
|
38
|
+
process.env[names.find(x => !!process.env[x])!];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static setOverrideConfig(cls: Class<Any>, namespace: string): void {
|
|
42
|
+
SchemaRegistryIndex.getForRegister(cls).registerMetadata<OverrideConfig>(OverrideConfigSymbol, { namespace });
|
|
43
|
+
}
|
|
44
|
+
}
|