@shapeshift-labs/frontier-lang-compiler 0.2.87 → 0.2.88
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.
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { SemanticPatchBundleRecord } from './semantic-patch-bundle.js';
|
|
2
|
+
|
|
3
|
+
export type SemanticPatchBundleOverlapKind =
|
|
4
|
+
| 'operation-content'
|
|
5
|
+
| 'edit-content'
|
|
6
|
+
| 'semantic-edit-key'
|
|
7
|
+
| 'semantic-identity'
|
|
8
|
+
| 'source-identity'
|
|
9
|
+
| 'region'
|
|
10
|
+
| 'conflict-key'
|
|
11
|
+
| 'source-path'
|
|
12
|
+
| string;
|
|
13
|
+
|
|
14
|
+
export type SemanticPatchBundleOverlapStatus =
|
|
15
|
+
| 'duplicate'
|
|
16
|
+
| 'semantic-overlap'
|
|
17
|
+
| 'source-overlap'
|
|
18
|
+
| 'independent'
|
|
19
|
+
| string;
|
|
20
|
+
|
|
21
|
+
export interface SemanticPatchBundleOverlapShared {
|
|
22
|
+
readonly operationContentHashes: readonly string[];
|
|
23
|
+
readonly editContentHashes: readonly string[];
|
|
24
|
+
readonly semanticEditKeys: readonly string[];
|
|
25
|
+
readonly semanticIdentityHashes: readonly string[];
|
|
26
|
+
readonly sourceIdentityHashes: readonly string[];
|
|
27
|
+
readonly regionKeys: readonly string[];
|
|
28
|
+
readonly conflictKeys: readonly string[];
|
|
29
|
+
readonly sourcePaths: readonly string[];
|
|
30
|
+
readonly baseHashes: readonly string[];
|
|
31
|
+
readonly targetHashes: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SemanticPatchBundleOverlapAdmission {
|
|
35
|
+
readonly status: SemanticPatchBundleOverlapStatus;
|
|
36
|
+
readonly reviewRequired: boolean;
|
|
37
|
+
readonly autoMergeClaim: false;
|
|
38
|
+
readonly reasonCodes: readonly string[];
|
|
39
|
+
readonly sharedKeyCount: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface SemanticPatchBundleOverlapRecord {
|
|
43
|
+
readonly kind: 'frontier.lang.semanticPatchBundleOverlapRecord';
|
|
44
|
+
readonly version: 1;
|
|
45
|
+
readonly schema: 'frontier.lang.semanticPatchBundleOverlapRecord.v1';
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly leftBundleId: string;
|
|
48
|
+
readonly rightBundleId: string;
|
|
49
|
+
readonly overlapKinds: readonly SemanticPatchBundleOverlapKind[];
|
|
50
|
+
readonly shared: SemanticPatchBundleOverlapShared;
|
|
51
|
+
readonly admission: SemanticPatchBundleOverlapAdmission;
|
|
52
|
+
readonly score: number;
|
|
53
|
+
readonly summary: {
|
|
54
|
+
readonly sharedKeys: number;
|
|
55
|
+
readonly duplicateSignals: number;
|
|
56
|
+
readonly semanticSignals: number;
|
|
57
|
+
readonly sourceSignals: number;
|
|
58
|
+
readonly baseHashMismatch: boolean;
|
|
59
|
+
readonly targetHashMismatch: boolean;
|
|
60
|
+
};
|
|
61
|
+
readonly metadata?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CompareSemanticPatchBundleRecordsOptions {
|
|
65
|
+
readonly id?: string;
|
|
66
|
+
readonly includeSourcePaths?: boolean;
|
|
67
|
+
readonly reviewIndependent?: boolean;
|
|
68
|
+
readonly metadata?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SemanticPatchBundleOverlapQuery {
|
|
72
|
+
readonly includeIndependent?: boolean;
|
|
73
|
+
readonly includeSourcePaths?: boolean;
|
|
74
|
+
readonly reviewIndependent?: boolean;
|
|
75
|
+
readonly metadata?: Record<string, unknown>;
|
|
76
|
+
readonly id?: string | readonly string[];
|
|
77
|
+
readonly ids?: readonly string[];
|
|
78
|
+
readonly bundleId?: string | readonly string[];
|
|
79
|
+
readonly bundleIds?: readonly string[];
|
|
80
|
+
readonly status?: SemanticPatchBundleOverlapStatus | readonly string[];
|
|
81
|
+
readonly statuses?: readonly string[];
|
|
82
|
+
readonly admissionStatus?: SemanticPatchBundleOverlapStatus | readonly string[];
|
|
83
|
+
readonly admissionStatuses?: readonly string[];
|
|
84
|
+
readonly overlapKind?: SemanticPatchBundleOverlapKind | readonly string[];
|
|
85
|
+
readonly overlapKinds?: readonly string[];
|
|
86
|
+
readonly reasonCode?: string | readonly string[];
|
|
87
|
+
readonly reasonCodes?: readonly string[];
|
|
88
|
+
readonly sourcePath?: string | readonly string[];
|
|
89
|
+
readonly sourcePaths?: readonly string[];
|
|
90
|
+
readonly conflictKey?: string | readonly string[];
|
|
91
|
+
readonly conflictKeys?: readonly string[];
|
|
92
|
+
readonly semanticEditKey?: string | readonly string[];
|
|
93
|
+
readonly semanticEditKeys?: readonly string[];
|
|
94
|
+
readonly operationContentHash?: string | readonly string[];
|
|
95
|
+
readonly operationContentHashes?: readonly string[];
|
|
96
|
+
readonly editContentHash?: string | readonly string[];
|
|
97
|
+
readonly editContentHashes?: readonly string[];
|
|
98
|
+
readonly reviewRequired?: boolean;
|
|
99
|
+
readonly minScore?: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export declare const SemanticPatchBundleOverlapKinds: readonly SemanticPatchBundleOverlapKind[];
|
|
103
|
+
export declare const SemanticPatchBundleOverlapStatuses: readonly SemanticPatchBundleOverlapStatus[];
|
|
104
|
+
export declare function compareSemanticPatchBundleRecords(
|
|
105
|
+
left?: SemanticPatchBundleRecord | Record<string, unknown>,
|
|
106
|
+
right?: SemanticPatchBundleRecord | Record<string, unknown>,
|
|
107
|
+
options?: CompareSemanticPatchBundleRecordsOptions
|
|
108
|
+
): SemanticPatchBundleOverlapRecord;
|
|
109
|
+
export declare function querySemanticPatchBundleOverlaps(
|
|
110
|
+
records: SemanticPatchBundleRecord | readonly SemanticPatchBundleRecord[],
|
|
111
|
+
query?: SemanticPatchBundleOverlapQuery
|
|
112
|
+
): readonly SemanticPatchBundleOverlapRecord[];
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export * from './declarations/semantic-edit-script.js';
|
|
|
20
20
|
export * from './declarations/semantic-lineage.js';
|
|
21
21
|
export * from './declarations/semantic-history.js';
|
|
22
22
|
export * from './declarations/semantic-patch-bundle.js';
|
|
23
|
+
export * from './declarations/semantic-patch-bundle-overlaps.js';
|
|
23
24
|
export * from './declarations/bidirectional-target-change.js';
|
|
24
25
|
export * from './declarations/semantic-impact.js';
|
|
25
26
|
export * from './declarations/semantic-sidecar.js';
|
package/dist/index.js
CHANGED
|
@@ -67,6 +67,7 @@ export { projectNativeImportToSource } from './internal/index-impl/projectNative
|
|
|
67
67
|
export { queryNativeParserFeatureMatrix } from './internal/index-impl/queryNativeParserFeatureMatrix.js';
|
|
68
68
|
export { queryProjectionReadinessMatrix } from './internal/index-impl/queryProjectionReadinessMatrix.js';
|
|
69
69
|
export { createSemanticPatchBundleRecord, querySemanticPatchBundleRecords, SemanticPatchBundleAdmissionStatuses } from './internal/index-impl/semanticPatchBundleRecords.js';
|
|
70
|
+
export { compareSemanticPatchBundleRecords, querySemanticPatchBundleOverlaps, SemanticPatchBundleOverlapKinds, SemanticPatchBundleOverlapStatuses } from './internal/index-impl/semanticPatchBundleOverlaps.js';
|
|
70
71
|
export { createSemanticMergeCandidateAdmissionRecord, decorateSemanticMergeCandidateForAdmission, querySemanticMergeCandidateAdmissionOverlaps, SemanticMergeCandidateProjectionRisks, semanticMergeCandidateReadinessSortKey, sortSemanticMergeCandidateAdmissionRecords } from './internal/index-impl/semanticMergeCandidateRecords.js';
|
|
71
72
|
export { querySemanticMergeConflictClasses, SemanticMergeConflictClasses, semanticMergeConflictRiskScore, sortSemanticMergeCandidatesByConflictRisk, summarizeSemanticMergeConflicts } from './internal/index-impl/semanticMergeConflicts.js';
|
|
72
73
|
export { createSemanticEditScript, SemanticEditScriptAdmissionStatuses } from './internal/index-impl/semanticEditScripts.js';
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import{idFragment,uniqueStrings as uniqueRawStrings}from'../../native-import-utils.js';
|
|
2
|
+
|
|
3
|
+
export const SemanticPatchBundleOverlapKinds=Object.freeze([
|
|
4
|
+
'operation-content',
|
|
5
|
+
'edit-content',
|
|
6
|
+
'semantic-edit-key',
|
|
7
|
+
'semantic-identity',
|
|
8
|
+
'source-identity',
|
|
9
|
+
'region',
|
|
10
|
+
'conflict-key',
|
|
11
|
+
'source-path'
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export const SemanticPatchBundleOverlapStatuses=Object.freeze([
|
|
15
|
+
'duplicate',
|
|
16
|
+
'semantic-overlap',
|
|
17
|
+
'source-overlap',
|
|
18
|
+
'independent'
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const KIND_FIELDS=Object.freeze({
|
|
22
|
+
'operation-content':'operationContentHashes',
|
|
23
|
+
'edit-content':'editContentHashes',
|
|
24
|
+
'semantic-edit-key':'semanticEditKeys',
|
|
25
|
+
'semantic-identity':'semanticIdentityHashes',
|
|
26
|
+
'source-identity':'sourceIdentityHashes',
|
|
27
|
+
region:'regionKeys',
|
|
28
|
+
'conflict-key':'conflictKeys',
|
|
29
|
+
'source-path':'sourcePaths'
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export function compareSemanticPatchBundleRecords(left={},right={},options={}){
|
|
33
|
+
const leftIndex=bundleIndex(left);
|
|
34
|
+
const rightIndex=bundleIndex(right);
|
|
35
|
+
const shared=sharedIndex(leftIndex,rightIndex,options);
|
|
36
|
+
const overlapKinds=SemanticPatchBundleOverlapKinds
|
|
37
|
+
.filter((kind)=>shared[KIND_FIELDS[kind]]?.length);
|
|
38
|
+
const admission=overlapAdmission(shared,{leftIndex,rightIndex,options});
|
|
39
|
+
return{
|
|
40
|
+
kind:'frontier.lang.semanticPatchBundleOverlapRecord',
|
|
41
|
+
version:1,
|
|
42
|
+
schema:'frontier.lang.semanticPatchBundleOverlapRecord.v1',
|
|
43
|
+
id:options.id??`semantic_patch_bundle_overlap_${idFragment(left?.id??'left')}_${idFragment(right?.id??'right')}`,
|
|
44
|
+
leftBundleId:String(left?.id??'left'),
|
|
45
|
+
rightBundleId:String(right?.id??'right'),
|
|
46
|
+
overlapKinds,
|
|
47
|
+
shared,
|
|
48
|
+
admission,
|
|
49
|
+
score:overlapScore(admission.status,shared,admission.reasonCodes),
|
|
50
|
+
summary:{
|
|
51
|
+
sharedKeys:countShared(shared),
|
|
52
|
+
duplicateSignals:shared.operationContentHashes.length+shared.editContentHashes.length,
|
|
53
|
+
semanticSignals:shared.semanticEditKeys.length+shared.semanticIdentityHashes.length+shared.sourceIdentityHashes.length,
|
|
54
|
+
sourceSignals:shared.regionKeys.length+shared.conflictKeys.length+shared.sourcePaths.length,
|
|
55
|
+
baseHashMismatch:admission.reasonCodes.includes('base-hash-mismatch'),
|
|
56
|
+
targetHashMismatch:admission.reasonCodes.includes('target-hash-mismatch')
|
|
57
|
+
},
|
|
58
|
+
metadata:compactRecord(options.metadata)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function querySemanticPatchBundleOverlaps(records,query={}){
|
|
63
|
+
const list=array(records).filter(Boolean);
|
|
64
|
+
const result=[];
|
|
65
|
+
const compareOptions={
|
|
66
|
+
includeSourcePaths:query.includeSourcePaths,
|
|
67
|
+
reviewIndependent:query.reviewIndependent,
|
|
68
|
+
metadata:query.metadata
|
|
69
|
+
};
|
|
70
|
+
for(let leftIndex=0;leftIndex<list.length;leftIndex+=1){
|
|
71
|
+
for(let rightIndex=leftIndex+1;rightIndex<list.length;rightIndex+=1){
|
|
72
|
+
const overlap=compareSemanticPatchBundleRecords(list[leftIndex],list[rightIndex],compareOptions);
|
|
73
|
+
if(matchesOverlap(overlap,query))result.push(overlap);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result.sort((left,right)=>right.score-left.score||left.leftBundleId.localeCompare(right.leftBundleId)
|
|
77
|
+
||left.rightBundleId.localeCompare(right.rightBundleId));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function sharedIndex(left,right,options){
|
|
81
|
+
return{
|
|
82
|
+
operationContentHashes:intersect(left.operationContentHashes,right.operationContentHashes),
|
|
83
|
+
editContentHashes:intersect(left.editContentHashes,right.editContentHashes),
|
|
84
|
+
semanticEditKeys:intersect(left.semanticEditKeys,right.semanticEditKeys),
|
|
85
|
+
semanticIdentityHashes:intersect(left.semanticIdentityHashes,right.semanticIdentityHashes),
|
|
86
|
+
sourceIdentityHashes:intersect(left.sourceIdentityHashes,right.sourceIdentityHashes),
|
|
87
|
+
regionKeys:intersect(left.regionKeys,right.regionKeys),
|
|
88
|
+
conflictKeys:intersect(left.conflictKeys,right.conflictKeys),
|
|
89
|
+
sourcePaths:options.includeSourcePaths===false?[]:intersect(left.sourcePaths,right.sourcePaths),
|
|
90
|
+
baseHashes:intersect(left.baseHashes,right.baseHashes),
|
|
91
|
+
targetHashes:intersect(left.targetHashes,right.targetHashes)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function overlapAdmission(shared,{leftIndex,rightIndex,options}){
|
|
96
|
+
const duplicate=shared.operationContentHashes.length||shared.editContentHashes.length;
|
|
97
|
+
const semantic=shared.semanticEditKeys.length||shared.semanticIdentityHashes.length||shared.sourceIdentityHashes.length;
|
|
98
|
+
const source=shared.regionKeys.length||shared.conflictKeys.length||shared.sourcePaths.length;
|
|
99
|
+
const status=duplicate?'duplicate':semantic?'semantic-overlap':source?'source-overlap':'independent';
|
|
100
|
+
const reasonCodes=uniqueStrings([
|
|
101
|
+
shared.operationContentHashes.length?'same-operation-content':undefined,
|
|
102
|
+
shared.editContentHashes.length?'same-edit-content':undefined,
|
|
103
|
+
shared.semanticEditKeys.length?'same-semantic-edit-key':undefined,
|
|
104
|
+
shared.semanticIdentityHashes.length?'same-semantic-identity':undefined,
|
|
105
|
+
shared.sourceIdentityHashes.length?'same-source-identity':undefined,
|
|
106
|
+
shared.regionKeys.length?'same-region-key':undefined,
|
|
107
|
+
shared.conflictKeys.length?'same-conflict-key':undefined,
|
|
108
|
+
shared.sourcePaths.length?'same-source-path':undefined,
|
|
109
|
+
status!=='independent'&&disjointNonEmpty(leftIndex.baseHashes,rightIndex.baseHashes)?'base-hash-mismatch':undefined,
|
|
110
|
+
status!=='independent'&&disjointNonEmpty(leftIndex.targetHashes,rightIndex.targetHashes)?'target-hash-mismatch':undefined
|
|
111
|
+
]);
|
|
112
|
+
return{
|
|
113
|
+
status,
|
|
114
|
+
reviewRequired:options.reviewIndependent?true:status!=='independent',
|
|
115
|
+
autoMergeClaim:false,
|
|
116
|
+
reasonCodes,
|
|
117
|
+
sharedKeyCount:countShared(shared)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function bundleIndex(record){
|
|
122
|
+
const index=record?.index??{};
|
|
123
|
+
return{
|
|
124
|
+
baseHashes:uniqueStrings([record?.baseHash,...strings(index.baseHashes)]),
|
|
125
|
+
targetHashes:uniqueStrings([record?.targetHash,...strings(index.targetHashes)]),
|
|
126
|
+
sourcePaths:uniqueStrings([record?.sourcePath,...strings(index.sourcePaths)]),
|
|
127
|
+
regionKeys:uniqueStrings(index.regionKeys),
|
|
128
|
+
conflictKeys:uniqueStrings(index.conflictKeys),
|
|
129
|
+
semanticEditKeys:uniqueStrings(index.semanticEditKeys),
|
|
130
|
+
semanticIdentityHashes:uniqueStrings(index.semanticIdentityHashes),
|
|
131
|
+
sourceIdentityHashes:uniqueStrings(index.sourceIdentityHashes),
|
|
132
|
+
operationContentHashes:uniqueStrings(index.operationContentHashes),
|
|
133
|
+
editContentHashes:uniqueStrings(index.editContentHashes)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function matchesOverlap(overlap,query){
|
|
138
|
+
const statusFilters=queryValues(query.status,query.statuses,query.admissionStatus,query.admissionStatuses);
|
|
139
|
+
const includeIndependent=query.includeIndependent===true||statusFilters.includes('independent');
|
|
140
|
+
if(!includeIndependent&&overlap.admission.status==='independent')return false;
|
|
141
|
+
return matchAny(queryValues(query.id,query.ids),[overlap.id])
|
|
142
|
+
&&matchAny(queryValues(query.bundleId,query.bundleIds),[overlap.leftBundleId,overlap.rightBundleId])
|
|
143
|
+
&&matchAny(statusFilters,[overlap.admission.status])
|
|
144
|
+
&&matchAny(queryValues(query.overlapKind,query.overlapKinds),overlap.overlapKinds)
|
|
145
|
+
&&matchAny(queryValues(query.reasonCode,query.reasonCodes),overlap.admission.reasonCodes)
|
|
146
|
+
&&matchAny(queryValues(query.sourcePath,query.sourcePaths),overlap.shared.sourcePaths)
|
|
147
|
+
&&matchAny(queryValues(query.conflictKey,query.conflictKeys),overlap.shared.conflictKeys)
|
|
148
|
+
&&matchAny(queryValues(query.semanticEditKey,query.semanticEditKeys),overlap.shared.semanticEditKeys)
|
|
149
|
+
&&matchAny(queryValues(query.operationContentHash,query.operationContentHashes),overlap.shared.operationContentHashes)
|
|
150
|
+
&&matchAny(queryValues(query.editContentHash,query.editContentHashes),overlap.shared.editContentHashes)
|
|
151
|
+
&&(query.reviewRequired===undefined||overlap.admission.reviewRequired===query.reviewRequired)
|
|
152
|
+
&&(query.minScore===undefined||overlap.score>=Number(query.minScore));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function overlapScore(status,shared,reasonCodes){
|
|
156
|
+
const base=status==='duplicate'?100:status==='semantic-overlap'?75:status==='source-overlap'?35:0;
|
|
157
|
+
const sharedBonus=Math.min(20,countShared(shared));
|
|
158
|
+
const stalePenalty=reasonCodes.includes('base-hash-mismatch')||reasonCodes.includes('target-hash-mismatch')?15:0;
|
|
159
|
+
return Math.max(0,base+sharedBonus-stalePenalty);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function countShared(shared){
|
|
163
|
+
return Object.values(shared).reduce((total,values)=>total+(Array.isArray(values)?values.length:0),0);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function intersect(left,right){const rightSet=new Set(right??[]);return uniqueStrings((left??[]).filter((value)=>rightSet.has(value)));}
|
|
167
|
+
function disjointNonEmpty(left,right){return Boolean(left?.length&&right?.length&&intersect(left,right).length===0);}
|
|
168
|
+
function queryValues(...values){return uniqueStrings(values.flatMap((value)=>strings(value)));}
|
|
169
|
+
function matchAny(filters,values){if(filters.length===0)return true;const valueSet=new Set(strings(values));return filters.some((filter)=>valueSet.has(filter));}
|
|
170
|
+
function array(value){if(value===undefined||value===null)return[];return Array.isArray(value)?value:[value];}
|
|
171
|
+
function strings(value){return array(value).map((entry)=>String(entry??'')).filter(Boolean);}
|
|
172
|
+
function uniqueStrings(values){return uniqueRawStrings((values??[]).filter((entry)=>entry!==undefined&&entry!==null&&String(entry)!==''));}
|
|
173
|
+
function compactRecord(value){return Object.fromEntries(Object.entries(value??{}).filter(([,entry])=>entry!==undefined&&(!Array.isArray(entry)||entry.length>0)));}
|
package/package.json
CHANGED