spec-controller 0.1.0-alpha.4 → 0.1.0-alpha.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +34 -6
  2. package/dist/cli-allocate/cli.d.ts +2 -0
  3. package/dist/cli-allocate/cli.d.ts.map +1 -0
  4. package/dist/cli-allocate/cli.js +57 -0
  5. package/dist/cli-allocate/cli.js.map +1 -0
  6. package/dist/cli-allocate/readHistory.d.ts +29 -0
  7. package/dist/cli-allocate/readHistory.d.ts.map +1 -0
  8. package/dist/cli-allocate/readHistory.js +136 -0
  9. package/dist/cli-allocate/readHistory.js.map +1 -0
  10. package/dist/cli-allocate/readParts.d.ts +3 -0
  11. package/dist/cli-allocate/readParts.d.ts.map +1 -0
  12. package/dist/cli-allocate/readParts.js +28 -0
  13. package/dist/cli-allocate/readParts.js.map +1 -0
  14. package/dist/cli-allocate/readTree.d.ts +16 -0
  15. package/dist/cli-allocate/readTree.d.ts.map +1 -0
  16. package/dist/cli-allocate/readTree.js +106 -0
  17. package/dist/cli-allocate/readTree.js.map +1 -0
  18. package/dist/cli-allocate/refStore.d.ts +3 -0
  19. package/dist/cli-allocate/refStore.d.ts.map +1 -0
  20. package/dist/cli-allocate/refStore.js +47 -0
  21. package/dist/cli-allocate/refStore.js.map +1 -0
  22. package/dist/cli-allocate/request.d.ts +11 -0
  23. package/dist/cli-allocate/request.d.ts.map +1 -0
  24. package/dist/cli-allocate/request.js +29 -0
  25. package/dist/cli-allocate/request.js.map +1 -0
  26. package/dist/cli-allocate/stderr.d.ts +14 -0
  27. package/dist/cli-allocate/stderr.d.ts.map +1 -0
  28. package/dist/cli-allocate/stderr.js +33 -0
  29. package/dist/cli-allocate/stderr.js.map +1 -0
  30. package/dist/cli-allocate/unreadable.d.ts +24 -0
  31. package/dist/cli-allocate/unreadable.d.ts.map +1 -0
  32. package/dist/cli-allocate/unreadable.js +43 -0
  33. package/dist/cli-allocate/unreadable.js.map +1 -0
  34. package/dist/cli-args.d.ts +73 -18
  35. package/dist/cli-args.d.ts.map +1 -1
  36. package/dist/cli-args.js +244 -22
  37. package/dist/cli-args.js.map +1 -1
  38. package/dist/cli-balance/cli.d.ts +31 -13
  39. package/dist/cli-balance/cli.d.ts.map +1 -1
  40. package/dist/cli-balance/cli.js +205 -251
  41. package/dist/cli-balance/cli.js.map +1 -1
  42. package/dist/cli-balance/emit/writer.d.ts +8 -23
  43. package/dist/cli-balance/emit/writer.d.ts.map +1 -1
  44. package/dist/cli-balance/emit/writer.js +24 -32
  45. package/dist/cli-balance/emit/writer.js.map +1 -1
  46. package/dist/cli-balance/exitStatus.d.ts +3 -0
  47. package/dist/cli-balance/exitStatus.d.ts.map +1 -0
  48. package/dist/cli-balance/exitStatus.js +12 -0
  49. package/dist/cli-balance/exitStatus.js.map +1 -0
  50. package/dist/cli-balance/reRender.d.ts +22 -0
  51. package/dist/cli-balance/reRender.d.ts.map +1 -0
  52. package/dist/cli-balance/reRender.js +36 -0
  53. package/dist/cli-balance/reRender.js.map +1 -0
  54. package/dist/cli-balance/unreadableInputs.d.ts +35 -0
  55. package/dist/cli-balance/unreadableInputs.d.ts.map +1 -0
  56. package/dist/cli-balance/unreadableInputs.js +101 -0
  57. package/dist/cli-balance/unreadableInputs.js.map +1 -0
  58. package/dist/cli-registry.d.ts +28 -0
  59. package/dist/cli-registry.d.ts.map +1 -1
  60. package/dist/cli-registry.js +40 -35
  61. package/dist/cli-registry.js.map +1 -1
  62. package/dist/cli.d.ts +4 -5
  63. package/dist/cli.d.ts.map +1 -1
  64. package/dist/cli.js +37 -21
  65. package/dist/cli.js.map +1 -1
  66. package/dist/host.d.ts +28 -3
  67. package/dist/host.d.ts.map +1 -1
  68. package/dist/host.js +137 -17
  69. package/dist/host.js.map +1 -1
  70. package/dist/ingest/gherkinValidation.d.ts +3 -7
  71. package/dist/ingest/gherkinValidation.d.ts.map +1 -1
  72. package/dist/ingest/gherkinValidation.js +3 -7
  73. package/dist/ingest/gherkinValidation.js.map +1 -1
  74. package/dist/ingest/ingestQualityChecks.d.ts +25 -54
  75. package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
  76. package/dist/ingest/ingestQualityChecks.js +112 -145
  77. package/dist/ingest/ingestQualityChecks.js.map +1 -1
  78. package/dist/ingest/ingestScenarios.d.ts +11 -17
  79. package/dist/ingest/ingestScenarios.d.ts.map +1 -1
  80. package/dist/ingest/ingestScenarios.js +52 -62
  81. package/dist/ingest/ingestScenarios.js.map +1 -1
  82. package/dist/ingest/inputShapes.d.ts +47 -0
  83. package/dist/ingest/inputShapes.d.ts.map +1 -0
  84. package/dist/ingest/inputShapes.js +143 -0
  85. package/dist/ingest/inputShapes.js.map +1 -0
  86. package/dist/outputLocation.d.ts +15 -0
  87. package/dist/outputLocation.d.ts.map +1 -0
  88. package/dist/outputLocation.js +39 -0
  89. package/dist/outputLocation.js.map +1 -0
  90. package/dist/run-management/keptRun.d.ts +27 -25
  91. package/dist/run-management/keptRun.d.ts.map +1 -1
  92. package/dist/run-management/keptRun.js +20 -21
  93. package/dist/run-management/keptRun.js.map +1 -1
  94. package/dist/storedRun.d.ts +27 -0
  95. package/dist/storedRun.d.ts.map +1 -0
  96. package/dist/storedRun.js +49 -0
  97. package/dist/storedRun.js.map +1 -0
  98. package/package.json +2 -2
  99. package/dist/corpus/cli.d.ts +0 -34
  100. package/dist/corpus/cli.d.ts.map +0 -1
  101. package/dist/corpus/cli.js +0 -128
  102. package/dist/corpus/cli.js.map +0 -1
  103. package/dist/mutation-ratchet/index.d.ts +0 -51
  104. package/dist/mutation-ratchet/index.d.ts.map +0 -1
  105. package/dist/mutation-ratchet/index.js +0 -51
  106. package/dist/mutation-ratchet/index.js.map +0 -1
  107. package/dist/mutation-ratchet/record.d.ts +0 -178
  108. package/dist/mutation-ratchet/record.d.ts.map +0 -1
  109. package/dist/mutation-ratchet/record.js +0 -314
  110. package/dist/mutation-ratchet/record.js.map +0 -1
  111. package/dist/mutation-ratchet/report.d.ts +0 -109
  112. package/dist/mutation-ratchet/report.d.ts.map +0 -1
  113. package/dist/mutation-ratchet/report.js +0 -156
  114. package/dist/mutation-ratchet/report.js.map +0 -1
