@shapeshift-labs/frontier-lang-compiler 0.2.135 → 0.2.136

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.
@@ -3,16 +3,25 @@ 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 { JsTsSafeMergeConflictCodes, JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
6
+ import {
7
+ createStagedDeclarationAlreadyAppliedReplay,
8
+ createStagedDeclarationProjection,
9
+ createStagedDeclarationReplayRecord
10
+ } from './js-ts-safe-merge-staged-declaration-replay.js';
11
+ import { createStagedTopLevelSemanticFallback } from './js-ts-safe-merge-staged-top-level-fallback.js';
6
12
  import { idFragment, uniqueStrings } from './native-import-utils.js';
7
13
 
8
14
  function semanticEditFallbackResult(input, topLevelResult) {
9
15
  if (!shouldTrySemanticEditFallback(topLevelResult)) return topLevelResult;
10
- const artifacts = createSemanticEditFallbackArtifacts(input, topLevelResult);
16
+ const stagedFallback = createStagedTopLevelSemanticFallback(input, topLevelResult);
17
+ const artifacts = createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallback);
11
18
  if (artifacts.status !== 'verified') return semanticEditFallbackBlockedResult(input, topLevelResult, artifacts);
19
+ const resultBase = stagedFallback?.stagedTopLevelResult ?? topLevelResult;
12
20
  const mergedSourceText = artifacts.projection.sourceText;
13
21
  const gates = semanticEditGates(artifacts);
14
22
  return {
15
- ...topLevelResult,
23
+ ...resultBase,
24
+ id: String(input.id ?? resultBase.id ?? topLevelResult.id),
16
25
  status: JsTsSafeMergeStatuses.merged,
17
26
  mergedSourceText,
18
27
  outputSourceText: mergedSourceText,
@@ -28,7 +37,8 @@ function semanticEditFallbackResult(input, topLevelResult) {
28
37
  reasonCodes: []
29
38
  },
30
39
  summary: {
31
- ...topLevelResult.summary,
40
+ ...resultBase.summary,
41
+ changedExistingDeclarations: topLevelResult.summary?.changedExistingDeclarations ?? resultBase.summary?.changedExistingDeclarations ?? 0,
32
42
  conflicts: 0,
33
43
  gatesPassed: gates.filter((gate) => gate.status === 'passed').length,
34
44
  semanticEditOperations: artifacts.script.summary.operations,
@@ -37,11 +47,13 @@ function semanticEditFallbackResult(input, topLevelResult) {
37
47
  composedPhases: 2
38
48
  },
39
49
  metadata: {
40
- ...topLevelResult.metadata,
50
+ ...resultBase.metadata,
41
51
  composed: {
42
- phase: 'semantic-edit-fallback',
43
- phases: ['top-level-ledger', 'semantic-edit'],
44
- originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
52
+ phase: stagedFallback ? 'staged-top-level-semantic-edit-fallback' : 'semantic-edit-fallback',
53
+ phases: stagedFallback ? ['top-level-neutralization', 'top-level-ledger', 'semantic-edit'] : ['top-level-ledger', 'semantic-edit'],
54
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
55
+ stagedTopLevelSummary: stagedFallback?.stagedTopLevelResult?.summary,
56
+ neutralization: stagedFallback?.neutralization?.summary
45
57
  }
46
58
  },
47
59
  semanticArtifacts: artifacts
@@ -53,42 +65,53 @@ function shouldTrySemanticEditFallback(result) {
53
65
  return conflicts.length > 0 && conflicts.every((conflict) => conflict.code === JsTsSafeMergeConflictCodes.changedExistingDeclaration);
54
66
  }
55
67
 
56
- function createSemanticEditFallbackArtifacts(input, topLevelResult) {
68
+ function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallback) {
57
69
  try {
58
70
  const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
59
71
  const language = input.language ?? topLevelResult.language ?? 'typescript';
60
72
  const sourcePath = input.sourcePath ?? topLevelResult.sourcePath ?? 'inline.ts';
73
+ const scriptInput = stagedFallback?.scriptInput ?? input;
74
+ const projectionHeadSourceText = stagedFallback?.projectionHeadSourceText ?? input.headSourceText;
75
+ const replayCurrentSourceText = stagedFallback?.replayCurrentSourceText ?? input.headSourceText;
61
76
  const script = createSemanticEditScript({
62
- ...input,
77
+ ...scriptInput,
63
78
  id: `${id}_semantic_edit`,
64
79
  language,
65
80
  sourcePath
66
81
  });
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
- });
82
+ const projection = stagedFallback
83
+ ? createStagedDeclarationProjection({ id, script, sourcePath, language, stagedFallback })
84
+ : projectSemanticEditScriptToSource({
85
+ id: `${id}_semantic_edit_projection`,
86
+ script,
87
+ workerSourceText: scriptInput.workerSourceText,
88
+ headSourceText: projectionHeadSourceText,
89
+ headSourcePath: sourcePath,
90
+ parser: input.parser,
91
+ metadata: stagedFallback?.metadata
92
+ });
93
+ const replay = stagedFallback
94
+ ? createStagedDeclarationReplayRecord({ id, projection, sourcePath, language, stagedFallback, replayCurrentSourceText })
95
+ : replaySemanticEditProjection({
96
+ id: `${id}_semantic_edit_replay`,
97
+ projection,
98
+ currentSourceText: replayCurrentSourceText,
99
+ currentSourcePath: sourcePath,
100
+ language,
101
+ parser: input.parser,
102
+ metadata: stagedFallback?.metadata
103
+ });
104
+ const alreadyAppliedReplay = stagedFallback
105
+ ? createStagedDeclarationAlreadyAppliedReplay({ id, projection, sourcePath, language })
106
+ : replaySemanticEditProjection({
107
+ id: `${id}_semantic_edit_already_applied`,
108
+ projection,
109
+ currentSourceText: projection.sourceText,
110
+ currentSourcePath: sourcePath,
111
+ currentSourceHash: projection.projectedHash,
112
+ language,
113
+ parser: input.parser
114
+ });
92
115
  return semanticEditArtifacts({
93
116
  id,
94
117
  language,
@@ -97,7 +120,8 @@ function createSemanticEditFallbackArtifacts(input, topLevelResult) {
97
120
  projection,
98
121
  replay,
99
122
  alreadyAppliedReplay,
100
- topLevelResult
123
+ topLevelResult,
124
+ stagedFallback
101
125
  });
102
126
  } catch (error) {
103
127
  return blockedSemanticEditArtifacts(input, topLevelResult, ['semantic-edit-fallback-error'], error);
@@ -148,8 +172,10 @@ function semanticEditArtifacts(input) {
148
172
  metadata: {
149
173
  autoMergeClaim: false,
150
174
  semanticEquivalenceClaim: false,
151
- source: 'js-ts-semantic-edit-fallback',
152
- originalReasonCodes: input.topLevelResult.admission?.reasonCodes ?? []
175
+ source: input.stagedFallback ? 'js-ts-staged-top-level-semantic-edit-fallback' : 'js-ts-semantic-edit-fallback',
176
+ originalReasonCodes: input.topLevelResult.admission?.reasonCodes ?? [],
177
+ stagedTopLevelSummary: input.stagedFallback?.stagedTopLevelResult?.summary,
178
+ neutralization: input.stagedFallback?.neutralization?.summary
153
179
  }
154
180
  };
155
181
  return { ...core, hash: hashSemanticValue(core) };
@@ -0,0 +1,200 @@
1
+ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
+ import { idFragment, uniqueStrings } from './native-import-utils.js';
3
+
4
+ function createStagedDeclarationProjection(input) {
5
+ const replay = input.stagedFallback.declarationReplay;
6
+ const reasonCodes = uniqueStrings(replay.reasonCodes);
7
+ const blocked = reasonCodes.length > 0;
8
+ const sourceText = blocked ? undefined : replay.outputSourceText;
9
+ const edits = blocked ? [] : replay.edits.map((edit, index) => stagedDeclarationProjectionEdit(edit, index, input));
10
+ const core = {
11
+ kind: 'frontier.lang.semanticEditProjection',
12
+ version: 1,
13
+ id: `${input.id}_semantic_edit_projection`,
14
+ scriptId: input.script.id,
15
+ status: blocked ? 'blocked' : 'projected',
16
+ sourcePath: input.sourcePath,
17
+ language: input.language,
18
+ baseHash: input.script.baseHash,
19
+ workerHash: input.script.workerHash,
20
+ headHash: input.script.headHash,
21
+ projectedHash: sourceText === undefined ? undefined : hashSemanticValue(sourceText),
22
+ appliedOperations: edits.map((edit) => edit.operationId),
23
+ skippedOperations: blocked ? (input.script.operations ?? []).map((operation) => operation.id) : [],
24
+ edits,
25
+ sourceText,
26
+ admission: {
27
+ status: blocked ? 'blocked' : 'auto-merge-candidate',
28
+ autoMergeClaim: false,
29
+ semanticEquivalenceClaim: false,
30
+ reasonCodes
31
+ },
32
+ metadata: {
33
+ autoMergeClaim: false,
34
+ semanticEquivalenceClaim: false,
35
+ source: 'js-ts-staged-declaration-replay',
36
+ ...input.stagedFallback.metadata
37
+ }
38
+ };
39
+ return { ...core, hash: hashSemanticValue(core) };
40
+ }
41
+
42
+ function stagedDeclarationProjectionEdit(edit, index, input) {
43
+ const operationId = `staged_declaration_replay_${idFragment([input.id, edit.key, index].join(':'))}`;
44
+ const replacementText = edit.replacementText;
45
+ const deletedText = edit.currentText;
46
+ return {
47
+ operationId,
48
+ status: 'applied',
49
+ kind: 'replaceDeclaration',
50
+ editKind: 'replace',
51
+ changeKind: 'modified',
52
+ anchorKey: edit.key,
53
+ conflictKey: `declaration:${edit.key}`,
54
+ regionKind: 'declaration',
55
+ sourcePath: input.sourcePath,
56
+ symbolName: edit.names?.[0],
57
+ symbolKind: edit.declarationKind,
58
+ editContentHash: hashSemanticValue({ operationId, replacementText, deletedText }),
59
+ headStart: edit.start,
60
+ headEnd: edit.end,
61
+ editOrder: index,
62
+ deletedBytes: deletedText.length,
63
+ replacementBytes: replacementText.length,
64
+ deletedTextHash: hashSemanticValue(deletedText),
65
+ replacementTextHash: hashSemanticValue(replacementText),
66
+ replacementText
67
+ };
68
+ }
69
+
70
+ function createStagedDeclarationReplayRecord(input) {
71
+ const projectionReady = input.projection.status === 'projected';
72
+ const outputSourceText = projectionReady ? input.projection.sourceText : undefined;
73
+ const edits = projectionReady ? input.projection.edits.map((edit) => ({
74
+ operationId: edit.operationId,
75
+ editKind: edit.editKind,
76
+ editOrder: edit.editOrder,
77
+ sourcePath: edit.sourcePath,
78
+ symbolName: edit.symbolName,
79
+ symbolKind: edit.symbolKind,
80
+ status: 'applied',
81
+ start: edit.headStart,
82
+ end: edit.headEnd,
83
+ replacementBytes: edit.replacementBytes,
84
+ replacementText: edit.replacementText,
85
+ reasonCodes: ['staged-declaration-replay']
86
+ })) : [];
87
+ const status = projectionReady ? 'accepted-clean' : 'blocked';
88
+ const reasonCodes = projectionReady ? [] : input.projection.admission.reasonCodes;
89
+ const core = {
90
+ kind: 'frontier.lang.semanticEditReplay',
91
+ version: 1,
92
+ schema: 'frontier.lang.semanticEditReplay.v1',
93
+ id: `${input.id}_semantic_edit_replay`,
94
+ projectionId: input.projection.id,
95
+ scriptId: input.projection.scriptId,
96
+ sourcePath: input.sourcePath,
97
+ language: input.language,
98
+ currentHash: hashSemanticValue(input.replayCurrentSourceText),
99
+ projectedHash: input.projection.projectedHash,
100
+ outputHash: outputSourceText === undefined ? undefined : hashSemanticValue(outputSourceText),
101
+ status,
102
+ edits,
103
+ appliedOperations: edits.map((edit) => edit.operationId),
104
+ skippedOperations: [],
105
+ admission: {
106
+ status,
107
+ action: projectionReady ? 'apply' : 'block',
108
+ reviewRequired: !projectionReady,
109
+ autoApplyCandidate: projectionReady,
110
+ autoMergeClaim: false,
111
+ semanticEquivalenceClaim: false,
112
+ reasonCodes
113
+ },
114
+ outputSourceText,
115
+ summary: {
116
+ edits: edits.length,
117
+ applied: edits.length,
118
+ alreadyApplied: 0,
119
+ conflicts: 0,
120
+ stale: 0,
121
+ blocked: projectionReady ? 0 : 1,
122
+ reasonCodes
123
+ },
124
+ metadata: {
125
+ autoMergeClaim: false,
126
+ semanticEquivalenceClaim: false,
127
+ source: 'js-ts-staged-declaration-replay',
128
+ ...input.stagedFallback.metadata
129
+ }
130
+ };
131
+ return { ...core, hash: hashSemanticValue(core) };
132
+ }
133
+
134
+ function createStagedDeclarationAlreadyAppliedReplay(input) {
135
+ const projectionReady = input.projection.status === 'projected';
136
+ const edits = projectionReady ? input.projection.edits.map((edit) => ({
137
+ operationId: edit.operationId,
138
+ editKind: edit.editKind,
139
+ editOrder: edit.editOrder,
140
+ sourcePath: edit.sourcePath,
141
+ symbolName: edit.symbolName,
142
+ symbolKind: edit.symbolKind,
143
+ status: 'already-applied',
144
+ start: edit.headStart,
145
+ end: edit.headStart + edit.replacementBytes,
146
+ replacementBytes: edit.replacementBytes,
147
+ replacementText: edit.replacementText,
148
+ reasonCodes: ['staged-declaration-already-applied']
149
+ })) : [];
150
+ const status = projectionReady ? 'already-applied' : 'blocked';
151
+ const reasonCodes = projectionReady ? [] : input.projection.admission.reasonCodes;
152
+ const core = {
153
+ kind: 'frontier.lang.semanticEditReplay',
154
+ version: 1,
155
+ schema: 'frontier.lang.semanticEditReplay.v1',
156
+ id: `${input.id}_semantic_edit_already_applied`,
157
+ projectionId: input.projection.id,
158
+ scriptId: input.projection.scriptId,
159
+ sourcePath: input.sourcePath,
160
+ language: input.language,
161
+ currentHash: input.projection.projectedHash,
162
+ projectedHash: input.projection.projectedHash,
163
+ outputHash: input.projection.projectedHash,
164
+ status,
165
+ edits,
166
+ appliedOperations: [],
167
+ skippedOperations: edits.map((edit) => edit.operationId),
168
+ admission: {
169
+ status,
170
+ action: projectionReady ? 'skip' : 'block',
171
+ reviewRequired: !projectionReady,
172
+ autoApplyCandidate: false,
173
+ autoMergeClaim: false,
174
+ semanticEquivalenceClaim: false,
175
+ reasonCodes
176
+ },
177
+ outputSourceText: input.projection.sourceText,
178
+ summary: {
179
+ edits: edits.length,
180
+ applied: 0,
181
+ alreadyApplied: edits.length,
182
+ conflicts: 0,
183
+ stale: 0,
184
+ blocked: projectionReady ? 0 : 1,
185
+ reasonCodes
186
+ },
187
+ metadata: {
188
+ autoMergeClaim: false,
189
+ semanticEquivalenceClaim: false,
190
+ source: 'js-ts-staged-declaration-replay'
191
+ }
192
+ };
193
+ return { ...core, hash: hashSemanticValue(core) };
194
+ }
195
+
196
+ export {
197
+ createStagedDeclarationAlreadyAppliedReplay,
198
+ createStagedDeclarationProjection,
199
+ createStagedDeclarationReplayRecord
200
+ };
@@ -0,0 +1,52 @@
1
+ import { safeMergeJsTsImportsAndDeclarations } from './js-ts-safe-merge.js';
2
+ import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
3
+ import { createJsTsChangedDeclarationReplay, neutralizeJsTsSafeTopLevelMergeSources } from './js-ts-safe-merge-top-level-neutralization.js';
4
+
5
+ function createStagedTopLevelSemanticFallback(input, topLevelResult) {
6
+ const neutralization = neutralizeJsTsSafeTopLevelMergeSources(input);
7
+ if (!neutralization.ok) return undefined;
8
+ const stagedTopLevelResult = safeMergeJsTsImportsAndDeclarations({
9
+ ...input,
10
+ baseSourceText: neutralization.baseSourceText,
11
+ workerSourceText: neutralization.topLevelWorkerSourceText,
12
+ headSourceText: neutralization.topLevelHeadSourceText
13
+ });
14
+ if (stagedTopLevelResult.status !== JsTsSafeMergeStatuses.merged) return undefined;
15
+ const safeTopLevelChanges = safeTopLevelChangeCount(stagedTopLevelResult.summary);
16
+ const declarationReplay = createJsTsChangedDeclarationReplay(input, neutralization, stagedTopLevelResult.mergedSourceText);
17
+ const workerDeclarationChanges = neutralization.summary.workerChangedExistingDeclarations ?? 0;
18
+ if (safeTopLevelChanges === 0 && workerDeclarationChanges === 0) return undefined;
19
+ return {
20
+ neutralization,
21
+ declarationReplay,
22
+ stagedTopLevelResult,
23
+ scriptInput: {
24
+ ...input,
25
+ workerSourceText: neutralization.semanticWorkerSourceText,
26
+ headSourceText: neutralization.semanticHeadSourceText,
27
+ workerSourceHash: undefined,
28
+ headSourceHash: undefined
29
+ },
30
+ projectionHeadSourceText: stagedTopLevelResult.mergedSourceText,
31
+ replayCurrentSourceText: stagedTopLevelResult.mergedSourceText,
32
+ metadata: {
33
+ stagedTopLevelSummary: stagedTopLevelResult.summary,
34
+ declarationReplay: {
35
+ edits: declarationReplay.edits.length,
36
+ reasonCodes: declarationReplay.reasonCodes
37
+ },
38
+ neutralization: neutralization.summary,
39
+ originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
40
+ }
41
+ };
42
+ }
43
+
44
+ function safeTopLevelChangeCount(summary = {}) {
45
+ return (summary.importSpecifierAdditions ?? 0)
46
+ + (summary.importDeclarationAdditions ?? 0)
47
+ + (summary.topLevelDeclarationAdditions ?? 0);
48
+ }
49
+
50
+ export {
51
+ createStagedTopLevelSemanticFallback
52
+ };
@@ -0,0 +1,190 @@
1
+ import { findCompatibleBaseImportEntry, findSameImportTargetBaseEntry } from './js-ts-safe-merge-import-shape.js';
2
+ import { scanJsTsTopLevelLedger } from './js-ts-safe-merge-ledger.js';
3
+ import { sameStatementText } from './js-ts-safe-merge-context.js';
4
+
5
+ function neutralizeJsTsSafeTopLevelMergeSources(input = {}) {
6
+ if (typeof input.baseSourceText !== 'string'
7
+ || typeof input.workerSourceText !== 'string'
8
+ || typeof input.headSourceText !== 'string') {
9
+ return { ok: false, reasonCodes: ['missing-source-text'] };
10
+ }
11
+
12
+ const context = quietLedgerContext(input);
13
+ const base = scanJsTsTopLevelLedger(input.baseSourceText, 'base', context);
14
+ const worker = scanJsTsTopLevelLedger(input.workerSourceText, 'worker', context);
15
+ const head = scanJsTsTopLevelLedger(input.headSourceText, 'head', context);
16
+ if (context.conflicts.length) {
17
+ return { ok: false, reasonCodes: context.conflicts.map((conflict) => conflict.code) };
18
+ }
19
+
20
+ const baseEntries = base.entries;
21
+ const topLevelWorker = neutralizeChangedExistingDeclarations(input.workerSourceText, worker, baseEntries);
22
+ const topLevelHead = neutralizeChangedExistingDeclarations(input.headSourceText, head, baseEntries);
23
+ const semanticWorker = neutralizeSafeTopLevelAdditions(input.workerSourceText, worker, baseEntries);
24
+ const semanticHead = neutralizeSafeTopLevelAdditions(input.headSourceText, head, baseEntries);
25
+
26
+ return {
27
+ ok: true,
28
+ base,
29
+ worker,
30
+ head,
31
+ baseSourceText: input.baseSourceText,
32
+ topLevelWorkerSourceText: topLevelWorker.sourceText,
33
+ topLevelHeadSourceText: topLevelHead.sourceText,
34
+ semanticWorkerSourceText: semanticWorker.sourceText,
35
+ semanticHeadSourceText: semanticHead.sourceText,
36
+ summary: {
37
+ workerChangedExistingDeclarations: topLevelWorker.changedExistingDeclarations,
38
+ headChangedExistingDeclarations: topLevelHead.changedExistingDeclarations,
39
+ workerNeutralizedSafeTopLevelChanges: semanticWorker.neutralizedSafeTopLevelChanges,
40
+ headNeutralizedSafeTopLevelChanges: semanticHead.neutralizedSafeTopLevelChanges
41
+ }
42
+ };
43
+ }
44
+
45
+ function createJsTsChangedDeclarationReplay(input = {}, neutralization, currentSourceText) {
46
+ if (!neutralization?.ok || typeof currentSourceText !== 'string') {
47
+ return { ok: false, reasonCodes: ['missing-neutralized-current-source'], edits: [] };
48
+ }
49
+ const context = quietLedgerContext(input);
50
+ const current = scanJsTsTopLevelLedger(currentSourceText, 'current', context);
51
+ if (context.conflicts.length) {
52
+ return { ok: false, reasonCodes: context.conflicts.map((conflict) => conflict.code), edits: [] };
53
+ }
54
+
55
+ const baseEntries = neutralization.base.entries;
56
+ const workerMatches = matchedEntriesByBaseKey(neutralization.worker, baseEntries);
57
+ const headMatches = matchedEntriesByBaseKey(neutralization.head, baseEntries);
58
+ const currentMatches = matchedEntriesByBaseKey(current, baseEntries);
59
+ const edits = [];
60
+ const reasonCodes = [];
61
+ for (const baseEntry of baseEntries) {
62
+ if (baseEntry.kind === 'import') continue;
63
+ const workerEntry = workerMatches.get(baseEntry.key);
64
+ if (!workerEntry || sameStatementText(workerEntry.text, baseEntry.text)) continue;
65
+ const headEntry = headMatches.get(baseEntry.key);
66
+ const currentEntry = currentMatches.get(baseEntry.key);
67
+ if (!headEntry || !sameStatementText(headEntry.text, baseEntry.text)) {
68
+ reasonCodes.push(`head-anchor-changed-since-base:${baseEntry.key}`);
69
+ continue;
70
+ }
71
+ if (!currentEntry || !sameStatementText(currentEntry.text, baseEntry.text)) {
72
+ reasonCodes.push(`current-anchor-changed-since-base:${baseEntry.key}`);
73
+ continue;
74
+ }
75
+ edits.push({
76
+ key: baseEntry.key,
77
+ names: workerEntry.names ?? baseEntry.names ?? [],
78
+ declarationKind: workerEntry.declarationInfo?.declarationKind ?? baseEntry.declarationInfo?.declarationKind,
79
+ start: currentEntry.start,
80
+ end: currentEntry.end,
81
+ currentText: currentEntry.text,
82
+ replacementText: workerEntry.text
83
+ });
84
+ }
85
+ return {
86
+ ok: reasonCodes.length === 0,
87
+ reasonCodes,
88
+ edits,
89
+ outputSourceText: reasonCodes.length ? undefined : applySourceEdits(
90
+ currentSourceText,
91
+ edits.map((edit) => ({ start: edit.start, end: edit.end, text: edit.replacementText }))
92
+ )
93
+ };
94
+ }
95
+
96
+ function neutralizeChangedExistingDeclarations(sourceText, ledger, baseEntries) {
97
+ const usedBaseKeys = new Set();
98
+ const edits = [];
99
+ let changedExistingDeclarations = 0;
100
+ for (const entry of ledger.entries) {
101
+ const baseEntry = findBaseEntry(entry, baseEntries, usedBaseKeys);
102
+ if (!baseEntry) continue;
103
+ usedBaseKeys.add(baseEntry.key);
104
+ if (entry.kind === 'import' || sameStatementText(entry.text, baseEntry.text)) continue;
105
+ edits.push({ start: entry.start, end: entry.end, text: baseEntry.text });
106
+ changedExistingDeclarations += 1;
107
+ }
108
+ return {
109
+ sourceText: applySourceEdits(sourceText, edits),
110
+ changedExistingDeclarations
111
+ };
112
+ }
113
+
114
+ function neutralizeSafeTopLevelAdditions(sourceText, ledger, baseEntries) {
115
+ const usedBaseKeys = new Set();
116
+ const edits = [];
117
+ let neutralizedSafeTopLevelChanges = 0;
118
+ for (const entry of ledger.entries) {
119
+ const baseEntry = findBaseEntry(entry, baseEntries, usedBaseKeys);
120
+ if (!baseEntry) {
121
+ edits.push(deleteEntryEdit(sourceText, entry));
122
+ neutralizedSafeTopLevelChanges += 1;
123
+ continue;
124
+ }
125
+ usedBaseKeys.add(baseEntry.key);
126
+ if (entry.kind !== 'import' || sameStatementText(entry.text, baseEntry.text)) continue;
127
+ edits.push({ start: entry.start, end: entry.end, text: baseEntry.text });
128
+ neutralizedSafeTopLevelChanges += 1;
129
+ }
130
+ return {
131
+ sourceText: applySourceEdits(sourceText, edits),
132
+ neutralizedSafeTopLevelChanges
133
+ };
134
+ }
135
+
136
+ function findBaseEntry(entry, baseEntries, usedBaseKeys) {
137
+ if (!entry) return undefined;
138
+ const direct = baseEntries.find((candidate) => candidate.key === entry.key && !usedBaseKeys.has(candidate.key));
139
+ if (direct) return direct;
140
+ return entry.kind === 'import'
141
+ ? findCompatibleBaseImportEntry(entry, baseEntries, usedBaseKeys)
142
+ ?? findSameImportTargetBaseEntry(entry, baseEntries, usedBaseKeys)
143
+ : undefined;
144
+ }
145
+
146
+ function matchedEntriesByBaseKey(ledger, baseEntries) {
147
+ const usedBaseKeys = new Set();
148
+ const matches = new Map();
149
+ for (const entry of ledger.entries) {
150
+ const baseEntry = findBaseEntry(entry, baseEntries, usedBaseKeys);
151
+ if (!baseEntry) continue;
152
+ usedBaseKeys.add(baseEntry.key);
153
+ matches.set(baseEntry.key, entry);
154
+ }
155
+ return matches;
156
+ }
157
+
158
+ function deleteEntryEdit(sourceText, entry) {
159
+ const lineEnd = sourceText.indexOf('\n', entry.end);
160
+ const end = lineEnd === -1 ? entry.end : lineEnd + 1;
161
+ const lineStart = sourceText.lastIndexOf('\n', Math.max(0, entry.start - 1)) + 1;
162
+ const start = onlyWhitespace(sourceText.slice(lineStart, entry.start)) ? lineStart : entry.start;
163
+ return { start, end, text: '' };
164
+ }
165
+
166
+ function onlyWhitespace(text) {
167
+ return /^[\t ]*$/.test(text);
168
+ }
169
+
170
+ function applySourceEdits(sourceText, edits) {
171
+ return [...edits]
172
+ .sort((left, right) => right.start - left.start || right.end - left.end)
173
+ .reduce((current, edit) => `${current.slice(0, edit.start)}${edit.text}${current.slice(edit.end)}`, sourceText);
174
+ }
175
+
176
+ function quietLedgerContext(input) {
177
+ return {
178
+ id: String(input.id ?? 'js_ts_safe_merge'),
179
+ sourcePath: input.sourcePath,
180
+ language: input.language ?? 'typescript',
181
+ conflicts: [],
182
+ blockedGateIds: new Set(),
183
+ gateReasonCodes: new Map()
184
+ };
185
+ }
186
+
187
+ export {
188
+ createJsTsChangedDeclarationReplay,
189
+ neutralizeJsTsSafeTopLevelMergeSources
190
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.135",
3
+ "version": "0.2.136",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",