@cplieger/deadset-ts 7.0.0-dev.1 → 7.0.0-dev.3
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/src/analysis.js +95 -60
- package/dist/src/augmentations.js +14 -12
- package/dist/src/component-layer.js +1 -1
- package/dist/src/components.js +62 -20
- package/dist/src/configuration-files.js +230 -30
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +104 -13
- package/dist/src/convention-rows.js +10 -0
- package/dist/src/conventions.js +35 -1
- package/dist/src/dependencies.js +90 -32
- package/dist/src/discover.js +1 -1
- package/dist/src/edges.js +1 -1
- package/dist/src/element-access.js +341 -0
- package/dist/src/erased-stores.js +317 -0
- package/dist/src/exempt-classes.js +9 -22
- package/dist/src/exempt.js +3 -15
- package/dist/src/findings/completion.js +78 -16
- package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
- package/dist/src/findings/emitters.js +1 -5
- package/dist/src/findings/self-check.js +27 -10
- package/dist/src/findings-pass.js +6 -5
- package/dist/src/in-operands.js +30 -0
- package/dist/src/indexed-access.js +264 -119
- package/dist/src/intra-function-parts.js +25 -11
- package/dist/src/inventory.js +28 -12
- package/dist/src/keyof-comparisons.js +170 -0
- package/dist/src/kinds.js +17 -7
- package/dist/src/manifest.js +6 -16
- package/dist/src/matrix.js +20 -2
- package/dist/src/query.js +3 -0
- package/dist/src/references.js +14 -3
- package/dist/src/report.js +4 -1
- package/dist/src/reporters/sarif.js +4 -0
- package/dist/src/roots.js +0 -1
- package/dist/src/run.js +0 -2
- package/dist/src/schema.js +1 -1
- package/dist/src/scope.js +2 -2
- package/dist/src/serialization-contract.js +43 -14
- package/dist/src/session.js +12 -8
- package/dist/src/suppress-file.js +58 -6
- package/dist/src/suppress-inline.js +14 -5
- package/dist/src/suppress.js +64 -35
- package/dist/src/unimplemented-callees.js +312 -0
- package/dist/src/value-reach.js +7 -7
- package/dist/src/verbs/analyze.js +11 -9
- package/dist/src/workspace-programs.js +0 -3
- package/dist/src/workspace-resolution.js +0 -2
- package/dist/src/workspace.js +0 -3
- package/jsr.json +1 -1
- package/package.json +3 -2
- package/src/analysis.ts +145 -82
- package/src/augmentations.ts +14 -11
- package/src/calls.ts +3 -3
- package/src/component-files.ts +7 -7
- package/src/component-layer.ts +2 -2
- package/src/components.ts +104 -22
- package/src/config.ts +4 -4
- package/src/configuration-files.ts +250 -31
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +135 -17
- package/src/convention-rows.ts +23 -0
- package/src/conventions.ts +53 -1
- package/src/dependencies.ts +119 -38
- package/src/discover.ts +3 -3
- package/src/edges.ts +3 -3
- package/src/element-access.ts +446 -0
- package/src/entry-points.ts +1 -1
- package/src/erased-stores.ts +413 -0
- package/src/exempt-classes.ts +10 -24
- package/src/exempt.ts +5 -29
- package/src/file-facts.ts +3 -1
- package/src/finding.ts +3 -1
- package/src/findings/completion.ts +97 -21
- package/src/findings/dependencies-and-module-machinery.ts +1 -1
- package/src/findings/emitters.ts +4 -9
- package/src/findings/self-check.ts +31 -14
- package/src/findings-pass.ts +14 -7
- package/src/graph.ts +2 -2
- package/src/html-entries.ts +1 -1
- package/src/in-operands.ts +45 -0
- package/src/indexed-access.ts +303 -137
- package/src/intra-function-parts.ts +41 -16
- package/src/inventory.ts +37 -33
- package/src/json-document.ts +1 -1
- package/src/keyof-comparisons.ts +211 -0
- package/src/kinds.ts +20 -7
- package/src/manifest.ts +7 -22
- package/src/matrix.ts +34 -2
- package/src/namespace-objects.ts +1 -1
- package/src/pnpm-workspace.ts +1 -1
- package/src/query.ts +10 -0
- package/src/references.ts +30 -7
- package/src/report.ts +11 -5
- package/src/reporters/sarif.ts +4 -0
- package/src/roots.ts +0 -3
- package/src/run.ts +0 -2
- package/src/schema.ts +2 -2
- package/src/scope.ts +2 -2
- package/src/serialization-contract.ts +53 -14
- package/src/session.ts +34 -31
- package/src/stores.ts +1 -1
- package/src/suppress-file.ts +68 -5
- package/src/suppress-inline.ts +17 -7
- package/src/suppress.ts +79 -47
- package/src/template-field.ts +1 -1
- package/src/unimplemented-callees.ts +411 -0
- package/src/value-reach.ts +12 -8
- package/src/verbs/analyze.ts +15 -9
- package/src/verbs/verb.ts +1 -4
- package/src/workspace-programs.ts +2 -9
- package/src/workspace-resolution.ts +0 -3
- package/src/workspace-run.ts +1 -1
- package/src/workspace.ts +1 -9
package/src/dependencies.ts
CHANGED
|
@@ -59,7 +59,7 @@ const SECTIONS: readonly (readonly [string, DependencySection])[] = [
|
|
|
59
59
|
const PROJECT_OWNER = "";
|
|
60
60
|
|
|
61
61
|
/** One dependency a manifest the run reads declares. */
|
|
62
|
-
|
|
62
|
+
interface DeclaredDependency {
|
|
63
63
|
readonly name: string;
|
|
64
64
|
readonly section: DependencySection;
|
|
65
65
|
/** Where the manifest writes the dependency's key, below the target root. */
|
|
@@ -71,28 +71,37 @@ export interface DeclaredDependency {
|
|
|
71
71
|
/** What a configuration's own reading needs: packages no deletion of a declaration removes. */
|
|
72
72
|
export function configurationNeeds(packages: readonly string[]): ProjectNeeds {
|
|
73
73
|
return {
|
|
74
|
-
packages: new Set(packages),
|
|
75
74
|
uses: new Map([[PROJECT_OWNER, new Set(packages)]]),
|
|
75
|
+
at: new Map([[ANYWHERE, new Set(packages)]]),
|
|
76
76
|
unanswered: [],
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/** The key of {@link ProjectNeeds.at} for a use no file of the target governs. */
|
|
81
|
+
const ANYWHERE = "";
|
|
82
|
+
|
|
80
83
|
/** What the installed copy of one declared dependency states about itself. */
|
|
81
|
-
|
|
84
|
+
interface InstalledDependency {
|
|
82
85
|
/** The commands its manifest's `bin` declares, by name. */
|
|
83
86
|
readonly commands: readonly string[];
|
|
87
|
+
/** The packages its manifest's `peerDependencies` names, optional ones included. */
|
|
88
|
+
readonly peers: readonly string[];
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
/** What one project's own files and configuration need from outside the target. */
|
|
87
92
|
export interface ProjectNeeds {
|
|
88
|
-
/** Every package a use names or resolves to. */
|
|
89
|
-
readonly packages: ReadonlySet<string>;
|
|
90
93
|
/**
|
|
91
94
|
* Per declaration of the inventory, the packages the uses written inside it name; a
|
|
92
95
|
* use is held by the innermost declaration around it. The empty key holds what the
|
|
93
96
|
* compiler configuration names, which no deletion removes.
|
|
94
97
|
*/
|
|
95
98
|
readonly uses: ReadonlyMap<string, ReadonlySet<string>>;
|
|
99
|
+
/**
|
|
100
|
+
* Per absolute path of the file a use is written in, the packages it names, which a
|
|
101
|
+
* dependency only a manifest at or above that file declares serves. The empty key holds
|
|
102
|
+
* the uses no one file holds, which every manifest's dependency of that name serves.
|
|
103
|
+
*/
|
|
104
|
+
readonly at: ReadonlyMap<string, ReadonlySet<string>>;
|
|
96
105
|
/**
|
|
97
106
|
* The files writing a module specifier the checker left unanswered, each of which may
|
|
98
107
|
* use any dependency a manifest at or above it declares.
|
|
@@ -104,11 +113,14 @@ export interface ProjectNeeds {
|
|
|
104
113
|
export interface Dependencies {
|
|
105
114
|
/** Every declared dependency, manifest by manifest, in the order each writes them. */
|
|
106
115
|
readonly declared: readonly DeclaredDependency[];
|
|
107
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The declared dependencies, by {@link declarationKey}, a use written at or below their
|
|
118
|
+
* manifest's directory needs, and each declared peer of a needed one.
|
|
119
|
+
*/
|
|
108
120
|
readonly needed: ReadonlySet<string>;
|
|
109
121
|
/**
|
|
110
122
|
* The declared dependencies whose command a script entry of the manifest declaring them,
|
|
111
|
-
* or a workflow step
|
|
123
|
+
* or a workflow step of the target, runs, each by {@link declarationKey}.
|
|
112
124
|
*/
|
|
113
125
|
readonly ran: ReadonlySet<string>;
|
|
114
126
|
/**
|
|
@@ -322,9 +334,10 @@ export function projectNeeds<Brand>(
|
|
|
322
334
|
programPackages,
|
|
323
335
|
);
|
|
324
336
|
const uses = new Map<string, Set<string>>();
|
|
325
|
-
const
|
|
337
|
+
const at = new Map<string, Set<string>>();
|
|
338
|
+
const use = (owners: readonly string[], named: Iterable<string>, path: string): void => {
|
|
326
339
|
for (const one of named) {
|
|
327
|
-
|
|
340
|
+
at.set(path, (at.get(path) ?? new Set()).add(one));
|
|
328
341
|
for (const owner of owners) {
|
|
329
342
|
let set = uses.get(owner);
|
|
330
343
|
if (set === undefined) {
|
|
@@ -335,11 +348,14 @@ export function projectNeeds<Brand>(
|
|
|
335
348
|
}
|
|
336
349
|
}
|
|
337
350
|
};
|
|
338
|
-
use([PROJECT_OWNER], packages);
|
|
351
|
+
use([PROJECT_OWNER], packages, project.configFile);
|
|
339
352
|
|
|
340
353
|
const unanswered: string[] = [];
|
|
341
354
|
for (const file of project.ownSourceFiles()) {
|
|
342
|
-
|
|
355
|
+
const inFile = (owners: readonly string[], named: Iterable<string>): void => {
|
|
356
|
+
use(owners, named, file.fileName);
|
|
357
|
+
};
|
|
358
|
+
if (fileNeeds(project, held, file, programPackages, inFile)) {
|
|
343
359
|
unanswered.push(file.fileName);
|
|
344
360
|
}
|
|
345
361
|
}
|
|
@@ -363,22 +379,26 @@ export function projectNeeds<Brand>(
|
|
|
363
379
|
for (const { file } of configuration.modules) {
|
|
364
380
|
const names = declaredAt(file.fileName);
|
|
365
381
|
for (const { node, text } of moduleStrings(file)) {
|
|
366
|
-
use(ownersOf(file, held, node), dependenciesNamed(text, names));
|
|
382
|
+
use(ownersOf(file, held, node), dependenciesNamed(text, names), file.fileName);
|
|
367
383
|
}
|
|
368
384
|
}
|
|
369
|
-
for (const
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
385
|
+
for (const { strings, manifestDirs } of configuration.documents) {
|
|
386
|
+
for (const manifestDir of manifestDirs) {
|
|
387
|
+
const names = declaredAt(joinPath(manifestDir, "package.json"));
|
|
388
|
+
for (const text of strings) {
|
|
389
|
+
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
390
|
+
}
|
|
373
391
|
}
|
|
374
392
|
}
|
|
375
|
-
for (const { file,
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
393
|
+
for (const { file, manifestDirs } of configuration.outside) {
|
|
394
|
+
for (const manifestDir of manifestDirs) {
|
|
395
|
+
const names = declaredAt(joinPath(manifestDir, "package.json"));
|
|
396
|
+
for (const { text } of moduleStrings(file)) {
|
|
397
|
+
use([PROJECT_OWNER], dependenciesNamed(text, names), joinPath(manifestDir, MANIFEST));
|
|
398
|
+
}
|
|
379
399
|
}
|
|
380
400
|
}
|
|
381
|
-
return {
|
|
401
|
+
return { uses, at, unanswered };
|
|
382
402
|
}
|
|
383
403
|
|
|
384
404
|
/** The declarations of the inventory a use written at one node belongs to. */
|
|
@@ -634,8 +654,10 @@ function installedOf(host: Host, root: string, name: string): InstalledDependenc
|
|
|
634
654
|
const read = readJSON(host, joinPath(dir, MODULES_DIR, name, MANIFEST));
|
|
635
655
|
if (read !== undefined && isRecord(read.value)) {
|
|
636
656
|
const bin = read.value["bin"];
|
|
657
|
+
const peers = read.value["peerDependencies"];
|
|
637
658
|
// A string `bin` names one command, the package's name without its scope.
|
|
638
659
|
return {
|
|
660
|
+
peers: isRecord(peers) ? Object.keys(peers) : [],
|
|
639
661
|
commands:
|
|
640
662
|
typeof bin === "string"
|
|
641
663
|
? bin === ""
|
|
@@ -707,13 +729,53 @@ function isInside(path: string, dir: string): boolean {
|
|
|
707
729
|
return dir === "" || path.startsWith(`${dir}/`);
|
|
708
730
|
}
|
|
709
731
|
|
|
732
|
+
/** The directory below the target root a declared dependency's manifest is in, `""` being the root. */
|
|
733
|
+
function manifestDir(dependency: DeclaredDependency): string {
|
|
734
|
+
const dir = dirnamePath(dependency.position.path);
|
|
735
|
+
return dir === "." ? "" : dir;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Adds to `needed` each declared dependency that the installed manifest of a needed or
|
|
740
|
+
* run declared dependency names as a peer, until no further one joins: the declaration of
|
|
741
|
+
* that name in the needing dependency's manifest or in a manifest above it, where the
|
|
742
|
+
* installed peer is resolved from. `peers` holds each declaration's installed peers by
|
|
743
|
+
* {@link declarationKey}.
|
|
744
|
+
*/
|
|
745
|
+
function addNeededPeers(
|
|
746
|
+
declared: readonly DeclaredDependency[],
|
|
747
|
+
peers: ReadonlyMap<string, readonly string[]>,
|
|
748
|
+
needed: Set<string>,
|
|
749
|
+
ran: ReadonlySet<string>,
|
|
750
|
+
): void {
|
|
751
|
+
const pending = declared.filter(
|
|
752
|
+
(dependency) => needed.has(declarationKey(dependency)) || ran.has(declarationKey(dependency)),
|
|
753
|
+
);
|
|
754
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
755
|
+
const below = manifestDir(next);
|
|
756
|
+
for (const peer of peers.get(declarationKey(next)) ?? []) {
|
|
757
|
+
for (const dependency of declared) {
|
|
758
|
+
const key = declarationKey(dependency);
|
|
759
|
+
if (
|
|
760
|
+
dependency.name === peer &&
|
|
761
|
+
!needed.has(key) &&
|
|
762
|
+
(below === manifestDir(dependency) || isInside(below, manifestDir(dependency)))
|
|
763
|
+
) {
|
|
764
|
+
needed.add(key);
|
|
765
|
+
pending.push(dependency);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
710
772
|
/**
|
|
711
|
-
* The run's dependency evidence
|
|
712
|
-
* `
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
773
|
+
* The run's dependency evidence over the target's manifest and those of `members` and
|
|
774
|
+
* `nested` packages below the root: each declared dependency's position, what the projects,
|
|
775
|
+
* scripts and workflow steps need, and each deletion candidate's last uses. A member in
|
|
776
|
+
* `unbuilt` holds source only in configurations the run could not build. An unreadable
|
|
777
|
+
* manifest declares nothing, nor does a nested package's uninstalled dependency, because
|
|
778
|
+
* no run requires a nested package to be installed.
|
|
717
779
|
*/
|
|
718
780
|
export function dependenciesOf(
|
|
719
781
|
host: Host,
|
|
@@ -722,12 +784,13 @@ export function dependenciesOf(
|
|
|
722
784
|
candidates: readonly string[],
|
|
723
785
|
members: readonly string[] = [],
|
|
724
786
|
unbuiltMembers: readonly string[] = [],
|
|
787
|
+
nested: readonly string[] = [],
|
|
725
788
|
): Dependencies {
|
|
726
789
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
727
790
|
const scopeOf = packageScope(host, targetRoot);
|
|
728
791
|
const manifests = new Map<string, string>([[root, MANIFEST]]);
|
|
729
792
|
const unbuilt = new Set<string>();
|
|
730
|
-
for (const dir of [...members, ...unbuiltMembers]) {
|
|
793
|
+
for (const dir of [...members, ...unbuiltMembers, ...nested]) {
|
|
731
794
|
const below = relativePath(root, dir);
|
|
732
795
|
if (below !== undefined && !manifests.has(dir)) {
|
|
733
796
|
manifests.set(dir, `${below}/${MANIFEST}`);
|
|
@@ -742,6 +805,8 @@ export function dependenciesOf(
|
|
|
742
805
|
const workflow = new Set(workflowTokens(host, targetRoot));
|
|
743
806
|
const declared: DeclaredDependency[] = [];
|
|
744
807
|
const ran = new Set<string>();
|
|
808
|
+
const peers = new Map<string, readonly string[]>();
|
|
809
|
+
const uninstalledHere = new Set(nested);
|
|
745
810
|
for (const [dir, path] of manifests) {
|
|
746
811
|
const read = readJSON(host, joinPath(dir, MANIFEST));
|
|
747
812
|
if (read === undefined || !isRecord(read.value)) {
|
|
@@ -749,13 +814,16 @@ export function dependenciesOf(
|
|
|
749
814
|
}
|
|
750
815
|
const tokens = scriptTokensOf(read.value);
|
|
751
816
|
for (const dependency of declaredIn(read.text, read.value, path, scopeOf(path))) {
|
|
752
|
-
declared.push(dependency);
|
|
753
817
|
const found = installedOf(host, dir, dependency.name);
|
|
818
|
+
if (found === undefined && uninstalledHere.has(dir)) {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
declared.push(dependency);
|
|
754
822
|
if (found === undefined) {
|
|
755
823
|
continue;
|
|
756
824
|
}
|
|
757
|
-
|
|
758
|
-
|
|
825
|
+
peers.set(declarationKey(dependency), found.peers);
|
|
826
|
+
const runs = (command: string): boolean => tokens.has(command) || workflow.has(command);
|
|
759
827
|
if (found.commands.some(runs)) {
|
|
760
828
|
ran.add(declarationKey(dependency));
|
|
761
829
|
}
|
|
@@ -765,25 +833,38 @@ export function dependenciesOf(
|
|
|
765
833
|
// A declaration several projects compile is one user, and a use any project makes is
|
|
766
834
|
// a use, so the uses are merged by declaration before anything is counted.
|
|
767
835
|
const needed = new Set<string>();
|
|
836
|
+
const byName = new Map<string, DeclaredDependency[]>();
|
|
837
|
+
for (const dependency of declared) {
|
|
838
|
+
byName.set(dependency.name, [...(byName.get(dependency.name) ?? []), dependency]);
|
|
839
|
+
}
|
|
768
840
|
const uses = new Map<string, Set<string>>();
|
|
769
841
|
for (const project of perProject) {
|
|
770
842
|
// A specifier the checker left unanswered may resolve to any dependency a manifest at
|
|
771
843
|
// or above its file declares, and what the configuration needs is never deleted.
|
|
772
844
|
const unanswered = project.unanswered.flatMap((file) => relativePath(root, file) ?? []);
|
|
773
|
-
const
|
|
774
|
-
.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
.map((dependency) => dependency.name);
|
|
845
|
+
const unknownDeclared = declared.filter((dependency) =>
|
|
846
|
+
unanswered.some((file) => isInside(file, manifestDir(dependency))),
|
|
847
|
+
);
|
|
848
|
+
unknownDeclared.forEach((dependency) => needed.add(declarationKey(dependency)));
|
|
849
|
+
const unknown = unknownDeclared.map((dependency) => dependency.name);
|
|
779
850
|
const held = unknown.length === 0 ? [] : [[PROJECT_OWNER, new Set(unknown)] as const];
|
|
780
851
|
for (const [id, used] of [...project.uses, ...held]) {
|
|
781
852
|
const merged = uses.get(id) ?? new Set<string>();
|
|
782
853
|
used.forEach((one) => merged.add(one));
|
|
783
854
|
uses.set(id, merged);
|
|
784
855
|
}
|
|
785
|
-
[
|
|
856
|
+
for (const [path, named] of project.at) {
|
|
857
|
+
const below = path === ANYWHERE ? undefined : relativePath(root, path);
|
|
858
|
+
for (const one of named) {
|
|
859
|
+
for (const dependency of byName.get(one) ?? []) {
|
|
860
|
+
if (below === undefined || isInside(below, manifestDir(dependency))) {
|
|
861
|
+
needed.add(declarationKey(dependency));
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
786
866
|
}
|
|
867
|
+
addNeededPeers(declared, peers, needed, ran);
|
|
787
868
|
const users = new Map<string, number>();
|
|
788
869
|
for (const used of uses.values()) {
|
|
789
870
|
used.forEach((one) => users.set(one, (users.get(one) ?? 0) + 1));
|
package/src/discover.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class DiscoveryError extends Error {
|
|
|
34
34
|
const ERROR_CATEGORY: number = DiagnosticCategory.Error;
|
|
35
35
|
|
|
36
36
|
/** Whether one diagnostic is an error, which is what fails a run closed. */
|
|
37
|
-
|
|
37
|
+
function isError(diagnostic: Diagnostic): boolean {
|
|
38
38
|
return diagnostic.category === ERROR_CATEGORY;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -113,7 +113,7 @@ export function projectErrors(sets: ProjectDiagnostics): ProjectErrors {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/** One project a run analyzes. */
|
|
116
|
-
|
|
116
|
+
interface DiscoveredProject {
|
|
117
117
|
/**
|
|
118
118
|
* The name a run gives the project: the identifier the build matrix declares for
|
|
119
119
|
* it, or, for a project discovery derived, its configuration file's path below the
|
|
@@ -162,7 +162,7 @@ export function isGuess(discovery: Discovery, configFile: string): boolean {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/** Every project discovery found, and where each came from. */
|
|
165
|
-
|
|
165
|
+
interface Discovery {
|
|
166
166
|
/** The projects, in discovery order, each once. */
|
|
167
167
|
readonly projects: readonly DiscoveredProject[];
|
|
168
168
|
/**
|
package/src/edges.ts
CHANGED
|
@@ -39,7 +39,7 @@ const REFERENCE_WANT =
|
|
|
39
39
|
"a stable symbol reference naming one symbol exactly, <language>://<scope>#<fragment>";
|
|
40
40
|
|
|
41
41
|
/** An edges document the Contract refuses: a malformed input, answered with the usage code. */
|
|
42
|
-
|
|
42
|
+
class EdgesError extends ConfigError {
|
|
43
43
|
constructor(at: string, text: string, want: string) {
|
|
44
44
|
super("malformed", EDGES_FILE, `${at}: ${text}: want ${want}`);
|
|
45
45
|
this.name = "EdgesError";
|
|
@@ -174,7 +174,7 @@ export function readEdgeSides(host: Host, targetRoot: string): readonly EdgeSide
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/** One side's verdict, as an evaluation names it. */
|
|
177
|
-
|
|
177
|
+
type EdgeState = "live" | "dead" | "absent";
|
|
178
178
|
|
|
179
179
|
/** One edge evaluation: one side of this language, its state, and the finding it holds. */
|
|
180
180
|
export interface EdgeEvaluation<F extends Finding = Finding> extends EdgeSide {
|
|
@@ -184,7 +184,7 @@ export interface EdgeEvaluation<F extends Finding = Finding> extends EdgeSide {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/** The findings a report publishes, and one evaluation per declared side of this language. */
|
|
187
|
-
|
|
187
|
+
interface Evaluated<F extends Finding> {
|
|
188
188
|
readonly findings: readonly F[];
|
|
189
189
|
/** Ordered by edge, then by side, each compared bytewise. */
|
|
190
190
|
readonly evaluations: readonly EdgeEvaluation<F>[];
|