@flowscripter/dynamic-cli-framework 12.0.1 → 12.2.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +3 -1
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts +6 -0
- package/dist/src/cli/BaseCLIFeatureOptions.d.ts.map +1 -1
- package/dist/src/runtime/runner.d.ts.map +1 -1
- package/dist/src/runtime/runner.js +4 -1
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts +33 -15
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/ConfigurationServiceProvider.js +80 -173
- package/dist/src/service/configuration/DefaultConfigurationService.d.ts +78 -0
- package/dist/src/service/configuration/DefaultConfigurationService.d.ts.map +1 -0
- package/dist/src/service/configuration/DefaultConfigurationService.js +296 -0
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultKeyValueService.js +7 -3
- package/dist/src/service/configuration/DefaultSecretService.d.ts +17 -2
- package/dist/src/service/configuration/DefaultSecretService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultSecretService.js +44 -5
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts +3 -1
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/KeyValueServiceProvider.js +6 -3
- package/dist/src/service/configuration/command/ConfigDumpSubCommand.d.ts +3 -7
- package/dist/src/service/configuration/command/ConfigDumpSubCommand.d.ts.map +1 -1
- package/dist/src/service/configuration/command/ConfigDumpSubCommand.js +5 -10
- package/dist/src/service/configuration/command/ConfigGetSubCommand.d.ts +16 -0
- package/dist/src/service/configuration/command/ConfigGetSubCommand.d.ts.map +1 -0
- package/dist/src/service/configuration/command/ConfigGetSubCommand.js +137 -0
- package/dist/src/service/configuration/command/ConfigSetSubCommand.d.ts +18 -0
- package/dist/src/service/configuration/command/ConfigSetSubCommand.d.ts.map +1 -0
- package/dist/src/service/configuration/command/ConfigSetSubCommand.js +289 -0
- package/dist/src/service/configuration/command/ConfigUnsetSubCommand.d.ts +15 -0
- package/dist/src/service/configuration/command/ConfigUnsetSubCommand.d.ts.map +1 -0
- package/dist/src/service/configuration/command/ConfigUnsetSubCommand.js +101 -0
- package/dist/src/service/configuration/configTargets.d.ts +91 -0
- package/dist/src/service/configuration/configTargets.d.ts.map +1 -0
- package/dist/src/service/configuration/configTargets.js +348 -0
- package/dist/src/service/configuration/format/ConfigFormat.d.ts +42 -0
- package/dist/src/service/configuration/format/ConfigFormat.d.ts.map +1 -0
- package/dist/src/service/configuration/format/ConfigFormat.js +0 -0
- package/dist/src/service/configuration/format/JsonConfigFormat.d.ts +7 -0
- package/dist/src/service/configuration/format/JsonConfigFormat.d.ts.map +1 -0
- package/dist/src/service/configuration/format/JsonConfigFormat.js +17 -0
- package/dist/src/service/configuration/format/TomlConfigFormat.d.ts +10 -0
- package/dist/src/service/configuration/format/TomlConfigFormat.d.ts.map +1 -0
- package/dist/src/service/configuration/format/TomlConfigFormat.js +78 -0
- package/dist/src/service/configuration/format/YamlConfigFormat.d.ts +9 -0
- package/dist/src/service/configuration/format/YamlConfigFormat.d.ts.map +1 -0
- package/dist/src/service/configuration/format/YamlConfigFormat.js +63 -0
- package/dist/src/service/configuration/resolveSecrets.d.ts.map +1 -1
- package/dist/src/service/configuration/resolveSecrets.js +7 -1
- package/dist/toml.d.ts +2 -0
- package/dist/toml.d.ts.map +1 -0
- package/dist/toml.js +1 -0
- package/dist/yaml.d.ts +2 -0
- package/dist/yaml.d.ts.map +1 -0
- package/dist/yaml.js +1 -0
- package/index.ts +3 -0
- package/package.json +27 -3
- package/src/cli/BaseCLI.ts +4 -0
- package/src/cli/BaseCLIFeatureOptions.ts +6 -0
- package/src/runtime/runner.ts +4 -1
- package/src/service/configuration/ConfigurationServiceProvider.ts +108 -214
- package/src/service/configuration/DefaultConfigurationService.ts +378 -0
- package/src/service/configuration/DefaultKeyValueService.ts +9 -3
- package/src/service/configuration/DefaultSecretService.ts +51 -8
- package/src/service/configuration/KeyValueServiceProvider.ts +7 -2
- package/src/service/configuration/command/ConfigDumpSubCommand.ts +6 -10
- package/src/service/configuration/command/ConfigGetSubCommand.ts +198 -0
- package/src/service/configuration/command/ConfigSetSubCommand.ts +401 -0
- package/src/service/configuration/command/ConfigUnsetSubCommand.ts +126 -0
- package/src/service/configuration/configTargets.ts +481 -0
- package/src/service/configuration/format/ConfigFormat.ts +42 -0
- package/src/service/configuration/format/JsonConfigFormat.ts +22 -0
- package/src/service/configuration/format/TomlConfigFormat.ts +92 -0
- package/src/service/configuration/format/YamlConfigFormat.ts +72 -0
- package/src/service/configuration/resolveSecrets.ts +7 -1
- package/toml.ts +1 -0
- package/yaml.ts +1 -0
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
4
|
-
import { Stats } from "node:fs";
|
|
5
1
|
import type { ServiceInfo, ServiceProvider } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
2
|
import ConfigCommand from "./command/ConfigCommand.ts";
|
|
7
3
|
import type {
|
|
@@ -13,16 +9,23 @@ import type {
|
|
|
13
9
|
} from "@flowscripter/dynamic-cli-framework-api";
|
|
14
10
|
import getLogger from "../../util/logger.ts";
|
|
15
11
|
import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
16
|
-
import type { GroupCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
12
|
+
import type { SubCommand, GroupCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
17
13
|
import ConfigDumpSubCommand from "./command/ConfigDumpSubCommand.ts";
|
|
18
14
|
import ConfigGroupCommand from "./command/ConfigGroupCommand.ts";
|
|
15
|
+
import ConfigGetSubCommand from "./command/ConfigGetSubCommand.ts";
|
|
16
|
+
import ConfigSetSubCommand from "./command/ConfigSetSubCommand.ts";
|
|
17
|
+
import ConfigUnsetSubCommand from "./command/ConfigUnsetSubCommand.ts";
|
|
18
|
+
import type { CommandRegistry } from "@flowscripter/dynamic-cli-framework-api";
|
|
19
19
|
import {
|
|
20
20
|
CONFIGURATION_SERVICE_ID,
|
|
21
|
-
|
|
21
|
+
type ConfigurationService,
|
|
22
22
|
} from "@flowscripter/dynamic-cli-framework-api";
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
import DefaultConfigurationService, {
|
|
24
|
+
type KeyValueServiceScopeType,
|
|
25
|
+
} from "./DefaultConfigurationService.ts";
|
|
26
|
+
import DefaultSecretService, { type SecretsApi } from "./DefaultSecretService.ts";
|
|
27
|
+
import type ConfigFormat from "./format/ConfigFormat.ts";
|
|
28
|
+
import jsonFormat from "./format/JsonConfigFormat.ts";
|
|
26
29
|
import type { Command } from "@flowscripter/dynamic-cli-framework-api";
|
|
27
30
|
import argumentValueMerge from "../../runtime/values/argumentValueMerge.ts";
|
|
28
31
|
import {
|
|
@@ -36,23 +39,29 @@ import {
|
|
|
36
39
|
getSubCommandValuesFromEnvVars,
|
|
37
40
|
} from "../../util/envVarHelper.ts";
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
export type { KeyValueServiceScopeType };
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
const logger = getLogger("ConfigurationServiceProvider");
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The only view of the configuration which is registered in the {@link Context}, and therefore reachable by
|
|
48
|
+
* every command and service. It deliberately exposes nothing but the config file location: the
|
|
49
|
+
* {@link DefaultConfigurationService} (default values, per-scope key-value data, secret resolution and
|
|
50
|
+
* persistence) is only handed to the `config` group commands and {@link KeyValueServiceProvider}.
|
|
51
|
+
*/
|
|
52
|
+
class ConfigurationLocationService implements ConfigurationService {
|
|
53
|
+
readonly #service: DefaultConfigurationService;
|
|
45
54
|
|
|
46
|
-
public constructor(
|
|
47
|
-
this.#
|
|
55
|
+
public constructor(service: DefaultConfigurationService) {
|
|
56
|
+
this.#service = service;
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
public get configLocation(): string | undefined {
|
|
51
|
-
return this.#
|
|
60
|
+
return this.#service.configLocation;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
public setConfigLocation(location: string): void {
|
|
55
|
-
this.#
|
|
64
|
+
this.#service.setConfigLocation(location);
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
|
|
@@ -62,11 +71,9 @@ class ConfigurationServiceImpl implements ConfigurationService {
|
|
|
62
71
|
*
|
|
63
72
|
* The same configuration file also backs the raw, per-scope key-value data consumed by
|
|
64
73
|
* {@link KeyValueServiceProvider} (which holds a direct reference to this provider - see
|
|
65
|
-
* {@link getKeyValueData} and {@link flushIfDirty}
|
|
66
|
-
* {@link
|
|
67
|
-
*
|
|
68
|
-
* reason - a full config dump would leak every other command's/service's key-value data if it
|
|
69
|
-
* were reachable through a general lookup).
|
|
74
|
+
* {@link getKeyValueData} and {@link flushIfDirty}). The configuration data and its persistence are
|
|
75
|
+
* owned by {@link DefaultConfigurationService}, which is passed directly to the `config` group commands. Only a
|
|
76
|
+
* view exposing the config file location is registered under {@link CONFIGURATION_SERVICE_ID}.
|
|
70
77
|
*
|
|
71
78
|
* **Default Command Arguments**
|
|
72
79
|
*
|
|
@@ -77,7 +84,7 @@ class ConfigurationServiceImpl implements ConfigurationService {
|
|
|
77
84
|
*
|
|
78
85
|
* *Configuration File*
|
|
79
86
|
*
|
|
80
|
-
* A JSON
|
|
87
|
+
* A file (JSON by default, see {@link ConfigFormat}) where the structure matches {@link Values} and the defaults are stored
|
|
81
88
|
* under a top level `defaults` property. The second level of properties is used to refer to {@link Command.name}
|
|
82
89
|
* (or `<group name>:<member name>` for a member of a {@link GroupCommand}) and the contained values are treated as
|
|
83
90
|
* command {@link Values}. As an example:
|
|
@@ -108,14 +115,19 @@ class ConfigurationServiceImpl implements ConfigurationService {
|
|
|
108
115
|
* }
|
|
109
116
|
* ```
|
|
110
117
|
*
|
|
111
|
-
* The
|
|
118
|
+
* The syntax of the file is set by the `configFormat` constructor argument: JSON (the default), or YAML or TOML
|
|
119
|
+
* from the `yaml` and `toml` entry points of this package. The format is never inferred from the file name or
|
|
120
|
+
* extension and existing files are not migrated between formats. For YAML and TOML, comments and layout in the
|
|
121
|
+
* existing file are preserved when it is written back.
|
|
122
|
+
*
|
|
123
|
+
* The default location of the configuration file is `$HOME/.<application_name>.<format extension>`. If `$HOME` is not defined no
|
|
112
124
|
* default configuration will be used. The location of the configuration file can be modified via the
|
|
113
125
|
* {@link ConfigCommand}.
|
|
114
126
|
*
|
|
115
|
-
* NOTE:
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
127
|
+
* NOTE: Secrets in default arguments are referenced in the config file or an environment variable using
|
|
128
|
+
* the sentinel format `__SECRET__:<bun_secret_name>` and resolved from the OS secret store before being
|
|
129
|
+
* returned as default argument values. A command's defaults may only reference secrets stored for that
|
|
130
|
+
* command via `config set` (named `defaults_Z<command>_Z<option path>`, each part escaped). See {@link KeyValueServiceProvider} for the
|
|
119
131
|
* generic key-value store which uses the same secret sentinel mechanism.
|
|
120
132
|
*
|
|
121
133
|
* *Environment Variables*
|
|
@@ -164,33 +176,17 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
164
176
|
public readonly envVarsEnabled: boolean;
|
|
165
177
|
public readonly configEnabled: boolean;
|
|
166
178
|
public readonly secretServiceEnabled: boolean;
|
|
167
|
-
|
|
168
|
-
// the location of the currently managed configuration data
|
|
169
|
-
public configLocation: string | undefined;
|
|
170
|
-
|
|
171
|
-
// the configuration data to be used by the CLI runner implementation when setting command defaults.
|
|
172
|
-
// SingleValueType is for GlobalCommandArgument values, Values is for
|
|
173
|
-
// SubCommandArgument values.
|
|
174
|
-
public defaultsData: Map<string, Values | SingleValueType> = new Map();
|
|
175
|
-
|
|
176
|
-
// the underlying per-scope key-value data, read from/written to the configuration file. Exposed
|
|
177
|
-
// (raw, not wrapped in a KeyValueService) only via getKeyValueData(), for KeyValueServiceProvider's
|
|
178
|
-
// exclusive use.
|
|
179
|
-
#commandKeyValueData = new Map<string, Map<string, ValueNode>>();
|
|
180
|
-
#serviceKeyValueData = new Map<string, Map<string, ValueNode>>();
|
|
181
|
-
|
|
182
|
-
// secret service used only to resolve secret sentinels embedded in default command argument
|
|
183
|
-
// values (getDefaultArgumentValues()) - unrelated to per-command/per-service KV scoping, so it
|
|
184
|
-
// uses its own fixed, dedicated scope prefix ("defaults"). Its scope is otherwise irrelevant,
|
|
185
|
-
// since only getSecret() (which doesn't consult scope) is ever called on it.
|
|
186
|
-
#defaultsSecretService: DefaultSecretService | undefined;
|
|
179
|
+
public readonly configFormat: ConfigFormat;
|
|
187
180
|
|
|
188
181
|
/**
|
|
189
|
-
*
|
|
190
|
-
* {@link
|
|
191
|
-
* current value.
|
|
182
|
+
* The {@link DefaultConfigurationService} which owns the configuration data. It is never registered
|
|
183
|
+
* in the {@link Context}: it is handed directly to the `config` group commands only.
|
|
192
184
|
*/
|
|
193
|
-
readonly #configurationService:
|
|
185
|
+
readonly #configurationService: DefaultConfigurationService;
|
|
186
|
+
|
|
187
|
+
readonly #hasCommandRegistry: boolean;
|
|
188
|
+
|
|
189
|
+
readonly #secretsApi: SecretsApi | undefined;
|
|
194
190
|
|
|
195
191
|
/**
|
|
196
192
|
* Create an instance of the service provider with the specified details.
|
|
@@ -200,12 +196,19 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
200
196
|
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
201
197
|
* @param secretServiceEnabled optionally enable OS-native secret storage via Bun.secrets for resolving
|
|
202
198
|
* secrets embedded in default argument values: `configEnabled` must be true in this case
|
|
199
|
+
* @param configFormat optionally set the syntax of the configuration file, defaults to JSON.
|
|
200
|
+
* @param commandRegistry optionally provide the registry of all commands, required for the `config:get`,
|
|
201
|
+
* `config:set` and `config:unset` commands to be provided.
|
|
202
|
+
* @param secretsApi optionally override the OS secret store used for storing SECRET default values.
|
|
203
203
|
*/
|
|
204
204
|
public constructor(
|
|
205
205
|
servicePriority: number,
|
|
206
206
|
envVarsEnabled = false,
|
|
207
207
|
configEnabled = false,
|
|
208
208
|
secretServiceEnabled = false,
|
|
209
|
+
configFormat: ConfigFormat = jsonFormat,
|
|
210
|
+
commandRegistry?: CommandRegistry,
|
|
211
|
+
secretsApi?: SecretsApi,
|
|
209
212
|
) {
|
|
210
213
|
if (!configEnabled && secretServiceEnabled) {
|
|
211
214
|
throw new Error("configEnabled must be true if secretServiceEnabled is true");
|
|
@@ -214,8 +217,15 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
214
217
|
this.envVarsEnabled = envVarsEnabled;
|
|
215
218
|
this.configEnabled = configEnabled;
|
|
216
219
|
this.secretServiceEnabled = secretServiceEnabled;
|
|
217
|
-
|
|
218
|
-
this.#
|
|
220
|
+
this.configFormat = configFormat;
|
|
221
|
+
this.#hasCommandRegistry = commandRegistry !== undefined;
|
|
222
|
+
this.#secretsApi = secretsApi;
|
|
223
|
+
|
|
224
|
+
this.#configurationService = new DefaultConfigurationService(
|
|
225
|
+
envVarsEnabled,
|
|
226
|
+
configFormat,
|
|
227
|
+
commandRegistry,
|
|
228
|
+
);
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
public getServiceInfo(cliConfig: CLIConfig): Promise<ServiceInfo> {
|
|
@@ -223,22 +233,53 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
223
233
|
|
|
224
234
|
if (this.configEnabled) {
|
|
225
235
|
commands.push(new ConfigCommand(this.servicePriority));
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
236
|
+
const members: Array<SubCommand> = [new ConfigDumpSubCommand(this.#configurationService)];
|
|
237
|
+
if (this.#hasCommandRegistry) {
|
|
238
|
+
members.push(
|
|
239
|
+
new ConfigGetSubCommand(this.#configurationService),
|
|
240
|
+
new ConfigSetSubCommand(this.#configurationService),
|
|
241
|
+
new ConfigUnsetSubCommand(this.#configurationService),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
commands.push(new ConfigGroupCommand(members));
|
|
229
245
|
}
|
|
230
246
|
if (this.secretServiceEnabled) {
|
|
231
|
-
this.#
|
|
247
|
+
this.#configurationService.setDefaultsSecretService(
|
|
248
|
+
new DefaultSecretService(cliConfig.name, ["defaults"], this.#secretsApi),
|
|
249
|
+
);
|
|
232
250
|
}
|
|
233
251
|
return Promise.resolve({
|
|
234
|
-
service: this.#configurationService,
|
|
252
|
+
service: new ConfigurationLocationService(this.#configurationService),
|
|
235
253
|
// this may be empty if configEnabled is false
|
|
236
254
|
commands,
|
|
237
255
|
});
|
|
238
256
|
}
|
|
239
257
|
|
|
258
|
+
/**
|
|
259
|
+
* The {@link DefaultConfigurationService} which owns the configuration data, for trusted wiring and tests.
|
|
260
|
+
*/
|
|
261
|
+
public get configurationService(): DefaultConfigurationService {
|
|
262
|
+
return this.#configurationService;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public get configLocation(): string | undefined {
|
|
266
|
+
return this.#configurationService.configLocation;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
public set configLocation(location: string | undefined) {
|
|
270
|
+
this.#configurationService.configLocation = location;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public get defaultsData(): Map<string, Values | SingleValueType> {
|
|
274
|
+
return this.#configurationService.defaultsData;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public set defaultsData(data: Map<string, Values | SingleValueType>) {
|
|
278
|
+
this.#configurationService.defaultsData = data;
|
|
279
|
+
}
|
|
280
|
+
|
|
240
281
|
public setConfigLocation(configLocation: string) {
|
|
241
|
-
this.configLocation
|
|
282
|
+
this.#configurationService.setConfigLocation(configLocation);
|
|
242
283
|
}
|
|
243
284
|
|
|
244
285
|
/**
|
|
@@ -293,19 +334,8 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
293
334
|
}
|
|
294
335
|
}
|
|
295
336
|
|
|
296
|
-
if (result !== undefined
|
|
297
|
-
result = await
|
|
298
|
-
result,
|
|
299
|
-
async (bunSecretName) => {
|
|
300
|
-
const secretValue = await this.#defaultsSecretService!.getSecret(bunSecretName);
|
|
301
|
-
if (secretValue === null) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
`Secret not found in OS secret store for sentinel: '${SECRET_SENTINEL_PREFIX}${bunSecretName}'`,
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
return secretValue;
|
|
307
|
-
},
|
|
308
|
-
);
|
|
337
|
+
if (result !== undefined) {
|
|
338
|
+
result = await this.#configurationService.resolveDefaultSecrets(result, commandKey);
|
|
309
339
|
}
|
|
310
340
|
|
|
311
341
|
return result;
|
|
@@ -322,12 +352,7 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
322
352
|
scopeType: KeyValueServiceScopeType,
|
|
323
353
|
scopeKey: string,
|
|
324
354
|
): Map<string, ValueNode> {
|
|
325
|
-
|
|
326
|
-
scopeType === "command" ? this.#commandKeyValueData : this.#serviceKeyValueData;
|
|
327
|
-
if (!keyValueData.has(scopeKey)) {
|
|
328
|
-
keyValueData.set(scopeKey, new Map());
|
|
329
|
-
}
|
|
330
|
-
return keyValueData.get(scopeKey)!;
|
|
355
|
+
return this.#configurationService.getKeyValueData(scopeType, scopeKey);
|
|
331
356
|
}
|
|
332
357
|
|
|
333
358
|
/**
|
|
@@ -337,149 +362,18 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
337
362
|
*
|
|
338
363
|
* @param isDirty whether any scoped KeyValueService has unwritten changes.
|
|
339
364
|
*/
|
|
340
|
-
public
|
|
341
|
-
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
if (this.configLocation === undefined) {
|
|
345
|
-
throw new Error("Attempt to write updated config with no configLocation set");
|
|
346
|
-
}
|
|
347
|
-
await this.#writeConfig(this.configLocation);
|
|
365
|
+
public flushIfDirty(isDirty: boolean): Promise<void> {
|
|
366
|
+
return this.#configurationService.flushIfDirty(isDirty);
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
public async initService(context: Context): Promise<void> {
|
|
351
370
|
if (!this.configEnabled) {
|
|
352
371
|
return;
|
|
353
372
|
}
|
|
354
|
-
|
|
355
|
-
let isDefault = false;
|
|
356
|
-
if (this.configLocation === undefined) {
|
|
357
|
-
isDefault = true;
|
|
358
|
-
// default to `$HOME/.<application_name>.json`
|
|
359
|
-
const home = process.env["HOME"];
|
|
360
|
-
if (home) {
|
|
361
|
-
this.configLocation = path.join(home, `.${context.cliConfig.name.replace(/\W/g, "")}.json`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (this.configLocation === undefined) {
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
let fileInfo: Stats;
|
|
368
|
-
try {
|
|
369
|
-
fileInfo = await fs.lstat(this.configLocation);
|
|
370
|
-
} catch (err) {
|
|
371
|
-
const error = err as Error;
|
|
372
|
-
if (isDefault) {
|
|
373
|
-
logger.debug(
|
|
374
|
-
"Default config file location: %s does not exist or is not visible - ignoring: %s",
|
|
375
|
-
this.configLocation,
|
|
376
|
-
error.message,
|
|
377
|
-
);
|
|
378
|
-
return;
|
|
379
|
-
} else {
|
|
380
|
-
throw new Error(
|
|
381
|
-
`Config file location: '${this.configLocation}' doesn't exist or not visible: ${error.message}`,
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
if (fileInfo.isDirectory()) {
|
|
386
|
-
throw new Error(
|
|
387
|
-
`Config file location: '${this.configLocation}' is a directory and not a file!`,
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
await this.#readConfig(this.configLocation);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async #readConfig(location: string): Promise<void> {
|
|
395
|
-
try {
|
|
396
|
-
logger.debug("Reading config from: %s", location);
|
|
397
|
-
|
|
398
|
-
const data = await fs.readFile(location);
|
|
399
|
-
const config = JSON.parse(data.toString());
|
|
400
|
-
|
|
401
|
-
if (config === undefined) {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
this.defaultsData.clear();
|
|
405
|
-
this.#commandKeyValueData.clear();
|
|
406
|
-
this.#serviceKeyValueData.clear();
|
|
407
|
-
if (config.defaults !== undefined) {
|
|
408
|
-
const defaults = config.defaults;
|
|
409
|
-
Object.keys(defaults).forEach((commandName) => {
|
|
410
|
-
this.defaultsData.set(commandName, defaults[commandName] as Values);
|
|
411
|
-
logger.debug("Set default argument values for command name: %s", commandName);
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
if (config["key-values"] !== undefined) {
|
|
415
|
-
const keyValues = config["key-values"];
|
|
416
|
-
if (keyValues.commands !== undefined) {
|
|
417
|
-
const commandKeyValues = keyValues.commands;
|
|
418
|
-
Object.keys(commandKeyValues).forEach((commandName) => {
|
|
419
|
-
this.#commandKeyValueData.set(
|
|
420
|
-
commandName,
|
|
421
|
-
new Map(Object.entries(commandKeyValues[commandName])),
|
|
422
|
-
);
|
|
423
|
-
logger.debug("Set default argument values for command name: %s", commandName);
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
if (keyValues.services !== undefined) {
|
|
427
|
-
const serviceKeyValues = keyValues.services;
|
|
428
|
-
Object.keys(serviceKeyValues).forEach((serviceId) => {
|
|
429
|
-
this.#serviceKeyValueData.set(
|
|
430
|
-
serviceId,
|
|
431
|
-
new Map(Object.entries(serviceKeyValues[serviceId])),
|
|
432
|
-
);
|
|
433
|
-
logger.debug("Set default argument values for service ID: %s", serviceId);
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
} catch (err) {
|
|
438
|
-
throw new Error(`Failed to read config at location: '${this.configLocation}': ${err}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
async #writeConfig(location: string): Promise<void> {
|
|
443
|
-
logger.debug("Writing config to: %s", location);
|
|
444
|
-
|
|
445
|
-
try {
|
|
446
|
-
await fs.writeFile(location, this.getConfigString());
|
|
447
|
-
} catch (err) {
|
|
448
|
-
throw new Error(`Failed to write config at location: '${this.configLocation}': ${err}`);
|
|
449
|
-
}
|
|
373
|
+
await this.#configurationService.init(context.cliConfig.name);
|
|
450
374
|
}
|
|
451
375
|
|
|
452
376
|
public getConfigString(): string {
|
|
453
|
-
|
|
454
|
-
defaults?: Record<string, Values | SingleValueType>;
|
|
455
|
-
"key-values"?: {
|
|
456
|
-
commands?: Record<string, Record<string, ValueNode>>;
|
|
457
|
-
services?: Record<string, Record<string, ValueNode>>;
|
|
458
|
-
};
|
|
459
|
-
} = {};
|
|
460
|
-
|
|
461
|
-
if (this.defaultsData.size > 0) {
|
|
462
|
-
config.defaults = Object.fromEntries(this.defaultsData);
|
|
463
|
-
}
|
|
464
|
-
if (this.#commandKeyValueData.size > 0 || this.#serviceKeyValueData.size > 0) {
|
|
465
|
-
config["key-values"] = {};
|
|
466
|
-
if (this.#commandKeyValueData.size > 0) {
|
|
467
|
-
config["key-values"].commands = {};
|
|
468
|
-
for (const [commandName, keyValueData] of this.#commandKeyValueData.entries()) {
|
|
469
|
-
if (keyValueData.size > 0) {
|
|
470
|
-
config["key-values"].commands[commandName] = Object.fromEntries(keyValueData);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
if (this.#serviceKeyValueData.size > 0) {
|
|
475
|
-
config["key-values"].services = {};
|
|
476
|
-
for (const [serviceId, keyValueData] of this.#serviceKeyValueData.entries()) {
|
|
477
|
-
if (keyValueData.size > 0) {
|
|
478
|
-
config["key-values"].services[serviceId] = Object.fromEntries(keyValueData);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
return JSON.stringify(config, null, 2);
|
|
377
|
+
return this.#configurationService.getConfigString();
|
|
484
378
|
}
|
|
485
379
|
}
|