@flowscripter/dynamic-cli-framework 1.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.
Files changed (133) hide show
  1. package/.github/workflows/check-bun-dependencies.yml +9 -0
  2. package/.github/workflows/lint-pr-message.yml +11 -0
  3. package/.github/workflows/release-bun-library.yml +8 -0
  4. package/.github/workflows/validate-bun-library-pr.yml +8 -0
  5. package/LICENSE +21 -0
  6. package/README.md +1475 -0
  7. package/bun.lock +63 -0
  8. package/index.ts +89 -0
  9. package/package.json +23 -0
  10. package/src/api/CLI.ts +17 -0
  11. package/src/api/CLIConfig.ts +19 -0
  12. package/src/api/Context.ts +25 -0
  13. package/src/api/RunResult.ts +142 -0
  14. package/src/api/argument/Argument.ts +36 -0
  15. package/src/api/argument/ArgumentValueTypes.ts +87 -0
  16. package/src/api/argument/ComplexOption.ts +31 -0
  17. package/src/api/argument/GlobalCommandArgument.ts +17 -0
  18. package/src/api/argument/Option.ts +32 -0
  19. package/src/api/argument/Positional.ts +27 -0
  20. package/src/api/argument/SubCommandArgument.ts +20 -0
  21. package/src/api/command/Command.ts +22 -0
  22. package/src/api/command/GlobalCommand.ts +33 -0
  23. package/src/api/command/GlobalModifierCommand.ts +12 -0
  24. package/src/api/command/GroupCommand.ts +20 -0
  25. package/src/api/command/SubCommand.ts +41 -0
  26. package/src/api/command/UsageExample.ts +21 -0
  27. package/src/api/service/ServiceProvider.ts +52 -0
  28. package/src/api/service/core/AsciiBannerGeneratorService.ts +35 -0
  29. package/src/api/service/core/KeyValueService.ts +28 -0
  30. package/src/api/service/core/PrinterService.ts +266 -0
  31. package/src/api/service/core/ShutdownService.ts +12 -0
  32. package/src/api/service/core/SyntaxHighlighterService.ts +42 -0
  33. package/src/cli/BaseCLI.ts +312 -0
  34. package/src/cli/DefaultRuntimeCLI.ts +54 -0
  35. package/src/command/MultiCommandCliHelpCommand.ts +394 -0
  36. package/src/command/SingleCommandCliHelpCommand.ts +183 -0
  37. package/src/command/UsageCommand.ts +35 -0
  38. package/src/command/VersionCommand.ts +22 -0
  39. package/src/launcher.ts +127 -0
  40. package/src/runtime/DefaultContext.ts +32 -0
  41. package/src/runtime/argument/ArgumentTypeGuards.ts +8 -0
  42. package/src/runtime/command/CommandTypeGuards.ts +30 -0
  43. package/src/runtime/command/CommandValidator.ts +549 -0
  44. package/src/runtime/parser.ts +219 -0
  45. package/src/runtime/registry/CommandRegistry.ts +121 -0
  46. package/src/runtime/registry/DefaultCommandRegistry.ts +318 -0
  47. package/src/runtime/registry/DefaultServiceProviderRegistry.ts +26 -0
  48. package/src/runtime/registry/ServiceProviderRegistry.ts +11 -0
  49. package/src/runtime/runner.ts +697 -0
  50. package/src/runtime/scanner.ts +417 -0
  51. package/src/runtime/values/ValuePopulationResult.ts +41 -0
  52. package/src/runtime/values/argumentValueMerge.ts +201 -0
  53. package/src/runtime/values/argumentValueValidation.ts +610 -0
  54. package/src/runtime/values/globalCommandValuePopulation.ts +96 -0
  55. package/src/runtime/values/subCommandValuePopulation.ts +680 -0
  56. package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +45 -0
  57. package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +38 -0
  58. package/src/service/asciiBannerGenerator/standard.flf.json +3 -0
  59. package/src/service/banner/BannerServiceProvider.ts +99 -0
  60. package/src/service/banner/command/NoBannerCommand.ts +42 -0
  61. package/src/service/configuration/ConfigurationServiceProvider.ts +571 -0
  62. package/src/service/configuration/DefaultKeyValueService.ts +60 -0
  63. package/src/service/configuration/command/ConfigCommand.ts +43 -0
  64. package/src/service/configuration/command/DumpConfigCommand.ts +47 -0
  65. package/src/service/printer/DefaultPrinterService.ts +256 -0
  66. package/src/service/printer/PrinterServiceProvider.ts +61 -0
  67. package/src/service/printer/command/DarkModeCommand.ts +43 -0
  68. package/src/service/printer/command/LogLevelCommand.ts +68 -0
  69. package/src/service/printer/command/NoColorCommand.ts +43 -0
  70. package/src/service/printer/terminal/Ansi.ts +10 -0
  71. package/src/service/printer/terminal/Color.ts +14 -0
  72. package/src/service/printer/terminal/Progress.ts +373 -0
  73. package/src/service/printer/terminal/Spinner.ts +100 -0
  74. package/src/service/printer/terminal/Styler.ts +5 -0
  75. package/src/service/printer/terminal/Terminal.ts +8 -0
  76. package/src/service/printer/terminal/Theme.ts +34 -0
  77. package/src/service/printer/terminal/TtyStyler.ts +39 -0
  78. package/src/service/printer/terminal/TtyTerminal.ts +65 -0
  79. package/src/service/shutdown/DefaultShutdownService.ts +9 -0
  80. package/src/service/shutdown/ShutdownServiceProvider.ts +55 -0
  81. package/src/service/syntaxHighlighter/DefaultSyntaxHighlighterService.ts +52 -0
  82. package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +51 -0
  83. package/src/util/configHelper.ts +95 -0
  84. package/src/util/envVarHelper.ts +338 -0
  85. package/src/util/helpHelper.ts +751 -0
  86. package/src/util/logger.ts +84 -0
  87. package/src/util/runnerHelper.ts +112 -0
  88. package/tests/cli/BaseCLI.test.ts +344 -0
  89. package/tests/command/MultiCommandCliHelpCommand.test.ts +650 -0
  90. package/tests/command/SingleCommandCliHelpCommand.test.ts +384 -0
  91. package/tests/command/UsageCommand.test.ts +53 -0
  92. package/tests/command/VersionCommand.test.ts +36 -0
  93. package/tests/fixtures/CLIConfig.ts +9 -0
  94. package/tests/fixtures/Command.ts +229 -0
  95. package/tests/fixtures/CommandRegistry.ts +8 -0
  96. package/tests/fixtures/ConfigurationServiceProvider.ts +28 -0
  97. package/tests/fixtures/Context.ts +33 -0
  98. package/tests/fixtures/ServiceProviderRegistry.ts +16 -0
  99. package/tests/fixtures/StreamString.ts +37 -0
  100. package/tests/fixtures/util.ts +41 -0
  101. package/tests/runtime/DefaultContext.test.ts +20 -0
  102. package/tests/runtime/argument/ArgumentTypeGuards.test.ts +49 -0
  103. package/tests/runtime/command/CommandTypeGuards.test.ts +43 -0
  104. package/tests/runtime/command/CommandValidator.test.ts +816 -0
  105. package/tests/runtime/parser.test.ts +766 -0
  106. package/tests/runtime/registry/DefaultCommandRegistry.test.ts +166 -0
  107. package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +42 -0
  108. package/tests/runtime/runner.test.ts +1298 -0
  109. package/tests/runtime/scanner.test.ts +1113 -0
  110. package/tests/runtime/values/argumentValueMerge.test.ts +314 -0
  111. package/tests/runtime/values/argumentValueValidation.test.ts +1020 -0
  112. package/tests/runtime/values/globalCommandValuePopulation.test.ts +662 -0
  113. package/tests/runtime/values/subCommandValuePopulation.test.ts +2658 -0
  114. package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +22 -0
  115. package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +70 -0
  116. package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +22 -0
  117. package/tests/service/asciiBannerGenerator/small.flf.json +3 -0
  118. package/tests/service/configuration/ConfigurationServiceProvider.test.ts +249 -0
  119. package/tests/service/configuration/DefaultKeyValueService.test.ts +65 -0
  120. package/tests/service/launchBanner/BannerServiceProvider.test.ts +49 -0
  121. package/tests/service/launchBanner/__snapshots__/BannerServiceProvider.test.ts.snap +3 -0
  122. package/tests/service/launchBanner/command/NoBannerCommand.test.ts +21 -0
  123. package/tests/service/printer/DefaultPrinterService.test.ts +409 -0
  124. package/tests/service/printer/PrinterServiceProvider.test.ts +29 -0
  125. package/tests/service/printer/command/LogLevelCommand.test.ts +58 -0
  126. package/tests/service/printer/terminal/Progress.test.ts +40 -0
  127. package/tests/service/printer/terminal/Spinner.test.ts +57 -0
  128. package/tests/service/shutdown/ShutdownServiceProvider.test.ts +13 -0
  129. package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +123 -0
  130. package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +41 -0
  131. package/tests/util/configHelper.test.ts +275 -0
  132. package/tests/util/envVarHelper.test.ts +318 -0
  133. package/tests/util/logger.test.ts +10 -0
