@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.6

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.
Files changed (88) hide show
  1. package/dist/src/analysis.js +3 -3
  2. package/dist/src/config.js +1 -1
  3. package/dist/src/conformance-record.js +1 -1
  4. package/dist/src/contract/contract.generated.js +1071 -0
  5. package/dist/src/contract/report.generated.js +796 -0
  6. package/dist/src/exempt-classes.js +1 -59
  7. package/dist/src/exempt.js +8 -19
  8. package/dist/src/findings/completion.js +11 -20
  9. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  10. package/dist/src/findings/emitters.js +4 -4
  11. package/dist/src/findings/interfaces.js +4 -5
  12. package/dist/src/findings/intra-function.js +8 -7
  13. package/dist/src/findings/non-code-artifacts.js +5 -6
  14. package/dist/src/findings/reads-and-writes.js +9 -8
  15. package/dist/src/findings/self-check.js +7 -10
  16. package/dist/src/findings/unused-declarations.js +28 -27
  17. package/dist/src/findings/visibility-narrowing.js +9 -11
  18. package/dist/src/findings-pass.js +1 -3
  19. package/dist/src/index.js +1 -1
  20. package/dist/src/intra-function-parts.js +8 -13
  21. package/dist/src/kinds.js +4 -360
  22. package/dist/src/report-schema.js +273 -0
  23. package/dist/src/report.js +17 -11
  24. package/dist/src/resolve.js +1 -6
  25. package/dist/src/run.js +2 -1
  26. package/dist/src/setup-failure.js +2 -6
  27. package/dist/src/suppress-file.js +4 -3
  28. package/dist/src/suppress-inline.js +3 -2
  29. package/dist/src/suppress.js +29 -48
  30. package/dist/src/verbs/analyze.js +7 -2
  31. package/dist/src/verbs/describe.js +4 -4
  32. package/dist/src/verbs/explain.js +2 -1
  33. package/dist/src/verbs/print-config.js +2 -1
  34. package/dist/src/verbs/print-projects.js +2 -1
  35. package/dist/src/verbs/print-retained.js +2 -1
  36. package/dist/src/verbs/print-roots.js +2 -1
  37. package/dist/src/verbs/verb.js +1 -5
  38. package/dist/src/verbs/version.js +2 -2
  39. package/jsr.json +1 -1
  40. package/package.json +2 -1
  41. package/src/analysis.ts +3 -3
  42. package/src/config.ts +7 -13
  43. package/src/conformance-record.ts +1 -1
  44. package/src/contract/contract.generated.ts +1123 -0
  45. package/src/contract/report.generated.ts +1157 -0
  46. package/src/exempt-classes.ts +1 -70
  47. package/src/exempt.ts +12 -20
  48. package/src/finding.ts +9 -9
  49. package/src/findings/completion.ts +22 -23
  50. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  51. package/src/findings/emitter.ts +6 -7
  52. package/src/findings/emitters.ts +6 -6
  53. package/src/findings/interfaces.ts +4 -6
  54. package/src/findings/intra-function.ts +8 -8
  55. package/src/findings/non-code-artifacts.ts +5 -7
  56. package/src/findings/reads-and-writes.ts +13 -11
  57. package/src/findings/self-check.ts +8 -15
  58. package/src/findings/unused-declarations.ts +28 -27
  59. package/src/findings/visibility-narrowing.ts +9 -12
  60. package/src/findings-pass.ts +1 -4
  61. package/src/index.ts +1 -1
  62. package/src/intra-function-parts.ts +8 -14
  63. package/src/kinds.ts +6 -406
  64. package/src/report-schema.ts +366 -0
  65. package/src/report.ts +42 -195
  66. package/src/reporters/annotations.ts +2 -2
  67. package/src/reporters/json.ts +1 -1
  68. package/src/reporters/reporter.ts +2 -2
  69. package/src/reporters/reporters.ts +1 -1
  70. package/src/reporters/sarif.ts +13 -8
  71. package/src/reporters/text.ts +3 -3
  72. package/src/resolve.ts +11 -11
  73. package/src/run.ts +2 -1
  74. package/src/setup-failure.ts +5 -7
  75. package/src/suppress-file.ts +3 -4
  76. package/src/suppress-inline.ts +2 -2
  77. package/src/suppress.ts +39 -55
  78. package/src/verbs/analyze.ts +16 -4
  79. package/src/verbs/describe.ts +10 -4
  80. package/src/verbs/explain.ts +2 -1
  81. package/src/verbs/print-config.ts +2 -1
  82. package/src/verbs/print-projects.ts +2 -1
  83. package/src/verbs/print-retained.ts +2 -1
  84. package/src/verbs/print-roots.ts +2 -1
  85. package/src/verbs/verb.ts +0 -6
  86. package/src/verbs/version.ts +2 -2
  87. package/dist/src/version.js +0 -7
  88. package/src/version.ts +0 -7
