@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowscripter/dynamic-cli-framework",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "A framework for developing CLI applications which supports dynamic discovery and installation of new commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"build": "tsc -p tsconfig.build.json"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@flowscripter/dynamic-cli-framework-api": "^
|
|
51
|
+
"@flowscripter/dynamic-cli-framework-api": "^4.0.1",
|
|
52
52
|
"@flowscripter/dynamic-plugin-framework": "^2.2.9",
|
|
53
53
|
"emphasize": "^7.0.0",
|
|
54
54
|
"fastest-levenshtein": "^1.0.16",
|
package/src/cli/BaseCLI.ts
CHANGED
|
@@ -28,8 +28,11 @@ import {
|
|
|
28
28
|
} from "../runtime/command/CommandTypeGuards.ts";
|
|
29
29
|
import CommandValidator from "../runtime/command/CommandValidator.ts";
|
|
30
30
|
import ShutdownServiceProvider from "../service/shutdown/ShutdownServiceProvider.ts";
|
|
31
|
+
import StartupServiceProvider from "../service/startup/StartupServiceProvider.ts";
|
|
32
|
+
import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
|
|
31
33
|
import { shutdownState } from "../service/shutdown/ShutdownState.ts";
|
|
32
34
|
import ConfigurationServiceProvider from "../service/configuration/ConfigurationServiceProvider.ts";
|
|
35
|
+
import KeyValueServiceProvider from "../service/configuration/KeyValueServiceProvider.ts";
|
|
33
36
|
import PrinterServiceProvider from "../service/printer/PrinterServiceProvider.ts";
|
|
34
37
|
import TableGeneratorServiceProvider from "../service/tableGenerator/TableGeneratorServiceProvider.ts";
|
|
35
38
|
import { run } from "../runtime/runner.ts";
|
|
@@ -53,7 +56,9 @@ import CompletionServiceProvider from "../service/completion/CompletionServicePr
|
|
|
53
56
|
import ImagePrinterServiceProvider from "../service/imagePrinter/ImagePrinterServiceProvider.ts";
|
|
54
57
|
import SpawnServiceProvider from "../service/spawn/SpawnServiceProvider.ts";
|
|
55
58
|
import FetchServiceProvider from "../service/fetch/FetchServiceProvider.ts";
|
|
56
|
-
import UpgradeServiceProvider
|
|
59
|
+
import UpgradeServiceProvider, {
|
|
60
|
+
createUpgradeCheckStartupTask,
|
|
61
|
+
} from "../service/upgrade/UpgradeServiceProvider.ts";
|
|
57
62
|
import PluginServiceProvider from "../service/plugin/PluginServiceProvider.ts";
|
|
58
63
|
const logger = getLogger("BaseCLI");
|
|
59
64
|
|
|
@@ -103,6 +108,7 @@ export default class BaseCLI implements CLI {
|
|
|
103
108
|
readonly #context: DefaultContext;
|
|
104
109
|
readonly #printerService: PrinterService;
|
|
105
110
|
readonly #stdoutTerminal: Terminal;
|
|
111
|
+
readonly #startupServiceProvider: StartupServiceProvider;
|
|
106
112
|
|
|
107
113
|
constructor(
|
|
108
114
|
cliConfig: CLIConfig,
|
|
@@ -168,6 +174,10 @@ export default class BaseCLI implements CLI {
|
|
|
168
174
|
// create a context
|
|
169
175
|
this.#context = new DefaultContext(this.#cliConfig);
|
|
170
176
|
|
|
177
|
+
// priority 95 sits between shutdown (100) and configuration (90) - it has no ordering
|
|
178
|
+
// dependency on anything since its own initService() is a no-op.
|
|
179
|
+
this.#startupServiceProvider = new StartupServiceProvider(95);
|
|
180
|
+
|
|
171
181
|
this.#stdoutTerminal = stdoutTerminal;
|
|
172
182
|
this.#printerService = new DefaultPrinterService(
|
|
173
183
|
stdoutWritableStream,
|
|
@@ -201,6 +211,16 @@ export default class BaseCLI implements CLI {
|
|
|
201
211
|
*
|
|
202
212
|
* @param command the {@link Command} to add.
|
|
203
213
|
*/
|
|
214
|
+
/**
|
|
215
|
+
* Register a {@link StartupTask} to run once during CLI startup, in priority order alongside
|
|
216
|
+
* every registered {@link ServiceProvider}'s `initService()` call.
|
|
217
|
+
*
|
|
218
|
+
* @param task the {@link StartupTask} to register.
|
|
219
|
+
*/
|
|
220
|
+
public addStartupTask(task: StartupTask) {
|
|
221
|
+
this.#startupServiceProvider.startupService.registerTask(task);
|
|
222
|
+
}
|
|
223
|
+
|
|
204
224
|
public addCommand(command: Command) {
|
|
205
225
|
if (!isGlobalModifierCommand(command)) {
|
|
206
226
|
// store the command locally to help determine if this CLI will be a single or multi-command CLI
|
|
@@ -229,6 +249,7 @@ export default class BaseCLI implements CLI {
|
|
|
229
249
|
|
|
230
250
|
// create and add core services
|
|
231
251
|
this.addServiceProvider(new ShutdownServiceProvider(100));
|
|
252
|
+
this.addServiceProvider(this.#startupServiceProvider);
|
|
232
253
|
this.addServiceProvider(new PrinterServiceProvider(80, this.#printerService));
|
|
233
254
|
this.addServiceProvider(new TableGeneratorServiceProvider(70));
|
|
234
255
|
|
|
@@ -293,10 +314,12 @@ export default class BaseCLI implements CLI {
|
|
|
293
314
|
);
|
|
294
315
|
}
|
|
295
316
|
|
|
317
|
+
let upgradeServiceProvider: UpgradeServiceProvider | undefined;
|
|
296
318
|
if (this.#options.upgradeServiceEnabled) {
|
|
297
319
|
// 56 runs after Spawn(58)/Fetch(57) - whose dependencies it needs
|
|
298
320
|
// but before the consumer-configured Banner/Plugin(50) priority band.
|
|
299
|
-
|
|
321
|
+
upgradeServiceProvider = new UpgradeServiceProvider(56, this.#options.upgradeLocationsConfig);
|
|
322
|
+
this.addServiceProvider(upgradeServiceProvider);
|
|
300
323
|
}
|
|
301
324
|
|
|
302
325
|
if (this.#options.pluginServiceEnabled) {
|
|
@@ -315,10 +338,18 @@ export default class BaseCLI implements CLI {
|
|
|
315
338
|
90,
|
|
316
339
|
this.#options.envVarsSupportEnabled,
|
|
317
340
|
this.#options.configFileSupportEnabled,
|
|
318
|
-
this.#options.keyValueServiceEnabled,
|
|
319
341
|
this.#options.secretServiceEnabled,
|
|
320
342
|
);
|
|
321
343
|
this.addServiceProvider(configurationServiceProvider);
|
|
344
|
+
// priority 89 sits just below ConfigurationServiceProvider's 90 - its config-file read
|
|
345
|
+
// happens in its own initService(), before KeyValueServiceProvider's initService() runs.
|
|
346
|
+
const keyValueServiceProvider = new KeyValueServiceProvider(
|
|
347
|
+
89,
|
|
348
|
+
configurationServiceProvider,
|
|
349
|
+
this.#options.keyValueServiceEnabled,
|
|
350
|
+
this.#options.secretServiceEnabled,
|
|
351
|
+
);
|
|
352
|
+
this.addServiceProvider(keyValueServiceProvider);
|
|
322
353
|
|
|
323
354
|
for (const serviceProvider of this.#serviceProviderRegistry.getServiceProviders()) {
|
|
324
355
|
const serviceInfo = await serviceProvider.getServiceInfo(this.#cliConfig);
|
|
@@ -332,6 +363,19 @@ export default class BaseCLI implements CLI {
|
|
|
332
363
|
});
|
|
333
364
|
}
|
|
334
365
|
|
|
366
|
+
if (upgradeServiceProvider?.upgradeService) {
|
|
367
|
+
this.addStartupTask(createUpgradeCheckStartupTask(upgradeServiceProvider.upgradeService, 56));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// directly-registered StartupTasks (e.g. the banner task) aren't backed by a ServiceProvider,
|
|
371
|
+
// so their modifierCommands need to be added to the command registry here instead, keyed by
|
|
372
|
+
// the task's own ID.
|
|
373
|
+
for (const task of this.#startupServiceProvider.startupService.getTasks()) {
|
|
374
|
+
(task.modifierCommands ?? []).forEach((command) => {
|
|
375
|
+
this.#commandRegistry.addCommand(command, task.id);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
335
379
|
logger.debug(() => `Running with args: ${args.join(" ")}`);
|
|
336
380
|
|
|
337
381
|
let helpSubCommand: SubCommand | undefined;
|
|
@@ -381,8 +425,10 @@ export default class BaseCLI implements CLI {
|
|
|
381
425
|
this.#commandRegistry,
|
|
382
426
|
this.#serviceProviderRegistry,
|
|
383
427
|
configurationServiceProvider,
|
|
428
|
+
keyValueServiceProvider,
|
|
384
429
|
this.#context,
|
|
385
430
|
defaultCommand,
|
|
431
|
+
this.#startupServiceProvider.startupService,
|
|
386
432
|
);
|
|
387
433
|
// then handle the result...
|
|
388
434
|
if (runResult.runState === RunState.NO_COMMAND) {
|
package/src/launcher.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
Command,
|
|
7
7
|
RunResult,
|
|
8
8
|
ServiceProvider,
|
|
9
|
+
StartupTask,
|
|
9
10
|
} from "@flowscripter/dynamic-cli-framework-api";
|
|
10
11
|
import type BaseCLIFeatureOptions from "./cli/BaseCLIFeatureOptions.ts";
|
|
11
12
|
import DefaultRuntimeCLI from "./cli/DefaultRuntimeCLI.ts";
|
|
@@ -17,6 +18,7 @@ export async function launchSingleCommandCLI(
|
|
|
17
18
|
version?: string,
|
|
18
19
|
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
19
20
|
options?: BaseCLIFeatureOptions,
|
|
21
|
+
startupTasks?: ReadonlyArray<StartupTask>,
|
|
20
22
|
): Promise<RunResult> {
|
|
21
23
|
if (!name) {
|
|
22
24
|
name = path.basename(process.execPath);
|
|
@@ -37,7 +39,8 @@ export async function launchSingleCommandCLI(
|
|
|
37
39
|
|
|
38
40
|
cli.addCommand(command);
|
|
39
41
|
|
|
40
|
-
serviceProviders?.forEach((
|
|
42
|
+
serviceProviders?.forEach((serviceProvider) => cli.addServiceProvider(serviceProvider));
|
|
43
|
+
startupTasks?.forEach((startupTask) => cli.addStartupTask(startupTask));
|
|
41
44
|
|
|
42
45
|
return await cli.run();
|
|
43
46
|
}
|
|
@@ -49,6 +52,7 @@ export async function launchMultiCommandCLI(
|
|
|
49
52
|
version?: string,
|
|
50
53
|
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
51
54
|
options?: BaseCLIFeatureOptions,
|
|
55
|
+
startupTasks?: ReadonlyArray<StartupTask>,
|
|
52
56
|
): Promise<RunResult> {
|
|
53
57
|
if (!name) {
|
|
54
58
|
name = path.basename(process.execPath);
|
|
@@ -69,7 +73,8 @@ export async function launchMultiCommandCLI(
|
|
|
69
73
|
|
|
70
74
|
commands.forEach((command) => cli.addCommand(command));
|
|
71
75
|
|
|
72
|
-
serviceProviders?.forEach((
|
|
76
|
+
serviceProviders?.forEach((serviceProvider) => cli.addServiceProvider(serviceProvider));
|
|
77
|
+
startupTasks?.forEach((startupTask) => cli.addStartupTask(startupTask));
|
|
73
78
|
|
|
74
79
|
return await cli.run();
|
|
75
80
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common shape shared by {@link StartupTask} and {@link ShutdownTask}: a task with an optional
|
|
3
|
+
* priority, used to order execution. Higher values run earlier. Undefined is treated as `0`.
|
|
4
|
+
*/
|
|
5
|
+
export interface PriorityTask {
|
|
6
|
+
readonly priority?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Stably sort tasks by descending priority (default `0`). Tasks with the same priority keep
|
|
11
|
+
* their relative order in the input array.
|
|
12
|
+
*/
|
|
13
|
+
export function sortByPriority<T extends PriorityTask>(tasks: ReadonlyArray<T>): Array<T> {
|
|
14
|
+
return tasks
|
|
15
|
+
.map((task, index) => ({ task, index }))
|
|
16
|
+
.sort((a, b) => (b.task.priority ?? 0) - (a.task.priority ?? 0) || a.index - b.index)
|
|
17
|
+
.map((entry) => entry.task);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Internal helper shared by {@link DefaultStartupService} and {@link DefaultShutdownService}:
|
|
22
|
+
* accumulates registered tasks and returns them in priority order on demand.
|
|
23
|
+
*/
|
|
24
|
+
export default class PriorityTaskList<T extends PriorityTask> {
|
|
25
|
+
readonly #tasks: Array<T> = [];
|
|
26
|
+
|
|
27
|
+
public add(task: T): void {
|
|
28
|
+
this.#tasks.push(task);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public sorted(): ReadonlyArray<T> {
|
|
32
|
+
return sortByPriority(this.#tasks);
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/runtime/runner.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { scanForGlobalModifierCommandClauses, scanForNonModifierCommandClause }
|
|
|
21
21
|
import type { ServiceProviderRegistry } from "@flowscripter/dynamic-cli-framework-api";
|
|
22
22
|
import { parseGlobalCommandClause, type ParseResult, parseSubCommandClause } from "./parser.ts";
|
|
23
23
|
import type ConfigurationServiceProvider from "../service/configuration/ConfigurationServiceProvider.ts";
|
|
24
|
+
import type KeyValueServiceProvider from "../service/configuration/KeyValueServiceProvider.ts";
|
|
24
25
|
import type { GlobalModifierCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
25
26
|
import type { GroupCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
26
27
|
import type { SubCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
@@ -28,9 +29,35 @@ import type { GlobalCommand } from "@flowscripter/dynamic-cli-framework-api";
|
|
|
28
29
|
import type { Command } from "@flowscripter/dynamic-cli-framework-api";
|
|
29
30
|
import type { ArgumentPrompterService } from "@flowscripter/dynamic-cli-framework-api";
|
|
30
31
|
import { ARGUMENT_PROMPTER_SERVICE_ID } from "@flowscripter/dynamic-cli-framework-api";
|
|
32
|
+
import type { StartupTask } from "@flowscripter/dynamic-cli-framework-api";
|
|
33
|
+
import type { ServiceProvider } from "@flowscripter/dynamic-cli-framework-api";
|
|
34
|
+
import DefaultStartupService from "../service/startup/DefaultStartupService.ts";
|
|
35
|
+
import { sortByPriority } from "./lifecycle/PriorityTaskList.ts";
|
|
31
36
|
|
|
32
37
|
const logger = getLogger("runner");
|
|
33
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Wrap a {@link ServiceProvider}'s `initService()` call as a {@link StartupTask} so it can be
|
|
41
|
+
* merged with directly-registered {@link StartupTask}s (e.g. the banner task) into a single
|
|
42
|
+
* priority-ordered sequence.
|
|
43
|
+
*/
|
|
44
|
+
function toProviderStartupTask(
|
|
45
|
+
serviceProvider: ServiceProvider,
|
|
46
|
+
commandRegistry: CommandRegistry,
|
|
47
|
+
): StartupTask {
|
|
48
|
+
return {
|
|
49
|
+
id: serviceProvider.serviceId,
|
|
50
|
+
priority: serviceProvider.servicePriority,
|
|
51
|
+
mode: "blocking",
|
|
52
|
+
modifierCommands: Array.from(
|
|
53
|
+
commandRegistry
|
|
54
|
+
.getGlobalModifierCommandsByNameProvidedByService(serviceProvider.serviceId)
|
|
55
|
+
.values(),
|
|
56
|
+
),
|
|
57
|
+
run: (context: Context) => serviceProvider.initService(context),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
34
61
|
async function attemptPromptForMissingArguments(
|
|
35
62
|
parseResult: ParseResult,
|
|
36
63
|
context: Context,
|
|
@@ -58,31 +85,26 @@ async function attemptPromptForMissingArguments(
|
|
|
58
85
|
*
|
|
59
86
|
* @param parseResult the {@link ParseResult} to execute.
|
|
60
87
|
* @param context the {@link Context} to use.
|
|
61
|
-
* @param
|
|
88
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
62
89
|
* @param isDefaultCommand whether the command is the default command for the CLI.
|
|
63
90
|
*/
|
|
64
91
|
async function executeParsedCommand(
|
|
65
92
|
parseResult: ParseResult,
|
|
66
93
|
context: Context,
|
|
67
|
-
|
|
94
|
+
keyValueServiceProvider: KeyValueServiceProvider | undefined,
|
|
68
95
|
isDefaultCommand = false,
|
|
69
96
|
): Promise<RunResult> {
|
|
70
97
|
try {
|
|
71
98
|
if (parseResult.groupCommand !== undefined) {
|
|
72
99
|
logger.debug("Executing group command with name: %s", parseResult!.groupCommand!.name);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
82
|
-
configurationServiceProvider?.secretServiceEnabled
|
|
83
|
-
) {
|
|
84
|
-
await configurationServiceProvider.clearKeyValueScope();
|
|
85
|
-
}
|
|
100
|
+
const groupContext = keyValueServiceProvider
|
|
101
|
+
? keyValueServiceProvider.getContextForScope(
|
|
102
|
+
context,
|
|
103
|
+
"command",
|
|
104
|
+
parseResult!.groupCommand!.name,
|
|
105
|
+
)
|
|
106
|
+
: context;
|
|
107
|
+
await parseResult.groupCommand.execute(groupContext);
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
logger.debug(
|
|
@@ -91,26 +113,20 @@ async function executeParsedCommand(
|
|
|
91
113
|
parseResult!.populatedArgumentValues,
|
|
92
114
|
);
|
|
93
115
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
) {
|
|
98
|
-
configurationServiceProvider.setCommandKeyValueScope(parseResult.command.name);
|
|
99
|
-
}
|
|
116
|
+
const commandContext = keyValueServiceProvider
|
|
117
|
+
? keyValueServiceProvider.getContextForScope(context, "command", parseResult.command.name)
|
|
118
|
+
: context;
|
|
100
119
|
if (isSubCommand(parseResult.command)) {
|
|
101
|
-
await parseResult.command.execute(
|
|
120
|
+
await parseResult.command.execute(
|
|
121
|
+
commandContext,
|
|
122
|
+
parseResult.populatedArgumentValues as Values,
|
|
123
|
+
);
|
|
102
124
|
} else {
|
|
103
125
|
await (parseResult.command as GlobalCommand).execute(
|
|
104
|
-
|
|
126
|
+
commandContext,
|
|
105
127
|
parseResult.populatedArgumentValues as SingleValueType,
|
|
106
128
|
);
|
|
107
129
|
}
|
|
108
|
-
if (
|
|
109
|
-
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
110
|
-
configurationServiceProvider?.secretServiceEnabled
|
|
111
|
-
) {
|
|
112
|
-
await configurationServiceProvider.clearKeyValueScope();
|
|
113
|
-
}
|
|
114
130
|
} catch (err) {
|
|
115
131
|
if ((err as Error).message === "Interrupted") {
|
|
116
132
|
throw err;
|
|
@@ -136,6 +152,7 @@ async function executeParsedCommand(
|
|
|
136
152
|
* @param globalModifierCommandsByName a map of non-modifier {@link Command} instances by name to parse for.
|
|
137
153
|
* @param globalModifierCommandsByShortAlias a map of {@link GlobalModifierCommand} instances by short alias to use when scanning.
|
|
138
154
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
155
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
139
156
|
* @param context the {@link Context} to use.
|
|
140
157
|
*/
|
|
141
158
|
async function findAndExecuteGlobalModifierCommands(
|
|
@@ -144,6 +161,7 @@ async function findAndExecuteGlobalModifierCommands(
|
|
|
144
161
|
globalModifierCommandsByName: ReadonlyMap<string, GlobalModifierCommand>,
|
|
145
162
|
globalModifierCommandsByShortAlias: ReadonlyMap<string, GlobalModifierCommand>,
|
|
146
163
|
configurationServiceProvider: ConfigurationServiceProvider | undefined,
|
|
164
|
+
keyValueServiceProvider: KeyValueServiceProvider | undefined,
|
|
147
165
|
context: Context,
|
|
148
166
|
): Promise<RunResult | undefined> {
|
|
149
167
|
// build a list of GlobalModifierCommands to execute
|
|
@@ -241,11 +259,7 @@ async function findAndExecuteGlobalModifierCommands(
|
|
|
241
259
|
);
|
|
242
260
|
for (const parseResult of globalModifierCommandParseResults) {
|
|
243
261
|
// execute and fast fail on error
|
|
244
|
-
const runResult = await executeParsedCommand(
|
|
245
|
-
parseResult,
|
|
246
|
-
context,
|
|
247
|
-
configurationServiceProvider,
|
|
248
|
-
);
|
|
262
|
+
const runResult = await executeParsedCommand(parseResult, context, keyValueServiceProvider);
|
|
249
263
|
if (runResult.runState !== RunState.SUCCESS) {
|
|
250
264
|
return runResult;
|
|
251
265
|
}
|
|
@@ -261,6 +275,7 @@ async function findAndExecuteGlobalModifierCommands(
|
|
|
261
275
|
* @param globalCommandsByShortAlias the map of {@link GlobalCommand} instances by short alias to use when scanning.
|
|
262
276
|
* @param groupAndMemberCommandsByJoinedName optional map of {@link GroupCommand} and member {@link SubCommand} instances to use when scanning.
|
|
263
277
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
278
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
264
279
|
* @param context the {@link Context} to use.
|
|
265
280
|
*/
|
|
266
281
|
async function findAndExecuteNonModifierCommand(
|
|
@@ -272,6 +287,7 @@ async function findAndExecuteNonModifierCommand(
|
|
|
272
287
|
| ReadonlyMap<string, { groupCommand: GroupCommand; command: SubCommand }>
|
|
273
288
|
| undefined,
|
|
274
289
|
configurationServiceProvider: ConfigurationServiceProvider | undefined,
|
|
290
|
+
keyValueServiceProvider: KeyValueServiceProvider | undefined,
|
|
275
291
|
context: Context,
|
|
276
292
|
): Promise<RunResult | undefined> {
|
|
277
293
|
let parseResult: ParseResult | undefined;
|
|
@@ -383,7 +399,7 @@ async function findAndExecuteNonModifierCommand(
|
|
|
383
399
|
await printUnusedArgsWarning(context, unusedArgs.flat());
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
return executeParsedCommand(parseResult, context,
|
|
402
|
+
return executeParsedCommand(parseResult, context, keyValueServiceProvider);
|
|
387
403
|
}
|
|
388
404
|
|
|
389
405
|
/**
|
|
@@ -393,6 +409,7 @@ async function findAndExecuteNonModifierCommand(
|
|
|
393
409
|
* @param unusedArgs an array to which any unused argument sequences will be added.
|
|
394
410
|
* @param defaultNonModifierCommand the default non-modifier {@link Command} to use.
|
|
395
411
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use to get default argument values.
|
|
412
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
396
413
|
* @param context the {@link Context} to use.
|
|
397
414
|
*/
|
|
398
415
|
async function findAndExecuteDefaultNonModifierCommand(
|
|
@@ -400,6 +417,7 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
400
417
|
unusedArgs: Array<ReadonlyArray<string>>,
|
|
401
418
|
defaultNonModifierCommand: Command,
|
|
402
419
|
configurationServiceProvider: ConfigurationServiceProvider | undefined,
|
|
420
|
+
keyValueServiceProvider: KeyValueServiceProvider | undefined,
|
|
403
421
|
context: Context,
|
|
404
422
|
): Promise<RunResult | undefined> {
|
|
405
423
|
const defaultArgumentValues = configurationServiceProvider
|
|
@@ -511,7 +529,7 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
511
529
|
await printUnusedArgsWarning(context, unusedArgs.flat());
|
|
512
530
|
}
|
|
513
531
|
|
|
514
|
-
return executeParsedCommand(parseResult, context,
|
|
532
|
+
return executeParsedCommand(parseResult, context, keyValueServiceProvider, true);
|
|
515
533
|
}
|
|
516
534
|
|
|
517
535
|
/**
|
|
@@ -520,14 +538,15 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
520
538
|
*
|
|
521
539
|
* An overview of the logic (assuming the optional ConfigurationServiceProvider is provided):
|
|
522
540
|
*
|
|
523
|
-
* 1. For each
|
|
524
|
-
*
|
|
541
|
+
* 1. For each StartupTask in priority order (whether backed by a ServiceProvider's
|
|
542
|
+
* initService() or registered directly, e.g. the banner task):
|
|
543
|
+
* - Scan arguments for GlobalModifierCommand clauses provided by the task
|
|
525
544
|
* - For each discovered clause:
|
|
526
545
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
527
546
|
* - Parse the arguments for the GlobalModifierCommand
|
|
528
547
|
* - Return on error
|
|
529
548
|
* - Add to list of GlobalModifierCommands to execute
|
|
530
|
-
* - Scan ConfigurationServiceProvider for GlobalModifierCommand clauses provided by the
|
|
549
|
+
* - Scan ConfigurationServiceProvider for GlobalModifierCommand clauses provided by the task not already found in arguments
|
|
531
550
|
* - For each configured clause:
|
|
532
551
|
* - Parse the arguments for the GlobalModifierCommand
|
|
533
552
|
* - Return on error
|
|
@@ -536,14 +555,18 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
536
555
|
* - For each GlobalModifierCommand clause:
|
|
537
556
|
* - Execute the GlobalModifierCommand
|
|
538
557
|
* - Return on error
|
|
539
|
-
* -
|
|
540
|
-
*
|
|
558
|
+
* - Give the task a Context whose KeyValueService is bound permanently to its own scope
|
|
559
|
+
* (keyed by task.id), via KeyValueServiceProvider if provided.
|
|
560
|
+
* - Run the task: awaited before continuing if its mode is "blocking" (the default, and how
|
|
561
|
+
* every ServiceProvider's initService() behaves); fired without awaiting if "background"
|
|
562
|
+
* (errors logged, not propagated).
|
|
563
|
+
* 2. Scan arguments for any GlobalModifierCommand clauses not provided by a StartupTask
|
|
541
564
|
* - For each discovered clause:
|
|
542
565
|
* - Set any argument defaults from ConfigurationServiceProvider
|
|
543
566
|
* - Parse the arguments for the GlobalModifierCommand
|
|
544
567
|
* - Return on error
|
|
545
568
|
* - Add to list of GlobalModifierCommands to execute
|
|
546
|
-
* 3. Scan ConfigurationServiceProvider for GlobalModifierCommand clauses not provided by
|
|
569
|
+
* 3. Scan ConfigurationServiceProvider for GlobalModifierCommand clauses not provided by a StartupTask and not already found in arguments
|
|
547
570
|
* - For each discovered clause:
|
|
548
571
|
* - Parse the arguments for the GlobalModifierCommand
|
|
549
572
|
* - Return on error
|
|
@@ -578,6 +601,7 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
578
601
|
* @param commandRegistry the {@link CommandRegistry} to use when scanning and parsing.
|
|
579
602
|
* @param serviceProviderRegistry the {@link ServiceProviderRegistry} to use when scanning and parsing.
|
|
580
603
|
* @param configurationServiceProvider optional {@link ConfigurationServiceProvider} to use for accessing argument defaults.
|
|
604
|
+
* @param keyValueServiceProvider optional {@link KeyValueServiceProvider} to use to get a scope-decorated {@link Context}.
|
|
581
605
|
* @param context the {@link Context} in which to execute specified {@link Command} instances.
|
|
582
606
|
* @param defaultCommand optional {@link SubCommand} or {@link GlobalCommand} implementation to attempt to parse arguments for and execute if
|
|
583
607
|
* no non-modifier {@link Command} name is identified in the provided arguments.
|
|
@@ -587,8 +611,10 @@ export async function run(
|
|
|
587
611
|
commandRegistry: CommandRegistry,
|
|
588
612
|
serviceProviderRegistry: ServiceProviderRegistry,
|
|
589
613
|
configurationServiceProvider: ConfigurationServiceProvider | undefined,
|
|
614
|
+
keyValueServiceProvider: KeyValueServiceProvider | undefined,
|
|
590
615
|
context: Context,
|
|
591
616
|
defaultCommand?: Command,
|
|
617
|
+
startupService?: DefaultStartupService,
|
|
592
618
|
): Promise<RunResult> {
|
|
593
619
|
if (defaultCommand && !isSubCommand(defaultCommand) && !isGlobalCommand(defaultCommand)) {
|
|
594
620
|
throw new Error(
|
|
@@ -601,17 +627,29 @@ export async function run(
|
|
|
601
627
|
let availableArgs: ReadonlyArray<ReadonlyArray<string>> = [args];
|
|
602
628
|
let unusedArgs: Array<ReadonlyArray<string>> = [];
|
|
603
629
|
|
|
604
|
-
//
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
630
|
+
// merge every ServiceProvider's initService() into a StartupTask, then combine with any
|
|
631
|
+
// directly-registered StartupTasks (e.g. the banner task) into one priority-ordered sequence
|
|
632
|
+
const startupTasks = sortByPriority<StartupTask>([
|
|
633
|
+
...serviceProviderRegistry
|
|
634
|
+
.getServiceProviders()
|
|
635
|
+
.map((serviceProvider) => toProviderStartupTask(serviceProvider, commandRegistry)),
|
|
636
|
+
...(startupService ? startupService.getTasks() : []),
|
|
637
|
+
]);
|
|
638
|
+
|
|
639
|
+
for (const task of startupTasks) {
|
|
640
|
+
// using the GlobalModifierCommands instances provided by the task...
|
|
641
|
+
const globalModifierCommandsByName = new Map(
|
|
642
|
+
(task.modifierCommands ?? []).map((command) => [command.name, command]),
|
|
643
|
+
);
|
|
644
|
+
const globalModifierCommandsByShortAlias = new Map(
|
|
645
|
+
(task.modifierCommands ?? [])
|
|
646
|
+
.filter((command): command is GlobalModifierCommand & { shortAlias: string } =>
|
|
647
|
+
Boolean(command.shortAlias),
|
|
648
|
+
)
|
|
649
|
+
.map((command) => [command.shortAlias, command]),
|
|
650
|
+
);
|
|
613
651
|
|
|
614
|
-
// if there are GlobalModifierCommands provided by the
|
|
652
|
+
// if there are GlobalModifierCommands provided by the task...
|
|
615
653
|
if (globalModifierCommandsByName.size > 0) {
|
|
616
654
|
// ...scan arguments, parse any resulting clauses and execute any resulting parsed GlobalModifierCommands
|
|
617
655
|
const runResult = await findAndExecuteGlobalModifierCommands(
|
|
@@ -620,6 +658,7 @@ export async function run(
|
|
|
620
658
|
globalModifierCommandsByName,
|
|
621
659
|
globalModifierCommandsByShortAlias,
|
|
622
660
|
configurationServiceProvider,
|
|
661
|
+
keyValueServiceProvider,
|
|
623
662
|
context,
|
|
624
663
|
);
|
|
625
664
|
|
|
@@ -633,23 +672,20 @@ export async function run(
|
|
|
633
672
|
unusedArgs = [];
|
|
634
673
|
}
|
|
635
674
|
|
|
636
|
-
|
|
675
|
+
logger.debug("Running startup task with ID: %s", task.id);
|
|
637
676
|
|
|
638
|
-
|
|
677
|
+
// each task gets a Context whose KeyValueService is bound permanently to its own scope (keyed
|
|
678
|
+
// by task.id)
|
|
679
|
+
const taskContext = keyValueServiceProvider
|
|
680
|
+
? keyValueServiceProvider.getContextForScope(context, "service", task.id)
|
|
681
|
+
: context;
|
|
639
682
|
|
|
640
|
-
if (
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
await serviceProvider.initService(context);
|
|
648
|
-
if (
|
|
649
|
-
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
650
|
-
configurationServiceProvider?.secretServiceEnabled
|
|
651
|
-
) {
|
|
652
|
-
await configurationServiceProvider.clearKeyValueScope();
|
|
683
|
+
if ((task.mode ?? "blocking") === "blocking") {
|
|
684
|
+
await task.run(taskContext);
|
|
685
|
+
} else {
|
|
686
|
+
void task.run(taskContext).catch((error: unknown) => {
|
|
687
|
+
logger.debug(() => `Background startup task '${task.id}' failed: ${error}`);
|
|
688
|
+
});
|
|
653
689
|
}
|
|
654
690
|
}
|
|
655
691
|
|
|
@@ -668,6 +704,7 @@ export async function run(
|
|
|
668
704
|
globalModifierCommandsByName,
|
|
669
705
|
globalModifierCommandsByShortAlias,
|
|
670
706
|
configurationServiceProvider,
|
|
707
|
+
keyValueServiceProvider,
|
|
671
708
|
context,
|
|
672
709
|
);
|
|
673
710
|
|
|
@@ -695,6 +732,7 @@ export async function run(
|
|
|
695
732
|
globalCommandsByShortAlias,
|
|
696
733
|
groupAndMemberCommandByJoinedName,
|
|
697
734
|
configurationServiceProvider,
|
|
735
|
+
keyValueServiceProvider,
|
|
698
736
|
context,
|
|
699
737
|
);
|
|
700
738
|
|
|
@@ -715,6 +753,7 @@ export async function run(
|
|
|
715
753
|
unusedArgs,
|
|
716
754
|
defaultCommand,
|
|
717
755
|
configurationServiceProvider,
|
|
756
|
+
keyValueServiceProvider,
|
|
718
757
|
context,
|
|
719
758
|
);
|
|
720
759
|
|