@@ -0,0 +1,1020 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ validateGlobalCommandArgumentValue,
4
+ validateOptionValue,
5
+ validatePositionalValue,
6
+ } from "../../../src/runtime/values/argumentValueValidation.ts";
7
+ import {
8
+ type InvalidArgument,
9
+ InvalidArgumentReason,
10
+ } from "../../../src/api/RunResult.ts";
11
+ import { getGlobalCommandWithShortAlias } from "../../fixtures/Command.ts";
12
+ import {
13
+ ArgumentValueTypeName,
14
+ ComplexValueTypeName,
15
+ type PopulatedArgumentValues,
16
+ } from "../../../src/api/argument/ArgumentValueTypes.ts";
17
+ import type ComplexOption from "../../../src/api/argument/ComplexOption.ts";
18
+ import type Positional from "../../../src/api/argument/Positional.ts";
19
+ import type GlobalCommandArgument from "../../../src/api/argument/GlobalCommandArgument.ts";
20
+ import type Option from "../../../src/api/argument/Option.ts";
21
+
22
+ describe("argumentValueValidation Tests", () => {
23
+ test("Option types", () => {
24
+ let option: Option = {
25
+ name: "foo",
26
+ type: ArgumentValueTypeName.STRING,
27
+ };
28
+ let invalidArguments: Array<InvalidArgument> = [];
29
+ expect(validateOptionValue(option, "foo", invalidArguments)).toEqual("foo");
30
+ expect(invalidArguments).toEqual([]);
31
+
32
+ option = {
33
+ name: "foo",
34
+ type: ArgumentValueTypeName.NUMBER,
35
+ };
36
+ expect(validateOptionValue(option, "1", invalidArguments)).toEqual(1);
37
+ expect(invalidArguments).toEqual([]);
38
+
39
+ option = {
40
+ name: "foo",
41
+ type: ArgumentValueTypeName.NUMBER,
42
+ };
43
+ expect(validateOptionValue(option, "1.1", invalidArguments)).toEqual(1.1);
44
+ expect(invalidArguments).toEqual([]);
45
+
46
+ option = {
47
+ name: "foo",
48
+ type: ArgumentValueTypeName.NUMBER,
49
+ };
50
+ expect(validateOptionValue(option, "-1.1", invalidArguments)).toEqual(-1.1);
51
+ expect(invalidArguments).toEqual([]);
52
+
53
+ option = {
54
+ name: "foo",
55
+ type: ArgumentValueTypeName.INTEGER,
56
+ };
57
+ expect(validateOptionValue(option, "1", invalidArguments)).toEqual(1);
58
+ expect(invalidArguments).toEqual([]);
59
+
60
+ option = {
61
+ name: "foo",
62
+ type: ArgumentValueTypeName.INTEGER,
63
+ };
64
+ expect(validateOptionValue(option, "-1", invalidArguments)).toEqual(-1);
65
+ expect(invalidArguments).toEqual([]);
66
+
67
+ option = {
68
+ name: "foo",
69
+ type: ArgumentValueTypeName.SECRET,
70
+ };
71
+ expect(validateOptionValue(option, "xxx", invalidArguments)).toEqual("xxx");
72
+ expect(invalidArguments).toEqual([]);
73
+
74
+ option = {
75
+ name: "foo",
76
+ type: ArgumentValueTypeName.BOOLEAN,
77
+ };
78
+ expect(validateOptionValue(option, "true", invalidArguments)).toBeTrue();
79
+ expect(invalidArguments).toEqual([]);
80
+
81
+ option = {
82
+ name: "foo",
83
+ type: ArgumentValueTypeName.BOOLEAN,
84
+ };
85
+ expect(validateOptionValue(option, "TRUE", invalidArguments)).toBeTrue();
86
+ expect(invalidArguments).toEqual([]);
87
+
88
+ option = {
89
+ name: "foo",
90
+ type: ArgumentValueTypeName.BOOLEAN,
91
+ };
92
+ expect(validateOptionValue(option, "True", invalidArguments)).toBeTrue();
93
+ expect(invalidArguments).toEqual([]);
94
+
95
+ option = {
96
+ name: "foo",
97
+ type: ArgumentValueTypeName.BOOLEAN,
98
+ };
99
+ expect(validateOptionValue(option, "false", invalidArguments)).toBeFalse();
100
+ expect(invalidArguments).toEqual([]);
101
+
102
+ option = {
103
+ name: "foo",
104
+ type: ArgumentValueTypeName.BOOLEAN,
105
+ };
106
+ expect(validateOptionValue(option, "FALSE", invalidArguments)).toBeFalse();
107
+ expect(invalidArguments).toEqual([]);
108
+
109
+ option = {
110
+ name: "foo",
111
+ type: ArgumentValueTypeName.BOOLEAN,
112
+ };
113
+ expect(validateOptionValue(option, "False", invalidArguments)).toBeFalse();
114
+ expect(invalidArguments).toEqual([]);
115
+
116
+ option = {
117
+ name: "foo",
118
+ type: ArgumentValueTypeName.BOOLEAN,
119
+ };
120
+ expect(validateOptionValue(option, true, invalidArguments)).toBeTrue();
121
+ expect(invalidArguments).toEqual([]);
122
+
123
+ option = {
124
+ name: "foo",
125
+ type: ArgumentValueTypeName.BOOLEAN,
126
+ };
127
+ expect(validateOptionValue(option, false, invalidArguments)).toBeFalse();
128
+ expect(invalidArguments).toEqual([]);
129
+
130
+ option = {
131
+ name: "foo",
132
+ type: ArgumentValueTypeName.STRING,
133
+ };
134
+ expect(validateOptionValue(option, "1", invalidArguments)).toEqual("1");
135
+ expect(invalidArguments).toEqual([]);
136
+
137
+ option = {
138
+ name: "foo",
139
+ type: ArgumentValueTypeName.NUMBER,
140
+ };
141
+ expect(
142
+ validateOptionValue(option, "foo", invalidArguments),
143
+ ).toBeUndefined();
144
+ expect(invalidArguments).toEqual([{
145
+ argument: option,
146
+ name: "foo",
147
+ value: "foo",
148
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
149
+ }]);
150
+
151
+ option = {
152
+ name: "foo",
153
+ type: ArgumentValueTypeName.BOOLEAN,
154
+ };
155
+ invalidArguments = [];
156
+ expect(
157
+ validateOptionValue(option, "foo", invalidArguments),
158
+ ).toBeUndefined();
159
+ expect(invalidArguments).toEqual([{
160
+ argument: option,
161
+ name: "foo",
162
+ value: "foo",
163
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
164
+ }]);
165
+
166
+ option = {
167
+ name: "foo",
168
+ type: ArgumentValueTypeName.INTEGER,
169
+ };
170
+ invalidArguments = [];
171
+ expect(
172
+ validateOptionValue(option, "1.1", invalidArguments),
173
+ ).toBeUndefined();
174
+ expect(invalidArguments).toEqual([{
175
+ argument: option,
176
+ name: "foo",
177
+ value: "1.1",
178
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
179
+ }]);
180
+ });
181
+
182
+ test("Option array", () => {
183
+ let option: Option = {
184
+ name: "foo",
185
+ type: ArgumentValueTypeName.STRING,
186
+ isArray: true,
187
+ };
188
+ const invalidArguments: Array<InvalidArgument> = [];
189
+ expect(
190
+ validateOptionValue(option, ["foo", "bar"], invalidArguments),
191
+ ).toEqual(
192
+ [
193
+ "foo",
194
+ "bar",
195
+ ],
196
+ );
197
+ expect(invalidArguments).toEqual([]);
198
+
199
+ option = {
200
+ name: "foo",
201
+ type: ArgumentValueTypeName.NUMBER,
202
+ isArray: true,
203
+ };
204
+ expect(validateOptionValue(option, ["1", "2"], invalidArguments)).toEqual([
205
+ 1,
206
+ 2,
207
+ ]);
208
+ expect(invalidArguments).toEqual([]);
209
+
210
+ option = {
211
+ name: "foo",
212
+ type: ArgumentValueTypeName.BOOLEAN,
213
+ isArray: true,
214
+ };
215
+ expect(
216
+ validateOptionValue(option, ["true", "false"], invalidArguments),
217
+ ).toEqual(
218
+ [true, false],
219
+ );
220
+ expect(invalidArguments).toEqual([]);
221
+
222
+ option = {
223
+ name: "foo",
224
+ type: ArgumentValueTypeName.STRING,
225
+ };
226
+ expect(
227
+ validateOptionValue(option, ["true", "false"], invalidArguments),
228
+ ).toBeUndefined();
229
+ expect(invalidArguments).toEqual([{
230
+ argument: option,
231
+ name: "foo",
232
+ value: ["true", "false"],
233
+ reason: InvalidArgumentReason.ILLEGAL_MULTIPLE_VALUES,
234
+ }]);
235
+ });
236
+
237
+ test("Optional option", () => {
238
+ let option: Option = {
239
+ name: "foo",
240
+ type: ArgumentValueTypeName.STRING,
241
+ isOptional: true,
242
+ };
243
+ let invalidArguments: Array<InvalidArgument> = [];
244
+ expect(
245
+ validateOptionValue(option, undefined, invalidArguments),
246
+ ).toBeUndefined();
247
+ expect(invalidArguments).toEqual([]);
248
+
249
+ option = {
250
+ name: "foo",
251
+ type: ArgumentValueTypeName.STRING,
252
+ isArray: true,
253
+ isOptional: true,
254
+ };
255
+ expect(
256
+ validateOptionValue(option, undefined, invalidArguments),
257
+ ).toBeUndefined();
258
+ expect(invalidArguments).toEqual([]);
259
+
260
+ option = {
261
+ name: "foo",
262
+ type: ArgumentValueTypeName.STRING,
263
+ };
264
+ expect(
265
+ validateOptionValue(option, undefined, invalidArguments),
266
+ ).toBeUndefined();
267
+ expect(invalidArguments).toEqual([{
268
+ argument: option,
269
+ name: "foo",
270
+ reason: InvalidArgumentReason.MISSING_VALUE,
271
+ }]);
272
+
273
+ option = {
274
+ name: "foo",
275
+ type: ArgumentValueTypeName.STRING,
276
+ isArray: true,
277
+ };
278
+ invalidArguments = [];
279
+ expect(
280
+ validateOptionValue(option, undefined, invalidArguments),
281
+ ).toBeUndefined();
282
+ expect(invalidArguments).toEqual([{
283
+ argument: option,
284
+ name: "foo",
285
+ reason: InvalidArgumentReason.MISSING_VALUE,
286
+ }]);
287
+ });
288
+
289
+ test("Invalid option argument value", () => {
290
+ let option: Option = {
291
+ name: "foo",
292
+ type: ArgumentValueTypeName.STRING,
293
+ allowableValues: ["bar", "two"],
294
+ };
295
+ const invalidArguments: Array<InvalidArgument> = [];
296
+ expect(validateOptionValue(option, "bar", invalidArguments), "bar");
297
+ expect(invalidArguments).toEqual([]);
298
+
299
+ option = {
300
+ name: "foo",
301
+ type: ArgumentValueTypeName.STRING,
302
+ allowableValues: ["bar", "two"],
303
+ };
304
+ expect(
305
+ validateOptionValue(option, "goo", invalidArguments),
306
+ ).toBeUndefined();
307
+ expect(invalidArguments).toEqual([{
308
+ argument: option,
309
+ name: "foo",
310
+ value: "goo",
311
+ reason: InvalidArgumentReason.ILLEGAL_VALUE,
312
+ }]);
313
+ });
314
+
315
+ test("Complex options", () => {
316
+ const option: ComplexOption = {
317
+ name: "foo",
318
+ type: ComplexValueTypeName.COMPLEX,
319
+ properties: [
320
+ {
321
+ name: "a",
322
+ type: ArgumentValueTypeName.NUMBER,
323
+ },
324
+ {
325
+ name: "b",
326
+ type: ArgumentValueTypeName.NUMBER,
327
+ },
328
+ ],
329
+ };
330
+ let invalidArguments: Array<InvalidArgument> = [];
331
+ expect(
332
+ validateOptionValue(option, { a: 1, b: 2 }, invalidArguments),
333
+ ).toEqual(
334
+ { a: 1, b: 2 },
335
+ );
336
+ expect(invalidArguments).toEqual([]);
337
+
338
+ invalidArguments = [];
339
+
340
+ expect(
341
+ validateOptionValue(option, { a: "bar", b: 2 }, invalidArguments),
342
+ ).toBeUndefined();
343
+ expect(invalidArguments).toEqual([{
344
+ argument: option.properties[0],
345
+ name: "foo.a",
346
+ value: "bar",
347
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
348
+ }]);
349
+
350
+ invalidArguments = [];
351
+
352
+ expect(
353
+ validateOptionValue(option, { b: 2 }, invalidArguments),
354
+ ).toBeUndefined();
355
+ expect(invalidArguments).toEqual([{
356
+ argument: option.properties[0],
357
+ name: "foo.a",
358
+ reason: InvalidArgumentReason.MISSING_VALUE,
359
+ }]);
360
+
361
+ invalidArguments = [];
362
+
363
+ expect(
364
+ validateOptionValue(option, { a: [1, 2], b: 2 }, invalidArguments),
365
+ ).toBeUndefined();
366
+ expect(invalidArguments).toEqual([{
367
+ argument: option.properties[0],
368
+ name: "foo.a",
369
+ value: [1, 2],
370
+ reason: InvalidArgumentReason.ILLEGAL_MULTIPLE_VALUES,
371
+ }]);
372
+
373
+ invalidArguments = [];
374
+
375
+ expect(
376
+ validateOptionValue(option, "bar", invalidArguments),
377
+ ).toBeUndefined();
378
+ expect(invalidArguments).toEqual([{
379
+ argument: option,
380
+ name: "foo",
381
+ value: "bar",
382
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
383
+ }]);
384
+
385
+ invalidArguments = [];
386
+
387
+ expect(
388
+ validateOptionValue(option, { a: 1, b: { c: 3 } }, invalidArguments),
389
+ ).toBeUndefined();
390
+ expect(invalidArguments).toEqual([{
391
+ argument: option.properties[1],
392
+ name: "foo.b",
393
+ value: { c: 3 },
394
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
395
+ }]);
396
+ });
397
+
398
+ test("Option array of complex options", () => {
399
+ const option: ComplexOption = {
400
+ name: "foo",
401
+ type: ComplexValueTypeName.COMPLEX,
402
+ isArray: true,
403
+ properties: [
404
+ {
405
+ name: "a",
406
+ type: ArgumentValueTypeName.NUMBER,
407
+ },
408
+ {
409
+ name: "b",
410
+ type: ArgumentValueTypeName.NUMBER,
411
+ },
412
+ ],
413
+ };
414
+ let invalidArguments: Array<InvalidArgument> = [];
415
+ expect(
416
+ validateOptionValue(option, [{ a: 1, b: 2 }], invalidArguments),
417
+ ).toEqual(
418
+ [{ a: 1, b: 2 }],
419
+ );
420
+ expect(invalidArguments).toEqual([]);
421
+
422
+ expect(
423
+ validateOptionValue(
424
+ option,
425
+ [{ a: 1, b: 2 }, { a: 3, b: 4 }],
426
+ invalidArguments,
427
+ ),
428
+ ).toEqual(
429
+ [{ a: 1, b: 2 }, { a: 3, b: 4 }],
430
+ );
431
+ expect(invalidArguments).toEqual([]);
432
+
433
+ invalidArguments = [];
434
+
435
+ expect(
436
+ validateOptionValue(
437
+ option,
438
+ [{ a: 1, b: 2 }, { a: "bar", b: 4 }],
439
+ invalidArguments,
440
+ ),
441
+ ).toBeUndefined();
442
+ expect(invalidArguments).toEqual([{
443
+ argument: option.properties[0],
444
+ name: "foo[1].a",
445
+ value: "bar",
446
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
447
+ }]);
448
+
449
+ invalidArguments = [];
450
+
451
+ expect(
452
+ validateOptionValue(option, [{ a: 1, b: 2 }, { b: 4 }], invalidArguments),
453
+ ).toBeUndefined();
454
+ expect(invalidArguments).toEqual([{
455
+ argument: option.properties[0],
456
+ name: "foo[1].a",
457
+ reason: InvalidArgumentReason.MISSING_VALUE,
458
+ }]);
459
+
460
+ invalidArguments = [];
461
+
462
+ expect(
463
+ validateOptionValue(
464
+ option,
465
+ [undefined, { a: 3, b: 4 }],
466
+ invalidArguments,
467
+ ),
468
+ ).toBeUndefined();
469
+ expect(invalidArguments).toEqual([{
470
+ argument: option,
471
+ name: "foo[0]",
472
+ reason: InvalidArgumentReason.ILLEGAL_SPARSE_ARRAY,
473
+ }]);
474
+ });
475
+
476
+ test("Complex nested options", () => {
477
+ const option: ComplexOption = {
478
+ name: "alpha",
479
+ shortAlias: "a",
480
+ type: ComplexValueTypeName.COMPLEX,
481
+ isArray: true,
482
+ properties: [{
483
+ name: "beta",
484
+ shortAlias: "b",
485
+ type: ComplexValueTypeName.COMPLEX,
486
+ isArray: true,
487
+ properties: [{
488
+ name: "gamma",
489
+ shortAlias: "g",
490
+ type: ArgumentValueTypeName.STRING,
491
+ }, {
492
+ name: "delta",
493
+ shortAlias: "d",
494
+ type: ArgumentValueTypeName.NUMBER,
495
+ isArray: true,
496
+ }],
497
+ }],
498
+ };
499
+ let invalidArguments: Array<InvalidArgument> = [];
500
+ expect(
501
+ validateOptionValue(option, [
502
+ {
503
+ beta: [
504
+ {
505
+ delta: [
506
+ 1,
507
+ ],
508
+ gamma: "foo1",
509
+ },
510
+ {
511
+ delta: [
512
+ 3,
513
+ 2,
514
+ ],
515
+ gamma: "foo2",
516
+ },
517
+ ],
518
+ },
519
+ ], invalidArguments),
520
+ ).toEqual(
521
+ [
522
+ {
523
+ beta: [
524
+ {
525
+ delta: [
526
+ 1,
527
+ ],
528
+ gamma: "foo1",
529
+ },
530
+ {
531
+ delta: [
532
+ 3,
533
+ 2,
534
+ ],
535
+ gamma: "foo2",
536
+ },
537
+ ],
538
+ },
539
+ ],
540
+ );
541
+ expect(invalidArguments).toEqual([]);
542
+
543
+ invalidArguments = [];
544
+
545
+ expect(
546
+ validateOptionValue(
547
+ option,
548
+ [
549
+ {
550
+ beta: 1,
551
+ },
552
+ ],
553
+ invalidArguments,
554
+ ),
555
+ ).toBeUndefined();
556
+ expect(invalidArguments).toEqual([{
557
+ argument: option.properties[0],
558
+ name: "alpha[0].beta",
559
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
560
+ value: 1,
561
+ }]);
562
+ });
563
+
564
+ test("Complex sparse array options", () => {
565
+ const option: ComplexOption = {
566
+ name: "alpha",
567
+ shortAlias: "a",
568
+ type: ComplexValueTypeName.COMPLEX,
569
+ isArray: true,
570
+ properties: [{
571
+ name: "beta",
572
+ shortAlias: "b",
573
+ type: ComplexValueTypeName.COMPLEX,
574
+ isArray: true,
575
+ properties: [{
576
+ name: "gamma",
577
+ shortAlias: "g",
578
+ type: ArgumentValueTypeName.STRING,
579
+ }, {
580
+ name: "delta",
581
+ shortAlias: "d",
582
+ type: ArgumentValueTypeName.NUMBER,
583
+ isArray: true,
584
+ }],
585
+ }],
586
+ };
587
+ const invalidArguments: Array<InvalidArgument> = [];
588
+ const value: Array<PopulatedArgumentValues> = [{
589
+ beta: [
590
+ {},
591
+ {
592
+ gamma: "foo1",
593
+ delta: [3, 2],
594
+ },
595
+ ],
596
+ }];
597
+
598
+ expect(
599
+ validateOptionValue(option, value, invalidArguments),
600
+ ).toBeUndefined();
601
+ expect(invalidArguments).toEqual([{
602
+ argument: (option.properties[0] as ComplexOption).properties[0],
603
+ name: "alpha[0].beta[0].gamma",
604
+ reason: 0,
605
+ }]);
606
+ });
607
+
608
+ test("Positional types", () => {
609
+ let positional: Positional = {
610
+ name: "foo",
611
+ type: ArgumentValueTypeName.STRING,
612
+ };
613
+ let invalidArguments: Array<InvalidArgument> = [];
614
+ expect(
615
+ validatePositionalValue(positional, "foo", invalidArguments),
616
+ "foo",
617
+ );
618
+ expect(invalidArguments).toEqual([]);
619
+
620
+ positional = {
621
+ name: "foo",
622
+ type: ArgumentValueTypeName.NUMBER,
623
+ };
624
+ expect(validatePositionalValue(positional, "1", invalidArguments)).toEqual(
625
+ 1,
626
+ );
627
+ expect(invalidArguments).toEqual([]);
628
+
629
+ positional = {
630
+ name: "foo",
631
+ type: ArgumentValueTypeName.BOOLEAN,
632
+ };
633
+ expect(
634
+ validatePositionalValue(positional, "true", invalidArguments),
635
+ ).toBeTrue();
636
+ expect(invalidArguments).toEqual([]);
637
+
638
+ positional = {
639
+ name: "foo",
640
+ type: ArgumentValueTypeName.STRING,
641
+ };
642
+ expect(
643
+ validatePositionalValue(positional, "1", invalidArguments),
644
+ ).toEqual(
645
+ "1",
646
+ );
647
+ expect(invalidArguments).toEqual([]);
648
+
649
+ positional = {
650
+ name: "foo",
651
+ type: ArgumentValueTypeName.NUMBER,
652
+ };
653
+ expect(
654
+ validatePositionalValue(positional, "foo", invalidArguments),
655
+ ).toBeUndefined();
656
+ expect(invalidArguments).toEqual([{
657
+ argument: positional,
658
+ name: "foo",
659
+ value: "foo",
660
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
661
+ }]);
662
+
663
+ positional = {
664
+ name: "foo",
665
+ type: ArgumentValueTypeName.BOOLEAN,
666
+ };
667
+ invalidArguments = [];
668
+ expect(
669
+ validatePositionalValue(positional, "foo", invalidArguments),
670
+ ).toBeUndefined();
671
+ expect(invalidArguments).toEqual([{
672
+ argument: positional,
673
+ name: "foo",
674
+ value: "foo",
675
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
676
+ }]);
677
+ });
678
+
679
+ test("Positional varargs multiple", () => {
680
+ let positional: Positional = {
681
+ name: "foo",
682
+ type: ArgumentValueTypeName.STRING,
683
+ isVarargMultiple: true,
684
+ };
685
+ const invalidArguments: Array<InvalidArgument> = [];
686
+ expect(
687
+ validatePositionalValue(positional, ["foo", "bar"], invalidArguments),
688
+ ).toEqual(
689
+ ["foo", "bar"],
690
+ );
691
+ expect(invalidArguments).toEqual([]);
692
+
693
+ positional = {
694
+ name: "foo",
695
+ type: ArgumentValueTypeName.NUMBER,
696
+ isVarargMultiple: true,
697
+ };
698
+ expect(
699
+ validatePositionalValue(positional, ["1", "2"], invalidArguments),
700
+ ).toEqual(
701
+ [1, 2],
702
+ );
703
+ expect(invalidArguments).toEqual([]);
704
+
705
+ positional = {
706
+ name: "foo",
707
+ type: ArgumentValueTypeName.BOOLEAN,
708
+ isVarargMultiple: true,
709
+ };
710
+ expect(
711
+ validatePositionalValue(positional, ["true", "false"], invalidArguments),
712
+ ).toEqual(
713
+ [true, false],
714
+ );
715
+ expect(invalidArguments).toEqual([]);
716
+
717
+ positional = {
718
+ name: "foo",
719
+ type: ArgumentValueTypeName.BOOLEAN,
720
+ isVarargMultiple: true,
721
+ };
722
+ expect(
723
+ validatePositionalValue(positional, undefined, invalidArguments),
724
+ ).toBeUndefined();
725
+ expect(invalidArguments).toEqual([{
726
+ argument: positional,
727
+ name: "foo",
728
+ reason: InvalidArgumentReason.MISSING_VALUE,
729
+ }]);
730
+ });
731
+
732
+ test("Positional varargs optional", () => {
733
+ let positional: Positional = {
734
+ name: "foo",
735
+ type: ArgumentValueTypeName.STRING,
736
+ isVarargOptional: true,
737
+ };
738
+ let invalidArguments: Array<InvalidArgument> = [];
739
+ expect(
740
+ validatePositionalValue(positional, undefined, invalidArguments),
741
+ ).toBeUndefined();
742
+ expect(invalidArguments).toEqual([]);
743
+
744
+ positional = {
745
+ name: "foo",
746
+ type: ArgumentValueTypeName.STRING,
747
+ isVarargOptional: true,
748
+ };
749
+ expect(
750
+ validatePositionalValue(positional, "foo", invalidArguments),
751
+ "foo",
752
+ );
753
+ expect(invalidArguments).toEqual([]);
754
+
755
+ positional = {
756
+ name: "foo",
757
+ type: ArgumentValueTypeName.STRING,
758
+ isVarargOptional: true,
759
+ };
760
+ expect(
761
+ validatePositionalValue(positional, ["foo", "bar"], invalidArguments),
762
+ ).toBeUndefined();
763
+ expect(invalidArguments).toEqual([{
764
+ argument: positional,
765
+ name: "foo",
766
+ value: ["foo", "bar"],
767
+ reason: InvalidArgumentReason.ILLEGAL_MULTIPLE_VALUES,
768
+ }]);
769
+
770
+ positional = {
771
+ name: "foo",
772
+ type: ArgumentValueTypeName.STRING,
773
+ isVarargMultiple: true,
774
+ isVarargOptional: true,
775
+ };
776
+ invalidArguments = [];
777
+ expect(
778
+ validatePositionalValue(positional, ["foo", "bar"], invalidArguments),
779
+ ).toEqual(
780
+ ["foo", "bar"],
781
+ );
782
+ expect(invalidArguments).toEqual([]);
783
+ });
784
+
785
+ test("Invalid positional argument value", () => {
786
+ let positional: Option = {
787
+ name: "foo",
788
+ type: ArgumentValueTypeName.STRING,
789
+ allowableValues: ["bar", "two"],
790
+ };
791
+ const invalidArguments: Array<InvalidArgument> = [];
792
+ expect(
793
+ validatePositionalValue(positional, "bar", invalidArguments),
794
+ "bar",
795
+ );
796
+ expect(invalidArguments).toEqual([]);
797
+
798
+ positional = {
799
+ name: "foo",
800
+ type: ArgumentValueTypeName.STRING,
801
+ allowableValues: ["bar", "two"],
802
+ };
803
+ expect(
804
+ validatePositionalValue(positional, "goo", invalidArguments),
805
+ ).toBeUndefined();
806
+ expect(invalidArguments).toEqual([{
807
+ argument: positional,
808
+ name: "foo",
809
+ value: "goo",
810
+ reason: InvalidArgumentReason.ILLEGAL_VALUE,
811
+ }]);
812
+ });
813
+
814
+ test("Global command argument types", () => {
815
+ let globalCommandArgument: GlobalCommandArgument = {
816
+ type: ArgumentValueTypeName.STRING,
817
+ };
818
+ let globalCommand = getGlobalCommandWithShortAlias(
819
+ "globalCommand",
820
+ "f",
821
+ globalCommandArgument,
822
+ );
823
+ let invalidArguments: Array<InvalidArgument> = [];
824
+ expect(
825
+ validateGlobalCommandArgumentValue(
826
+ globalCommand,
827
+ "foo",
828
+ invalidArguments,
829
+ ),
830
+ "foo",
831
+ );
832
+ expect(invalidArguments).toEqual([]);
833
+
834
+ globalCommandArgument = {
835
+ type: ArgumentValueTypeName.NUMBER,
836
+ };
837
+ globalCommand = getGlobalCommandWithShortAlias(
838
+ "globalCommand",
839
+ "f",
840
+ globalCommandArgument,
841
+ );
842
+ expect(
843
+ validateGlobalCommandArgumentValue(
844
+ globalCommand,
845
+ "1",
846
+ invalidArguments,
847
+ ),
848
+ ).toEqual(
849
+ 1,
850
+ );
851
+ expect(invalidArguments).toEqual([]);
852
+
853
+ globalCommandArgument = {
854
+ type: ArgumentValueTypeName.BOOLEAN,
855
+ };
856
+ globalCommand = getGlobalCommandWithShortAlias(
857
+ "globalCommand",
858
+ "f",
859
+ globalCommandArgument,
860
+ );
861
+ expect(
862
+ validateGlobalCommandArgumentValue(
863
+ globalCommand,
864
+ "true",
865
+ invalidArguments,
866
+ ),
867
+ ).toBeTrue();
868
+ expect(invalidArguments).toEqual([]);
869
+
870
+ globalCommandArgument = {
871
+ type: ArgumentValueTypeName.STRING,
872
+ };
873
+ globalCommand = getGlobalCommandWithShortAlias(
874
+ "globalCommand",
875
+ "f",
876
+ globalCommandArgument,
877
+ );
878
+ expect(
879
+ validateGlobalCommandArgumentValue(
880
+ globalCommand,
881
+ "1",
882
+ invalidArguments,
883
+ ),
884
+ "1",
885
+ );
886
+ expect(invalidArguments).toEqual([]);
887
+
888
+ globalCommandArgument = {
889
+ type: ArgumentValueTypeName.NUMBER,
890
+ };
891
+ globalCommand = getGlobalCommandWithShortAlias(
892
+ "globalCommand",
893
+ "f",
894
+ globalCommandArgument,
895
+ );
896
+ expect(
897
+ validateGlobalCommandArgumentValue(
898
+ globalCommand,
899
+ "foo",
900
+ invalidArguments,
901
+ ),
902
+ ).toBeUndefined();
903
+ expect(invalidArguments).toEqual([{
904
+ argument: globalCommandArgument,
905
+ name: "globalCommand",
906
+ value: "foo",
907
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
908
+ }]);
909
+
910
+ globalCommandArgument = {
911
+ type: ArgumentValueTypeName.BOOLEAN,
912
+ };
913
+ globalCommand = getGlobalCommandWithShortAlias(
914
+ "globalCommand",
915
+ "f",
916
+ globalCommandArgument,
917
+ );
918
+ invalidArguments = [];
919
+ expect(
920
+ validateGlobalCommandArgumentValue(
921
+ globalCommand,
922
+ "foo",
923
+ invalidArguments,
924
+ ),
925
+ ).toBeUndefined();
926
+ expect(invalidArguments).toEqual([{
927
+ argument: globalCommandArgument,
928
+ name: "globalCommand",
929
+ value: "foo",
930
+ reason: InvalidArgumentReason.INCORRECT_VALUE_TYPE,
931
+ }]);
932
+ });
933
+
934
+ test("Optional global command argument", () => {
935
+ let globalCommandArgument: GlobalCommandArgument = {
936
+ type: ArgumentValueTypeName.STRING,
937
+ isOptional: true,
938
+ };
939
+ let globalCommand = getGlobalCommandWithShortAlias(
940
+ "globalCommand",
941
+ "f",
942
+ globalCommandArgument,
943
+ );
944
+ const invalidArguments: Array<InvalidArgument> = [];
945
+ expect(
946
+ validateGlobalCommandArgumentValue(
947
+ globalCommand,
948
+ undefined,
949
+ invalidArguments,
950
+ ),
951
+ ).toBeUndefined();
952
+ expect(invalidArguments).toEqual([]);
953
+
954
+ globalCommandArgument = {
955
+ type: ArgumentValueTypeName.STRING,
956
+ };
957
+ globalCommand = getGlobalCommandWithShortAlias(
958
+ "globalCommand",
959
+ "f",
960
+ globalCommandArgument,
961
+ );
962
+ expect(
963
+ validateGlobalCommandArgumentValue(
964
+ globalCommand,
965
+ undefined,
966
+ invalidArguments,
967
+ ),
968
+ ).toBeUndefined();
969
+ expect(invalidArguments).toEqual([{
970
+ argument: globalCommandArgument,
971
+ name: "globalCommand",
972
+ reason: InvalidArgumentReason.MISSING_VALUE,
973
+ }]);
974
+ });
975
+
976
+ test("Invalid global command argument value", () => {
977
+ let globalCommandArgument: GlobalCommandArgument = {
978
+ type: ArgumentValueTypeName.STRING,
979
+ allowableValues: ["bar", "two"],
980
+ };
981
+ let globalCommand = getGlobalCommandWithShortAlias(
982
+ "globalCommand",
983
+ "f",
984
+ globalCommandArgument,
985
+ );
986
+ const invalidArguments: Array<InvalidArgument> = [];
987
+ expect(
988
+ validateGlobalCommandArgumentValue(
989
+ globalCommand,
990
+ "bar",
991
+ invalidArguments,
992
+ ),
993
+ "bar",
994
+ );
995
+ expect(invalidArguments).toEqual([]);
996
+
997
+ globalCommandArgument = {
998
+ type: ArgumentValueTypeName.STRING,
999
+ allowableValues: ["bar", "two"],
1000
+ };
1001
+ globalCommand = getGlobalCommandWithShortAlias(
1002
+ "globalCommand",
1003
+ "f",
1004
+ globalCommandArgument,
1005
+ );
1006
+ expect(
1007
+ validateGlobalCommandArgumentValue(
1008
+ globalCommand,
1009
+ "goo",
1010
+ invalidArguments,
1011
+ ),
1012
+ ).toBeUndefined();
1013
+ expect(invalidArguments).toEqual([{
1014
+ argument: globalCommandArgument,
1015
+ name: "globalCommand",
1016
+ value: "goo",
1017
+ reason: InvalidArgumentReason.ILLEGAL_VALUE,
1018
+ }]);
1019
+ });
1020
+ });