@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
@@ -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
+ };
@@ -0,0 +1,202 @@
1
+ import {
2
+ applyMemberAdditions,
3
+ canonicalizeSourceBodies,
4
+ findContainer,
5
+ normalizeKind,
6
+ normalizeMemberText,
7
+ parseMembers,
8
+ uniqueStrings
9
+ } from './js-ts-semantic-merge-parse.js';
10
+ import { mergeResult } from './js-ts-safe-member-merge-result.js';
11
+
12
+ const NonSemanticRegionKinds = new Set(['property', 'type', 'config', 'content']);
13
+ const OrderSensitiveRegionKinds = new Set(['body', 'call', 'controlFlow', 'effect', 'import', 'mutation', 'route']);
14
+
15
+ function safeMergeJsTsMembers(input = {}) {
16
+ return mergeJsTsSafeMemberAdditions(input);
17
+ }
18
+
19
+ function mergeJsTsSafeMemberAdditions(input = {}) {
20
+ const baseSourceText = input.baseSourceText;
21
+ const workerSourceText = input.workerSourceText;
22
+ const headSourceText = input.headSourceText;
23
+ const reasonCodes = [];
24
+ if (typeof baseSourceText !== 'string') reasonCodes.push('missing-base-source-text');
25
+ if (typeof workerSourceText !== 'string') reasonCodes.push('missing-worker-source-text');
26
+ if (typeof headSourceText !== 'string') reasonCodes.push('missing-head-source-text');
27
+ const policyRegions = normalizePolicyRegions(input.policy ?? input.mergePolicy ?? input);
28
+ if (!policyRegions.length) reasonCodes.push('missing-unordered-region-policy');
29
+ const preparedRegions = [];
30
+ for (const region of policyRegions) {
31
+ const policyReasons = validatePolicyRegion(region);
32
+ reasonCodes.push(...policyReasons);
33
+ if (policyReasons.length) continue;
34
+ if (typeof baseSourceText !== 'string' || typeof workerSourceText !== 'string' || typeof headSourceText !== 'string') continue;
35
+ const prepared = prepareRegion({ region, baseSourceText, workerSourceText, headSourceText });
36
+ reasonCodes.push(...prepared.reasonCodes);
37
+ if (prepared.ok) preparedRegions.push(prepared.value);
38
+ }
39
+ if (typeof baseSourceText === 'string' && typeof workerSourceText === 'string' && preparedRegions.length) {
40
+ const canonicalWorker = canonicalizeSourceBodies(workerSourceText, preparedRegions, 'worker');
41
+ if (canonicalWorker !== baseSourceText) reasonCodes.push('non-policy-source-change:worker');
42
+ }
43
+ if (typeof baseSourceText === 'string' && typeof headSourceText === 'string' && preparedRegions.length) {
44
+ const canonicalHead = canonicalizeSourceBodies(headSourceText, preparedRegions, 'head');
45
+ if (canonicalHead !== baseSourceText) reasonCodes.push('non-policy-source-change:head');
46
+ }
47
+ const uniqueReasons = uniqueStrings(reasonCodes);
48
+ const explicitPolicy = policyRegions.length > 0;
49
+ if (uniqueReasons.length) return mergeResult('rejected', undefined, uniqueReasons, preparedRegions, input, explicitPolicy);
50
+ const sourceText = applyMemberAdditions(headSourceText, preparedRegions);
51
+ return mergeResult('merged', sourceText, [], preparedRegions, input, explicitPolicy);
52
+ }
53
+
54
+ function normalizePolicyRegions(policy) {
55
+ const direct = Array.isArray(policy) ? policy : undefined;
56
+ const regions = direct
57
+ ?? policy?.unorderedRegions
58
+ ?? policy?.unorderedMemberRegions
59
+ ?? policy?.safeList
60
+ ?? policy?.safeMembers
61
+ ?? [];
62
+ return Array.isArray(regions) ? regions : [];
63
+ }
64
+
65
+ function validatePolicyRegion(region) {
66
+ const reasonCodes = [];
67
+ const kind = normalizeKind(region?.kind);
68
+ if (!kind || !['interface', 'type', 'class', 'object'].includes(kind)) reasonCodes.push('unsupported-region-kind');
69
+ if (!region?.name || typeof region.name !== 'string') reasonCodes.push('missing-region-name');
70
+ if (!regionDeclaresNonSemanticOrder(region)) reasonCodes.push('region-not-declared-non-semantic');
71
+ const regionKind = region?.regionKind;
72
+ if (regionKind && OrderSensitiveRegionKinds.has(regionKind)) reasonCodes.push(`order-sensitive-region-kind:${regionKind}`);
73
+ if (kind === 'object' && (!regionKind || !NonSemanticRegionKinds.has(regionKind))) reasonCodes.push('object-region-kind-not-safe-listed');
74
+ return reasonCodes;
75
+ }
76
+
77
+ function regionDeclaresNonSemanticOrder(region) {
78
+ return region?.order === 'non-semantic'
79
+ || region?.ordering === 'non-semantic'
80
+ || region?.nonSemanticOrder === true
81
+ || region?.orderSensitive === false;
82
+ }
83
+
84
+ function prepareRegion(input) {
85
+ const kind = normalizeKind(input.region.kind);
86
+ const base = findContainer(input.baseSourceText, input.region);
87
+ const worker = findContainer(input.workerSourceText, input.region);
88
+ const head = findContainer(input.headSourceText, input.region);
89
+ const reasonCodes = [];
90
+ for (const [side, match] of [['base', base], ['worker', worker], ['head', head]]) {
91
+ if (match.reasonCodes.length) reasonCodes.push(...match.reasonCodes.map((reason) => `${reason}:${side}:${kind}:${input.region.name}`));
92
+ }
93
+ if (reasonCodes.length) return { ok: false, reasonCodes };
94
+ const baseMembers = parseMembers(base.value.body, kind);
95
+ const workerMembers = parseMembers(worker.value.body, kind);
96
+ const headMembers = parseMembers(head.value.body, kind);
97
+ reasonCodes.push(...regionParseReasons(input.region, 'base', baseMembers));
98
+ reasonCodes.push(...regionParseReasons(input.region, 'worker', workerMembers));
99
+ reasonCodes.push(...regionParseReasons(input.region, 'head', headMembers));
100
+ reasonCodes.push(...duplicateReasons(input.region, 'base', baseMembers.members));
101
+ reasonCodes.push(...duplicateReasons(input.region, 'worker', workerMembers.members));
102
+ reasonCodes.push(...duplicateReasons(input.region, 'head', headMembers.members));
103
+ if (reasonCodes.length) return { ok: false, reasonCodes };
104
+ const baseByKey = membersByKey(baseMembers.members);
105
+ const workerByKey = membersByKey(workerMembers.members);
106
+ const headByKey = membersByKey(headMembers.members);
107
+ reasonCodes.push(...existingMemberReasons(input.region, 'worker', baseMembers.members, workerMembers.members, workerByKey));
108
+ reasonCodes.push(...existingMemberReasons(input.region, 'head', baseMembers.members, headMembers.members, headByKey));
109
+ const workerAddedKeys = workerMembers.members.map((member) => member.key).filter((key) => !baseByKey.has(key));
110
+ const headAddedKeys = headMembers.members.map((member) => member.key).filter((key) => !baseByKey.has(key));
111
+ reasonCodes.push(...duplicateAddedReasons(input.region, workerAddedKeys, workerByKey, headByKey));
112
+ if (reasonCodes.length) return { ok: false, reasonCodes };
113
+ return {
114
+ ok: true,
115
+ reasonCodes: [],
116
+ value: {
117
+ policy: input.region,
118
+ kind,
119
+ name: input.region.name,
120
+ base: base.value,
121
+ worker: worker.value,
122
+ head: head.value,
123
+ baseMembers: baseMembers.members,
124
+ workerMembers: workerMembers.members,
125
+ headMembers: headMembers.members,
126
+ workerAddedKeys,
127
+ headAddedKeys,
128
+ workerAddedMembers: workerMembers.members.filter((member) => workerAddedKeys.includes(member.key))
129
+ }
130
+ };
131
+ }
132
+
133
+ function regionParseReasons(region, side, parsed) {
134
+ return parsed.reasonCodes.map((reason) => regionReason(region, `${reason}:${side}`));
135
+ }
136
+
137
+ function duplicateReasons(region, side, members) {
138
+ const seen = new Map();
139
+ const duplicateGroups = new Map();
140
+ for (const member of members) {
141
+ const group = seen.get(member.key);
142
+ if (group) {
143
+ group.push(member);
144
+ duplicateGroups.set(member.key, group);
145
+ } else {
146
+ seen.set(member.key, [member]);
147
+ }
148
+ }
149
+ return [...duplicateGroups].map(([key, group]) => {
150
+ const reason = group.some(isOverloadLikeMember) ? `overload-collision:${side}:${key}` : `duplicate-key:${side}:${key}`;
151
+ return regionReason(region, reason);
152
+ });
153
+ }
154
+
155
+ function existingMemberReasons(region, side, baseMembers, sideMembers, sideByKey) {
156
+ const reasonCodes = [];
157
+ const sideBaseOrder = sideMembers.map((member) => member.key).filter((key) => baseMembers.some((baseMember) => baseMember.key === key));
158
+ const baseOrder = baseMembers.map((member) => member.key);
159
+ if (sideBaseOrder.join('\u0000') !== baseOrder.join('\u0000')) {
160
+ reasonCodes.push(regionReason(region, `existing-member-order-changed:${side}`));
161
+ }
162
+ for (const baseMember of baseMembers) {
163
+ const sideMember = sideByKey.get(baseMember.key);
164
+ if (!sideMember) {
165
+ reasonCodes.push(regionReason(region, `existing-member-removed:${side}:${baseMember.key}`));
166
+ continue;
167
+ }
168
+ if (normalizeMemberText(sideMember.text) !== normalizeMemberText(baseMember.text)) {
169
+ reasonCodes.push(regionReason(region, `existing-member-changed:${side}:${baseMember.key}`));
170
+ }
171
+ }
172
+ return reasonCodes;
173
+ }
174
+
175
+ function duplicateAddedReasons(region, workerAddedKeys, workerByKey, headByKey) {
176
+ const duplicateAddedKeys = workerAddedKeys.filter((key) => headByKey.has(key));
177
+ return duplicateAddedKeys.map((key) => {
178
+ const workerMember = workerByKey.get(key);
179
+ const headMember = headByKey.get(key);
180
+ const reason = isOverloadLikeMember(workerMember) || isOverloadLikeMember(headMember)
181
+ ? `overload-collision:worker-head:${key}`
182
+ : `duplicate-added-key:${key}`;
183
+ return regionReason(region, reason);
184
+ });
185
+ }
186
+
187
+ function isOverloadLikeMember(member) {
188
+ return member?.memberKind === 'method' || member?.memberKind === 'constructor';
189
+ }
190
+
191
+ function membersByKey(members) {
192
+ return new Map(members.map((member) => [member.key, member]));
193
+ }
194
+
195
+ function regionReason(region, reason) {
196
+ return `${reason}:${normalizeKind(region.kind)}:${region.name}`;
197
+ }
198
+
199
+ export {
200
+ mergeJsTsSafeMemberAdditions,
201
+ safeMergeJsTsMembers
202
+ };