@shapeshift-labs/frontier-lang-compiler 0.2.102 → 0.2.104

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.
Files changed (134) hide show
  1. package/README.md +13 -0
  2. package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
  3. package/dist/declarations/bidirectional-target-change.d.ts +10 -0
  4. package/dist/declarations/js-ts-safe-member-merge.d.ts +58 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +120 -0
  6. package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
  7. package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
  8. package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
  9. package/dist/declarations/native-import-losses.d.ts +3 -0
  10. package/dist/declarations/native-project-admission-semantic-evidence.d.ts +34 -0
  11. package/dist/declarations/native-project-admission.d.ts +6 -10
  12. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  13. package/dist/declarations/semantic-edit-script.d.ts +10 -4
  14. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  15. package/dist/declarations/semantic-patch-bundle-overlaps.d.ts +1 -0
  16. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  17. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  18. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  19. package/dist/declarations/source-preservation.d.ts +72 -0
  20. package/dist/declarations/universal-capability.d.ts +4 -0
  21. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  22. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  23. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  24. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.js +3 -0
  27. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  28. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  29. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  30. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  31. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  32. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  33. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  34. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  35. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  36. package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +14 -2
  37. package/dist/internal/index-impl/diffNativeSymbols.js +82 -1
  38. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  39. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +1 -1
  40. package/dist/internal/index-impl/projectImportAdmissionSemanticWarnings.js +178 -0
  41. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +22 -3
  42. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +54 -69
  43. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  44. package/dist/internal/index-impl/replaySemanticEditProjection.js +78 -78
  45. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  46. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  47. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  48. package/dist/internal/index-impl/semanticEditImportProjection.js +53 -0
  49. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  50. package/dist/internal/index-impl/semanticEditProjectionRecord.js +108 -0
  51. package/dist/internal/index-impl/semanticEditReplayAnchors.js +63 -0
  52. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  53. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  54. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  55. package/dist/internal/index-impl/semanticEditSourceRanges.js +32 -0
  56. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  57. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +92 -9
  58. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +33 -16
  59. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  60. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  61. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  62. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  63. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  64. package/dist/js-ts-safe-member-merge-result.js +158 -0
  65. package/dist/js-ts-safe-member-merge.js +202 -0
  66. package/dist/js-ts-safe-merge-analyze.js +279 -0
  67. package/dist/js-ts-safe-merge-constants.js +50 -0
  68. package/dist/js-ts-safe-merge-context.js +118 -0
  69. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  70. package/dist/js-ts-safe-merge-ledger.js +85 -0
  71. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  72. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  73. package/dist/js-ts-safe-merge-plan.js +190 -0
  74. package/dist/js-ts-safe-merge.js +175 -0
  75. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  76. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  77. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  78. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  79. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  80. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  81. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  82. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  83. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  84. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  85. package/dist/js-ts-semantic-merge-member-source.js +64 -0
  86. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  87. package/dist/js-ts-semantic-merge-parse.js +15 -0
  88. package/dist/js-ts-semantic-merge.js +21 -0
  89. package/dist/lightweight-dependency-effects.js +51 -0
  90. package/dist/lightweight-dependency-language.js +12 -1
  91. package/dist/lightweight-dependency-relations.js +14 -27
  92. package/dist/native-region-scanner-core.js +33 -1
  93. package/dist/native-region-scanner-csharp.js +151 -0
  94. package/dist/native-region-scanner-dart.js +91 -0
  95. package/dist/native-region-scanner-dynamic.js +21 -151
  96. package/dist/native-region-scanner-functional.js +40 -13
  97. package/dist/native-region-scanner-java.js +97 -0
  98. package/dist/native-region-scanner-js-class.js +100 -0
  99. package/dist/native-region-scanner-js-helpers.js +28 -86
  100. package/dist/native-region-scanner-js-imports.js +121 -1
  101. package/dist/native-region-scanner-js-nested.js +96 -8
  102. package/dist/native-region-scanner-js-structure.js +27 -0
  103. package/dist/native-region-scanner-js-types.js +99 -0
  104. package/dist/native-region-scanner-js.js +70 -118
  105. package/dist/native-region-scanner-kotlin.js +94 -0
  106. package/dist/native-region-scanner-main.js +15 -181
  107. package/dist/native-region-scanner-php.js +80 -0
  108. package/dist/native-region-scanner-python.js +62 -0
  109. package/dist/native-region-scanner-ruby.js +72 -0
  110. package/dist/native-region-scanner-scala.js +91 -0
  111. package/dist/native-region-scanner-spans.js +74 -0
  112. package/dist/native-region-scanner-swift.js +155 -0
  113. package/dist/native-region-scanner.js +14 -10
  114. package/dist/native-source-ledger-helpers.js +195 -0
  115. package/dist/native-source-ledger.js +306 -0
  116. package/dist/native-source-preservation-scanner.js +4 -0
  117. package/dist/semantic-import-callsite-regions.js +136 -0
  118. package/dist/semantic-import-effect-regions.js +283 -0
  119. package/dist/semantic-import-regions.js +11 -2
  120. package/dist/semantic-import-sidecar-entry.js +16 -2
  121. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  122. package/dist/semantic-sidecar-example.js +68 -0
  123. package/dist/universal-capability-matrix.js +23 -0
  124. package/dist/universal-conversion-artifact-query.js +79 -2
  125. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  126. package/dist/universal-conversion-artifact-summary.js +33 -1
  127. package/dist/universal-conversion-artifacts.js +13 -48
  128. package/dist/universal-conversion-plan-scoring.js +21 -1
  129. package/dist/universal-conversion-plan-summary.js +30 -0
  130. package/dist/universal-conversion-plan.js +25 -9
  131. package/dist/universal-conversion-route-metadata.js +96 -0
  132. package/dist/universal-conversion-route-operations.js +7 -0
  133. package/dist/universal-representation-coverage.js +193 -0
  134. package/package.json +1 -1
