@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,279 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
2
+ import { addConflict, arraysEqual, sameStatementText } from './js-ts-safe-merge-context.js';
3
+
4
+ export function analyzeVariantLedger(base, variant, baseIndex, side, context) {
5
+ const projectedBaseKeys = [];
6
+ const addedEntries = [];
7
+ const importAdditions = new Map();
8
+ const baseKeys = new Set(baseIndex.orderedKeys);
9
+
10
+ for (const entry of variant.entries) {
11
+ const baseEntry = baseIndex.entriesByKey.get(entry.key);
12
+ if (!baseEntry) {
13
+ if (entry.kind === 'import') {
14
+ addConflict(context, {
15
+ code: JsTsSafeMergeConflictCodes.newImportDeclaration,
16
+ gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
17
+ side,
18
+ message: `${side} source adds a new import declaration; only specifier additions to existing imports are accepted.`,
19
+ details: { key: entry.key, statement: entry.text.trim() }
20
+ });
21
+ } else {
22
+ addedEntries.push(entry);
23
+ }
24
+ continue;
25
+ }
26
+ projectedBaseKeys.push(entry.key);
27
+ if (entry.kind !== baseEntry.kind) {
28
+ addConflict(context, {
29
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
30
+ gateId: JsTsSafeMergeGateIds.parseLedger,
31
+ side,
32
+ message: `${side} source changes a base ledger entry kind.`,
33
+ details: { key: entry.key, baseKind: baseEntry.kind, sideKind: entry.kind }
34
+ });
35
+ continue;
36
+ }
37
+ if (entry.kind === 'import') {
38
+ const additions = analyzeImportStatementChange(baseEntry, entry, side, context);
39
+ if (additions.length) importAdditions.set(entry.key, additions);
40
+ } else if (!sameStatementText(baseEntry.text, entry.text)) {
41
+ const typeAliasConflict = baseEntry.declarationInfo?.declarationKind === 'type'
42
+ || entry.declarationInfo?.declarationKind === 'type';
43
+ addConflict(context, {
44
+ code: typeAliasConflict ? JsTsSafeMergeConflictCodes.typeAliasConflict : JsTsSafeMergeConflictCodes.changedExistingDeclaration,
45
+ gateId: JsTsSafeMergeGateIds.stableExistingDeclarations,
46
+ side,
47
+ message: typeAliasConflict
48
+ ? `${side} source changes an existing type alias body.`
49
+ : `${side} source changes an existing top-level declaration or export body.`,
50
+ details: { key: entry.key, name: entry.names?.[0], declarationKind: entry.declarationInfo?.declarationKind }
51
+ });
52
+ }
53
+ }
54
+
55
+ if (!arraysEqual(projectedBaseKeys, baseIndex.orderedKeys)) {
56
+ const code = base.entries.some((entry) => entry.kind === 'import' && entry.importInfo.sideEffectOnly)
57
+ ? JsTsSafeMergeConflictCodes.sideEffectImportReorder
58
+ : JsTsSafeMergeConflictCodes.topLevelOrderChanged;
59
+ addConflict(context, {
60
+ code,
61
+ gateId: JsTsSafeMergeGateIds.preserveBaseOrder,
62
+ side,
63
+ message: `${side} source changes the order or presence of base top-level entries.`,
64
+ details: {
65
+ expected: baseIndex.orderedKeys,
66
+ actual: projectedBaseKeys
67
+ }
68
+ });
69
+ }
70
+
71
+ return {
72
+ side,
73
+ addedEntries,
74
+ importAdditions,
75
+ baseKeys
76
+ };
77
+ }
78
+
79
+ function analyzeImportStatementChange(baseEntry, variantEntry, side, context) {
80
+ const baseImport = baseEntry.importInfo;
81
+ const variantImport = variantEntry.importInfo;
82
+ if (!sameImportShape(baseImport, variantImport)) {
83
+ addConflict(context, {
84
+ code: baseImport.sideEffectOnly ? JsTsSafeMergeConflictCodes.sideEffectImportReorder : JsTsSafeMergeConflictCodes.importShapeChanged,
85
+ gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
86
+ side,
87
+ message: `${side} source changes an existing import shape instead of adding named specifiers.`,
88
+ details: { key: baseEntry.key }
89
+ });
90
+ return [];
91
+ }
92
+ if (baseImport.sideEffectOnly) {
93
+ if (!sameStatementText(baseEntry.text, variantEntry.text)) {
94
+ addConflict(context, {
95
+ code: JsTsSafeMergeConflictCodes.sideEffectImportReorder,
96
+ gateId: JsTsSafeMergeGateIds.preserveBaseOrder,
97
+ side,
98
+ message: `${side} source changes a side-effect import.`,
99
+ details: { key: baseEntry.key }
100
+ });
101
+ }
102
+ return [];
103
+ }
104
+
105
+ const baseSpecifiers = baseImport.specifiers;
106
+ const variantSpecifiers = variantImport.specifiers;
107
+ if (variantSpecifiers.length < baseSpecifiers.length) {
108
+ addConflict(context, {
109
+ code: JsTsSafeMergeConflictCodes.importSpecifierRemoved,
110
+ gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
111
+ side,
112
+ message: `${side} source removes import specifiers.`,
113
+ details: { key: baseEntry.key }
114
+ });
115
+ return [];
116
+ }
117
+ for (let index = 0; index < baseSpecifiers.length; index += 1) {
118
+ if (variantSpecifiers[index]?.canonical !== baseSpecifiers[index].canonical) {
119
+ addConflict(context, {
120
+ code: JsTsSafeMergeConflictCodes.importSpecifierReordered,
121
+ gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
122
+ side,
123
+ message: `${side} source reorders or changes existing import specifiers.`,
124
+ details: {
125
+ key: baseEntry.key,
126
+ expected: baseSpecifiers.map((specifier) => specifier.canonical),
127
+ actual: variantSpecifiers.map((specifier) => specifier.canonical)
128
+ }
129
+ });
130
+ return [];
131
+ }
132
+ }
133
+ const additions = variantSpecifiers.slice(baseSpecifiers.length);
134
+ if (additions.length === 0 && !sameStatementText(baseEntry.text, variantEntry.text)) {
135
+ addConflict(context, {
136
+ code: JsTsSafeMergeConflictCodes.importFormattingChanged,
137
+ gateId: JsTsSafeMergeGateIds.independentImportSpecifiers,
138
+ side,
139
+ message: `${side} source changes import formatting without a specifier addition.`,
140
+ details: { key: baseEntry.key }
141
+ });
142
+ }
143
+ return additions;
144
+ }
145
+
146
+ function sameImportShape(left, right) {
147
+ return left.moduleSpecifier === right.moduleSpecifier
148
+ && left.typeOnly === right.typeOnly
149
+ && left.sideEffectOnly === right.sideEffectOnly
150
+ && left.defaultLocalName === right.defaultLocalName
151
+ && left.namespaceLocalName === right.namespaceLocalName;
152
+ }
153
+
154
+ export function validateIndependentAdditions(base, workerPlan, headPlan, context) {
155
+ const declarationNames = new Set();
156
+ for (const entry of base.entries.filter((item) => item.kind !== 'import')) {
157
+ for (const name of entry.names ?? []) declarationNames.add(`${entry.kind}:${name}`);
158
+ }
159
+ validateAddedEntryNames(workerPlan, declarationNames, context);
160
+ validateAddedEntryNames(headPlan, declarationNames, context);
161
+ validateCrossSideAddedNames(workerPlan, headPlan, context);
162
+ validateCrossSideImportAdditions(workerPlan, headPlan, context);
163
+ validateMergedImportAndDeclarationNames(base, workerPlan, headPlan, context);
164
+ }
165
+
166
+ function validateAddedEntryNames(plan, baseNames, context) {
167
+ for (const entry of plan.addedEntries) {
168
+ if (entry.kind !== 'declaration' && entry.kind !== 'export') {
169
+ addConflict(context, {
170
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
171
+ gateId: JsTsSafeMergeGateIds.independentTopLevelDeclarations,
172
+ side: plan.side,
173
+ message: `${plan.side} source adds an unsupported top-level entry.`,
174
+ details: { kind: entry.kind, statement: entry.text.trim() }
175
+ });
176
+ continue;
177
+ }
178
+ for (const name of entry.names ?? []) {
179
+ const nameKey = `${entry.kind}:${name}`;
180
+ if (baseNames.has(nameKey)) {
181
+ addConflict(context, {
182
+ code: JsTsSafeMergeConflictCodes.duplicateName,
183
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
184
+ side: plan.side,
185
+ message: `${plan.side} source adds a duplicate top-level name.`,
186
+ details: { name, kind: entry.kind }
187
+ });
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ function validateCrossSideAddedNames(workerPlan, headPlan, context) {
194
+ const headEntriesByName = new Map();
195
+ for (const entry of headPlan.addedEntries) {
196
+ for (const name of entry.names ?? []) headEntriesByName.set(`${entry.kind}:${name}`, entry);
197
+ }
198
+ for (const entry of workerPlan.addedEntries) {
199
+ for (const name of entry.names ?? []) {
200
+ const nameKey = `${entry.kind}:${name}`;
201
+ const headEntry = headEntriesByName.get(nameKey);
202
+ if (headEntry) {
203
+ const typeAliasConflict = entry.declarationInfo?.declarationKind === 'type'
204
+ || headEntry.declarationInfo?.declarationKind === 'type';
205
+ addConflict(context, {
206
+ code: typeAliasConflict ? JsTsSafeMergeConflictCodes.typeAliasConflict : JsTsSafeMergeConflictCodes.duplicateName,
207
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
208
+ side: 'worker',
209
+ message: typeAliasConflict
210
+ ? 'Worker and head add conflicting type aliases.'
211
+ : 'Worker and head add the same top-level name.',
212
+ details: { name, kind: entry.kind, workerDeclarationKind: entry.declarationInfo?.declarationKind, headDeclarationKind: headEntry.declarationInfo?.declarationKind }
213
+ });
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ function validateCrossSideImportAdditions(workerPlan, headPlan, context) {
220
+ for (const [key, workerAdditions] of workerPlan.importAdditions) {
221
+ const headAdditions = headPlan.importAdditions.get(key) ?? [];
222
+ const headLocalNames = new Set(headAdditions.map((specifier) => specifier.localName));
223
+ const headImportedNames = new Set(headAdditions.map((specifier) => `${specifier.typeOnly ? 'type:' : 'value:'}${specifier.importedName}`));
224
+ for (const specifier of workerAdditions) {
225
+ if (headLocalNames.has(specifier.localName) || headImportedNames.has(`${specifier.typeOnly ? 'type:' : 'value:'}${specifier.importedName}`)) {
226
+ addConflict(context, {
227
+ code: JsTsSafeMergeConflictCodes.duplicateName,
228
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
229
+ side: 'worker',
230
+ message: 'Worker and head add duplicate import specifiers.',
231
+ details: { key, specifier: specifier.canonical }
232
+ });
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ function validateMergedImportAndDeclarationNames(base, workerPlan, headPlan, context) {
239
+ const topLevelBindingNames = new Set();
240
+ for (const entry of base.entries.filter((item) => item.kind !== 'import')) {
241
+ for (const name of entry.names ?? []) topLevelBindingNames.add(name);
242
+ }
243
+ for (const plan of [workerPlan, headPlan]) {
244
+ for (const entry of plan.addedEntries.filter((item) => item.kind !== 'import')) {
245
+ for (const name of entry.names ?? []) topLevelBindingNames.add(name);
246
+ }
247
+ }
248
+ const importLocalNames = new Set();
249
+ for (const entry of base.entries.filter((item) => item.kind === 'import')) {
250
+ const additions = [
251
+ ...(workerPlan.importAdditions.get(entry.key) ?? []),
252
+ ...(headPlan.importAdditions.get(entry.key) ?? [])
253
+ ];
254
+ for (const specifier of [
255
+ ...(entry.importInfo.defaultLocalName ? [{ localName: entry.importInfo.defaultLocalName, canonical: `default:${entry.importInfo.defaultLocalName}` }] : []),
256
+ ...(entry.importInfo.namespaceLocalName ? [{ localName: entry.importInfo.namespaceLocalName, canonical: `namespace:${entry.importInfo.namespaceLocalName}` }] : []),
257
+ ...entry.importInfo.specifiers,
258
+ ...additions
259
+ ]) {
260
+ if (importLocalNames.has(specifier.localName)) {
261
+ addConflict(context, {
262
+ code: JsTsSafeMergeConflictCodes.duplicateName,
263
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
264
+ message: 'Merged imports would contain duplicate local names.',
265
+ details: { localName: specifier.localName, specifier: specifier.canonical }
266
+ });
267
+ }
268
+ if (topLevelBindingNames.has(specifier.localName)) {
269
+ addConflict(context, {
270
+ code: JsTsSafeMergeConflictCodes.duplicateName,
271
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
272
+ message: 'Merged imports would duplicate a top-level declaration name.',
273
+ details: { localName: specifier.localName, specifier: specifier.canonical }
274
+ });
275
+ }
276
+ importLocalNames.add(specifier.localName);
277
+ }
278
+ }
279
+ }
@@ -0,0 +1,50 @@
1
+ export const JsTsSafeMergeStatuses = Object.freeze({
2
+ merged: 'merged',
3
+ blocked: 'blocked'
4
+ });
5
+
6
+ export const JsTsSafeMergeGateIds = Object.freeze({
7
+ parseLedger: 'parse-ledger',
8
+ preserveBaseOrder: 'preserve-base-order',
9
+ stableExistingDeclarations: 'stable-existing-declarations',
10
+ independentImportSpecifiers: 'independent-import-specifiers',
11
+ independentTopLevelDeclarations: 'independent-top-level-declarations',
12
+ uniqueNames: 'unique-names',
13
+ resolvedInsertionAnchors: 'resolved-insertion-anchors'
14
+ });
15
+
16
+ export const JsTsSafeMergeConflictCodes = Object.freeze({
17
+ invalidInput: 'invalid-input',
18
+ parserLedgerLoss: 'parser-ledger-loss',
19
+ malformedSyntax: 'malformed-syntax',
20
+ sideEffectImportReorder: 'side-effect-import-reorder',
21
+ topLevelOrderChanged: 'top-level-order-changed',
22
+ changedExistingDeclaration: 'changed-existing-declaration',
23
+ typeAliasConflict: 'type-alias-conflict',
24
+ importShapeChanged: 'import-shape-changed',
25
+ importSpecifierRemoved: 'import-specifier-removed',
26
+ importSpecifierReordered: 'import-specifier-reordered',
27
+ importFormattingChanged: 'import-formatting-changed',
28
+ newImportDeclaration: 'new-import-declaration',
29
+ duplicateName: 'duplicate-name',
30
+ computedKey: 'computed-key',
31
+ unsupportedDecorator: 'unsupported-decorator-merge-anchor',
32
+ unsupportedOverload: 'unsupported-overload-merge-anchor',
33
+ staleSourceHash: 'stale-source-hash',
34
+ missingSourceLedgerSpan: 'missing-source-ledger-span',
35
+ ambiguousInsertionPoint: 'ambiguous-insertion-point',
36
+ insertionAnchorMissing: 'insertion-anchor-missing'
37
+ });
38
+
39
+ export const jsTsSafeMergeGateOrder = Object.freeze([
40
+ JsTsSafeMergeGateIds.parseLedger,
41
+ JsTsSafeMergeGateIds.preserveBaseOrder,
42
+ JsTsSafeMergeGateIds.stableExistingDeclarations,
43
+ JsTsSafeMergeGateIds.independentImportSpecifiers,
44
+ JsTsSafeMergeGateIds.independentTopLevelDeclarations,
45
+ JsTsSafeMergeGateIds.uniqueNames,
46
+ JsTsSafeMergeGateIds.resolvedInsertionAnchors
47
+ ]);
48
+
49
+ const identifierPattern = '[A-Za-z_$][\\w$]*';
50
+ export const identifierRegExp = new RegExp(`^${identifierPattern}$`);
@@ -0,0 +1,118 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds, JsTsSafeMergeStatuses, jsTsSafeMergeGateOrder } from './js-ts-safe-merge-constants.js';
2
+
3
+ export function createMergeContext(input) {
4
+ return {
5
+ id: String(input.id ?? 'js_ts_safe_merge'),
6
+ sourcePath: input.sourcePath,
7
+ language: input.language ?? 'typescript',
8
+ conflicts: [],
9
+ blockedGateIds: new Set(),
10
+ gateReasonCodes: new Map()
11
+ };
12
+ }
13
+
14
+ export function blockedResult(context, ledgers = {}) {
15
+ const reasonCodes = uniqueStrings(context.conflicts.map((conflict) => conflict.code));
16
+ return {
17
+ kind: 'frontier.lang.jsTsSafeMerge',
18
+ version: 1,
19
+ schema: 'frontier.lang.jsTsSafeMerge.v1',
20
+ id: context.id,
21
+ status: JsTsSafeMergeStatuses.blocked,
22
+ sourcePath: context.sourcePath,
23
+ language: context.language,
24
+ conflicts: context.conflicts,
25
+ gates: gatesFor(context),
26
+ admission: {
27
+ status: 'blocked',
28
+ action: 'human-review',
29
+ reviewRequired: true,
30
+ autoApplyCandidate: false,
31
+ autoMergeClaim: false,
32
+ semanticEquivalenceClaim: false,
33
+ reasonCodes
34
+ },
35
+ summary: {
36
+ importSpecifierAdditions: 0,
37
+ topLevelDeclarationAdditions: 0,
38
+ changedExistingDeclarations: context.conflicts.filter((conflict) => conflict.code === JsTsSafeMergeConflictCodes.changedExistingDeclaration).length,
39
+ conflicts: context.conflicts.length,
40
+ gatesPassed: gatesFor(context).filter((gate) => gate.status === 'passed').length
41
+ },
42
+ metadata: {
43
+ ledgers: compactRecord({
44
+ base: ledgerSummary(ledgers.base),
45
+ worker: ledgerSummary(ledgers.worker),
46
+ head: ledgerSummary(ledgers.head),
47
+ merged: ledgerSummary(ledgers.merged)
48
+ })
49
+ }
50
+ };
51
+ }
52
+
53
+ export function addConflict(context, conflict) {
54
+ const gateId = conflict.gateId ?? JsTsSafeMergeGateIds.parseLedger;
55
+ const record = {
56
+ code: conflict.code,
57
+ gateId,
58
+ message: conflict.message,
59
+ side: conflict.side,
60
+ sourcePath: context.sourcePath,
61
+ details: compactRecord(conflict.details)
62
+ };
63
+ context.conflicts.push(record);
64
+ context.blockedGateIds.add(gateId);
65
+ const gateReasonCodes = context.gateReasonCodes.get(gateId) ?? [];
66
+ gateReasonCodes.push(conflict.code);
67
+ context.gateReasonCodes.set(gateId, uniqueStrings(gateReasonCodes));
68
+ }
69
+
70
+ export function gatesFor(context) {
71
+ let blockedSeen = false;
72
+ return jsTsSafeMergeGateOrder.map((id) => {
73
+ const blocked = context.blockedGateIds.has(id);
74
+ const status = blocked ? 'blocked' : blockedSeen ? 'skipped' : 'passed';
75
+ if (blocked) blockedSeen = true;
76
+ return {
77
+ id,
78
+ status,
79
+ reasonCodes: context.gateReasonCodes.get(id) ?? []
80
+ };
81
+ });
82
+ }
83
+
84
+ export function sameStatementText(left, right) {
85
+ return normalizeLineEndings(String(left ?? '').trim(), '\n') === normalizeLineEndings(String(right ?? '').trim(), '\n');
86
+ }
87
+
88
+ export function normalizeLineEndings(text, lineEnding) {
89
+ return String(text ?? '').replace(/\r\n?/g, '\n').replace(/\n/g, lineEnding);
90
+ }
91
+
92
+ export function detectLineEnding(text) {
93
+ return String(text ?? '').includes('\r\n') ? '\r\n' : '\n';
94
+ }
95
+
96
+ export function arraysEqual(left, right) {
97
+ if (left.length !== right.length) return false;
98
+ return left.every((value, index) => value === right[index]);
99
+ }
100
+
101
+ export function uniqueStrings(values) {
102
+ return [...new Set((values ?? []).filter((value) => typeof value === 'string' && value.length > 0))];
103
+ }
104
+
105
+ export function compactRecord(record) {
106
+ return Object.fromEntries(Object.entries(record ?? {}).filter(([, value]) => value !== undefined));
107
+ }
108
+
109
+ export function ledgerSummary(ledger) {
110
+ if (!ledger) return undefined;
111
+ return {
112
+ label: ledger.label,
113
+ entries: ledger.entries.length,
114
+ imports: ledger.entries.filter((entry) => entry.kind === 'import').length,
115
+ declarations: ledger.entries.filter((entry) => entry.kind === 'declaration').length,
116
+ exports: ledger.entries.filter((entry) => entry.kind === 'export').length
117
+ };
118
+ }
@@ -0,0 +1,92 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
2
+ import { addConflict } from './js-ts-safe-merge-context.js';
3
+
4
+ export function validateLedgerUniqueness(ledger, context) {
5
+ const keyOwners = new Map();
6
+ for (const entry of ledger.entries) {
7
+ const existing = keyOwners.get(entry.key);
8
+ if (existing) {
9
+ addConflict(context, {
10
+ code: JsTsSafeMergeConflictCodes.duplicateName,
11
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
12
+ side: ledger.label,
13
+ message: `${ledger.label} source has duplicate top-level ledger keys.`,
14
+ details: { key: entry.key, firstOffset: existing.start, secondOffset: entry.start }
15
+ });
16
+ } else {
17
+ keyOwners.set(entry.key, entry);
18
+ }
19
+ }
20
+
21
+ const nameOwners = new Map();
22
+ for (const entry of ledger.entries) {
23
+ for (const name of entry.names ?? []) {
24
+ const nameKey = `${entry.kind}:${name}`;
25
+ const existing = nameOwners.get(nameKey);
26
+ if (existing) {
27
+ addConflict(context, {
28
+ code: JsTsSafeMergeConflictCodes.duplicateName,
29
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
30
+ side: ledger.label,
31
+ message: `${ledger.label} source has duplicate top-level names.`,
32
+ details: { name, firstOffset: existing.start, secondOffset: entry.start }
33
+ });
34
+ } else {
35
+ nameOwners.set(nameKey, entry);
36
+ }
37
+ }
38
+ }
39
+
40
+ for (const entry of ledger.entries.filter((item) => item.kind === 'import')) {
41
+ validateUniqueImportSpecifiers(entry, ledger.label, context);
42
+ }
43
+ }
44
+
45
+ function validateUniqueImportSpecifiers(entry, side, context) {
46
+ const localNames = new Map();
47
+ const specifiers = [
48
+ ...(entry.importInfo.defaultLocalName ? [{ localName: entry.importInfo.defaultLocalName, canonical: `default:${entry.importInfo.defaultLocalName}` }] : []),
49
+ ...(entry.importInfo.namespaceLocalName ? [{ localName: entry.importInfo.namespaceLocalName, canonical: `namespace:${entry.importInfo.namespaceLocalName}` }] : []),
50
+ ...entry.importInfo.specifiers
51
+ ];
52
+ for (const specifier of specifiers) {
53
+ const existing = localNames.get(specifier.localName);
54
+ if (existing) {
55
+ addConflict(context, {
56
+ code: JsTsSafeMergeConflictCodes.duplicateName,
57
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
58
+ side,
59
+ message: `${side} source has duplicate import binding names.`,
60
+ details: { importKey: entry.key, localName: specifier.localName, first: existing.canonical, second: specifier.canonical }
61
+ });
62
+ } else {
63
+ localNames.set(specifier.localName, specifier);
64
+ }
65
+ }
66
+ }
67
+
68
+ export function indexBaseLedger(base, context) {
69
+ const entriesByKey = new Map();
70
+ for (const entry of base.entries) entriesByKey.set(entry.key, entry);
71
+ const names = new Set();
72
+ for (const entry of base.entries) {
73
+ for (const name of entry.names ?? []) {
74
+ const nameKey = `${entry.kind}:${name}`;
75
+ if (names.has(nameKey)) {
76
+ addConflict(context, {
77
+ code: JsTsSafeMergeConflictCodes.duplicateName,
78
+ gateId: JsTsSafeMergeGateIds.uniqueNames,
79
+ side: 'base',
80
+ message: 'Base source has duplicate declaration or export names.',
81
+ details: { name }
82
+ });
83
+ }
84
+ names.add(nameKey);
85
+ }
86
+ }
87
+ return {
88
+ entriesByKey,
89
+ orderedKeys: base.entries.map((entry) => entry.key),
90
+ names
91
+ };
92
+ }
@@ -0,0 +1,85 @@
1
+ import { JsTsSafeMergeConflictCodes, JsTsSafeMergeGateIds } from './js-ts-safe-merge-constants.js';
2
+ import { addConflict } from './js-ts-safe-merge-context.js';
3
+ import { classifyStatement, importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
4
+ import { findStatementEnd, skipTopLevelTrivia } from './js-ts-safe-merge-parse-statements.js';
5
+
6
+ export function scanJsTsTopLevelLedger(sourceText, label, context) {
7
+ const entries = [];
8
+ let offset = 0;
9
+ while (offset < sourceText.length) {
10
+ const skipped = skipTopLevelTrivia(sourceText, offset);
11
+ if (skipped.error) {
12
+ addConflict(context, {
13
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
14
+ gateId: JsTsSafeMergeGateIds.parseLedger,
15
+ side: label,
16
+ message: `${label} source contains unterminated top-level trivia.`,
17
+ details: { offset, error: skipped.error }
18
+ });
19
+ return createLedger(label, sourceText, entries);
20
+ }
21
+ offset = skipped.offset;
22
+ if (offset >= sourceText.length) break;
23
+
24
+ if (sourceText[offset] === '@') {
25
+ addConflict(context, {
26
+ code: JsTsSafeMergeConflictCodes.unsupportedDecorator,
27
+ gateId: JsTsSafeMergeGateIds.parseLedger,
28
+ side: label,
29
+ message: `${label} source contains decorator syntax that is not safe for ledger merge anchors.`,
30
+ details: { offset, reasonCode: 'unsupported-js-ts-syntax', preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
31
+ });
32
+ return createLedger(label, sourceText, entries);
33
+ }
34
+
35
+ const end = findStatementEnd(sourceText, offset);
36
+ if (end.error) {
37
+ addConflict(context, {
38
+ code: JsTsSafeMergeConflictCodes.malformedSyntax,
39
+ gateId: JsTsSafeMergeGateIds.parseLedger,
40
+ side: label,
41
+ message: `${label} source contains a top-level statement the narrow ledger cannot bound.`,
42
+ details: { offset, error: end.error, preview: sourceText.slice(offset, Math.min(sourceText.length, offset + 80)) }
43
+ });
44
+ return createLedger(label, sourceText, entries);
45
+ }
46
+
47
+ const statementText = sourceText.slice(offset, end.offset);
48
+ const entry = classifyStatement(statementText, offset, end.offset);
49
+ if (entry?.unsupported) {
50
+ addConflict(context, {
51
+ code: entry.unsupported.code,
52
+ gateId: JsTsSafeMergeGateIds.parseLedger,
53
+ side: label,
54
+ message: `${label} source contains syntax that is not safe for automatic JS/TS ledger merge.`,
55
+ details: { offset, ...entry.unsupported.details, statement: statementText.trim().slice(0, 120) }
56
+ });
57
+ return createLedger(label, sourceText, entries);
58
+ }
59
+ if (!entry) {
60
+ addConflict(context, {
61
+ code: JsTsSafeMergeConflictCodes.parserLedgerLoss,
62
+ gateId: JsTsSafeMergeGateIds.parseLedger,
63
+ side: label,
64
+ message: `${label} source contains an unsupported top-level statement.`,
65
+ details: { offset, statement: statementText.trim().slice(0, 120) }
66
+ });
67
+ return createLedger(label, sourceText, entries);
68
+ }
69
+ entries.push(entry);
70
+ offset = end.offset;
71
+ }
72
+ return createLedger(label, sourceText, entries);
73
+ }
74
+
75
+ function createLedger(label, sourceText, entries) {
76
+ return {
77
+ label,
78
+ sourceText,
79
+ entries,
80
+ baseEntries: entries.filter((entry) => entry.kind === 'import' || entry.kind === 'declaration' || entry.kind === 'export')
81
+ };
82
+ }
83
+
84
+ export { importSpecifierCanonical } from './js-ts-safe-merge-parse-declarations.js';
85
+ export { indexBaseLedger, validateLedgerUniqueness } from './js-ts-safe-merge-ledger-validation.js';