@shapeshift-labs/frontier-lang-compiler 0.2.146 → 0.2.148

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 { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import {
3
+ independentTopLevelDeletionOperation,
4
+ independentTopLevelDeletionProjection,
5
+ independentTopLevelDeletionReplay,
6
+ independentTopLevelDeletionScript
7
+ } from './js-ts-safe-merge-independent-deletion-records.js';
8
+ import { idFragment, uniqueStrings } from './native-import-utils.js';
9
+
10
+ function createIndependentTopLevelDeletionArtifacts(input, topLevelResult, deletionPlan) {
11
+ const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
12
+ const language = input.language ?? topLevelResult.language ?? 'typescript';
13
+ const sourcePath = input.sourcePath ?? topLevelResult.sourcePath ?? 'inline.ts';
14
+ const operationId = `js_ts_independent_top_level_delete_${idFragment([id, deletionPlan.deletedEntry.key].join(':'))}`;
15
+ const operation = independentTopLevelDeletionOperation({ id, operationId, language, sourcePath, deletionPlan, input });
16
+ const script = independentTopLevelDeletionScript({ id, language, sourcePath, operation, input });
17
+ const projection = independentTopLevelDeletionProjection({ id, language, sourcePath, operation, script, deletionPlan, input });
18
+ const replay = independentTopLevelDeletionReplay({
19
+ id: `${id}_semantic_edit_replay`,
20
+ language,
21
+ sourcePath,
22
+ operation,
23
+ projection,
24
+ deletionPlan,
25
+ currentSourceText: input.headSourceText,
26
+ status: 'accepted-clean',
27
+ editStatus: 'applied',
28
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion'],
29
+ outputSourceText: deletionPlan.mergedSourceText
30
+ });
31
+ const alreadyAppliedReplay = independentTopLevelDeletionReplay({
32
+ id: `${id}_semantic_edit_already_applied`,
33
+ language,
34
+ sourcePath,
35
+ operation,
36
+ projection,
37
+ deletionPlan,
38
+ currentSourceText: deletionPlan.mergedSourceText,
39
+ status: 'already-applied',
40
+ editStatus: 'already-applied',
41
+ reasonCodes: ['independent-top-level-deletion-already-applied'],
42
+ outputSourceText: deletionPlan.mergedSourceText
43
+ });
44
+ const status = projection.status === 'projected'
45
+ && replay.status === 'accepted-clean'
46
+ && replay.outputSourceText === deletionPlan.mergedSourceText
47
+ && alreadyAppliedReplay.status === 'already-applied'
48
+ ? 'verified'
49
+ : 'blocked';
50
+ const reasonCodes = status === 'verified'
51
+ ? []
52
+ : uniqueStrings([
53
+ ...(projection.admission?.reasonCodes ?? []),
54
+ ...(replay.admission?.reasonCodes ?? []),
55
+ ...(alreadyAppliedReplay.admission?.reasonCodes ?? [])
56
+ ]);
57
+ const core = {
58
+ kind: 'frontier.lang.jsTsSafeMergeSemanticArtifacts',
59
+ version: 1,
60
+ schema: 'frontier.lang.jsTsSafeMergeSemanticArtifacts.v1',
61
+ id: `js_ts_safe_merge_semantic_edit_artifacts_${idFragment(id)}`,
62
+ sourcePath,
63
+ language,
64
+ status,
65
+ script,
66
+ projection,
67
+ replay,
68
+ alreadyAppliedReplay,
69
+ admission: {
70
+ status: status === 'verified' ? 'auto-merge-candidate' : 'blocked',
71
+ action: status === 'verified' ? 'apply' : 'human-review',
72
+ reviewRequired: status !== 'verified',
73
+ autoApplyCandidate: status === 'verified',
74
+ autoMergeClaim: false,
75
+ semanticEquivalenceClaim: false,
76
+ reasonCodes
77
+ },
78
+ summary: {
79
+ operations: script.summary.operations,
80
+ edits: projection.edits.length,
81
+ replayStatus: replay.status,
82
+ alreadyAppliedReplayStatus: alreadyAppliedReplay.status,
83
+ projectedSourceMatchesMerged: projection.sourceText === deletionPlan.mergedSourceText,
84
+ replayOutputMatchesMerged: replay.outputSourceText === deletionPlan.mergedSourceText
85
+ },
86
+ evidence: [{
87
+ id: `evidence_${idFragment(id)}_independent_top_level_deletion`,
88
+ kind: 'js-ts-independent-top-level-deletion-replay',
89
+ status: status === 'verified' ? 'passed' : 'needs-review',
90
+ path: sourcePath,
91
+ summary: status === 'verified'
92
+ ? 'JS/TS independent top-level deletion replay verified 1 operation.'
93
+ : `JS/TS independent top-level deletion requires review: ${reasonCodes.join(', ')}.`,
94
+ metadata: {
95
+ autoMergeClaim: false,
96
+ semanticEquivalenceClaim: false,
97
+ deletedKey: deletionPlan.deletedEntry.key,
98
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
99
+ }
100
+ }],
101
+ metadata: {
102
+ autoMergeClaim: false,
103
+ semanticEquivalenceClaim: false,
104
+ source: 'independent-top-level-deletion-fallback',
105
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
106
+ deletion: deletionPlan.summary
107
+ }
108
+ };
109
+ return { ...core, hash: hashSemanticValue(core) };
110
+ }
111
+
112
+ export { createIndependentTopLevelDeletionArtifacts };
@@ -0,0 +1,53 @@
1
+ import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
2
+ import { createIndependentTopLevelDeletionArtifacts } from './js-ts-safe-merge-independent-deletion-artifacts.js';
3
+ import { createIndependentTopLevelDeletionPlan } from './js-ts-safe-merge-independent-deletion-plan.js';
4
+ import { semanticEditGates } from './js-ts-safe-merge-semantic-edit-gates.js';
5
+
6
+ function independentTopLevelDeletionFallbackResult(input, topLevelResult) {
7
+ const deletionPlan = createIndependentTopLevelDeletionPlan(input, topLevelResult);
8
+ if (!deletionPlan.ok) return undefined;
9
+ const artifacts = createIndependentTopLevelDeletionArtifacts(input, topLevelResult, deletionPlan);
10
+ if (artifacts.status !== 'verified') return undefined;
11
+ const gates = semanticEditGates(artifacts);
12
+ return {
13
+ ...topLevelResult,
14
+ id: String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge'),
15
+ status: JsTsSafeMergeStatuses.merged,
16
+ mergedSourceText: deletionPlan.mergedSourceText,
17
+ outputSourceText: deletionPlan.mergedSourceText,
18
+ conflicts: [],
19
+ gates,
20
+ admission: {
21
+ status: 'auto-merge-candidate',
22
+ action: 'apply',
23
+ reviewRequired: false,
24
+ autoApplyCandidate: true,
25
+ autoMergeClaim: false,
26
+ semanticEquivalenceClaim: false,
27
+ reasonCodes: []
28
+ },
29
+ summary: {
30
+ ...topLevelResult.summary,
31
+ changedExistingDeclarations: 0,
32
+ conflicts: 0,
33
+ gatesPassed: gates.filter((gateRecord) => gateRecord.status === 'passed').length,
34
+ topLevelDeclarationDeletions: 1,
35
+ semanticEditOperations: artifacts.script.summary.operations,
36
+ semanticEditAppliedOperations: artifacts.replay.summary.applied,
37
+ semanticEditReplayStatus: artifacts.replay.status,
38
+ composedPhases: 2
39
+ },
40
+ metadata: {
41
+ ...topLevelResult.metadata,
42
+ composed: {
43
+ phase: 'independent-top-level-deletion-fallback',
44
+ phases: ['top-level-ledger', 'independent-top-level-deletion'],
45
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
46
+ deletion: deletionPlan.summary
47
+ }
48
+ },
49
+ semanticArtifacts: artifacts
50
+ };
51
+ }
52
+
53
+ export { independentTopLevelDeletionFallbackResult };
@@ -0,0 +1,156 @@
1
+ import { JsTsSafeMergeConflictCodes } from './js-ts-safe-merge-constants.js';
2
+ import { sameStatementText } from './js-ts-safe-merge-context.js';
3
+ import { scanJsTsTopLevelLedger } from './js-ts-safe-merge-ledger.js';
4
+ import { uniqueStrings } from './native-import-utils.js';
5
+
6
+ function createIndependentTopLevelDeletionPlan(input, topLevelResult) {
7
+ const originalReasonCodes = topLevelResult?.admission?.reasonCodes ?? [];
8
+ if (!originalReasonCodes.includes(JsTsSafeMergeConflictCodes.topLevelOrderChanged)) {
9
+ return { ok: false, reasonCodes: ['top-level-order-not-deletion-shaped'] };
10
+ }
11
+ const allowedOriginalReasonCodes = new Set([
12
+ JsTsSafeMergeConflictCodes.topLevelOrderChanged,
13
+ JsTsSafeMergeConflictCodes.changedExistingDeclaration,
14
+ JsTsSafeMergeConflictCodes.typeAliasConflict
15
+ ]);
16
+ if (originalReasonCodes.some((reason) => !allowedOriginalReasonCodes.has(reason))) {
17
+ return { ok: false, reasonCodes: ['top-level-deletion-has-unsafe-original-conflict'] };
18
+ }
19
+ if (typeof input.baseSourceText !== 'string'
20
+ || typeof input.workerSourceText !== 'string'
21
+ || typeof input.headSourceText !== 'string') {
22
+ return { ok: false, reasonCodes: ['missing-source-text'] };
23
+ }
24
+
25
+ const context = quietDeletionLedgerContext(input);
26
+ const base = scanJsTsTopLevelLedger(input.baseSourceText, 'base', context);
27
+ const worker = scanJsTsTopLevelLedger(input.workerSourceText, 'worker', context);
28
+ const head = scanJsTsTopLevelLedger(input.headSourceText, 'head', context);
29
+ if (context.conflicts.length) {
30
+ return { ok: false, reasonCodes: uniqueStrings(context.conflicts.map((conflict) => conflict.code)) };
31
+ }
32
+
33
+ const duplicateLedgerReason = firstDuplicateLedgerReason(base, worker, head);
34
+ if (duplicateLedgerReason) return { ok: false, reasonCodes: [duplicateLedgerReason] };
35
+
36
+ const baseByKey = entriesByKey(base.entries);
37
+ const workerByKey = entriesByKey(worker.entries);
38
+ const headByKey = entriesByKey(head.entries);
39
+ const baseKeys = base.entries.map((entry) => entry.key);
40
+ const missingWorkerBaseEntries = base.entries.filter((entry) => !workerByKey.has(entry.key));
41
+ const deletedWorkerEntries = missingWorkerBaseEntries.filter((entry) => entry.kind !== 'import');
42
+ const workerAddedEntries = worker.entries.filter((entry) => !baseByKey.has(entry.key));
43
+ if (missingWorkerBaseEntries.length !== 1
44
+ || deletedWorkerEntries.length !== 1
45
+ || workerAddedEntries.length !== 0) {
46
+ return { ok: false, reasonCodes: ['worker-top-level-deletion-not-isolated'] };
47
+ }
48
+
49
+ const deletedEntry = deletedWorkerEntries[0];
50
+ if (deletedEntry.kind !== 'declaration' || deletedEntry.declarationInfo?.exported === true) {
51
+ return { ok: false, reasonCodes: ['exported-or-unsupported-top-level-deletion'] };
52
+ }
53
+ if (isUnsupportedTopLevelDeletion(deletedEntry)) {
54
+ return { ok: false, reasonCodes: ['unsupported-top-level-deletion-declaration'] };
55
+ }
56
+
57
+ const expectedWorkerKeys = base.entries
58
+ .filter((entry) => entry.key !== deletedEntry.key)
59
+ .map((entry) => entry.key);
60
+ if (!sameStringList(worker.entries.map((entry) => entry.key), expectedWorkerKeys)) {
61
+ return { ok: false, reasonCodes: ['worker-top-level-deletion-order-changed'] };
62
+ }
63
+ for (const baseEntry of base.entries) {
64
+ if (baseEntry.key === deletedEntry.key) continue;
65
+ const workerEntry = workerByKey.get(baseEntry.key);
66
+ if (!workerEntry || !sameStatementText(workerEntry.text, baseEntry.text)) {
67
+ return { ok: false, reasonCodes: [`worker-changed-nondeleted-entry:${baseEntry.key}`] };
68
+ }
69
+ }
70
+
71
+ const headProjectedBaseKeys = head.entries
72
+ .filter((entry) => baseByKey.has(entry.key))
73
+ .map((entry) => entry.key);
74
+ if (!sameStringList(headProjectedBaseKeys, baseKeys)) {
75
+ return { ok: false, reasonCodes: ['head-base-order-or-presence-changed'] };
76
+ }
77
+
78
+ const headEntry = headByKey.get(deletedEntry.key);
79
+ if (!headEntry) return { ok: false, reasonCodes: [`head-anchor-missing:${deletedEntry.key}`] };
80
+ if (!sameStatementText(headEntry.text, deletedEntry.text)) {
81
+ return { ok: false, reasonCodes: [`head-anchor-changed-since-base:${deletedEntry.key}`] };
82
+ }
83
+
84
+ const edit = deleteEntryEdit(input.headSourceText, headEntry);
85
+ const deletedText = input.headSourceText.slice(edit.start, edit.end);
86
+ const mergedSourceText = applySourceEdits(input.headSourceText, [edit]);
87
+ return {
88
+ ok: true,
89
+ base,
90
+ worker,
91
+ head,
92
+ deletedEntry,
93
+ headEntry,
94
+ edit,
95
+ deletedText,
96
+ mergedSourceText,
97
+ summary: {
98
+ key: deletedEntry.key,
99
+ name: deletedEntry.names?.[0],
100
+ declarationKind: deletedEntry.declarationInfo?.declarationKind,
101
+ exported: false,
102
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion']
103
+ }
104
+ };
105
+ }
106
+
107
+ function quietDeletionLedgerContext(input) {
108
+ return {
109
+ id: String(input.id ?? 'js_ts_safe_merge'),
110
+ sourcePath: input.sourcePath,
111
+ language: input.language ?? 'typescript',
112
+ conflicts: [],
113
+ blockedGateIds: new Set(),
114
+ gateReasonCodes: new Map()
115
+ };
116
+ }
117
+
118
+ function firstDuplicateLedgerReason(...ledgers) {
119
+ for (const ledger of ledgers) {
120
+ const seen = new Set();
121
+ for (const entry of ledger.entries) {
122
+ if (seen.has(entry.key)) return `duplicate-ledger-key:${ledger.label}:${entry.key}`;
123
+ seen.add(entry.key);
124
+ }
125
+ }
126
+ return undefined;
127
+ }
128
+
129
+ function entriesByKey(entries) {
130
+ return new Map(entries.map((entry) => [entry.key, entry]));
131
+ }
132
+
133
+ function isUnsupportedTopLevelDeletion(entry) {
134
+ if (entry.declarationInfo?.declarationKind === 'module') return true;
135
+ return /^\s*(?:export\s+)?declare\b/.test(entry.text ?? '');
136
+ }
137
+
138
+ function sameStringList(left, right) {
139
+ return left.length === right.length && left.every((value, index) => value === right[index]);
140
+ }
141
+
142
+ function deleteEntryEdit(sourceText, entry) {
143
+ const lineEnd = sourceText.indexOf('\n', entry.end);
144
+ const end = lineEnd === -1 ? entry.end : lineEnd + 1;
145
+ const lineStart = sourceText.lastIndexOf('\n', Math.max(0, entry.start - 1)) + 1;
146
+ const start = /^[\t ]*$/.test(sourceText.slice(lineStart, entry.start)) ? lineStart : entry.start;
147
+ return { start, end, text: '' };
148
+ }
149
+
150
+ function applySourceEdits(sourceText, edits) {
151
+ return [...edits]
152
+ .sort((left, right) => right.start - left.start || right.end - left.end)
153
+ .reduce((current, edit) => `${current.slice(0, edit.start)}${edit.text}${current.slice(edit.end)}`, sourceText);
154
+ }
155
+
156
+ export { createIndependentTopLevelDeletionPlan };
@@ -0,0 +1,251 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { idFragment } from './native-import-utils.js';
3
+
4
+ function independentTopLevelDeletionOperation(input) {
5
+ const entry = input.deletionPlan.deletedEntry;
6
+ const name = entry.names?.[0] ?? entry.key;
7
+ const symbolKind = entry.declarationInfo?.declarationKind ?? 'declaration';
8
+ const anchorKey = `source#${input.sourcePath}#declaration#${name}`;
9
+ const operation = {
10
+ id: input.operationId,
11
+ kind: 'jsTsRemoveTopLevelDeclaration',
12
+ changeKind: 'removed',
13
+ anchor: {
14
+ key: anchorKey,
15
+ conflictKey: `declaration:${entry.key}`,
16
+ regionId: anchorKey,
17
+ regionKind: 'declaration',
18
+ granularity: 'js-ts-ledger-entry',
19
+ language: input.language,
20
+ sourcePath: input.sourcePath,
21
+ symbolId: anchorKey,
22
+ symbolName: name,
23
+ symbolKind,
24
+ sourceSpan: { start: input.deletionPlan.headEntry.start, end: input.deletionPlan.headEntry.end }
25
+ },
26
+ spans: {
27
+ base: { start: entry.start, end: entry.end },
28
+ head: { start: input.deletionPlan.headEntry.start, end: input.deletionPlan.headEntry.end }
29
+ },
30
+ hashes: {
31
+ baseSourceHash: input.input.baseHash,
32
+ workerSourceHash: input.input.workerHash,
33
+ headSourceHash: input.input.headHash,
34
+ baseTextHash: hashSemanticValue(entry.text),
35
+ headTextHash: hashSemanticValue(input.deletionPlan.headEntry.text),
36
+ workerTextHash: hashSemanticValue('')
37
+ },
38
+ status: 'portable',
39
+ readiness: 'ready',
40
+ confidence: 1,
41
+ reasonCodes: ['head-anchor-matches-base', 'independent-top-level-deletion'],
42
+ evidenceIds: [`evidence_${idFragment(input.id)}_independent_top_level_deletion`],
43
+ metadata: {
44
+ autoMergeClaim: false,
45
+ semanticEquivalenceClaim: false,
46
+ ledgerKey: entry.key,
47
+ exported: false
48
+ }
49
+ };
50
+ return { ...operation, operationContentHash: hashSemanticValue(operation) };
51
+ }
52
+
53
+ function independentTopLevelDeletionScript(input) {
54
+ const core = {
55
+ kind: 'frontier.lang.semanticEditScript',
56
+ version: 1,
57
+ schema: 'frontier.lang.semanticEditScript.v1',
58
+ id: `${input.id}_semantic_edit`,
59
+ stableId: `semantic_edit_script_${idFragment([input.id, input.operation.operationContentHash].join(':'))}`,
60
+ language: input.language,
61
+ sourcePath: input.sourcePath,
62
+ baseHash: input.input.baseHash,
63
+ workerHash: input.input.workerHash,
64
+ headHash: input.input.headHash,
65
+ operations: [input.operation],
66
+ summary: {
67
+ operations: 1,
68
+ byStatus: { portable: 1 },
69
+ byKind: { jsTsRemoveTopLevelDeclaration: 1 },
70
+ portable: 1,
71
+ alreadyApplied: 0,
72
+ needsPort: 0,
73
+ conflicts: 0,
74
+ stale: 0,
75
+ blocked: 0,
76
+ candidates: 0,
77
+ autoMergeCandidates: 1,
78
+ operationContentHashes: [input.operation.operationContentHash]
79
+ },
80
+ admission: {
81
+ status: 'auto-merge-candidate',
82
+ action: 'run-gates-and-apply',
83
+ reviewRequired: false,
84
+ autoApplyCandidate: true,
85
+ autoMergeClaim: false,
86
+ semanticEquivalenceClaim: false,
87
+ reasonCodes: input.operation.reasonCodes,
88
+ conflictKeys: [input.operation.anchor.conflictKey],
89
+ evidenceIds: input.operation.evidenceIds
90
+ },
91
+ evidence: [{
92
+ id: `evidence_${idFragment(input.id)}_independent_top_level_deletion_script`,
93
+ kind: 'semantic-edit-script',
94
+ status: 'passed',
95
+ path: input.sourcePath,
96
+ summary: 'Created independent top-level deletion script with 1 operation.',
97
+ metadata: {
98
+ autoMergeClaim: false,
99
+ semanticEquivalenceClaim: false
100
+ }
101
+ }],
102
+ metadata: {
103
+ autoMergeClaim: false,
104
+ semanticEquivalenceClaim: false,
105
+ source: 'independent-top-level-deletion-fallback'
106
+ }
107
+ };
108
+ return { ...core, hash: hashSemanticValue(core) };
109
+ }
110
+
111
+ function independentTopLevelDeletionProjection(input) {
112
+ const editContentHash = hashSemanticValue({
113
+ operationId: input.operation.id,
114
+ deletedText: input.deletionPlan.deletedText,
115
+ replacementText: ''
116
+ });
117
+ const edit = {
118
+ operationId: input.operation.id,
119
+ status: 'applied',
120
+ kind: input.operation.kind,
121
+ editKind: 'delete',
122
+ changeKind: 'removed',
123
+ anchorKey: input.operation.anchor.key,
124
+ conflictKey: input.operation.anchor.conflictKey,
125
+ regionId: input.operation.anchor.regionId,
126
+ regionKind: input.operation.anchor.regionKind,
127
+ sourcePath: input.sourcePath,
128
+ symbolId: input.operation.anchor.symbolId,
129
+ symbolName: input.operation.anchor.symbolName,
130
+ symbolKind: input.operation.anchor.symbolKind,
131
+ operationContentHash: input.operation.operationContentHash,
132
+ editContentHash,
133
+ headStart: input.deletionPlan.edit.start,
134
+ headEnd: input.deletionPlan.edit.end,
135
+ editOrder: 0,
136
+ deletedBytes: input.deletionPlan.deletedText.length,
137
+ replacementBytes: 0,
138
+ deletedTextHash: hashSemanticValue(input.deletionPlan.deletedText),
139
+ replacementTextHash: hashSemanticValue(''),
140
+ deletedTextLineEndingStableHash: lineEndingStableTextHash(input.deletionPlan.deletedText),
141
+ replacementTextLineEndingStableHash: lineEndingStableTextHash(''),
142
+ replacementText: ''
143
+ };
144
+ const core = {
145
+ kind: 'frontier.lang.semanticEditProjection',
146
+ version: 1,
147
+ id: `${input.id}_semantic_edit_projection`,
148
+ scriptId: input.script.id,
149
+ status: 'projected',
150
+ sourcePath: input.sourcePath,
151
+ language: input.language,
152
+ baseHash: input.input.baseHash,
153
+ workerHash: input.input.workerHash,
154
+ headHash: input.input.headHash,
155
+ projectedHash: hashSemanticValue(input.deletionPlan.mergedSourceText),
156
+ appliedOperations: [input.operation.id],
157
+ skippedOperations: [],
158
+ edits: [edit],
159
+ sourceText: input.deletionPlan.mergedSourceText,
160
+ admission: {
161
+ status: 'auto-merge-candidate',
162
+ autoMergeClaim: false,
163
+ semanticEquivalenceClaim: false,
164
+ reasonCodes: []
165
+ },
166
+ metadata: {
167
+ autoMergeClaim: false,
168
+ semanticEquivalenceClaim: false,
169
+ source: 'independent-top-level-deletion-fallback'
170
+ }
171
+ };
172
+ return { ...core, hash: hashSemanticValue(core) };
173
+ }
174
+
175
+ function independentTopLevelDeletionReplay(input) {
176
+ const applied = input.editStatus === 'applied';
177
+ const edit = {
178
+ operationId: input.operation.id,
179
+ editKind: 'delete',
180
+ editOrder: 0,
181
+ sourcePath: input.sourcePath,
182
+ symbolName: input.operation.anchor.symbolName,
183
+ symbolKind: input.operation.anchor.symbolKind,
184
+ status: input.editStatus,
185
+ start: applied ? input.deletionPlan.edit.start : undefined,
186
+ end: applied ? input.deletionPlan.edit.end : undefined,
187
+ replacementBytes: 0,
188
+ replacementText: '',
189
+ reasonCodes: input.reasonCodes
190
+ };
191
+ const core = {
192
+ kind: 'frontier.lang.semanticEditReplay',
193
+ version: 1,
194
+ schema: 'frontier.lang.semanticEditReplay.v1',
195
+ id: input.id,
196
+ projectionId: input.projection.id,
197
+ scriptId: input.projection.scriptId,
198
+ sourcePath: input.sourcePath,
199
+ language: input.language,
200
+ currentHash: hashSemanticValue(input.currentSourceText),
201
+ projectedHash: input.projection.projectedHash,
202
+ outputHash: hashSemanticValue(input.outputSourceText),
203
+ status: input.status,
204
+ edits: [edit],
205
+ appliedOperations: applied ? [input.operation.id] : [],
206
+ skippedOperations: applied ? [] : [input.operation.id],
207
+ admission: {
208
+ status: input.status,
209
+ action: applied ? 'apply' : 'skip',
210
+ reviewRequired: false,
211
+ autoApplyCandidate: applied,
212
+ autoMergeClaim: false,
213
+ semanticEquivalenceClaim: false,
214
+ reasonCodes: []
215
+ },
216
+ outputSourceText: input.outputSourceText,
217
+ summary: {
218
+ edits: 1,
219
+ applied: applied ? 1 : 0,
220
+ alreadyApplied: applied ? 0 : 1,
221
+ conflicts: 0,
222
+ stale: 0,
223
+ blocked: 0,
224
+ reasonCodes: []
225
+ },
226
+ metadata: {
227
+ autoMergeClaim: false,
228
+ semanticEquivalenceClaim: false,
229
+ source: 'independent-top-level-deletion-fallback'
230
+ }
231
+ };
232
+ return { ...core, hash: hashSemanticValue(core) };
233
+ }
234
+
235
+ function lineEndingStableTextHash(value) {
236
+ const normalized = lineEndingStableText(value);
237
+ return normalized === undefined ? undefined : hashSemanticValue(normalized);
238
+ }
239
+
240
+ function lineEndingStableText(value) {
241
+ if (typeof value !== 'string') return undefined;
242
+ const normalized = value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
243
+ return normalized.length > 1 && normalized.endsWith('\n') ? normalized.slice(0, -1) : normalized;
244
+ }
245
+
246
+ export {
247
+ independentTopLevelDeletionOperation,
248
+ independentTopLevelDeletionProjection,
249
+ independentTopLevelDeletionReplay,
250
+ independentTopLevelDeletionScript
251
+ };
@@ -0,0 +1,127 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { uniqueStrings } from './native-import-utils.js';
3
+
4
+ function normalizeAlreadyAppliedDeleteReplay(input) {
5
+ if (!isProjectedDeleteAlreadyAppliedReplay(input)) return input.alreadyAppliedReplay;
6
+ const { hash: _hash, ...alreadyAppliedReplay } = input.alreadyAppliedReplay;
7
+ const normalizedEdits = input.alreadyAppliedReplay.edits.map((edit) => alreadyAppliedDeleteEdit(edit));
8
+ const reasonCodes = uniqueStrings(normalizedEdits.flatMap((edit) => edit.reasonCodes ?? []));
9
+ const core = {
10
+ ...alreadyAppliedReplay,
11
+ currentHash: input.projection.projectedHash,
12
+ outputHash: input.projection.projectedHash,
13
+ status: 'already-applied',
14
+ edits: normalizedEdits,
15
+ appliedOperations: [],
16
+ skippedOperations: normalizedEdits.map((edit) => edit.operationId).filter(Boolean),
17
+ diagnostics: [],
18
+ admission: {
19
+ status: 'already-applied',
20
+ action: 'skip',
21
+ reviewRequired: false,
22
+ autoApplyCandidate: false,
23
+ autoMergeClaim: false,
24
+ semanticEquivalenceClaim: false,
25
+ reasonCodes
26
+ },
27
+ outputSourceText: input.projection.sourceText,
28
+ summary: {
29
+ edits: normalizedEdits.length,
30
+ applied: 0,
31
+ alreadyApplied: normalizedEdits.length,
32
+ conflicts: 0,
33
+ stale: 0,
34
+ blocked: 0,
35
+ reasonCodes
36
+ },
37
+ metadata: {
38
+ ...input.alreadyAppliedReplay.metadata,
39
+ normalizedAlreadyAppliedReplay: 'projected-delete-anchor-absent'
40
+ }
41
+ };
42
+ return { ...core, hash: hashSemanticValue(core) };
43
+ }
44
+
45
+ function isProjectedDeleteAlreadyAppliedReplay(input) {
46
+ if (input.projection?.status !== 'projected') return false;
47
+ if (input.replay?.status !== 'accepted-clean') return false;
48
+ if (input.replay.outputSourceText !== input.projection.sourceText) return false;
49
+ const replay = input.alreadyAppliedReplay;
50
+ if (replay?.status !== 'stale') return false;
51
+ if (replay.currentHash !== input.projection.projectedHash) return false;
52
+ const edits = replay.edits ?? [];
53
+ if (!edits.length) return false;
54
+ const appliedDeleteIds = new Set((input.replay.edits ?? [])
55
+ .filter((edit) => edit.status === 'applied' && edit.editKind === 'delete')
56
+ .map((edit) => edit.operationId));
57
+ const projectionDeleteIds = new Set((input.projection.edits ?? [])
58
+ .filter((edit) => edit.editKind === 'delete')
59
+ .map((edit) => edit.operationId));
60
+ const staleDeletes = edits.filter(isProjectedDeleteMissingAnchor);
61
+ if (hasUnsafeProjectedDeleteCompanions(input, staleDeletes)) return false;
62
+ return staleDeletes.length > 0
63
+ && edits.every((edit) => edit.status === 'already-applied' || isProjectedDeleteMissingAnchor(edit))
64
+ && staleDeletes.every((edit) => appliedDeleteIds.has(edit.operationId) && projectionDeleteIds.has(edit.operationId));
65
+ }
66
+
67
+ function isProjectedDeleteMissingAnchor(edit) {
68
+ return edit.editKind === 'delete'
69
+ && edit.status === 'stale'
70
+ && (edit.reasonCodes ?? []).includes('current-symbol-anchor-missing');
71
+ }
72
+
73
+ function alreadyAppliedDeleteEdit(edit) {
74
+ if (!isProjectedDeleteMissingAnchor(edit)) return edit;
75
+ return {
76
+ ...edit,
77
+ status: 'already-applied',
78
+ reasonCodes: ['projected-delete-anchor-absent'],
79
+ diagnostics: []
80
+ };
81
+ }
82
+
83
+ function hasUnsafeProjectedDeleteCompanions(input, staleDeletes) {
84
+ const projectionEdits = input.projection?.edits ?? [];
85
+ if (!projectionEdits.length || !staleDeletes.length) return false;
86
+ const projectionDeleteById = new Map(projectionEdits
87
+ .filter((edit) => edit.editKind === 'delete')
88
+ .map((edit) => [edit.operationId, edit]));
89
+ for (const staleDelete of staleDeletes) {
90
+ const projectedDelete = projectionDeleteById.get(staleDelete.operationId);
91
+ const scope = memberBodyDeleteScope(projectedDelete ?? staleDelete);
92
+ if (!scope) continue;
93
+ const companions = projectionEdits.filter((edit) => (
94
+ edit.operationId !== staleDelete.operationId
95
+ && edit.editKind !== 'delete'
96
+ && symbolContainerName(edit.symbolName) === scope.container
97
+ ));
98
+ if (!companions.length) return true;
99
+ const bodyInserts = companions.filter((edit) => (
100
+ edit.editKind === 'insert'
101
+ && edit.regionKind === scope.regionKind
102
+ && edit.symbolKind === scope.symbolKind
103
+ ));
104
+ if (bodyInserts.length === 0) return true;
105
+ if (companions.some((edit) => !bodyInserts.includes(edit))) return true;
106
+ }
107
+ return false;
108
+ }
109
+
110
+ function memberBodyDeleteScope(edit) {
111
+ if (edit?.editKind !== 'delete' || edit?.regionKind !== 'body') return undefined;
112
+ const container = symbolContainerName(edit.symbolName);
113
+ if (!container) return undefined;
114
+ return {
115
+ container,
116
+ regionKind: edit.regionKind,
117
+ symbolKind: edit.symbolKind
118
+ };
119
+ }
120
+
121
+ function symbolContainerName(symbolName) {
122
+ const normalized = String(symbolName ?? '').split(':controlFlow:')[0];
123
+ const separator = normalized.lastIndexOf('.');
124
+ return separator === -1 ? undefined : normalized.slice(0, separator);
125
+ }
126
+
127
+ export { normalizeAlreadyAppliedDeleteReplay };
@@ -3,12 +3,15 @@ import { createSemanticEditScript } from './internal/index-impl/semanticEditScri
3
3
  import { projectSemanticEditScriptToSource } from './internal/index-impl/projectSemanticEditScriptToSource.js';
4
4
  import { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
5
5
  import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
6
+ import { independentTopLevelDeletionFallbackResult } from './js-ts-safe-merge-independent-deletion-fallback.js';
7
+ import { normalizeAlreadyAppliedDeleteReplay } from './js-ts-safe-merge-semantic-edit-already-applied.js';
6
8
  import {
7
9
  semanticFallbackChangedExistingDeclarations,
8
10
  semanticFallbackConflictCode,
9
11
  semanticFallbackPhase,
10
12
  shouldTrySemanticEditFallback
11
13
  } from './js-ts-safe-merge-semantic-edit-fallback-utils.js';
14
+ import { semanticEditGates } from './js-ts-safe-merge-semantic-edit-gates.js';
12
15
  import {
13
16
  createStagedDeclarationAlreadyAppliedReplay,
14
17
  createStagedDeclarationProjection,
@@ -19,6 +22,8 @@ import { createSourceShapeSemanticFallbackResult } from './js-ts-safe-merge-sour
19
22
  import { idFragment, uniqueStrings } from './native-import-utils.js';
20
23
 
21
24
  function semanticEditFallbackResult(input, topLevelResult) {
25
+ const independentDeletionResult = independentTopLevelDeletionFallbackResult(input, topLevelResult);
26
+ if (independentDeletionResult) return independentDeletionResult;
22
27
  if (!shouldTrySemanticEditFallback(topLevelResult)) return topLevelResult;
23
28
  const stagedFallback = createStagedTopLevelSemanticFallback(input, topLevelResult);
24
29
  const candidates = semanticFallbackCandidates(stagedFallback);
@@ -132,14 +137,18 @@ function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallba
132
137
  });
133
138
  const alreadyAppliedReplay = stagedDeclarationProjection
134
139
  ? createStagedDeclarationAlreadyAppliedReplay({ id, projection, sourcePath, language })
135
- : replaySemanticEditProjection({
136
- id: `${id}_semantic_edit_already_applied`,
140
+ : normalizeAlreadyAppliedDeleteReplay({
137
141
  projection,
138
- currentSourceText: projection.sourceText,
139
- currentSourcePath: sourcePath,
140
- currentSourceHash: projection.projectedHash,
141
- language,
142
- parser: input.parser
142
+ replay,
143
+ alreadyAppliedReplay: replaySemanticEditProjection({
144
+ id: `${id}_semantic_edit_already_applied`,
145
+ projection,
146
+ currentSourceText: projection.sourceText,
147
+ currentSourcePath: sourcePath,
148
+ currentSourceHash: projection.projectedHash,
149
+ language,
150
+ parser: input.parser
151
+ })
143
152
  });
144
153
  return semanticEditArtifacts({
145
154
  id,
@@ -301,19 +310,4 @@ function semanticEditFallbackBlockedResult(input, topLevelResult, artifacts) {
301
310
  };
302
311
  }
303
312
 
304
- function semanticEditGates(artifacts) {
305
- return [
306
- gate('semantic-edit-script', artifacts.script?.admission?.status === 'auto-merge-candidate', artifacts.script?.admission?.reasonCodes),
307
- gate('semantic-edit-projection', artifacts.projection?.status === 'projected', artifacts.projection?.admission?.reasonCodes),
308
- gate('semantic-edit-replay', artifacts.replay?.status === 'accepted-clean', artifacts.replay?.admission?.reasonCodes),
309
- gate('semantic-edit-already-applied', artifacts.alreadyAppliedReplay?.status === 'already-applied', artifacts.alreadyAppliedReplay?.admission?.reasonCodes)
310
- ];
311
- }
312
-
313
- function gate(id, passed, reasonCodes = []) {
314
- return { id, status: passed ? 'passed' : 'blocked', reasonCodes: passed ? [] : uniqueStrings(reasonCodes) };
315
- }
316
-
317
- export {
318
- semanticEditFallbackResult
319
- };
313
+ export { semanticEditFallbackResult };
@@ -0,0 +1,16 @@
1
+ import { uniqueStrings } from './native-import-utils.js';
2
+
3
+ function semanticEditGates(artifacts) {
4
+ return [
5
+ gate('semantic-edit-script', artifacts.script?.admission?.status === 'auto-merge-candidate', artifacts.script?.admission?.reasonCodes),
6
+ gate('semantic-edit-projection', artifacts.projection?.status === 'projected', artifacts.projection?.admission?.reasonCodes),
7
+ gate('semantic-edit-replay', artifacts.replay?.status === 'accepted-clean', artifacts.replay?.admission?.reasonCodes),
8
+ gate('semantic-edit-already-applied', artifacts.alreadyAppliedReplay?.status === 'already-applied', artifacts.alreadyAppliedReplay?.admission?.reasonCodes)
9
+ ];
10
+ }
11
+
12
+ function gate(id, passed, reasonCodes = []) {
13
+ return { id, status: passed ? 'passed' : 'blocked', reasonCodes: passed ? [] : uniqueStrings(reasonCodes) };
14
+ }
15
+
16
+ export { semanticEditGates };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.146",
3
+ "version": "0.2.148",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",