@shrkcrft/boundaries 0.1.0-alpha.27 → 0.1.0-alpha.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baseline/canonicalize.d.ts +35 -0
- package/dist/baseline/canonicalize.d.ts.map +1 -0
- package/dist/baseline/canonicalize.js +87 -0
- package/dist/baseline/compute-baseline.d.ts +23 -0
- package/dist/baseline/compute-baseline.d.ts.map +1 -0
- package/dist/baseline/compute-baseline.js +30 -0
- package/dist/baseline/diff-baseline.d.ts +68 -0
- package/dist/baseline/diff-baseline.d.ts.map +1 -0
- package/dist/baseline/diff-baseline.js +110 -0
- package/dist/baseline/json-path-keys.d.ts +10 -0
- package/dist/baseline/json-path-keys.d.ts.map +1 -0
- package/dist/baseline/json-path-keys.js +13 -0
- package/dist/extract/code-zones.d.ts +32 -0
- package/dist/extract/code-zones.d.ts.map +1 -0
- package/dist/extract/code-zones.js +59 -0
- package/dist/extract/extract-tokens.d.ts +30 -0
- package/dist/extract/extract-tokens.d.ts.map +1 -0
- package/dist/extract/extract-tokens.js +328 -0
- package/dist/extract/inspect-source.d.ts +24 -0
- package/dist/extract/inspect-source.d.ts.map +1 -0
- package/dist/extract/inspect-source.js +27 -0
- package/dist/extract/scan-literals.d.ts +54 -0
- package/dist/extract/scan-literals.d.ts.map +1 -0
- package/dist/extract/scan-literals.js +177 -0
- package/dist/generated/check-provenance.d.ts +37 -0
- package/dist/generated/check-provenance.d.ts.map +1 -0
- package/dist/generated/check-provenance.js +89 -0
- package/dist/generated/compare-trees.d.ts +26 -0
- package/dist/generated/compare-trees.d.ts.map +1 -0
- package/dist/generated/compare-trees.js +41 -0
- package/dist/generated/scan-generated.d.ts +17 -0
- package/dist/generated/scan-generated.d.ts.map +1 -0
- package/dist/generated/scan-generated.js +27 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/policy/evaluate-policy.d.ts +41 -5
- package/dist/policy/evaluate-policy.d.ts.map +1 -1
- package/dist/policy/evaluate-policy.js +112 -10
- package/dist/policy/run-policy.d.ts.map +1 -1
- package/dist/policy/run-policy.js +24 -3
- package/dist/wiring/evaluate-wiring.d.ts +81 -28
- package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
- package/dist/wiring/evaluate-wiring.js +0 -0
- package/dist/wiring/explain-wiring.d.ts +15 -7
- package/dist/wiring/explain-wiring.d.ts.map +1 -1
- package/dist/wiring/explain-wiring.js +28 -27
- package/dist/wiring/registry-query.d.ts +9 -0
- package/dist/wiring/registry-query.d.ts.map +1 -1
- package/dist/wiring/registry-query.js +11 -0
- package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
- package/dist/wiring/scan-wiring-files.js +4 -7
- package/package.json +2 -2
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type IWiringRule, type IWiringSource } from '@shrkcrft/core';
|
|
2
|
+
import { type IExtractFileEntry, type IExtractedSite } from '../extract/extract-tokens.js';
|
|
2
3
|
export declare const WIRING_SCHEMA: "sharkcraft.wiring/v1";
|
|
3
4
|
/** A file made available to the engine. */
|
|
4
|
-
export
|
|
5
|
-
/** Project-relative POSIX path. */
|
|
6
|
-
readonly path: string;
|
|
7
|
-
readonly content: string;
|
|
8
|
-
}
|
|
5
|
+
export type IWiringFileEntry = IExtractFileEntry;
|
|
9
6
|
/** A captured token + where it was captured. */
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
export type IWiringTokenSite = IExtractedSite;
|
|
8
|
+
/**
|
|
9
|
+
* Per-rule outcome. `skipped` is deliberately DISTINCT from `passed`: a rule
|
|
10
|
+
* whose source side extracted nothing checked nothing, and reporting that as a
|
|
11
|
+
* pass is the single failure mode that lets a stale selector ship a real
|
|
12
|
+
* violation. See {@link IWiringSkip}.
|
|
13
|
+
*/
|
|
14
|
+
export type WiringRuleStatus = 'passed' | 'failed' | 'skipped' | 'error';
|
|
15
15
|
export interface IWiringViolation {
|
|
16
16
|
readonly ruleId: string;
|
|
17
|
-
/** The token that is missing from the other side. */
|
|
17
|
+
/** The token that is missing from (or wrongly shared with) the other side. */
|
|
18
18
|
readonly token: string;
|
|
19
19
|
/** Declaring (or registering) file (project-relative) + 1-based line. */
|
|
20
20
|
readonly file: string;
|
|
@@ -23,20 +23,53 @@ export interface IWiringViolation {
|
|
|
23
23
|
/**
|
|
24
24
|
* Which side the token is missing from. `declared-missing` (the subset case):
|
|
25
25
|
* declared but not registered. `registered-missing` (parity only): registered
|
|
26
|
-
* but never declared.
|
|
26
|
+
* but never declared. `overlap` (disjoint only): present on both sides.
|
|
27
27
|
*/
|
|
28
|
-
readonly direction?: 'declared-missing' | 'registered-missing';
|
|
28
|
+
readonly direction?: 'declared-missing' | 'registered-missing' | 'overlap';
|
|
29
|
+
/** For a `chain` rule: which hop pair failed (0-based). */
|
|
30
|
+
readonly hop?: number;
|
|
31
|
+
/** Rendered `message` template, when the rule sets one. */
|
|
32
|
+
readonly message?: string;
|
|
29
33
|
readonly hint?: string;
|
|
30
34
|
}
|
|
35
|
+
/** A rule that could not be evaluated because its source side matched nothing. */
|
|
36
|
+
export interface IWiringSkip {
|
|
37
|
+
readonly ruleId: string;
|
|
38
|
+
/** Human-readable cause (`0 files matched` / `0 ids extracted`). */
|
|
39
|
+
readonly reason: string;
|
|
40
|
+
/** True when the rule set `failOnEmpty` — the skip counts as a failure. */
|
|
41
|
+
readonly failed: boolean;
|
|
42
|
+
readonly severity: 'error' | 'warning';
|
|
43
|
+
}
|
|
44
|
+
/** One `hop_i ⊆ hop_i+1` step of a multi-hop chain rule. */
|
|
45
|
+
export interface IWiringHopResult {
|
|
46
|
+
readonly index: number;
|
|
47
|
+
readonly fromCount: number;
|
|
48
|
+
readonly toCount: number;
|
|
49
|
+
readonly missing: number;
|
|
50
|
+
}
|
|
31
51
|
export interface IWiringRuleResult {
|
|
32
52
|
readonly ruleId: string;
|
|
33
53
|
readonly description?: string;
|
|
34
54
|
readonly severity: 'error' | 'warning';
|
|
55
|
+
readonly status: WiringRuleStatus;
|
|
35
56
|
readonly declaredCount: number;
|
|
36
57
|
readonly registeredCount: number;
|
|
58
|
+
/** Files actually scanned per side (after glob resolution) — the stale-glob signal. */
|
|
59
|
+
readonly declaredFiles: number;
|
|
60
|
+
readonly registeredFiles: number;
|
|
37
61
|
readonly violations: readonly IWiringViolation[];
|
|
38
62
|
/** Set when the rule is misconfigured (bad regex / no capture group / bad source). */
|
|
39
63
|
readonly error?: string;
|
|
64
|
+
/**
|
|
65
|
+
* True when the SINK extracted 0 ids while the source extracted some. Every
|
|
66
|
+
* declared token then "fails", which is a real failure — but almost always
|
|
67
|
+
* because the sink glob went stale, so the diff is annotated rather than
|
|
68
|
+
* silently reported as N unrelated violations.
|
|
69
|
+
*/
|
|
70
|
+
readonly emptySink?: boolean;
|
|
71
|
+
/** Per-hop breakdown for a `chain` rule. */
|
|
72
|
+
readonly hops?: readonly IWiringHopResult[];
|
|
40
73
|
}
|
|
41
74
|
export interface IWiringReport {
|
|
42
75
|
readonly schema: typeof WIRING_SCHEMA;
|
|
@@ -44,34 +77,54 @@ export interface IWiringReport {
|
|
|
44
77
|
readonly violations: readonly IWiringViolation[];
|
|
45
78
|
/** Rule-level misconfiguration messages (never throws — degrades gracefully). */
|
|
46
79
|
readonly diagnostics: readonly string[];
|
|
80
|
+
/** Rules that checked nothing, reported loudly instead of as a green pass. */
|
|
81
|
+
readonly skipped: readonly IWiringSkip[];
|
|
47
82
|
/**
|
|
48
|
-
* Count of rules that actually ran a comparison. A rule whose
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* error is not swallowed.
|
|
83
|
+
* Count of rules that actually ran a comparison. A rule whose source side
|
|
84
|
+
* matched 0 files or extracted 0 ids is NOT evaluated (see {@link skipped}).
|
|
85
|
+
* Misconfigured rules count as evaluated so their error is not swallowed.
|
|
52
86
|
*/
|
|
53
87
|
readonly evaluated: number;
|
|
54
88
|
readonly verdict: 'pass' | 'errors' | 'warnings';
|
|
55
89
|
}
|
|
56
90
|
/** Resolves a rule-side's globs to the concrete files (path + content) to scan. */
|
|
57
91
|
export type WiringFileResolver = (source: IWiringSource) => readonly IWiringFileEntry[];
|
|
92
|
+
/** Normalize the `registered` field (single source or union array) to an array. */
|
|
93
|
+
export declare function registeredSources(reg: IWiringRule['registered']): readonly IWiringSource[];
|
|
94
|
+
/**
|
|
95
|
+
* The rule's SOURCE side — the set whose membership is being asserted. For a
|
|
96
|
+
* chain rule that is hop 0. Used for glob collection, the loud-skip test, and
|
|
97
|
+
* `selfTest` expectations.
|
|
98
|
+
*/
|
|
99
|
+
export declare function wiringSourceSide(rule: IWiringRule): IWiringSource | undefined;
|
|
100
|
+
/** Every source a rule references, in declaration order. */
|
|
101
|
+
export declare function wiringSourcesOf(rule: IWiringRule): readonly IWiringSource[];
|
|
102
|
+
/** Every glob a rule references (both sides / every hop). */
|
|
103
|
+
export declare function wiringGlobsOf(rule: IWiringRule): string[];
|
|
58
104
|
/**
|
|
59
|
-
* Public: extract every token site from ONE wiring source
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* (never throws).
|
|
105
|
+
* Public: extract every token site from ONE wiring source over the given files.
|
|
106
|
+
* Shared by the wiring evaluator, the registry-inventory engine, and the
|
|
107
|
+
* extractor-backed baseline compute, so all three honour identical semantics. A
|
|
108
|
+
* misconfigured source returns an `error` and no sites (never throws).
|
|
64
109
|
*/
|
|
65
110
|
export declare function collectSourceSites(source: IWiringSource, files: readonly IWiringFileEntry[]): {
|
|
66
111
|
sites: readonly IWiringTokenSite[];
|
|
67
112
|
error?: string;
|
|
68
113
|
};
|
|
114
|
+
/** Structural validation of a whole rule (both forms), independent of the tree. */
|
|
115
|
+
export declare function validateWiringRule(rule: IWiringRule): string | undefined;
|
|
69
116
|
/**
|
|
70
|
-
* Pure wiring evaluation.
|
|
71
|
-
*
|
|
72
|
-
* the
|
|
73
|
-
*
|
|
74
|
-
*
|
|
117
|
+
* Pure wiring evaluation.
|
|
118
|
+
*
|
|
119
|
+
* For each rule the SOURCE token set is compared against the SINK sets per
|
|
120
|
+
* `mode` (`subset` / `parity` / `disjoint`) and `registeredMode` (`union` /
|
|
121
|
+
* `intersection`). A `chain` rule runs the same comparison across each adjacent
|
|
122
|
+
* hop pair. With `groupBy`, membership is checked within the same dir/package.
|
|
123
|
+
*
|
|
124
|
+
* Three outcomes are kept distinct, because collapsing them is what makes a
|
|
125
|
+
* homegrown gate untrustworthy: a rule that PASSED, a rule that FAILED, and a
|
|
126
|
+
* rule that checked NOTHING (`skipped`, promoted to a failure by `failOnEmpty`).
|
|
127
|
+
*
|
|
75
128
|
* The `resolve` callback supplies the files for a given rule-side (injected so
|
|
76
129
|
* the engine stays pure / testable — see `runWiring` for the fs-backed wiring).
|
|
77
130
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluate-wiring.d.ts","sourceRoot":"","sources":["../../src/wiring/evaluate-wiring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"evaluate-wiring.d.ts","sourceRoot":"","sources":["../../src/wiring/evaluate-wiring.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IWiringRule } from '@shrkcrft/core';
|
|
2
|
-
import { type IWiringTokenSite } from './evaluate-wiring.js';
|
|
2
|
+
import { type IWiringHopResult, type IWiringTokenSite } from './evaluate-wiring.js';
|
|
3
3
|
export declare const WIRING_EXPLAIN_SCHEMA: "sharkcraft.wiring-explain/v1";
|
|
4
4
|
/** One side (declared or registered) of a wiring rule, as extracted from the tree. */
|
|
5
5
|
export interface IWiringSideExplain {
|
|
@@ -22,7 +22,8 @@ export interface IWiringExplain {
|
|
|
22
22
|
readonly schema: typeof WIRING_EXPLAIN_SCHEMA;
|
|
23
23
|
readonly ruleId: string;
|
|
24
24
|
readonly description?: string;
|
|
25
|
-
readonly mode: 'subset' | 'parity';
|
|
25
|
+
readonly mode: 'subset' | 'parity' | 'disjoint';
|
|
26
|
+
readonly registeredMode: 'union' | 'intersection';
|
|
26
27
|
readonly groupBy?: 'dir' | 'package';
|
|
27
28
|
readonly severity: 'error' | 'warning';
|
|
28
29
|
readonly declared: IWiringSideExplain;
|
|
@@ -31,6 +32,14 @@ export interface IWiringExplain {
|
|
|
31
32
|
readonly declaredNotRegistered: readonly IWiringTokenSite[];
|
|
32
33
|
/** Registered tokens absent from the declared set (parity-only `registered-missing`). */
|
|
33
34
|
readonly registeredNotDeclared: readonly IWiringTokenSite[];
|
|
35
|
+
/** Tokens present on BOTH sides (disjoint-only `overlap`). */
|
|
36
|
+
readonly overlap: readonly IWiringTokenSite[];
|
|
37
|
+
/** Per-hop breakdown when the rule is a multi-hop `chain`. */
|
|
38
|
+
readonly hops?: readonly IWiringHopResult[];
|
|
39
|
+
/** `passed` / `failed` / `skipped` / `error` — skipped is never a pass. */
|
|
40
|
+
readonly status: 'passed' | 'failed' | 'skipped' | 'error';
|
|
41
|
+
/** Why the rule checked nothing, when it was skipped. */
|
|
42
|
+
readonly skipReason?: string;
|
|
34
43
|
readonly verdict: 'pass' | 'errors' | 'warnings';
|
|
35
44
|
/** Rule-level misconfiguration messages (engine degrades gracefully). */
|
|
36
45
|
readonly diagnostics: readonly string[];
|
|
@@ -43,11 +52,10 @@ export interface IExplainWiringOptions {
|
|
|
43
52
|
* Dry-run a single wiring rule against the live tree and return what each side
|
|
44
53
|
* extracted (declared set, registered set, the set-difference, the verdict) —
|
|
45
54
|
* WITHOUT writing config. Powers `wiring explain <ruleId>`, `wiring test
|
|
46
|
-
* <candidate>`, and `check wiring --explain <ruleId>`: the
|
|
47
|
-
*
|
|
48
|
-
* a rule. The diff/verdict reuse {@link evaluateWiring} so they match
|
|
49
|
-
* exactly (incl. `groupBy` membership)
|
|
50
|
-
* with the shared {@link collectSourceSites}. Never throws.
|
|
55
|
+
* <candidate>`, `gates explain <id>`, and `check wiring --explain <ruleId>`: the
|
|
56
|
+
* author can SEE the cross-file set-difference the gate computes before
|
|
57
|
+
* committing a rule. The diff/verdict reuse {@link evaluateWiring} so they match
|
|
58
|
+
* the gate exactly (incl. `groupBy` membership). Never throws.
|
|
51
59
|
*/
|
|
52
60
|
export declare function explainWiring(projectRoot: string, rule: IWiringRule, options?: IExplainWiringOptions): IWiringExplain;
|
|
53
61
|
//# sourceMappingURL=explain-wiring.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-wiring.d.ts","sourceRoot":"","sources":["../../src/wiring/explain-wiring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,gBAAgB,CAAC;AAGjE,OAAO,
|
|
1
|
+
{"version":3,"file":"explain-wiring.d.ts","sourceRoot":"","sources":["../../src/wiring/explain-wiring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,qBAAqB,EAAG,8BAAuC,CAAC;AAE7E,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,kFAAkF;IAClF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IAChD,QAAQ,CAAC,cAAc,EAAE,OAAO,GAAG,cAAc,CAAC;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,oFAAoF;IACpF,QAAQ,CAAC,qBAAqB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5D,yFAAyF;IACzF,QAAQ,CAAC,qBAAqB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5D,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IAC3D,yDAAyD;IACzD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IACjD,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,qBAAqB;IACpC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAQD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAyEhB"}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { matchesAny } from "../scan/glob.js";
|
|
2
2
|
import { readMatchingFiles } from "../util/walk-files.js";
|
|
3
|
-
import { collectSourceSites, evaluateWiring, } from "./evaluate-wiring.js";
|
|
3
|
+
import { collectSourceSites, evaluateWiring, registeredSources, wiringGlobsOf, wiringSourceSide, } from "./evaluate-wiring.js";
|
|
4
4
|
export const WIRING_EXPLAIN_SCHEMA = 'sharkcraft.wiring-explain/v1';
|
|
5
|
-
function registeredSources(reg) {
|
|
6
|
-
return Array.isArray(reg) ? reg : [reg];
|
|
7
|
-
}
|
|
8
5
|
function sortSites(sites) {
|
|
9
6
|
return [...sites].sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.token.localeCompare(b.token));
|
|
10
7
|
}
|
|
@@ -12,31 +9,32 @@ function sortSites(sites) {
|
|
|
12
9
|
* Dry-run a single wiring rule against the live tree and return what each side
|
|
13
10
|
* extracted (declared set, registered set, the set-difference, the verdict) —
|
|
14
11
|
* WITHOUT writing config. Powers `wiring explain <ruleId>`, `wiring test
|
|
15
|
-
* <candidate>`, and `check wiring --explain <ruleId>`: the
|
|
16
|
-
*
|
|
17
|
-
* a rule. The diff/verdict reuse {@link evaluateWiring} so they match
|
|
18
|
-
* exactly (incl. `groupBy` membership)
|
|
19
|
-
* with the shared {@link collectSourceSites}. Never throws.
|
|
12
|
+
* <candidate>`, `gates explain <id>`, and `check wiring --explain <ruleId>`: the
|
|
13
|
+
* author can SEE the cross-file set-difference the gate computes before
|
|
14
|
+
* committing a rule. The diff/verdict reuse {@link evaluateWiring} so they match
|
|
15
|
+
* the gate exactly (incl. `groupBy` membership). Never throws.
|
|
20
16
|
*/
|
|
21
17
|
export function explainWiring(projectRoot, rule, options = {}) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const sourceSide = wiringSourceSide(rule);
|
|
19
|
+
// For a chain, the reported "registered" side is the LAST hop (the far end).
|
|
20
|
+
const sinkSources = rule.chain && rule.chain.length >= 2
|
|
21
|
+
? [rule.chain[rule.chain.length - 1]]
|
|
22
|
+
: registeredSources(rule.registered);
|
|
23
|
+
const allGlobs = [...new Set(wiringGlobsOf(rule))];
|
|
26
24
|
const cache = readMatchingFiles(projectRoot, allGlobs, new Set(options.excludeDirs ?? []));
|
|
27
25
|
const entries = [...cache.entries()].map(([path, content]) => ({
|
|
28
26
|
path,
|
|
29
27
|
content,
|
|
30
28
|
}));
|
|
31
|
-
const filesFor = (source) => entries.filter((f) => matchesAny(f.path, source.files));
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
const filesFor = (source) => entries.filter((f) => matchesAny(f.path, source.files ?? []));
|
|
30
|
+
const declaredFiles = sourceSide ? filesFor(sourceSide) : [];
|
|
31
|
+
const declaredRes = sourceSide
|
|
32
|
+
? collectSourceSites(sourceSide, declaredFiles)
|
|
33
|
+
: { sites: [], error: 'rule sets no source side' };
|
|
36
34
|
const registeredFiles = new Set();
|
|
37
35
|
const registeredSites = [];
|
|
38
36
|
let registeredError;
|
|
39
|
-
for (const source of
|
|
37
|
+
for (const source of sinkSources) {
|
|
40
38
|
const files = filesFor(source);
|
|
41
39
|
for (const f of files)
|
|
42
40
|
registeredFiles.add(f.path);
|
|
@@ -48,17 +46,16 @@ export function explainWiring(projectRoot, rule, options = {}) {
|
|
|
48
46
|
// Canonical diff + counts + verdict from the gate engine (same groupBy logic).
|
|
49
47
|
const report = evaluateWiring([rule], filesFor);
|
|
50
48
|
const ruleResult = report.rules[0];
|
|
51
|
-
const
|
|
52
|
-
.filter((v) => v.direction ===
|
|
53
|
-
.map((v) => ({ token: v.token, file: v.file, line: v.line })));
|
|
54
|
-
const registeredNotDeclared = sortSites((ruleResult?.violations ?? [])
|
|
55
|
-
.filter((v) => v.direction === 'registered-missing')
|
|
49
|
+
const byDirection = (d) => sortSites((ruleResult?.violations ?? [])
|
|
50
|
+
.filter((v) => v.direction === d)
|
|
56
51
|
.map((v) => ({ token: v.token, file: v.file, line: v.line })));
|
|
52
|
+
const skip = report.skipped[0];
|
|
57
53
|
return {
|
|
58
54
|
schema: WIRING_EXPLAIN_SCHEMA,
|
|
59
55
|
ruleId: rule.id,
|
|
60
56
|
...(rule.description ? { description: rule.description } : {}),
|
|
61
|
-
mode: rule.mode
|
|
57
|
+
mode: rule.mode ?? 'subset',
|
|
58
|
+
registeredMode: rule.registeredMode ?? 'union',
|
|
62
59
|
...(rule.groupBy ? { groupBy: rule.groupBy } : {}),
|
|
63
60
|
severity: rule.severity ?? 'error',
|
|
64
61
|
declared: {
|
|
@@ -73,8 +70,12 @@ export function explainWiring(projectRoot, rule, options = {}) {
|
|
|
73
70
|
filesScanned: registeredFiles.size,
|
|
74
71
|
...(registeredError ? { error: registeredError } : {}),
|
|
75
72
|
},
|
|
76
|
-
declaredNotRegistered,
|
|
77
|
-
registeredNotDeclared,
|
|
73
|
+
declaredNotRegistered: byDirection('declared-missing'),
|
|
74
|
+
registeredNotDeclared: byDirection('registered-missing'),
|
|
75
|
+
overlap: byDirection('overlap'),
|
|
76
|
+
...(ruleResult?.hops ? { hops: ruleResult.hops } : {}),
|
|
77
|
+
status: ruleResult?.status ?? 'error',
|
|
78
|
+
...(skip ? { skipReason: skip.reason } : {}),
|
|
78
79
|
verdict: report.verdict,
|
|
79
80
|
diagnostics: report.diagnostics,
|
|
80
81
|
};
|
|
@@ -37,4 +37,13 @@ export declare function scanRegistry(projectRoot: string, decl: IRegistryDeclara
|
|
|
37
37
|
export declare function registryExists(inventory: IRegistryInventory, id: string): boolean;
|
|
38
38
|
/** The entry for `id`, or undefined if not declared. */
|
|
39
39
|
export declare function registryWhere(inventory: IRegistryInventory, id: string): IRegistryEntry | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Ids declared in more than one place.
|
|
42
|
+
*
|
|
43
|
+
* A latent bug the compiler cannot see: two roots both claiming the same
|
|
44
|
+
* registry id compile fine, and whichever registration wins at runtime is an
|
|
45
|
+
* accident of load order. Reported with EVERY site so the duplicate can be
|
|
46
|
+
* resolved, not just detected.
|
|
47
|
+
*/
|
|
48
|
+
export declare function registryDuplicates(inventory: IRegistryInventory): readonly IRegistryEntry[];
|
|
40
49
|
//# sourceMappingURL=registry-query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK3D,eAAO,MAAM,eAAe,EAAG,kCAA2C,CAAC;AAE3E,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACnD;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,eAAe,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,GAAE,oBAAyB,GACjC,kBAAkB,CA+CpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEnG"}
|
|
1
|
+
{"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAK3D,eAAO,MAAM,eAAe,EAAG,kCAA2C,CAAC;AAE3E,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACnD;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,eAAe,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,GAAE,oBAAyB,GACjC,kBAAkB,CA+CpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjF;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEnG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,kBAAkB,GAAG,SAAS,cAAc,EAAE,CAE3F"}
|
|
@@ -63,3 +63,14 @@ export function registryExists(inventory, id) {
|
|
|
63
63
|
export function registryWhere(inventory, id) {
|
|
64
64
|
return inventory.entries.find((e) => e.id === id);
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Ids declared in more than one place.
|
|
68
|
+
*
|
|
69
|
+
* A latent bug the compiler cannot see: two roots both claiming the same
|
|
70
|
+
* registry id compile fine, and whichever registration wins at runtime is an
|
|
71
|
+
* accident of load order. Reported with EVERY site so the duplicate can be
|
|
72
|
+
* resolved, not just detected.
|
|
73
|
+
*/
|
|
74
|
+
export function registryDuplicates(inventory) {
|
|
75
|
+
return inventory.entries.filter((e) => e.sites.length > 1);
|
|
76
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAO9B,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,iGAAiG;IACjG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,GAAE,iBAAsB,GAC9B,aAAa,CA+Bf"}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { matchesAny } from "../scan/glob.js";
|
|
2
2
|
import { readMatchingFiles } from "../util/walk-files.js";
|
|
3
|
-
import { evaluateWiring, } from "./evaluate-wiring.js";
|
|
4
|
-
/** All file globs a rule references:
|
|
3
|
+
import { evaluateWiring, wiringGlobsOf, } from "./evaluate-wiring.js";
|
|
4
|
+
/** All file globs a rule references: every side / every hop. */
|
|
5
5
|
function ruleGlobs(rule) {
|
|
6
|
-
|
|
7
|
-
const sources = Array.isArray(reg)
|
|
8
|
-
? reg
|
|
9
|
-
: [reg];
|
|
10
|
-
return [...rule.declared.files, ...sources.flatMap((s) => [...s.files])];
|
|
6
|
+
return wiringGlobsOf(rule);
|
|
11
7
|
}
|
|
12
8
|
/**
|
|
13
9
|
* Filesystem-backed wiring run: walks the project once, reads only the files a
|
|
@@ -33,6 +29,7 @@ export function runWiring(projectRoot, rules, options = {}) {
|
|
|
33
29
|
rules: [],
|
|
34
30
|
violations: [],
|
|
35
31
|
diagnostics: [],
|
|
32
|
+
skipped: [],
|
|
36
33
|
evaluated: 0,
|
|
37
34
|
verdict: 'pass',
|
|
38
35
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/boundaries",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.28",
|
|
4
4
|
"description": "SharkCraft boundary rules: detect when a repository violates its own architecture (forbidden imports across folder/package/layer boundaries).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
46
|
+
"@shrkcrft/core": "^0.1.0-alpha.28"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|