spec-controller 0.1.0-alpha.5 → 0.1.0-alpha.51
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/README.md +34 -6
- package/dist/cli-allocate/cli.d.ts +2 -0
- package/dist/cli-allocate/cli.d.ts.map +1 -0
- package/dist/cli-allocate/cli.js +60 -0
- package/dist/cli-allocate/cli.js.map +1 -0
- package/dist/cli-allocate/readHistory.d.ts +15 -0
- package/dist/cli-allocate/readHistory.d.ts.map +1 -0
- package/dist/cli-allocate/readHistory.js +108 -0
- package/dist/cli-allocate/readHistory.js.map +1 -0
- package/dist/cli-allocate/readParts.d.ts +2 -0
- package/dist/cli-allocate/readParts.d.ts.map +1 -0
- package/dist/cli-allocate/readParts.js +22 -0
- package/dist/cli-allocate/readParts.js.map +1 -0
- package/dist/cli-allocate/readTree.d.ts +5 -0
- package/dist/cli-allocate/readTree.d.ts.map +1 -0
- package/dist/cli-allocate/readTree.js +74 -0
- package/dist/cli-allocate/readTree.js.map +1 -0
- package/dist/cli-allocate/refStore.d.ts +2 -0
- package/dist/cli-allocate/refStore.d.ts.map +1 -0
- package/dist/cli-allocate/refStore.js +34 -0
- package/dist/cli-allocate/refStore.js.map +1 -0
- package/dist/cli-allocate/request.d.ts +9 -0
- package/dist/cli-allocate/request.d.ts.map +1 -0
- package/dist/cli-allocate/request.js +22 -0
- package/dist/cli-allocate/request.js.map +1 -0
- package/dist/cli-allocate/stderr.d.ts +7 -0
- package/dist/cli-allocate/stderr.d.ts.map +1 -0
- package/dist/cli-allocate/stderr.js +26 -0
- package/dist/cli-allocate/stderr.js.map +1 -0
- package/dist/cli-allocate/unreadable.d.ts +11 -0
- package/dist/cli-allocate/unreadable.d.ts.map +1 -0
- package/dist/cli-allocate/unreadable.js +30 -0
- package/dist/cli-allocate/unreadable.js.map +1 -0
- package/dist/cli-args.d.ts +12 -70
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +158 -81
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-balance/cli.d.ts +1 -81
- package/dist/cli-balance/cli.d.ts.map +1 -1
- package/dist/cli-balance/cli.js +71 -357
- package/dist/cli-balance/cli.js.map +1 -1
- package/dist/cli-balance/emit/format.d.ts +16 -38
- package/dist/cli-balance/emit/format.d.ts.map +1 -1
- package/dist/cli-balance/emit/format.js +31 -58
- package/dist/cli-balance/emit/format.js.map +1 -1
- package/dist/cli-balance/emit/writer.d.ts +2 -32
- package/dist/cli-balance/emit/writer.d.ts.map +1 -1
- package/dist/cli-balance/emit/writer.js +50 -37
- package/dist/cli-balance/emit/writer.js.map +1 -1
- package/dist/cli-balance/exitStatus.d.ts +2 -0
- package/dist/cli-balance/exitStatus.d.ts.map +1 -0
- package/dist/cli-balance/exitStatus.js +5 -0
- package/dist/cli-balance/exitStatus.js.map +1 -0
- package/dist/cli-balance/inputsRead.d.ts +11 -0
- package/dist/cli-balance/inputsRead.d.ts.map +1 -0
- package/dist/cli-balance/inputsRead.js +53 -0
- package/dist/cli-balance/inputsRead.js.map +1 -0
- package/dist/cli-balance/reRender.d.ts +8 -0
- package/dist/cli-balance/reRender.d.ts.map +1 -0
- package/dist/cli-balance/reRender.js +21 -0
- package/dist/cli-balance/reRender.js.map +1 -0
- package/dist/cli-balance/unreadableInputs.d.ts +14 -0
- package/dist/cli-balance/unreadableInputs.d.ts.map +1 -0
- package/dist/cli-balance/unreadableInputs.js +80 -0
- package/dist/cli-balance/unreadableInputs.js.map +1 -0
- package/dist/cli-registry.d.ts +12 -70
- package/dist/cli-registry.d.ts.map +1 -1
- package/dist/cli-registry.js +37 -97
- package/dist/cli-registry.js.map +1 -1
- package/dist/cli.d.ts +0 -21
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +62 -55
- 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 +10 -41
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +103 -50
- 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 -80
- package/dist/ingest/gherkinValidation.d.ts.map +1 -1
- package/dist/ingest/gherkinValidation.js +0 -80
- package/dist/ingest/gherkinValidation.js.map +1 -1
- package/dist/ingest/ingestQualityChecks.d.ts +8 -93
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
- package/dist/ingest/ingestQualityChecks.js +57 -214
- package/dist/ingest/ingestQualityChecks.js.map +1 -1
- package/dist/ingest/ingestScenarios.d.ts +0 -45
- package/dist/ingest/ingestScenarios.d.ts.map +1 -1
- package/dist/ingest/ingestScenarios.js +28 -108
- package/dist/ingest/ingestScenarios.js.map +1 -1
- package/dist/ingest/inputShapes.d.ts +16 -0
- package/dist/ingest/inputShapes.d.ts.map +1 -0
- package/dist/ingest/inputShapes.js +113 -0
- package/dist/ingest/inputShapes.js.map +1 -0
- package/dist/outputLocation.d.ts +27 -0
- package/dist/outputLocation.d.ts.map +1 -0
- package/dist/outputLocation.js +135 -0
- package/dist/outputLocation.js.map +1 -0
- package/dist/run-management/keptRun.d.ts +5 -105
- package/dist/run-management/keptRun.d.ts.map +1 -1
- package/dist/run-management/keptRun.js +16 -82
- 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 +6 -0
- package/dist/storedRun.d.ts.map +1 -0
- package/dist/storedRun.js +28 -0
- package/dist/storedRun.js.map +1 -0
- package/package.json +2 -2
- package/dist/corpus/cli.d.ts +0 -34
- package/dist/corpus/cli.d.ts.map +0 -1
- package/dist/corpus/cli.js +0 -125
- package/dist/corpus/cli.js.map +0 -1
|
@@ -1,109 +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, @SCN-LNT-005):
|
|
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 (@SCN-LNT-021, @SCN-LNT-028) — 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
|
-
|
|
47
|
-
|
|
48
|
-
* `.feature` with the REAL Gherkin parser and report the files it cannot read.
|
|
49
|
-
*
|
|
50
|
-
* The feature corpus is the ledger's DEBIT side, and it used to be read by a tag-line scan that
|
|
51
|
-
* never consulted a parser — so a malformed tag line silently deleted its scenario, and a corrupt
|
|
52
|
-
* spec could read BALANCED. A file the parser rejects is one cucumber would refuse to run: the
|
|
53
|
-
* CLI halts on it (exit 2) rather than reconciling a corpus that is a fiction.
|
|
54
|
-
*
|
|
55
|
-
* THIS IS NOW THE CATCH SITE, NOT THE CALL SITE (@SCN-GPG-001, 3F-2817). The validation moved
|
|
56
|
-
* into `discoverFeatures`, which REFUSES an unreadable corpus rather than returning one, so every
|
|
57
|
-
* corpus reader inherits the refusal instead of each restating it. What is left here is the
|
|
58
|
-
* translation back into the list this CLI edge already prints — same files, same order, same
|
|
59
|
-
* verbatim parser messages, so `balance`'s behaviour is unchanged. Anything that is not a corpus
|
|
60
|
-
* refusal is not ours to interpret and rethrows.
|
|
61
|
-
*/
|
|
12
|
+
export declare const DEFAULT_CI_PATH = ".github/workflows";
|
|
13
|
+
export declare const DEFAULT_PACKAGE_JSON = "package.json";
|
|
62
14
|
export declare function featureCorpusParseErrors(featuresDir: string | undefined): GherkinParseError[];
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
* WHY THIS EXISTS. Tag inheritance made this reachable: an `@SCN` hoisted to a `Feature:`/`Rule:`
|
|
71
|
-
* is inherited by every scenario beneath it (cucumber does this, so we do — parity). Left
|
|
72
|
-
* unhandled, ONE passing test then marked EVERY scenario sharing that id `balanced`, including
|
|
73
|
-
* scenarios nothing proves — a BALANCED-WHEN-BROKEN run, exit 0. A new silent false-green,
|
|
74
|
-
* introduced by the very fix that closed the last one.
|
|
75
|
-
*
|
|
76
|
-
* Refused at the edge (exit 2) rather than reconciled, under the Rule already shipped for the
|
|
77
|
-
* observed side and the corrupt corpus (@SCN-CLI-014/015/016): an input the tool cannot read is a
|
|
78
|
-
* usage error, never a verdict. Whether hoisting an `@SCN` is an authoring fault worth a richer
|
|
79
|
-
* diagnostic is the deferred policy question (3F-1774's sibling); refusing to reconcile a corpus we
|
|
80
|
-
* cannot read is not a policy — it is the floor.
|
|
81
|
-
*/
|
|
82
|
-
export declare function featureCorpusDuplicateScnIds(featuresDir: string | undefined): string[];
|
|
83
|
-
/**
|
|
84
|
-
* Read the target's `feature-code → slug` map over the same `discoverFeatures` corpus
|
|
85
|
-
* (@SCN-RPT-006) — each coded feature file's `@<FFF>` header (`featureCode`)
|
|
86
|
-
* mapped to its filename `slug`, the full feature name. This is the mapping
|
|
87
|
-
* `scenariosFromFeatures` discards (it keeps only the parsed scenarios). A sibling read to
|
|
88
|
-
* the balance, threaded to `renderMarkdown` (via `writeOutputs`) as the 5th sibling arg
|
|
89
|
-
* (markdown-only — the by-feature cut is markdown-only, so it is NOT serialised to JSON).
|
|
90
|
-
* Uncoded files (`featureCode === null`) are omitted, so the by-feature emitter falls
|
|
91
|
-
* back to the bare `FFF` for any id whose code no file registers. One-prefix-one-file holds
|
|
92
|
-
* by invariant (`corpusFaults` raises `duplicate-feature-code`, @SCN-STG-010), so the map is
|
|
93
|
-
* unambiguous. That invariant was `lintFeatureCodes`' until 3F-3313; it is the shipped command's
|
|
94
|
-
* now, which is the first time a CONSUMER of this reader has it too.
|
|
95
|
-
*/
|
|
15
|
+
export interface InputReadFault {
|
|
16
|
+
readonly path: string;
|
|
17
|
+
readonly reason: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function corpusReadFault(featuresDir: string | undefined): InputReadFault | null;
|
|
20
|
+
export declare function ciReadFault(ciPath: string | undefined): InputReadFault | null;
|
|
96
21
|
export declare function readFeatureNames(featuresDir?: string): Map<string, string>;
|
|
97
|
-
/**
|
|
98
|
-
* Read the scenario-corpus census over the discovered feature files (the same
|
|
99
|
-
* `discoverFeatures` corpus `scenariosFromFeatures` reads): sum the raw @SCN
|
|
100
|
-
* occurrences (`countScnOccurrences`) and the scenario count
|
|
101
|
-
* (`parseScenarios(...).length`) across every file, and build the `EvidenceObligations`
|
|
102
|
-
* sibling (@SCN-RPT-008). Also collects the UNTAGGED scenarios
|
|
103
|
-
* (@SCN-RPT-014) — `parseUntaggedScenarios` per file, each paired with its feature-file
|
|
104
|
-
* locator, sorted by (feature, scenario). Read alongside the balance in `main()` and
|
|
105
|
-
* threaded to the renderers via `writeOutputs`; `runReconcile` stays unchanged.
|
|
106
|
-
*/
|
|
107
22
|
export declare function readEvidenceObligations(features?: string): EvidenceObligations;
|
|
108
23
|
export declare function runReconcile(opts: ReconcileOptions): EvidenceReconciliation;
|
|
109
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":"AAUA,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,CAmB9E;AAmBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,CA2B3E"}
|
|
@@ -1,91 +1,25 @@
|
|
|
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 { readFileSync, existsSync } from "node:fs";
|
|
16
2
|
import { basename } from "node:path";
|
|
17
3
|
import { ciSourcePathsOf } from "./ciSourcePaths.js";
|
|
18
4
|
import { discoverFeatures } from "./ingestScenarios.js";
|
|
19
|
-
import { parseScenarios, countScnOccurrences, parseUntaggedScenarios,
|
|
5
|
+
import { parseScenarios, countScnOccurrences, parseUntaggedScenarios, sharedScnCodesOf, } from "@3f-consulting/spec-controller-core";
|
|
20
6
|
import { MissingFeatureCorpusError, UnreadableFeatureCorpusError, } from "./gherkinValidation.js";
|
|
21
7
|
import { DEFAULT_DEFERRAL_TAGS } from "../deferralTags.js";
|
|
22
8
|
import { evidenceObligations, } from "@3f-consulting/spec-controller-core";
|
|
23
9
|
import { parseVitestEvidenceObservations, } from "@3f-consulting/spec-controller-core";
|
|
24
10
|
import { parseCucumberEvidenceObservations, } from "@3f-consulting/spec-controller-core";
|
|
25
|
-
// THE WHOLE COMPOSITION, THROUGH THE PUBLIC BARREL AND NOTHING ELSE (3F-3086). This module travels
|
|
26
|
-
// in the tarball, and until 3F-3089 it reached nine core symbols through the UNPUBLISHED
|
|
27
|
-
// `./internal` subpath to assemble the reconciliation itself — which is why an installed
|
|
28
|
-
// `spec-controller balance --help` threw ERR_PACKAGE_PATH_NOT_EXPORTED while every gate in this
|
|
29
|
-
// workspace stayed green. The nine are still internal (3F-2837 stands, unamended); what moved is
|
|
30
|
-
// their CALLER. What is left here is the reads: this edge opens the files and hands the bytes over.
|
|
31
11
|
import { evidenceReconciliationFromSources } from "@3f-consulting/spec-controller-core";
|
|
32
12
|
import { resolveTargetRoot } from "../run-management/resolveRunInputs.js";
|
|
33
|
-
/**
|
|
34
|
-
* The per-input FALLBACK quality-check for each JS-ecosystem result source — the
|
|
35
|
-
* source→quality-check mapping, SET at the CLI edge rather than hardcoded in the core
|
|
36
|
-
* parsers / countRuntimeEvidenceObservationKinds. A second ecosystem (pytest / JUnit) sets its own here (or
|
|
37
|
-
* via an adapter); the core never assumes a mapping. No CLI override flag yet — that
|
|
38
|
-
* earns its place at the second ecosystem, not now.
|
|
39
|
-
*/
|
|
40
13
|
const VITEST_QUALITY_CHECK = "unit";
|
|
41
14
|
const CUCUMBER_QUALITY_CHECK = "integration";
|
|
42
|
-
/**
|
|
43
|
-
* THIS TOOL, RUN TO RECONCILE (@SCN-USG-014, 3F-3343) — the invocation whose step is the
|
|
44
|
-
* reconciler rather than one of the reconciled rows, SET at the CLI edge and injected into the
|
|
45
|
-
* core like the prefixes above and the workflow names below.
|
|
46
|
-
*
|
|
47
|
-
* HOW THIS PRODUCT IS INVOKED IS THIS LAYER'S FACT. `spec-controller` is the `bin` this package
|
|
48
|
-
* publishes and `balance` is the command that reconciles; the engine holds neither, and a core
|
|
49
|
-
* that assumed them would be asserting something about a command line it never sees.
|
|
50
|
-
*
|
|
51
|
-
* BOTH TOKENS, AND THE SECOND IS NOT DECORATION. This repository's own pipeline runs
|
|
52
|
-
* `pnpm spec-controller tags` beside its balance step — the same binary, doing something that is
|
|
53
|
-
* an ordinary step of the pipeline being reconciled — so an invocation naming the binary alone
|
|
54
|
-
* would take that step off the books. The comparison is whole, adjacent shell tokens
|
|
55
|
-
* (`parse/commandRuns`), so this names the reconciliation and nothing else the tool can do.
|
|
56
|
-
*/
|
|
57
15
|
const RECONCILER_INVOCATION = "spec-controller balance";
|
|
58
|
-
|
|
16
|
+
export const DEFAULT_CI_PATH = ".github/workflows";
|
|
17
|
+
export const DEFAULT_PACKAGE_JSON = "package.json";
|
|
59
18
|
function readJsonIfPresent(path) {
|
|
60
19
|
if (!path || !existsSync(path))
|
|
61
20
|
return undefined;
|
|
62
21
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
63
22
|
}
|
|
64
|
-
/**
|
|
65
|
-
* THE CORPUS `balance` READS — the shared discovery, with an ABSENT directory read as NO CORPUS
|
|
66
|
-
* (@SCN-CLI-017, 3F-3345). The single place this command opts in.
|
|
67
|
-
*
|
|
68
|
-
* `new-client` is a supported adoption state, not a degraded one, and most targets have no
|
|
69
|
-
* `features/` directory at all — so an absent DEFAULT corpus is the target's honest shape and
|
|
70
|
-
* `balance` reports on it. Left to escape it was a raw `ENOENT` landing on Node's default exit 1,
|
|
71
|
-
* the OUT-OF-BALANCE code, which is exactly the collision @SCN-CLI-014/015/016 closed on every
|
|
72
|
-
* other input, alive on the one path each new consumer is guaranteed to take.
|
|
73
|
-
*
|
|
74
|
-
* ONE HELPER, NOT FIVE CATCHES, and that is the whole reason it exists: five readers each doing
|
|
75
|
-
* this by hand is five chances for one to stay intolerant, and a run that reports on a corpus for
|
|
76
|
-
* four of its readings and throws on the fifth is worse than one that throws. The translation is
|
|
77
|
-
* also why `discoverFeatures` is not the place to be tolerant — see its own note, and
|
|
78
|
-
* `MissingFeatureCorpusError`'s: `tags` reaches the same reader and keeps refusing.
|
|
79
|
-
*
|
|
80
|
-
* IT IS `[]`, NOT A VERDICT. Zero rows is what @SCN-CLI-019 reads as NOTHING TO RECONCILE, and a
|
|
81
|
-
* corpus-less target that still has uncited credits keeps its suspense row and its exit 1
|
|
82
|
-
* (@SCN-CLI-020) — this reader says what it found, never what the run means.
|
|
83
|
-
*
|
|
84
|
-
* A supplied-but-absent `--features` never arrives here: `checkInputsExist` refuses it at the CLI
|
|
85
|
-
* edge, exit 2 (@SCN-CLI-002). The precedent is `ciSourcePathsOf`, which already answers an absent
|
|
86
|
-
* DEFAULT `.github/workflows` with `[]` for the same reason — the default finding nothing is a fact
|
|
87
|
-
* about the target, not a mistake by its author.
|
|
88
|
-
*/
|
|
89
23
|
function balanceCorpus(featuresDir) {
|
|
90
24
|
try {
|
|
91
25
|
return featuresDir !== undefined ? discoverFeatures(featuresDir) : discoverFeatures();
|
|
@@ -96,49 +30,15 @@ function balanceCorpus(featuresDir) {
|
|
|
96
30
|
throw err;
|
|
97
31
|
}
|
|
98
32
|
}
|
|
99
|
-
/**
|
|
100
|
-
* The discovered feature corpus's SOURCES — read once so the two readings a citing run needs (the
|
|
101
|
-
* obligations, and the corpus hop's `check tag → @SCN`) are taken from the same bytes. Two
|
|
102
|
-
* discoveries could disagree about the corpus, and a citation resolved against a corpus the ledger
|
|
103
|
-
* was not built from is exactly the phantom-row fault (@SCN-LNT-009).
|
|
104
|
-
*/
|
|
105
33
|
function featureSources(featuresDir) {
|
|
106
34
|
return balanceCorpus(featuresDir).map((f) => readFileSync(f.path, "utf8"));
|
|
107
35
|
}
|
|
108
36
|
function scenariosFromFeatures(featuresDir, deferralTags = DEFAULT_DEFERRAL_TAGS) {
|
|
109
37
|
return featureSources(featuresDir).flatMap((source) => parseScenarios(source, deferralTags));
|
|
110
38
|
}
|
|
111
|
-
/**
|
|
112
|
-
* The feature corpus's UNTAGGED half (@SCN-LDG-021, 3F-1774) — the scenarios `parseScenarios`
|
|
113
|
-
* discards because they carry no `@SCN`. They are REAL scenarios: cucumber runs them, so each
|
|
114
|
-
* owns a real Evidence Obligation — but the obligation has no code for an Evidence Observation to
|
|
115
|
-
* link to, so it can never be discharged. `reconcile` gives each its own out-of-balance row
|
|
116
|
-
* (`no-scn-code`); before this read they were invisible to the ledger, and a run whose only fault
|
|
117
|
-
* was an untagged scenario printed "N scenario(s) carry no @SCN tag" and exited 0 BALANCED.
|
|
118
|
-
*
|
|
119
|
-
* Each is handled by its `<feature> : <scenario>` locator — the SAME locator the untagged warning
|
|
120
|
-
* prints — because a bare scenario name is not unique across the corpus, and the row must say
|
|
121
|
-
* where to go.
|
|
122
|
-
*/
|
|
123
39
|
function untaggedScenariosFromFeatures(featuresDir) {
|
|
124
40
|
return balanceCorpus(featuresDir).flatMap((f) => parseUntaggedScenarios(readFileSync(f.path, "utf8")).map((scenario) => `${f.slug}.feature : ${scenario}`));
|
|
125
41
|
}
|
|
126
|
-
/**
|
|
127
|
-
* The feature corpus's INTEGRITY CHECKPOINT (@SCN-CLI-016, 3F-1767) — validate every discovered
|
|
128
|
-
* `.feature` with the REAL Gherkin parser and report the files it cannot read.
|
|
129
|
-
*
|
|
130
|
-
* The feature corpus is the ledger's DEBIT side, and it used to be read by a tag-line scan that
|
|
131
|
-
* never consulted a parser — so a malformed tag line silently deleted its scenario, and a corrupt
|
|
132
|
-
* spec could read BALANCED. A file the parser rejects is one cucumber would refuse to run: the
|
|
133
|
-
* CLI halts on it (exit 2) rather than reconciling a corpus that is a fiction.
|
|
134
|
-
*
|
|
135
|
-
* THIS IS NOW THE CATCH SITE, NOT THE CALL SITE (@SCN-GPG-001, 3F-2817). The validation moved
|
|
136
|
-
* into `discoverFeatures`, which REFUSES an unreadable corpus rather than returning one, so every
|
|
137
|
-
* corpus reader inherits the refusal instead of each restating it. What is left here is the
|
|
138
|
-
* translation back into the list this CLI edge already prints — same files, same order, same
|
|
139
|
-
* verbatim parser messages, so `balance`'s behaviour is unchanged. Anything that is not a corpus
|
|
140
|
-
* refusal is not ours to interpret and rethrows.
|
|
141
|
-
*/
|
|
142
42
|
export function featureCorpusParseErrors(featuresDir) {
|
|
143
43
|
try {
|
|
144
44
|
balanceCorpus(featuresDir);
|
|
@@ -150,48 +50,50 @@ export function featureCorpusParseErrors(featuresDir) {
|
|
|
150
50
|
throw err;
|
|
151
51
|
}
|
|
152
52
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
53
|
+
function readFaultOf(err, path) {
|
|
54
|
+
const errno = err;
|
|
55
|
+
if (!(err instanceof Error) || typeof errno.code !== "string")
|
|
56
|
+
return null;
|
|
57
|
+
return { path: errno.path ?? path, reason: err.message };
|
|
58
|
+
}
|
|
59
|
+
export function corpusReadFault(featuresDir) {
|
|
60
|
+
try {
|
|
61
|
+
balanceCorpus(featuresDir);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof UnreadableFeatureCorpusError)
|
|
66
|
+
return null;
|
|
67
|
+
const fault = readFaultOf(err, featuresDir ?? "features");
|
|
68
|
+
if (fault === null)
|
|
69
|
+
throw err;
|
|
70
|
+
return fault;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function ciReadFault(ciPath) {
|
|
74
|
+
const path = ciPath ?? DEFAULT_CI_PATH;
|
|
75
|
+
try {
|
|
76
|
+
for (const source of ciSourcePathsOf(path))
|
|
77
|
+
readCiSource(source);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const fault = readFaultOf(err, path);
|
|
82
|
+
if (fault === null)
|
|
83
|
+
throw err;
|
|
84
|
+
return fault;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function readCiSource(source) {
|
|
88
|
+
try {
|
|
89
|
+
readFileSync(source, "utf8");
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const errno = err;
|
|
93
|
+
errno.path ??= source;
|
|
94
|
+
throw err;
|
|
179
95
|
}
|
|
180
|
-
return [...duplicates].sort();
|
|
181
96
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Read the target's `feature-code → slug` map over the same `discoverFeatures` corpus
|
|
184
|
-
* (@SCN-RPT-006) — each coded feature file's `@<FFF>` header (`featureCode`)
|
|
185
|
-
* mapped to its filename `slug`, the full feature name. This is the mapping
|
|
186
|
-
* `scenariosFromFeatures` discards (it keeps only the parsed scenarios). A sibling read to
|
|
187
|
-
* the balance, threaded to `renderMarkdown` (via `writeOutputs`) as the 5th sibling arg
|
|
188
|
-
* (markdown-only — the by-feature cut is markdown-only, so it is NOT serialised to JSON).
|
|
189
|
-
* Uncoded files (`featureCode === null`) are omitted, so the by-feature emitter falls
|
|
190
|
-
* back to the bare `FFF` for any id whose code no file registers. One-prefix-one-file holds
|
|
191
|
-
* by invariant (`corpusFaults` raises `duplicate-feature-code`, @SCN-STG-010), so the map is
|
|
192
|
-
* unambiguous. That invariant was `lintFeatureCodes`' until 3F-3313; it is the shipped command's
|
|
193
|
-
* now, which is the first time a CONSUMER of this reader has it too.
|
|
194
|
-
*/
|
|
195
97
|
export function readFeatureNames(featuresDir) {
|
|
196
98
|
const map = new Map();
|
|
197
99
|
for (const f of balanceCorpus(featuresDir)) {
|
|
@@ -200,25 +102,15 @@ export function readFeatureNames(featuresDir) {
|
|
|
200
102
|
}
|
|
201
103
|
return map;
|
|
202
104
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Read the scenario-corpus census over the discovered feature files (the same
|
|
205
|
-
* `discoverFeatures` corpus `scenariosFromFeatures` reads): sum the raw @SCN
|
|
206
|
-
* occurrences (`countScnOccurrences`) and the scenario count
|
|
207
|
-
* (`parseScenarios(...).length`) across every file, and build the `EvidenceObligations`
|
|
208
|
-
* sibling (@SCN-RPT-008). Also collects the UNTAGGED scenarios
|
|
209
|
-
* (@SCN-RPT-014) — `parseUntaggedScenarios` per file, each paired with its feature-file
|
|
210
|
-
* locator, sorted by (feature, scenario). Read alongside the balance in `main()` and
|
|
211
|
-
* threaded to the renderers via `writeOutputs`; `runReconcile` stays unchanged.
|
|
212
|
-
*/
|
|
213
105
|
export function readEvidenceObligations(features) {
|
|
214
106
|
const featureFiles = balanceCorpus(features);
|
|
215
107
|
let raw = 0;
|
|
216
108
|
let scenarioCount = 0;
|
|
217
109
|
const untagged = [];
|
|
218
|
-
const
|
|
110
|
+
const files = [];
|
|
219
111
|
for (const f of featureFiles) {
|
|
220
112
|
const source = readFileSync(f.path, "utf8");
|
|
221
|
-
|
|
113
|
+
files.push({ feature: `${f.slug}.feature`, source });
|
|
222
114
|
raw += countScnOccurrences(source);
|
|
223
115
|
scenarioCount += parseScenarios(source).length;
|
|
224
116
|
for (const scenario of parseUntaggedScenarios(source)) {
|
|
@@ -226,18 +118,8 @@ export function readEvidenceObligations(features) {
|
|
|
226
118
|
}
|
|
227
119
|
}
|
|
228
120
|
untagged.sort((a, b) => a.feature.localeCompare(b.feature) || a.scenario.localeCompare(b.scenario));
|
|
229
|
-
|
|
230
|
-
// dangling), so it reads all sources at once — not per-file like the others.
|
|
231
|
-
return evidenceObligations(raw, scenarioCount, untagged, proseOnlyCodes(sources));
|
|
121
|
+
return evidenceObligations(raw, scenarioCount, untagged, sharedScnCodesOf(files));
|
|
232
122
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Parse the target's RUNTIME observations ONCE — the tag-derived, per-tier observation set
|
|
235
|
-
* (@SCN-RPT-017 / 3F-1452 FULL UNIFY) fed to BOTH the reconciler and the runtime census, so the
|
|
236
|
-
* two can never disagree. Each source's FALLBACK quality-check is set at the CLI edge
|
|
237
|
-
* (vitest⇒unit, cucumber⇒integration); the parsers DERIVE the level from the cited scenario's tier
|
|
238
|
-
* tag (@SCN-LDG-011 / 3F-1434), using the fallback only for an untagged scenario. A missing report
|
|
239
|
-
* contributes nothing. Root-relative so a phantom-scenario row's where-to-fix stays portable.
|
|
240
|
-
*/
|
|
241
123
|
function parseRuntimeObservations(opts, root) {
|
|
242
124
|
const out = [];
|
|
243
125
|
const vitestReport = readJsonIfPresent(opts.vitest);
|
|
@@ -249,66 +131,27 @@ function parseRuntimeObservations(opts, root) {
|
|
|
249
131
|
return out;
|
|
250
132
|
}
|
|
251
133
|
export function runReconcile(opts) {
|
|
252
|
-
|
|
253
|
-
// extension (@SCN-PND-021) and the configured fitness rows, and its `scripts` back the `exists`
|
|
254
|
-
// read. The disk read stays at this impure edge; the engine takes the resolved values as
|
|
255
|
-
// injected params, staying pure.
|
|
256
|
-
const pkgPath = opts.packageJson ?? "package.json";
|
|
134
|
+
const pkgPath = opts.packageJson ?? DEFAULT_PACKAGE_JSON;
|
|
257
135
|
const packageJson = readJsonIfPresent(pkgPath) ?? {};
|
|
258
|
-
// The TARGET repo root — the credit locators are made relative to it, so a phantom-scenario row's
|
|
259
|
-
// where-to-fix (ruling Q5) stays portable in a banked artefact. Resolved ONCE here and threaded,
|
|
260
|
-
// which is also what keeps `execFileSync` and `process.cwd()` out of the engine.
|
|
261
136
|
const root = resolveTargetRoot({ features: opts.features });
|
|
262
|
-
|
|
263
|
-
// (@SCN-USG-006, 3F-2238): a pipeline split across files enforces its checks just as one
|
|
264
|
-
// file does, and reading only ci.yml both invents watermelons for named checks enforced
|
|
265
|
-
// elsewhere and drops uncited ones off the books entirely. Path semantics carry the
|
|
266
|
-
// file-vs-directory distinction, so one flag stays one flag and an explicit file is still
|
|
267
|
-
// a narrowing (@SCN-USG-008).
|
|
268
|
-
const ciPath = opts.ci ?? ".github/workflows";
|
|
269
|
-
// RESOLVED ONCE, READ TWICE (@SCN-USG-015). The engine takes anonymous source strings and holds
|
|
270
|
-
// no filesystem, so the mapping from a step's source index back to the workflow file it came
|
|
271
|
-
// from is made HERE, where the paths are already in hand — `ciSourcePaths.ts` is explicit that
|
|
272
|
-
// keeping the core that way is the point. Resolving the list twice would be two chances for the
|
|
273
|
-
// bytes and the names to fall out of the order they are paired by.
|
|
137
|
+
const ciPath = opts.ci ?? DEFAULT_CI_PATH;
|
|
274
138
|
const ciPaths = ciSourcePathsOf(ciPath);
|
|
139
|
+
const corpusFiles = balanceCorpus(opts.features).map((f) => ({
|
|
140
|
+
slug: f.slug,
|
|
141
|
+
featureCode: f.featureCode,
|
|
142
|
+
source: readFileSync(f.path, "utf8"),
|
|
143
|
+
}));
|
|
275
144
|
return evidenceReconciliationFromSources({
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
// discoveries that disagree about the corpus (@SCN-LNT-009).
|
|
279
|
-
featureSources: featureSources(opts.features),
|
|
280
|
-
// The untagged half by its `<feature> : <scenario>` locator — parsed here because the locator
|
|
281
|
-
// names the FILE, which the discovery knows and a source string does not.
|
|
145
|
+
featureSources: corpusFiles.map((f) => f.source),
|
|
146
|
+
featureFiles: corpusFiles.map(({ slug, featureCode }) => ({ slug, featureCode })),
|
|
282
147
|
untaggedScenarios: untaggedScenariosFromFeatures(opts.features),
|
|
283
148
|
packageJson,
|
|
284
149
|
ciSources: ciPaths.map((path) => readFileSync(path, "utf8")),
|
|
285
|
-
// THE FILE, NOT THE PATH. A `notChecks` entry opens with the workflow's own name — `ci.yml`,
|
|
286
|
-
// `release.yml` — which is what a consumer reads at the head of their run log and the only
|
|
287
|
-
// half of the entry that does not come out of the workflow's own text. The directory it sits
|
|
288
|
-
// in is the caller's `--ci`, not the consumer's subject, so it is not part of what they write.
|
|
289
150
|
ciSourceNames: ciPaths.map((path) => basename(path)),
|
|
290
|
-
// The CONVERTED checks' credit stream, read at this edge like every other report. Absent it is
|
|
291
|
-
// undefined rather than empty, and that distinction reaches the reader (@SCN-LNT-027): "no
|
|
292
|
-
// record" is a run nobody recorded, "a record holding nothing" is a run that recorded none of
|
|
293
|
-
// these steps, and those are opposite facts.
|
|
294
|
-
// STRUCTURALLY, NOT BY NAME: this module travels in the tarball and reaches core through the PUBLIC barrel alone (3F-3086), and
|
|
295
|
-
// `CiStepRecord` is not on it. The shape is the document's, which is the consumer's, so what is
|
|
296
|
-
// written here is what the file holds rather than a contract owed to anyone.
|
|
297
151
|
ciStepRecord: readJsonIfPresent(opts.ciSteps),
|
|
298
152
|
runtimeObservations: parseRuntimeObservations(opts, root),
|
|
299
|
-
// The two things the engine's resolvers cannot hold, and each for its own reason: the
|
|
300
|
-
// starter deferral set is the application layer's own policy, and how this tool is invoked to
|
|
301
|
-
// reconcile is a fact about this product's command line. THE PREFIX SET USED TO SIT HERE AND IS GONE (@SCN-USG-004, 3F-3343): the
|
|
302
|
-
// engine no longer decides a check's membership by its name, so there is no convention left
|
|
303
|
-
// for this edge to own.
|
|
304
153
|
deferralTags: DEFAULT_DEFERRAL_TAGS,
|
|
305
154
|
reconcilerInvocation: RECONCILER_INVOCATION,
|
|
306
155
|
});
|
|
307
156
|
}
|
|
308
|
-
// The static-check kind-counts are no longer read as a parallel census (@SCN-USG-001 / 3F-2118):
|
|
309
|
-
// the former `readStaticEvidenceObservationCounts` re-built the static check observations and
|
|
310
|
-
// re-derived the running / uncited split from a second read of the CI config. That counting now
|
|
311
|
-
// FOLDS onto the Ledger the reconciler already produced (`staticObservationTally`, an internal
|
|
312
|
-
// domain fold the renderers apply), so it cannot drift from the verdict — it counts the SAME posted
|
|
313
|
-
// static-check credits `runReconcile` banked. The census read is deleted, not relocated.
|
|
314
157
|
//# sourceMappingURL=ingestQualityChecks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingestQualityChecks.js","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ingestQualityChecks.js","sourceRoot":"","sources":["../../src/ingest/ingestQualityChecks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,53 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ingest the target's feature files — the IMPURE reader that walks features/ on disk
|
|
3
|
-
* and parses each file's feature code + scenario codes into DiscoveredFeature
|
|
4
|
-
* records (split out of the old spec/featureCodes.ts). The pure per-source
|
|
5
|
-
* parsers (extractFeatureCode / parseScenarioCodes) live here with their sole consumer;
|
|
6
|
-
* the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
|
|
7
|
-
* featureCode.ts); the duplicate-code lint is engine-side too since 3F-3313, as @SCN-STG-010 in
|
|
8
|
-
* parse/corpusFaults.ts, and no longer a private check under tests/checks/.
|
|
9
|
-
*
|
|
10
|
-
* The foundation, folded into the @LDG runtime spine.
|
|
11
|
-
*/
|
|
12
|
-
/** A feature file discovered on disk, with its `@<FFF>` header code (if any). */
|
|
13
1
|
export interface DiscoveredFeature {
|
|
14
|
-
/** The feature-file basename without the .feature extension. */
|
|
15
2
|
slug: string;
|
|
16
|
-
/** Absolute path to the feature file. */
|
|
17
3
|
path: string;
|
|
18
4
|
featureCode: string | null;
|
|
19
|
-
/** Every @SCN-<FFF>-<NNN> scenario code in the file, in order, duplicates kept. */
|
|
20
|
-
scenarioCodes: string[];
|
|
21
5
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Read the Feature-level @<FFF> header tag from a feature file's source — the
|
|
24
|
-
* first tag immediately preceding the `Feature:` keyword that is not an @SCN id
|
|
25
|
-
* or a recognised evidence/routing tag. Returns null if absent.
|
|
26
|
-
*/
|
|
27
6
|
export declare function extractFeatureCode(source: string): string | null;
|
|
28
|
-
/**
|
|
29
|
-
* Parse every @SCN-<FFF>-<NNN> scenario code in a feature file's source, in
|
|
30
|
-
* document order, duplicates preserved. Only genuine TAG LINES are scanned (a
|
|
31
|
-
* line whose first non-blank character is `@` and whose every whitespace-
|
|
32
|
-
* separated token is a tag) — prose that merely mentions a code is skipped.
|
|
33
|
-
*/
|
|
34
|
-
export declare function parseScenarioCodes(source: string): string[];
|
|
35
|
-
/**
|
|
36
|
-
* Discover every feature file on disk and its header code — REFUSING a corpus the Gherkin
|
|
37
|
-
* parser rejects (@SCN-GPG-001, 3F-2817).
|
|
38
|
-
*
|
|
39
|
-
* The refusal lives HERE, in the one reader every corpus consumer already goes through, so a
|
|
40
|
-
* consumer INHERITS it rather than restating it. It is not a second parser: it is the existing
|
|
41
|
-
* `parseErrorsIn` — cucumber's own — reached from the shared discovery instead of only from the
|
|
42
|
-
* balance CLI's edge. One answer to "what is a feature file".
|
|
43
|
-
*
|
|
44
|
-
* WHY THE WHOLE CORPUS, NOT THE FIRST BAD FILE. `featureCorpusParseErrors` reports every
|
|
45
|
-
* unreadable file and the balance CLI prints them all (@SCN-CLI-016). Refusing at the first
|
|
46
|
-
* failure would silently narrow that message, so every file is parsed and the refusal is raised
|
|
47
|
-
* once, carrying the lot in discovery order.
|
|
48
|
-
*
|
|
49
|
-
* And NO PARTIAL CORPUS is ever returned: a caller that could scan the readable half of a
|
|
50
|
-
* rejected tree would be back to reporting a verdict over a corpus it cannot read.
|
|
51
|
-
*/
|
|
52
7
|
export declare function discoverFeatures(dir?: string): DiscoveredFeature[];
|
|
53
8
|
//# sourceMappingURL=ingestScenarios.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE;AA0CD,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,EAAE,CAiBjF"}
|