@ttsc/evidence 0.28.5 → 0.29.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/lib/structures/ITtscEvidenceGraphClaimBase.d.ts +7 -0
- package/lib/structures/ITtscEvidenceGraphReferenceBase.d.ts +7 -0
- package/native/config.go +31 -12
- package/native/graph.go +81 -71
- package/native/markdown.go +17 -11
- package/native/model.go +12 -7
- package/native/prisma.go +33 -26
- package/native/prisma_cache.go +16 -4
- package/native/severity.go +156 -0
- package/native/swagger.go +23 -16
- package/native/typescript.go +3 -3
- package/native/unreadable.go +5 -6
- package/package.json +4 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TtscLintSeverity } from "@ttsc/lint";
|
|
1
2
|
import type { ITtscEvidenceGraphReference } from "./ITtscEvidenceGraphReference";
|
|
2
3
|
/**
|
|
3
4
|
* What every claim declares: which artifact kind makes the claim, where its
|
|
@@ -21,6 +22,12 @@ export interface ITtscEvidenceGraphClaimBase<Type extends string> {
|
|
|
21
22
|
* configuration entries.
|
|
22
23
|
*/
|
|
23
24
|
name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Diagnostic level for this claim. Omit it or use `undefined` to inherit the
|
|
27
|
+
* outer `evidence/graph` rule level. A reference may override this level
|
|
28
|
+
* again. `"off"` disables the entire claim, including its references.
|
|
29
|
+
*/
|
|
30
|
+
severity?: TtscLintSeverity | undefined;
|
|
24
31
|
/**
|
|
25
32
|
* Excludes this claim from graph loading and evaluation.
|
|
26
33
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TtscLintSeverity } from "@ttsc/lint";
|
|
1
2
|
/**
|
|
2
3
|
* What every evidence population declares: which artifact kind it materializes,
|
|
3
4
|
* and how strictly the owning claim must acknowledge it.
|
|
@@ -25,6 +26,12 @@
|
|
|
25
26
|
export interface ITtscEvidenceGraphReferenceBase<Type extends string> {
|
|
26
27
|
/** Identifies the artifact kind this population materializes. */
|
|
27
28
|
type: Type;
|
|
29
|
+
/**
|
|
30
|
+
* Diagnostic level for this reference's obligation. Omit it or use
|
|
31
|
+
* `undefined` to inherit the claim level, then the outer `evidence/graph`
|
|
32
|
+
* rule level. `"off"` disables this reference's population and obligation.
|
|
33
|
+
*/
|
|
34
|
+
severity?: TtscLintSeverity | undefined;
|
|
28
35
|
/**
|
|
29
36
|
* Whether this reference refuses `@evidenceExclude` as an acknowledgement.
|
|
30
37
|
*
|
package/native/config.go
CHANGED
|
@@ -8,6 +8,8 @@ import (
|
|
|
8
8
|
"path"
|
|
9
9
|
"sort"
|
|
10
10
|
"strings"
|
|
11
|
+
|
|
12
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
func decodeGraphConfig(raw json.RawMessage) (graphConfig, []string) {
|
|
@@ -54,7 +56,7 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
54
56
|
}
|
|
55
57
|
problems := rejectUnknownFields(
|
|
56
58
|
object,
|
|
57
|
-
[]string{"type", "name", "disabled", "root", "files", "evidenceExcludeCarriers", "symbol", "reference"},
|
|
59
|
+
[]string{"type", "name", "severity", "disabled", "root", "files", "evidenceExcludeCarriers", "symbol", "reference"},
|
|
58
60
|
graphRuleName,
|
|
59
61
|
path,
|
|
60
62
|
)
|
|
@@ -78,6 +80,8 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
78
80
|
problems = append(problems, "Invalid evidence/graph configuration at "+path+".disabled: expected a boolean.")
|
|
79
81
|
}
|
|
80
82
|
}
|
|
83
|
+
severity, severityProblems := decodeGraphSeverity(object["severity"], path+".severity")
|
|
84
|
+
problems = append(problems, severityProblems...)
|
|
81
85
|
root, rootProblems := decodeClaimRoot(object["root"], kind, path+".root")
|
|
82
86
|
problems = append(problems, rootProblems...)
|
|
83
87
|
files, fileProblems := decodeFiles(object["files"], path+".files")
|
|
@@ -98,6 +102,7 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
98
102
|
}
|
|
99
103
|
return claimSpec{
|
|
100
104
|
Index: index,
|
|
105
|
+
Severity: severity,
|
|
101
106
|
Type: kind,
|
|
102
107
|
Name: name,
|
|
103
108
|
Disabled: disabled,
|
|
@@ -109,16 +114,26 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
109
114
|
}, nil
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
// enabledGraphConfig removes
|
|
117
|
+
// enabledGraphConfig removes disabled claims and references after their public
|
|
113
118
|
// configuration has been validated. Keeping the filter separate from decoding
|
|
114
|
-
// preserves original
|
|
119
|
+
// preserves original indexes and prevents disabled entries from hiding a
|
|
115
120
|
// malformed shape.
|
|
116
121
|
func enabledGraphConfig(config graphConfig) graphConfig {
|
|
117
122
|
claims := make([]claimSpec, 0, len(config.Claims))
|
|
118
123
|
for _, claim := range config.Claims {
|
|
119
|
-
if claim.Disabled {
|
|
124
|
+
if claim.Disabled || claim.Severity != nil && *claim.Severity == rule.SeverityOff {
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
references := make([]referenceSpec, 0, len(claim.References))
|
|
128
|
+
for _, reference := range claim.References {
|
|
129
|
+
if reference.Severity == nil || *reference.Severity != rule.SeverityOff {
|
|
130
|
+
references = append(references, reference)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if len(references) == 0 {
|
|
120
134
|
continue
|
|
121
135
|
}
|
|
136
|
+
claim.References = references
|
|
122
137
|
claims = append(claims, claim)
|
|
123
138
|
}
|
|
124
139
|
config.Claims = claims
|
|
@@ -183,6 +198,7 @@ func decodeReference(
|
|
|
183
198
|
object,
|
|
184
199
|
[]string{
|
|
185
200
|
"type",
|
|
201
|
+
"severity",
|
|
186
202
|
"noEvidenceExclude",
|
|
187
203
|
"uniqueEvidence",
|
|
188
204
|
"singleEvidencePerSymbol",
|
|
@@ -204,6 +220,8 @@ func decodeReference(
|
|
|
204
220
|
if problem := rejectForeignTypeScriptReference(claimKind, kind, path); problem != "" {
|
|
205
221
|
problems = append(problems, problem)
|
|
206
222
|
}
|
|
223
|
+
severity, severityProblems := decodeGraphSeverity(object["severity"], path+".severity")
|
|
224
|
+
problems = append(problems, severityProblems...)
|
|
207
225
|
root, rootProblems := decodeRoot(object["root"], kind, path+".root")
|
|
208
226
|
problems = append(problems, rootProblems...)
|
|
209
227
|
policy, policyProblems := decodeReferencePolicy(object, kind, path)
|
|
@@ -265,14 +283,15 @@ func decodeReference(
|
|
|
265
283
|
return referenceSpec{}, problems
|
|
266
284
|
}
|
|
267
285
|
return referenceSpec{
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
286
|
+
Severity: severity,
|
|
287
|
+
Index: index,
|
|
288
|
+
Type: kind,
|
|
289
|
+
Policy: policy,
|
|
290
|
+
Root: root,
|
|
291
|
+
Files: files,
|
|
292
|
+
Source: source,
|
|
293
|
+
Package: packageName,
|
|
294
|
+
Symbols: symbols,
|
|
276
295
|
}, nil
|
|
277
296
|
}
|
|
278
297
|
|
package/native/graph.go
CHANGED
|
@@ -31,6 +31,7 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
31
31
|
reportProblems(ctx, problems)
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
|
+
resolveGraphSeverities(&config, ctx.Severity)
|
|
34
35
|
config = enabledGraphConfig(config)
|
|
35
36
|
if len(config.Claims) == 0 {
|
|
36
37
|
cycle.Corpus = graphCorpus{Config: config}
|
|
@@ -46,6 +47,7 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
46
47
|
ctx.Report("Evidence graph project root '" + root + "' is not a readable directory. Fix the ttsc project identity before evaluating evidence globs.")
|
|
47
48
|
return
|
|
48
49
|
}
|
|
50
|
+
diagnostics := graphDiagnostics{}
|
|
49
51
|
// Every population is anchored before anything is read, so a loader, a
|
|
50
52
|
// diagnostic, and the corpus the editor receives all speak of one resolved
|
|
51
53
|
// base rather than each re-deriving it from the author's spelling.
|
|
@@ -68,15 +70,15 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
68
70
|
root,
|
|
69
71
|
claimPopulationConfig(config, artifactPrisma),
|
|
70
72
|
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
diagnostics = append(
|
|
74
|
+
diagnostics,
|
|
73
75
|
typeScriptBaseProblems(
|
|
74
76
|
claimPopulationConfig(config, artifactTypeScript),
|
|
75
77
|
typescript,
|
|
76
78
|
)...,
|
|
77
79
|
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
diagnostics = append(diagnostics, markdownClaimProblems...)
|
|
81
|
+
diagnostics = append(diagnostics, prismaClaimProblems...)
|
|
80
82
|
// Governance is judged against the configuration as declared, before
|
|
81
83
|
// activation drops a claim whose population materialized no unit of its
|
|
82
84
|
// symbol kind. The question is whether an author put this file in a
|
|
@@ -91,10 +93,10 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
91
93
|
markdown, markdownProblems := loadMarkdownInventories(root, config)
|
|
92
94
|
prisma, prismaProblems := loadPrismaInventories(root, config)
|
|
93
95
|
swagger, swaggerProblems := loadSwaggerInventories(root, config)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
diagnostics = append(diagnostics, markdownProblems...)
|
|
97
|
+
diagnostics = append(diagnostics, prismaProblems...)
|
|
98
|
+
diagnostics = append(diagnostics, swaggerProblems...)
|
|
99
|
+
diagnostics = append(diagnostics, unreadableTypeScriptTags(typescript, governed, declared)...)
|
|
98
100
|
loader := newTypeScriptLoader(root, typescript)
|
|
99
101
|
states, stateProblems := materializeClaimStates(
|
|
100
102
|
config,
|
|
@@ -104,10 +106,10 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
104
106
|
typescript,
|
|
105
107
|
loader,
|
|
106
108
|
)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if len(
|
|
109
|
+
diagnostics = append(diagnostics, stateProblems...)
|
|
110
|
+
diagnostics = append(diagnostics, evaluateEvidenceGraph(states, loader)...)
|
|
111
|
+
diagnostics.report(ctx)
|
|
112
|
+
if len(diagnostics) == 0 {
|
|
111
113
|
// Published only on a clean evaluation, because the host reads state
|
|
112
114
|
// from a rule that passed and reporting anything marks this one failed
|
|
113
115
|
// (`linthost/hints.go:147-149`, `linthost/project_engine.go:68-77`).
|
|
@@ -238,10 +240,11 @@ func materializeClaimStates(
|
|
|
238
240
|
swagger map[string]*artifactInventory,
|
|
239
241
|
typescript map[string]*artifactInventory,
|
|
240
242
|
loader *typeScriptLoader,
|
|
241
|
-
) ([]claimState,
|
|
243
|
+
) ([]claimState, graphDiagnostics) {
|
|
242
244
|
states := make([]claimState, 0, len(config.Claims))
|
|
243
|
-
problems :=
|
|
245
|
+
problems := graphDiagnostics{}
|
|
244
246
|
for _, claim := range config.Claims {
|
|
247
|
+
severity := claim.Level
|
|
245
248
|
inventories := inventoriesOf(claim.Type, markdown, prisma, swagger, typescript)
|
|
246
249
|
paths := matchingInventoryPaths(inventories, claim.Base, claim.Files)
|
|
247
250
|
state := claimState{
|
|
@@ -268,8 +271,8 @@ func materializeClaimStates(
|
|
|
268
271
|
// exclusion the claim writes, so the misspelling is reported where
|
|
269
272
|
// it was made rather than as a placement finding on each tag.
|
|
270
273
|
if len(carrierPaths) == 0 && len(paths) != 0 {
|
|
271
|
-
problems =
|
|
272
|
-
|
|
274
|
+
problems = problems.add(
|
|
275
|
+
severity,
|
|
273
276
|
claimLabel(claim)+" declares evidenceExcludeCarriers "+describePopulation(claim.Base, claim.ExclusionCarriers)+", which selects none of its "+decimal(len(paths))+" claim file(s). Point the patterns at a file this claim already selects, or drop the property to accept an exclusion anywhere in the population.",
|
|
274
277
|
)
|
|
275
278
|
}
|
|
@@ -346,6 +349,7 @@ func materializeClaimStates(
|
|
|
346
349
|
}
|
|
347
350
|
sortUnits(state.Hosts)
|
|
348
351
|
for _, reference := range claim.References {
|
|
352
|
+
severity := reference.Level
|
|
349
353
|
referenceInventories := inventoriesOf(
|
|
350
354
|
reference.Type,
|
|
351
355
|
markdown,
|
|
@@ -359,7 +363,7 @@ func materializeClaimStates(
|
|
|
359
363
|
reference,
|
|
360
364
|
loader,
|
|
361
365
|
)
|
|
362
|
-
problems =
|
|
366
|
+
problems = problems.add(severity, entryProblems...)
|
|
363
367
|
state.References = append(state.References, entryState)
|
|
364
368
|
continue
|
|
365
369
|
}
|
|
@@ -369,7 +373,7 @@ func materializeClaimStates(
|
|
|
369
373
|
reference,
|
|
370
374
|
loader,
|
|
371
375
|
)
|
|
372
|
-
problems =
|
|
376
|
+
problems = problems.add(severity, packageProblems...)
|
|
373
377
|
state.References = append(state.References, packageState)
|
|
374
378
|
continue
|
|
375
379
|
}
|
|
@@ -380,7 +384,7 @@ func materializeClaimStates(
|
|
|
380
384
|
referenceInventories,
|
|
381
385
|
loader,
|
|
382
386
|
)
|
|
383
|
-
problems =
|
|
387
|
+
problems = problems.add(severity, localProblems...)
|
|
384
388
|
state.References = append(state.References, localState)
|
|
385
389
|
continue
|
|
386
390
|
}
|
|
@@ -396,13 +400,13 @@ func materializeClaimStates(
|
|
|
396
400
|
}
|
|
397
401
|
if len(referencePaths) == 0 && referenceState.Healthy {
|
|
398
402
|
if reference.Type == artifactSwagger {
|
|
399
|
-
problems =
|
|
400
|
-
|
|
403
|
+
problems = problems.add(
|
|
404
|
+
severity,
|
|
401
405
|
claimLabel(claim)+" "+referenceLabel(reference)+" matched no swagger source for "+describeReferenceSources(reference)+". Fix the reference location; this obligation cannot materialize evidence units without a source.",
|
|
402
406
|
)
|
|
403
407
|
} else {
|
|
404
|
-
problems =
|
|
405
|
-
|
|
408
|
+
problems = problems.add(
|
|
409
|
+
severity,
|
|
406
410
|
claimLabel(claim)+" "+referenceLabel(reference)+" matched no "+string(reference.Type)+" files for "+describePopulation(reference.Base, reference.Files)+". Fix the reference globs or the root they resolve against; this obligation cannot materialize evidence units without files.",
|
|
407
411
|
)
|
|
408
412
|
}
|
|
@@ -477,8 +481,8 @@ func materializeClaimStates(
|
|
|
477
481
|
len(referenceState.Units) == 0 &&
|
|
478
482
|
referenceState.Healthy &&
|
|
479
483
|
!selectedInventoryProblem {
|
|
480
|
-
problems =
|
|
481
|
-
|
|
484
|
+
problems = problems.add(
|
|
485
|
+
severity,
|
|
482
486
|
claimLabel(claim)+" "+referenceLabel(reference)+" matched "+decimal(len(referencePaths))+" file(s) but found no selected evidence units ("+reference.Symbols.names()+"). Select symbol kinds present in those files or correct the reference globs.",
|
|
483
487
|
)
|
|
484
488
|
}
|
|
@@ -492,8 +496,8 @@ func materializeClaimStates(
|
|
|
492
496
|
func evaluateEvidenceGraph(
|
|
493
497
|
states []claimState,
|
|
494
498
|
loader *typeScriptLoader,
|
|
495
|
-
)
|
|
496
|
-
problems :=
|
|
499
|
+
) graphDiagnostics {
|
|
500
|
+
problems := graphDiagnostics{}
|
|
497
501
|
targets := map[string]map[string]*evidenceUnit{}
|
|
498
502
|
markdownTargets := map[string]map[string]*evidenceUnit{}
|
|
499
503
|
// Scoped targets are keyed by owning file as well as name, which is what
|
|
@@ -604,11 +608,12 @@ func evaluateEvidenceGraph(
|
|
|
604
608
|
|
|
605
609
|
resolved := map[string]string{}
|
|
606
610
|
for _, id := range declarationIDs {
|
|
611
|
+
severity := ownerSeverity(owners[id])
|
|
607
612
|
declaration := declarations[id]
|
|
608
613
|
context := declarationObligationContext(owners[id])
|
|
609
614
|
if !declaration.valid() {
|
|
610
|
-
problems =
|
|
611
|
-
|
|
615
|
+
problems = problems.add(
|
|
616
|
+
severity,
|
|
612
617
|
"Malformed @"+string(declaration.Tag)+" declaration at "+declaration.location()+" for "+context+": target and non-empty reason are mandatory. Write '@"+string(declaration.Tag)+" <target> <reason>'."+untrueTagWarning,
|
|
613
618
|
)
|
|
614
619
|
continue
|
|
@@ -622,7 +627,7 @@ func evaluateEvidenceGraph(
|
|
|
622
627
|
context,
|
|
623
628
|
)
|
|
624
629
|
if problem != "" {
|
|
625
|
-
problems =
|
|
630
|
+
problems = problems.add(severity, problem)
|
|
626
631
|
continue
|
|
627
632
|
}
|
|
628
633
|
resolved[id] = unitID
|
|
@@ -630,8 +635,8 @@ func evaluateEvidenceGraph(
|
|
|
630
635
|
}
|
|
631
636
|
if declaration.Type == artifactTypeScript &&
|
|
632
637
|
looksLikeTypeScriptTarget(declaration.Target, targets, markdownTargets) {
|
|
633
|
-
problems =
|
|
634
|
-
|
|
638
|
+
problems = problems.add(
|
|
639
|
+
severity,
|
|
635
640
|
"Unbraced TypeScript evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": a target naming a symbol is now written as an inline link, so the citing module's import is what resolves it. Write '@"+string(declaration.Tag)+" {@link "+declaration.Target+"} <reason>' and import the symbol; 'import type' is enough.",
|
|
636
641
|
)
|
|
637
642
|
continue
|
|
@@ -648,8 +653,8 @@ func evaluateEvidenceGraph(
|
|
|
648
653
|
if declaration.Type != artifactTypeScript {
|
|
649
654
|
addressable, code := splitCodeCandidates(candidates)
|
|
650
655
|
if len(addressable) == 0 && len(code) != 0 {
|
|
651
|
-
problems =
|
|
652
|
-
|
|
656
|
+
problems = problems.add(
|
|
657
|
+
severity,
|
|
653
658
|
"Code evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": a "+string(declaration.Type)+" claim cannot cite a TypeScript symbol, because a symbol citation resolves through the citing module's imports and this artifact has none. Invert the obligation so the code cites this artifact, or move the citation into TypeScript.",
|
|
654
659
|
)
|
|
655
660
|
continue
|
|
@@ -667,14 +672,11 @@ func evaluateEvidenceGraph(
|
|
|
667
672
|
continue
|
|
668
673
|
}
|
|
669
674
|
if hidden := hiddenTargets[declaration.Target]; hidden != nil {
|
|
670
|
-
problems =
|
|
671
|
-
problems,
|
|
672
|
-
hiddenTargetProblem(declaration, hidden, context),
|
|
673
|
-
)
|
|
675
|
+
problems = problems.add(severity, hiddenTargetProblem(declaration, hidden, context))
|
|
674
676
|
continue
|
|
675
677
|
}
|
|
676
|
-
problems =
|
|
677
|
-
|
|
678
|
+
problems = problems.add(
|
|
679
|
+
severity,
|
|
678
680
|
"Unresolved evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": no configured source materializes that evidence unit. Correct the target, make one of the named references select the source unit this claim actually uses, or remove the tag when this host does not answer for that target."+untrueTagWarning,
|
|
679
681
|
)
|
|
680
682
|
case 1:
|
|
@@ -687,8 +689,8 @@ func evaluateEvidenceGraph(
|
|
|
687
689
|
descriptions = append(descriptions, unit.Readable+" at "+unit.location())
|
|
688
690
|
}
|
|
689
691
|
sort.Strings(descriptions)
|
|
690
|
-
problems =
|
|
691
|
-
|
|
692
|
+
problems = problems.add(
|
|
693
|
+
severity,
|
|
692
694
|
"Ambiguous evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": it matches "+strings.Join(descriptions, "; ")+". Rename or qualify the source symbols so the target has exactly one meaning.",
|
|
693
695
|
)
|
|
694
696
|
}
|
|
@@ -697,11 +699,13 @@ func evaluateEvidenceGraph(
|
|
|
697
699
|
participates := map[string]bool{}
|
|
698
700
|
uncertain := map[string]bool{}
|
|
699
701
|
outOfScope := map[string][]string{}
|
|
702
|
+
outOfScopeLevels := map[string]rule.Severity{}
|
|
700
703
|
outOfScopeSelections := map[string]symbolSet{}
|
|
701
704
|
// An exclusion outside its claim's declared carriers is a placement
|
|
702
705
|
// finding, not a host-kind one, so it carries its own obligations and the
|
|
703
706
|
// carrier globs its message must name.
|
|
704
707
|
outsideCarrier := map[string][]string{}
|
|
708
|
+
outsideCarrierLevels := map[string]rule.Severity{}
|
|
705
709
|
outsideCarrierGlobs := map[string]string{}
|
|
706
710
|
// A checklist tag speaking for no selected host is a non-participation
|
|
707
711
|
// finding: within its reference it answers nothing, and whether that earns a
|
|
@@ -711,6 +715,7 @@ func evaluateEvidenceGraph(
|
|
|
711
715
|
// elsewhere — so the finding is recorded here and judged after the walk,
|
|
712
716
|
// when `answers` can say whether anything consumed the tag.
|
|
713
717
|
unhosted := map[string][]string{}
|
|
718
|
+
unhostedLevels := map[string]rule.Severity{}
|
|
714
719
|
unhostedSelections := map[string]symbolSet{}
|
|
715
720
|
// answers marks a declaration that wrote at least one acknowledgement
|
|
716
721
|
// ledger, or was refused as an aggregate, which is that citation's own
|
|
@@ -732,6 +737,7 @@ func evaluateEvidenceGraph(
|
|
|
732
737
|
// reviewing reference pays nothing.
|
|
733
738
|
var reviewLedgerForClaim *reviewLedger
|
|
734
739
|
for _, reference := range state.References {
|
|
740
|
+
severity := reference.Spec.Level
|
|
735
741
|
if !reference.Healthy {
|
|
736
742
|
for _, declaration := range state.Declarations {
|
|
737
743
|
uncertain[declaration.ID] = true
|
|
@@ -819,6 +825,7 @@ func evaluateEvidenceGraph(
|
|
|
819
825
|
continue
|
|
820
826
|
}
|
|
821
827
|
if declaration.Tag == tagExclude && state.OutsideCarrier[declaration.ID] {
|
|
828
|
+
outsideCarrierLevels[declaration.ID] = max(outsideCarrierLevels[declaration.ID], severity)
|
|
822
829
|
outsideCarrier[declaration.ID] = appendUniqueString(
|
|
823
830
|
outsideCarrier[declaration.ID],
|
|
824
831
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -827,6 +834,7 @@ func evaluateEvidenceGraph(
|
|
|
827
834
|
continue
|
|
828
835
|
}
|
|
829
836
|
if !declarationEligibleForClaim(declaration, state.Spec) {
|
|
837
|
+
outOfScopeLevels[declaration.ID] = max(outOfScopeLevels[declaration.ID], severity)
|
|
830
838
|
outOfScope[declaration.ID] = appendUniqueString(
|
|
831
839
|
outOfScope[declaration.ID],
|
|
832
840
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -849,8 +857,8 @@ func evaluateEvidenceGraph(
|
|
|
849
857
|
continue
|
|
850
858
|
}
|
|
851
859
|
if declaration.Tag == tagExclude && reference.Spec.Policy.NoExclude {
|
|
852
|
-
problems =
|
|
853
|
-
|
|
860
|
+
problems = problems.add(
|
|
861
|
+
severity,
|
|
854
862
|
"Forbidden @evidenceExclude for '"+scopesByID[scopeID].Target+"' at "+declaration.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": noEvidenceExclude requires positive @evidence for this reference. Remove the exclusion and cite the target from a selected "+string(state.Spec.Type)+" host that answers for it."+untrueTagWarning,
|
|
855
863
|
)
|
|
856
864
|
continue
|
|
@@ -889,6 +897,7 @@ func evaluateEvidenceGraph(
|
|
|
889
897
|
// answer, and a hard diagnostic here left that valid configuration
|
|
890
898
|
// no placement to exist in. The end-of-run block reports the tag
|
|
891
899
|
// once nothing has consumed it.
|
|
900
|
+
unhostedLevels[declaration.ID] = max(unhostedLevels[declaration.ID], severity)
|
|
892
901
|
unhosted[declaration.ID] = appendUniqueString(
|
|
893
902
|
unhosted[declaration.ID],
|
|
894
903
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -910,8 +919,8 @@ func evaluateEvidenceGraph(
|
|
|
910
919
|
for _, unit := range covered {
|
|
911
920
|
targets = append(targets, "'"+unit.Target+"'")
|
|
912
921
|
}
|
|
913
|
-
problems =
|
|
914
|
-
|
|
922
|
+
problems = problems.add(
|
|
923
|
+
severity,
|
|
915
924
|
"Aggregate @evidence target '"+scopesByID[scopeID].Target+"' at "+declaration.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": this reference is a checklist, so a citation answers for the item it names, and this target names a scope containing "+decimal(len(covered))+" item(s) ("+strings.Join(targets, ", ")+") rather than one of them. Cite each item this host answers for, or write @evidenceExclude on this scope when none of it applies here."+untrueTagWarning,
|
|
916
925
|
)
|
|
917
926
|
for _, unit := range covered {
|
|
@@ -936,7 +945,7 @@ func evaluateEvidenceGraph(
|
|
|
936
945
|
if reviewLedgerForClaim == nil {
|
|
937
946
|
reviewLedgerForClaim = newReviewLedger(state.Reviews)
|
|
938
947
|
}
|
|
939
|
-
problems =
|
|
948
|
+
problems = problems.add(severity, reviewProblems(
|
|
940
949
|
declaration,
|
|
941
950
|
scopesByID[scopeID],
|
|
942
951
|
reference,
|
|
@@ -952,8 +961,8 @@ func evaluateEvidenceGraph(
|
|
|
952
961
|
evidenceByHostAndScope[declaration.HostID] = byScope
|
|
953
962
|
}
|
|
954
963
|
if first := byScope[scopeID]; first != nil {
|
|
955
|
-
problems =
|
|
956
|
-
|
|
964
|
+
problems = problems.add(
|
|
965
|
+
severity,
|
|
957
966
|
"Duplicate @evidence for '"+scopesByID[scopeID].Target+"' on the same host at "+declaration.location()+"; first declared at "+first.location()+".",
|
|
958
967
|
)
|
|
959
968
|
} else {
|
|
@@ -1060,14 +1069,14 @@ func evaluateEvidenceGraph(
|
|
|
1060
1069
|
evidence = conflictingDeclaration
|
|
1061
1070
|
exclusion = declaration
|
|
1062
1071
|
}
|
|
1063
|
-
problems =
|
|
1064
|
-
|
|
1072
|
+
problems = problems.add(
|
|
1073
|
+
severity,
|
|
1065
1074
|
"Conflicting acknowledgements for '"+conflictingUnit.Target+"' in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": @evidence at "+evidence.location()+" overlaps @evidenceExclude at "+exclusion.location()+". Delete whichever is untrue of this host."+untrueTagWarning,
|
|
1066
1075
|
)
|
|
1067
1076
|
}
|
|
1068
1077
|
if duplicateExclusionUnit != nil {
|
|
1069
|
-
problems =
|
|
1070
|
-
|
|
1078
|
+
problems = problems.add(
|
|
1079
|
+
severity,
|
|
1071
1080
|
"Duplicate @evidenceExclude for '"+duplicateExclusionUnit.Target+"' in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": exclusion at "+declaration.location()+" overlaps exclusion at "+firstExclusion.location()+".",
|
|
1072
1081
|
)
|
|
1073
1082
|
}
|
|
@@ -1084,8 +1093,8 @@ func evaluateEvidenceGraph(
|
|
|
1084
1093
|
if count == 1 {
|
|
1085
1094
|
continue
|
|
1086
1095
|
}
|
|
1087
|
-
problems =
|
|
1088
|
-
|
|
1096
|
+
problems = problems.add(
|
|
1097
|
+
severity,
|
|
1089
1098
|
"Evidence host "+host.Readable+" at "+host.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+" cites "+decimal(count)+" distinct selected evidence unit(s); singleEvidencePerSymbol requires exactly 1. Split this host so each unit has one that owns it, or keep positive @evidence citations on this semantic host to exactly one distinct unit.",
|
|
1090
1099
|
)
|
|
1091
1100
|
}
|
|
@@ -1116,8 +1125,8 @@ func evaluateEvidenceGraph(
|
|
|
1116
1125
|
if reference.Spec.Policy.NoExclude {
|
|
1117
1126
|
repair = "Do what each item requires and cite it with @evidence on this host; this reference forbids @evidenceExclude." + untrueTagWarning
|
|
1118
1127
|
}
|
|
1119
|
-
problems =
|
|
1120
|
-
|
|
1128
|
+
problems = problems.add(
|
|
1129
|
+
severity,
|
|
1121
1130
|
"Evidence host "+host.Readable+" at "+host.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+" has not acknowledged "+decimal(len(missing))+" of "+decimal(len(reference.Units))+" checklist item(s): "+strings.Join(missing, ", ")+". "+repair,
|
|
1122
1131
|
)
|
|
1123
1132
|
}
|
|
@@ -1131,8 +1140,8 @@ func evaluateEvidenceGraph(
|
|
|
1131
1140
|
if reference.Spec.Policy.NoExclude {
|
|
1132
1141
|
repair = "Cite the artifact that answers for this unit with @evidence on a selected " + string(state.Spec.Type) + " host, building that artifact first when none does; this reference forbids @evidenceExclude." + untrueTagWarning
|
|
1133
1142
|
}
|
|
1134
|
-
problems =
|
|
1135
|
-
|
|
1143
|
+
problems = problems.add(
|
|
1144
|
+
severity,
|
|
1136
1145
|
"Missing acknowledgement for '"+unit.Target+"' ("+unit.Readable+" at "+unit.location()+") in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+". "+repair,
|
|
1137
1146
|
)
|
|
1138
1147
|
}
|
|
@@ -1140,14 +1149,15 @@ func evaluateEvidenceGraph(
|
|
|
1140
1149
|
if !unique || hostCount <= 1 {
|
|
1141
1150
|
continue
|
|
1142
1151
|
}
|
|
1143
|
-
problems =
|
|
1144
|
-
|
|
1152
|
+
problems = problems.add(
|
|
1153
|
+
severity,
|
|
1145
1154
|
"Evidence unit '"+unit.Target+"' ("+unit.Readable+" at "+unit.location()+") in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+" has "+decimal(hostCount)+" distinct positive evidence host(s); uniqueEvidence allows at most 1. Keep the one selected "+string(state.Spec.Type)+" host that owns this unit and remove the other citation(s).",
|
|
1146
1155
|
)
|
|
1147
1156
|
}
|
|
1148
1157
|
}
|
|
1149
1158
|
}
|
|
1150
1159
|
for _, id := range declarationIDs {
|
|
1160
|
+
severity := ownerSeverity(owners[id])
|
|
1151
1161
|
if resolved[id] == "" {
|
|
1152
1162
|
continue
|
|
1153
1163
|
}
|
|
@@ -1161,8 +1171,8 @@ func evaluateEvidenceGraph(
|
|
|
1161
1171
|
// withholds its own.
|
|
1162
1172
|
if obligations := unhosted[id]; len(obligations) != 0 &&
|
|
1163
1173
|
!answers[id] && !uncertain[id] {
|
|
1164
|
-
problems =
|
|
1165
|
-
|
|
1174
|
+
problems = problems.add(
|
|
1175
|
+
unhostedLevels[id],
|
|
1166
1176
|
"Unhosted @"+string(declaration.Tag)+" at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': a checklist acknowledgement answers for one selected host of its claim, and this declaration sits on no selected host and discharges no other obligation. Move the tag onto a host of a selected kind ("+unhostedSelections[id].names()+") in a claim that owes it."+untrueTagWarning,
|
|
1167
1177
|
)
|
|
1168
1178
|
}
|
|
@@ -1171,8 +1181,8 @@ func evaluateEvidenceGraph(
|
|
|
1171
1181
|
}
|
|
1172
1182
|
context := declarationObligationContext(owners[id])
|
|
1173
1183
|
if obligations := outsideCarrier[id]; len(obligations) != 0 {
|
|
1174
|
-
problems =
|
|
1175
|
-
|
|
1184
|
+
problems = problems.add(
|
|
1185
|
+
outsideCarrierLevels[id],
|
|
1176
1186
|
"Misplaced @evidenceExclude at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': evidenceExcludeCarriers confines this claim's exclusions to "+outsideCarrierGlobs[id]+". Move the tag there, or delete it and build what the target requires of this claim."+untrueTagWarning,
|
|
1177
1187
|
)
|
|
1178
1188
|
continue
|
|
@@ -1183,14 +1193,14 @@ func evaluateEvidenceGraph(
|
|
|
1183
1193
|
host = "unsupported or non-exported declaration"
|
|
1184
1194
|
}
|
|
1185
1195
|
if declaration.Tag == tagExclude {
|
|
1186
|
-
problems =
|
|
1187
|
-
|
|
1196
|
+
problems = problems.add(
|
|
1197
|
+
outOfScopeLevels[id],
|
|
1188
1198
|
"Out-of-scope @evidenceExclude carrier at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': '"+host+"' is not an eligible exclusion carrier in these matching claim files. Move the exclusion to a supported public export or selected declaration host, or use a top-level unattached Prisma documentation comment.",
|
|
1189
1199
|
)
|
|
1190
1200
|
continue
|
|
1191
1201
|
}
|
|
1192
|
-
problems =
|
|
1193
|
-
|
|
1202
|
+
problems = problems.add(
|
|
1203
|
+
outOfScopeLevels[id],
|
|
1194
1204
|
"Out-of-scope @"+string(declaration.Tag)+" host at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': host kind '"+host+"' is not selected ("+outOfScopeSelections[id].names()+") by any of these claim obligations. Move the declaration to a selected host, or widen the claim symbol selector only when it genuinely owns this target."+untrueTagWarning,
|
|
1195
1205
|
)
|
|
1196
1206
|
continue
|
|
@@ -1201,8 +1211,8 @@ func evaluateEvidenceGraph(
|
|
|
1201
1211
|
// derive a second claim from an incomplete graph.
|
|
1202
1212
|
continue
|
|
1203
1213
|
}
|
|
1204
|
-
problems =
|
|
1205
|
-
|
|
1214
|
+
problems = problems.add(
|
|
1215
|
+
severity,
|
|
1206
1216
|
"Non-participating @"+string(declaration.Tag)+" target '"+displayTarget(declaration.Target)+"' at "+declaration.location()+" for "+context+": the target resolves, but none of this declaration's configured references selects it. Correct the target or reference, or move the tag to an eligible host or exclusion carrier in the claim that owes it; a resolving tag must discharge at least one obligation."+untrueTagWarning,
|
|
1207
1217
|
)
|
|
1208
1218
|
}
|
package/native/markdown.go
CHANGED
|
@@ -23,9 +23,9 @@ var explicitAnchorPattern = regexp.MustCompile(`\s*\{#([A-Za-z0-9][A-Za-z0-9._:-
|
|
|
23
23
|
func loadMarkdownInventories(
|
|
24
24
|
root string,
|
|
25
25
|
config graphConfig,
|
|
26
|
-
) (map[string]*artifactInventory,
|
|
26
|
+
) (map[string]*artifactInventory, graphDiagnostics) {
|
|
27
27
|
inventories := map[string]*artifactInventory{}
|
|
28
|
-
problems :=
|
|
28
|
+
problems := graphDiagnostics{}
|
|
29
29
|
for _, base := range configuredBases(config, artifactMarkdown) {
|
|
30
30
|
problems = append(
|
|
31
31
|
problems,
|
|
@@ -39,16 +39,17 @@ func loadMarkdownBase(
|
|
|
39
39
|
base populationBase,
|
|
40
40
|
config graphConfig,
|
|
41
41
|
inventories map[string]*artifactInventory,
|
|
42
|
-
)
|
|
43
|
-
problems :=
|
|
42
|
+
) graphDiagnostics {
|
|
43
|
+
problems := graphDiagnostics{}
|
|
44
|
+
severity := populationSeverity(config, artifactMarkdown, base, "", "*", false)
|
|
44
45
|
if problem := baseDirectoryProblem(base, artifactMarkdown); problem != "" {
|
|
45
46
|
recordPopulationFailure(inventories, artifactMarkdown, base)
|
|
46
|
-
return
|
|
47
|
+
return problems.add(severity, problem)
|
|
47
48
|
}
|
|
48
49
|
from, resolved := resolvedBaseDirectory(base)
|
|
49
50
|
if !resolved {
|
|
50
51
|
recordPopulationFailure(inventories, artifactMarkdown, base)
|
|
51
|
-
return
|
|
52
|
+
return problems.add(severity, unresolvedBaseProblem(base, artifactMarkdown))
|
|
52
53
|
}
|
|
53
54
|
err := filepath.WalkDir(from, func(current string, entry fs.DirEntry, walkErr error) error {
|
|
54
55
|
if walkErr != nil {
|
|
@@ -82,7 +83,8 @@ func loadMarkdownBase(
|
|
|
82
83
|
)
|
|
83
84
|
if relevant {
|
|
84
85
|
recordPopulationFailure(inventories, artifactMarkdown, base)
|
|
85
|
-
|
|
86
|
+
relative, _ := relativeProjectPath(from, current)
|
|
87
|
+
problems = problems.add(populationSeverity(config, artifactMarkdown, base, relative, "*", true), problem)
|
|
86
88
|
}
|
|
87
89
|
// `WalkDir` passes a nil entry only for its root, which the guard above
|
|
88
90
|
// answers, so this error belongs to a directory whose listing failed and
|
|
@@ -105,6 +107,7 @@ func loadMarkdownBase(
|
|
|
105
107
|
if !matchesConfiguredMarkdownFile(config, base, relative) {
|
|
106
108
|
return nil
|
|
107
109
|
}
|
|
110
|
+
severity := populationSeverity(config, artifactMarkdown, base, relative, "*", false)
|
|
108
111
|
address := base.addressOf(relative)
|
|
109
112
|
content, readErr := os.ReadFile(current)
|
|
110
113
|
if readErr != nil {
|
|
@@ -113,26 +116,29 @@ func loadMarkdownBase(
|
|
|
113
116
|
Type: artifactMarkdown,
|
|
114
117
|
LoadFailed: true,
|
|
115
118
|
}
|
|
116
|
-
problems =
|
|
119
|
+
problems = problems.add(
|
|
120
|
+
severity,
|
|
121
|
+
"Evidence graph could not read Markdown file '"+address.Display+"': "+causeText(readErr)+". Fix filesystem access or exclude the file from configured globs.",
|
|
122
|
+
)
|
|
117
123
|
return nil
|
|
118
124
|
}
|
|
119
125
|
inventory, _ := scanMarkdownInventory(address, string(content))
|
|
120
126
|
inventories[address.Key] = inventory
|
|
121
127
|
for _, inventoryProblem := range inventory.Problems {
|
|
122
128
|
if selectedByMarkdownPopulation(config, base, relative, inventoryProblem.Symbol) {
|
|
123
|
-
problems =
|
|
129
|
+
problems = problems.add(populationSeverity(config, artifactMarkdown, base, relative, inventoryProblem.Symbol, false), inventoryProblem.Message)
|
|
124
130
|
}
|
|
125
131
|
}
|
|
126
132
|
// An unreadable tag is not a health question and not a symbol question
|
|
127
133
|
// either: the file loaded, its units are complete, and the tag reaches no
|
|
128
134
|
// host whichever symbol a reference selects. The walk already refuses a
|
|
129
135
|
// path no configured glob takes, so reaching here is enough to report.
|
|
130
|
-
problems =
|
|
136
|
+
problems = problems.add(severity, inventory.Unreadable...)
|
|
131
137
|
return nil
|
|
132
138
|
})
|
|
133
139
|
if err != nil {
|
|
134
140
|
recordPopulationFailure(inventories, artifactMarkdown, base)
|
|
135
|
-
problems =
|
|
141
|
+
problems = problems.add(severity, unlistableBaseProblem(base, "Markdown", err))
|
|
136
142
|
}
|
|
137
143
|
return problems
|
|
138
144
|
}
|
package/native/model.go
CHANGED
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"strings"
|
|
6
6
|
|
|
7
7
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
const graphRuleName = "evidence/graph"
|
|
@@ -57,6 +58,8 @@ type graphConfig struct {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
type claimSpec struct {
|
|
61
|
+
Severity *rule.Severity
|
|
62
|
+
Level rule.Severity
|
|
60
63
|
Index int
|
|
61
64
|
Type artifactKind
|
|
62
65
|
Name string
|
|
@@ -84,13 +87,15 @@ type claimSpec struct {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
type referenceSpec struct {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
Severity *rule.Severity
|
|
91
|
+
Level rule.Severity
|
|
92
|
+
Index int
|
|
93
|
+
Type artifactKind
|
|
94
|
+
Policy referencePolicy
|
|
95
|
+
Root string
|
|
96
|
+
Base populationBase
|
|
97
|
+
Files globSet
|
|
98
|
+
Source string
|
|
94
99
|
// Package moves the base that Files resolves against from the project to an
|
|
95
100
|
// installed package. With no globs it also becomes the selection itself: the
|
|
96
101
|
// package's declaration entry defines the population by reachability, while
|
package/native/prisma.go
CHANGED
|
@@ -12,6 +12,8 @@ import (
|
|
|
12
12
|
"sort"
|
|
13
13
|
"strings"
|
|
14
14
|
"time"
|
|
15
|
+
|
|
16
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
15
17
|
)
|
|
16
18
|
|
|
17
19
|
const prismaBridgeTimeout = 60 * time.Second
|
|
@@ -127,7 +129,7 @@ type prismaField struct {
|
|
|
127
129
|
func loadPrismaInventories(
|
|
128
130
|
root string,
|
|
129
131
|
config graphConfig,
|
|
130
|
-
) (map[string]*artifactInventory,
|
|
132
|
+
) (map[string]*artifactInventory, graphDiagnostics) {
|
|
131
133
|
// A graph that names no Prisma glob never pays for one. The walk below is
|
|
132
134
|
// cheap on such a project — every directory fails the descendant test and is
|
|
133
135
|
// skipped at once — but "cheap" is still a full listing of the project root
|
|
@@ -160,27 +162,28 @@ func loadPrismaInventories(
|
|
|
160
162
|
// ten-model schema and a two-hundred-model one pay nearly the same. A
|
|
161
163
|
// resident host repeats this every cycle, so an unchanged schema would
|
|
162
164
|
// otherwise be re-parsed on every TypeScript keystroke that rebuilds.
|
|
165
|
+
severity := prismaSetSeverity(config, inventories)
|
|
163
166
|
digest := prismaContentDigest(root, set.Sources)
|
|
164
167
|
if outcome, hit := prismaSchemas.lookup(digest); hit {
|
|
165
168
|
return inventories, append(
|
|
166
169
|
problems,
|
|
167
|
-
prismaUnitsFromOutcome(root, set, inventories, outcome)...,
|
|
170
|
+
prismaUnitsFromOutcome(root, set, inventories, outcome, config)...,
|
|
168
171
|
)
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
result, err := normalizePrismaSet(root, set.Sources)
|
|
172
175
|
if err != nil {
|
|
173
176
|
message := "Evidence graph could not run its Prisma schema loader: " + causeText(err) + ". Prisma references require Node.js and a resolvable @prisma/prisma-schema-wasm."
|
|
174
|
-
return inventories,
|
|
177
|
+
return inventories, problems.add(severity, failPrismaSet(inventories, set, message))
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
outcome, problem := prismaOutcomeOf(result)
|
|
178
181
|
if problem != "" {
|
|
179
|
-
return inventories,
|
|
182
|
+
return inventories, problems.add(severity, failPrismaSet(inventories, set, problem))
|
|
180
183
|
}
|
|
181
184
|
problems = append(
|
|
182
185
|
problems,
|
|
183
|
-
prismaUnitsFromOutcome(root, set, inventories, outcome)...,
|
|
186
|
+
prismaUnitsFromOutcome(root, set, inventories, outcome, config)...,
|
|
184
187
|
)
|
|
185
188
|
rememberPrismaSchema(outcome.digest, outcome)
|
|
186
189
|
return inventories, problems
|
|
@@ -223,27 +226,28 @@ func prismaOutcomeOf(result prismaNormalizationResult) (prismaSetOutcome, string
|
|
|
223
226
|
// Order is the digest's, so it must not depend on filesystem enumeration.
|
|
224
227
|
func configuredPrismaAddresses(
|
|
225
228
|
config graphConfig,
|
|
226
|
-
) ([]artifactAddress,
|
|
229
|
+
) ([]artifactAddress, graphDiagnostics) {
|
|
227
230
|
addresses, _, problems := configuredPrismaAddressesWithHealth(config)
|
|
228
231
|
return addresses, problems
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
func configuredPrismaAddressesWithHealth(
|
|
232
235
|
config graphConfig,
|
|
233
|
-
) ([]artifactAddress, []populationBase,
|
|
236
|
+
) ([]artifactAddress, []populationBase, graphDiagnostics) {
|
|
234
237
|
addresses := []artifactAddress{}
|
|
235
238
|
failedBases := []populationBase{}
|
|
236
|
-
problems :=
|
|
239
|
+
problems := graphDiagnostics{}
|
|
237
240
|
for _, base := range configuredBases(config, artifactPrisma) {
|
|
241
|
+
severity := populationSeverity(config, artifactPrisma, base, "", "*", false)
|
|
238
242
|
if problem := baseDirectoryProblem(base, artifactPrisma); problem != "" {
|
|
239
|
-
problems =
|
|
243
|
+
problems = problems.add(severity, problem)
|
|
240
244
|
failedBases = append(failedBases, base)
|
|
241
245
|
continue
|
|
242
246
|
}
|
|
243
247
|
baseFailed := false
|
|
244
248
|
from, resolved := resolvedBaseDirectory(base)
|
|
245
249
|
if !resolved {
|
|
246
|
-
problems =
|
|
250
|
+
problems = problems.add(severity, unresolvedBaseProblem(base, artifactPrisma))
|
|
247
251
|
failedBases = append(failedBases, base)
|
|
248
252
|
continue
|
|
249
253
|
}
|
|
@@ -272,7 +276,8 @@ func configuredPrismaAddressesWithHealth(
|
|
|
272
276
|
)
|
|
273
277
|
if relevant {
|
|
274
278
|
baseFailed = true
|
|
275
|
-
|
|
279
|
+
relative, _ := relativeProjectPath(from, current)
|
|
280
|
+
problems = problems.add(populationSeverity(config, artifactPrisma, base, relative, "*", true), problem)
|
|
276
281
|
}
|
|
277
282
|
return filepath.SkipDir
|
|
278
283
|
}
|
|
@@ -295,7 +300,7 @@ func configuredPrismaAddressesWithHealth(
|
|
|
295
300
|
})
|
|
296
301
|
if err != nil {
|
|
297
302
|
baseFailed = true
|
|
298
|
-
problems =
|
|
303
|
+
problems = problems.add(severity, unlistableBaseProblem(base, "Prisma", err))
|
|
299
304
|
}
|
|
300
305
|
if baseFailed {
|
|
301
306
|
failedBases = append(failedBases, base)
|
|
@@ -647,15 +652,17 @@ func prismaDeclarationsFromComments(
|
|
|
647
652
|
comments []prismaCommentRun,
|
|
648
653
|
hosts map[string]*evidenceUnit,
|
|
649
654
|
inventories map[string][]*artifactInventory,
|
|
650
|
-
|
|
651
|
-
|
|
655
|
+
levels map[string]rule.Severity,
|
|
656
|
+
) graphDiagnostics {
|
|
657
|
+
problems := graphDiagnostics{}
|
|
652
658
|
sequence := 0
|
|
653
659
|
for _, run := range comments {
|
|
660
|
+
severity := levels[run.Path]
|
|
654
661
|
location := run.Path + ":" + decimal(run.Line)
|
|
655
662
|
if run.Form != prismaDocComment {
|
|
656
663
|
if prismaCommentCarriesTag(run.Body) {
|
|
657
|
-
problems =
|
|
658
|
-
|
|
664
|
+
problems = problems.add(
|
|
665
|
+
severity,
|
|
659
666
|
"Evidence tag at "+location+" sits in a '//' line comment, which Prisma discards rather than attaching to the declaration below it. Write the citation on a '///' or '/* */' documentation comment directly above the model, column, or relation it grounds.",
|
|
660
667
|
)
|
|
661
668
|
}
|
|
@@ -667,8 +674,8 @@ func prismaDeclarationsFromComments(
|
|
|
667
674
|
continue
|
|
668
675
|
}
|
|
669
676
|
for _, offset := range prismaBuriedTagLines(run.Body) {
|
|
670
|
-
problems =
|
|
671
|
-
|
|
677
|
+
problems = problems.add(
|
|
678
|
+
severity,
|
|
672
679
|
"Evidence tag at "+run.Path+":"+decimal(run.Line+offset)+" is buried behind an extra slash. Prisma reads a fourth slash as content, so the tag no longer opens its documentation line and nothing resolves it. Write exactly three slashes.",
|
|
673
680
|
)
|
|
674
681
|
}
|
|
@@ -676,8 +683,8 @@ func prismaDeclarationsFromComments(
|
|
|
676
683
|
sequence++
|
|
677
684
|
line := run.Line + parsed.LineOffset
|
|
678
685
|
if parsed.Tag == tagEvidence {
|
|
679
|
-
problems =
|
|
680
|
-
|
|
686
|
+
problems = problems.add(
|
|
687
|
+
severity,
|
|
681
688
|
"@evidence at "+run.Path+":"+decimal(line)+" is on a file-level Prisma exclusion carrier. Move ownership evidence directly above the selected model, column, or relation it grounds; only @evidenceExclude may be unattached at file level.",
|
|
682
689
|
)
|
|
683
690
|
continue
|
|
@@ -724,8 +731,8 @@ func prismaDeclarationsFromComments(
|
|
|
724
731
|
}
|
|
725
732
|
if run.Key == "" {
|
|
726
733
|
if prismaCommentCarriesTag(run.Body) {
|
|
727
|
-
problems =
|
|
728
|
-
|
|
734
|
+
problems = problems.add(
|
|
735
|
+
severity,
|
|
729
736
|
"Evidence tag at "+location+" documents no declaration. Prisma attaches a '///' comment to the declaration that immediately follows it, so a blank line before a top-level block, a block attribute, or a closing brace leaves the comment documenting nothing. Move the citation directly above the model, column, or relation it grounds.",
|
|
730
737
|
)
|
|
731
738
|
}
|
|
@@ -734,8 +741,8 @@ func prismaDeclarationsFromComments(
|
|
|
734
741
|
host := hosts[run.Key]
|
|
735
742
|
if host == nil {
|
|
736
743
|
if prismaCommentCarriesTag(run.Body) {
|
|
737
|
-
problems =
|
|
738
|
-
|
|
744
|
+
problems = problems.add(
|
|
745
|
+
severity,
|
|
739
746
|
"Evidence tag at "+location+" documents '"+run.Key+"', which is not a model, column, or relation. A model or a view and their members host an evidence citation; an enum, a composite type, and a datasource or generator setting do not.",
|
|
740
747
|
)
|
|
741
748
|
}
|
|
@@ -746,8 +753,8 @@ func prismaDeclarationsFromComments(
|
|
|
746
753
|
continue
|
|
747
754
|
}
|
|
748
755
|
for _, offset := range prismaBuriedTagLines(run.Body) {
|
|
749
|
-
problems =
|
|
750
|
-
|
|
756
|
+
problems = problems.add(
|
|
757
|
+
severity,
|
|
751
758
|
"Evidence tag at "+run.Path+":"+decimal(run.Line+offset)+" is buried behind an extra slash. Prisma reads a fourth slash as content, so the tag no longer opens its documentation line and nothing resolves it. Write exactly three slashes.",
|
|
752
759
|
)
|
|
753
760
|
}
|
package/native/prisma_cache.go
CHANGED
|
@@ -6,6 +6,8 @@ import (
|
|
|
6
6
|
"os"
|
|
7
7
|
"strings"
|
|
8
8
|
"sync"
|
|
9
|
+
|
|
10
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
9
11
|
)
|
|
10
12
|
|
|
11
13
|
// prismaCacheLimit bounds the cache so a resident host cannot grow without end.
|
|
@@ -176,13 +178,14 @@ func prismaUnitsFromOutcome(
|
|
|
176
178
|
set prismaSourceSet,
|
|
177
179
|
inventories map[string]*artifactInventory,
|
|
178
180
|
outcome prismaSetOutcome,
|
|
179
|
-
|
|
181
|
+
config graphConfig,
|
|
182
|
+
) graphDiagnostics {
|
|
180
183
|
if outcome.Rejected {
|
|
181
|
-
return
|
|
184
|
+
return graphDiagnostics{}.add(prismaSetSeverity(config, inventories), failPrismaSet(
|
|
182
185
|
inventories,
|
|
183
186
|
set,
|
|
184
187
|
prismaNormalizationFailure(outcome.Problem),
|
|
185
|
-
)
|
|
188
|
+
))
|
|
186
189
|
}
|
|
187
190
|
locations, comments := locatePrismaDeclarations(root, set.Sources)
|
|
188
191
|
fallback := ""
|
|
@@ -230,7 +233,16 @@ func prismaUnitsFromOutcome(
|
|
|
230
233
|
for _, inventory := range inventories {
|
|
231
234
|
sortUnits(inventory.Units)
|
|
232
235
|
}
|
|
233
|
-
|
|
236
|
+
levels := map[string]rule.Severity{}
|
|
237
|
+
for key, inventory := range inventories {
|
|
238
|
+
levels[inventory.Path] = max(levels[inventory.Path], inventorySeverity(config, artifactPrisma, key, "*"))
|
|
239
|
+
}
|
|
240
|
+
for _, source := range set.Sources {
|
|
241
|
+
for _, spelling := range set.Spellings[source] {
|
|
242
|
+
levels[source] = max(levels[source], levels[spelling])
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return prismaDeclarationsFromComments(comments, hosts, indexed, levels)
|
|
234
246
|
}
|
|
235
247
|
|
|
236
248
|
// joinPrismaIdentity renders a unit's identity the way the locator keys one.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
package evidence
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"sort"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func decodeGraphSeverity(raw json.RawMessage, path string) (*rule.Severity, []string) {
|
|
11
|
+
if len(raw) == 0 {
|
|
12
|
+
return nil, nil
|
|
13
|
+
}
|
|
14
|
+
var severity rule.Severity
|
|
15
|
+
var value any
|
|
16
|
+
if err := json.Unmarshal(raw, &value); err != nil {
|
|
17
|
+
return nil, []string{"Invalid evidence/graph configuration at " + path + ": expected a lint severity value."}
|
|
18
|
+
}
|
|
19
|
+
switch value {
|
|
20
|
+
case "off", float64(0):
|
|
21
|
+
severity = rule.SeverityOff
|
|
22
|
+
case "warning", "warn", float64(1):
|
|
23
|
+
severity = rule.SeverityWarn
|
|
24
|
+
case "error", float64(2):
|
|
25
|
+
severity = rule.SeverityError
|
|
26
|
+
default:
|
|
27
|
+
return nil, []string{"Invalid evidence/graph configuration at " + path + ": expected 'off', 'warning', 'warn', 'error', 0, 1, or 2; omit severity or use undefined to inherit the enclosing level."}
|
|
28
|
+
}
|
|
29
|
+
return &severity, nil
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Resolve inheritance once, before populations are loaded. A nil severity is
|
|
33
|
+
// unset; an explicit zero disables its population.
|
|
34
|
+
func resolveGraphSeverities(config *graphConfig, severity rule.Severity) {
|
|
35
|
+
for i := range config.Claims {
|
|
36
|
+
claim := &config.Claims[i]
|
|
37
|
+
claim.Level = severity
|
|
38
|
+
if claim.Severity != nil {
|
|
39
|
+
claim.Level = *claim.Severity
|
|
40
|
+
}
|
|
41
|
+
for j := range claim.References {
|
|
42
|
+
reference := &claim.References[j]
|
|
43
|
+
reference.Level = claim.Level
|
|
44
|
+
if reference.Severity != nil {
|
|
45
|
+
reference.Level = *reference.Severity
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type graphDiagnostic struct {
|
|
52
|
+
Message string
|
|
53
|
+
Severity rule.Severity
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type graphDiagnostics []graphDiagnostic
|
|
57
|
+
|
|
58
|
+
func (problems graphDiagnostics) add(severity rule.Severity, messages ...string) graphDiagnostics {
|
|
59
|
+
for _, message := range messages {
|
|
60
|
+
problems = append(problems, graphDiagnostic{Message: message, Severity: severity})
|
|
61
|
+
}
|
|
62
|
+
return problems
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func (problems graphDiagnostics) report(ctx *rule.ProjectContext) {
|
|
66
|
+
levels := map[string]rule.Severity{}
|
|
67
|
+
for _, problem := range problems {
|
|
68
|
+
levels[problem.Message] = max(levels[problem.Message], problem.Severity)
|
|
69
|
+
}
|
|
70
|
+
messages := make([]string, 0, len(levels))
|
|
71
|
+
for message := range levels {
|
|
72
|
+
messages = append(messages, message)
|
|
73
|
+
}
|
|
74
|
+
sort.Strings(messages)
|
|
75
|
+
for _, message := range messages {
|
|
76
|
+
ctx.ReportSeverity(levels[message], message)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A shared source problem belongs to every population that reads it. Keep the
|
|
81
|
+
// strongest owning level without pooling independent coverage obligations.
|
|
82
|
+
func populationSeverity(config graphConfig, kind artifactKind, base populationBase, path string, symbol string, descendants bool) rule.Severity {
|
|
83
|
+
var severity rule.Severity
|
|
84
|
+
matches := func(candidate populationBase, files globSet, symbols symbolSet) bool {
|
|
85
|
+
return candidate.Absolute == base.Absolute &&
|
|
86
|
+
(path == "" || files.matches(path) || descendants && files.couldMatchDescendant(path)) &&
|
|
87
|
+
(symbol == "*" || symbols.contains(symbol))
|
|
88
|
+
}
|
|
89
|
+
for _, claim := range config.Claims {
|
|
90
|
+
if claim.Type == kind && matches(claim.Base, claim.Files, claim.Symbols) {
|
|
91
|
+
severity = max(severity, claim.Level)
|
|
92
|
+
}
|
|
93
|
+
for _, reference := range claim.References {
|
|
94
|
+
if reference.Type == kind && reference.Package == "" && matches(reference.Base, reference.Files, reference.Symbols) {
|
|
95
|
+
severity = max(severity, reference.Level)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return severity
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func artifactSeverity(config graphConfig, kind artifactKind) rule.Severity {
|
|
103
|
+
var severity rule.Severity
|
|
104
|
+
for _, claim := range config.Claims {
|
|
105
|
+
if claim.Type == kind {
|
|
106
|
+
severity = max(severity, claim.Level)
|
|
107
|
+
}
|
|
108
|
+
for _, reference := range claim.References {
|
|
109
|
+
if reference.Type == kind {
|
|
110
|
+
severity = max(severity, reference.Level)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return severity
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func swaggerSeverity(config graphConfig, source string) rule.Severity {
|
|
118
|
+
var severity rule.Severity
|
|
119
|
+
for _, claim := range config.Claims {
|
|
120
|
+
for _, reference := range claim.References {
|
|
121
|
+
if reference.Type == artifactSwagger && reference.Source == source {
|
|
122
|
+
severity = max(severity, reference.Level)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return severity
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func ownerSeverity(owners []claimState) rule.Severity {
|
|
130
|
+
var severity rule.Severity
|
|
131
|
+
for _, owner := range owners {
|
|
132
|
+
severity = max(severity, owner.Spec.Level)
|
|
133
|
+
}
|
|
134
|
+
return severity
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
func inventorySeverity(config graphConfig, kind artifactKind, address string, symbol string) rule.Severity {
|
|
138
|
+
var severity rule.Severity
|
|
139
|
+
for _, base := range configuredBases(config, kind) {
|
|
140
|
+
if relative, ok := base.relativeOf(address); ok {
|
|
141
|
+
severity = max(severity, populationSeverity(config, kind, base, relative, symbol, false))
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return severity
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Empty or failed populations contribute no file to the shared Prisma parse.
|
|
148
|
+
func prismaSetSeverity(config graphConfig, inventories map[string]*artifactInventory) rule.Severity {
|
|
149
|
+
var severity rule.Severity
|
|
150
|
+
for address, inventory := range inventories {
|
|
151
|
+
if inventory != nil && inventory.FailureBase == "" {
|
|
152
|
+
severity = max(severity, inventorySeverity(config, artifactPrisma, address, "*"))
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return severity
|
|
156
|
+
}
|
package/native/swagger.go
CHANGED
|
@@ -11,6 +11,8 @@ import (
|
|
|
11
11
|
"sort"
|
|
12
12
|
"strings"
|
|
13
13
|
"time"
|
|
14
|
+
|
|
15
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
14
16
|
)
|
|
15
17
|
|
|
16
18
|
const swaggerBridgeTimeout = 60 * time.Second
|
|
@@ -94,7 +96,7 @@ type swaggerOperation struct {
|
|
|
94
96
|
func loadSwaggerInventories(
|
|
95
97
|
root string,
|
|
96
98
|
config graphConfig,
|
|
97
|
-
) (map[string]*artifactInventory,
|
|
99
|
+
) (map[string]*artifactInventory, graphDiagnostics) {
|
|
98
100
|
sources := configuredSwaggerSources(config)
|
|
99
101
|
inventories := map[string]*artifactInventory{}
|
|
100
102
|
for _, source := range sources {
|
|
@@ -114,15 +116,16 @@ func loadSwaggerInventories(
|
|
|
114
116
|
// TypeScript keystroke that triggers a rebuild.
|
|
115
117
|
digests := swaggerContentDigests(root, sources)
|
|
116
118
|
pending := []string{}
|
|
117
|
-
problems :=
|
|
119
|
+
problems := graphDiagnostics{}
|
|
120
|
+
severity := artifactSeverity(config, artifactSwagger)
|
|
118
121
|
for _, source := range sources {
|
|
119
122
|
outcome, hit := lookupSwaggerDocument(source, digests[source])
|
|
120
123
|
if !hit {
|
|
121
124
|
pending = append(pending, source)
|
|
122
125
|
continue
|
|
123
126
|
}
|
|
124
|
-
problems =
|
|
125
|
-
|
|
127
|
+
problems = problems.add(
|
|
128
|
+
swaggerSeverity(config, source),
|
|
126
129
|
swaggerUnitsFromOutcome(source, inventories[source], outcome)...,
|
|
127
130
|
)
|
|
128
131
|
}
|
|
@@ -140,30 +143,34 @@ func loadSwaggerInventories(
|
|
|
140
143
|
inventoryProblem{Symbol: "operation", Message: message},
|
|
141
144
|
)
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
var pendingSeverity rule.Severity
|
|
147
|
+
for _, source := range pending {
|
|
148
|
+
pendingSeverity = max(pendingSeverity, swaggerSeverity(config, source))
|
|
149
|
+
}
|
|
150
|
+
return inventories, problems.add(pendingSeverity, message)
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
seen := map[string]bool{}
|
|
147
154
|
for _, document := range result.Documents {
|
|
148
155
|
inventory := inventories[document.Source]
|
|
149
156
|
if inventory == nil {
|
|
150
|
-
problems =
|
|
151
|
-
|
|
157
|
+
problems = problems.add(
|
|
158
|
+
severity,
|
|
152
159
|
"Evidence graph Swagger normalizer returned an unconfigured source '"+displaySwaggerSource(document.Source)+"'. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
|
|
153
160
|
)
|
|
154
161
|
continue
|
|
155
162
|
}
|
|
156
163
|
if seen[document.Source] {
|
|
157
|
-
problems =
|
|
158
|
-
|
|
164
|
+
problems = problems.add(
|
|
165
|
+
swaggerSeverity(config, document.Source),
|
|
159
166
|
"Evidence graph Swagger normalizer returned source '"+displaySwaggerSource(document.Source)+"' more than once. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
|
|
160
167
|
)
|
|
161
168
|
continue
|
|
162
169
|
}
|
|
163
170
|
seen[document.Source] = true
|
|
164
171
|
outcome := swaggerDocumentOutcome{Operations: document.Operations}
|
|
165
|
-
problems =
|
|
166
|
-
|
|
172
|
+
problems = problems.add(
|
|
173
|
+
swaggerSeverity(config, document.Source),
|
|
167
174
|
swaggerUnitsFromOutcome(document.Source, inventory, outcome)...,
|
|
168
175
|
)
|
|
169
176
|
rememberSwaggerDocument(document.Source, document.Digest, outcome)
|
|
@@ -171,8 +178,8 @@ func loadSwaggerInventories(
|
|
|
171
178
|
for _, problem := range result.Problems {
|
|
172
179
|
inventory := inventories[problem.Source]
|
|
173
180
|
if inventory == nil {
|
|
174
|
-
problems =
|
|
175
|
-
|
|
181
|
+
problems = problems.add(
|
|
182
|
+
severity,
|
|
176
183
|
"Evidence graph Swagger normalizer rejected an unconfigured source '"+displaySwaggerSource(problem.Source)+"'. Reinstall @ttsc/evidence; the native and JavaScript bridge contracts disagree.",
|
|
177
184
|
)
|
|
178
185
|
continue
|
|
@@ -182,8 +189,8 @@ func loadSwaggerInventories(
|
|
|
182
189
|
Rejected: true,
|
|
183
190
|
Problem: problem.Message,
|
|
184
191
|
}
|
|
185
|
-
problems =
|
|
186
|
-
|
|
192
|
+
problems = problems.add(
|
|
193
|
+
swaggerSeverity(config, problem.Source),
|
|
187
194
|
swaggerUnitsFromOutcome(problem.Source, inventory, outcome)...,
|
|
188
195
|
)
|
|
189
196
|
rememberSwaggerDocument(problem.Source, problem.Digest, outcome)
|
|
@@ -198,7 +205,7 @@ func loadSwaggerInventories(
|
|
|
198
205
|
inventories[source].Problems,
|
|
199
206
|
inventoryProblem{Symbol: "operation", Message: message},
|
|
200
207
|
)
|
|
201
|
-
problems =
|
|
208
|
+
problems = problems.add(swaggerSeverity(config, source), message)
|
|
202
209
|
}
|
|
203
210
|
return inventories, problems
|
|
204
211
|
}
|
package/native/typescript.go
CHANGED
|
@@ -79,8 +79,8 @@ func extendTypeScriptInventories(
|
|
|
79
79
|
func typeScriptBaseProblems(
|
|
80
80
|
config graphConfig,
|
|
81
81
|
inventories map[string]*artifactInventory,
|
|
82
|
-
)
|
|
83
|
-
problems :=
|
|
82
|
+
) graphDiagnostics {
|
|
83
|
+
problems := graphDiagnostics{}
|
|
84
84
|
for _, base := range configuredBases(config, artifactTypeScript) {
|
|
85
85
|
problem := baseDirectoryProblem(base, artifactTypeScript)
|
|
86
86
|
// The stat accepts a chain of links the resolver will not finish, and this
|
|
@@ -111,7 +111,7 @@ func typeScriptBaseProblems(
|
|
|
111
111
|
continue
|
|
112
112
|
}
|
|
113
113
|
recordPopulationFailure(inventories, artifactTypeScript, base)
|
|
114
|
-
problems =
|
|
114
|
+
problems = problems.add(populationSeverity(config, artifactTypeScript, base, "", "*", false), problem)
|
|
115
115
|
}
|
|
116
116
|
return problems
|
|
117
117
|
}
|
package/native/unreadable.go
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
package evidence
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"sort"
|
|
5
4
|
"strings"
|
|
6
5
|
|
|
7
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
@@ -142,14 +141,14 @@ func readableCommentBody(comment string) string {
|
|
|
142
141
|
func unreadableTypeScriptTags(
|
|
143
142
|
inventories map[string]*artifactInventory,
|
|
144
143
|
governed map[string]bool,
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
config graphConfig,
|
|
145
|
+
) graphDiagnostics {
|
|
146
|
+
reported := graphDiagnostics{}
|
|
147
147
|
for address, inventory := range inventories {
|
|
148
|
-
if inventory == nil || !governed[address] {
|
|
148
|
+
if inventory == nil || len(inventory.Unreadable) == 0 || !governed[address] {
|
|
149
149
|
continue
|
|
150
150
|
}
|
|
151
|
-
reported =
|
|
151
|
+
reported = reported.add(inventorySeverity(config, artifactTypeScript, address, "*"), inventory.Unreadable...)
|
|
152
152
|
}
|
|
153
|
-
sort.Strings(reported)
|
|
154
153
|
return reported
|
|
155
154
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttsc/evidence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Evidence-graph lint contributor for @ttsc/lint: declare provenance with a JSDoc @evidence tag and fail the build when it is missing or dangling.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"url": "https://github.com/samchon/ttsc/issues"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@ttsc/lint": "
|
|
45
|
+
"@ttsc/lint": ">0.28.6"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^25.3.0",
|
|
49
49
|
"rimraf": "^6.1.2",
|
|
50
50
|
"typescript": "^7.0.2",
|
|
51
|
-
"
|
|
52
|
-
"ttsc": "0.
|
|
51
|
+
"ttsc": "0.29.0",
|
|
52
|
+
"@ttsc/lint": "0.29.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@prisma/prisma-schema-wasm": "7.10.0-2.d0368e25c88e68caa4d283b5f2d88d1b76ed9356",
|