@@ -0,0 +1,175 @@
1
+ import { analyzeVariantLedger, validateIndependentAdditions } from './js-ts-safe-merge-analyze.js';
2
+ import { addConflict, blockedResult, compactRecord, createMergeContext, gatesFor } from './js-ts-safe-merge-context.js';
3
+ import {
4
+ JsTsSafeMergeConflictCodes,
5
+ JsTsSafeMergeGateIds,
6
+ JsTsSafeMergeStatuses,
7
+ jsTsSafeMergeGateOrder
8
+ } from './js-ts-safe-merge-constants.js';
9
+ import { indexBaseLedger, scanJsTsTopLevelLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger.js';
10
+ import { applySourceMergePlan, createSourceMergePlan } from './js-ts-safe-merge-plan.js';
11
+
12
+ export { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds, JsTsSafeMergeStatuses };
13
+
14
+ export function safeMergeJsTsImportsAndDeclarations(input = {}) {
15
+ const context = createMergeContext(input);
16
+ const baseSourceText = input.baseSourceText;
17
+ const workerSourceText = input.workerSourceText;
18
+ const headSourceText = input.headSourceText;
19
+
20
+ if (typeof baseSourceText !== 'string' || typeof workerSourceText !== 'string' || typeof headSourceText !== 'string') {
21
+ addConflict(context, {
22
+ code: JsTsSafeMergeConflictCodes.invalidInput,
23
+ gateId: JsTsSafeMergeGateIds.parseLedger,
24
+ message: 'baseSourceText, workerSourceText, and headSourceText must be strings.',
25
+ details: {
26
+ baseSourceText: typeof baseSourceText,
27
+ workerSourceText: typeof workerSourceText,
28
+ headSourceText: typeof headSourceText
29
+ }
30
+ });
31
+ return blockedResult(context);
32
+ }
33
+
34
+ validateStaleSourceHashes(input, context);
35
+ validateSourceLedgerSpans(input, context);
36
+ if (context.conflicts.length) return blockedResult(context);
37
+
38
+ const base = scanJsTsTopLevelLedger(baseSourceText, 'base', context);
39
+ const worker = scanJsTsTopLevelLedger(workerSourceText, 'worker', context);
40
+ const head = scanJsTsTopLevelLedger(headSourceText, 'head', context);
41
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head });
42
+
43
+ validateLedgerUniqueness(base, context);
44
+ validateLedgerUniqueness(worker, context);
45
+ validateLedgerUniqueness(head, context);
46
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head });
47
+
48
+ const baseIndex = indexBaseLedger(base, context);
49
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head });
50
+
51
+ const workerPlan = analyzeVariantLedger(base, worker, baseIndex, 'worker', context);
52
+ const headPlan = analyzeVariantLedger(base, head, baseIndex, 'head', context);
53
+ validateIndependentAdditions(base, workerPlan, headPlan, context);
54
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head });
55
+
56
+ const mergePlan = createSourceMergePlan(base, worker, head, workerPlan, headPlan, context);
57
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head });
58
+
59
+ const mergedSourceText = applySourceMergePlan(headSourceText, mergePlan);
60
+ const merged = scanJsTsTopLevelLedger(mergedSourceText, 'merged', context);
61
+ if (!context.conflicts.length) validateLedgerUniqueness(merged, context);
62
+ if (context.conflicts.length) return blockedResult(context, { base, worker, head, merged });
63
+
64
+ return {
65
+ kind: 'frontier.lang.jsTsSafeMerge',
66
+ version: 1,
67
+ schema: 'frontier.lang.jsTsSafeMerge.v1',
68
+ id: context.id,
69
+ status: JsTsSafeMergeStatuses.merged,
70
+ sourcePath: context.sourcePath,
71
+ language: context.language,
72
+ mergedSourceText,
73
+ outputSourceText: mergedSourceText,
74
+ conflicts: [],
75
+ gates: gatesFor(context),
76
+ admission: {
77
+ status: 'auto-merge-candidate',
78
+ action: 'apply',
79
+ reviewRequired: false,
80
+ autoApplyCandidate: true,
81
+ autoMergeClaim: false,
82
+ semanticEquivalenceClaim: false,
83
+ reasonCodes: []
84
+ },
85
+ summary: {
86
+ importSpecifierAdditions: mergePlan.importSpecifierAdditions,
87
+ topLevelDeclarationAdditions: mergePlan.topLevelDeclarationAdditions,
88
+ changedExistingDeclarations: 0,
89
+ conflicts: 0,
90
+ gatesPassed: jsTsSafeMergeGateOrder.length
91
+ },
92
+ metadata: compactRecord({
93
+ workerChangeSetId: input.workerChangeSetId,
94
+ headChangeSetId: input.headChangeSetId,
95
+ baseHash: input.baseHash,
96
+ workerHash: input.workerHash,
97
+ headHash: input.headHash,
98
+ expectedSourceHash: input.expectedSourceHash,
99
+ currentSourceHash: input.currentSourceHash
100
+ })
101
+ };
102
+ }
103
+
104
+ function validateStaleSourceHashes(input, context) {
105
+ for (const pair of [
106
+ ['expectedSourceHash', 'currentSourceHash', 'head'],
107
+ ['expectedHeadHash', 'headHash', 'head'],
108
+ ['expectedBaseHash', 'baseHash', 'base'],
109
+ ['expectedWorkerHash', 'workerHash', 'worker']
110
+ ]) {
111
+ const [expectedField, currentField, side] = pair;
112
+ const expected = input[expectedField];
113
+ const current = input[currentField];
114
+ if (typeof expected !== 'string' || typeof current !== 'string' || expected === current) continue;
115
+ addConflict(context, {
116
+ code: JsTsSafeMergeConflictCodes.staleSourceHash,
117
+ gateId: JsTsSafeMergeGateIds.parseLedger,
118
+ side,
119
+ message: `${side} source hash is stale for safe merge anchors.`,
120
+ details: { expectedField, currentField, expected, current }
121
+ });
122
+ }
123
+ }
124
+
125
+ function validateSourceLedgerSpans(input, context) {
126
+ if (!input.requireSourceLedgerSpans && !input.sourceLedgers && !input.sourceLedger) return;
127
+ for (const side of ['base', 'worker', 'head']) {
128
+ const ledger = sourceLedgerForSide(input, side);
129
+ if (!ledger) {
130
+ addConflict(context, {
131
+ code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
132
+ gateId: JsTsSafeMergeGateIds.parseLedger,
133
+ side,
134
+ message: `${side} source is missing source ledger span evidence.`,
135
+ details: { side, missing: 'source-ledger' }
136
+ });
137
+ continue;
138
+ }
139
+ const spans = Array.isArray(ledger.spans) ? ledger.spans : Array.isArray(ledger.entries) ? ledger.entries : [];
140
+ if (!spans.length) {
141
+ addConflict(context, {
142
+ code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
143
+ gateId: JsTsSafeMergeGateIds.parseLedger,
144
+ side,
145
+ message: `${side} source ledger does not include span entries.`,
146
+ details: { side, missing: 'spans' }
147
+ });
148
+ continue;
149
+ }
150
+ const missingIndex = spans.findIndex((entry) => !hasLedgerSpan(entry));
151
+ if (missingIndex >= 0) {
152
+ addConflict(context, {
153
+ code: JsTsSafeMergeConflictCodes.missingSourceLedgerSpan,
154
+ gateId: JsTsSafeMergeGateIds.parseLedger,
155
+ side,
156
+ message: `${side} source ledger contains an entry without a source span.`,
157
+ details: { side, index: missingIndex, id: spans[missingIndex]?.id, kind: spans[missingIndex]?.kind }
158
+ });
159
+ }
160
+ }
161
+ }
162
+
163
+ function sourceLedgerForSide(input, side) {
164
+ return input.sourceLedgers?.[side]
165
+ ?? input[`${side}SourceLedger`]
166
+ ?? (input.sourceLedgerSide === side ? input.sourceLedger : undefined);
167
+ }
168
+
169
+ function hasLedgerSpan(entry) {
170
+ const span = entry?.span ?? entry?.sourceSpan;
171
+ if (!span) return false;
172
+ const hasOffsets = Number.isFinite(span.start) && Number.isFinite(span.end);
173
+ const hasLines = Number.isFinite(span.startLine) && Number.isFinite(span.endLine);
174
+ return hasOffsets || hasLines;
175
+ }
@@ -0,0 +1,77 @@
1
+ export const JsTsSemanticConflictSidecarClasses = Object.freeze([
2
+ 'same-region',
3
+ 'delete-modify',
4
+ 'duplicate-export',
5
+ 'duplicate-member',
6
+ 'ordered-list-conflict',
7
+ 'parser-ledger-loss',
8
+ 'stale-source-hash',
9
+ 'unsupported-syntax'
10
+ ]);
11
+
12
+ export const classDefaults = Object.freeze({
13
+ 'same-region': {
14
+ severity: 'error',
15
+ risk: 'high',
16
+ readiness: 'blocked',
17
+ reasonCodes: ['same-region-concurrent-edit'],
18
+ suggestedOutcome: 'manual-merge-required'
19
+ },
20
+ 'delete-modify': {
21
+ severity: 'error',
22
+ risk: 'high',
23
+ readiness: 'blocked',
24
+ reasonCodes: ['delete-modify-same-region'],
25
+ suggestedOutcome: 'choose-delete-or-port-modification'
26
+ },
27
+ 'duplicate-export': {
28
+ severity: 'error',
29
+ risk: 'high',
30
+ readiness: 'blocked',
31
+ reasonCodes: ['duplicate-export-name'],
32
+ suggestedOutcome: 'rename-or-remove-duplicate-export'
33
+ },
34
+ 'duplicate-member': {
35
+ severity: 'error',
36
+ risk: 'high',
37
+ readiness: 'blocked',
38
+ reasonCodes: ['duplicate-member-name'],
39
+ suggestedOutcome: 'rename-or-remove-duplicate-member'
40
+ },
41
+ 'ordered-list-conflict': {
42
+ severity: 'warning',
43
+ risk: 'medium',
44
+ readiness: 'needs-review',
45
+ reasonCodes: ['ordered-list-concurrent-position'],
46
+ suggestedOutcome: 'preserve-intended-order-with-human-review'
47
+ },
48
+ 'parser-ledger-loss': {
49
+ severity: 'error',
50
+ risk: 'high',
51
+ readiness: 'blocked',
52
+ reasonCodes: ['parser-or-ledger-loss'],
53
+ suggestedOutcome: 'rerun-parser-and-ledger-before-merge'
54
+ },
55
+ 'stale-source-hash': {
56
+ severity: 'error',
57
+ risk: 'high',
58
+ readiness: 'blocked',
59
+ reasonCodes: ['stale-source-hash'],
60
+ suggestedOutcome: 'rerun-semantic-import-before-merge'
61
+ },
62
+ 'unsupported-syntax': {
63
+ severity: 'error',
64
+ risk: 'high',
65
+ readiness: 'blocked',
66
+ reasonCodes: ['unsupported-js-ts-syntax'],
67
+ suggestedOutcome: 'fall-back-to-textual-review'
68
+ }
69
+ });
70
+
71
+ export const severityRank = Object.freeze({ info: 1, warning: 2, error: 3 });
72
+ export const riskRank = Object.freeze({ low: 1, medium: 2, high: 3 });
73
+ export const orderedChangeKinds = /^(?:insert|add|move|reorder|replace|modify|update)$/i;
74
+ export const deleteChangeKinds = /^(?:delete|remove|removed|deleted)$/i;
75
+ export const modifyChangeKinds = /^(?:modify|modified|replace|update|edit|move|reorder|insert|add)$/i;
76
+ export const parserLedgerLossPattern = /(?:parser|parse|ledger|source[-_ ]?map|anchor|projection).*(?:loss|lost|missing|failed|error)|(?:loss|lost|missing).*(?:parser|ledger|source[-_ ]?map|anchor|projection)/i;
77
+ export const unsupportedSyntaxPattern = /unsupported|unhandled|unknown syntax|syntax unsupported/i;
@@ -0,0 +1,195 @@
1
+ import { idFragment, normalizeSemanticMergeReadiness, uniqueStrings } from './native-import-utils.js';
2
+ import { classDefaults, JsTsSemanticConflictSidecarClasses, orderedChangeKinds, parserLedgerLossPattern, unsupportedSyntaxPattern } from './js-ts-semantic-conflict-sidecar-constants.js';
3
+ import { affectedFromEntries, normalizeAffected, normalizeChange, normalizeDeclaration, sameChangeRegion } from './js-ts-semantic-conflict-sidecar-normalize.js';
4
+ import { array, changePairs, compactRecord, duplicateGroups, firstString, isDeleteChange, isInsertChange, isModifyChange, lossReasonCodes, lossText, normalizeRisk, normalizeSeverity, strings } from './js-ts-semantic-conflict-sidecar-utils.js';
5
+
6
+ export function explicitSidecars(input, context) {
7
+ return [
8
+ ...array(input.conflicts),
9
+ ...array(input.sidecars),
10
+ ...array(input.conflictSidecars)
11
+ ]
12
+ .filter((record) => JsTsSemanticConflictSidecarClasses.includes(record?.class))
13
+ .map((record) => conflictSidecar(record.class, record, context));
14
+ }
15
+
16
+ export function sameRegionSidecars(changes, context) {
17
+ const records = [];
18
+ for (const [left, right] of changePairs(changes)) {
19
+ if (!sameChangeRegion(left, right)) continue;
20
+ records.push(conflictSidecar('same-region', {
21
+ id: `js_ts_conflict_${idFragment(left.id)}_${idFragment(right.id)}_same_region`,
22
+ affected: affectedFromEntries([left, right], context),
23
+ reasonCodes: ['same-region-concurrent-edit', ...left.reasonCodes, ...right.reasonCodes],
24
+ metadata: { leftChangeId: left.id, rightChangeId: right.id, leftChangeKind: left.changeKind, rightChangeKind: right.changeKind }
25
+ }, context));
26
+ }
27
+ return records;
28
+ }
29
+
30
+ export function deleteModifySidecars(changes, context) {
31
+ const records = [];
32
+ for (const [left, right] of changePairs(changes)) {
33
+ if (!sameChangeRegion(left, right)) continue;
34
+ if (!(isDeleteChange(left) && isModifyChange(right)) && !(isDeleteChange(right) && isModifyChange(left))) continue;
35
+ const deleted = isDeleteChange(left) ? left : right;
36
+ const modified = deleted === left ? right : left;
37
+ records.push(conflictSidecar('delete-modify', {
38
+ id: `js_ts_conflict_${idFragment(deleted.id)}_${idFragment(modified.id)}_delete_modify`,
39
+ affected: affectedFromEntries([deleted, modified], context),
40
+ reasonCodes: ['delete-modify-same-region', ...deleted.reasonCodes, ...modified.reasonCodes],
41
+ metadata: { deletedChangeId: deleted.id, modifiedChangeId: modified.id, deletedChangeKind: deleted.changeKind, modifiedChangeKind: modified.changeKind }
42
+ }, context));
43
+ }
44
+ return records;
45
+ }
46
+
47
+ export function duplicateDeclarationSidecars(input, changes, context) {
48
+ const declarations = [
49
+ ...array(input.declarations),
50
+ ...array(input.exports).map((entry) => ({ ...entry, exported: true })),
51
+ ...array(input.members).map((entry) => ({ ...entry, member: true })),
52
+ ...array(input.symbols),
53
+ ...changes.filter((change) => isInsertChange(change) && (change.memberName || change.exportName || change.symbolName))
54
+ ].map((entry, index) => normalizeDeclaration(entry, index, context)).filter((entry) => entry.name);
55
+ return [
56
+ ...duplicateGroups(declarations.filter((entry) => entry.exported), (entry) => [entry.sourcePath, entry.name].join('|'))
57
+ .map((group) => duplicateRecord('duplicate-export', group, context)),
58
+ ...duplicateGroups(declarations.filter((entry) => entry.member || entry.containerKey), (entry) => [entry.sourcePath, entry.containerKey, entry.name].join('|'))
59
+ .map((group) => duplicateRecord('duplicate-member', group, context))
60
+ ];
61
+ }
62
+
63
+ export function orderedListSidecars(input, changes, context) {
64
+ const explicitLists = array(input.orderedLists).flatMap((list, listIndex) =>
65
+ array(list.changes ?? list.operations ?? list.edits).map((change, changeIndex) => normalizeChange({
66
+ ...change,
67
+ listKey: change.listKey ?? list.key ?? list.id,
68
+ orderedListKey: change.orderedListKey ?? list.key ?? list.id,
69
+ sourcePath: change.sourcePath ?? list.sourcePath,
70
+ sourceSpan: change.sourceSpan ?? list.sourceSpan,
71
+ id: change.id ?? `${list.id ?? list.key ?? 'ordered_list'}_${changeIndex}`,
72
+ side: change.side ?? change.author ?? `list_${listIndex}`
73
+ }, context, `ordered_list_${listIndex}_${changeIndex}`))
74
+ );
75
+ const orderedChanges = [...changes, ...explicitLists].filter((change) =>
76
+ change.listKey && orderedChangeKinds.test(change.changeKind)
77
+ );
78
+ const groups = duplicateGroups(orderedChanges, (change) => [
79
+ change.sourcePath,
80
+ change.listKey,
81
+ firstString(change.index, change.position, change.orderKey, change.beforeKey, change.afterKey, change.anchorKey)
82
+ ].join('|'));
83
+ return groups.map((group) => conflictSidecar('ordered-list-conflict', {
84
+ id: `js_ts_conflict_${idFragment(group[0].listKey)}_${idFragment(firstString(group[0].index, group[0].position, group[0].orderKey, 'position'))}_ordered_list`,
85
+ affected: affectedFromEntries(group, context),
86
+ reasonCodes: ['ordered-list-concurrent-position', ...group.flatMap((entry) => entry.reasonCodes)],
87
+ metadata: {
88
+ listKey: group[0].listKey,
89
+ position: firstString(group[0].index, group[0].position, group[0].orderKey, group[0].beforeKey, group[0].afterKey),
90
+ changeIds: group.map((entry) => entry.id)
91
+ }
92
+ }, context));
93
+ }
94
+
95
+ export function parserLedgerLossSidecars(input, context) {
96
+ const losses = [
97
+ ...array(input.parserLosses),
98
+ ...array(input.ledgerLosses),
99
+ ...array(input.losses),
100
+ ...array(input.diagnostics)
101
+ ].filter((entry) => parserLedgerLossPattern.test(lossText(entry)) || entry?.kind === 'parser-ledger-loss');
102
+ if (!losses.length) return [];
103
+ return [conflictSidecar('parser-ledger-loss', {
104
+ id: `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_parser_ledger_loss`,
105
+ severity: losses.some((entry) => entry.severity === 'error' || entry.status === 'failed') ? 'error' : undefined,
106
+ affected: affectedFromEntries(losses, context),
107
+ reasonCodes: ['parser-or-ledger-loss', ...losses.flatMap(lossReasonCodes)],
108
+ metadata: { lossIds: uniqueStrings(losses.map((entry) => entry.id)), lossKinds: uniqueStrings(losses.map((entry) => entry.kind ?? entry.code)) }
109
+ }, context)];
110
+ }
111
+
112
+ export function staleSourceHashSidecars(input, context) {
113
+ if (!context.expectedSourceHash || !context.currentSourceHash) return [];
114
+ if (context.expectedSourceHash === context.currentSourceHash && input.staleSourceHash !== true) return [];
115
+ return [conflictSidecar('stale-source-hash', {
116
+ id: `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_stale_source_hash`,
117
+ affected: affectedFromEntries([{ sourcePath: context.sourcePath, sourceHash: context.currentSourceHash, key: context.sourcePath }], context),
118
+ reasonCodes: ['stale-source-hash', 'expected-source-hash-mismatch'],
119
+ metadata: { expectedSourceHash: context.expectedSourceHash, currentSourceHash: context.currentSourceHash }
120
+ }, context)];
121
+ }
122
+
123
+ export function unsupportedSyntaxSidecars(input, context) {
124
+ const entries = [
125
+ ...array(input.unsupportedSyntax),
126
+ ...array(input.syntaxUnsupported),
127
+ ...array(input.syntaxLosses),
128
+ ...array(input.losses).filter((entry) => unsupportedSyntaxPattern.test(lossText(entry)))
129
+ ];
130
+ if (!entries.length) return [];
131
+ return entries.map((entry, index) => conflictSidecar('unsupported-syntax', {
132
+ id: entry.id ?? `js_ts_conflict_${idFragment(context.sourcePath ?? context.language ?? 'source')}_${index + 1}_unsupported_syntax`,
133
+ affected: affectedFromEntries([entry], context),
134
+ reasonCodes: ['unsupported-js-ts-syntax', ...lossReasonCodes(entry)],
135
+ metadata: compactRecord({
136
+ syntaxKind: entry.kind ?? entry.syntaxKind,
137
+ parser: entry.parser,
138
+ message: entry.message ?? entry.summary
139
+ })
140
+ }, context));
141
+ }
142
+
143
+ export function duplicateRecord(conflictClass, group, context) {
144
+ return conflictSidecar(conflictClass, {
145
+ id: `js_ts_conflict_${idFragment(group[0].sourcePath ?? context.sourcePath ?? 'source')}_${idFragment(group[0].containerKey ?? 'export')}_${idFragment(group[0].name)}_${conflictClass.replace(/-/g, '_')}`,
146
+ affected: affectedFromEntries(group, context),
147
+ reasonCodes: [conflictClass === 'duplicate-member' ? 'duplicate-member-name' : 'duplicate-export-name'],
148
+ metadata: {
149
+ duplicateName: group[0].name,
150
+ containerKey: group[0].containerKey,
151
+ declarationIds: group.map((entry) => entry.id)
152
+ }
153
+ }, context);
154
+ }
155
+
156
+ export function conflictSidecar(conflictClass, input, context) {
157
+ const defaults = classDefaults[conflictClass] ?? classDefaults['same-region'];
158
+ const affected = normalizeAffected(input.affected ?? input, context);
159
+ const reasonCodes = uniqueStrings([
160
+ ...defaults.reasonCodes,
161
+ ...strings(input.reasonCode),
162
+ ...strings(input.reasonCodes)
163
+ ]);
164
+ const severity = normalizeSeverity(input.severity) ?? defaults.severity;
165
+ const risk = normalizeRisk(input.risk) ?? defaults.risk;
166
+ const readiness = normalizeSemanticMergeReadiness(input.readiness) ?? defaults.readiness;
167
+ const suggestedOutcome = input.suggestedOutcome ?? input.outcome ?? defaults.suggestedOutcome;
168
+ const id = input.id ?? `js_ts_conflict_${idFragment(conflictClass)}_${idFragment([
169
+ ...affected.keys,
170
+ ...affected.sourcePaths,
171
+ ...reasonCodes
172
+ ].join('_'))}`;
173
+ return {
174
+ kind: 'frontier.lang.jsTsSemanticMergeConflictSidecar',
175
+ version: 1,
176
+ schema: 'frontier.lang.jsTsSemanticMergeConflictSidecar.v1',
177
+ id,
178
+ class: conflictClass,
179
+ severity,
180
+ risk,
181
+ readiness,
182
+ affected,
183
+ reasonCodes,
184
+ suggestedOutcome,
185
+ explanation: {
186
+ class: conflictClass,
187
+ severity,
188
+ risk,
189
+ affected,
190
+ reasonCodes,
191
+ suggestedOutcome
192
+ },
193
+ metadata: compactRecord(input.metadata)
194
+ };
195
+ }
@@ -0,0 +1,203 @@
1
+ import { uniqueStrings } from './native-import-utils.js';
2
+ import { array, compactRecord, firstString, keysForAffected, keysForChange, normalizeChangeKind, numberOrUndefined, numberValue, strings } from './js-ts-semantic-conflict-sidecar-utils.js';
3
+
4
+ export function normalizedChanges(input, context) {
5
+ return [
6
+ ...array(input.changes),
7
+ ...array(input.operations),
8
+ ...array(input.edits),
9
+ ...array(input.left?.changes).map((change) => ({ ...change, side: change.side ?? 'left' })),
10
+ ...array(input.left?.operations).map((change) => ({ ...change, side: change.side ?? 'left' })),
11
+ ...array(input.right?.changes).map((change) => ({ ...change, side: change.side ?? 'right' })),
12
+ ...array(input.right?.operations).map((change) => ({ ...change, side: change.side ?? 'right' }))
13
+ ].map((change, index) => normalizeChange(change, context, `change_${index + 1}`));
14
+ }
15
+
16
+ export function normalizeChange(change, context, fallbackId) {
17
+ const anchor = change.anchor ?? {};
18
+ const insertion = change.insertion ?? {};
19
+ const spans = change.spans ?? {};
20
+ const sourcePath = firstString(
21
+ change.sourcePath,
22
+ change.originalSourcePath,
23
+ change.targetSourcePath,
24
+ anchor.sourcePath,
25
+ insertion.sourcePath,
26
+ insertion.insertedSourcePath,
27
+ context.sourcePath
28
+ );
29
+ const span = normalizeSpan(
30
+ change.sourceSpan
31
+ ?? change.span
32
+ ?? change.range
33
+ ?? spans.head
34
+ ?? spans.worker
35
+ ?? spans.base
36
+ ?? anchor.sourceSpan
37
+ ?? insertion.insertedSourceSpan
38
+ ?? insertion.baseSpan
39
+ ?? insertion.headSpan,
40
+ sourcePath
41
+ );
42
+ const key = firstString(
43
+ change.conflictKey,
44
+ change.regionKey,
45
+ change.key,
46
+ change.semanticKey,
47
+ change.anchorKey,
48
+ anchor.conflictKey,
49
+ anchor.key,
50
+ anchor.regionId,
51
+ insertion.anchorKey
52
+ );
53
+ const listKey = firstString(change.listKey, change.orderedListKey, change.containerKey, change.parentKey, change.orderingKey);
54
+ return {
55
+ raw: change,
56
+ id: firstString(change.id, change.operationId, fallbackId),
57
+ side: firstString(change.side, change.author, change.branch, change.source, fallbackId),
58
+ changeKind: normalizeChangeKind(change.changeKind ?? change.kind ?? change.editKind ?? change.op),
59
+ sourcePath,
60
+ span,
61
+ key,
62
+ regionKey: firstString(change.regionKey, anchor.regionId, key),
63
+ conflictKey: firstString(change.conflictKey, anchor.conflictKey, key),
64
+ symbolId: firstString(change.symbolId, anchor.symbolId, insertion.insertedSymbolId),
65
+ symbolName: firstString(change.symbolName, anchor.symbolName, insertion.insertedSymbolName),
66
+ symbolKind: firstString(change.symbolKind, anchor.symbolKind, insertion.insertedSymbolKind),
67
+ memberName: firstString(change.memberName, change.name, change.symbolName, insertion.insertedSymbolName),
68
+ exportName: firstString(change.exportName, change.exportedName),
69
+ containerKey: firstString(change.containerKey, change.parentKey, anchor.regionId),
70
+ listKey,
71
+ orderedListKey: firstString(change.orderedListKey, listKey),
72
+ index: firstString(change.index, change.position, change.orderIndex),
73
+ position: firstString(change.position, change.index),
74
+ orderKey: firstString(change.orderKey, change.order, change.sortKey),
75
+ beforeKey: firstString(change.beforeKey, change.before, change.previousKey),
76
+ afterKey: firstString(change.afterKey, change.after, change.nextKey),
77
+ anchorKey: firstString(change.anchorKey, insertion.anchorKey, anchor.key),
78
+ sourceHash: firstString(change.sourceHash, change.currentSourceHash, change.targetHash),
79
+ reasonCodes: uniqueStrings([...strings(change.reasonCode), ...strings(change.reasonCodes)])
80
+ };
81
+ }
82
+
83
+ export function normalizeDeclaration(entry, index, context) {
84
+ const change = normalizeChange(entry, context, `declaration_${index + 1}`);
85
+ const name = firstString(entry.name, entry.memberName, entry.exportName, entry.symbolName, change.memberName, change.exportName, change.symbolName);
86
+ const exported = entry.exported === true || entry.isExport === true || Boolean(entry.exportName) || /export/i.test(String(entry.kind ?? entry.symbolKind ?? ''));
87
+ const member = entry.member === true || entry.isMember === true || Boolean(entry.memberName || entry.containerKey || entry.parentKey) || /member|method|field|property/i.test(String(entry.kind ?? entry.symbolKind ?? ''));
88
+ return {
89
+ ...change,
90
+ name,
91
+ exported,
92
+ member,
93
+ containerKey: firstString(entry.containerKey, entry.parentKey, entry.classKey, entry.ownerKey, change.containerKey),
94
+ key: firstString(entry.key, entry.semanticKey, change.key, name)
95
+ };
96
+ }
97
+
98
+ export function sameChangeRegion(left, right) {
99
+ if (left.id === right.id) return false;
100
+ if (left.side && right.side && left.side === right.side) return false;
101
+ const leftKeys = keysForChange(left);
102
+ const rightKeys = keysForChange(right);
103
+ if (leftKeys.some((key) => rightKeys.includes(key))) return true;
104
+ return spansOverlap(left.span, right.span);
105
+ }
106
+
107
+ export function spansOverlap(left, right) {
108
+ if (!left || !right) return false;
109
+ if (left.sourcePath && right.sourcePath && left.sourcePath !== right.sourcePath) return false;
110
+ const leftStart = numberValue(left.startOffset, left.start);
111
+ const leftEnd = numberValue(left.endOffset, left.end);
112
+ const rightStart = numberValue(right.startOffset, right.start);
113
+ const rightEnd = numberValue(right.endOffset, right.end);
114
+ if (Number.isFinite(leftStart) && Number.isFinite(leftEnd) && Number.isFinite(rightStart) && Number.isFinite(rightEnd)) {
115
+ return leftStart < rightEnd && rightStart < leftEnd;
116
+ }
117
+ const leftStartLine = numberValue(left.startLine, left.line);
118
+ const leftEndLine = numberValue(left.endLine, leftStartLine);
119
+ const rightStartLine = numberValue(right.startLine, right.line);
120
+ const rightEndLine = numberValue(right.endLine, rightStartLine);
121
+ if (Number.isFinite(leftStartLine) && Number.isFinite(leftEndLine) && Number.isFinite(rightStartLine) && Number.isFinite(rightEndLine)) {
122
+ return leftStartLine <= rightEndLine && rightStartLine <= leftEndLine;
123
+ }
124
+ return false;
125
+ }
126
+
127
+ export function affectedFromEntries(entries, context) {
128
+ return normalizeAffected({
129
+ sourcePath: uniqueStrings(entries.map((entry) => entry.sourcePath)),
130
+ sourceSpan: entries.map((entry) => entry.span ?? entry.sourceSpan).filter(Boolean),
131
+ key: entries.flatMap((entry) => keysForAffected(entry)),
132
+ symbolId: entries.map((entry) => entry.symbolId),
133
+ symbolName: entries.map((entry) => entry.symbolName ?? entry.name),
134
+ memberKey: entries.map((entry) => entry.memberName ? `${entry.containerKey ?? entry.sourcePath ?? context.sourcePath ?? 'member'}#${entry.memberName}` : undefined),
135
+ exportName: entries.map((entry) => entry.exportName ?? (entry.exported ? entry.name : undefined)),
136
+ orderedListKey: entries.map((entry) => entry.listKey ?? entry.orderedListKey),
137
+ sourceHash: entries.map((entry) => entry.sourceHash)
138
+ }, context);
139
+ }
140
+
141
+ export function normalizeAffected(value, context) {
142
+ const sourcePaths = uniqueStrings([
143
+ context.sourcePath,
144
+ ...strings(value.sourcePath),
145
+ ...strings(value.sourcePaths),
146
+ ...array(value.spans).map((span) => span?.sourcePath),
147
+ ...array(value.sourceSpan).map((span) => span?.sourcePath)
148
+ ]);
149
+ const spans = uniqueSpans([
150
+ ...array(value.sourceSpan),
151
+ ...array(value.span),
152
+ ...array(value.spans)
153
+ ].map((span) => normalizeSpan(span, sourcePaths[0])).filter(Boolean));
154
+ const keys = uniqueStrings([
155
+ ...strings(value.key),
156
+ ...strings(value.keys),
157
+ ...strings(value.conflictKey),
158
+ ...strings(value.conflictKeys),
159
+ ...strings(value.regionKey),
160
+ ...strings(value.regionKeys),
161
+ ...strings(value.semanticKey),
162
+ ...strings(value.anchorKey)
163
+ ]);
164
+ return {
165
+ sourcePaths,
166
+ spans,
167
+ keys,
168
+ regionKeys: uniqueStrings([...strings(value.regionKey), ...strings(value.regionKeys)]),
169
+ symbolIds: uniqueStrings([...strings(value.symbolId), ...strings(value.symbolIds)]),
170
+ symbolNames: uniqueStrings([...strings(value.symbolName), ...strings(value.symbolNames)]),
171
+ memberKeys: uniqueStrings([...strings(value.memberKey), ...strings(value.memberKeys)]),
172
+ exportNames: uniqueStrings([...strings(value.exportName), ...strings(value.exportNames)]),
173
+ orderedListKeys: uniqueStrings([...strings(value.orderedListKey), ...strings(value.orderedListKeys), ...strings(value.listKey)]),
174
+ sourceHashes: uniqueStrings([...strings(value.sourceHash), ...strings(value.sourceHashes)])
175
+ };
176
+ }
177
+
178
+ export function normalizeSpan(span, sourcePath) {
179
+ if (!span || typeof span !== 'object') return undefined;
180
+ return compactRecord({
181
+ sourcePath: span.sourcePath ?? span.path ?? sourcePath,
182
+ start: numberOrUndefined(span.start),
183
+ end: numberOrUndefined(span.end),
184
+ startOffset: numberOrUndefined(span.startOffset ?? span.offset),
185
+ endOffset: numberOrUndefined(span.endOffset),
186
+ startLine: numberOrUndefined(span.startLine ?? span.line),
187
+ startColumn: numberOrUndefined(span.startColumn ?? span.column),
188
+ endLine: numberOrUndefined(span.endLine ?? span.line),
189
+ endColumn: numberOrUndefined(span.endColumn)
190
+ });
191
+ }
192
+
193
+ export function uniqueSpans(spans) {
194
+ const seen = new Set();
195
+ const result = [];
196
+ for (const span of spans) {
197
+ const key = JSON.stringify(span);
198
+ if (seen.has(key)) continue;
199
+ seen.add(key);
200
+ result.push(span);
201
+ }
202
+ return result;
203
+ }