@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.105

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 (125) hide show
  1. package/README.md +14 -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 +86 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +131 -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/semantic-edit-replay-diagnostics.d.ts +12 -0
  11. package/dist/declarations/semantic-edit-script.d.ts +7 -4
  12. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  13. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  14. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  15. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  16. package/dist/declarations/source-preservation.d.ts +72 -0
  17. package/dist/declarations/universal-capability.d.ts +4 -0
  18. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  19. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  20. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  21. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +3 -0
  24. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  25. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  26. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  27. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  28. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  29. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  30. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  31. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  32. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  33. package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
  34. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  35. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
  36. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  37. package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
  38. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  39. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  40. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  41. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  42. package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
  43. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  44. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  45. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  46. package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
  47. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  48. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
  49. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  50. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  51. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  52. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  53. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  54. package/dist/js-ts-safe-member-merge-result.js +158 -0
  55. package/dist/js-ts-safe-member-merge.js +265 -0
  56. package/dist/js-ts-safe-merge-analyze.js +279 -0
  57. package/dist/js-ts-safe-merge-composed.js +170 -0
  58. package/dist/js-ts-safe-merge-constants.js +50 -0
  59. package/dist/js-ts-safe-merge-context.js +118 -0
  60. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  61. package/dist/js-ts-safe-merge-ledger.js +85 -0
  62. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  63. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  64. package/dist/js-ts-safe-merge-plan.js +190 -0
  65. package/dist/js-ts-safe-merge.js +175 -0
  66. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  67. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  68. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  69. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  70. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  71. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  72. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  73. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  74. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  75. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  76. package/dist/js-ts-semantic-merge-member-source.js +82 -0
  77. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  78. package/dist/js-ts-semantic-merge-parse.js +16 -0
  79. package/dist/js-ts-semantic-merge.js +24 -0
  80. package/dist/lightweight-dependency-effects.js +51 -0
  81. package/dist/lightweight-dependency-language.js +12 -1
  82. package/dist/lightweight-dependency-relations.js +14 -27
  83. package/dist/native-region-scanner-core.js +33 -1
  84. package/dist/native-region-scanner-csharp.js +151 -0
  85. package/dist/native-region-scanner-dart.js +91 -0
  86. package/dist/native-region-scanner-dynamic.js +21 -151
  87. package/dist/native-region-scanner-functional.js +40 -13
  88. package/dist/native-region-scanner-java.js +97 -0
  89. package/dist/native-region-scanner-js-class.js +100 -0
  90. package/dist/native-region-scanner-js-helpers.js +28 -86
  91. package/dist/native-region-scanner-js-imports.js +121 -1
  92. package/dist/native-region-scanner-js-nested.js +96 -8
  93. package/dist/native-region-scanner-js-structure.js +27 -0
  94. package/dist/native-region-scanner-js-types.js +99 -0
  95. package/dist/native-region-scanner-js.js +70 -118
  96. package/dist/native-region-scanner-kotlin.js +94 -0
  97. package/dist/native-region-scanner-main.js +15 -181
  98. package/dist/native-region-scanner-php.js +80 -0
  99. package/dist/native-region-scanner-python.js +62 -0
  100. package/dist/native-region-scanner-ruby.js +72 -0
  101. package/dist/native-region-scanner-scala.js +91 -0
  102. package/dist/native-region-scanner-spans.js +74 -0
  103. package/dist/native-region-scanner-swift.js +155 -0
  104. package/dist/native-region-scanner.js +14 -10
  105. package/dist/native-source-ledger-helpers.js +195 -0
  106. package/dist/native-source-ledger.js +306 -0
  107. package/dist/native-source-preservation-scanner.js +4 -0
  108. package/dist/semantic-import-callsite-regions.js +136 -0
  109. package/dist/semantic-import-effect-regions.js +283 -0
  110. package/dist/semantic-import-regions.js +11 -2
  111. package/dist/semantic-import-sidecar-entry.js +16 -2
  112. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  113. package/dist/semantic-sidecar-example.js +68 -0
  114. package/dist/universal-capability-matrix.js +23 -0
  115. package/dist/universal-conversion-artifact-query.js +79 -2
  116. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  117. package/dist/universal-conversion-artifact-summary.js +33 -1
  118. package/dist/universal-conversion-artifacts.js +13 -48
  119. package/dist/universal-conversion-plan-scoring.js +21 -1
  120. package/dist/universal-conversion-plan-summary.js +30 -0
  121. package/dist/universal-conversion-plan.js +25 -9
  122. package/dist/universal-conversion-route-metadata.js +96 -0
  123. package/dist/universal-conversion-route-operations.js +7 -0
  124. package/dist/universal-representation-coverage.js +193 -0
  125. package/package.json +1 -1
@@ -102,6 +102,8 @@ export function normalizeSourceMapLinks(links) {
102
102
  precision: link.precision,
103
103
  sourceSpan: link.sourceSpan,
104
104
  generatedSpan: link.generatedSpan,
105
+ sourceReplacementText: link.sourceReplacementText,
106
+ sourceReplacementTextHash: link.sourceReplacementTextHash,
105
107
  regionKey: link.ownershipRegionKey,
106
108
  regionKind: link.ownershipRegionKind
107
109
  }));
