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

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 (47) hide show
  1. package/dist/declarations/bidirectional-target-change-evidence.d.ts +299 -0
  2. package/dist/declarations/bidirectional-target-change.d.ts +19 -120
  3. package/dist/declarations/import-adapter-core.d.ts +6 -0
  4. package/dist/declarations/native-project-admission.d.ts +43 -22
  5. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +24 -0
  6. package/dist/declarations/semantic-edit-script.d.ts +20 -15
  7. package/dist/declarations/semantic-lineage.d.ts +3 -21
  8. package/dist/declarations/semantic-merge-candidates.d.ts +39 -0
  9. package/dist/declarations/semantic-sidecar-admission.d.ts +14 -0
  10. package/dist/declarations/semantic-sidecar.d.ts +12 -14
  11. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +200 -0
  12. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +62 -17
  13. package/dist/internal/index-impl/createLightweightNativeImport.js +9 -1
  14. package/dist/internal/index-impl/createNativeSourcePreservation.js +16 -1
  15. package/dist/internal/index-impl/createProjectImportAdmissionRecord.js +151 -1
  16. package/dist/internal/index-impl/createSemanticImportSidecar.js +5 -0
  17. package/dist/internal/index-impl/createSemanticImportSidecarAdmission.js +29 -11
  18. package/dist/internal/index-impl/importNativeSource.js +14 -14
  19. package/dist/internal/index-impl/nativeChangeProjectionEndpoint.js +56 -16
  20. package/dist/internal/index-impl/nativeImportSemanticIndex.js +33 -0
  21. package/dist/internal/index-impl/projectImportAdmissionMergeScore.js +26 -74
  22. package/dist/internal/index-impl/projectImportAdmissionProjectionCoverage.js +74 -0
  23. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +39 -13
  24. package/dist/internal/index-impl/replaySemanticEditProjection.js +65 -23
  25. package/dist/internal/index-impl/semanticEditInsertionAnchors.js +8 -5
  26. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +167 -0
  27. package/dist/internal/index-impl/semanticEditSourceRanges.js +94 -15
  28. package/dist/internal/index-impl/semanticHistoryLineageResolution.js +21 -2
  29. package/dist/internal/index-impl/semanticLineageHashEvidence.js +97 -0
  30. package/dist/internal/index-impl/semanticLineageInferenceMatching.js +8 -0
  31. package/dist/internal/index-impl/semanticLineageResolutionRecords.js +18 -1
  32. package/dist/internal/index-impl/semanticMergeCandidateRecords.js +22 -2
  33. package/dist/internal/index-impl/semanticMergeCandidateScoreFacets.js +221 -0
  34. package/dist/internal/index-impl/semanticPatchBundleOverlaps.js +23 -1
  35. package/dist/internal/index-impl/sourcePreservationFromProjectionContext.js +9 -2
  36. package/dist/native-import-language-profiles.js +10 -2
  37. package/dist/native-region-scanner-js-helpers.js +8 -2
  38. package/dist/native-region-scanner-js-imports.js +7 -0
  39. package/dist/native-region-scanner-js.js +4 -4
  40. package/dist/native-region-scanner.js +2 -1
  41. package/dist/semantic-import-regions.js +18 -5
  42. package/dist/semantic-import-sidecar-admission-types.d.ts +14 -0
  43. package/dist/semantic-import-sidecar-entry.js +151 -7
  44. package/dist/semantic-import-sidecar-types.d.ts +18 -13
  45. package/dist/semantic-import-source-preservation-utils.js +55 -0
  46. package/dist/semantic-import-source-preservation.js +98 -3
  47. package/package.json +1 -1
