@shapeshift-labs/frontier-lang-compiler 0.2.138 → 0.2.140

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
@@ -200,6 +200,11 @@ interface, type, class, or object member additions. Existing class/object method
200
200
  or property body edits inside the declared member region are preserved for
201
201
  semantic replay while added members are neutralized; object member additions are
202
202
  re-emitted with safe commas when both sides add final properties.
203
+ If staged whole-declaration replay cannot verify because head changed a sibling
204
+ member inside the same declaration, `safeMergeJsTsSource` retries the direct
205
+ semantic edit projection and admits the merge only when replay still verifies
206
+ cleanly. That direct retry can project onto the staged top-level merge output,
207
+ so safe import/declaration additions are preserved alongside the sibling edit.
203
208
 
204
209
  Project-level JS/TS safe merges compose the same file-level gates across a
205
210
  base/worker/head file set. They preserve head-only files, admit worker-only
@@ -0,0 +1,41 @@
1
+ import { JsTsSafeMergeConflictCodes } from './js-ts-safe-merge-constants.js';
2
+
3
+ const semanticFallbackConflictCodes = new Set([
4
+ JsTsSafeMergeConflictCodes.changedExistingDeclaration,
5
+ JsTsSafeMergeConflictCodes.typeAliasConflict
6
+ ]);
7
+
8
+ function shouldTrySemanticEditFallback(result) {
9
+ const conflicts = result.conflicts ?? [];
10
+ return conflicts.length > 0 && conflicts.every((conflict) => semanticFallbackConflictCodes.has(conflict.code));
11
+ }
12
+
13
+ function semanticFallbackConflictCode(result) {
14
+ return result.conflicts?.find((conflict) => semanticFallbackConflictCodes.has(conflict.code))?.code
15
+ ?? JsTsSafeMergeConflictCodes.changedExistingDeclaration;
16
+ }
17
+
18
+ function semanticFallbackChangedExistingDeclarations(topLevelResult, resultBase, stagedFallback) {
19
+ const conflictCount = (topLevelResult.conflicts ?? [])
20
+ .filter((conflict) => semanticFallbackConflictCodes.has(conflict.code)).length;
21
+ return Math.max(
22
+ topLevelResult.summary?.changedExistingDeclarations ?? 0,
23
+ resultBase?.summary?.changedExistingDeclarations ?? 0,
24
+ stagedFallback?.neutralization?.summary?.workerChangedExistingDeclarations ?? 0,
25
+ conflictCount
26
+ );
27
+ }
28
+
29
+ function semanticFallbackPhase(fallback) {
30
+ if (!fallback) return 'semantic-edit-fallback';
31
+ return fallback.projectionMode === 'direct'
32
+ ? 'staged-top-level-direct-semantic-edit-fallback'
33
+ : 'staged-top-level-semantic-edit-fallback';
34
+ }
35
+
36
+ export {
37
+ semanticFallbackChangedExistingDeclarations,
38
+ semanticFallbackConflictCode,
39
+ semanticFallbackPhase,
40
+ shouldTrySemanticEditFallback
41
+ };
@@ -2,7 +2,13 @@ import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
2
2
  import { createSemanticEditScript } from './internal/index-impl/semanticEditScripts.js';
3
3
  import { projectSemanticEditScriptToSource } from './internal/index-impl/projectSemanticEditScriptToSource.js';
4
4
  import { replaySemanticEditProjection } from './internal/index-impl/replaySemanticEditProjection.js';