@@ -1,11 +1,14 @@
1
1
  /**
2
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/.
3
+ * and reads each file's header code into DiscoveredFeature records (split out of the old
4
+ * spec/featureCodes.ts). The pure header parser (extractFeatureCode) lives here with its sole
5
+ * consumer; the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
6
+ * featureCode.ts); the duplicate-code lint is engine-side too, in
7
+ * parse/scenarioCodeErrors.ts, and no longer a private check under tests/checks/.
8
+ *
9
+ * NO PER-FILE LIST OF SCENARIO CODES. One used to ride here, read by a copy of the
10
+ * @SCN grammar of its own, and nothing but its own test read it; a scenario's code is read by the
11
+ * engine's scenario walk, which is the reading the ledger's accounts are minted from.
9
12
  *
10
13
  * The foundation, folded into the @LDG runtime spine.
11
14
  */
@@ -16,8 +19,6 @@ export interface DiscoveredFeature {
16
19
  /** Absolute path to the feature file. */
17
20
  path: string;
18
21
  featureCode: string | null;
19
- /** Every @SCN-<FFF>-<NNN> scenario code in the file, in order, duplicates kept. */
20
- scenarioCodes: string[];
21
22
  }
22
23
  /**
23
24
  * Read the Feature-level @<FFF> header tag from a feature file's source — the
@@ -25,16 +26,9 @@ export interface DiscoveredFeature {
25
26
  * or a recognised evidence/routing tag. Returns null if absent.
26
27
  */
