spec-controller 0.1.0-alpha.44 → 0.1.0-alpha.46
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/dist/cli-allocate/cli.d.ts.map +1 -1
- package/dist/cli-allocate/cli.js +13 -14
- package/dist/cli-allocate/cli.js.map +1 -1
- package/dist/cli-allocate/readHistory.d.ts +0 -14
- package/dist/cli-allocate/readHistory.d.ts.map +1 -1
- package/dist/cli-allocate/readHistory.js +8 -36
- package/dist/cli-allocate/readHistory.js.map +1 -1
- package/dist/cli-allocate/readParts.d.ts +0 -1
- package/dist/cli-allocate/readParts.d.ts.map +1 -1
- package/dist/cli-allocate/readParts.js +0 -6
- package/dist/cli-allocate/readParts.js.map +1 -1
- package/dist/cli-allocate/readTree.d.ts +0 -11
- package/dist/cli-allocate/readTree.d.ts.map +1 -1
- package/dist/cli-allocate/readTree.js +0 -32
- package/dist/cli-allocate/readTree.js.map +1 -1
- package/dist/cli-allocate/refStore.d.ts +0 -1
- package/dist/cli-allocate/refStore.d.ts.map +1 -1
- package/dist/cli-allocate/refStore.js +0 -13
- package/dist/cli-allocate/refStore.js.map +1 -1
- package/dist/cli-allocate/request.d.ts +0 -2
- package/dist/cli-allocate/request.d.ts.map +1 -1
- package/dist/cli-allocate/request.js +0 -7
- package/dist/cli-allocate/request.js.map +1 -1
- package/dist/cli-allocate/stderr.d.ts +0 -7
- package/dist/cli-allocate/stderr.d.ts.map +1 -1
- package/dist/cli-allocate/stderr.js +0 -7
- package/dist/cli-allocate/stderr.js.map +1 -1
- package/dist/cli-allocate/unreadable.d.ts +0 -13
- package/dist/cli-allocate/unreadable.d.ts.map +1 -1
- package/dist/cli-allocate/unreadable.js +0 -13
- package/dist/cli-allocate/unreadable.js.map +1 -1
- package/dist/cli-args.d.ts +0 -113
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +0 -145
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-balance/cli.d.ts +0 -85
- package/dist/cli-balance/cli.d.ts.map +1 -1
- package/dist/cli-balance/cli.js +0 -230
- package/dist/cli-balance/cli.js.map +1 -1
- package/dist/cli-balance/emit/format.d.ts +0 -52
- package/dist/cli-balance/emit/format.d.ts.map +1 -1
- package/dist/cli-balance/emit/format.js +0 -70
- package/dist/cli-balance/emit/format.js.map +1 -1
- package/dist/cli-balance/emit/writer.d.ts +0 -17
- package/dist/cli-balance/emit/writer.d.ts.map +1 -1
- package/dist/cli-balance/emit/writer.js +0 -13
- package/dist/cli-balance/emit/writer.js.map +1 -1
- package/dist/cli-balance/exitStatus.d.ts +0 -1
- package/dist/cli-balance/exitStatus.d.ts.map +1 -1
- package/dist/cli-balance/exitStatus.js +0 -7
- package/dist/cli-balance/exitStatus.js.map +1 -1
- package/dist/cli-balance/inputsRead.d.ts +0 -12
- package/dist/cli-balance/inputsRead.d.ts.map +1 -1
- package/dist/cli-balance/inputsRead.js +0 -12
- package/dist/cli-balance/inputsRead.js.map +1 -1
- package/dist/cli-balance/reRender.d.ts +0 -14
- package/dist/cli-balance/reRender.d.ts.map +1 -1
- package/dist/cli-balance/reRender.js +0 -15
- package/dist/cli-balance/reRender.js.map +1 -1
- package/dist/cli-balance/unreadableInputs.d.ts +0 -21
- package/dist/cli-balance/unreadableInputs.d.ts.map +1 -1
- package/dist/cli-balance/unreadableInputs.js +0 -21
- package/dist/cli-balance/unreadableInputs.js.map +1 -1
- package/dist/cli-registry.d.ts +0 -86
- package/dist/cli-registry.d.ts.map +1 -1
- package/dist/cli-registry.js +0 -70
- package/dist/cli-registry.js.map +1 -1
- package/dist/cli.d.ts +0 -20
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -53
- package/dist/cli.js.map +1 -1
- package/dist/deferralTags.d.ts +0 -14
- package/dist/deferralTags.d.ts.map +1 -1
- package/dist/deferralTags.js +0 -14
- package/dist/deferralTags.js.map +1 -1
- package/dist/host.d.ts +0 -61
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -84
- package/dist/host.js.map +1 -1
- package/dist/ingest/ciSourcePaths.d.ts +0 -38
- package/dist/ingest/ciSourcePaths.d.ts.map +1 -1
- package/dist/ingest/ciSourcePaths.js +0 -38
- package/dist/ingest/ciSourcePaths.js.map +1 -1
- package/dist/ingest/gherkinValidation.d.ts +0 -76
- package/dist/ingest/gherkinValidation.d.ts.map +1 -1
- package/dist/ingest/gherkinValidation.js +0 -76
- package/dist/ingest/gherkinValidation.js.map +1 -1
- package/dist/ingest/ingestQualityChecks.d.ts +0 -81
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
- package/dist/ingest/ingestQualityChecks.js +0 -194
- package/dist/ingest/ingestQualityChecks.js.map +1 -1
- package/dist/ingest/ingestScenarios.d.ts +0 -39
- package/dist/ingest/ingestScenarios.d.ts.map +1 -1
- package/dist/ingest/ingestScenarios.js +0 -70
- package/dist/ingest/ingestScenarios.js.map +1 -1
- package/dist/ingest/inputShapes.d.ts +0 -31
- package/dist/ingest/inputShapes.d.ts.map +1 -1
- package/dist/ingest/inputShapes.js +0 -30
- package/dist/ingest/inputShapes.js.map +1 -1
- package/dist/outputLocation.d.ts +0 -48
- package/dist/outputLocation.d.ts.map +1 -1
- package/dist/outputLocation.js +0 -48
- package/dist/outputLocation.js.map +1 -1
- package/dist/run-management/keptRun.d.ts +0 -107
- package/dist/run-management/keptRun.d.ts.map +1 -1
- package/dist/run-management/keptRun.js +0 -75
- package/dist/run-management/keptRun.js.map +1 -1
- package/dist/run-management/resolveRunInputs.d.ts +0 -67
- package/dist/run-management/resolveRunInputs.d.ts.map +1 -1
- package/dist/run-management/resolveRunInputs.js +0 -88
- package/dist/run-management/resolveRunInputs.js.map +1 -1
- package/dist/storedRun.d.ts +0 -21
- package/dist/storedRun.d.ts.map +1 -1
- package/dist/storedRun.js +0 -21
- package/dist/storedRun.js.map +1 -1
- package/package.json +2 -2
package/dist/host.js
CHANGED
|
@@ -1,46 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The host-invocation parse — the pure half of the `spec-controller` command host.
|
|
3
|
-
*
|
|
4
|
-
* The shipped binary is `spec-controller [--store <root>] [--run-id <id>] <command>
|
|
5
|
-
* [command-args]`. `--store` / `--run-id` are CROSS-CUTTING host options recognised
|
|
6
|
-
* ONLY in leading position, before the command: storage applies across balance /
|
|
7
|
-
* analyse / migrate, so it belongs at the host (wrapping any command via the kept-run
|
|
8
|
-
* scaffold), not as a peer command and never as a flag inside `balance`. This module
|
|
9
|
-
* holds the pure rules the impure dispatcher (`cli.ts`) drives — parsing the leading
|
|
10
|
-
* options, building the wrapped command argv, and peeking the conventional provenance
|
|
11
|
-
* flags — so the whole host contract is `@unit`-testable with no IO.
|
|
12
|
-
*/
|
|
13
1
|
import { asksForHelp, equalsFormRefusal, givenTwiceRefusal, isEqualsForm, isMissingValue, missingValueRefusal, valueFlagsOf, } from "./cli-args.js";
|
|
14
2
|
import { cliRegistry } from "./cli-registry.js";
|
|
15
|
-
/**
|
|
16
|
-
* Parse the host argv into its leading global options (`--store`, `--run-id`) and the
|
|
17
|
-
* command + its clean args. Global options are recognised ONLY before the command: the
|
|
18
|
-
* first token that is not a consumed global option (or its value) is the command, and
|
|
19
|
-
* everything after it is the command's own args, verbatim. So `--store /s balance
|
|
20
|
-
* --features f` yields the store + `balance` + `[--features, f]`, while `balance --store
|
|
21
|
-
* /s` leaves `--store` in the command's args (a `--store` AFTER the command is the
|
|
22
|
-
* command's own arg, never a host store directive) — the "global before command" contract.
|
|
23
|
-
* A modifier with nothing after it is refused; an empty argv is the bare help. A `--help` or `-h`
|
|
24
|
-
* among the host's own tokens answers the top-level help ahead of any refusal beside it.
|
|
25
|
-
*/
|
|
26
3
|
export function parseHostInvocation(argv, globalFlags = cliRegistry.global, commands = cliRegistry.commands.map((command) => command.name)) {
|
|
27
4
|
const read = readHostTokens(argv, globalFlags, commands);
|
|
28
5
|
const help = argv.slice(0, hostTokenCount(argv, read)).find((token) => TOP_LEVEL_HELP.has(token));
|
|
29
6
|
return help === undefined ? read : { command: help, commandArgs: [] };
|
|
30
7
|
}
|
|
31
|
-
/**
|
|
32
|
-
* How many leading tokens are the host's own: those before the command it hands the rest to, or
|
|
33
|
-
* every token when it hands them to none, because it refused one or found no command.
|
|
34
|
-
*/
|
|
35
8
|
function hostTokenCount(argv, read) {
|
|
36
9
|
if (read.refusal !== undefined || read.command === undefined)
|
|
37
10
|
return argv.length;
|
|
38
11
|
return argv.length - read.commandArgs.length - 1;
|
|
39
12
|
}
|
|
40
|
-
/** The host's tokens read in order: its modifiers, then the command and the command's own args. */
|
|
41
13
|
function readHostTokens(argv, globalFlags, commands) {
|
|
42
|
-
// The modifiers and the values they take are the registry's global scope, never a list kept here:
|
|
43
|
-
// a modifier added later is read, and refused without its value, by construction.
|
|
44
14
|
const modifiers = valueFlagsOf(globalFlags);
|
|
45
15
|
const values = new Map();
|
|
46
16
|
let i = 0;
|
|
@@ -51,7 +21,7 @@ function readHostTokens(argv, globalFlags, commands) {
|
|
|
51
21
|
const refusal = leadingFlagRefusal(token, modifiers, globalFlags);
|
|
52
22
|
if (refusal !== undefined)
|
|
53
23
|
return { commandArgs: [], refusal };
|
|
54
|
-
break;
|
|
24
|
+
break;
|
|
55
25
|
}
|
|
56
26
|
const value = argv[i + 1];
|
|
57
27
|
const refusal = modifierRefusal(token, modifier, argv.slice(i + 1, i + 3), { values, commands });
|
|
@@ -69,15 +39,6 @@ function readHostTokens(argv, globalFlags, commands) {
|
|
|
69
39
|
commandArgs: argv.slice(i + 1),
|
|
70
40
|
};
|
|
71
41
|
}
|
|
72
|
-
/**
|
|
73
|
-
* The refusal a modifier earns where it stands, else undefined; `ahead` is its value and the token
|
|
74
|
-
* after it. Given twice, it is never read as its last value: `--store A --store B` used
|
|
75
|
-
* to store under B without a word. Given no value, it is never handed the next token:
|
|
76
|
-
* `--store --run-id r1` used to store under a directory called `--run-id`. Given the command as its
|
|
77
|
-
* value with no command after it, it has eaten the command: `--store balance
|
|
78
|
-
* --features f` used to blame `--features`, and `--store balance` alone answered help at exit 0. A
|
|
79
|
-
* command name followed by the command is still a value: `--store balance balance …` is read.
|
|
80
|
-
*/
|
|
81
42
|
function modifierRefusal(token, modifier, [value, after], read) {
|
|
82
43
|
if (read.values.has(modifier.name))
|
|
83
44
|
return givenTwiceRefusal(token, "spec-controller");
|
|
@@ -88,29 +49,16 @@ function modifierRefusal(token, modifier, [value, after], read) {
|
|
|
88
49
|
}
|
|
89
50
|
return undefined;
|
|
90
51
|
}
|
|
91
|
-
/**
|
|
92
|
-
* The refusal for host modifiers with nothing after them: every modifier given, in one line, and the
|
|
93
|
-
* fix. Given last, `--store <root>` used to answer the top-level help at exit 0 having
|
|
94
|
-
* run nothing, a false green for a CI step that lost its command word. Help after a
|
|
95
|
-
* modifier is a command here, and a bare `spec-controller` never reaches this.
|
|
96
|
-
*/
|
|
97
52
|
function noCommandRefusal(given) {
|
|
98
53
|
return (`No command followed the host modifiers given (${given.join(", ")}), so nothing ran: ` +
|
|
99
54
|
`a host modifier needs a command after it, or --help.\n` +
|
|
100
55
|
`Run 'spec-controller --help' to see the commands and host modifiers this version accepts.`);
|
|
101
56
|
}
|
|
102
|
-
/** The refusal for a modifier that took the command as its value: the modifier, the command, and the fix. */
|
|
103
57
|
function commandAsValueRefusal(token, modifier, command) {
|
|
104
58
|
return (`The ${token} host modifier took '${command}', a command, as its value, and left no command to run.\n` +
|
|
105
59
|
`Give ${token} its own value, as in 'spec-controller ${token} ${modifier.arg ?? ""} ${command} …'. ` +
|
|
106
60
|
`Run 'spec-controller --help' to see the commands and host modifiers this version accepts.`);
|
|
107
61
|
}
|
|
108
|
-
/**
|
|
109
|
-
* The refusal a leading token that is no modifier earns, else undefined (it is the command). A
|
|
110
|
-
* modifier written `--store=<root>` is a form that is not read, never a flag out of
|
|
111
|
-
* place; any other flag here is out of place, never a command: `--exit-zero balance`
|
|
112
|
-
* used to answer `unknown command "--exit-zero"`. The top-level help stays where it was.
|
|
113
|
-
*/
|
|
114
62
|
function leadingFlagRefusal(token, modifiers, globalFlags) {
|
|
115
63
|
if (isEqualsForm(token))
|
|
116
64
|
return equalsFormRefusal(token, modifiers, "spec-controller");
|
|
@@ -118,13 +66,10 @@ function leadingFlagRefusal(token, modifiers, globalFlags) {
|
|
|
118
66
|
return flagBeforeCommandRefusal(token, globalFlags);
|
|
119
67
|
return undefined;
|
|
120
68
|
}
|
|
121
|
-
/** The help the host answers in the command's place, which is the one flag it takes there. */
|
|
122
69
|
const TOP_LEVEL_HELP = new Set(["--help", "-h"]);
|
|
123
|
-
/** True for a flag-shaped token that is not the top-level help. */
|
|
124
70
|
function isFlagOutOfPlace(token) {
|
|
125
71
|
return token.length > 1 && token.startsWith("-") && !TOP_LEVEL_HELP.has(token);
|
|
126
72
|
}
|
|
127
|
-
/** The refusal for a flag given before the command: the flag, what goes there, and where it goes. */
|
|
128
73
|
function flagBeforeCommandRefusal(flag, globalFlags) {
|
|
129
74
|
const modifiers = globalFlags.map((modifier) => modifier.name).join(", ");
|
|
130
75
|
return (`The ${flag} flag was given before the command. Before the command, spec-controller takes ` +
|
|
@@ -132,21 +77,9 @@ function flagBeforeCommandRefusal(flag, globalFlags) {
|
|
|
132
77
|
`'spec-controller <command> ${flag}'.\n` +
|
|
133
78
|
`Run 'spec-controller --help' to see the commands and host modifiers this version accepts.`);
|
|
134
79
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Build the wrapped command argv: the command's clean args followed by a repeated
|
|
137
|
-
* `--format <spec>` per store-dir output spec. Pure — the impure runner hands the result
|
|
138
|
-
* straight to the command, so the command's OWN writer produces the stored artefacts
|
|
139
|
-
* (byte-identical to a bare `--format` run; no writer duplication).
|
|
140
|
-
*/
|
|
141
80
|
export function wrapCommandArgv(commandArgs, formatSpecs) {
|
|
142
81
|
return [...commandArgs, ...formatSpecs.flatMap((spec) => ["--format", spec])];
|
|
143
82
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Read a `--<name> <value>` flag's value from an arg list WITHOUT consuming it — the host
|
|
146
|
-
* peeks the conventional cross-cutting provenance flags (`--target`, `--features`,
|
|
147
|
-
* `--source-sha`) to name the run dir + stamp run.yaml, while the command still receives
|
|
148
|
-
* them in its own args. Returns undefined when the flag is absent or has no value.
|
|
149
|
-
*/
|
|
150
83
|
export function peekFlag(argv, name) {
|
|
151
84
|
const flag = `--${name}`;
|
|
152
85
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -157,24 +90,12 @@ export function peekFlag(argv, name) {
|
|
|
157
90
|
}
|
|
158
91
|
return undefined;
|
|
159
92
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Answer a command the registry does not carry. It is given the command token and the rendered
|
|
162
|
-
* help, never the command's args, so no flag after the command can change the answer.
|
|
163
|
-
*
|
|
164
|
-
* An unknown command is a usage fault, so it exits 2, the published usage code
|
|
165
|
-
* (packages/spec-controller/README.md), which `--exit-zero` never masks.
|
|
166
|
-
*/
|
|
167
93
|
export function answerUnknownCommand(command, help) {
|
|
168
94
|
return {
|
|
169
95
|
exitCode: 2,
|
|
170
96
|
stderr: `spec-controller: unknown command "${command}".\n\n${help}\n`,
|
|
171
97
|
};
|
|
172
98
|
}
|
|
173
|
-
/**
|
|
174
|
-
* The refusal for host modifiers given before a command that takes none, else undefined. A request
|
|
175
|
-
* for that command's own help is answered instead, wherever the help token stands among its args:
|
|
176
|
-
* help runs nothing and keeps no run, so there is nothing for the modifiers to be refused from.
|
|
177
|
-
*/
|
|
178
99
|
export function modifiersNotTakenRefusal(inv, commands = cliRegistry.commands) {
|
|
179
100
|
const entry = commands.find((command) => command.name === inv.command);
|
|
180
101
|
if (entry?.acceptsHostModifiers !== false || asksForHelp(inv.commandArgs))
|
|
@@ -186,10 +107,6 @@ export function modifiersNotTakenRefusal(inv, commands = cliRegistry.commands) {
|
|
|
186
107
|
`it answers a query and keeps no run.\n` +
|
|
187
108
|
`Run 'spec-controller ${entry.name} …' without them.`);
|
|
188
109
|
}
|
|
189
|
-
/**
|
|
190
|
-
* The file paths a command's own `--format` values name: each value's text after its first colon,
|
|
191
|
-
* as `planOutput` reads it. A value with no colon goes to stdout and names no file.
|
|
192
|
-
*/
|
|
193
110
|
export function formatFilePaths(argv) {
|
|
194
111
|
const paths = [];
|
|
195
112
|
argv.forEach((token, index) => {
|
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,46 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve a CI input to the workflow files it names (@SCN-USG-006, 3F-2497).
|
|
3
|
-
*
|
|
4
|
-
* A target's pipeline is routinely split across files — a nightly sweep, a security
|
|
5
|
-
* scan — and reading one of them scores the rest against the target twice over: a
|
|
6
|
-
* NAMED check enforced elsewhere is called a watermelon it is not, an UNNAMED one
|
|
7
|
-
* never reaches the books at all (3F-2238).
|
|
8
|
-
*
|
|
9
|
-
* THIS IS THE ONLY PLACE THAT KNOWS A CI INPUT CAN BE A DIRECTORY. The core takes
|
|
10
|
-
* anonymous source strings so a second ecosystem can supply its own; teaching it to
|
|
11
|
-
* walk a folder would import a filesystem and one provider's layout into a pure
|
|
12
|
-
* parser and close that seam for good. So the walk lives here, at the edge that
|
|
13
|
-
* already resolved `.github/workflows/ci.yml`, and the edge knows exactly as much
|
|
14
|
-
* about GitHub after this slice as it did before.
|
|
15
|
-
*
|
|
16
|
-
* Pure, with the filesystem injected — the resolution rules are the part worth
|
|
17
|
-
* asserting directly, and a fixture tree can only show one of them going wrong at a
|
|
18
|
-
* time.
|
|
19
|
-
*/
|
|
20
|
-
/** The filesystem reads the resolution needs, injected so the rules stay pure. */
|
|
21
1
|
export interface CiPathReader {
|
|
22
2
|
exists(path: string): boolean;
|
|
23
3
|
isDirectory(path: string): boolean;
|
|
24
4
|
readDir(path: string): string[];
|
|
25
5
|
}
|
|
26
|
-
/** The real filesystem, for the CLI edge. */
|
|
27
6
|
export declare const NODE_CI_PATH_READER: CiPathReader;
|
|
28
|
-
/**
|
|
29
|
-
* The workflow files a CI input names: every workflow in a directory, or the single
|
|
30
|
-
* file itself.
|
|
31
|
-
*
|
|
32
|
-
* Sorted, so two runs over one tree cannot differ and a diff of two reports never
|
|
33
|
-
* turns on `readdir` ordering.
|
|
34
|
-
*
|
|
35
|
-
* A FILE RESOLVES TO ITSELF ALONE. Sweeping its parent directory would silently widen
|
|
36
|
-
* a caller's deliberate scope, which is the promise @SCN-USG-008 pins at the report level.
|
|
37
|
-
*
|
|
38
|
-
* An absent path resolves to nothing — no sources, no commands, every static check
|
|
39
|
-
* read as unenforced. That over-reports gaps and hides none, the same fail-safe
|
|
40
|
-
* direction `ciRunCommands` takes on a document it cannot parse. The HARD error for a
|
|
41
|
-
* path the caller SUPPLIED and that does not exist is @SCN-CLI-002's, raised upstream;
|
|
42
|
-
* reaching here with nothing means the DEFAULT found no workflows, which is a fact
|
|
43
|
-
* about the target rather than a mistake by its author.
|
|
44
|
-
*/
|
|
45
7
|
export declare function ciSourcePathsOf(ciPath: string, reader?: CiPathReader): string[];
|
|
46
8
|
//# sourceMappingURL=ciSourcePaths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ciSourcePaths.d.ts","sourceRoot":"","sources":["../../src/ingest/ciSourcePaths.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ciSourcePaths.d.ts","sourceRoot":"","sources":["../../src/ingest/ciSourcePaths.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACjC;AAED,eAAO,MAAM,mBAAmB,EAAE,YAIjC,CAAC;AAIF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,YAAkC,GACzC,MAAM,EAAE,CAQV"}
|
|
@@ -1,49 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve a CI input to the workflow files it names (@SCN-USG-006, 3F-2497).
|
|
3
|
-
*
|
|
4
|
-
* A target's pipeline is routinely split across files — a nightly sweep, a security
|
|
5
|
-
* scan — and reading one of them scores the rest against the target twice over: a
|
|
6
|
-
* NAMED check enforced elsewhere is called a watermelon it is not, an UNNAMED one
|
|
7
|
-
* never reaches the books at all (3F-2238).
|
|
8
|
-
*
|
|
9
|
-
* THIS IS THE ONLY PLACE THAT KNOWS A CI INPUT CAN BE A DIRECTORY. The core takes
|
|
10
|
-
* anonymous source strings so a second ecosystem can supply its own; teaching it to
|
|
11
|
-
* walk a folder would import a filesystem and one provider's layout into a pure
|
|
12
|
-
* parser and close that seam for good. So the walk lives here, at the edge that
|
|
13
|
-
* already resolved `.github/workflows/ci.yml`, and the edge knows exactly as much
|
|
14
|
-
* about GitHub after this slice as it did before.
|
|
15
|
-
*
|
|
16
|
-
* Pure, with the filesystem injected — the resolution rules are the part worth
|
|
17
|
-
* asserting directly, and a fixture tree can only show one of them going wrong at a
|
|
18
|
-
* time.
|
|
19
|
-
*/
|
|
20
1
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
21
2
|
import { join } from "node:path";
|
|
22
|
-
/** The real filesystem, for the CLI edge. */
|
|
23
3
|
export const NODE_CI_PATH_READER = {
|
|
24
4
|
exists: existsSync,
|
|
25
5
|
isDirectory: (path) => statSync(path).isDirectory(),
|
|
26
6
|
readDir: readdirSync,
|
|
27
7
|
};
|
|
28
|
-
/** Both YAML spellings: a target picks one, and the tool does not get to mind which. */
|
|
29
8
|
const WORKFLOW_EXTENSIONS = [".yml", ".yaml"];
|
|
30
|
-
/**
|
|
31
|
-
* The workflow files a CI input names: every workflow in a directory, or the single
|
|
32
|
-
* file itself.
|
|
33
|
-
*
|
|
34
|
-
* Sorted, so two runs over one tree cannot differ and a diff of two reports never
|
|
35
|
-
* turns on `readdir` ordering.
|
|
36
|
-
*
|
|
37
|
-
* A FILE RESOLVES TO ITSELF ALONE. Sweeping its parent directory would silently widen
|
|
38
|
-
* a caller's deliberate scope, which is the promise @SCN-USG-008 pins at the report level.
|
|
39
|
-
*
|
|
40
|
-
* An absent path resolves to nothing — no sources, no commands, every static check
|
|
41
|
-
* read as unenforced. That over-reports gaps and hides none, the same fail-safe
|
|
42
|
-
* direction `ciRunCommands` takes on a document it cannot parse. The HARD error for a
|
|
43
|
-
* path the caller SUPPLIED and that does not exist is @SCN-CLI-002's, raised upstream;
|
|
44
|
-
* reaching here with nothing means the DEFAULT found no workflows, which is a fact
|
|
45
|
-
* about the target rather than a mistake by its author.
|
|
46
|
-
*/
|
|
47
9
|
export function ciSourcePathsOf(ciPath, reader = NODE_CI_PATH_READER) {
|
|
48
10
|
if (!reader.exists(ciPath))
|
|
49
11
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ciSourcePaths.js","sourceRoot":"","sources":["../../src/ingest/ciSourcePaths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ciSourcePaths.js","sourceRoot":"","sources":["../../src/ingest/ciSourcePaths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,MAAM,EAAE,UAAU;IAClB,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;IACnD,OAAO,EAAE,WAAW;CACrB,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,SAAuB,mBAAmB;IAE1C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,OAAO,MAAM;SACV,OAAO,CAAC,MAAM,CAAC;SACf,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;SACvE,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -1,89 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Gherkin VALIDATOR — the feature corpus's integrity checkpoint (@SCN-CLI-016, 3F-1767).
|
|
3
|
-
*
|
|
4
|
-
* WHY THIS EXISTS. The feature corpus is the ledger's DEBIT side, and it was read by a
|
|
5
|
-
* hand-rolled TAG-LINE SCAN (`parseScenarios`) that never looked for a `Scenario:` header at
|
|
6
|
-
* all: a scenario EXISTED purely because an `@SCN-FFF-NNN` tag line did. So a
|
|
7
|
-
* malformed tag line simply STOPPED BEING A TAG LINE, and its scenario ceased to exist —
|
|
8
|
-
* silently. One stray character, in a file cucumber itself rejects, deleted a failing row and
|
|
9
|
-
* turned an out-of-balance run (exit 1) into a clean BALANCED (exit 0). A dropped scenario
|
|
10
|
-
* cannot even reconcile `missing`, because it was never raised to be missing: the run reads
|
|
11
|
-
* CLEANER THAN REALITY. That is the false-green this tool exists to prevent, aimed at itself.
|
|
12
|
-
*
|
|
13
|
-
* THE FIX (this slice). VALIDATE every feature file with the REAL Gherkin parser — the same one
|
|
14
|
-
* cucumber uses. A file the parser rejects is an input the tool CANNOT READ, and the CLI refuses
|
|
15
|
-
* it: exit 2, halting before reconciling (@SCN-CLI-014/015's Rule — "an input the tool cannot read
|
|
16
|
-
* is a usage error, never a verdict" — extended from the OBSERVATION side to the OBLIGATION side).
|
|
17
|
-
*
|
|
18
|
-
* VALIDATION IS HALF THE STORY. This decides whether a feature file can be READ at all; the corpus
|
|
19
|
-
* is then EXTRACTED from the same real AST by `parseScenarios` (@SCN-LDG-020, 3F-1775), so the
|
|
20
|
-
* scenarios spec-controller reconciles carry exactly the tags cucumber gives them — Feature/Rule
|
|
21
|
-
* tag inheritance included. One reading of the file, not two.
|
|
22
|
-
*
|
|
23
|
-
* PURE: `string → parse errors`. No fs, no process — the file READ lives beside it at the ingest
|
|
24
|
-
* edge, as it does for every other input.
|
|
25
|
-
*
|
|
26
|
-
* WHY IT LIVES IN THE APP, NOT CORE. Core's barrel is the v1 contract surface downstream consumers
|
|
27
|
-
* build on. Since 3F-3449 nothing reds when a symbol is added there — the guard that reconciled it
|
|
28
|
-
* both directions is gone — so keeping it narrow is a discipline rather than a gate. This validator
|
|
29
|
-
* is an INGEST concern (it decides whether an input can be read at all), not part of the
|
|
30
|
-
* reconciliation contract, so it belongs with the other readers at the app edge and the v1 surface
|
|
31
|
-
* stays untouched.
|
|
32
|
-
*/
|
|
33
|
-
/** A Gherkin parse failure: the file could not be read as Gherkin at all. */
|
|
34
1
|
export interface GherkinParseError {
|
|
35
|
-
/** The offending file, as supplied (the operator's locator — what to go and fix). */
|
|
36
2
|
file: string;
|
|
37
|
-
/** The parser's own message, verbatim — WHAT broke, in the parser's words. */
|
|
38
3
|
message: string;
|
|
39
4
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Validate one `.feature` source with the real Gherkin parser. Returns the parse errors it
|
|
42
|
-
* reports — empty when the file is well-formed Gherkin.
|
|
43
|
-
*
|
|
44
|
-
* The parser is authoritative BY CONSTRUCTION: it is cucumber's own. A file it rejects is one
|
|
45
|
-
* cucumber would refuse to RUN, so a scenario corpus built from it would be a FICTION — which is
|
|
46
|
-
* precisely how a corrupt spec used to read balanced.
|
|
47
|
-
*
|
|
48
|
-
* A parse failure is reported, never thrown: the CLI edge routes it to stderr + exit 2 (the
|
|
49
|
-
* shared typed-error surface), so it can never reach Node's default exit 1 and masquerade as an
|
|
50
|
-
* out-of-balance verdict (@SCN-CLI-014's collision, on the obligation side).
|
|
51
|
-
*
|
|
52
|
-
* @SCN-CLI-016 (3F-1773).
|
|
53
|
-
*/
|
|
54
5
|
export declare function parseErrorsIn(file: string, source: string): GherkinParseError[];
|
|
55
|
-
/**
|
|
56
|
-
* The corpus-level REFUSAL (@SCN-GPG-001, 3F-2817). A parse failure is reported per file by
|
|
57
|
-
* `parseErrorsIn` above; this is how a reader of the WHOLE corpus says no.
|
|
58
|
-
*
|
|
59
|
-
* WHY IT CARRIES EVERY FILE. The balance CLI already names every unparseable file it found
|
|
60
|
-
* (@SCN-CLI-016), and a refusal raised at the first bad one would silently narrow that message —
|
|
61
|
-
* a behaviour change dressed as an implementation detail. So the discovery collects the corpus
|
|
62
|
-
* and refuses ONCE, carrying the lot in discovery order.
|
|
63
|
-
*
|
|
64
|
-
* The rendered message is the parser's words, per file, verbatim: a reader must be told WHAT
|
|
65
|
-
* broke by the parser rather than by our paraphrase of it.
|
|
66
|
-
*/
|
|
67
6
|
export declare class UnreadableFeatureCorpusError extends Error {
|
|
68
7
|
readonly errors: readonly GherkinParseError[];
|
|
69
8
|
constructor(errors: readonly GherkinParseError[]);
|
|
70
9
|
}
|
|
71
|
-
/**
|
|
72
|
-
* The corpus-level ABSENCE refusal (@SCN-CLI-017, 3F-3345) — sibling to the parse refusal above,
|
|
73
|
-
* and deliberately the same shape: the reader refuses ONCE, and each command decides what the
|
|
74
|
-
* refusal means for it.
|
|
75
|
-
*
|
|
76
|
-
* WHY A TYPED ERROR RATHER THAN AN EMPTY CORPUS. The obvious fix is for `discoverFeatures` to hand
|
|
77
|
-
* back `[]` when the directory is not there, and that would decide for every reader that absence
|
|
78
|
-
* is empty. Typed, each caller decides: `balance` reads it as no corpus (@SCN-CLI-017), and any
|
|
79
|
-
* other reader still meets a refusal. (The second command this guarded, `tags`, left at 3F-3635.)
|
|
80
|
-
*
|
|
81
|
-
* IT CARRIES THE DIRECTORY because the report is owed the name of what it examined — "I found no
|
|
82
|
-
* record" and "I found no regression" must never wear one word (3F-2947), and a consumer told
|
|
83
|
-
* their corpus is empty deserves to know which directory was read to reach that.
|
|
84
|
-
*/
|
|
85
10
|
export declare class MissingFeatureCorpusError extends Error {
|
|
86
|
-
/** The directory that is not there — as resolved, so a reader can print the path examined. */
|
|
87
11
|
readonly directory: string;
|
|
88
12
|
constructor(directory: string);
|
|
89
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gherkinValidation.d.ts","sourceRoot":"","sources":["../../src/ingest/gherkinValidation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gherkinValidation.d.ts","sourceRoot":"","sources":["../../src/ingest/gherkinValidation.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAU/E;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;gBAElC,MAAM,EAAE,SAAS,iBAAiB,EAAE;CAUjD;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAK9B"}
|
|
@@ -1,51 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Gherkin VALIDATOR — the feature corpus's integrity checkpoint (@SCN-CLI-016, 3F-1767).
|
|
3
|
-
*
|
|
4
|
-
* WHY THIS EXISTS. The feature corpus is the ledger's DEBIT side, and it was read by a
|
|
5
|
-
* hand-rolled TAG-LINE SCAN (`parseScenarios`) that never looked for a `Scenario:` header at
|
|
6
|
-
* all: a scenario EXISTED purely because an `@SCN-FFF-NNN` tag line did. So a
|
|
7
|
-
* malformed tag line simply STOPPED BEING A TAG LINE, and its scenario ceased to exist —
|
|
8
|
-
* silently. One stray character, in a file cucumber itself rejects, deleted a failing row and
|
|
9
|
-
* turned an out-of-balance run (exit 1) into a clean BALANCED (exit 0). A dropped scenario
|
|
10
|
-
* cannot even reconcile `missing`, because it was never raised to be missing: the run reads
|
|
11
|
-
* CLEANER THAN REALITY. That is the false-green this tool exists to prevent, aimed at itself.
|
|
12
|
-
*
|
|
13
|
-
* THE FIX (this slice). VALIDATE every feature file with the REAL Gherkin parser — the same one
|
|
14
|
-
* cucumber uses. A file the parser rejects is an input the tool CANNOT READ, and the CLI refuses
|
|
15
|
-
* it: exit 2, halting before reconciling (@SCN-CLI-014/015's Rule — "an input the tool cannot read
|
|
16
|
-
* is a usage error, never a verdict" — extended from the OBSERVATION side to the OBLIGATION side).
|
|
17
|
-
*
|
|
18
|
-
* VALIDATION IS HALF THE STORY. This decides whether a feature file can be READ at all; the corpus
|
|
19
|
-
* is then EXTRACTED from the same real AST by `parseScenarios` (@SCN-LDG-020, 3F-1775), so the
|
|
20
|
-
* scenarios spec-controller reconciles carry exactly the tags cucumber gives them — Feature/Rule
|
|
21
|
-
* tag inheritance included. One reading of the file, not two.
|
|
22
|
-
*
|
|
23
|
-
* PURE: `string → parse errors`. No fs, no process — the file READ lives beside it at the ingest
|
|
24
|
-
* edge, as it does for every other input.
|
|
25
|
-
*
|
|
26
|
-
* WHY IT LIVES IN THE APP, NOT CORE. Core's barrel is the v1 contract surface downstream consumers
|
|
27
|
-
* build on. Since 3F-3449 nothing reds when a symbol is added there — the guard that reconciled it
|
|
28
|
-
* both directions is gone — so keeping it narrow is a discipline rather than a gate. This validator
|
|
29
|
-
* is an INGEST concern (it decides whether an input can be read at all), not part of the
|
|
30
|
-
* reconciliation contract, so it belongs with the other readers at the app edge and the v1 surface
|
|
31
|
-
* stays untouched.
|
|
32
|
-
*/
|
|
33
1
|
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from "@cucumber/gherkin";
|
|
34
2
|
import { IdGenerator } from "@cucumber/messages";
|
|
35
|
-
/**
|
|
36
|
-
* Validate one `.feature` source with the real Gherkin parser. Returns the parse errors it
|
|
37
|
-
* reports — empty when the file is well-formed Gherkin.
|
|
38
|
-
*
|
|
39
|
-
* The parser is authoritative BY CONSTRUCTION: it is cucumber's own. A file it rejects is one
|
|
40
|
-
* cucumber would refuse to RUN, so a scenario corpus built from it would be a FICTION — which is
|
|
41
|
-
* precisely how a corrupt spec used to read balanced.
|
|
42
|
-
*
|
|
43
|
-
* A parse failure is reported, never thrown: the CLI edge routes it to stderr + exit 2 (the
|
|
44
|
-
* shared typed-error surface), so it can never reach Node's default exit 1 and masquerade as an
|
|
45
|
-
* out-of-balance verdict (@SCN-CLI-014's collision, on the obligation side).
|
|
46
|
-
*
|
|
47
|
-
* @SCN-CLI-016 (3F-1773).
|
|
48
|
-
*/
|
|
49
3
|
export function parseErrorsIn(file, source) {
|
|
50
4
|
const parser = new Parser(new AstBuilder(IdGenerator.uuid()), new GherkinClassicTokenMatcher());
|
|
51
5
|
try {
|
|
@@ -53,25 +7,10 @@ export function parseErrorsIn(file, source) {
|
|
|
53
7
|
return [];
|
|
54
8
|
}
|
|
55
9
|
catch (err) {
|
|
56
|
-
// The parser raises a CompositeParserException carrying every error it found; its message
|
|
57
|
-
// is the operator's actionable payload (line, column, and what it expected). Reported
|
|
58
|
-
// verbatim — the tool must not paraphrase the parser and risk describing a fault wrongly.
|
|
59
10
|
const message = err instanceof Error ? err.message : String(err);
|
|
60
11
|
return [{ file, message }];
|
|
61
12
|
}
|
|
62
13
|
}
|
|
63
|
-
/**
|
|
64
|
-
* The corpus-level REFUSAL (@SCN-GPG-001, 3F-2817). A parse failure is reported per file by
|
|
65
|
-
* `parseErrorsIn` above; this is how a reader of the WHOLE corpus says no.
|
|
66
|
-
*
|
|
67
|
-
* WHY IT CARRIES EVERY FILE. The balance CLI already names every unparseable file it found
|
|
68
|
-
* (@SCN-CLI-016), and a refusal raised at the first bad one would silently narrow that message —
|
|
69
|
-
* a behaviour change dressed as an implementation detail. So the discovery collects the corpus
|
|
70
|
-
* and refuses ONCE, carrying the lot in discovery order.
|
|
71
|
-
*
|
|
72
|
-
* The rendered message is the parser's words, per file, verbatim: a reader must be told WHAT
|
|
73
|
-
* broke by the parser rather than by our paraphrase of it.
|
|
74
|
-
*/
|
|
75
14
|
export class UnreadableFeatureCorpusError extends Error {
|
|
76
15
|
errors;
|
|
77
16
|
constructor(errors) {
|
|
@@ -83,22 +22,7 @@ export class UnreadableFeatureCorpusError extends Error {
|
|
|
83
22
|
this.errors = errors;
|
|
84
23
|
}
|
|
85
24
|
}
|
|
86
|
-
/**
|
|
87
|
-
* The corpus-level ABSENCE refusal (@SCN-CLI-017, 3F-3345) — sibling to the parse refusal above,
|
|
88
|
-
* and deliberately the same shape: the reader refuses ONCE, and each command decides what the
|
|
89
|
-
* refusal means for it.
|
|
90
|
-
*
|
|
91
|
-
* WHY A TYPED ERROR RATHER THAN AN EMPTY CORPUS. The obvious fix is for `discoverFeatures` to hand
|
|
92
|
-
* back `[]` when the directory is not there, and that would decide for every reader that absence
|
|
93
|
-
* is empty. Typed, each caller decides: `balance` reads it as no corpus (@SCN-CLI-017), and any
|
|
94
|
-
* other reader still meets a refusal. (The second command this guarded, `tags`, left at 3F-3635.)
|
|
95
|
-
*
|
|
96
|
-
* IT CARRIES THE DIRECTORY because the report is owed the name of what it examined — "I found no
|
|
97
|
-
* record" and "I found no regression" must never wear one word (3F-2947), and a consumer told
|
|
98
|
-
* their corpus is empty deserves to know which directory was read to reach that.
|
|
99
|
-
*/
|
|
100
25
|
export class MissingFeatureCorpusError extends Error {
|
|
101
|
-
/** The directory that is not there — as resolved, so a reader can print the path examined. */
|
|
102
26
|
directory;
|
|
103
27
|
constructor(directory) {
|
|
104
28
|
super(`The feature corpus directory does not exist: ${directory}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gherkinValidation.js","sourceRoot":"","sources":["../../src/ingest/gherkinValidation.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gherkinValidation.js","sourceRoot":"","sources":["../../src/ingest/gherkinValidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOjD,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,MAAc;IACxD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,0BAA0B,EAAE,CAAC,CAAC;IAEhG,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAC5C,MAAM,CAA+B;IAE9C,YAAY,MAAoC;QAC9C,KAAK,CACH;YACE,yEAAyE;YACzE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;SAC5D,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IACzC,SAAS,CAAS;IAE3B,YAAY,SAAiB;QAC3B,KAAK,CAAC,gDAAgD,SAAS,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -1,105 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ingest the target's quality-check inputs — the IMPURE reads behind the balance
|
|
3
|
-
* command (split out of the old cli.ts). Reads evidence
|
|
4
|
-
* obligations from the repo's feature files, observed evidence from the Vitest +
|
|
5
|
-
* Cucumber result JSON, and the static-check evidence from the CI config + package.json,
|
|
6
|
-
* turning each into the engine's in-memory model. The orchestration (arg parsing, the
|
|
7
|
-
* output plan/write, main) stays in cli-balance/cli.ts; these are the file reads it calls.
|
|
8
|
-
*
|
|
9
|
-
* Configurable input paths (no adapter registry in v1 — the readers are named, not
|
|
10
|
-
* registered). Missing result files are tolerated: their evidence is simply absent
|
|
11
|
-
* (the scenario reconciles as missing for that level). The static check kind reads
|
|
12
|
-
* `--ci` (a workflow file or a directory of them, default `.github/workflows`) +
|
|
13
|
-
* `package.json` to observe each fitness check the corpus cites.
|
|
14
|
-
*/
|
|
15
1
|
import { type GherkinParseError } from "./gherkinValidation.js";
|
|
16
2
|
import { type EvidenceObligations } from "@3f-consulting/spec-controller-core";
|
|
17
3
|
import type { EvidenceReconciliation } from "@3f-consulting/spec-controller-core";
|
|
18
|
-
/**
|
|
19
|
-
* The reconciler's resolved input paths — the CLI options threaded to the readers.
|
|
20
|
-
* Every path is optional; a missing result file is tolerated (its evidence is
|
|
21
|
-
* simply absent). `packageJson` is the EXTERNALLY-SPECIFIED package.json the static
|
|
22
|
-
* static-check reader reads `exists` from (the --package-json seam):
|
|
23
|
-
* default the tool's own cwd `package.json`; set it to reconcile ANOTHER repo's
|
|
24
|
-
* static checks — required to render the historical watermelon correctly.
|
|
25
|
-
*/
|
|
26
4
|
export interface ReconcileOptions {
|
|
27
5
|
features?: string;
|
|
28
6
|
vitest?: string;
|
|
29
7
|
cucumber?: string;
|
|
30
8
|
ci?: string;
|
|
31
9
|
packageJson?: string;
|
|
32
|
-
/**
|
|
33
|
-
* The CI RUN'S OWN RECORD of what each of its steps did — the credit
|
|
34
|
-
* side of every CONVERTED fitness check, written by the consumer's own pipeline and by neither
|
|
35
|
-
* party to this reconciliation.
|
|
36
|
-
*
|
|
37
|
-
* KEPT APART FROM `ci` BECAUSE THE TWO DOCUMENTS ANSWER DIFFERENT QUESTIONS. The workflow FILE
|
|
38
|
-
* says a check is wired into this pipeline and which row its step cites — a fact about a
|
|
39
|
-
* repository, true between runs. This is one RUN's record: did it run this time, and what did it
|
|
40
|
-
* find. Optional like the other result files: a consumer whose pipeline dumps no record, or a
|
|
41
|
-
* local run with no pipeline at all, hands none, and the converted rows it would have answered
|
|
42
|
-
* read `missing`.
|
|
43
|
-
*/
|
|
44
10
|
ciSteps?: string;
|
|
45
11
|
}
|
|
46
|
-
/** Where the CI workflows are read from when `--ci` is not given. */
|
|
47
12
|
export declare const DEFAULT_CI_PATH = ".github/workflows";
|
|
48
|
-
/** The target's manifest when `--package-json` is not given: the one in the directory the run is in. */
|
|
49
13
|
export declare const DEFAULT_PACKAGE_JSON = "package.json";
|
|
50
|
-
/**
|
|
51
|
-
* The feature corpus's INTEGRITY CHECKPOINT — validate every discovered
|
|
52
|
-
* `.feature` with the REAL Gherkin parser and report the files it cannot read.
|
|
53
|
-
*
|
|
54
|
-
* The feature corpus is the ledger's DEBIT side, and it used to be read by a tag-line scan that
|
|
55
|
-
* never consulted a parser — so a malformed tag line silently deleted its scenario, and a corrupt
|
|
56
|
-
* spec could read BALANCED. A file the parser rejects is one cucumber would refuse to run: the
|
|
57
|
-
* CLI halts on it (exit 2) rather than reconciling a corpus that is a fiction.
|
|
58
|
-
*
|
|
59
|
-
* THIS IS NOW THE CATCH SITE, NOT THE CALL SITE. The validation moved
|
|
60
|
-
* into `discoverFeatures`, which REFUSES an unreadable corpus rather than returning one, so every
|
|
61
|
-
* corpus reader inherits the refusal instead of each restating it. What is left here is the
|
|
62
|
-
* translation back into the list this CLI edge already prints — same files, same order, same
|
|
63
|
-
* verbatim parser messages, so `balance`'s behaviour is unchanged. Anything that is not a corpus
|
|
64
|
-
* refusal is not ours to interpret and rethrows.
|
|
65
|
-
*/
|
|
66
14
|
export declare function featureCorpusParseErrors(featuresDir: string | undefined): GherkinParseError[];
|
|
67
|
-
/** A read of the feature corpus or the CI workflows that the operating system refused. */
|
|
68
15
|
export interface InputReadFault {
|
|
69
16
|
readonly path: string;
|
|
70
17
|
readonly reason: string;
|
|
71
18
|
}
|
|
72
|
-
/**
|
|
73
|
-
* The read of the feature corpus that failed, taken by the same discovery the reconcile uses, or
|
|
74
|
-
* null when it reads. A file the Gherkin parser rejects is not this fault: it is reported by
|
|
75
|
-
* `featureCorpusParseErrors`, after this.
|
|
76
|
-
*/
|
|
77
19
|
export declare function corpusReadFault(featuresDir: string | undefined): InputReadFault | null;
|
|
78
|
-
/** The read of a CI workflow that failed, over the same files the reconcile reads, or null when they all read. */
|
|
79
20
|
export declare function ciReadFault(ciPath: string | undefined): InputReadFault | null;
|
|
80
|
-
/**
|
|
81
|
-
* Read the target's `feature-code → slug` map over the same `discoverFeatures` corpus
|
|
82
|
-
* — each coded feature file's `@<FFF>` header (`featureCode`)
|
|
83
|
-
* mapped to its filename `slug`, the full feature name. This is the mapping
|
|
84
|
-
* `scenariosFromFeatures` discards (it keeps only the parsed scenarios). A sibling read to
|
|
85
|
-
* the balance, threaded to `renderMarkdown` (via `writeOutputs`) as the 5th sibling arg
|
|
86
|
-
* (markdown-only — the by-feature cut is markdown-only, so it is NOT serialised to JSON).
|
|
87
|
-
* Uncoded files (`featureCode === null`) are omitted, so the by-feature emitter falls
|
|
88
|
-
* back to the bare `FFF` for any id whose code no file registers. When two files carry one
|
|
89
|
-
* code the map keeps the last, and that run lists `duplicate-feature-code` under Scenario code
|
|
90
|
-
* errors and fails, so a report whose map is ambiguous never passes.
|
|
91
|
-
*/
|
|
92
21
|
export declare function readFeatureNames(featuresDir?: string): Map<string, string>;
|
|
93
|
-
/**
|
|
94
|
-
* Read the scenario-corpus census over the discovered feature files (the same
|
|
95
|
-
* `discoverFeatures` corpus `scenariosFromFeatures` reads): sum the raw @SCN
|
|
96
|
-
* occurrences (`countScnOccurrences`) and the scenario count
|
|
97
|
-
* (`parseScenarios(...).length`) across every file, and build the `EvidenceObligations`
|
|
98
|
-
* sibling. Also collects the UNTAGGED scenarios
|
|
99
|
-
* — `parseUntaggedScenarios` per file, each paired with its feature-file
|
|
100
|
-
* locator, sorted by (feature, scenario). Read alongside the balance in `main()` and
|
|
101
|
-
* threaded to the renderers via `writeOutputs`; `runReconcile` stays unchanged.
|
|
102
|
-
*/
|
|
103
22
|
export declare function readEvidenceObligations(features?: string): EvidenceObligations;
|
|
104
23
|
export declare function runReconcile(opts: ReconcileOptions): EvidenceReconciliation;
|
|
105
24
|
//# sourceMappingURL=ingestQualityChecks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingestQualityChecks.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ingestQualityChecks.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,qCAAqC,CAAC;AAY7C,OAAO,KAAK,EAIV,sBAAsB,EACvB,MAAM,qCAAqC,CAAC;AAO7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAkCnD,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,EAAE,CAQ7F;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAQD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CAUtF;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CAU7E;AAYD,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAM1E;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAqB9E;AAmBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,CA2B3E"}
|