@shapeshift-labs/frontier-lang-compiler 0.2.102 → 0.2.104
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 +13 -0
- package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
- package/dist/declarations/bidirectional-target-change.d.ts +10 -0
- package/dist/declarations/js-ts-safe-member-merge.d.ts +58 -0
- package/dist/declarations/js-ts-safe-merge.d.ts +120 -0
- package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
- package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
- package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
- package/dist/declarations/native-import-losses.d.ts +3 -0
- package/dist/declarations/native-project-admission-semantic-evidence.d.ts +34 -0
- package/dist/declarations/native-project-admission.d.ts +6 -10
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
- package/dist/declarations/semantic-edit-script.d.ts +10 -4
- package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
- package/dist/declarations/semantic-patch-bundle-overlaps.d.ts +1 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
- package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
- package/dist/declarations/semantic-transform-identity.d.ts +3 -0
- package/dist/declarations/source-preservation.d.ts +72 -0
- package/dist/declarations/universal-capability.d.ts +4 -0
- package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
- package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
- package/dist/declarations/universal-conversion-plan.d.ts +6 -1
- package/dist/declarations/universal-representation-coverage.d.ts +90 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
- package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
- package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
- package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
- package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
- package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
- package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +14 -2
- package/dist/internal/index-impl/diffNativeSymbols.js +82 -1
- package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
- package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +1 -1
- package/dist/internal/index-impl/projectImportAdmissionSemanticWarnings.js +178 -0
- package/dist/internal/index-impl/projectImportAdmissionSummaries.js +22 -3
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +54 -69
- package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
- package/dist/internal/index-impl/replaySemanticEditProjection.js +78 -78
- package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
- package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
- package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
- package/dist/internal/index-impl/semanticEditImportProjection.js +53 -0
- package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
- package/dist/internal/index-impl/semanticEditProjectionRecord.js +108 -0
- package/dist/internal/index-impl/semanticEditReplayAnchors.js +63 -0
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
- package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
- package/dist/internal/index-impl/semanticEditScripts.js +4 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +32 -0
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
- package/dist/internal/index-impl/semanticPatchBundleAdmission.js +92 -9
- package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +33 -16
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
- package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
- package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
- package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
- package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
- package/dist/js-ts-safe-member-merge-result.js +158 -0
- package/dist/js-ts-safe-member-merge.js +202 -0
- package/dist/js-ts-safe-merge-analyze.js +279 -0
- package/dist/js-ts-safe-merge-constants.js +50 -0
- package/dist/js-ts-safe-merge-context.js +118 -0
- package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
- package/dist/js-ts-safe-merge-ledger.js +85 -0
- package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
- package/dist/js-ts-safe-merge-parse-statements.js +155 -0
- package/dist/js-ts-safe-merge-plan.js +190 -0
- package/dist/js-ts-safe-merge.js +175 -0
- package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
- package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
- package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
- package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
- package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
- package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
- package/dist/js-ts-semantic-merge-contracts.js +217 -0
- package/dist/js-ts-semantic-merge-member-containers.js +100 -0
- package/dist/js-ts-semantic-merge-member-keys.js +142 -0
- package/dist/js-ts-semantic-merge-member-segments.js +185 -0
- package/dist/js-ts-semantic-merge-member-source.js +64 -0
- package/dist/js-ts-semantic-merge-member-utils.js +18 -0
- package/dist/js-ts-semantic-merge-parse.js +15 -0
- package/dist/js-ts-semantic-merge.js +21 -0
- package/dist/lightweight-dependency-effects.js +51 -0
- package/dist/lightweight-dependency-language.js +12 -1
- package/dist/lightweight-dependency-relations.js +14 -27
- package/dist/native-region-scanner-core.js +33 -1
- package/dist/native-region-scanner-csharp.js +151 -0
- package/dist/native-region-scanner-dart.js +91 -0
- package/dist/native-region-scanner-dynamic.js +21 -151
- package/dist/native-region-scanner-functional.js +40 -13
- package/dist/native-region-scanner-java.js +97 -0
- package/dist/native-region-scanner-js-class.js +100 -0
- package/dist/native-region-scanner-js-helpers.js +28 -86
- package/dist/native-region-scanner-js-imports.js +121 -1
- package/dist/native-region-scanner-js-nested.js +96 -8
- package/dist/native-region-scanner-js-structure.js +27 -0
- package/dist/native-region-scanner-js-types.js +99 -0
- package/dist/native-region-scanner-js.js +70 -118
- package/dist/native-region-scanner-kotlin.js +94 -0
- package/dist/native-region-scanner-main.js +15 -181
- package/dist/native-region-scanner-php.js +80 -0
- package/dist/native-region-scanner-python.js +62 -0
- package/dist/native-region-scanner-ruby.js +72 -0
- package/dist/native-region-scanner-scala.js +91 -0
- package/dist/native-region-scanner-spans.js +74 -0
- package/dist/native-region-scanner-swift.js +155 -0
- package/dist/native-region-scanner.js +14 -10
- package/dist/native-source-ledger-helpers.js +195 -0
- package/dist/native-source-ledger.js +306 -0
- package/dist/native-source-preservation-scanner.js +4 -0
- package/dist/semantic-import-callsite-regions.js +136 -0
- package/dist/semantic-import-effect-regions.js +283 -0
- package/dist/semantic-import-regions.js +11 -2
- package/dist/semantic-import-sidecar-entry.js +16 -2
- package/dist/semantic-import-sidecar-types.d.ts +2 -0
- package/dist/semantic-sidecar-example.js +68 -0
- package/dist/universal-capability-matrix.js +23 -0
- package/dist/universal-conversion-artifact-query.js +79 -2
- package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
- package/dist/universal-conversion-artifact-summary.js +33 -1
- package/dist/universal-conversion-artifacts.js +13 -48
- package/dist/universal-conversion-plan-scoring.js +21 -1
- package/dist/universal-conversion-plan-summary.js +30 -0
- package/dist/universal-conversion-plan.js +25 -9
- package/dist/universal-conversion-route-metadata.js +96 -0
- package/dist/universal-conversion-route-operations.js +7 -0
- package/dist/universal-representation-coverage.js +193 -0
- package/package.json +1 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
import { deleteChangeKinds, modifyChangeKinds, riskRank, severityRank } from './js-ts-semantic-conflict-sidecar-constants.js';
|
|
3
|
+
|
|
4
|
+
export function changePairs(changes) {
|
|
5
|
+
const pairs = [];
|
|
6
|
+
for (let leftIndex = 0; leftIndex < changes.length; leftIndex += 1) {
|
|
7
|
+
for (let rightIndex = leftIndex + 1; rightIndex < changes.length; rightIndex += 1) {
|
|
8
|
+
pairs.push([changes[leftIndex], changes[rightIndex]]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return pairs;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function duplicateGroups(entries, keyFn) {
|
|
15
|
+
const groups = new Map();
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const key = keyFn(entry);
|
|
18
|
+
if (!key || /^(\|)+$/.test(key)) continue;
|
|
19
|
+
groups.set(key, [...(groups.get(key) ?? []), entry]);
|
|
20
|
+
}
|
|
21
|
+
return [...groups.values()].filter((group) => group.length > 1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function dedupeSidecars(records) {
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
const result = [];
|
|
27
|
+
for (const record of records) {
|
|
28
|
+
const key = `${record.class}:${record.id}:${record.reasonCodes.join('|')}`;
|
|
29
|
+
if (seen.has(key)) continue;
|
|
30
|
+
seen.add(key);
|
|
31
|
+
result.push(record);
|
|
32
|
+
}
|
|
33
|
+
return result.sort(compareSidecars);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function compareSidecars(left, right) {
|
|
37
|
+
return (riskRank[right.risk] ?? 2) - (riskRank[left.risk] ?? 2)
|
|
38
|
+
|| (severityRank[right.severity] ?? 2) - (severityRank[left.severity] ?? 2)
|
|
39
|
+
|| String(left.class).localeCompare(String(right.class))
|
|
40
|
+
|| String(left.id).localeCompare(String(right.id));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function mergeContext(input, options) {
|
|
44
|
+
return {
|
|
45
|
+
language: options.language ?? input.language ?? input.sourceLanguage ?? 'typescript',
|
|
46
|
+
sourcePath: options.sourcePath ?? input.sourcePath ?? input.path,
|
|
47
|
+
readiness: options.readiness ?? input.readiness,
|
|
48
|
+
expectedSourceHash: firstString(
|
|
49
|
+
options.expectedSourceHash,
|
|
50
|
+
input.expectedSourceHash,
|
|
51
|
+
input.expectedHash,
|
|
52
|
+
input.sourceHashAssertions?.expectedSourceHash,
|
|
53
|
+
input.sourceHashes?.expectedSourceHash
|
|
54
|
+
),
|
|
55
|
+
currentSourceHash: firstString(
|
|
56
|
+
options.currentSourceHash,
|
|
57
|
+
input.currentSourceHash,
|
|
58
|
+
input.actualSourceHash,
|
|
59
|
+
input.sourceHashAssertions?.currentSourceHash,
|
|
60
|
+
input.sourceHashes?.currentSourceHash
|
|
61
|
+
)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function keysForChange(change) {
|
|
66
|
+
return uniqueStrings([
|
|
67
|
+
change.key,
|
|
68
|
+
change.conflictKey,
|
|
69
|
+
change.regionKey,
|
|
70
|
+
change.semanticKey,
|
|
71
|
+
change.anchorKey
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function keysForAffected(entry) {
|
|
76
|
+
return uniqueStrings([
|
|
77
|
+
entry.key,
|
|
78
|
+
entry.conflictKey,
|
|
79
|
+
entry.regionKey,
|
|
80
|
+
entry.semanticKey,
|
|
81
|
+
entry.anchorKey,
|
|
82
|
+
entry.containerKey,
|
|
83
|
+
entry.id
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function lossText(entry) {
|
|
88
|
+
return [
|
|
89
|
+
entry?.kind,
|
|
90
|
+
entry?.code,
|
|
91
|
+
entry?.reasonCode,
|
|
92
|
+
...(entry?.reasonCodes ?? []),
|
|
93
|
+
entry?.message,
|
|
94
|
+
entry?.summary,
|
|
95
|
+
entry?.category,
|
|
96
|
+
entry?.lossClass
|
|
97
|
+
].filter(Boolean).join(' ');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function lossReasonCodes(entry) {
|
|
101
|
+
return uniqueStrings([
|
|
102
|
+
...strings(entry?.reasonCode),
|
|
103
|
+
...strings(entry?.reasonCodes),
|
|
104
|
+
entry?.code,
|
|
105
|
+
entry?.kind
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function classesFromRecords(records) {
|
|
110
|
+
return records.map((record) => record.class);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function summarySuggestedOutcome(records, readiness) {
|
|
114
|
+
if (!records.length) return 'auto-merge-safe';
|
|
115
|
+
const outcomes = uniqueStrings(records.map((record) => record.suggestedOutcome));
|
|
116
|
+
if (readiness === 'blocked') return outcomes.includes('rerun-parser-and-ledger-before-merge')
|
|
117
|
+
? 'rerun-parser-and-ledger-before-merge'
|
|
118
|
+
: 'manual-merge-required';
|
|
119
|
+
return outcomes[0] ?? 'human-review';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function highestSeverity(records) {
|
|
123
|
+
const value = Math.max(0, ...records.map((record) => severityRank[record.severity] ?? 2));
|
|
124
|
+
return Object.keys(severityRank).find((severity) => severityRank[severity] === value) ?? 'info';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function highestRisk(records) {
|
|
128
|
+
const value = Math.max(0, ...records.map((record) => riskRank[record.risk] ?? 2));
|
|
129
|
+
return Object.keys(riskRank).find((risk) => riskRank[risk] === value) ?? 'low';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function normalizeSeverity(value) {
|
|
133
|
+
const severity = String(value ?? '').toLowerCase();
|
|
134
|
+
return Object.prototype.hasOwnProperty.call(severityRank, severity) ? severity : undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function normalizeRisk(value) {
|
|
138
|
+
const risk = String(value ?? '').toLowerCase();
|
|
139
|
+
return Object.prototype.hasOwnProperty.call(riskRank, risk) ? risk : undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function normalizeChangeKind(value) {
|
|
143
|
+
const text = String(value ?? 'modify').toLowerCase();
|
|
144
|
+
if (text === 'removed') return 'delete';
|
|
145
|
+
if (text === 'added') return 'insert';
|
|
146
|
+
if (text === 'updated') return 'modify';
|
|
147
|
+
return text;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function isDeleteChange(change) {
|
|
151
|
+
return deleteChangeKinds.test(change.changeKind);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function isModifyChange(change) {
|
|
155
|
+
return modifyChangeKinds.test(change.changeKind);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function isInsertChange(change) {
|
|
159
|
+
return /^(?:insert|add|create)$/i.test(change.changeKind);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function numberValue(...values) {
|
|
163
|
+
for (const value of values) {
|
|
164
|
+
const number = Number(value);
|
|
165
|
+
if (Number.isFinite(number)) return number;
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function numberOrUndefined(value) {
|
|
171
|
+
const number = Number(value);
|
|
172
|
+
return Number.isFinite(number) ? number : undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function array(value) {
|
|
176
|
+
if (value === undefined || value === null) return [];
|
|
177
|
+
return Array.isArray(value) ? value : [value];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function strings(value) {
|
|
181
|
+
return array(value).flatMap((entry) => Array.isArray(entry) ? strings(entry) : [String(entry ?? '')]).filter(Boolean);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function firstString(...values) {
|
|
185
|
+
return values.map((value) => value === undefined || value === null ? '' : String(value)).find(Boolean);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function compactRecord(value) {
|
|
189
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && entry !== ''));
|
|
190
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { countBy, idFragment, maxSemanticMergeReadiness, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
import { JsTsSemanticConflictSidecarClasses } from './js-ts-semantic-conflict-sidecar-constants.js';
|
|
3
|
+
import { deleteModifySidecars, duplicateDeclarationSidecars, explicitSidecars, orderedListSidecars, parserLedgerLossSidecars, sameRegionSidecars, staleSourceHashSidecars, unsupportedSyntaxSidecars } from './js-ts-semantic-conflict-sidecar-detectors.js';
|
|
4
|
+
import { normalizedChanges } from './js-ts-semantic-conflict-sidecar-normalize.js';
|
|
5
|
+
import { array, classesFromRecords, compactRecord, dedupeSidecars, highestRisk, highestSeverity, mergeContext, summarySuggestedOutcome } from './js-ts-semantic-conflict-sidecar-utils.js';
|
|
6
|
+
|
|
7
|
+
export { JsTsSemanticConflictSidecarClasses } from './js-ts-semantic-conflict-sidecar-constants.js';
|
|
8
|
+
|
|
9
|
+
export function createJsTsSemanticConflictSidecars(input = {}, options = {}) {
|
|
10
|
+
const context = mergeContext(input, options);
|
|
11
|
+
const changes = normalizedChanges(input, context);
|
|
12
|
+
const records = dedupeSidecars([
|
|
13
|
+
...explicitSidecars(input, context),
|
|
14
|
+
...sameRegionSidecars(changes, context),
|
|
15
|
+
...deleteModifySidecars(changes, context),
|
|
16
|
+
...duplicateDeclarationSidecars(input, changes, context),
|
|
17
|
+
...orderedListSidecars(input, changes, context),
|
|
18
|
+
...parserLedgerLossSidecars(input, context),
|
|
19
|
+
...staleSourceHashSidecars(input, context),
|
|
20
|
+
...unsupportedSyntaxSidecars(input, context)
|
|
21
|
+
]);
|
|
22
|
+
const summary = summarizeJsTsSemanticConflictSidecars(records, context);
|
|
23
|
+
const id = options.id
|
|
24
|
+
?? input.id
|
|
25
|
+
?? `js_ts_semantic_conflict_sidecars_${idFragment(context.sourcePath ?? context.language ?? 'source')}`;
|
|
26
|
+
return {
|
|
27
|
+
kind: 'frontier.lang.jsTsSemanticMergeConflictSidecars',
|
|
28
|
+
version: 1,
|
|
29
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictSidecars.v1',
|
|
30
|
+
id,
|
|
31
|
+
language: context.language,
|
|
32
|
+
sourcePath: context.sourcePath,
|
|
33
|
+
expectedSourceHash: context.expectedSourceHash,
|
|
34
|
+
currentSourceHash: context.currentSourceHash,
|
|
35
|
+
conflicts: records,
|
|
36
|
+
sidecars: records,
|
|
37
|
+
summary,
|
|
38
|
+
admission: {
|
|
39
|
+
status: records.length ? summary.readiness : 'ready',
|
|
40
|
+
reviewRequired: records.length > 0,
|
|
41
|
+
autoMergeSafe: records.length === 0,
|
|
42
|
+
suggestedOutcome: summary.suggestedOutcome,
|
|
43
|
+
reasonCodes: summary.reasonCodes
|
|
44
|
+
},
|
|
45
|
+
metadata: compactRecord({
|
|
46
|
+
conflictSidecarSource: 'js-ts-semantic-merge',
|
|
47
|
+
...options.metadata,
|
|
48
|
+
...input.metadata
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function summarizeJsTsSemanticConflictSidecars(records = [], context = {}) {
|
|
54
|
+
const list = array(records).filter(Boolean);
|
|
55
|
+
const classes = uniqueStrings(list.map((record) => record.class));
|
|
56
|
+
const reasonCodes = uniqueStrings(list.flatMap((record) => record.reasonCodes ?? []));
|
|
57
|
+
const affectedSourcePaths = uniqueStrings([
|
|
58
|
+
context.sourcePath,
|
|
59
|
+
...list.flatMap((record) => record.affected?.sourcePaths ?? [])
|
|
60
|
+
]);
|
|
61
|
+
const affectedKeys = uniqueStrings(list.flatMap((record) => record.affected?.keys ?? []));
|
|
62
|
+
const readiness = list.reduce(
|
|
63
|
+
(current, record) => maxSemanticMergeReadiness(current, record.readiness ?? 'needs-review'),
|
|
64
|
+
normalizeSemanticMergeReadiness(context.readiness) ?? 'ready'
|
|
65
|
+
);
|
|
66
|
+
return {
|
|
67
|
+
schema: 'frontier.lang.jsTsSemanticConflictSidecarSummary.v1',
|
|
68
|
+
total: list.length,
|
|
69
|
+
classes,
|
|
70
|
+
byClass: countBy(classesFromRecords(list)),
|
|
71
|
+
bySeverity: countBy(list.map((record) => record.severity ?? 'warning')),
|
|
72
|
+
byRisk: countBy(list.map((record) => record.risk ?? 'medium')),
|
|
73
|
+
highestSeverity: highestSeverity(list),
|
|
74
|
+
highestRisk: highestRisk(list),
|
|
75
|
+
readiness,
|
|
76
|
+
affectedSourcePaths,
|
|
77
|
+
affectedKeys,
|
|
78
|
+
reasonCodes,
|
|
79
|
+
suggestedOutcome: summarySuggestedOutcome(list, readiness)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { idFragment, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
|
|
2
|
+
import { JsTsSemanticMergeGateStatuses } from './js-ts-semantic-merge-contracts.js';
|
|
3
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
4
|
+
|
|
5
|
+
export function normalizeConflictSides(sides = {}) {
|
|
6
|
+
return compactRecord({
|
|
7
|
+
base: normalizeConflictSide('base', sides.base),
|
|
8
|
+
left: normalizeConflictSide('left', sides.left),
|
|
9
|
+
right: normalizeConflictSide('right', sides.right)
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeConflictSide(side, value) {
|
|
14
|
+
if (!value) return undefined;
|
|
15
|
+
return compactRecord({
|
|
16
|
+
side: firstString(value.side, side),
|
|
17
|
+
sourcePath: firstString(value.sourcePath),
|
|
18
|
+
sourceHash: firstString(value.sourceHash),
|
|
19
|
+
editIds: uniqueStrings(value.editIds),
|
|
20
|
+
regionIds: uniqueStrings(value.regionIds),
|
|
21
|
+
tokenIds: uniqueStrings(value.tokenIds),
|
|
22
|
+
summary: firstString(value.summary),
|
|
23
|
+
metadata: value.metadata
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function normalizeGateChecks(checks, fallbackStatus) {
|
|
28
|
+
return array(checks).filter(Boolean).map((check, index) => {
|
|
29
|
+
const status = normalizeGateStatus(check.status) ?? fallbackStatus;
|
|
30
|
+
const name = firstString(check.name, check.id, `check-${index + 1}`);
|
|
31
|
+
const reasonCodes = uniqueStrings([
|
|
32
|
+
...strings(check.reasonCodes),
|
|
33
|
+
...strings(check.reasons),
|
|
34
|
+
...(status === 'passed' ? [] : [`js-ts-merge-check:${status}`])
|
|
35
|
+
]);
|
|
36
|
+
const stablePayload = compactRecord({
|
|
37
|
+
name,
|
|
38
|
+
status,
|
|
39
|
+
readiness: normalizeSemanticMergeReadiness(check.readiness) ?? check.readiness,
|
|
40
|
+
reasonCodes,
|
|
41
|
+
evidenceIds: uniqueStrings(check.evidenceIds)
|
|
42
|
+
});
|
|
43
|
+
const hash = firstString(check.hash) || hashSemanticValue(stablePayload);
|
|
44
|
+
return compactRecord({
|
|
45
|
+
id: firstString(check.id) || `js_ts_merge_check_${idFragment(name)}_${idFragment(hash)}`,
|
|
46
|
+
name,
|
|
47
|
+
status,
|
|
48
|
+
readiness: stablePayload.readiness,
|
|
49
|
+
reasonCodes,
|
|
50
|
+
evidenceIds: stablePayload.evidenceIds,
|
|
51
|
+
hash,
|
|
52
|
+
metadata: check.metadata
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function readinessForRisk(risk) {
|
|
58
|
+
if (risk === 'high') return 'blocked';
|
|
59
|
+
if (risk === 'low') return 'ready-with-losses';
|
|
60
|
+
return 'needs-review';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function readinessForGateStatus(status, checks, conflictExplanationIds) {
|
|
64
|
+
if (status === 'blocked' || status === 'failed') return 'blocked';
|
|
65
|
+
if (conflictExplanationIds.length > 0) return 'needs-review';
|
|
66
|
+
if (checks.some((check) => check.status === 'failed' || check.status === 'blocked')) return 'blocked';
|
|
67
|
+
if (status === 'warning' || checks.some((check) => check.status === 'warning')) return 'ready-with-losses';
|
|
68
|
+
if (status === 'passed') return 'ready';
|
|
69
|
+
return 'needs-review';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function actionForConflict({ risk, readiness }) {
|
|
73
|
+
if (readiness === 'blocked' || risk === 'high') return 'block';
|
|
74
|
+
if (readiness === 'ready') return 'merge';
|
|
75
|
+
return 'manual-review';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function actionForGate({ status, readiness, mergeable }) {
|
|
79
|
+
if (mergeable) return 'merge';
|
|
80
|
+
if (status === 'blocked' || status === 'failed' || readiness === 'blocked') return 'block';
|
|
81
|
+
if (status === 'skipped') return 'skip';
|
|
82
|
+
return 'review';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function titleForConflictClass(conflictClass) {
|
|
86
|
+
return String(conflictClass).split(/[-_]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function normalizeRisk(value) {
|
|
90
|
+
const risk = String(value ?? '').toLowerCase();
|
|
91
|
+
return risk === 'low' || risk === 'medium' || risk === 'high' ? risk : undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function normalizeGateStatus(value) {
|
|
95
|
+
const status = String(value ?? '').toLowerCase();
|
|
96
|
+
return JsTsSemanticMergeGateStatuses.includes(status) ? status : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function booleanValue(...values) {
|
|
100
|
+
for (const value of values) {
|
|
101
|
+
if (typeof value === 'boolean') return value;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function numberValue(...values) {
|
|
107
|
+
for (const value of values) {
|
|
108
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function array(value) {
|
|
114
|
+
if (value === undefined || value === null) return [];
|
|
115
|
+
return Array.isArray(value) ? value : [value];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function strings(value) {
|
|
119
|
+
return array(value).map((entry) => entry === undefined || entry === null ? '' : String(entry)).filter(Boolean);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function firstString(...values) {
|
|
123
|
+
return values.map((value) => value === undefined || value === null ? '' : String(value)).find(Boolean);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function compactRecord(value) {
|
|
127
|
+
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined));
|
|
128
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { idFragment, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
|
|
3
|
+
|
|
4
|
+
export const JsTsSemanticMergeConflictClasses = Object.freeze([
|
|
5
|
+
'same-token-edit',
|
|
6
|
+
'same-region-edit',
|
|
7
|
+
'delete-modify',
|
|
8
|
+
'move-edit',
|
|
9
|
+
'rename-rename',
|
|
10
|
+
'import-order',
|
|
11
|
+
'type-surface-drift',
|
|
12
|
+
'trivia-preservation',
|
|
13
|
+
'behavior-evidence-needed'
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const JsTsSemanticMergeGateStatuses = Object.freeze([
|
|
17
|
+
'passed',
|
|
18
|
+
'warning',
|
|
19
|
+
'failed',
|
|
20
|
+
'skipped',
|
|
21
|
+
'blocked'
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
import { actionForConflict, actionForGate, array, booleanValue, compactRecord, firstString, normalizeConflictSides, normalizeGateChecks, normalizeGateStatus, normalizeRisk, numberValue, readinessForGateStatus, readinessForRisk, strings, titleForConflictClass } from './js-ts-semantic-merge-contract-helpers.js';
|
|
25
|
+
|
|
26
|
+
export function createJsTsSemanticMergeConflictExplanation(input = {}, options = {}) {
|
|
27
|
+
const conflictClass = firstString(options.conflictClass, input.conflictClass, input.class, 'same-region-edit');
|
|
28
|
+
const risk = normalizeRisk(firstString(options.risk, input.risk, input.severity)) ?? 'medium';
|
|
29
|
+
const readiness = normalizeSemanticMergeReadiness(firstString(options.readiness, input.readiness)) ?? readinessForRisk(risk);
|
|
30
|
+
const language = firstString(options.language, input.language);
|
|
31
|
+
const sourcePath = firstString(options.sourcePath, input.sourcePath);
|
|
32
|
+
const conflictKeys = uniqueStrings([
|
|
33
|
+
...strings(input.conflictKeys),
|
|
34
|
+
input.conflictKey,
|
|
35
|
+
...strings(options.conflictKeys),
|
|
36
|
+
options.conflictKey
|
|
37
|
+
]);
|
|
38
|
+
const matchIds = uniqueStrings([...strings(input.matchIds), ...strings(options.matchIds)]);
|
|
39
|
+
const editIds = uniqueStrings([...strings(input.editIds), ...strings(options.editIds)]);
|
|
40
|
+
const regionIds = uniqueStrings([...strings(input.regionIds), ...strings(options.regionIds)]);
|
|
41
|
+
const tokenIds = uniqueStrings([...strings(input.tokenIds), ...strings(options.tokenIds)]);
|
|
42
|
+
const triviaIds = uniqueStrings([...strings(input.triviaIds), ...strings(options.triviaIds)]);
|
|
43
|
+
const evidenceIds = uniqueStrings([...strings(input.evidenceIds), ...strings(options.evidenceIds)]);
|
|
44
|
+
const reasonCodes = uniqueStrings([
|
|
45
|
+
...strings(input.reasonCodes),
|
|
46
|
+
...strings(input.reasons),
|
|
47
|
+
...strings(options.reasonCodes),
|
|
48
|
+
`js-ts-conflict:${conflictClass}`
|
|
49
|
+
]);
|
|
50
|
+
const sides = normalizeConflictSides(options.sides ?? input.sides);
|
|
51
|
+
const title = firstString(options.title, input.title, titleForConflictClass(conflictClass));
|
|
52
|
+
const summary = firstString(options.summary, input.summary, input.message);
|
|
53
|
+
const suggestedAction = firstString(options.suggestedAction, input.suggestedAction, actionForConflict({ risk, readiness }));
|
|
54
|
+
const stablePayload = compactRecord({
|
|
55
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictExplanation.v1',
|
|
56
|
+
conflictClass,
|
|
57
|
+
risk,
|
|
58
|
+
readiness,
|
|
59
|
+
language,
|
|
60
|
+
sourcePath,
|
|
61
|
+
title,
|
|
62
|
+
summary,
|
|
63
|
+
conflictKeys,
|
|
64
|
+
matchIds,
|
|
65
|
+
editIds,
|
|
66
|
+
regionIds,
|
|
67
|
+
tokenIds,
|
|
68
|
+
triviaIds,
|
|
69
|
+
evidenceIds,
|
|
70
|
+
reasonCodes,
|
|
71
|
+
sides,
|
|
72
|
+
suggestedAction
|
|
73
|
+
});
|
|
74
|
+
const hash = hashSemanticValue(stablePayload);
|
|
75
|
+
const stableId = firstString(options.stableId, input.stableId)
|
|
76
|
+
|| `js_ts_merge_conflict_${idFragment(firstString(conflictKeys[0], sourcePath, conflictClass))}_${idFragment(hash)}`;
|
|
77
|
+
return compactRecord({
|
|
78
|
+
kind: 'frontier.lang.jsTsSemanticMergeConflictExplanation',
|
|
79
|
+
version: 1,
|
|
80
|
+
schema: 'frontier.lang.jsTsSemanticMergeConflictExplanation.v1',
|
|
81
|
+
id: firstString(options.id, input.id, stableId),
|
|
82
|
+
stableId,
|
|
83
|
+
hash,
|
|
84
|
+
conflictClass,
|
|
85
|
+
risk,
|
|
86
|
+
readiness,
|
|
87
|
+
language,
|
|
88
|
+
sourcePath,
|
|
89
|
+
title,
|
|
90
|
+
summary,
|
|
91
|
+
conflictKeys,
|
|
92
|
+
matchIds,
|
|
93
|
+
editIds,
|
|
94
|
+
regionIds,
|
|
95
|
+
tokenIds,
|
|
96
|
+
triviaIds,
|
|
97
|
+
evidenceIds,
|
|
98
|
+
reasonCodes,
|
|
99
|
+
sides,
|
|
100
|
+
suggestedAction,
|
|
101
|
+
metadata: compactRecord({
|
|
102
|
+
normalizedBy: 'frontier-lang-compiler/js-ts-semantic-merge',
|
|
103
|
+
structuralContract: true,
|
|
104
|
+
...(input.metadata ?? {}),
|
|
105
|
+
...(options.metadata ?? {})
|
|
106
|
+
})
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function createJsTsSemanticMergeGateResult(input = {}, options = {}) {
|
|
111
|
+
const gateId = firstString(options.gateId, input.gateId, input.id, 'js-ts-semantic-merge');
|
|
112
|
+
const status = normalizeGateStatus(firstString(options.status, input.status)) ?? 'skipped';
|
|
113
|
+
const conflicts = [
|
|
114
|
+
...array(input.conflictExplanations),
|
|
115
|
+
...array(input.conflicts),
|
|
116
|
+
...array(options.conflictExplanations),
|
|
117
|
+
...array(options.conflicts)
|
|
118
|
+
].filter(Boolean).map((conflict) => createJsTsSemanticMergeConflictExplanation(conflict));
|
|
119
|
+
const conflictExplanationIds = uniqueStrings([
|
|
120
|
+
...strings(input.conflictExplanationIds),
|
|
121
|
+
...strings(options.conflictExplanationIds),
|
|
122
|
+
...conflicts.map((conflict) => conflict.id)
|
|
123
|
+
]);
|
|
124
|
+
const checks = normalizeGateChecks([
|
|
125
|
+
...array(input.checks),
|
|
126
|
+
...array(options.checks)
|
|
127
|
+
], status);
|
|
128
|
+
const readiness = normalizeSemanticMergeReadiness(firstString(options.readiness, input.readiness)) ?? readinessForGateStatus(status, checks, conflictExplanationIds);
|
|
129
|
+
const evidenceIds = uniqueStrings([
|
|
130
|
+
...strings(input.evidenceIds),
|
|
131
|
+
...strings(options.evidenceIds),
|
|
132
|
+
...checks.flatMap((check) => strings(check.evidenceIds)),
|
|
133
|
+
...conflicts.flatMap((conflict) => conflict.evidenceIds)
|
|
134
|
+
]);
|
|
135
|
+
const reasonCodes = uniqueStrings([
|
|
136
|
+
...strings(input.reasonCodes),
|
|
137
|
+
...strings(options.reasonCodes),
|
|
138
|
+
...checks.flatMap((check) => strings(check.reasonCodes)),
|
|
139
|
+
...conflicts.flatMap((conflict) => conflict.reasonCodes),
|
|
140
|
+
...(status === 'passed' ? [] : [`js-ts-merge-gate:${status}`])
|
|
141
|
+
]);
|
|
142
|
+
const structuredEditIds = uniqueStrings([
|
|
143
|
+
...strings(input.structuredEditIds),
|
|
144
|
+
...strings(options.structuredEditIds),
|
|
145
|
+
...array(input.structuredEdits).map((edit) => edit?.id),
|
|
146
|
+
...array(options.structuredEdits).map((edit) => edit?.id)
|
|
147
|
+
]);
|
|
148
|
+
const semanticRegionIds = uniqueStrings([
|
|
149
|
+
...strings(input.semanticRegionIds),
|
|
150
|
+
...strings(options.semanticRegionIds),
|
|
151
|
+
...array(input.semanticRegions).map((region) => region?.id),
|
|
152
|
+
...array(options.semanticRegions).map((region) => region?.id)
|
|
153
|
+
]);
|
|
154
|
+
const mergeable = booleanValue(options.mergeable, input.mergeable)
|
|
155
|
+
?? (status === 'passed' && readiness === 'ready' && conflictExplanationIds.length === 0);
|
|
156
|
+
const action = firstString(options.action, input.action, actionForGate({ status, readiness, mergeable }));
|
|
157
|
+
const confidence = numberValue(options.confidence, input.confidence, status === 'passed' ? 1 : 0);
|
|
158
|
+
const failedChecks = checks.filter((check) => check.status === 'failed').length;
|
|
159
|
+
const warningChecks = checks.filter((check) => check.status === 'warning').length;
|
|
160
|
+
const blockedChecks = checks.filter((check) => check.status === 'blocked').length;
|
|
161
|
+
const summary = {
|
|
162
|
+
checks: checks.length,
|
|
163
|
+
conflicts: conflictExplanationIds.length,
|
|
164
|
+
failedChecks,
|
|
165
|
+
warningChecks,
|
|
166
|
+
blockedChecks,
|
|
167
|
+
evidenceIds: evidenceIds.length,
|
|
168
|
+
reasonCodes
|
|
169
|
+
};
|
|
170
|
+
const stablePayload = compactRecord({
|
|
171
|
+
schema: 'frontier.lang.jsTsSemanticMergeGateResult.v1',
|
|
172
|
+
gateId,
|
|
173
|
+
status,
|
|
174
|
+
readiness,
|
|
175
|
+
mergeable,
|
|
176
|
+
action,
|
|
177
|
+
confidence,
|
|
178
|
+
checks,
|
|
179
|
+
conflictExplanationIds,
|
|
180
|
+
structuredEditIds,
|
|
181
|
+
semanticRegionIds,
|
|
182
|
+
evidenceIds,
|
|
183
|
+
reasonCodes,
|
|
184
|
+
summary
|
|
185
|
+
});
|
|
186
|
+
const hash = hashSemanticValue(stablePayload);
|
|
187
|
+
const stableId = firstString(options.stableId, input.stableId)
|
|
188
|
+
|| `js_ts_merge_gate_${idFragment(gateId)}_${idFragment(hash)}`;
|
|
189
|
+
return compactRecord({
|
|
190
|
+
kind: 'frontier.lang.jsTsSemanticMergeGateResult',
|
|
191
|
+
version: 1,
|
|
192
|
+
schema: 'frontier.lang.jsTsSemanticMergeGateResult.v1',
|
|
193
|
+
id: firstString(options.id, input.id, stableId),
|
|
194
|
+
stableId,
|
|
195
|
+
hash,
|
|
196
|
+
gateId,
|
|
197
|
+
status,
|
|
198
|
+
readiness,
|
|
199
|
+
mergeable,
|
|
200
|
+
action,
|
|
201
|
+
confidence,
|
|
202
|
+
checks,
|
|
203
|
+
conflictExplanations: conflicts,
|
|
204
|
+
conflictExplanationIds,
|
|
205
|
+
structuredEditIds,
|
|
206
|
+
semanticRegionIds,
|
|
207
|
+
evidenceIds,
|
|
208
|
+
reasonCodes,
|
|
209
|
+
summary,
|
|
210
|
+
metadata: compactRecord({
|
|
211
|
+
normalizedBy: 'frontier-lang-compiler/js-ts-semantic-merge',
|
|
212
|
+
structuralContract: true,
|
|
213
|
+
...(input.metadata ?? {}),
|
|
214
|
+
...(options.metadata ?? {})
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
}
|