5
- import { JsTsSafeMergeConflictCodes, JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
5
+ import { JsTsSafeMergeStatuses } from './js-ts-safe-merge-constants.js';
6
+ import {
7
+ semanticFallbackChangedExistingDeclarations,
8
+ semanticFallbackConflictCode,
9
+ semanticFallbackPhase,
10
+ shouldTrySemanticEditFallback
11
+ } from './js-ts-safe-merge-semantic-edit-fallback-utils.js';
6
12
  import {
7
13
  createStagedDeclarationAlreadyAppliedReplay,
8
14
  createStagedDeclarationProjection,
@@ -14,9 +20,26 @@ import { idFragment, uniqueStrings } from './native-import-utils.js';
14
20
  function semanticEditFallbackResult(input, topLevelResult) {
15
21
  if (!shouldTrySemanticEditFallback(topLevelResult)) return topLevelResult;
16
22
  const stagedFallback = createStagedTopLevelSemanticFallback(input, topLevelResult);
17
- const artifacts = createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallback);
23
+ let selectedFallback = stagedFallback;
24
+ let artifacts = createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallback);
25
+ if (stagedFallback && artifacts.status !== 'verified') {
26
+ const directFallback = stagedFallback.directProjectionHeadSourceText && stagedFallback.safeTopLevelChanges > 0
27
+ ? { ...stagedFallback, projectionMode: 'direct' }
28
+ : undefined;
29
+ const directArtifacts = createSemanticEditFallbackArtifacts(input, topLevelResult, directFallback);
30
+ if (directArtifacts.status === 'verified') {
31
+ artifacts = directArtifacts;
32
+ selectedFallback = directFallback;
33
+ } else if (directFallback) {
34
+ const plainDirectArtifacts = createSemanticEditFallbackArtifacts(input, topLevelResult, undefined);
35
+ if (plainDirectArtifacts.status === 'verified') {
36
+ artifacts = plainDirectArtifacts;
37
+ selectedFallback = undefined;
38
+ }
39
+ }
40
+ }
18
41
  if (artifacts.status !== 'verified') return semanticEditFallbackBlockedResult(input, topLevelResult, artifacts);
19
- const resultBase = stagedFallback?.stagedTopLevelResult ?? topLevelResult;
42
+ const resultBase = selectedFallback?.stagedTopLevelResult ?? topLevelResult;
20
43
  const mergedSourceText = artifacts.projection.sourceText;
21
44
  const gates = semanticEditGates(artifacts);
22
45
  return {
@@ -38,7 +61,7 @@ function semanticEditFallbackResult(input, topLevelResult) {
38
61
  },
39
62
  summary: {
40
63
  ...resultBase.summary,
41
- changedExistingDeclarations: semanticFallbackChangedExistingDeclarations(topLevelResult, resultBase, stagedFallback),
64
+ changedExistingDeclarations: semanticFallbackChangedExistingDeclarations(topLevelResult, resultBase, selectedFallback),
42
65
  conflicts: 0,
43
66
  gatesPassed: gates.filter((gate) => gate.status === 'passed').length,
44
67
  semanticEditOperations: artifacts.script.summary.operations,
@@ -49,58 +72,37 @@ function semanticEditFallbackResult(input, topLevelResult) {
49
72
  metadata: {
50
73
  ...resultBase.metadata,
51
74
  composed: {
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'],
75
+ phase: semanticFallbackPhase(selectedFallback),
76
+ phases: selectedFallback ? ['top-level-neutralization', 'top-level-ledger', 'semantic-edit'] : ['top-level-ledger', 'semantic-edit'],
54
77
  originalReasonCodes: topLevelResult.admission?.reasonCodes ?? [],
55
- stagedTopLevelSummary: stagedFallback?.stagedTopLevelResult?.summary,
56
- neutralization: stagedFallback?.neutralization?.summary
78
+ stagedTopLevelSummary: selectedFallback?.stagedTopLevelResult?.summary,
79
+ neutralization: selectedFallback?.neutralization?.summary
57
80
  }
58
81
  },
59
82
  semanticArtifacts: artifacts
60
83
  };
61
84
  }
62
85
 
63
- function shouldTrySemanticEditFallback(result) {
64
- const conflicts = result.conflicts ?? [];
65
- return conflicts.length > 0 && conflicts.every((conflict) => semanticFallbackConflictCodes.has(conflict.code));
66
- }
67
-
68
- const semanticFallbackConflictCodes = new Set([
69
- JsTsSafeMergeConflictCodes.changedExistingDeclaration,
70
- JsTsSafeMergeConflictCodes.typeAliasConflict
71
- ]);
72
-
73
- function semanticFallbackConflictCode(result) {
74
- return result.conflicts?.find((conflict) => semanticFallbackConflictCodes.has(conflict.code))?.code
75
- ?? JsTsSafeMergeConflictCodes.changedExistingDeclaration;
76
- }
77
-
78
- function semanticFallbackChangedExistingDeclarations(topLevelResult, resultBase, stagedFallback) {
79
- const conflictCount = (topLevelResult.conflicts ?? [])
80
- .filter((conflict) => semanticFallbackConflictCodes.has(conflict.code)).length;
81
- return Math.max(
82
- topLevelResult.summary?.changedExistingDeclarations ?? 0,
83
- resultBase?.summary?.changedExistingDeclarations ?? 0,
84
- stagedFallback?.neutralization?.summary?.workerChangedExistingDeclarations ?? 0,
85
- conflictCount
86
- );
87
- }
88
-
89
86
  function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallback) {
90
87
  try {
91
88
  const id = String(input.id ?? topLevelResult.id ?? 'js_ts_safe_merge');
92
89
  const language = input.language ?? topLevelResult.language ?? 'typescript';
93
90
  const sourcePath = input.sourcePath ?? topLevelResult.sourcePath ?? 'inline.ts';
91
+ const stagedDeclarationProjection = stagedFallback && stagedFallback.projectionMode !== 'direct';
94
92
  const scriptInput = stagedFallback?.scriptInput ?? input;
95
- const projectionHeadSourceText = stagedFallback?.projectionHeadSourceText ?? input.headSourceText;
96
- const replayCurrentSourceText = stagedFallback?.replayCurrentSourceText ?? input.headSourceText;
93
+ const projectionHeadSourceText = stagedFallback?.projectionMode === 'direct'
94
+ ? stagedFallback.directProjectionHeadSourceText
95
+ : stagedFallback?.projectionHeadSourceText ?? input.headSourceText;
96
+ const replayCurrentSourceText = stagedFallback?.projectionMode === 'direct'
97
+ ? stagedFallback.directReplayCurrentSourceText
98
+ : stagedFallback?.replayCurrentSourceText ?? input.headSourceText;
97
99
  const script = createSemanticEditScript({
98
100
  ...scriptInput,
99
101
  id: `${id}_semantic_edit`,
100
102
  language,
101
103
  sourcePath
102
104
  });
103
- const projection = stagedFallback
105
+ const projection = stagedDeclarationProjection
104
106
  ? createStagedDeclarationProjection({ id, script, sourcePath, language, stagedFallback })
105
107
  : projectSemanticEditScriptToSource({
106
108
  id: `${id}_semantic_edit_projection`,
@@ -111,7 +113,7 @@ function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallba
111
113
  parser: input.parser,
112
114
  metadata: stagedFallback?.metadata
113
115
  });
114
- const replay = stagedFallback
116
+ const replay = stagedDeclarationProjection
115
117
  ? createStagedDeclarationReplayRecord({ id, projection, sourcePath, language, stagedFallback, replayCurrentSourceText })
116
118
  : replaySemanticEditProjection({
117
119
  id: `${id}_semantic_edit_replay`,
@@ -122,7 +124,7 @@ function createSemanticEditFallbackArtifacts(input, topLevelResult, stagedFallba
122
124
  parser: input.parser,
123
125
  metadata: stagedFallback?.metadata
124
126
  });
125
- const alreadyAppliedReplay = stagedFallback
127
+ const alreadyAppliedReplay = stagedDeclarationProjection
126
128
  ? createStagedDeclarationAlreadyAppliedReplay({ id, projection, sourcePath, language })
127
129
  : replaySemanticEditProjection({
128
130
  id: `${id}_semantic_edit_already_applied`,
@@ -193,7 +195,7 @@ function semanticEditArtifacts(input) {
193
195
  metadata: {
194
196
  autoMergeClaim: false,
195
197
  semanticEquivalenceClaim: false,
196
- source: input.stagedFallback ? 'js-ts-staged-top-level-semantic-edit-fallback' : 'js-ts-semantic-edit-fallback',
198
+ source: input.stagedFallback ? semanticFallbackPhase(input.stagedFallback) : 'js-ts-semantic-edit-fallback',
197
199
  originalReasonCodes: input.topLevelResult.admission?.reasonCodes ?? [],
198
200
  stagedTopLevelSummary: input.stagedFallback?.stagedTopLevelResult?.summary,
199
201
  neutralization: input.stagedFallback?.neutralization?.summary
@@ -14,11 +14,13 @@ function createStagedTopLevelSemanticFallback(input, topLevelResult) {
14
14
  if (stagedTopLevelResult.status !== JsTsSafeMergeStatuses.merged) return undefined;
15
15
  const safeTopLevelChanges = safeTopLevelChangeCount(stagedTopLevelResult.summary);
16
16
  const declarationReplay = createJsTsChangedDeclarationReplay(input, neutralization, stagedTopLevelResult.mergedSourceText);
17
+ const directSemanticHeadReplay = createJsTsChangedDeclarationReplay(input, neutralization, stagedTopLevelResult.mergedSourceText, 'head');
17
18
  const workerDeclarationChanges = neutralization.summary.workerChangedExistingDeclarations ?? 0;
18
19
  if (safeTopLevelChanges === 0 && workerDeclarationChanges === 0) return undefined;
19
20
  return {
20
21
  neutralization,
21
22
  declarationReplay,
23
+ safeTopLevelChanges,
22
24
  stagedTopLevelResult,
23
25
  scriptInput: {
24
26
  ...input,
@@ -28,13 +30,19 @@ function createStagedTopLevelSemanticFallback(input, topLevelResult) {
28
30
  headSourceHash: undefined
29
31
  },
30
32
  projectionHeadSourceText: stagedTopLevelResult.mergedSourceText,
33
+ directProjectionHeadSourceText: directSemanticHeadReplay.outputSourceText,
31
34
  replayCurrentSourceText: stagedTopLevelResult.mergedSourceText,
35
+ directReplayCurrentSourceText: directSemanticHeadReplay.outputSourceText,
32
36
  metadata: {
33
37
  stagedTopLevelSummary: stagedTopLevelResult.summary,
34
38
  declarationReplay: {
35
39
  edits: declarationReplay.edits.length,
36
40
  reasonCodes: declarationReplay.reasonCodes
37
41
  },
42
+ directSemanticHeadReplay: {
43
+ edits: directSemanticHeadReplay.edits.length,
44
+ reasonCodes: directSemanticHeadReplay.reasonCodes
45
+ },
38
46
  neutralization: neutralization.summary,
39
47
  originalReasonCodes: topLevelResult.admission?.reasonCodes ?? []
40
48
  }
@@ -42,7 +42,7 @@ function neutralizeJsTsSafeTopLevelMergeSources(input = {}) {
42
42
  };
43
43
  }
44
44
 
45
- function createJsTsChangedDeclarationReplay(input = {}, neutralization, currentSourceText) {
45
+ function createJsTsChangedDeclarationReplay(input = {}, neutralization, currentSourceText, side = 'worker') {
46
46
  if (!neutralization?.ok || typeof currentSourceText !== 'string') {
47
47
  return { ok: false, reasonCodes: ['missing-neutralized-current-source'], edits: [] };
48
48
  }
@@ -56,15 +56,17 @@ function createJsTsChangedDeclarationReplay(input = {}, neutralization, currentS
56
56
  const workerMatches = matchedEntriesByBaseKey(neutralization.worker, baseEntries);
57
57
  const headMatches = matchedEntriesByBaseKey(neutralization.head, baseEntries);
58
58
  const currentMatches = matchedEntriesByBaseKey(current, baseEntries);
59
+ const sourceMatches = side === 'head' ? headMatches : workerMatches;
60
+ const guardMatches = side === 'head' ? undefined : headMatches;
59
61
  const edits = [];
60
62
  const reasonCodes = [];
61
63
  for (const baseEntry of baseEntries) {
62
64
  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);
65
+ const sourceEntry = sourceMatches.get(baseEntry.key);
66
+ if (!sourceEntry || sameStatementText(sourceEntry.text, baseEntry.text)) continue;
66
67
  const currentEntry = currentMatches.get(baseEntry.key);
67
- if (!headEntry || !sameStatementText(headEntry.text, baseEntry.text)) {
68
+ const guardEntry = guardMatches?.get(baseEntry.key);
69
+ if (guardMatches && (!guardEntry || !sameStatementText(guardEntry.text, baseEntry.text))) {
68
70
  reasonCodes.push(`head-anchor-changed-since-base:${baseEntry.key}`);
69
71
  continue;
70
72
  }
@@ -74,12 +76,12 @@ function createJsTsChangedDeclarationReplay(input = {}, neutralization, currentS
74
76
  }
75
77
  edits.push({
76
78
  key: baseEntry.key,
77
- names: workerEntry.names ?? baseEntry.names ?? [],
78
- declarationKind: workerEntry.declarationInfo?.declarationKind ?? baseEntry.declarationInfo?.declarationKind,
79
+ names: sourceEntry.names ?? baseEntry.names ?? [],
80
+ declarationKind: sourceEntry.declarationInfo?.declarationKind ?? baseEntry.declarationInfo?.declarationKind,
79
81
  start: currentEntry.start,
80
82
  end: currentEntry.end,
81
83
  currentText: currentEntry.text,
82
- replacementText: workerEntry.text
84
+ replacementText: sourceEntry.text
83
85
  });
84
86
  }
85
87
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.138",
3
+ "version": "0.2.140",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",