package/src/report.ts CHANGED
@@ -4,7 +4,9 @@
4
4
  * renders this document, so no two renderings disagree about which findings exist.
5
5
  */
6
6
 
7
- import type { Severity, Sort, TargetKind } from "./config.ts";
7
+ import { SCHEMA_VERSION, SCHEMA_VERSIONS } from "./contract/contract.generated.ts";
8
+ import type * as wire from "./contract/report.generated.ts";
9
+ import type { Sort, TargetKind } from "./config.ts";
8
10
  import type { Conformance, DeclaredGap } from "./conformance.ts";
9
11
  import type { EdgeEvaluation } from "./edges.ts";
10
12
  import type {
@@ -18,134 +20,12 @@ import type { PassResult } from "./findings-pass.ts";
18
20
  import type { StaleSuppression } from "./findings/self-check.ts";
19
21
  import type { TestFileRule } from "./references.ts";
20
22
 
21
- /** The version of the report schema this analyzer writes a report to. */
22
- const SCHEMA_VERSION = "9.0.0";
23
-
24
- /** Every report schema version this analyzer reads: the one the Contract it implements admits. */
25
- export const SCHEMA_VERSIONS_ACCEPTED: readonly string[] = [SCHEMA_VERSION];
26
-
27
23
  /** The name this analyzer writes every document under and mints every identifier with. */
28
24
  export const ANALYZER_NAME = "deadset-ts";
29
25
 
30
26
  /** The language this analyzer claims. */
31
27
  export const LANGUAGE = "ts";
32
28
 
33
- interface WirePosition {
34
- readonly path: string;
35
- readonly line: number;
36
- readonly column: number;
37
- readonly end_line: number;
38
- }
39
-
40
- interface WireDetails {
41
- readonly narrower_visibility?: string;
42
- readonly implementations?: readonly {
43
- readonly ref: string;
44
- readonly name: string;
45
- readonly position: WirePosition;
46
- }[];
47
- readonly write_positions?: readonly WirePosition[];
48
- /** Set under the name-literal rule, which no finding of this analyzer meets; SARIF still maps it. */
49
- readonly name_literal?: WirePosition;
50
- readonly dependency_class?: string;
51
- readonly mechanism?: string;
52
- readonly entry?: {
53
- readonly code: string;
54
- readonly symbol?: string;
55
- readonly path?: string;
56
- readonly reason?: string;
57
- };
58
- readonly overlap?: readonly string[];
59
- readonly removes_last_use_of?: readonly string[];
60
- }
61
-
62
- /** One finding as the finding schema spells it. */
63
- export interface WireFinding {
64
- readonly code: string;
65
- readonly kind: string;
66
- readonly language: string;
67
- readonly position: WirePosition;
68
- readonly symbol: {
69
- readonly ref: string;
70
- readonly kind: string;
71
- readonly name: string;
72
- readonly size_lines: number;
73
- };
74
- readonly reachability_class: string;
75
- readonly confidence: string;
76
- readonly liveness_relation?: string;
77
- readonly test_only: boolean;
78
- readonly generated: boolean;
79
- readonly component: {
80
- readonly id: string;
81
- readonly root: boolean;
82
- readonly symbol_count: number;
83
- readonly deletable_lines: number;
84
- };
85
- readonly retained_by: readonly string[];
86
- readonly configurations: readonly string[];
87
- readonly consumers_loaded: readonly string[];
88
- readonly fixability: string;
89
- readonly severity: Severity;
90
- readonly message: string;
91
- readonly details: WireDetails;
92
- }
93
-
94
- /** One dead component a finding names, with its members, as the report lists it once. */
95
- export interface WireComponent {
96
- readonly id: string;
97
- readonly members: readonly {
98
- readonly ref: string;
99
- readonly name: string;
100
- readonly position: WirePosition;
101
- }[];
102
- }
103
-
104
- interface WireEdgeEvaluation {
105
- readonly edge: string;
106
- readonly side: string;
107
- readonly symbol: string;
108
- readonly state: string;
109
- readonly finding?: WireFinding;
110
- }
111
-
112
- export interface WireStaleSuppression {
113
- readonly code: string;
114
- readonly mechanism: string;
115
- readonly entry: {
116
- readonly code: string;
117
- readonly symbol?: string;
118
- readonly path: string;
119
- readonly reason: string;
120
- };
121
- readonly position: { readonly path: string; readonly line: number; readonly column: number };
122
- readonly symbol: string;
123
- readonly message: string;
124
- }
125
-
126
- interface WireDeclaredGap {
127
- readonly fixture: string;
128
- readonly symbol?: string;
129
- readonly capability: string;
130
- readonly reason: string;
131
- }
132
-
133
- export interface WireTotals {
134
- readonly findings: number;
135
- readonly by_severity: { readonly allow: number; readonly warn: number; readonly deny: number };
136
- readonly deletable_lines: number;
137
- readonly suppressions_in_effect: number;
138
- readonly reasons_recorded: number;
139
- readonly stale_suppressions: number;
140
- readonly pending: number;
141
- readonly omitted: number;
142
- readonly withheld: {
143
- readonly certain: number;
144
- readonly probable: number;
145
- readonly possible: number;
146
- };
147
- }
148
-
149
29
  /** One project configuration of the matrix a run analyzed. */
150
30
  export interface ReportConfiguration {
151
31
  readonly id: string;
@@ -191,52 +71,6 @@ interface UnavailableConsumer {
191
71
  readonly reason: string;
192
72
  }
193
73
 
194
- /** The report document. */
195
- export interface Report {
196
- readonly schema_version: string;
197
- readonly contract_version: string;
198
- readonly analyzer: {
199
- readonly name: string;
200
- readonly version: string;
201
- readonly languages: readonly string[];
202
- readonly schema_versions_accepted: readonly string[];
203
- readonly conformance: {
204
- readonly corpus_version: string;
205
- readonly result: string;
206
- readonly digest: string;
207
- };
208
- };
209
- readonly target: { readonly kind: TargetKind; readonly root: string; readonly identity: string };
210
- readonly configurations: readonly ReportConfiguration[];
211
- readonly configurations_not_built: readonly ReportNotBuilt[];
212
- readonly consumers: {
213
- readonly declared: number;
214
- readonly loaded: readonly {
215
- readonly id: string;
216
- readonly role: "consumer";
217
- readonly path: string;
218
- }[];
219
- readonly unavailable: readonly {
220
- readonly id: string;
221
- readonly role: "consumer";
222
- readonly reason: string;
223
- }[];
224
- };
225
- readonly findings: readonly WireFinding[];
226
- /** Present where the run lists each component in full. */
227
- readonly components?: readonly WireComponent[];
228
- readonly edge_evaluations: readonly WireEdgeEvaluation[];
229
- readonly stale_suppressions: readonly WireStaleSuppression[];
230
- readonly declared_gaps: readonly WireDeclaredGap[];
231
- readonly excluded_by_cgo: readonly never[];
232
- readonly test_file_rules: readonly TestFileRule[];
233
- readonly type_error_skips: readonly TypeErrorSkip[];
234
- readonly notes: readonly ReportNote[];
235
- readonly unanswered_questions: readonly UnansweredCount[];
236
- readonly conventions_applied: readonly AppliedConvention[];
237
- readonly totals: WireTotals;
238
- }
239
-
240
74
  /** One hint about the run's setup, as the report schema's `notes` spells it. */
241
75
  export type ReportNote =
242
76
  | {
@@ -282,13 +116,16 @@ export interface ReportInput {
282
116
  * sorted, then the edge evaluations' findings.
283
117
  */
284
118
  function componentsOf(
285
- findings: readonly (readonly [WireFinding, CompletedFinding])[],
286
- ): readonly WireComponent[] {
287
- const listed = new Map<string, WireComponent>();
288
- for (const [wire, finding] of findings) {
119
+ findings: readonly (readonly [wire.Finding, CompletedFinding])[],
120
+ ): readonly wire.ComponentsEntry[] {
121
+ const listed = new Map<string, wire.ComponentsEntry>();
122
+ for (const [written, finding] of findings) {
289
123
  const { members } = finding.component;
290
- if (members !== undefined && !listed.has(wire.component.id)) {
291
- listed.set(wire.component.id, { id: wire.component.id, members: members.map(wireSymbol) });
124
+ if (members !== undefined && !listed.has(written.component.id)) {
125
+ listed.set(written.component.id, {
126
+ id: written.component.id,
127
+ members: members.map(wireSymbol),
128
+ });
292
129
  }
293
130
  }
294
131
  return [...listed.values()];
@@ -302,7 +139,7 @@ function compare(a: string, b: string): number {
302
139
  return a < b ? -1 : 1;
303
140
  }
304
141
 
305
- function wirePosition(position: FindingPosition): WirePosition {
142
+ function wirePosition(position: FindingPosition): wire.Position {
306
143
  return {
307
144
  path: position.path,
308
145
  line: position.line,
@@ -314,13 +151,13 @@ function wirePosition(position: FindingPosition): WirePosition {
314
151
  function wireSymbol(symbol: PositionedSymbol): {
315
152
  ref: string;
316
153
  name: string;
317
- position: WirePosition;
154
+ position: wire.Position;
318
155
  } {
319
156
  return { ref: symbol.ref, name: symbol.name, position: wirePosition(symbol.position) };
320
157
  }
321
158
 
322
159
  /** The details one code carries, in the order the finding schema lists them. */
323
- function wireDetails(details: FindingDetails): WireDetails {
160
+ function wireDetails(details: FindingDetails): wire.Details {
324
161
  const { entry } = details;
325
162
  return {
326
163
  ...(details.narrowerVisibility === undefined
@@ -352,7 +189,7 @@ function wireDetails(details: FindingDetails): WireDetails {
352
189
  }
353
190
 
354
191
  /** One finding as the finding schema spells it, member for member in its order. */
355
- export function wireFinding(finding: CompletedFinding): WireFinding {
192
+ export function wireFinding(finding: CompletedFinding): wire.Finding {
356
193
  return {
357
194
  code: finding.code,
358
195
  kind: finding.kind,
@@ -387,7 +224,7 @@ export function wireFinding(finding: CompletedFinding): WireFinding {
387
224
  };
388
225
  }
389
226
 
390
- function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): WireEdgeEvaluation {
227
+ function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): wire.EdgeEvaluationsEntry {
391
228
  return {
392
229
  edge: evaluation.edge,
393
230
  side: evaluation.side,
@@ -397,7 +234,7 @@ function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): WireEdgeE
397
234
  };
398
235
  }
399
236
 
400
- function wireStale(stale: StaleSuppression): WireStaleSuppression {
237
+ function wireStale(stale: StaleSuppression): wire.StaleSuppressionsEntry {
401
238
  return {
402
239
  code: stale.code,
403
240
  mechanism: stale.mechanism,
@@ -418,7 +255,7 @@ function wireStale(stale: StaleSuppression): WireStaleSuppression {
418
255
  }
419
256
 
420
257
  /** The canonical key over two findings: path, line, column, code, then the symbol reference. */
421
- function canonical(a: WireFinding, b: WireFinding): number {
258
+ function canonical(a: wire.Finding, b: wire.Finding): number {
422
259
  return (
423
260
  compare(a.position.path, b.position.path) ||
424
261
  a.position.line - b.position.line ||
@@ -429,7 +266,7 @@ function canonical(a: WireFinding, b: WireFinding): number {
429
266
  }
430
267
 
431
268
  /** The canonical key over two stale suppressions, each component from the record's own member. */
432
- function canonicalStale(a: WireStaleSuppression, b: WireStaleSuppression): number {
269
+ function canonicalStale(a: wire.StaleSuppressionsEntry, b: wire.StaleSuppressionsEntry): number {
433
270
  return (
434
271
  compare(a.position.path, b.position.path) ||
435
272
  a.position.line - b.position.line ||
@@ -440,7 +277,7 @@ function canonicalStale(a: WireStaleSuppression, b: WireStaleSuppression): numbe
440
277
  }
441
278
 
442
279
  /** One declared gap as a report carries it, its members in the schema's order. */
443
- function wireGap(gap: DeclaredGap): WireDeclaredGap {
280
+ function wireGap(gap: DeclaredGap): wire.DeclaredGapsEntry {
444
281
  return {
445
282
  fixture: gap.fixture,
446
283
  ...(gap.symbol === undefined ? {} : { symbol: gap.symbol }),
@@ -450,7 +287,7 @@ function wireGap(gap: DeclaredGap): WireDeclaredGap {
450
287
  }
451
288
 
452
289
  /** Edge evaluations by edge, then side, then their compact encodings. */
453
- function byEdge(a: WireEdgeEvaluation, b: WireEdgeEvaluation): number {
290
+ function byEdge(a: wire.EdgeEvaluationsEntry, b: wire.EdgeEvaluationsEntry): number {
454
291
  return (
455
292
  compare(a.edge, b.edge) ||
456
293
  compare(a.side, b.side) ||
@@ -462,7 +299,7 @@ function byEdge(a: WireEdgeEvaluation, b: WireEdgeEvaluation): number {
462
299
  * The lines a reader deletes by acting on every finding: each distinct component a root
463
300
  * finding names, counted once however many of its roots the findings hold.
464
301
  */
465
- function deletableLines(findings: readonly WireFinding[]): number {
302
+ function deletableLines(findings: readonly wire.Finding[]): number {
466
303
  const counted = new Map<string, number>();
467
304
  for (const finding of findings) {
468
305
  if (finding.component.root) {
@@ -477,7 +314,7 @@ function deletableLines(findings: readonly WireFinding[]): number {
477
314
  }
478
315
 
479
316
  /** The totals of one finding set, counted over the whole set. */
480
- function totalsOf(findings: readonly WireFinding[], result: PassResult): WireTotals {
317
+ function totalsOf(findings: readonly wire.Finding[], result: PassResult): wire.Totals {
481
318
  const bySeverity = { allow: 0, warn: 0, deny: 0 };
482
319
  for (const finding of findings) {
483
320
  bySeverity[finding.severity] += 1;
@@ -500,7 +337,7 @@ function totalsOf(findings: readonly WireFinding[], result: PassResult): WireTot
500
337
  }
501
338
 
502
339
  /** Builds the report of one run, every array in the order the Contract fixes for it. */
503
- export function buildReport(input: ReportInput): Report {
340
+ export function buildReport(input: ReportInput): wire.Report {
504
341
  const paired = input.result.findings
505
342
  .map((finding) => [wireFinding(finding), finding] as const)
506
343
  .sort(([a], [b]) => canonical(a, b));
@@ -521,7 +358,7 @@ export function buildReport(input: ReportInput): Report {
521
358
  name: ANALYZER_NAME,
522
359
  version: input.version,
523
360
  languages: [LANGUAGE],
524
- schema_versions_accepted: SCHEMA_VERSIONS_ACCEPTED,
361
+ schema_versions_accepted: SCHEMA_VERSIONS,
525
362
  conformance: {
526
363
  corpus_version: input.conformance.corpusVersion,
527
364
  result: input.conformance.result,
@@ -590,8 +427,8 @@ export function buildReport(input: ReportInput): Report {
590
427
  * whose component a deletion shrinks more comes first, then one whose subject is larger,
591
428
  * then the canonical key, so the order stays total; by position it is the canonical key.
592
429
  */
593
- export function sortFindings(report: Report, by: Sort): Report {
594
- const bySize = (a: WireFinding, b: WireFinding): number =>
430
+ export function sortFindings(report: wire.Report, by: Sort): wire.Report {
431
+ const bySize = (a: wire.Finding, b: wire.Finding): number =>
595
432
  b.component.deletable_lines - a.component.deletable_lines ||
596
433
  b.symbol.size_lines - a.symbol.size_lines ||
597
434
  canonical(a, b);
@@ -599,16 +436,26 @@ export function sortFindings(report: Report, by: Sort): Report {
599
436
  }
600
437
 
601
438
  /**
602
- * The report holding the first `most` findings of its order, the rest counted as omitted.
603
- * Every other total keeps describing the whole set. Zero keeps every finding.
439
+ * The report holding the first `most` findings of its order, the rest counted as omitted,
440
+ * and listing only the components those findings name. Every other total keeps describing
441
+ * the whole set. Zero keeps every finding.
604
442
  */
605
- export function capFindings(report: Report, most: number): Report {
443
+ export function capFindings(report: wire.Report, most: number): wire.Report {
606
444
  if (most <= 0 || report.findings.length <= most) {
607
445
  return report;
608
446
  }
447
+ const findings = report.findings.slice(0, most);
448
+ const named = new Set(
449
+ [...findings, ...report.edge_evaluations.flatMap((one) => one.finding ?? [])].map(
450
+ (finding) => finding.component.id,
451
+ ),
452
+ );
609
453
  return {
610
454
  ...report,
611
- findings: report.findings.slice(0, most),
455
+ findings,
456
+ ...(report.components === undefined
457
+ ? {}
458
+ : { components: report.components.filter((listed) => named.has(listed.id)) }),
612
459
  totals: { ...report.totals, omitted: report.findings.length - most },
613
460
  };
614
461
  }
@@ -1,7 +1,7 @@
1
- import type { Severity } from "../config.ts";
1
+ import type { Severity } from "../contract/contract.generated.ts";
2
2
  import { coalesced } from "../json-chunks.ts";
3
3
  import { KINDS } from "../kinds.ts";
4
- import type { Report } from "../report.ts";
4
+ import type { Report } from "../contract/report.generated.ts";
5
5
 
6
6
  /** The escapes a workflow command's message needs, so a line break does not end the command. */
7
7
  const DATA_ESCAPES: Readonly<Record<string, string>> = { "%": "%25", "\r": "%0D", "\n": "%0A" };
@@ -1,5 +1,5 @@
1
1
  import { jsonChunks } from "../json-chunks.ts";
2
- import type { Report } from "../report.ts";
2
+ import type { Report } from "../contract/report.generated.ts";
3
3
 
4
4
  /** The indentation of the report document, which with the schema's member order fixes its bytes. */
5
5
  const INDENT = 2;
@@ -1,5 +1,5 @@
1
- import type { Format } from "../config.ts";
2
- import type { Report } from "../report.ts";
1
+ import type { Format } from "../contract/contract.generated.ts";
2
+ import type { Report } from "../contract/report.generated.ts";
3
3
  import type { Template } from "./template.ts";
4
4
 
5
5
  /** What a rendering reads beside the report. */
@@ -1,4 +1,4 @@
1
- import type { Format } from "../config.ts";
1
+ import type { Format } from "../contract/contract.generated.ts";
2
2
  import { annotations } from "./annotations.ts";
3
3
  import { json } from "./json.ts";
4
4
  import { RenderError, type Rendering, type Renderings } from "./reporter.ts";
@@ -1,8 +1,13 @@
1
- import type { Confidence, Severity } from "../config.ts";
2
1
  import { jsonChunks } from "../json-chunks.ts";
3
- import { KINDS, type KindRow } from "../kinds.ts";
2
+ import type { KindRow, Confidence, Severity } from "../contract/contract.generated.ts";
3
+ import { KINDS } from "../kinds.ts";
4
4
  import { declarationKey } from "../ref.ts";
5
- import type { Report, WireComponent, WireFinding, WireStaleSuppression } from "../report.ts";
5
+ import type {
6
+ ComponentsEntry,
7
+ Finding,
8
+ Report,
9
+ StaleSuppressionsEntry,
10
+ } from "../contract/report.generated.ts";
6
11
  import { lineHashes, symbolFingerprint } from "./fingerprint.ts";
7
12
  import { RenderError, type RenderOptions } from "./reporter.ts";
8
13
  import { utf8Bytes } from "./sha256.ts";
@@ -152,10 +157,10 @@ interface Related {
152
157
  }
153
158
 
154
159
  /** The members the report lists for one component, none where it lists none. */
155
- type MembersOf = (id: string) => WireComponent["members"];
160
+ type MembersOf = (id: string) => ComponentsEntry["members"];
156
161
 
157
162
  /** Every position a finding names beyond its own, in the mapping's order, the first hundred. */
158
- function relatedOf(found: WireFinding, members: MembersOf): readonly Related[] {
163
+ function relatedOf(found: Finding, members: MembersOf): readonly Related[] {
159
164
  const related: Related[] = [];
160
165
  for (const one of found.details.implementations ?? []) {
161
166
  related.push({ label: "implementation", at: one.position, endLine: one.position.end_line });
@@ -192,7 +197,7 @@ function messageWithLinks(message: string, related: readonly Related[]): string
192
197
 
193
198
  /** Every member of the finding the mapping places nowhere else, under its schema name. */
194
199
  /** A result's member bag, its component holding the members the report lists for it once. */
195
- function propertiesOf(found: WireFinding, members: MembersOf): Record<string, unknown> {
200
+ function propertiesOf(found: Finding, members: MembersOf): Record<string, unknown> {
196
201
  const listed = members(found.component.id);
197
202
  return {
198
203
  language: found.language,
@@ -222,7 +227,7 @@ function ruleIndex(index: ReadonlyMap<string, number>, code: string): number {
222
227
  }
223
228
 
224
229
  function findingResult(
225
- found: WireFinding,
230
+ found: Finding,
226
231
  index: ReadonlyMap<string, number>,
227
232
  hashOf: (at: Located) => string,
228
233
  members: MembersOf,
@@ -253,7 +258,7 @@ function findingResult(
253
258
 
254
259
  /** A stale suppression at the failing level, at its own site; it is no finding, so it carries no member bag. */
255
260
  function staleResult(
256
- stale: WireStaleSuppression,
261
+ stale: StaleSuppressionsEntry,
257
262
  index: ReadonlyMap<string, number>,
258
263
  hashOf: (at: Located) => string,
259
264
  ): Record<string, unknown> {
@@ -1,5 +1,5 @@
1
1
  import { coalesced } from "../json-chunks.ts";
2
- import type { Report, WireTotals } from "../report.ts";
2
+ import type { Report, Totals } from "../contract/report.generated.ts";
3
3
 
4
4
  /** The kind token and the confidence a stale suppression is rendered with, both fixed. */
5
5
  const STALE_KIND = "suppression";
@@ -11,7 +11,7 @@ function counted(n: number, one: string, many: string): string {
11
11
  }
12
12
 
13
13
  /** The report's totals in one line, in the order the report declares them. */
14
- function summary(totals: WireTotals): string {
14
+ function summary(totals: Totals): string {
15
15
  const { allow, warn, deny } = totals.by_severity;
16
16
  return [
17
17
  `summary: ${counted(totals.findings, "finding", "findings")} (${String(allow)} allow, ${String(warn)} warn, ${String(deny)} deny)`,
@@ -31,7 +31,7 @@ const WITHHELD_ORDER = ["probable", "possible"] as const;
31
31
  * The line naming what the minimum confidence withheld and the setting that shows it all,
32
32
  * or undefined where it withheld nothing.
33
33
  */
34
- export function withheldLine(totals: WireTotals): string | undefined {
34
+ export function withheldLine(totals: Totals): string | undefined {
35
35
  const named = WITHHELD_ORDER.filter((confidence) => totals.withheld[confidence] > 0);
36
36
  const lowest = named.at(-1);
37
37
  if (lowest === undefined) {
package/src/resolve.ts CHANGED
@@ -1,21 +1,28 @@
1
+ import {
2
+ SEVERITIES,
3
+ LANGUAGES,
4
+ CONFIDENCES,
5
+ FORMATS,
6
+ type Confidence,
7
+ type Format,
8
+ type Language,
9
+ type Severity,
10
+ FAMILY_KEY_LENGTH,
11
+ } from "./contract/contract.generated.ts";
1
12
  import {
2
13
  ConfigError,
3
14
  defaultConfig,
4
15
  type BuildConfiguration,
5
16
  type Cascade,
6
17
  type Config,
7
- type Confidence,
8
18
  type ConsumerTests,
9
19
  type DeclarationEntry,
10
- type Format,
11
20
  type GeneratedFiles,
12
21
  type Inputs,
13
- type Language,
14
22
  type LifecycleContract,
15
23
  type Origin,
16
24
  type Provenance,
17
25
  type Provider,
18
- type Severity,
19
26
  type Sort,
20
27
  type Source,
21
28
  type TargetKind,
@@ -36,9 +43,6 @@ import {
36
43
  /** Names the command-line flags in a refusal, where a document is named by its path. */
37
44
  const FLAGS_LABEL = "the command-line flags";
38
45
 
39
- /** The length of a severity key naming a whole family: the prefix and two digits. */
40
- export const FAMILY_KEY_LENGTH = 4;
41
-
42
46
  const SEVERITY_KEY = /^DS[0-9]{2}([0-9]{2})?$/u;
43
47
  const EXEMPTION_CLASS = /^[a-z][a-z0-9-]*$/u;
44
48
  const CONTRACT_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+$/u;
@@ -69,12 +73,8 @@ const PROJECT_SEGMENT = String.raw`(?:[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.[^/
69
73
  const PROJECT_PATH = new RegExp(`^${PROJECT_SEGMENT}(?:/${PROJECT_SEGMENT})*$`, "u");
70
74
 
71
75
  const TARGET_KINDS: readonly TargetKind[] = ["application", "library"];
72
- const CONFIDENCES: readonly Confidence[] = ["certain", "probable", "possible"];
73
76
  const GENERATED_FILES: readonly GeneratedFiles[] = ["exclude", "include"];
74
77
  const CONSUMER_TESTS: readonly ConsumerTests[] = ["test", "production"];
75
- const LANGUAGES: readonly Language[] = ["go", "ts"];
76
- const SEVERITIES: readonly Severity[] = ["allow", "warn", "deny"];
77
- const FORMATS: readonly Format[] = ["text", "json", "github", "sarif", "template"];
78
78
  const SORTS: readonly Sort[] = ["position", "size"];
79
79
  const CASCADES: readonly Cascade[] = ["roots", "full"];
80
80
 
package/src/run.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { EXIT_FAILURE, EXIT_USAGE } from "./contract/contract.generated.ts";
1
2
  import { ConfigError, REPOSITORY_DOCUMENT, type Inputs } from "./config.ts";
2
3
  import { DiscoveryError, renderDiagnostic } from "./discover.ts";
3
4
  import type { Host } from "./host.ts";
@@ -12,7 +13,7 @@ import { printRootsVerb } from "./verbs/print-roots.ts";
12
13
  import { ANALYZE_OPTIONS, analyzeVerb } from "./verbs/analyze.ts";
13
14
  import { describeVerb } from "./verbs/describe.ts";
14
15
  import { EXPLAIN_OPTIONS, explainVerb } from "./verbs/explain.ts";
15
- import { EXIT_FAILURE, EXIT_USAGE, type Verb, type VerbOptions } from "./verbs/verb.ts";
16
+ import { type Verb, type VerbOptions } from "./verbs/verb.ts";
16
17
  import { versionVerb } from "./verbs/version.ts";
17
18
 
18
19
  /** One output stream of the command line. `process.stdout` and `process.stderr` satisfy it. */
@@ -1,15 +1,13 @@
1
+ import { SETUP_FAILURE_PREFIX, type SETUP_CLASSES } from "./contract/contract.generated.ts";
2
+
1
3
  /**
2
4
  * A setup failure: a file or a component the analysis needs and the project does not
3
5
  * provide. Each one names what is missing and the fix, on one line of the error stream
4
6
  * that starts with its class.
5
7
  */
6
8
 
7
- /** The classes of setup failure this analyzer meets. */
8
- type SetupClass =
9
- | "missing-module"
10
- | "missing-consumer"
11
- | "workspace-member-without-source"
12
- | "convention-not-literal";
9
+ /** A class of setup failure the Contract names. */
10
+ type SetupClass = (typeof SETUP_CLASSES)[number]["class"];
13
11
 
14
12
  export interface SetupFailure {
15
13
  readonly setupClass: SetupClass;
@@ -19,7 +17,7 @@ export interface SetupFailure {
19
17
 
20
18
  /** The failure as the one line of the error stream that names it. */
21
19
  export function setupLine(failure: SetupFailure): string {
22
- return `setup failure: ${failure.setupClass}: ${failure.detail}`;
20
+ return `${SETUP_FAILURE_PREFIX}${failure.setupClass}: ${failure.detail}`;
23
21
  }
24
22
 
25
23
  /**
@@ -5,6 +5,7 @@
5
5
  * ignore file by gaining a maintainer's reason.
6
6
  */
7
7
 
8
+ import { CODE } from "./contract/contract.generated.ts";
8
9
  import type { Host } from "./host.ts";
9
10
  import type { InventorySymbol } from "./inventory.ts";
10
11
  import { jsonChunks } from "./json-chunks.ts";
@@ -14,9 +15,7 @@ import type { Position } from "./position.ts";
14
15
  import { declarationKey } from "./ref.ts";
15
16
  import {
16
17
  CODE_FORM,
17
- SUPPRESSION_WITHOUT_REASON,
18
18
  SuppressionError,
19
- UNSCOPED_ENTRY,
20
19
  blankReason,
21
20
  isEntryPath,
22
21
  isPartKind,
@@ -148,10 +147,10 @@ function readRecord(value: unknown, site: Position, byReference: ByReference, sh
148
147
  };
149
148
  const refusals: Refusal[] = [];
150
149
  if (blankReason(reason)) {
151
- refusals.push({ ...written, reported: SUPPRESSION_WITHOUT_REASON });
150
+ refusals.push({ ...written, reported: CODE.suppressionWithoutReason });
152
151
  }
153
152
  if (written.path === "") {
154
- refusals.push({ ...written, reported: UNSCOPED_ENTRY });
153
+ refusals.push({ ...written, reported: CODE.unscopedIgnoreEntry });
155
154
  }
156
155
  if (refusals.length > 0) {
157
156
  return { records: [], refusals };
@@ -4,6 +4,7 @@
4
4
  * below it, or on its own line where it trails a token there.
5
5
  */
6
6
 
7
+ import { CODE } from "./contract/contract.generated.ts";
7
8
  import {
8
9
  isConstructorDeclaration,
9
10
  SyntaxKind,
@@ -18,7 +19,6 @@ import type { ProjectView } from "./session.ts";
18
19
  import { signatureOf } from "./signatures.ts";
19
20
  import {
20
21
  isPartKind,
21
- SUPPRESSION_WITHOUT_REASON,
22
22
  SuppressionError,
23
23
  siteText,
24
24
  type Refusal,
@@ -354,7 +354,7 @@ export function inlineSuppressions(
354
354
  case "no-reason":
355
355
  for (const code of directive.codes) {
356
356
  refusals.push({
357
- reported: SUPPRESSION_WITHOUT_REASON,
357
+ reported: CODE.suppressionWithoutReason,
358
358
  code,
359
359
  symbol: "",
360
360
  path: site.path,