@ontrails/cli 1.0.0-beta.2 → 1.0.0-beta.3
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +21 -0
- package/dist/commander/blaze.d.ts +1 -1
- package/dist/commander/blaze.d.ts.map +1 -1
- package/dist/commander/blaze.js +2 -2
- package/dist/commander/blaze.js.map +1 -1
- package/dist/commander/to-commander.d.ts.map +1 -1
- package/dist/commander/to-commander.js +25 -7
- package/dist/commander/to-commander.js.map +1 -1
- package/dist/flags.d.ts.map +1 -1
- package/dist/flags.js +2 -0
- package/dist/flags.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/blaze.test.ts +10 -0
- package/src/__tests__/to-commander.test.ts +164 -0
- package/src/commander/blaze.ts +5 -2
- package/src/commander/to-commander.ts +30 -7
- package/src/flags.ts +2 -0
package/.turbo/turbo-lint.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @ontrails/cli
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Bug fixes across all surface packages found via parallel Codex review.
|
|
8
|
+
|
|
9
|
+
**core**: Fix Result.toJson false circular detection on DAGs, deserializeError subclass round-trip, topo cross-kind ID collisions, validateTopo multi-node cycle detection, error example input validation bypass, and deriveFields array type collapse.
|
|
10
|
+
|
|
11
|
+
**cli**: Switch blaze to parseAsync for proper async error handling, add boolean flag negation (--no-flag), and strict number parsing that rejects partial input.
|
|
12
|
+
|
|
13
|
+
**mcp**: Align BlobRef with core (including ReadableStream support) and detect tool-name collisions after normalization.
|
|
14
|
+
|
|
15
|
+
**testing**: Include hikes in testContracts validation, with follow-context awareness.
|
|
16
|
+
|
|
17
|
+
**warden**: Collect hike detour targets, validate detour refs in hike specs, and stop implementation-returns-result from walking into nested function bodies.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- @ontrails/core@1.0.0-beta.3
|
|
23
|
+
|
|
3
24
|
## 1.0.0-beta.2
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -27,5 +27,5 @@ export interface BlazeCliOptions {
|
|
|
27
27
|
* blaze(app);
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
|
-
export declare const blaze: (app: Topo, options?: BlazeCliOptions) => void
|
|
30
|
+
export declare const blaze: (app: Topo, options?: BlazeCliOptions) => Promise<void>;
|
|
31
31
|
//# sourceMappingURL=blaze.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blaze.d.ts","sourceRoot":"","sources":["../../src/commander/blaze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IACrE,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAMD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"blaze.d.ts","sourceRoot":"","sources":["../../src/commander/blaze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IACrE,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAMD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAChB,KAAK,IAAI,EACT,UAAS,eAAoB,KAC5B,OAAO,CAAC,IAAI,CAqBd,CAAC"}
|
package/dist/commander/blaze.js
CHANGED
|
@@ -19,7 +19,7 @@ import { toCommander } from './to-commander.js';
|
|
|
19
19
|
* blaze(app);
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
export const blaze = (app, options = {}) => {
|
|
22
|
+
export const blaze = async (app, options = {}) => {
|
|
23
23
|
const commands = buildCliCommands(app, {
|
|
24
24
|
createContext: options.createContext,
|
|
25
25
|
layers: options.layers,
|
|
@@ -37,6 +37,6 @@ export const blaze = (app, options = {}) => {
|
|
|
37
37
|
commanderOpts.description = options.description;
|
|
38
38
|
}
|
|
39
39
|
const program = toCommander(commands, commanderOpts);
|
|
40
|
-
program.
|
|
40
|
+
await program.parseAsync();
|
|
41
41
|
};
|
|
42
42
|
//# sourceMappingURL=blaze.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blaze.js","sourceRoot":"","sources":["../../src/commander/blaze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiBhD,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"blaze.js","sourceRoot":"","sources":["../../src/commander/blaze.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiBhD,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EACxB,GAAS,EACT,UAA2B,EAAE,EACd,EAAE;IACjB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,EAAE;QACrC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,eAAe;QAC7C,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAuB;QACxC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI;KAC/B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,aAAa,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAC1C,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACtC,aAAa,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAClD,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACrD,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;AAC7B,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-commander.d.ts","sourceRoot":"","sources":["../../src/commander/to-commander.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"to-commander.d.ts","sourceRoot":"","sources":["../../src/commander/to-commander.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAW,MAAM,eAAe,CAAC;AAMzD,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAiLD,eAAO,MAAM,WAAW,GACtB,UAAU,UAAU,EAAE,EACtB,UAAU,kBAAkB,KAC3B,OAWF,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Adapt framework-agnostic CliCommand[] to a Commander program.
|
|
3
3
|
*/
|
|
4
4
|
import { exitCodeMap, isTrailsError } from '@ontrails/core';
|
|
5
|
-
import { Command, Option } from 'commander';
|
|
5
|
+
import { Command, InvalidArgumentError, Option } from 'commander';
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
// Helpers
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
@@ -22,9 +22,16 @@ const buildFlagString = (flag) => {
|
|
|
22
22
|
const argPart = buildFlagArgument(flag);
|
|
23
23
|
return short ? `${short}, ${long} ${argPart}` : `${long} ${argPart}`;
|
|
24
24
|
};
|
|
25
|
-
/**
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
/** Strict number parser that rejects partial parses and non-finite values. */
|
|
26
|
+
const strictParseNumber = (value) => {
|
|
27
|
+
const n = Number(value);
|
|
28
|
+
if (Number.isNaN(n) || !Number.isFinite(n)) {
|
|
29
|
+
throw new InvalidArgumentError(`"${value}" is not a valid number`);
|
|
30
|
+
}
|
|
31
|
+
return n;
|
|
32
|
+
};
|
|
33
|
+
/** Apply common modifiers (choices, default, arg parser) to a Commander Option. */
|
|
34
|
+
const applyOptionModifiers = (opt, flag) => {
|
|
28
35
|
if (flag.choices) {
|
|
29
36
|
opt.choices(flag.choices);
|
|
30
37
|
}
|
|
@@ -32,9 +39,18 @@ const buildOption = (flag) => {
|
|
|
32
39
|
opt.default(flag.default);
|
|
33
40
|
}
|
|
34
41
|
if (flag.type === 'number' || flag.type === 'number[]') {
|
|
35
|
-
opt.argParser(
|
|
42
|
+
opt.argParser(strictParseNumber);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/** Build Commander Option(s) from a CliFlag. Returns one or two options. */
|
|
46
|
+
const buildOptions = (flag) => {
|
|
47
|
+
const opt = new Option(buildFlagString(flag), flag.description);
|
|
48
|
+
applyOptionModifiers(opt, flag);
|
|
49
|
+
if (flag.type === 'boolean') {
|
|
50
|
+
const negation = new Option(`--no-${flag.name}`, flag.description ? `Negate ${flag.description}` : undefined);
|
|
51
|
+
return [opt, negation];
|
|
36
52
|
}
|
|
37
|
-
return opt;
|
|
53
|
+
return [opt];
|
|
38
54
|
};
|
|
39
55
|
/** Add positional args to a Commander subcommand. */
|
|
40
56
|
const buildArgTemplate = (arg) => {
|
|
@@ -129,7 +145,9 @@ const buildSubcommand = (cmd) => {
|
|
|
129
145
|
sub.description(cmd.description);
|
|
130
146
|
}
|
|
131
147
|
for (const flag of cmd.flags) {
|
|
132
|
-
|
|
148
|
+
for (const opt of buildOptions(flag)) {
|
|
149
|
+
sub.addOption(opt);
|
|
150
|
+
}
|
|
133
151
|
}
|
|
134
152
|
addArgs(sub, cmd);
|
|
135
153
|
wireAction(sub, cmd);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-commander.js","sourceRoot":"","sources":["../../src/commander/to-commander.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"to-commander.js","sourceRoot":"","sources":["../../src/commander/to-commander.ts"],"names":[],"mappings":"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"}
|
package/dist/flags.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAmD5C,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAI,QAAQ,SAAS,KAAK,EAAE,KAAG,OAAO,EACnC,CAAC;AA2HxB,gDAAgD;AAChD,eAAO,MAAM,WAAW,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAO,EAYtD,CAAC;AAMF,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,QAAO,OAAO,EAyB1C,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,SAAS,QAAO,OAAO,EAQnC,CAAC;AAEF,uCAAuC;AACvC,eAAO,MAAM,YAAY,QAAO,OAAO,EAStC,CAAC"}
|
package/dist/flags.js
CHANGED
|
@@ -11,7 +11,9 @@ const fieldTypeToCliFlag = {
|
|
|
11
11
|
enum: { type: 'string', variadic: false },
|
|
12
12
|
multiselect: { type: 'string[]', variadic: true },
|
|
13
13
|
number: { type: 'number', variadic: false },
|
|
14
|
+
'number[]': { type: 'number[]', variadic: true },
|
|
14
15
|
string: { type: 'string', variadic: false },
|
|
16
|
+
'string[]': { type: 'string[]', variadic: true },
|
|
15
17
|
};
|
|
16
18
|
/** Convert a derived field into a CLI flag descriptor. */
|
|
17
19
|
const toCliFlag = (field) => {
|
package/dist/flags.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,uCAAuC;AACvC,MAAM,OAAO,GAAG,CAAC,GAAW,EAAU,EAAE,CACtC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAe3D,MAAM,kBAAkB,GAAwC;IAC9D,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC7C,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE;IACzC,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE;IACjD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE;
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import { Result, trail, topo } from '@ontrails/core';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
import { buildCliCommands } from '../build.js';
|
|
7
|
+
import { blaze } from '../commander/blaze.js';
|
|
7
8
|
import { toCommander } from '../commander/to-commander.js';
|
|
8
9
|
import { defaultOnResult } from '../on-result.js';
|
|
9
10
|
|
|
@@ -47,6 +48,15 @@ describe('blaze', () => {
|
|
|
47
48
|
expect(program.commands[0]?.name()).toBe('echo');
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
test('blaze returns a Promise (async signature)', () => {
|
|
52
|
+
// Verify blaze's return type is a Promise by checking its constructor name.
|
|
53
|
+
// We don't call blaze() here because it invokes parseAsync on real argv.
|
|
54
|
+
expect(blaze).toBeDefined();
|
|
55
|
+
// The function is async, so calling it returns a Promise.
|
|
56
|
+
// We verify the type signature indirectly: async functions have AsyncFunction constructor.
|
|
57
|
+
expect(blaze.constructor.name).toBe('AsyncFunction');
|
|
58
|
+
});
|
|
59
|
+
|
|
50
60
|
test('end-to-end: define trail, build commands, execute, verify output', async () => {
|
|
51
61
|
const written: string[] = [];
|
|
52
62
|
const originalWrite = process.stdout.write;
|
|
@@ -11,6 +11,11 @@ import { toCommander } from '../commander/to-commander.js';
|
|
|
11
11
|
// Helpers
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
|
+
// oxlint-disable-next-line no-empty-function, require-await -- intentional noop for callback type
|
|
15
|
+
const noopResult = async () => {};
|
|
16
|
+
// oxlint-disable-next-line no-empty-function -- intentional noop for callback type
|
|
17
|
+
const noopWrite = () => {};
|
|
18
|
+
|
|
14
19
|
const makeApp = (...trails: AnyTrail[]) => {
|
|
15
20
|
const mod: Record<string, unknown> = {};
|
|
16
21
|
for (const t of trails) {
|
|
@@ -19,6 +24,25 @@ const makeApp = (...trails: AnyTrail[]) => {
|
|
|
19
24
|
return topo('test-app', mod);
|
|
20
25
|
};
|
|
21
26
|
|
|
27
|
+
/** Intercept a command's execute to capture parsed opts. */
|
|
28
|
+
const interceptOpts = (commands: ReturnType<typeof buildCliCommands>) => {
|
|
29
|
+
let received: Record<string, unknown> = {};
|
|
30
|
+
const [cmd] = commands;
|
|
31
|
+
if (!cmd) {
|
|
32
|
+
throw new Error('No commands built');
|
|
33
|
+
}
|
|
34
|
+
const original = cmd.execute;
|
|
35
|
+
cmd.execute = (args, opts) => {
|
|
36
|
+
received = opts;
|
|
37
|
+
return original(args, opts);
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
get received() {
|
|
41
|
+
return received;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
22
46
|
const requireCommand = (
|
|
23
47
|
program: ReturnType<typeof toCommander>,
|
|
24
48
|
name: string
|
|
@@ -90,6 +114,7 @@ describe('toCommander', () => {
|
|
|
90
114
|
const program = toCommander(commands);
|
|
91
115
|
|
|
92
116
|
const opts = requireCommand(program, 'search').options;
|
|
117
|
+
// 5 flags + negation options for boolean flags
|
|
93
118
|
expect(opts.length).toBeGreaterThanOrEqual(5);
|
|
94
119
|
|
|
95
120
|
const formatOpt = opts.find((entry) => entry.long === '--format');
|
|
@@ -97,6 +122,145 @@ describe('toCommander', () => {
|
|
|
97
122
|
expect(formatOpt?.argChoices).toEqual(['json', 'text']);
|
|
98
123
|
});
|
|
99
124
|
|
|
125
|
+
describe('boolean flag negation', () => {
|
|
126
|
+
test('boolean flags get --no-<name> negation options', () => {
|
|
127
|
+
const t = trail('check', {
|
|
128
|
+
implementation: () => Result.ok('ok'),
|
|
129
|
+
input: z.object({ strict: z.boolean() }),
|
|
130
|
+
});
|
|
131
|
+
const app = makeApp(t);
|
|
132
|
+
const commands = buildCliCommands(app);
|
|
133
|
+
const program = toCommander(commands);
|
|
134
|
+
|
|
135
|
+
const cmd = requireCommand(program, 'check');
|
|
136
|
+
const strictOpt = cmd.options.find((o) => o.long === '--strict');
|
|
137
|
+
const noStrictOpt = cmd.options.find((o) => o.long === '--no-strict');
|
|
138
|
+
|
|
139
|
+
expect(strictOpt).toBeDefined();
|
|
140
|
+
expect(noStrictOpt).toBeDefined();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('--no-<flag> sets value to false via parseAsync', async () => {
|
|
144
|
+
const t = trail('check', {
|
|
145
|
+
implementation: () => Result.ok('ok'),
|
|
146
|
+
input: z.object({ strict: z.boolean().default(true) }),
|
|
147
|
+
});
|
|
148
|
+
const app = makeApp(t);
|
|
149
|
+
const commands = buildCliCommands(app, { onResult: noopResult });
|
|
150
|
+
const spy = interceptOpts(commands);
|
|
151
|
+
const program = toCommander(commands, { name: 'test' });
|
|
152
|
+
program.exitOverride();
|
|
153
|
+
|
|
154
|
+
await program.parseAsync(['node', 'test', 'check', '--no-strict']);
|
|
155
|
+
expect(spy.received['strict']).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('--flag sets boolean value to true via parseAsync', async () => {
|
|
159
|
+
const t = trail('check', {
|
|
160
|
+
implementation: () => Result.ok('ok'),
|
|
161
|
+
input: z.object({ strict: z.boolean().default(false) }),
|
|
162
|
+
});
|
|
163
|
+
const app = makeApp(t);
|
|
164
|
+
const commands = buildCliCommands(app, { onResult: noopResult });
|
|
165
|
+
const spy = interceptOpts(commands);
|
|
166
|
+
const program = toCommander(commands, { name: 'test' });
|
|
167
|
+
program.exitOverride();
|
|
168
|
+
|
|
169
|
+
await program.parseAsync(['node', 'test', 'check', '--strict']);
|
|
170
|
+
expect(spy.received['strict']).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('strict number parsing', () => {
|
|
175
|
+
const buildNumberProgram = () => {
|
|
176
|
+
const t = trail('count', {
|
|
177
|
+
implementation: () => Result.ok('ok'),
|
|
178
|
+
input: z.object({ limit: z.number() }),
|
|
179
|
+
});
|
|
180
|
+
const app = makeApp(t);
|
|
181
|
+
const commands = buildCliCommands(app);
|
|
182
|
+
const program = toCommander(commands, { name: 'test' });
|
|
183
|
+
program.exitOverride();
|
|
184
|
+
program.configureOutput({
|
|
185
|
+
writeErr: noopWrite,
|
|
186
|
+
writeOut: noopWrite,
|
|
187
|
+
});
|
|
188
|
+
// Also configure on the subcommand directly
|
|
189
|
+
for (const sub of program.commands) {
|
|
190
|
+
sub.exitOverride();
|
|
191
|
+
sub.configureOutput({
|
|
192
|
+
writeErr: noopWrite,
|
|
193
|
+
writeOut: noopWrite,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return program;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
test('rejects partial number like "123abc"', async () => {
|
|
200
|
+
const program = buildNumberProgram();
|
|
201
|
+
let threw = false;
|
|
202
|
+
try {
|
|
203
|
+
await program.parseAsync([
|
|
204
|
+
'node',
|
|
205
|
+
'test',
|
|
206
|
+
'count',
|
|
207
|
+
'--limit',
|
|
208
|
+
'123abc',
|
|
209
|
+
]);
|
|
210
|
+
} catch {
|
|
211
|
+
threw = true;
|
|
212
|
+
}
|
|
213
|
+
expect(threw).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('rejects Infinity', async () => {
|
|
217
|
+
const program = buildNumberProgram();
|
|
218
|
+
let threw = false;
|
|
219
|
+
try {
|
|
220
|
+
await program.parseAsync([
|
|
221
|
+
'node',
|
|
222
|
+
'test',
|
|
223
|
+
'count',
|
|
224
|
+
'--limit',
|
|
225
|
+
'Infinity',
|
|
226
|
+
]);
|
|
227
|
+
} catch {
|
|
228
|
+
threw = true;
|
|
229
|
+
}
|
|
230
|
+
expect(threw).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('rejects NaN', async () => {
|
|
234
|
+
const program = buildNumberProgram();
|
|
235
|
+
let threw = false;
|
|
236
|
+
try {
|
|
237
|
+
await program.parseAsync(['node', 'test', 'count', '--limit', 'abc']);
|
|
238
|
+
} catch {
|
|
239
|
+
threw = true;
|
|
240
|
+
}
|
|
241
|
+
expect(threw).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test.each([
|
|
245
|
+
{ expected: 42, input: '42' },
|
|
246
|
+
{ expected: 3.14, input: '3.14' },
|
|
247
|
+
{ expected: -5, input: '-5' },
|
|
248
|
+
])('accepts valid number "$input"', async ({ expected, input }) => {
|
|
249
|
+
const t = trail('count', {
|
|
250
|
+
implementation: () => Result.ok('ok'),
|
|
251
|
+
input: z.object({ limit: z.number() }),
|
|
252
|
+
});
|
|
253
|
+
const app = makeApp(t);
|
|
254
|
+
const commands = buildCliCommands(app, { onResult: noopResult });
|
|
255
|
+
const spy = interceptOpts(commands);
|
|
256
|
+
const program = toCommander(commands, { name: 'test' });
|
|
257
|
+
program.exitOverride();
|
|
258
|
+
|
|
259
|
+
await program.parseAsync(['node', 'test', 'count', '--limit', input]);
|
|
260
|
+
expect(spy.received['limit']).toBe(expected);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
100
264
|
test('sets version when provided', () => {
|
|
101
265
|
const t = trail('ping', {
|
|
102
266
|
implementation: () => Result.ok('pong'),
|
package/src/commander/blaze.ts
CHANGED
|
@@ -43,7 +43,10 @@ export interface BlazeCliOptions {
|
|
|
43
43
|
* blaze(app);
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
|
-
export const blaze = (
|
|
46
|
+
export const blaze = async (
|
|
47
|
+
app: Topo,
|
|
48
|
+
options: BlazeCliOptions = {}
|
|
49
|
+
): Promise<void> => {
|
|
47
50
|
const commands = buildCliCommands(app, {
|
|
48
51
|
createContext: options.createContext,
|
|
49
52
|
layers: options.layers,
|
|
@@ -63,5 +66,5 @@ export const blaze = (app: Topo, options: BlazeCliOptions = {}): void => {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
const program = toCommander(commands, commanderOpts);
|
|
66
|
-
program.
|
|
69
|
+
await program.parseAsync();
|
|
67
70
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { exitCodeMap, isTrailsError } from '@ontrails/core';
|
|
6
|
-
import { Command, Option } from 'commander';
|
|
6
|
+
import { Command, InvalidArgumentError, Option } from 'commander';
|
|
7
7
|
|
|
8
8
|
import type { CliCommand, CliFlag } from '../command.js';
|
|
9
9
|
|
|
@@ -41,9 +41,17 @@ const buildFlagString = (flag: CliFlag): string => {
|
|
|
41
41
|
return short ? `${short}, ${long} ${argPart}` : `${long} ${argPart}`;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
const
|
|
46
|
-
const
|
|
44
|
+
/** Strict number parser that rejects partial parses and non-finite values. */
|
|
45
|
+
const strictParseNumber = (value: string): number => {
|
|
46
|
+
const n = Number(value);
|
|
47
|
+
if (Number.isNaN(n) || !Number.isFinite(n)) {
|
|
48
|
+
throw new InvalidArgumentError(`"${value}" is not a valid number`);
|
|
49
|
+
}
|
|
50
|
+
return n;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Apply common modifiers (choices, default, arg parser) to a Commander Option. */
|
|
54
|
+
const applyOptionModifiers = (opt: Option, flag: CliFlag): void => {
|
|
47
55
|
if (flag.choices) {
|
|
48
56
|
opt.choices(flag.choices);
|
|
49
57
|
}
|
|
@@ -51,9 +59,22 @@ const buildOption = (flag: CliFlag): Option => {
|
|
|
51
59
|
opt.default(flag.default);
|
|
52
60
|
}
|
|
53
61
|
if (flag.type === 'number' || flag.type === 'number[]') {
|
|
54
|
-
opt.argParser(
|
|
62
|
+
opt.argParser(strictParseNumber);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Build Commander Option(s) from a CliFlag. Returns one or two options. */
|
|
67
|
+
const buildOptions = (flag: CliFlag): Option[] => {
|
|
68
|
+
const opt = new Option(buildFlagString(flag), flag.description);
|
|
69
|
+
applyOptionModifiers(opt, flag);
|
|
70
|
+
if (flag.type === 'boolean') {
|
|
71
|
+
const negation = new Option(
|
|
72
|
+
`--no-${flag.name}`,
|
|
73
|
+
flag.description ? `Negate ${flag.description}` : undefined
|
|
74
|
+
);
|
|
75
|
+
return [opt, negation];
|
|
55
76
|
}
|
|
56
|
-
return opt;
|
|
77
|
+
return [opt];
|
|
57
78
|
};
|
|
58
79
|
|
|
59
80
|
/** Add positional args to a Commander subcommand. */
|
|
@@ -162,7 +183,9 @@ const buildSubcommand = (cmd: CliCommand): Command => {
|
|
|
162
183
|
sub.description(cmd.description);
|
|
163
184
|
}
|
|
164
185
|
for (const flag of cmd.flags) {
|
|
165
|
-
|
|
186
|
+
for (const opt of buildOptions(flag)) {
|
|
187
|
+
sub.addOption(opt);
|
|
188
|
+
}
|
|
166
189
|
}
|
|
167
190
|
addArgs(sub, cmd);
|
|
168
191
|
wireAction(sub, cmd);
|
package/src/flags.ts
CHANGED
|
@@ -33,7 +33,9 @@ const fieldTypeToCliFlag: Record<Field['type'], CliFlagShape> = {
|
|
|
33
33
|
enum: { type: 'string', variadic: false },
|
|
34
34
|
multiselect: { type: 'string[]', variadic: true },
|
|
35
35
|
number: { type: 'number', variadic: false },
|
|
36
|
+
'number[]': { type: 'number[]', variadic: true },
|
|
36
37
|
string: { type: 'string', variadic: false },
|
|
38
|
+
'string[]': { type: 'string[]', variadic: true },
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
/** Convert a derived field into a CLI flag descriptor. */
|