@descryy/core 0.1.0
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/LICENSE +21 -0
- package/dist/capabilities/index.d.ts +3 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +2 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/preconditions.d.ts +99 -0
- package/dist/capabilities/preconditions.d.ts.map +1 -0
- package/dist/capabilities/preconditions.js +94 -0
- package/dist/capabilities/preconditions.js.map +1 -0
- package/dist/contracts/engine.d.ts +90 -0
- package/dist/contracts/engine.d.ts.map +1 -0
- package/dist/contracts/engine.js +291 -0
- package/dist/contracts/engine.js.map +1 -0
- package/dist/contracts/index.d.ts +9 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/orphans.d.ts +96 -0
- package/dist/contracts/orphans.d.ts.map +1 -0
- package/dist/contracts/orphans.js +241 -0
- package/dist/contracts/orphans.js.map +1 -0
- package/dist/contracts/paths.d.ts +132 -0
- package/dist/contracts/paths.d.ts.map +1 -0
- package/dist/contracts/paths.js +245 -0
- package/dist/contracts/paths.js.map +1 -0
- package/dist/contracts/shapes.d.ts +99 -0
- package/dist/contracts/shapes.d.ts.map +1 -0
- package/dist/contracts/shapes.js +196 -0
- package/dist/contracts/shapes.js.map +1 -0
- package/dist/governance/budget.d.ts +89 -0
- package/dist/governance/budget.d.ts.map +1 -0
- package/dist/governance/budget.js +101 -0
- package/dist/governance/budget.js.map +1 -0
- package/dist/governance/candidate-boundary.d.ts +49 -0
- package/dist/governance/candidate-boundary.d.ts.map +1 -0
- package/dist/governance/candidate-boundary.js +51 -0
- package/dist/governance/candidate-boundary.js.map +1 -0
- package/dist/governance/escalation.d.ts +63 -0
- package/dist/governance/escalation.d.ts.map +1 -0
- package/dist/governance/escalation.js +72 -0
- package/dist/governance/escalation.js.map +1 -0
- package/dist/governance/fact-boundary.d.ts +77 -0
- package/dist/governance/fact-boundary.d.ts.map +1 -0
- package/dist/governance/fact-boundary.js +91 -0
- package/dist/governance/fact-boundary.js.map +1 -0
- package/dist/governance/finding-funnel.d.ts +109 -0
- package/dist/governance/finding-funnel.d.ts.map +1 -0
- package/dist/governance/finding-funnel.js +137 -0
- package/dist/governance/finding-funnel.js.map +1 -0
- package/dist/governance/index.d.ts +11 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +6 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/graph/build.d.ts +127 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +0 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/cross-language.d.ts +70 -0
- package/dist/graph/cross-language.d.ts.map +1 -0
- package/dist/graph/cross-language.js +104 -0
- package/dist/graph/cross-language.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/merge.d.ts +95 -0
- package/dist/graph/merge.d.ts.map +1 -0
- package/dist/graph/merge.js +131 -0
- package/dist/graph/merge.js.map +1 -0
- package/dist/graph/persist.d.ts +42 -0
- package/dist/graph/persist.d.ts.map +1 -0
- package/dist/graph/persist.js +52 -0
- package/dist/graph/persist.js.map +1 -0
- package/dist/graph/runtime-confirmation.d.ts +169 -0
- package/dist/graph/runtime-confirmation.d.ts.map +1 -0
- package/dist/graph/runtime-confirmation.js +318 -0
- package/dist/graph/runtime-confirmation.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/index.d.ts +5 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +3 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/mint.d.ts +81 -0
- package/dist/ledger/mint.d.ts.map +1 -0
- package/dist/ledger/mint.js +138 -0
- package/dist/ledger/mint.js.map +1 -0
- package/dist/ledger/resolve.d.ts +124 -0
- package/dist/ledger/resolve.d.ts.map +1 -0
- package/dist/ledger/resolve.js +290 -0
- package/dist/ledger/resolve.js.map +1 -0
- package/dist/multipr/fingerprint.d.ts +56 -0
- package/dist/multipr/fingerprint.d.ts.map +1 -0
- package/dist/multipr/fingerprint.js +169 -0
- package/dist/multipr/fingerprint.js.map +1 -0
- package/dist/multipr/hidden-dependency.d.ts +120 -0
- package/dist/multipr/hidden-dependency.d.ts.map +1 -0
- package/dist/multipr/hidden-dependency.js +185 -0
- package/dist/multipr/hidden-dependency.js.map +1 -0
- package/dist/multipr/index.d.ts +14 -0
- package/dist/multipr/index.d.ts.map +1 -0
- package/dist/multipr/index.js +8 -0
- package/dist/multipr/index.js.map +1 -0
- package/dist/multipr/mechanical.d.ts +239 -0
- package/dist/multipr/mechanical.d.ts.map +1 -0
- package/dist/multipr/mechanical.js +333 -0
- package/dist/multipr/mechanical.js.map +1 -0
- package/dist/multipr/migration-heads.d.ts +160 -0
- package/dist/multipr/migration-heads.d.ts.map +1 -0
- package/dist/multipr/migration-heads.js +212 -0
- package/dist/multipr/migration-heads.js.map +1 -0
- package/dist/multipr/overlap.d.ts +186 -0
- package/dist/multipr/overlap.d.ts.map +1 -0
- package/dist/multipr/overlap.js +254 -0
- package/dist/multipr/overlap.js.map +1 -0
- package/dist/multipr/scope-store.d.ts +105 -0
- package/dist/multipr/scope-store.d.ts.map +1 -0
- package/dist/multipr/scope-store.js +182 -0
- package/dist/multipr/scope-store.js.map +1 -0
- package/dist/multipr/superseded.d.ts +133 -0
- package/dist/multipr/superseded.d.ts.map +1 -0
- package/dist/multipr/superseded.js +128 -0
- package/dist/multipr/superseded.js.map +1 -0
- package/dist/query/confirmed-facts.d.ts +56 -0
- package/dist/query/confirmed-facts.d.ts.map +1 -0
- package/dist/query/confirmed-facts.js +64 -0
- package/dist/query/confirmed-facts.js.map +1 -0
- package/dist/query/declared-value-closure.d.ts +126 -0
- package/dist/query/declared-value-closure.d.ts.map +1 -0
- package/dist/query/declared-value-closure.js +156 -0
- package/dist/query/declared-value-closure.js.map +1 -0
- package/dist/query/index.d.ts +28 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +15 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/memory.d.ts +20 -0
- package/dist/query/memory.d.ts.map +1 -0
- package/dist/query/memory.js +55 -0
- package/dist/query/memory.js.map +1 -0
- package/dist/query/provider.d.ts +127 -0
- package/dist/query/provider.d.ts.map +1 -0
- package/dist/query/provider.js +54 -0
- package/dist/query/provider.js.map +1 -0
- package/dist/query/queries.d.ts +119 -0
- package/dist/query/queries.d.ts.map +1 -0
- package/dist/query/queries.js +267 -0
- package/dist/query/queries.js.map +1 -0
- package/dist/query/refusal-fetch.d.ts +114 -0
- package/dist/query/refusal-fetch.d.ts.map +1 -0
- package/dist/query/refusal-fetch.js +200 -0
- package/dist/query/refusal-fetch.js.map +1 -0
- package/dist/query/refusal-questions.d.ts +123 -0
- package/dist/query/refusal-questions.d.ts.map +1 -0
- package/dist/query/refusal-questions.js +122 -0
- package/dist/query/refusal-questions.js.map +1 -0
- package/dist/query/root-cause-score.d.ts +218 -0
- package/dist/query/root-cause-score.d.ts.map +1 -0
- package/dist/query/root-cause-score.js +251 -0
- package/dist/query/root-cause-score.js.map +1 -0
- package/dist/query/row-closure-picture.d.ts +108 -0
- package/dist/query/row-closure-picture.d.ts.map +1 -0
- package/dist/query/row-closure-picture.js +131 -0
- package/dist/query/row-closure-picture.js.map +1 -0
- package/dist/query/similar-incidents.d.ts +76 -0
- package/dist/query/similar-incidents.d.ts.map +1 -0
- package/dist/query/similar-incidents.js +169 -0
- package/dist/query/similar-incidents.js.map +1 -0
- package/dist/query/sqlite.d.ts +13 -0
- package/dist/query/sqlite.d.ts.map +1 -0
- package/dist/query/sqlite.js +54 -0
- package/dist/query/sqlite.js.map +1 -0
- package/dist/query/traverse.d.ts +110 -0
- package/dist/query/traverse.d.ts.map +1 -0
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -0
- package/dist/query/unresolved.d.ts +195 -0
- package/dist/query/unresolved.d.ts.map +1 -0
- package/dist/query/unresolved.js +225 -0
- package/dist/query/unresolved.js.map +1 -0
- package/dist/query/verification-status.d.ts +134 -0
- package/dist/query/verification-status.d.ts.map +1 -0
- package/dist/query/verification-status.js +163 -0
- package/dist/query/verification-status.js.map +1 -0
- package/dist/recording/index.d.ts +26 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +30 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/migrate.d.ts +22 -0
- package/dist/recording/migrate.d.ts.map +1 -0
- package/dist/recording/migrate.js +51 -0
- package/dist/recording/migrate.js.map +1 -0
- package/dist/recording/reader.d.ts +27 -0
- package/dist/recording/reader.d.ts.map +1 -0
- package/dist/recording/reader.js +55 -0
- package/dist/recording/reader.js.map +1 -0
- package/dist/recording/redact.d.ts +63 -0
- package/dist/recording/redact.d.ts.map +1 -0
- package/dist/recording/redact.js +98 -0
- package/dist/recording/redact.js.map +1 -0
- package/dist/recording/schema.d.ts +41 -0
- package/dist/recording/schema.d.ts.map +1 -0
- package/dist/recording/schema.js +104 -0
- package/dist/recording/schema.js.map +1 -0
- package/dist/recording/writer.d.ts +120 -0
- package/dist/recording/writer.d.ts.map +1 -0
- package/dist/recording/writer.js +81 -0
- package/dist/recording/writer.js.map +1 -0
- package/dist/scoping/fanout.d.ts +86 -0
- package/dist/scoping/fanout.d.ts.map +1 -0
- package/dist/scoping/fanout.js +95 -0
- package/dist/scoping/fanout.js.map +1 -0
- package/dist/scoping/index.d.ts +10 -0
- package/dist/scoping/index.d.ts.map +1 -0
- package/dist/scoping/index.js +6 -0
- package/dist/scoping/index.js.map +1 -0
- package/dist/scoping/score.d.ts +83 -0
- package/dist/scoping/score.d.ts.map +1 -0
- package/dist/scoping/score.js +125 -0
- package/dist/scoping/score.js.map +1 -0
- package/dist/scoping/tiers.d.ts +32 -0
- package/dist/scoping/tiers.d.ts.map +1 -0
- package/dist/scoping/tiers.js +35 -0
- package/dist/scoping/tiers.js.map +1 -0
- package/dist/scoping/traverse.d.ts +60 -0
- package/dist/scoping/traverse.d.ts.map +1 -0
- package/dist/scoping/traverse.js +172 -0
- package/dist/scoping/traverse.js.map +1 -0
- package/dist/scoping/weights.d.ts +97 -0
- package/dist/scoping/weights.d.ts.map +1 -0
- package/dist/scoping/weights.js +167 -0
- package/dist/scoping/weights.js.map +1 -0
- package/dist/sources/git/diff.d.ts +53 -0
- package/dist/sources/git/diff.d.ts.map +1 -0
- package/dist/sources/git/diff.js +140 -0
- package/dist/sources/git/diff.js.map +1 -0
- package/dist/sources/git/env.d.ts +2 -0
- package/dist/sources/git/env.d.ts.map +1 -0
- package/dist/sources/git/env.js +21 -0
- package/dist/sources/git/env.js.map +1 -0
- package/dist/sources/git/history.d.ts +140 -0
- package/dist/sources/git/history.d.ts.map +1 -0
- package/dist/sources/git/history.js +314 -0
- package/dist/sources/git/history.js.map +1 -0
- package/dist/sources/git/index.d.ts +7 -0
- package/dist/sources/git/index.d.ts.map +1 -0
- package/dist/sources/git/index.js +4 -0
- package/dist/sources/git/index.js.map +1 -0
- package/dist/sources/git/source.d.ts +95 -0
- package/dist/sources/git/source.d.ts.map +1 -0
- package/dist/sources/git/source.js +0 -0
- package/dist/sources/git/source.js.map +1 -0
- package/dist/sources/migrations/dialects.d.ts +59 -0
- package/dist/sources/migrations/dialects.d.ts.map +1 -0
- package/dist/sources/migrations/dialects.js +84 -0
- package/dist/sources/migrations/dialects.js.map +1 -0
- package/dist/sources/migrations/index.d.ts +3 -0
- package/dist/sources/migrations/index.d.ts.map +1 -0
- package/dist/sources/migrations/index.js +2 -0
- package/dist/sources/migrations/index.js.map +1 -0
- package/dist/sources/migrations/read.d.ts +113 -0
- package/dist/sources/migrations/read.d.ts.map +1 -0
- package/dist/sources/migrations/read.js +270 -0
- package/dist/sources/migrations/read.js.map +1 -0
- package/dist/sources/workspace/index.d.ts +3 -0
- package/dist/sources/workspace/index.d.ts.map +1 -0
- package/dist/sources/workspace/index.js +2 -0
- package/dist/sources/workspace/index.js.map +1 -0
- package/dist/sources/workspace/workspace.d.ts +64 -0
- package/dist/sources/workspace/workspace.d.ts.map +1 -0
- package/dist/sources/workspace/workspace.js +294 -0
- package/dist/sources/workspace/workspace.js.map +1 -0
- package/dist/store/driver/driver.d.ts +82 -0
- package/dist/store/driver/driver.d.ts.map +1 -0
- package/dist/store/driver/driver.js +31 -0
- package/dist/store/driver/driver.js.map +1 -0
- package/dist/store/driver/node-sqlite.d.ts +28 -0
- package/dist/store/driver/node-sqlite.d.ts.map +1 -0
- package/dist/store/driver/node-sqlite.js +188 -0
- package/dist/store/driver/node-sqlite.js.map +1 -0
- package/dist/store/index.d.ts +33 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +46 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/migrate.d.ts +68 -0
- package/dist/store/migrate.d.ts.map +1 -0
- package/dist/store/migrate.js +140 -0
- package/dist/store/migrate.js.map +1 -0
- package/dist/store/reader.d.ts +127 -0
- package/dist/store/reader.d.ts.map +1 -0
- package/dist/store/reader.js +269 -0
- package/dist/store/reader.js.map +1 -0
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +322 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/writer.d.ts +116 -0
- package/dist/store/writer.d.ts.map +1 -0
- package/dist/store/writer.js +279 -0
- package/dist/store/writer.js.map +1 -0
- package/dist/tiers/certify.d.ts +375 -0
- package/dist/tiers/certify.d.ts.map +1 -0
- package/dist/tiers/certify.js +434 -0
- package/dist/tiers/certify.js.map +1 -0
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.d.ts.map +1 -0
- package/dist/tiers/index.js +3 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/tiers/ladder.d.ts +73 -0
- package/dist/tiers/ladder.d.ts.map +1 -0
- package/dist/tiers/ladder.js +197 -0
- package/dist/tiers/ladder.js.map +1 -0
- package/dist/validation/config-graph.d.ts +104 -0
- package/dist/validation/config-graph.d.ts.map +1 -0
- package/dist/validation/config-graph.js +205 -0
- package/dist/validation/config-graph.js.map +1 -0
- package/dist/validation/env.d.ts +243 -0
- package/dist/validation/env.d.ts.map +1 -0
- package/dist/validation/env.js +447 -0
- package/dist/validation/env.js.map +1 -0
- package/dist/validation/index.d.ts +16 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/join-substitution.d.ts +186 -0
- package/dist/validation/join-substitution.d.ts.map +1 -0
- package/dist/validation/join-substitution.js +191 -0
- package/dist/validation/join-substitution.js.map +1 -0
- package/dist/validation/migrations.d.ts +133 -0
- package/dist/validation/migrations.d.ts.map +1 -0
- package/dist/validation/migrations.js +315 -0
- package/dist/validation/migrations.js.map +1 -0
- package/dist/validation/nodes.d.ts +32 -0
- package/dist/validation/nodes.d.ts.map +1 -0
- package/dist/validation/nodes.js +45 -0
- package/dist/validation/nodes.js.map +1 -0
- package/dist/validation/rollback.d.ts +124 -0
- package/dist/validation/rollback.d.ts.map +1 -0
- package/dist/validation/rollback.js +302 -0
- package/dist/validation/rollback.js.map +1 -0
- package/dist/validation/route-drift.d.ts +108 -0
- package/dist/validation/route-drift.d.ts.map +1 -0
- package/dist/validation/route-drift.js +103 -0
- package/dist/validation/route-drift.js.map +1 -0
- package/dist/validation/sources.d.ts +153 -0
- package/dist/validation/sources.d.ts.map +1 -0
- package/dist/validation/sources.js +409 -0
- package/dist/validation/sources.js.map +1 -0
- package/dist/worker/index.d.ts +5 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +3 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/pool.d.ts +73 -0
- package/dist/worker/pool.d.ts.map +1 -0
- package/dist/worker/pool.js +203 -0
- package/dist/worker/pool.js.map +1 -0
- package/dist/worker/protocol.d.ts +91 -0
- package/dist/worker/protocol.d.ts.map +1 -0
- package/dist/worker/protocol.js +19 -0
- package/dist/worker/protocol.js.map +1 -0
- package/dist/worker/traversal.worker.d.ts +25 -0
- package/dist/worker/traversal.worker.d.ts.map +1 -0
- package/dist/worker/traversal.worker.js +124 -0
- package/dist/worker/traversal.worker.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four capability tiers of §11A.11, expressed as requirements a machine can
|
|
3
|
+
* check — and the ones it cannot, named as such.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this file exists at all
|
|
6
|
+
*
|
|
7
|
+
* Nine adapters state a tier in a doc comment. **DEC-075 §6 says a tier is
|
|
8
|
+
* generated from `capabilities()` and the gate results, never written in
|
|
9
|
+
* prose**, and until now nothing implemented that, so the only tier claims in
|
|
10
|
+
* the repository were the ones a human typed. That is the single piece of
|
|
11
|
+
* standing dishonesty in the codebase: a claim about what works for a language,
|
|
12
|
+
* made by the person who wanted it to be true, in a comment nothing checks.
|
|
13
|
+
*
|
|
14
|
+
* ## The ladder, from §11A.11
|
|
15
|
+
*
|
|
16
|
+
* | tier | what works | what it costs |
|
|
17
|
+
* | --- | --- | --- |
|
|
18
|
+
* | **S — Full** | contract *shape* matching, data propagation, oracle-derived tests | R3 + router extractor + ORM extractor |
|
|
19
|
+
* | **A — Contract** | endpoint matching, path-level orphans, migration DAG | R2/R3 + router extractor |
|
|
20
|
+
* | **B — Graph** | blast radius, impact scoring, test discovery, in-language root cause | R2 adapter |
|
|
21
|
+
* | **C — Signal** | env/config, rollback readiness, hidden dependency, file-level blast radius | R0, one grammar and one query set |
|
|
22
|
+
*
|
|
23
|
+
* A tier is **cumulative**: S implies A implies B implies C. So a language is
|
|
24
|
+
* awarded the highest tier whose requirements are *all* met, and the
|
|
25
|
+
* requirements of every tier below it are re-checked rather than assumed —
|
|
26
|
+
* because an adapter can reach R3 and still not emit a `FUNCTION`, and a ladder
|
|
27
|
+
* that assumed monotonicity would hand it an A on the strength of its routes.
|
|
28
|
+
*
|
|
29
|
+
* ## What a requirement may be
|
|
30
|
+
*
|
|
31
|
+
* Three kinds, and the split is the honest part:
|
|
32
|
+
*
|
|
33
|
+
* - **`capability`** — read straight off `capabilities()`. Machine-checked, no
|
|
34
|
+
* judgement. `capabilities()` is itself machine-checked against what the
|
|
35
|
+
* adapter actually emitted (DEC-070), so this is evidence, not a promise.
|
|
36
|
+
* - **`conformance`** — the corpus passes at the resolution the tier claims.
|
|
37
|
+
* Machine-checked by running the harness.
|
|
38
|
+
* - **`attested`** — a fact no machine in this repository can establish, so it
|
|
39
|
+
* is *declared* with its evidence and marked as declared. There is exactly
|
|
40
|
+
* one, and it is DEC-075's second condition: a ≥200-edge precision draw on
|
|
41
|
+
* two repositories **the adapter's author did not choose**. A machine can
|
|
42
|
+
* check the 200 and the two; it cannot check "did not choose", which is a
|
|
43
|
+
* fact about a person's intent. Recording it as attested is what stops the
|
|
44
|
+
* generator laundering a human judgement into a computed result.
|
|
45
|
+
*/
|
|
46
|
+
/** Highest first. The order the generator walks, and the only place it lives. */
|
|
47
|
+
export const TIERS = ["S", "A", "B", "C"];
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// There is deliberately no list of framework names here.
|
|
50
|
+
//
|
|
51
|
+
// The first version of this file carried three: ROUTER_EXTRACTORS with
|
|
52
|
+
// `express`, `fastapi`, `flask`, `django`; ORM_EXTRACTORS with `sqlalchemy`,
|
|
53
|
+
// `prisma`, `gorm`; SCHEMA_EXTRACTORS with `pydantic`, `zod`. The boundary lint
|
|
54
|
+
// rejected all twelve, and it was right on the merits rather than pedantic:
|
|
55
|
+
// **that is engine code above the Canonical IR knowing what a Python web
|
|
56
|
+
// framework is called**, which is rule 1, and a list that must grow every time
|
|
57
|
+
// anyone writes an extractor is the shape rule 1 exists to prevent.
|
|
58
|
+
//
|
|
59
|
+
// The fix is not an allowlist entry. It is that the requirement was
|
|
60
|
+
// **redundant**. "A router extractor is bound" was asking, indirectly, whether
|
|
61
|
+
// the adapter can produce routes — and the requirement immediately above it
|
|
62
|
+
// already asks that directly, in vocabulary terms: does it emit `API_ROUTE`,
|
|
63
|
+
// `API_ENDPOINT` and `SERVES_API`. An adapter emitting those has a router
|
|
64
|
+
// extractor by definition, whatever it is called, and one that does not cannot
|
|
65
|
+
// be rescued by a name in `frameworkExtractors`. The same held for the ORM
|
|
66
|
+
// requirement against `MODEL`/`READS`/`WRITES`, and for the schema requirement
|
|
67
|
+
// against `DTO`/`USES_TYPE`.
|
|
68
|
+
//
|
|
69
|
+
// So the name check bought nothing and cost the boundary. Deleting it changed
|
|
70
|
+
// no adapter's tier, which is the evidence that it was never load-bearing.
|
|
71
|
+
//
|
|
72
|
+
// The general form, worth keeping: **when engine code needs to know what a
|
|
73
|
+
// framework is, ask what it produced.** The vocabulary is the shared language
|
|
74
|
+
// precisely so the engine never has to learn a framework's name.
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// The ladder itself.
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
const C_REQUIREMENTS = [
|
|
80
|
+
{
|
|
81
|
+
id: "C.parses",
|
|
82
|
+
kind: "capability",
|
|
83
|
+
statement: "the adapter produces a file-level graph",
|
|
84
|
+
// `MODULE`, not `FILE`. The vocabulary has both, and this requirement first
|
|
85
|
+
// asked for both — at which point every adapter in the repository failed
|
|
86
|
+
// Tier C. Checked against a real run rather than assumed: **no adapter
|
|
87
|
+
// emits or declares `FILE`**, and `MODULE` is what a file resolves to in
|
|
88
|
+
// all nine. A requirement no producer can satisfy does not make a generator
|
|
89
|
+
// strict, it makes it useless, and the ladder would have been "corrected"
|
|
90
|
+
// by whoever needed a tier first.
|
|
91
|
+
nodeTypes: ["MODULE"],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "C.corpus-r0",
|
|
95
|
+
kind: "conformance",
|
|
96
|
+
statement: "the conformance corpus passes at R0",
|
|
97
|
+
minResolution: 0,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
const B_REQUIREMENTS = [
|
|
101
|
+
{
|
|
102
|
+
id: "B.declarations",
|
|
103
|
+
kind: "capability",
|
|
104
|
+
statement: "declarations and calls are resolved, so blast radius has edges to walk",
|
|
105
|
+
nodeTypes: ["FUNCTION", "CLASS"],
|
|
106
|
+
edgeTypes: ["CALLS", "IMPORTS"],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "B.tests",
|
|
110
|
+
kind: "capability",
|
|
111
|
+
statement: "tests are discoverable, which is what makes impact scoring actionable",
|
|
112
|
+
nodeTypes: ["TEST_CASE"],
|
|
113
|
+
edgeTypes: ["TESTS"],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "B.r2",
|
|
117
|
+
kind: "capability",
|
|
118
|
+
statement: "the adapter reaches R2 — references resolved, not only parsed",
|
|
119
|
+
minResolution: 2,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "B.corpus-r2",
|
|
123
|
+
kind: "conformance",
|
|
124
|
+
statement: "the conformance corpus passes at R2",
|
|
125
|
+
minResolution: 2,
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
const A_REQUIREMENTS = [
|
|
129
|
+
{
|
|
130
|
+
id: "A.routes",
|
|
131
|
+
kind: "capability",
|
|
132
|
+
statement: "routes are emitted from source and joined to the shared endpoint node",
|
|
133
|
+
nodeTypes: ["API_ROUTE", "API_ENDPOINT"],
|
|
134
|
+
edgeTypes: ["SERVES_API"],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "A.callers",
|
|
138
|
+
kind: "capability",
|
|
139
|
+
statement: "a caller reaches an endpoint through USES_API, which is the other half of the join",
|
|
140
|
+
edgeTypes: ["USES_API"],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "A.precision-draw",
|
|
144
|
+
kind: "attested",
|
|
145
|
+
statement: "a >=200-edge precision draw passes on two repositories the adapter's author did not choose (DEC-075 §6.2)",
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
const S_REQUIREMENTS = [
|
|
149
|
+
{
|
|
150
|
+
id: "S.r3",
|
|
151
|
+
kind: "capability",
|
|
152
|
+
statement: "the adapter reaches R3 — shapes, not only names, which is what shape matching compares",
|
|
153
|
+
minResolution: 3,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "S.shapes",
|
|
157
|
+
kind: "capability",
|
|
158
|
+
statement: "transport shapes are emitted, so a caller's body can be compared against a route's",
|
|
159
|
+
nodeTypes: ["DTO"],
|
|
160
|
+
edgeTypes: ["USES_TYPE"],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "S.persistence",
|
|
164
|
+
kind: "capability",
|
|
165
|
+
statement: "models and their storage are in the graph, so propagation reaches the database",
|
|
166
|
+
nodeTypes: ["MODEL"],
|
|
167
|
+
edgeTypes: ["READS", "WRITES"],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "S.corpus-r3",
|
|
171
|
+
kind: "conformance",
|
|
172
|
+
statement: "the conformance corpus passes at R3",
|
|
173
|
+
minResolution: 3,
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
export const LADDER = [
|
|
177
|
+
{ tier: "C", name: "Signal", requires: C_REQUIREMENTS },
|
|
178
|
+
{ tier: "B", name: "Graph", requires: B_REQUIREMENTS },
|
|
179
|
+
{ tier: "A", name: "Contract", requires: A_REQUIREMENTS },
|
|
180
|
+
{ tier: "S", name: "Full", requires: S_REQUIREMENTS },
|
|
181
|
+
];
|
|
182
|
+
/**
|
|
183
|
+
* Every requirement a tier needs, including the tiers beneath it.
|
|
184
|
+
*
|
|
185
|
+
* Cumulative and re-checked rather than assumed — see the header. `C` is the
|
|
186
|
+
* floor, so `requirementsFor("C")` is C's own list.
|
|
187
|
+
*/
|
|
188
|
+
export function requirementsFor(tier) {
|
|
189
|
+
const order = ["C", "B", "A", "S"];
|
|
190
|
+
const upTo = order.indexOf(tier);
|
|
191
|
+
if (upTo < 0)
|
|
192
|
+
throw new Error(`unknown tier ${tier}`);
|
|
193
|
+
return order
|
|
194
|
+
.slice(0, upTo + 1)
|
|
195
|
+
.flatMap((t) => LADDER.find((d) => d.tier === t)?.requires ?? []);
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=ladder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ladder.js","sourceRoot":"","sources":["../../src/tiers/ladder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §15.3's config dependency graph — *"track env vars, secrets, flags, config
|
|
3
|
+
* files and what uses them"*, and the row whose note reads *"feeds the
|
|
4
|
+
* deploy-target check"*.
|
|
5
|
+
*
|
|
6
|
+
* ## This is a query, not a check, and the distinction is the design
|
|
7
|
+
*
|
|
8
|
+
* Checks 1 and 2 emit findings. This one emits **structure**: for every variable
|
|
9
|
+
* the repository knows about, where it is declared, where it is read, and what
|
|
10
|
+
* transitively depends on the files that read it. §15.3 says it feeds check 1,
|
|
11
|
+
* and that is exactly what it is for — a missing variable read by a module forty
|
|
12
|
+
* others import is a different sentence from one read by a single script, and
|
|
13
|
+
* until now the check had no way to tell them apart.
|
|
14
|
+
*
|
|
15
|
+
* Because it produces no findings of its own, it has **no precision gate of its
|
|
16
|
+
* own**, and that is stated rather than quietly skipped. Its correctness rests
|
|
17
|
+
* entirely on the `IMPORTS` edges beneath it, whose precision is already gated
|
|
18
|
+
* elsewhere. What this module can get wrong on its own is the *join* — attaching
|
|
19
|
+
* the wrong file to a variable — and that is what its bench exercises.
|
|
20
|
+
*
|
|
21
|
+
* ## No new vocabulary, which is the whole reason it is shaped this way
|
|
22
|
+
*
|
|
23
|
+
* *"New capabilities are new queries over these edges, not new types."* A
|
|
24
|
+
* variable still has no node (DEC-120). The dependent set is `scopeChange`
|
|
25
|
+
* seeded with the **file that reads the variable** — impact flowing outward from
|
|
26
|
+
* a changed file is precisely the existing traversal, with its budgets, its decay
|
|
27
|
+
* scoring and its truncation disclosure already correct.
|
|
28
|
+
*
|
|
29
|
+
* Taking the scope rather than re-deriving it is deliberate: a second traversal
|
|
30
|
+
* with its own rules would drift from the one the rest of the engine uses, and
|
|
31
|
+
* the first symptom would be two different blast radiuses for the same file.
|
|
32
|
+
*
|
|
33
|
+
* ## Why R1, and what it degrades to
|
|
34
|
+
*
|
|
35
|
+
* Reaching *"what depends on this"* needs module resolution — `IMPORTS` edges
|
|
36
|
+
* that actually resolve. At R0 there are none, so the dependent set is empty and
|
|
37
|
+
* the report says the graph carried no import edges rather than reporting zero
|
|
38
|
+
* dependents as a fact. **An empty dependent set from a graph with no imports and
|
|
39
|
+
* an empty one from a genuinely unimported module are different statements**, and
|
|
40
|
+
* only the second is information.
|
|
41
|
+
*
|
|
42
|
+
* ## The completeness fence
|
|
43
|
+
*
|
|
44
|
+
* Every count here is *what this graph can see* and is worded that way. NFR-9's
|
|
45
|
+
* rule, and §15.3's own instruction for the neighbouring DB-impact check —
|
|
46
|
+
* *"output must state what the tool can see, never implying completeness"* —
|
|
47
|
+
* applies with equal force: a dependent set is a lower bound, always.
|
|
48
|
+
*/
|
|
49
|
+
import type { AdjacencyProvider, QueryResult } from "../query/provider.ts";
|
|
50
|
+
import type { SurfaceScan } from "./sources.ts";
|
|
51
|
+
export interface ConfigDeclaration {
|
|
52
|
+
readonly file: string;
|
|
53
|
+
readonly line: number;
|
|
54
|
+
readonly commented: boolean;
|
|
55
|
+
readonly role: "declaration" | "deployTarget";
|
|
56
|
+
}
|
|
57
|
+
export interface ConfigReadSite {
|
|
58
|
+
readonly file: string;
|
|
59
|
+
readonly line: number;
|
|
60
|
+
/** The read fails when the variable is unset — DEC-121's discriminator. */
|
|
61
|
+
readonly required: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface ConfigVariable {
|
|
64
|
+
readonly name: string;
|
|
65
|
+
readonly declaredAt: readonly ConfigDeclaration[];
|
|
66
|
+
readonly readAt: readonly ConfigReadSite[];
|
|
67
|
+
/**
|
|
68
|
+
* Files that transitively depend on a file reading this variable, as far as
|
|
69
|
+
* this graph can see.
|
|
70
|
+
*
|
|
71
|
+
* **A lower bound, never a total.** Null when the dependent set could not be
|
|
72
|
+
* computed at all — no reading file is in the graph, or the graph carries no
|
|
73
|
+
* import edges — which is not the same as zero.
|
|
74
|
+
*/
|
|
75
|
+
readonly dependents: number | null;
|
|
76
|
+
/** A few dependent files, for a reader who wants to look. */
|
|
77
|
+
readonly dependentSample: readonly string[];
|
|
78
|
+
/** True when the traversal's budget stopped it short. */
|
|
79
|
+
readonly dependentsTruncated: boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface ConfigGraph {
|
|
82
|
+
readonly variables: readonly ConfigVariable[];
|
|
83
|
+
/** Variables named by a declaration, a read, or both. */
|
|
84
|
+
readonly counts: {
|
|
85
|
+
readonly total: number;
|
|
86
|
+
readonly declaredOnly: number;
|
|
87
|
+
readonly readOnly: number;
|
|
88
|
+
readonly both: number;
|
|
89
|
+
/** Variables whose dependent set could be computed at all. */
|
|
90
|
+
readonly withDependents: number;
|
|
91
|
+
};
|
|
92
|
+
/** True when the graph carried at least one resolved import edge. */
|
|
93
|
+
readonly importsAvailable: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface ConfigGraphOptions {
|
|
96
|
+
/** Cap on variables whose dependent set is computed. Traversal is not free. */
|
|
97
|
+
readonly maxTraversals?: number;
|
|
98
|
+
/** Cap on file nodes enumerated. */
|
|
99
|
+
readonly limit?: number;
|
|
100
|
+
/** Budget handed to each dependent traversal. */
|
|
101
|
+
readonly budget?: number;
|
|
102
|
+
}
|
|
103
|
+
export declare function buildConfigGraph(provider: AdjacencyProvider, scan: SurfaceScan, options?: ConfigGraphOptions): QueryResult<ConfigGraph>;
|
|
104
|
+
//# sourceMappingURL=config-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-graph.d.ts","sourceRoot":"","sources":["../../src/validation/config-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAc,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,CAAC;CAC/C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,yDAAyD;IACzD,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAC;IAC9C,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,8DAA8D;QAC9D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KACjC,CAAC;IACF,qEAAqE;IACrE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,oCAAoC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,WAAW,EACjB,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAAC,WAAW,CAAC,CAgL1B"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §15.3's config dependency graph — *"track env vars, secrets, flags, config
|
|
3
|
+
* files and what uses them"*, and the row whose note reads *"feeds the
|
|
4
|
+
* deploy-target check"*.
|
|
5
|
+
*
|
|
6
|
+
* ## This is a query, not a check, and the distinction is the design
|
|
7
|
+
*
|
|
8
|
+
* Checks 1 and 2 emit findings. This one emits **structure**: for every variable
|
|
9
|
+
* the repository knows about, where it is declared, where it is read, and what
|
|
10
|
+
* transitively depends on the files that read it. §15.3 says it feeds check 1,
|
|
11
|
+
* and that is exactly what it is for — a missing variable read by a module forty
|
|
12
|
+
* others import is a different sentence from one read by a single script, and
|
|
13
|
+
* until now the check had no way to tell them apart.
|
|
14
|
+
*
|
|
15
|
+
* Because it produces no findings of its own, it has **no precision gate of its
|
|
16
|
+
* own**, and that is stated rather than quietly skipped. Its correctness rests
|
|
17
|
+
* entirely on the `IMPORTS` edges beneath it, whose precision is already gated
|
|
18
|
+
* elsewhere. What this module can get wrong on its own is the *join* — attaching
|
|
19
|
+
* the wrong file to a variable — and that is what its bench exercises.
|
|
20
|
+
*
|
|
21
|
+
* ## No new vocabulary, which is the whole reason it is shaped this way
|
|
22
|
+
*
|
|
23
|
+
* *"New capabilities are new queries over these edges, not new types."* A
|
|
24
|
+
* variable still has no node (DEC-120). The dependent set is `scopeChange`
|
|
25
|
+
* seeded with the **file that reads the variable** — impact flowing outward from
|
|
26
|
+
* a changed file is precisely the existing traversal, with its budgets, its decay
|
|
27
|
+
* scoring and its truncation disclosure already correct.
|
|
28
|
+
*
|
|
29
|
+
* Taking the scope rather than re-deriving it is deliberate: a second traversal
|
|
30
|
+
* with its own rules would drift from the one the rest of the engine uses, and
|
|
31
|
+
* the first symptom would be two different blast radiuses for the same file.
|
|
32
|
+
*
|
|
33
|
+
* ## Why R1, and what it degrades to
|
|
34
|
+
*
|
|
35
|
+
* Reaching *"what depends on this"* needs module resolution — `IMPORTS` edges
|
|
36
|
+
* that actually resolve. At R0 there are none, so the dependent set is empty and
|
|
37
|
+
* the report says the graph carried no import edges rather than reporting zero
|
|
38
|
+
* dependents as a fact. **An empty dependent set from a graph with no imports and
|
|
39
|
+
* an empty one from a genuinely unimported module are different statements**, and
|
|
40
|
+
* only the second is information.
|
|
41
|
+
*
|
|
42
|
+
* ## The completeness fence
|
|
43
|
+
*
|
|
44
|
+
* Every count here is *what this graph can see* and is worded that way. NFR-9's
|
|
45
|
+
* rule, and §15.3's own instruction for the neighbouring DB-impact check —
|
|
46
|
+
* *"output must state what the tool can see, never implying completeness"* —
|
|
47
|
+
* applies with equal force: a dependent set is a lower bound, always.
|
|
48
|
+
*/
|
|
49
|
+
import { scopeChange } from "../scoping/traverse.js";
|
|
50
|
+
import { readEnvReads } from "./env.js";
|
|
51
|
+
import { fileScopedNodes } from "./nodes.js";
|
|
52
|
+
const DEFAULT_MAX_TRAVERSALS = 50;
|
|
53
|
+
const DEFAULT_BUDGET = 200;
|
|
54
|
+
const SAMPLE = 5;
|
|
55
|
+
export function buildConfigGraph(provider, scan, options = {}) {
|
|
56
|
+
const maxTraversals = options.maxTraversals ?? DEFAULT_MAX_TRAVERSALS;
|
|
57
|
+
const budget = options.budget ?? DEFAULT_BUDGET;
|
|
58
|
+
const notes = [];
|
|
59
|
+
// --- declarations, from the repository -----------------------------------
|
|
60
|
+
const declarations = new Map();
|
|
61
|
+
for (const surface of scan.surfaces) {
|
|
62
|
+
for (const declaration of surface.declarations) {
|
|
63
|
+
let bucket = declarations.get(declaration.name);
|
|
64
|
+
if (bucket === undefined)
|
|
65
|
+
declarations.set(declaration.name, (bucket = []));
|
|
66
|
+
bucket.push({
|
|
67
|
+
file: surface.file,
|
|
68
|
+
line: declaration.line,
|
|
69
|
+
commented: declaration.commented,
|
|
70
|
+
role: surface.role,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// --- reads, from the graph -----------------------------------------------
|
|
75
|
+
const files = fileScopedNodes(provider, options.limit);
|
|
76
|
+
const reads = new Map();
|
|
77
|
+
const readingFile = new Map();
|
|
78
|
+
let importEdges = 0;
|
|
79
|
+
for (const node of files.nodes) {
|
|
80
|
+
const path = node.file;
|
|
81
|
+
if (path === null)
|
|
82
|
+
continue;
|
|
83
|
+
// Counted across every file, not only the reading ones: "this graph has no
|
|
84
|
+
// import edges" is a property of the graph, and sampling it from the files
|
|
85
|
+
// that happen to read configuration would be a property of those files.
|
|
86
|
+
importEdges += provider.outgoing(node.id, { types: ["IMPORTS"], limit: 1 }).length;
|
|
87
|
+
const extracted = readEnvReads(node.attrs, path, node.resolution);
|
|
88
|
+
if (extracted.reads.length === 0)
|
|
89
|
+
continue;
|
|
90
|
+
readingFile.set(path, node);
|
|
91
|
+
for (const read of extracted.reads) {
|
|
92
|
+
let bucket = reads.get(read.name);
|
|
93
|
+
if (bucket === undefined)
|
|
94
|
+
reads.set(read.name, (bucket = []));
|
|
95
|
+
bucket.push({ file: read.file, line: read.line, required: read.required });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const importsAvailable = importEdges > 0;
|
|
99
|
+
// --- assemble, and traverse for the ones worth traversing ----------------
|
|
100
|
+
const names = [...new Set([...declarations.keys(), ...reads.keys()])].sort();
|
|
101
|
+
const variables = [];
|
|
102
|
+
let declaredOnly = 0;
|
|
103
|
+
let readOnly = 0;
|
|
104
|
+
let both = 0;
|
|
105
|
+
let withDependents = 0;
|
|
106
|
+
let traversals = 0;
|
|
107
|
+
let floor = 4;
|
|
108
|
+
let anyTruncated = false;
|
|
109
|
+
// Variables with a read site come first: a dependent set is only meaningful
|
|
110
|
+
// for one with a reading file, and the traversal budget should not be spent on
|
|
111
|
+
// declarations nothing reads.
|
|
112
|
+
const ordered = names.sort((a, b) => {
|
|
113
|
+
const ar = reads.has(a) ? 0 : 1;
|
|
114
|
+
const br = reads.has(b) ? 0 : 1;
|
|
115
|
+
return ar - br || a.localeCompare(b);
|
|
116
|
+
});
|
|
117
|
+
for (const name of ordered) {
|
|
118
|
+
const declaredAt = declarations.get(name) ?? [];
|
|
119
|
+
const readAt = reads.get(name) ?? [];
|
|
120
|
+
if (declaredAt.length > 0 && readAt.length > 0)
|
|
121
|
+
both += 1;
|
|
122
|
+
else if (declaredAt.length > 0)
|
|
123
|
+
declaredOnly += 1;
|
|
124
|
+
else
|
|
125
|
+
readOnly += 1;
|
|
126
|
+
let dependents = null;
|
|
127
|
+
let dependentSample = [];
|
|
128
|
+
let truncated = false;
|
|
129
|
+
const seeds = readAt
|
|
130
|
+
.map((site) => readingFile.get(site.file))
|
|
131
|
+
.filter((node) => node !== undefined)
|
|
132
|
+
.map((node) => node.id);
|
|
133
|
+
if (seeds.length > 0 && importsAvailable && traversals < maxTraversals) {
|
|
134
|
+
traversals += 1;
|
|
135
|
+
const scope = scopeChange(provider, [...new Set(seeds)], { budget });
|
|
136
|
+
// `changed` is the seed set itself — the files that read the variable —
|
|
137
|
+
// so the dependent count is `affected` alone. Including the seeds would
|
|
138
|
+
// report a variable read in one file as having one dependent.
|
|
139
|
+
const affected = scope.data.affected;
|
|
140
|
+
dependents = affected.length;
|
|
141
|
+
dependentSample = affected
|
|
142
|
+
.map((entry) => entry.node.file)
|
|
143
|
+
.filter((file) => file !== null)
|
|
144
|
+
.slice(0, SAMPLE);
|
|
145
|
+
truncated = scope.truncated || scope.data.dropped.length > 0;
|
|
146
|
+
if (truncated)
|
|
147
|
+
anyTruncated = true;
|
|
148
|
+
if (scope.stamp.resolutionFloor < floor)
|
|
149
|
+
floor = scope.stamp.resolutionFloor;
|
|
150
|
+
withDependents += 1;
|
|
151
|
+
}
|
|
152
|
+
variables.push({
|
|
153
|
+
name,
|
|
154
|
+
declaredAt,
|
|
155
|
+
readAt,
|
|
156
|
+
dependents,
|
|
157
|
+
dependentSample,
|
|
158
|
+
dependentsTruncated: truncated,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
variables.sort((a, b) => (b.dependents ?? -1) - (a.dependents ?? -1) || a.name.localeCompare(b.name));
|
|
162
|
+
// --- disclosures ---------------------------------------------------------
|
|
163
|
+
if (!importsAvailable) {
|
|
164
|
+
notes.push("No resolved import edge is in this graph, so no dependent set could be computed for any " +
|
|
165
|
+
"variable. Every dependent count is reported as unknown rather than as zero: a module " +
|
|
166
|
+
"nothing imports and a graph that resolved no imports produce the same empty set, and " +
|
|
167
|
+
"only the first is information. This needs R1 — module resolution — which the producer " +
|
|
168
|
+
"did not reach.");
|
|
169
|
+
}
|
|
170
|
+
if (reads.size === 0) {
|
|
171
|
+
notes.push("No file in the graph carries environment reads, so this graph describes declarations only " +
|
|
172
|
+
"and nothing about what uses them. The engine reads attrs.envReads on a file-scoped node.");
|
|
173
|
+
}
|
|
174
|
+
if (traversals >= maxTraversals) {
|
|
175
|
+
notes.push(`Dependent sets were computed for the first ${maxTraversals} variable(s) with a read ` +
|
|
176
|
+
"site; the rest carry an unknown dependent count rather than zero. Raise " +
|
|
177
|
+
'"maxTraversals" to widen it.');
|
|
178
|
+
}
|
|
179
|
+
if (anyTruncated) {
|
|
180
|
+
notes.push("At least one dependent traversal hit its budget, so those counts are a floor rather than a " +
|
|
181
|
+
"total. The dropped nodes were the lowest-scoring ones reached.");
|
|
182
|
+
}
|
|
183
|
+
if (files.truncated) {
|
|
184
|
+
notes.push("More files exist in the graph than were enumerated, so this covers a prefix of the " +
|
|
185
|
+
"repository rather than all of it.");
|
|
186
|
+
}
|
|
187
|
+
notes.push("Every dependent count is what this graph can see, and is a lower bound. A consumer in a " +
|
|
188
|
+
"language with no adapter, in another repository, or reached through a call this analysis " +
|
|
189
|
+
"could not resolve does not appear here.");
|
|
190
|
+
const stamp = {
|
|
191
|
+
...provider.baseStamp(),
|
|
192
|
+
resolutionFloor: withDependents === 0 ? 1 : floor,
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
data: {
|
|
196
|
+
variables,
|
|
197
|
+
counts: { total: names.length, declaredOnly, readOnly, both, withDependents },
|
|
198
|
+
importsAvailable,
|
|
199
|
+
},
|
|
200
|
+
stamp,
|
|
201
|
+
truncated: files.truncated || anyTruncated,
|
|
202
|
+
notes,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=config-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-graph.js","sourceRoot":"","sources":["../../src/validation/config-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA4D7C,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,MAAM,GAAG,CAAC,CAAC;AAEjB,MAAM,UAAU,gBAAgB,CAC9B,QAA2B,EAC3B,IAAiB,EACjB,UAA8B,EAAE;IAEhC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,4EAA4E;IAE5E,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC5D,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpC,KAAK,MAAM,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC/C,IAAI,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,MAAM,KAAK,SAAS;gBAAE,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;YAC5E,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,SAAS,EAAE,WAAW,CAAC,SAAS;gBAChC,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,4EAA4E;IAE5E,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAClD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,IAAI,KAAK,IAAI;YAAE,SAAS;QAC5B,2EAA2E;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;QAEnF,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC3C,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,WAAW,GAAG,CAAC,CAAC;IAEzC,4EAA4E;IAE5E,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,MAAM,SAAS,GAAqB,EAAE,CAAC;IAEvC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,KAAK,GAAoB,CAAC,CAAC;IAC/B,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,4EAA4E;IAC5E,+EAA+E;IAC/E,8BAA8B;IAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,IAAI,CAAC,CAAC;aACrD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,YAAY,IAAI,CAAC,CAAC;;YAC7C,QAAQ,IAAI,CAAC,CAAC;QAEnB,IAAI,UAAU,GAAkB,IAAI,CAAC;QACrC,IAAI,eAAe,GAAa,EAAE,CAAC;QACnC,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,MAAM,KAAK,GAAG,MAAM;aACjB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACzC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC;aACpD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE1B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,IAAI,UAAU,GAAG,aAAa,EAAE,CAAC;YACvE,UAAU,IAAI,CAAC,CAAC;YAChB,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YACrE,wEAAwE;YACxE,wEAAwE;YACxE,8DAA8D;YAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;YACrC,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;YAC7B,eAAe,GAAG,QAAQ;iBACvB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;iBAC/B,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;iBAC/C,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;YAC7D,IAAI,SAAS;gBAAE,YAAY,GAAG,IAAI,CAAC;YACnC,IAAI,KAAK,CAAC,KAAK,CAAC,eAAe,GAAG,KAAK;gBAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC;YAC7E,cAAc,IAAI,CAAC,CAAC;QACtB,CAAC;QAED,SAAS,CAAC,IAAI,CAAC;YACb,IAAI;YACJ,UAAU;YACV,MAAM;YACN,UAAU;YACV,eAAe;YACf,mBAAmB,EAAE,SAAS;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,SAAS,CAAC,IAAI,CACZ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CACtF,CAAC;IAEF,4EAA4E;IAE5E,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CACR,0FAA0F;YACxF,uFAAuF;YACvF,uFAAuF;YACvF,wFAAwF;YACxF,gBAAgB,CACnB,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CACR,4FAA4F;YAC1F,0FAA0F,CAC7F,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,IAAI,aAAa,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CACR,8CAA8C,aAAa,2BAA2B;YACpF,0EAA0E;YAC1E,8BAA8B,CACjC,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,6FAA6F;YAC3F,gEAAgE,CACnE,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CACR,qFAAqF;YACnF,mCAAmC,CACtC,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CACR,0FAA0F;QACxF,2FAA2F;QAC3F,yCAAyC,CAC5C,CAAC;IAEF,MAAM,KAAK,GAAe;QACxB,GAAG,QAAQ,CAAC,SAAS,EAAE;QACvB,eAAe,EAAE,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;KAClD,CAAC;IAEF,OAAO;QACL,IAAI,EAAE;YACJ,SAAS;YACT,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE;YAC7E,gBAAgB;SACjB;QACD,KAAK;QACL,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,YAAY;QAC1C,KAAK;KACN,CAAC;AACJ,CAAC"}
|