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

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
@@ -10,6 +10,7 @@ import {
10
10
  hashSemanticValue,
11
11
  nativeSourceNode,
12
12
  stableUniversalAstJson,
13
+ validateSourceMapRecord,
13
14
  validateUniversalAstEnvelope
14
15
  } from '@shapeshift-labs/frontier-lang-kernel';
15
16
  import { parseFrontierFile, parseFrontierSource } from '@shapeshift-labs/frontier-lang-parser';
@@ -52,6 +53,56 @@ const canonicalTargets = Object.freeze({
52
53
  h: 'c'
53
54
  });
54
55
 
56
+ const lossSeverityRank = Object.freeze({
57
+ none: 0,
58
+ info: 1,
59
+ warning: 2,
60
+ error: 3
61
+ });
62
+
63
+ const semanticMergeReadinessRank = Object.freeze({
64
+ ready: 0,
65
+ 'ready-with-losses': 1,
66
+ 'needs-review': 2,
67
+ blocked: 3
68
+ });
69
+
70
+ export const NativeImportTaxonomyKinds = Object.freeze([
71
+ 'exactAstImport',
72
+ 'declarationsOnly',
73
+ 'opaqueBodies',
74
+ 'macroExpansion',
75
+ 'preprocessor',
76
+ 'metaprogramming',
77
+ 'generatedCode',
78
+ 'sourcePreservation',
79
+ 'parserDiagnostics',
80
+ 'unsupportedSyntax',
81
+ 'partialSemanticIndex',
82
+ 'sourceMapApproximation'
83
+ ]);
84
+
85
+ export const NativeImportLossKinds = Object.freeze([
86
+ 'declarationOnlyCoverage',
87
+ 'opaqueNative',
88
+ 'macroExpansion',
89
+ 'preprocessor',
90
+ 'metaprogramming',
91
+ 'generatedCode',
92
+ 'sourcePreservation',
93
+ 'parserDiagnostic',
94
+ 'unsupportedSyntax',
95
+ 'partialSemanticIndex',
96
+ 'sourceMapApproximation'
97
+ ]);
98
+
99
+ export const NativeImportReadinessBySeverity = Object.freeze({
100
+ none: 'ready',
101
+ info: 'ready-with-losses',
102
+ warning: 'needs-review',
103
+ error: 'blocked'
104
+ });
105
+
55
106
  export function normalizeCompileTarget(target) {
56
107
  const normalized = String(target ?? 'typescript').toLowerCase();
57
108
  const canonical = canonicalTargets[normalized] ?? normalized;
@@ -136,6 +187,187 @@ export function resolveCapabilityAdapters(document, target = 'typescript', optio
136
187
  });
137
188
  }
138
189
 
