@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.
Files changed (160) hide show
  1. package/.github/workflows/release-bun-library.yml +1 -0
  2. package/README/core-commands.md +40 -0
  3. package/README/core-service-providers.md +198 -0
  4. package/README/development.md +60 -0
  5. package/README/implementation-details.md +474 -0
  6. package/README/key-concepts.md +751 -0
  7. package/README.md +12 -1413
  8. package/bun.lock +20 -18
  9. package/index.ts +97 -1
  10. package/package.json +8 -7
  11. package/src/api/BaseCLIFeatureOptions.ts +10 -0
  12. package/src/api/CLIConfig.ts +5 -0
  13. package/src/api/RunResult.ts +16 -0
  14. package/src/api/argument/Argument.ts +24 -0
  15. package/src/api/service/ServiceProvider.ts +3 -3
  16. package/src/api/service/core/ArgumentPrompterService.ts +10 -0
  17. package/src/api/service/core/AsciiBannerGeneratorService.ts +44 -7
  18. package/src/api/service/core/CompletionService.ts +36 -0
  19. package/src/api/service/core/DataDumpGeneratorService.ts +27 -0
  20. package/src/api/service/core/ImagePrinterService.ts +26 -0
  21. package/src/api/service/core/KeyValueService.ts +22 -5
  22. package/src/api/service/core/PrinterService.ts +127 -1
  23. package/src/api/service/core/PrompterService.ts +49 -0
  24. package/src/api/service/core/SecretService.ts +27 -0
  25. package/src/api/service/core/ShutdownService.ts +17 -1
  26. package/src/api/service/core/Table.ts +85 -0
  27. package/src/api/service/core/TableGeneratorService.ts +38 -0
  28. package/src/api/service/core/TreePrinterService.ts +11 -0
  29. package/src/cli/BaseCLI.ts +116 -44
  30. package/src/cli/DefaultRuntimeCLI.ts +10 -18
  31. package/src/command/CompletionCommand.ts +179 -0
  32. package/src/command/MultiCommandCliHelpCommand.ts +18 -2
  33. package/src/command/SingleCommandCliHelpCommand.ts +10 -1
  34. package/src/launcher.ts +16 -58
  35. package/src/runtime/command/CommandValidator.ts +3 -3
  36. package/src/runtime/runner.ts +102 -30
  37. package/src/runtime/scanner.ts +18 -15
  38. package/src/runtime/values/argumentValueValidation.ts +49 -5
  39. package/src/runtime/values/globalCommandValuePopulation.ts +1 -1
  40. package/src/runtime/values/subCommandValuePopulation.ts +9 -8
  41. package/src/service/argumentPrompter/ArgumentPrompterServiceProvider.ts +34 -0
  42. package/src/service/argumentPrompter/DefaultArgumentPrompterService.ts +330 -0
  43. package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +1 -1
  44. package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +254 -17
  45. package/src/service/banner/BannerServiceProvider.ts +2 -2
  46. package/src/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.ts +85 -0
  47. package/src/service/chiselAsciiBannerGenerator/chisel.flf.json +3 -0
  48. package/src/service/completion/CompletionServiceProvider.ts +212 -0
  49. package/src/service/completion/DefaultCompletionService.ts +364 -0
  50. package/src/service/completion/shell/ShellHandler.ts +16 -0
  51. package/src/service/completion/shell/bash.ts +47 -0
  52. package/src/service/completion/shell/fish.ts +43 -0
  53. package/src/service/completion/shell/powershell.ts +66 -0
  54. package/src/service/completion/shell/zsh.ts +50 -0
  55. package/src/service/configuration/ConfigurationServiceProvider.ts +138 -42
  56. package/src/service/configuration/DefaultKeyValueService.ts +53 -7
  57. package/src/service/configuration/DefaultSecretService.ts +85 -0
  58. package/src/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.ts +41 -0
  59. package/src/service/dataDumpGenerator/DefaultDataDumpGeneratorService.ts +80 -0
  60. package/src/service/imagePrinter/DefaultImagePrinterService.ts +23 -0
  61. package/src/service/imagePrinter/ImagePrinterServiceProvider.ts +32 -0
  62. package/src/service/prettyPrinter/PrettyPrinterServiceProvider.ts +1 -1
  63. package/src/service/printer/DefaultPrinterService.ts +147 -27
  64. package/src/service/printer/PrinterServiceProvider.ts +7 -7
  65. package/src/service/printer/command/NoHyperlinksCommand.ts +40 -0
  66. package/src/service/printer/terminal/Progress.ts +21 -4
  67. package/src/service/printer/terminal/Spinner.ts +32 -8
  68. package/src/service/printer/terminal/Theme.ts +1 -1
  69. package/src/service/prompter/DefaultPrompterService.ts +87 -0
  70. package/src/service/prompter/PrompterServiceProvider.ts +36 -0
  71. package/src/service/prompter/command/NoPromptCommand.ts +40 -0
  72. package/src/service/prompter/prompt/PromptContext.ts +47 -0
  73. package/src/service/prompter/prompt/promptAcknowledge.ts +28 -0
  74. package/src/service/prompter/prompt/promptMultiSelect.ts +99 -0
  75. package/src/service/prompter/prompt/promptOpenUrl.ts +97 -0
  76. package/src/service/prompter/prompt/promptSingleSelect.ts +102 -0
  77. package/src/service/prompter/prompt/promptText.ts +107 -0
  78. package/src/service/prompter/prompt/promptToggle.ts +28 -0
  79. package/src/service/shutdown/DefaultShutdownService.ts +13 -0
  80. package/src/service/shutdown/ShutdownServiceProvider.ts +28 -9
  81. package/src/service/shutdown/ShutdownState.ts +5 -0
  82. package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +1 -1
  83. package/src/service/tableGenerator/DefaultTableGeneratorService.ts +415 -0
  84. package/src/service/tableGenerator/TableGeneratorServiceProvider.ts +43 -0
  85. package/src/service/treePrinter/DefaultTreePrinterService.ts +49 -0
  86. package/src/service/treePrinter/TreePrinterServiceProvider.ts +40 -0
  87. package/src/terminal/Ansi.ts +27 -0
  88. package/src/terminal/ImageRenderer.ts +199 -0
  89. package/src/terminal/KeyReader.ts +23 -0
  90. package/src/terminal/PngDecoder.ts +143 -0
  91. package/src/{service/printer/terminal → terminal}/Styler.ts +3 -0
  92. package/src/{service/printer/terminal → terminal}/Terminal.ts +1 -0
  93. package/src/terminal/TtyKeyReader.ts +83 -0
  94. package/src/terminal/TtyStyler.ts +78 -0
  95. package/src/{service/printer/terminal → terminal}/TtyTerminal.ts +9 -1
  96. package/src/terminal/supportsHyperlinks.ts +110 -0
  97. package/src/util/configHelper.ts +2 -2
  98. package/src/util/envVarHelper.ts +2 -2
  99. package/src/util/helpHelper.ts +66 -122
  100. package/src/util/logger.ts +16 -14
  101. package/src/util/runnerHelper.ts +1 -1
  102. package/tests/cli/BaseCLI.test.ts +50 -54
  103. package/tests/command/CompletionCommand.test.ts +68 -0
  104. package/tests/command/UsageCommand.test.ts +3 -2
  105. package/tests/command/VersionCommand.test.ts +3 -2
  106. package/tests/fixtures/Context.ts +9 -2
  107. package/tests/fixtures/util.ts +1 -1
  108. package/tests/runtime/parser.test.ts +9 -7
  109. package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +3 -3
  110. package/tests/runtime/values/argumentValueValidation.test.ts +193 -0
  111. package/tests/service/argumentPrompter/ArgumentPrompterServiceProvider.test.ts +47 -0
  112. package/tests/service/argumentPrompter/DefaultArgumentPrompterService.test.ts +754 -0
  113. package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +5 -4
  114. package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +163 -3
  115. package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +1 -1
  116. package/tests/service/banner/BannerServiceProvider.test.ts +8 -4
  117. package/tests/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.test.ts +52 -0
  118. package/tests/service/completion/CompletionServiceProvider.test.ts +404 -0
  119. package/tests/service/completion/DefaultCompletionService.test.ts +278 -0
  120. package/tests/service/completion/shell/bash.test.ts +73 -0
  121. package/tests/service/completion/shell/fish.test.ts +62 -0
  122. package/tests/service/completion/shell/powershell.test.ts +59 -0
  123. package/tests/service/completion/shell/zsh.test.ts +60 -0
  124. package/tests/service/configuration/ConfigurationServiceProvider.test.ts +24 -12
  125. package/tests/service/configuration/DefaultKeyValueService.test.ts +118 -16
  126. package/tests/service/configuration/DefaultSecretService.test.ts +148 -0
  127. package/tests/service/configuration/command/ConfigCommand.test.ts +29 -0
  128. package/tests/service/configuration/command/DumpConfigCommand.test.ts +60 -0
  129. package/tests/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.test.ts +57 -0
  130. package/tests/service/dataDumpGenerator/DefaultDataDumpGeneratorService.test.ts +217 -0
  131. package/tests/service/imagePrinter/DefaultImagePrinterService.test.ts +96 -0
  132. package/tests/service/imagePrinter/ImagePrinterServiceProvider.test.ts +17 -0
  133. package/tests/service/prettyPrinter/DefaultPrettyPrinterService.test.ts +2 -2
  134. package/tests/service/prettyPrinter/PrettyPrinterServiceProvider.test.ts +5 -4
  135. package/tests/service/printer/DefaultPrinterService.test.ts +233 -2
  136. package/tests/service/printer/PrinterServiceProvider.test.ts +6 -5
  137. package/tests/service/printer/command/DarkModeCommand.test.ts +83 -0
  138. package/tests/service/printer/command/LogLevelCommand.test.ts +7 -5
  139. package/tests/service/printer/command/NoColorCommand.test.ts +83 -0
  140. package/tests/service/printer/command/NoHyperlinksCommand.test.ts +89 -0
  141. package/tests/service/printer/terminal/Progress.test.ts +64 -4
  142. package/tests/service/printer/terminal/Spinner.test.ts +44 -3
  143. package/tests/service/prompter/DefaultPrompterService.test.ts +615 -0
  144. package/tests/service/prompter/PrompterServiceProvider.test.ts +47 -0
  145. package/tests/service/prompter/command/NoPromptCommand.test.ts +58 -0
  146. package/tests/service/shutdown/ShutdownServiceProvider.test.ts +4 -2
  147. package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +3 -2
  148. package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +5 -4
  149. package/tests/service/tableGenerator/DefaultTableGeneratorService.test.ts +363 -0
  150. package/tests/service/tableGenerator/TableGeneratorServiceProvider.test.ts +47 -0
  151. package/tests/service/treePrinter/DefaultTreePrinterService.test.ts +144 -0
  152. package/tests/service/treePrinter/TreePrinterServiceProvider.test.ts +55 -0
  153. package/tests/terminal/ImageRenderer.test.ts +161 -0
  154. package/tests/terminal/PngDecoder.test.ts +223 -0
  155. package/tests/terminal/supportsHyperlinks.test.ts +149 -0
  156. package/tests/util/configHelper.test.ts +4 -4
  157. package/tests/util/logger.test.ts +23 -1
  158. package/tsconfig.json +31 -0
  159. package/src/service/printer/terminal/Ansi.ts +0 -10
  160. package/src/service/printer/terminal/TtyStyler.ts +0 -39
