@shapeshift-labs/frontier-lang-compiler 0.2.8 → 0.2.9

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.
package/dist/index.js CHANGED
@@ -103,6 +103,62 @@ export const NativeImportReadinessBySeverity = Object.freeze({
103
103
  error: 'blocked'
104
104
  });
105
105
 
106
+ export const NativeImportLanguageProfiles = Object.freeze([
107
+ nativeImportLanguageProfile('javascript', {
108
+ aliases: ['js', 'mjs', 'cjs', 'jsx'],
109
+ extensions: ['.js', '.mjs', '.cjs', '.jsx'],
110
+ parserAdapters: ['estree', 'babel', 'tree-sitter'],
111
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
112
+ }),
113
+ nativeImportLanguageProfile('typescript', {
114
+ aliases: ['ts', 'tsx'],
115
+ extensions: ['.ts', '.tsx'],
116
+ parserAdapters: ['typescript-compiler-api', 'babel', 'tree-sitter'],
117
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'unsupportedSyntax']
118
+ }),
119
+ nativeImportLanguageProfile('python', {
120
+ aliases: ['py'],
121
+ extensions: ['.py', '.pyi'],
122
+ parserAdapters: ['python-ast', 'libcst', 'parso', 'tree-sitter'],
123
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation', 'dynamicRuntime']
124
+ }),
125
+ nativeImportLanguageProfile('rust', {
126
+ aliases: ['rs'],
127
+ extensions: ['.rs'],
128
+ parserAdapters: ['syn', 'rust-analyzer-rowan', 'tree-sitter'],
129
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation']
130
+ }),
131
+ nativeImportLanguageProfile('c', {
132
+ aliases: ['h'],
133
+ extensions: ['.c', '.h'],
134
+ parserAdapters: ['clang', 'libclang', 'tree-sitter'],
135
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'sourceMapApproximation', 'sourcePreservation']
136
+ }),
137
+ nativeImportLanguageProfile('cpp', {
138
+ aliases: ['c++', 'cc', 'cxx', 'hpp'],
139
+ extensions: ['.cc', '.cpp', '.cxx', '.hpp', '.hh'],
140
+ parserAdapters: ['clang', 'libclang', 'tree-sitter'],
141
+ lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation']
142
+ }),
143
+ nativeImportLanguageProfile('java', { extensions: ['.java'], parserAdapters: ['javac', 'jdt', 'javaparser', 'tree-sitter'] }),
144
+ nativeImportLanguageProfile('go', { extensions: ['.go'], parserAdapters: ['go/parser', 'tree-sitter'] }),
145
+ nativeImportLanguageProfile('swift', { extensions: ['.swift'], parserAdapters: ['swift-syntax', 'tree-sitter'] }),
146
+ nativeImportLanguageProfile('csharp', { aliases: ['c#', 'cs'], extensions: ['.cs'], parserAdapters: ['roslyn', 'tree-sitter'] }),
147
+ nativeImportLanguageProfile('php', { extensions: ['.php'], parserAdapters: ['php-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation'] }),
148
+ nativeImportLanguageProfile('ruby', { aliases: ['rb'], extensions: ['.rb', '.rake'], parserAdapters: ['prism', 'ripper', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'metaprogramming', 'sourceMapApproximation', 'sourcePreservation'] }),
149
+ nativeImportLanguageProfile('kotlin', { aliases: ['kt', 'kts'], extensions: ['.kt', '.kts'], parserAdapters: ['kotlin-compiler', 'intellij-psi', 'tree-sitter'] }),
150
+ nativeImportLanguageProfile('scala', { aliases: ['sc'], extensions: ['.scala', '.sc'], parserAdapters: ['scala-compiler', 'scalameta', 'tree-sitter'] }),
151
+ nativeImportLanguageProfile('dart', { extensions: ['.dart'], parserAdapters: ['dart-analyzer', 'tree-sitter'] }),
152
+ nativeImportLanguageProfile('lua', { extensions: ['.lua'], parserAdapters: ['luaparse', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] }),
153
+ nativeImportLanguageProfile('shell', { aliases: ['sh', 'bash', 'zsh'], extensions: ['.sh', '.bash', '.zsh'], parserAdapters: ['bash-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] }),
154
+ nativeImportLanguageProfile('sql', { aliases: ['postgresql', 'postgres', 'mysql', 'sqlite'], extensions: ['.sql'], parserAdapters: ['sqlparser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'unsupportedSyntax', 'sourceMapApproximation', 'sourcePreservation'] }),
155
+ nativeImportLanguageProfile('zig', { extensions: ['.zig'], parserAdapters: ['zig-ast', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'generatedCode', 'sourceMapApproximation', 'sourcePreservation'] }),
156
+ nativeImportLanguageProfile('elixir', { aliases: ['ex', 'exs'], extensions: ['.ex', '.exs'], parserAdapters: ['elixir-quoted', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
157
+ nativeImportLanguageProfile('erlang', { aliases: ['erl', 'hrl'], extensions: ['.erl', '.hrl'], parserAdapters: ['erl_parse', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'preprocessor', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
158
+ nativeImportLanguageProfile('haskell', { aliases: ['hs'], extensions: ['.hs', '.lhs'], parserAdapters: ['ghc-api', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'macroExpansion', 'sourceMapApproximation', 'sourcePreservation'] }),
159
+ nativeImportLanguageProfile('r', { aliases: ['R'], extensions: ['.r', '.R'], parserAdapters: ['r-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] })
160
+ ]);
161
+
106
162
  export function normalizeCompileTarget(target) {
107
163
  const normalized = String(target ?? 'typescript').toLowerCase();
108
164
  const canonical = canonicalTargets[normalized] ?? normalized;
@@ -256,6 +312,117 @@ export function classifyNativeImportReadiness(losses = [], options = {}) {
256
312
  };
257
313
  }
258
314
 
315
+ export function createNativeImportCoverageMatrix(input = {}) {
316
+ const imports = input.imports ?? [];
317
+ const adapters = input.adapters ?? [];
318
+ const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
319
+ const languages = profiles.map((profile) => nativeImportCoverageForProfile(profile, imports, adapters));
320
+ const summary = languages.reduce((totals, entry) => {
321
+ totals.languages += 1;
322
+ if (entry.supportsLightweightScan) totals.lightweightScanners += 1;
323
+ if (entry.parserAdapters.length) totals.parserAdapterSlots += entry.parserAdapters.length;
324
+ totals.imports += entry.imports.total;
325
+ totals.symbols += entry.imports.symbols;
326
+ totals.sourceMaps += entry.imports.sourceMaps;
327
+ totals.sourceMapMappings += entry.imports.sourceMapMappings;
328
+ totals.losses += entry.imports.losses;
329
+ totals.byReadiness[entry.imports.readiness] = (totals.byReadiness[entry.imports.readiness] ?? 0) + 1;
330
+ for (const [kind, count] of Object.entries(entry.imports.lossKinds)) {
331
+ totals.lossKinds[kind] = (totals.lossKinds[kind] ?? 0) + count;
332
+ }
333
+ return totals;
334
+ }, {
335
+ languages: 0,
336
+ lightweightScanners: 0,
337
+ parserAdapterSlots: 0,
338
+ imports: 0,
339
+ symbols: 0,
340
+ sourceMaps: 0,
341
+ sourceMapMappings: 0,
342
+ losses: 0,
343
+ byReadiness: {},
344
+ lossKinds: {}
345
+ });
346
+ return {
347
+ kind: 'frontier.lang.nativeImportCoverageMatrix',
348
+ version: 1,
349
+ generatedAt: input.generatedAt ?? Date.now(),
350
+ languages,
351
+ summary,
352
+ metadata: {
353
+ compileTargets: [...FrontierCompileTargets],
354
+ note: 'Coverage is evidence and capability metadata, not a claim that every language feature is losslessly portable.'
355
+ }
356
+ };
357
+ }
358
+
359
+ export function createSemanticImportSidecar(importResult, options = {}) {
360
+ const imports = Array.isArray(importResult?.imports) ? importResult.imports : [importResult].filter(Boolean);
361
+ const importEntries = imports.map((imported, index) => semanticImportSidecarEntry(imported, index, options));
362
+ const symbols = importEntries.flatMap((entry) => entry.symbols);
363
+ const ownershipRegions = uniqueRecordsById(importEntries.flatMap((entry) => entry.ownershipRegions));
364
+ const sourceMaps = imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
365
+ const sourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
366
+ const losses = imports.flatMap((imported) => imported?.losses ?? []);
367
+ const evidence = uniqueRecordsById(imports.flatMap((imported) => imported?.evidence ?? []));
368
+ const mergeCandidates = imports.flatMap((imported) => imported?.mergeCandidates ?? []);
369
+ const lossSummary = summarizeNativeImportLosses(losses, { evidence });
370
+ const readiness = mergeCandidates.reduce(
371
+ (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
372
+ lossSummary.semanticMergeReadiness
373
+ );
374
+ const patchHints = ownershipRegions.map((region) => semanticPatchHintForRegion(region, readiness, options));
375
+ return {
376
+ kind: 'frontier.lang.semanticImportSidecar',
377
+ version: 1,
378
+ id: options.id ?? `semantic_import_${idFragment(importResult?.id ?? importResult?.projectRoot ?? imports[0]?.sourcePath ?? imports[0]?.language ?? 'source')}`,
379
+ generatedAt: options.generatedAt ?? Date.now(),
380
+ language: importResult?.language ?? (imports.length === 1 ? imports[0]?.language : 'mixed'),
381
+ projectRoot: importResult?.projectRoot,
382
+ imports: importEntries.map(({ ownershipRegions: _regions, symbols: _symbols, ...entry }) => entry),
383
+ symbols,
384
+ ownershipRegions,
385
+ sourceMaps: {
386
+ total: sourceMaps.length,
387
+ mappings: sourceMapMappings.length,
388
+ ids: sourceMaps.map((sourceMap) => sourceMap.id).filter(Boolean)
389
+ },
390
+ patchHints,
391
+ mergeCandidates: mergeCandidates.map((candidate) => ({
392
+ id: candidate.id,
393
+ readiness: candidate.readiness,
394
+ reasons: candidate.reasons ?? [],
395
+ risk: candidate.risk,
396
+ operationCount: candidate.operations?.length ?? candidate.patch?.operations?.length ?? 0
397
+ })),
398
+ losses: {
399
+ total: losses.length,
400
+ byKind: lossSummary.byKind,
401
+ bySeverity: lossSummary.bySeverity,
402
+ categories: lossSummary.categories,
403
+ blockingLossIds: lossSummary.blockingLossIds,
404
+ reviewLossIds: lossSummary.reviewLossIds
405
+ },
406
+ evidence: {
407
+ total: evidence.length,
408
+ failed: evidence.filter((record) => record.status === 'failed').map((record) => record.id),
409
+ ids: evidence.map((record) => record.id)
410
+ },
411
+ summary: {
412
+ imports: imports.length,
413
+ symbols: symbols.length,
414
+ ownershipRegions: ownershipRegions.length,
415
+ sourceMapMappings: sourceMapMappings.length,
416
+ readiness,
417
+ emptySemanticIndex: symbols.length === 0
418
+ },
419
+ metadata: {
420
+ note: 'Sidecar is source-addressable semantic evidence for merge admission; lightweight scanner regions remain review-required unless exact parser evidence upgrades readiness.',
421
+ ...options.metadata
422
+ }
423
+ };
424
+ }
425
+
259
426
  export function createEstreeNativeImporterAdapter(options = {}) {
260
427
  return createJavaScriptSyntaxImporterAdapter({
261
428
  id: 'frontier.estree-native-importer',
@@ -487,6 +654,84 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
487
654
  };
488
655
  }
489
656
 
657
+ export function projectNativeImportToSource(importResult, options = {}) {
658
+ if (!importResult || typeof importResult !== 'object') {
659
+ throw new Error('projectNativeImportToSource requires a native import result');
660
+ }
661
+ const context = nativeImportProjectionContext(importResult, options);
662
+ const candidateSource = nativeProjectionSourceCandidate(context, options);
663
+ const declarations = nativeProjectionDeclarations(importResult, context);
664
+ const preserveSource = options.preferPreservedSource !== false && candidateSource?.exact === true;
665
+ const mode = preserveSource ? 'preserved-source' : 'native-source-stubs';
666
+ const sourceText = preserveSource
667
+ ? candidateSource.sourceText
668
+ : renderNativeProjectionStubs(context, declarations, options);
669
+ const losses = preserveSource ? [] : nativeProjectionStubLosses(context, candidateSource, declarations, options);
670
+ const evidence = [{
671
+ id: options.evidenceId ?? `evidence_${context.idPart}_native_source_projection`,
672
+ kind: 'projection',
673
+ status: losses.some((loss) => loss.severity === 'error') ? 'failed' : 'passed',
674
+ path: context.sourcePath,
675
+ summary: preserveSource
676
+ ? `Preserved exact ${context.language} source for native projection.`
677
+ : `Projected ${context.language} native import to ${declarations.length} declaration stub(s).`,
678
+ metadata: {
679
+ mode,
680
+ language: context.language,
681
+ sourcePath: context.sourcePath,
682
+ expectedSourceHash: context.sourceHash,
683
+ providedSourceHash: candidateSource?.sourceHash,
684
+ sourceHashVerified: candidateSource?.hashVerified ?? false,
685
+ declarationCount: declarations.length
686
+ }
687
+ }];
688
+ const lossSummary = summarizeNativeImportLosses(losses, {
689
+ evidence,
690
+ parser: context.parser,
691
+ scanKind: 'native-source-projection',
692
+ semanticStatus: context.semanticStatus
693
+ });
694
+ const readiness = classifyNativeImportReadiness(losses, {
695
+ evidence,
696
+ parser: context.parser,
697
+ scanKind: 'native-source-projection',
698
+ semanticStatus: context.semanticStatus
699
+ });
700
+ const nativeImportLossSummary = importResult.metadata?.nativeImportLossSummary ?? summarizeNativeImportLosses(importResult.losses ?? context.nativeAst?.losses ?? [], {
701
+ evidence: importResult.evidence,
702
+ parser: context.parser,
703
+ semanticStatus: context.semanticStatus
704
+ });
705
+ return {
706
+ kind: 'frontier.lang.nativeSourceProjection',
707
+ version: 1,
708
+ id: options.id ?? `native_source_projection_${context.idPart}`,
709
+ language: context.language,
710
+ sourcePath: context.sourcePath,
711
+ sourceHash: context.sourceHash,
712
+ mode,
713
+ sourceText,
714
+ outputHash: hashSemanticValue(sourceText),
715
+ declarations,
716
+ losses,
717
+ lossSummary,
718
+ readiness,
719
+ evidence,
720
+ metadata: {
721
+ nativeImportId: importResult.id,
722
+ nativeSourceId: context.nativeSource?.id,
723
+ nativeAstId: context.nativeAst?.id,
724
+ semanticIndexId: context.semanticIndex?.id,
725
+ universalAstId: importResult.universalAst?.id,
726
+ exactSourceAvailable: candidateSource?.exact === true,
727
+ sourceTextAvailable: typeof candidateSource?.sourceText === 'string',
728
+ sourceHashVerified: candidateSource?.hashVerified ?? false,
729
+ nativeImportLossSummary,
730
+ ...options.metadata
731
+ }
732
+ };
733
+ }
734
+
490
735
  export function importNativeSource(input) {
491
736
  const language = input.language ?? input.nativeAst?.language;
492
737
  if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
@@ -725,6 +970,7 @@ function createLightweightNativeImport(input) {
725
970
  const evidenceId = `evidence_${idFragment(input.sourcePath ?? input.language)}_lightweight_scan`;
726
971
 
727
972
  for (const declaration of declarations) {
973
+ const ownershipRegion = semanticOwnershipRegionForDeclaration(input, declaration, documentId);
728
974
  nodes[rootId].children.push(declaration.nodeId);
729
975
  nodes[declaration.nodeId] = {
730
976
  id: declaration.nodeId,
@@ -733,7 +979,11 @@ function createLightweightNativeImport(input) {
733
979
  span: declaration.span,
734
980
  value: declaration.name ?? declaration.importPath ?? null,
735
981
  fields: declaration.fields,
736
- metadata: declaration.metadata
982
+ metadata: {
983
+ ...declaration.metadata,
984
+ ownershipRegionId: ownershipRegion.id,
985
+ ownershipRegionKey: ownershipRegion.key
986
+ }
737
987
  };
738
988
  if (declaration.symbolId) {
739
989
  const occurrenceId = `occ_${idFragment(declaration.nodeId)}_def`;
@@ -745,7 +995,11 @@ function createLightweightNativeImport(input) {
745
995
  language: input.language,
746
996
  nativeAstNodeId: declaration.nodeId,
747
997
  signatureHash: hashSemanticValue([input.language, declaration.kind, declaration.name, declaration.fields ?? {}]),
748
- definitionSpan: declaration.span
998
+ definitionSpan: declaration.span,
999
+ metadata: {
1000
+ ownershipRegionId: ownershipRegion.id,
1001
+ ownershipRegionKey: ownershipRegion.key
1002
+ }
749
1003
  });
750
1004
  occurrences.push({
751
1005
  id: occurrenceId,
@@ -766,6 +1020,11 @@ function createLightweightNativeImport(input) {
766
1020
  predicate: 'nativeKind',
767
1021
  subjectId: declaration.symbolId,
768
1022
  value: declaration.languageKind
1023
+ }, {
1024
+ id: `fact_${idFragment(declaration.nodeId)}_ownership_region`,
1025
+ predicate: 'semanticOwnershipRegion',
1026
+ subjectId: declaration.symbolId,
1027
+ value: ownershipRegion
769
1028
  });
770
1029
  mappings.push({
771
1030
  id: `map_${idFragment(declaration.nodeId)}`,
@@ -775,6 +1034,7 @@ function createLightweightNativeImport(input) {
775
1034
  sourceSpan: declaration.span,
776
1035
  evidenceIds: [evidenceId],
777
1036
  lossIds: declaration.loss ? [declaration.loss.id] : [],
1037
+ ownershipRegionId: ownershipRegion.id,
778
1038
  precision: 'declaration'
779
1039
  });
780
1040
  }
@@ -820,6 +1080,314 @@ function createLightweightNativeImport(input) {
820
1080
  };
821
1081
  }
822
1082
 
1083
+ function nativeImportProjectionContext(importResult, options) {
1084
+ const nativeSource = options.nativeSource
1085
+ ?? importResult.nativeSource
1086
+ ?? importResult.nativeSources?.[0]
1087
+ ?? importResult.universalAst?.nativeSources?.[0];
1088
+ const nativeAst = options.nativeAst
1089
+ ?? importResult.nativeAst
1090
+ ?? nativeSource?.ast
1091
+ ?? importResult.universalAst?.nativeSources?.[0]?.ast;
1092
+ const semanticIndex = options.semanticIndex
1093
+ ?? importResult.semanticIndex
1094
+ ?? importResult.universalAst?.semanticIndex;
1095
+ const language = options.language
1096
+ ?? importResult.language
1097
+ ?? nativeSource?.language
1098
+ ?? nativeAst?.language
1099
+ ?? importResult.universalAst?.metadata?.sourceLanguage
1100
+ ?? 'source';
1101
+ const sourcePath = options.sourcePath
1102
+ ?? importResult.sourcePath
1103
+ ?? nativeSource?.sourcePath
1104
+ ?? nativeAst?.sourcePath
1105
+ ?? importResult.universalAst?.metadata?.sourcePath;
1106
+ const sourceHash = options.expectedSourceHash
1107
+ ?? importResult.sourceHash
1108
+ ?? nativeSource?.sourceHash
1109
+ ?? nativeAst?.sourceHash;
1110
+ return {
1111
+ nativeSource,
1112
+ nativeAst,
1113
+ semanticIndex,
1114
+ language,
1115
+ sourcePath,
1116
+ sourceHash,
1117
+ parser: options.parser ?? nativeAst?.parser ?? nativeSource?.parser,
1118
+ semanticStatus: options.semanticStatus ?? importResult.metadata?.semanticStatus ?? nativeSource?.metadata?.semanticStatus,
1119
+ idPart: idFragment(options.id ?? importResult.id ?? nativeSource?.id ?? sourcePath ?? language)
1120
+ };
1121
+ }
1122
+
1123
+ function nativeProjectionSourceCandidate(context, options) {
1124
+ const sourceText = options.sourceText ?? options.preservedSourceText ?? options.exactSourceText;
1125
+ if (typeof sourceText !== 'string') return undefined;
1126
+ const sourceHash = options.sourceHash ?? hashSemanticValue(sourceText);
1127
+ const hashVerified = Boolean(context.sourceHash);
1128
+ const exact = !context.sourceHash || sourceHash === context.sourceHash || options.verifySourceHash === false;
1129
+ return {
1130
+ sourceText,
1131
+ sourceHash,
1132
+ hashVerified,
1133
+ exact,
1134
+ mismatch: hashVerified && sourceHash !== context.sourceHash && options.verifySourceHash !== false
1135
+ };
1136
+ }
1137
+
1138
+ function nativeProjectionDeclarations(importResult, context) {
1139
+ const semanticIndex = context.semanticIndex;
1140
+ const occurrencesBySymbol = new Map();
1141
+ for (const occurrence of semanticIndex?.occurrences ?? []) {
1142
+ const list = occurrencesBySymbol.get(occurrence.symbolId) ?? [];
1143
+ list.push(occurrence);
1144
+ occurrencesBySymbol.set(occurrence.symbolId, list);
1145
+ }
1146
+ const declarations = (semanticIndex?.symbols ?? [])
1147
+ .filter((symbol) => !nativeProjectionImportOnlySymbol(symbol, occurrencesBySymbol.get(symbol.id)))
1148
+ .map((symbol) => {
1149
+ const occurrence = occurrencesBySymbol.get(symbol.id)?.find((item) => item.role !== 'import');
1150
+ const mapping = (importResult.sourceMaps ?? importResult.universalAst?.sourceMaps ?? [])
1151
+ .flatMap((sourceMap) => sourceMap.mappings ?? [])
1152
+ .find((item) => item.semanticSymbolId === symbol.id);
1153
+ return {
1154
+ name: symbol.name,
1155
+ kind: nativeProjectionDeclarationKind(symbol.kind),
1156
+ symbolId: symbol.id,
1157
+ nativeAstNodeId: symbol.nativeAstNodeId ?? occurrence?.nativeAstNodeId,
1158
+ sourceSpan: symbol.definitionSpan ?? occurrence?.span ?? mapping?.sourceSpan,
1159
+ ownershipRegionId: mapping?.ownershipRegionId ?? symbol.metadata?.ownershipRegionId,
1160
+ metadata: {
1161
+ semanticKind: symbol.kind,
1162
+ language: symbol.language,
1163
+ signatureHash: symbol.signatureHash
1164
+ }
1165
+ };
1166
+ })
1167
+ .filter((declaration) => declaration.name);
1168
+ if (declarations.length) return uniqueNativeProjectionDeclarations(declarations);
1169
+ return uniqueNativeProjectionDeclarations(Object.values(context.nativeAst?.nodes ?? {})
1170
+ .map((node) => {
1171
+ const name = typeof node.value === 'string' && node.value.trim() ? node.value.trim() : node.fields?.name;
1172
+ const kind = nativeProjectionKindForNode(node);
1173
+ if (!name || !kind) return undefined;
1174
+ return {
1175
+ name,
1176
+ kind,
1177
+ nativeAstNodeId: node.id,
1178
+ sourceSpan: node.span,
1179
+ ownershipRegionId: node.metadata?.ownershipRegionId,
1180
+ metadata: { nativeKind: node.kind, language: context.language }
1181
+ };
1182
+ })
1183
+ .filter(Boolean));
1184
+ }
1185
+
1186
+ function nativeProjectionImportOnlySymbol(symbol, occurrences = []) {
1187
+ if (String(symbol.id ?? '').includes(':import:')) return true;
1188
+ if (occurrences.length && occurrences.every((occurrence) => occurrence.role === 'import')) return true;
1189
+ return symbol.kind === 'module' && occurrences.some((occurrence) => occurrence.role === 'import');
1190
+ }
1191
+
1192
+ function nativeProjectionDeclarationKind(kind) {
1193
+ const normalized = String(kind ?? 'value').toLowerCase();
1194
+ if (normalized === 'function' || normalized === 'method' || normalized === 'procedure') return 'function';
1195
+ if (normalized === 'class') return 'class';
1196
+ if (normalized === 'interface' || normalized === 'protocol') return 'interface';
1197
+ if (normalized === 'trait') return 'trait';
1198
+ if (normalized === 'type' || normalized === 'struct' || normalized === 'enum' || normalized === 'record') return 'type';
1199
+ if (normalized === 'constant' || normalized === 'const') return 'constant';
1200
+ if (normalized === 'variable' || normalized === 'property' || normalized === 'field') return 'variable';
1201
+ if (normalized === 'module' || normalized === 'namespace' || normalized === 'package') return 'module';
1202
+ return normalized;
1203
+ }
1204
+
1205
+ function nativeProjectionKindForNode(node) {
1206
+ const kind = String(node?.kind ?? node?.languageKind ?? '').toLowerCase();
1207
+ if (/function|method|procedure|funcdecl|itemfn|fndeclaration|\bdef\b/.test(kind)) return 'function';
1208
+ if (/class/.test(kind)) return 'class';
1209
+ if (/interface|protocol/.test(kind)) return 'interface';
1210
+ if (/trait/.test(kind)) return 'trait';
1211
+ if (/struct|enum|record|typedef|typealias|type/.test(kind)) return 'type';
1212
+ if (/const|constant|macro|define/.test(kind)) return 'constant';
1213
+ if (/var|property|field/.test(kind)) return 'variable';
1214
+ if (/module|namespace|package/.test(kind)) return 'module';
1215
+ return undefined;
1216
+ }
1217
+
1218
+ function uniqueNativeProjectionDeclarations(declarations) {
1219
+ const seen = new Set();
1220
+ return declarations.filter((declaration) => {
1221
+ const key = `${declaration.kind}:${declaration.name}:${declaration.symbolId ?? declaration.nativeAstNodeId ?? ''}`;
1222
+ if (seen.has(key)) return false;
1223
+ seen.add(key);
1224
+ return true;
1225
+ });
1226
+ }
1227
+
1228
+ function nativeProjectionStubLosses(context, candidateSource, declarations, options) {
1229
+ const reason = candidateSource?.mismatch
1230
+ ? 'source-hash-mismatch'
1231
+ : options.preferPreservedSource === false && candidateSource?.sourceText
1232
+ ? 'preserved-source-disabled'
1233
+ : 'exact-source-unavailable';
1234
+ const message = candidateSource?.mismatch
1235
+ ? 'Provided native source text hash did not match the import result source hash; emitted declaration stubs instead of preserving stale source.'
1236
+ : options.preferPreservedSource === false && candidateSource?.sourceText
1237
+ ? 'Native source projection was asked to emit declaration stubs instead of preserving available source text.'
1238
+ : 'Exact native source text was not provided; emitted declaration stubs reconstructed from import metadata.';
1239
+ const losses = [nativeProjectionLoss(context, {
1240
+ id: `loss_${context.idPart}_native_source_stub`,
1241
+ kind: 'sourcePreservation',
1242
+ severity: 'warning',
1243
+ message,
1244
+ metadata: {
1245
+ reason,
1246
+ projectionMode: 'native-source-stubs',
1247
+ expectedSourceHash: context.sourceHash,
1248
+ providedSourceHash: candidateSource?.sourceHash
1249
+ }
1250
+ })];
1251
+ if (!declarations.length) {
1252
+ losses.push(nativeProjectionLoss(context, {
1253
+ id: `loss_${context.idPart}_native_source_stub_empty`,
1254
+ kind: 'declarationOnlyCoverage',
1255
+ severity: 'warning',
1256
+ message: 'Native import result did not expose semantic declarations for source stub generation.',
1257
+ metadata: { reason: 'no-stub-declarations', projectionMode: 'native-source-stubs' }
1258
+ }));
1259
+ }
1260
+ return losses;
1261
+ }
1262
+
1263
+ function nativeProjectionLoss(context, input) {
1264
+ const rootSpan = context.nativeAst?.nodes?.[context.nativeAst.rootId]?.span;
1265
+ return {
1266
+ id: input.id,
1267
+ severity: input.severity,
1268
+ phase: 'emit',
1269
+ sourceFormat: context.language,
1270
+ kind: input.kind,
1271
+ message: input.message,
1272
+ span: rootSpan ?? {
1273
+ sourceId: context.sourceHash,
1274
+ path: context.sourcePath,
1275
+ startLine: 1,
1276
+ startColumn: 1
1277
+ },
1278
+ metadata: {
1279
+ nativeSourceId: context.nativeSource?.id,
1280
+ nativeAstId: context.nativeAst?.id,
1281
+ parser: context.parser,
1282
+ ...input.metadata
1283
+ }
1284
+ };
1285
+ }
1286
+
1287
+ function renderNativeProjectionStubs(context, declarations, options) {
1288
+ const language = String(context.language ?? 'source').toLowerCase();
1289
+ const header = nativeProjectionStubHeader(language, context, options);
1290
+ let body;
1291
+ if (language === 'typescript' || language === 'ts') body = renderTypeScriptProjectionStubs(declarations);
1292
+ else if (language === 'javascript' || language === 'js') body = renderJavaScriptProjectionStubs(declarations);
1293
+ else if (language === 'python' || language === 'py') body = renderPythonProjectionStubs(declarations);
1294
+ else if (language === 'rust' || language === 'rs') body = renderRustProjectionStubs(declarations);
1295
+ else if (language === 'c' || language === 'cpp' || language === 'c++' || language === 'h') body = renderCProjectionStubs(declarations);
1296
+ else body = renderGenericProjectionStubs(declarations, language);
1297
+ return ensureTrailingNewline([header, body].filter(Boolean).join('\n'));
1298
+ }
1299
+
1300
+ function nativeProjectionStubHeader(language, context, options) {
1301
+ if (options.stubBanner === false) return '';
1302
+ if (typeof options.stubBanner === 'string') return ensureTrailingNewline(options.stubBanner).trimEnd();
1303
+ const comment = nativeProjectionLineComment(language);
1304
+ const suffix = context.sourcePath ? ` for ${oneLine(context.sourcePath)}` : '';
1305
+ return [
1306
+ `${comment} Frontier native source stubs${suffix}`,
1307
+ `${comment} Exact source text was unavailable; declarations are reconstructed from native import metadata.`
1308
+ ].join('\n');
1309
+ }
1310
+
1311
+ function nativeProjectionLineComment(language) {
1312
+ if (language === 'python' || language === 'py' || language === 'ruby' || language === 'rb' || language === 'shell' || language === 'sh') return '#';
1313
+ if (language === 'sql') return '--';
1314
+ return '//';
1315
+ }
1316
+
1317
+ function renderJavaScriptProjectionStubs(declarations) {
1318
+ const used = new Set();
1319
+ if (!declarations.length) return 'export {};';
1320
+ return declarations.map((declaration) => {
1321
+ const name = reserveUniqueId(safeProjectionIdentifier(declaration.name), used);
1322
+ if (declaration.kind === 'function') return `export function ${name}(...args) {\n throw new Error('Frontier native source stub: implementation unavailable.');\n}`;
1323
+ if (declaration.kind === 'class') return `export class ${name} {}`;
1324
+ return `export const ${name} = undefined;`;
1325
+ }).join('\n\n');
1326
+ }
1327
+
1328
+ function renderTypeScriptProjectionStubs(declarations) {
1329
+ const used = new Set();
1330
+ if (!declarations.length) return 'export {};';
1331
+ return declarations.map((declaration) => {
1332
+ const name = reserveUniqueId(safeProjectionIdentifier(declaration.name), used);
1333
+ if (declaration.kind === 'function') return `export function ${name}(...args: unknown[]): never {\n throw new Error('Frontier native source stub: implementation unavailable.');\n}`;
1334
+ if (declaration.kind === 'class') return `export class ${name} {}`;
1335
+ if (declaration.kind === 'interface') return `export interface ${name} {}`;
1336
+ if (declaration.kind === 'type' || declaration.kind === 'trait') return `export type ${name} = unknown;`;
1337
+ return `export const ${name}: unknown = undefined;`;
1338
+ }).join('\n\n');
1339
+ }
1340
+
1341
+ function renderPythonProjectionStubs(declarations) {
1342
+ if (!declarations.length) return 'pass';
1343
+ return declarations.map((declaration) => {
1344
+ const name = safeProjectionIdentifier(declaration.name);
1345
+ if (declaration.kind === 'function') return `def ${name}(*args, **kwargs):\n raise NotImplementedError("Frontier native source stub")`;
1346
+ if (declaration.kind === 'class') return `class ${name}:\n pass`;
1347
+ return `${name} = None`;
1348
+ }).join('\n\n');
1349
+ }
1350
+
1351
+ function renderRustProjectionStubs(declarations) {
1352
+ if (!declarations.length) return '';
1353
+ return declarations.map((declaration) => {
1354
+ const name = safeProjectionIdentifier(declaration.name);
1355
+ if (declaration.kind === 'function') return `pub fn ${name}() {\n unimplemented!(\"Frontier native source stub\");\n}`;
1356
+ if (declaration.kind === 'type' || declaration.kind === 'class') return `pub struct ${upperFirst(name)};`;
1357
+ return `pub const ${name.toUpperCase()}: () = ();`;
1358
+ }).join('\n\n');
1359
+ }
1360
+
1361
+ function renderCProjectionStubs(declarations) {
1362
+ if (!declarations.length) return '';
1363
+ return declarations.map((declaration) => {
1364
+ const name = safeProjectionIdentifier(declaration.name);
1365
+ if (declaration.kind === 'function') return `void ${name}(void);`;
1366
+ if (declaration.kind === 'type' || declaration.kind === 'class') return `typedef struct ${upperFirst(name)} ${upperFirst(name)};`;
1367
+ return `extern const int ${name};`;
1368
+ }).join('\n');
1369
+ }
1370
+
1371
+ function renderGenericProjectionStubs(declarations, language) {
1372
+ if (!declarations.length) return `${nativeProjectionLineComment(language)} no declarations available`;
1373
+ return declarations.map((declaration) => `${declaration.kind} ${declaration.name}`).join('\n');
1374
+ }
1375
+
1376
+ function safeProjectionIdentifier(name) {
1377
+ const text = String(name ?? 'value').split('.').at(-1).replace(/[^A-Za-z0-9_$]/g, '_');
1378
+ const identifier = text || 'value';
1379
+ return /^[A-Za-z_$]/.test(identifier) ? identifier : `_${identifier}`;
1380
+ }
1381
+
1382
+ function ensureTrailingNewline(value) {
1383
+ const text = String(value ?? '');
1384
+ return text.endsWith('\n') ? text : `${text}\n`;
1385
+ }
1386
+
1387
+ function oneLine(value) {
1388
+ return String(value ?? '').replace(/\s+/g, ' ').trim();
1389
+ }
1390
+
823
1391
  function scanNativeDeclarations(input) {
824
1392
  const language = String(input.language).toLowerCase();
825
1393
  if (language === 'javascript' || language === 'typescript') return scanJavaScriptLike(input);
@@ -848,23 +1416,63 @@ function scanNativeDeclarations(input) {
848
1416
 
849
1417
  function scanJavaScriptLike(input) {
850
1418
  const declarations = [];
1419
+ let currentClass;
1420
+ let classDepth = 0;
851
1421
  for (const { line, number } of sourceLines(input.sourceText)) {
852
1422
  const trimmed = line.trim();
1423
+ const declarationLine = trimmed.replace(/^(?:export\s+)?(?:declare\s+)?/, '');
853
1424
  let match;
854
- if ((match = trimmed.match(/^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)/))) {
855
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
856
- } else if ((match = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
857
- declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'class', match[1], {}, trimmed.includes('{')));
858
- } else if ((match = trimmed.match(/^(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/))) {
859
- declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, trimmed.includes('{')));
860
- } else if ((match = trimmed.match(/^(?:export\s+)?type\s+([A-Za-z_$][\w$]*)\s*=/))) {
861
- declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
862
- } else if ((match = trimmed.match(/^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
863
- declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
864
- } else if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
1425
+ if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
865
1426
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
866
- } else if ((match = trimmed.match(/^export\s+\{[^}]*\}\s+from\s+['"]([^'"]+)['"]/))) {
1427
+ } else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
1428
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'DynamicImportExpression', 'module'));
1429
+ } else if ((match = trimmed.match(/^export\s+(?:\*\s+from|\{[^}]*\}\s+from)\s+['"]([^'"]+)['"]/))) {
867
1430
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ExportFromDeclaration', 'module'));
1431
+ } else if ((match = declarationLine.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)/))) {
1432
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, declarationLine.includes('{')));
1433
+ } else if ((match = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*\(([^)]*)\)/))) {
1434
+ declarations.push(nativeDeclaration(input, number, 'ExportDefaultFunctionDeclaration', 'function', match[1] ?? 'default', { parameters: splitParameters(match[2]), exportDefault: true }, trimmed.includes('{')));
1435
+ } else if ((match = declarationLine.match(/^(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
1436
+ declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'class', match[1], {}, declarationLine.includes('{')));
1437
+ if (declarationLine.includes('{') && !declarationLine.includes('}')) {
1438
+ currentClass = match[1];
1439
+ classDepth = 0;
1440
+ }
1441
+ } else if ((match = declarationLine.match(/^interface\s+([A-Za-z_$][\w$]*)/))) {
1442
+ declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, declarationLine.includes('{')));
1443
+ } else if ((match = declarationLine.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/))) {
1444
+ declarations.push(nativeDeclaration(input, number, 'EnumDeclaration', 'type', match[1], {}, declarationLine.includes('{')));
1445
+ } else if ((match = declarationLine.match(/^(?:namespace|module)\s+([A-Za-z_$][\w$.]*)/))) {
1446
+ declarations.push(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, declarationLine.includes('{')));
1447
+ } else if ((match = declarationLine.match(/^type\s+([A-Za-z_$][\w$]*)\s*=/))) {
1448
+ declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
1449
+ } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
1450
+ declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
1451
+ } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
1452
+ declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
1453
+ } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
1454
+ declarations.push(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
1455
+ } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
1456
+ declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false));
1457
+ } 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*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
1458
+ declarations.push(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
1459
+ methodName: match[1],
1460
+ owner: currentClass,
1461
+ parameters: splitParameters(match[2])
1462
+ }, declarationLine.includes('{') || declarationLine.includes('=>')));
1463
+ } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
1464
+ declarations.push(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
1465
+ propertyName: match[1],
1466
+ owner: currentClass,
1467
+ valueType: match[2]?.trim()
1468
+ }, false));
1469
+ }
1470
+ if (currentClass) {
1471
+ classDepth += braceDelta(trimmed);
1472
+ if (classDepth <= 0) {
1473
+ currentClass = undefined;
1474
+ classDepth = 0;
1475
+ }
868
1476
  }