27
28
  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
29
  /**
36
30
  * Discover every feature file on disk and its header code — REFUSING a corpus the Gherkin
37
- * parser rejects (@SCN-GPG-001, 3F-2817).
31
+ * parser rejects.
38
32
  *
39
33
  * The refusal lives HERE, in the one reader every corpus consumer already goes through, so a
40
34
  * consumer INHERITS it rather than restating it. It is not a second parser: it is the existing
@@ -42,7 +36,7 @@ export declare function parseScenarioCodes(source: string): string[];
42
36
  * balance CLI's edge. One answer to "what is a feature file".
43
37
  *
44
38
  * 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
39
+ * unreadable file and the balance CLI prints them all. Refusing at the first
46
40
  * failure would silently narrow that message, so every file is parsed and the refusal is raised
47
41
  * once, carrying the lot in discovery order.
48
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBhE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAc3D;AAwCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,EAAE,CAkBjF"}
1
+ {"version":3,"file":"ingestScenarios.d.ts","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKhE;AA2ED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,EAAE,CAiBjF"}
@@ -1,11 +1,14 @@
1
1
  /**
2
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/.
3
+ * and reads each file's header code into DiscoveredFeature records (split out of the old
4
+ * spec/featureCodes.ts). The pure header parser (extractFeatureCode) lives here with its sole
5
+ * consumer; the `scnId → feature-code` grammar helper is engine-side (packages/core/src/lib/
6
+ * featureCode.ts); the duplicate-code lint is engine-side too, in
7
+ * parse/scenarioCodeErrors.ts, and no longer a private check under tests/checks/.
8
+ *
9
+ * NO PER-FILE LIST OF SCENARIO CODES. One used to ride here, read by a copy of the
10
+ * @SCN grammar of its own, and nothing but its own test read it; a scenario's code is read by the
11
+ * engine's scenario walk, which is the reading the ledger's accounts are minted from.
9
12
  *
10
13
  * The foundation, folded into the @LDG runtime spine.
11
14
  */
@@ -21,56 +24,32 @@ const CODE_PATTERN = /^[A-Z0-9]{2,5}$/;
21
24
  */
