@travetto/config 3.4.0-rc.3 → 3.4.0-rc.5
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 +56 -44
- package/package.json +4 -4
- package/src/parser/parser.ts +37 -0
- package/src/service.ts +36 -22
- package/src/source/env.ts +7 -6
- package/src/source/file.ts +25 -42
- package/src/source/memory.ts +8 -13
- package/src/source/override.ts +5 -12
- package/src/source/types.ts +3 -4
package/README.md
CHANGED
|
@@ -20,11 +20,18 @@ The configuration information is comprised of:
|
|
|
20
20
|
* configuration files - [YAML](https://en.wikipedia.org/wiki/YAML), [JSON](https://www.json.org), and basic properties file
|
|
21
21
|
* configuration classes
|
|
22
22
|
Config loading follows a defined resolution path, below is the order in increasing specificity (`ext` can be `yaml`, `yml`, `json`, `properties`):
|
|
23
|
-
1. `resources/application.<ext>` - Load the default `application.<ext>` if available.
|
|
24
|
-
1. `resources
|
|
25
|
-
1. `resources
|
|
23
|
+
1. `resources/application.<ext>` - Priority `100` - Load the default `application.<ext>` if available.
|
|
24
|
+
1. `resources/{env}.<ext>` - Priority `200` - Load environment specific profile configurations as defined by the values of `process.env.TRV_ENV`.
|
|
25
|
+
1. `resources/*.<ext>` - Priority `300` - Load profile specific configurations as defined by the values in `process.env.TRV_PROFILES`
|
|
26
|
+
1. [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) [ConfigSource](https://github.com/travetto/travetto/tree/main/module/config/src/source/types.ts#L6) - Priority `???` - These are custom config sources provided by the module, and are able to define their own priorities
|
|
27
|
+
1. [OverrideConfigSource](https://github.com/travetto/travetto/tree/main/module/config/src/source/override.ts#L11) - Priority `999` - This is for [EnvVar](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L34) overrides, and is at the top priority for all built-in config sources.
|
|
26
28
|
By default all configuration data is inert, and will only be applied when constructing an instance of a configuration class.
|
|
27
29
|
|
|
30
|
+
**Note**: When working in a monorepo, the parent resources folder will also be searched with a lower priority than the the module's specific resources. This allows for shared-global configuration that can be overridden at the module level. The general priority is:
|
|
31
|
+
1. Mono-repo root
|
|
32
|
+
1. Module root
|
|
33
|
+
1. Folders for `TRV_RESOURCES`, in order
|
|
34
|
+
|
|
28
35
|
### A Complete Example
|
|
29
36
|
A more complete example setup would look like:
|
|
30
37
|
|
|
@@ -65,9 +72,17 @@ $ trv main doc/resolve.ts
|
|
|
65
72
|
|
|
66
73
|
Config {
|
|
67
74
|
sources: [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
{
|
|
76
|
+
priority: 100,
|
|
77
|
+
source: 'file://application',
|
|
78
|
+
detail: 'module/config/doc/resources/application.yml'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
priority: 300,
|
|
82
|
+
source: 'file://prod',
|
|
83
|
+
detail: 'module/config/doc/resources/prod.json'
|
|
84
|
+
},
|
|
85
|
+
{ priority: 999, source: 'memory://override' }
|
|
71
86
|
],
|
|
72
87
|
active: {
|
|
73
88
|
DBConfig: { host: 'prod-host-db', port: 2000, creds: { user: 'admin-user' } }
|
|
@@ -81,95 +96,84 @@ The framework provides two simple base classes that assist with existing pattern
|
|
|
81
96
|
**Code: Memory Provider**
|
|
82
97
|
```typescript
|
|
83
98
|
import { ConfigData } from '../parser/types';
|
|
84
|
-
import { ConfigSource
|
|
99
|
+
import { ConfigSource } from './types';
|
|
85
100
|
|
|
86
101
|
/**
|
|
87
102
|
* Meant to be instantiated and provided as a unique config source
|
|
88
103
|
*/
|
|
89
104
|
export class MemoryConfigSource implements ConfigSource {
|
|
90
|
-
priority
|
|
91
|
-
data:
|
|
92
|
-
|
|
105
|
+
priority: number;
|
|
106
|
+
data: ConfigData;
|
|
107
|
+
source: string;
|
|
93
108
|
|
|
94
|
-
constructor(
|
|
109
|
+
constructor(key: string, data: ConfigData, priority: number = 500) {
|
|
95
110
|
this.data = data;
|
|
96
111
|
this.priority = priority;
|
|
112
|
+
this.source = `memory://${key}`;
|
|
97
113
|
}
|
|
98
114
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
for (const profile of profiles) {
|
|
102
|
-
if (this.data[profile]) {
|
|
103
|
-
out.push({ profile, config: this.data[profile], source: `${this.name}://${profile}`, priority: this.priority });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return out;
|
|
115
|
+
getData(): ConfigData {
|
|
116
|
+
return this.data;
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
**Code: Environment JSON Provider**
|
|
112
122
|
```typescript
|
|
113
|
-
import { Env
|
|
123
|
+
import { Env } from '@travetto/base';
|
|
114
124
|
|
|
115
|
-
import { ConfigSource
|
|
125
|
+
import { ConfigSource } from './types';
|
|
126
|
+
import { ConfigData } from '../parser/types';
|
|
116
127
|
|
|
117
128
|
/**
|
|
118
129
|
* Represents the environment mapped data as a JSON blob
|
|
119
130
|
*/
|
|
120
131
|
export class EnvConfigSource implements ConfigSource {
|
|
121
132
|
priority: number;
|
|
122
|
-
|
|
133
|
+
source: string;
|
|
123
134
|
#envKey: string;
|
|
124
135
|
|
|
125
136
|
constructor(key: string, priority: number) {
|
|
126
137
|
this.#envKey = key;
|
|
127
138
|
this.priority = priority;
|
|
139
|
+
this.source = `env://${this.#envKey}`;
|
|
128
140
|
}
|
|
129
141
|
|
|
130
|
-
|
|
142
|
+
getData(): ConfigData | undefined {
|
|
131
143
|
try {
|
|
132
144
|
const data = JSON.parse(Env.get(this.#envKey, '{}'));
|
|
133
|
-
return
|
|
145
|
+
return data;
|
|
134
146
|
} catch (e) {
|
|
135
147
|
console.error(`env.${this.#envKey} is an invalid format`, { text: Env.get(this.#envKey) });
|
|
136
|
-
return [];
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
151
|
```
|
|
141
152
|
|
|
142
153
|
### Custom Configuration Provider
|
|
143
|
-
In addition to files and environment variables, configuration sources can also be provided via the class itself. This is useful for reading remote configurations, or dealing with complex configuration normalization. The only caveat to this pattern, is that the these configuration sources cannot rely on the [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#
|
|
154
|
+
In addition to files and environment variables, configuration sources can also be provided via the class itself. This is useful for reading remote configurations, or dealing with complex configuration normalization. The only caveat to this pattern, is that the these configuration sources cannot rely on the [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#L21) service for input. This means any needed configuration will need to be accessed via specific patterns.
|
|
144
155
|
|
|
145
156
|
**Code: Custom Configuration Source**
|
|
146
157
|
```typescript
|
|
147
|
-
import {
|
|
158
|
+
import { ConfigData, ConfigSource } from '@travetto/config';
|
|
148
159
|
import { Injectable } from '@travetto/di';
|
|
149
160
|
|
|
150
161
|
@Injectable()
|
|
151
162
|
export class CustomConfigSource implements ConfigSource {
|
|
152
|
-
priority =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
async
|
|
156
|
-
return
|
|
157
|
-
{
|
|
158
|
-
config: { user: { name: 'bob' } },
|
|
159
|
-
priority: this.priority,
|
|
160
|
-
profile: 'override',
|
|
161
|
-
source: `custom://${CustomConfigSource.name}`
|
|
162
|
-
}
|
|
163
|
-
];
|
|
163
|
+
priority = 2000;
|
|
164
|
+
source = 'custom://override';
|
|
165
|
+
|
|
166
|
+
async getData(): Promise<ConfigData> {
|
|
167
|
+
return { user: { name: 'bob' } };
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
```
|
|
167
171
|
|
|
168
172
|
## Startup
|
|
169
|
-
At startup, the [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#
|
|
173
|
+
At startup, the [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#L21) service will log out all the registered configuration objects. The configuration state output is useful to determine if everything is configured properly when diagnosing runtime errors. This service will find all configurations, and output a redacted version with all secrets removed. The default pattern for secrets is `/password|private|secret/i`. More values can be added in your configuration under the path `config.secrets`. These values can either be simple strings (for exact match), or `/pattern/` to create a regular expression.
|
|
170
174
|
|
|
171
175
|
## Consuming
|
|
172
|
-
The [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#
|
|
176
|
+
The [ConfigurationService](https://github.com/travetto/travetto/tree/main/module/config/src/service.ts#L21) 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.
|
|
173
177
|
|
|
174
178
|
### Environment Variables
|
|
175
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#L34) supports override configuration values when environment variables are present.
|
|
@@ -228,9 +232,17 @@ $ DATABASE_PORT=200 trv main doc/dbconfig-run.ts
|
|
|
228
232
|
|
|
229
233
|
Config {
|
|
230
234
|
sources: [
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
235
|
+
{
|
|
236
|
+
priority: 100,
|
|
237
|
+
source: 'file://application',
|
|
238
|
+
detail: 'module/config/doc/resources/application.yml'
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
priority: 300,
|
|
242
|
+
source: 'file://prod',
|
|
243
|
+
detail: 'module/config/doc/resources/prod.json'
|
|
244
|
+
},
|
|
245
|
+
{ priority: 999, source: 'memory://override' }
|
|
234
246
|
],
|
|
235
247
|
active: {
|
|
236
248
|
DBConfig: { host: 'prod-host-db', port: 200, creds: { user: 'admin-user' } }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/config",
|
|
3
|
-
"version": "3.4.0-rc.
|
|
3
|
+
"version": "3.4.0-rc.5",
|
|
4
4
|
"description": "Configuration support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yaml",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"directory": "module/config"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/di": "^3.4.0-rc.
|
|
30
|
-
"@travetto/schema": "^3.4.0-rc.
|
|
31
|
-
"@travetto/yaml": "^3.4.0-rc.
|
|
29
|
+
"@travetto/di": "^3.4.0-rc.5",
|
|
30
|
+
"@travetto/schema": "^3.4.0-rc.5",
|
|
31
|
+
"@travetto/yaml": "^3.4.0-rc.5"
|
|
32
32
|
},
|
|
33
33
|
"travetto": {
|
|
34
34
|
"displayName": "Configuration"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { DependencyRegistry, Injectable } from '@travetto/di';
|
|
4
|
+
import { AppError } from '@travetto/base';
|
|
5
|
+
import { path } from '@travetto/manifest';
|
|
6
|
+
|
|
7
|
+
import { ConfigParserTarget } from '../internal/types';
|
|
8
|
+
import { ConfigData, ConfigParser } from './types';
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class ParserManager {
|
|
12
|
+
|
|
13
|
+
#extMatch: RegExp;
|
|
14
|
+
#parsers: Record<string, ConfigParser>;
|
|
15
|
+
|
|
16
|
+
async postConstruct(): Promise<void> {
|
|
17
|
+
const parserClasses = await DependencyRegistry.getCandidateTypes(ConfigParserTarget);
|
|
18
|
+
const parsers = await Promise.all(parserClasses.map(x => DependencyRegistry.getInstance<ConfigParser>(x.class, x.qualifier)));
|
|
19
|
+
|
|
20
|
+
// Register parsers
|
|
21
|
+
this.#parsers = Object.fromEntries(parsers.flatMap(p => p.ext.map(e => [e, p])));
|
|
22
|
+
|
|
23
|
+
this.#extMatch = parsers.length ? new RegExp(`(${Object.keys(this.#parsers).join('|').replaceAll('.', '[.]')})`) : /^$/;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async parse(file: string): Promise<ConfigData> {
|
|
27
|
+
const ext = path.extname(file);
|
|
28
|
+
if (!this.#parsers[ext]) {
|
|
29
|
+
throw new AppError(`Unknown config format: ${ext}`, 'data');
|
|
30
|
+
}
|
|
31
|
+
return fs.readFile(file, 'utf8').then(content => this.#parsers[ext].parse(content));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
matches(file: string): boolean {
|
|
35
|
+
return this.#extMatch.test(file);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/service.ts
CHANGED
|
@@ -6,8 +6,13 @@ import { RootIndex } from '@travetto/manifest';
|
|
|
6
6
|
import { BindUtil, SchemaRegistry, SchemaValidator, ValidationResultError } from '@travetto/schema';
|
|
7
7
|
|
|
8
8
|
import { ConfigSourceTarget, ConfigTarget } from './internal/types';
|
|
9
|
+
import { ParserManager } from './parser/parser';
|
|
9
10
|
import { ConfigData } from './parser/types';
|
|
10
|
-
import { ConfigSource
|
|
11
|
+
import { ConfigSource } from './source/types';
|
|
12
|
+
import { FileConfigSource } from './source/file';
|
|
13
|
+
import { OverrideConfigSource } from './source/override';
|
|
14
|
+
|
|
15
|
+
type ConfigSpec = { source: string, priority: number, detail?: string };
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* Manager for application configuration
|
|
@@ -15,21 +20,23 @@ import { ConfigSource, ConfigValue } from './source/types';
|
|
|
15
20
|
@Injectable()
|
|
16
21
|
export class ConfigurationService {
|
|
17
22
|
|
|
18
|
-
private static getSorted(configs: ConfigValue[], profiles: string[]): ConfigValue[] {
|
|
19
|
-
const order = Object.fromEntries(Object.entries(profiles).map(([k, v]) => [v, +k] as const));
|
|
20
|
-
|
|
21
|
-
return configs.sort((left, right) =>
|
|
22
|
-
(order[left.profile] - order[right.profile]) ||
|
|
23
|
-
left.priority - right.priority ||
|
|
24
|
-
left.source.localeCompare(right.source)
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
#storage: Record<string, unknown> = {}; // Lowered, and flattened
|
|
29
|
-
#
|
|
30
|
-
#sources: string[] = [];
|
|
24
|
+
#specs: ConfigSpec[] = [];
|
|
31
25
|
#secrets: (RegExp | string)[] = [/secure(-|_|[a-z])|password|private|secret|salt|(api(-|_)?key)/i];
|
|
32
26
|
|
|
27
|
+
async #toSpecPairs(cfg: ConfigSource): Promise<[ConfigSpec, ConfigData][]> {
|
|
28
|
+
const data = await cfg.getData();
|
|
29
|
+
if (!data) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const arr = Array.isArray(data) ? data : [data];
|
|
33
|
+
return arr.map((d, i) => [{
|
|
34
|
+
priority: cfg.priority + i,
|
|
35
|
+
source: cfg.source,
|
|
36
|
+
...(d.__ID__ ? { detail: d.__ID__?.toString() } : {})
|
|
37
|
+
}, d]);
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
/**
|
|
34
41
|
* Get a sub tree of the config, or everything if namespace is not passed
|
|
35
42
|
* @param ns The namespace of the config to search for, can be dotted for accessing sub namespaces
|
|
@@ -57,17 +64,24 @@ export class ConfigurationService {
|
|
|
57
64
|
const providers = await DependencyRegistry.getCandidateTypes(ConfigSourceTarget);
|
|
58
65
|
|
|
59
66
|
const configs = await Promise.all(
|
|
60
|
-
providers.map(async (el) =>
|
|
61
|
-
const inst = await DependencyRegistry.getInstance<ConfigSource>(el.class, el.qualifier);
|
|
62
|
-
return inst.getValues(this.#profiles);
|
|
63
|
-
})
|
|
67
|
+
providers.map(async (el) => await DependencyRegistry.getInstance<ConfigSource>(el.class, el.qualifier))
|
|
64
68
|
);
|
|
65
69
|
|
|
66
|
-
const
|
|
70
|
+
const parser = await DependencyRegistry.getInstance(ParserManager);
|
|
71
|
+
|
|
72
|
+
const specPairs = await Promise.all([
|
|
73
|
+
new FileConfigSource(parser, 'application', 100),
|
|
74
|
+
new FileConfigSource(parser, GlobalEnv.envName, 200),
|
|
75
|
+
...(Env.getList('TRV_PROFILES') ?? []).map((p, i) => new FileConfigSource(parser, p, 300 + i * 10)),
|
|
76
|
+
...configs,
|
|
77
|
+
new OverrideConfigSource()
|
|
78
|
+
].map(src => this.#toSpecPairs(src)));
|
|
79
|
+
|
|
80
|
+
const specs = specPairs.flat().sort(([a], [b]) => a.priority - b.priority);
|
|
67
81
|
|
|
68
|
-
this.#
|
|
82
|
+
this.#specs = specs.map(([v]) => v);
|
|
69
83
|
|
|
70
|
-
for (const
|
|
84
|
+
for (const [, element] of specs) {
|
|
71
85
|
DataUtil.deepAssign(this.#storage, BindUtil.expandPaths(element), 'coerce');
|
|
72
86
|
}
|
|
73
87
|
|
|
@@ -88,7 +102,7 @@ export class ConfigurationService {
|
|
|
88
102
|
* Export all active configuration, useful for displaying active state
|
|
89
103
|
* - Will not show fields marked as secret
|
|
90
104
|
*/
|
|
91
|
-
async exportActive(): Promise<{ sources:
|
|
105
|
+
async exportActive(): Promise<{ sources: ConfigSpec[], active: ConfigData }> {
|
|
92
106
|
const configTargets = await DependencyRegistry.getCandidateTypes(ConfigTarget);
|
|
93
107
|
const configs = await Promise.all(
|
|
94
108
|
configTargets
|
|
@@ -106,7 +120,7 @@ export class ConfigurationService {
|
|
|
106
120
|
);
|
|
107
121
|
out[el.class.name] = DataUtil.filterByKeys(data, this.#secrets);
|
|
108
122
|
}
|
|
109
|
-
return { sources: this.#
|
|
123
|
+
return { sources: this.#specs, active: out };
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
/**
|
package/src/source/env.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { Env
|
|
1
|
+
import { Env } from '@travetto/base';
|
|
2
2
|
|
|
3
|
-
import { ConfigSource
|
|
3
|
+
import { ConfigSource } from './types';
|
|
4
|
+
import { ConfigData } from '../parser/types';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Represents the environment mapped data as a JSON blob
|
|
7
8
|
*/
|
|
8
9
|
export class EnvConfigSource implements ConfigSource {
|
|
9
10
|
priority: number;
|
|
10
|
-
|
|
11
|
+
source: string;
|
|
11
12
|
#envKey: string;
|
|
12
13
|
|
|
13
14
|
constructor(key: string, priority: number) {
|
|
14
15
|
this.#envKey = key;
|
|
15
16
|
this.priority = priority;
|
|
17
|
+
this.source = `env://${this.#envKey}`;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
getData(): ConfigData | undefined {
|
|
19
21
|
try {
|
|
20
22
|
const data = JSON.parse(Env.get(this.#envKey, '{}'));
|
|
21
|
-
return
|
|
23
|
+
return data;
|
|
22
24
|
} catch (e) {
|
|
23
25
|
console.error(`env.${this.#envKey} is an invalid format`, { text: Env.get(this.#envKey) });
|
|
24
|
-
return [];
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
}
|
package/src/source/file.ts
CHANGED
|
@@ -1,59 +1,42 @@
|
|
|
1
|
-
import { path } from '@travetto/manifest';
|
|
2
1
|
import { FileQueryProvider } from '@travetto/base';
|
|
3
|
-
import {
|
|
2
|
+
import { RootIndex, path } from '@travetto/manifest';
|
|
4
3
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { ConfigSource } from './types';
|
|
5
|
+
import { ParserManager } from '../parser/parser';
|
|
6
|
+
import { ConfigData } from '../parser/types';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* File-base config source, builds on common file resource provider
|
|
11
10
|
*/
|
|
12
11
|
export class FileConfigSource extends FileQueryProvider implements ConfigSource {
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
depth = 1;
|
|
20
|
-
extMatch: RegExp;
|
|
21
|
-
parsers: Record<string, ConfigParser>;
|
|
22
|
-
priority = 1;
|
|
13
|
+
priority = 10;
|
|
14
|
+
source: string;
|
|
15
|
+
profile: string;
|
|
16
|
+
parser: ParserManager;
|
|
23
17
|
|
|
24
|
-
constructor(paths: string[] = []) {
|
|
18
|
+
constructor(parser: ParserManager, profile: string, priority: number, paths: string[] = []) {
|
|
25
19
|
super({ includeCommon: true, paths });
|
|
20
|
+
this.priority = priority;
|
|
21
|
+
this.profile = profile;
|
|
22
|
+
this.parser = parser;
|
|
23
|
+
this.source = `file://${profile}`;
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
async
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
// Register parsers
|
|
33
|
-
this.parsers = Object.fromEntries(parsers.flatMap(p => p.ext.map(e => [e, p])));
|
|
34
|
-
|
|
35
|
-
this.extMatch = parsers.length ? new RegExp(`(${Object.keys(this.parsers).join('|').replaceAll('.', '[.]')})`) : /^$/;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async getValues(profiles: string[]): Promise<ConfigValue[]> {
|
|
39
|
-
const out: ConfigValue[] = [];
|
|
40
|
-
|
|
41
|
-
for await (const file of this.query(f => this.extMatch.test(f))) {
|
|
26
|
+
async getData(): Promise<ConfigData[]> {
|
|
27
|
+
const out: ConfigData[] = [];
|
|
28
|
+
for await (const file of this.query(f => this.parser.matches(f))) {
|
|
42
29
|
const ext = path.extname(file);
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
|
|
30
|
+
const base = path.basename(file, ext);
|
|
31
|
+
if (base === this.profile && !file.includes('/')) { // Ensures no nesting
|
|
32
|
+
for (const resolved of await this.resolveAll(file)) {
|
|
33
|
+
// Ensure more specific files are processed later, .resolveAll returns with most specific first
|
|
34
|
+
const data = await this.parser.parse(resolved);
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
36
|
+
out.push({ ...data, __ID__: resolved.replace(`${RootIndex.manifest.workspacePath}/`, '') });
|
|
37
|
+
}
|
|
46
38
|
}
|
|
47
|
-
const content = await this.read(file);
|
|
48
|
-
const desc = await this.describe(file);
|
|
49
|
-
out.push({
|
|
50
|
-
profile,
|
|
51
|
-
config: await this.parsers[ext].parse(content),
|
|
52
|
-
source: `file://${desc.path}`,
|
|
53
|
-
priority: this.priority
|
|
54
|
-
});
|
|
55
39
|
}
|
|
56
|
-
|
|
57
|
-
return out.sort((a, b) => (a.source.length - b.source.length) || a.source.localeCompare(b.source));
|
|
40
|
+
return out.reverse();
|
|
58
41
|
}
|
|
59
42
|
}
|
package/src/source/memory.ts
CHANGED
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { ConfigData } from '../parser/types';
|
|
2
|
-
import { ConfigSource
|
|
2
|
+
import { ConfigSource } from './types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Meant to be instantiated and provided as a unique config source
|
|
6
6
|
*/
|
|
7
7
|
export class MemoryConfigSource implements ConfigSource {
|
|
8
|
-
priority
|
|
9
|
-
data:
|
|
10
|
-
|
|
8
|
+
priority: number;
|
|
9
|
+
data: ConfigData;
|
|
10
|
+
source: string;
|
|
11
11
|
|
|
12
|
-
constructor(
|
|
12
|
+
constructor(key: string, data: ConfigData, priority: number = 500) {
|
|
13
13
|
this.data = data;
|
|
14
14
|
this.priority = priority;
|
|
15
|
+
this.source = `memory://${key}`;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
for (const profile of profiles) {
|
|
20
|
-
if (this.data[profile]) {
|
|
21
|
-
out.push({ profile, config: this.data[profile], source: `${this.name}://${profile}`, priority: this.priority });
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return out;
|
|
18
|
+
getData(): ConfigData {
|
|
19
|
+
return this.data;
|
|
25
20
|
}
|
|
26
21
|
}
|
package/src/source/override.ts
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { Injectable } from '@travetto/di';
|
|
2
1
|
import { SchemaRegistry } from '@travetto/schema';
|
|
3
2
|
|
|
4
3
|
import { ConfigOverrides, CONFIG_OVERRIDES } from '../internal/types';
|
|
5
4
|
import { ConfigData } from '../parser/types';
|
|
6
|
-
import { ConfigSource
|
|
5
|
+
import { ConfigSource } from './types';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Overridable config source, provides ability to override field level values, currently used by
|
|
10
9
|
* - @EnvVar as a means to allow environment specific overrides
|
|
11
10
|
*/
|
|
12
|
-
@Injectable()
|
|
13
11
|
export class OverrideConfigSource implements ConfigSource {
|
|
14
|
-
priority =
|
|
15
|
-
|
|
12
|
+
priority = 999;
|
|
13
|
+
source = 'memory://override';
|
|
16
14
|
|
|
17
15
|
#build(): ConfigData {
|
|
18
16
|
const out: ConfigData = {};
|
|
@@ -28,12 +26,7 @@ export class OverrideConfigSource implements ConfigSource {
|
|
|
28
26
|
return out;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
return
|
|
33
|
-
config: this.#build(),
|
|
34
|
-
profile: 'override',
|
|
35
|
-
source: 'memory://override',
|
|
36
|
-
priority: this.priority
|
|
37
|
-
}];
|
|
29
|
+
getData(): ConfigData {
|
|
30
|
+
return this.#build();
|
|
38
31
|
}
|
|
39
32
|
}
|
package/src/source/types.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { ConfigData } from '../parser/types';
|
|
2
2
|
|
|
3
|
-
export type ConfigValue = { config: ConfigData, source: string, profile: string, priority: number };
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* @concrete ../internal/types:ConfigSourceTarget
|
|
7
5
|
*/
|
|
8
6
|
export interface ConfigSource {
|
|
9
7
|
priority: number;
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
source: string;
|
|
9
|
+
getData(): Promise<ConfigData[] | ConfigData | undefined> | ConfigData[] | ConfigData | undefined;
|
|
10
|
+
}
|