@@ -0,0 +1,111 @@
1
+ import { uniqueStrings } from '../../native-import-utils.js';
2
+
3
+ function summarizeSemanticSidecarQuality(sources) {
4
+ const records = uniqueSemanticSidecarQualityRecords(array(sources).flatMap(semanticSidecarQualityRecords));
5
+ const warningCodes = uniqueStrings(records.flatMap(sidecarQualityWarningCodes));
6
+ const zeroRecordWarningCodes = uniqueStrings(records.flatMap(sidecarQualityZeroRecordWarningCodes));
7
+ return {
8
+ records: records.length,
9
+ symbols: records.reduce((sum, record) => sum + sidecarQualityCount(record, 'symbolCount', 'symbols'), 0),
10
+ ownershipRegions: records.reduce((sum, record) => sum + sidecarQualityCount(record, 'ownershipRegionCount', 'ownershipRegions'), 0),
11
+ patchHints: records.reduce((sum, record) => sum + sidecarQualityCount(record, 'patchHintCount', 'patchHints'), 0),
12
+ warnings: records.reduce((sum, record) => sum + sidecarQualityWarningCount(record), 0),
13
+ zeroRecordWarnings: zeroRecordWarningCodes.length,
14
+ warningCodes,
15
+ zeroRecordWarningCodes
16
+ };
17
+ }
18
+
19
+ function semanticSidecarQualityRecords(source) {
20
+ if (!source || typeof source !== 'object') return [];
21
+ return [
22
+ source.semanticSidecarQuality,
23
+ source.sidecarQuality,
24
+ source.semanticSidecar?.quality,
25
+ source.sidecar?.quality,
26
+ source.semanticSidecarAdmission,
27
+ source.sidecarAdmission,
28
+ source.semanticSidecar?.admission,
29
+ source.sidecar?.admission,
30
+ source.metadata?.semanticSidecarQuality,
31
+ source.metadata?.sidecarQuality,
32
+ source.metadata?.semanticSidecarAdmission,
33
+ source.metadata?.sidecarAdmission
34
+ ].filter(looksLikeSemanticSidecarQuality);
35
+ }
36
+
37
+ function looksLikeSemanticSidecarQuality(value) {
38
+ return value && typeof value === 'object' && (
39
+ value.schema === 'frontier.lang.semanticSidecarQuality.v1' ||
40
+ value.schema === 'frontier.lang.semanticSidecarAdmission.v1' ||
41
+ value.imported !== undefined ||
42
+ value.eligible !== undefined ||
43
+ value.expectedSatisfied !== undefined ||
44
+ value.warningCount !== undefined ||
45
+ value.emptyEvidenceWarnings !== undefined ||
46
+ value.proofSummary !== undefined ||
47
+ value.counts !== undefined
48
+ );
49
+ }
50
+
51
+ function uniqueSemanticSidecarQualityRecords(records) {
52
+ const seen = new Set();
53
+ const result = [];
54
+ for (const record of records) {
55
+ const key = record.id ?? JSON.stringify({
56
+ schema: record.schema,
57
+ symbols: sidecarQualityCount(record, 'symbolCount', 'symbols'),
58
+ ownershipRegions: sidecarQualityCount(record, 'ownershipRegionCount', 'ownershipRegions'),
59
+ patchHints: sidecarQualityCount(record, 'patchHintCount', 'patchHints'),
60
+ warningCodes: sidecarQualityWarningCodes(record)
61
+ });
62
+ if (seen.has(key)) continue;
63
+ seen.add(key);
64
+ result.push(record);
65
+ }
66
+ return result;
67
+ }
68
+
69
+ function sidecarQualityCount(record, field, countsField) {
70
+ for (const value of [record?.[field], record?.counts?.[countsField]]) {
71
+ const count = Number(value ?? 0);
72
+ if (Number.isFinite(count) && count > 0) return count;
73
+ }
74
+ return 0;
75
+ }
76
+
77
+ function sidecarQualityWarningCount(record) {
78
+ const count = Number(record?.warningCount ?? 0);
79
+ if (Number.isFinite(count) && count > 0) return count;
80
+ return sidecarQualityWarningCodes(record).length;
81
+ }
82
+
83
+ function sidecarQualityWarningCodes(record) {
84
+ return uniqueStrings([
85
+ ...array(record?.warnings).map((warning) => warning?.code ?? warning?.reasonCode),
86
+ ...array(record?.emptyEvidenceWarnings).map((warning) => warning?.code ?? warning?.reasonCode),
87
+ ...strings(record?.expectedMissingReasonCodes),
88
+ ...strings(record?.reasonCodes)
89
+ ].filter(Boolean));
90
+ }
91
+
92
+ function sidecarQualityZeroRecordWarningCodes(record) {
93
+ return sidecarQualityWarningCodes(record).filter(isZeroRecordWarningCode);
94
+ }
95
+
96
+ function isZeroRecordWarningCode(code) {
97
+ return [
98
+ 'empty-evidence',
99
+ 'empty-semantic-index',
100
+ 'expected-semantic-import-empty',
101
+ 'expected-semantic-import-missing',
102
+ 'missing-imports',
103
+ 'missing-ownership-regions',
104
+ 'missing-patch-hints'
105
+ ].includes(code);
106
+ }
107
+
108
+ function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
109
+ function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
110
+
111
+ export { summarizeSemanticSidecarQuality };
@@ -8,11 +8,18 @@ export function applySourceEdits(sourceText, edits) {
8
8
  }
