@shrkcrft/boundaries 0.1.0-alpha.30 → 0.1.0-alpha.31
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/compute-baseline.d.ts +8 -0
- package/dist/baseline/compute-baseline.d.ts.map +1 -1
- package/dist/baseline/compute-baseline.js +8 -7
- package/dist/baseline/diff-baseline.d.ts +28 -0
- package/dist/baseline/diff-baseline.d.ts.map +1 -1
- package/dist/baseline/diff-baseline.js +29 -0
- package/dist/evaluate/boundary-unit-finding.d.ts +10 -0
- package/dist/evaluate/boundary-unit-finding.d.ts.map +1 -0
- package/dist/evaluate/boundary-unit-finding.js +21 -0
- package/dist/evaluate/boundary-unit-kind.d.ts +7 -0
- package/dist/evaluate/boundary-unit-kind.d.ts.map +1 -0
- package/dist/evaluate/boundary-unit-kind.js +14 -0
- package/dist/evaluate/evaluate-boundaries.d.ts +230 -3
- package/dist/evaluate/evaluate-boundaries.d.ts.map +1 -1
- package/dist/evaluate/evaluate-boundaries.js +481 -53
- package/dist/evaluate/i-boundary-rule-settle-input.d.ts +27 -0
- package/dist/evaluate/i-boundary-rule-settle-input.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-rule-settle-input.js +1 -0
- package/dist/evaluate/i-boundary-rule-settlement.d.ts +23 -0
- package/dist/evaluate/i-boundary-rule-settlement.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-rule-settlement.js +1 -0
- package/dist/evaluate/i-boundary-unit-finding.d.ts +22 -0
- package/dist/evaluate/i-boundary-unit-finding.d.ts.map +1 -0
- package/dist/evaluate/i-boundary-unit-finding.js +1 -0
- package/dist/evaluate/settle-boundary-rule.d.ts +23 -0
- package/dist/evaluate/settle-boundary-rule.d.ts.map +1 -0
- package/dist/evaluate/settle-boundary-rule.js +73 -0
- package/dist/evaluate/with-boundary-rule-settlement.d.ts +11 -0
- package/dist/evaluate/with-boundary-rule-settlement.d.ts.map +1 -0
- package/dist/evaluate/with-boundary-rule-settlement.js +44 -0
- package/dist/extract/code-zones.d.ts +95 -3
- package/dist/extract/code-zones.d.ts.map +1 -1
- package/dist/extract/code-zones.js +313 -4
- package/dist/extract/extract-tokens.d.ts +30 -0
- package/dist/extract/extract-tokens.d.ts.map +1 -1
- package/dist/extract/extract-tokens.js +233 -27
- package/dist/extract/i-labeled-source.d.ts +12 -0
- package/dist/extract/i-labeled-source.d.ts.map +1 -0
- package/dist/extract/i-labeled-source.js +1 -0
- package/dist/extract/import-edges.d.ts +1 -0
- package/dist/extract/import-edges.d.ts.map +1 -1
- package/dist/extract/import-edges.js +20 -7
- package/dist/extract/inspect-source.d.ts +32 -3
- package/dist/extract/inspect-source.d.ts.map +1 -1
- package/dist/extract/inspect-source.js +50 -7
- package/dist/extract/parse-imports.d.ts +34 -12
- package/dist/extract/parse-imports.d.ts.map +1 -1
- package/dist/extract/parse-imports.js +119 -20
- package/dist/extract/scan-literals.d.ts +12 -1
- package/dist/extract/scan-literals.d.ts.map +1 -1
- package/dist/extract/scan-literals.js +15 -1
- package/dist/extract/source-liveness-request.d.ts +24 -0
- package/dist/extract/source-liveness-request.d.ts.map +1 -0
- package/dist/extract/source-liveness-request.js +45 -0
- package/dist/generated/check-provenance.d.ts.map +1 -1
- package/dist/generated/check-provenance.js +3 -1
- package/dist/generated/read-regen-tree.d.ts +16 -0
- package/dist/generated/read-regen-tree.d.ts.map +1 -0
- package/dist/generated/read-regen-tree.js +61 -0
- package/dist/generated/scan-generated.d.ts +8 -0
- package/dist/generated/scan-generated.d.ts.map +1 -1
- package/dist/generated/scan-generated.js +25 -10
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -0
- package/dist/model/boundary-intended-empty-line.d.ts +10 -0
- package/dist/model/boundary-intended-empty-line.d.ts.map +1 -0
- package/dist/model/boundary-intended-empty-line.js +11 -0
- package/dist/model/boundary-markable-list.d.ts +15 -0
- package/dist/model/boundary-markable-list.d.ts.map +1 -0
- package/dist/model/boundary-markable-list.js +15 -0
- package/dist/model/boundary-rule-input-keys.d.ts +11 -0
- package/dist/model/boundary-rule-input-keys.d.ts.map +1 -0
- package/dist/model/boundary-rule-input-keys.js +29 -0
- package/dist/model/boundary-rule-input.d.ts +23 -0
- package/dist/model/boundary-rule-input.d.ts.map +1 -0
- package/dist/model/boundary-rule-input.js +1 -0
- package/dist/model/boundary-rule-key-problems.d.ts +11 -0
- package/dist/model/boundary-rule-key-problems.d.ts.map +1 -0
- package/dist/model/boundary-rule-key-problems.js +36 -0
- package/dist/model/boundary-rule-marker-problems.d.ts +26 -0
- package/dist/model/boundary-rule-marker-problems.d.ts.map +1 -0
- package/dist/model/boundary-rule-marker-problems.js +94 -0
- package/dist/model/boundary-rule-scope.d.ts +88 -0
- package/dist/model/boundary-rule-scope.d.ts.map +1 -0
- package/dist/model/boundary-rule-scope.js +175 -0
- package/dist/model/boundary-rule.d.ts +120 -5
- package/dist/model/boundary-rule.d.ts.map +1 -1
- package/dist/model/boundary-rule.js +148 -1
- package/dist/model/boundary-unit-problem-issue.d.ts +9 -0
- package/dist/model/boundary-unit-problem-issue.d.ts.map +1 -0
- package/dist/model/boundary-unit-problem-issue.js +16 -0
- package/dist/model/normalize-boundary-rule.d.ts +21 -0
- package/dist/model/normalize-boundary-rule.d.ts.map +1 -0
- package/dist/model/normalize-boundary-rule.js +62 -0
- package/dist/policy/evaluate-policy.d.ts +80 -3
- package/dist/policy/evaluate-policy.d.ts.map +1 -1
- package/dist/policy/evaluate-policy.js +152 -19
- package/dist/policy/i-policy-rule-liveness.d.ts +15 -0
- package/dist/policy/i-policy-rule-liveness.d.ts.map +1 -0
- package/dist/policy/i-policy-rule-liveness.js +1 -0
- package/dist/policy/run-policy.d.ts +1 -1
- package/dist/policy/run-policy.d.ts.map +1 -1
- package/dist/policy/run-policy.js +171 -21
- package/dist/registry/load-boundary-rules.d.ts +29 -1
- package/dist/registry/load-boundary-rules.d.ts.map +1 -1
- package/dist/registry/load-boundary-rules.js +38 -8
- package/dist/scan/glob.d.ts +90 -0
- package/dist/scan/glob.d.ts.map +1 -1
- package/dist/scan/glob.js +205 -0
- package/dist/scan/i-glob-unit-measure.d.ts +22 -0
- package/dist/scan/i-glob-unit-measure.d.ts.map +1 -0
- package/dist/scan/i-glob-unit-measure.js +1 -0
- package/dist/scan/import-pattern.d.ts +109 -0
- package/dist/scan/import-pattern.d.ts.map +1 -0
- package/dist/scan/import-pattern.js +191 -0
- package/dist/scan/node-builtin-package-names.d.ts +13 -0
- package/dist/scan/node-builtin-package-names.d.ts.map +1 -0
- package/dist/scan/node-builtin-package-names.js +25 -0
- package/dist/scan/scan-imports.d.ts +36 -3
- package/dist/scan/scan-imports.d.ts.map +1 -1
- package/dist/scan/scan-imports.js +114 -45
- package/dist/util/blank-run-hazard-finding.d.ts +34 -0
- package/dist/util/blank-run-hazard-finding.d.ts.map +1 -0
- package/dist/util/blank-run-hazard-finding.js +1 -0
- package/dist/util/blank-run-hazard.d.ts +29 -0
- package/dist/util/blank-run-hazard.d.ts.map +1 -0
- package/dist/util/blank-run-hazard.js +450 -0
- package/dist/util/dead-glob-units.d.ts +46 -0
- package/dist/util/dead-glob-units.d.ts.map +1 -0
- package/dist/util/dead-glob-units.js +116 -0
- package/dist/util/glob-list-liveness-input.d.ts +31 -0
- package/dist/util/glob-list-liveness-input.d.ts.map +1 -0
- package/dist/util/glob-list-liveness-input.js +65 -0
- package/dist/util/i-dead-glob-unit.d.ts +24 -0
- package/dist/util/i-dead-glob-unit.d.ts.map +1 -0
- package/dist/util/i-dead-glob-unit.js +1 -0
- package/dist/util/i-glob-list-units.d.ts +25 -0
- package/dist/util/i-glob-list-units.d.ts.map +1 -0
- package/dist/util/i-glob-list-units.js +1 -0
- package/dist/util/i-glob-liveness-list.d.ts +23 -0
- package/dist/util/i-glob-liveness-list.d.ts.map +1 -0
- package/dist/util/i-glob-liveness-list.js +1 -0
- package/dist/util/i-glob-liveness-request.d.ts +11 -0
- package/dist/util/i-glob-liveness-request.d.ts.map +1 -0
- package/dist/util/i-glob-liveness-request.js +1 -0
- package/dist/util/i-glob-negation.d.ts +13 -0
- package/dist/util/i-glob-negation.d.ts.map +1 -0
- package/dist/util/i-glob-negation.js +1 -0
- package/dist/util/matched-files.d.ts +22 -0
- package/dist/util/matched-files.d.ts.map +1 -0
- package/dist/util/matched-files.js +1 -0
- package/dist/util/negation-cause.d.ts +15 -0
- package/dist/util/negation-cause.d.ts.map +1 -0
- package/dist/util/negation-cause.js +21 -0
- package/dist/util/plane-scan-exclude-dirs.d.ts +17 -0
- package/dist/util/plane-scan-exclude-dirs.d.ts.map +1 -0
- package/dist/util/plane-scan-exclude-dirs.js +22 -0
- package/dist/util/read-glob-list-liveness.d.ts +20 -0
- package/dist/util/read-glob-list-liveness.d.ts.map +1 -0
- package/dist/util/read-glob-list-liveness.js +20 -0
- package/dist/util/read-scope-coverage.d.ts +90 -0
- package/dist/util/read-scope-coverage.d.ts.map +1 -0
- package/dist/util/read-scope-coverage.js +173 -0
- package/dist/util/read-scope.d.ts +14 -0
- package/dist/util/read-scope.d.ts.map +1 -0
- package/dist/util/read-scope.js +1 -0
- package/dist/util/read-selected-files.d.ts +16 -0
- package/dist/util/read-selected-files.d.ts.map +1 -0
- package/dist/util/read-selected-files.js +25 -0
- package/dist/util/settle-glob-lists.d.ts +12 -0
- package/dist/util/settle-glob-lists.d.ts.map +1 -0
- package/dist/util/settle-glob-lists.js +13 -0
- package/dist/util/unread-file-reason.d.ts +26 -0
- package/dist/util/unread-file-reason.d.ts.map +1 -0
- package/dist/util/unread-file-reason.js +26 -0
- package/dist/util/unread-file.d.ts +10 -0
- package/dist/util/unread-file.d.ts.map +1 -0
- package/dist/util/unread-file.js +1 -0
- package/dist/util/walk-files.d.ts +74 -10
- package/dist/util/walk-files.d.ts.map +1 -1
- package/dist/util/walk-files.js +137 -31
- package/dist/wiring/evaluate-wiring.d.ts +79 -5
- package/dist/wiring/evaluate-wiring.d.ts.map +1 -1
- package/dist/wiring/evaluate-wiring.js +267 -12
- package/dist/wiring/explain-wiring.d.ts +49 -5
- package/dist/wiring/explain-wiring.d.ts.map +1 -1
- package/dist/wiring/explain-wiring.js +118 -10
- package/dist/wiring/i-idiom-role-coverage.d.ts +24 -0
- package/dist/wiring/i-idiom-role-coverage.d.ts.map +1 -0
- package/dist/wiring/i-idiom-role-coverage.js +1 -0
- package/dist/wiring/i-registration-query-verdict.d.ts +38 -0
- package/dist/wiring/i-registration-query-verdict.d.ts.map +1 -0
- package/dist/wiring/i-registration-query-verdict.js +1 -0
- package/dist/wiring/i-registration-roles.d.ts +71 -0
- package/dist/wiring/i-registration-roles.d.ts.map +1 -0
- package/dist/wiring/i-registration-roles.js +1 -0
- package/dist/wiring/measure-idiom-role-coverage.d.ts +12 -0
- package/dist/wiring/measure-idiom-role-coverage.d.ts.map +1 -0
- package/dist/wiring/measure-idiom-role-coverage.js +20 -0
- package/dist/wiring/measure-registration-roles.d.ts +25 -0
- package/dist/wiring/measure-registration-roles.d.ts.map +1 -0
- package/dist/wiring/measure-registration-roles.js +124 -0
- package/dist/wiring/plan-wiring-fix.js +2 -2
- package/dist/wiring/registration-graph.d.ts +12 -0
- package/dist/wiring/registration-graph.d.ts.map +1 -1
- package/dist/wiring/registration-graph.js +16 -5
- package/dist/wiring/registration-query-verdict.d.ts +23 -0
- package/dist/wiring/registration-query-verdict.d.ts.map +1 -0
- package/dist/wiring/registration-query-verdict.js +118 -0
- package/dist/wiring/registry-query.d.ts +8 -0
- package/dist/wiring/registry-query.d.ts.map +1 -1
- package/dist/wiring/registry-query.js +12 -5
- package/dist/wiring/scan-wiring-files.d.ts +4 -2
- package/dist/wiring/scan-wiring-files.d.ts.map +1 -1
- package/dist/wiring/scan-wiring-files.js +52 -15
- package/dist/wiring/sink-imports.d.ts +11 -0
- package/dist/wiring/sink-imports.d.ts.map +1 -1
- package/dist/wiring/sink-imports.js +11 -2
- package/dist/wiring/trace-literal.d.ts +6 -0
- package/dist/wiring/trace-literal.d.ts.map +1 -1
- package/dist/wiring/trace-literal.js +5 -2
- package/dist/wiring/wiring-labeled-sources.d.ts +12 -0
- package/dist/wiring/wiring-labeled-sources.d.ts.map +1 -0
- package/dist/wiring/wiring-labeled-sources.js +20 -0
- package/package.json +2 -2
|
@@ -2,8 +2,9 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { statSync } from 'node:fs';
|
|
3
3
|
import * as nodePath from 'node:path';
|
|
4
4
|
import { resolveSourceGlobs } from '@shrkcrft/core';
|
|
5
|
-
import {
|
|
5
|
+
import { globListSelects } from "../scan/glob.js";
|
|
6
6
|
import { readMatchingFiles, walkMatching } from "../util/walk-files.js";
|
|
7
|
+
import { readScopeOfLists } from "../util/read-scope-coverage.js";
|
|
7
8
|
import { collectSourceSites } from "./evaluate-wiring.js";
|
|
8
9
|
export const REGISTRATION_GRAPH_SCHEMA = 'sharkcraft.registration-graph/v1';
|
|
9
10
|
function sortSites(sites) {
|
|
@@ -28,12 +29,14 @@ function registrationGlobs(idioms) {
|
|
|
28
29
|
* diagnostic.
|
|
29
30
|
*/
|
|
30
31
|
export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
|
|
31
|
-
const
|
|
32
|
-
const entries = [...
|
|
32
|
+
const matched = readMatchingFiles(projectRoot, registrationGlobs(idioms), new Set(options.excludeDirs ?? []));
|
|
33
|
+
const entries = [...matched.files.entries()].map(([path, content]) => ({
|
|
33
34
|
path,
|
|
34
35
|
content,
|
|
35
36
|
}));
|
|
36
|
-
|
|
37
|
+
// The walk is the POSITIVE union of every role of every idiom; each role
|
|
38
|
+
// selects its own files, so one role's `!x` never hides another role's file.
|
|
39
|
+
const filesFor = (source) => entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(source)));
|
|
37
40
|
const declared = new Map();
|
|
38
41
|
const provided = new Map();
|
|
39
42
|
const consumed = new Map();
|
|
@@ -60,11 +63,15 @@ export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
|
|
|
60
63
|
provided: sortSites(provided.get(token) ?? []),
|
|
61
64
|
consumed: sortSites(consumed.get(token) ?? []),
|
|
62
65
|
}));
|
|
66
|
+
// Read scope PER ROLE LIST (one file counted once): an unread file every
|
|
67
|
+
// role that could reach it EXCLUDES is out of scope, not a gap.
|
|
68
|
+
const scope = readScopeOfLists(matched, idioms.flatMap((i) => [i.declared, i.provided, i.consumed].map((s) => resolveSourceGlobs(s))));
|
|
63
69
|
return {
|
|
64
70
|
schema: REGISTRATION_GRAPH_SCHEMA,
|
|
65
71
|
idioms: idioms.map((i) => i.name),
|
|
66
72
|
tokens,
|
|
67
73
|
diagnostics,
|
|
74
|
+
...(scope.unread.length > 0 ? { readScope: scope } : {}),
|
|
68
75
|
};
|
|
69
76
|
}
|
|
70
77
|
/**
|
|
@@ -75,6 +82,10 @@ export function buildRegistrationGraph(projectRoot, idioms, options = {}) {
|
|
|
75
82
|
* persisted registration-graph cache — a source edit shifts the signature even
|
|
76
83
|
* when no reindex has run, so a stale wiring verdict is impossible. Walk-and-stat
|
|
77
84
|
* only (no file reads), so it stays far cheaper than a full build. Never throws.
|
|
85
|
+
*
|
|
86
|
+
* It signs the POSITIVE union walk, not each role's selected set: a file a
|
|
87
|
+
* role's `!` excludes still shifts the signature. That over-inclusion can only
|
|
88
|
+
* cause a spurious cache miss — the safe direction — never a stale verdict.
|
|
78
89
|
*/
|
|
79
90
|
export function registrationGraphSignature(projectRoot, idioms, options = {}) {
|
|
80
91
|
const files = walkMatching(projectRoot, registrationGlobs(idioms), new Set(options.excludeDirs ?? [])).sort();
|
|
@@ -101,7 +112,7 @@ export function registrationGraphSignature(projectRoot, idioms, options = {}) {
|
|
|
101
112
|
export function providedTokensFromEntries(idioms, entries) {
|
|
102
113
|
const tokens = new Set();
|
|
103
114
|
for (const idiom of idioms) {
|
|
104
|
-
const files = entries.filter((f) =>
|
|
115
|
+
const files = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(idiom.provided)));
|
|
105
116
|
for (const s of collectSourceSites(idiom.provided, files).sites)
|
|
106
117
|
tokens.add(s.token);
|
|
107
118
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type IRegistrationIdiom, type IVerdictCoverage } from '@shrkcrft/core';
|
|
2
|
+
import type { IIdiomRoleCoverage } from './i-idiom-role-coverage.js';
|
|
3
|
+
import type { IRegistrationQueryVerdict } from './i-registration-query-verdict.js';
|
|
4
|
+
import { type IOrphanRegistration, type IRegistrationGraph, type IUnprovidedToken } from './registration-graph.js';
|
|
5
|
+
/**
|
|
6
|
+
* THE registration graph's read-scope record: the idiom files the reader could
|
|
7
|
+
* not read, named (`examined 3 of 4 files, 1 over the 1MB read cap: …`), or
|
|
8
|
+
* `undefined` when it read every file its idioms matched. `wiring unprovided |
|
|
9
|
+
* orphans`, finish's unprovided sub-gate and MCP `get_wiring_graph` all read
|
|
10
|
+
* it here, so the verb and its siblings cannot disagree about what was read.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registrationGraphCoverage(graph: IRegistrationGraph): IVerdictCoverage | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* `wiring unprovided` settled against what the graph read AND what every
|
|
15
|
+
* idiom's roles examined (`roles`: `measureIdiomRoleCoverage`, THE role
|
|
16
|
+
* authority — required, so no surface can forget the fold). `extra` adds
|
|
17
|
+
* candidates found another way (finish's base-ref provider regressions); they
|
|
18
|
+
* are merged by token and sorted, as finish always did.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registrationUnprovidedVerdict(graph: IRegistrationGraph, idioms: readonly IRegistrationIdiom[], roles: readonly IIdiomRoleCoverage[], changedFiles?: readonly string[], extra?: readonly IUnprovidedToken[]): IRegistrationQueryVerdict<IUnprovidedToken>;
|
|
21
|
+
/** `wiring orphans` settled against what the graph read and what every idiom's roles examined. */
|
|
22
|
+
export declare function registrationOrphansVerdict(graph: IRegistrationGraph, idioms: readonly IRegistrationIdiom[], roles: readonly IIdiomRoleCoverage[], changedFiles?: readonly string[]): IRegistrationQueryVerdict<IOrphanRegistration>;
|
|
23
|
+
//# sourceMappingURL=registration-query-verdict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration-query-verdict.d.ts","sourceRoot":"","sources":["../../src/wiring/registration-query-verdict.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAKjC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,SAAS,CASjG;AAwFD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,EAChC,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAClC,yBAAyB,CAAC,gBAAgB,CAAC,CAQ7C;AAED,kGAAkG;AAClG,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAC/B,yBAAyB,CAAC,mBAAmB,CAAC,CAUhD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { coverageShortfall, resolveSourceGlobs, ruleVerdictRecords, } from '@shrkcrft/core';
|
|
2
|
+
import { describeUnread, readScopeCoverage, unreadMatching } from "../util/read-scope-coverage.js";
|
|
3
|
+
import { registrationOrphans, registrationUnprovided, } from "./registration-graph.js";
|
|
4
|
+
/** At most this many demoted tokens ride on one coverage record. */
|
|
5
|
+
const TOKEN_LABEL_CAP = 20;
|
|
6
|
+
/**
|
|
7
|
+
* THE registration graph's read-scope record: the idiom files the reader could
|
|
8
|
+
* not read, named (`examined 3 of 4 files, 1 over the 1MB read cap: …`), or
|
|
9
|
+
* `undefined` when it read every file its idioms matched. `wiring unprovided |
|
|
10
|
+
* orphans`, finish's unprovided sub-gate and MCP `get_wiring_graph` all read
|
|
11
|
+
* it here, so the verb and its siblings cannot disagree about what was read.
|
|
12
|
+
*/
|
|
13
|
+
export function registrationGraphCoverage(graph) {
|
|
14
|
+
if (!graph.readScope || graph.readScope.unread.length === 0)
|
|
15
|
+
return undefined;
|
|
16
|
+
return {
|
|
17
|
+
...readScopeCoverage({ unit: 'registration idioms', expected: graph.idioms.length, examined: graph.idioms.length }, graph.readScope),
|
|
18
|
+
subject: 'registration graph',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every idiom's role records, as an absence query settles them (round 13, P4):
|
|
23
|
+
* the role authority's coverage (`examined 2 of 3 roles … declared (0 files)`)
|
|
24
|
+
* plus its expectEmpty acceptance, folded by core's `ruleVerdictRecords` and
|
|
25
|
+
* subject-prefixed with the idiom. A role record that is only the idiom's read
|
|
26
|
+
* gap is dropped while the graph's own read-scope record is carried — that
|
|
27
|
+
* record already names every unread idiom file (a union over idioms); its
|
|
28
|
+
* acceptance is kept, so a record B is never dropped silently.
|
|
29
|
+
*/
|
|
30
|
+
function roleRecords(graph, roles) {
|
|
31
|
+
const graphNamesUnread = registrationGraphCoverage(graph) !== undefined;
|
|
32
|
+
return roles.flatMap((r) => {
|
|
33
|
+
const records = graphNamesUnread && r.readGap
|
|
34
|
+
? r.unitAcceptance
|
|
35
|
+
? [r.unitAcceptance]
|
|
36
|
+
: []
|
|
37
|
+
: ruleVerdictRecords(r.coverage, r.unitAcceptance);
|
|
38
|
+
return records.map((c) => ({ ...c, subject: c.subject ?? r.idiom }));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* THE rule for an absence claim over an incomplete read. The graph merges
|
|
43
|
+
* tokens across idioms, so a site of the missing role in ANY idiom satisfies a
|
|
44
|
+
* token. When an unread file matches any idiom's `missingRole` globs, every
|
|
45
|
+
* candidate could be refuted there: all are demoted to `unproven`, named in a
|
|
46
|
+
* second coverage record. Otherwise the candidates are findings.
|
|
47
|
+
*
|
|
48
|
+
* Round 13 (P4): every idiom's ROLE coverage is carried too, always — the
|
|
49
|
+
* graph's read scope said nothing about a role whose globs matched no file, so
|
|
50
|
+
* "every declared token has a provider" printed a ✓ over a declared role that
|
|
51
|
+
* examined nothing. A dead role never demotes a candidate (a finding stays a
|
|
52
|
+
* finding, as on `gates check`); it keeps a clean answer from being a pass.
|
|
53
|
+
*/
|
|
54
|
+
function absenceVerdict(graph, idioms, roles, missingRole, candidates, unit, subject) {
|
|
55
|
+
const unread = graph.readScope?.unread ?? [];
|
|
56
|
+
// Per idiom's role LIST, never a flattened union: a negation subtracts from
|
|
57
|
+
// its own list only, so one idiom's `!x` cannot hide a refuting file from
|
|
58
|
+
// another idiom that reads it. Deduped by path, sorted.
|
|
59
|
+
const refutingByPath = new Map();
|
|
60
|
+
for (const i of unread.length > 0 ? idioms : []) {
|
|
61
|
+
for (const u of unreadMatching(unread, resolveSourceGlobs(i[missingRole]))) {
|
|
62
|
+
if (!refutingByPath.has(u.path))
|
|
63
|
+
refutingByPath.set(u.path, u);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const refuting = [...refutingByPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
67
|
+
const unproven = refuting.length > 0 ? candidates : [];
|
|
68
|
+
const fileRecord = registrationGraphCoverage(graph);
|
|
69
|
+
const coverage = [
|
|
70
|
+
...(fileRecord ? [fileRecord] : []),
|
|
71
|
+
...roleRecords(graph, roles),
|
|
72
|
+
...(unproven.length > 0
|
|
73
|
+
? [
|
|
74
|
+
{
|
|
75
|
+
unit,
|
|
76
|
+
expected: unproven.length,
|
|
77
|
+
examined: 0,
|
|
78
|
+
unexamined: unproven.slice(0, TOKEN_LABEL_CAP).map((t) => t.token),
|
|
79
|
+
unexaminedTotal: unproven.length,
|
|
80
|
+
reason: `may be ${missingRole} in a file the reader did not read — ${describeUnread(refuting)}`,
|
|
81
|
+
subject,
|
|
82
|
+
},
|
|
83
|
+
]
|
|
84
|
+
: []),
|
|
85
|
+
];
|
|
86
|
+
return {
|
|
87
|
+
findings: refuting.length > 0 ? [] : candidates,
|
|
88
|
+
unproven,
|
|
89
|
+
coverage,
|
|
90
|
+
shortfalls: coverage.flatMap((c) => {
|
|
91
|
+
const s = coverageShortfall(c);
|
|
92
|
+
return s === undefined ? [] : [s];
|
|
93
|
+
}),
|
|
94
|
+
unread,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `wiring unprovided` settled against what the graph read AND what every
|
|
99
|
+
* idiom's roles examined (`roles`: `measureIdiomRoleCoverage`, THE role
|
|
100
|
+
* authority — required, so no surface can forget the fold). `extra` adds
|
|
101
|
+
* candidates found another way (finish's base-ref provider regressions); they
|
|
102
|
+
* are merged by token and sorted, as finish always did.
|
|
103
|
+
*/
|
|
104
|
+
export function registrationUnprovidedVerdict(graph, idioms, roles, changedFiles, extra) {
|
|
105
|
+
let candidates = registrationUnprovided(graph, changedFiles);
|
|
106
|
+
if (extra !== undefined) {
|
|
107
|
+
const byToken = new Map();
|
|
108
|
+
for (const u of [...candidates, ...extra])
|
|
109
|
+
if (!byToken.has(u.token))
|
|
110
|
+
byToken.set(u.token, u);
|
|
111
|
+
candidates = [...byToken.values()].sort((a, b) => a.token.localeCompare(b.token));
|
|
112
|
+
}
|
|
113
|
+
return absenceVerdict(graph, idioms, roles, 'provided', candidates, 'unprovided tokens', 'unprovided');
|
|
114
|
+
}
|
|
115
|
+
/** `wiring orphans` settled against what the graph read and what every idiom's roles examined. */
|
|
116
|
+
export function registrationOrphansVerdict(graph, idioms, roles, changedFiles) {
|
|
117
|
+
return absenceVerdict(graph, idioms, roles, 'consumed', registrationOrphans(graph, changedFiles), 'orphan registrations', 'orphans');
|
|
118
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type IRegistryDeclaration } from '@shrkcrft/core';
|
|
2
|
+
import type { IReadScope } from '../util/read-scope.js';
|
|
2
3
|
export declare const REGISTRY_SCHEMA: "sharkcraft.registry-inventory/v1";
|
|
3
4
|
/** Where an id was found (project-relative path + 1-based line). */
|
|
4
5
|
export interface IRegistrySite {
|
|
@@ -22,6 +23,13 @@ export interface IRegistryInventory {
|
|
|
22
23
|
readonly entries: readonly IRegistryEntry[];
|
|
23
24
|
/** Misconfiguration messages (bad regex / no capture group / bad source). */
|
|
24
25
|
readonly diagnostics: readonly string[];
|
|
26
|
+
/**
|
|
27
|
+
* The SOURCE side's read scope: declaration files read, and matched ones the
|
|
28
|
+
* reader did not read (over the read cap, or unreadable). With any unread
|
|
29
|
+
* the inventory is incomplete: an absent id, "no duplicates" or an empty
|
|
30
|
+
* inventory proves nothing.
|
|
31
|
+
*/
|
|
32
|
+
readonly readScope: IReadScope;
|
|
25
33
|
}
|
|
26
34
|
export interface IScanRegistryOptions {
|
|
27
35
|
/** Project-relative directories to prune from the walk. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry-query.d.ts","sourceRoot":"","sources":["../../src/wiring/registry-query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAIxD,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;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAChC;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,CAwDpB;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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveSourceGlobs } from '@shrkcrft/core';
|
|
2
|
-
import {
|
|
2
|
+
import { globListSelects } from "../scan/glob.js";
|
|
3
3
|
import { readMatchingFiles } from "../util/walk-files.js";
|
|
4
|
+
import { unreadMatching } from "../util/read-scope-coverage.js";
|
|
4
5
|
import { collectSourceSites } from "./evaluate-wiring.js";
|
|
5
6
|
export const REGISTRY_SCHEMA = 'sharkcraft.registry-inventory/v1';
|
|
6
7
|
function sortSites(sites) {
|
|
@@ -18,16 +19,18 @@ export function scanRegistry(projectRoot, decl, options = {}) {
|
|
|
18
19
|
...(decl.consumer ? resolveSourceGlobs(decl.consumer) : []),
|
|
19
20
|
]),
|
|
20
21
|
];
|
|
21
|
-
const
|
|
22
|
-
const entries = [...
|
|
22
|
+
const matched = readMatchingFiles(projectRoot, globs, new Set(options.excludeDirs ?? []));
|
|
23
|
+
const entries = [...matched.files.entries()].map(([path, content]) => ({ path, content }));
|
|
23
24
|
const diagnostics = [];
|
|
24
|
-
|
|
25
|
+
// The walk above is the POSITIVE union of both sides; each side selects its
|
|
26
|
+
// own files, so the source's `!` never hides a consumer file (or vice versa).
|
|
27
|
+
const sourceFiles = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(decl.source)));
|
|
25
28
|
const declared = collectSourceSites(decl.source, sourceFiles);
|
|
26
29
|
if (declared.error)
|
|
27
30
|
diagnostics.push(`registry "${decl.name}" source: ${declared.error}`);
|
|
28
31
|
const consumerByToken = new Map();
|
|
29
32
|
if (decl.consumer) {
|
|
30
|
-
const consumerFiles = entries.filter((f) =>
|
|
33
|
+
const consumerFiles = entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(decl.consumer)));
|
|
31
34
|
const consumed = collectSourceSites(decl.consumer, consumerFiles);
|
|
32
35
|
if (consumed.error)
|
|
33
36
|
diagnostics.push(`registry "${decl.name}" consumer: ${consumed.error}`);
|
|
@@ -57,6 +60,10 @@ export function scanRegistry(projectRoot, decl, options = {}) {
|
|
|
57
60
|
...(decl.description ? { description: decl.description } : {}),
|
|
58
61
|
entries: entriesOut,
|
|
59
62
|
diagnostics,
|
|
63
|
+
readScope: {
|
|
64
|
+
read: sourceFiles.length,
|
|
65
|
+
unread: unreadMatching(matched.unread, resolveSourceGlobs(decl.source)),
|
|
66
|
+
},
|
|
60
67
|
};
|
|
61
68
|
}
|
|
62
69
|
/** True if `id` is declared in the registry (alias-blind exact membership). */
|
|
@@ -12,8 +12,10 @@ export interface IRunWiringOptions {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Filesystem-backed wiring run: walks the project once, reads only the files a
|
|
15
|
-
* rule references
|
|
16
|
-
*
|
|
15
|
+
* rule references, and evaluates every (selected) rule. A matched file the
|
|
16
|
+
* reader could not read (over the read cap) is handed to the engine as
|
|
17
|
+
* unread, so the rule's coverage names it instead of passing over it.
|
|
18
|
+
* Pure-engine output; the only IO is the read-only tree walk + reads.
|
|
17
19
|
*/
|
|
18
20
|
export declare function runWiring(projectRoot: string, rules: readonly IWiringRule[], options?: IRunWiringOptions): IWiringReport;
|
|
19
21
|
//# sourceMappingURL=scan-wiring-files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"scan-wiring-files.d.ts","sourceRoot":"","sources":["../../src/wiring/scan-wiring-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQ3F,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,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;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,OAAO,GAAE,iBAAsB,GAC9B,aAAa,CAsEf"}
|
|
@@ -1,29 +1,49 @@
|
|
|
1
|
-
import { resolveSourceGlobs } from '@shrkcrft/core';
|
|
2
|
-
import {
|
|
1
|
+
import { normalizeWiringRule, resolveSourceGlobs } from '@shrkcrft/core';
|
|
2
|
+
import { globListSelects } from "../scan/glob.js";
|
|
3
3
|
import { readMatchingFiles } from "../util/walk-files.js";
|
|
4
|
+
import { unreadMatching } from "../util/read-scope-coverage.js";
|
|
5
|
+
import { globListUnits } from "../util/dead-glob-units.js";
|
|
6
|
+
import { settleGlobLists } from "../util/settle-glob-lists.js";
|
|
7
|
+
import { sourceLivenessRequest } from "../extract/source-liveness-request.js";
|
|
4
8
|
import { loadTsconfigPaths } from "../scan/tsconfig-aliases.js";
|
|
5
|
-
import { evaluateWiring, wiringGlobsOf, } from "./evaluate-wiring.js";
|
|
9
|
+
import { evaluateWiring, wiringGlobsOf, wiringSourcesOf, } from "./evaluate-wiring.js";
|
|
10
|
+
import { wiringLabeledSources } from "./wiring-labeled-sources.js";
|
|
6
11
|
/** All file globs a rule references: every side / every hop. */
|
|
7
12
|
function ruleGlobs(rule) {
|
|
8
13
|
return wiringGlobsOf(rule);
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
11
16
|
* Filesystem-backed wiring run: walks the project once, reads only the files a
|
|
12
|
-
* rule references
|
|
13
|
-
*
|
|
17
|
+
* rule references, and evaluates every (selected) rule. A matched file the
|
|
18
|
+
* reader could not read (over the read cap) is handed to the engine as
|
|
19
|
+
* unread, so the rule's coverage names it instead of passing over it.
|
|
20
|
+
* Pure-engine output; the only IO is the read-only tree walk + reads.
|
|
14
21
|
*/
|
|
15
22
|
export function runWiring(projectRoot, rules, options = {}) {
|
|
16
|
-
|
|
23
|
+
// The engine entry normalises idempotently (round 13): a loaded rule comes
|
|
24
|
+
// back equal, a hand-built `{ pattern, expectEmpty }` entry becomes a glob
|
|
25
|
+
// plus a marker. A MALFORMED entry stays on the authored rule, which
|
|
26
|
+
// `evaluateWiring` reports as misconfigured — no glob reader here sees it.
|
|
27
|
+
const malformed = new Set();
|
|
28
|
+
let selected = rules.map((r) => {
|
|
29
|
+
const n = normalizeWiringRule(r);
|
|
30
|
+
if (n.ok)
|
|
31
|
+
return n.value;
|
|
32
|
+
malformed.add(r);
|
|
33
|
+
return r;
|
|
34
|
+
});
|
|
17
35
|
if (options.only && options.only.length > 0) {
|
|
18
36
|
const ids = new Set(options.only);
|
|
19
37
|
selected = selected.filter((r) => ids.has(r.id));
|
|
20
38
|
}
|
|
21
39
|
if (options.changedOnly) {
|
|
22
40
|
const changed = options.changedFiles ?? [];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
// Per SOURCE, never through the flattened union: a negation subtracts from
|
|
42
|
+
// its own list only, so one side's `!x` cannot hide a changed file another
|
|
43
|
+
// side reads, and a change touching only excluded files selects nothing.
|
|
44
|
+
// A malformed rule has no footprint to prove it out of scope: it stays in.
|
|
45
|
+
selected = selected.filter((r) => malformed.has(r) ||
|
|
46
|
+
wiringSourcesOf(r).some((s) => changed.some((c) => globListSelects(c, resolveSourceGlobs(s)))));
|
|
27
47
|
}
|
|
28
48
|
if (selected.length === 0) {
|
|
29
49
|
return {
|
|
@@ -33,14 +53,31 @@ export function runWiring(projectRoot, rules, options = {}) {
|
|
|
33
53
|
diagnostics: [],
|
|
34
54
|
skipped: [],
|
|
35
55
|
evaluated: 0,
|
|
56
|
+
acceptedEmpty: 0,
|
|
36
57
|
verdict: 'pass',
|
|
37
58
|
};
|
|
38
59
|
}
|
|
39
|
-
// Union of all globs across selected rules → one tree walk, cached
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
60
|
+
// Union of all globs across selected rules → one POSITIVE tree walk, cached
|
|
61
|
+
// reads; each source then selects its own files from it (`globListSelects`).
|
|
62
|
+
const allGlobs = [...new Set(selected.filter((r) => !malformed.has(r)).flatMap(ruleGlobs))];
|
|
63
|
+
const excludeDirs = options.excludeDirs ?? [];
|
|
64
|
+
const matched = readMatchingFiles(projectRoot, allGlobs, new Set(excludeDirs));
|
|
65
|
+
const entries = [...matched.files.entries()].map(([path, content]) => ({ path, content }));
|
|
66
|
+
const walked = entries.map((f) => f.path);
|
|
67
|
+
// THE one dead-unit decision (`globListUnits`, the one `gates coverage` and
|
|
68
|
+
// `policy-lint` read) over this run's positive walk.
|
|
69
|
+
const unitsOf = (globs) => globListUnits(walked, matched.unread, globs);
|
|
43
70
|
// The tsconfig paths let an `import-edges` source resolve alias specifiers the
|
|
44
71
|
// way the compiler does — the same map `check boundaries` resolves with.
|
|
45
|
-
return evaluateWiring(selected, (source) => entries.filter((f) =>
|
|
72
|
+
return evaluateWiring(selected, (source) => entries.filter((f) => globListSelects(f.path, resolveSourceGlobs(source))), { tsconfigPaths: loadTsconfigPaths(projectRoot) }, (source) => unreadMatching(matched.unread, resolveSourceGlobs(source)),
|
|
73
|
+
// Which negations emptied a source's list, so its skip reason says so (R12-DOC-2).
|
|
74
|
+
(source) => {
|
|
75
|
+
const units = unitsOf(resolveSourceGlobs(source));
|
|
76
|
+
return units.allExcluded ? units.negations : undefined;
|
|
77
|
+
},
|
|
78
|
+
// Every source's glob units settled with their `expectEmpty` markers
|
|
79
|
+
// (round 13) — THE per-source request `gates coverage` builds too
|
|
80
|
+
// (`sourceLivenessRequest` over the engine's own side labels), so `check
|
|
81
|
+
// wiring` and `gates coverage` accept, and word, one planned glob alike.
|
|
82
|
+
(rule) => settleGlobLists(sourceLivenessRequest(projectRoot, wiringLabeledSources(rule), excludeDirs, rule.id, unitsOf)));
|
|
46
83
|
}
|
|
@@ -31,6 +31,17 @@ export interface ISinkBinding {
|
|
|
31
31
|
/** Module specifier, for an import binding. */
|
|
32
32
|
readonly specifier?: string;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Top-level value/type declarations — the names a file binds itself.
|
|
36
|
+
*
|
|
37
|
+
* Every whitespace run is single-line (`[ \t]`). The old form opened with
|
|
38
|
+
* `(?:^|\n)\s*`: from every newline of a long blank block, `\s*` ran across all
|
|
39
|
+
* the lines below it and backtracked through each — O(lines × run) per block
|
|
40
|
+
* (`findBlankRunHazards` flags that shape). Modifiers and the declared name sit
|
|
41
|
+
* on the declaration's own line in real TS, so the matches are the same
|
|
42
|
+
* (`r75-blank-run-linearity.test.ts` holds old ≡ new over every source file).
|
|
43
|
+
*/
|
|
44
|
+
export declare const LOCAL_DECL: RegExp;
|
|
34
45
|
/**
|
|
35
46
|
* Every value name bound in the file: imports plus its own top-level
|
|
36
47
|
* declarations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sink-imports.d.ts","sourceRoot":"","sources":["../../src/wiring/sink-imports.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAE/F,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"sink-imports.d.ts","sourceRoot":"","sources":["../../src/wiring/sink-imports.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,kBAAkB,GAAG,OAAO,CAAC;AAE/F,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,QAC0I,CAAC;AAElK;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAsBnE;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,eAAe,GAAG,SAAS,CA6B7B;AAED,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,SAAS,CAe9B"}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { lineOf } from "../extract/scan-literals.js";
|
|
2
2
|
import { parseImportStatements } from "../extract/parse-imports.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Top-level value/type declarations — the names a file binds itself.
|
|
5
|
+
*
|
|
6
|
+
* Every whitespace run is single-line (`[ \t]`). The old form opened with
|
|
7
|
+
* `(?:^|\n)\s*`: from every newline of a long blank block, `\s*` ran across all
|
|
8
|
+
* the lines below it and backtracked through each — O(lines × run) per block
|
|
9
|
+
* (`findBlankRunHazards` flags that shape). Modifiers and the declared name sit
|
|
10
|
+
* on the declaration's own line in real TS, so the matches are the same
|
|
11
|
+
* (`r75-blank-run-linearity.test.ts` holds old ≡ new over every source file).
|
|
12
|
+
*/
|
|
13
|
+
export const LOCAL_DECL = /(?:^|\n)[ \t]*(?:export[ \t]+)?(?:declare[ \t]+)?(?:default[ \t]+)?(?:const|let|var|function(?:[ \t]*\*)?|class|enum|interface|type)[ \t]+([A-Za-z_$][\w$]*)/g;
|
|
5
14
|
/**
|
|
6
15
|
* Every value name bound in the file: imports plus its own top-level
|
|
7
16
|
* declarations.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IUnreadFile } from '../util/unread-file.js';
|
|
1
2
|
export declare const TRACE_SCHEMA: "sharkcraft.trace/v1";
|
|
2
3
|
/** Default source globs scanned when `trace` is given no `--glob`. */
|
|
3
4
|
export declare const TRACE_DEFAULT_GLOBS: readonly string[];
|
|
@@ -41,6 +42,11 @@ export interface ITraceReport {
|
|
|
41
42
|
readonly byRole: Readonly<Record<TraceRole, readonly ITraceSite[]>>;
|
|
42
43
|
/** Const names found bound to the literal (`const NAME = 'literal'`), if any. */
|
|
43
44
|
readonly aliases: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* Matched files the reader did not read (over the read cap, or unreadable),
|
|
47
|
+
* so a site inside one is missing from `byRole`. Set only when non-empty.
|
|
48
|
+
*/
|
|
49
|
+
readonly unread?: readonly IUnreadFile[];
|
|
44
50
|
}
|
|
45
51
|
export interface ITraceOptions {
|
|
46
52
|
/** Override the default source globs. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC;AAE3D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,oBAAY,SAAS;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,cAAc;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AA6ED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,YAAY,CA0Fd"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readSelectedFiles } from "../util/read-selected-files.js";
|
|
2
2
|
export const TRACE_SCHEMA = 'sharkcraft.trace/v1';
|
|
3
3
|
/** Default source globs scanned when `trace` is given no `--glob`. */
|
|
4
4
|
export const TRACE_DEFAULT_GLOBS = [
|
|
@@ -125,7 +125,9 @@ const ALIAS_NAME = /^[A-Z][\w$]*$/;
|
|
|
125
125
|
export function traceLiteral(projectRoot, literal, options = {}) {
|
|
126
126
|
const globs = options.globs && options.globs.length > 0 ? options.globs : TRACE_DEFAULT_GLOBS;
|
|
127
127
|
const exclude = new Set(options.excludeDirs ?? []);
|
|
128
|
-
|
|
128
|
+
// One glob list (the caller's `--glob`s, or the defaults): a `!` excludes.
|
|
129
|
+
const matched = readSelectedFiles(projectRoot, globs, exclude);
|
|
130
|
+
const cache = matched.files;
|
|
129
131
|
const sites = [];
|
|
130
132
|
const aliasNames = new Set();
|
|
131
133
|
// Exact quoted-literal match: the closing quote immediately follows, so the
|
|
@@ -205,5 +207,6 @@ export function traceLiteral(projectRoot, literal, options = {}) {
|
|
|
205
207
|
[TraceRole.Reference]: sortSites(byRole[TraceRole.Reference]),
|
|
206
208
|
},
|
|
207
209
|
aliases: [...aliasNames].sort(),
|
|
210
|
+
...(matched.unread.length > 0 ? { unread: matched.unread } : {}),
|
|
208
211
|
};
|
|
209
212
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IWiringRule } from '@shrkcrft/core';
|
|
2
|
+
import type { ILabeledSource } from '../extract/i-labeled-source.js';
|
|
3
|
+
/**
|
|
4
|
+
* THE enumeration of a wiring rule's sources, each named by its side —
|
|
5
|
+
* `declared`, `registered` (or `registered[i]` when a rule unions several
|
|
6
|
+
* sinks), `chain[i]`. `gates coverage` / `gates explain` (the CLI's
|
|
7
|
+
* `gateRuleLabeledSources`) and the wiring engine's liveness settle
|
|
8
|
+
* (`runWiring`) both read it, so a marker or a dead glob is named by one side
|
|
9
|
+
* label on every surface.
|
|
10
|
+
*/
|
|
11
|
+
export declare function wiringLabeledSources(rule: IWiringRule): readonly ILabeledSource[];
|
|
12
|
+
//# sourceMappingURL=wiring-labeled-sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiring-labeled-sources.d.ts","sourceRoot":"","sources":["../../src/wiring/wiring-labeled-sources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,cAAc,EAAE,CAWjF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE enumeration of a wiring rule's sources, each named by its side —
|
|
3
|
+
* `declared`, `registered` (or `registered[i]` when a rule unions several
|
|
4
|
+
* sinks), `chain[i]`. `gates coverage` / `gates explain` (the CLI's
|
|
5
|
+
* `gateRuleLabeledSources`) and the wiring engine's liveness settle
|
|
6
|
+
* (`runWiring`) both read it, so a marker or a dead glob is named by one side
|
|
7
|
+
* label on every surface.
|
|
8
|
+
*/
|
|
9
|
+
export function wiringLabeledSources(rule) {
|
|
10
|
+
const registered = Array.isArray(rule.registered)
|
|
11
|
+
? rule.registered
|
|
12
|
+
: rule.registered
|
|
13
|
+
? [rule.registered]
|
|
14
|
+
: [];
|
|
15
|
+
return [
|
|
16
|
+
...(rule.declared ? [{ label: 'declared', source: rule.declared }] : []),
|
|
17
|
+
...registered.map((source, i) => ({ label: registered.length > 1 ? `registered[${i}]` : 'registered', source })),
|
|
18
|
+
...(rule.chain ?? []).map((source, i) => ({ label: `chain[${i}]`, source })),
|
|
19
|
+
];
|
|
20
|
+
}
|
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.31",
|
|
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.31"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|