22
25
  export function extractFeatureCode(source) {
23
26
  const lines = source.split(/\r?\n/);
24
- for (let i = 0; i < lines.length; i++) {
25
- const line = lines[i].trim();
26
- if (line === "" || line.startsWith("#"))
27
- continue;
28
- if (line.startsWith("Feature:")) {
29
- for (let j = i - 1; j >= 0; j--) {
30
- const prev = lines[j].trim();
31
- if (prev === "")
32
- continue;
33
- if (!prev.startsWith("@"))
34
- break;
35
- const tags = prev.split(/\s+/).filter((t) => t.startsWith("@"));
36
- for (const tag of tags) {
37
- const bare = tag.slice(1);
38
- if (bare.startsWith("SCN-"))
39
- continue;
40
- if (["unit", "integration", "e2e", "performance", "smoke"].includes(bare))
41
- continue;
42
- if (CODE_PATTERN.test(bare))
43
- return bare;
44
- }
45
- }
46
- return null;
47
- }
48
- }
49
- return null;
27
+ const featureLine = lines.findIndex((line) => line.trim().startsWith("Feature:"));
28
+ if (featureLine < 0)
29
+ return null;
30
+ return headerTagsAbove(lines, featureLine).find(isHeaderCode) ?? null;
31
+ }
32
+ /** The tags a tier or routing concern owns, which are never a file's header code. */
33
+ const NON_CODE_TAGS = ["unit", "integration", "e2e", "performance", "smoke"];
34
+ /** Is this bare tag the file's header code — not an @SCN id, not a tier tag, and code-shaped? */
35
+ function isHeaderCode(bare) {
36
+ return !bare.startsWith("SCN-") && !NON_CODE_TAGS.includes(bare) && CODE_PATTERN.test(bare);
50
37
  }
51
38
  /**
52
- * Parse every @SCN-<FFF>-<NNN> scenario code in a feature file's source, in
53
- * document order, duplicates preserved. Only genuine TAG LINES are scanned (a
54
- * line whose first non-blank character is `@` and whose every whitespace-
55
- * separated token is a tag) — prose that merely mentions a code is skipped.
39
+ * The bare tags on the tag lines directly above the `Feature:` line, nearest line first and each
40
+ * line left to right. Blank lines are passed over; the first line that is not a tag line ends it.
56
41
  */
