@flowscripter/dynamic-cli-framework 1.1.6 → 1.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/.github/workflows/release-bun-library.yml +1 -0
- package/README/core-commands.md +40 -0
- package/README/core-service-providers.md +198 -0
- package/README/development.md +60 -0
- package/README/implementation-details.md +474 -0
- package/README/key-concepts.md +751 -0
- package/README.md +12 -1413
- package/bun.lock +20 -18
- package/index.ts +97 -1
- package/package.json +8 -7
- package/src/api/BaseCLIFeatureOptions.ts +10 -0
- package/src/api/CLIConfig.ts +5 -0
- package/src/api/RunResult.ts +16 -0
- package/src/api/argument/Argument.ts +24 -0
- package/src/api/service/ServiceProvider.ts +3 -3
- package/src/api/service/core/ArgumentPrompterService.ts +10 -0
- package/src/api/service/core/AsciiBannerGeneratorService.ts +44 -7
- package/src/api/service/core/CompletionService.ts +36 -0
- package/src/api/service/core/DataDumpGeneratorService.ts +27 -0
- package/src/api/service/core/ImagePrinterService.ts +26 -0
- package/src/api/service/core/KeyValueService.ts +22 -5
- package/src/api/service/core/PrinterService.ts +127 -1
- package/src/api/service/core/PrompterService.ts +49 -0
- package/src/api/service/core/SecretService.ts +27 -0
- package/src/api/service/core/ShutdownService.ts +17 -1
- package/src/api/service/core/Table.ts +85 -0
- package/src/api/service/core/TableGeneratorService.ts +38 -0
- package/src/api/service/core/TreePrinterService.ts +11 -0
- package/src/cli/BaseCLI.ts +116 -44
- package/src/cli/DefaultRuntimeCLI.ts +10 -18
- package/src/command/CompletionCommand.ts +179 -0
- package/src/command/MultiCommandCliHelpCommand.ts +18 -2
- package/src/command/SingleCommandCliHelpCommand.ts +10 -1
- package/src/launcher.ts +16 -58
- package/src/runtime/command/CommandValidator.ts +3 -3
- package/src/runtime/runner.ts +102 -30
- package/src/runtime/scanner.ts +18 -15
- package/src/runtime/values/argumentValueValidation.ts +49 -5
- package/src/runtime/values/globalCommandValuePopulation.ts +1 -1
- package/src/runtime/values/subCommandValuePopulation.ts +9 -8
- package/src/service/argumentPrompter/ArgumentPrompterServiceProvider.ts +34 -0
- package/src/service/argumentPrompter/DefaultArgumentPrompterService.ts +330 -0
- package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +1 -1
- package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +254 -17
- package/src/service/banner/BannerServiceProvider.ts +2 -2
- package/src/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.ts +85 -0
- package/src/service/chiselAsciiBannerGenerator/chisel.flf.json +3 -0
- package/src/service/completion/CompletionServiceProvider.ts +212 -0
- package/src/service/completion/DefaultCompletionService.ts +364 -0
- package/src/service/completion/shell/ShellHandler.ts +16 -0
- package/src/service/completion/shell/bash.ts +47 -0
- package/src/service/completion/shell/fish.ts +43 -0
- package/src/service/completion/shell/powershell.ts +66 -0
- package/src/service/completion/shell/zsh.ts +50 -0
- package/src/service/configuration/ConfigurationServiceProvider.ts +138 -42
- package/src/service/configuration/DefaultKeyValueService.ts +53 -7
- package/src/service/configuration/DefaultSecretService.ts +85 -0
- package/src/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.ts +41 -0
- package/src/service/dataDumpGenerator/DefaultDataDumpGeneratorService.ts +80 -0
- package/src/service/imagePrinter/DefaultImagePrinterService.ts +23 -0
- package/src/service/imagePrinter/ImagePrinterServiceProvider.ts +32 -0
- package/src/service/prettyPrinter/PrettyPrinterServiceProvider.ts +1 -1
- package/src/service/printer/DefaultPrinterService.ts +147 -27
- package/src/service/printer/PrinterServiceProvider.ts +7 -7
- package/src/service/printer/command/NoHyperlinksCommand.ts +40 -0
- package/src/service/printer/terminal/Progress.ts +21 -4
- package/src/service/printer/terminal/Spinner.ts +32 -8
- package/src/service/printer/terminal/Theme.ts +1 -1
- package/src/service/prompter/DefaultPrompterService.ts +87 -0
- package/src/service/prompter/PrompterServiceProvider.ts +36 -0
- package/src/service/prompter/command/NoPromptCommand.ts +40 -0
- package/src/service/prompter/prompt/PromptContext.ts +47 -0
- package/src/service/prompter/prompt/promptAcknowledge.ts +28 -0
- package/src/service/prompter/prompt/promptMultiSelect.ts +99 -0
- package/src/service/prompter/prompt/promptOpenUrl.ts +97 -0
- package/src/service/prompter/prompt/promptSingleSelect.ts +102 -0
- package/src/service/prompter/prompt/promptText.ts +107 -0
- package/src/service/prompter/prompt/promptToggle.ts +28 -0
- package/src/service/shutdown/DefaultShutdownService.ts +13 -0
- package/src/service/shutdown/ShutdownServiceProvider.ts +28 -9
- package/src/service/shutdown/ShutdownState.ts +5 -0
- package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +1 -1
- package/src/service/tableGenerator/DefaultTableGeneratorService.ts +415 -0
- package/src/service/tableGenerator/TableGeneratorServiceProvider.ts +43 -0
- package/src/service/treePrinter/DefaultTreePrinterService.ts +49 -0
- package/src/service/treePrinter/TreePrinterServiceProvider.ts +40 -0
- package/src/terminal/Ansi.ts +27 -0
- package/src/terminal/ImageRenderer.ts +199 -0
- package/src/terminal/KeyReader.ts +23 -0
- package/src/terminal/PngDecoder.ts +143 -0
- package/src/{service/printer/terminal → terminal}/Styler.ts +3 -0
- package/src/{service/printer/terminal → terminal}/Terminal.ts +1 -0
- package/src/terminal/TtyKeyReader.ts +83 -0
- package/src/terminal/TtyStyler.ts +78 -0
- package/src/{service/printer/terminal → terminal}/TtyTerminal.ts +9 -1
- package/src/terminal/supportsHyperlinks.ts +110 -0
- package/src/util/configHelper.ts +2 -2
- package/src/util/envVarHelper.ts +2 -2
- package/src/util/helpHelper.ts +66 -122
- package/src/util/logger.ts +16 -14
- package/src/util/runnerHelper.ts +1 -1
- package/tests/cli/BaseCLI.test.ts +50 -54
- package/tests/command/CompletionCommand.test.ts +68 -0
- package/tests/command/UsageCommand.test.ts +3 -2
- package/tests/command/VersionCommand.test.ts +3 -2
- package/tests/fixtures/Context.ts +9 -2
- package/tests/fixtures/util.ts +1 -1
- package/tests/runtime/parser.test.ts +9 -7
- package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +3 -3
- package/tests/runtime/values/argumentValueValidation.test.ts +193 -0
- package/tests/service/argumentPrompter/ArgumentPrompterServiceProvider.test.ts +47 -0
- package/tests/service/argumentPrompter/DefaultArgumentPrompterService.test.ts +754 -0
- package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +5 -4
- package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +163 -3
- package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +1 -1
- package/tests/service/banner/BannerServiceProvider.test.ts +8 -4
- package/tests/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.test.ts +52 -0
- package/tests/service/completion/CompletionServiceProvider.test.ts +404 -0
- package/tests/service/completion/DefaultCompletionService.test.ts +278 -0
- package/tests/service/completion/shell/bash.test.ts +73 -0
- package/tests/service/completion/shell/fish.test.ts +62 -0
- package/tests/service/completion/shell/powershell.test.ts +59 -0
- package/tests/service/completion/shell/zsh.test.ts +60 -0
- package/tests/service/configuration/ConfigurationServiceProvider.test.ts +24 -12
- package/tests/service/configuration/DefaultKeyValueService.test.ts +118 -16
- package/tests/service/configuration/DefaultSecretService.test.ts +148 -0
- package/tests/service/configuration/command/ConfigCommand.test.ts +29 -0
- package/tests/service/configuration/command/DumpConfigCommand.test.ts +60 -0
- package/tests/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.test.ts +57 -0
- package/tests/service/dataDumpGenerator/DefaultDataDumpGeneratorService.test.ts +217 -0
- package/tests/service/imagePrinter/DefaultImagePrinterService.test.ts +96 -0
- package/tests/service/imagePrinter/ImagePrinterServiceProvider.test.ts +17 -0
- package/tests/service/prettyPrinter/DefaultPrettyPrinterService.test.ts +2 -2
- package/tests/service/prettyPrinter/PrettyPrinterServiceProvider.test.ts +5 -4
- package/tests/service/printer/DefaultPrinterService.test.ts +233 -2
- package/tests/service/printer/PrinterServiceProvider.test.ts +6 -5
- package/tests/service/printer/command/DarkModeCommand.test.ts +83 -0
- package/tests/service/printer/command/LogLevelCommand.test.ts +7 -5
- package/tests/service/printer/command/NoColorCommand.test.ts +83 -0
- package/tests/service/printer/command/NoHyperlinksCommand.test.ts +89 -0
- package/tests/service/printer/terminal/Progress.test.ts +64 -4
- package/tests/service/printer/terminal/Spinner.test.ts +44 -3
- package/tests/service/prompter/DefaultPrompterService.test.ts +615 -0
- package/tests/service/prompter/PrompterServiceProvider.test.ts +47 -0
- package/tests/service/prompter/command/NoPromptCommand.test.ts +58 -0
- package/tests/service/shutdown/ShutdownServiceProvider.test.ts +4 -2
- package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +3 -2
- package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +5 -4
- package/tests/service/tableGenerator/DefaultTableGeneratorService.test.ts +363 -0
- package/tests/service/tableGenerator/TableGeneratorServiceProvider.test.ts +47 -0
- package/tests/service/treePrinter/DefaultTreePrinterService.test.ts +144 -0
- package/tests/service/treePrinter/TreePrinterServiceProvider.test.ts +55 -0
- package/tests/terminal/ImageRenderer.test.ts +161 -0
- package/tests/terminal/PngDecoder.test.ts +223 -0
- package/tests/terminal/supportsHyperlinks.test.ts +149 -0
- package/tests/util/configHelper.test.ts +4 -4
- package/tests/util/logger.test.ts +23 -1
- package/tsconfig.json +31 -0
- package/src/service/printer/terminal/Ansi.ts +0 -10
- package/src/service/printer/terminal/TtyStyler.ts +0 -39
package/src/launcher.ts
CHANGED
|
@@ -1,33 +1,20 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type CLIConfig from "./api/CLIConfig.ts";
|
|
4
|
+
import type BaseCLIFeatureOptions from "./api/BaseCLIFeatureOptions.ts";
|
|
4
5
|
import type SubCommand from "./api/command/SubCommand.ts";
|
|
5
6
|
import DefaultRuntimeCLI from "./cli/DefaultRuntimeCLI.ts";
|
|
6
7
|
import type Command from "./api/command/Command.ts";
|
|
7
8
|
import type RunResult from "./api/RunResult.ts";
|
|
8
9
|
import type { ServiceProvider } from "./api/service/ServiceProvider.ts";
|
|
9
10
|
|
|
10
|
-
/**
|
|
11
|
-
* Launch a {@link DefaultRuntimeCLI} with a single specified {@link SubCommand} instance.
|
|
12
|
-
*
|
|
13
|
-
* @param command the {@link SubCommand} to add to the CLI.
|
|
14
|
-
* @param description optional description of the CLI.
|
|
15
|
-
* @param name optional name of the CLI. If not provided, `process.execPath` will be used in an attempt to derive the name.
|
|
16
|
-
* @param version optional version of the CLI.
|
|
17
|
-
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
18
|
-
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
19
|
-
* @param keyValueServiceEnabled optionally provide a {@link KeyValueService} implementation: `configEnabled` must be true in this case
|
|
20
|
-
* @param serviceProviders optional array of {@link ServiceProvider} instances to add to the CLI.
|
|
21
|
-
*/
|
|
22
11
|
export async function launchSingleCommandCLI(
|
|
23
12
|
command: SubCommand,
|
|
24
13
|
description?: string,
|
|
25
14
|
name?: string,
|
|
26
15
|
version?: string,
|
|
27
|
-
envVarsEnabled = false,
|
|
28
|
-
configEnabled = false,
|
|
29
|
-
keyValueServiceEnabled = false,
|
|
30
16
|
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
17
|
+
options?: BaseCLIFeatureOptions,
|
|
31
18
|
): Promise<RunResult> {
|
|
32
19
|
if (!name) {
|
|
33
20
|
name = path.basename(process.execPath);
|
|
@@ -38,20 +25,12 @@ export async function launchSingleCommandCLI(
|
|
|
38
25
|
version: version || "N/A",
|
|
39
26
|
};
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
const
|
|
47
|
-
process.env.DYNAMIC_CLI_FRAMEWORK_VALIDATE_ALL !== undefined;
|
|
48
|
-
const cli = new DefaultRuntimeCLI(
|
|
49
|
-
cliConfig,
|
|
50
|
-
envVarsEnabled,
|
|
51
|
-
configEnabled,
|
|
52
|
-
keyValueServiceEnabled,
|
|
53
|
-
validateAllCommands,
|
|
54
|
-
);
|
|
28
|
+
const mergedOptions: BaseCLIFeatureOptions = {
|
|
29
|
+
...options,
|
|
30
|
+
validateAllCommands: (options?.validateAllCommands ?? false) ||
|
|
31
|
+
process.env.DYNAMIC_CLI_FRAMEWORK_VALIDATE_ALL !== undefined,
|
|
32
|
+
};
|
|
33
|
+
const cli = new DefaultRuntimeCLI(cliConfig, mergedOptions);
|
|
55
34
|
|
|
56
35
|
cli.addCommand(command);
|
|
57
36
|
|
|
@@ -60,27 +39,13 @@ export async function launchSingleCommandCLI(
|
|
|
60
39
|
return await cli.run();
|
|
61
40
|
}
|
|
62
41
|
|
|
63
|
-
/**
|
|
64
|
-
* Launch a {@link DefaultRuntimeCLI} with multiple specified {@link Command} instances.
|
|
65
|
-
*
|
|
66
|
-
* @param commands the {@link Command} instances to add to the CLI.
|
|
67
|
-
* @param description optional description of the CLI.
|
|
68
|
-
* @param name optional name of the CLI. If not provided, `process.execPath` will be used in an attempt to derive the name.
|
|
69
|
-
* @param version optional version of the CLI.
|
|
70
|
-
* @param envVarsEnabled optionally support checking env variables for default argument values.
|
|
71
|
-
* @param configEnabled optionally enable configuration file support for default argument values.
|
|
72
|
-
* @param keyValueServiceEnabled optionally provide a {@link KeyValueService} implementation: `configEnabled` must be true in this case
|
|
73
|
-
* @param serviceProviders optional array of {@link ServiceProvider} instances to add to the CLI.
|
|
74
|
-
*/
|
|
75
42
|
export async function launchMultiCommandCLI(
|
|
76
43
|
commands: ReadonlyArray<Command>,
|
|
77
44
|
description?: string,
|
|
78
45
|
name?: string,
|
|
79
46
|
version?: string,
|
|
80
|
-
envVarsEnabled = false,
|
|
81
|
-
configEnabled = false,
|
|
82
|
-
keyValueServiceEnabled = false,
|
|
83
47
|
serviceProviders?: ReadonlyArray<ServiceProvider>,
|
|
48
|
+
options?: BaseCLIFeatureOptions,
|
|
84
49
|
): Promise<RunResult> {
|
|
85
50
|
if (!name) {
|
|
86
51
|
name = path.basename(process.execPath);
|
|
@@ -90,20 +55,13 @@ export async function launchMultiCommandCLI(
|
|
|
90
55
|
name,
|
|
91
56
|
version: version || "N/A",
|
|
92
57
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const cli = new DefaultRuntimeCLI(
|
|
101
|
-
cliConfig,
|
|
102
|
-
envVarsEnabled,
|
|
103
|
-
configEnabled,
|
|
104
|
-
keyValueServiceEnabled,
|
|
105
|
-
validateAllCommands,
|
|
106
|
-
);
|
|
58
|
+
|
|
59
|
+
const mergedOptions: BaseCLIFeatureOptions = {
|
|
60
|
+
...options,
|
|
61
|
+
validateAllCommands: (options?.validateAllCommands ?? false) ||
|
|
62
|
+
process.env.DYNAMIC_CLI_FRAMEWORK_VALIDATE_ALL !== undefined,
|
|
63
|
+
};
|
|
64
|
+
const cli = new DefaultRuntimeCLI(cliConfig, mergedOptions);
|
|
107
65
|
|
|
108
66
|
commands.forEach((command) => cli.addCommand(command));
|
|
109
67
|
|
|
@@ -98,7 +98,7 @@ function isNameLegal(name: string): boolean {
|
|
|
98
98
|
function isConfigurationKeyLegal(configurationKey: string): boolean {
|
|
99
99
|
return (configurationKey.length > 0) &&
|
|
100
100
|
isUppercaseAlphaNumericOrUnderscore(configurationKey) &&
|
|
101
|
-
!configurationKey[0]
|
|
101
|
+
!configurationKey[0]!.match(/[0-9]/);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function validateArgument(argument: Argument, name: string): void {
|
|
@@ -368,7 +368,7 @@ export default class CommandValidator {
|
|
|
368
368
|
}
|
|
369
369
|
if (subCommand.positionals) {
|
|
370
370
|
for (let i = 0; i < subCommand.positionals.length; i += 1) {
|
|
371
|
-
const positional = subCommand.positionals[i]
|
|
371
|
+
const positional = subCommand.positionals[i]!;
|
|
372
372
|
validateSubCommandArgument(positional);
|
|
373
373
|
if (argumentNames.includes(positional.name)) {
|
|
374
374
|
throw new Error(
|
|
@@ -396,7 +396,7 @@ export default class CommandValidator {
|
|
|
396
396
|
command: Command,
|
|
397
397
|
argumentAncestry: Array<SubCommandArgument>,
|
|
398
398
|
) {
|
|
399
|
-
const argument = argumentAncestry[argumentAncestry.length - 1]
|
|
399
|
+
const argument = argumentAncestry[argumentAncestry.length - 1]!;
|
|
400
400
|
if (command.enableConfiguration === true) {
|
|
401
401
|
if (
|
|
402
402
|
(argument.configurationKey !== undefined) &&
|
package/src/runtime/runner.ts
CHANGED
|
@@ -33,9 +33,35 @@ import type GroupCommand from "../api/command/GroupCommand.ts";
|
|
|
33
33
|
import type SubCommand from "../api/command/SubCommand.ts";
|
|
34
34
|
import type GlobalCommand from "../api/command/GlobalCommand.ts";
|
|
35
35
|
import type Command from "../api/command/Command.ts";
|
|
36
|
+
import type ArgumentPrompterService from "../api/service/core/ArgumentPrompterService.ts";
|
|
37
|
+
import { ARGUMENT_PROMPTER_SERVICE_ID } from "../api/service/core/ArgumentPrompterService.ts";
|
|
36
38
|
|
|
37
39
|
const logger = getLogger("runner");
|
|
38
40
|
|
|
41
|
+
async function attemptPromptForMissingArguments(
|
|
42
|
+
parseResult: ParseResult,
|
|
43
|
+
context: Context,
|
|
44
|
+
): Promise<ParseResult | undefined> {
|
|
45
|
+
if (!context.doesServiceExist(ARGUMENT_PROMPTER_SERVICE_ID)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const service = context.getServiceById(
|
|
49
|
+
ARGUMENT_PROMPTER_SERVICE_ID,
|
|
50
|
+
) as ArgumentPrompterService;
|
|
51
|
+
try {
|
|
52
|
+
const updated = await service.promptForMissingArguments(parseResult);
|
|
53
|
+
if (updated.invalidArguments.length === 0) {
|
|
54
|
+
return updated;
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if ((error as Error).message === "Interrupted") {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
39
65
|
/**
|
|
40
66
|
* Execute the {@link Command} with arguments contained in the provided {@link ParseResult}.
|
|
41
67
|
*
|
|
@@ -56,13 +82,19 @@ async function executeParsedCommand(
|
|
|
56
82
|
"Executing group command with name: %s",
|
|
57
83
|
parseResult!.groupCommand!.name,
|
|
58
84
|
);
|
|
59
|
-
if (
|
|
85
|
+
if (
|
|
86
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
87
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
88
|
+
) {
|
|
60
89
|
configurationServiceProvider.setCommandKeyValueScope(
|
|
61
90
|
parseResult!.groupCommand!.name,
|
|
62
91
|
);
|
|
63
92
|
}
|
|
64
93
|
await parseResult.groupCommand.execute(context);
|
|
65
|
-
if (
|
|
94
|
+
if (
|
|
95
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
96
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
97
|
+
) {
|
|
66
98
|
await configurationServiceProvider.clearKeyValueScope();
|
|
67
99
|
}
|
|
68
100
|
}
|
|
@@ -73,7 +105,10 @@ async function executeParsedCommand(
|
|
|
73
105
|
parseResult!.populatedArgumentValues,
|
|
74
106
|
);
|
|
75
107
|
|
|
76
|
-
if (
|
|
108
|
+
if (
|
|
109
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
110
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
111
|
+
) {
|
|
77
112
|
configurationServiceProvider.setCommandKeyValueScope(
|
|
78
113
|
parseResult.command.name,
|
|
79
114
|
);
|
|
@@ -89,10 +124,16 @@ async function executeParsedCommand(
|
|
|
89
124
|
parseResult.populatedArgumentValues as ArgumentSingleValueType,
|
|
90
125
|
);
|
|
91
126
|
}
|
|
92
|
-
if (
|
|
127
|
+
if (
|
|
128
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
129
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
130
|
+
) {
|
|
93
131
|
await configurationServiceProvider.clearKeyValueScope();
|
|
94
132
|
}
|
|
95
133
|
} catch (err) {
|
|
134
|
+
if ((err as Error).message === "Interrupted") {
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
96
137
|
await printCommandExecutionError(
|
|
97
138
|
context,
|
|
98
139
|
parseResult,
|
|
@@ -155,7 +196,7 @@ async function findAndExecuteGlobalModifierCommands(
|
|
|
155
196
|
) {
|
|
156
197
|
// parse and fast fail on error
|
|
157
198
|
const defaultArgumentValues = configurationServiceProvider
|
|
158
|
-
? configurationServiceProvider
|
|
199
|
+
? await configurationServiceProvider
|
|
159
200
|
.getDefaultArgumentValues(
|
|
160
201
|
context.cliConfig,
|
|
161
202
|
globalModifierCommandClause.command,
|
|
@@ -195,7 +236,7 @@ async function findAndExecuteGlobalModifierCommands(
|
|
|
195
236
|
for (const globalModifierCommand of remainingGlobalModifierCommands) {
|
|
196
237
|
// if there is a config entry run this by default even though no arguments were provided on the command line
|
|
197
238
|
const defaultArgumentValues = configurationServiceProvider
|
|
198
|
-
? configurationServiceProvider
|
|
239
|
+
? await configurationServiceProvider
|
|
199
240
|
.getDefaultArgumentValues(context.cliConfig, globalModifierCommand)
|
|
200
241
|
: undefined;
|
|
201
242
|
|
|
@@ -274,7 +315,7 @@ async function findAndExecuteNonModifierCommand(
|
|
|
274
315
|
|
|
275
316
|
if (scanResult.nonModifierCommandClause) {
|
|
276
317
|
const defaultArgumentValues = configurationServiceProvider
|
|
277
|
-
? configurationServiceProvider
|
|
318
|
+
? await configurationServiceProvider
|
|
278
319
|
.getDefaultArgumentValues(
|
|
279
320
|
context.cliConfig,
|
|
280
321
|
scanResult.nonModifierCommandClause.command,
|
|
@@ -294,12 +335,20 @@ async function findAndExecuteNonModifierCommand(
|
|
|
294
335
|
}
|
|
295
336
|
|
|
296
337
|
if (parseResult.invalidArguments.length > 0) {
|
|
297
|
-
await
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
338
|
+
const prompted = await attemptPromptForMissingArguments(
|
|
339
|
+
parseResult,
|
|
340
|
+
context,
|
|
341
|
+
);
|
|
342
|
+
if (prompted) {
|
|
343
|
+
parseResult = prompted;
|
|
344
|
+
} else {
|
|
345
|
+
await printParseResultError(context, parseResult);
|
|
346
|
+
return {
|
|
347
|
+
runState: RunState.PARSE_ERROR,
|
|
348
|
+
command: parseResult.command,
|
|
349
|
+
invalidArguments: parseResult.invalidArguments,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
303
352
|
}
|
|
304
353
|
|
|
305
354
|
// add any unused args back to the list of still unused args
|
|
@@ -311,7 +360,7 @@ async function findAndExecuteNonModifierCommand(
|
|
|
311
360
|
for (const nonModifierCommand of nonModifierCommandsByName.values()) {
|
|
312
361
|
// if there is a config entry run this by default even though no arguments were provided on the command line
|
|
313
362
|
const defaultArgumentValues = configurationServiceProvider
|
|
314
|
-
? configurationServiceProvider
|
|
363
|
+
? await configurationServiceProvider
|
|
315
364
|
.getDefaultArgumentValues(context.cliConfig, nonModifierCommand)
|
|
316
365
|
: undefined;
|
|
317
366
|
|
|
@@ -329,12 +378,20 @@ async function findAndExecuteNonModifierCommand(
|
|
|
329
378
|
}
|
|
330
379
|
|
|
331
380
|
if (parseResult.invalidArguments.length > 0) {
|
|
332
|
-
await
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
381
|
+
const prompted = await attemptPromptForMissingArguments(
|
|
382
|
+
parseResult,
|
|
383
|
+
context,
|
|
384
|
+
);
|
|
385
|
+
if (prompted) {
|
|
386
|
+
parseResult = prompted;
|
|
387
|
+
} else {
|
|
388
|
+
await printParseResultError(context, parseResult);
|
|
389
|
+
return {
|
|
390
|
+
runState: RunState.PARSE_ERROR,
|
|
391
|
+
command: parseResult.command,
|
|
392
|
+
invalidArguments: parseResult.invalidArguments,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
338
395
|
}
|
|
339
396
|
|
|
340
397
|
break;
|
|
@@ -376,7 +433,7 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
376
433
|
context: Context,
|
|
377
434
|
): Promise<RunResult | undefined> {
|
|
378
435
|
const defaultArgumentValues = configurationServiceProvider
|
|
379
|
-
? configurationServiceProvider
|
|
436
|
+
? await configurationServiceProvider
|
|
380
437
|
.getDefaultArgumentValues(context.cliConfig, defaultNonModifierCommand)
|
|
381
438
|
: undefined;
|
|
382
439
|
const isGlobal = isGlobalCommand(defaultNonModifierCommand);
|
|
@@ -448,14 +505,23 @@ async function findAndExecuteDefaultNonModifierCommand(
|
|
|
448
505
|
}
|
|
449
506
|
}
|
|
450
507
|
|
|
451
|
-
// if there were invalid arguments,
|
|
508
|
+
// if there were invalid arguments, attempt prompting before treating as error
|
|
452
509
|
if (lastErroneousParseResult) {
|
|
453
|
-
await
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
510
|
+
const prompted = await attemptPromptForMissingArguments(
|
|
511
|
+
lastErroneousParseResult,
|
|
512
|
+
context,
|
|
513
|
+
);
|
|
514
|
+
if (prompted) {
|
|
515
|
+
parseResult = prompted;
|
|
516
|
+
lastErroneousParseResult = undefined;
|
|
517
|
+
} else {
|
|
518
|
+
await printParseResultError(context, lastErroneousParseResult, true);
|
|
519
|
+
return {
|
|
520
|
+
runState: RunState.PARSE_ERROR,
|
|
521
|
+
command: defaultNonModifierCommand,
|
|
522
|
+
invalidArguments: lastErroneousParseResult.invalidArguments,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
459
525
|
}
|
|
460
526
|
|
|
461
527
|
// give up if still nothing found
|
|
@@ -604,14 +670,20 @@ export async function run(
|
|
|
604
670
|
|
|
605
671
|
logger.debug("Initialising service with ID: %s", serviceProvider.serviceId);
|
|
606
672
|
|
|
607
|
-
if (
|
|
673
|
+
if (
|
|
674
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
675
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
676
|
+
) {
|
|
608
677
|
configurationServiceProvider.setServiceKeyValueScope(
|
|
609
678
|
serviceProvider.serviceId,
|
|
610
679
|
);
|
|
611
680
|
}
|
|
612
681
|
|
|
613
682
|
await serviceProvider.initService(context);
|
|
614
|
-
if (
|
|
683
|
+
if (
|
|
684
|
+
configurationServiceProvider?.keyValueServiceEnabled ||
|
|
685
|
+
configurationServiceProvider?.secretServiceEnabled
|
|
686
|
+
) {
|
|
615
687
|
await configurationServiceProvider.clearKeyValueScope();
|
|
616
688
|
}
|
|
617
689
|
}
|
package/src/runtime/scanner.ts
CHANGED
|
@@ -67,15 +67,15 @@ function normaliseFirstArgumentIfRequired(
|
|
|
67
67
|
// --<global_[modifier_]command_name>=<value>
|
|
68
68
|
// -<global_[modifier_]command_short_alias>
|
|
69
69
|
// -<global_[modifier_]command_short_alias>=<value>
|
|
70
|
-
if ((firstArg
|
|
70
|
+
if ((firstArg!.length < 2) || (firstArg!.charAt(0) !== "-")) {
|
|
71
71
|
return args;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// looking for:
|
|
75
75
|
// --<global_[modifier_]command_name>
|
|
76
76
|
// --<global_[modifier_]command_name>=<value>
|
|
77
|
-
if ((firstArg
|
|
78
|
-
let potentialGlobalCommandName = firstArg
|
|
77
|
+
if ((firstArg!.length > 2) && (firstArg!.charAt(1) === "-")) {
|
|
78
|
+
let potentialGlobalCommandName = firstArg!.slice(2);
|
|
79
79
|
|
|
80
80
|
// looking for:
|
|
81
81
|
// <global_command_name>=<value>
|
|
@@ -84,7 +84,7 @@ function normaliseFirstArgumentIfRequired(
|
|
|
84
84
|
if (potentialGlobalCommandName.includes("=")) {
|
|
85
85
|
[potentialGlobalCommandName, nextArg] = potentialGlobalCommandName.split(
|
|
86
86
|
/=(.*)/,
|
|
87
|
-
);
|
|
87
|
+
) as [string, string | undefined];
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
if (!globalCommandsByName.has(potentialGlobalCommandName)) {
|
|
@@ -105,14 +105,17 @@ function normaliseFirstArgumentIfRequired(
|
|
|
105
105
|
// -<global_[modifier_]command_short_alias>
|
|
106
106
|
// -<global_[modifier_]command_short_alias>=<value>
|
|
107
107
|
// (by now we know the arg starts with -<char>)
|
|
108
|
-
let potentialGlobalCommandShortAlias = firstArg
|
|
108
|
+
let potentialGlobalCommandShortAlias = firstArg!.slice(1);
|
|
109
109
|
|
|
110
110
|
// looking for:
|
|
111
111
|
// <global_[modifier_]command_short_alias>=<value>
|
|
112
112
|
let nextArg: string | undefined;
|
|
113
113
|
if (potentialGlobalCommandShortAlias.includes("=")) {
|
|
114
114
|
[potentialGlobalCommandShortAlias, nextArg] =
|
|
115
|
-
potentialGlobalCommandShortAlias.split(/=(.*)/)
|
|
115
|
+
potentialGlobalCommandShortAlias.split(/=(.*)/) as [
|
|
116
|
+
string,
|
|
117
|
+
string | undefined,
|
|
118
|
+
];
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
if (!globalCommandsByShortAlias) {
|
|
@@ -169,8 +172,8 @@ function scanForNextCommandClause(
|
|
|
169
172
|
|
|
170
173
|
// looking for:
|
|
171
174
|
// <global_[modifier_]command_name>
|
|
172
|
-
if (currentArg
|
|
173
|
-
const potentialCommandName = currentArg
|
|
175
|
+
if (currentArg!.startsWith("--")) {
|
|
176
|
+
const potentialCommandName = currentArg!.slice(2);
|
|
174
177
|
|
|
175
178
|
// check for Global[Modifier]Command name
|
|
176
179
|
const command = commandsByName.get(potentialCommandName);
|
|
@@ -188,8 +191,8 @@ function scanForNextCommandClause(
|
|
|
188
191
|
};
|
|
189
192
|
}
|
|
190
193
|
}
|
|
191
|
-
if (!includeSubCommands || (currentArg[0] === "-")) {
|
|
192
|
-
unusedArgs.push(currentArg);
|
|
194
|
+
if (!includeSubCommands || (currentArg![0] === "-")) {
|
|
195
|
+
unusedArgs.push(currentArg!);
|
|
193
196
|
continue;
|
|
194
197
|
}
|
|
195
198
|
|
|
@@ -202,11 +205,11 @@ function scanForNextCommandClause(
|
|
|
202
205
|
let potentialMemberSubCommandName: string | undefined;
|
|
203
206
|
let potentialMemberCommandNameArgumentUsed = false;
|
|
204
207
|
|
|
205
|
-
if (potentialGroupCommandName
|
|
208
|
+
if (potentialGroupCommandName!.includes(":")) {
|
|
206
209
|
// looking for:
|
|
207
210
|
// <group_command_name>:<member_sub_command_name>
|
|
208
211
|
[potentialGroupCommandName, potentialMemberSubCommandName] =
|
|
209
|
-
potentialGroupCommandName
|
|
212
|
+
potentialGroupCommandName!.split(":") as [string, string | undefined];
|
|
210
213
|
} else if (pendingArgs.length > 0) {
|
|
211
214
|
// looking for:
|
|
212
215
|
// <group_command_name> <member_sub_command_name>
|
|
@@ -250,9 +253,9 @@ function scanForNextCommandClause(
|
|
|
250
253
|
//
|
|
251
254
|
// if we find it is a group command we skip it as we didn't find a member command
|
|
252
255
|
// following it the logic above
|
|
253
|
-
const command = commandsByName.get(currentArg);
|
|
256
|
+
const command = commandsByName.get(currentArg!);
|
|
254
257
|
if (command && !isGroupCommand(command)) {
|
|
255
|
-
logger.debug("Found sub-command name: %s", currentArg);
|
|
258
|
+
logger.debug("Found sub-command name: %s", currentArg!);
|
|
256
259
|
return {
|
|
257
260
|
commandClause: {
|
|
258
261
|
command,
|
|
@@ -263,7 +266,7 @@ function scanForNextCommandClause(
|
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
// if we got here we haven't found a command so add the current arg to unused args
|
|
266
|
-
unusedArgs.push(currentArg);
|
|
269
|
+
unusedArgs.push(currentArg!);
|
|
267
270
|
}
|
|
268
271
|
// if we got here we haven't found a command and we have processed all provided args
|
|
269
272
|
return {
|
|
@@ -120,7 +120,7 @@ function validatePrimitiveValue(
|
|
|
120
120
|
(argument.type === ArgumentValueTypeName.STRING) &&
|
|
121
121
|
argument.isCaseInsensitive
|
|
122
122
|
) {
|
|
123
|
-
searchValue = convertedValue.toLowerCase();
|
|
123
|
+
searchValue = (convertedValue as string).toLowerCase();
|
|
124
124
|
allowableValues = argument.allowableValues.map((v) =>
|
|
125
125
|
(v as string).toLowerCase()
|
|
126
126
|
);
|
|
@@ -136,7 +136,7 @@ function validatePrimitiveValue(
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
if (argument.minValueInclusive !== undefined) {
|
|
139
|
-
if (convertedValue < argument.minValueInclusive) {
|
|
139
|
+
if ((convertedValue as number) < argument.minValueInclusive) {
|
|
140
140
|
return {
|
|
141
141
|
invalidArgument: {
|
|
142
142
|
argument,
|
|
@@ -148,7 +148,7 @@ function validatePrimitiveValue(
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (argument.maxValueInclusive !== undefined) {
|
|
151
|
-
if (convertedValue > argument.maxValueInclusive) {
|
|
151
|
+
if ((convertedValue as number) > argument.maxValueInclusive) {
|
|
152
152
|
return {
|
|
153
153
|
invalidArgument: {
|
|
154
154
|
argument,
|
|
@@ -275,7 +275,7 @@ function validateObjectValue(
|
|
|
275
275
|
const convertedObjectValue: ArgumentValues = {};
|
|
276
276
|
|
|
277
277
|
for (let i = 0; i < argument.properties.length; i++) {
|
|
278
|
-
const propertyArg = argument.properties[i]
|
|
278
|
+
const propertyArg = argument.properties[i]!;
|
|
279
279
|
const propertyValue = objectValue[propertyArg.name];
|
|
280
280
|
|
|
281
281
|
if (propertyValue === undefined) {
|
|
@@ -306,7 +306,10 @@ function validateObjectValue(
|
|
|
306
306
|
},
|
|
307
307
|
};
|
|
308
308
|
}
|
|
309
|
-
validationResult = validateArrayValue(
|
|
309
|
+
validationResult = validateArrayValue(
|
|
310
|
+
propertyArg as ComplexOption | SubCommandArgument,
|
|
311
|
+
propertyValue,
|
|
312
|
+
);
|
|
310
313
|
} else if (typeof propertyValue === "object") {
|
|
311
314
|
if (propertyArg.type !== ComplexValueTypeName.COMPLEX) {
|
|
312
315
|
return {
|
|
@@ -468,6 +471,24 @@ function doSubCommandArgumentValidation(
|
|
|
468
471
|
invalidArguments.push(validationResult.invalidArgument);
|
|
469
472
|
return undefined;
|
|
470
473
|
}
|
|
474
|
+
if (argument.validate && validationResult.validValue !== undefined) {
|
|
475
|
+
const customError = argument.validate(
|
|
476
|
+
validationResult.validValue as
|
|
477
|
+
| ArgumentValueType
|
|
478
|
+
| ArgumentValues
|
|
479
|
+
| Array<ArgumentValues>,
|
|
480
|
+
);
|
|
481
|
+
if (customError !== undefined) {
|
|
482
|
+
invalidArguments.push({
|
|
483
|
+
argument,
|
|
484
|
+
name: argument.name,
|
|
485
|
+
value: validationResult.validValue as PopulatedArgumentValueType,
|
|
486
|
+
reason: InvalidArgumentReason.CUSTOM_VALIDATION,
|
|
487
|
+
message: customError,
|
|
488
|
+
});
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
471
492
|
return validationResult.validValue as
|
|
472
493
|
| PopulatedArgumentValueType
|
|
473
494
|
| PopulatedArgumentValues
|
|
@@ -574,6 +595,24 @@ export function validateGlobalCommandArgumentValue(
|
|
|
574
595
|
return undefined;
|
|
575
596
|
}
|
|
576
597
|
|
|
598
|
+
if (
|
|
599
|
+
globalCommandArgument.validate &&
|
|
600
|
+
validationResult.validValue !== undefined
|
|
601
|
+
) {
|
|
602
|
+
const customError = globalCommandArgument.validate(
|
|
603
|
+
validationResult.validValue as ArgumentValueType,
|
|
604
|
+
);
|
|
605
|
+
if (customError !== undefined) {
|
|
606
|
+
invalidArguments.push({
|
|
607
|
+
argument: globalCommandArgument,
|
|
608
|
+
name: globalCommand.name,
|
|
609
|
+
value: validationResult.validValue,
|
|
610
|
+
reason: InvalidArgumentReason.CUSTOM_VALIDATION,
|
|
611
|
+
message: customError,
|
|
612
|
+
});
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
577
616
|
return validationResult.validValue as PopulatedArgumentSingleValueType;
|
|
578
617
|
}
|
|
579
618
|
|
|
@@ -641,6 +680,11 @@ export function getInvalidArgumentString(
|
|
|
641
680
|
case InvalidArgumentReason.OPTION_IS_COMPLEX:
|
|
642
681
|
invalidString = "(specified option is complex)";
|
|
643
682
|
break;
|
|
683
|
+
case InvalidArgumentReason.CUSTOM_VALIDATION:
|
|
684
|
+
invalidString = invalidArgument.message
|
|
685
|
+
? `(custom validation: ${invalidArgument.message})`
|
|
686
|
+
: "(custom validation failed)";
|
|
687
|
+
break;
|
|
644
688
|
default:
|
|
645
689
|
invalidString = "";
|
|
646
690
|
break;
|
|
@@ -49,7 +49,7 @@ export default function populateGlobalCommandValue(
|
|
|
49
49
|
|
|
50
50
|
// don't use potentialArg if it cannot be a boolean value...
|
|
51
51
|
if (argument.type === ArgumentValueTypeName.BOOLEAN) {
|
|
52
|
-
const firstPotentialArgLower = firstPotentialArg
|
|
52
|
+
const firstPotentialArgLower = firstPotentialArg!.toLowerCase();
|
|
53
53
|
|
|
54
54
|
if (
|
|
55
55
|
(firstPotentialArgLower !== "true") &&
|
|
@@ -175,14 +175,14 @@ class ParseContext {
|
|
|
175
175
|
|
|
176
176
|
for (let i = 0; i < complexPathElements.length; i++) {
|
|
177
177
|
let arrayIndexString: string | undefined;
|
|
178
|
-
let complexPathElement = complexPathElements[i]
|
|
178
|
+
let complexPathElement = complexPathElements[i]!;
|
|
179
179
|
|
|
180
180
|
// look for array index
|
|
181
181
|
if (complexPathElement.endsWith("]")) {
|
|
182
182
|
[complexPathElement, arrayIndexString] = complexPathElement.slice(
|
|
183
183
|
0,
|
|
184
184
|
complexPathElement.length - 1,
|
|
185
|
-
).split("[");
|
|
185
|
+
).split("[") as [string, string | undefined];
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
let lookupPath = this.subCommand.name;
|
|
@@ -237,7 +237,7 @@ class ParseContext {
|
|
|
237
237
|
}
|
|
238
238
|
option = this.optionLookupMapsByPath.get(lookupPath)!.get(
|
|
239
239
|
complexPathElement,
|
|
240
|
-
);
|
|
240
|
+
) as Option | ComplexOption | undefined;
|
|
241
241
|
|
|
242
242
|
// now we are parsing properties of complex options it is an error if we cannot match an option name or alias
|
|
243
243
|
if (option === undefined) {
|
|
@@ -423,8 +423,8 @@ class ParseContext {
|
|
|
423
423
|
if (typeof pathElement === "string") {
|
|
424
424
|
if (Array.isArray(optionValue)) {
|
|
425
425
|
optionValue = (optionValue as Array<PopulatedArgumentValues>)[
|
|
426
|
-
optionValue.length - 1
|
|
427
|
-
][pathElement as string] as
|
|
426
|
+
(optionValue as Array<PopulatedArgumentValues>).length - 1
|
|
427
|
+
]![pathElement as string] as
|
|
428
428
|
| Array<PopulatedArgumentValues>
|
|
429
429
|
| PopulatedArgumentValues
|
|
430
430
|
| PopulatedArgumentValueType;
|
|
@@ -459,7 +459,8 @@ class ParseContext {
|
|
|
459
459
|
* @param value the value to attempt to set.
|
|
460
460
|
*/
|
|
461
461
|
setPositionalValue(value: ArgumentSingleValueType): boolean {
|
|
462
|
-
const positional = this.subCommand
|
|
462
|
+
const positional = this.subCommand
|
|
463
|
+
.positionals[this.currentPositionalIndex]!;
|
|
463
464
|
const currentValue = this.populatedArgumentValues[positional.name];
|
|
464
465
|
|
|
465
466
|
// if we haven't populated the argument value before...
|
|
@@ -536,7 +537,7 @@ class ParseContext {
|
|
|
536
537
|
// check for option AND value
|
|
537
538
|
if (potentialOptionPath.includes("=")) {
|
|
538
539
|
[potentialOptionPath, potentialOptionValue] = potentialOptionPath
|
|
539
|
-
.split(/=(.*)/);
|
|
540
|
+
.split(/=(.*)/) as [string, string | undefined, ...string[]];
|
|
540
541
|
|
|
541
542
|
// check for illegal syntax e.g. --optionName=
|
|
542
543
|
if (potentialOptionValue === "") {
|
|
@@ -623,7 +624,7 @@ export default function populateSubCommandValues(
|
|
|
623
624
|
const unusedArgs: Array<string> = [];
|
|
624
625
|
|
|
625
626
|
for (let i = 0; i < potentialArgs.length; i++) {
|
|
626
|
-
const potentialArg = potentialArgs[i]
|
|
627
|
+
const potentialArg = potentialArgs[i]!;
|
|
627
628
|
|
|
628
629
|
// parse the next arg
|
|
629
630
|
parseContext.parse(potentialArg);
|