190
+ export function summarizeNativeImportLosses(losses = [], options = {}) {
191
+ const normalizedLosses = normalizeNativeLossRecords(losses);
192
+ const bySeverity = { info: 0, warning: 0, error: 0 };
193
+ const byKind = {};
194
+ const blockingLossIds = [];
195
+ const reviewLossIds = [];
196
+ const informationalLossIds = [];
197
+ let highestSeverity = 'none';
198
+
199
+ for (const loss of normalizedLosses) {
200
+ bySeverity[loss.severity] += 1;
201
+ byKind[loss.kind] = (byKind[loss.kind] ?? 0) + 1;
202
+ if (lossSeverityRank[loss.severity] > lossSeverityRank[highestSeverity]) {
203
+ highestSeverity = loss.severity;
204
+ }
205
+ if (loss.severity === 'error') blockingLossIds.push(loss.id);
206
+ else if (loss.severity === 'warning') reviewLossIds.push(loss.id);
207
+ else informationalLossIds.push(loss.id);
208
+ }
209
+
210
+ const failedEvidenceIds = (options.evidence ?? [])
211
+ .filter((record) => record?.status === 'failed')
212
+ .map((record) => record.id)
213
+ .filter(Boolean);
214
+ const exactAst = Boolean(options.exactAst) && normalizedLosses.length === 0;
215
+ const categories = uniqueStrings([
216
+ ...(exactAst ? ['exactAstImport'] : []),
217
+ ...normalizedLosses.map((loss) => nativeImportCategoryForLossKind(loss.kind))
218
+ ]);
219
+ const semanticMergeReadiness = failedEvidenceIds.length
220
+ ? 'blocked'
221
+ : NativeImportReadinessBySeverity[highestSeverity];
222
+ const readinessReasons = nativeImportReadinessReasons({
223
+ exactAst,
224
+ failedEvidenceIds,
225
+ blockingLossIds,
226
+ reviewLossIds,
227
+ informationalLossIds
228
+ });
229
+
230
+ return {
231
+ total: normalizedLosses.length,
232
+ hasLosses: normalizedLosses.length > 0,
233
+ exactAst,
234
+ highestSeverity,
235
+ semanticMergeReadiness,
236
+ readinessReasons,
237
+ categories,
238
+ bySeverity,
239
+ byKind,
240
+ blockingLossIds,
241
+ reviewLossIds,
242
+ informationalLossIds,
243
+ failedEvidenceIds,
244
+ parser: options.parser,
245
+ scanKind: options.scanKind,
246
+ semanticStatus: options.semanticStatus
247
+ };
248
+ }
249
+
250
+ export function classifyNativeImportReadiness(losses = [], options = {}) {
251
+ const summary = summarizeNativeImportLosses(losses, options);
252
+ return {
253
+ readiness: summary.semanticMergeReadiness,
254
+ reasons: summary.readinessReasons,
255
+ summary
256
+ };
257
+ }
258
+
259
+ export function createEstreeNativeImporterAdapter(options = {}) {
260
+ return createJavaScriptSyntaxImporterAdapter({
261
+ id: 'frontier.estree-native-importer',
262
+ language: 'javascript',
263
+ parser: 'estree',
264
+ supportedExtensions: ['.js', '.mjs', '.cjs', '.jsx'],
265
+ astFormat: 'estree',
266
+ ...options
267
+ });
268
+ }
269
+
270
+ export function createBabelNativeImporterAdapter(options = {}) {
271
+ return createJavaScriptSyntaxImporterAdapter({
272
+ id: 'frontier.babel-native-importer',
273
+ language: 'javascript',
274
+ parser: 'babel',
275
+ supportedExtensions: ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx'],
276
+ astFormat: 'babel',
277
+ defaultParserOptions: {
278
+ errorRecovery: true,
279
+ ranges: true,
280
+ sourceType: 'unambiguous',
281
+ plugins: ['typescript', 'jsx']
282
+ },
283
+ ...options
284
+ });
285
+ }
286
+
287
+ export function createTypeScriptCompilerNativeImporterAdapter(options = {}) {
288
+ return {
289
+ id: options.id ?? 'frontier.typescript-compiler-native-importer',
290
+ language: options.language ?? 'typescript',
291
+ parser: options.parser ?? 'typescript-compiler-api',
292
+ version: options.version,
293
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
294
+ supportedExtensions: options.supportedExtensions ?? ['.ts', '.tsx', '.js', '.jsx'],
295
+ diagnostics: options.diagnostics,
296
+ parse(input) {
297
+ const ts = options.typescript ?? options.ts ?? input.options?.typescript ?? input.options?.ts;
298
+ const sourceFile = input.options?.sourceFile ?? input.options?.ast ?? options.sourceFile ?? createTypeScriptSourceFile(ts, input, options);
299
+ if (!sourceFile) {
300
+ return missingInjectedParserResult(input, {
301
+ parser: options.parser ?? 'typescript-compiler-api',
302
+ adapterId: options.id ?? 'frontier.typescript-compiler-native-importer',
303
+ message: 'createTypeScriptCompilerNativeImporterAdapter requires an injected TypeScript module, createSourceFile function, sourceFile, or adapterOptions.sourceFile.'
304
+ });
305
+ }
306
+ return createNativeImportFromTypeScriptAst(sourceFile, input, {
307
+ parser: options.parser ?? 'typescript-compiler-api',
308
+ astFormat: 'typescript-compiler-api',
309
+ ts,
310
+ maxNodes: options.maxNodes,
311
+ includeTokens: options.includeTokens
312
+ });
313
+ }
314
+ };
315
+ }
316
+
317
+ export function createTreeSitterNativeImporterAdapter(options = {}) {
318
+ return {
319
+ id: options.id ?? `frontier.tree-sitter-${idFragment(options.language ?? 'source')}-native-importer`,
320
+ language: options.language ?? 'source',
321
+ parser: options.parserName ?? options.parser ?? 'tree-sitter',
322
+ version: options.version,
323
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
324
+ supportedExtensions: options.supportedExtensions ?? [],
325
+ diagnostics: options.diagnostics,
326
+ parse(input) {
327
+ const tree = input.options?.tree ?? options.tree ?? parseTreeSitterSource(input, options);
328
+ const root = tree?.rootNode ?? tree;
329
+ if (!root) {
330
+ return missingInjectedParserResult(input, {
331
+ parser: options.parserName ?? options.parser ?? 'tree-sitter',
332
+ adapterId: options.id ?? `frontier.tree-sitter-${idFragment(options.language ?? input.language)}-native-importer`,
333
+ message: 'createTreeSitterNativeImporterAdapter requires an injected tree-sitter parser/tree or adapterOptions.tree.'
334
+ });
335
+ }
336
+ return createNativeImportFromTreeSitter(root, input, {
337
+ parser: options.parserName ?? options.parser ?? 'tree-sitter',
338
+ astFormat: 'tree-sitter',
339
+ maxNodes: options.maxNodes
340
+ });
341
+ }
342
+ };
343
+ }
344
+
345
+ export async function importNativeProject(input = {}) {
346
+ const sources = input.sources ?? [];
347
+ const adapters = input.adapters ?? [];
348
+ const imports = [];
349
+ for (const [index, source] of sources.entries()) {
350
+ const adapter = source.adapter && typeof source.adapter === 'object'
351
+ ? source.adapter
352
+ : resolveNativeProjectAdapter(source, adapters, input);
353
+ if (adapter) {
354
+ imports.push(await runNativeImporterAdapter(adapter, {
355
+ ...source,
356
+ adapterOptions: source.adapterOptions ?? input.adapterOptions,
357
+ adapterMetadata: {
358
+ projectImportId: input.id,
359
+ sourceIndex: index,
360
+ ...input.adapterMetadata,
361
+ ...source.adapterMetadata
362
+ }
363
+ }));
364
+ } else {
365
+ imports.push(importNativeSource(source));
366
+ }
367
+ }
368
+ return createNativeProjectImportResult(input, imports);
369
+ }
370
+
139
371
  export async function runNativeImporterAdapter(adapter, input = {}) {
140
372
  const summary = normalizeNativeImporterAdapter(adapter);
141
373
  const language = input.language ?? summary.language;
@@ -260,6 +492,8 @@ export function importNativeSource(input) {
260
492
  if (!language) throw new Error('importNativeSource requires a language or nativeAst.language');
261
493
  const sourcePath = input.sourcePath ?? input.nativeAst?.sourcePath;
262
494
  const sourceHash = input.sourceHash ?? input.nativeAst?.sourceHash ?? (input.sourceText ? hashSemanticValue(input.sourceText) : hashSemanticValue(input.nativeAst?.nodes ?? input.nativeAst ?? {}));
495
+ const targetPath = input.targetPath ?? input.target?.emitPath;
496
+ const targetHash = input.targetHash;
263
497
  const importIdPart = idFragment(input.id ?? input.nativeSourceId ?? sourcePath ?? language);
264
498
  const lightweight = !input.nativeAst && !input.nodes && input.sourceText
265
499
  ? createLightweightNativeImport({
@@ -295,7 +529,9 @@ export function importNativeSource(input) {
295
529
  }
296
530
  });
297
531
  const frontierNodeIds = input.frontierNodeIds ?? input.semanticNodes?.map((node) => node.id) ?? [];
298
- const losses = input.losses ?? nativeAst.losses ?? lightweight?.losses ?? [];
532
+ const semanticNodes = input.semanticNodes ?? [];
533
+ const semanticStatus = input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only');
534
+ const losses = normalizeNativeLossRecords(input.losses ?? nativeAst.losses ?? lightweight?.losses ?? []);
299
535
  const nativeSource = nativeSourceNode({
300
536
  id: input.nativeSourceId ?? `native_source_${importIdPart}`,
301
537
  name: input.name ?? sourcePath?.split(/[\\/]/).filter(Boolean).at(-1) ?? `${language}NativeSource`,
@@ -310,12 +546,11 @@ export function importNativeSource(input) {
310
546
  losses,
311
547
  target: input.target,
312
548
  metadata: {
313
- semanticStatus: input.semanticStatus ?? (input.semanticNodes?.length ? 'mapped' : 'native-only'),
549
+ semanticStatus,
314
550
  mappings: input.mappings ?? [],
315
551
  ...input.nativeSourceMetadata
316
552
  }
317
553
  });
318
- const semanticNodes = input.semanticNodes ?? [];
319
554
  const document = createDocument({
320
555
  id: input.documentId ?? `document_${importIdPart}`,
321
556
  name: input.documentName ?? nativeSource.name,
@@ -323,11 +558,11 @@ export function importNativeSource(input) {
323
558
  rootIds: input.rootIds,
324
559
  metadata: {
325
560
  sourceLanguage: language,
326
- semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only'),
561
+ semanticStatus,
327
562
  ...input.documentMetadata
328
563
  }
329
564
  });
330
- const evidence = input.evidence ?? [{
565
+ const baseEvidence = input.evidence ?? [{
331
566
  id: input.evidenceId ?? `evidence_${importIdPart}_import`,
332
567
  kind: 'import',
333
568
  status: losses.some((loss) => loss.severity === 'error') ? 'failed' : 'passed',
@@ -336,9 +571,17 @@ export function importNativeSource(input) {
336
571
  metadata: {
337
572
  parser: nativeAst.parser,
338
573
  sourcePath,
339
- semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only')
574
+ semanticStatus
340
575
  }
341
576
  }];
577
+ const lossSummary = summarizeNativeImportLosses(losses, {
578
+ exactAst: Boolean(input.nativeAst || input.nodes),
579
+ evidence: baseEvidence,
580
+ parser: nativeAst.parser,
581
+ scanKind: lightweight?.metadata?.scanKind,
582
+ semanticStatus
583
+ });
584
+ const evidence = attachNativeImportLossSummary(baseEvidence, lossSummary);
342
585
  const semanticIndex = input.semanticIndex ?? lightweight?.semanticIndex;
343
586
  const sourceMapMappings = normalizeSourceMapMappings(
344
587
  input.mappings ?? lightweight?.mappings ?? inferSourceMapMappings({
@@ -353,16 +596,20 @@ export function importNativeSource(input) {
353
596
  nativeSource,
354
597
  evidence,
355
598
  losses,
356
- target: input.target
599
+ target: input.target,
600
+ targetPath,
601
+ targetHash
357
602
  }
358
603
  );
604
+ const inferredTargetPath = targetPath ?? commonGeneratedTargetPath(sourceMapMappings);
359
605
  const inferredSourceMaps = sourceMapMappings.length
360
606
  ? [createSourceMapRecord({
361
607
  id: input.sourceMapId ?? `source_map_${importIdPart}`,
362
608
  sourcePath,
363
609
  sourceHash,
364
610
  target: input.target,
365
- targetPath: input.target?.emitPath,
611
+ targetPath: inferredTargetPath,
612
+ targetHash,
366
613
  semanticIndexId: semanticIndex?.id,
367
614
  nativeAstId: nativeAst.id,
368
615
  nativeSourceId: nativeSource.id,
@@ -371,11 +618,26 @@ export function importNativeSource(input) {
371
618
  metadata: {
372
619
  sourceLanguage: language,
373
620
  parser: nativeAst.parser,
374
- semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only')
621
+ semanticStatus
375
622
  }
376
623
  })]
377
624
  : [];
378
- const sourceMaps = input.sourceMaps ?? inferredSourceMaps;
625
+ const sourceMaps = normalizeSourceMaps(input.sourceMaps ?? inferredSourceMaps, {
626
+ document,
627
+ nativeSources: [nativeSource],
628
+ nativeAst,
629
+ nativeSource,
630
+ semanticIndex,
631
+ evidence,
632
+ losses,
633
+ target: input.target,
634
+ targetPath: inferredTargetPath,
635
+ targetHash,
636
+ sourcePath,
637
+ sourceHash,
638
+ defaultSourceMapId: `source_map_${importIdPart}`
639
+ });
640
+ const resultSourceMapMappings = sourceMaps.flatMap((sourceMap) => sourceMap.mappings ?? []);
379
641
  const universalAst = createUniversalAstEnvelope({
380
642
  id: input.universalAstId ?? `universal_ast_${importIdPart}`,
381
643
  document,
@@ -387,7 +649,8 @@ export function importNativeSource(input) {
387
649
  metadata: {
388
650
  sourceLanguage: language,
389
651
  sourcePath,
390
- semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only'),
652
+ semanticStatus,
653
+ nativeImportLossSummary: lossSummary,
391
654
  ...input.universalAstMetadata
392
655
  }
393
656
  });
@@ -406,32 +669,35 @@ export function importNativeSource(input) {
406
669
  sourcePath,
407
670
  semanticIndexId: semanticIndex?.id,
408
671
  universalAstId: universalAst.id,
409
- sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id)
672
+ sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
673
+ nativeImportLossSummary: lossSummary
674
+ }
675
+ });
676
+ const importResult = createImportResult({
677
+ id: input.id ?? `import_${importIdPart}`,
678
+ language,
679
+ sourcePath,
680
+ document,
681
+ patch,
682
+ nativeAst,
683
+ semanticIndex,
684
+ universalAst,
685
+ sourceMaps,
686
+ losses,
687
+ evidence,
688
+ metadata: {
689
+ nativeSourceId: nativeSource.id,
690
+ semanticIndexId: semanticIndex?.id,
691
+ universalAstId: universalAst.id,
692
+ sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
693
+ semanticStatus,
694
+ mappings: resultSourceMapMappings,
695
+ nativeImportLossSummary: lossSummary,
696
+ ...input.metadata
410
697
  }
411
698
  });
412
699
  return {
413
- ...createImportResult({
414
- id: input.id ?? `import_${importIdPart}`,
415
- language,
416
- sourcePath,
417
- document,
418
- patch,
419
- nativeAst,
420
- semanticIndex,
421
- universalAst,
422
- sourceMaps,
423
- losses,
424
- evidence,
425
- metadata: {
426
- nativeSourceId: nativeSource.id,
427
- semanticIndexId: semanticIndex?.id,
428
- universalAstId: universalAst.id,
429
- sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
430
- semanticStatus: input.semanticStatus ?? (semanticNodes.length ? 'mapped' : 'native-only'),
431
- mappings: sourceMapMappings,
432
- ...input.metadata
433
- }
434
- }),
700
+ ...withNativeImportReadiness(importResult, lossSummary),
435
701
  nativeSource
436
702
  };
437
703
  }
@@ -566,6 +832,17 @@ function scanNativeDeclarations(input) {
566
832
  if (language === 'csharp' || language === 'c#') return scanCSharp(input);
567
833
  if (language === 'php') return scanPhp(input);
568
834
  if (language === 'ruby' || language === 'rb') return scanRuby(input);
835
+ if (language === 'kotlin' || language === 'kt') return scanKotlin(input);
836
+ if (language === 'scala' || language === 'sc') return scanScala(input);
837
+ if (language === 'dart') return scanDart(input);
838
+ if (language === 'lua') return scanLua(input);
839
+ if (language === 'shell' || language === 'sh' || language === 'bash' || language === 'zsh') return scanShell(input);
840
+ if (language === 'sql' || language === 'postgresql' || language === 'postgres' || language === 'mysql' || language === 'sqlite') return scanSql(input);
841
+ if (language === 'zig') return scanZig(input);
842
+ if (language === 'elixir' || language === 'ex' || language === 'exs') return scanElixir(input);
843
+ if (language === 'erlang' || language === 'erl' || language === 'hrl') return scanErlang(input);
844
+ if (language === 'haskell' || language === 'hs') return scanHaskell(input);
845
+ if (language === 'r') return scanR(input);
569
846
  return scanGenericDeclarations(input);
570
847
  }
571
848
 
@@ -768,6 +1045,298 @@ function scanRuby(input) {
768
1045
  return declarations;
769
1046
  }
770
1047
 
1048
+ function scanKotlin(input) {
1049
+ const declarations = [];
1050
+ for (const { line, number } of sourceLines(input.sourceText)) {
1051
+ const trimmed = line.trim();
1052
+ let match;
1053
+ if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
1054
+ declarations.push(nativeDeclaration(input, number, 'PackageHeader', 'package', match[1], {}, false));
1055
+ } else if ((match = trimmed.match(/^import\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?:\.\*)?)(?:\s+as\s+[A-Za-z_]\w*)?$/))) {
1056
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDirective', 'package'));
1057
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|sealed|data|value)\s+)*(?:(enum|annotation)\s+)?(class|interface|object)\s+([A-Za-z_]\w*)/))) {
1058
+ declarations.push(nativeDeclaration(input, number, kotlinDeclarationKind(match[2], match[1]), kotlinSymbolKind(match[2], match[1]), match[3], {}, trimmed.includes('{')));
1059
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|inline|tailrec|operator|infix|external|suspend|override)\s+)*fun\s+(?:<[^>]+>\s*)?(?:[A-Za-z_][\w.<>?]*\.)?([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
1060
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=')));
1061
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual)\s+)*typealias\s+([A-Za-z_]\w*)\s*=/))) {
1062
+ declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
1063
+ } else if ((match = trimmed.match(/^(?:(?:public|private|protected|internal|expect|actual|open|final|abstract|override|const|lateinit)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
1064
+ declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'variable', match[1], {}, false));
1065
+ }
1066
+ }
1067
+ return declarations;
1068
+ }
1069
+
1070
+ function scanScala(input) {
1071
+ const declarations = [];
1072
+ for (const { line, number } of sourceLines(input.sourceText)) {
1073
+ const trimmed = line.trim();
1074
+ let match;
1075
+ if ((match = trimmed.match(/^package\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/))) {
1076
+ declarations.push(nativeDeclaration(input, number, 'PackageClause', 'package', match[1], {}, false));
1077
+ } else if ((match = trimmed.match(/^import\s+(.+?);?$/))) {
1078
+ declarations.push(nativeImportDeclaration(input, number, match[1].trim(), 'Import', 'package'));
1079
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|sealed|abstract|case|implicit|lazy|override|inline|transparent|open)\s+)*(class|trait|object|enum)\s+([A-Za-z_]\w*)/))) {
1080
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Def`, scalaSymbolKind(match[1]), match[2], {}, trimmed.includes('{') || trimmed.includes(':')));
1081
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|override|inline)\s+)*def\s+([A-Za-z_]\w*)\s*(?:\[[^\]]+\])?\s*\(([^)]*)\)/))) {
1082
+ declarations.push(nativeDeclaration(input, number, 'DefDef', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=')));
1083
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|opaque)\s+)*type\s+([A-Za-z_]\w*)\b/))) {
1084
+ declarations.push(nativeDeclaration(input, number, 'TypeDef', 'type', match[1], {}, false));
1085
+ } else if ((match = trimmed.match(/^(?:(?:private|protected|final|implicit|lazy|override|inline)\s+)*(?:val|var)\s+([A-Za-z_]\w*)\b/))) {
1086
+ declarations.push(nativeDeclaration(input, number, 'ValDef', 'variable', match[1], {}, false));
1087
+ }
1088
+ }
1089
+ return declarations;
1090
+ }
1091
+
1092
+ function scanDart(input) {
1093
+ const declarations = [];
1094
+ for (const { line, number } of sourceLines(input.sourceText)) {
1095
+ const trimmed = line.trim();
1096
+ let match;
1097
+ if ((match = trimmed.match(/^(?:import|export)\s+['"]([^'"]+)['"]/))) {
1098
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UriBasedDirective', 'library'));
1099
+ } else if ((match = trimmed.match(/^part\s+['"]([^'"]+)['"]/))) {
1100
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'PartDirective', 'library'));
1101
+ } else if ((match = trimmed.match(/^(?:(?:abstract|base|final|interface|sealed)\s+)*(class|mixin|enum)\s+([A-Za-z_]\w*)/))) {
1102
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, dartSymbolKind(match[1]), match[2], {}, trimmed.includes('{')));
1103
+ } else if ((match = trimmed.match(/^extension\s+([A-Za-z_]\w*)\s+on\s+.+\{/))) {
1104
+ declarations.push(nativeDeclaration(input, number, 'ExtensionDeclaration', 'implementation', match[1], {}, true));
1105
+ } else if ((match = trimmed.match(/^typedef\s+([A-Za-z_]\w*)\b/))) {
1106
+ declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], {}, false));
1107
+ } else if ((match = trimmed.match(/^(?:(?:external|static)\s+)*(?:[A-Za-z_]\w*(?:<[^>]+>)?\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:async\s*)?(?:\{|=>|;)/))) {
1108
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{') || trimmed.includes('=>')));
1109
+ } else if ((match = trimmed.match(/^(?:(?:static|external|late)\s+)*(?:const|final|var)\s+(?:[A-Za-z_]\w*(?:<[^>]+>)?\??\s+)?([A-Za-z_]\w*)\b/))) {
1110
+ declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
1111
+ }
1112
+ }
1113
+ return declarations;
1114
+ }
1115
+
1116
+ function scanLua(input) {
1117
+ const declarations = [];
1118
+ for (const { line, number } of sourceLines(input.sourceText)) {
1119
+ const trimmed = line.trim();
1120
+ let match;
1121
+ if ((match = trimmed.match(/^(?:local\s+[A-Za-z_]\w*\s*=\s*)?require\s*\(?\s*['"]([^'"]+)['"]\s*\)?/))) {
1122
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'RequireCall', 'module'));
1123
+ } else if ((match = trimmed.match(/^(?:local\s+)?function\s+([A-Za-z_]\w*(?:[.:][A-Za-z_]\w*)*)\s*\(([^)]*)\)/))) {
1124
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
1125
+ } else if ((match = trimmed.match(/^(?:local\s+)?([A-Za-z_]\w*(?:[.:][A-Za-z_]\w*)*)\s*=\s*function\s*\(([^)]*)\)/))) {
1126
+ declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
1127
+ } else if ((match = trimmed.match(/^local\s+([A-Za-z_]\w*)\s*=\s*(?:\{|\w+)/))) {
1128
+ declarations.push(nativeDeclaration(input, number, 'LocalDeclaration', 'variable', match[1], {}, false));
1129
+ }
1130
+ }
1131
+ return declarations;
1132
+ }
1133
+
1134
+ function scanShell(input) {
1135
+ const declarations = [];
1136
+ for (const { line, number } of sourceLines(input.sourceText)) {
1137
+ const trimmed = line.trim();
1138
+ let match;
1139
+ if ((match = trimmed.match(/^(?:source|\.)\s+(?:"([^"]+)"|'([^']+)'|([./A-Za-z0-9_-][\w./-]*))(?:\s|$)/))) {
1140
+ declarations.push(nativeImportDeclaration(input, number, match[1] ?? match[2] ?? match[3], 'SourceCommand', 'file'));
1141
+ } else if ((match = trimmed.match(/^function\s+([A-Za-z_][\w-]*)\s*(?:\(\s*\))?\s*(?:\{|$)/))) {
1142
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true));
1143
+ } else if ((match = trimmed.match(/^([A-Za-z_][\w-]*)\s*\(\s*\)\s*(?:\{|$)/))) {
1144
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], {}, true));
1145
+ } else if ((match = trimmed.match(/^(?:export\s+)?(?:readonly\s+)?([A-Za-z_]\w*)=/))) {
1146
+ declarations.push(nativeDeclaration(input, number, 'VariableAssignment', 'variable', match[1], {}, false));
1147
+ } else if ((match = trimmed.match(/^alias\s+([A-Za-z_][\w-]*)=/))) {
1148
+ declarations.push(nativeDeclaration(input, number, 'AliasDeclaration', 'function', match[1], {}, false));
1149
+ }
1150
+ }
1151
+ return declarations;
1152
+ }
1153
+
1154
+ function scanSql(input) {
1155
+ const declarations = [];
1156
+ for (const { line, number } of sourceLines(input.sourceText)) {
1157
+ const trimmed = line.trim();
1158
+ let match;
1159
+ if ((match = trimmed.match(/^CREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$-]*))/i))) {
1160
+ declarations.push(nativeImportDeclaration(input, number, normalizeSqlIdentifier(match[1]), 'CreateExtensionStatement', 'extension'));
1161
+ } else if ((match = trimmed.match(/^CREATE\s+(?:OR\s+REPLACE\s+)?(?:TEMP(?:ORARY)?\s+)?((?:UNIQUE\s+)?INDEX|MATERIALIZED\s+VIEW|TABLE|VIEW|FUNCTION|PROCEDURE|TRIGGER|SCHEMA|TYPE)\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$]*)(?:\s*\.\s*(?:"[^"]+"|`[^`]+`|\[[^\]]+\]|[A-Za-z_][\w$]*))?)/i))) {
1162
+ const objectKind = match[1].toUpperCase().replace(/\s+/g, ' ');
1163
+ declarations.push(nativeDeclaration(input, number, sqlLanguageKind(objectKind), sqlSymbolKind(objectKind), normalizeSqlIdentifier(match[2]), { objectKind }, trimmed.includes('(')));
1164
+ }
1165
+ }
1166
+ return declarations;
1167
+ }
1168
+
1169
+ function scanZig(input) {
1170
+ const declarations = [];
1171
+ for (const { line, number } of sourceLines(input.sourceText)) {
1172
+ const trimmed = line.trim();
1173
+ let match;
1174
+ if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?(?:const|var)\s+([A-Za-z_]\w*)\s*=\s*@import\(\s*["']([^"']+)["']\s*\)\s*;?/))) {
1175
+ declarations.push(nativeImportDeclaration(input, number, match[2], 'ImportDeclaration', 'module'));
1176
+ } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?usingnamespace\s+@import\(\s*["']([^"']+)["']\s*\)\s*;?/))) {
1177
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingNamespaceImport', 'module'));
1178
+ } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?const\s+([A-Za-z_]\w*)\s*=\s*(?:extern\s+)?(struct|enum|union|opaque|error)\b/))) {
1179
+ declarations.push(nativeDeclaration(input, number, `Const${upperFirst(match[2])}Declaration`, 'type', match[1], { zigKind: match[2] }, trimmed.includes('{')));
1180
+ } else if ((match = trimmed.match(/^(?:(?:pub|export|extern|inline)\s+)*(?:fn)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/))) {
1181
+ declarations.push(nativeDeclaration(input, number, 'FnDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
1182
+ } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?const\s+([A-Za-z_]\w*)\b/))) {
1183
+ declarations.push(nativeDeclaration(input, number, 'ConstDeclaration', 'constant', match[1], {}, false));
1184
+ } else if ((match = trimmed.match(/^(?:(?:pub|export)\s+)?var\s+([A-Za-z_]\w*)\b/))) {
1185
+ declarations.push(nativeDeclaration(input, number, 'VarDeclaration', 'variable', match[1], {}, false));
1186
+ }
1187
+ if (/^\s*comptime\b|@(?:cImport|compileError|field|hasDecl|hasField|setEvalBranchQuota|This|Type|typeInfo)\b/.test(trimmed)) {
1188
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'generatedCode', zigMetaName(trimmed)));
1189
+ }
1190
+ }
1191
+ return declarations;
1192
+ }
1193
+
1194
+ function scanElixir(input) {
1195
+ const declarations = [];
1196
+ let currentModule;
1197
+ for (const { line, number } of sourceLines(input.sourceText)) {
1198
+ const trimmed = line.trim();
1199
+ let match;
1200
+ let recordedMeta = false;
1201
+ if ((match = trimmed.match(/^defmodule\s+([A-Z]\w*(?:\.[A-Z]\w*)*)\s+do\b/))) {
1202
+ currentModule = match[1];
1203
+ declarations.push(nativeDeclaration(input, number, 'ModuleDefinition', 'module', match[1], {}, true));
1204
+ } else if ((match = trimmed.match(/^(?:alias|import|require)\s+([A-Z]\w*(?:\.[A-Z]\w*)*)/))) {
1205
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDirective', 'module'));
1206
+ } else if ((match = trimmed.match(/^use\s+([A-Z]\w*(?:\.[A-Z]\w*)*)/))) {
1207
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', match[1]));
1208
+ recordedMeta = true;
1209
+ } else if ((match = trimmed.match(/^(defmacro|defmacrop|defguard|defguardp|defdelegate)\s+([A-Za-z_]\w*[!?]?)/))) {
1210
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', match[2]));
1211
+ recordedMeta = true;
1212
+ } else if ((match = trimmed.match(/^defp?\s+([A-Za-z_]\w*[!?]?)\s*(?:\(([^)]*)\)|([^,]*))?/))) {
1213
+ declarations.push(nativeDeclaration(input, number, 'FunctionDefinition', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, /\bdo\b/.test(trimmed)));
1214
+ } else if (trimmed.startsWith('defstruct')) {
1215
+ declarations.push(nativeDeclaration(input, number, 'StructDefinition', 'type', currentModule ?? `struct_${number}`, {}, true));
1216
+ } else if ((match = trimmed.match(/^@(type|typep|opaque|callback)\s+([A-Za-z_]\w*[!?]?)/))) {
1217
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Attribute`, match[1] === 'callback' ? 'function' : 'type', match[2], {}, false));
1218
+ }
1219
+ if (!recordedMeta && /(?:\bquote\s+do\b|\bunquote(?:_splicing)?\b|@(?:before_compile|after_compile|on_definition|derive)\b)/.test(trimmed)) {
1220
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', elixirMetaName(trimmed)));
1221
+ }
1222
+ }
1223
+ return declarations;
1224
+ }
1225
+
1226
+ function scanErlang(input) {
1227
+ const declarations = [];
1228
+ const seenFunctions = new Set();
1229
+ for (const { line, number } of sourceLines(input.sourceText)) {
1230
+ const trimmed = line.trim();
1231
+ let match;
1232
+ let recordedMacro = false;
1233
+ if ((match = trimmed.match(/^-module\(([a-z][A-Za-z0-9_@]*)\)\./))) {
1234
+ declarations.push(nativeDeclaration(input, number, 'ModuleAttribute', 'module', match[1], {}, false));
1235
+ } else if ((match = trimmed.match(/^-include(?:_lib)?\(["']([^"']+)["']\)\./))) {
1236
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'IncludeAttribute', 'module'));
1237
+ } else if ((match = trimmed.match(/^-import\(([a-z][A-Za-z0-9_@]*)\s*,/))) {
1238
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportAttribute', 'module'));
1239
+ } else if ((match = trimmed.match(/^-behaviou?r\(([a-z][A-Za-z0-9_@]*)\)\./))) {
1240
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'BehaviourAttribute', 'module'));
1241
+ } else if ((match = trimmed.match(/^-record\(([a-z][A-Za-z0-9_@]*)\s*,/))) {
1242
+ declarations.push(nativeDeclaration(input, number, 'RecordAttribute', 'type', match[1], {}, false));
1243
+ } else if ((match = trimmed.match(/^-(type|opaque)\s+([a-z][A-Za-z0-9_@]*)\s*\(/))) {
1244
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Attribute`, 'type', match[2], {}, false));
1245
+ } else if ((match = trimmed.match(/^-callback\s+([a-z][A-Za-z0-9_@]*)\s*\(/))) {
1246
+ declarations.push(nativeDeclaration(input, number, 'CallbackAttribute', 'function', match[1], {}, false));
1247
+ } else if ((match = trimmed.match(/^-define\(([^,\s)]+)/))) {
1248
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'preprocessor', match[1]));
1249
+ recordedMacro = true;
1250
+ } else if (/^-compile\([^)]*parse_transform/.test(trimmed)) {
1251
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'generatedCode', 'parse_transform'));
1252
+ recordedMacro = true;
1253
+ } else if ((match = trimmed.match(/^([a-z][A-Za-z0-9_@]*|'[^']+')\s*\(([^)]*)\)\s*(?:when\s+.*?)?->/))) {
1254
+ const name = erlangAtomName(match[1]);
1255
+ if (!seenFunctions.has(name)) {
1256
+ seenFunctions.add(name);
1257
+ declarations.push(nativeDeclaration(input, number, 'FunctionClause', 'function', name, { parameters: splitParameters(match[2]) }, true));
1258
+ }
1259
+ }
1260
+ if (!recordedMacro && /(^|[^A-Za-z0-9_])\?[A-Za-z_]\w*/.test(trimmed)) {
1261
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', erlangMacroName(trimmed)));
1262
+ }
1263
+ }
1264
+ return declarations;
1265
+ }
1266
+
1267
+ function scanHaskell(input) {
1268
+ const declarations = [];
1269
+ const seenFunctions = new Set();
1270
+ for (const { line, number } of sourceLines(input.sourceText)) {
1271
+ const trimmed = line.trim();
1272
+ let match;
1273
+ if (/^#\s*(?:if|ifdef|ifndef|else|elif|endif|define|include)\b/.test(trimmed)) {
1274
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'preprocessor', haskellMetaName(trimmed)));
1275
+ } else if ((match = trimmed.match(/^\{-#\s+LANGUAGE\s+(.+?)#-\}/))) {
1276
+ const extensions = match[1].split(',').map((part) => part.trim());
1277
+ if (extensions.some((extension) => /^(TemplateHaskell|QuasiQuotes|CPP)$/.test(extension))) {
1278
+ declarations.push(nativeMacroLoss(input, number, trimmed, extensions.includes('CPP') ? 'preprocessor' : 'macroExpansion', extensions.join('_')));
1279
+ }
1280
+ } else if (/^\$\(|\[[a-zA-Z]*\||\b\$\([^)]+\)/.test(trimmed)) {
1281
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'macroExpansion', haskellMetaName(trimmed)));
1282
+ } else if ((match = trimmed.match(/^module\s+([A-Z][A-Za-z0-9_.']*)\b/))) {
1283
+ declarations.push(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, false));
1284
+ } else if ((match = trimmed.match(/^import\s+(?:safe\s+)?(?:qualified\s+)?([A-Z][A-Za-z0-9_.']*)/))) {
1285
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
1286
+ } else if ((match = trimmed.match(/^foreign\s+import\s+([A-Za-z_]\w*)/))) {
1287
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ForeignImportDeclaration', 'foreign'));
1288
+ } else if ((match = trimmed.match(/^(data|newtype|type)\s+([A-Z][A-Za-z0-9_']*)\b/))) {
1289
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, 'type', match[2], {}, /(?:where|=)/.test(trimmed)));
1290
+ } else if ((match = trimmed.match(/^class\s+(?:\([^)]*\)\s*=>\s*)?([A-Z][A-Za-z0-9_']*)\b/))) {
1291
+ declarations.push(nativeDeclaration(input, number, 'ClassDeclaration', 'type', match[1], {}, /\bwhere\b/.test(trimmed)));
1292
+ } else if ((match = trimmed.match(/^([a-z_][A-Za-z0-9_']*)\s*::\s*(.+)$/))) {
1293
+ seenFunctions.add(match[1]);
1294
+ declarations.push(nativeDeclaration(input, number, 'FunctionSignature', 'function', match[1], { signature: match[2].trim() }, false));
1295
+ } else if ((match = trimmed.match(/^([a-z_][A-Za-z0-9_']*)\b[^=]*=/))) {
1296
+ if (!seenFunctions.has(match[1])) {
1297
+ seenFunctions.add(match[1]);
1298
+ declarations.push(nativeDeclaration(input, number, 'FunctionBinding', 'function', match[1], {}, true));
1299
+ }
1300
+ }
1301
+ }
1302
+ return declarations;
1303
+ }
1304
+
1305
+ function scanR(input) {
1306
+ const declarations = [];
1307
+ for (const { line, number } of sourceLines(input.sourceText)) {
1308
+ const trimmed = line.trim();
1309
+ let match;
1310
+ if ((match = trimmed.match(/^(?:library|require)\s*\(\s*["']?([A-Za-z_][\w.-]*)["']?/))) {
1311
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'LibraryCall', 'package'));
1312
+ } else if ((match = trimmed.match(/^importFrom\s*\(\s*["']?([A-Za-z_][\w.-]*)["']?/))) {
1313
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportFromCall', 'package'));
1314
+ } else if ((match = trimmed.match(/^source\s*\(\s*["']([^"']+)["']/))) {
1315
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'SourceCall', 'module'));
1316
+ } else if ((match = trimmed.match(/^([A-Za-z_][\w.]*)\s*(?:<-|=)\s*function\s*\(([^)]*)\)/))) {
1317
+ declarations.push(nativeDeclaration(input, number, 'FunctionAssignment', 'function', match[1], { parameters: splitParameters(match[2]) }, trimmed.includes('{')));
1318
+ } else if ((match = trimmed.match(/^([A-Za-z_][\w.]*)\s*<-\s*R6Class\s*\(\s*["']([^"']+)["']/))) {
1319
+ declarations.push(nativeDeclaration(input, number, 'R6ClassDeclaration', 'class', match[2] || match[1], { binding: match[1] }, true));
1320
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicRuntime', match[2] || match[1]));
1321
+ } else if ((match = trimmed.match(/^setClass\s*\(\s*["']([^"']+)["']/))) {
1322
+ declarations.push(nativeDeclaration(input, number, 'S4ClassDeclaration', 'class', match[1], {}, true));
1323
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicRuntime', match[1]));
1324
+ } else if ((match = trimmed.match(/^setGeneric\s*\(\s*["']([^"']+)["']/))) {
1325
+ declarations.push(nativeDeclaration(input, number, 'S4GenericDeclaration', 'function', match[1], {}, true));
1326
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicRuntime', match[1]));
1327
+ } else if ((match = trimmed.match(/^setMethod\s*\(\s*["']([^"']+)["']/))) {
1328
+ declarations.push(nativeDeclaration(input, number, 'S4MethodDeclaration', 'method', match[1], {}, true));
1329
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicDispatch', match[1]));
1330
+ } else if ((match = trimmed.match(/^([A-Z][A-Za-z0-9_.]*)\s*(?:<-|=)\s*/))) {
1331
+ declarations.push(nativeDeclaration(input, number, 'ConstantAssignment', 'constant', match[1], {}, false));
1332
+ }
1333
+ if (/(?:eval|parse|substitute|quote|bquote|assign)\s*\(|<<-|\{\{|!!!|!!/.test(trimmed)) {
1334
+ declarations.push(nativeMacroLoss(input, number, trimmed, 'dynamicRuntime', rMetaName(trimmed)));
1335
+ }
1336
+ }
1337
+ return declarations;
1338
+ }
1339
+
771
1340
  function scanGenericDeclarations(input) {
772
1341
  return sourceLines(input.sourceText)
773
1342
  .filter(({ line }) => /\b(function|class|struct|enum|trait|interface|def)\b/.test(line))
@@ -804,6 +1373,78 @@ function phpSymbolKind(kind) {
804
1373
  return 'class';
805
1374
  }
806
1375
 
1376
+ function kotlinDeclarationKind(kind, prefix) {
1377
+ if (prefix === 'enum') return 'EnumClassDeclaration';
1378
+ if (prefix === 'annotation') return 'AnnotationClassDeclaration';
1379
+ return `${upperFirst(kind)}Declaration`;
1380
+ }
1381
+
1382
+ function kotlinSymbolKind(kind, prefix) {
1383
+ if (kind === 'interface') return 'interface';
1384
+ if (kind === 'object') return 'module';
1385
+ if (prefix === 'enum' || prefix === 'annotation') return 'type';
1386
+ return 'class';
1387
+ }
1388
+
1389
+ function scalaSymbolKind(kind) {
1390
+ if (kind === 'trait') return 'trait';
1391
+ if (kind === 'object') return 'module';
1392
+ if (kind === 'enum') return 'type';
1393
+ return 'class';
1394
+ }
1395
+
1396
+ function dartSymbolKind(kind) {
1397
+ if (kind === 'mixin') return 'trait';
1398
+ if (kind === 'enum') return 'type';
1399
+ return 'class';
1400
+ }
1401
+
1402
+ function sqlSymbolKind(kind) {
1403
+ if (kind === 'FUNCTION' || kind === 'PROCEDURE' || kind === 'TRIGGER') return 'function';
1404
+ if (kind.includes('INDEX')) return 'index';
1405
+ if (kind === 'SCHEMA') return 'namespace';
1406
+ return 'type';
1407
+ }
1408
+
1409
+ function sqlLanguageKind(kind) {
1410
+ return `Create${kind.toLowerCase().split(/\s+/).map(upperFirst).join('')}Statement`;
1411
+ }
1412
+
1413
+ function normalizeSqlIdentifier(value) {
1414
+ return String(value)
1415
+ .split(/\s*\.\s*/)
1416
+ .map((part) => part.replace(/^"|"$/g, '').replace(/^`|`$/g, '').replace(/^\[|\]$/g, ''))
1417
+ .join('.');
1418
+ }
1419
+
1420
+ function zigMetaName(source) {
1421
+ const match = source.match(/@([A-Za-z_]\w*)|^\s*(comptime)\b/);
1422
+ return match?.[1] ?? match?.[2] ?? 'comptime';
1423
+ }
1424
+
1425
+ function elixirMetaName(source) {
1426
+ const match = source.match(/@([A-Za-z_]\w*)|\b(unquote(?:_splicing)?|quote)\b/);
1427
+ return match?.[1] ?? match?.[2] ?? 'macro';
1428
+ }
1429
+
1430
+ function erlangAtomName(value) {
1431
+ return String(value).startsWith("'") ? String(value).slice(1, -1) : String(value);
1432
+ }
1433
+
1434
+ function erlangMacroName(source) {
1435
+ const match = source.match(/\?([A-Za-z_]\w*)/);
1436
+ return match?.[1] ?? 'macro';
1437
+ }
1438
+
1439
+ function haskellMetaName(source) {
1440
+ return idFragment(source).slice(0, 40);
1441
+ }
1442
+
1443
+ function rMetaName(source) {
1444
+ const match = source.match(/([A-Za-z_][\w.]*)\s*\(/);
1445
+ return match?.[1] ?? 'dynamic';
1446
+ }
1447
+
807
1448
  function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, hasBody = false) {
808
1449
  const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
809
1450
  return {
@@ -990,33 +1631,61 @@ function inferSourceMapMappings(input) {
990
1631
  }
991
1632
 
992
1633
  function normalizeSourceMapMappings(mappings, context) {
1634
+ if (mappings === undefined || mappings === null) return [];
1635
+ if (!Array.isArray(mappings)) {
1636
+ throw new Error('Source-map mappings must be an array');
1637
+ }
993
1638
  const semanticIndex = context.semanticIndex;
994
1639
  const nativeAst = context.nativeAst;
995
1640
  const nativeSource = context.nativeSource;
996
1641
  const symbolsById = new Map((semanticIndex?.symbols ?? []).map((symbol) => [symbol.id, symbol]));
997
1642
  const occurrencesById = new Map((semanticIndex?.occurrences ?? []).map((occurrence) => [occurrence.id, occurrence]));
998
- const evidenceIds = (context.evidence ?? []).map((record) => record.id);
999
- return (mappings ?? [])
1000
- .filter((mapping) => mapping && typeof mapping === 'object')
1643
+ const evidenceIds = uniqueStrings([
1644
+ ...(semanticIndex?.evidence ?? []).map((record) => record.id),
1645
+ ...(context.evidence ?? []).map((record) => record.id),
1646
+ ...(context.sourceMapEvidence ?? []).map((record) => record.id)
1647
+ ]);
1648
+ const usedMappingIds = new Set();
1649
+ return mappings
1001
1650
  .map((mapping, index) => {
1651
+ if (!mapping || typeof mapping !== 'object') {
1652
+ throw new Error(`Source-map mapping ${index + 1} must be an object`);
1653
+ }
1002
1654
  const occurrence = mapping.semanticOccurrenceId ? occurrencesById.get(mapping.semanticOccurrenceId) : undefined;
1003
1655
  const symbol = mapping.semanticSymbolId ? symbolsById.get(mapping.semanticSymbolId) : occurrence ? symbolsById.get(occurrence.symbolId) : undefined;
1004
1656
  const nativeAstNodeId = mapping.nativeAstNodeId ?? occurrence?.nativeAstNodeId;
1005
1657
  const nativeNode = nativeAstNodeId ? nativeAst?.nodes?.[nativeAstNodeId] : undefined;
1006
1658
  const sourceSpan = mapping.sourceSpan ?? occurrence?.span ?? nativeNode?.span;
1007
- return {
1659
+ const target = mapping.target ?? mapping.generatedSpan?.target ?? context.target;
1660
+ const generatedSpan = normalizeGeneratedSpan(mapping.generatedSpan, target, context.targetPath, context.targetHash);
1661
+ if (
1662
+ !nativeAstNodeId &&
1663
+ !mapping.semanticNodeId &&
1664
+ !mapping.semanticSymbolId &&
1665
+ !mapping.semanticOccurrenceId &&
1666
+ !sourceSpan &&
1667
+ !generatedSpan &&
1668
+ !mapping.generatedName
1669
+ ) {
1670
+ throw new Error(`Source-map mapping ${index + 1} must reference a native AST node, semantic node, symbol, occurrence, source span, or generated span`);
1671
+ }
1672
+ const normalizedMapping = {
1008
1673
  ...mapping,
1009
- id: mapping.id ?? `map_${idFragment(nativeAstNodeId ?? mapping.semanticSymbolId ?? mapping.semanticNodeId ?? index + 1)}`,
1010
1674
  nativeSourceId: mapping.nativeSourceId ?? nativeSource?.id,
1011
1675
  nativeAstNodeId,
1012
1676
  semanticSymbolId: mapping.semanticSymbolId ?? occurrence?.symbolId,
1013
1677
  semanticOccurrenceId: mapping.semanticOccurrenceId ?? occurrence?.id,
1014
1678
  semanticNodeId: mapping.semanticNodeId ?? occurrence?.semanticNodeId ?? symbol?.semanticNodeId,
1015
1679
  sourceSpan,
1016
- target: mapping.target ?? context.target,
1017
- evidenceIds: mapping.evidenceIds ?? evidenceIds,
1018
- lossIds: mapping.lossIds ?? lossIdsForNativeNode(context.losses ?? nativeAst?.losses ?? [], nativeAstNodeId),
1019
- precision: mapping.precision ?? (sourceSpan ? 'declaration' : 'unknown')
1680
+ generatedSpan,
1681
+ target,
1682
+ evidenceIds: normalizeReferenceIds(mapping.evidenceIds, evidenceIds),
1683
+ lossIds: normalizeReferenceIds(mapping.lossIds, lossIdsForNativeNode(context.losses ?? nativeAst?.losses ?? [], nativeAstNodeId)),
1684
+ precision: normalizeSourceMapPrecision(mapping.precision, sourceSpan, generatedSpan)
1685
+ };
1686
+ return {
1687
+ ...normalizedMapping,
1688
+ id: reserveUniqueId(sourceMapMappingBaseId(normalizedMapping, index), usedMappingIds)
1020
1689
  };
1021
1690
  });
1022
1691
  }
@@ -1028,19 +1697,293 @@ function lossIdsForNativeNode(losses, nativeAstNodeId) {
1028
1697
  .map((loss) => loss.id);
1029
1698
  }
1030
1699
 
1031
- export function createUniversalAstFromDocument(document, input = {}) {
1032
- return createUniversalAstEnvelope({
1033
- id: input.id ?? `universal_ast_${idFragment(document.id)}`,
1034
- document,
1035
- semanticIndex: input.semanticIndex,
1036
- sourceMaps: input.sourceMaps ?? [],
1037
- evidence: input.evidence ?? [],
1038
- metadata: input.metadata
1700
+ function normalizeSourceMaps(sourceMaps, context) {
1701
+ if (sourceMaps === undefined || sourceMaps === null) return [];
1702
+ if (!Array.isArray(sourceMaps)) {
1703
+ throw new Error('Native import sourceMaps must be an array');
1704
+ }
1705
+ const usedSourceMapIds = new Set();
1706
+ return sourceMaps.map((sourceMap, index) => {
1707
+ if (!sourceMap || typeof sourceMap !== 'object') {
1708
+ throw new Error(`Native import source map ${index + 1} must be an object`);
1709
+ }
1710
+ const id = reserveUniqueId(String(sourceMap.id ?? `${context.defaultSourceMapId}_${index + 1}`), usedSourceMapIds);
1711
+ const evidence = uniqueRecordsById([...(sourceMap.evidence ?? []), ...(context.evidence ?? [])]);
1712
+ const target = sourceMap.target ?? context.target;
1713
+ const targetPath = sourceMap.targetPath ?? context.targetPath;
1714
+ const targetHash = sourceMap.targetHash ?? context.targetHash;
1715
+ const mappings = normalizeSourceMapMappings(sourceMap.mappings ?? [], {
1716
+ ...context,
1717
+ target,
1718
+ targetPath,
1719
+ targetHash,
1720
+ sourceMapEvidence: evidence
1721
+ });
1722
+ const normalized = createSourceMapRecord({
1723
+ ...sourceMap,
1724
+ id,
1725
+ sourcePath: sourceMap.sourcePath ?? context.sourcePath,
1726
+ sourceHash: sourceMap.sourceHash ?? context.sourceHash,
1727
+ target,
1728
+ targetPath: targetPath ?? commonGeneratedTargetPath(mappings),
1729
+ targetHash,
1730
+ semanticIndexId: sourceMap.semanticIndexId ?? context.semanticIndex?.id,
1731
+ nativeAstId: sourceMap.nativeAstId ?? context.nativeAst?.id,
1732
+ nativeSourceId: sourceMap.nativeSourceId ?? context.nativeSource?.id,
1733
+ mappings,
1734
+ evidence
1735
+ });
1736
+ const issues = validateSourceMapRecord(normalized, {
1737
+ document: context.document,
1738
+ nativeSources: context.nativeSources,
1739
+ nativeAst: context.nativeAst,
1740
+ semanticIndex: context.semanticIndex,
1741
+ losses: context.losses,
1742
+ evidence: context.evidence
1743
+ });
1744
+ if (issues.length) {
1745
+ throw new Error(`Invalid Frontier native source map ${normalized.id}: ${issues.join('; ')}`);
1746
+ }
1747
+ return normalized;
1039
1748
  });
1040
1749
  }
1041
1750
 
1042
- export function readUniversalAstJson(source) {
1043
- const envelope = JSON.parse(source);
1751
+ function normalizeGeneratedSpan(generatedSpan, target, targetPath, targetHash) {
1752
+ if (!generatedSpan) return generatedSpan;
1753
+ return {
1754
+ ...generatedSpan,
1755
+ target: generatedSpan.target ?? target,
1756
+ targetPath: generatedSpan.targetPath ?? target?.emitPath ?? targetPath,
1757
+ targetHash: generatedSpan.targetHash ?? targetHash
1758
+ };
1759
+ }
1760
+
1761
+ function normalizeSourceMapPrecision(value, sourceSpan, generatedSpan) {
1762
+ const explicit = value === undefined || value === null ? '' : String(value).trim();
1763
+ if (explicit) {
1764
+ const normalized = explicit.toLowerCase();
1765
+ if (normalized === 'exact' || normalized === 'declaration' || normalized === 'line' || normalized === 'estimated' || normalized === 'unknown') return normalized;
1766
+ if (normalized === 'estimate' || normalized === 'approx' || normalized === 'approximate' || normalized === 'approximated') return 'estimated';
1767
+ return explicit;
1768
+ }
1769
+ if (hasExactSpan(sourceSpan) && hasExactSpan(generatedSpan)) return 'exact';
1770
+ if (sourceSpan?.startLine && generatedSpan?.startLine) return 'line';
1771
+ if (sourceSpan?.startLine) return 'declaration';
1772
+ if (generatedSpan?.startLine) return 'line';
1773
+ return 'unknown';
1774
+ }
1775
+
1776
+ function hasExactSpan(span) {
1777
+ return Boolean(span && (
1778
+ (typeof span.start === 'number' && typeof span.end === 'number') ||
1779
+ (
1780
+ typeof span.startLine === 'number' &&
1781
+ typeof span.startColumn === 'number' &&
1782
+ typeof span.endLine === 'number' &&
1783
+ typeof span.endColumn === 'number'
1784
+ )
1785
+ ));
1786
+ }
1787
+
1788
+ function normalizeReferenceIds(value, fallback = []) {
1789
+ if (value === undefined || value === null) return uniqueStrings(fallback);
1790
+ return uniqueStrings(Array.isArray(value) ? value : [value]);
1791
+ }
1792
+
1793
+ function sourceMapMappingBaseId(mapping, index) {
1794
+ const explicit = mapping.id === undefined || mapping.id === null ? '' : String(mapping.id).trim();
1795
+ if (explicit) return explicit;
1796
+ const span = mapping.sourceSpan ?? mapping.generatedSpan;
1797
+ const spanPart = span
1798
+ ? `${span.path ?? span.targetPath ?? span.sourceId ?? 'span'}_${span.start ?? span.startLine ?? ''}_${span.end ?? span.endLine ?? ''}`
1799
+ : undefined;
1800
+ return `map_${idFragment([
1801
+ mapping.nativeAstNodeId,
1802
+ mapping.semanticOccurrenceId,
1803
+ mapping.semanticSymbolId,
1804
+ mapping.semanticNodeId,
1805
+ spanPart,
1806
+ index + 1
1807
+ ].filter(Boolean).join('_'))}`;
1808
+ }
1809
+
1810
+ function reserveUniqueId(baseId, usedIds) {
1811
+ const safeBase = String(baseId || 'id');
1812
+ if (!usedIds.has(safeBase)) {
1813
+ usedIds.add(safeBase);
1814
+ return safeBase;
1815
+ }
1816
+ let index = 2;
1817
+ while (usedIds.has(`${safeBase}_${index}`)) index += 1;
1818
+ const id = `${safeBase}_${index}`;
1819
+ usedIds.add(id);
1820
+ return id;
1821
+ }
1822
+
1823
+ function commonGeneratedTargetPath(mappings) {
1824
+ const paths = uniqueStrings((mappings ?? [])
1825
+ .map((mapping) => mapping.generatedSpan?.targetPath ?? mapping.target?.emitPath)
1826
+ .filter(Boolean));
1827
+ return paths.length === 1 ? paths[0] : undefined;
1828
+ }
1829
+
1830
+ function uniqueRecordsById(records) {
1831
+ const seen = new Set();
1832
+ const result = [];
1833
+ for (const record of records ?? []) {
1834
+ if (!record?.id || seen.has(record.id)) continue;
1835
+ seen.add(record.id);
1836
+ result.push(record);
1837
+ }
1838
+ return result;
1839
+ }
1840
+
1841
+ function normalizeNativeLossRecords(losses) {
1842
+ return (Array.isArray(losses) ? losses : [losses])
1843
+ .filter((loss) => loss !== undefined && loss !== null)
1844
+ .map((loss, index) => normalizeNativeLossRecord(loss, `loss_${index + 1}`));
1845
+ }
1846
+
1847
+ function normalizeNativeLossRecord(loss, fallbackId) {
1848
+ const record = typeof loss === 'string' ? { message: loss } : loss ?? {};
1849
+ const severity = normalizeLossSeverity(record.severity);
1850
+ const kind = normalizeNativeLossKind(record, severity);
1851
+ const category = nativeImportCategoryForLossKind(kind);
1852
+ return {
1853
+ ...record,
1854
+ id: String(record.id ?? fallbackId),
1855
+ severity,
1856
+ kind,
1857
+ message: String(record.message ?? `${kind} loss during native import.`),
1858
+ metadata: {
1859
+ lossCategory: category,
1860
+ semanticMergeAdmission: semanticMergeAdmissionForSeverity(severity),
1861
+ dashboardSeverity: severity,
1862
+ ...record.metadata
1863
+ }
1864
+ };
1865
+ }
1866
+
1867
+ function normalizeLossSeverity(value) {
1868
+ const severity = String(value ?? 'warning').toLowerCase();
1869
+ if (severity === 'error') return 'error';
1870
+ if (severity === 'info') return 'info';
1871
+ return 'warning';
1872
+ }
1873
+
1874
+ function normalizeNativeLossKind(loss, severity) {
1875
+ const kind = String(loss.kind ?? '').trim();
1876
+ if (kind) return kind;
1877
+ if (loss.metadata?.diagnosticId || loss.metadata?.diagnosticCode) {
1878
+ return severity === 'error' ? 'unsupportedSyntax' : 'parserDiagnostic';
1879
+ }
1880
+ return severity === 'error' ? 'unsupportedSyntax' : 'opaqueNative';
1881
+ }
1882
+
1883
+ function nativeImportCategoryForLossKind(kind) {
1884
+ if (kind === 'declarationOnlyCoverage') return 'declarationsOnly';
1885
+ if (kind === 'opaqueNative') return 'opaqueBodies';
1886
+ if (kind === 'macroExpansion') return 'macroExpansion';
1887
+ if (kind === 'preprocessor' || kind === 'conditionalCompilation' || kind === 'macroHygiene') return 'preprocessor';
1888
+ if (kind === 'metaprogramming' || kind === 'reflection' || kind === 'dynamicDispatch' || kind === 'dynamicRuntime') return 'metaprogramming';
1889
+ if (kind === 'generatedCode') return 'generatedCode';
1890
+ if (kind === 'sourcePreservation' || kind === 'nonRoundTrippable') return 'sourcePreservation';
1891
+ if (kind === 'parserDiagnostic') return 'parserDiagnostics';
1892
+ if (kind === 'unsupportedSyntax' || kind === 'unsupportedSemantic') return 'unsupportedSyntax';
1893
+ if (kind === 'partialSemanticIndex') return 'partialSemanticIndex';
1894
+ if (kind === 'sourceMapApproximation') return 'sourceMapApproximation';
1895
+ return String(kind ?? 'opaqueNative');
1896
+ }
1897
+
1898
+ function semanticMergeAdmissionForSeverity(severity) {
1899
+ if (severity === 'error') return 'blocked';
1900
+ if (severity === 'warning') return 'review';
1901
+ return 'disclose';
1902
+ }
1903
+
1904
+ function nativeImportReadinessReasons(input) {
1905
+ if (input.failedEvidenceIds.length) {
1906
+ return [`Failed native import evidence prevents merge: ${input.failedEvidenceIds.join(', ')}`];
1907
+ }
1908
+ if (input.blockingLossIds.length) {
1909
+ return [`Native import error loss(es) block semantic merge: ${input.blockingLossIds.join(', ')}`];
1910
+ }
1911
+ if (input.reviewLossIds.length) {
1912
+ return [`Native import warning loss(es) require review: ${input.reviewLossIds.join(', ')}`];
1913
+ }
1914
+ if (input.informationalLossIds.length) {
1915
+ return [`Native import recorded informational loss(es): ${input.informationalLossIds.join(', ')}`];
1916
+ }
1917
+ if (input.exactAst) return ['Native import supplied exact AST coverage with no recorded loss.'];
1918
+ return ['Native import has no recorded loss.'];
1919
+ }
1920
+
1921
+ function attachNativeImportLossSummary(evidence, lossSummary) {
1922
+ return (evidence ?? []).map((record) => ({
1923
+ ...record,
1924
+ metadata: {
1925
+ ...record.metadata,
1926
+ nativeImportLossSummary: lossSummary,
1927
+ semanticMergeReadiness: lossSummary.semanticMergeReadiness,
1928
+ lossCategories: lossSummary.categories
1929
+ }
1930
+ }));
1931
+ }
1932
+
1933
+ function withNativeImportReadiness(importResult, lossSummary) {
1934
+ const mergeCandidates = (importResult.mergeCandidates ?? []).map((candidate) => {
1935
+ const readiness = maxSemanticMergeReadiness(candidate.readiness, lossSummary.semanticMergeReadiness);
1936
+ return {
1937
+ ...candidate,
1938
+ readiness,
1939
+ reasons: uniqueStrings([
1940
+ ...(candidate.reasons ?? []),
1941
+ ...lossSummary.readinessReasons
1942
+ ]),
1943
+ metadata: {
1944
+ ...candidate.metadata,
1945
+ nativeImportLossSummary: lossSummary,
1946
+ severityReadiness: lossSummary.semanticMergeReadiness,
1947
+ finalReadiness: readiness,
1948
+ lossCategories: lossSummary.categories,
1949
+ lossSeverityCounts: lossSummary.bySeverity,
1950
+ lossKindCounts: lossSummary.byKind
1951
+ }
1952
+ };
1953
+ });
1954
+ return {
1955
+ ...importResult,
1956
+ mergeCandidates,
1957
+ metadata: {
1958
+ ...importResult.metadata,
1959
+ nativeImportLossSummary: lossSummary,
1960
+ semanticMergeReadiness: mergeCandidates[0]?.readiness ?? lossSummary.semanticMergeReadiness,
1961
+ lossCategories: lossSummary.categories,
1962
+ lossSeverityCounts: lossSummary.bySeverity,
1963
+ lossKindCounts: lossSummary.byKind
1964
+ }
1965
+ };
1966
+ }
1967
+
1968
+ function maxSemanticMergeReadiness(left, right) {
1969
+ const leftRank = semanticMergeReadinessRank[left] ?? semanticMergeReadinessRank['needs-review'];
1970
+ const rightRank = semanticMergeReadinessRank[right] ?? semanticMergeReadinessRank['needs-review'];
1971
+ return leftRank >= rightRank ? left : right;
1972
+ }
1973
+
1974
+ export function createUniversalAstFromDocument(document, input = {}) {
1975
+ return createUniversalAstEnvelope({
1976
+ id: input.id ?? `universal_ast_${idFragment(document.id)}`,
1977
+ document,
1978
+ semanticIndex: input.semanticIndex,
1979
+ sourceMaps: input.sourceMaps ?? [],
1980
+ evidence: input.evidence ?? [],
1981
+ metadata: input.metadata
1982
+ });
1983
+ }
1984
+
1985
+ export function readUniversalAstJson(source) {
1986
+ const envelope = JSON.parse(source);
1044
1987
  const issues = validateUniversalAstEnvelope(envelope);
1045
1988
  if (issues.length > 0) {
1046
1989
  throw new Error(`Invalid Frontier universal AST JSON: ${issues.join('; ')}`);
@@ -1060,6 +2003,536 @@ export function emitForTarget(document, target = 'typescript', options = {}) {
1060
2003
  return renderTargetAst(projectFrontierAst(document, target, options), target);
1061
2004
  }
1062
2005
 
2006
+ function createJavaScriptSyntaxImporterAdapter(options) {
2007
+ return {
2008
+ id: options.id,
2009
+ language: options.language,
2010
+ parser: options.parser,
2011
+ version: options.version,
2012
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
2013
+ supportedExtensions: options.supportedExtensions,
2014
+ diagnostics: options.diagnostics,
2015
+ parse(input) {
2016
+ const ast = input.options?.ast ?? input.options?.nativeAst ?? options.ast ?? parseJavaScriptSyntax(input, options);
2017
+ if (!ast) {
2018
+ return missingInjectedParserResult(input, {
2019
+ parser: options.parser,
2020
+ adapterId: options.id,
2021
+ message: `${options.id} requires an injected parse function, parserModule.parse, ast, or adapterOptions.ast.`
2022
+ });
2023
+ }
2024
+ const parseDiagnostics = normalizeParserErrors(ast.errors, input, options);
2025
+ return createNativeImportFromSyntaxAst(ast, input, {
2026
+ parser: options.parser,
2027
+ astFormat: options.astFormat,
2028
+ maxNodes: options.maxNodes,
2029
+ diagnostics: parseDiagnostics
2030
+ });
2031
+ }
2032
+ };
2033
+ }
2034
+
2035
+ function parseJavaScriptSyntax(input, options) {
2036
+ const parse = options.parse ?? options.parserModule?.parse ?? options.babelParser?.parse;
2037
+ if (typeof parse !== 'function') return undefined;
2038
+ const parserOptions = {
2039
+ sourceFilename: input.sourcePath,
2040
+ ...(options.defaultParserOptions ?? {}),
2041
+ ...(typeof options.parserOptions === 'function'
2042
+ ? options.parserOptions(input)
2043
+ : options.parserOptions ?? {}),
2044
+ ...(input.options?.parserOptions ?? {})
2045
+ };
2046
+ return parse(input.sourceText, parserOptions);
2047
+ }
2048
+
2049
+ function createTypeScriptSourceFile(ts, input, options) {
2050
+ if (typeof options.createSourceFile === 'function') return options.createSourceFile(input, ts);
2051
+ if (!ts || typeof ts.createSourceFile !== 'function') return undefined;
2052
+ const scriptTarget = options.scriptTarget ?? ts.ScriptTarget?.Latest ?? ts.ScriptTarget?.ESNext ?? 99;
2053
+ const scriptKind = options.scriptKind ?? inferTypeScriptScriptKind(ts, input);
2054
+ return ts.createSourceFile(input.sourcePath ?? 'frontier-input.ts', input.sourceText, scriptTarget, true, scriptKind);
2055
+ }
2056
+
2057
+ function inferTypeScriptScriptKind(ts, input) {
2058
+ const path = String(input.sourcePath ?? '').toLowerCase();
2059
+ if (path.endsWith('.tsx')) return ts.ScriptKind?.TSX;
2060
+ if (path.endsWith('.jsx')) return ts.ScriptKind?.JSX;
2061
+ if (path.endsWith('.js') || path.endsWith('.mjs') || path.endsWith('.cjs')) return ts.ScriptKind?.JS;
2062
+ return ts.ScriptKind?.TS;
2063
+ }
2064
+
2065
+ function parseTreeSitterSource(input, options) {
2066
+ const parser = options.parserInstance ?? options.treeSitterParser ?? options.parser;
2067
+ if (parser && typeof parser.parse === 'function') return parser.parse(input.sourceText);
2068
+ if (typeof options.parse === 'function') return options.parse(input);
2069
+ return undefined;
2070
+ }
2071
+
2072
+ function createNativeImportFromSyntaxAst(ast, input, options) {
2073
+ const root = normalizeSyntaxAstRoot(ast, options.astFormat);
2074
+ if (!root) {
2075
+ return missingInjectedParserResult(input, {
2076
+ parser: options.parser,
2077
+ adapterId: input.adapterId,
2078
+ message: 'Injected AST did not contain an object root node.'
2079
+ });
2080
+ }
2081
+ const context = createAstNormalizationContext(input, options);
2082
+ visitSyntaxAstNode(root, context, 'root');
2083
+ if (context.truncated) {
2084
+ context.losses.push(truncatedAstLoss(input, context, options));
2085
+ }
2086
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
2087
+ return {
2088
+ rootId: context.rootId,
2089
+ nodes: context.nodes,
2090
+ semanticIndex: semantic.semanticIndex,
2091
+ mappings: semantic.mappings,
2092
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
2093
+ id: input.adapterId,
2094
+ version: input.adapterVersion
2095
+ }, input)) ?? []),
2096
+ evidence: semantic.evidence,
2097
+ diagnostics: options.diagnostics,
2098
+ metadata: {
2099
+ astFormat: options.astFormat,
2100
+ parser: options.parser,
2101
+ normalizedNodeCount: Object.keys(context.nodes).length,
2102
+ declarationCount: context.declarations.length,
2103
+ truncated: context.truncated
2104
+ }
2105
+ };
2106
+ }
2107
+
2108
+ function createNativeImportFromTypeScriptAst(sourceFile, input, options) {
2109
+ const context = createAstNormalizationContext(input, options);
2110
+ visitTypeScriptAstNode(sourceFile, sourceFile, context, 'root', options.ts);
2111
+ if (context.truncated) {
2112
+ context.losses.push(truncatedAstLoss(input, context, options));
2113
+ }
2114
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
2115
+ return {
2116
+ rootId: context.rootId,
2117
+ nodes: context.nodes,
2118
+ semanticIndex: semantic.semanticIndex,
2119
+ mappings: semantic.mappings,
2120
+ losses: context.losses,
2121
+ evidence: semantic.evidence,
2122
+ metadata: {
2123
+ astFormat: options.astFormat,
2124
+ parser: options.parser,
2125
+ normalizedNodeCount: Object.keys(context.nodes).length,
2126
+ declarationCount: context.declarations.length,
2127
+ truncated: context.truncated
2128
+ }
2129
+ };
2130
+ }
2131
+
2132
+ function createNativeImportFromTreeSitter(root, input, options) {
2133
+ const context = createAstNormalizationContext(input, options);
2134
+ visitTreeSitterNode(root, context, 'root');
2135
+ if (context.truncated) {
2136
+ context.losses.push(truncatedAstLoss(input, context, options));
2137
+ }
2138
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
2139
+ return {
2140
+ rootId: context.rootId,
2141
+ nodes: context.nodes,
2142
+ semanticIndex: semantic.semanticIndex,
2143
+ mappings: semantic.mappings,
2144
+ losses: context.losses,
2145
+ evidence: semantic.evidence,
2146
+ metadata: {
2147
+ astFormat: options.astFormat,
2148
+ parser: options.parser,
2149
+ normalizedNodeCount: Object.keys(context.nodes).length,
2150
+ declarationCount: context.declarations.length,
2151
+ truncated: context.truncated
2152
+ }
2153
+ };
2154
+ }
2155
+
2156
+ function createAstNormalizationContext(input, options) {
2157
+ return {
2158
+ input,
2159
+ options,
2160
+ maxNodes: Number.isFinite(options.maxNodes) ? Math.max(1, options.maxNodes) : 5000,
2161
+ counter: 0,
2162
+ objectIds: new WeakMap(),
2163
+ nodes: {},
2164
+ declarations: [],
2165
+ losses: [],
2166
+ rootId: undefined,
2167
+ truncated: false
2168
+ };
2169
+ }
2170
+
2171
+ function normalizeSyntaxAstRoot(ast, astFormat) {
2172
+ if (!ast || typeof ast !== 'object') return undefined;
2173
+ if (astFormat === 'babel' && ast.program && typeof ast.program === 'object') return ast.program;
2174
+ return ast;
2175
+ }
2176
+
2177
+ function visitSyntaxAstNode(node, context, propertyPath) {
2178
+ if (!isSyntaxAstNode(node) || context.truncated) return undefined;
2179
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
2180
+ if (context.counter >= context.maxNodes) {
2181
+ context.truncated = true;
2182
+ return undefined;
2183
+ }
2184
+ const id = nativeNodeId(context, node.type ?? node.kind ?? 'Node', node.loc, propertyPath);
2185
+ context.objectIds.set(node, id);
2186
+ if (!context.rootId) context.rootId = id;
2187
+ const children = [];
2188
+ const fields = primitiveSyntaxFields(node);
2189
+ for (const [key, value] of Object.entries(node)) {
2190
+ if (ignoredSyntaxField(key)) continue;
2191
+ if (Array.isArray(value)) {
2192
+ value.forEach((entry, index) => {
2193
+ const childId = visitSyntaxAstNode(entry, context, `${propertyPath}.${key}[${index}]`);
2194
+ if (childId) children.push(childId);
2195
+ });
2196
+ } else {
2197
+ const childId = visitSyntaxAstNode(value, context, `${propertyPath}.${key}`);
2198
+ if (childId) children.push(childId);
2199
+ }
2200
+ }
2201
+ const declaration = syntaxDeclaration(node, id, context.input, context.options);
2202
+ const nativeNode = {
2203
+ id,
2204
+ kind: String(node.type ?? node.kind ?? 'Node'),
2205
+ languageKind: `${context.input.language}.${node.type ?? node.kind ?? 'Node'}`,
2206
+ span: spanFromLoc(node.loc, context.input),
2207
+ value: declaration?.name ?? literalSyntaxValue(node),
2208
+ fields,
2209
+ children,
2210
+ metadata: {
2211
+ astFormat: context.options.astFormat,
2212
+ propertyPath,
2213
+ start: numberOrUndefined(node.start),
2214
+ end: numberOrUndefined(node.end),
2215
+ range: Array.isArray(node.range) ? node.range.slice(0, 2) : undefined
2216
+ }
2217
+ };
2218
+ context.nodes[id] = nativeNode;
2219
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
2220
+ return id;
2221
+ }
2222
+
2223
+ function visitTypeScriptAstNode(node, sourceFile, context, propertyPath, ts) {
2224
+ if (!node || typeof node !== 'object' || context.truncated) return undefined;
2225
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
2226
+ if (context.counter >= context.maxNodes) {
2227
+ context.truncated = true;
2228
+ return undefined;
2229
+ }
2230
+ const kind = typeScriptKindName(node, ts);
2231
+ const span = spanFromTypeScriptNode(node, sourceFile);
2232
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
2233
+ context.objectIds.set(node, id);
2234
+ if (!context.rootId) context.rootId = id;
2235
+ const children = [];
2236
+ const visitChild = (child) => {
2237
+ const childId = visitTypeScriptAstNode(child, sourceFile, context, `${propertyPath}.${children.length}`, ts);
2238
+ if (childId) children.push(childId);
2239
+ };
2240
+ if (ts && typeof ts.forEachChild === 'function') {
2241
+ ts.forEachChild(node, visitChild);
2242
+ } else if (typeof node.forEachChild === 'function') {
2243
+ node.forEachChild(visitChild);
2244
+ } else if (Array.isArray(node.children)) {
2245
+ node.children.forEach(visitChild);
2246
+ }
2247
+ const declaration = typeScriptDeclaration(node, kind, id, context.input, context.options);
2248
+ const nativeNode = {
2249
+ id,
2250
+ kind,
2251
+ languageKind: `${context.input.language}.${kind}`,
2252
+ span,
2253
+ value: declaration?.name ?? typeScriptNodeValue(node),
2254
+ fields: primitiveTypeScriptFields(node, kind),
2255
+ children,
2256
+ metadata: {
2257
+ astFormat: context.options.astFormat,
2258
+ propertyPath,
2259
+ pos: numberOrUndefined(node.pos),
2260
+ end: numberOrUndefined(node.end)
2261
+ }
2262
+ };
2263
+ context.nodes[id] = nativeNode;
2264
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
2265
+ return id;
2266
+ }
2267
+
2268
+ function visitTreeSitterNode(node, context, propertyPath) {
2269
+ if (!node || typeof node !== 'object' || context.truncated) return undefined;
2270
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
2271
+ if (context.counter >= context.maxNodes) {
2272
+ context.truncated = true;
2273
+ return undefined;
2274
+ }
2275
+ const kind = String(node.type ?? node.kind ?? 'node');
2276
+ const span = spanFromTreeSitterNode(node, context.input);
2277
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
2278
+ context.objectIds.set(node, id);
2279
+ if (!context.rootId) context.rootId = id;
2280
+ const children = [];
2281
+ const rawChildren = Array.isArray(node.namedChildren)
2282
+ ? node.namedChildren
2283
+ : Array.isArray(node.children)
2284
+ ? node.children
2285
+ : [];
2286
+ rawChildren.forEach((child, index) => {
2287
+ const childId = visitTreeSitterNode(child, context, `${propertyPath}.children[${index}]`);
2288
+ if (childId) children.push(childId);
2289
+ });
2290
+ const declaration = treeSitterDeclaration(node, kind, id, context.input, context.options);
2291
+ const nativeNode = {
2292
+ id,
2293
+ kind,
2294
+ languageKind: `${context.input.language}.${kind}`,
2295
+ span,
2296
+ value: declaration?.name ?? shortNodeText(node),
2297
+ fields: {
2298
+ named: Boolean(node.isNamed ?? node.named),
2299
+ missing: Boolean(node.isMissing),
2300
+ error: Boolean(node.hasError || kind === 'ERROR')
2301
+ },
2302
+ children,
2303
+ metadata: {
2304
+ astFormat: context.options.astFormat,
2305
+ propertyPath,
2306
+ startIndex: numberOrUndefined(node.startIndex),
2307
+ endIndex: numberOrUndefined(node.endIndex)
2308
+ }
2309
+ };
2310
+ context.nodes[id] = nativeNode;
2311
+ if (declaration) context.declarations.push({ ...declaration, nativeNode });
2312
+ if (node.hasError || kind === 'ERROR') {
2313
+ context.losses.push({
2314
+ id: `loss_${idFragment(id)}_tree_sitter_error`,
2315
+ severity: 'error',
2316
+ phase: 'parse',
2317
+ sourceFormat: context.input.language,
2318
+ kind: 'unsupportedSyntax',
2319
+ message: 'Tree-sitter reported a parse error node.',
2320
+ span,
2321
+ nodeId: id
2322
+ });
2323
+ }
2324
+ return id;
2325
+ }
2326
+
2327
+ function semanticIndexFromNativeDeclarations(declarations, input, options) {
2328
+ const documentId = `doc_${idFragment(input.sourcePath ?? input.language)}_${idFragment(input.sourceHash)}`;
2329
+ const evidenceId = `evidence_${idFragment(input.sourcePath ?? input.language)}_${idFragment(options.astFormat ?? options.parser)}_import`;
2330
+ const symbols = [];
2331
+ const occurrences = [];
2332
+ const relations = [];
2333
+ const facts = [];
2334
+ const mappings = [];
2335
+ for (const declaration of declarations) {
2336
+ const symbolId = declaration.symbolId ?? `symbol:${input.language}:${declaration.role === 'import' ? 'import:' : ''}${idFragment(declaration.name)}`;
2337
+ const occurrenceId = `occ_${idFragment(declaration.nativeNode.id)}_${declaration.role ?? 'definition'}`;
2338
+ symbols.push({
2339
+ id: symbolId,
2340
+ scheme: 'frontier',
2341
+ name: declaration.name,
2342
+ kind: declaration.symbolKind,
2343
+ language: input.language,
2344
+ nativeAstNodeId: declaration.nativeNode.id,
2345
+ signatureHash: hashSemanticValue([input.language, declaration.nativeNode.kind, declaration.name, declaration.nativeNode.fields ?? {}]),
2346
+ definitionSpan: declaration.nativeNode.span
2347
+ });
2348
+ occurrences.push({
2349
+ id: occurrenceId,
2350
+ documentId,
2351
+ symbolId,
2352
+ role: declaration.role ?? 'definition',
2353
+ span: declaration.nativeNode.span,
2354
+ nativeAstNodeId: declaration.nativeNode.id
2355
+ });
2356
+ relations.push({
2357
+ id: `rel_${idFragment(documentId)}_${idFragment(declaration.nativeNode.id)}`,
2358
+ sourceId: documentId,
2359
+ predicate: relationPredicateForDeclaration(declaration),
2360
+ targetId: symbolId
2361
+ });
2362
+ facts.push({
2363
+ id: `fact_${idFragment(declaration.nativeNode.id)}_kind`,
2364
+ predicate: 'nativeKind',
2365
+ subjectId: symbolId,
2366
+ value: declaration.nativeNode.languageKind
2367
+ });
2368
+ mappings.push({
2369
+ id: `map_${idFragment(declaration.nativeNode.id)}`,
2370
+ nativeAstNodeId: declaration.nativeNode.id,
2371
+ semanticSymbolId: symbolId,
2372
+ semanticOccurrenceId: occurrenceId,
2373
+ sourceSpan: declaration.nativeNode.span,
2374
+ evidenceIds: [evidenceId],
2375
+ lossIds: [],
2376
+ precision: declaration.nativeNode.span ? 'declaration' : 'unknown'
2377
+ });
2378
+ }
2379
+ const evidence = [{
2380
+ id: evidenceId,
2381
+ kind: 'import',
2382
+ status: 'passed',
2383
+ path: input.sourcePath,
2384
+ summary: `Normalized ${options.astFormat ?? options.parser} native AST with ${declarations.length} declaration(s).`,
2385
+ metadata: {
2386
+ parser: options.parser,
2387
+ astFormat: options.astFormat,
2388
+ language: input.language,
2389
+ sourceHash: input.sourceHash
2390
+ }
2391
+ }];
2392
+ return {
2393
+ semanticIndex: createSemanticIndexRecord({
2394
+ id: `index_${idFragment(input.sourcePath ?? input.language)}_${idFragment(options.astFormat ?? options.parser)}`,
2395
+ documents: [{
2396
+ id: documentId,
2397
+ path: input.sourcePath ?? `${input.language}:memory`,
2398
+ language: input.language,
2399
+ sourceHash: input.sourceHash
2400
+ }],
2401
+ symbols,
2402
+ occurrences,
2403
+ relations,
2404
+ facts,
2405
+ evidence,
2406
+ metadata: {
2407
+ parser: options.parser,
2408
+ astFormat: options.astFormat,
2409
+ coverage: 'native-ast-declarations'
2410
+ }
2411
+ }),
2412
+ mappings,
2413
+ evidence
2414
+ };
2415
+ }
2416
+
2417
+ function createNativeProjectImportResult(input, imports) {
2418
+ const idPart = idFragment(input.id ?? input.projectRoot ?? 'native_project');
2419
+ const nodes = {};
2420
+ const rootIds = [];
2421
+ const semanticIndex = mergeSemanticIndexes(imports, input, idPart);
2422
+ const nativeSources = [];
2423
+ const sourceMaps = [];
2424
+ const losses = [];
2425
+ const evidence = [];
2426
+ const mergeCandidates = [];
2427
+ const operations = [];
2428
+ for (const result of imports) {
2429
+ for (const node of Object.values(result.document?.nodes ?? {})) {
2430
+ nodes[node.id] = node;
2431
+ }
2432
+ rootIds.push(...(result.document?.rootIds ?? []));
2433
+ if (result.nativeSource) nativeSources.push(result.nativeSource);
2434
+ sourceMaps.push(...(result.sourceMaps ?? []));
2435
+ losses.push(...(result.losses ?? []));
2436
+ evidence.push(...(result.evidence ?? []));
2437
+ mergeCandidates.push(...(result.mergeCandidates ?? []));
2438
+ operations.push(...(result.patch?.operations ?? []));
2439
+ }
2440
+ const document = createDocument({
2441
+ id: input.documentId ?? `document_${idPart}`,
2442
+ name: input.documentName ?? input.name ?? 'NativeProject',
2443
+ nodes: Object.values(nodes),
2444
+ rootIds: uniqueStrings(rootIds),
2445
+ metadata: {
2446
+ sourceLanguage: input.language ?? 'mixed',
2447
+ semanticStatus: losses.some((loss) => loss.severity === 'error') ? 'partial' : 'mapped',
2448
+ projectRoot: input.projectRoot,
2449
+ sourceCount: imports.length,
2450
+ ...input.documentMetadata
2451
+ }
2452
+ });
2453
+ const universalAst = createUniversalAstEnvelope({
2454
+ id: input.universalAstId ?? `universal_ast_${idPart}`,
2455
+ document,
2456
+ nativeSources,
2457
+ semanticIndex,
2458
+ sourceMaps,
2459
+ losses: uniqueByLossId(losses),
2460
+ evidence: uniqueByEvidenceId(evidence),
2461
+ metadata: {
2462
+ sourceLanguage: input.language ?? 'mixed',
2463
+ projectRoot: input.projectRoot,
2464
+ sourceCount: imports.length,
2465
+ ...input.universalAstMetadata
2466
+ }
2467
+ });
2468
+ const patch = createPatch({
2469
+ id: input.patchId ?? `patch_${idPart}_project_import`,
2470
+ author: input.author ?? '@shapeshift-labs/frontier-lang-compiler/importNativeProject',
2471
+ risk: losses.some((loss) => loss.severity === 'error') ? 'high' : losses.some((loss) => loss.severity === 'warning') ? 'medium' : 'low',
2472
+ operations,
2473
+ evidence: uniqueByEvidenceId(evidence),
2474
+ metadata: {
2475
+ semanticIndexId: semanticIndex?.id,
2476
+ universalAstId: universalAst.id,
2477
+ sourceMapIds: sourceMaps.map((sourceMap) => sourceMap.id),
2478
+ sourceCount: imports.length
2479
+ }
2480
+ });
2481
+ return {
2482
+ kind: 'frontier.lang.projectImportResult',
2483
+ version: 1,
2484
+ id: input.id ?? `project_import_${idPart}`,
2485
+ language: input.language ?? 'mixed',
2486
+ projectRoot: input.projectRoot,
2487
+ imports,
2488
+ document,
2489
+ patch,
2490
+ nativeSources,
2491
+ semanticIndex,
2492
+ universalAst,
2493
+ sourceMaps,
2494
+ losses: uniqueByLossId(losses),
2495
+ evidence: uniqueByEvidenceId(evidence),
2496
+ mergeCandidates,
2497
+ metadata: {
2498
+ sourceCount: imports.length,
2499
+ sourcePaths: imports.map((result) => result.sourcePath).filter(Boolean),
2500
+ ...input.metadata
2501
+ }
2502
+ };
2503
+ }
2504
+
2505
+ function mergeSemanticIndexes(imports, input, idPart) {
2506
+ const indexes = imports.map((result) => result.semanticIndex ?? result.universalAst?.semanticIndex).filter(Boolean);
2507
+ if (!indexes.length) return undefined;
2508
+ return createSemanticIndexRecord({
2509
+ id: input.semanticIndexId ?? `index_${idPart}_project`,
2510
+ documents: indexes.flatMap((index) => index.documents ?? []),
2511
+ symbols: indexes.flatMap((index) => index.symbols ?? []),
2512
+ occurrences: indexes.flatMap((index) => index.occurrences ?? []),
2513
+ relations: indexes.flatMap((index) => index.relations ?? []),
2514
+ facts: indexes.flatMap((index) => index.facts ?? []),
2515
+ evidence: indexes.flatMap((index) => index.evidence ?? []),
2516
+ metadata: {
2517
+ projectRoot: input.projectRoot,
2518
+ sourceCount: imports.length,
2519
+ mergedIndexCount: indexes.length
2520
+ }
2521
+ });
2522
+ }
2523
+
2524
+ function resolveNativeProjectAdapter(source, adapters, input) {
2525
+ if (typeof input.adapterResolver === 'function') return input.adapterResolver(source, adapters);
2526
+ const language = source.language;
2527
+ const sourcePath = source.sourcePath ?? '';
2528
+ return adapters.find((adapter) => {
2529
+ if (source.adapter && adapter.id === source.adapter) return true;
2530
+ if (language && adapter.language !== language) return false;
2531
+ const extensions = adapter.supportedExtensions ?? [];
2532
+ return !extensions.length || extensions.some((extension) => sourcePath.toLowerCase().endsWith(extension.toLowerCase()));
2533
+ });
2534
+ }
2535
+
1063
2536
  function normalizeNativeImporterAdapter(adapter) {
1064
2537
  if (!adapter || typeof adapter !== 'object') {
1065
2538
  throw new Error('Native importer adapter must be an object');
@@ -1201,6 +2674,336 @@ function serializableDiagnostic(diagnostic) {
1201
2674
  };
1202
2675
  }
1203
2676
 
2677
+ function missingInjectedParserResult(input, details) {
2678
+ const rootId = `native_${idFragment(details.adapterId ?? details.parser)}_missing_parser`;
2679
+ const diagnostic = {
2680
+ severity: 'error',
2681
+ code: 'adapter.parser.missing',
2682
+ phase: 'parse',
2683
+ kind: 'unsupportedSyntax',
2684
+ message: details.message,
2685
+ path: input.sourcePath,
2686
+ metadata: {
2687
+ adapterId: details.adapterId,
2688
+ parser: details.parser
2689
+ }
2690
+ };
2691
+ return {
2692
+ rootId,
2693
+ nodes: {
2694
+ [rootId]: {
2695
+ id: rootId,
2696
+ kind: 'MissingInjectedParser',
2697
+ languageKind: `${input.language}.missingInjectedParser`,
2698
+ value: details.parser,
2699
+ metadata: {
2700
+ adapterId: details.adapterId,
2701
+ parser: details.parser,
2702
+ reason: 'missing-injected-parser'
2703
+ }
2704
+ }
2705
+ },
2706
+ diagnostics: [diagnostic],
2707
+ losses: [{
2708
+ id: `loss_${idFragment(rootId)}`,
2709
+ severity: 'error',
2710
+ phase: 'parse',
2711
+ sourceFormat: input.language,
2712
+ kind: 'unsupportedSyntax',
2713
+ message: details.message,
2714
+ nodeId: rootId,
2715
+ metadata: {
2716
+ adapterId: details.adapterId,
2717
+ parser: details.parser
2718
+ }
2719
+ }],
2720
+ metadata: {
2721
+ parser: details.parser,
2722
+ adapterId: details.adapterId,
2723
+ missingInjectedParser: true
2724
+ }
2725
+ };
2726
+ }
2727
+
2728
+ function normalizeParserErrors(errors, input, options) {
2729
+ return (errors ?? []).map((error, index) => ({
2730
+ id: `diagnostic_${idFragment(options.parser)}_parser_error_${index + 1}`,
2731
+ severity: 'error',
2732
+ code: error.code ?? error.reasonCode,
2733
+ phase: 'parse',
2734
+ kind: 'unsupportedSyntax',
2735
+ message: String(error.message ?? 'Parser reported a syntax error.'),
2736
+ path: input.sourcePath,
2737
+ span: spanFromLoc(error.loc ? { start: error.loc, end: error.loc } : undefined, input),
2738
+ metadata: {
2739
+ parser: options.parser,
2740
+ reasonCode: error.reasonCode
2741
+ }
2742
+ }));
2743
+ }
2744
+
2745
+ function nativeNodeId(context, kind, loc, propertyPath) {
2746
+ context.counter += 1;
2747
+ const start = loc?.start;
2748
+ const line = start?.line ?? 'x';
2749
+ const column = start?.column ?? 'x';
2750
+ return `native_${idFragment(kind)}_${idFragment(line)}_${idFragment(column)}_${context.counter}_${idFragment(propertyPath)}`;
2751
+ }
2752
+
2753
+ function isSyntaxAstNode(value) {
2754
+ return Boolean(value && typeof value === 'object' && typeof (value.type ?? value.kind) === 'string');
2755
+ }
2756
+
2757
+ function ignoredSyntaxField(key) {
2758
+ return key === 'type'
2759
+ || key === 'kind'
2760
+ || key === 'loc'
2761
+ || key === 'start'
2762
+ || key === 'end'
2763
+ || key === 'range'
2764
+ || key === 'comments'
2765
+ || key === 'leadingComments'
2766
+ || key === 'trailingComments'
2767
+ || key === 'innerComments'
2768
+ || key === 'tokens'
2769
+ || key === 'extra'
2770
+ || key === 'parent';
2771
+ }
2772
+
2773
+ function primitiveSyntaxFields(node) {
2774
+ const fields = {};
2775
+ for (const key of ['name', 'operator', 'sourceType', 'async', 'generator', 'computed', 'static', 'exportKind', 'importKind', 'optional']) {
2776
+ if (typeof node[key] === 'string' || typeof node[key] === 'number' || typeof node[key] === 'boolean' || node[key] === null) {
2777
+ fields[key] = node[key];
2778
+ }
2779
+ }
2780
+ const literal = literalSyntaxValue(node);
2781
+ if (literal !== undefined) fields.literal = literal;
2782
+ if (node.source && typeof node.source === 'object' && typeof node.source.value === 'string') fields.source = node.source.value;
2783
+ return fields;
2784
+ }
2785
+
2786
+ function literalSyntaxValue(node) {
2787
+ if (node.value === null || typeof node.value === 'string' || typeof node.value === 'number' || typeof node.value === 'boolean') return node.value;
2788
+ return undefined;
2789
+ }
2790
+
2791
+ function spanFromLoc(loc, input) {
2792
+ if (!loc?.start) return undefined;
2793
+ return {
2794
+ sourceId: input.sourceHash,
2795
+ path: input.sourcePath ?? loc.filename,
2796
+ startLine: loc.start.line,
2797
+ startColumn: typeof loc.start.column === 'number' ? loc.start.column + 1 : undefined,
2798
+ endLine: loc.end?.line,
2799
+ endColumn: typeof loc.end?.column === 'number' ? loc.end.column + 1 : undefined
2800
+ };
2801
+ }
2802
+
2803
+ function syntaxDeclaration(node, nativeNodeId, input) {
2804
+ const kind = String(node.type ?? node.kind ?? '');
2805
+ if (kind === 'ImportDeclaration') {
2806
+ const name = node.source?.value;
2807
+ if (typeof name === 'string') return declarationRecord(input, nativeNodeId, name, 'module', 'import');
2808
+ }
2809
+ if (kind === 'ExportNamedDeclaration' || kind === 'ExportAllDeclaration') {
2810
+ const name = node.source?.value;
2811
+ if (typeof name === 'string') return declarationRecord(input, nativeNodeId, name, 'module', 'export');
2812
+ }
2813
+ if (kind === 'FunctionDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'function');
2814
+ if (kind === 'ClassDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'class');
2815
+ if (kind === 'TSInterfaceDeclaration' || kind === 'InterfaceDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'interface');
2816
+ if (kind === 'TSTypeAliasDeclaration' || kind === 'TypeAliasDeclaration') return namedDeclaration(input, nativeNodeId, node.id, 'type');
2817
+ if (kind === 'VariableDeclarator') return namedDeclaration(input, nativeNodeId, node.id, 'variable');
2818
+ return undefined;
2819
+ }
2820
+
2821
+ function typeScriptDeclaration(node, kind, nativeNodeId, input) {
2822
+ if (kind === 'ImportDeclaration' || kind === 'ImportEqualsDeclaration') {
2823
+ const name = stringFromTsExpression(node.moduleSpecifier) ?? stringFromTsExpression(node.externalModuleReference?.expression);
2824
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
2825
+ }
2826
+ if (kind === 'FunctionDeclaration') return namedDeclaration(input, nativeNodeId, node.name, 'function');
2827
+ if (kind === 'ClassDeclaration') return namedDeclaration(input, nativeNodeId, node.name, 'class');
2828
+ if (kind === 'InterfaceDeclaration') return namedDeclaration(input, nativeNodeId, node.name, 'interface');
2829
+ if (kind === 'TypeAliasDeclaration' || kind === 'EnumDeclaration') return namedDeclaration(input, nativeNodeId, node.name, 'type');
2830
+ if (kind === 'VariableDeclaration') return namedDeclaration(input, nativeNodeId, node.name, 'variable');
2831
+ if (kind === 'MethodDeclaration' || kind === 'MethodSignature') return namedDeclaration(input, nativeNodeId, node.name, 'method');
2832
+ return undefined;
2833
+ }
2834
+
2835
+ function treeSitterDeclaration(node, kind, nativeNodeId, input) {
2836
+ if (/import|include|use/.test(kind)) {
2837
+ const name = treeSitterFieldText(node, 'path') ?? treeSitterFieldText(node, 'source') ?? shortNodeText(node);
2838
+ if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
2839
+ }
2840
+ if (/function|method|fn_item|function_declaration/.test(kind)) {
2841
+ const name = treeSitterFieldText(node, 'name');
2842
+ if (name) return declarationRecord(input, nativeNodeId, name, 'function', 'definition');
2843
+ }
2844
+ if (/class/.test(kind)) {
2845
+ const name = treeSitterFieldText(node, 'name');
2846
+ if (name) return declarationRecord(input, nativeNodeId, name, 'class', 'definition');
2847
+ }
2848
+ if (/interface/.test(kind)) {
2849
+ const name = treeSitterFieldText(node, 'name');
2850
+ if (name) return declarationRecord(input, nativeNodeId, name, 'interface', 'definition');
2851
+ }
2852
+ if (/struct|enum|type/.test(kind)) {
2853
+ const name = treeSitterFieldText(node, 'name');
2854
+ if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
2855
+ }
2856
+ return undefined;
2857
+ }
2858
+
2859
+ function namedDeclaration(input, nativeNodeId, nameNode, symbolKind) {
2860
+ const name = identifierName(nameNode);
2861
+ return name ? declarationRecord(input, nativeNodeId, name, symbolKind, 'definition') : undefined;
2862
+ }
2863
+
2864
+ function declarationRecord(input, nativeNodeId, name, symbolKind, role = 'definition') {
2865
+ return {
2866
+ name: String(name),
2867
+ symbolKind,
2868
+ role,
2869
+ symbolId: `symbol:${input.language}:${role === 'import' ? 'import:' : ''}${idFragment(name)}`,
2870
+ nativeNodeId
2871
+ };
2872
+ }
2873
+
2874
+ function relationPredicateForDeclaration(declaration) {
2875
+ if (declaration.role === 'import') return 'imports';
2876
+ if (declaration.role === 'export') return 'exports';
2877
+ return 'defines';
2878
+ }
2879
+
2880
+ function identifierName(node) {
2881
+ if (!node) return undefined;
2882
+ if (typeof node === 'string') return node;
2883
+ if (typeof node.name === 'string') return node.name;
2884
+ if (typeof node.escapedText === 'string') return node.escapedText;
2885
+ if (typeof node.text === 'string') return node.text;
2886
+ if (node.type === 'Identifier' && typeof node.value === 'string') return node.value;
2887
+ return undefined;
2888
+ }
2889
+
2890
+ function stringFromTsExpression(node) {
2891
+ if (!node) return undefined;
2892
+ if (typeof node.text === 'string') return node.text;
2893
+ if (typeof node.value === 'string') return node.value;
2894
+ return identifierName(node);
2895
+ }
2896
+
2897
+ function typeScriptKindName(node, ts) {
2898
+ if (typeof node.kindName === 'string') return node.kindName;
2899
+ if (ts?.SyntaxKind && node.kind !== undefined) return ts.SyntaxKind[node.kind] ?? `SyntaxKind${node.kind}`;
2900
+ if (typeof node.kind === 'string') return node.kind;
2901
+ return `SyntaxKind${node.kind ?? 'Unknown'}`;
2902
+ }
2903
+
2904
+ function spanFromTypeScriptNode(node, sourceFile) {
2905
+ const start = typeof node.getStart === 'function' ? node.getStart(sourceFile) : node.pos;
2906
+ const end = typeof node.getEnd === 'function' ? node.getEnd() : node.end;
2907
+ if (typeof start !== 'number' || typeof sourceFile?.getLineAndCharacterOfPosition !== 'function') return undefined;
2908
+ const startPos = sourceFile.getLineAndCharacterOfPosition(start);
2909
+ const endPos = typeof end === 'number' ? sourceFile.getLineAndCharacterOfPosition(end) : undefined;
2910
+ return {
2911
+ sourceId: sourceFile.sourceHash,
2912
+ path: sourceFile.fileName,
2913
+ startLine: startPos.line + 1,
2914
+ startColumn: startPos.character + 1,
2915
+ endLine: endPos ? endPos.line + 1 : undefined,
2916
+ endColumn: endPos ? endPos.character + 1 : undefined
2917
+ };
2918
+ }
2919
+
2920
+ function typeScriptNodeValue(node) {
2921
+ return identifierName(node.name) ?? stringFromTsExpression(node.moduleSpecifier) ?? undefined;
2922
+ }
2923
+
2924
+ function primitiveTypeScriptFields(node, kind) {
2925
+ const fields = { kind };
2926
+ const name = identifierName(node.name);
2927
+ if (name) fields.name = name;
2928
+ const moduleSpecifier = stringFromTsExpression(node.moduleSpecifier);
2929
+ if (moduleSpecifier) fields.moduleSpecifier = moduleSpecifier;
2930
+ return fields;
2931
+ }
2932
+
2933
+ function spanFromTreeSitterNode(node, input) {
2934
+ const start = node.startPosition;
2935
+ if (!start) return undefined;
2936
+ const end = node.endPosition;
2937
+ return {
2938
+ sourceId: input.sourceHash,
2939
+ path: input.sourcePath,
2940
+ startLine: start.row + 1,
2941
+ startColumn: start.column + 1,
2942
+ endLine: end ? end.row + 1 : undefined,
2943
+ endColumn: end ? end.column + 1 : undefined
2944
+ };
2945
+ }
2946
+
2947
+ function treeSitterFieldText(node, field) {
2948
+ if (typeof node.childForFieldName !== 'function') return undefined;
2949
+ return shortNodeText(node.childForFieldName(field));
2950
+ }
2951
+
2952
+ function shortNodeText(node) {
2953
+ if (!node || typeof node.text !== 'string') return undefined;
2954
+ const text = node.text.trim();
2955
+ if (!text || text.length > 160) return undefined;
2956
+ return text.replace(/^['"]|['"]$/g, '');
2957
+ }
2958
+
2959
+ function truncatedAstLoss(input, context, options) {
2960
+ return {
2961
+ id: `loss_${idFragment(input.sourcePath ?? input.language)}_${idFragment(options.astFormat ?? options.parser)}_truncated`,
2962
+ severity: 'warning',
2963
+ phase: 'read',
2964
+ sourceFormat: input.language,
2965
+ kind: 'opaqueNative',
2966
+ message: `Native AST normalization stopped after ${context.maxNodes} node(s).`,
2967
+ metadata: {
2968
+ parser: options.parser,
2969
+ astFormat: options.astFormat,
2970
+ maxNodes: context.maxNodes
2971
+ }
2972
+ };
2973
+ }
2974
+
2975
+ function uniqueStrings(values) {
2976
+ return [...new Set((values ?? []).map((value) => String(value)).filter(Boolean))];
2977
+ }
2978
+
2979
+ function uniqueByLossId(values) {
2980
+ const seen = new Set();
2981
+ const result = [];
2982
+ for (const value of values ?? []) {
2983
+ const id = value?.id ?? `loss_${result.length + 1}`;
2984
+ if (seen.has(id)) continue;
2985
+ seen.add(id);
2986
+ result.push(value.id ? value : { ...value, id });
2987
+ }
2988
+ return result;
2989
+ }
2990
+
2991
+ function uniqueByEvidenceId(values) {
2992
+ const seen = new Set();
2993
+ const result = [];
2994
+ for (const value of values ?? []) {
2995
+ const id = value?.id ?? `evidence_${result.length + 1}`;
2996
+ if (seen.has(id)) continue;
2997
+ seen.add(id);
2998
+ result.push(value.id ? value : { ...value, id });
2999
+ }
3000
+ return result;
3001
+ }
3002
+
3003
+ function numberOrUndefined(value) {
3004
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
3005
+ }
3006
+
1204
3007
  function idFragment(value) {
1205
3008
  return String(value ?? 'native')
1206
3009
  .toLowerCase()