@flowscripter/dynamic-cli-framework 6.0.1 → 7.0.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 +2 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/src/cli/BaseCLI.d.ts +8 -0
- package/dist/src/cli/BaseCLI.d.ts.map +1 -1
- package/dist/src/cli/BaseCLI.js +37 -4
- package/dist/src/launcher.d.ts +3 -3
- package/dist/src/launcher.d.ts.map +1 -1
- package/dist/src/launcher.js +6 -4
- package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts +22 -0
- package/dist/src/runtime/lifecycle/PriorityTaskList.d.ts.map +1 -0
- package/dist/src/runtime/lifecycle/PriorityTaskList.js +23 -0
- package/dist/src/runtime/runner.d.ts +15 -7
- package/dist/src/runtime/runner.d.ts.map +1 -1
- package/dist/src/runtime/runner.js +82 -53
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts +26 -82
- package/dist/src/service/configuration/ConfigurationServiceProvider.d.ts.map +1 -1
- package/dist/src/service/configuration/ConfigurationServiceProvider.js +68 -163
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts +6 -3
- package/dist/src/service/configuration/DefaultKeyValueService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultKeyValueService.js +7 -24
- package/dist/src/service/configuration/DefaultSecretService.d.ts +1 -3
- package/dist/src/service/configuration/DefaultSecretService.d.ts.map +1 -1
- package/dist/src/service/configuration/DefaultSecretService.js +2 -10
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts +105 -0
- package/dist/src/service/configuration/KeyValueServiceProvider.d.ts.map +1 -0
- package/dist/src/service/configuration/KeyValueServiceProvider.js +201 -0
- package/dist/src/service/configuration/command/ConfigCommand.d.ts +4 -5
- package/dist/src/service/configuration/command/ConfigCommand.d.ts.map +1 -1
- package/dist/src/service/configuration/command/ConfigCommand.js +7 -6
- package/dist/src/service/configuration/command/DumpConfigCommand.d.ts +7 -3
- package/dist/src/service/configuration/command/DumpConfigCommand.d.ts.map +1 -1
- package/dist/src/service/configuration/command/DumpConfigCommand.js +10 -5
- package/dist/src/service/fetch/DefaultFetchService.d.ts +2 -2
- package/dist/src/service/fetch/DefaultFetchService.d.ts.map +1 -1
- package/dist/src/service/fetch/DefaultFetchService.js +17 -12
- package/dist/src/service/fetch/FetchServiceProvider.d.ts.map +1 -1
- package/dist/src/service/fetch/FetchServiceProvider.js +2 -3
- package/dist/src/service/printer/PrinterServiceProvider.d.ts.map +1 -1
- package/dist/src/service/printer/PrinterServiceProvider.js +11 -7
- package/dist/src/service/printer/command/DarkModeCommand.d.ts +3 -5
- package/dist/src/service/printer/command/DarkModeCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/DarkModeCommand.js +6 -6
- package/dist/src/service/printer/command/LogLevelCommand.d.ts +3 -5
- package/dist/src/service/printer/command/LogLevelCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/LogLevelCommand.js +9 -9
- package/dist/src/service/printer/command/NoColorCommand.d.ts +3 -5
- package/dist/src/service/printer/command/NoColorCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/NoColorCommand.js +6 -6
- package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts +2 -4
- package/dist/src/service/printer/command/NoHyperlinksCommand.d.ts.map +1 -1
- package/dist/src/service/printer/command/NoHyperlinksCommand.js +5 -5
- package/dist/src/service/prompter/PrompterServiceProvider.js +1 -1
- package/dist/src/service/prompter/command/NoPromptCommand.d.ts +2 -4
- package/dist/src/service/prompter/command/NoPromptCommand.d.ts.map +1 -1
- package/dist/src/service/prompter/command/NoPromptCommand.js +5 -5
- package/dist/src/service/shutdown/DefaultShutdownService.d.ts +4 -3
- package/dist/src/service/shutdown/DefaultShutdownService.d.ts.map +1 -1
- package/dist/src/service/shutdown/DefaultShutdownService.js +4 -3
- package/dist/src/service/shutdown/ShutdownServiceProvider.js +2 -2
- package/dist/src/service/spawn/DefaultSpawnService.d.ts +2 -2
- package/dist/src/service/spawn/DefaultSpawnService.d.ts.map +1 -1
- package/dist/src/service/spawn/DefaultSpawnService.js +17 -14
- package/dist/src/service/spawn/SpawnServiceProvider.d.ts.map +1 -1
- package/dist/src/service/spawn/SpawnServiceProvider.js +1 -4
- package/dist/src/service/startup/DefaultStartupService.d.ts +10 -0
- package/dist/src/service/startup/DefaultStartupService.d.ts.map +1 -0
- package/dist/src/service/startup/DefaultStartupService.js +13 -0
- package/dist/src/service/startup/StartupServiceProvider.d.ts +17 -0
- package/dist/src/service/startup/StartupServiceProvider.d.ts.map +1 -0
- package/dist/src/service/startup/StartupServiceProvider.js +26 -0
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts +10 -4
- package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
- package/dist/src/service/upgrade/DefaultUpgradeService.js +156 -37
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts +10 -0
- package/dist/src/service/upgrade/UpgradeServiceProvider.d.ts.map +1 -1
- package/dist/src/service/upgrade/UpgradeServiceProvider.js +25 -6
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts +0 -3
- package/dist/src/service/upgrade/command/UpgradeSubCommand.d.ts.map +1 -1
- package/dist/src/service/upgrade/command/UpgradeSubCommand.js +5 -13
- package/dist/src/startup/banner/bannerStartupTask.d.ts +17 -0
- package/dist/src/startup/banner/bannerStartupTask.d.ts.map +1 -0
- package/dist/src/startup/banner/bannerStartupTask.js +66 -0
- package/dist/src/{service → startup}/banner/command/NoBannerCommand.d.ts +8 -2
- package/dist/src/startup/banner/command/NoBannerCommand.d.ts.map +1 -0
- package/dist/src/{service → startup}/banner/command/NoBannerCommand.js +4 -4
- package/index.ts +19 -3
- package/package.json +2 -2
- package/src/cli/BaseCLI.ts +49 -3
- package/src/launcher.ts +7 -2
- package/src/runtime/lifecycle/PriorityTaskList.ts +34 -0
- package/src/runtime/runner.ts +106 -67
- package/src/service/configuration/ConfigurationServiceProvider.ts +80 -180
- package/src/service/configuration/DefaultKeyValueService.ts +8 -27
- package/src/service/configuration/DefaultSecretService.ts +3 -13
- package/src/service/configuration/KeyValueServiceProvider.ts +246 -0
- package/src/service/configuration/command/ConfigCommand.ts +12 -11
- package/src/service/configuration/command/DumpConfigCommand.ts +10 -9
- package/src/service/fetch/DefaultFetchService.ts +18 -12
- package/src/service/fetch/FetchServiceProvider.ts +2 -4
- package/src/service/printer/PrinterServiceProvider.ts +11 -7
- package/src/service/printer/command/DarkModeCommand.ts +7 -8
- package/src/service/printer/command/LogLevelCommand.ts +10 -11
- package/src/service/printer/command/NoColorCommand.ts +7 -8
- package/src/service/printer/command/NoHyperlinksCommand.ts +6 -7
- package/src/service/prompter/PrompterServiceProvider.ts +1 -1
- package/src/service/prompter/command/NoPromptCommand.ts +6 -7
- package/src/service/shutdown/DefaultShutdownService.ts +5 -4
- package/src/service/shutdown/ShutdownServiceProvider.ts +2 -2
- package/src/service/spawn/DefaultSpawnService.ts +18 -14
- package/src/service/spawn/SpawnServiceProvider.ts +1 -5
- package/src/service/startup/DefaultStartupService.ts +17 -0
- package/src/service/startup/StartupServiceProvider.ts +36 -0
- package/src/service/upgrade/DefaultUpgradeService.ts +202 -42
- package/src/service/upgrade/UpgradeServiceProvider.ts +31 -8
- package/src/service/upgrade/command/UpgradeSubCommand.ts +5 -15
- package/src/startup/banner/bannerStartupTask.ts +96 -0
- package/src/{service → startup}/banner/command/NoBannerCommand.ts +12 -5
- package/dist/src/service/banner/BannerServiceProvider.d.ts +0 -29
- package/dist/src/service/banner/BannerServiceProvider.d.ts.map +0 -1
- package/dist/src/service/banner/BannerServiceProvider.js +0 -69
- package/dist/src/service/banner/command/NoBannerCommand.d.ts.map +0 -1
- package/src/service/banner/BannerServiceProvider.ts +0 -89
|
@@ -5,7 +5,25 @@ import { printCommandExecutionError, printNoCommandRecognisedError, printNoComma
|
|
|
5
5
|
import { scanForGlobalModifierCommandClauses, scanForNonModifierCommandClause } from "./scanner.js";
|
|
6
6
|
import { parseGlobalCommandClause, parseSubCommandClause } from "./parser.js";
|
|
7
7
|
import { ARGUMENT_PROMPTER_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
8
|
+
import DefaultStartupService from "../service/startup/DefaultStartupService.js";
|
|
9
|
+
import { sortByPriority } from "./lifecycle/PriorityTaskList.js";
|
|
8
10
|
const logger = getLogger("runner");
|
|
11
|
+
/**
|
|
12
|
+
* Wrap a {@link ServiceProvider}'s `initService()` call as a {@link StartupTask} so it can be
|
|
13
|
+
* merged with directly-registered {@link StartupTask}s (e.g. the banner task) into a single
|
|
14
|
+
* priority-ordered sequence.
|
|
15
|
+
*/
|
|
16
|
+
function toProviderStartupTask(serviceProvider, commandRegistry) {
|
|
17
|
+
return {
|
|
18
|
+
id: serviceProvider.serviceId,
|
|
19
|
+
priority: serviceProvider.servicePriority,
|
|
20
|
+
mode: "blocking",
|
|
21
|
+
modifierCommands: Array.from(commandRegistry
|
|
22
|
+
.getGlobalModifierCommandsByNameProvidedByService(serviceProvider.serviceId)
|
|
23
|
+
.values()),
|
|
24
|
+
run: (context) => serviceProvider.initService(context),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
9
27
|
async function attemptPromptForMissingArguments(parseResult, context) {
|
|
10
28
|
if (!context.doesServiceExist(ARGUMENT_PROMPTER_SERVICE_ID)) {
|
|
11
29
|
return undefined;
|
|
@@ -30,37 +48,27 @@ async function attemptPromptForMissingArguments(parseResult, context) {
|
|
|
30
48
|
*
|
|
31
49
|
* @param parseResult the {@link ParseResult} to execute.
|
|
32
50
|
* @param context the {@link Context} to use.
|
|
33
|
-
* @param
|
|
51
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
34
52
|
* @param isDefaultCommand whether the command is the default command for the CLI.
|
|
35
53
|
*/
|
|
36
|
-
async function executeParsedCommand(parseResult, context,
|
|
54
|
+
async function executeParsedCommand(parseResult, context, keyValueServiceProvider, isDefaultCommand = false) {
|
|
37
55
|
try {
|
|
38
56
|
if (parseResult.groupCommand !== undefined) {
|
|
39
57
|
logger.debug("Executing group command with name: %s", parseResult.groupCommand.name);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await parseResult.groupCommand.execute(context);
|
|
45
|
-
if (configurationServiceProvider?.keyValueServiceEnabled ||
|
|
46
|
-
configurationServiceProvider?.secretServiceEnabled) {
|
|
47
|
-
await configurationServiceProvider.clearKeyValueScope();
|
|
48
|
-
}
|
|
58
|
+
const groupContext = keyValueServiceProvider
|
|
59
|
+
? keyValueServiceProvider.getContextForScope(context, "command", parseResult.groupCommand.name)
|
|
60
|
+
: context;
|
|
61
|
+
await parseResult.groupCommand.execute(groupContext);
|
|
49
62
|
}
|
|
50
63
|
logger.debug("Executing command with name: %s and args: %O", parseResult.command.name, parseResult.populatedArgumentValues);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
64
|
+
const commandContext = keyValueServiceProvider
|
|
65
|
+
? keyValueServiceProvider.getContextForScope(context, "command", parseResult.command.name)
|
|
66
|
+
: context;
|
|
55
67
|
if (isSubCommand(parseResult.command)) {
|
|
56
|
-
await parseResult.command.execute(
|
|
68
|
+
await parseResult.command.execute(commandContext, parseResult.populatedArgumentValues);
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
|
-
await parseResult.command.execute(
|
|
60
|
-
}
|
|
61
|
-
if (configurationServiceProvider?.keyValueServiceEnabled ||
|
|
62
|
-
configurationServiceProvider?.secretServiceEnabled) {
|
|
63
|
-
await configurationServiceProvider.clearKeyValueScope();
|
|
71
|
+
await parseResult.command.execute(commandContext, parseResult.populatedArgumentValues);
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
catch (err) {
|
|
@@ -86,9 +94,10 @@ async function executeParsedCommand(parseResult, context, configurationServicePr
|
|
|
86
94
|
* @param globalModifierCommandsByName a map of non-modifier {@link Command} instances by name to parse for.
|
|
87
95
|
* @param globalModifierCommandsByShortAlias a map of {@link GlobalModifierCommand} instances by short alias to use when scanning.
|
|
88
96
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
97
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
89
98
|
* @param context the {@link Context} to use.
|
|
90
99
|
*/
|
|
91
|
-
async function findAndExecuteGlobalModifierCommands(availableArgSequences, unusedArgSequences, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, context) {
|
|
100
|
+
async function findAndExecuteGlobalModifierCommands(availableArgSequences, unusedArgSequences, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, keyValueServiceProvider, context) {
|
|
92
101
|
// build a list of GlobalModifierCommands to execute
|
|
93
102
|
const globalModifierCommandParseResults = [];
|
|
94
103
|
// maintain a list of remaining GlobalModifierCommands to scan
|
|
@@ -151,7 +160,7 @@ async function findAndExecuteGlobalModifierCommands(availableArgSequences, unuse
|
|
|
151
160
|
a.command.executePriority);
|
|
152
161
|
for (const parseResult of globalModifierCommandParseResults) {
|
|
153
162
|
// execute and fast fail on error
|
|
154
|
-
const runResult = await executeParsedCommand(parseResult, context,
|
|
163
|
+
const runResult = await executeParsedCommand(parseResult, context, keyValueServiceProvider);
|
|
155
164
|
if (runResult.runState !== RunState.SUCCESS) {
|
|
156
165
|
return runResult;
|
|
157
166
|
}
|
|
@@ -166,9 +175,10 @@ async function findAndExecuteGlobalModifierCommands(availableArgSequences, unuse
|
|
|
166
175
|
* @param globalCommandsByShortAlias the map of {@link GlobalCommand} instances by short alias to use when scanning.
|
|
167
176
|
* @param groupAndMemberCommandsByJoinedName optional map of {@link GroupCommand} and member {@link SubCommand} instances to use when scanning.
|
|
168
177
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
178
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
169
179
|
* @param context the {@link Context} to use.
|
|
170
180
|
*/
|
|
171
|
-
async function findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonModifierCommandsByName, globalCommandsByShortAlias, groupAndMemberCommandsByJoinedName, configurationServiceProvider, context) {
|
|
181
|
+
async function findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonModifierCommandsByName, globalCommandsByShortAlias, groupAndMemberCommandsByJoinedName, configurationServiceProvider, keyValueServiceProvider, context) {
|
|
172
182
|
let parseResult;
|
|
173
183
|
const scanResult = scanForNonModifierCommandClause(availableArgs, nonModifierCommandsByName, globalCommandsByShortAlias, groupAndMemberCommandsByJoinedName);
|
|
174
184
|
// add any unused args back to the list of still available args for next scan operation
|
|
@@ -248,7 +258,7 @@ async function findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonMo
|
|
|
248
258
|
if (unusedArgs.length > 0) {
|
|
249
259
|
await printUnusedArgsWarning(context, unusedArgs.flat());
|
|
250
260
|
}
|
|
251
|
-
return executeParsedCommand(parseResult, context,
|
|
261
|
+
return executeParsedCommand(parseResult, context, keyValueServiceProvider);
|
|
252
262
|
}
|
|
253
263
|
/**
|
|
254
264
|
* Assume provided command in use and parse arguments and execute with specified argument values if valid.
|
|
@@ -257,9 +267,10 @@ async function findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonMo
|
|
|
257
267
|
* @param unusedArgs an array to which any unused argument sequences will be added.
|
|
258
268
|
* @param defaultNonModifierCommand the default non-modifier {@link Command} to use.
|
|
259
269
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
270
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
260
271
|
* @param context the {@link Context} to use.
|
|
261
272
|
*/
|
|
262
|
-
async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs, defaultNonModifierCommand, configurationServiceProvider, context) {
|
|
273
|
+
async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs, defaultNonModifierCommand, configurationServiceProvider, keyValueServiceProvider, context) {
|
|
263
274
|
const defaultArgumentValues = configurationServiceProvider
|
|
264
275
|
? await configurationServiceProvider.getDefaultArgumentValues(context.cliConfig, defaultNonModifierCommand)
|
|
265
276
|
: undefined;
|
|
@@ -348,7 +359,7 @@ async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs
|
|
|
348
359
|
if (unusedArgs.length > 0) {
|
|
349
360
|
await printUnusedArgsWarning(context, unusedArgs.flat());
|
|
350
361
|
}
|
|
351
|
-
return executeParsedCommand(parseResult, context,
|
|
362
|
+
return executeParsedCommand(parseResult, context, keyValueServiceProvider, true);
|
|
352
363
|
}
|
|
353
364
|
/**
|
|
354
365
|
* Scan arguments and execute the parsed {@link Command} clauses with provided argument values
|
|
@@ -356,14 +367,15 @@ async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs
|
|
|
356
367
|
*
|
|
357
368
|
* An overview of the logic (assuming the optional ConfigurationServiceProvider is provided):
|
|
358
369
|
*
|
|
359
|
-
* 1. For each
|
|
360
|
-
*
|
|
370
|
+
* 1. For each StartupTask in priority order (whether backed by a ServiceProvider's
|
|
371
|
+
* initService() or registered directly, e.g. the banner task):
|
|
372
|
+
* - Scan arguments for GlobalModifierCommand clauses provided by the task
|
|
361
373
|
* - For each discovered clause:
|
|
362
374
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
363
375
|
* - Parse the arguments for the GlobalModifierCommand
|
|
364
376
|
* - Return on error
|
|
365
377
|
* - Add to list of GlobalModifierCommands to execute
|
|
366
|
-
* - Scan ConfigurationServiceProvider for GlobalModifierCommand clauses provided by the
|
|
378
|
+
* - Scan ConfigurationServiceProvider for GlobalModifierCommand clauses provided by the task not already found in arguments
|
|
367
379
|
* - For each configured clause:
|
|
368
380
|
* - Parse the arguments for the GlobalModifierCommand
|
|
369
381
|
* - Return on error
|
|
@@ -372,14 +384,18 @@ async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs
|
|
|
372
384
|
* - For each GlobalModifierCommand clause:
|
|
373
385
|
* - Execute the GlobalModifierCommand
|
|
374
386
|
* - Return on error
|
|
375
|
-
* -
|
|
376
|
-
*
|
|
387
|
+
* - Give the task a Context whose KeyValueService is bound permanently to its own scope
|
|
388
|
+
* (keyed by task.id), via KeyValueServiceProvider if provided.
|
|
389
|
+
* - Run the task: awaited before continuing if its mode is "blocking" (the default, and how
|
|
390
|
+
* every ServiceProvider's initService() behaves); fired without awaiting if "background"
|
|
391
|
+
* (errors logged, not propagated).
|
|
392
|
+
* 2. Scan arguments for any GlobalModifierCommand clauses not provided by a StartupTask
|
|
377
393
|
* - For each discovered clause:
|
|
378
394
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
379
395
|
* - Parse the arguments for the GlobalModifierCommand
|
|
380
396
|
* - Return on error
|
|
381
397
|
* - Add to list of GlobalModifierCommands to execute
|
|
382
|
-
* 3. Scan ConfigurationServiceProvider for GlobalModifierCommand clauses not provided by
|
|
398
|
+
* 3. Scan ConfigurationServiceProvider for GlobalModifierCommand clauses not provided by a StartupTask and not already found in arguments
|
|
383
399
|
* - For each discovered clause:
|
|
384
400
|
* - Parse the arguments for the GlobalModifierCommand
|
|
385
401
|
* - Return on error
|
|
@@ -414,11 +430,12 @@ async function findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs
|
|
|
414
430
|
* @param commandRegistry the {@link CommandRegistry} to use when scanning and parsing.
|
|
415
431
|
* @param serviceProviderRegistry the {@link ServiceProviderRegistry} to use when scanning and parsing.
|
|
416
432
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use for accessing argument defaults.
|
|
433
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
417
434
|
* @param context the {@link Context} in which to execute specified {@link Command} instances.
|
|
418
435
|
* @param defaultCommand optional {@link SubCommand} or {@link GlobalCommand} implementation to attempt to parse arguments for and execute if
|
|
419
436
|
* no non-modifier {@link Command} name is identified in the provided arguments.
|
|
420
437
|
*/
|
|
421
|
-
export async function run(args, commandRegistry, serviceProviderRegistry, configurationServiceProvider, context, defaultCommand) {
|
|
438
|
+
export async function run(args, commandRegistry, serviceProviderRegistry, configurationServiceProvider, keyValueServiceProvider, context, defaultCommand, startupService) {
|
|
422
439
|
if (defaultCommand && !isSubCommand(defaultCommand) && !isGlobalCommand(defaultCommand)) {
|
|
423
440
|
throw new Error("If a default command is provided, if must be a global command or sub-command!");
|
|
424
441
|
}
|
|
@@ -427,15 +444,24 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
427
444
|
// this is to prevent incorrectly parsing arguments as if they were interleaved across multiple commands which is not supported
|
|
428
445
|
let availableArgs = [args];
|
|
429
446
|
let unusedArgs = [];
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
447
|
+
// merge every ServiceProvider's initService() into a StartupTask, then combine with any
|
|
448
|
+
// directly-registered StartupTasks (e.g. the banner task) into one priority-ordered sequence
|
|
449
|
+
const startupTasks = sortByPriority([
|
|
450
|
+
...serviceProviderRegistry
|
|
451
|
+
.getServiceProviders()
|
|
452
|
+
.map((serviceProvider) => toProviderStartupTask(serviceProvider, commandRegistry)),
|
|
453
|
+
...(startupService ? startupService.getTasks() : []),
|
|
454
|
+
]);
|
|
455
|
+
for (const task of startupTasks) {
|
|
456
|
+
// using the GlobalModifierCommands instances provided by the task...
|
|
457
|
+
const globalModifierCommandsByName = new Map((task.modifierCommands ?? []).map((command) => [command.name, command]));
|
|
458
|
+
const globalModifierCommandsByShortAlias = new Map((task.modifierCommands ?? [])
|
|
459
|
+
.filter((command) => Boolean(command.shortAlias))
|
|
460
|
+
.map((command) => [command.shortAlias, command]));
|
|
461
|
+
// if there are GlobalModifierCommands provided by the task...
|
|
436
462
|
if (globalModifierCommandsByName.size > 0) {
|
|
437
463
|
// ...scan arguments, parse any resulting clauses and execute any resulting parsed GlobalModifierCommands
|
|
438
|
-
const runResult = await findAndExecuteGlobalModifierCommands(availableArgs, unusedArgs, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, context);
|
|
464
|
+
const runResult = await findAndExecuteGlobalModifierCommands(availableArgs, unusedArgs, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, keyValueServiceProvider, context);
|
|
439
465
|
// nothing is returned unless a command was found and there was an error, in which case return the error
|
|
440
466
|
if (runResult) {
|
|
441
467
|
return runResult;
|
|
@@ -444,16 +470,19 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
444
470
|
availableArgs = unusedArgs;
|
|
445
471
|
unusedArgs = [];
|
|
446
472
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
473
|
+
logger.debug("Running startup task with ID: %s", task.id);
|
|
474
|
+
// each task gets a Context whose KeyValueService is bound permanently to its own scope (keyed
|
|
475
|
+
// by task.id)
|
|
476
|
+
const taskContext = keyValueServiceProvider
|
|
477
|
+
? keyValueServiceProvider.getContextForScope(context, "service", task.id)
|
|
478
|
+
: context;
|
|
479
|
+
if ((task.mode ?? "blocking") === "blocking") {
|
|
480
|
+
await task.run(taskContext);
|
|
452
481
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
482
|
+
else {
|
|
483
|
+
void task.run(taskContext).catch((error) => {
|
|
484
|
+
logger.debug(() => `Background startup task '${task.id}' failed: ${error}`);
|
|
485
|
+
});
|
|
457
486
|
}
|
|
458
487
|
}
|
|
459
488
|
// using the GlobalModifierCommands instances which are NOT provided by a ServiceProvider...
|
|
@@ -462,7 +491,7 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
462
491
|
// if there are GlobalModifierCommands NOT provided by the ServiceProvider...
|
|
463
492
|
if (globalModifierCommandsByName.size > 0) {
|
|
464
493
|
// ...scan arguments, parse any resulting clauses and execute any resulting parsed GlobalModifierCommands
|
|
465
|
-
const runResult = await findAndExecuteGlobalModifierCommands(availableArgs, unusedArgs, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, context);
|
|
494
|
+
const runResult = await findAndExecuteGlobalModifierCommands(availableArgs, unusedArgs, globalModifierCommandsByName, globalModifierCommandsByShortAlias, configurationServiceProvider, keyValueServiceProvider, context);
|
|
466
495
|
// nothing is returned unless a command was found and there was an error, in which case return the error
|
|
467
496
|
if (runResult) {
|
|
468
497
|
return runResult;
|
|
@@ -477,7 +506,7 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
477
506
|
// and any group/member instance combinations...
|
|
478
507
|
const groupAndMemberCommandByJoinedName = commandRegistry.getGroupAndMemberCommandsByJoinedName();
|
|
479
508
|
// ...scan arguments, parse a resulting clause and execute a resulting parsed non-modifier Command
|
|
480
|
-
let runResult = await findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonModifierCommandsByName, globalCommandsByShortAlias, groupAndMemberCommandByJoinedName, configurationServiceProvider, context);
|
|
509
|
+
let runResult = await findAndExecuteNonModifierCommand(availableArgs, unusedArgs, nonModifierCommandsByName, globalCommandsByShortAlias, groupAndMemberCommandByJoinedName, configurationServiceProvider, keyValueServiceProvider, context);
|
|
481
510
|
// nothing is returned if no command was discovered (otherwise a run result is returned)
|
|
482
511
|
if (runResult) {
|
|
483
512
|
return runResult;
|
|
@@ -488,7 +517,7 @@ export async function run(args, commandRegistry, serviceProviderRegistry, config
|
|
|
488
517
|
// if default non-modifier Command provided...
|
|
489
518
|
if (defaultCommand) {
|
|
490
519
|
// ...assume default command name and parse resulting clauses and execute a resulting parsed non-modifier Command if found
|
|
491
|
-
runResult = await findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs, defaultCommand, configurationServiceProvider, context);
|
|
520
|
+
runResult = await findAndExecuteDefaultNonModifierCommand(availableArgs, unusedArgs, defaultCommand, configurationServiceProvider, keyValueServiceProvider, context);
|
|
492
521
|
// nothing is returned if no command was discovered (otherwise a run result is returned)
|
|
493
522
|
if (runResult) {
|
|
494
523
|
return runResult;
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { ServiceInfo, ServiceProvider } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
-
import type { SingleValueType, Values, PopulatedValues, PopulatedValueType } from "@flowscripter/dynamic-cli-framework-api";
|
|
2
|
+
import type { SingleValueType, Values, ValueNode, PopulatedValues, PopulatedValueType } from "@flowscripter/dynamic-cli-framework-api";
|
|
3
3
|
import type { Context } from "@flowscripter/dynamic-cli-framework-api";
|
|
4
4
|
import type { Command } from "@flowscripter/dynamic-cli-framework-api";
|
|
5
5
|
import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
6
|
+
export type KeyValueServiceScopeType = "command" | "service";
|
|
6
7
|
/**
|
|
7
|
-
* Provides
|
|
8
|
+
* Provides configuration of default command arguments using a configuration file and/or
|
|
9
|
+
* environment variables.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* The same configuration file also backs the raw, per-scope key-value data consumed by
|
|
12
|
+
* {@link KeyValueServiceProvider} (which holds a direct reference to this provider - see
|
|
13
|
+
* {@link getKeyValueData} and {@link flushIfDirty} - rather than looking this provider up via
|
|
14
|
+
* {@link Context}, since only {@link configLocation}/{@link setConfigLocation} are safe to expose
|
|
15
|
+
* generally; {@link getConfigString} is wired directly to {@link DumpConfigCommand} for the same
|
|
16
|
+
* reason - a full config dump would leak every other command's/service's key-value data if it
|
|
17
|
+
* were reachable through a general lookup).
|
|
11
18
|
*
|
|
12
19
|
* **Default Command Arguments**
|
|
13
20
|
*
|
|
@@ -52,7 +59,8 @@ import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
52
59
|
* NOTE: You may store default arguments for secrets by manually configuring secrets in your OS
|
|
53
60
|
* credential store and referencing them in the config file using the sentinel format
|
|
54
61
|
* `__SECRET__:<bun_secret_name>`. These values will be resolved from the OS secret store
|
|
55
|
-
* before being returned as default argument values. See
|
|
62
|
+
* before being returned as default argument values. See {@link KeyValueServiceProvider} for the
|
|
63
|
+
* generic key-value store which uses the same secret sentinel mechanism.
|
|
56
64
|
*
|
|
57
65
|
* *Environment Variables*
|
|
58
66
|
*
|
|
@@ -90,60 +98,6 @@ import type { CLIConfig } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
90
98
|
* * executable: `MyCLI`, command: `command1`, nested sub-argument with both levels being arrays and referring to the 2nd element of each: `arg1[1].arg2[1]`, arg2 configuration key: `BAR` => environment variable: `MYCLI_COMMAND1_ARG1_1_BAR_2`
|
|
91
99
|
*
|
|
92
100
|
* NOTE: Any default values from the above configuration sources will be overridden by any arguments provided on the command line.
|
|
93
|
-
*
|
|
94
|
-
* **Generic Key-Value Service**
|
|
95
|
-
*
|
|
96
|
-
* The same configuration file above is used to provide storage for the provided {@link KeyValueService}. The values
|
|
97
|
-
* are stored under a top level `key-values` property. These are expected to not be modified by the user of the CLI.
|
|
98
|
-
*
|
|
99
|
-
* The second and third level of properties is used to scope the key-values to specific command names
|
|
100
|
-
* (via {@link Command.name} values) and specific service IDs (via {@link ServiceProvider.serviceId} values).
|
|
101
|
-
* Keys are strings, but values are arbitrary JSON (see {@link ValueNode}) - not limited to strings -
|
|
102
|
-
* and may be deep objects or arrays.
|
|
103
|
-
*
|
|
104
|
-
* Any node within a value passed to {@link KeyValueService.set} can be wrapped in {@link Secret} -
|
|
105
|
-
* at any depth - to have that node, and only that node, stored as an OS-native secret. When a node
|
|
106
|
-
* is wrapped in {@link Secret}, its value (of any shape) is JSON-serialized and stored as a single
|
|
107
|
-
* OS-native secret via Bun.secrets, with a sentinel value of the format `__SECRET__:<bun_secret_name>`
|
|
108
|
-
* substituted in its place in the structure that gets stored in the config file. Everything else in
|
|
109
|
-
* the value is stored as plain (unencrypted) config data. The sentinel prefix `__SECRET__:` is
|
|
110
|
-
* reserved and must not be used for regular key-value data.
|
|
111
|
-
*
|
|
112
|
-
* Independently of how a value was written, {@link KeyValueService.get} recursively resolves any
|
|
113
|
-
* string leaf - at any depth within the retrieved value - which starts with the sentinel prefix,
|
|
114
|
-
* via the OS secret store. This means a secret reference may also be hand-embedded (nested
|
|
115
|
-
* arbitrarily deep) directly within a plain, non-secret value in the config file.
|
|
116
|
-
*
|
|
117
|
-
* Secret support requires `secretServiceEnabled=true` in the constructor (which also requires
|
|
118
|
-
* `configEnabled=true`).
|
|
119
|
-
*
|
|
120
|
-
* As an example:
|
|
121
|
-
* ```
|
|
122
|
-
* {
|
|
123
|
-
* "defaults": {
|
|
124
|
-
* ...
|
|
125
|
-
* },
|
|
126
|
-
* "key-values": {
|
|
127
|
-
* "commands": {
|
|
128
|
-
* "command1": {
|
|
129
|
-
* "foo1": "bar1",
|
|
130
|
-
* "foo2": "__SECRET__:command_command1_foo2",
|
|
131
|
-
* "foo3": {
|
|
132
|
-
* "nested": ["a", "__SECRET__:command_command1_foo3_nested"]
|
|
133
|
-
* }
|
|
134
|
-
* },
|
|
135
|
-
* "command2": {
|
|
136
|
-
* "foo1": "bar3"
|
|
137
|
-
* }
|
|
138
|
-
* },
|
|
139
|
-
* "services": {
|
|
140
|
-
* "service-id-1": {
|
|
141
|
-
* "foo1": "bar"
|
|
142
|
-
* }
|
|
143
|
-
* }
|
|
144
|
-
* }
|
|
145
|
-
* }
|
|
146
|
-
* ```
|
|
147
101
|
*/
|
|
148
102
|
export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
149
103
|
#private;
|
|
@@ -151,7 +105,6 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
151
105
|
readonly servicePriority: number;
|
|
152
106
|
readonly envVarsEnabled: boolean;
|
|
153
107
|
readonly configEnabled: boolean;
|
|
154
|
-
readonly keyValueServiceEnabled: boolean;
|
|
155
108
|
readonly secretServiceEnabled: boolean;
|
|
156
109
|
configLocation: string | undefined;
|
|
157
110
|
defaultsData: Map<string, Values | SingleValueType>;
|
|
@@ -161,10 +114,10 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
161
114
|
* @param servicePriority the priority of the service.
|
|
162
115
|
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
163
116
|
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
164
|
-
* @param
|
|
165
|
-
*
|
|
117
|
+
* @param secretServiceEnabled optionally enable OS-native secret storage via Bun.secrets for resolving
|
|
118
|
+
* secrets embedded in default argument values: `configEnabled` must be true in this case
|
|
166
119
|
*/
|
|
167
|
-
constructor(servicePriority: number, envVarsEnabled?: boolean, configEnabled?: boolean,
|
|
120
|
+
constructor(servicePriority: number, envVarsEnabled?: boolean, configEnabled?: boolean, secretServiceEnabled?: boolean);
|
|
168
121
|
getServiceInfo(cliConfig: CLIConfig): Promise<ServiceInfo>;
|
|
169
122
|
setConfigLocation(configLocation: string): void;
|
|
170
123
|
/**
|
|
@@ -177,30 +130,21 @@ export default class ConfigurationServiceProvider implements ServiceProvider {
|
|
|
177
130
|
*/
|
|
178
131
|
getDefaultArgumentValues(cliConfig: CLIConfig, command: Command): Promise<PopulatedValues | PopulatedValueType | undefined>;
|
|
179
132
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* @param commandName the name of the command to scope the key-value data to.
|
|
184
|
-
*
|
|
185
|
-
* @throws {Error} if an existing scope is set.
|
|
186
|
-
*/
|
|
187
|
-
setCommandKeyValueScope(commandName: string): void;
|
|
188
|
-
/**
|
|
189
|
-
* Indicate that the contained {@link DefaultKeyValueService} should be set to have a scope of the provided
|
|
190
|
-
* service ID.
|
|
191
|
-
*
|
|
192
|
-
* @param serviceId the ID of the service to scope the key-value data to.
|
|
133
|
+
* Get-or-create the underlying, raw {@link ValueNode} `Map` for the given scope - for
|
|
134
|
+
* {@link KeyValueServiceProvider}'s exclusive use, via its direct reference to this provider.
|
|
193
135
|
*
|
|
194
|
-
* @
|
|
136
|
+
* @param scopeType whether `scopeKey` is a command name or a service/task ID.
|
|
137
|
+
* @param scopeKey the command name or service/task ID to scope the key-value data to.
|
|
195
138
|
*/
|
|
196
|
-
|
|
139
|
+
getKeyValueData(scopeType: KeyValueServiceScopeType, scopeKey: string): Map<string, ValueNode>;
|
|
197
140
|
/**
|
|
198
|
-
*
|
|
141
|
+
* Write the configuration file, if `isDirty` is true - for {@link KeyValueServiceProvider}'s
|
|
142
|
+
* exclusive use, via its direct reference to this provider, called from its shutdown-time
|
|
143
|
+
* flush task.
|
|
199
144
|
*
|
|
200
|
-
*
|
|
201
|
-
* to the current configuration location.
|
|
145
|
+
* @param isDirty whether any scoped KeyValueService has unwritten changes.
|
|
202
146
|
*/
|
|
203
|
-
|
|
147
|
+
flushIfDirty(isDirty: boolean): Promise<void>;
|
|
204
148
|
initService(context: Context): Promise<void>;
|
|
205
149
|
getConfigString(): string;
|
|
206
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigurationServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/ConfigurationServiceProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE5F,OAAO,KAAK,EACV,eAAe,EACf,MAAM,
|
|
1
|
+
{"version":3,"file":"ConfigurationServiceProvider.d.ts","sourceRoot":"","sources":["../../../../src/service/configuration/ConfigurationServiceProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE5F,OAAO,KAAK,EACV,eAAe,EACf,MAAM,EACN,SAAS,EACT,eAAe,EACf,kBAAkB,EACnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AASvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAOvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,CAAC;AAkB7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AACH,MAAM,CAAC,OAAO,OAAO,4BAA6B,YAAW,eAAe;;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAA4B;IACtD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC,SAAgB,cAAc,EAAE,OAAO,CAAC;IACxC,SAAgB,aAAa,EAAE,OAAO,CAAC;IACvC,SAAgB,oBAAoB,EAAE,OAAO,CAAC;IAGvC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAKnC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAAC,CAAa;IAqBvE;;;;;;;;OAQG;IACH,YACE,eAAe,EAAE,MAAM,EACvB,cAAc,UAAQ,EACtB,aAAa,UAAQ,EACrB,oBAAoB,UAAQ,EAW7B;IAEM,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAehE;IAEM,iBAAiB,CAAC,cAAc,EAAE,MAAM,QAE9C;IAED;;;;;;;OAOG;IACU,wBAAwB,CACnC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,eAAe,GAAG,kBAAkB,GAAG,SAAS,CAAC,CAoD3D;IAED;;;;;;OAMG;IACI,eAAe,CACpB,SAAS,EAAE,wBAAwB,EACnC,QAAQ,EAAE,MAAM,GACf,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAOxB;IAED;;;;;;OAMG;IACU,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzD;IAEY,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CxD;IA4DM,eAAe,IAAI,MAAM,CAgC/B;CACF"}
|