9
9
 
10
10
  export function dedupeSourceEdits(edits) {
11
- const exact = dedupeExactInsertions(edits);
12
- const covered = removeCoveredContainerReplacements(exact.edits);
11
+ const exact = dedupeExactEdits(edits);
12
+ const noops = removeNoopReplacements(exact.edits);
13
+ const coveredDeletes = removeContainedDeletionEdits(noops.edits);
14
+ const covered = removeCoveredContainerReplacements(coveredDeletes.edits);
13
15
  return {
14
16
  edits: covered.edits,
15
- skippedOperationIds: [...exact.skippedOperationIds, ...covered.skippedOperationIds]
17
+ skippedOperationIds: [
18
+ ...exact.skippedOperationIds,
19
+ ...noops.skippedOperationIds,
20
+ ...coveredDeletes.skippedOperationIds,
21
+ ...covered.skippedOperationIds
22
+ ]
16
23
  };
17
24
  }
18
25
 
@@ -27,7 +34,7 @@ export function validateSourceEdits(edits) {
27
34
  return uniqueStrings(reasons);
28
35
  }
29
36
 
30
- function dedupeExactInsertions(edits) {
37
+ function dedupeExactEdits(edits) {
31
38
  const seen = new Map();
32
39
  const result = [];
33
40
  const skippedOperationIds = [];
@@ -43,6 +50,19 @@ function dedupeExactInsertions(edits) {
43
50
  return { edits: result, skippedOperationIds };
44
51
  }
45
52
 
53
+ function removeNoopReplacements(edits) {
54
+ const skippedOperationIds = [];
55
+ const result = [];
56
+ for (const edit of edits) {
57
+ if (edit.editKind === 'replace' && !edit.alreadyApplied && edit.current === edit.replacement) {
58
+ skippedOperationIds.push(edit.operationId);
59
+ continue;
60
+ }
61
+ result.push(edit);
62
+ }
63
+ return { edits: result, skippedOperationIds };
64
+ }
65
+
46
66
  function removeCoveredContainerReplacements(edits) {
47
67
  const skippedOperationIds = [];
48
68
  const result = [];
@@ -56,6 +76,31 @@ function removeCoveredContainerReplacements(edits) {
56
76
  return { edits: result, skippedOperationIds };
57
77
  }
58
78
 
79
+ function removeContainedDeletionEdits(edits) {
80
+ const skippedOperationIds = [];
81
+ const result = [];
82
+ for (const edit of edits) {
83
+ if (deleteCoveredByLargerDelete(edit, edits)) {
84
+ skippedOperationIds.push(edit.operationId);
85
+ continue;
86
+ }
87
+ result.push(edit);
88
+ }
89
+ return { edits: result, skippedOperationIds };
90
+ }
91
+
92
+ function deleteCoveredByLargerDelete(edit, edits) {
93
+ return edit.editKind === 'delete' && !edit.alreadyApplied && edits.some((candidate) => (
94
+ candidate !== edit &&
95
+ candidate.editKind === 'delete' &&
96
+ !candidate.alreadyApplied &&
97
+ sameSourcePath(candidate, edit) &&
98
+ candidate.start <= edit.start &&
99
+ edit.end <= candidate.end &&
100
+ (candidate.start !== edit.start || candidate.end !== edit.end)
101
+ ));
102
+ }
103
+
59
104
  function containerReplacementCoveredByInsertions(edit, edits) {
60
105
  if (edit.editKind !== 'replace' || edit.alreadyApplied) return false;
61
106
  const insertions = containedInsertions(edit, edits);
@@ -76,15 +121,30 @@ function containedInsertions(container, edits) {
76
121
  .sort((left, right) => left.start - right.start || (left.order ?? 0) - (right.order ?? 0));
77
122
  }
78
123
 
124
+ function sameSourcePath(left, right) {
125
+ const leftPath = left.targetSourcePath ?? left.sourcePath;
126
+ const rightPath = right.targetSourcePath ?? right.sourcePath;
127
+ return !leftPath || !rightPath || leftPath === rightPath;
128
+ }
129
+
79
130
  function duplicateEditKey(edit) {
80
- if (edit.editKind !== 'insert') return undefined;
131
+ if (edit.alreadyApplied) return undefined;
132
+ if (edit.editKind === 'insert') {
133
+ return [
134
+ 'insert',
135
+ edit.start,
136
+ edit.end,
137
+ edit.insertion?.mode,
138
+ edit.insertion?.anchorKey,
139
+ hashSemanticValue(edit.replacementSpanText ?? edit.replacement)
140
+ ].join(':');
141
+ }
81
142
  return [
82
- 'insert',
143
+ edit.editKind,
83
144
  edit.start,
84
145
  edit.end,
85
- edit.insertion?.mode,
86
- edit.insertion?.anchorKey,
87
- hashSemanticValue(edit.replacementSpanText ?? edit.replacement)
146
+ hashSemanticValue(edit.current),
147
+ hashSemanticValue(edit.replacement)
88
148
  ].join(':');
89
149
  }
90
150
 
@@ -4,7 +4,7 @@ import { semanticEditIdentityFields } from './semanticEditIdentityRecords.js';
4
4
 
5
5
  export function createSemanticTransformIdentityRecord(input = {}, options = {}) {
6
6
  const source = input?.transform ?? input;
7
- const merged = { ...source, ...options };
7
+ const merged = { ...source, ...definedEntries(options) };
8
8
  const editIdentity = semanticEditIdentityFields(merged);
9
9
  const semanticKey = firstString(merged.semanticKey, editIdentity.semanticKey);
10
10
  const transformKey = firstString(merged.transformKey, semanticTransformKey({ ...merged, semanticKey }));
@@ -32,6 +32,9 @@ export function createSemanticTransformIdentityRecord(input = {}, options = {})
32
32
  merged.transformContentHash,
33
33
  operationContentHash || editContentHash ? hashSemanticValue(compactRecord({ transformIdentityHash, operationContentHash, editContentHash })) : undefined
34
34
  );
35
+ const sourceMapIds = uniqueStrings([...strings(merged.sourceMapIds), ...strings(merged.sourceMapId), ...strings(merged.metadata?.sourceMapIds), ...strings(merged.metadata?.sourceMapId)]);
36
+ const sourceMapLinkIds = uniqueStrings([...strings(merged.sourceMapLinkIds), ...strings(merged.sourceMapLinkId), ...strings(merged.metadata?.sourceMapLinkIds), ...strings(merged.metadata?.sourceMapLinkId)]);
37
+ const sourceMapMappingIds = uniqueStrings([...strings(merged.sourceMapMappingIds), ...strings(merged.sourceMapMappingId), ...strings(merged.metadata?.sourceMapMappingIds), ...strings(merged.metadata?.sourceMapMappingId)]);
35
38
  const id = merged.id && source.kind === 'frontier.lang.semanticTransformIdentityRecord'
36
39
  ? merged.id
37
40
  : firstString(merged.id, `semantic_transform_${idFragment(firstString(transformKey, semanticKey, sourcePath, targetPath, 'record'))}`);
@@ -55,6 +58,9 @@ export function createSemanticTransformIdentityRecord(input = {}, options = {})
55
58
  transformIdentityHash,
56
59
  projectionIdentityHash,
57
60
  transformContentHash,
61
+ sourceMapIds,
62
+ sourceMapLinkIds,
63
+ sourceMapMappingIds,
58
64
  readiness: firstString(merged.readiness),
59
65
  confidence: typeof merged.confidence === 'number' ? merged.confidence : undefined,
60
66
  evidenceIds: uniqueStrings(merged.evidenceIds),
@@ -80,20 +86,29 @@ export function semanticTransformInputs(source = {}, options = {}) {
80
86
  ...array(options.semanticEditProjections ?? options.semanticEditProjection),
81
87
  ...array(source.semanticEditProjections ?? source.semanticEditProjection)
82
88
  ];
89
+ const scripts = [
90
+ ...array(options.semanticEditScripts ?? options.semanticEditScript),
91
+ ...array(source.semanticEditScripts ?? source.semanticEditScript)
92
+ ];
83
93
  return [
84
94
  ...array(options.semanticTransformIdentities ?? options.semanticTransformIdentity),
85
95
  ...array(source.semanticTransformIdentities ?? source.semanticTransformIdentity ?? source.semanticTransforms),
86
96
  ...array(source.index?.semanticTransformIdentities),
87
- ...deriveSemanticTransformIdentityRecords({ semanticEditProjections: projections }, { ...source, ...options })
97
+ ...deriveSemanticTransformIdentityRecords({ semanticEditProjections: projections, semanticEditScripts: scripts }, { ...source, ...options })
88
98
  ];
89
99
  }
90
100
 
91
101
  export function deriveSemanticTransformIdentityRecords(input = {}, options = {}) {
92
102
  const projections = semanticEditProjectionInputs(input);
93
- return uniqueRecords(projections.flatMap((projection, projectionIndex) => {
103
+ const projectionRecords = projections.flatMap((projection, projectionIndex) => {
94
104
  const edits = array(projection.edits).filter((edit) => edit && typeof edit === 'object');
95
105
  return edits.map((edit, editIndex) => transformRecordForProjectionEdit(edit, projection, input, options, projectionIndex, editIndex));
96
- }));
106
+ });
107
+ const scriptRecords = projections.length ? [] : semanticEditScriptInputs(input).flatMap((script, scriptIndex) => {
108
+ const operations = array(script.operations).filter((operation) => operation && typeof operation === 'object');
109
+ return operations.map((operation, operationIndex) => transformRecordForScriptOperation(operation, script, input, options, scriptIndex, operationIndex));
110
+ });
111
+ return uniqueRecords([...projectionRecords, ...scriptRecords]);
97
112
  }
98
113
 
99
114
  export function semanticTransformRecordIndex(records, source = {}) {
@@ -104,12 +119,18 @@ export function semanticTransformRecordIndex(records, source = {}) {
104
119
  semanticTransformIdentityHashes: uniqueStrings([...strings(source.semanticTransformIdentityHashes), ...strings(index.semanticTransformIdentityHashes), ...records.map((record) => record.transformIdentityHash)]),
105
120
  semanticTransformContentHashes: uniqueStrings([...strings(source.semanticTransformContentHashes), ...strings(index.semanticTransformContentHashes), ...records.map((record) => record.transformContentHash)]),
106
121
  projectionIdentityHashes: uniqueStrings([...strings(source.projectionIdentityHashes), ...strings(index.projectionIdentityHashes), ...records.map((record) => record.projectionIdentityHash)]),
122
+ transformBaseHashes: uniqueStrings([...strings(source.transformBaseHashes), ...strings(index.transformBaseHashes), ...records.map((record) => record.baseHash)]),
123
+ transformTargetHashes: uniqueStrings([...strings(source.transformTargetHashes), ...strings(index.transformTargetHashes), ...records.map((record) => record.targetHash)]),
107
124
  semanticTransformReadinesses: uniqueStrings([...strings(source.semanticTransformReadinesses), ...strings(index.semanticTransformReadinesses), ...records.map((record) => record.readiness)]),
108
125
  semanticTransformEvidenceIds: uniqueStrings([...strings(source.semanticTransformEvidenceIds), ...strings(index.semanticTransformEvidenceIds), ...records.flatMap((record) => record.evidenceIds)]),
109
126
  transformSourceLanguages: uniqueStrings([...strings(source.transformSourceLanguages), ...strings(index.transformSourceLanguages), ...records.map((record) => record.sourceLanguage)]),
110
127
  transformTargetLanguages: uniqueStrings([...strings(source.transformTargetLanguages), ...strings(index.transformTargetLanguages), ...records.map((record) => record.targetLanguage)]),
111
128
  transformSourcePaths: uniqueStrings([...strings(source.transformSourcePaths), ...strings(index.transformSourcePaths), ...records.map((record) => record.sourcePath)]),
112
- transformTargetPaths: uniqueStrings([...strings(source.transformTargetPaths), ...strings(index.transformTargetPaths), ...records.map((record) => record.targetPath)])
129
+ transformTargetPaths: uniqueStrings([...strings(source.transformTargetPaths), ...strings(index.transformTargetPaths), ...records.map((record) => record.targetPath)]),
130
+ transformCrossLanguages: uniqueStrings([...strings(source.transformCrossLanguages), ...strings(index.transformCrossLanguages), ...records.map(transformCrossLanguageFlag)]),
131
+ transformSourceMapIds: uniqueStrings([...strings(source.transformSourceMapIds), ...strings(index.transformSourceMapIds), ...records.flatMap((record) => record.sourceMapIds)]),
132
+ transformSourceMapLinkIds: uniqueStrings([...strings(source.transformSourceMapLinkIds), ...strings(index.transformSourceMapLinkIds), ...records.flatMap((record) => record.sourceMapLinkIds)]),
133
+ transformSourceMapMappingIds: uniqueStrings([...strings(source.transformSourceMapMappingIds), ...strings(index.transformSourceMapMappingIds), ...records.flatMap((record) => record.sourceMapMappingIds)])
113
134
  };
114
135
  }
115
136
 
@@ -121,14 +142,24 @@ export function semanticTransformSummary(index) {
121
142
  identityHashes: index.semanticTransformIdentityHashes,
122
143
  contentHashes: index.semanticTransformContentHashes,
123
144
  projectionIdentityHashes: index.projectionIdentityHashes,
145
+ baseHashes: index.transformBaseHashes,
146
+ targetHashes: index.transformTargetHashes,
124
147
  readinesses: index.semanticTransformReadinesses,
125
148
  evidenceIds: index.semanticTransformEvidenceIds,
126
149
  sourceLanguages: index.transformSourceLanguages,
127
150
  targetLanguages: index.transformTargetLanguages,
128
- targetPaths: index.transformTargetPaths
151
+ targetPaths: index.transformTargetPaths,
152
+ crossLanguages: index.transformCrossLanguages,
153
+ sourceMapIds: index.transformSourceMapIds,
154
+ sourceMapLinkIds: index.transformSourceMapLinkIds,
155
+ sourceMapMappingIds: index.transformSourceMapMappingIds
129
156
  });
130
157
  }
131
158
 
159
+ function transformCrossLanguageFlag(record) {
160
+ return record.sourceLanguage && record.targetLanguage && record.sourceLanguage !== record.targetLanguage ? 'true' : 'false';
161
+ }
162
+
132
163
  function semanticTransformKey(record) {
133
164
  const scope = record.semanticKey ?? (record.symbolName
134
165
  ? `${record.symbolKind ?? 'symbol'}:${record.symbolName}`
@@ -145,11 +176,19 @@ function semanticEditProjectionInputs(input) {
145
176
  ].filter((entry) => entry && typeof entry === 'object');
146
177
  }
147
178
 
179
+ function semanticEditScriptInputs(input) {
180
+ if (input.kind === 'frontier.lang.semanticEditScript') return [input];
181
+ return [
182
+ ...array(input.semanticEditScripts ?? input.semanticEditScript),
183
+ ...array(input.scripts ?? input.script)
184
+ ].filter((entry) => entry && typeof entry === 'object');
185
+ }
186
+
148
187
  function transformRecordForProjectionEdit(edit, projection, input, options, projectionIndex, editIndex) {
149
188
  const sourceLanguage = firstString(edit.sourceLanguage, edit.language, input.sourceLanguage, options.sourceLanguage, projection.sourceLanguage, projection.language);
150
- const targetLanguage = firstString(edit.targetLanguage, edit.projectedLanguage, input.targetLanguage, options.targetLanguage, projection.targetLanguage, projection.projectedLanguage, projection.language);
189
+ const targetLanguage = firstString(edit.targetLanguage, edit.projectedLanguage, input.targetLanguage, options.targetLanguage, projection.metadata?.targetLanguage, projection.targetLanguage, projection.projectedLanguage, projection.language);
151
190
  const sourcePath = firstString(edit.originalSourcePath, edit.sourcePath, input.sourcePath, options.sourcePath, projection.sourcePath);
152
- const targetPath = firstString(edit.targetPath, edit.targetSourcePath, input.targetPath, options.targetPath, projection.targetPath, projection.projectedSourcePath, projection.sourcePath);
191
+ const targetPath = firstString(edit.targetPath, edit.targetSourcePath, input.targetPath, options.targetPath, projection.metadata?.targetPath, projection.targetPath, projection.projectedSourcePath, projection.sourcePath);
153
192
  const transformId = [projection.id, edit.operationId, projectionIndex, editIndex].filter((entry) => entry !== undefined && entry !== null).join(':');
154
193
  return createSemanticTransformIdentityRecord(edit, {
155
194
  id: `semantic_transform_${idFragment(transformId)}`,
@@ -158,7 +197,10 @@ function transformRecordForProjectionEdit(edit, projection, input, options, proj
158
197
  sourcePath,
159
198
  targetPath,
160
199
  baseHash: firstString(edit.baseHash, projection.baseHash, input.baseHash, options.baseHash),
161
- targetHash: firstString(edit.targetHash, projection.projectedHash, projection.targetHash, input.targetHash, options.targetHash),
200
+ targetHash: firstString(edit.targetHash, projection.metadata?.targetHash, projection.projectedHash, projection.targetHash, input.targetHash, options.targetHash),
201
+ sourceMapIds: projection.metadata?.sourceMapIds,
202
+ sourceMapLinkIds: projection.metadata?.sourceMapLinkIds,
203
+ sourceMapMappingIds: projection.metadata?.sourceMapMappingIds,
162
204
  readiness: firstString(edit.readiness, projection.admission?.status, projection.status),
163
205
  evidenceIds: uniqueStrings([...strings(input.evidenceIds), ...strings(options.evidenceIds), ...strings(projection.evidenceIds), ...strings(edit.evidenceIds)]),
164
206
  metadata: compactRecord({
@@ -168,6 +210,39 @@ function transformRecordForProjectionEdit(edit, projection, input, options, proj
168
210
  });
169
211
  }
170
212
 
213
+ function transformRecordForScriptOperation(operation, script, input, options, scriptIndex, operationIndex) {
214
+ const targetRegion = operation.metadata?.targetRegion ?? {};
215
+ const transformId = [script.id, operation.id, scriptIndex, operationIndex].filter((entry) => entry !== undefined && entry !== null).join(':');
216
+ return createSemanticTransformIdentityRecord(operation, {
217
+ id: `semantic_transform_${idFragment(transformId)}`,
218
+ sourceLanguage: firstString(operation.anchor?.language, script.language, input.sourceLanguage, options.sourceLanguage),
219
+ targetLanguage: firstString(targetRegion.language, script.metadata?.targetLanguage, input.targetLanguage, options.targetLanguage),
220
+ sourcePath: firstString(operation.anchor?.sourcePath, script.sourcePath, input.sourcePath, options.sourcePath),
221
+ targetPath: firstString(targetRegion.sourcePath, script.metadata?.targetPath, input.targetPath, options.targetPath),
222
+ baseHash: firstString(operation.hashes?.baseSourceHash, script.baseHash, input.baseHash, options.baseHash),
223
+ targetHash: firstString(targetRegion.sourceHash, operation.hashes?.workerSourceHash, input.targetHash, options.targetHash),
224
+ sourceMapIds: script.metadata?.sourceProjectionHint?.sourceMapIds,
225
+ sourceMapLinkIds: operation.metadata?.sourceMapLinkIds,
226
+ sourceMapMappingIds: operation.metadata?.sourceMapMappingIds,
227
+ readiness: firstString(script.admission?.status, script.metadata?.sourceProjectionHint?.status),
228
+ evidenceIds: uniqueStrings([
229
+ ...strings(input.evidenceIds),
230
+ ...strings(options.evidenceIds),
231
+ ...array(script.evidence).map((record) => record?.id),
232
+ ...strings(operation.evidenceIds)
233
+ ]),
234
+ metadata: compactRecord({
235
+ sourceEditScriptId: script.id,
236
+ sourceEditOperationId: operation.id,
237
+ sourceProjectionHintId: script.metadata?.sourceProjectionHint?.id,
238
+ sourceMapIds: script.metadata?.sourceProjectionHint?.sourceMapIds,
239
+ sourceMapLinkIds: operation.metadata?.sourceMapLinkIds,
240
+ sourceMapMappingIds: operation.metadata?.sourceMapMappingIds,
241
+ reviewOnly: script.admission?.reviewRequired === true
242
+ })
243
+ });
244
+ }
245
+
171
246
  function uniqueRecords(records) {
172
247
  const seen = new Set();
173
248
  const result = [];
@@ -184,4 +259,5 @@ function array(value) { if (value === undefined || value === null) return []; re
184
259
  function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
185
260
  function firstString(...values) { return values.map((value) => value === undefined || value === null ? '' : String(value)).find(Boolean); }
186
261
  function uniqueStrings(values) { return uniqueRawStrings((values ?? []).filter((entry) => entry !== undefined && entry !== null && String(entry) !== '')); }
262
+ function definedEntries(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined)); }
187
263
  function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
@@ -0,0 +1,158 @@
1
+ import {
2
+ JsTsSafeMergeConflictCodes,
3
+ JsTsSafeMergeGateIds,
4
+ jsTsSafeMergeGateOrder
5
+ } from './js-ts-safe-merge-constants.js';
6
+ import { uniqueStrings } from './js-ts-semantic-merge-parse.js';
7
+
8
+ function mergeResult(status, sourceText, reasonCodes, regions, input, explicitPolicy) {
9
+ const conflicts = status === 'merged'
10
+ ? []
11
+ : reasonCodes.map((reason, index) => memberConflictForReason(reason, input, index));
12
+ const mergedRegions = regions.map((region) => ({
13
+ kind: region.kind,
14
+ name: region.name,
15
+ regionKind: region.policy.regionKind,
16
+ workerAddedKeys: region.workerAddedKeys,
17
+ headAddedKeys: region.headAddedKeys
18
+ }));
19
+ return {
20
+ kind: 'frontier.lang.jsTsSafeMemberMerge',
21
+ version: 1,
22
+ status,
23
+ sourceText,
24
+ reasonCodes,
25
+ conflicts,
26
+ gates: memberGates(conflicts),
27
+ admission: memberAdmission(status, conflicts),
28
+ mergedRegions,
29
+ summary: {
30
+ regions: mergedRegions.length,
31
+ workerAdditions: mergedRegions.reduce((total, region) => total + region.workerAddedKeys.length, 0),
32
+ headAdditions: mergedRegions.reduce((total, region) => total + region.headAddedKeys.length, 0),
33
+ appliedAdditions: status === 'merged' ? mergedRegions.reduce((total, region) => total + region.workerAddedKeys.length, 0) : 0,
34
+ conflicts: conflicts.length
35
+ },
36
+ metadata: { explicitPolicy }
37
+ };
38
+ }
39
+
40
+ function memberAdmission(status, conflicts) {
41
+ const reasonCodes = uniqueStrings(conflicts.map((conflict) => conflict.details.reasonCode ?? conflict.code));
42
+ if (status === 'merged') {
43
+ return {
44
+ status: 'auto-merge-candidate',
45
+ action: 'apply',
46
+ reviewRequired: false,
47
+ autoApplyCandidate: true,
48
+ reasonCodes: []
49
+ };
50
+ }
51
+ return {
52
+ status: 'blocked',
53
+ action: 'human-review',
54
+ reviewRequired: true,
55
+ autoApplyCandidate: false,
56
+ reasonCodes
57
+ };
58
+ }
59
+
60
+ function memberGates(conflicts) {
61
+ const blockedGateIds = new Set(conflicts.map((conflict) => conflict.gateId));
62
+ const reasonCodesByGate = new Map();
63
+ for (const conflict of conflicts) {
64
+ const gateReasonCodes = reasonCodesByGate.get(conflict.gateId) ?? [];
65
+ gateReasonCodes.push(conflict.details.reasonCode ?? conflict.code);
66
+ reasonCodesByGate.set(conflict.gateId, uniqueStrings(gateReasonCodes));
67
+ }
68
+ let blockedSeen = false;
69
+ return jsTsSafeMergeGateOrder.map((id) => {
70
+ const blocked = blockedGateIds.has(id);
71
+ const status = blocked ? 'blocked' : blockedSeen ? 'skipped' : 'passed';
72
+ if (blocked) blockedSeen = true;
73
+ return { id, status, reasonCodes: reasonCodesByGate.get(id) ?? [] };
74
+ });
75
+ }
76
+
77
+ function memberConflictForReason(reason, input, index) {
78
+ const reasonCode = blockedReasonCode(reason);
79
+ const code = conflictCodeForReason(reasonCode);
80
+ const gateId = gateIdForReason(reasonCode);
81
+ const details = reasonDetails(reason, reasonCode);
82
+ return {
83
+ code,
84
+ gateId,
85
+ message: memberConflictMessage(reasonCode, details),
86
+ side: details.side,
87
+ sourcePath: input.sourcePath,
88
+ details: { ...details, index }
89
+ };
90
+ }
91
+
92
+ function blockedReasonCode(reason) {
93
+ if (reason.includes('computed-key')) return 'computed-key';
94
+ if (reason.includes('spread-like-member') || reason.includes('spread-member')) return 'spread-like-member';
95
+ if (reason.includes('overload-collision')) return 'overload-collision';
96
+ if (reason.includes('type-alias-conflict')) return 'type-alias-conflict';
97
+ if (reason.includes('duplicate-added-key') || reason.includes('duplicate-key')) return 'duplicate-member-name';
98
+ if (reason.includes('existing-member-changed')) return 'changed-existing-member';
99
+ if (reason.includes('existing-member-removed')) return 'removed-existing-member';
100
+ if (reason.includes('existing-member-order-changed')) return 'existing-member-order-changed';
101
+ if (reason.includes('order-sensitive-region-kind')) return 'order-sensitive-region-kind';
102
+ if (reason.includes('non-policy-source-change')) return 'non-policy-source-change';
103
+ if (reason.includes('unterminated-container')) return 'malformed-syntax';
104
+ if (reason.includes('unsupported-')) return 'unsupported-js-ts-syntax';
105
+ if (reason.includes('missing-') || reason.includes('not-safe-listed') || reason.includes('not-declared')) return 'invalid-input';
106
+ if (reason.includes('ambiguous-container') || reason.includes('container-not-found')) return 'parser-or-ledger-loss';
107
+ return reason.split(':')[0] || 'member-merge-blocked';
108
+ }
109
+
110
+ function conflictCodeForReason(reasonCode) {
111
+ if (reasonCode === 'computed-key') return JsTsSafeMergeConflictCodes.computedKey;
112
+ if (reasonCode === 'overload-collision') return JsTsSafeMergeConflictCodes.unsupportedOverload;
113
+ if (reasonCode === 'type-alias-conflict') return JsTsSafeMergeConflictCodes.typeAliasConflict;
114
+ if (reasonCode === 'duplicate-member-name') return JsTsSafeMergeConflictCodes.duplicateName;
115
+ if (reasonCode === 'changed-existing-member' || reasonCode === 'removed-existing-member' || reasonCode === 'non-policy-source-change') {
116
+ return JsTsSafeMergeConflictCodes.changedExistingDeclaration;
117
+ }
118
+ if (reasonCode === 'existing-member-order-changed' || reasonCode === 'order-sensitive-region-kind') return JsTsSafeMergeConflictCodes.topLevelOrderChanged;
119
+ if (reasonCode === 'malformed-syntax') return JsTsSafeMergeConflictCodes.malformedSyntax;
120
+ if (reasonCode === 'invalid-input') return JsTsSafeMergeConflictCodes.invalidInput;
121
+ return JsTsSafeMergeConflictCodes.parserLedgerLoss;
122
+ }
123
+
124
+ function gateIdForReason(reasonCode) {
125
+ if (reasonCode === 'duplicate-member-name' || reasonCode === 'overload-collision') return JsTsSafeMergeGateIds.uniqueNames;
126
+ if (reasonCode === 'changed-existing-member' || reasonCode === 'removed-existing-member' || reasonCode === 'non-policy-source-change') {
127
+ return JsTsSafeMergeGateIds.stableExistingDeclarations;
128
+ }
129
+ if (reasonCode === 'existing-member-order-changed' || reasonCode === 'order-sensitive-region-kind') return JsTsSafeMergeGateIds.preserveBaseOrder;
130
+ return JsTsSafeMergeGateIds.parseLedger;
131
+ }
132
+
133
+ function reasonDetails(reason, reasonCode) {
134
+ const parts = String(reason).split(':');
135
+ const details = { reason, reasonCode };
136
+ if (['base', 'worker', 'head'].includes(parts[1])) details.side = parts[1];
137
+ if (['base', 'worker', 'head'].includes(parts[2])) details.side = parts[2];
138
+ const kind = parts.find((part) => ['interface', 'type', 'class', 'object'].includes(part));
139
+ if (kind) details.regionKind = kind;
140
+ if (reasonCode === 'duplicate-member-name' || reasonCode === 'overload-collision') details.memberName = parts.at(-3) ?? parts.at(-1);
141
+ if (reasonCode === 'changed-existing-member' || reasonCode === 'removed-existing-member') details.memberName = parts.at(-3);
142
+ return details;
143
+ }
144
+
145
+ function memberConflictMessage(reasonCode, details) {
146
+ if (reasonCode === 'computed-key') return 'Computed member keys are not safe for automatic member merge.';
147
+ if (reasonCode === 'spread-like-member') return 'Spread-like member syntax makes object member merge ambiguous.';
148
+ if (reasonCode === 'overload-collision') return 'Member overloads or duplicate method signatures collide in an unordered member region.';
149
+ if (reasonCode === 'changed-existing-member') return 'An existing member changed inside a safe member-addition region.';
150
+ if (reasonCode === 'removed-existing-member') return 'An existing member was removed inside a safe member-addition region.';
151
+ if (reasonCode === 'duplicate-member-name') return 'Worker and head member additions contain the same member key.';
152
+ if (reasonCode === 'existing-member-order-changed') return 'A side changed the order of existing members in a non-semantic member region.';
153
+ return `JS/TS member merge blocked: ${details.reason}`;
154
+ }
155
+
156
+ export {
157
+ mergeResult
158
+ };