@shapeshift-labs/frontier-lang-compiler 0.2.65 → 0.2.67
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 +37 -8
- package/bench/smoke.mjs +15 -1
- package/bench/universal-fixture-suite.mjs +183 -0
- package/dist/declarations/import-adapter-core.d.ts +3 -0
- package/dist/declarations/native-project-admission.d.ts +133 -0
- package/dist/declarations/roundtrip-audit.d.ts +177 -0
- package/dist/declarations/roundtrip.d.ts +2 -53
- package/dist/declarations/semantic-history-records.d.ts +277 -0
- package/dist/declarations/semantic-history.d.ts +45 -92
- package/dist/declarations/semantic-merge-candidates.d.ts +200 -0
- package/dist/declarations/semantic-merge-conflicts.d.ts +12 -0
- package/dist/declarations/semantic-sidecar.d.ts +8 -3
- 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 +2 -0
- package/dist/index.js +2 -0
- package/dist/internal/index-impl/attachExternalOwnership.js +18 -10
- package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
- package/dist/internal/index-impl/createSemanticImportSidecar.js +6 -0
- package/dist/internal/index-impl/createSemanticSlice.js +4 -3
- package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
- package/dist/internal/index-impl/diffNativeSourceImports.js +3 -2
- package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
- package/dist/internal/index-impl/externalSemanticBase.js +1 -0
- package/dist/internal/index-impl/importExternalSemanticIndex.js +4 -0
- 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 +77 -117
- 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/semanticMergeCandidateRecordInternals.js +314 -0
- package/dist/internal/index-impl/semanticMergeCandidateRecords.js +241 -0
- 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/internal/index-impl/withExternalEmptyLoss.js +1 -0
- package/dist/language-adapter-package-contracts.js +12 -57
- package/dist/language-adapter-package-rows.js +116 -0
- package/dist/lightweight-dependency-language.js +8 -0
- package/dist/native-region-scanner-core.js +42 -10
- package/dist/native-region-scanner-js-helpers.js +2 -0
- package/dist/native-region-scanner-js-imports.js +111 -0
- package/dist/native-region-scanner-js.js +111 -28
- 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-adapters.mjs +89 -0
- package/examples/js-frontier-rust-workbench-bounds.mjs +4 -3
- package/examples/js-frontier-rust-workbench-client.mjs +135 -59
- package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
- package/examples/js-frontier-rust-workbench-html.mjs +20 -13
- 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 +12 -54
- package/examples/js-frontier-rust-workbench.mjs +54 -214
- package/package.json +1 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import{idFragment,maxSemanticMergeReadiness,normalizeSemanticMergeReadiness,uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
import{semanticMergeConflictRiskScore}from'./semanticMergeConflicts.js';
|
|
3
|
+
|
|
4
|
+
export const projectionRiskRank=Object.freeze({low:3,medium:2,unknown:1,high:0});
|
|
5
|
+
const readinessSeverityRank=Object.freeze({ready:0,'ready-with-losses':1,'needs-review':2,blocked:3});
|
|
6
|
+
const projectionRiskSeverityRank=Object.freeze({low:0,medium:1,unknown:2,high:3});
|
|
7
|
+
const projectionRiskFromReadiness=Object.freeze({ready:'low','ready-with-losses':'medium','needs-review':'medium',blocked:'high'});
|
|
8
|
+
const sourcePreservationRisk=Object.freeze({exact:'low',declaration:'medium',estimated:'medium',line:'medium',unknown:'medium',blocked:'high'});
|
|
9
|
+
|
|
10
|
+
export function normalizeChangedSemanticRegions(input){
|
|
11
|
+
const records=[
|
|
12
|
+
...input.changedRegions.map((region,index)=>regionFromChangedRegion(region,index,input)),
|
|
13
|
+
...array(input.candidate.nativeSpans).map((span,index)=>regionFromNativeSpan(span,index,input)),
|
|
14
|
+
...array(input.candidate.touchedSymbols).map((symbol,index)=>regionFromTouchedSymbol(symbol,index,input)),
|
|
15
|
+
...array(input.candidate.touchedSemanticNodes).map((node,index)=>regionFromTouchedNode(node,index,input))
|
|
16
|
+
].filter(Boolean);
|
|
17
|
+
const seen=new Set();
|
|
18
|
+
const result=[];
|
|
19
|
+
for(const record of records){
|
|
20
|
+
const key=[record.conflictKey,record.sourcePath,spanKey(record.sourceSpan),record.symbolId,record.semanticNodeId,record.nativeAstNodeId].join('|');
|
|
21
|
+
if(seen.has(key))continue;
|
|
22
|
+
seen.add(key);
|
|
23
|
+
result.push(record);
|
|
24
|
+
}
|
|
25
|
+
return result.sort((left,right)=>String(left.sourcePath??'').localeCompare(String(right.sourcePath??''))||String(left.conflictKey??left.key??left.id).localeCompare(String(right.conflictKey??right.key??right.id)));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function regionFromChangedRegion(region,index,input){
|
|
29
|
+
if(!region)return undefined;
|
|
30
|
+
const projection=region.metadata?.changedRegionProjection??region.projection;
|
|
31
|
+
const projected=projection?.region??{};
|
|
32
|
+
const before=projection?.before??{};
|
|
33
|
+
const after=projection?.after??{};
|
|
34
|
+
const key=firstString(region.key,region.ownershipKey,projected.key,region.conflictKey,region.id);
|
|
35
|
+
const readiness=normalizeSemanticMergeReadiness(projection?.admission?.readiness??region.admission?.readiness);
|
|
36
|
+
const sourceHash=firstString(region.sourceHash,after.sourceHash,before.sourceHash,input.sourceHash);
|
|
37
|
+
return compactRecord({
|
|
38
|
+
id:firstString(region.id,projected.id,`changed_region_${index+1}`),
|
|
39
|
+
key,
|
|
40
|
+
conflictKey:firstString(region.conflictKey,projection?.conflictKey,key),
|
|
41
|
+
changeKind:region.changeKind??projection?.changeKind,
|
|
42
|
+
regionKind:region.regionKind??region.ownershipRegionKind??projected.kind,
|
|
43
|
+
granularity:region.granularity??projected.granularity,
|
|
44
|
+
precision:region.precision??projected.precision,
|
|
45
|
+
projectionRisk:region.projectionRisk??riskForRegion(region,projection),
|
|
46
|
+
language:region.language??projection?.language??input.language,
|
|
47
|
+
sourcePath:region.sourcePath??projection?.sourcePath??region.sourceSpan?.path??input.sourcePath,
|
|
48
|
+
sourceHash,
|
|
49
|
+
baseHash:firstString(before.sourceHash,input.baseHash),
|
|
50
|
+
targetHash:firstString(after.sourceHash,input.targetHash),
|
|
51
|
+
symbolId:region.symbolId??projected.symbolId,
|
|
52
|
+
symbolName:region.symbolName??region.name??projected.symbolName,
|
|
53
|
+
symbolKind:region.symbolKind??projected.symbolKind,
|
|
54
|
+
semanticNodeId:region.semanticNodeId??projected.semanticNodeId,
|
|
55
|
+
nativeAstNodeId:region.nativeAstNodeId??projected.nativeAstNodeId,
|
|
56
|
+
sourceSpan:region.sourceSpan??projected.sourceSpan,
|
|
57
|
+
sourceMapLinkIds:uniqueStrings([...(region.sourceMapLinkIds??[]),...(projection?.sourceMapLinks??[]).map((link)=>link.id)]),
|
|
58
|
+
sourceMapIds:uniqueStrings([...(region.sourceMapIds??[]),...(projection?.sourceMapLinks??[]).map((link)=>link.sourceMapId)]),
|
|
59
|
+
sourceMapMappingIds:uniqueStrings([...(region.sourceMapMappingIds??[]),...(projection?.sourceMapLinks??[]).map((link)=>link.sourceMapMappingId)]),
|
|
60
|
+
admission:compactRecord({
|
|
61
|
+
readiness,
|
|
62
|
+
action:projection?.admission?.action??region.admission?.action,
|
|
63
|
+
reasonCodes:uniqueStrings([...(projection?.admission?.reasons??[]),...(region.admission?.reasonCodes??[])]),
|
|
64
|
+
conflictKeys:uniqueStrings([...(projection?.admission?.conflictKeys??[]),...(region.admission?.conflictKeys??[])])
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function regionFromNativeSpan(span,index,input){
|
|
70
|
+
if(!span)return undefined;
|
|
71
|
+
const projection=span.metadata?.changedRegionProjection;
|
|
72
|
+
const readiness=normalizeSemanticMergeReadiness(projection?.admission?.readiness);
|
|
73
|
+
return compactRecord({
|
|
74
|
+
id:firstString(span.id,`native_span_region_${index+1}`),
|
|
75
|
+
key:firstString(span.metadata?.ownershipRegionKey,span.conflictKey,span.id),
|
|
76
|
+
conflictKey:firstString(span.conflictKey,span.metadata?.ownershipRegionKey,span.id),
|
|
77
|
+
changeKind:span.metadata?.changeKind,
|
|
78
|
+
regionKind:span.metadata?.regionKind??projection?.regionKind,
|
|
79
|
+
granularity:span.metadata?.granularity,
|
|
80
|
+
precision:span.metadata?.precision??projection?.precision,
|
|
81
|
+
projectionRisk:riskForRegion(span,projection),
|
|
82
|
+
language:span.language??input.language,
|
|
83
|
+
sourcePath:span.path??span.sourcePath??span.span?.path??input.sourcePath,
|
|
84
|
+
sourceHash:span.sourceHash??input.sourceHash,
|
|
85
|
+
baseHash:input.baseHash,
|
|
86
|
+
targetHash:input.targetHash,
|
|
87
|
+
symbolId:span.symbolId,
|
|
88
|
+
semanticNodeId:span.semanticNodeId,
|
|
89
|
+
nativeAstNodeId:span.nativeAstNodeId,
|
|
90
|
+
sourceSpan:span.span,
|
|
91
|
+
admission:compactRecord({readiness,action:projection?.admission?.action,reasonCodes:uniqueStrings(projection?.admission?.reasons),conflictKeys:uniqueStrings(projection?.admission?.conflictKeys)})
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function regionFromTouchedSymbol(symbol,index,input){
|
|
96
|
+
if(!symbol)return undefined;
|
|
97
|
+
return compactRecord({
|
|
98
|
+
id:firstString(symbol.id,`symbol_region_${index+1}`),
|
|
99
|
+
key:firstString(symbol.metadata?.ownershipRegionKey,symbol.conflictKey,symbol.id),
|
|
100
|
+
conflictKey:firstString(symbol.conflictKey,symbol.metadata?.ownershipRegionKey,symbol.id),
|
|
101
|
+
regionKind:'symbol',
|
|
102
|
+
projectionRisk:riskForRegion(symbol),
|
|
103
|
+
language:input.language,
|
|
104
|
+
sourcePath:symbol.span?.path??input.sourcePath,
|
|
105
|
+
sourceHash:input.sourceHash,
|
|
106
|
+
baseHash:input.baseHash,
|
|
107
|
+
targetHash:input.targetHash,
|
|
108
|
+
symbolId:symbol.id,
|
|
109
|
+
symbolName:symbol.name,
|
|
110
|
+
symbolKind:symbol.kind,
|
|
111
|
+
semanticNodeId:symbol.semanticNodeId,
|
|
112
|
+
nativeAstNodeId:symbol.nativeAstNodeId,
|
|
113
|
+
sourceSpan:symbol.span
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function regionFromTouchedNode(node,index,input){
|
|
118
|
+
if(!node)return undefined;
|
|
119
|
+
return compactRecord({
|
|
120
|
+
id:firstString(node.id,`semantic_node_region_${index+1}`),
|
|
121
|
+
key:firstString(node.conflictKey,node.id),
|
|
122
|
+
conflictKey:firstString(node.conflictKey,node.id),
|
|
123
|
+
regionKind:'semantic-node',
|
|
124
|
+
projectionRisk:'medium',
|
|
125
|
+
language:input.language,
|
|
126
|
+
sourcePath:input.sourcePath,
|
|
127
|
+
sourceHash:input.sourceHash,
|
|
128
|
+
baseHash:input.baseHash,
|
|
129
|
+
targetHash:input.targetHash,
|
|
130
|
+
semanticNodeId:node.id,
|
|
131
|
+
symbolName:node.name,
|
|
132
|
+
symbolKind:node.kind
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function riskForRegion(region,projection){
|
|
137
|
+
const readiness=normalizeSemanticMergeReadiness(region?.admission?.readiness??projection?.admission?.readiness);
|
|
138
|
+
const explicit=normalizeProjectionRisk(region?.projectionRisk??projection?.projectionRisk);
|
|
139
|
+
if(explicit)return explicit;
|
|
140
|
+
if(readiness)return projectionRiskFromReadiness[readiness]??'medium';
|
|
141
|
+
const preservation=sourcePreservationRisk[String(region?.sourcePreservationLevel??projection?.sourcePreservationLevel??region?.precision??projection?.precision??'').toLowerCase()];
|
|
142
|
+
if(preservation)return preservation;
|
|
143
|
+
if((projection?.sourceMapLinks??region?.sourceMapLinks??[]).length)return 'low';
|
|
144
|
+
if(region?.sourceSpan||projection?.region?.sourceSpan)return 'medium';
|
|
145
|
+
return 'unknown';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function inferProjectionRisk(input){
|
|
149
|
+
const regionRisk=input.changedSemanticRegions.reduce((current,region)=>maxProjectionRisk(current,normalizeProjectionRisk(region.projectionRisk)??riskForRegion(region)),'low');
|
|
150
|
+
const readinessRisk=projectionRiskFromReadiness[input.readiness]??'medium';
|
|
151
|
+
const conflictScore=semanticMergeConflictRiskScore(input.candidate);
|
|
152
|
+
const conflictRisk=conflictScore>=5000?'high':conflictScore>=3000?'medium':'low';
|
|
153
|
+
const patchRisk=normalizeProjectionRisk(input.patch?.risk??input.candidate?.risk)??'low';
|
|
154
|
+
const missingRegionRisk=input.conflictKeys.length&&!input.changedSemanticRegions.length?'medium':'low';
|
|
155
|
+
return [regionRisk,readinessRisk,conflictRisk,patchRisk,missingRegionRisk].reduce(maxProjectionRisk,'low');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function maxProjectionRisk(left,right){
|
|
159
|
+
return (projectionRiskSeverityRank[left]??2)>=(projectionRiskSeverityRank[right]??2)?left:right;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function normalizeProjectionRisk(value){
|
|
163
|
+
const risk=String(value??'').toLowerCase();
|
|
164
|
+
return Object.prototype.hasOwnProperty.call(projectionRiskRank,risk)?risk:undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function internalOverlaps(regions){
|
|
168
|
+
const synthetic=regions.map((region,index)=>({id:`internal_${index+1}`,candidateId:`internal_${index+1}`,readiness:region.admission?.readiness??'ready',changedSemanticRegions:[region]}));
|
|
169
|
+
return queryInternalOverlaps(synthetic);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function queryAdmissionOverlaps(list,query={}){
|
|
173
|
+
const result=[];
|
|
174
|
+
for(let leftIndex=0;leftIndex<list.length;leftIndex+=1){
|
|
175
|
+
for(let rightIndex=leftIndex+1;rightIndex<list.length;rightIndex+=1){
|
|
176
|
+
result.push(...candidateOverlaps(list[leftIndex],list[rightIndex]));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return result.filter((overlap)=>matchesOverlap(overlap,query)).sort(compareOverlapRecords);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function queryInternalOverlaps(records){
|
|
183
|
+
const result=[];
|
|
184
|
+
for(let left=0;left<records.length;left+=1){
|
|
185
|
+
for(let right=left+1;right<records.length;right+=1){
|
|
186
|
+
result.push(...candidateOverlaps(records[left],records[right]));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function candidateOverlaps(left,right){
|
|
193
|
+
const records=[];
|
|
194
|
+
for(const leftRegion of left.changedSemanticRegions??[]){
|
|
195
|
+
for(const rightRegion of right.changedSemanticRegions??[]){
|
|
196
|
+
const overlap=regionOverlap(left,right,leftRegion,rightRegion,records.length+1);
|
|
197
|
+
if(overlap)records.push(overlap);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return dedupeOverlaps(records);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function regionOverlap(left,right,leftRegion,rightRegion,index){
|
|
204
|
+
const sameConflictKey=Boolean(leftRegion.conflictKey&&rightRegion.conflictKey&&leftRegion.conflictKey===rightRegion.conflictKey);
|
|
205
|
+
const sameRegionKey=Boolean(leftRegion.key&&rightRegion.key&&leftRegion.key===rightRegion.key);
|
|
206
|
+
const spanOverlap=regionsSpanOverlap(leftRegion,rightRegion);
|
|
207
|
+
if(!sameConflictKey&&!sameRegionKey&&!spanOverlap)return undefined;
|
|
208
|
+
const overlapKind=sameConflictKey?'conflict-key':sameRegionKey?'region-key':'source-span';
|
|
209
|
+
const conflictKeys=uniqueStrings([leftRegion.conflictKey,rightRegion.conflictKey]);
|
|
210
|
+
return {
|
|
211
|
+
schema:'frontier.lang.semanticMergeCandidateOverlap.v1',
|
|
212
|
+
id:`overlap_${idFragment(left.id)}_${idFragment(right.id)}_${index}`,
|
|
213
|
+
overlapKind,
|
|
214
|
+
risk:overlapKind==='source-span'?'medium':'high',
|
|
215
|
+
readiness:maxSemanticMergeReadiness(left.readiness,right.readiness),
|
|
216
|
+
recordIds:[left.id,right.id],
|
|
217
|
+
candidateIds:uniqueStrings([left.candidateId,right.candidateId]),
|
|
218
|
+
regionIds:uniqueStrings([leftRegion.id,rightRegion.id]),
|
|
219
|
+
regionKeys:uniqueStrings([leftRegion.key,rightRegion.key]),
|
|
220
|
+
conflictKeys,
|
|
221
|
+
sourcePath:firstString(leftRegion.sourcePath,rightRegion.sourcePath),
|
|
222
|
+
leftRegion:compactOverlapRegion(leftRegion),
|
|
223
|
+
rightRegion:compactOverlapRegion(rightRegion)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function summarizeOverlaps(overlaps){
|
|
228
|
+
return {
|
|
229
|
+
total:overlaps.length,
|
|
230
|
+
hasOverlaps:overlaps.length>0,
|
|
231
|
+
byKind:countBy(overlaps.map((overlap)=>overlap.overlapKind)),
|
|
232
|
+
conflictKeys:uniqueStrings(overlaps.flatMap((overlap)=>overlap.conflictKeys??[])),
|
|
233
|
+
pairs:overlaps
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function matchesOverlap(overlap,query){
|
|
238
|
+
return matchAny(queryValues(query.id,query.ids),[overlap.id])
|
|
239
|
+
&&matchAny(queryValues(query.candidateId,query.candidateIds),overlap.candidateIds)
|
|
240
|
+
&&matchAny(queryValues(query.recordId,query.recordIds),overlap.recordIds)
|
|
241
|
+
&&matchAny(queryValues(query.regionId,query.regionIds),overlap.regionIds)
|
|
242
|
+
&&matchAny(queryValues(query.regionKey,query.regionKeys),overlap.regionKeys)
|
|
243
|
+
&&matchAny(queryValues(query.conflictKey,query.conflictKeys),overlap.conflictKeys)
|
|
244
|
+
&&matchAny(queryValues(query.sourcePath,query.sourcePaths),[overlap.sourcePath])
|
|
245
|
+
&&matchAny(queryValues(query.overlapKind,query.overlapKinds),[overlap.overlapKind])
|
|
246
|
+
&&matchAny(queryValues(query.readiness,query.readinesses),[overlap.readiness])
|
|
247
|
+
&&matchAny(queryValues(query.risk,query.risks),[overlap.risk]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function compareOverlapRecords(left,right){
|
|
251
|
+
return (projectionRiskSeverityRank[right.risk]??1)-(projectionRiskSeverityRank[left.risk]??1)
|
|
252
|
+
||(readinessSeverityRank[right.readiness]??1)-(readinessSeverityRank[left.readiness]??1)
|
|
253
|
+
||String(left.id).localeCompare(String(right.id));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function regionsSpanOverlap(left,right){
|
|
257
|
+
const leftPath=left.sourcePath??left.sourceSpan?.path;
|
|
258
|
+
const rightPath=right.sourcePath??right.sourceSpan?.path;
|
|
259
|
+
if(leftPath&&rightPath&&leftPath!==rightPath)return false;
|
|
260
|
+
return spansOverlap(left.sourceSpan,right.sourceSpan);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function spansOverlap(left,right){
|
|
264
|
+
if(!left||!right)return false;
|
|
265
|
+
const leftRange=spanRange(left);
|
|
266
|
+
const rightRange=spanRange(right);
|
|
267
|
+
if(!leftRange||!rightRange)return false;
|
|
268
|
+
return leftRange.start<=rightRange.end&&rightRange.start<=leftRange.end;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function spanRange(span){
|
|
272
|
+
const startLine=numberValue(span.startLine??span.line??span.start?.line);
|
|
273
|
+
const endLine=numberValue(span.endLine??span.end?.line??startLine);
|
|
274
|
+
if(!Number.isFinite(startLine)||!Number.isFinite(endLine))return undefined;
|
|
275
|
+
const startColumn=numberValue(span.startColumn??span.column??span.start?.column??0);
|
|
276
|
+
const endColumn=numberValue(span.endColumn??span.end?.column??startColumn);
|
|
277
|
+
return {start:startLine*100000+startColumn,end:endLine*100000+Math.max(startColumn,endColumn)};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function dedupeOverlaps(records){
|
|
281
|
+
const seen=new Set();
|
|
282
|
+
const result=[];
|
|
283
|
+
for(const record of records){
|
|
284
|
+
const key=[record.overlapKind,record.recordIds.join('|'),record.regionIds.join('|'),record.conflictKeys.join('|')].join(':');
|
|
285
|
+
if(seen.has(key))continue;
|
|
286
|
+
seen.add(key);
|
|
287
|
+
result.push(record);
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function compactOverlapRegion(region){
|
|
293
|
+
return compactRecord({
|
|
294
|
+
id:region.id,
|
|
295
|
+
key:region.key,
|
|
296
|
+
conflictKey:region.conflictKey,
|
|
297
|
+
sourcePath:region.sourcePath,
|
|
298
|
+
sourceSpan:region.sourceSpan,
|
|
299
|
+
symbolId:region.symbolId,
|
|
300
|
+
semanticNodeId:region.semanticNodeId,
|
|
301
|
+
nativeAstNodeId:region.nativeAstNodeId,
|
|
302
|
+
projectionRisk:region.projectionRisk
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function countBy(values){const counts={};for(const value of values??[]){const key=String(value??'unknown');counts[key]=(counts[key]??0)+1;}return counts;}
|
|
307
|
+
function queryValues(...values){return uniqueStrings(values.flatMap((value)=>strings(value)));}
|
|
308
|
+
function matchAny(filters,values){if(filters.length===0)return true;const valueSet=new Set(strings(values));return filters.some((filter)=>valueSet.has(filter));}
|
|
309
|
+
function spanKey(span){if(!span)return'';const range=spanRange(span);return range?`${range.start}-${range.end}`:'';}
|
|
310
|
+
function numberValue(value){const number=Number(value);return Number.isFinite(number)?number:undefined;}
|
|
311
|
+
function array(value){if(value===undefined||value===null)return[];return Array.isArray(value)?value:[value];}
|
|
312
|
+
function strings(value){return array(value).map((entry)=>String(entry??'')).filter(Boolean);}
|
|
313
|
+
function firstString(...values){return values.map((value)=>value===undefined||value===null?'':String(value)).find(Boolean);}
|
|
314
|
+
function compactRecord(value){return Object.fromEntries(Object.entries(value??{}).filter(([,entry])=>entry!==undefined&&(!Array.isArray(entry)||entry.length>0)));}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import{idFragment,normalizeSemanticMergeReadiness,uniqueStrings}from'../../native-import-utils.js';
|
|
2
|
+
import{semanticMergeConflictRiskScore}from'./semanticMergeConflicts.js';
|
|
3
|
+
import{inferProjectionRisk,internalOverlaps,normalizeChangedSemanticRegions,normalizeProjectionRisk,projectionRiskRank,queryAdmissionOverlaps,summarizeOverlaps}from'./semanticMergeCandidateRecordInternals.js';
|
|
4
|
+
|
|
5
|
+
export const SemanticMergeCandidateProjectionRisks=Object.freeze(['low','medium','high','unknown']);
|
|
6
|
+
|
|
7
|
+
const readinessRank=Object.freeze({ready:3,'ready-with-losses':2,'needs-review':1,blocked:0});
|
|
8
|
+
|
|
9
|
+
export function createSemanticMergeCandidateAdmissionRecord(input={},options={}){
|
|
10
|
+
const source=input?.kind==='frontier.lang.nativeSourceChangeSet'?input:options.changeSet??input;
|
|
11
|
+
const candidate=options.candidate??source.mergeCandidate??source.candidate??input;
|
|
12
|
+
const patch=options.patch??source.patch??candidate.patch;
|
|
13
|
+
const readiness=normalizeSemanticMergeReadiness(options.readiness??candidate.readiness??source.readiness)
|
|
14
|
+
??options.readiness??candidate.readiness??source.readiness??'needs-review';
|
|
15
|
+
const evidenceRecords=uniqueRecords([
|
|
16
|
+
...array(options.evidence),
|
|
17
|
+
...array(source.evidence),
|
|
18
|
+
...array(candidate.evidence),
|
|
19
|
+
...array(patch?.evidence)
|
|
20
|
+
]);
|
|
21
|
+
const evidenceIds=uniqueStrings([
|
|
22
|
+
...strings(options.evidenceIds),
|
|
23
|
+
...strings(source.evidenceIds),
|
|
24
|
+
...strings(candidate.evidenceIds),
|
|
25
|
+
...evidenceRecords.map((record)=>record?.id)
|
|
26
|
+
]);
|
|
27
|
+
const proofIds=uniqueStrings([
|
|
28
|
+
...strings(options.proofIds),
|
|
29
|
+
...strings(source.proofIds),
|
|
30
|
+
...strings(candidate.proofIds),
|
|
31
|
+
...evidenceRecords.filter((record)=>isProofEvidence(record)).map((record)=>record.id)
|
|
32
|
+
]);
|
|
33
|
+
const changedSemanticRegions=normalizeChangedSemanticRegions({
|
|
34
|
+
candidate,
|
|
35
|
+
changedRegions:[
|
|
36
|
+
...array(options.changedRegions),
|
|
37
|
+
...array(source.changedRegions),
|
|
38
|
+
...array(candidate.changedSemanticRegions),
|
|
39
|
+
...array(candidate.changedRegions),
|
|
40
|
+
...array(candidate.metadata?.changedSemanticRegions),
|
|
41
|
+
...array(candidate.metadata?.changedRegions)
|
|
42
|
+
],
|
|
43
|
+
language:options.language??candidate.language??source.language,
|
|
44
|
+
sourcePath:options.sourcePath??candidate.sourcePath??source.sourcePath,
|
|
45
|
+
baseHash:firstString(options.baseHash,source.beforeHash,source.baseHash,patch?.baseHash,candidate.baseHash),
|
|
46
|
+
targetHash:firstString(options.targetHash,source.afterHash,source.targetHash,patch?.targetHash,candidate.targetHash),
|
|
47
|
+
sourceHash:firstString(options.sourceHash,source.afterHash,source.targetHash,candidate.targetHash,source.beforeHash,source.baseHash,candidate.baseHash)
|
|
48
|
+
});
|
|
49
|
+
const conflictKeys=uniqueStrings([
|
|
50
|
+
...strings(options.conflictKeys),
|
|
51
|
+
...strings(source.conflictKeys),
|
|
52
|
+
...strings(candidate.conflictKeys),
|
|
53
|
+
...changedSemanticRegions.flatMap((region)=>[region.conflictKey,...array(region.admission?.conflictKeys)]),
|
|
54
|
+
...array(candidate.conflictClasses).flatMap((record)=>record?.conflictKeys??[]),
|
|
55
|
+
...(candidate.conflictSummary?.conflictKeys??candidate.metadata?.conflictSummary?.conflictKeys??[]),
|
|
56
|
+
...(source.metadata?.semanticMergeConflictSummary?.conflictKeys??[])
|
|
57
|
+
]);
|
|
58
|
+
const baseHash=firstString(options.baseHash,source.beforeHash,source.baseHash,patch?.baseHash,candidate.baseHash,...changedSemanticRegions.map((region)=>region.baseHash));
|
|
59
|
+
const targetHash=firstString(options.targetHash,source.afterHash,source.targetHash,patch?.targetHash,candidate.targetHash,...changedSemanticRegions.map((region)=>region.targetHash));
|
|
60
|
+
const sourceHashes=uniqueStrings([
|
|
61
|
+
...strings(options.sourceHashes),
|
|
62
|
+
...sourceHashValues(source.sourceHashes),
|
|
63
|
+
...sourceHashValues(candidate.sourceHashes),
|
|
64
|
+
source.beforeHash,
|
|
65
|
+
source.afterHash,
|
|
66
|
+
source.baseHash,
|
|
67
|
+
source.targetHash,
|
|
68
|
+
candidate.baseHash,
|
|
69
|
+
candidate.targetHash,
|
|
70
|
+
patch?.baseHash,
|
|
71
|
+
patch?.targetHash,
|
|
72
|
+
...changedSemanticRegions.flatMap((region)=>[region.sourceHash,region.baseHash,region.targetHash])
|
|
73
|
+
]);
|
|
74
|
+
const projectionRisk=normalizeProjectionRisk(options.projectionRisk??candidate.projectionRisk??candidate.risk)
|
|
75
|
+
??inferProjectionRisk({readiness,candidate,patch,changedSemanticRegions,conflictKeys});
|
|
76
|
+
const overlaps=internalOverlaps(changedSemanticRegions);
|
|
77
|
+
const readinessSortKey=semanticMergeCandidateReadinessSortKey({
|
|
78
|
+
readiness,
|
|
79
|
+
projectionRisk,
|
|
80
|
+
evidenceIds,
|
|
81
|
+
proofIds,
|
|
82
|
+
changedSemanticRegions,
|
|
83
|
+
overlaps
|
|
84
|
+
});
|
|
85
|
+
const candidateId=firstString(options.candidateId,candidate.id,source.mergeCandidateId);
|
|
86
|
+
const id=options.id??(source.kind==='frontier.lang.semanticMergeCandidateAdmissionRecord'?source.id:undefined)
|
|
87
|
+
??`semantic_merge_candidate_${idFragment(firstString(candidateId,source.id,source.sourcePath,source.language,'candidate'))}`;
|
|
88
|
+
return {
|
|
89
|
+
kind:'frontier.lang.semanticMergeCandidateAdmissionRecord',
|
|
90
|
+
version:1,
|
|
91
|
+
schema:'frontier.lang.semanticMergeCandidateAdmissionRecord.v1',
|
|
92
|
+
id,
|
|
93
|
+
candidateId,
|
|
94
|
+
importResultId:firstString(options.importResultId,candidate.importResultId,source.importResultId,source.after?.id,source.before?.id),
|
|
95
|
+
patchId:firstString(options.patchId,candidate.patchId,patch?.id,source.patchId),
|
|
96
|
+
language:options.language??candidate.language??source.language,
|
|
97
|
+
sourcePath:options.sourcePath??candidate.sourcePath??source.sourcePath,
|
|
98
|
+
readiness,
|
|
99
|
+
readinessSortKey,
|
|
100
|
+
projectionRisk,
|
|
101
|
+
sourceHashes:{baseHash,targetHash,values:sourceHashes},
|
|
102
|
+
baseHash,
|
|
103
|
+
targetHash,
|
|
104
|
+
changedSemanticRegions,
|
|
105
|
+
conflictKeys,
|
|
106
|
+
evidenceIds,
|
|
107
|
+
proofIds,
|
|
108
|
+
overlapSummary:summarizeOverlaps(overlaps),
|
|
109
|
+
admission:{
|
|
110
|
+
readiness,
|
|
111
|
+
reviewRequired:readiness!=='ready'||projectionRisk!=='low'||overlaps.length>0,
|
|
112
|
+
action:admissionAction({readiness,projectionRisk,overlaps}),
|
|
113
|
+
sortKey:readinessSortKey,
|
|
114
|
+
reasonCodes:uniqueStrings([
|
|
115
|
+
...strings(options.reasonCodes),
|
|
116
|
+
...strings(source.reasons),
|
|
117
|
+
...strings(candidate.reasons),
|
|
118
|
+
...(projectionRisk!=='low'?[`projection-risk:${projectionRisk}`]:[]),
|
|
119
|
+
...(overlaps.length?['overlapping-semantic-regions']:[])
|
|
120
|
+
]),
|
|
121
|
+
conflictKeys
|
|
122
|
+
},
|
|
123
|
+
index:{
|
|
124
|
+
candidateIds:uniqueStrings([candidateId]),
|
|
125
|
+
importResultIds:uniqueStrings([candidate.importResultId,source.importResultId,source.after?.id,source.before?.id]),
|
|
126
|
+
patchIds:uniqueStrings([candidate.patchId,patch?.id,source.patchId]),
|
|
127
|
+
sourcePaths:uniqueStrings([options.sourcePath,candidate.sourcePath,source.sourcePath,...changedSemanticRegions.map((region)=>region.sourcePath)]),
|
|
128
|
+
sourceHashes,
|
|
129
|
+
baseHashes:uniqueStrings([baseHash]),
|
|
130
|
+
targetHashes:uniqueStrings([targetHash]),
|
|
131
|
+
regionIds:uniqueStrings(changedSemanticRegions.map((region)=>region.id)),
|
|
132
|
+
regionKeys:uniqueStrings(changedSemanticRegions.map((region)=>region.key)),
|
|
133
|
+
regionKinds:uniqueStrings(changedSemanticRegions.map((region)=>region.regionKind)),
|
|
134
|
+
conflictKeys,
|
|
135
|
+
evidenceIds,
|
|
136
|
+
proofIds,
|
|
137
|
+
readinesses:uniqueStrings([readiness,...changedSemanticRegions.map((region)=>region.admission?.readiness)]),
|
|
138
|
+
projectionRisks:uniqueStrings([projectionRisk,...changedSemanticRegions.map((region)=>region.projectionRisk)])
|
|
139
|
+
},
|
|
140
|
+
summary:{
|
|
141
|
+
changedSemanticRegions:changedSemanticRegions.length,
|
|
142
|
+
conflictKeys:conflictKeys.length,
|
|
143
|
+
evidenceIds:evidenceIds.length,
|
|
144
|
+
proofIds:proofIds.length,
|
|
145
|
+
overlaps:overlaps.length,
|
|
146
|
+
readiness,
|
|
147
|
+
projectionRisk,
|
|
148
|
+
reviewRequired:readiness!=='ready'||projectionRisk!=='low'||overlaps.length>0
|
|
149
|
+
},
|
|
150
|
+
metadata:compactRecord({
|
|
151
|
+
sourceChangeSetId:source.kind==='frontier.lang.nativeSourceChangeSet'?source.id:undefined,
|
|
152
|
+
conflictRiskScore:semanticMergeConflictRiskScore(candidate),
|
|
153
|
+
conflictSummary:candidate.conflictSummary??candidate.metadata?.conflictSummary??source.metadata?.semanticMergeConflictSummary,
|
|
154
|
+
changedRegionProjectionSummary:source.metadata?.changedRegionProjectionSummary??candidate.metadata?.changedRegionProjectionSummary,
|
|
155
|
+
compact:true,
|
|
156
|
+
note:'Semantic merge candidate admission records normalize changed regions, hashes, evidence, conflict keys, readiness, projection risk, and overlap signals for coordinator merge admission.',
|
|
157
|
+
...options.metadata
|
|
158
|
+
})
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function decorateSemanticMergeCandidateForAdmission(input={},options={}){
|
|
163
|
+
const admissionRecord=createSemanticMergeCandidateAdmissionRecord(input,options);
|
|
164
|
+
const candidate=options.candidate??(input?.kind==='frontier.lang.nativeSourceChangeSet'?input.mergeCandidate:input);
|
|
165
|
+
return {
|
|
166
|
+
...candidate,
|
|
167
|
+
changedSemanticRegions:admissionRecord.changedSemanticRegions,
|
|
168
|
+
sourceHashes:admissionRecord.sourceHashes,
|
|
169
|
+
evidenceIds:admissionRecord.evidenceIds,
|
|
170
|
+
proofIds:admissionRecord.proofIds,
|
|
171
|
+
projectionRisk:admissionRecord.projectionRisk,
|
|
172
|
+
readinessSortKey:admissionRecord.readinessSortKey,
|
|
173
|
+
mergeAdmission:admissionRecord
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function sortSemanticMergeCandidateAdmissionRecords(records,options={}){
|
|
178
|
+
const normalized=array(records).filter(Boolean).map((record)=>
|
|
179
|
+
record.kind==='frontier.lang.semanticMergeCandidateAdmissionRecord'?record:createSemanticMergeCandidateAdmissionRecord(record)
|
|
180
|
+
);
|
|
181
|
+
const sorted=normalized.sort((left,right)=>
|
|
182
|
+
right.readinessSortKey-left.readinessSortKey
|
|
183
|
+
||(projectionRiskRank[right.projectionRisk]??1)-(projectionRiskRank[left.projectionRisk]??1)
|
|
184
|
+
||String(left.candidateId??left.id??'').localeCompare(String(right.candidateId??right.id??''))
|
|
185
|
+
);
|
|
186
|
+
return options.desc===false?sorted.reverse():sorted;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function querySemanticMergeCandidateAdmissionOverlaps(records,query={}){
|
|
190
|
+
const list=array(records).filter(Boolean).map((record)=>
|
|
191
|
+
record.kind==='frontier.lang.semanticMergeCandidateAdmissionRecord'?record:createSemanticMergeCandidateAdmissionRecord(record)
|
|
192
|
+
);
|
|
193
|
+
return queryAdmissionOverlaps(list,query);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function semanticMergeCandidateReadinessSortKey(candidateOrRecord){
|
|
197
|
+
const readiness=normalizeSemanticMergeReadiness(candidateOrRecord?.readiness)??candidateOrRecord?.readiness??'needs-review';
|
|
198
|
+
const projectionRisk=normalizeProjectionRisk(candidateOrRecord?.projectionRisk)??'unknown';
|
|
199
|
+
const evidenceIds=array(candidateOrRecord?.evidenceIds);
|
|
200
|
+
const proofIds=array(candidateOrRecord?.proofIds);
|
|
201
|
+
const changedSemanticRegions=array(candidateOrRecord?.changedSemanticRegions);
|
|
202
|
+
const overlaps=array(candidateOrRecord?.overlaps??candidateOrRecord?.overlapSummary?.pairs);
|
|
203
|
+
return (readinessRank[readiness]??readinessRank['needs-review'])*100000
|
|
204
|
+
+(projectionRiskRank[projectionRisk]??1)*10000
|
|
205
|
+
+Math.min(99,evidenceIds.length)*100
|
|
206
|
+
+Math.min(99,proofIds.length)*25
|
|
207
|
+
-Math.min(99,changedSemanticRegions.length)
|
|
208
|
+
-Math.min(99,overlaps.length)*250;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function admissionAction(input){
|
|
212
|
+
if(input.readiness==='blocked'||input.projectionRisk==='high')return'block';
|
|
213
|
+
if(input.readiness==='needs-review'||input.projectionRisk!=='low'||input.overlaps.length)return'prioritize-review';
|
|
214
|
+
return'admit';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function uniqueRecords(records){
|
|
218
|
+
const seen=new Set();
|
|
219
|
+
const result=[];
|
|
220
|
+
for(const record of records.filter(Boolean)){
|
|
221
|
+
const id=record.id??`record_${result.length+1}`;
|
|
222
|
+
if(seen.has(id))continue;
|
|
223
|
+
seen.add(id);
|
|
224
|
+
result.push(record.id?record:{...record,id});
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function sourceHashValues(value){
|
|
230
|
+
if(value&&typeof value==='object'&&!Array.isArray(value))return [value.baseHash,value.targetHash,...array(value.values)].filter(Boolean);
|
|
231
|
+
return strings(value);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isProofEvidence(record){
|
|
235
|
+
return /proof|test|trace|verify|verification|benchmark|behavior|contract/i.test(String(record?.kind??record?.metadata?.kind??record?.type??''));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function array(value){if(value===undefined||value===null)return[];return Array.isArray(value)?value:[value];}
|
|
239
|
+
function strings(value){return array(value).map((entry)=>String(entry??'')).filter(Boolean);}
|
|
240
|
+
function firstString(...values){return values.map((value)=>value===undefined||value===null?'':String(value)).find(Boolean);}
|
|
241
|
+
function compactRecord(value){return Object.fromEntries(Object.entries(value??{}).filter(([,entry])=>entry!==undefined&&(!Array.isArray(entry)||entry.length>0)));}
|