@shapeshift-labs/frontier-lang-compiler 0.2.99 → 0.2.101
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/dist/declarations/bidirectional-target-change-evidence.d.ts +299 -0
- package/dist/declarations/bidirectional-target-change.d.ts +19 -120
- package/dist/declarations/native-project-admission.d.ts +43 -22
- package/dist/declarations/semantic-edit-bundle.d.ts +13 -1
- package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +24 -0
- package/dist/declarations/semantic-edit-script.d.ts +53 -51
- package/dist/declarations/semantic-lineage.d.ts +62 -51
- package/dist/declarations/semantic-merge-candidates.d.ts +39 -0
- package/dist/declarations/semantic-patch-bundle.d.ts +13 -0
- package/dist/declarations/semantic-sidecar-admission.d.ts +14 -0
- package/dist/declarations/semantic-sidecar.d.ts +12 -14
- package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +200 -0
- package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +62 -17
- package/dist/internal/index-impl/createNativeSourcePreservation.js +16 -1
- package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +151 -1
- package/dist/internal/index-impl/createSemanticImportSidecar.js +5 -0
- package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +29 -11
- package/dist/internal/index-impl/declarationRecord.js +2 -2
- package/dist/internal/index-impl/inferSemanticLineageEvents.js +8 -0
- package/dist/internal/index-impl/nativeChangeProjectionEndpoint.js +56 -16
- package/dist/internal/index-impl/projectImportAdmissionMergeScore.js +26 -74
- package/dist/internal/index-impl/projectImportAdmissionProjectionCoverage.js +74 -0
- package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +92 -74
- package/dist/internal/index-impl/replaySemanticEditProjection.js +114 -40
- package/dist/internal/index-impl/semanticEditBundleAdmission.js +95 -12
- package/dist/internal/index-impl/semanticEditBundleIndex.js +16 -10
- package/dist/internal/index-impl/semanticEditInsertionAnchors.js +8 -5
- package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +167 -0
- package/dist/internal/index-impl/semanticEditSourceRanges.js +283 -0
- package/dist/internal/index-impl/semanticHistoryLineageResolution.js +56 -3
- package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +2 -2
- package/dist/internal/index-impl/semanticLineageHashEvidence.js +97 -0
- package/dist/internal/index-impl/semanticLineageInferenceMatching.js +158 -13
- package/dist/internal/index-impl/semanticLineageResolutionRecords.js +46 -2
- package/dist/internal/index-impl/semanticMergeCandidateRecords.js +22 -2
- package/dist/internal/index-impl/semanticMergeCandidateScoreFacets.js +221 -0
- package/dist/internal/index-impl/semanticPatchBundleAdmission.js +122 -20
- package/dist/internal/index-impl/semanticPatchBundleLineageLinks.js +199 -0
- package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +29 -3
- package/dist/internal/index-impl/semanticPatchBundleRecords.js +28 -104
- package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +127 -0
- package/dist/internal/index-impl/sourcePreservationFromProjectionContext.js +9 -2
- package/dist/internal/index-impl/sourceTextForSpan.js +4 -9
- package/dist/lightweight-dependency-relations.js +113 -7
- package/dist/native-import-language-profiles.js +10 -2
- package/dist/native-import-utils.js +15 -1
- package/dist/native-region-scanner-js-helpers.js +68 -18
- package/dist/native-region-scanner-js-imports.js +7 -0
- package/dist/native-region-scanner-js.js +16 -8
- package/dist/native-region-scanner.js +2 -1
- package/dist/semantic-import-regions.js +8 -6
- package/dist/semantic-import-sidecar-admission-types.d.ts +14 -0
- package/dist/semantic-import-sidecar-entry.js +151 -7
- package/dist/semantic-import-sidecar-types.d.ts +18 -13
- package/dist/semantic-import-source-preservation-utils.js +55 -0
- package/dist/semantic-import-source-preservation.js +98 -3
- package/package.json +1 -1
|
@@ -14,16 +14,20 @@ export function createSemanticEditBundleAdmission(input = {}, options = {}) {
|
|
|
14
14
|
const scripts = array(input.semanticEditScripts ?? input.scripts ?? input.semanticEditScript);
|
|
15
15
|
const projections = array(input.semanticEditProjections ?? input.projections ?? input.semanticEditProjection);
|
|
16
16
|
const replays = array(input.semanticEditReplays ?? input.replays ?? input.semanticEditReplay);
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const evidence = evidenceRecords(input, options);
|
|
18
|
+
const summary = summarizeSemanticEditBundle(scripts, projections, replays, evidence);
|
|
19
|
+
const computedStatus = semanticEditBundleStatus(summary);
|
|
20
|
+
const status = safeStatus(input.status ?? options.status, computedStatus, summary);
|
|
19
21
|
const readiness = normalizeSemanticMergeReadiness(input.readiness ?? options.readiness ?? readinessForStatus(status))
|
|
20
22
|
?? input.readiness ?? options.readiness ?? readinessForStatus(status);
|
|
23
|
+
const positiveAutoApplyCandidate = status === 'ready' && hasPositiveAutoMergeProof(summary);
|
|
24
|
+
const computedReviewRequired = !['ready', 'already-applied', 'none'].includes(status) || (status === 'ready' && !positiveAutoApplyCandidate);
|
|
21
25
|
return compactRecord({
|
|
22
26
|
status,
|
|
23
|
-
action: input.action ?? options.action
|
|
27
|
+
action: safeAction(input.action ?? options.action, status, positiveAutoApplyCandidate),
|
|
24
28
|
readiness,
|
|
25
|
-
reviewRequired: input.reviewRequired
|
|
26
|
-
autoApplyCandidate: input.autoApplyCandidate
|
|
29
|
+
reviewRequired: input.reviewRequired === true || computedReviewRequired,
|
|
30
|
+
autoApplyCandidate: input.autoApplyCandidate === false ? false : positiveAutoApplyCandidate,
|
|
27
31
|
autoMergeClaim: false,
|
|
28
32
|
semanticEquivalenceClaim: false,
|
|
29
33
|
reasonCodes: uniqueStrings([
|
|
@@ -31,7 +35,7 @@ export function createSemanticEditBundleAdmission(input = {}, options = {}) {
|
|
|
31
35
|
...strings(options.reasonCodes),
|
|
32
36
|
...summary.reasonCodes,
|
|
33
37
|
...derivedReasonCodes(summary, status)
|
|
34
|
-
]),
|
|
38
|
+
].filter(Boolean)),
|
|
35
39
|
sourcePaths: summary.sourcePaths,
|
|
36
40
|
scriptIds: summary.scriptIds,
|
|
37
41
|
projectionIds: summary.projectionIds,
|
|
@@ -41,7 +45,7 @@ export function createSemanticEditBundleAdmission(input = {}, options = {}) {
|
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
function summarizeSemanticEditBundle(scripts, projections, replays) {
|
|
48
|
+
function summarizeSemanticEditBundle(scripts, projections, replays, evidence) {
|
|
45
49
|
const scriptStatusEntries = scripts.map((script) => script.admission?.status);
|
|
46
50
|
const projectionStatusEntries = projections.flatMap((projection) => [projection.status, projection.admission?.status]);
|
|
47
51
|
const replayStatusEntries = replays.map((replay) => replay.status);
|
|
@@ -49,11 +53,14 @@ function summarizeSemanticEditBundle(scripts, projections, replays) {
|
|
|
49
53
|
const projectionStatuses = uniqueStrings(strings(projectionStatusEntries));
|
|
50
54
|
const replayStatuses = uniqueStrings(strings(replayStatusEntries));
|
|
51
55
|
const replayActions = uniqueStrings(strings(replays.map((replay) => replay.admission?.action)));
|
|
56
|
+
const evidenceSummary = summarizeEvidence(evidence);
|
|
52
57
|
return {
|
|
53
58
|
scripts: scripts.length,
|
|
54
59
|
projections: projections.length,
|
|
55
60
|
replays: replays.length,
|
|
56
61
|
files: sourcePaths(scripts, projections, replays).length,
|
|
62
|
+
portableScripts: scripts.filter((script) => script.admission?.status === 'auto-merge-candidate').length,
|
|
63
|
+
portableProjections: projections.filter((projection) => projection.status === 'projected' && projection.admission?.status === 'auto-merge-candidate').length,
|
|
57
64
|
acceptedClean: replays.filter((replay) => replay.status === 'accepted-clean').length,
|
|
58
65
|
alreadyApplied: replays.filter((replay) => replay.status === 'already-applied').length,
|
|
59
66
|
conflicts: countStatuses(scriptStatusEntries, replayStatusEntries, ['conflict']),
|
|
@@ -70,30 +77,43 @@ function summarizeSemanticEditBundle(scripts, projections, replays) {
|
|
|
70
77
|
scriptIds: uniqueStrings(scripts.map((script) => script.id)),
|
|
71
78
|
projectionIds: uniqueStrings(projections.map((projection) => projection.id)),
|
|
72
79
|
replayIds: uniqueStrings(replays.map((replay) => replay.id)),
|
|
80
|
+
evidenceIds: evidenceSummary.evidenceIds,
|
|
81
|
+
passedTestEvidence: evidenceSummary.passed,
|
|
82
|
+
failedTestEvidence: evidenceSummary.failed,
|
|
83
|
+
conflictEvidence: evidenceSummary.conflict,
|
|
84
|
+
staleEvidence: evidenceSummary.stale,
|
|
73
85
|
reasonCodes: uniqueStrings([
|
|
74
86
|
...scripts.flatMap((script) => strings(script.admission?.reasonCodes)),
|
|
75
87
|
...projections.flatMap((projection) => strings(projection.admission?.reasonCodes)),
|
|
76
|
-
...replays.flatMap((replay) => strings(replay.admission?.reasonCodes))
|
|
88
|
+
...replays.flatMap((replay) => strings(replay.admission?.reasonCodes)),
|
|
89
|
+
...evidenceSummary.reasonCodes
|
|
77
90
|
])
|
|
78
91
|
};
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
function semanticEditBundleStatus(summary) {
|
|
82
95
|
const total = summary.scripts + summary.projections + summary.replays;
|
|
96
|
+
if (summary.blocked || summary.projectionBlocked || summary.failedTestEvidence) return 'blocked';
|
|
97
|
+
if (summary.conflicts || summary.conflictEvidence) return 'conflict';
|
|
98
|
+
if (summary.stale || summary.staleEvidence) return 'stale';
|
|
83
99
|
if (total === 0) return 'none';
|
|
84
|
-
if (summary.blocked || summary.projectionBlocked) return 'blocked';
|
|
85
|
-
if (summary.conflicts) return 'conflict';
|
|
86
|
-
if (summary.stale) return 'stale';
|
|
87
100
|
if (!summary.replays || summary.needsReview) return 'needs-review';
|
|
88
101
|
if (summary.acceptedClean === 0 && summary.alreadyApplied === summary.replays) return 'already-applied';
|
|
89
|
-
return summary
|
|
102
|
+
return hasPositiveAutoMergeProof(summary) ? 'ready' : 'needs-review';
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
function derivedReasonCodes(summary, status) {
|
|
93
106
|
return [
|
|
94
107
|
summary.scripts && !summary.projections ? 'semantic-edit-projection-missing' : undefined,
|
|
95
108
|
(summary.scripts || summary.projections) && !summary.replays ? 'semantic-edit-replay-missing' : undefined,
|
|
109
|
+
summary.scripts && summary.portableScripts !== summary.scripts ? 'semantic-edit-script-not-portable' : undefined,
|
|
110
|
+
summary.projections && summary.portableProjections !== summary.projections ? 'semantic-edit-projection-not-portable' : undefined,
|
|
111
|
+
summary.acceptedClean && !summary.passedTestEvidence ? 'semantic-edit-tests-passed-evidence-missing' : undefined,
|
|
112
|
+
summary.failedTestEvidence ? 'semantic-edit-tests-failed' : undefined,
|
|
113
|
+
summary.conflictEvidence ? 'semantic-edit-conflict-evidence' : undefined,
|
|
114
|
+
summary.staleEvidence ? 'semantic-edit-stale-evidence' : undefined,
|
|
96
115
|
status === 'ready' ? 'semantic-edit-replay-accepted-clean' : undefined,
|
|
116
|
+
status === 'ready' ? 'semantic-edit-positive-auto-merge-proof' : undefined,
|
|
97
117
|
status === 'already-applied' ? 'semantic-edit-replay-already-applied' : undefined,
|
|
98
118
|
status === 'blocked' ? 'semantic-edit-blocked' : undefined,
|
|
99
119
|
status === 'conflict' ? 'semantic-edit-conflict' : undefined,
|
|
@@ -101,6 +121,19 @@ function derivedReasonCodes(summary, status) {
|
|
|
101
121
|
];
|
|
102
122
|
}
|
|
103
123
|
|
|
124
|
+
function hasPositiveAutoMergeProof(summary) {
|
|
125
|
+
return summary.acceptedClean > 0 &&
|
|
126
|
+
summary.acceptedClean + summary.alreadyApplied === summary.replays &&
|
|
127
|
+
summary.scripts > 0 &&
|
|
128
|
+
summary.projections > 0 &&
|
|
129
|
+
summary.portableScripts === summary.scripts &&
|
|
130
|
+
summary.portableProjections === summary.projections &&
|
|
131
|
+
summary.passedTestEvidence > 0 &&
|
|
132
|
+
summary.failedTestEvidence === 0 &&
|
|
133
|
+
summary.conflictEvidence === 0 &&
|
|
134
|
+
summary.staleEvidence === 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
104
137
|
function readinessForStatus(status) {
|
|
105
138
|
if (['ready', 'already-applied'].includes(status)) return 'ready';
|
|
106
139
|
if (['blocked', 'conflict'].includes(status)) return 'blocked';
|
|
@@ -116,6 +149,20 @@ function actionForStatus(status) {
|
|
|
116
149
|
return 'review';
|
|
117
150
|
}
|
|
118
151
|
|
|
152
|
+
function safeStatus(requested, computed, summary) {
|
|
153
|
+
if (!requested) return computed;
|
|
154
|
+
if (requested === 'ready' && !hasPositiveAutoMergeProof(summary)) return computed;
|
|
155
|
+
if (requested === 'already-applied' && computed !== 'already-applied') return computed;
|
|
156
|
+
if (['blocked', 'conflict', 'stale'].includes(requested)) return requested;
|
|
157
|
+
return computed;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function safeAction(requested, status, positiveAutoApplyCandidate) {
|
|
161
|
+
if (requested === 'admit' && !positiveAutoApplyCandidate) return actionForStatus(status);
|
|
162
|
+
if (requested === 'skip' && status !== 'already-applied') return actionForStatus(status);
|
|
163
|
+
return requested ?? actionForStatus(status);
|
|
164
|
+
}
|
|
165
|
+
|
|
119
166
|
function sourcePaths(scripts, projections, replays) {
|
|
120
167
|
return uniqueStrings(strings([
|
|
121
168
|
...scripts.map((script) => script.sourcePath),
|
|
@@ -132,6 +179,42 @@ function countStatuses(...args) {
|
|
|
132
179
|
return statuses.filter((status) => needles.has(status)).length;
|
|
133
180
|
}
|
|
134
181
|
|
|
182
|
+
function evidenceRecords(...sources) {
|
|
183
|
+
return sources.flatMap((source) => [
|
|
184
|
+
...array(source?.evidence),
|
|
185
|
+
...array(source?.testEvidence),
|
|
186
|
+
...array(source?.testResults),
|
|
187
|
+
...array(source?.gateEvidence),
|
|
188
|
+
...array(source?.proofEvidence)
|
|
189
|
+
]).filter(Boolean);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function summarizeEvidence(evidence) {
|
|
193
|
+
const testLike = evidence.filter(isAutoMergeTestEvidence);
|
|
194
|
+
const conflict = evidence.filter((record) => evidenceStatus(record, ['conflict', 'conflicted']) || record?.metadata?.conflict === true || strings(record?.reasonCodes ?? record?.reasons).some((reason) => reason.toLowerCase().includes('conflict')));
|
|
195
|
+
const stale = evidence.filter((record) => evidenceStatus(record, ['stale']) || record?.metadata?.stale === true || strings(record?.reasonCodes ?? record?.reasons).some((reason) => reason.toLowerCase().includes('stale')));
|
|
196
|
+
const failed = testLike.filter((record) => evidenceStatus(record, ['failed', 'failure', 'error', 'blocked', 'rejected']));
|
|
197
|
+
const passed = testLike.filter((record) => evidenceStatus(record, ['passed', 'ok', 'success', 'succeeded', 'accepted', 'verified']));
|
|
198
|
+
return {
|
|
199
|
+
evidenceIds: uniqueStrings(evidence.map((record) => record.id)),
|
|
200
|
+
passed: passed.length,
|
|
201
|
+
failed: failed.length,
|
|
202
|
+
conflict: conflict.length,
|
|
203
|
+
stale: stale.length,
|
|
204
|
+
reasonCodes: uniqueStrings(evidence.flatMap((record) => strings(record.reasonCodes ?? record.reasons)))
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function isAutoMergeTestEvidence(record) {
|
|
209
|
+
const kind = String(record?.kind ?? record?.type ?? '').toLowerCase();
|
|
210
|
+
return ['test', 'tests', 'proof', 'gate', 'verification', 'check'].includes(kind);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function evidenceStatus(record, statuses) {
|
|
214
|
+
const status = String(record?.status ?? record?.outcome ?? '').toLowerCase();
|
|
215
|
+
return statuses.includes(status);
|
|
216
|
+
}
|
|
217
|
+
|
|
135
218
|
function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
|
|
136
219
|
function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
|
|
137
220
|
function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
|
|
@@ -15,16 +15,16 @@ export function semanticEditRecordIndex(scripts, projections, replays, source =
|
|
|
15
15
|
semanticEditReplayEditCount: replayEdits.length,
|
|
16
16
|
semanticEditReplayStatuses: uniqueStrings([...strings(source.semanticEditReplayStatuses), ...strings(index.semanticEditReplayStatuses), ...strings(summary.replayStatuses), ...replays.map((replay) => replay.status)]),
|
|
17
17
|
semanticEditReplayActions: uniqueStrings([...strings(source.semanticEditReplayActions), ...strings(index.semanticEditReplayActions), ...strings(summary.replayActions), ...replays.map((replay) => replay.admission?.action)]),
|
|
18
|
-
semanticEditReplayCurrentHashes: uniqueStrings([...strings(source.semanticEditReplayCurrentHashes), ...strings(index.semanticEditReplayCurrentHashes), ...replays.map((replay) => replay.currentHash)]),
|
|
19
|
-
semanticEditReplayOutputHashes: uniqueStrings([...strings(source.semanticEditReplayOutputHashes), ...strings(index.semanticEditReplayOutputHashes), ...replays.map((replay) => replay.outputHash)]),
|
|
20
|
-
semanticEditKeys: uniqueStrings([...strings(source.semanticEditKeys), ...strings(index.semanticEditKeys), ...operations.map((operation) => operation.semanticKey), ...edits.map((edit) => edit.semanticKey), ...replayEdits.map((edit) => edit.semanticKey)]),
|
|
21
|
-
semanticIdentityHashes: uniqueStrings([...strings(source.semanticIdentityHashes), ...strings(index.semanticIdentityHashes), ...operations.map((operation) => operation.semanticIdentityHash), ...edits.map((edit) => edit.semanticIdentityHash), ...replayEdits.map((edit) => edit.semanticIdentityHash)]),
|
|
22
|
-
sourceIdentityHashes: uniqueStrings([...strings(source.sourceIdentityHashes), ...strings(index.sourceIdentityHashes), ...operations.map((operation) => operation.sourceIdentityHash), ...edits.map((edit) => edit.sourceIdentityHash), ...replayEdits.map((edit) => edit.sourceIdentityHash)]),
|
|
23
|
-
operationContentHashes: uniqueStrings([...strings(source.operationContentHashes), ...strings(index.operationContentHashes), ...operations.map((operation) => operation.operationContentHash), ...edits.map((edit) => edit.operationContentHash)]),
|
|
24
|
-
editContentHashes: uniqueStrings([...strings(source.editContentHashes), ...strings(index.editContentHashes), ...edits.map((edit) => edit.editContentHash), ...replayEdits.map((edit) => edit.editContentHash)]),
|
|
25
|
-
anchorKeys: uniqueStrings([...operations.map((operation) => operation.anchor?.key), ...edits.map((edit) => edit.anchorKey)]),
|
|
26
|
-
conflictKeys: uniqueStrings([...operations.map((operation) => operation.anchor?.conflictKey), ...edits.map((edit) => edit.conflictKey)]),
|
|
27
|
-
projectedSourcePaths: uniqueStrings([...projections.map((projection) => projection.sourcePath), ...edits.flatMap((edit) => [edit.sourcePath, edit.targetSourcePath]), ...replays.map((replay) => replay.sourcePath), ...replayEdits.map((edit) => edit.sourcePath)])
|
|
18
|
+
semanticEditReplayCurrentHashes: uniqueStrings([...strings(source.semanticEditReplayCurrentHashes), ...strings(index.semanticEditReplayCurrentHashes), ...strings(summary.replayCurrentHashes), ...strings(summary.semanticEditReplayCurrentHashes), ...replays.map((replay) => replay.currentHash)]),
|
|
19
|
+
semanticEditReplayOutputHashes: uniqueStrings([...strings(source.semanticEditReplayOutputHashes), ...strings(index.semanticEditReplayOutputHashes), ...strings(summary.replayOutputHashes), ...strings(summary.semanticEditReplayOutputHashes), ...replays.map((replay) => replay.outputHash)]),
|
|
20
|
+
semanticEditKeys: uniqueStrings([...strings(source.semanticEditKeys), ...strings(index.semanticEditKeys), ...strings(summary.semanticEditKeys), ...operations.map((operation) => operation.semanticKey), ...edits.map((edit) => edit.semanticKey), ...replayEdits.map((edit) => edit.semanticKey)]),
|
|
21
|
+
semanticIdentityHashes: uniqueStrings([...strings(source.semanticIdentityHashes), ...strings(index.semanticIdentityHashes), ...strings(summary.semanticIdentityHashes), ...operations.map((operation) => operation.semanticIdentityHash), ...edits.map((edit) => edit.semanticIdentityHash), ...replayEdits.map((edit) => edit.semanticIdentityHash)]),
|
|
22
|
+
sourceIdentityHashes: uniqueStrings([...strings(source.sourceIdentityHashes), ...strings(index.sourceIdentityHashes), ...strings(summary.sourceIdentityHashes), ...operations.map((operation) => operation.sourceIdentityHash), ...edits.map((edit) => edit.sourceIdentityHash), ...replayEdits.map((edit) => edit.sourceIdentityHash)]),
|
|
23
|
+
operationContentHashes: uniqueStrings([...strings(source.operationContentHashes), ...strings(index.operationContentHashes), ...strings(summary.operationContentHashes), ...operations.map((operation) => operation.operationContentHash), ...edits.map((edit) => edit.operationContentHash), ...replayEdits.map((edit) => edit.operationContentHash)]),
|
|
24
|
+
editContentHashes: uniqueStrings([...strings(source.editContentHashes), ...strings(index.editContentHashes), ...strings(summary.editContentHashes), ...edits.map((edit) => edit.editContentHash), ...replayEdits.map((edit) => edit.editContentHash)]),
|
|
25
|
+
anchorKeys: uniqueStrings([...strings(source.anchorKeys), ...strings(index.anchorKeys), ...strings(summary.anchorKeys), ...operations.map((operation) => operation.anchor?.key), ...edits.map((edit) => edit.anchorKey), ...replayEdits.map((edit) => edit.anchorKey)]),
|
|
26
|
+
conflictKeys: uniqueStrings([...strings(source.conflictKeys), ...strings(index.conflictKeys), ...strings(summary.conflictKeys), ...operations.map((operation) => operation.anchor?.conflictKey), ...edits.map((edit) => edit.conflictKey), ...replayEdits.map((edit) => edit.conflictKey)]),
|
|
27
|
+
projectedSourcePaths: uniqueStrings([...strings(source.projectedSourcePaths), ...strings(index.projectedSourcePaths), ...strings(summary.projectedSourcePaths), ...projections.map((projection) => projection.sourcePath), ...edits.flatMap((edit) => [edit.sourcePath, edit.targetSourcePath]), ...replays.map((replay) => replay.sourcePath), ...replayEdits.map((edit) => edit.sourcePath)])
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -36,9 +36,15 @@ export function semanticEditSummary(index) {
|
|
|
36
36
|
replayIds: index.semanticEditReplayIds,
|
|
37
37
|
replayStatuses: index.semanticEditReplayStatuses,
|
|
38
38
|
replayActions: index.semanticEditReplayActions,
|
|
39
|
+
replayCurrentHashes: index.semanticEditReplayCurrentHashes,
|
|
40
|
+
replayOutputHashes: index.semanticEditReplayOutputHashes,
|
|
39
41
|
semanticEditKeys: index.semanticEditKeys,
|
|
42
|
+
semanticIdentityHashes: index.semanticIdentityHashes,
|
|
43
|
+
sourceIdentityHashes: index.sourceIdentityHashes,
|
|
40
44
|
operationContentHashes: index.operationContentHashes,
|
|
41
45
|
editContentHashes: index.editContentHashes,
|
|
46
|
+
anchorKeys: index.anchorKeys,
|
|
47
|
+
conflictKeys: index.conflictKeys,
|
|
42
48
|
projectedSourcePaths: index.projectedSourcePaths,
|
|
43
49
|
replayEditCount: index.semanticEditReplayEditCount
|
|
44
50
|
});
|
|
@@ -7,13 +7,16 @@ export function semanticEditInsertionAnchor(region, context) {
|
|
|
7
7
|
.filter((symbol) => hasSymbol(context.baseSymbols, symbol));
|
|
8
8
|
const before = nearestBefore(workers, workerSymbol);
|
|
9
9
|
const after = nearestAfter(workers, workerSymbol);
|
|
10
|
-
const
|
|
11
|
-
? insertionFromSymbol('after', before, context, 'nearest-previous-base-symbol')
|
|
12
|
-
:
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
const anchorCandidates = [
|
|
11
|
+
before ? insertionFromSymbol('after', before, context, 'nearest-previous-base-symbol') : undefined,
|
|
12
|
+
after ? insertionFromSymbol('before', after, context, 'nearest-next-base-symbol') : undefined
|
|
13
|
+
].filter(Boolean);
|
|
14
|
+
const anchor = anchorCandidates.find((candidate) => candidate.headSpan)
|
|
15
|
+
?? anchorCandidates[0]
|
|
16
|
+
?? fallbackInsertion(region, context, 'no-neighbor-base-symbol');
|
|
15
17
|
return compactRecord({
|
|
16
18
|
...anchor,
|
|
19
|
+
anchorCandidates,
|
|
17
20
|
insertedSymbolId: workerSymbol.id,
|
|
18
21
|
insertedSymbolName: workerSymbol.name,
|
|
19
22
|
insertedSymbolKind: workerSymbol.kind,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { uniqueStrings } from '../../native-import-utils.js';
|
|
3
|
+
|
|
4
|
+
export function replayEditDiagnostics(edit, status, range, reasonCodes, sourceText) {
|
|
5
|
+
if (status === 'applied' || status === 'already-applied') return [];
|
|
6
|
+
return reasonCodes.map((code) => replayDiagnostic(code, {
|
|
7
|
+
scope: 'edit',
|
|
8
|
+
status,
|
|
9
|
+
operationId: edit.operationId,
|
|
10
|
+
sourcePath: edit.targetSourcePath ?? edit.sourcePath,
|
|
11
|
+
symbolName: edit.targetSymbolName ?? edit.symbolName,
|
|
12
|
+
symbolKind: edit.targetSymbolKind ?? edit.symbolKind,
|
|
13
|
+
editKind: edit.editKind,
|
|
14
|
+
start: range?.start,
|
|
15
|
+
end: range?.end,
|
|
16
|
+
expectedHash: replayDiagnosticExpectedHash(code, edit),
|
|
17
|
+
actualHash: replayDiagnosticActualHash(range, sourceText),
|
|
18
|
+
replacementHash: edit.replacementTextHash ?? edit.replacementSpanTextHash
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function replayEditsWithOverlapDiagnostics(edits) {
|
|
23
|
+
const overlapDiagnostics = new Map();
|
|
24
|
+
const ordered = edits
|
|
25
|
+
.filter((edit) => edit.status === 'applied' && hasNumericReplayRange(edit))
|
|
26
|
+
.sort((left, right) => left.start - right.start || left.end - right.end || (left.editOrder ?? 0) - (right.editOrder ?? 0));
|
|
27
|
+
for (let leftIndex = 0; leftIndex < ordered.length; leftIndex += 1) {
|
|
28
|
+
for (let rightIndex = leftIndex + 1; rightIndex < ordered.length; rightIndex += 1) {
|
|
29
|
+
const left = ordered[leftIndex];
|
|
30
|
+
const right = ordered[rightIndex];
|
|
31
|
+
if (!rangesOverlap(left, right)) continue;
|
|
32
|
+
const operationIds = [left.operationId, right.operationId].filter(Boolean);
|
|
33
|
+
const fallbackId = `${left.editOrder ?? leftIndex}:${right.editOrder ?? rightIndex}`;
|
|
34
|
+
const code = `replay-edit-overlap:${operationIds.join(':') || fallbackId}`;
|
|
35
|
+
appendOverlapDiagnostic(overlapDiagnostics, left, code, operationIds);
|
|
36
|
+
appendOverlapDiagnostic(overlapDiagnostics, right, code, operationIds);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!overlapDiagnostics.size) return edits;
|
|
40
|
+
return edits.map((edit) => {
|
|
41
|
+
const diagnostics = overlapDiagnostics.get(edit);
|
|
42
|
+
if (!diagnostics) return edit;
|
|
43
|
+
return diagnostics.reduce((record, diagnostic) => appendReplayEditDiagnostic(record, diagnostic), edit);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function replayDiagnostics(input) {
|
|
48
|
+
return uniqueDiagnostics([
|
|
49
|
+
...reasonList(input.reasonCodes).map((code) => replayDiagnostic(code, {
|
|
50
|
+
scope: 'replay',
|
|
51
|
+
status: input.status,
|
|
52
|
+
sourcePath: input.sourcePath,
|
|
53
|
+
expectedHash: code === 'current-source-hash-mismatch' ? input.expectedCurrentHash : undefined,
|
|
54
|
+
actualHash: code === 'current-source-hash-mismatch' ? input.currentHash : undefined
|
|
55
|
+
})),
|
|
56
|
+
...input.edits.flatMap((edit) => edit.diagnostics ?? [])
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function appendOverlapDiagnostic(overlapDiagnostics, edit, code, operationIds) {
|
|
61
|
+
const diagnostics = overlapDiagnostics.get(edit) ?? [];
|
|
62
|
+
diagnostics.push(replayDiagnostic(code, {
|
|
63
|
+
scope: 'edit',
|
|
64
|
+
status: 'conflict',
|
|
65
|
+
operationId: edit.operationId,
|
|
66
|
+
sourcePath: edit.sourcePath,
|
|
67
|
+
symbolName: edit.symbolName,
|
|
68
|
+
symbolKind: edit.symbolKind,
|
|
69
|
+
editKind: edit.editKind,
|
|
70
|
+
start: edit.start,
|
|
71
|
+
end: edit.end,
|
|
72
|
+
overlapOperationIds: operationIds
|
|
73
|
+
}));
|
|
74
|
+
overlapDiagnostics.set(edit, diagnostics);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function appendReplayEditDiagnostic(edit, diagnostic) {
|
|
78
|
+
return compactRecord({
|
|
79
|
+
...edit,
|
|
80
|
+
status: edit.status === 'applied' ? 'conflict' : edit.status,
|
|
81
|
+
reasonCodes: reasonList([...(edit.reasonCodes ?? []), diagnostic.code]),
|
|
82
|
+
diagnostics: uniqueDiagnostics([...(edit.diagnostics ?? []), diagnostic])
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function replayDiagnostic(code, context) {
|
|
87
|
+
const category = replayDiagnosticCategory(code, context.status);
|
|
88
|
+
return compactRecord({
|
|
89
|
+
code,
|
|
90
|
+
category,
|
|
91
|
+
severity: replayDiagnosticSeverity(code, category, context.status),
|
|
92
|
+
scope: context.scope,
|
|
93
|
+
status: context.status,
|
|
94
|
+
operationId: context.operationId,
|
|
95
|
+
sourcePath: context.sourcePath,
|
|
96
|
+
symbolName: context.symbolName,
|
|
97
|
+
symbolKind: context.symbolKind,
|
|
98
|
+
editKind: context.editKind,
|
|
99
|
+
start: context.start,
|
|
100
|
+
end: context.end,
|
|
101
|
+
expectedHash: context.expectedHash,
|
|
102
|
+
actualHash: context.actualHash,
|
|
103
|
+
replacementHash: context.replacementHash,
|
|
104
|
+
overlapOperationIds: context.overlapOperationIds
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function replayDiagnosticCategory(code, status) {
|
|
109
|
+
if (code.includes('overlap')) return 'overlap';
|
|
110
|
+
if (code.startsWith('missing-current-source') || code.startsWith('missing-head-source') || code.startsWith('missing-worker-source')) return 'missing-source';
|
|
111
|
+
if (code === 'current-symbol-anchor-missing' || code.includes('anchor-missing') || code.includes('anchor-unusable')) return 'stale-anchor';
|
|
112
|
+
if (code.includes('content-mismatch') || code.includes('hash-mismatch') || code.includes('span-not-resolvable') || code.startsWith('projection-not-') || code === 'missing-replacement-text') return 'projection-mismatch';
|
|
113
|
+
if (code.includes('reanchored')) return 'reanchored';
|
|
114
|
+
if (code.includes('matches-')) return 'matched-source';
|
|
115
|
+
if (status === 'stale') return 'stale-anchor';
|
|
116
|
+
if (status === 'conflict' || status === 'blocked') return 'projection-mismatch';
|
|
117
|
+
return 'replay';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function replayDiagnosticSeverity(code, category, status) {
|
|
121
|
+
if (code === 'current-source-hash-mismatch' && (status === 'accepted-clean' || status === 'already-applied')) return 'warning';
|
|
122
|
+
if (category === 'matched-source' || category === 'reanchored' || status === 'applied' || status === 'already-applied') return 'info';
|
|
123
|
+
if (category === 'overlap' || category === 'missing-source' || category === 'stale-anchor' || category === 'projection-mismatch') return 'error';
|
|
124
|
+
return status === 'accepted-clean' ? 'info' : 'warning';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function replayDiagnosticExpectedHash(code, edit) {
|
|
128
|
+
if (code.includes('matches-replacement')) return edit.replacementTextHash ?? edit.replacementSpanTextHash;
|
|
129
|
+
if (code.includes('content-mismatch') || code.includes('matches-deleted')) return edit.deletedTextHash ?? edit.anchorDeletedTextHash;
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function replayDiagnosticActualHash(range, sourceText) {
|
|
134
|
+
if (!range || typeof sourceText !== 'string') return undefined;
|
|
135
|
+
return hashSemanticValue(sourceText.slice(range.start, range.end));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function uniqueDiagnostics(diagnostics) {
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
const result = [];
|
|
141
|
+
for (const diagnostic of diagnostics.filter(Boolean)) {
|
|
142
|
+
const key = [
|
|
143
|
+
diagnostic.scope,
|
|
144
|
+
diagnostic.operationId,
|
|
145
|
+
diagnostic.status,
|
|
146
|
+
diagnostic.code,
|
|
147
|
+
diagnostic.start,
|
|
148
|
+
diagnostic.end,
|
|
149
|
+
(diagnostic.overlapOperationIds ?? []).join('|')
|
|
150
|
+
].join(':');
|
|
151
|
+
if (seen.has(key)) continue;
|
|
152
|
+
seen.add(key);
|
|
153
|
+
result.push(diagnostic);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function rangesOverlap(left, right) {
|
|
159
|
+
return Boolean(left && right && left.start < right.end && right.start < left.end);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hasNumericReplayRange(edit) {
|
|
163
|
+
return typeof edit.start === 'number' && typeof edit.end === 'number';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function reasonList(values) { return uniqueStrings((values ?? []).filter(Boolean)); }
|
|
167
|
+
function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
|