@nielspeter/eess 0.4.0 → 0.5.1
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 +94 -1
- package/dist/baseline-generator.d.ts +9 -0
- package/dist/baseline-generator.d.ts.map +1 -1
- package/dist/baseline-generator.js +9 -0
- package/dist/baseline-generator.js.map +1 -1
- package/dist/baseline.d.ts +1 -1
- package/dist/cardinality.d.ts.map +1 -1
- package/dist/cardinality.js +20 -5
- package/dist/cardinality.js.map +1 -1
- package/dist/cli-config.d.ts +106 -0
- package/dist/cli-config.d.ts.map +1 -0
- package/dist/cli-config.js +231 -0
- package/dist/cli-config.js.map +1 -0
- package/dist/collect-result.d.ts +94 -0
- package/dist/collect-result.d.ts.map +1 -0
- package/dist/collect-result.js +142 -0
- package/dist/collect-result.js.map +1 -0
- package/dist/combinators.d.ts +14 -0
- package/dist/combinators.d.ts.map +1 -1
- package/dist/combinators.js +53 -16
- package/dist/combinators.js.map +1 -1
- package/dist/condition.d.ts +37 -0
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +48 -1
- package/dist/condition.js.map +1 -1
- package/dist/correspondence.d.ts +30 -0
- package/dist/correspondence.d.ts.map +1 -1
- package/dist/correspondence.js +36 -1
- package/dist/correspondence.js.map +1 -1
- package/dist/dedupe-config-findings.d.ts.map +1 -1
- package/dist/dedupe-config-findings.js +27 -0
- package/dist/dedupe-config-findings.js.map +1 -1
- package/dist/diff-aware.d.ts +8 -9
- package/dist/diff-aware.d.ts.map +1 -1
- package/dist/diff-aware.js +38 -17
- package/dist/diff-aware.js.map +1 -1
- package/dist/emitter-findings.d.ts +167 -0
- package/dist/emitter-findings.d.ts.map +1 -0
- package/dist/emitter-findings.js +237 -0
- package/dist/emitter-findings.js.map +1 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -1
- package/dist/exclusion-comments.d.ts +36 -1
- package/dist/exclusion-comments.d.ts.map +1 -1
- package/dist/exclusion-comments.js +185 -37
- package/dist/exclusion-comments.js.map +1 -1
- package/dist/execute-rule.d.ts +12 -3
- package/dist/execute-rule.d.ts.map +1 -1
- package/dist/execute-rule.js +184 -7
- package/dist/execute-rule.js.map +1 -1
- package/dist/file-walk.d.ts +22 -0
- package/dist/file-walk.d.ts.map +1 -0
- package/dist/file-walk.js +51 -0
- package/dist/file-walk.js.map +1 -0
- package/dist/format-json.d.ts +13 -0
- package/dist/format-json.d.ts.map +1 -1
- package/dist/format-json.js +24 -0
- package/dist/format-json.js.map +1 -1
- package/dist/index.d.ts +14 -46
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -41
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +55 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +92 -0
- package/dist/internal.js.map +1 -0
- package/dist/mask-non-comment.d.ts +49 -0
- package/dist/mask-non-comment.d.ts.map +1 -0
- package/dist/mask-non-comment.js +261 -0
- package/dist/mask-non-comment.js.map +1 -0
- package/dist/owns-empty-discovery.d.ts.map +1 -1
- package/dist/owns-empty-discovery.js +12 -4
- package/dist/owns-empty-discovery.js.map +1 -1
- package/dist/path-suffix.d.ts +57 -0
- package/dist/path-suffix.d.ts.map +1 -0
- package/dist/path-suffix.js +73 -0
- package/dist/path-suffix.js.map +1 -0
- package/dist/predicate.d.ts +12 -0
- package/dist/predicate.d.ts.map +1 -1
- package/dist/predicate.js +20 -1
- package/dist/predicate.js.map +1 -1
- package/dist/preset-dispatch.d.ts +6 -34
- package/dist/preset-dispatch.d.ts.map +1 -1
- package/dist/preset-dispatch.js +30 -52
- package/dist/preset-dispatch.js.map +1 -1
- package/dist/report.d.ts +31 -7
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +174 -15
- package/dist/report.js.map +1 -1
- package/dist/rule-builder-like.d.ts +8 -2
- package/dist/rule-builder-like.d.ts.map +1 -1
- package/dist/rule-builder.d.ts +15 -0
- package/dist/rule-builder.d.ts.map +1 -1
- package/dist/rule-builder.js +41 -36
- package/dist/rule-builder.js.map +1 -1
- package/dist/rule-description.d.ts +50 -0
- package/dist/rule-description.d.ts.map +1 -1
- package/dist/rule-description.js +58 -0
- package/dist/rule-description.js.map +1 -1
- package/dist/selection-memo.d.ts +1 -3
- package/dist/selection-memo.d.ts.map +1 -1
- package/dist/selection-memo.js +1 -3
- package/dist/selection-memo.js.map +1 -1
- package/dist/silent-exclusion.d.ts +12 -0
- package/dist/silent-exclusion.d.ts.map +1 -1
- package/dist/silent-exclusion.js +22 -0
- package/dist/silent-exclusion.js.map +1 -1
- package/dist/terminal-builder.d.ts +29 -15
- package/dist/terminal-builder.d.ts.map +1 -1
- package/dist/terminal-builder.js +97 -51
- package/dist/terminal-builder.js.map +1 -1
- package/dist/vacuity-findings.d.ts +0 -6
- package/dist/vacuity-findings.d.ts.map +1 -1
- package/dist/vacuity-findings.js +27 -57
- package/dist/vacuity-findings.js.map +1 -1
- package/dist/violation.d.ts +22 -1
- package/dist/violation.d.ts.map +1 -1
- package/dist/violation.js.map +1 -1
- package/dist/watch.d.ts +71 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +129 -0
- package/dist/watch.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ArchViolation } from './violation.js';
|
|
2
|
+
/**
|
|
3
|
+
* The receipt — [ADR-014](../../../adr/014-the-emitter-refuses-a-verdict-without-evidence.md).
|
|
4
|
+
*
|
|
5
|
+
* **An `ArchViolation[]` carrying its own evidence**, not an object wrapping
|
|
6
|
+
* one. That shape is load-bearing and the review measured why: a plain object
|
|
7
|
+
* turns every untyped consumer green, because `result.length` reads `undefined`
|
|
8
|
+
* and `undefined > 0` is `false`. An array keeps `.length`, iteration,
|
|
9
|
+
* `flatMap` and every existing `for…of` working and correct, so a JavaScript
|
|
10
|
+
* consumer who never reads `examined` still exits non-zero on violations.
|
|
11
|
+
* "`CollectResult`'s shape by name" is a tidiness argument and it loses to that.
|
|
12
|
+
*
|
|
13
|
+
* **The name stays `CollectResult`.** Plan 0235 calls it a "receipt" in prose;
|
|
14
|
+
* there is deliberately no second exported name for one concept.
|
|
15
|
+
*
|
|
16
|
+
* `JSON.stringify` drops an array's own properties, which is why
|
|
17
|
+
* `formatViolationsJson` carries `examined` across by hand — see the note there.
|
|
18
|
+
*/
|
|
19
|
+
export interface CollectResult extends Array<ArchViolation> {
|
|
20
|
+
/** Units this rule actually reached its assertion over. The denominator. */
|
|
21
|
+
readonly examined: number;
|
|
22
|
+
/** The source loaded nothing at all, before any selection ran. */
|
|
23
|
+
readonly sourceEmpty?: boolean;
|
|
24
|
+
/** A caller declared this set legitimately empty, and the claim can expire. */
|
|
25
|
+
readonly declaredEmpty?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* This rule was never run — explicitly disabled, not merely empty.
|
|
28
|
+
*
|
|
29
|
+
* A third state, and it has to be distinguishable from both. A rule that RAN
|
|
30
|
+
* and examined nothing is dead and the merge must say so. A rule turned off
|
|
31
|
+
* never ran, so it has no denominator to be suspicious about — and marking it
|
|
32
|
+
* `declaredEmpty` instead would mint the declaration ADR-014 §3's amendment
|
|
33
|
+
* refuses to infer from `overrides: { id: 'off' }`.
|
|
34
|
+
*
|
|
35
|
+
* **It is falsifiable, like every other declaration here.** A rule that never
|
|
36
|
+
* ran examined nothing and found nothing, so `notRun` beside a non-zero
|
|
37
|
+
* `examined` or a violation is `emitter/contradictory-evidence` — checked at
|
|
38
|
+
* the gate and in the merge. Without that check this would be the one
|
|
39
|
+
* suppressing flag in the vocabulary that nothing can contradict, which is the
|
|
40
|
+
* shape ADR-014 §3's amendment exists to refuse.
|
|
41
|
+
*
|
|
42
|
+
* The two are not interchangeable: without this, one disabled rule reddened a
|
|
43
|
+
* whole preset whose other rules examined plenty (measured — `adrEnforcement`
|
|
44
|
+
* with `adr/valid-tiers` off). With it, a preset where EVERY member is `notRun`
|
|
45
|
+
* still sums to zero examined with no declaration, so the all-off case reports
|
|
46
|
+
* exactly as §3 requires.
|
|
47
|
+
*/
|
|
48
|
+
readonly notRun?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A specific dead-glob explanation for a zero-examined rule, pre-formatted
|
|
51
|
+
* by the dialect that computed it (`RuleBuilder.deadGlobDiagnosis()`) — the
|
|
52
|
+
* kernel never touches picomatch or a project type to build this itself.
|
|
53
|
+
*/
|
|
54
|
+
readonly deadGlob?: string;
|
|
55
|
+
}
|
|
56
|
+
/** Evidence a caller supplies alongside the violations. */
|
|
57
|
+
export interface Evidence {
|
|
58
|
+
readonly examined: number;
|
|
59
|
+
readonly sourceEmpty?: boolean;
|
|
60
|
+
readonly declaredEmpty?: boolean;
|
|
61
|
+
readonly notRun?: boolean;
|
|
62
|
+
readonly deadGlob?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The one constructor. Every terminal produces its receipt through this.
|
|
66
|
+
*
|
|
67
|
+
* **Stamps a FRESH array, deliberately.** `applyFilters` returns the *same
|
|
68
|
+
* reference* when no exclusion applies, so stamping `examined` onto what it
|
|
69
|
+
* hands back would mutate whatever a family memoized — a rule's evidence
|
|
70
|
+
* appearing on someone else's array, changing when it is re-read. The copy is
|
|
71
|
+
* the cost of not having that bug.
|
|
72
|
+
*/
|
|
73
|
+
export declare function collectResult(violations: readonly ArchViolation[], evidence: Evidence): CollectResult;
|
|
74
|
+
/** Does this value carry evidence at all, or is it a bare array? */
|
|
75
|
+
export declare function hasEvidence(value: readonly ArchViolation[]): value is CollectResult;
|
|
76
|
+
/**
|
|
77
|
+
* The one merge — ADR-014 §7. Fail-closed, and each rule below is a rule a
|
|
78
|
+
* hand-rolled sum got wrong somewhere (bug 0205's class: four emitters
|
|
79
|
+
* restating one rule and disagreeing).
|
|
80
|
+
*
|
|
81
|
+
* - **`examined` sums.** Zero members sums to zero — an absent denominator is
|
|
82
|
+
* not a large one.
|
|
83
|
+
* - **`declaredEmpty` requires EVERY zero-contributing member to declare.** A
|
|
84
|
+
* `some()` here lets one declared part vouch for a hand-counted zero beside
|
|
85
|
+
* it, which is the whole failure this merge exists to prevent.
|
|
86
|
+
* - **`sourceEmpty` on any member outranks every declaration.** ADR-010 §3's
|
|
87
|
+
* precedence: a declaration asserts a fact about a loaded corpus, and over
|
|
88
|
+
* an empty source it asserts nothing.
|
|
89
|
+
* - **A member with no evidence poisons the merge**, rather than being counted
|
|
90
|
+
* as zero. A dead member must not be able to hide inside a healthy sum, so
|
|
91
|
+
* the result carries no evidence either and the emitter says so.
|
|
92
|
+
*/
|
|
93
|
+
export declare function mergeCollectResults(parts: readonly (readonly ArchViolation[])[]): CollectResult;
|
|
94
|
+
//# sourceMappingURL=collect-result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-result.d.ts","sourceRoot":"","sources":["../src/collect-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAQnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,aAAa,CAAC;IACzD,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,2DAA2D;AAC3D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,QAAQ,EAAE,QAAQ,GACjB,aAAa,CAWf;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,KAAK,IAAI,aAAa,CAGnF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,aAAa,EAAE,CAAC,EAAE,GAAG,aAAa,CA0G/F"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { noReceiptViolation, passWithoutEvidenceViolation, contradictoryEvidenceViolation, sourceEmptyViolation, } from './emitter-findings.js';
|
|
2
|
+
/**
|
|
3
|
+
* The one constructor. Every terminal produces its receipt through this.
|
|
4
|
+
*
|
|
5
|
+
* **Stamps a FRESH array, deliberately.** `applyFilters` returns the *same
|
|
6
|
+
* reference* when no exclusion applies, so stamping `examined` onto what it
|
|
7
|
+
* hands back would mutate whatever a family memoized — a rule's evidence
|
|
8
|
+
* appearing on someone else's array, changing when it is re-read. The copy is
|
|
9
|
+
* the cost of not having that bug.
|
|
10
|
+
*/
|
|
11
|
+
export function collectResult(violations, evidence) {
|
|
12
|
+
// `Object.assign` returns the intersection type, so no assertion is needed —
|
|
13
|
+
// ADR-005. An earlier cut wrote `[...violations] as ArchViolation[] & {…}` and
|
|
14
|
+
// the repo's own `check:arch` reported it, in the module this plan adds.
|
|
15
|
+
return Object.assign([...violations], {
|
|
16
|
+
examined: evidence.examined,
|
|
17
|
+
...(evidence.sourceEmpty === true ? { sourceEmpty: true } : {}),
|
|
18
|
+
...(evidence.declaredEmpty === true ? { declaredEmpty: true } : {}),
|
|
19
|
+
...(evidence.notRun === true ? { notRun: true } : {}),
|
|
20
|
+
...(evidence.deadGlob === undefined ? {} : { deadGlob: evidence.deadGlob }),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Does this value carry evidence at all, or is it a bare array? */
|
|
24
|
+
export function hasEvidence(value) {
|
|
25
|
+
// `in` narrows without an assertion — ADR-005.
|
|
26
|
+
return 'examined' in value && typeof value.examined === 'number';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The one merge — ADR-014 §7. Fail-closed, and each rule below is a rule a
|
|
30
|
+
* hand-rolled sum got wrong somewhere (bug 0205's class: four emitters
|
|
31
|
+
* restating one rule and disagreeing).
|
|
32
|
+
*
|
|
33
|
+
* - **`examined` sums.** Zero members sums to zero — an absent denominator is
|
|
34
|
+
* not a large one.
|
|
35
|
+
* - **`declaredEmpty` requires EVERY zero-contributing member to declare.** A
|
|
36
|
+
* `some()` here lets one declared part vouch for a hand-counted zero beside
|
|
37
|
+
* it, which is the whole failure this merge exists to prevent.
|
|
38
|
+
* - **`sourceEmpty` on any member outranks every declaration.** ADR-010 §3's
|
|
39
|
+
* precedence: a declaration asserts a fact about a loaded corpus, and over
|
|
40
|
+
* an empty source it asserts nothing.
|
|
41
|
+
* - **A member with no evidence poisons the merge**, rather than being counted
|
|
42
|
+
* as zero. A dead member must not be able to hide inside a healthy sum, so
|
|
43
|
+
* the result carries no evidence either and the emitter says so.
|
|
44
|
+
*/
|
|
45
|
+
export function mergeCollectResults(parts) {
|
|
46
|
+
const violations = parts.flat();
|
|
47
|
+
if (!parts.every(hasEvidence)) {
|
|
48
|
+
// A member with no evidence at all. The merge REPORTS it rather than
|
|
49
|
+
// returning an evidence-free value for the emitter to notice later: the
|
|
50
|
+
// guarantee is this function's, and deferring it was also the last `as` in
|
|
51
|
+
// this file (an evidence-free value is not a `CollectResult`).
|
|
52
|
+
return collectResult([...violations, noReceiptViolation()], { examined: 0 });
|
|
53
|
+
}
|
|
54
|
+
// **ADR-014 §7's headline guarantee, and it needs its own pass.** Summing
|
|
55
|
+
// alone lets a dead member hide: measured, a check examining 0 merged with one
|
|
56
|
+
// examining 900 produced `examined: 900` and no finding — nine checks in
|
|
57
|
+
// `check-corpus.mjs`, one of them with a `continue` planted at the top of its
|
|
58
|
+
// loop, and the total still reads healthy. That is the plan's own top success
|
|
59
|
+
// criterion, and the field failure proposal 009 records, reproduced inside the
|
|
60
|
+
// primitive built to prevent it.
|
|
61
|
+
//
|
|
62
|
+
// A member is dead when it examined nothing, said nothing, and declared
|
|
63
|
+
// nothing. One that carries its own finding has spoken; one that declared
|
|
64
|
+
// empty has been accounted for.
|
|
65
|
+
// A member that never RAN is not a dead check — it is an absent one, with no
|
|
66
|
+
// denominator to be suspicious about. Excluding it is what keeps ONE disabled
|
|
67
|
+
// rule from reddening a preset whose others examined plenty (measured:
|
|
68
|
+
// `adrEnforcement` with `adr/valid-tiers` off). The all-off case still
|
|
69
|
+
// reports: every member is then `notRun`, the sum is zero and nobody
|
|
70
|
+
// declared, which the emitter's own gate catches downstream.
|
|
71
|
+
// A member whose `notRun` contradicts its own evidence, checked HERE and not
|
|
72
|
+
// only at the gate: the merged receipt carries no `notRun`, so a contradictory
|
|
73
|
+
// member is invisible downstream — it would slip through the one door that
|
|
74
|
+
// could see it. `notRun` is also the flag that exempts a member from the dead
|
|
75
|
+
// filter below, so an unchecked one is a suppressor with no falsifier (the
|
|
76
|
+
// asymmetry with `declaredEmpty` an enforcement review found in plan 0235).
|
|
77
|
+
const contradictory = parts.find((p) => p.notRun === true && (p.examined > 0 || p.length > 0));
|
|
78
|
+
if (contradictory !== undefined) {
|
|
79
|
+
return collectResult([...violations, contradictoryEvidenceViolation(contradictory.examined, contradictory.length)], { examined: parts.reduce((n, p) => n + p.examined, 0) });
|
|
80
|
+
}
|
|
81
|
+
// **An empty source is a member-level fault, checked HERE for the same reason
|
|
82
|
+
// the contradictory `notRun` above is** — ADR-014 §4: "an empty source
|
|
83
|
+
// outranks any declaration and names the source."
|
|
84
|
+
//
|
|
85
|
+
// The dead filter below EXEMPTS a `sourceEmpty` member, which is right for
|
|
86
|
+
// "did this member contribute nothing without saying why" — it said why. But
|
|
87
|
+
// §4 makes that answer a fault rather than an excuse, so the exemption alone
|
|
88
|
+
// left the merge disagreeing with the gate about the same receipt: measured,
|
|
89
|
+
// `finishPreset` reds a `{ examined: 0, sourceEmpty: true, declaredEmpty: true }`
|
|
90
|
+
// member while `mergeCollectResults([thatMember, aHealthyOne])` was GREEN,
|
|
91
|
+
// because the healthy sibling's `examined` carries the merged receipt past the
|
|
92
|
+
// gate's own check. That is the `notRun` asymmetry this ADR already fixed once,
|
|
93
|
+
// recreated for `sourceEmpty` by the change that fixed it at the gate.
|
|
94
|
+
const emptySource = parts.find((p) => p.sourceEmpty === true && p.examined === 0);
|
|
95
|
+
if (emptySource !== undefined) {
|
|
96
|
+
const summed = parts.reduce((n, p) => n + p.examined, 0);
|
|
97
|
+
return collectResult([...violations, sourceEmptyViolation()], {
|
|
98
|
+
examined: summed,
|
|
99
|
+
// **Only when it is true of the WHOLE.** Stamping it unconditionally
|
|
100
|
+
// minted a receipt saying "the source loaded nothing" beside a non-zero
|
|
101
|
+
// sum from healthy siblings — and the gate then contradicted it, blaming a
|
|
102
|
+
// flag nobody set. Four reviewers measured that on this branch's own
|
|
103
|
+
// headline scenario. The member-level fact is already carried by the
|
|
104
|
+
// finding above; the flag describes the merged whole, and of the whole it
|
|
105
|
+
// is only true when nothing was examined anywhere.
|
|
106
|
+
sourceEmpty: summed === 0 ? true : undefined,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const dead = parts.filter((p) => p.examined === 0 &&
|
|
110
|
+
p.length === 0 &&
|
|
111
|
+
p.notRun !== true &&
|
|
112
|
+
p.declaredEmpty !== true &&
|
|
113
|
+
p.sourceEmpty !== true);
|
|
114
|
+
if (dead.length > 0 && parts.length > 1) {
|
|
115
|
+
return collectResult([...violations, passWithoutEvidenceViolation()], {
|
|
116
|
+
examined: parts.reduce((n, p) => n + p.examined, 0),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const examined = parts.reduce((n, p) => n + p.examined, 0);
|
|
120
|
+
const anySourceEmpty = parts.some((p) => p.sourceEmpty === true);
|
|
121
|
+
return collectResult(violations, {
|
|
122
|
+
examined,
|
|
123
|
+
sourceEmpty: anySourceEmpty ? true : undefined,
|
|
124
|
+
// **Only when the WHOLE verdict examined nothing.** A member's declaration
|
|
125
|
+
// is about that member: `honestyAtClose` merges three rules and one of them
|
|
126
|
+
// legitimately declares itself empty while the other two examine plenty.
|
|
127
|
+
// Propagating that up marked the merged verdict declared-empty over a
|
|
128
|
+
// non-zero total, and the emitter's expiry then fired on it — correctly,
|
|
129
|
+
// which is how this was found. The member's own declaration is already
|
|
130
|
+
// satisfied by its own zero; the merge has nothing to add.
|
|
131
|
+
//
|
|
132
|
+
// `every`, not `some`: one declared part must never vouch for a
|
|
133
|
+
// hand-counted zero beside it.
|
|
134
|
+
declaredEmpty: !anySourceEmpty &&
|
|
135
|
+
examined === 0 &&
|
|
136
|
+
parts.length > 0 &&
|
|
137
|
+
parts.every((p) => p.declaredEmpty === true)
|
|
138
|
+
? true
|
|
139
|
+
: undefined,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=collect-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-result.js","sourceRoot":"","sources":["../src/collect-result.ts"],"names":[],"mappings":"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"}
|
package/dist/combinators.d.ts
CHANGED
|
@@ -22,6 +22,20 @@ export type Matcher<V> = (value: V) => boolean;
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function not<T>(input: Predicate<T>): Predicate<T>;
|
|
24
24
|
export declare function not<V>(input: Matcher<V>): Matcher<V>;
|
|
25
|
+
/**
|
|
26
|
+
* Every input to `and()`/`or()` must be the same kind.
|
|
27
|
+
*
|
|
28
|
+
* `eess-ts` carried a byte-identical copy — `no-copy-paste` reported it at
|
|
29
|
+
* 100%, with only the noun in the message differing — so the noun is now the
|
|
30
|
+
* parameter and the check has one owner.
|
|
31
|
+
*
|
|
32
|
+
* A `TypeError` and not a filtered result: mixing an object predicate with a
|
|
33
|
+
* matcher function silently drops one kind (the implementations below filter
|
|
34
|
+
* by `typeof`), so a rule composed that way would narrow by half of what its
|
|
35
|
+
* author wrote and pass on the rest. Refusing at composition time is the only
|
|
36
|
+
* point where the mistake is still visible.
|
|
37
|
+
*/
|
|
38
|
+
export declare function assertHomogeneous<T, V>(inputs: (Predicate<T> | Matcher<V>)[], matcherNoun?: string): void;
|
|
25
39
|
/**
|
|
26
40
|
* Combines predicates or matchers with AND logic.
|
|
27
41
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combinators.d.ts","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"combinators.d.ts","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG/C;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAA;AAE9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;AACzD,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAiBrD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EACpC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EACrC,WAAW,SAAY,GACtB,IAAI,CAMN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;AACnE,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAK7D;;;;;;;;;;;;GAYG;AACH,wBAAgB,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;AAClE,wBAAgB,EAAE,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA"}
|
package/dist/combinators.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { combineGlobs, negateGlobs } from './glob-site.js';
|
|
1
2
|
export function not(input) {
|
|
2
3
|
if (typeof input === 'function') {
|
|
3
4
|
return (value) => !input(value);
|
|
@@ -5,39 +6,75 @@ export function not(input) {
|
|
|
5
6
|
return {
|
|
6
7
|
description: `not (${input.description})`,
|
|
7
8
|
test: (element) => !input.test(element),
|
|
9
|
+
// Negation-normal-form push-down: `op` inverts as well as `polarity`.
|
|
10
|
+
// `not(unsatisfiable)` selects everything, so a negated site is
|
|
11
|
+
// over-selection rather than vacuity and can never be a fault — but a
|
|
12
|
+
// `not` nested inside the subtree flips it back, which is why this cannot
|
|
13
|
+
// just flip polarity. See `negateGlobs`.
|
|
14
|
+
globs: input.globs && negateGlobs(input.globs),
|
|
8
15
|
};
|
|
9
16
|
}
|
|
10
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Every input to `and()`/`or()` must be the same kind.
|
|
19
|
+
*
|
|
20
|
+
* `eess-ts` carried a byte-identical copy — `no-copy-paste` reported it at
|
|
21
|
+
* 100%, with only the noun in the message differing — so the noun is now the
|
|
22
|
+
* parameter and the check has one owner.
|
|
23
|
+
*
|
|
24
|
+
* A `TypeError` and not a filtered result: mixing an object predicate with a
|
|
25
|
+
* matcher function silently drops one kind (the implementations below filter
|
|
26
|
+
* by `typeof`), so a rule composed that way would narrow by half of what its
|
|
27
|
+
* author wrote and pass on the rest. Refusing at composition time is the only
|
|
28
|
+
* point where the mistake is still visible.
|
|
29
|
+
*/
|
|
30
|
+
export function assertHomogeneous(inputs, matcherNoun = 'Matcher') {
|
|
11
31
|
if (inputs.length === 0)
|
|
12
32
|
return;
|
|
13
33
|
const firstIsFunction = typeof inputs[0] === 'function';
|
|
14
34
|
if (inputs.some((i) => (typeof i === 'function') !== firstIsFunction)) {
|
|
15
|
-
throw new TypeError(
|
|
35
|
+
throw new TypeError(`Cannot mix Predicate objects and ${matcherNoun} functions in and()/or()`);
|
|
16
36
|
}
|
|
17
37
|
}
|
|
18
38
|
export function and(...inputs) {
|
|
19
|
-
|
|
20
|
-
if (typeof inputs[0] === 'function') {
|
|
21
|
-
const matchers = inputs.filter((input) => typeof input === 'function');
|
|
22
|
-
const fn = (value) => matchers.every((m) => m(value));
|
|
23
|
-
return fn;
|
|
24
|
-
}
|
|
25
|
-
const predicates = inputs.filter((input) => typeof input !== 'function');
|
|
26
|
-
return {
|
|
27
|
-
description: predicates.map((p) => p.description).join(' and '),
|
|
28
|
-
test: (element) => predicates.every((p) => p.test(element)),
|
|
29
|
-
};
|
|
39
|
+
return combine(inputs, 'all');
|
|
30
40
|
}
|
|
31
41
|
export function or(...inputs) {
|
|
42
|
+
return combine(inputs, 'any');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The body `and()` and `or()` share, with the quantifier as its one parameter.
|
|
46
|
+
*
|
|
47
|
+
* They were the same fifteen lines twice — `no-copy-paste` reported them at
|
|
48
|
+
* 98%, across both packages — but the reason to share them is that **three
|
|
49
|
+
* choices have to agree and nothing was making them**:
|
|
50
|
+
*
|
|
51
|
+
* | `op` | quantifier | joined with | glob node |
|
|
52
|
+
* | ------- | ---------- | ----------- | --------- |
|
|
53
|
+
* | `'all'` | `every` | `' and '` | `'all'` |
|
|
54
|
+
* | `'any'` | `some` | `' or '` | `'any'` |
|
|
55
|
+
*
|
|
56
|
+
* A conjunction selects nothing as soon as ONE input does; a disjunction only
|
|
57
|
+
* when EVERY input does. Pairing `some` with a `'all'` glob node — one edit
|
|
58
|
+
* away, in two files, when they were written out separately — reports a live
|
|
59
|
+
* selector as dead, which is a configuration finding against a rule that is
|
|
60
|
+
* fine. Deriving all three from `op` is what makes that unreachable.
|
|
61
|
+
*
|
|
62
|
+
* Inputs that declare no globs become retained opaque children rather than
|
|
63
|
+
* being dropped: dropping them is what would red `or(deadGlob, byName)`.
|
|
64
|
+
*/
|
|
65
|
+
function combine(inputs, op) {
|
|
32
66
|
assertHomogeneous(inputs);
|
|
67
|
+
const holds = (xs, f) => op === 'all' ? xs.every(f) : xs.some(f);
|
|
33
68
|
if (typeof inputs[0] === 'function') {
|
|
34
69
|
const matchers = inputs.filter((input) => typeof input === 'function');
|
|
35
|
-
|
|
70
|
+
const fn = (value) => holds(matchers, (m) => m(value));
|
|
71
|
+
return fn;
|
|
36
72
|
}
|
|
37
73
|
const predicates = inputs.filter((input) => typeof input !== 'function');
|
|
38
74
|
return {
|
|
39
|
-
description: predicates.map((p) => p.description).join(' or '),
|
|
40
|
-
test: (element) => predicates
|
|
75
|
+
description: predicates.map((p) => p.description).join(op === 'all' ? ' and ' : ' or '),
|
|
76
|
+
test: (element) => holds(predicates, (p) => p.test(element)),
|
|
77
|
+
globs: combineGlobs(op, predicates.map((p) => p.globs)),
|
|
41
78
|
};
|
|
42
79
|
}
|
|
43
80
|
//# sourceMappingURL=combinators.js.map
|
package/dist/combinators.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"combinators.js","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"combinators.js","sourceRoot":"","sources":["../src/combinators.ts"],"names":[],"mappings":"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"}
|
package/dist/condition.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ArchViolation } from './violation.js';
|
|
2
|
+
import { type CollectResult } from './collect-result.js';
|
|
2
3
|
import type { DeclaredGlobs } from './glob-site.js';
|
|
4
|
+
import type { RuleMetadata } from './rule-metadata.js';
|
|
3
5
|
/**
|
|
4
6
|
* Context passed to conditions during evaluation.
|
|
5
7
|
*
|
|
@@ -33,6 +35,25 @@ export interface ConditionContext {
|
|
|
33
35
|
*/
|
|
34
36
|
identifyByArgument?: number;
|
|
35
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The context a condition receives, projected from what the rule declared.
|
|
40
|
+
*
|
|
41
|
+
* Four of the five fields are read off `RuleMetadata` identically everywhere;
|
|
42
|
+
* only `rule` — the assembled English sentence — differs, because the kernel's
|
|
43
|
+
* `RuleBuilder` builds it from its own lists and `eess-ts`'s builds it from a
|
|
44
|
+
* `DeclaredRule`. The two bodies were 93% similar with that as the sole varying
|
|
45
|
+
* axis.
|
|
46
|
+
*
|
|
47
|
+
* Worth one owner beyond the duplicate count: this context is what a violation
|
|
48
|
+
* message is written from, so a field silently absent on one side of the family
|
|
49
|
+
* produces findings that name their rule in one dialect and not the other —
|
|
50
|
+
* exactly the "a violation must be actionable" clause of ADR-009.
|
|
51
|
+
*/
|
|
52
|
+
export declare function conditionContextFrom(opts: {
|
|
53
|
+
metadata: RuleMetadata | undefined;
|
|
54
|
+
reason: string | undefined;
|
|
55
|
+
rule: string;
|
|
56
|
+
}): ConditionContext;
|
|
36
57
|
/**
|
|
37
58
|
* A condition that evaluates filtered elements and returns violations.
|
|
38
59
|
*
|
|
@@ -59,4 +80,20 @@ export interface Condition<T> {
|
|
|
59
80
|
*/
|
|
60
81
|
readonly globs?: DeclaredGlobs;
|
|
61
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Run every condition over the selected elements, carrying the evidence.
|
|
85
|
+
*
|
|
86
|
+
* `examined` is the count the conditions were actually HANDED — not the
|
|
87
|
+
* pre-predicate population — and that distinction is the reason this is shared
|
|
88
|
+
* rather than written per builder. `graphql/resolver-rule-builder.ts` records
|
|
89
|
+
* what happened when two sibling builders derived it separately: one counted
|
|
90
|
+
* pre-predicate and the other post, and a chain whose `.that()` selected nothing
|
|
91
|
+
* reported 14 units examined, handed its conditions 0, and passed green with
|
|
92
|
+
* `diagnose()` silent. That is the fail-open cell ADR-009 exists to close.
|
|
93
|
+
*
|
|
94
|
+
* The zero-element early exit is stated rather than implied by an empty
|
|
95
|
+
* violation list (plan 0098): a receipt with `examined: 0` is the
|
|
96
|
+
* zero-evidence case, and ADR-010 wants it said out loud.
|
|
97
|
+
*/
|
|
98
|
+
export declare function evaluateConditions<T>(elements: T[], conditions: readonly Condition<T>[], context: ConditionContext): CollectResult;
|
|
62
99
|
//# sourceMappingURL=condition.d.ts.map
|
package/dist/condition.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,qBAAqB,CAAA;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAA;IAClC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,gBAAgB,CAQnB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAAA;IAEnE;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAC/B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,QAAQ,EAAE,CAAC,EAAE,EACb,UAAU,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,EACnC,OAAO,EAAE,gBAAgB,GACxB,aAAa,CAOf"}
|
package/dist/condition.js
CHANGED
|
@@ -1,2 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
import { collectResult } from './collect-result.js';
|
|
2
|
+
/**
|
|
3
|
+
* The context a condition receives, projected from what the rule declared.
|
|
4
|
+
*
|
|
5
|
+
* Four of the five fields are read off `RuleMetadata` identically everywhere;
|
|
6
|
+
* only `rule` — the assembled English sentence — differs, because the kernel's
|
|
7
|
+
* `RuleBuilder` builds it from its own lists and `eess-ts`'s builds it from a
|
|
8
|
+
* `DeclaredRule`. The two bodies were 93% similar with that as the sole varying
|
|
9
|
+
* axis.
|
|
10
|
+
*
|
|
11
|
+
* Worth one owner beyond the duplicate count: this context is what a violation
|
|
12
|
+
* message is written from, so a field silently absent on one side of the family
|
|
13
|
+
* produces findings that name their rule in one dialect and not the other —
|
|
14
|
+
* exactly the "a violation must be actionable" clause of ADR-009.
|
|
15
|
+
*/
|
|
16
|
+
export function conditionContextFrom(opts) {
|
|
17
|
+
return {
|
|
18
|
+
rule: opts.rule,
|
|
19
|
+
because: opts.reason,
|
|
20
|
+
ruleId: opts.metadata?.id,
|
|
21
|
+
suggestion: opts.metadata?.suggestion,
|
|
22
|
+
docs: opts.metadata?.docs,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run every condition over the selected elements, carrying the evidence.
|
|
27
|
+
*
|
|
28
|
+
* `examined` is the count the conditions were actually HANDED — not the
|
|
29
|
+
* pre-predicate population — and that distinction is the reason this is shared
|
|
30
|
+
* rather than written per builder. `graphql/resolver-rule-builder.ts` records
|
|
31
|
+
* what happened when two sibling builders derived it separately: one counted
|
|
32
|
+
* pre-predicate and the other post, and a chain whose `.that()` selected nothing
|
|
33
|
+
* reported 14 units examined, handed its conditions 0, and passed green with
|
|
34
|
+
* `diagnose()` silent. That is the fail-open cell ADR-009 exists to close.
|
|
35
|
+
*
|
|
36
|
+
* The zero-element early exit is stated rather than implied by an empty
|
|
37
|
+
* violation list (plan 0098): a receipt with `examined: 0` is the
|
|
38
|
+
* zero-evidence case, and ADR-010 wants it said out loud.
|
|
39
|
+
*/
|
|
40
|
+
export function evaluateConditions(elements, conditions, context) {
|
|
41
|
+
if (elements.length === 0)
|
|
42
|
+
return collectResult([], { examined: 0 });
|
|
43
|
+
const violations = [];
|
|
44
|
+
for (const condition of conditions) {
|
|
45
|
+
violations.push(...condition.evaluate(elements, context));
|
|
46
|
+
}
|
|
47
|
+
return collectResult(violations, { examined: elements.length });
|
|
48
|
+
}
|
|
2
49
|
//# sourceMappingURL=condition.js.map
|
package/dist/condition.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.js","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"condition.js","sourceRoot":"","sources":["../src/condition.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAsCvE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAIpC;IACC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,MAAM;QACpB,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE;QACzB,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU;QACrC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI;KAC1B,CAAA;AACH,CAAC;AA+BD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAa,EACb,UAAmC,EACnC,OAAyB;IAEzB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,aAAa,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IACpE,MAAM,UAAU,GAAoB,EAAE,CAAA;IACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,UAAU,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,aAAa,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;AACjE,CAAC"}
|
package/dist/correspondence.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Predicate } from './predicate.js';
|
|
1
2
|
import { TerminalBuilder, type CollectResult } from './terminal-builder.js';
|
|
2
3
|
/** How to identify an element for a two-sided violation message. */
|
|
3
4
|
export interface ElementInfo {
|
|
@@ -97,4 +98,33 @@ export declare class CorrespondenceBuilder<L, R> extends TerminalBuilder {
|
|
|
97
98
|
}
|
|
98
99
|
/** Bind two selections into a correspondence. */
|
|
99
100
|
export declare function correspondence<L, R>(opts: CorrespondenceOptions<L, R>): CorrespondenceBuilder<L, R>;
|
|
101
|
+
/**
|
|
102
|
+
* The elements a rule's predicates admit, as a `Selection`.
|
|
103
|
+
*
|
|
104
|
+
* A free function over the elements and the predicates rather than a method,
|
|
105
|
+
* for the same reason as `recordExclusions`: the two builders that need it sit
|
|
106
|
+
* in incompatible hierarchies — `eess-ts` has three levels where the kernel has
|
|
107
|
+
* two — so there is no common base to hold it. The two copies were
|
|
108
|
+
* byte-identical.
|
|
109
|
+
*/
|
|
110
|
+
export declare function selectMatching<T>(elements: readonly T[], predicates: readonly Predicate<T>[], opts: {
|
|
111
|
+
label: string;
|
|
112
|
+
identify: (element: T) => ElementInfo;
|
|
113
|
+
}): Selection<T>;
|
|
114
|
+
/**
|
|
115
|
+
* The elements every predicate admits.
|
|
116
|
+
*
|
|
117
|
+
* The same conjunction as `selectMatching`, without a `Selection` around it —
|
|
118
|
+
* split out because `eess-ts`'s two GraphQL builders want the ARRAY, to memoise
|
|
119
|
+
* it as the one set both `check()` and `diagnose()` read (plan 0096). They each
|
|
120
|
+
* carried this filter inline, byte for byte, which `no-copy-paste` reported at
|
|
121
|
+
* 100%; folding them onto `selectMatching` was not open, since a `Selection`
|
|
122
|
+
* carries a label and an `identify` neither of them has at that point.
|
|
123
|
+
*
|
|
124
|
+
* Sharing it with `selectMatching` matters beyond the duplicate count: "the
|
|
125
|
+
* elements a rule selected" must be ONE answer, or a builder's `.select()` side
|
|
126
|
+
* and its condition side can disagree about which elements the predicates
|
|
127
|
+
* admitted — the divergence ADR-010 exists to make impossible.
|
|
128
|
+
*/
|
|
129
|
+
export declare function matchingElements<T>(elements: readonly T[], predicates: readonly Predicate<T>[]): T[];
|
|
100
130
|
//# sourceMappingURL=correspondence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correspondence.d.ts","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"correspondence.d.ts","sourceRoot":"","sources":["../src/correspondence.ts"],"names":[],"mappings":"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"}
|
package/dist/correspondence.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { collectResult } from './collect-result.js';
|
|
1
2
|
import { TerminalBuilder } from './terminal-builder.js';
|
|
2
3
|
import { matchSelections } from './matching.js';
|
|
3
4
|
import { marksAssertsCardinality, assertsCardinality as checkAssertsCardinality, } from './cardinality.js';
|
|
@@ -60,7 +61,7 @@ export class CorrespondenceBuilder extends TerminalBuilder {
|
|
|
60
61
|
// many checks were chained; one side alone being empty is still real
|
|
61
62
|
// examination of the other.
|
|
62
63
|
const examined = this.opts.left.elements.length + this.opts.right.elements.length;
|
|
63
|
-
return
|
|
64
|
+
return collectResult(violations, { examined });
|
|
64
65
|
}
|
|
65
66
|
/**
|
|
66
67
|
* ADR-010: mirrors `RuleBuilder.assertsCardinality()` — per-check, not a
|
|
@@ -158,4 +159,38 @@ function relations(o, spec, ruleId) {
|
|
|
158
159
|
}
|
|
159
160
|
return out;
|
|
160
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* The elements a rule's predicates admit, as a `Selection`.
|
|
164
|
+
*
|
|
165
|
+
* A free function over the elements and the predicates rather than a method,
|
|
166
|
+
* for the same reason as `recordExclusions`: the two builders that need it sit
|
|
167
|
+
* in incompatible hierarchies — `eess-ts` has three levels where the kernel has
|
|
168
|
+
* two — so there is no common base to hold it. The two copies were
|
|
169
|
+
* byte-identical.
|
|
170
|
+
*/
|
|
171
|
+
export function selectMatching(elements, predicates, opts) {
|
|
172
|
+
return {
|
|
173
|
+
elements: matchingElements(elements, predicates),
|
|
174
|
+
label: opts.label,
|
|
175
|
+
identify: opts.identify,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The elements every predicate admits.
|
|
180
|
+
*
|
|
181
|
+
* The same conjunction as `selectMatching`, without a `Selection` around it —
|
|
182
|
+
* split out because `eess-ts`'s two GraphQL builders want the ARRAY, to memoise
|
|
183
|
+
* it as the one set both `check()` and `diagnose()` read (plan 0096). They each
|
|
184
|
+
* carried this filter inline, byte for byte, which `no-copy-paste` reported at
|
|
185
|
+
* 100%; folding them onto `selectMatching` was not open, since a `Selection`
|
|
186
|
+
* carries a label and an `identify` neither of them has at that point.
|
|
187
|
+
*
|
|
188
|
+
* Sharing it with `selectMatching` matters beyond the duplicate count: "the
|
|
189
|
+
* elements a rule selected" must be ONE answer, or a builder's `.select()` side
|
|
190
|
+
* and its condition side can disagree about which elements the predicates
|
|
191
|
+
* admitted — the divergence ADR-010 exists to make impossible.
|
|
192
|
+
*/
|
|
193
|
+
export function matchingElements(elements, predicates) {
|
|
194
|
+
return elements.filter((element) => predicates.every((predicate) => predicate.test(element)));
|
|
195
|
+
}
|
|
161
196
|
//# sourceMappingURL=correspondence.js.map
|