@ttsc/evidence 0.28.6 → 0.30.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/lib/structures/ITtscEvidenceGraphTypeScriptReference.d.ts +18 -6
- package/lib/typings/TtscEvidenceGraphTypeScriptSymbol.d.ts +11 -12
- package/native/config.go +43 -47
- package/native/declaration.go +50 -1
- package/native/default_exports.go +62 -0
- package/native/export_resolution.go +275 -0
- package/native/exports.go +62 -144
- package/native/file_link.go +370 -0
- package/native/file_link_diagnostics.go +160 -0
- package/native/fingerprint.go +33 -4
- package/native/graph.go +119 -79
- package/native/imports.go +21 -8
- package/native/inputs.go +13 -4
- package/native/loader.go +170 -23
- package/native/markdown.go +17 -11
- package/native/model.go +16 -4
- package/native/prisma.go +33 -26
- package/native/prisma_cache.go +16 -4
- package/native/rooted_typescript.go +116 -0
- package/native/severity.go +156 -0
- package/native/swagger.go +23 -16
- package/native/typescript.go +33 -14
- package/native/typescript_cache.go +5 -4
- package/native/unreadable.go +5 -6
- package/package.json +6 -6
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,18 +373,24 @@ 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
|
}
|
|
376
380
|
if reference.Type == artifactTypeScript {
|
|
381
|
+
if reference.Rooted {
|
|
382
|
+
rootedState, rootedProblems := materializeRootedTypeScriptReference(claim, reference, loader)
|
|
383
|
+
problems = problems.add(severity, rootedProblems...)
|
|
384
|
+
state.References = append(state.References, rootedState)
|
|
385
|
+
continue
|
|
386
|
+
}
|
|
377
387
|
localState, localProblems := materializeLocalTypeScriptReference(
|
|
378
388
|
claim,
|
|
379
389
|
reference,
|
|
380
390
|
referenceInventories,
|
|
381
391
|
loader,
|
|
382
392
|
)
|
|
383
|
-
problems =
|
|
393
|
+
problems = problems.add(severity, localProblems...)
|
|
384
394
|
state.References = append(state.References, localState)
|
|
385
395
|
continue
|
|
386
396
|
}
|
|
@@ -396,13 +406,13 @@ func materializeClaimStates(
|
|
|
396
406
|
}
|
|
397
407
|
if len(referencePaths) == 0 && referenceState.Healthy {
|
|
398
408
|
if reference.Type == artifactSwagger {
|
|
399
|
-
problems =
|
|
400
|
-
|
|
409
|
+
problems = problems.add(
|
|
410
|
+
severity,
|
|
401
411
|
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
412
|
)
|
|
403
413
|
} else {
|
|
404
|
-
problems =
|
|
405
|
-
|
|
414
|
+
problems = problems.add(
|
|
415
|
+
severity,
|
|
406
416
|
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
417
|
)
|
|
408
418
|
}
|
|
@@ -477,8 +487,8 @@ func materializeClaimStates(
|
|
|
477
487
|
len(referenceState.Units) == 0 &&
|
|
478
488
|
referenceState.Healthy &&
|
|
479
489
|
!selectedInventoryProblem {
|
|
480
|
-
problems =
|
|
481
|
-
|
|
490
|
+
problems = problems.add(
|
|
491
|
+
severity,
|
|
482
492
|
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
493
|
)
|
|
484
494
|
}
|
|
@@ -492,8 +502,8 @@ func materializeClaimStates(
|
|
|
492
502
|
func evaluateEvidenceGraph(
|
|
493
503
|
states []claimState,
|
|
494
504
|
loader *typeScriptLoader,
|
|
495
|
-
)
|
|
496
|
-
problems :=
|
|
505
|
+
) graphDiagnostics {
|
|
506
|
+
problems := graphDiagnostics{}
|
|
497
507
|
targets := map[string]map[string]*evidenceUnit{}
|
|
498
508
|
markdownTargets := map[string]map[string]*evidenceUnit{}
|
|
499
509
|
// Scoped targets are keyed by owning file as well as name, which is what
|
|
@@ -603,26 +613,39 @@ func evaluateEvidenceGraph(
|
|
|
603
613
|
sort.Strings(declarationIDs)
|
|
604
614
|
|
|
605
615
|
resolved := map[string]string{}
|
|
616
|
+
fileLinks := indexFileLinkClaims(states, loader)
|
|
606
617
|
for _, id := range declarationIDs {
|
|
618
|
+
severity := ownerSeverity(owners[id])
|
|
607
619
|
declaration := declarations[id]
|
|
608
620
|
context := declarationObligationContext(owners[id])
|
|
609
621
|
if !declaration.valid() {
|
|
610
|
-
problems =
|
|
611
|
-
|
|
622
|
+
problems = problems.add(
|
|
623
|
+
severity,
|
|
612
624
|
"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
625
|
)
|
|
614
626
|
continue
|
|
615
627
|
}
|
|
628
|
+
if isFileLinkTarget(declaration.Target) {
|
|
629
|
+
unitID, problem := resolveFileLinkDeclaration(declaration, owners[id], loader, fileLinks, context)
|
|
630
|
+
if problem != "" {
|
|
631
|
+
problems = problems.add(severity, problem)
|
|
632
|
+
} else {
|
|
633
|
+
resolved[id] = unitID
|
|
634
|
+
}
|
|
635
|
+
continue
|
|
636
|
+
}
|
|
616
637
|
if isInlineLinkTarget(declaration.Target) {
|
|
617
638
|
unitID, problem := resolveInlineLinkDeclaration(
|
|
618
639
|
declaration,
|
|
619
640
|
loader,
|
|
620
641
|
scopedTargets,
|
|
621
642
|
scopedHidden,
|
|
643
|
+
fileLinks,
|
|
644
|
+
states,
|
|
622
645
|
context,
|
|
623
646
|
)
|
|
624
647
|
if problem != "" {
|
|
625
|
-
problems =
|
|
648
|
+
problems = problems.add(severity, problem)
|
|
626
649
|
continue
|
|
627
650
|
}
|
|
628
651
|
resolved[id] = unitID
|
|
@@ -630,8 +653,8 @@ func evaluateEvidenceGraph(
|
|
|
630
653
|
}
|
|
631
654
|
if declaration.Type == artifactTypeScript &&
|
|
632
655
|
looksLikeTypeScriptTarget(declaration.Target, targets, markdownTargets) {
|
|
633
|
-
problems =
|
|
634
|
-
|
|
656
|
+
problems = problems.add(
|
|
657
|
+
severity,
|
|
635
658
|
"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
659
|
)
|
|
637
660
|
continue
|
|
@@ -648,9 +671,9 @@ func evaluateEvidenceGraph(
|
|
|
648
671
|
if declaration.Type != artifactTypeScript {
|
|
649
672
|
addressable, code := splitCodeCandidates(candidates)
|
|
650
673
|
if len(addressable) == 0 && len(code) != 0 {
|
|
651
|
-
problems =
|
|
652
|
-
|
|
653
|
-
"Code evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+":
|
|
674
|
+
problems = problems.add(
|
|
675
|
+
severity,
|
|
676
|
+
"Code evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": an unqualified symbol has no module identity in this artifact. In Markdown, write '@link <file.ts>#<Accessor> <reason>' using a path relative to this document. Otherwise move the citation to a TypeScript claim.",
|
|
654
677
|
)
|
|
655
678
|
continue
|
|
656
679
|
}
|
|
@@ -667,14 +690,11 @@ func evaluateEvidenceGraph(
|
|
|
667
690
|
continue
|
|
668
691
|
}
|
|
669
692
|
if hidden := hiddenTargets[declaration.Target]; hidden != nil {
|
|
670
|
-
problems =
|
|
671
|
-
problems,
|
|
672
|
-
hiddenTargetProblem(declaration, hidden, context),
|
|
673
|
-
)
|
|
693
|
+
problems = problems.add(severity, hiddenTargetProblem(declaration, hidden, context))
|
|
674
694
|
continue
|
|
675
695
|
}
|
|
676
|
-
problems =
|
|
677
|
-
|
|
696
|
+
problems = problems.add(
|
|
697
|
+
severity,
|
|
678
698
|
"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
699
|
)
|
|
680
700
|
case 1:
|
|
@@ -687,8 +707,8 @@ func evaluateEvidenceGraph(
|
|
|
687
707
|
descriptions = append(descriptions, unit.Readable+" at "+unit.location())
|
|
688
708
|
}
|
|
689
709
|
sort.Strings(descriptions)
|
|
690
|
-
problems =
|
|
691
|
-
|
|
710
|
+
problems = problems.add(
|
|
711
|
+
severity,
|
|
692
712
|
"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
713
|
)
|
|
694
714
|
}
|
|
@@ -697,11 +717,13 @@ func evaluateEvidenceGraph(
|
|
|
697
717
|
participates := map[string]bool{}
|
|
698
718
|
uncertain := map[string]bool{}
|
|
699
719
|
outOfScope := map[string][]string{}
|
|
720
|
+
outOfScopeLevels := map[string]rule.Severity{}
|
|
700
721
|
outOfScopeSelections := map[string]symbolSet{}
|
|
701
722
|
// An exclusion outside its claim's declared carriers is a placement
|
|
702
723
|
// finding, not a host-kind one, so it carries its own obligations and the
|
|
703
724
|
// carrier globs its message must name.
|
|
704
725
|
outsideCarrier := map[string][]string{}
|
|
726
|
+
outsideCarrierLevels := map[string]rule.Severity{}
|
|
705
727
|
outsideCarrierGlobs := map[string]string{}
|
|
706
728
|
// A checklist tag speaking for no selected host is a non-participation
|
|
707
729
|
// finding: within its reference it answers nothing, and whether that earns a
|
|
@@ -711,6 +733,7 @@ func evaluateEvidenceGraph(
|
|
|
711
733
|
// elsewhere — so the finding is recorded here and judged after the walk,
|
|
712
734
|
// when `answers` can say whether anything consumed the tag.
|
|
713
735
|
unhosted := map[string][]string{}
|
|
736
|
+
unhostedLevels := map[string]rule.Severity{}
|
|
714
737
|
unhostedSelections := map[string]symbolSet{}
|
|
715
738
|
// answers marks a declaration that wrote at least one acknowledgement
|
|
716
739
|
// ledger, or was refused as an aggregate, which is that citation's own
|
|
@@ -732,6 +755,7 @@ func evaluateEvidenceGraph(
|
|
|
732
755
|
// reviewing reference pays nothing.
|
|
733
756
|
var reviewLedgerForClaim *reviewLedger
|
|
734
757
|
for _, reference := range state.References {
|
|
758
|
+
severity := reference.Spec.Level
|
|
735
759
|
if !reference.Healthy {
|
|
736
760
|
for _, declaration := range state.Declarations {
|
|
737
761
|
uncertain[declaration.ID] = true
|
|
@@ -819,6 +843,7 @@ func evaluateEvidenceGraph(
|
|
|
819
843
|
continue
|
|
820
844
|
}
|
|
821
845
|
if declaration.Tag == tagExclude && state.OutsideCarrier[declaration.ID] {
|
|
846
|
+
outsideCarrierLevels[declaration.ID] = max(outsideCarrierLevels[declaration.ID], severity)
|
|
822
847
|
outsideCarrier[declaration.ID] = appendUniqueString(
|
|
823
848
|
outsideCarrier[declaration.ID],
|
|
824
849
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -827,6 +852,7 @@ func evaluateEvidenceGraph(
|
|
|
827
852
|
continue
|
|
828
853
|
}
|
|
829
854
|
if !declarationEligibleForClaim(declaration, state.Spec) {
|
|
855
|
+
outOfScopeLevels[declaration.ID] = max(outOfScopeLevels[declaration.ID], severity)
|
|
830
856
|
outOfScope[declaration.ID] = appendUniqueString(
|
|
831
857
|
outOfScope[declaration.ID],
|
|
832
858
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -849,8 +875,8 @@ func evaluateEvidenceGraph(
|
|
|
849
875
|
continue
|
|
850
876
|
}
|
|
851
877
|
if declaration.Tag == tagExclude && reference.Spec.Policy.NoExclude {
|
|
852
|
-
problems =
|
|
853
|
-
|
|
878
|
+
problems = problems.add(
|
|
879
|
+
severity,
|
|
854
880
|
"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
881
|
)
|
|
856
882
|
continue
|
|
@@ -889,6 +915,7 @@ func evaluateEvidenceGraph(
|
|
|
889
915
|
// answer, and a hard diagnostic here left that valid configuration
|
|
890
916
|
// no placement to exist in. The end-of-run block reports the tag
|
|
891
917
|
// once nothing has consumed it.
|
|
918
|
+
unhostedLevels[declaration.ID] = max(unhostedLevels[declaration.ID], severity)
|
|
892
919
|
unhosted[declaration.ID] = appendUniqueString(
|
|
893
920
|
unhosted[declaration.ID],
|
|
894
921
|
claimLabel(state.Spec)+" "+referenceLabel(reference.Spec),
|
|
@@ -910,8 +937,8 @@ func evaluateEvidenceGraph(
|
|
|
910
937
|
for _, unit := range covered {
|
|
911
938
|
targets = append(targets, "'"+unit.Target+"'")
|
|
912
939
|
}
|
|
913
|
-
problems =
|
|
914
|
-
|
|
940
|
+
problems = problems.add(
|
|
941
|
+
severity,
|
|
915
942
|
"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
943
|
)
|
|
917
944
|
for _, unit := range covered {
|
|
@@ -926,17 +953,12 @@ func evaluateEvidenceGraph(
|
|
|
926
953
|
// Withdrawn units are absent from Population by construction, and
|
|
927
954
|
// the scope composite still needs their identities so a member
|
|
928
955
|
// leaving the public surface moves the fingerprint.
|
|
929
|
-
reviewScopes =
|
|
930
|
-
append(
|
|
931
|
-
append([]*evidenceUnit{}, reference.Population...),
|
|
932
|
-
reference.Hidden...,
|
|
933
|
-
),
|
|
934
|
-
)
|
|
956
|
+
reviewScopes = referenceReviewScopes(reference, loader)
|
|
935
957
|
}
|
|
936
958
|
if reviewLedgerForClaim == nil {
|
|
937
959
|
reviewLedgerForClaim = newReviewLedger(state.Reviews)
|
|
938
960
|
}
|
|
939
|
-
problems =
|
|
961
|
+
problems = problems.add(severity, reviewProblems(
|
|
940
962
|
declaration,
|
|
941
963
|
scopesByID[scopeID],
|
|
942
964
|
reference,
|
|
@@ -952,8 +974,8 @@ func evaluateEvidenceGraph(
|
|
|
952
974
|
evidenceByHostAndScope[declaration.HostID] = byScope
|
|
953
975
|
}
|
|
954
976
|
if first := byScope[scopeID]; first != nil {
|
|
955
|
-
problems =
|
|
956
|
-
|
|
977
|
+
problems = problems.add(
|
|
978
|
+
severity,
|
|
957
979
|
"Duplicate @evidence for '"+scopesByID[scopeID].Target+"' on the same host at "+declaration.location()+"; first declared at "+first.location()+".",
|
|
958
980
|
)
|
|
959
981
|
} else {
|
|
@@ -1060,14 +1082,14 @@ func evaluateEvidenceGraph(
|
|
|
1060
1082
|
evidence = conflictingDeclaration
|
|
1061
1083
|
exclusion = declaration
|
|
1062
1084
|
}
|
|
1063
|
-
problems =
|
|
1064
|
-
|
|
1085
|
+
problems = problems.add(
|
|
1086
|
+
severity,
|
|
1065
1087
|
"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
1088
|
)
|
|
1067
1089
|
}
|
|
1068
1090
|
if duplicateExclusionUnit != nil {
|
|
1069
|
-
problems =
|
|
1070
|
-
|
|
1091
|
+
problems = problems.add(
|
|
1092
|
+
severity,
|
|
1071
1093
|
"Duplicate @evidenceExclude for '"+duplicateExclusionUnit.Target+"' in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": exclusion at "+declaration.location()+" overlaps exclusion at "+firstExclusion.location()+".",
|
|
1072
1094
|
)
|
|
1073
1095
|
}
|
|
@@ -1084,8 +1106,8 @@ func evaluateEvidenceGraph(
|
|
|
1084
1106
|
if count == 1 {
|
|
1085
1107
|
continue
|
|
1086
1108
|
}
|
|
1087
|
-
problems =
|
|
1088
|
-
|
|
1109
|
+
problems = problems.add(
|
|
1110
|
+
severity,
|
|
1089
1111
|
"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
1112
|
)
|
|
1091
1113
|
}
|
|
@@ -1116,8 +1138,8 @@ func evaluateEvidenceGraph(
|
|
|
1116
1138
|
if reference.Spec.Policy.NoExclude {
|
|
1117
1139
|
repair = "Do what each item requires and cite it with @evidence on this host; this reference forbids @evidenceExclude." + untrueTagWarning
|
|
1118
1140
|
}
|
|
1119
|
-
problems =
|
|
1120
|
-
|
|
1141
|
+
problems = problems.add(
|
|
1142
|
+
severity,
|
|
1121
1143
|
"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
1144
|
)
|
|
1123
1145
|
}
|
|
@@ -1131,8 +1153,8 @@ func evaluateEvidenceGraph(
|
|
|
1131
1153
|
if reference.Spec.Policy.NoExclude {
|
|
1132
1154
|
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
1155
|
}
|
|
1134
|
-
problems =
|
|
1135
|
-
|
|
1156
|
+
problems = problems.add(
|
|
1157
|
+
severity,
|
|
1136
1158
|
"Missing acknowledgement for '"+unit.Target+"' ("+unit.Readable+" at "+unit.location()+") in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+". "+repair,
|
|
1137
1159
|
)
|
|
1138
1160
|
}
|
|
@@ -1140,14 +1162,15 @@ func evaluateEvidenceGraph(
|
|
|
1140
1162
|
if !unique || hostCount <= 1 {
|
|
1141
1163
|
continue
|
|
1142
1164
|
}
|
|
1143
|
-
problems =
|
|
1144
|
-
|
|
1165
|
+
problems = problems.add(
|
|
1166
|
+
severity,
|
|
1145
1167
|
"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
1168
|
)
|
|
1147
1169
|
}
|
|
1148
1170
|
}
|
|
1149
1171
|
}
|
|
1150
1172
|
for _, id := range declarationIDs {
|
|
1173
|
+
severity := ownerSeverity(owners[id])
|
|
1151
1174
|
if resolved[id] == "" {
|
|
1152
1175
|
continue
|
|
1153
1176
|
}
|
|
@@ -1161,8 +1184,8 @@ func evaluateEvidenceGraph(
|
|
|
1161
1184
|
// withholds its own.
|
|
1162
1185
|
if obligations := unhosted[id]; len(obligations) != 0 &&
|
|
1163
1186
|
!answers[id] && !uncertain[id] {
|
|
1164
|
-
problems =
|
|
1165
|
-
|
|
1187
|
+
problems = problems.add(
|
|
1188
|
+
unhostedLevels[id],
|
|
1166
1189
|
"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
1190
|
)
|
|
1168
1191
|
}
|
|
@@ -1171,8 +1194,8 @@ func evaluateEvidenceGraph(
|
|
|
1171
1194
|
}
|
|
1172
1195
|
context := declarationObligationContext(owners[id])
|
|
1173
1196
|
if obligations := outsideCarrier[id]; len(obligations) != 0 {
|
|
1174
|
-
problems =
|
|
1175
|
-
|
|
1197
|
+
problems = problems.add(
|
|
1198
|
+
outsideCarrierLevels[id],
|
|
1176
1199
|
"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
1200
|
)
|
|
1178
1201
|
continue
|
|
@@ -1183,14 +1206,14 @@ func evaluateEvidenceGraph(
|
|
|
1183
1206
|
host = "unsupported or non-exported declaration"
|
|
1184
1207
|
}
|
|
1185
1208
|
if declaration.Tag == tagExclude {
|
|
1186
|
-
problems =
|
|
1187
|
-
|
|
1209
|
+
problems = problems.add(
|
|
1210
|
+
outOfScopeLevels[id],
|
|
1188
1211
|
"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
1212
|
)
|
|
1190
1213
|
continue
|
|
1191
1214
|
}
|
|
1192
|
-
problems =
|
|
1193
|
-
|
|
1215
|
+
problems = problems.add(
|
|
1216
|
+
outOfScopeLevels[id],
|
|
1194
1217
|
"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
1218
|
)
|
|
1196
1219
|
continue
|
|
@@ -1201,8 +1224,8 @@ func evaluateEvidenceGraph(
|
|
|
1201
1224
|
// derive a second claim from an incomplete graph.
|
|
1202
1225
|
continue
|
|
1203
1226
|
}
|
|
1204
|
-
problems =
|
|
1205
|
-
|
|
1227
|
+
problems = problems.add(
|
|
1228
|
+
severity,
|
|
1206
1229
|
"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
1230
|
)
|
|
1208
1231
|
}
|
|
@@ -1438,6 +1461,7 @@ func materializeEntryReference(
|
|
|
1438
1461
|
state.Units = population.Units
|
|
1439
1462
|
state.Hidden = population.Hidden
|
|
1440
1463
|
state.Published = population.Published
|
|
1464
|
+
state.Code = population.Code
|
|
1441
1465
|
if failure := loader.failure(entry); failure != "" {
|
|
1442
1466
|
state.Healthy = false
|
|
1443
1467
|
return state, []string{
|
|
@@ -1529,6 +1553,7 @@ func materializeLocalTypeScriptReference(
|
|
|
1529
1553
|
state.Units = population.Units
|
|
1530
1554
|
state.Hidden = population.Hidden
|
|
1531
1555
|
state.Published = population.Published
|
|
1556
|
+
state.Code = population.Code
|
|
1532
1557
|
applyTraversedScopes(&state, population.Reached)
|
|
1533
1558
|
if !state.Healthy {
|
|
1534
1559
|
return state, nil
|
|
@@ -1614,6 +1639,7 @@ func materializePackageGlobReference(
|
|
|
1614
1639
|
state.Units = population.Units
|
|
1615
1640
|
state.Hidden = population.Hidden
|
|
1616
1641
|
state.Published = population.Published
|
|
1642
|
+
state.Code = population.Code
|
|
1617
1643
|
applyTraversedScopes(&state, population.Reached)
|
|
1618
1644
|
if !state.Healthy {
|
|
1619
1645
|
return state, problems
|
|
@@ -1681,6 +1707,8 @@ func resolveInlineLinkDeclaration(
|
|
|
1681
1707
|
loader *typeScriptLoader,
|
|
1682
1708
|
scopedTargets map[scopedTargetKey]map[string]*evidenceUnit,
|
|
1683
1709
|
scopedHidden map[scopedTargetKey]*evidenceUnit,
|
|
1710
|
+
fileLinks map[int]*fileLinkClaimIndex,
|
|
1711
|
+
states []claimState,
|
|
1684
1712
|
context string,
|
|
1685
1713
|
) (string, string) {
|
|
1686
1714
|
target := inlineLinkTarget(declaration.Target)
|
|
@@ -1711,7 +1739,19 @@ func resolveInlineLinkDeclaration(
|
|
|
1711
1739
|
return "", "Incomplete evidence target '" + displayTarget(declaration.Target) + "' at " + declaration.location() + " for " + context + ": a namespace import names a module rather than a unit. Name a symbol inside '" + binding.Specifier + "' that the named reference selects."
|
|
1712
1740
|
}
|
|
1713
1741
|
name := strings.Join(remaining, ".")
|
|
1714
|
-
|
|
1742
|
+
identity, _ := loader.moduleIdentity(resolvedPath)
|
|
1743
|
+
candidates, hidden := queryFileLinkClaims(fileLinks, states, identity, remaining, true)
|
|
1744
|
+
for id, unit := range scopedTargets[scopedTargetKey{path: resolvedPath, target: name}] {
|
|
1745
|
+
candidates[id] = unit
|
|
1746
|
+
}
|
|
1747
|
+
if len(candidates) == 0 && len(hidden) != 0 {
|
|
1748
|
+
ids := make([]string, 0, len(hidden))
|
|
1749
|
+
for id := range hidden {
|
|
1750
|
+
ids = append(ids, id)
|
|
1751
|
+
}
|
|
1752
|
+
sort.Strings(ids)
|
|
1753
|
+
return "", hiddenTargetProblem(declaration, hidden[ids[0]], context)
|
|
1754
|
+
}
|
|
1715
1755
|
switch len(candidates) {
|
|
1716
1756
|
case 0:
|
|
1717
1757
|
if hidden := scopedHidden[scopedTargetKey{
|
package/native/imports.go
CHANGED
|
@@ -19,6 +19,7 @@ type importBinding struct {
|
|
|
19
19
|
Specifier string
|
|
20
20
|
Imported string
|
|
21
21
|
Namespace bool
|
|
22
|
+
TypeOnly bool
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
// collectImportBindings indexes a file's imports by the local name each binds.
|
|
@@ -52,6 +53,7 @@ func collectImportBindings(file *shimast.SourceFile) map[string]importBinding {
|
|
|
52
53
|
Local: name,
|
|
53
54
|
Specifier: specifier,
|
|
54
55
|
Imported: "default",
|
|
56
|
+
TypeOnly: clause.IsTypeOnly(),
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
if clause.NamedBindings == nil {
|
|
@@ -64,6 +66,7 @@ func collectImportBindings(file *shimast.SourceFile) map[string]importBinding {
|
|
|
64
66
|
Local: name,
|
|
65
67
|
Specifier: specifier,
|
|
66
68
|
Namespace: true,
|
|
69
|
+
TypeOnly: clause.IsTypeOnly(),
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
case shimast.KindNamedImports:
|
|
@@ -91,6 +94,7 @@ func collectImportBindings(file *shimast.SourceFile) map[string]importBinding {
|
|
|
91
94
|
Local: local,
|
|
92
95
|
Specifier: specifier,
|
|
93
96
|
Imported: imported,
|
|
97
|
+
TypeOnly: clause.IsTypeOnly() || specifierNode.IsTypeOnly,
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
}
|
|
@@ -124,19 +128,28 @@ var typeScriptModuleExtensions = []string{
|
|
|
124
128
|
// must spell its sibling as `./x.js`, so refusing to map it back would make the
|
|
125
129
|
// correct import form unresolvable.
|
|
126
130
|
func moduleCandidates(base string) []string {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
if isTypeScriptPath(base) {
|
|
132
|
+
return []string{base}
|
|
133
|
+
}
|
|
134
|
+
substitutions := map[string][]string{
|
|
135
|
+
".js": {".ts", ".tsx", ".d.ts"},
|
|
136
|
+
".mjs": {".mts", ".d.mts"},
|
|
137
|
+
".cjs": {".cts", ".d.cts"},
|
|
138
|
+
}
|
|
139
|
+
if extensions, emitted := substitutions[path.Ext(base)]; emitted {
|
|
140
|
+
stripped := strings.TrimSuffix(base, path.Ext(base))
|
|
141
|
+
candidates := make([]string, 0, len(extensions))
|
|
142
|
+
for _, extension := range extensions {
|
|
143
|
+
candidates = append(candidates, stripped+extension)
|
|
133
144
|
}
|
|
145
|
+
return candidates
|
|
134
146
|
}
|
|
147
|
+
candidates := []string{}
|
|
135
148
|
for _, extension := range typeScriptModuleExtensions {
|
|
136
|
-
candidates = append(candidates,
|
|
149
|
+
candidates = append(candidates, base+extension)
|
|
137
150
|
}
|
|
138
151
|
for _, extension := range typeScriptModuleExtensions {
|
|
139
|
-
candidates = append(candidates, path.Join(
|
|
152
|
+
candidates = append(candidates, path.Join(base, "index"+extension))
|
|
140
153
|
}
|
|
141
154
|
return candidates
|
|
142
155
|
}
|
package/native/inputs.go
CHANGED
|
@@ -35,10 +35,8 @@ func (graphRule) ProjectInputs(ctx *rule.ProjectInputContext) []rule.ProjectInpu
|
|
|
35
35
|
// graphProjectInputs collects the external dependency of every configured
|
|
36
36
|
// obligation.
|
|
37
37
|
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// watches, so declaring them again would ask for a second watcher on a file
|
|
41
|
-
// that already has one.
|
|
38
|
+
// Program sources are already watched. Rooted TypeScript references additionally
|
|
39
|
+
// declare the directory containing their disk-loaded export closure.
|
|
42
40
|
func graphProjectInputs(config graphConfig) []rule.ProjectInput {
|
|
43
41
|
config = enabledGraphConfig(config)
|
|
44
42
|
inputs := []rule.ProjectInput{}
|
|
@@ -49,6 +47,17 @@ func graphProjectInputs(config graphConfig) []rule.ProjectInput {
|
|
|
49
47
|
}
|
|
50
48
|
for _, reference := range claim.References {
|
|
51
49
|
switch reference.Type {
|
|
50
|
+
case artifactTypeScript:
|
|
51
|
+
if reference.Rooted {
|
|
52
|
+
// Export traversal can reach a module outside the entry globs, and
|
|
53
|
+
// this pre-Program protocol cannot discover that closure. The root
|
|
54
|
+
// is its explicit boundary, including future files and repairs.
|
|
55
|
+
pattern := "**"
|
|
56
|
+
if reference.Root != "" {
|
|
57
|
+
pattern = strings.TrimSuffix(reference.Root, "/") + "/**"
|
|
58
|
+
}
|
|
59
|
+
inputs = append(inputs, rule.ProjectInput{Kind: rule.ProjectInputGlob, Pattern: pattern})
|
|
60
|
+
}
|
|
52
61
|
case artifactMarkdown, artifactPrisma:
|
|
53
62
|
inputs = append(inputs, globInputs(reference.Root, reference.Files)...)
|
|
54
63
|
case artifactSwagger:
|