57
- export function parseScenarioCodes(source) {
58
- const codes = [];
59
- const codePattern = /^SCN-[A-Z0-9]+-\d+$/;
60
- for (const rawLine of source.split(/\r?\n/)) {
61
- const line = rawLine.trim();
62
- if (!line.startsWith("@"))
42
+ function headerTagsAbove(lines, featureLine) {
43
+ const tags = [];
44
+ for (let j = featureLine - 1; j >= 0; j--) {
45
+ const prev = lines[j].trim();
46
+ if (prev === "")
63
47
  continue;
64
- const tokens = line.split(/\s+/);
65
- if (!tokens.every((t) => t.startsWith("@")))
66
- continue;
67
- for (const token of tokens) {
68
- const bare = token.slice(1);
69
- if (codePattern.test(bare))
70
- codes.push(bare);
71
- }
48
+ if (!prev.startsWith("@"))
49
+ break;
50
+ tags.push(...prev.split(/\s+/).filter((t) => t.startsWith("@")).map((t) => t.slice(1)));
72
51
  }
73
- return codes;
52
+ return tags;
74
53
  }
75
54
  /**
76
55
  * Locate the default features/ directory — `./features` under the current
@@ -83,21 +62,19 @@ function featuresDir() {
83
62
  return join(process.cwd(), "features");
84
63
  }
85
64
  /**
86
- * List the corpus directory, REFUSING an absent one by name rather than by `ENOENT` (@SCN-CLI-017,
87
- * 3F-3345).
65
+ * List the corpus directory, REFUSING an absent one by name rather than by `ENOENT`.
88
66
  *
89
67
  * Absence stays a REFUSAL AT THE READER — what changed is that it is typed, so a caller can decide.
90
68
  * `balance` opts in to reporting on a target that has no corpus (most targets have no `features/`
91
- * at all, and `new-client` is a supported adoption state); `tags` keeps refusing, unedited, because
92
- * a `MissingFeatureCorpusError` is still an `Error` and still lands in its generic catch. Returning
93
- * `[]` here instead would make that decision for both, and measured it turns two named refusals
94
- * into `tags: OK …` at exit 0.
69
+ * at all, and `new-client` is a supported adoption state) by catching `MissingFeatureCorpusError` by
70
+ * name. Returning `[]` here instead would make that decision for every caller, and one that
71
+ * certifies a corpus would certify an absent one as empty.
95
72
  *
96
73
  * ONLY `ENOENT`. A `features` path that exists but is a FILE, or one the process may not read, is
97
74
  * a different fault and is not ours to reinterpret — it rethrows.
98
75
  *
99
76
  * A SUPPLIED `--features` never reaches here absent: `checkInputsExist` refuses it at the CLI edge
100
- * with exit 2 (@SCN-CLI-002). Supplied-but-absent is the operator mistyping; absent-by-default is
77
+ * with exit 2. Supplied-but-absent is the operator mistyping; absent-by-default is
101
78
  * the target's honest shape.
102
79
  */
103
80
  function corpusFileNames(dir) {
@@ -110,9 +87,23 @@ function corpusFileNames(dir) {
110
87
  throw err;
111
88
  }
112
89
  }
90
+ /**
91
+ * A feature file's text. A read the operating system refuses keeps its error, and gains the file's
92
+ * path when it has none: reading a directory fails with no path at all.
93
+ */
94
+ function readFeatureSource(path) {
95
+ try {
96
+ return readFileSync(path, "utf8");
97
+ }
98
+ catch (err) {
99
+ const errno = err;
100
+ errno.path ??= path;
101
+ throw err;
102
+ }
103
+ }
113
104
  /**
114
105
  * Discover every feature file on disk and its header code — REFUSING a corpus the Gherkin
115
- * parser rejects (@SCN-GPG-001, 3F-2817).
106
+ * parser rejects.
116
107
  *
117
108
  * The refusal lives HERE, in the one reader every corpus consumer already goes through, so a
118
109
  * consumer INHERITS it rather than restating it. It is not a second parser: it is the existing
@@ -120,7 +111,7 @@ function corpusFileNames(dir) {
120
111
  * balance CLI's edge. One answer to "what is a feature file".
121
112
  *
122
113
  * WHY THE WHOLE CORPUS, NOT THE FIRST BAD FILE. `featureCorpusParseErrors` reports every
123
- * unreadable file and the balance CLI prints them all (@SCN-CLI-016). Refusing at the first
114
+ * unreadable file and the balance CLI prints them all. Refusing at the first
124
115
  * failure would silently narrow that message, so every file is parsed and the refusal is raised
125
116
  * once, carrying the lot in discovery order.
126
117
  *
@@ -133,7 +124,7 @@ export function discoverFeatures(dir = featuresDir()) {
133
124
  .sort()
134
125
  .map((name) => {
135
126
  const path = join(dir, name);
136
- return { name, path, source: readFileSync(path, "utf8") };
127
+ return { name, path, source: readFeatureSource(path) };
137
128
  });
138
129
  const parseErrors = files.flatMap(({ path, source }) => parseErrorsIn(path, source));
139
130
  if (parseErrors.length > 0)
@@ -142,7 +133,6 @@ export function discoverFeatures(dir = featuresDir()) {
142
133
  slug: name.replace(/\.feature$/, ""),
143
134
  path,
144
135
  featureCode: extractFeatureCode(source),
145
- scenarioCodes: parseScenarioCodes(source),
146
136
  }));
147
137
  }
148
138
  //# sourceMappingURL=ingestScenarios.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ingestScenarios.js","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ingestScenarios.js","sourceRoot":"","sources":["../../src/ingest/ingestScenarios.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The shape each JSON input `balance` reads must have before its reader is handed it: the fields
3
+ * that reader goes on to read, and nothing more. A report is its producer's document, so a field
4
+ * the reader never touches is never checked, and a well-formed empty report is still a report.
5
+ *
6
+ * A document that is not this shape is an input the tool cannot read. Read anyway, it either
7
+ * throws part-way or is read as nothing and reaches a verdict about evidence it discarded, and
8
+ * both of those end on the out-of-balance code.
9
+ */
10
+ /** A leaf kind, a list of one shape, a record of named fields, or a map whose entries share one. */
11
+ export type Shape = "string" | "number" | "boolean" | "anything" | {
12
+ readonly list: Shape;
13
+ } | {
14
+ readonly fields: Readonly<Record<string, Shape>>;
15
+ readonly required?: readonly string[];
16
+ } | {
17
+ readonly entries: Shape;
18
+ readonly keys?: RegExp;
19
+ };
20
+ /** What the Vitest JSON reporter writes, as far as the Vitest reader reads it. */
21
+ export declare const VITEST_REPORT: Shape;
22
+ /** What the Cucumber JSON formatter writes, as far as the Cucumber reader reads it. */
23
+ export declare const CUCUMBER_REPORT: Shape;
24
+ /**
25
+ * A CI run's step record, as far as the step-record reader reads it: an object of steps keyed by
26
+ * step id, of which it credits only those keyed by a scenario code, reading each one's `outcome`.
27
+ * The key grammar is the reader's own, so a step it never credits is never checked.
28
+ */
29
+ export declare const CI_STEP_RECORD: Shape;
30
+ /**
31
+ * A package.json, as far as the run reads it outside the `specController` block: an object whose
32
+ * `scripts` are a map of commands. The `specController` block is left to its own resolvers, which
33
+ * read a value they cannot use as absent rather than refuse the run over it.
34
+ */
35
+ export declare const PACKAGE_MANIFEST: Shape;
36
+ /**
37
+ * A reconciliation this tool saved, at its top: the sections every one carries, and the optional
38
+ * ones in their kind. Below the top the renderer is the reader, and a document it cannot render
39
+ * is refused where it renders.
40
+ */
41
+ export declare const SAVED_RECONCILIATION: Shape;
42
+ /**
43
+ * The first place `value` is not `shape`, said as the path to it and what was found there, or null
44
+ * when the whole value is that shape. `where` is the path so far; the top level has none.
45
+ */
46
+ export declare function shapeFaultIn(value: unknown, shape: Shape, where?: string): string | null;
47
+ //# sourceMappingURL=inputShapes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputShapes.d.ts","sourceRoot":"","sources":["../../src/ingest/inputShapes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oGAAoG;AACpG,MAAM,MAAM,KAAK,GACb,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,UAAU,GACV;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC3F;IAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKxD,kFAAkF;AAClF,eAAO,MAAM,aAAa,EAAE,KAc3B,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,eAAe,EAAE,KAe7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAG5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAsD,CAAC;AAEtF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAWlC,CAAC;AAkBF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,SAAK,GAAG,MAAM,GAAG,IAAI,CASpF"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * The shape each JSON input `balance` reads must have before its reader is handed it: the fields
3
+ * that reader goes on to read, and nothing more. A report is its producer's document, so a field
4
+ * the reader never touches is never checked, and a well-formed empty report is still a report.
5
+ *
6
+ * A document that is not this shape is an input the tool cannot read. Read anyway, it either
7
+ * throws part-way or is read as nothing and reaches a verdict about evidence it discarded, and
8
+ * both of those end on the out-of-balance code.
9
+ */
10
+ /** What the Vitest JSON reporter writes, as far as the Vitest reader reads it. */
11
+ export const VITEST_REPORT = {
12
+ fields: {
13
+ testResults: {
14
+ list: {
15
+ fields: {
16
+ name: "string",
17
+ status: "string",
18
+ message: "string",
19
+ assertionResults: { list: { fields: { fullName: "string", title: "string", status: "string" } } },
20
+ },
21
+ },
22
+ },
23
+ },
24
+ required: ["testResults"],
25
+ };
26
+ /** What the Cucumber JSON formatter writes, as far as the Cucumber reader reads it. */
27
+ export const CUCUMBER_REPORT = {
28
+ list: {
29
+ fields: {
30
+ uri: "string",
31
+ name: "string",
32
+ elements: {
33
+ list: {
34
+ fields: {
35
+ tags: { list: { fields: { name: "string" } } },
36
+ steps: { list: { fields: { keyword: "string", name: "string", result: { fields: { status: "string" } } } } },
37
+ },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ };
43
+ /**
44
+ * A CI run's step record, as far as the step-record reader reads it: an object of steps keyed by
45
+ * step id, of which it credits only those keyed by a scenario code, reading each one's `outcome`.
46
+ * The key grammar is the reader's own, so a step it never credits is never checked.
47
+ */
48
+ export const CI_STEP_RECORD = {
49
+ entries: { fields: { outcome: "string" } },
50
+ keys: /^SCN-[A-Z0-9]{2,5}-\d+$/,
51
+ };
52
+ /**
53
+ * A package.json, as far as the run reads it outside the `specController` block: an object whose
54
+ * `scripts` are a map of commands. The `specController` block is left to its own resolvers, which
55
+ * read a value they cannot use as absent rather than refuse the run over it.
56
+ */
57
+ export const PACKAGE_MANIFEST = { fields: { scripts: { entries: "string" } } };
58
+ /**
59
+ * A reconciliation this tool saved, at its top: the sections every one carries, and the optional
60
+ * ones in their kind. Below the top the renderer is the reader, and a document it cannot render
61
+ * is refused where it renders.
62
+ */
63
+ export const SAVED_RECONCILIATION = {
64
+ fields: {
65
+ runProvenance: { fields: {} },
66
+ summary: { fields: {} },
67
+ ledger: { fields: { scenarioReconciliations: { list: "anything" } }, required: ["scenarioReconciliations"] },
68
+ scenarioCodeErrors: { list: "anything" },
69
+ scenarioCodePopulation: { fields: {} },
70
+ evidenceObligations: { fields: {} },
71
+ metadata: { fields: {} },
72
+ },
73
+ required: ["runProvenance", "summary", "ledger"],
74
+ };
75
+ function kindOf(value) {
76
+ if (value === null)
77
+ return "null";
78
+ if (Array.isArray(value))
79
+ return "a list";
80
+ if (typeof value === "object")
81
+ return "an object";
82
+ return `a ${typeof value}`;
83
+ }
84
+ function expectedKindOf(shape) {
85
+ if (typeof shape === "string")
86
+ return `a ${shape}`;
87
+ return "list" in shape ? "a list" : "an object";
88
+ }
89
+ function pathOf(where, key) {
90
+ return where === "" ? key : `${where}.${key}`;
91
+ }
92
+ /**
93
+ * The first place `value` is not `shape`, said as the path to it and what was found there, or null
94
+ * when the whole value is that shape. `where` is the path so far; the top level has none.
95
+ */
96
+ export function shapeFaultIn(value, shape, where = "") {
97
+ if (shape === "anything")
98
+ return null;
99
+ const found = kindOf(value);
100
+ const expected = expectedKindOf(shape);
101
+ if (found !== expected)
102
+ return `${where === "" ? "the top level" : where} is ${found}, not ${expected}`;
103
+ if (typeof shape === "string")
104
+ return null;
105
+ if ("list" in shape)
106
+ return listFault(value, shape.list, where);
107
+ if ("entries" in shape)
108
+ return entriesFault(value, shape, where);
109
+ return fieldsFault(value, shape, where);
110
+ }
111
+ function listFault(items, item, where) {
112
+ for (const [index, value] of items.entries()) {
113
+ const fault = shapeFaultIn(value, item, `${where}[${index}]`);
114
+ if (fault !== null)
115
+ return fault;
116
+ }
117
+ return null;
118
+ }
119
+ function fieldsFault(record, shape, where) {
120
+ for (const [key, field] of Object.entries(shape.fields)) {
121
+ const path = pathOf(where, key);
122
+ if (record[key] === undefined) {
123
+ if (shape.required?.includes(key) === true)
124
+ return `${path} is missing`;
125
+ continue;
126
+ }
127
+ const fault = shapeFaultIn(record[key], field, path);
128
+ if (fault !== null)
129
+ return fault;
130
+ }
131
+ return null;
132
+ }
133
+ function entriesFault(record, shape, where) {
134
+ for (const [key, value] of Object.entries(record)) {
135
+ if (shape.keys !== undefined && !shape.keys.test(key))
136
+ continue;
137
+ const fault = shapeFaultIn(value, shape.entries, pathOf(where, key));
138
+ if (fault !== null)
139
+ return fault;
140
+ }
141
+ return null;
142
+ }
143
+ //# sourceMappingURL=inputShapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputShapes.js","sourceRoot":"","sources":["../../src/ingest/inputShapes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export declare function unwritableRefusal(subject: string, path: string, err: unknown): string;
2
+ /**
3
+ * The catch sits on the one write it guards rather than on the process, so a refusal can only ever
4
+ * name a location this call was asked to write, and a crash anywhere else is not dressed up as one.
5
+ */
6
+ export declare function writeOrRefuse(subject: string, path: string, write: () => void): void;
7
+ /**
8
+ * Throws the operating system's own error when `path` cannot be written as a file, and leaves the
9
+ * path as it found it: an existing file is opened write-only without truncation (not "r+", which
10
+ * would also demand read permission), and an absent one is created exclusively and removed again,
11
+ * so a missing, read-only or non-directory parent answers with its real reason instead of one this
12
+ * module would have to invent.
13
+ */
14
+ export declare function probeWritable(path: string): void;
15
+ //# sourceMappingURL=outputLocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputLocation.d.ts","sourceRoot":"","sources":["../src/outputLocation.ts"],"names":[],"mappings":"AAIA,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAGrF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,IAAI,CAOpF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAShD"}
@@ -0,0 +1,39 @@
1
+ import { closeSync, constants, openSync, unlinkSync } from "node:fs";
2
+ const USAGE_EXIT_CODE = 2;
3
+ export function unwritableRefusal(subject, path, err) {
4
+ const reason = err instanceof Error ? err.message : String(err);
5
+ return `${subject}: ${path} (${reason})`;
6
+ }
7
+ /**
8
+ * The catch sits on the one write it guards rather than on the process, so a refusal can only ever
9
+ * name a location this call was asked to write, and a crash anywhere else is not dressed up as one.
10
+ */
11
+ export function writeOrRefuse(subject, path, write) {
12
+ try {
13
+ write();
14
+ }
15
+ catch (err) {
16
+ process.stderr.write(unwritableRefusal(subject, path, err) + "\n");
17
+ process.exit(USAGE_EXIT_CODE);
18
+ }
19
+ }
20
+ /**
21
+ * Throws the operating system's own error when `path` cannot be written as a file, and leaves the
22
+ * path as it found it: an existing file is opened write-only without truncation (not "r+", which
23
+ * would also demand read permission), and an absent one is created exclusively and removed again,
24
+ * so a missing, read-only or non-directory parent answers with its real reason instead of one this
25
+ * module would have to invent.
26
+ */
27
+ export function probeWritable(path) {
28
+ try {
29
+ closeSync(openSync(path, constants.O_WRONLY));
30
+ return;
31
+ }
32
+ catch (err) {
33
+ if (err.code !== "ENOENT")
34
+ throw err;
35
+ }
36
+ closeSync(openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL));
37
+ unlinkSync(path);
38
+ }
39
+ //# sourceMappingURL=outputLocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputLocation.js","sourceRoot":"","sources":["../src/outputLocation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErE,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,IAAY,EAAE,GAAY;IAC3E,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChE,OAAO,GAAG,OAAO,KAAK,IAAI,KAAK,MAAM,GAAG,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,IAAY,EAAE,KAAiB;IAC5E,IAAI,CAAC;QACH,KAAK,EAAE,CAAC;IACV,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;IACD,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,UAAU,CAAC,IAAI,CAAC,CAAC;AACnB,CAAC"}