869
1477
  }
870
1478
  return declarations;
@@ -953,17 +1561,41 @@ function scanJava(input) {
953
1561
 
954
1562
  function scanGo(input) {
955
1563
  const declarations = [];
1564
+ let inImportBlock = false;
956
1565
  for (const { line, number } of sourceLines(input.sourceText)) {
957
1566
  const trimmed = line.trim();
958
1567
  let match;
1568
+ if (inImportBlock) {
1569
+ if (trimmed === ')') {
1570
+ inImportBlock = false;
1571
+ } else if ((match = trimmed.match(/^(?:(?:[A-Za-z_]\w*|[_.])\s+)?["']([^"']+)["']/))) {
1572
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportSpec', 'package'));
1573
+ }
1574
+ continue;
1575
+ }
959
1576
  if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*)/))) {
960
1577
  declarations.push(nativeDeclaration(input, number, 'PackageClause', 'package', match[1], {}, false));
961
- } else if ((match = trimmed.match(/^import\s+(?:[A-Za-z_]\w*\s+)?["']([^"']+)["']/))) {
1578
+ } else if (/^import\s*\(/.test(trimmed)) {
1579
+ inImportBlock = true;
1580
+ } else if ((match = trimmed.match(/^import\s+(?:(?:[A-Za-z_]\w*|[_.])\s+)?["']([^"']+)["']/))) {
962
1581
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportSpec', 'package'));
1582
+ } else if ((match = trimmed.match(/^type\s+([A-Za-z_]\w*)\s*=\s*(.+)$/))) {
1583
+ declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], { target: match[2].trim() }, false));
963
1584
  } else if ((match = trimmed.match(/^type\s+([A-Za-z_]\w*)\s+(struct|interface)\b/))) {
964
1585
  declarations.push(nativeDeclaration(input, number, match[2] === 'struct' ? 'TypeSpecStruct' : 'TypeSpecInterface', 'type', match[1], {}, trimmed.includes('{')));
965
- } else if ((match = trimmed.match(/^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
966
- declarations.push(nativeDeclaration(input, number, 'FuncDecl', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
1586
+ } else if ((match = trimmed.match(/^func\s+\(([^)]*)\)\s*([A-Za-z_]\w*)(?:\s*\[([^\]]+)\])?\s*\(([^)]*)\)/))) {
1587
+ const receiver = parseGoReceiver(match[1]);
1588
+ declarations.push(nativeDeclaration(input, number, 'MethodDecl', 'method', goReceiverMethodName(receiver, match[2]), {
1589
+ methodName: match[2],
1590
+ receiver,
1591
+ typeParameters: splitTypeParameters(match[3]),
1592
+ parameters: splitParameters(match[4])
1593
+ }, trimmed.includes('{')));
1594
+ } else if ((match = trimmed.match(/^func\s+([A-Za-z_]\w*)(?:\s*\[([^\]]+)\])?\s*\(([^)]*)\)/))) {
1595
+ declarations.push(nativeDeclaration(input, number, 'FuncDecl', 'function', match[1], {
1596
+ typeParameters: splitTypeParameters(match[2]),
1597
+ parameters: splitParameters(match[3])
1598
+ }, trimmed.includes('{')));
967
1599
  } else if ((match = trimmed.match(/^var\s+([A-Za-z_]\w*)\b/))) {
968
1600
  declarations.push(nativeDeclaration(input, number, 'VarDecl', 'variable', match[1], {}, false));
969
1601
  } else if ((match = trimmed.match(/^const\s+([A-Za-z_]\w*)\b/))) {
@@ -975,17 +1607,35 @@ function scanGo(input) {
975
1607
 
976
1608
  function scanSwift(input) {
977
1609
  const declarations = [];
1610
+ const protocols = new Set();
978
1611
  for (const { line, number } of sourceLines(input.sourceText)) {
979
1612
  const trimmed = line.trim();
1613
+ const declarationLine = trimmed.replace(/^(?:@[A-Za-z_][\w.]+(?:\([^)]*\))?\s+)*/, '');
980
1614
  let match;
981
- if ((match = trimmed.match(/^import\s+([A-Za-z_]\w*)/))) {
1615
+ if ((match = declarationLine.match(/^import\s+(?:(?:struct|class|enum|protocol|func|var)\s+)?([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
982
1616
  declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDecl', 'module'));
983
- } else if ((match = trimmed.match(/^(?:(?:public|private|fileprivate|internal|open|final)\s+)*(struct|class|enum|protocol|actor|extension)\s+([A-Za-z_]\w*)/))) {
984
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Decl`, swiftSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
985
- } else if ((match = trimmed.match(/^(?:(?:public|private|fileprivate|internal|open|static|class|mutating)\s+)*func\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
986
- declarations.push(nativeDeclaration(input, number, 'FunctionDecl', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
987
- } else if ((match = trimmed.match(/^(?:let|var)\s+([A-Za-z_]\w*)\b/))) {
988
- declarations.push(nativeDeclaration(input, number, 'ValueBindingDecl', 'variable', match[1], {}, false));
1617
+ } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|final|indirect)\s+)*(struct|class|enum|protocol|actor)\s+([A-Za-z_]\w*)/))) {
1618
+ if (match[1] === 'protocol') protocols.add(match[2]);
1619
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Decl`, swiftSymbolKind(match[1]), match[2], {}, declarationLine.includes('{')));
1620
+ } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*extension\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(.*)$/))) {
1621
+ const extensionFields = parseSwiftExtensionTail(match[2]);
1622
+ const isProtocolExtension = protocols.has(match[1]) || /Protocol$/.test(match[1]);
1623
+ declarations.push(nativeDeclaration(input, number, isProtocolExtension ? 'ProtocolExtensionDecl' : 'ExtensionDecl', 'implementation', `${match[1]}.${isProtocolExtension ? 'protocolExtension' : 'extension'}`, {
1624
+ extendedType: match[1],
1625
+ ...extensionFields
1626
+ }, declarationLine.includes('{')));
1627
+ } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|mutating|nonmutating|override|required|convenience|isolated|nonisolated)\s+)*func\s+([A-Za-z_]\w*|`[^`]+`)(?:\s*<([^>]+)>)?\s*\(([^)]*)\)/))) {
1628
+ declarations.push(nativeDeclaration(input, number, 'FunctionDecl', 'function', unquoteSwiftIdentifier(match[1]), {
1629
+ typeParameters: splitTypeParameters(match[2]),
1630
+ parameters: splitParameters(match[3])
1631
+ }, declarationLine.includes('{')));
1632
+ } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open|static|class|final|lazy|weak|unowned|override|required|nonisolated)\s+)*(let|var)\s+([A-Za-z_]\w*)\b(?::\s*([^={]+))?/))) {
1633
+ declarations.push(nativeDeclaration(input, number, 'PropertyDecl', 'property', match[2], {
1634
+ binding: match[1],
1635
+ valueType: match[3]?.trim()
1636
+ }, declarationLine.includes('{') || declarationLine.includes('=>')));
1637
+ } else if ((match = declarationLine.match(/^(?:(?:public|private(?:\([^)]*\))?|fileprivate|internal|open)\s+)*typealias\s+([A-Za-z_]\w*)\b(?:\s*=\s*(.+))?/))) {
1638
+ declarations.push(nativeDeclaration(input, number, 'TypealiasDecl', 'type', match[1], { target: match[2]?.trim() }, false));
989
1639
  }
990
1640
  }
991
1641
  return declarations;
@@ -996,14 +1646,36 @@ function scanCSharp(input) {
996
1646
  for (const { line, number } of sourceLines(input.sourceText)) {
997
1647
  const trimmed = line.trim();
998
1648
  let match;
999
- if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
1649
+ if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
1650
+ declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
1651
+ } else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
1000
1652
  declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
1001
1653
  } else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
1002
1654
  declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, trimmed.includes('{')));
1003
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly)\s+)*(class|interface|struct|enum|record)\s+([A-Za-z_]\w*)/))) {
1004
- declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, csharpSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
1005
- } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern)\s+)*[A-Za-z_][\w<>\[\].?,\s]*\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:\{|;)?$/))) {
1006
- declarations.push(nativeDeclaration(input, number, 'MethodDeclaration', 'method', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
1655
+ } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|unsafe|new)\s+)*delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
1656
+ declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[2], {
1657
+ returnType: match[1].trim(),
1658
+ parameters: splitParameters(match[3])
1659
+ }, false));
1660
+ } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly|ref|unsafe)\s+)*(class|interface|struct|enum|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/))) {
1661
+ declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[1]), csharpSymbolKind(match[1]), match[2], { csharpKind: match[1].replace(/\s+/g, ' ') }, trimmed.includes('{')));
1662
+ } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern|new|unsafe|readonly)\s+)*(?:[A-Za-z_][\w<>\[\].?,\s]*\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:=>.*|\{|;)?$/))) {
1663
+ const parameters = splitParameters(match[2]);
1664
+ const extensionReceiver = csharpExtensionReceiver(parameters);
1665
+ declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', 'method', match[1], {
1666
+ parameters,
1667
+ ...(extensionReceiver ? { extensionReceiver } : {})
1668
+ }, trimmed.includes('{') || trimmed.includes('=>')));
1669
+ } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|unsafe)\s+)*event\s+(.+?)\s+([A-Za-z_]\w*)\s*(?:[;{=]|=>)/))) {
1670
+ declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', match[2], {
1671
+ eventType: match[1].trim(),
1672
+ accessors: csharpAccessors(trimmed)
1673
+ }, trimmed.includes('{')));
1674
+ } else if ((match = trimmed.match(/^(?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|required|readonly|unsafe)\s+)*([A-Za-z_][\w<>\[\].?,\s]*\??)\s+([A-Za-z_]\w*)\s*(?:\{|=>)/))) {
1675
+ declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', match[2], {
1676
+ propertyType: match[1].trim(),
1677
+ accessors: csharpAccessors(trimmed)
1678
+ }, trimmed.includes('{') || trimmed.includes('=>')));
1007
1679
  }
1008
1680
  }
1009
1681
  return declarations;
@@ -1347,6 +2019,48 @@ function upperFirst(value) {
1347
2019
  return String(value).charAt(0).toUpperCase() + String(value).slice(1);
1348
2020
  }
1349
2021
 
2022
+ function parseGoReceiver(raw) {
2023
+ const value = String(raw ?? '').trim();
2024
+ const match = value.match(/^(?:(\w+)\s+)?(.+)$/);
2025
+ const rawType = String(match?.[2] ?? value).trim();
2026
+ return {
2027
+ raw: value,
2028
+ ...(match?.[1] ? { name: match[1] } : {}),
2029
+ rawType,
2030
+ type: normalizeGoReceiverType(rawType)
2031
+ };
2032
+ }
2033
+
2034
+ function normalizeGoReceiverType(rawType) {
2035
+ return String(rawType ?? '')
2036
+ .trim()
2037
+ .replace(/^[*&\s]+/, '')
2038
+ .replace(/\[[^\]]+\]/g, '')
2039
+ .replace(/\s+/g, ' ');
2040
+ }
2041
+
2042
+ function goReceiverMethodName(receiver, methodName) {
2043
+ return receiver?.type ? `${receiver.type}.${methodName}` : methodName;
2044
+ }
2045
+
2046
+ function parseSwiftExtensionTail(rawTail) {
2047
+ let tail = String(rawTail ?? '').split('{')[0].trim();
2048
+ const fields = {};
2049
+ const whereMatch = tail.match(/\bwhere\b(.+)$/);
2050
+ if (whereMatch) {
2051
+ fields.constraints = whereMatch[1].trim();
2052
+ tail = tail.slice(0, whereMatch.index).trim();
2053
+ }
2054
+ if (tail.startsWith(':')) {
2055
+ fields.conformances = tail.slice(1).split(',').map((part) => part.trim()).filter(Boolean);
2056
+ }
2057
+ return fields;
2058
+ }
2059
+
2060
+ function unquoteSwiftIdentifier(identifier) {
2061
+ return String(identifier).replace(/^`|`$/g, '');
2062
+ }
2063
+
1350
2064
  function javaSymbolKind(kind) {
1351
2065
  if (kind === 'interface' || kind === '@interface') return 'interface';
1352
2066
  if (kind === 'enum' || kind === 'record') return 'type';
@@ -1361,11 +2075,29 @@ function swiftSymbolKind(kind) {
1361
2075
  }
1362
2076
 
1363
2077
  function csharpSymbolKind(kind) {
1364
- if (kind === 'interface') return 'interface';
1365
- if (kind === 'struct' || kind === 'enum' || kind === 'record') return 'type';
2078
+ const normalized = String(kind).replace(/\s+/g, ' ');
2079
+ if (normalized === 'interface') return 'interface';
2080
+ if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
1366
2081
  return 'class';
1367
2082
  }
1368
2083
 
2084
+ function csharpDeclarationKind(kind) {
2085
+ const normalized = String(kind).replace(/\s+/g, ' ');
2086
+ if (normalized === 'record struct') return 'RecordStructDeclaration';
2087
+ if (normalized === 'record class') return 'RecordClassDeclaration';
2088
+ if (normalized === 'record') return 'RecordDeclaration';
2089
+ return `${upperFirst(normalized)}Declaration`;
2090
+ }
2091
+
2092
+ function csharpExtensionReceiver(parameters) {
2093
+ const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
2094
+ return match ? { type: match[1].trim(), name: match[2] } : undefined;
2095
+ }
2096
+
2097
+ function csharpAccessors(source) {
2098
+ return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
2099
+ }
2100
+
1369
2101
  function phpSymbolKind(kind) {
1370
2102
  if (kind === 'interface') return 'interface';
1371
2103
  if (kind === 'trait') return 'trait';
@@ -1586,6 +2318,23 @@ function splitParameters(raw) {
1586
2318
  .filter(Boolean);
1587
2319
  }
1588
2320
 
2321
+ function splitTypeParameters(raw) {
2322
+ return splitParameters(raw);
2323
+ }
2324
+
2325
+ function braceDelta(source) {
2326
+ let delta = 0;
2327
+ for (const char of String(source ?? '')) {
2328
+ if (char === '{') delta += 1;
2329
+ if (char === '}') delta -= 1;
2330
+ }
2331
+ return delta;
2332
+ }
2333
+
2334
+ function jsControlKeyword(value) {
2335
+ return ['if', 'for', 'while', 'switch', 'catch', 'with'].includes(String(value));
2336
+ }
2337
+
1589
2338
  function inferSourceMapMappings(input) {
1590
2339
  const semanticIndex = input.semanticIndex;
1591
2340
  const nativeAst = input.nativeAst;
@@ -1895,6 +2644,251 @@ function nativeImportCategoryForLossKind(kind) {
1895
2644
  return String(kind ?? 'opaqueNative');
1896
2645
  }
1897
2646
 
2647
+ function nativeImportLanguageProfile(language, input = {}) {
2648
+ const lossKinds = input.lossKinds ?? ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation'];
2649
+ return Object.freeze({
2650
+ language,
2651
+ aliases: Object.freeze(uniqueStrings(input.aliases ?? [])),
2652
+ extensions: Object.freeze(uniqueStrings(input.extensions ?? [])),
2653
+ supportsLightweightScan: input.supportsLightweightScan !== false,
2654
+ parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? ['tree-sitter'])),
2655
+ projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? FrontierCompileTargets)),
2656
+ knownLossKinds: Object.freeze(uniqueStrings(lossKinds)),
2657
+ defaultReadiness: input.defaultReadiness ?? 'needs-review',
2658
+ notes: Object.freeze(uniqueStrings(input.notes ?? ['lightweight scanner records declarations only; exact parser adapters must be injected by the host']))
2659
+ });
2660
+ }
2661
+
2662
+ function mergeNativeImportProfiles(languages, imports, adapters) {
2663
+ const profilesByLanguage = new Map();
2664
+ for (const profile of languages) {
2665
+ const normalized = normalizeNativeLanguageId(profile.language ?? profile);
2666
+ profilesByLanguage.set(normalized, normalizeNativeImportLanguageProfile(profile, normalized));
2667
+ }
2668
+ for (const imported of imports) {
2669
+ const normalized = normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language);
2670
+ if (!normalized || profilesByLanguage.has(normalized)) continue;
2671
+ profilesByLanguage.set(normalized, nativeImportLanguageProfile(normalized, {
2672
+ supportsLightweightScan: false,
2673
+ parserAdapters: [],
2674
+ defaultReadiness: 'blocked',
2675
+ lossKinds: ['unsupportedSyntax'],
2676
+ notes: ['language appeared in import evidence but has no declared Frontier coverage profile']
2677
+ }));
2678
+ }
2679
+ for (const adapter of adapters) {
2680
+ const normalized = normalizeNativeLanguageId(adapter?.language);
2681
+ if (!normalized) continue;
2682
+ const existing = profilesByLanguage.get(normalized) ?? nativeImportLanguageProfile(normalized, { supportsLightweightScan: false, parserAdapters: [] });
2683
+ profilesByLanguage.set(normalized, {
2684
+ ...existing,
2685
+ parserAdapters: uniqueStrings([...(existing.parserAdapters ?? []), adapter.parser ?? adapter.id].filter(Boolean))
2686
+ });
2687
+ }
2688
+ return [...profilesByLanguage.values()].sort((left, right) => left.language.localeCompare(right.language));
2689
+ }
2690
+
2691
+ function normalizeNativeImportLanguageProfile(profile, fallbackLanguage) {
2692
+ const language = normalizeNativeLanguageId(profile.language ?? fallbackLanguage);
2693
+ return {
2694
+ language,
2695
+ aliases: uniqueStrings(profile.aliases ?? []),
2696
+ extensions: uniqueStrings(profile.extensions ?? []),
2697
+ supportsLightweightScan: profile.supportsLightweightScan !== false,
2698
+ parserAdapters: uniqueStrings(profile.parserAdapters ?? []),
2699
+ projectionTargets: uniqueStrings(profile.projectionTargets ?? FrontierCompileTargets),
2700
+ knownLossKinds: uniqueStrings(profile.knownLossKinds ?? profile.lossKinds ?? []),
2701
+ defaultReadiness: profile.defaultReadiness ?? 'needs-review',
2702
+ notes: uniqueStrings(profile.notes ?? [])
2703
+ };
2704
+ }
2705
+
2706
+ function nativeImportCoverageForProfile(profile, imports, adapters) {
2707
+ const aliases = new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
2708
+ const matchingImports = imports.filter((imported) => aliases.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language)));
2709
+ const matchingAdapters = adapters.filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.language)));
2710
+ const lossSummary = summarizeNativeImportLosses(matchingImports.flatMap((imported) => imported?.losses ?? []), {
2711
+ evidence: matchingImports.flatMap((imported) => imported?.evidence ?? [])
2712
+ });
2713
+ const readiness = matchingImports.length
2714
+ ? lossSummary.semanticMergeReadiness
2715
+ : profile.supportsLightweightScan ? profile.defaultReadiness : 'blocked';
2716
+ const importedParsers = uniqueStrings(matchingImports.map((imported) => imported?.nativeAst?.parser ?? imported?.parser ?? imported?.metadata?.parser).filter(Boolean));
2717
+ const sourceMaps = matchingImports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
2718
+ return {
2719
+ language: profile.language,
2720
+ aliases: profile.aliases,
2721
+ extensions: profile.extensions,
2722
+ supportsLightweightScan: profile.supportsLightweightScan,
2723
+ parserAdapters: uniqueStrings([...(profile.parserAdapters ?? []), ...matchingAdapters.map((adapter) => adapter.parser ?? adapter.id).filter(Boolean)]),
2724
+ projectionTargets: profile.projectionTargets,
2725
+ knownLossKinds: uniqueStrings([...(profile.knownLossKinds ?? []), ...Object.keys(lossSummary.byKind)]),
2726
+ defaultReadiness: profile.defaultReadiness,
2727
+ notes: profile.notes,
2728
+ imports: {
2729
+ total: matchingImports.length,
2730
+ parsers: importedParsers,
2731
+ readiness,
2732
+ readinessReasons: matchingImports.length ? lossSummary.readinessReasons : nativeImportCoverageReasons(profile),
2733
+ symbols: matchingImports.reduce((sum, imported) => sum + (imported?.semanticIndex?.symbols?.length ?? imported?.universalAst?.semanticIndex?.symbols?.length ?? 0), 0),
2734
+ sourceMaps: sourceMaps.length,
2735
+ sourceMapMappings: sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0),
2736
+ losses: lossSummary.total,
2737
+ lossKinds: lossSummary.byKind,
2738
+ lossCategories: lossSummary.categories
2739
+ }
2740
+ };
2741
+ }
2742
+
2743
+ function semanticImportSidecarEntry(imported, index, options) {
2744
+ const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
2745
+ const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
2746
+ const sourceMaps = imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? [];
2747
+ const sourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap?.mappings ?? []);
2748
+ const mappingsBySymbolId = new Map();
2749
+ for (const mapping of sourceMapMappings) {
2750
+ if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
2751
+ mappingsBySymbolId.set(mapping.semanticSymbolId, mapping);
2752
+ }
2753
+ }
2754
+ const symbols = [];
2755
+ const regions = [];
2756
+ for (const symbol of semanticIndex?.symbols ?? []) {
2757
+ const mapping = mappingsBySymbolId.get(symbol.id);
2758
+ const nativeNode = symbol.nativeAstNodeId ? nativeAst?.nodes?.[symbol.nativeAstNodeId] : undefined;
2759
+ const region = semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode, options);
2760
+ regions.push(region);
2761
+ symbols.push({
2762
+ id: symbol.id,
2763
+ name: symbol.name,
2764
+ kind: symbol.kind,
2765
+ language: symbol.language ?? imported?.language,
2766
+ nativeAstNodeId: symbol.nativeAstNodeId,
2767
+ semanticOccurrenceId: mapping?.semanticOccurrenceId,
2768
+ sourceMapMappingId: mapping?.id,
2769
+ sourceSpan: mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span,
2770
+ signatureHash: symbol.signatureHash,
2771
+ ownershipRegionId: region.id,
2772
+ ownershipKey: region.key,
2773
+ readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review'
2774
+ });
2775
+ }
2776
+ return {
2777
+ id: imported?.id ?? `import_${index + 1}`,
2778
+ language: imported?.language,
2779
+ sourcePath: imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? nativeAst?.sourcePath,
2780
+ sourceHash: imported?.nativeSource?.sourceHash ?? nativeAst?.sourceHash,
2781
+ parser: imported?.nativeAst?.parser ?? nativeAst?.parser,
2782
+ nativeSourceId: imported?.nativeSource?.id,
2783
+ nativeAstId: nativeAst?.id,
2784
+ semanticIndexId: semanticIndex?.id,
2785
+ universalAstId: imported?.universalAst?.id,
2786
+ symbolCount: symbols.length,
2787
+ sourceMapCount: sourceMaps.length,
2788
+ sourceMapMappingCount: sourceMapMappings.length,
2789
+ readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
2790
+ emptySemanticIndex: symbols.length === 0,
2791
+ symbols,
2792
+ ownershipRegions: uniqueRecordsById(regions)
2793
+ };
2794
+ }
2795
+
2796
+ function semanticOwnershipRegionForSymbol(imported, symbol, mapping, nativeNode, options = {}) {
2797
+ const sourcePath = mapping?.sourceSpan?.path ?? symbol.definitionSpan?.path ?? nativeNode?.span?.path ?? imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? imported?.nativeAst?.sourcePath;
2798
+ const language = symbol.language ?? imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language;
2799
+ const sourceSpan = mapping?.sourceSpan ?? symbol.definitionSpan ?? nativeNode?.span;
2800
+ const key = [
2801
+ options.regionPrefix ?? 'source',
2802
+ sourcePath ?? `${language}:memory`,
2803
+ symbol.kind ?? 'symbol',
2804
+ symbol.name ?? symbol.id
2805
+ ].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
2806
+ return {
2807
+ id: `region_${idFragment(key)}`,
2808
+ key,
2809
+ granularity: 'symbol',
2810
+ language,
2811
+ sourcePath,
2812
+ sourceHash: imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash,
2813
+ symbolId: symbol.id,
2814
+ symbolName: symbol.name,
2815
+ symbolKind: symbol.kind,
2816
+ nativeAstNodeId: symbol.nativeAstNodeId ?? nativeNode?.id,
2817
+ sourceSpan,
2818
+ precision: mapping?.precision ?? (sourceSpan ? 'declaration' : 'unknown'),
2819
+ mergePolicy: 'single-writer-review-required'
2820
+ };
2821
+ }
2822
+
2823
+ function semanticOwnershipRegionForDeclaration(input, declaration, documentId) {
2824
+ const name = declaration.name ?? declaration.importPath ?? declaration.nodeId ?? declaration.nativeNode?.id;
2825
+ const kind = declaration.symbolKind ?? declaration.kind ?? declaration.nativeNode?.kind ?? 'symbol';
2826
+ const sourcePath = declaration.span?.path ?? declaration.nativeNode?.span?.path ?? input.sourcePath ?? `${input.language}:memory`;
2827
+ const key = ['source', sourcePath, kind, name].map((part) => String(part).replace(/\s+/g, ' ').trim()).join('#');
2828
+ return {
2829
+ id: `region_${idFragment(key)}`,
2830
+ key,
2831
+ granularity: 'symbol',
2832
+ language: input.language,
2833
+ documentId,
2834
+ sourcePath,
2835
+ sourceHash: input.sourceHash,
2836
+ symbolId: declaration.symbolId,
2837
+ symbolName: name,
2838
+ symbolKind: kind,
2839
+ nativeAstNodeId: declaration.nodeId ?? declaration.nativeNode?.id,
2840
+ sourceSpan: declaration.span ?? declaration.nativeNode?.span,
2841
+ precision: declaration.span || declaration.nativeNode?.span ? 'declaration' : 'unknown',
2842
+ mergePolicy: 'single-writer-review-required'
2843
+ };
2844
+ }
2845
+
2846
+ function semanticPatchHintForRegion(region, readiness, options = {}) {
2847
+ return {
2848
+ id: `hint_${idFragment(region.id)}`,
2849
+ kind: 'source-region-patch',
2850
+ ownershipRegionId: region.id,
2851
+ ownershipKey: region.key,
2852
+ sourcePath: region.sourcePath,
2853
+ sourceHash: region.sourceHash,
2854
+ sourceSpan: region.sourceSpan,
2855
+ readiness,
2856
+ precision: region.precision,
2857
+ supportedOperations: ['replace-region', 'insert-before-region', 'insert-after-region'],
2858
+ projection: {
2859
+ sourceLanguage: region.language,
2860
+ targetPath: options.targetPath ?? region.sourcePath,
2861
+ requiresSourceMap: true
2862
+ }
2863
+ };
2864
+ }
2865
+
2866
+ function nativeImportCoverageReasons(profile) {
2867
+ if (!profile.supportsLightweightScan) return ['No built-in scanner coverage profile; host must provide an exact adapter or mark unsupported.'];
2868
+ return ['Built-in coverage is declaration-level only; use injected parser adapters for exact AST/CST, tokens, trivia, type resolution, and round-trip evidence.'];
2869
+ }
2870
+
2871
+ function normalizeNativeLanguageId(value) {
2872
+ if (!value) return '';
2873
+ const text = String(value).trim().toLowerCase();
2874
+ if (text === 'js' || text === 'mjs' || text === 'cjs' || text === 'jsx') return 'javascript';
2875
+ if (text === 'ts' || text === 'tsx') return 'typescript';
2876
+ if (text === 'py' || text === 'pyi') return 'python';
2877
+ if (text === 'rs') return 'rust';
2878
+ if (text === 'h') return 'c';
2879
+ if (text === 'c++' || text === 'cc' || text === 'cxx' || text === 'hpp' || text === 'hh') return 'cpp';
2880
+ if (text === 'c#' || text === 'cs') return 'csharp';
2881
+ if (text === 'rb' || text === 'rake') return 'ruby';
2882
+ if (text === 'kt' || text === 'kts') return 'kotlin';
2883
+ if (text === 'sc') return 'scala';
2884
+ if (text === 'sh' || text === 'bash' || text === 'zsh') return 'shell';
2885
+ if (text === 'postgresql' || text === 'postgres' || text === 'mysql' || text === 'sqlite') return 'sql';
2886
+ if (text === 'ex' || text === 'exs') return 'elixir';
2887
+ if (text === 'erl' || text === 'hrl') return 'erlang';
2888
+ if (text === 'hs' || text === 'lhs') return 'haskell';
2889
+ return text;
2890
+ }
2891
+
1898
2892
  function semanticMergeAdmissionForSeverity(severity) {
1899
2893
  if (severity === 'error') return 'blocked';
1900
2894
  if (severity === 'warning') return 'review';
@@ -2335,6 +3329,19 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
2335
3329
  for (const declaration of declarations) {
2336
3330
  const symbolId = declaration.symbolId ?? `symbol:${input.language}:${declaration.role === 'import' ? 'import:' : ''}${idFragment(declaration.name)}`;
2337
3331
  const occurrenceId = `occ_${idFragment(declaration.nativeNode.id)}_${declaration.role ?? 'definition'}`;
3332
+ const ownershipRegion = semanticOwnershipRegionForDeclaration(input, {
3333
+ ...declaration,
3334
+ nodeId: declaration.nativeNode.id,
3335
+ kind: declaration.nativeNode.kind,
3336
+ languageKind: declaration.nativeNode.languageKind,
3337
+ span: declaration.nativeNode.span,
3338
+ symbolId
3339
+ }, documentId);
3340
+ declaration.nativeNode.metadata = {
3341
+ ...declaration.nativeNode.metadata,
3342
+ ownershipRegionId: ownershipRegion.id,
3343
+ ownershipRegionKey: ownershipRegion.key
3344
+ };
2338
3345
  symbols.push({
2339
3346
  id: symbolId,
2340
3347
  scheme: 'frontier',
@@ -2343,7 +3350,11 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
2343
3350
  language: input.language,
2344
3351
  nativeAstNodeId: declaration.nativeNode.id,
2345
3352
  signatureHash: hashSemanticValue([input.language, declaration.nativeNode.kind, declaration.name, declaration.nativeNode.fields ?? {}]),
2346
- definitionSpan: declaration.nativeNode.span
3353
+ definitionSpan: declaration.nativeNode.span,
3354
+ metadata: {
3355
+ ownershipRegionId: ownershipRegion.id,
3356
+ ownershipRegionKey: ownershipRegion.key
3357
+ }
2347
3358
  });
2348
3359
  occurrences.push({
2349
3360
  id: occurrenceId,
@@ -2364,6 +3375,11 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
2364
3375
  predicate: 'nativeKind',
2365
3376
  subjectId: symbolId,
2366
3377
  value: declaration.nativeNode.languageKind
3378
+ }, {
3379
+ id: `fact_${idFragment(declaration.nativeNode.id)}_ownership_region`,
3380
+ predicate: 'semanticOwnershipRegion',
3381
+ subjectId: symbolId,
3382
+ value: ownershipRegion
2367
3383
  });
2368
3384
  mappings.push({
2369
3385
  id: `map_${idFragment(declaration.nativeNode.id)}`,
@@ -2373,6 +3389,7 @@ function semanticIndexFromNativeDeclarations(declarations, input, options) {
2373
3389
  sourceSpan: declaration.nativeNode.span,
2374
3390
  evidenceIds: [evidenceId],
2375
3391
  lossIds: [],
3392
+ ownershipRegionId: ownershipRegion.id,
2376
3393
  precision: declaration.nativeNode.span ? 'declaration' : 'unknown'
2377
3394
  });
2378
3395
  }