@@ -0,0 +1,221 @@
1
+ import { uniqueStrings } from '../../native-import-utils.js';
2
+
3
+ const scoreFacetWeights = Object.freeze({ ownership: 18, staleStatus: 20, testEvidence: 18, overlap: 18, size: 10, semanticSidecarQuality: 16 });
4
+
5
+ export function semanticMergeCandidateScoreFacets(input) {
6
+ const components = {
7
+ ownership: ownershipFacet(input),
8
+ staleStatus: staleStatusFacet(input),
9
+ testEvidence: testEvidenceFacet(input),
10
+ overlap: overlapFacet(input),
11
+ size: sizeFacet(input),
12
+ semanticSidecarQuality: semanticSidecarQualityFacet(input)
13
+ };
14
+ const weightedTotal = Object.values(components).reduce((sum, component) => sum + component.weightedScore, 0);
15
+ const weightTotal = Object.values(components).reduce((sum, component) => sum + component.weight, 0);
16
+ const value = roundScore(weightTotal ? weightedTotal * 100 / weightTotal : 0);
17
+ const weakFacets = Object.values(components).filter((component) => component.status === 'weak').map((component) => component.key);
18
+ const blockedFacets = Object.values(components).filter((component) => component.status === 'blocked').map((component) => component.key);
19
+ const lowestScore = Math.min(...Object.values(components).map((component) => component.score));
20
+ return {
21
+ schema: 'frontier.lang.semanticMergeCandidateScoreFacets.v1',
22
+ version: 1,
23
+ higherIsBetter: true,
24
+ value,
25
+ risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
26
+ components,
27
+ summary: {
28
+ value,
29
+ risk: value < 50 || blockedFacets.length ? 'high' : value < 80 || weakFacets.length ? 'medium' : 'low',
30
+ lowestScore,
31
+ weakFacets,
32
+ blockedFacets,
33
+ availableFacets: Object.values(components).filter((component) => component.signals.available !== false).map((component) => component.key)
34
+ }
35
+ };
36
+ }
37
+
38
+ function ownershipFacet(input) {
39
+ const regions = input.changedSemanticRegions;
40
+ const keyedRegions = regions.filter((region) => region.key || region.conflictKey);
41
+ const kindedRegions = regions.filter((region) => region.regionKind);
42
+ const spannedRegions = regions.filter((region) => region.sourceSpan);
43
+ const score = regions.length
44
+ ? roundScore(keyedRegions.length * 70 / regions.length + kindedRegions.length * 15 / regions.length + spannedRegions.length * 15 / regions.length)
45
+ : input.conflictKeys.length ? 45 : 70;
46
+ return scoreFacet('ownership', score, [
47
+ ...(regions.length === 0 ? ['missing-changed-semantic-regions'] : []),
48
+ ...(regions.length && keyedRegions.length < regions.length ? ['missing-ownership-keys'] : [])
49
+ ], {
50
+ changedSemanticRegions: regions.length,
51
+ keyedRegions: keyedRegions.length,
52
+ regionKinds: uniqueStrings(regions.map((region) => region.regionKind)),
53
+ conflictKeys: input.conflictKeys,
54
+ ownershipKeys: uniqueStrings(regions.map((region) => region.key))
55
+ });
56
+ }
57
+
58
+ function staleStatusFacet(input) {
59
+ const staleEvidence = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale' || record?.metadata?.stale === true);
60
+ const sourceHashVerified = [
61
+ input.source?.metadata?.sourceHashVerified,
62
+ input.source?.before?.metadata?.sourceHashVerified,
63
+ input.source?.after?.metadata?.sourceHashVerified,
64
+ input.candidate?.metadata?.sourceHashVerified,
65
+ input.patch?.metadata?.sourceHashVerified
66
+ ].filter((value) => value !== undefined);
67
+ const staleProofs = semanticSidecarQuality(input)?.proofSummary?.stale ?? 0;
68
+ const sourceHashStale = sourceHashVerified.includes(false);
69
+ const score = sourceHashStale ? 0 : staleEvidence.length ? 35 : staleProofs > 0 ? 55 : 100;
70
+ return scoreFacet('staleStatus', score, [
71
+ ...(sourceHashStale ? ['stale-source-hash'] : []),
72
+ ...(staleEvidence.length ? ['stale-evidence'] : []),
73
+ ...(staleProofs > 0 ? ['stale-sidecar-proof-obligations'] : [])
74
+ ], {
75
+ staleEvidenceIds: staleEvidence.map((record) => record.id).filter(Boolean),
76
+ staleProofObligations: staleProofs,
77
+ sourceHashVerified
78
+ });
79
+ }
80
+
81
+ function testEvidenceFacet(input) {
82
+ const failed = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'failed');
83
+ const stale = input.evidenceRecords.filter((record) => evidenceStatus(record) === 'stale');
84
+ const pending = input.evidenceRecords.filter((record) => ['pending', 'assumed', 'unknown'].includes(evidenceStatus(record)));
85
+ const passed = input.evidenceRecords.filter((record) => ['passed', 'ok', 'success'].includes(evidenceStatus(record)));
86
+ const score = failed.length ? 0 : stale.length ? 45 : pending.length ? 65 : input.proofIds.length ? 100 : input.evidenceIds.length ? 82 : 35;
87
+ return scoreFacet('testEvidence', score, [
88
+ ...(failed.length ? ['failed-evidence'] : []),
89
+ ...(stale.length ? ['stale-evidence'] : []),
90
+ ...(pending.length ? ['pending-evidence'] : []),
91
+ ...(!input.evidenceIds.length && !input.proofIds.length ? ['missing-evidence'] : [])
92
+ ], {
93
+ evidenceIds: input.evidenceIds,
94
+ proofIds: input.proofIds,
95
+ evidenceRecords: input.evidenceRecords.length,
96
+ passedEvidenceIds: passed.map((record) => record.id).filter(Boolean),
97
+ failedEvidenceIds: failed.map((record) => record.id).filter(Boolean),
98
+ staleEvidenceIds: stale.map((record) => record.id).filter(Boolean),
99
+ pendingEvidenceIds: pending.map((record) => record.id).filter(Boolean)
100
+ });
101
+ }
102
+
103
+ function overlapFacet(input) {
104
+ const high = input.overlaps.filter((overlap) => overlap.risk === 'high').length;
105
+ const medium = input.overlaps.filter((overlap) => overlap.risk === 'medium').length;
106
+ const score = clampScore(100 - high * 45 - medium * 25 - Math.max(0, input.overlaps.length - high - medium) * 15);
107
+ return scoreFacet('overlap', score, [
108
+ ...(input.overlaps.length ? ['overlapping-semantic-regions'] : [])
109
+ ], {
110
+ overlaps: input.overlaps.length,
111
+ highRiskOverlaps: high,
112
+ mediumRiskOverlaps: medium,
113
+ conflictKeys: uniqueStrings(input.overlaps.flatMap((overlap) => overlap.conflictKeys ?? [])),
114
+ byKind: countBy(input.overlaps.map((overlap) => overlap.overlapKind))
115
+ });
116
+ }
117
+
118
+ function sizeFacet(input) {
119
+ const operationCount = array(input.candidate?.operations ?? input.patch?.operations).length;
120
+ const changedSemanticRegions = input.changedSemanticRegions.length;
121
+ const conflictKeys = input.conflictKeys.length;
122
+ const score = clampScore(100 - Math.max(0, changedSemanticRegions - 3) * 8 - Math.max(0, operationCount - 3) * 4 - Math.max(0, conflictKeys - 4) * 3);
123
+ return scoreFacet('size', score, [
124
+ ...(changedSemanticRegions > 3 ? ['large-changed-region-set'] : []),
125
+ ...(operationCount > 3 ? ['large-operation-set'] : []),
126
+ ...(conflictKeys > 4 ? ['many-conflict-keys'] : [])
127
+ ], { changedSemanticRegions, operationCount, conflictKeys });
128
+ }
129
+
130
+ function semanticSidecarQualityFacet(input) {
131
+ const quality = semanticSidecarQuality(input);
132
+ if (!quality) return scoreFacet('semanticSidecarQuality', 100, [], { available: false });
133
+ const proofSummary = quality.proofSummary ?? {};
134
+ const warningCodes = uniqueStrings([...(quality.warnings ?? []).map((warning) => warning.code), ...strings(quality.expectedMissingReasonCodes)]);
135
+ const reviewProofs = (proofSummary.open ?? 0) + (proofSummary.stale ?? 0) + (proofSummary.assumed ?? 0) + (proofSummary.externalToolRequired ?? 0) + (proofSummary.unknown ?? 0);
136
+ const score = clampScore(
137
+ (quality.imported === false || quality.selected === false ? 25 : 100)
138
+ - (quality.eligible === false ? 35 : 0)
139
+ - Math.min(35, (quality.warningCount ?? warningCodes.length ?? 0) * 7)
140
+ - Math.min(40, (proofSummary.failed ?? 0) * 20)
141
+ - Math.min(25, reviewProofs * 5)
142
+ );
143
+ return scoreFacet('semanticSidecarQuality', score, [
144
+ ...(quality.imported === false || quality.selected === false ? ['semantic-sidecar-not-imported'] : []),
145
+ ...(quality.eligible === false ? ['semantic-sidecar-not-eligible'] : []),
146
+ ...warningCodes
147
+ ], {
148
+ available: true,
149
+ expected: quality.expected,
150
+ expectedSatisfied: quality.expectedSatisfied,
151
+ selected: quality.selected,
152
+ imported: quality.imported,
153
+ eligible: quality.eligible,
154
+ importCount: quality.importCount,
155
+ symbolCount: quality.symbolCount,
156
+ ownershipRegionCount: quality.ownershipRegionCount,
157
+ patchHintCount: quality.patchHintCount,
158
+ evidenceCount: quality.evidenceCount,
159
+ warningCount: quality.warningCount ?? warningCodes.length,
160
+ warningCodes,
161
+ proofSummary
162
+ });
163
+ }
164
+
165
+ function semanticSidecarQuality(input) {
166
+ return [
167
+ input.candidate?.semanticSidecarQuality,
168
+ input.candidate?.sidecarQuality,
169
+ input.candidate?.semanticSidecar?.quality,
170
+ input.candidate?.sidecar?.quality,
171
+ input.candidate?.metadata?.semanticSidecarQuality,
172
+ input.candidate?.metadata?.sidecarQuality,
173
+ input.source?.semanticSidecarQuality,
174
+ input.source?.sidecarQuality,
175
+ input.source?.semanticSidecar?.quality,
176
+ input.source?.sidecar?.quality,
177
+ input.source?.metadata?.semanticSidecarQuality,
178
+ input.source?.metadata?.sidecarQuality,
179
+ input.patch?.semanticSidecarQuality,
180
+ input.patch?.metadata?.semanticSidecarQuality
181
+ ].find(looksLikeSemanticSidecarQuality);
182
+ }
183
+
184
+ function looksLikeSemanticSidecarQuality(value) {
185
+ return value && typeof value === 'object' && (
186
+ value.schema === 'frontier.lang.semanticSidecarQuality.v1'
187
+ || value.eligible !== undefined
188
+ || value.imported !== undefined
189
+ || value.proofSummary !== undefined
190
+ || value.warningCount !== undefined
191
+ );
192
+ }
193
+
194
+ function scoreFacet(key, score, reasonCodes, signals) {
195
+ const normalizedScore = clampScore(score);
196
+ const weight = scoreFacetWeights[key] ?? 1;
197
+ return {
198
+ key,
199
+ score: normalizedScore,
200
+ weight,
201
+ weightedScore: roundScore(normalizedScore * weight / 100),
202
+ status: facetStatus(normalizedScore),
203
+ reasonCodes: uniqueStrings(reasonCodes),
204
+ signals: compactRecord(signals)
205
+ };
206
+ }
207
+
208
+ function facetStatus(score) {
209
+ if (score <= 0) return 'blocked';
210
+ if (score < 50) return 'weak';
211
+ if (score < 80) return 'partial';
212
+ return 'strong';
213
+ }
214
+
215
+ function evidenceStatus(record) { return String(record?.status ?? record?.metadata?.status ?? '').toLowerCase(); }
216
+ function countBy(values) { const counts = {}; for (const value of values ?? []) { const key = String(value ?? 'unknown'); counts[key] = (counts[key] ?? 0) + 1; } return counts; }
217
+ function clampScore(value) { return Math.max(0, Math.min(100, roundScore(value))); }
218
+ function roundScore(value) { return Math.round((Number.isFinite(value) ? value : 0) * 100) / 100; }
219
+ function array(value) { if (value === undefined || value === null) return []; return Array.isArray(value) ? value : [value]; }
220
+ function strings(value) { return array(value).map((entry) => String(entry ?? '')).filter(Boolean); }
221
+ function compactRecord(value) { return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0))); }
@@ -90,7 +90,7 @@ export function querySemanticPatchBundleOverlaps(records,query={}){
90
90
  }