@@ -0,0 +1,751 @@
1
+ # Concepts
2
+
3
+ The key concepts are:
4
+
5
+ - Specific command functionality is implemented within one or more `Command`
6
+ instances.
7
+ - Generic support functionality is implemented within or or more `Service`
8
+ instances.
9
+ - The `CLI` is responsible for:
10
+ - maintaining a `CommandRegistry` and ensuring the `Command` instances it has
11
+ registered are available when scanning and parsing arguments and executing
12
+ specified commands.
13
+ - maintaining a `ServiceProviderRegistry` and ensuring that services provided
14
+ by each of the registered `ServiceProvider` instances are available for use
15
+ by any invoked `Command` via a a `Context`.
16
+ - Dynamic plugins (enabled by
17
+ [dynamic-plugin-framework](https://github.com/flowscripter/dynamic-plugin-framework))
18
+ allow:
19
+ - dynamic load and import of one or more `CommandFactory` implementations
20
+ providing one or more `Command` implementations.
21
+ - dynamic load and import of one or more `ServiceProviderFactory`
22
+ implementations providing one or more `ServiceProvider` implementations.
23
+
24
+ The following high-level conceptual diagram illustrates these relationships:
25
+
26
+ ```mermaid
27
+ classDiagram
28
+ class CLI {
29
+ <<interface>>
30
+ }
31
+
32
+ class CLIConfig {
33
+ }
34
+
35
+ class Command {
36
+ <<interface>>
37
+ }
38
+
39
+ class ServiceProvider {
40
+ <<interface>>
41
+ serviceId
42
+ servicePriority
43
+ }
44
+
45
+ class Context {
46
+ <<interface>>
47
+ }
48
+
49
+ class CommandRegistry {
50
+ <<interface>>
51
+ }
52
+
53
+ class ServiceProviderRegistry {
54
+ <<interface>>
55
+ }
56
+
57
+ class Service {
58
+ <<interface>>
59
+ }
60
+
61
+ CLI --> CLIConfig
62
+
63
+ CLI --> Context
64
+
65
+ CLI --> CommandRegistry
66
+
67
+ CommandRegistry --> "*" Command : registers
68
+
69
+ ServiceProviderRegistry --> "*" ServiceProvider : registers
70
+
71
+ CLI --> "1..*" Command : executes
72
+
73
+ Command --> Context : executed within
74
+
75
+ Context --> CLIConfig
76
+
77
+ Context --> "*" Service : access to
78
+
79
+ ServiceProvider --> "*" Service : provides
80
+
81
+ class PluginManager {
82
+ }
83
+
84
+ class Plugin {
85
+ <<interface>>
86
+ }
87
+
88
+ CLI --> PluginManager
89
+
90
+ class CommandFactory {
91
+ <<interface>>
92
+ }
93
+
94
+ class ServiceProviderFactory {
95
+ <<interface>>
96
+ }
97
+
98
+ CLI-->ServiceProviderRegistry
99
+
100
+ PluginManager --> "*" Plugin : registers
101
+
102
+ Plugin --> "*" CommandFactory: implements
103
+
104
+ Plugin --> "*" ServiceProviderFactory: implements
105
+
106
+ CommandFactory --> "*" Command : provides
107
+
108
+ ServiceProviderFactory --> "*" ServiceProvider : provides
109
+ ```
110
+
111
+ ## Commands
112
+
113
+ A `Command` declares:
114
+
115
+ - a `name` which is to be used in command line arguments to invoke it.
116
+ - a function `execute()` which is invoked if the command is specified. This
117
+ function is invoked with the parsed arguments and a context.
118
+ - an optional declaration of arguments it supports.
119
+
120
+ The sub-types of command are: `GlobalCommand`, `GlobalModifierCommand`,
121
+ `SubCommand` and `GroupCommand`.
122
+
123
+ The following high-level conceptual diagram illustrates the Command API:
124
+
125
+ ```mermaid
126
+ classDiagram
127
+ class Command {
128
+ <<interface>>
129
+ name
130
+ description
131
+ enableConfiguration
132
+ }
133
+
134
+ class SubCommand {
135
+ <<interface>>
136
+ execute()
137
+ }
138
+
139
+ class GroupCommand {
140
+ <<interface>>
141
+ execute()
142
+ }
143
+
144
+ class GlobalCommand {
145
+ <<interface>>
146
+ shortAlias
147
+ execute()
148
+ }
149
+
150
+ class GlobalModifierCommand {
151
+ <<interface>>
152
+ executePriority
153
+ }
154
+
155
+ class Argument {
156
+ <<interface>>
157
+ type
158
+ allowableValues
159
+ configurationKey
160
+ }
161
+
162
+ class SubCommandArgument {
163
+ name
164
+ description
165
+ <<interface>>
166
+ }
167
+
168
+ class Option {
169
+ <<interface>>
170
+ shortAlias
171
+ defaultValue
172
+ isOptional
173
+ isArray
174
+ }
175
+
176
+ class Positional {
177
+ <<interface>>
178
+ isVarArgMultiple
179
+ isVarArgOptional
180
+ }
181
+
182
+ class ComplexOption {
183
+ <<interface>>
184
+ }
185
+
186
+ class GlobalCommandArgument {
187
+ <<interface>>
188
+ defaultValue
189
+ isOptional
190
+ }
191
+
192
+ class UsageExample {
193
+ description
194
+ exampleArguments
195
+ output
196
+ }
197
+
198
+ Argument <|-- SubCommandArgument
199
+
200
+ Argument <|-- GlobalCommandArgument
201
+
202
+ SubCommandArgument <|-- Option
203
+
204
+ SubCommandArgument <|-- Positional
205
+
206
+ Option <|-- ComplexOption
207
+
208
+ Option "1..*" <-- ComplexOption : properties
209
+
210
+ Command <|-- GroupCommand
211
+
212
+ Option "0..*" <-- SubCommand
213
+
214
+ Positional "0..*" <-- SubCommand
215
+
216
+ GlobalCommand --> "0..1" GlobalCommandArgument
217
+
218
+ Command <|-- SubCommand
219
+
220
+ GlobalCommand <|-- GlobalModifierCommand
221
+
222
+ Command <|-- GlobalCommand
223
+
224
+ GroupCommand --> "1..*" SubCommand : memberSubCommands
225
+
226
+ SubCommand --> "0..*" UsageExample
227
+ ```
228
+
229
+ ### Global Command
230
+
231
+ A `GlobalCommand` provides the ability to invoke functionality via a global
232
+ argument and one optional value:
233
+
234
+ executable --<global_command>[=<value>]
235
+
236
+ A concrete example:
237
+
238
+ myNetworkApp --help=connect
239
+
240
+ A `GlobalCommand` also supports a short character alias:
241
+
242
+ executable -<global_command_short_alias>[=<value>]
243
+
244
+ A concrete example:
245
+
246
+ myNetworkApp -h=connect
247
+
248
+ ### Global Modifier Commands
249
+
250
+ Any number of `GlobalModifierCommand` instances can be specified as long as they
251
+ are accompanied by a `GlobalCommand` or a `SubCommand`:
252
+
253
+ executable --<global_modifier_command_1>[=<global_modifier_command_1_argument>] \
254
+ --<global_modifier_command_2>[=<global_modifier_command_2_argument>] \
255
+ <global_command>[=<value>]
256
+
257
+ Each `GlobalModifierCommand` will be executed before the single specified
258
+ `GlobalCommand` or `SubCommand` is executed. This behaviour allows
259
+ `GlobalModifierCommands` to modify the context in which later commands execute.
260
+
261
+ A `GlobalModifierCommand` defines an "execution priority" which is used to
262
+ determine the order of execution when multiple `GlobalModifierCommands` are
263
+ specified.
264
+
265
+ A concrete example:
266
+
267
+ myNetworkApp --log-level=DEBUG --config=./config.json --help
268
+
269
+ where:
270
+
271
+ - `log-level` is a `GlobalModifierCommand` with a value of `DEBUG`
272
+ - `config` is a `GlobalModifierCommand` with a value of `./config.json`
273
+ - `help` is a `GlobalCommand`.
274
+
275
+ ### Sub-Command
276
+
277
+ A `SubCommand` provides the ability to invoke functionality via specifying the
278
+ command name followed by any number of option and positional arguments.
279
+
280
+ A sub-command is invoked as follows:
281
+
282
+ executable <sub_command> [sub_command_arguments]
283
+
284
+ A concrete example:
285
+
286
+ myNetworkApp serve --host=localhost
287
+
288
+ ### Group Command
289
+
290
+ A `GroupCommand` allows multiple member `SubCommand` instances to be grouped
291
+ under a single named group. The name of the `GroupCommand` is specified before
292
+ the desired member `SubCommand` in one of two ways:
293
+
294
+ executable <group_command> <member_sub_command> [member_sub_command_arguments]
295
+ executable <group_command>:<member_sub_command> [member_sub_command_arguments]
296
+
297
+ Some concrete examples:
298
+
299
+ myNetworkApp utils ping --host=localhost
300
+ myNetworkApp utils:ping --host=localhost
301
+
302
+ **NOTE**: A `GroupCommand` also provides for it's own logic to be invoked BEFORE
303
+ the specified sub-command. However, a `GroupCommand` does not support any
304
+ arguments itself.
305
+
306
+ ## Global Command and Global Modifier Command Arguments
307
+
308
+ `GlobalCommand` and `GlobalModifierCommand` instances support the definition of
309
+ a single `GlobalCommandArgument` consisting of:
310
+
311
+ - a type: `NUMBER`, `INTEGER`, `BOOLEAN`, `STRING` or `SECRET`.
312
+ - an optional set of allowable values.
313
+ - an optional default value.
314
+ - whether the value is mandatory.
315
+
316
+ There are four ways in which these argument values can be specified:
317
+
318
+ executable --<global_command>=<value>
319
+ executable --<global_command> <value>
320
+ executable -<global_command_short_alias>=<value>
321
+ executable -<global_command_short_alias> <value>
322
+
323
+ Some concrete examples:
324
+
325
+ myNetworkApp --help=connect
326
+ myNetworkApp --help connect
327
+ myNetworkApp -h=connect
328
+ myNetworkApp -h connect
329
+
330
+ #### Boolean Values
331
+
332
+ For boolean options, specifying the value as `true` is not required. All of the
333
+ following set the value to `true`:
334
+
335
+ executable --<global_command>=true
336
+ executable --<global_command> true
337
+ executable --<global_command>
338
+ executable -<global_command_short_alias>=true
339
+ executable -<global_command_short_alias> true
340
+ executable -<global_command_short_alias>
341
+
342
+ ## Sub-Command Arguments
343
+
344
+ Arguments for a `SubCommand` can take two forms: `Option` or `Positional`.
345
+
346
+ Common to both are the following features:
347
+
348
+ - a name which must consist of alphanumeric non-whitespace ASCII characters or
349
+ `_` and `-` characters. It cannot start with `-`.
350
+ - a type: `NUMBER`, `INTEGER`, `BOOLEAN`, `STRING` or `SECRET`.
351
+ - an optional set of valid value choices.
352
+
353
+ ### Options
354
+
355
+ An `option` argument also provides for:
356
+
357
+ - a short character alias for the option.
358
+ - whether the option is mandatory.
359
+ - an optional default value.
360
+ - whether the option can be specified more than once (i.e. it is an array value)
361
+ - an additional type: `COMPLEX` which allows for nested arguments
362
+
363
+ There are four ways in which options can be specified:
364
+
365
+ executable <sub_command> --<option_name>=<value>
366
+ executable <sub_command> --<option_name> <value>
367
+ executable <sub_command> -<option_short_alias>=<value>
368
+ executable <sub_command> -<option_short_alias> <value>
369
+
370
+ #### Boolean Values
371
+
372
+ For boolean options, specifying the value as `true` is not required. All of the
373
+ following set the value to `true`:
374
+
375
+ executable <sub_command> --<boolean_option_name>=true
376
+ executable <sub_command> --<boolean_option_name> true
377
+ executable <sub_command> --<boolean_option_name>
378
+ executable <sub_command> -<boolean_option_short_alias>=true
379
+ executable <sub_command> -<boolean_option_short_alias> true
380
+ executable <sub_command> -<boolean_option_short_alias>
381
+
382
+ #### Array Options
383
+
384
+ For array options, multiple values can be specified using explicit indices:
385
+
386
+ executable <sub_command> --<array_option_name>[index]=value --<array_option_name>[index]=value
387
+
388
+ or with implicit indices:
389
+
390
+ executable <sub_command> --<array_option_name>=value --<array_option_name>=value
391
+
392
+ Some concrete examples:
393
+
394
+ myNetworkApp --bind --interface[0]=eth0 --interface[1]=eth1
395
+ myNetworkApp --bind --interface=eth0 --interface=eth1
396
+
397
+ **NOTE**: When using explicit indices, the indices may be specified in any
398
+ order, however specifying a sparse array is not supported:
399
+
400
+ myNetworkApp --bind --interface[2]=eth0 --interface[1]=eth1 --interface[0]=eth2
401
+ myNetworkApp --bind --interface[1]=eth0 --interface[2]=eth1 // invalid as index 0 is not specified
402
+
403
+ **NOTE**: Arrays of arrays are not supported:
404
+
405
+ myNetworkApp --bind --interfaces[0][0]=eth0 // invalid as multi-dimensional arrays are not supported
406
+
407
+ #### Complex Options
408
+
409
+ For complex options, the path to the desired property is specified using a `.`
410
+ separator:
411
+
412
+ executable <sub_command> --<parent_option_name>.<property_name>=<value> --<parent_option_name>.<property_name>.<sub-property-name>=<value>
413
+ executable <sub_command> --<parent_option_name>.<property_short_alias>=<value> --<parent_option_short_alias>.<property_name>.<sub-property-name>=<value>
414
+
415
+ Mixed use of option names and short aliases is supported when specifying nested
416
+ complex option properties. If the property specification starts with the root
417
+ options's name then `--` must be used; whereas if it starts with the root
418
+ option's short alias then `-` must be used. The following are all equivalent:
419
+
420
+ --alpha.beta.gamma=1
421
+ --alpha.b.gamma=1
422
+ --alpha.b.g=1
423
+ -a.beta.gamma=1
424
+ -a.beta.g=1
425
+ -a.b.g=1
426
+
427
+ Implicit and explicit array indexing is supported when specifying nested complex
428
+ option properties. However the implicit indexing is only applied to the last
429
+ nested property reference. The following are equivalent:
430
+
431
+ --foo.bar=1 --foo.bar=2
432
+ --foo.bar[0]=1 --foo.bar[1]=2
433
+
434
+ If arrays of complex options need to be referenced then explicit indexing is
435
+ required. As an example:
436
+
437
+ --foo[0].bar=1 --foo[1].bar=2
438
+
439
+ Some concrete examples:
440
+
441
+ myNetworkApp --connect --address.host=127.0.0.1 --address.port=8080
442
+ myNetworkApp --poll --address[0].host=127.0.0.1 --address[0].port=8080 --address[1].host=10.0.10.1 --address[1].port=443
443
+
444
+ ### Positionals
445
+
446
+ A `positional` argument is specified by a value which appears at the correct
447
+ position in the list of `SubCommand` arguments:
448
+
449
+ executable <sub_command_name> <positional_1_value> <positional_2_value>
450
+
451
+ A concrete example:
452
+
453
+ myHelloWorldApp say hello
454
+
455
+ where:
456
+
457
+ - `say` is a sub-command.
458
+ - `hello` is the value for the first positional argument.
459
+
460
+ #### Varargs Positionals
461
+
462
+ A `positional` argument also provides for "varargs" support (both optional and
463
+ multiple) which allows for zero, one or more entries:
464
+
465
+ **NOTE**: Only one "varargs" `positional` can be defined and it must be the last
466
+ positional expected for the command.
467
+
468
+ If "varargs" optional is set for `positional_1`, these are both valid:
469
+
470
+ executable <sub_command>
471
+ executable <sub_command> <positional_1_value_1>
472
+
473
+ If "varargs" multiple is set for `positional_1`, these are both valid:
474
+
475
+ executable <sub_command> <positional_1_value_1>
476
+ executable <sub_command> <positional_1_value_1> <positional_1_value_2>
477
+
478
+ If "varargs" optional AND multiple is set for `positional_1`, these are all
479
+ valid:
480
+
481
+ executable <sub_command>
482
+ executable <sub_command> <positional_1_value_1>
483
+ executable <sub_command> <positional_1_value_1> <positional_1_value_2> <positional_1_value_3>
484
+
485
+ ## Argument Value Types
486
+
487
+ The supported value types which can be specified for an `Option`, a `Positional`
488
+ or `GlobalCommandArgument` are:
489
+
490
+ - `STRING` - a string value where whitespace can be included by using double
491
+ quotes e.g. `myHelloWorldApp say "hello world"`
492
+ - `NUMBER` - any number value such as `0.01` or `-10`
493
+ - `INTEGER` - any positive or negative integer value. Note that these values
494
+ will still be stored as a JavaScript number and specification as `INTEGER` is
495
+ only used for validation when parsing arguments.
496
+ - `BOOLEAN` - string values of `true`, `TRUE`, `false` and `FALSE` are converted
497
+ to a JavaScript boolean value. Specifying just the argument name is also
498
+ sufficient to indicate a value of `true`.
499
+ - `SECRET` - a string value which may be specified as an argument (not
500
+ recommended), provided as a response to an argument value prompt, provided as
501
+ a default value in a configuration file (using native OS secret storage
502
+ functionality provided in the ConfigurationService) or sourced from an
503
+ environment variable.
504
+
505
+ ## Default Argument Values
506
+
507
+ Support for reading default argument values from a configuration file and/or
508
+ environment variables is provided by `ConfigurationServiceProvider`.
509
+
510
+ Usage of configured default values for each `Command` is only enabled if
511
+ `Command.enableConfiguration` is `true`.
512
+
513
+ ### Configuration File
514
+
515
+ Default values can be stored in a JSON file which contains a top level
516
+ `defaults` property. The second level of properties under `defaults` is used to
517
+ refer to each `Command` by `Command.name` and the contained values are treated
518
+ as command argument values. As an example:
519
+
520
+ ```
521
+ {
522
+ "defaults": {
523
+ "subCommand1": {
524
+ "arg1": [
525
+ 1,
526
+ 2
527
+ ],
528
+ "arg2": {
529
+ "arg3": "foo"
530
+ }
531
+ },
532
+ "subCommand2": {
533
+ "arg4": true
534
+ },
535
+ "globalCommand": "globalArgumentValue"
536
+ }
537
+ }
538
+ ```
539
+
540
+ The default location of the configuration file is
541
+ `$HOME/.<application_name>.json`. If `$HOME` is not defined then no default
542
+ configuration will be used. The location of the configuration file can be
543
+ modified via the `ConfigCommand` global modifier command.
544
+
545
+ As a concrete example, the following command line:
546
+
547
+ `myNetworkApp --connect --address.host=127.0.0.1 --address.port=8080`
548
+
549
+ is equivalent to specifying default values for the `address.host` and
550
+ `address.port` arguments in the configuration file `$HOME/.myNetworkApp.json`:
551
+
552
+ ```
553
+ {
554
+ "defaults": {
555
+ "connect": {
556
+ "address.host": "127.0.0.1",
557
+ "address.port": 8080
558
+ }
559
+ }
560
+ }
561
+ ```
562
+
563
+ and using the command line:
564
+
565
+ `myNetworkApp --connect`
566
+
567
+ These defaults can then be overridden on the command line:
568
+
569
+ `myNetworkApp --connect --address.host=192.168.1.1`
570
+
571
+ ### Environment Variables
572
+
573
+ Default values are also sourced from environment variable values. Any values set
574
+ by environment variables will override those sourced from a configuration file.
575
+ Any values set on the command line override environment variable and
576
+ configuration file values.
577
+
578
+ The optional `Argument.configurationKey` value is a configuration key to use for
579
+ the argument. It must consist of alphanumeric non-whitespace uppercase ASCII or
580
+ `_` characters and must not start with a digit.
581
+
582
+ If not specified a default configuration key is determined as follows:
583
+
584
+ The `Argument.name` is capitalized and any `-` characters are replaced with `_`
585
+ characters. If the result starts with a digit, it is prefixed with `_`. Some
586
+ examples:
587
+
588
+ - name: `FooBar` => default configuration key: `FOOBAR`
589
+ - name: `Hello-World-` => default configuration key: `HELLO_WORLD_`
590
+ - name: `3` => default configuration key: `_3`
591
+
592
+ **NOTE**: Regardless of whether a `configurationKey` is specified, or the
593
+ default is relied upon, it will only be used if the parent `Command` has
594
+ `Command.enableConfiguration` specified as `true`.
595
+
596
+ The full key for an argument (or a nested option in a complex option) is
597
+ determined as follows:
598
+
599
+ - Nested argument configuration keys are concatenated with a `_` separator.
600
+ - Any arguments which support array values must by suffixed with `_` and an
601
+ explicit array index.
602
+ - If the root argument in the path does not use a custom
603
+ `Argument.configurationKey` then the key path is additionally suffixed with
604
+ the `CLIConfig.name` and the `Command.name` with `_` separators.
605
+
606
+ This is best explained with examples...
607
+
608
+ Examples for no custom configuration key:
609
+
610
+ - executable: `MyCLI`, command: `command1`, simple argument: `arg1` =>
611
+ environment variable: `MYCLI_COMMAND1_ARG1`
612
+ - executable: `MyCLI`, command: `command1`, array argument referring to the 1st
613
+ element: `arg2[0]` => environment variable: `MYCLI_COMMAND1_ARG2_0`
614
+ - executable: `MyCLI`, command: `command1`, argument name: `3` (this is a digit
615
+ so it is by default suffixed with `_`) => environment variable:
616
+ `MYCLI_COMMAND1__3`
617
+ - executable: `MyCLI`, command: `command1`, nested option: `arg1.arg2` =>
618
+ environment variable: `MYCLI_COMMAND1_ARG1_ARG2`
619
+ - executable: `MyCLI`, command: `command1`, nested option with both levels being
620
+ arrays and referring to the 2nd element of each: `arg1[1].arg2[1]` =>
621
+ environment variable: `MYCLI_COMMAND1_ARG1_1_ARG2_1`
622
+
623
+ Examples for custom configuration key at the root level (and therefore not
624
+ prefixed with CLI and command names):
625
+
626
+ - executable: `MyCLI`, command: `command1`, simple argument: `arg1` with
627
+ configuration key: `FOO` => environment variable: `FOO`
628
+ - executable: `MyCLI`, command: `command1`, array argument referring to the 1st
629
+ element: `arg2[0]` and with configuration key: `BAR` => environment variable:
630
+ `BAR_0`
631
+
632
+ Examples for custom configuration key NOT at the root level (and therefore
633
+ prefixed with CLI and command names) examples:
634
+
635
+ - executable: `MyCLI`, command: `command1`, nested option: `arg1.arg2` with
636
+ `arg2` configuration key: `FOO` => environment variable:
637
+ `MYCLI_COMMAND1_ARG1_FOO`
638
+ - executable: `MyCLI`, command: `command1`, nested option with both levels being
639
+ arrays and referring to the 2nd element of each: `arg1[1].arg2[1]` and with
640
+ arg2 configuration key: `BAR` => environment variable:
641
+ `MYCLI_COMMAND1_ARG1_1_BAR_1`
642
+
643
+ As a concrete example, the following command line:
644
+
645
+ `myNetworkApp --connect --address.host=127.0.0.1 --address.port=8080`
646
+
647
+ is equivalent to defining the following environment variables:
648
+
649
+ - `MYNETWORKAPP_CONNECT_ADDRESS_HOST=127.0.0.1`
650
+ - `MYNETWORKAPP_CONNECT_ADDRESS_PORT=8080`
651
+
652
+ and using the command line:
653
+
654
+ `myNetworkApp --connect`
655
+
656
+ **NOTE**: For boolean values, defining the environment variable with ANY value
657
+ is equivalent to setting it to `true`. All of the following set the value to
658
+ `true`:
659
+
660
+ NO_COLOR=1
661
+ NO_COLOR=true
662
+ NO_COLOR=false // sets the value to true as the environment variable has a value set!
663
+
664
+ To specify the value as `false`, set the environment variable to be an empty
665
+ string e.g.
666
+
667
+ NO_COLOR= // sets the value to false
668
+
669
+ ### Configured Value Merging
670
+
671
+ Any configured values are merged with parsed values before being validated based
672
+ on their associated argument definitions.
673
+
674
+ The following logic is applied during merging of configured and parsed values:
675
+
676
+ **Configured primitive values are used unless overridden by parsed values**
677
+
678
+ Example:
679
+
680
+ configured: { "foo": "bar" }
681
+ parsed: undefined
682
+ result: { "foo": "bar1" }
683
+
684
+ Example:
685
+
686
+ configured: { "foo": "bar" }
687
+ parsed: { "foo": "bar1" }
688
+ result: { "foo": "bar1" }
689
+
690
+ **Configured complex values are merged (union of unique property names) or
691
+ overridden (replacement of duplicate property names) by parsed values**
692
+
693
+ Example:
694
+
695
+ configured: { "foo": { "a": 1, "b": 2 } }
696
+ parsed: { "foo": { "a": 3, "c": 4 } }
697
+ result: { "foo": { "a": 1, "b": 2, "c": 4 } }
698
+
699
+ **Configured array values are merged (for unique indices) or overridden
700
+ (replacement of duplicate indices) by parsed values**
701
+
702
+ Example:
703
+
704
+ configured: { "foo": [ 0, 1, 2, 3 ] }
705
+ parsed: { "foo": [ 0, 4, 2 ] }
706
+ result: { "foo": [ 0, 4, 2, 3 ] }
707
+
708
+ Example:
709
+
710
+ configured: { "foo": [ 0, 1, 2, 3 ] }
711
+ parsed: { "foo": [ 0, undefined, 2 ] }
712
+ result: { "foo": [ 0, 4, 2, 3 ] }
713
+
714
+ Example:
715
+
716
+ configured: { "foo": [ { "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 } ] }
717
+ parsed: { "foo": [ { "a": 5 }, undefined, { "a": 6, "b": 7 } ] }
718
+ result: { "foo": [ { "a": 5 }, { "a": 2 }, { "a": 6, "b": 7 }, { "a": 4 } ] }
719
+
720
+ ## Argument Value Validation
721
+
722
+ After parsing of specified arguments, merging with configured defaults and
723
+ assigning values to relevant command arguments, values are validated based on
724
+ the argument definitions.
725
+
726
+ The following scenarios produce validation errors:
727
+
728
+ - **Missing Value**: If an argument is not optional and no value has been
729
+ provided.
730
+ - **Incorrect Value Type**: The value specified was not the correct type for the
731
+ argument. e.g. `foo` cannot be provided for a boolean argument, nor for a
732
+ complex object argument.
733
+ - **Illegal Multiple Values**: The argument does not support multiple values but
734
+ multiple values have been provided.
735
+ - **Illegal Value**: If the argument defines possible allowable values and the
736
+ value provided is not one of these.
737
+ - **Illegal Sparse Array**: If multiple values were specified using array
738
+ indices and this resulted in empty entries in the array of values.
739
+ - **Unknown Property**: If the value provided is for a property which is not
740
+ defined on a complex object argument.
741
+ - **Nesting Depth Exceeded**: If there is an attempt specify a complex option
742
+ property at a nesting depth more than the maximum (10).
743
+ - **Array Size Exceeded**: If there is an attempt set more than the maximum
744
+ (255) number of values for an array option.
745
+ - **Option Is Complex**: If the argument attempts to set a value on a complex
746
+ option rather than on a primitive option or a primitive property of a complex
747
+ option.
748
+ - **Custom Validation**: If the argument defines a custom `validate` function
749
+ and the value (after passing all built-in validations) fails the custom
750
+ validation check. The error message provided by the custom validator is
751
+ included in the error output.