@shapeshift-labs/frontier-lang-compiler 0.2.132 → 0.2.133
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
CHANGED
|
@@ -188,6 +188,14 @@ artifacts keep `autoMergeClaim: false` and `semanticEquivalenceClaim: false`,
|
|
|
188
188
|
but give coordinators machine-readable proof that the projected source matches
|
|
189
189
|
the merge output and that applying the same projection again is a no-op.
|
|
190
190
|
|
|
191
|
+
When the top-level JS/TS ledger blocks only because an existing declaration
|
|
192
|
+
body or semantic fact changed, `safeMergeJsTsSource` can fall back to the
|
|
193
|
+
generic semantic edit script path. The fallback admits the merge only after the
|
|
194
|
+
script is an auto-merge candidate, the source projection succeeds, replay on
|
|
195
|
+
current head is `accepted-clean`, and replay on the projected source is
|
|
196
|
+
`already-applied`. Same-anchor head edits, stale anchors, and non-body conflicts
|
|
197
|
+
remain blocked for review.
|
|
198
|
+
|
|
191
199
|
Project-level JS/TS safe merges compose the same file-level gates across a
|
|
192
200
|
base/worker/head file set. They preserve head-only files, admit worker-only
|
|
193
201
|
file additions when file additions are enabled, block conflicting same-path
|
|
@@ -109,6 +109,9 @@ export interface JsTsSafeMergeSummary {
|
|
|
109
109
|
readonly gatesPassed: number;
|
|
110
110
|
readonly memberRegions?: number;
|
|
111
111
|
readonly memberAdditions?: number;
|
|
112
|
+
readonly semanticEditOperations?: number;
|
|
113
|
+
readonly semanticEditAppliedOperations?: number;
|
|
114
|
+
readonly semanticEditReplayStatus?: string;
|
|
112
115
|
readonly composedPhases?: number;
|
|
113
116
|
}
|
|
114
117
|
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
jsTsSafeMergeGateOrder
|
|
6
6
|
} from './js-ts-safe-merge-constants.js';
|
|
7
7
|
import { safeMergeJsTsImportsAndDeclarations } from './js-ts-safe-merge.js';
|
|
8
|
+
import { semanticEditFallbackResult } from './js-ts-safe-merge-semantic-edit-fallback.js';
|
|
8
9
|
import { createJsTsSafeMergeSemanticArtifacts } from './js-ts-safe-merge-semantic-artifacts.js';
|
|
9
10
|
import {
|
|
10
11
|
applyJsTsPreparedMemberAdditions,
|
|
@@ -12,7 +13,12 @@ import {
|
|
|
12
13
|
} from './js-ts-safe-member-merge.js';
|
|
13
14
|
|
|
14
15
|
function safeMergeJsTsSource(input = {}) {
|
|
15
|
-
if (!hasMemberMergePolicy(input))
|
|
16
|
+
if (!hasMemberMergePolicy(input)) {
|
|
17
|
+
const topLevelResult = safeMergeJsTsImportsAndDeclarations(input);
|
|
18
|
+
return topLevelResult.status === JsTsSafeMergeStatuses.merged
|
|
19
|
+
? topLevelResult
|
|
20
|
+
: semanticEditFallbackResult(input, topLevelResult);
|
|
21
|
+
}
|
|
16
22
|
|
|
17
23
|
const memberNeutralization = neutralizeJsTsSafeMemberMergeSources(input);
|
|
18
24
|
if (!memberNeutralization.ok) {
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { createSemanticEditScript } from './internal/index-impl/semanticEditScripts.js';
|
|
3
|
+
import { projectSemanticEditScriptToSource } from './internal/index-impl/projectSemanticEditScriptToSource.js';
|
|
4
|
+
import { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
|
|
5
|
+
import { JsTsSafeMergeConflictCodes, JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
|
|
6
|
+
import { idFragment, uniqueStrings } from './native-import-utils.js';
|
|
7
|
+
|
|
8
|
+
function semanticEditFallbackResult(input, topLevelResult) {
|
|
9
|
+
if (!shouldTrySemanticEditFallback(topLevelResult)) return topLevelResult;
|
|
10
|
+
const artifacts = createSemanticEditFallbackArtifacts(input, topLevelResult);
|
|
11
|
+
if (artifacts.status !== 'verified') return semanticEditFallbackBlockedResult(input, topLevelResult, artifacts);
|
|
12
|
+
const mergedSourceText = artifacts.projection.sourceText;
|
|
13
|
+
const gates = semanticEditGates(artifacts);
|
|
14
|
+
return {
|
|
15
|
+
...topLevelResult,
|
|
16
|
+
status: JsTsSafeMergeStatuses.merged,
|
|
17
|
+
mergedSourceText,
|
|
18
|
+
outputSourceText: mergedSourceText,
|
|
19
|
+
conflicts: [],
|
|
20
|
+
gates,
|
|
21
|
+
admission: {
|
|
22
|
+
status: 'auto-merge-candidate',
|
|
23
|
+
action: 'apply',
|
|
24
|
+
reviewRequired: false,
|
|
25
|
+
autoApplyCandidate: true,
|
|
26
|
+
autoMergeClaim: false,
|
|
27
|
+
semanticEquivalenceClaim: false,
|
|
28
|
+
reasonCodes: []
|
|
29
|
+
},
|
|
30
|
+
summary: {
|
|
31
|
+
...topLevelResult.summary,
|
|
32
|
+
conflicts: 0,
|
|
33
|
+
gatesPassed: gates.filter((gate) => gate.status === 'passed').length,
|
|
34
|
+
semanticEditOperations: artifacts.script.summary.operations,
|
|
35
|
+
semanticEditAppliedOperations: artifacts.replay.summary.applied,
|
|
36
|
+
semanticEditReplayStatus: artifacts.replay.status,
|
|
37
|
+
composedPhases: 2
|
|
38
|
+
},
|
|
39
|
+
metadata: {
|
|
40
|
+
...topLevelResult.metadata,
|
|
41
|
+
composed: {
|
|
42
|
+
phase: 'semantic-edit-fallback',
|
|
43
|
+
phases: ['top-level-ledger', 'semantic-edit'],
|
|
44
|
+
originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
semanticArtifacts: artifacts
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function shouldTrySemanticEditFallback(result) {
|
|
52
|
+
const conflicts = result.conflicts ?? [];
|
|
53
|
+
return conflicts.length > 0 && conflicts.every((conflict) => conflict.code === JsTsSafeMergeConflictCodes.changedExistingDeclaration);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createSemanticEditFallbackArtifacts(input, topLevelResult) {
|
|
57
|
+
try {
|
|
58
|
+
const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
|
|
59
|
+
const language = input.language ?? topLevelResult.language ?? 'typescript';
|
|
60
|
+
const sourcePath = input.sourcePath ?? topLevelResult.sourcePath ?? 'inline.ts';
|
|
61
|
+
const script = createSemanticEditScript({
|
|
62
|
+
...input,
|
|
63
|
+
id: `${id}_semantic_edit`,
|
|
64
|
+
language,
|
|
65
|
+
sourcePath
|
|
66
|
+
});
|
|
67
|
+
const projection = projectSemanticEditScriptToSource({
|
|
68
|
+
id: `${id}_semantic_edit_projection`,
|
|
69
|
+
script,
|
|
70
|
+
workerSourceText: input.workerSourceText,
|
|
71
|
+
headSourceText: input.headSourceText,
|
|
72
|
+
headSourcePath: sourcePath,
|
|
73
|
+
parser: input.parser
|
|
74
|
+
});
|
|
75
|
+
const replay = replaySemanticEditProjection({
|
|
76
|
+
id: `${id}_semantic_edit_replay`,
|
|
77
|
+
projection,
|
|
78
|
+
currentSourceText: input.headSourceText,
|
|
79
|
+
currentSourcePath: sourcePath,
|
|
80
|
+
language,
|
|
81
|
+
parser: input.parser
|
|
82
|
+
});
|
|
83
|
+
const alreadyAppliedReplay = replaySemanticEditProjection({
|
|
84
|
+
id: `${id}_semantic_edit_already_applied`,
|
|
85
|
+
projection,
|
|
86
|
+
currentSourceText: projection.sourceText,
|
|
87
|
+
currentSourcePath: sourcePath,
|
|
88
|
+
currentSourceHash: projection.projectedHash,
|
|
89
|
+
language,
|
|
90
|
+
parser: input.parser
|
|
91
|
+
});
|
|
92
|
+
return semanticEditArtifacts({
|
|
93
|
+
id,
|
|
94
|
+
language,
|
|
95
|
+
sourcePath,
|
|
96
|
+
script,
|
|
97
|
+
projection,
|
|
98
|
+
replay,
|
|
99
|
+
alreadyAppliedReplay,
|
|
100
|
+
topLevelResult
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return blockedSemanticEditArtifacts(input, topLevelResult, ['semantic-edit-fallback-error'], error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function semanticEditArtifacts(input) {
|
|
108
|
+
const reasonCodes = semanticEditArtifactReasonCodes(input);
|
|
109
|
+
const status = reasonCodes.length ? 'blocked' : 'verified';
|
|
110
|
+
const core = {
|
|
111
|
+
kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
|
|
112
|
+
version: 1,
|
|
113
|
+
schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
|
|
114
|
+
id: `js_ts_safe_merge_semantic_edit_artifacts_${idFragment(input.id)}`,
|
|
115
|
+
sourcePath: input.sourcePath,
|
|
116
|
+
language: input.language,
|
|
117
|
+
status,
|
|
118
|
+
script: input.script,
|
|
119
|
+
projection: input.projection,
|
|
120
|
+
replay: input.replay,
|
|
121
|
+
alreadyAppliedReplay: input.alreadyAppliedReplay,
|
|
122
|
+
admission: {
|
|
123
|
+
status: status === 'verified' ? 'auto-merge-candidate' : 'blocked',
|
|
124
|
+
action: status === 'verified' ? 'apply' : 'human-review',
|
|
125
|
+
reviewRequired: status !== 'verified',
|
|
126
|
+
autoApplyCandidate: status === 'verified',
|
|
127
|
+
autoMergeClaim: false,
|
|
128
|
+
semanticEquivalenceClaim: false,
|
|
129
|
+
reasonCodes
|
|
130
|
+
},
|
|
131
|
+
summary: {
|
|
132
|
+
operations: input.script.summary.operations,
|
|
133
|
+
edits: input.projection.edits.length,
|
|
134
|
+
replayStatus: input.replay.status,
|
|
135
|
+
alreadyAppliedReplayStatus: input.alreadyAppliedReplay.status,
|
|
136
|
+
projectedSourceMatchesMerged: input.projection.sourceText === input.replay.outputSourceText,
|
|
137
|
+
replayOutputMatchesMerged: input.replay.outputSourceText === input.projection.sourceText
|
|
138
|
+
},
|
|
139
|
+
evidence: [{
|
|
140
|
+
id: `evidence_${idFragment(input.id)}_js_ts_semantic_edit_replay`,
|
|
141
|
+
kind: 'js-ts-semantic-edit-replay',
|
|
142
|
+
status: status === 'verified' ? 'passed' : 'needs-review',
|
|
143
|
+
path: input.sourcePath,
|
|
144
|
+
summary: status === 'verified'
|
|
145
|
+
? `JS/TS semantic edit replay verified ${input.script.summary.operations} operation(s).`
|
|
146
|
+
: `JS/TS semantic edit replay requires review: ${reasonCodes.join(', ')}.`
|
|
147
|
+
}],
|
|
148
|
+
metadata: {
|
|
149
|
+
autoMergeClaim: false,
|
|
150
|
+
semanticEquivalenceClaim: false,
|
|
151
|
+
source: 'js-ts-semantic-edit-fallback',
|
|
152
|
+
originalReasonCodes: input.topLevelResult.admission?.reasonCodes ?? []
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return { ...core, hash: hashSemanticValue(core) };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function semanticEditArtifactReasonCodes(input) {
|
|
159
|
+
const scriptReady = input.script.admission.status === 'auto-merge-candidate';
|
|
160
|
+
const projectionReady = input.projection.status === 'projected';
|
|
161
|
+
const replayReady = input.replay.status === 'accepted-clean';
|
|
162
|
+
const alreadyAppliedReady = input.alreadyAppliedReplay.status === 'already-applied';
|
|
163
|
+
return uniqueStrings([
|
|
164
|
+
scriptReady ? undefined : `semantic-edit-script-${input.script.admission.status}`,
|
|
165
|
+
projectionReady ? undefined : `semantic-edit-projection-${input.projection.status}`,
|
|
166
|
+
replayReady ? undefined : `semantic-edit-replay-${input.replay.status}`,
|
|
167
|
+
input.replay.outputSourceText !== input.projection.sourceText ? 'semantic-edit-replay-output-mismatch' : undefined,
|
|
168
|
+
alreadyAppliedReady ? undefined : `semantic-edit-already-applied-${input.alreadyAppliedReplay.status}`,
|
|
169
|
+
...(scriptReady ? [] : input.script.admission.reasonCodes),
|
|
170
|
+
...(projectionReady ? [] : input.projection.admission.reasonCodes),
|
|
171
|
+
...(replayReady ? [] : input.replay.admission.reasonCodes),
|
|
172
|
+
...(alreadyAppliedReady ? [] : input.alreadyAppliedReplay.admission.reasonCodes)
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function blockedSemanticEditArtifacts(input, topLevelResult, reasonCodes, error) {
|
|
177
|
+
const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
|
|
178
|
+
const core = {
|
|
179
|
+
kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
|
|
180
|
+
version: 1,
|
|
181
|
+
schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
|
|
182
|
+
id: `js_ts_safe_merge_semantic_edit_artifacts_${idFragment(id)}`,
|
|
183
|
+
sourcePath: input.sourcePath ?? topLevelResult.sourcePath,
|
|
184
|
+
language: input.language ?? topLevelResult.language ?? 'typescript',
|
|
185
|
+
status: 'blocked',
|
|
186
|
+
admission: {
|
|
187
|
+
status: 'blocked',
|
|
188
|
+
action: 'human-review',
|
|
189
|
+
reviewRequired: true,
|
|
190
|
+
autoApplyCandidate: false,
|
|
191
|
+
autoMergeClaim: false,
|
|
192
|
+
semanticEquivalenceClaim: false,
|
|
193
|
+
reasonCodes
|
|
194
|
+
},
|
|
195
|
+
summary: {
|
|
196
|
+
operations: 0,
|
|
197
|
+
edits: 0,
|
|
198
|
+
replayStatus: 'blocked',
|
|
199
|
+
alreadyAppliedReplayStatus: 'blocked',
|
|
200
|
+
projectedSourceMatchesMerged: false,
|
|
201
|
+
replayOutputMatchesMerged: false
|
|
202
|
+
},
|
|
203
|
+
metadata: {
|
|
204
|
+
source: 'js-ts-semantic-edit-fallback',
|
|
205
|
+
error: error?.message
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return { ...core, hash: hashSemanticValue(core) };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function semanticEditFallbackBlockedResult(input, topLevelResult, artifacts) {
|
|
212
|
+
const reasonCodes = artifacts.admission.reasonCodes.length
|
|
213
|
+
? artifacts.admission.reasonCodes
|
|
214
|
+
: topLevelResult.admission?.reasonCodes ?? [];
|
|
215
|
+
const gates = semanticEditGates(artifacts);
|
|
216
|
+
const conflict = {
|
|
217
|
+
code: JsTsSafeMergeConflictCodes.changedExistingDeclaration,
|
|
218
|
+
gateId: 'semantic-edit-replay',
|
|
219
|
+
message: 'JS/TS semantic edit fallback did not verify a clean replay.',
|
|
220
|
+
side: 'worker',
|
|
221
|
+
sourcePath: input.sourcePath ?? topLevelResult.sourcePath,
|
|
222
|
+
details: { reasonCodes }
|
|
223
|
+
};
|
|
224
|
+
return {
|
|
225
|
+
...topLevelResult,
|
|
226
|
+
conflicts: [conflict],
|
|
227
|
+
gates,
|
|
228
|
+
admission: {
|
|
229
|
+
status: 'blocked',
|
|
230
|
+
action: 'human-review',
|
|
231
|
+
reviewRequired: true,
|
|
232
|
+
autoApplyCandidate: false,
|
|
233
|
+
autoMergeClaim: false,
|
|
234
|
+
semanticEquivalenceClaim: false,
|
|
235
|
+
reasonCodes
|
|
236
|
+
},
|
|
237
|
+
summary: {
|
|
238
|
+
...topLevelResult.summary,
|
|
239
|
+
conflicts: 1,
|
|
240
|
+
gatesPassed: gates.filter((gate) => gate.status === 'passed').length,
|
|
241
|
+
semanticEditOperations: artifacts.summary.operations,
|
|
242
|
+
semanticEditReplayStatus: artifacts.summary.replayStatus
|
|
243
|
+
},
|
|
244
|
+
semanticArtifacts: artifacts
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function semanticEditGates(artifacts) {
|
|
249
|
+
return [
|
|
250
|
+
gate('semantic-edit-script', artifacts.script?.admission?.status === 'auto-merge-candidate', artifacts.script?.admission?.reasonCodes),
|
|
251
|
+
gate('semantic-edit-projection', artifacts.projection?.status === 'projected', artifacts.projection?.admission?.reasonCodes),
|
|
252
|
+
gate('semantic-edit-replay', artifacts.replay?.status === 'accepted-clean', artifacts.replay?.admission?.reasonCodes),
|
|
253
|
+
gate('semantic-edit-already-applied', artifacts.alreadyAppliedReplay?.status === 'already-applied', artifacts.alreadyAppliedReplay?.admission?.reasonCodes)
|
|
254
|
+
];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function gate(id, passed, reasonCodes = []) {
|
|
258
|
+
return { id, status: passed ? 'passed' : 'blocked', reasonCodes: passed ? [] : uniqueStrings(reasonCodes) };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export {
|
|
262
|
+
semanticEditFallbackResult
|
|
263
|
+
};
|
package/package.json
CHANGED