91
91
 
92
92
  function sharedIndex(left,right,options){
93
- return{
93
+ const shared={
94
94
  operationContentHashes:intersect(left.operationContentHashes,right.operationContentHashes),
95
95
  editContentHashes:intersect(left.editContentHashes,right.editContentHashes),
96
96
  semanticEditKeys:intersect(left.semanticEditKeys,right.semanticEditKeys),
@@ -110,6 +110,28 @@ function sharedIndex(left,right,options){
110
110
  baseHashes:intersect(left.baseHashes,right.baseHashes),
111
111
  targetHashes:intersect(left.targetHashes,right.targetHashes)
112
112
  };
113
+ const scopedEdit=hasSharedEditScope(shared);
114
+ const scopedSource=hasSharedSourceScope(shared);
115
+ return{
116
+ ...shared,
117
+ operationContentHashes:scopedEdit?shared.operationContentHashes:[],
118
+ editContentHashes:scopedEdit?shared.editContentHashes:[],
119
+ semanticEditKeys:scopedSource?shared.semanticEditKeys:[],
120
+ semanticIdentityHashes:scopedSource?shared.semanticIdentityHashes:[],
121
+ semanticEditReplayCurrentHashes:scopedSource?shared.semanticEditReplayCurrentHashes:[],
122
+ semanticEditReplayOutputHashes:scopedEdit?shared.semanticEditReplayOutputHashes:[],
123
+ semanticTransformContentHashes:shared.projectionIdentityHashes.length?shared.semanticTransformContentHashes:[],
124
+ semanticTransformIdentityHashes:shared.projectionIdentityHashes.length?shared.semanticTransformIdentityHashes:[]
125
+ };
126
+ }
127
+
128
+ function hasSharedEditScope(shared){
129
+ return Boolean(shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length
130
+ ||(shared.sourcePaths.length&&(shared.semanticEditKeys.length||shared.semanticIdentityHashes.length)));
131
+ }
132
+
133
+ function hasSharedSourceScope(shared){
134
+ return Boolean(shared.sourcePaths.length||shared.regionKeys.length||shared.conflictKeys.length||shared.sourceIdentityHashes.length);
113
135
  }
114
136
 
115
137
  function overlapAdmission(shared,{leftIndex,rightIndex,options}){
@@ -1,5 +1,12 @@
1
1
  export function sourcePreservationFromProjectionContext(context) {
2
- return context.nativeSource?.metadata?.sourcePreservation
2
+ return context.sourcePreservation
3
+ ?? context.metadata?.sourcePreservation
4
+ ?? context.importResult?.metadata?.sourcePreservation
5
+ ?? context.importResult?.nativeSource?.metadata?.sourcePreservation
6
+ ?? context.importResult?.nativeAst?.metadata?.sourcePreservation
7
+ ?? context.nativeSource?.metadata?.sourcePreservation
3
8
  ?? context.nativeAst?.metadata?.sourcePreservation
4
- ?? context.nativeSource?.ast?.metadata?.sourcePreservation;
9
+ ?? context.nativeSource?.ast?.metadata?.sourcePreservation
10
+ ?? context.universalAst?.metadata?.sourcePreservation
11
+ ?? context.importResult?.universalAst?.metadata?.sourcePreservation;
5
12
  }
@@ -17,13 +17,21 @@ export const NativeImportLanguageProfiles = Object.freeze([
17
17
  aliases: ['js', 'mjs', 'cjs', 'jsx'],
18
18
  extensions: ['.js', '.mjs', '.cjs', '.jsx'],
19
19
  parserAdapters: ['estree', 'babel', 'tree-sitter'],
20
- lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
20
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime'],
21
+ notes: [
22
+ 'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
23
+ '.jsx sources are classified as javascript for declaration scanning; JSX element trees remain opaque without host parser evidence'
24
+ ]
21
25
  }),
22
26
  nativeImportLanguageProfile('typescript', {
23
27
  aliases: ['ts', 'tsx'],
24
28
  extensions: ['.ts', '.tsx'],
25
29
  parserAdapters: ['typescript-compiler-api', 'babel', 'tree-sitter'],
26
- lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax']
30
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax'],
31
+ notes: [
32
+ 'lightweight scanner records declarations only; exact parser adapters must be injected by the host',
33
+ '.tsx sources are classified as typescript for declaration scanning; JSX element trees remain opaque without host parser evidence'
34
+ ]
27
35
  }),
28
36
  nativeImportLanguageProfile('python', {
29
37
  aliases: ['py'],
@@ -4,7 +4,6 @@ import {
4
4
  splitParameters
5
5
  } from './native-region-scanner-core.js';
6
6
  import { jsImportDeclarations } from './native-region-scanner-js-imports.js';
7
-
8
7
  function jsCommentOnlyLine(trimmed) {
9
8
  return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
10
9
  }
@@ -128,7 +127,7 @@ function jsExportedContainerDeclaration(input, lineNumber, trimmed) {
128
127
  }
129
128
 
130
129
  function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
131
- const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer))\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
130
+ const match = trimmed.match(/^export\s+default\s+((?:React\.)?(?:forwardRef|memo|lazy|observer)|Object\.freeze)\s*(?:<[^>]+>)?\s*\(\s*(.+)$/);
132
131
  if (!match) return undefined;
133
132
  const wrapper = match[1];
134
133
  const argument = match[2].trim();
@@ -148,6 +147,13 @@ function jsExportedFunctionWrapperDeclaration(input, lineNumber, trimmed) {
148
147
  parameters: splitParameters(functionMatch[1] ?? functionMatch[2])
149
148
  }, true);
150
149
  }
150
+ const classMatch = argument.match(/^(?:abstract\s+)?class\b(?:\s+(?!(?:extends|implements)\b)([A-Za-z_$][\w$]*))?/);
151
+ if (classMatch) {
152
+ return nativeDeclaration(input, lineNumber, 'ExportDefaultClassWrapperDeclaration', 'class', classMatch[1] ?? 'default', {
153
+ exportDefault: true,
154
+ wrapper
155
+ }, true);
156
+ }
151
157
  const aliasMatch = argument.match(/^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*(?:[,)]|$)/);
152
158
  if (!aliasMatch) return undefined;
153
159
  return nativeDeclaration(input, lineNumber, 'ExportDefaultWrappedAlias', 'variable', 'default', {
@@ -44,6 +44,13 @@ export function jsImportDeclarations(input, lineNumber, trimmed) {
44
44
  importedName: 'default',
45
45
  importKind: trimmed.includes('import') ? 'dynamic-import-binding' : 'commonjs-require'
46
46
  }]);
47
+ const sideEffectRequireMatch = trimmed.match(/^require\s*\(\s*(['"])([^'"]+)\1\s*\)\s*;?$/);
48
+ if (sideEffectRequireMatch) {
49
+ return jsImportModuleDeclarations(input, lineNumber, sideEffectRequireMatch[2], 'CommonJsSideEffectRequireDeclaration', [], {
50
+ sideEffectOnly: true,
51
+ importKind: 'commonjs-require'
52
+ });
53
+ }
47
54
  return [];
48
55
  }
49
56
 
@@ -126,18 +126,18 @@ function scanJavaScriptLike(input) {
126
126
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
127
127
  const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
128
128
  pushDeclaration(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
129
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
129
+ } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
130
130
  pushDeclaration(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
131
131
  methodName: match[1],
132
132
  owner: currentClass,
133
133
  parameters: splitParameters(match[2])
134
134
  }, declarationLine.includes('{') || declarationLine.includes('=>')));
135
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
135
+ } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*(#?[A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
136
136
  pushDeclaration(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
137
137
  propertyName: match[1],
138
138
  owner: currentClass,
139
139
  valueType: match[2]?.trim()
140
- }, false));
140
+ }, false, { regionKind: 'property' }));
141
141
  }
