@shapeshift-labs/frontier-lang-compiler 0.2.66 → 0.2.68
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/README.md +6 -2
- package/bench/smoke.mjs +15 -1
- package/bench/universal-fixture-suite.mjs +183 -0
- package/dist/declarations/native-project-admission.d.ts +115 -0
- package/dist/declarations/roundtrip-audit.d.ts +186 -0
- package/dist/declarations/roundtrip.d.ts +2 -53
- package/dist/declarations/runtime.d.ts +0 -11
- package/dist/declarations/semantic-history-records.d.ts +277 -0
- package/dist/declarations/semantic-history.d.ts +45 -92
- package/dist/declarations/semantic-slice-admission.d.ts +111 -0
- package/dist/declarations/semantic-slice.d.ts +36 -1
- package/dist/declarations/universal-conversion-plan.d.ts +59 -0
- package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
- package/dist/internal/index-impl/createSemanticSlice.js +1 -1
- package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
- package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
- package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
- package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
- package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
- package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
- package/dist/internal/index-impl/projectImportAdmissionSummaries.js +46 -111
- package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
- package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
- package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
- package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
- package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
- package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
- package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
- package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
- package/dist/internal/index-impl/testSemanticSlice.js +4 -1
- package/dist/language-adapter-package-contracts.js +12 -57
- package/dist/language-adapter-package-rows.js +116 -0
- package/dist/universal-conversion-plan-summary.js +42 -0
- package/dist/universal-conversion-plan.js +46 -40
- package/dist/universal-runtime-capabilities.js +92 -0
- package/dist/universal-runtime-host-selectors.js +192 -0
- package/dist/universal-runtime-profiles.js +109 -0
- package/dist/universal-runtime-route-records.js +162 -0
- package/examples/js-frontier-rust-workbench-client.mjs +58 -1
- package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
- package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
- package/examples/js-frontier-rust-workbench-route.mjs +190 -0
- package/examples/js-frontier-rust-workbench-styles.mjs +3 -38
- package/examples/js-frontier-rust-workbench.mjs +22 -128
- package/package.json +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import{countBy,uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
const precisionRank=Object.freeze({exact:0,line:1,declaration:2,estimated:3,unknown:4,none:5});
|
|
4
|
+
|
|
5
|
+
export function createRoundtripAudit(input){
|
|
6
|
+
const disposition=roundtripAuditDisposition(input);
|
|
7
|
+
return{
|
|
8
|
+
schema:'frontier.lang.nativeRoundtripAuditSignal',
|
|
9
|
+
version:1,
|
|
10
|
+
disposition,
|
|
11
|
+
claim:roundtripAuditClaim(disposition),
|
|
12
|
+
sourceLanguage:input.sourceLanguage,
|
|
13
|
+
target:input.target,
|
|
14
|
+
sameLanguage:input.sameLanguage,
|
|
15
|
+
outputMode:input.outputMode,
|
|
16
|
+
projectionMode:input.projectionMode,
|
|
17
|
+
sourceHashVerified:input.sourceHashVerified,
|
|
18
|
+
outputSourceMapPrecision:input.sourceMapEvidence.precision,
|
|
19
|
+
universalSourceMapPrecision:input.universalSourceMapEvidence.precision,
|
|
20
|
+
targetProjectionAdapterId:input.targetProjection?.adapter?.id,
|
|
21
|
+
targetCoverageLossClass:selectedTargetCoverageLossClass(input),
|
|
22
|
+
reviewRequired:input.semanticMerge!=='ready',
|
|
23
|
+
semanticMergeReadiness:input.semanticMerge,
|
|
24
|
+
semanticEquivalenceClaim:false,
|
|
25
|
+
autoMergeClaim:false,
|
|
26
|
+
paths:roundtripAuditPaths(input,disposition),
|
|
27
|
+
sourcePreservation:input.sourcePreservation,
|
|
28
|
+
generatedStubs:summarizeGeneratedStubs(input,disposition),
|
|
29
|
+
adapterProjection:summarizeAdapterProjection(input,disposition),
|
|
30
|
+
sourceMaps:summarizeRouteSourceMaps(input),
|
|
31
|
+
hashChecks:input.hashChecks,
|
|
32
|
+
commentsTrivia:summarizeCommentsTrivia(input.sourcePreservation),
|
|
33
|
+
targetCoverage:summarizeTargetCoverage(input),
|
|
34
|
+
semanticEquivalence:summarizeSemanticEquivalence(),
|
|
35
|
+
blockingLossCount:input.lossSummary.blockingLossIds.length,
|
|
36
|
+
reviewLossCount:input.lossSummary.reviewLossIds.length,
|
|
37
|
+
reasonCodes:uniqueStrings([
|
|
38
|
+
`status:${input.status}`,
|
|
39
|
+
`semantic:${input.semanticMerge}`,
|
|
40
|
+
`output:${input.outputMode??'unknown'}`,
|
|
41
|
+
`projection:${input.projectionMode??'unknown'}`,
|
|
42
|
+
input.sourceHashVerified?'source-hash:verified':'source-hash:unverified',
|
|
43
|
+
`output-source-map:${input.sourceMapEvidence.precision}`,
|
|
44
|
+
`universal-source-map:${input.universalSourceMapEvidence.precision}`,
|
|
45
|
+
selectedTargetCoverageLossClass(input)?`target-loss:${selectedTargetCoverageLossClass(input)}`:undefined,
|
|
46
|
+
matrixTargetCoverageLossClass(input)?`target-matrix-loss:${matrixTargetCoverageLossClass(input)}`:undefined,
|
|
47
|
+
input.targetProjection?.adapter?.id?`target-adapter:${input.targetProjection.adapter.id}`:undefined,
|
|
48
|
+
input.lossSummary.blockingLossIds.length?'losses:blocking':undefined,
|
|
49
|
+
input.lossSummary.reviewLossIds.length?'losses:review':undefined
|
|
50
|
+
])
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function roundtripAuditDisposition(input){
|
|
54
|
+
if(input.outputMode==='target-adapter')return'adapter-projected';
|
|
55
|
+
if(input.projectionMode==='native-source-stubs'||input.outputMode==='target-stubs')return'stub-only';
|
|
56
|
+
if(input.sourceHashVerified&&input.outputMode==='preserved-source'&&input.hasOutputSourceMaps&&input.sourceMapEvidence.precision==='exact')return'reversible';
|
|
57
|
+
if(input.sourceHashVerified||input.projectionMode==='preserved-source')return'preserved-source';
|
|
58
|
+
return'review-required';
|
|
59
|
+
}
|
|
60
|
+
function roundtripAuditClaim(disposition){
|
|
61
|
+
if(disposition==='reversible')return'source-text-reversible';
|
|
62
|
+
if(disposition==='preserved-source')return'source-preserved';
|
|
63
|
+
if(disposition==='stub-only')return'declaration-stubs-only';
|
|
64
|
+
if(disposition==='adapter-projected')return'host-adapter-projected';
|
|
65
|
+
return'review-required';
|
|
66
|
+
}
|
|
67
|
+
function roundtripAuditPaths(input,disposition){
|
|
68
|
+
const reversibleAvailable=input.sourceHashVerified&&input.outputMode==='preserved-source'&&input.hasOutputSourceMaps&&input.sourceMapEvidence.precision==='exact';
|
|
69
|
+
const preservedAvailable=input.sourceHashVerified||input.projectionMode==='preserved-source';
|
|
70
|
+
const stubAvailable=input.projectionMode==='native-source-stubs'||input.outputMode==='target-stubs';
|
|
71
|
+
const adapterAvailable=input.outputMode==='target-adapter'||Boolean(input.targetProjection?.adapter?.id);
|
|
72
|
+
return{
|
|
73
|
+
reversible:routePathSignal(disposition==='reversible',reversibleAvailable,[
|
|
74
|
+
input.sourceHashVerified?'source-hash:verified':'source-hash:unverified',
|
|
75
|
+
input.hasOutputSourceMaps?'output-source-map:present':'output-source-map:missing',
|
|
76
|
+
`output-source-map:${input.sourceMapEvidence.precision}`
|
|
77
|
+
]),
|
|
78
|
+
preservedSource:routePathSignal(disposition==='preserved-source',preservedAvailable,[
|
|
79
|
+
`projection:${input.projectionMode??'unknown'}`,
|
|
80
|
+
input.sourceHashVerified?'source-hash:verified':'source-hash:unverified'
|
|
81
|
+
]),
|
|
82
|
+
stubOnly:routePathSignal(disposition==='stub-only',stubAvailable,[
|
|
83
|
+
`projection:${input.projectionMode??'unknown'}`,
|
|
84
|
+
`output:${input.outputMode??'unknown'}`
|
|
85
|
+
]),
|
|
86
|
+
adapterProjected:routePathSignal(disposition==='adapter-projected',adapterAvailable,[
|
|
87
|
+
`output:${input.outputMode??'unknown'}`,
|
|
88
|
+
input.targetProjection?.adapter?.id?`target-adapter:${input.targetProjection.adapter.id}`:undefined
|
|
89
|
+
])
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function routePathSignal(selected,available,reasonCodes){
|
|
93
|
+
return{
|
|
94
|
+
selected,
|
|
95
|
+
available,
|
|
96
|
+
reasonCodes:uniqueStrings([
|
|
97
|
+
selected?'path:selected':undefined,
|
|
98
|
+
available?'path:available':'path:not-available',
|
|
99
|
+
...reasonCodes
|
|
100
|
+
])
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function summarizeGeneratedStubs(input,disposition){
|
|
104
|
+
const declarations=input.targetProjection?[]:input.projection?.declarations??[];
|
|
105
|
+
const projectionDeclarations=input.projection?.declarations??[];
|
|
106
|
+
return{
|
|
107
|
+
available:input.projectionMode==='native-source-stubs'||input.outputMode==='target-stubs'||projectionDeclarations.length>0,
|
|
108
|
+
selected:disposition==='stub-only',
|
|
109
|
+
projectionMode:input.projectionMode,
|
|
110
|
+
outputMode:input.outputMode,
|
|
111
|
+
declarationCount:projectionDeclarations.length,
|
|
112
|
+
emittedDeclarationCount:declarations.length,
|
|
113
|
+
declarationKinds:countBy(projectionDeclarations.map((declaration)=>declaration?.kind??'unknown')),
|
|
114
|
+
declarationsWithSourceSpan:projectionDeclarations.filter((declaration)=>declaration?.sourceSpan).length,
|
|
115
|
+
symbolIds:uniqueStrings(projectionDeclarations.map((declaration)=>declaration?.symbolId)),
|
|
116
|
+
nativeAstNodeIds:uniqueStrings(projectionDeclarations.map((declaration)=>declaration?.nativeAstNodeId)),
|
|
117
|
+
lossCount:input.projection?.losses?.length??0
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function summarizeAdapterProjection(input,disposition){
|
|
121
|
+
const targetProjection=input.targetProjection;
|
|
122
|
+
return{
|
|
123
|
+
available:Boolean(targetProjection),
|
|
124
|
+
selected:disposition==='adapter-projected',
|
|
125
|
+
id:targetProjection?.id,
|
|
126
|
+
adapterId:targetProjection?.adapter?.id,
|
|
127
|
+
adapterVersion:targetProjection?.adapter?.version,
|
|
128
|
+
outputMode:targetProjection?.outputMode,
|
|
129
|
+
readiness:targetProjection?.readiness?.readiness,
|
|
130
|
+
lossCount:targetProjection?.losses?.length??0,
|
|
131
|
+
evidenceIds:evidenceIds(targetProjection?.evidence),
|
|
132
|
+
sourceMaps:summarizeSourceMaps(targetProjection?.sourceMaps??[])
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function summarizeRouteSourceMaps(input){
|
|
136
|
+
return{
|
|
137
|
+
hasOutputSourceMaps:input.hasOutputSourceMaps,
|
|
138
|
+
output:input.sourceMapEvidence,
|
|
139
|
+
universal:input.universalSourceMapEvidence,
|
|
140
|
+
outputExact:input.sourceMapEvidence.precision==='exact',
|
|
141
|
+
outputEstimated:input.sourceMapEvidence.precision==='estimated'||numeric(input.sourceMapEvidence.byPrecision?.estimated)>0,
|
|
142
|
+
universalEstimated:input.universalSourceMapEvidence.precision==='estimated'||numeric(input.universalSourceMapEvidence.byPrecision?.estimated)>0
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function summarizeCommentsTrivia(sourcePreservation){
|
|
146
|
+
return{
|
|
147
|
+
comments:sourcePreservation.comments,
|
|
148
|
+
trivia:sourcePreservation.trivia,
|
|
149
|
+
directives:sourcePreservation.directives,
|
|
150
|
+
tokens:sourcePreservation.tokens,
|
|
151
|
+
whitespace:sourcePreservation.whitespace,
|
|
152
|
+
truncated:sourcePreservation.truncated,
|
|
153
|
+
exactSourceAvailable:sourcePreservation.exactSourceAvailable,
|
|
154
|
+
sourceTextAvailable:sourcePreservation.sourceTextAvailable
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function summarizeTargetCoverage(input){
|
|
158
|
+
const coverage=input.targetCoverage;
|
|
159
|
+
const lossClass=selectedTargetCoverageLossClass(input);
|
|
160
|
+
return{
|
|
161
|
+
target:input.target??coverage?.target??input.targetProjection?.target,
|
|
162
|
+
supported:adapterProjectionSelected(input)?true:coverage?.supported,
|
|
163
|
+
readiness:coverage?.readiness??input.targetProjection?.readiness?.readiness,
|
|
164
|
+
lossClass,
|
|
165
|
+
adapterId:coverage?.adapter??input.targetProjection?.adapter?.id,
|
|
166
|
+
adapterKind:adapterProjectionSelected(input)?'targetProjection':coverage?.adapterKind,
|
|
167
|
+
adapterVersion:coverage?.adapterVersion??input.targetProjection?.adapter?.version,
|
|
168
|
+
lossKinds:uniqueStrings(coverage?.lossKinds),
|
|
169
|
+
categories:uniqueStrings(coverage?.categories),
|
|
170
|
+
reason:coverage?.reason,
|
|
171
|
+
notes:uniqueStrings(coverage?.notes)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function selectedTargetCoverageLossClass(input){
|
|
175
|
+
const projectedLossClass=input.targetProjection?.coverage?.lossClass
|
|
176
|
+
??input.targetProjection?.metadata?.targetProjectionCoverage?.lossClass
|
|
177
|
+
??input.targetProjection?.metadata?.lossClass;
|
|
178
|
+
if(projectedLossClass)return projectedLossClass;
|
|
179
|
+
if(adapterProjectionSelected(input))return'targetAdapterProjection';
|
|
180
|
+
return input.targetCoverage?.lossClass;
|
|
181
|
+
}
|
|
182
|
+
function matrixTargetCoverageLossClass(input){
|
|
183
|
+
const selected=selectedTargetCoverageLossClass(input);
|
|
184
|
+
const matrix=input.targetCoverage?.lossClass;
|
|
185
|
+
return matrix&&matrix!==selected?matrix:undefined;
|
|
186
|
+
}
|
|
187
|
+
function adapterProjectionSelected(input){
|
|
188
|
+
return input.outputMode==='target-adapter'||Boolean(input.targetProjection?.adapter?.id);
|
|
189
|
+
}
|
|
190
|
+
function summarizeSemanticEquivalence(){
|
|
191
|
+
return{
|
|
192
|
+
claimed:false,
|
|
193
|
+
evidenceIds:[],
|
|
194
|
+
reasonCode:'proof-adapter:not-provided'
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function evidenceIds(evidence){return uniqueStrings((evidence??[]).map((record)=>record?.id));}
|
|
198
|
+
function summarizeSourceMaps(sourceMaps){
|
|
199
|
+
const maps=sourceMaps??[];
|
|
200
|
+
const mappings=maps.flatMap((sourceMap)=>sourceMap?.mappings??[]);
|
|
201
|
+
const precisions=mappings.map((mapping)=>normalizePrecision(mapping?.precision));
|
|
202
|
+
return{
|
|
203
|
+
total:maps.length,
|
|
204
|
+
ids:uniqueStrings(maps.map((sourceMap)=>sourceMap?.id)),
|
|
205
|
+
mappings:mappings.length,
|
|
206
|
+
precision:worstPrecision(precisions),
|
|
207
|
+
byPrecision:countBy(precisions),
|
|
208
|
+
byOrigin:countBy(mappings.map((mapping)=>mapping?.metadata?.sourceMapOrigin??'native-import')),
|
|
209
|
+
withSourceSpan:mappings.filter((mapping)=>mapping?.sourceSpan).length,
|
|
210
|
+
withGeneratedSpan:mappings.filter((mapping)=>mapping?.generatedSpan).length,
|
|
211
|
+
withSemanticSymbol:mappings.filter((mapping)=>mapping?.semanticSymbolId||mapping?.semanticNodeId).length,
|
|
212
|
+
targetPaths:uniqueStrings(maps.map((sourceMap)=>sourceMap?.targetPath).concat(mappings.map((mapping)=>mapping?.generatedSpan?.targetPath)))
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function normalizePrecision(value){const precision=String(value??'unknown');return Object.prototype.hasOwnProperty.call(precisionRank,precision)?precision:'unknown';}
|
|
216
|
+
function worstPrecision(precisions){if(!precisions.length)return'none';return precisions.reduce((worst,next)=>precisionRank[next]>precisionRank[worst]?next:worst,'exact');}
|
|
217
|
+
function numeric(value,fallback=0){const number=Number(value);return Number.isFinite(number)?number:fallback;}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import{uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
import{nativeImportCategoryForLossKind}from'./nativeImportCategoryForLossKind.js';
|
|
3
|
+
|
|
4
|
+
export function importLosses(imported){
|
|
5
|
+
const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
|
|
6
|
+
const losses=[
|
|
7
|
+
...(imported?.losses??[]),
|
|
8
|
+
...(nativeAst?.losses??[]),
|
|
9
|
+
...(imported?.universalAst?.losses??[])
|
|
10
|
+
];
|
|
11
|
+
const seen=new Set();
|
|
12
|
+
const result=[];
|
|
13
|
+
for(const loss of losses){
|
|
14
|
+
const id=loss?.id??`loss_${result.length+1}`;
|
|
15
|
+
if(seen.has(id)) continue;
|
|
16
|
+
seen.add(id);
|
|
17
|
+
result.push(loss?.id?loss:{...loss,id});
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function sourceLossClasses(imported,losses){
|
|
23
|
+
const lossSummary=imported?.metadata?.nativeImportLossSummary
|
|
24
|
+
??imported?.nativeSource?.metadata?.nativeImportLossSummary
|
|
25
|
+
??imported?.nativeAst?.metadata?.nativeImportLossSummary
|
|
26
|
+
??imported?.universalAst?.metadata?.nativeImportLossSummary;
|
|
27
|
+
return uniqueStrings([
|
|
28
|
+
...(lossSummary?.categories??[]),
|
|
29
|
+
...(losses??[]).map(lossClassForLoss),
|
|
30
|
+
...sourceProjectionLossClasses(imported)
|
|
31
|
+
].filter(Boolean));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function lossClassForLoss(loss){
|
|
35
|
+
return loss?.metadata?.targetLossClass
|
|
36
|
+
??loss?.metadata?.lossClass
|
|
37
|
+
??loss?.metadata?.lossCategory
|
|
38
|
+
??nativeImportCategoryForLossKind(loss?.kind);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sourceProjectionLossClasses(imported){
|
|
42
|
+
const matrixClasses=projectionMatricesForImport(imported).flatMap((matrix)=>[
|
|
43
|
+
...Object.entries(matrix?.summary?.byLossClass??{}).filter(([,count])=>count>0).map(([lossClass])=>lossClass),
|
|
44
|
+
...(matrix?.languages??[]).flatMap((language)=>[
|
|
45
|
+
...Object.entries(language?.summary?.byLossClass??{}).filter(([,count])=>count>0).map(([lossClass])=>lossClass),
|
|
46
|
+
...(language?.targets??[]).map((target)=>target?.lossClass)
|
|
47
|
+
])
|
|
48
|
+
]);
|
|
49
|
+
return uniqueStrings([
|
|
50
|
+
...targetProjectionEntriesForImport(imported).map((entry)=>entry.lossClass),
|
|
51
|
+
...matrixClasses
|
|
52
|
+
].filter(Boolean));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function targetProjectionEntriesForImport(imported){
|
|
56
|
+
return [
|
|
57
|
+
imported?.targetCoverage,
|
|
58
|
+
imported?.metadata?.targetCoverage,
|
|
59
|
+
imported?.metadata?.targetProjectionCoverage,
|
|
60
|
+
...(imported?.targetCoverages??[]),
|
|
61
|
+
...(imported?.metadata?.targetCoverages??[])
|
|
62
|
+
].flatMap((entry)=>Array.isArray(entry)?entry:[entry])
|
|
63
|
+
.filter((entry)=>entry&&typeof entry==='object'&&(entry.target||entry.lossClass||entry.supported!==undefined));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function projectionMatricesForImport(imported){
|
|
67
|
+
return [
|
|
68
|
+
imported?.projectionMatrix,
|
|
69
|
+
imported?.metadata?.projectionMatrix,
|
|
70
|
+
...(imported?.projectionMatrices??[]),
|
|
71
|
+
...(imported?.metadata?.projectionMatrices??[])
|
|
72
|
+
].filter((matrix)=>matrix?.kind==='frontier.lang.projectionTargetLossMatrix'||Array.isArray(matrix?.languages));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function summarizeParserEvidence(imported,source,losses){
|
|
76
|
+
const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
|
|
77
|
+
const rootNode=nativeAst?.nodes?.[nativeAst?.rootId];
|
|
78
|
+
const coverage=imported?.adapter?.coverage
|
|
79
|
+
??imported?.metadata?.adapterCoverage
|
|
80
|
+
??imported?.nativeSource?.adapter?.coverage
|
|
81
|
+
??nativeAst?.metadata?.adapterCoverage;
|
|
82
|
+
const observed=coverage?.observed;
|
|
83
|
+
const effective=coverage?.capabilityEvidence?.effective;
|
|
84
|
+
const diagnostics=[
|
|
85
|
+
...(imported?.diagnostics??[]),
|
|
86
|
+
...(imported?.nativeSource?.diagnostics??[])
|
|
87
|
+
];
|
|
88
|
+
const parserDiagnosticLosses=(losses??[]).filter((loss)=>
|
|
89
|
+
loss?.kind==='parserDiagnostic'
|
|
90
|
+
|| loss?.phase==='parse'&&loss?.severity==='error'
|
|
91
|
+
|| loss?.metadata?.parserDiagnostic===true
|
|
92
|
+
);
|
|
93
|
+
const parser=source?.parser
|
|
94
|
+
??imported?.adapter?.parser
|
|
95
|
+
??coverage?.parser
|
|
96
|
+
??nativeAst?.parser
|
|
97
|
+
??imported?.metadata?.parser
|
|
98
|
+
??imported?.nativeSource?.parser;
|
|
99
|
+
return {
|
|
100
|
+
parser,
|
|
101
|
+
astFormat:imported?.metadata?.astFormat??nativeAst?.metadata?.astFormat,
|
|
102
|
+
exactness:coverage?.exactness??effective?.exactness??observed?.exactness,
|
|
103
|
+
semanticCoverageLevel:coverage?.semanticCoverage?.level??effective?.semanticCoverage?.level??observed?.semanticCoverage?.level,
|
|
104
|
+
exactAst:Boolean(coverage?.exactAst??effective?.exactAst??observed?.exactAst),
|
|
105
|
+
tokens:Boolean(coverage?.tokens??effective?.tokens??observed?.tokens),
|
|
106
|
+
trivia:Boolean(coverage?.trivia??effective?.trivia??observed?.trivia),
|
|
107
|
+
diagnostics:Boolean(coverage?.diagnostics??effective?.diagnostics)||(diagnostics.length+parserDiagnosticLosses.length)>0,
|
|
108
|
+
diagnosticCount:diagnostics.length+parserDiagnosticLosses.length+(observed?.parserDiagnostics??0),
|
|
109
|
+
sourceRanges:Boolean(coverage?.sourceRanges??effective?.sourceRanges??observed?.sourceRanges),
|
|
110
|
+
generatedRanges:Boolean(coverage?.generatedRanges??effective?.generatedRanges??observed?.generatedRanges),
|
|
111
|
+
evidenceRecords:source?.evidenceCount??imported?.evidence?.length??0,
|
|
112
|
+
missing:Boolean(
|
|
113
|
+
imported?.metadata?.missingInjectedParser
|
|
114
|
+
|| nativeAst?.metadata?.missingInjectedParser
|
|
115
|
+
|| rootNode?.kind==='MissingInjectedParser'
|
|
116
|
+
|| rootNode?.metadata?.reason==='missing-injected-parser'
|
|
117
|
+
|| parserDiagnosticLosses.some((loss)=>loss?.metadata?.reason==='missing-injected-parser'||loss?.metadata?.missingInjectedParser)
|
|
118
|
+
)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function compactAdmissionSource(imported,index){
|
|
123
|
+
const semanticIndex=imported?.semanticIndex??imported?.universalAst?.semanticIndex;
|
|
124
|
+
const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
|
|
125
|
+
const sourceMaps=imported?.sourceMaps??imported?.universalAst?.sourceMaps??[];
|
|
126
|
+
return {
|
|
127
|
+
id:imported?.id??`import_${index+1}`,
|
|
128
|
+
language:imported?.language??imported?.nativeSource?.language??nativeAst?.language,
|
|
129
|
+
sourcePath:imported?.sourcePath??imported?.nativeSource?.sourcePath??nativeAst?.sourcePath,
|
|
130
|
+
sourceHash:imported?.nativeSource?.sourceHash??nativeAst?.sourceHash,
|
|
131
|
+
parser:nativeAst?.parser??imported?.nativeSource?.parser,
|
|
132
|
+
sourceMapIds:sourceMaps.map((sourceMap)=>sourceMap.id).filter(Boolean),
|
|
133
|
+
sourceMapMappings:sourceMaps.reduce((sum,sourceMap)=>sum+(sourceMap.mappings?.length??0),0),
|
|
134
|
+
symbolCount:semanticIndex?.symbols?.length??0,
|
|
135
|
+
lossCount:imported?.losses?.length??nativeAst?.losses?.length??0,
|
|
136
|
+
evidenceCount:imported?.evidence?.length??0,
|
|
137
|
+
readiness:imported?.metadata?.semanticMergeReadiness??imported?.mergeCandidates?.[0]?.readiness
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function summarizeImportPreservation(imported,source){
|
|
142
|
+
const nativeAst=imported?.nativeAst??imported?.nativeSource?.ast;
|
|
143
|
+
const record=imported?.metadata?.sourcePreservation
|
|
144
|
+
??imported?.nativeSource?.metadata?.sourcePreservation
|
|
145
|
+
??nativeAst?.metadata?.sourcePreservation
|
|
146
|
+
??imported?.universalAst?.metadata?.sourcePreservation;
|
|
147
|
+
const sourceHash=source?.sourceHash??imported?.nativeSource?.sourceHash??nativeAst?.sourceHash;
|
|
148
|
+
const sourcePreservationLosses=(imported?.losses??nativeAst?.losses??[]).filter((loss)=>loss.kind==='sourcePreservation');
|
|
149
|
+
const stale=imported?.metadata?.sourceHashVerified===false
|
|
150
|
+
||imported?.nativeSource?.metadata?.sourceHashVerified===false
|
|
151
|
+
||nativeAst?.metadata?.sourceHashVerified===false
|
|
152
|
+
||record?.metadata?.sourceHashVerified===false
|
|
153
|
+
||Boolean(record?.sourceHash&&sourceHash&&record.sourceHash!==sourceHash);
|
|
154
|
+
const missing=!record;
|
|
155
|
+
const truncated=record?.summary?.truncated===true;
|
|
156
|
+
const exactSourceAvailable=record?.summary?.exactSourceAvailable===true;
|
|
157
|
+
const quality=stale?'stale':missing?'missing':truncated||!exactSourceAvailable||sourcePreservationLosses.length?'lossy':'exact';
|
|
158
|
+
return {quality,missing,stale,truncated,exactSourceAvailable,lossCount:sourcePreservationLosses.length,id:record?.id};
|
|
159
|
+
}
|
|
160
|
+
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import{countBy,maxSemanticMergeReadiness,uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
import{candidateRisk,maxPreservationQuality,maxRisk,maxTaskPriority,readinessSort,roundScore,taskPriorityRank}from'./projectImportAdmissionRanks.js';
|
|
3
|
+
|
|
4
|
+
export function admissionLanguages(importSummaries){
|
|
5
|
+
const grouped=new Map();
|
|
6
|
+
for(const entry of importSummaries){
|
|
7
|
+
const key=entry.language??'unknown';
|
|
8
|
+
if(!grouped.has(key)) grouped.set(key,emptyLanguageRow(key));
|
|
9
|
+
const row=grouped.get(key);
|
|
10
|
+
row.sourceCount+=1;
|
|
11
|
+
row.sourcePaths.push(entry.sourcePath);
|
|
12
|
+
row.sources.push(entry);
|
|
13
|
+
row.readiness=maxSemanticMergeReadiness(row.readiness,entry.readiness);
|
|
14
|
+
row.semanticSymbols+=entry.semanticCounts.symbols;
|
|
15
|
+
if(entry.emptySemanticEvidence) row.emptySemanticEvidenceSources+=1;
|
|
16
|
+
row.sourcePreservationQuality=maxPreservationQuality(row.sourcePreservationQuality,entry.sourcePreservation.quality);
|
|
17
|
+
if(entry.sourcePreservation.stale&&entry.sourcePath) row.staleSourcePaths.push(entry.sourcePath);
|
|
18
|
+
row.mergeCandidates+=entry.mergeCandidates.length;
|
|
19
|
+
row.highestRisk=maxRisk(row.highestRisk,entry.mergeCandidates.reduce((current,candidate)=>maxRisk(current,candidateRisk(candidate)),'low'));
|
|
20
|
+
}
|
|
21
|
+
const rows=[...grouped.values()].map((row)=>{
|
|
22
|
+
const sources=row.sources;
|
|
23
|
+
return {
|
|
24
|
+
language:row.language,
|
|
25
|
+
sourceCount:row.sourceCount,
|
|
26
|
+
sourcePaths:uniqueStrings(row.sourcePaths.filter(Boolean)),
|
|
27
|
+
readiness:row.readiness,
|
|
28
|
+
semanticSymbols:row.semanticSymbols,
|
|
29
|
+
emptySemanticEvidenceSources:row.emptySemanticEvidenceSources,
|
|
30
|
+
sourcePreservationQuality:row.sourcePreservationQuality,
|
|
31
|
+
staleSourcePaths:uniqueStrings(row.staleSourcePaths.filter(Boolean)),
|
|
32
|
+
mergeCandidates:row.mergeCandidates,
|
|
33
|
+
highestRisk:row.highestRisk,
|
|
34
|
+
byReadiness:countBy(sources.map((source)=>source.readiness)),
|
|
35
|
+
byLossClass:lossClassCounts(sources),
|
|
36
|
+
parserEvidence:parserEvidenceSummary(sources),
|
|
37
|
+
semanticMergeScore:semanticMergeScoreSummary(sources),
|
|
38
|
+
topMissingEvidence:topMissingEvidence(sources),
|
|
39
|
+
nextMissingTasks:topTaskHints(sources)
|
|
40
|
+
};
|
|
41
|
+
}).sort((left,right)=>left.language.localeCompare(right.language));
|
|
42
|
+
return {
|
|
43
|
+
total:rows.length,
|
|
44
|
+
byReadiness:countBy(rows.map((row)=>row.readiness)),
|
|
45
|
+
bySourceReadiness:countBy(importSummaries.map((entry)=>entry.readiness)),
|
|
46
|
+
byLossClass:lossClassCounts(importSummaries),
|
|
47
|
+
bySourcePreservationQuality:countBy(rows.map((row)=>row.sourcePreservationQuality)),
|
|
48
|
+
parserEvidence:parserEvidenceSummary(importSummaries),
|
|
49
|
+
semanticMergeScore:semanticMergeScoreSummary(importSummaries),
|
|
50
|
+
topMissingEvidence:topMissingEvidence(importSummaries),
|
|
51
|
+
nextMissingTasks:topTaskHints(importSummaries),
|
|
52
|
+
sourceRows:importSummaries.map(compactAdmissionSourceRow),
|
|
53
|
+
readinessRows:languageReadinessRows(importSummaries),
|
|
54
|
+
rows
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function compactAdmissionSourceRow(entry){
|
|
59
|
+
return {
|
|
60
|
+
id:entry.id,
|
|
61
|
+
language:entry.language,
|
|
62
|
+
sourcePath:entry.sourcePath,
|
|
63
|
+
sourceHash:entry.sourceHash,
|
|
64
|
+
readiness:entry.readiness,
|
|
65
|
+
parser:entry.parserEvidence.parser,
|
|
66
|
+
parserEvidence:entry.parserEvidence,
|
|
67
|
+
semanticCounts:entry.semanticCounts,
|
|
68
|
+
semanticSymbols:entry.semanticCounts.symbols,
|
|
69
|
+
emptySemanticEvidence:entry.emptySemanticEvidence,
|
|
70
|
+
sourcePreservationQuality:entry.sourcePreservation.quality,
|
|
71
|
+
lossClasses:entry.lossClasses,
|
|
72
|
+
semanticMergeScore:entry.semanticMergeScore,
|
|
73
|
+
missingEvidence:entry.missingEvidence,
|
|
74
|
+
nextMissingTask:entry.nextMissingTasks[0],
|
|
75
|
+
nextMissingTasks:entry.nextMissingTasks
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function languageReadinessRows(importSummaries){
|
|
80
|
+
const grouped=new Map();
|
|
81
|
+
for(const entry of importSummaries){
|
|
82
|
+
const key=`${entry.language??'unknown'}\u0000${entry.readiness??'needs-review'}`;
|
|
83
|
+
if(!grouped.has(key)) grouped.set(key,{language:entry.language??'unknown',readiness:entry.readiness??'needs-review',sources:[]});
|
|
84
|
+
grouped.get(key).sources.push(entry);
|
|
85
|
+
}
|
|
86
|
+
return [...grouped.values()].map((group)=>sourceGroupSummary(group.language,group.readiness,group.sources))
|
|
87
|
+
.sort((left,right)=>left.language.localeCompare(right.language)||readinessSort(left.readiness)-readinessSort(right.readiness));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function sourceGroupSummary(language,readiness,sources){
|
|
91
|
+
return {
|
|
92
|
+
language,
|
|
93
|
+
readiness,
|
|
94
|
+
sourceCount:sources.length,
|
|
95
|
+
sourcePaths:uniqueStrings(sources.map((source)=>source.sourcePath).filter(Boolean)),
|
|
96
|
+
byLossClass:lossClassCounts(sources),
|
|
97
|
+
parserEvidence:parserEvidenceSummary(sources),
|
|
98
|
+
semanticMergeScore:semanticMergeScoreSummary(sources),
|
|
99
|
+
topMissingEvidence:topMissingEvidence(sources),
|
|
100
|
+
nextMissingTasks:topTaskHints(sources)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function lossClassCounts(sources){
|
|
105
|
+
return countBy((sources??[]).flatMap((source)=>source.lossClasses?.length?source.lossClasses:['none']));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parserEvidenceSummary(sources){
|
|
109
|
+
const records=(sources??[]).map((source)=>source.parserEvidence??{});
|
|
110
|
+
return {
|
|
111
|
+
parsers:uniqueStrings(records.map((record)=>record.parser).filter(Boolean)),
|
|
112
|
+
byParser:countBy(records.map((record)=>record.parser??'unknown')),
|
|
113
|
+
byExactness:countBy(records.map((record)=>record.exactness??'unknown')),
|
|
114
|
+
semanticCoverageLevels:uniqueStrings(records.map((record)=>record.semanticCoverageLevel).filter(Boolean)),
|
|
115
|
+
exactAstSources:records.filter((record)=>record.exactAst).length,
|
|
116
|
+
tokenSources:records.filter((record)=>record.tokens).length,
|
|
117
|
+
triviaSources:records.filter((record)=>record.trivia).length,
|
|
118
|
+
sourceRangeSources:records.filter((record)=>record.sourceRanges).length,
|
|
119
|
+
generatedRangeSources:records.filter((record)=>record.generatedRanges).length,
|
|
120
|
+
diagnosticsSources:records.filter((record)=>(record.diagnosticCount??0)>0).length,
|
|
121
|
+
missingParserSources:records.filter((record)=>record.missing).length,
|
|
122
|
+
evidenceRecords:records.reduce((sum,record)=>sum+(record.evidenceRecords??0),0)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function semanticMergeScoreSummary(sources){
|
|
127
|
+
const scored=(sources??[])
|
|
128
|
+
.filter((source)=>Number.isFinite(source.semanticMergeScore))
|
|
129
|
+
.sort((left,right)=>left.semanticMergeScore-right.semanticMergeScore||String(left.sourcePath??'').localeCompare(String(right.sourcePath??'')));
|
|
130
|
+
const values=scored.map((source)=>source.semanticMergeScore);
|
|
131
|
+
if(!values.length){
|
|
132
|
+
return {sourceCount:0,min:0,max:0,average:0,sortKey:0,lowestSourcePaths:[]};
|
|
133
|
+
}
|
|
134
|
+
const min=Math.min(...values);
|
|
135
|
+
const max=Math.max(...values);
|
|
136
|
+
const average=roundScore(values.reduce((sum,value)=>sum+value,0)/values.length);
|
|
137
|
+
return {
|
|
138
|
+
sourceCount:values.length,
|
|
139
|
+
min,
|
|
140
|
+
max,
|
|
141
|
+
average,
|
|
142
|
+
sortKey:roundScore(average*100-(100-min)),
|
|
143
|
+
lowestSourcePaths:uniqueStrings(scored.slice(0,5).map((source)=>source.sourcePath).filter(Boolean))
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function topMissingEvidence(sources,limit=5){
|
|
148
|
+
const grouped=new Map();
|
|
149
|
+
for(const source of sources??[]){
|
|
150
|
+
for(const hint of source.missingEvidence??[]){
|
|
151
|
+
const key=hint.evidenceKey??hint.task;
|
|
152
|
+
if(!grouped.has(key)){
|
|
153
|
+
grouped.set(key,{
|
|
154
|
+
evidenceKey:hint.evidenceKey,
|
|
155
|
+
task:hint.task,
|
|
156
|
+
count:0,
|
|
157
|
+
sourcePaths:[],
|
|
158
|
+
languages:[],
|
|
159
|
+
lossIds:[],
|
|
160
|
+
lossKinds:[],
|
|
161
|
+
lossClasses:[],
|
|
162
|
+
readiness:'ready'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const entry=grouped.get(key);
|
|
166
|
+
entry.count+=hint.count??1;
|
|
167
|
+
entry.sourcePaths.push(...(hint.sourcePaths??[]));
|
|
168
|
+
entry.languages.push(...(hint.languages??[]));
|
|
169
|
+
entry.lossIds.push(...(hint.lossIds??[]));
|
|
170
|
+
entry.lossKinds.push(...(hint.lossKinds??[]));
|
|
171
|
+
entry.lossClasses.push(...(hint.lossClasses??[]));
|
|
172
|
+
entry.readiness=maxSemanticMergeReadiness(entry.readiness,hint.readiness??source.readiness??'ready');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return [...grouped.values()].map((entry)=>({
|
|
176
|
+
...entry,
|
|
177
|
+
sourcePaths:uniqueStrings(entry.sourcePaths),
|
|
178
|
+
languages:uniqueStrings(entry.languages),
|
|
179
|
+
lossIds:uniqueStrings(entry.lossIds),
|
|
180
|
+
lossKinds:uniqueStrings(entry.lossKinds),
|
|
181
|
+
lossClasses:uniqueStrings(entry.lossClasses)
|
|
182
|
+
})).sort((left,right)=>
|
|
183
|
+
readinessSort(right.readiness)-readinessSort(left.readiness)
|
|
184
|
+
|| right.count-left.count
|
|
185
|
+
|| String(left.evidenceKey??left.task).localeCompare(String(right.evidenceKey??right.task))
|
|
186
|
+
).slice(0,limit);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function topTaskHints(sources,limit=6){
|
|
190
|
+
const grouped=new Map();
|
|
191
|
+
for(const source of sources??[]){
|
|
192
|
+
for(const hint of source.nextMissingTasks??[]){
|
|
193
|
+
const key=hint.id??hint.task;
|
|
194
|
+
if(!grouped.has(key)){
|
|
195
|
+
grouped.set(key,{
|
|
196
|
+
id:hint.id,
|
|
197
|
+
task:hint.task,
|
|
198
|
+
reason:hint.reason,
|
|
199
|
+
priority:hint.priority??'normal',
|
|
200
|
+
readiness:'ready',
|
|
201
|
+
count:0,
|
|
202
|
+
sourcePaths:[],
|
|
203
|
+
languages:[],
|
|
204
|
+
lossClasses:[],
|
|
205
|
+
evidenceKeys:[]
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const entry=grouped.get(key);
|
|
209
|
+
entry.count+=hint.count??1;
|
|
210
|
+
entry.priority=maxTaskPriority(entry.priority,hint.priority??'normal');
|
|
211
|
+
entry.readiness=maxSemanticMergeReadiness(entry.readiness,hint.readiness??source.readiness??'ready');
|
|
212
|
+
entry.sourcePaths.push(...(hint.sourcePaths??[]));
|
|
213
|
+
entry.languages.push(...(hint.languages??[]));
|
|
214
|
+
entry.lossClasses.push(...(hint.lossClasses??[]));
|
|
215
|
+
entry.evidenceKeys.push(...(hint.evidenceKeys??[]));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return [...grouped.values()].map((entry)=>({
|
|
219
|
+
...entry,
|
|
220
|
+
sourcePaths:uniqueStrings(entry.sourcePaths),
|
|
221
|
+
languages:uniqueStrings(entry.languages),
|
|
222
|
+
lossClasses:uniqueStrings(entry.lossClasses),
|
|
223
|
+
evidenceKeys:uniqueStrings(entry.evidenceKeys)
|
|
224
|
+
})).sort((left,right)=>
|
|
225
|
+
taskPriorityRank[right.priority]-taskPriorityRank[left.priority]
|
|
226
|
+
|| readinessSort(right.readiness)-readinessSort(left.readiness)
|
|
227
|
+
|| right.count-left.count
|
|
228
|
+
|| String(left.task).localeCompare(String(right.task))
|
|
229
|
+
).slice(0,limit);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function emptyLanguageRow(language){
|
|
233
|
+
return {
|
|
234
|
+
language,
|
|
235
|
+
sourceCount:0,
|
|
236
|
+
sourcePaths:[],
|
|
237
|
+
sources:[],
|
|
238
|
+
readiness:'ready',
|
|
239
|
+
semanticSymbols:0,
|
|
240
|
+
emptySemanticEvidenceSources:0,
|
|
241
|
+
sourcePreservationQuality:'exact',
|
|
242
|
+
staleSourcePaths:[],
|
|
243
|
+
mergeCandidates:0,
|
|
244
|
+
highestRisk:'low'
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const readinessRank=Object.freeze({ready:0,'ready-with-losses':1,'needs-review':2,blocked:3});
|
|
2
|
+
export const sourceScoreByReadiness=Object.freeze({ready:100,'ready-with-losses':75,'needs-review':45,blocked:0});
|
|
3
|
+
export const riskRank=Object.freeze({low:0,medium:1,unknown:2,high:3});
|
|
4
|
+
export const taskPriorityRank=Object.freeze({low:0,normal:1,high:2,critical:3,blocker:4});
|
|
5
|
+
export const preservationRank=Object.freeze({exact:0,lossy:1,missing:2,stale:3,empty:4});
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function taskPriorityForReadiness(readiness){
|
|
9
|
+
if(readiness==='blocked') return 'blocker';
|
|
10
|
+
if(readiness==='needs-review') return 'high';
|
|
11
|
+
if(readiness==='ready-with-losses') return 'normal';
|
|
12
|
+
return 'low';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function maxTaskPriority(left,right){
|
|
16
|
+
return taskPriorityRank[left]>=taskPriorityRank[right]?left:right;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function readinessSort(readiness){
|
|
20
|
+
return readinessRank[readiness]??readinessRank['needs-review'];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function clampScore(value){
|
|
24
|
+
return Math.max(0,Math.min(100,roundScore(value)));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function roundScore(value){
|
|
28
|
+
return Math.round(value*100)/100;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function candidateRisk(candidate,patch){
|
|
32
|
+
return normalizeRisk(candidate?.risk)??normalizeRisk(patch?.risk)??readinessRisk(candidate?.readiness);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function maxRisk(left,right){
|
|
36
|
+
return riskRank[left]>=riskRank[right]?left:right;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function maxPreservationQuality(left,right){
|
|
40
|
+
return preservationRank[left]>=preservationRank[right]?left:right;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function readinessRisk(readiness){
|
|
44
|
+
if(readiness==='blocked') return 'high';
|
|
45
|
+
if(readiness==='needs-review'||readiness==='ready-with-losses') return 'medium';
|
|
46
|
+
return 'low';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function normalizeRisk(value){
|
|
50
|
+
const risk=String(value??'').toLowerCase();
|
|
51
|
+
return Object.prototype.hasOwnProperty.call(riskRank,risk)?risk:undefined;
|
|
52
|
+
}
|