142
142
  if (currentClass) {
143
143
  classDepth += braceDelta(trimmed);
@@ -271,7 +271,7 @@ function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, cla
271
271
  if (open < 0 || close <= open) return [];
272
272
  const body = declarationLine.slice(open + 1, close);
273
273
  const declarations = [];
274
- for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
274
+ for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?(#?[A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
275
275
  if (jsControlKeyword(match[1])) continue;
276
276
  declarations.push(nativeDeclaration(input, lineNumber, 'MethodDefinition', 'method', `${className}.${match[1]}`, {
277
277
  methodName: match[1],
@@ -26,6 +26,7 @@ import {
26
26
  scanHaskell,
27
27
  scanR
28
28
  } from './native-region-scanner-functional.js';
29
+ import { normalizeNativeLanguageId } from './native-import-utils.js';
29
30
  export { lightweightCoverageLosses } from './native-region-scanner-core.js';
30
31
  export {
31
32
  detectNewlineStyle,
@@ -34,7 +35,7 @@ export {
34
35
  } from './native-source-preservation-scanner.js';
35
36
 
36
37
  function scanNativeDeclarations(input) {
37
- const language = String(input.language).toLowerCase();
38
+ const language = normalizeNativeLanguageId(input.language) || String(input.language).toLowerCase();
38
39
  if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
39
40
  if (language === 'python') return scanPython(input);
40
41
  if (language === 'rust') return scanRust(input);
@@ -1,4 +1,4 @@
1
- import { caseSensitiveIdFragment, idFragment, uniqueStrings } from './native-import-utils.js';
1
+ import { caseSensitiveIdFragment, idFragment, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
2
 
3
3
  const NativeImportRegionTaxonomyKinds = Object.freeze([
4
4
  'symbol',
@@ -20,20 +20,20 @@ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode,
20
20
  const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
21
21
  const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
22
22
  const regionKind = semanticRegionKindForSymbol(symbol, mapping, nativeNode);
23
- const key = [
23
+ const key = symbol?.metadata?.ownershipRegionKey ?? [
24
24
  options.regionPrefix ?? 'source',
25
25
  sourcePath ?? `${language}:memory`,
26
26
  regionKind,
27
27
  symbol.name ?? symbol.id
28
28
  ].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
29
29
  return {
30
- id: `region_${caseSensitiveIdFragment(key)}`,
30
+ id: symbol?.metadata?.ownershipRegionId ?? `region_${caseSensitiveIdFragment(key)}`,
31
31
  key,
32
32
  regionKind,
33
33
  granularity: 'symbol',
34
34
  language,
35
35
  sourcePath,
36
- sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash,
36
+ sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? imported?.sourceHash,
37
37
  symbolId: symbol.id,
38
38
  symbolName: symbol.name,
39
39
  symbolKind: symbol.kind,
@@ -76,17 +76,19 @@ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
76
76
  }
77
77
 
78
78
  function semanticPatchHintForRegion(region, readiness, options = {}) {
79
+ const supportedOperations = semanticRegionSupportedOperations(region);
79
80
  return {
80
81
  id: `hint_${idFragment(region.id)}`,
81
82
  kind: 'source-region-patch',
82
83
  ownershipRegionId: region.id,
83
84
  ownershipKey: region.key,
85
+ operation: supportedOperations[0] ?? 'replace-region',
84
86
  sourcePath: region.sourcePath,
85
87
  sourceHash: region.sourceHash,
86
88
  sourceSpan: region.sourceSpan,
87
89
  readiness,
88
90
  precision: region.precision,
89
- supportedOperations: semanticRegionSupportedOperations(region),
91
+ supportedOperations,
90
92
  projection: {
91
93
  sourceLanguage: region.language,
92
94
  targetPath: options.targetPath ?? region.sourcePath,
@@ -170,9 +172,20 @@ function summarizeSemanticImportRegionTaxonomy(regions) {
170
172
  };
171
173
  }
172
174
 
175
+ function semanticOwnershipRegionsFromSemanticIndex(semanticIndex) {
176
+ const factRegions = (semanticIndex?.facts ?? [])
177
+ .filter((fact) => fact?.predicate === 'semanticOwnershipRegion' && fact.value && typeof fact.value === 'object')
178
+ .map((fact) => fact.value);
179
+ return uniqueRecordsById([
180
+ ...(Array.isArray(semanticIndex?.ownershipRegions) ? semanticIndex.ownershipRegions : []),
181
+ ...factRegions
182
+ ]);
183
+ }
184
+
173
185
  export {
174
186
  NativeImportRegionTaxonomyKinds,
175
187
  semanticOwnershipRegionForDeclaration,
188
+ semanticOwnershipRegionsFromSemanticIndex,
176
189
  semanticOwnershipRegionForSymbol,
177
190
  semanticPatchHintForRegion,
178
191
  semanticRegionKindForSymbol,
@@ -8,6 +8,16 @@ export interface SemanticImportSidecarQualityWarning {
8
8
  readonly sourcePaths: readonly string[];
9
9
  }
10
10
 
11
+ export type SemanticImportSidecarRecordClassification = 'useful' | 'expected-empty' | 'unexpectedly-empty' | 'missing' | string;
12
+
13
+ export interface SemanticImportSidecarQualityRecord {
14
+ readonly classification: SemanticImportSidecarRecordClassification;
15
+ readonly reasonCode: string;
16
+ readonly message: string;
17
+ readonly action: string;
18
+ readonly sourcePaths: readonly string[];
19
+ }
20
+
11
21
  export interface SemanticImportSidecarProofAdmissionSummary {
12
22
  readonly total: number;
13
23
  readonly obligations: number;
@@ -26,12 +36,14 @@ export interface SemanticImportSidecarProofAdmissionSummary {
26
36
  export interface SemanticImportSidecarQuality {
27
37
  readonly schema: 'frontier.lang.semanticSidecarQuality.v1';
28
38
  readonly expected: boolean;
39
+ readonly expectedEmpty: boolean;
29
40
  readonly expectedSatisfied: boolean;
30
41
  readonly expectedMissingReasonCodes: readonly string[];
31
42
  readonly selected: boolean;
32
43
  readonly eligible: boolean;
33
44
  readonly imported: boolean;
34
45
  readonly importCount: number;
46
+ readonly record: SemanticImportSidecarQualityRecord;
35
47
  readonly symbolCount: number;
36
48
  readonly ownershipRegionCount: number;
37
49
  readonly patchHintCount: number;
@@ -45,6 +57,7 @@ export interface SemanticImportSidecarQuality {
45
57
  export interface SemanticImportSidecarAdmission {
46
58
  readonly schema: 'frontier.lang.semanticSidecarAdmission.v1';
47
59
  readonly expected: boolean;
60
+ readonly expectedEmpty: boolean;
48
61
  readonly expectedSatisfied: boolean;
49
62
  readonly expectedMissingReasonCodes: readonly string[];
50
63
  readonly selected: boolean;
@@ -53,6 +66,7 @@ export interface SemanticImportSidecarAdmission {
53
66
  readonly importCount: number;
54
67
  readonly readiness: SemanticMergeReadiness;
55
68
  readonly action: string;
69
+ readonly record: SemanticImportSidecarQualityRecord;
56
70
  readonly counts: {
57
71
  readonly symbols: number;
58
72
  readonly ownershipRegions: number;