@shapeshift-labs/frontier-lang-compiler 0.2.22 → 0.2.23

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
@@ -329,6 +329,14 @@ export const NativeImportLanguageProfiles = Object.freeze([
329
329
  nativeImportLanguageProfile('r', { aliases: ['R'], extensions: ['.r', '.R'], parserAdapters: ['r-parser', 'tree-sitter'], lossKinds: ['declarationOnlyCoverage', 'opaqueNative', 'dynamicRuntime', 'sourceMapApproximation', 'sourcePreservation'] })
330
330
  ]);
331
331
 
332
+ export const ExternalSemanticIndexFormats = Object.freeze([
333
+ 'frontier-semantic-index',
334
+ 'scip',
335
+ 'lsif',
336
+ 'lsp',
337
+ 'semanticdb'
338
+ ]);
339
+
332
340
  export function normalizeCompileTarget(target) {
333
341
  const normalized = String(target ?? 'typescript').toLowerCase();
334
342
  const canonical = canonicalTargets[normalized] ?? normalized;
@@ -544,6 +552,1216 @@ export function compileNativeSource(input, options = {}) {
544
552
  };
545
553
  }
546
554
 
555
+ export function importExternalSemanticIndex(input) {
556
+ const payload = input?.payload ?? input?.semanticIndex ?? input;
557
+ if (!payload || typeof payload !== 'object') {
558
+ throw new Error('importExternalSemanticIndex requires a payload object');
559
+ }
560
+ const format = normalizeExternalSemanticIndexFormat(input?.format ?? inferExternalSemanticIndexFormat(payload));
561
+ const idPart = idFragment(input?.id ?? input?.sourcePath ?? input?.projectRoot ?? format);
562
+ const context = {
563
+ format,
564
+ idPart,
565
+ language: normalizeExternalSemanticLanguage(input?.language ?? payload.language ?? payload.languageId),
566
+ sourcePath: input?.sourcePath ?? payload.sourcePath ?? payload.uri ?? payload.path,
567
+ sourceHash: input?.sourceHash ?? payload.sourceHash ?? payload.md5,
568
+ projectRoot: input?.projectRoot ?? payload.projectRoot ?? payload.project_root ?? payload.metadata?.projectRoot ?? payload.metadata?.project_root,
569
+ parser: input?.parser ?? `${format}.external-semantic-index`,
570
+ metadata: input?.metadata ?? {}
571
+ };
572
+ const normalized = normalizeExternalSemanticIndexPayload(payload, context);
573
+ const evidence = attachNativeImportLossSummary(
574
+ uniqueByEvidenceId([...(normalized.evidence ?? []), ...(input?.evidence ?? [])]),
575
+ summarizeNativeImportLosses(normalized.losses ?? [], {
576
+ evidence: [...(normalized.evidence ?? []), ...(input?.evidence ?? [])],
577
+ parser: context.parser,
578
+ scanKind: 'external-semantic-index',
579
+ semanticStatus: normalized.semanticStatus
580
+ })
581
+ );
582
+ const losses = normalizeNativeLossRecords(normalized.losses ?? []);
583
+ const semanticIndex = createSemanticIndexRecord({
584
+ id: input?.semanticIndexId ?? normalized.semanticIndexId ?? `index_${idPart}_${idFragment(format)}`,
585
+ repository: normalized.repository,
586
+ documents: normalized.documents,
587
+ symbols: normalized.symbols,
588
+ occurrences: normalized.occurrences,
589
+ relations: normalized.relations,
590
+ facts: normalized.facts,
591
+ evidence,
592
+ metadata: {
593
+ format,
594
+ parser: context.parser,
595
+ source: 'external-semantic-index',
596
+ projectRoot: context.projectRoot,
597
+ semanticStatus: normalized.semanticStatus,
598
+ ...normalized.metadata,
599
+ ...context.metadata
600
+ }
601
+ });
602
+ const sourceMapMappings = externalSemanticSourceMapMappings(semanticIndex, {
603
+ evidence,
604
+ losses,
605
+ sourcePath: context.sourcePath,
606
+ sourceHash: context.sourceHash
607
+ });
608
+ const sourceMaps = sourceMapMappings.length
609
+ ? [createSourceMapRecord({
610
+ id: input?.sourceMapId ?? `source_map_${idPart}_${idFragment(format)}`,
611
+ sourcePath: context.sourcePath,
612
+ sourceHash: context.sourceHash,
613
+ semanticIndexId: semanticIndex.id,
614
+ mappings: sourceMapMappings,
615
+ evidence,
616
+ metadata: {
617
+ format,
618
+ source: 'external-semantic-index',
619
+ projectRoot: context.projectRoot
620
+ }
621
+ })]
622
+ : [];
623
+ const document = createDocument({
624
+ id: input?.documentId ?? `document_${idPart}_${idFragment(format)}`,
625
+ name: input?.documentName ?? context.sourcePath ?? `${format} semantic index`,
626
+ nodes: [],
627
+ rootIds: [],
628
+ metadata: {
629
+ sourceLanguage: context.language,
630
+ sourcePath: context.sourcePath,
631
+ sourceHash: context.sourceHash,
632
+ semanticStatus: normalized.semanticStatus,
633
+ externalSemanticIndexFormat: format
634
+ }
635
+ });
636
+ const universalAst = createUniversalAstEnvelope({
637
+ id: input?.universalAstId ?? `universal_ast_${idPart}_${idFragment(format)}`,
638
+ document,
639
+ nativeSources: [],
640
+ semanticIndex,
641
+ sourceMaps,
642
+ losses,
643
+ evidence,
644
+ metadata: {
645
+ sourceLanguage: context.language,
646
+ sourcePath: context.sourcePath,
647
+ sourceHash: context.sourceHash,
648
+ projectRoot: context.projectRoot,
649
+ externalSemanticIndexFormat: format,
650
+ semanticStatus: normalized.semanticStatus,
651
+ ...input?.universalAstMetadata
652
+ }
653
+ });
654
+ const readiness = classifyNativeImportReadiness(losses, {
655
+ evidence,
656
+ parser: context.parser,
657
+ scanKind: 'external-semantic-index',
658
+ semanticStatus: normalized.semanticStatus
659
+ });
660
+ return {
661
+ kind: 'frontier.lang.externalSemanticIndexImport',
662
+ version: 1,
663
+ id: input?.id ?? `external_semantic_index_${idPart}_${idFragment(format)}`,
664
+ format,
665
+ language: context.language,
666
+ sourcePath: context.sourcePath,
667
+ projectRoot: context.projectRoot,
668
+ semanticIndex,
669
+ universalAst,
670
+ sourceMaps,
671
+ losses,
672
+ evidence,
673
+ readiness,
674
+ summary: {
675
+ documents: semanticIndex.documents.length,
676
+ symbols: semanticIndex.symbols.length,
677
+ occurrences: semanticIndex.occurrences.length,
678
+ relations: semanticIndex.relations.length,
679
+ facts: semanticIndex.facts.length,
680
+ sourceMapMappings: sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap.mappings?.length ?? 0), 0),
681
+ losses: losses.length,
682
+ readiness: readiness.readiness
683
+ },
684
+ metadata: {
685
+ format,
686
+ parser: context.parser,
687
+ semanticStatus: normalized.semanticStatus,
688
+ payloadHash: hashSemanticValue(payload),
689
+ ...normalized.metadata,
690
+ ...context.metadata
691
+ }
692
+ };
693
+ }
694
+
695
+ function normalizeExternalSemanticIndexFormat(format) {
696
+ const normalized = String(format ?? 'frontier-semantic-index').trim().toLowerCase();
697
+ const aliases = {
698
+ frontier: 'frontier-semantic-index',
699
+ 'frontier.semantic-index': 'frontier-semantic-index',
700
+ 'frontier.lang.semanticindex': 'frontier-semantic-index',
701
+ scipindex: 'scip',
702
+ 'sourcegraph-scip': 'scip',
703
+ lsp: 'lsp',
704
+ 'language-server-protocol': 'lsp',
705
+ semanticdb: 'semanticdb',
706
+ 'scala-semanticdb': 'semanticdb'
707
+ };
708
+ return aliases[normalized] ?? normalized;
709
+ }
710
+
711
+ function inferExternalSemanticIndexFormat(payload) {
712
+ if (payload.kind === 'frontier.lang.semanticIndex') return 'frontier-semantic-index';
713
+ if (Array.isArray(payload) && payload.some((entry) => entry?.type === 'vertex' || entry?.type === 'edge')) return 'lsif';
714
+ if (Array.isArray(payload.vertices) || Array.isArray(payload.edges)) return 'lsif';
715
+ if (Array.isArray(payload.documents) && payload.documents.some((document) => document?.relative_path ?? document?.relativePath)) return 'scip';
716
+ if (payload.metadata?.project_root || payload.metadata?.projectRoot || payload.external_symbols || payload.externalSymbols) return 'scip';
717
+ if (Array.isArray(payload.documents) && payload.documents.some((document) => document?.symbols && document?.occurrences && (document?.uri || document?.md5 || document?.schema))) return 'semanticdb';
718
+ if (Array.isArray(payload.documentSymbols) || Array.isArray(payload.symbols) || payload.semanticTokens || payload.location || payload.range) return 'lsp';
719
+ return 'frontier-semantic-index';
720
+ }
721
+
722
+ function normalizeExternalSemanticIndexPayload(payload, context) {
723
+ if (context.format === 'frontier-semantic-index') return normalizeFrontierSemanticIndexPayload(payload, context);
724
+ if (context.format === 'scip') return normalizeScipPayload(payload, context);
725
+ if (context.format === 'lsif') return normalizeLsifPayload(payload, context);
726
+ if (context.format === 'lsp') return normalizeLspPayload(payload, context);
727
+ if (context.format === 'semanticdb') return normalizeSemanticDbPayload(payload, context);
728
+ return normalizeGenericExternalSemanticIndexPayload(payload, context);
729
+ }
730
+
731
+ function externalSemanticBase(context, metadata = {}) {
732
+ return {
733
+ repository: context.projectRoot ? { root: context.projectRoot } : undefined,
734
+ documents: [],
735
+ symbols: [],
736
+ occurrences: [],
737
+ relations: [],
738
+ facts: [],
739
+ evidence: [externalSemanticEvidence(context, 'passed', `Imported ${context.format} semantic index payload.`)],
740
+ losses: [externalSemanticCoverageLoss(context)],
741
+ semanticStatus: 'external-semantic-index',
742
+ metadata
743
+ };
744
+ }
745
+
746
+ function normalizeFrontierSemanticIndexPayload(payload, context) {
747
+ const result = externalSemanticBase(context, { sourceFormat: payload.kind ?? 'frontier.lang.semanticIndex' });
748
+ result.repository = payload.repository ?? result.repository;
749
+ result.documents = normalizeArray(payload.documents).map((document, index) => externalDocument(document, context, index));
750
+ result.symbols = normalizeArray(payload.symbols).map((symbol, index) => externalSymbol(symbol, context, index));
751
+ result.occurrences = normalizeArray(payload.occurrences).map((occurrence, index) => externalOccurrence(occurrence, context, index));
752
+ result.relations = normalizeArray(payload.relations).map((relation, index) => externalRelation(relation, context, index));
753
+ result.facts = normalizeArray(payload.facts).map((fact, index) => externalFact(fact, context, index));
754
+ result.evidence = uniqueByEvidenceId([...(payload.evidence ?? []), ...result.evidence]);
755
+ if (payload.metadata) result.metadata = { ...result.metadata, ...payload.metadata };
756
+ return withExternalEmptyLoss(result, context);
757
+ }
758
+
759
+ function normalizeScipPayload(payload, context) {
760
+ const result = externalSemanticBase(context, { sourceFormat: 'scip' });
761
+ const metadata = payload.metadata ?? {};
762
+ const projectRoot = context.projectRoot ?? metadata.project_root ?? metadata.projectRoot;
763
+ result.repository = projectRoot ? { root: projectRoot } : undefined;
764
+ const documents = normalizeArray(payload.documents);
765
+ for (const [documentIndex, document] of documents.entries()) {
766
+ const path = document.relative_path ?? document.relativePath ?? document.path ?? context.sourcePath ?? `scip-document-${documentIndex + 1}`;
767
+ const language = normalizeExternalSemanticLanguage(document.language ?? context.language);
768
+ const documentId = document.id ?? `doc_${idFragment(path)}`;
769
+ result.documents.push({
770
+ id: documentId,
771
+ path,
772
+ language,
773
+ sourceHash: document.sourceHash ?? document.md5 ?? context.sourceHash,
774
+ metadata: {
775
+ format: 'scip',
776
+ projectRoot,
777
+ textDocumentEncoding: metadata.text_document_encoding ?? metadata.textDocumentEncoding,
778
+ documentIndex
779
+ }
780
+ });
781
+ const documentSymbols = new Map();
782
+ for (const symbolInfo of [...normalizeArray(document.symbols), ...normalizeArray(payload.external_symbols ?? payload.externalSymbols)]) {
783
+ const symbolId = scipSymbolId(symbolInfo.symbol, context, normalizeArray(document.symbols).includes(symbolInfo) ? documentId : undefined);
784
+ if (!symbolId || documentSymbols.has(symbolId)) continue;
785
+ documentSymbols.set(symbolId, true);
786
+ result.symbols.push({
787
+ id: symbolId,
788
+ scheme: 'scip',
789
+ name: symbolInfo.display_name ?? symbolInfo.displayName ?? nameFromExternalSymbol(symbolInfo.symbol),
790
+ kind: normalizeExternalSymbolKind(symbolInfo.kind),
791
+ language,
792
+ signatureHash: hashSemanticValue([symbolInfo.symbol, symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation]),
793
+ metadata: {
794
+ format: 'scip',
795
+ rawSymbol: symbolInfo.symbol,
796
+ documentation: symbolInfo.documentation,
797
+ enclosingSymbol: symbolInfo.enclosing_symbol ?? symbolInfo.enclosingSymbol,
798
+ external: !normalizeArray(document.symbols).includes(symbolInfo)
799
+ }
800
+ });
801
+ result.facts.push(...scipSymbolFacts(symbolInfo, symbolId));
802
+ result.relations.push(...scipRelationshipRelations(symbolInfo, symbolId, context));
803
+ }
804
+ for (const [occurrenceIndex, occurrence] of normalizeArray(document.occurrences).entries()) {
805
+ const symbolId = scipSymbolId(occurrence.symbol, context, documentId);
806
+ if (!symbolId) continue;
807
+ const role = scipOccurrenceRole(occurrence.symbol_roles ?? occurrence.symbolRoles);
808
+ if (!documentSymbols.has(symbolId)) {
809
+ documentSymbols.set(symbolId, true);
810
+ result.symbols.push({
811
+ id: symbolId,
812
+ scheme: 'scip',
813
+ name: nameFromExternalSymbol(occurrence.symbol),
814
+ kind: scipSyntaxKind(occurrence.syntax_kind ?? occurrence.syntaxKind),
815
+ language,
816
+ metadata: { format: 'scip', rawSymbol: occurrence.symbol, inferredFromOccurrence: true }
817
+ });
818
+ }
819
+ result.occurrences.push({
820
+ id: occurrence.id ?? `occ_${idFragment(documentId)}_${occurrenceIndex + 1}`,
821
+ documentId,
822
+ symbolId,
823
+ role,
824
+ span: spanFromScipOccurrence(occurrence, path, context.sourceHash),
825
+ metadata: {
826
+ format: 'scip',
827
+ symbolRoles: occurrence.symbol_roles ?? occurrence.symbolRoles,
828
+ roleSet: scipOccurrenceRoleSet(occurrence.symbol_roles ?? occurrence.symbolRoles),
829
+ syntaxKind: occurrence.syntax_kind ?? occurrence.syntaxKind,
830
+ overrideDocumentation: occurrence.override_documentation ?? occurrence.overrideDocumentation
831
+ }
832
+ });
833
+ for (const diagnostic of normalizeArray(occurrence.diagnostics)) {
834
+ const scopedDiagnostic = { ...diagnostic, range: diagnostic.range ?? occurrence.range };
835
+ result.facts.push(externalDiagnosticFact(scopedDiagnostic, context, documentId, path, result.facts.length));
836
+ result.losses.push(externalDiagnosticLoss(scopedDiagnostic, context, path));
837
+ }
838
+ if (scipOccurrenceRoleSet(occurrence.symbol_roles ?? occurrence.symbolRoles).includes('generated')) {
839
+ result.losses.push({
840
+ id: `loss_${idFragment(documentId)}_${occurrenceIndex + 1}_generated_scip_occurrence`,
841
+ severity: 'warning',
842
+ phase: 'index',
843
+ sourceFormat: 'scip',
844
+ kind: 'generatedCode',
845
+ message: 'SCIP occurrence is marked generated; merge admission should review generated/source ownership before applying patches.',
846
+ span: spanFromScipOccurrence(occurrence, path, context.sourceHash),
847
+ semanticSymbolId: symbolId,
848
+ metadata: { format: 'scip', symbolRoles: occurrence.symbol_roles ?? occurrence.symbolRoles }
849
+ });
850
+ }
851
+ }
852
+ }
853
+ return withExternalEmptyLoss(result, context);
854
+ }
855
+
856
+ function normalizeLsifPayload(payload, context) {
857
+ const result = externalSemanticBase(context, { sourceFormat: 'lsif' });
858
+ const records = Array.isArray(payload) ? payload : [...normalizeArray(payload.vertices), ...normalizeArray(payload.edges)];
859
+ const vertices = new Map(records.filter((record) => record?.type === 'vertex').map((record) => [record.id, record]));
860
+ const edges = records.filter((record) => record?.type === 'edge');
861
+ const documentByVertex = new Map();
862
+ const documentIdByRange = new Map();
863
+ const resultSetByRange = new Map();
864
+ const monikerByOut = new Map();
865
+ const definitionRangeIds = new Set();
866
+ for (const vertex of vertices.values()) {
867
+ if (vertex.label === 'document') {
868
+ const path = uriToPath(vertex.uri) ?? vertex.uri ?? context.sourcePath ?? `lsif-document-${result.documents.length + 1}`;
869
+ const documentId = `doc_${idFragment(vertex.id ?? path)}`;
870
+ documentByVertex.set(vertex.id, { id: documentId, path, language: normalizeExternalSemanticLanguage(vertex.languageId ?? context.language) });
871
+ result.documents.push({
872
+ id: documentId,
873
+ path,
874
+ language: normalizeExternalSemanticLanguage(vertex.languageId ?? context.language),
875
+ metadata: { format: 'lsif', vertexId: vertex.id, uri: vertex.uri }
876
+ });
877
+ }
878
+ if (vertex.label === 'moniker') monikerByOut.set(vertex.id, vertex);
879
+ }
880
+ for (const edge of edges) {
881
+ if (edge.label === 'next') resultSetByRange.set(edge.outV, edge.inV);
882
+ if (edge.label === 'moniker') monikerByOut.set(edge.outV, vertices.get(edge.inV) ?? edge);
883
+ if (edge.label === 'contains') {
884
+ const document = documentByVertex.get(edge.outV);
885
+ if (document) {
886
+ for (const rangeId of normalizeArray(edge.inVs ?? edge.inV)) {
887
+ documentIdByRange.set(rangeId, document.id);
888
+ }
889
+ }
890
+ }
891
+ if (edge.label === 'item' && (edge.property === 'definitions' || edge.property === 'declarations')) {
892
+ for (const rangeId of normalizeArray(edge.inVs ?? edge.inV)) definitionRangeIds.add(rangeId);
893
+ }
894
+ }
895
+ const documentIds = result.documents.map((document) => document.id);
896
+ const defaultDocument = result.documents[0] ?? {
897
+ id: `doc_${idFragment(context.sourcePath ?? 'lsif')}`,
898
+ path: context.sourcePath ?? 'lsif:memory',
899
+ language: context.language
900
+ };
901
+ if (!result.documents.length) result.documents.push(defaultDocument);
902
+ for (const [vertexId, vertex] of vertices.entries()) {
903
+ if (vertex.label !== 'range') continue;
904
+ const resultSetId = resultSetByRange.get(vertexId);
905
+ const moniker = monikerByOut.get(resultSetId) ?? monikerByOut.get(vertexId);
906
+ const symbolId = moniker?.identifier
907
+ ? `symbol:lsif:${idFragment(moniker.scheme ?? moniker.kind ?? 'moniker')}:${idFragment(moniker.identifier)}`
908
+ : `symbol:lsif:${idFragment(resultSetId ?? vertexId)}`;
909
+ const documentId = documentIdByRange.get(vertexId) ?? documentIds[0] ?? defaultDocument.id;
910
+ const owningDocument = result.documents.find((document) => document.id === documentId) ?? defaultDocument;
911
+ const span = spanFromLspRange(vertex, owningDocument.path, context.sourceHash, 0);
912
+ if (!result.symbols.some((symbol) => symbol.id === symbolId)) {
913
+ result.symbols.push({
914
+ id: symbolId,
915
+ scheme: 'lsif',
916
+ name: moniker?.identifier ?? `range:${vertexId}`,
917
+ kind: moniker?.kind ?? 'symbol',
918
+ language: owningDocument.language,
919
+ definitionSpan: definitionRangeIds.has(vertexId) ? span : undefined,
920
+ metadata: { format: 'lsif', resultSetId, moniker }
921
+ });
922
+ }
923
+ result.occurrences.push({
924
+ id: `occ_${idFragment(vertexId)}`,
925
+ documentId,
926
+ symbolId,
927
+ role: definitionRangeIds.has(vertexId) ? 'definition' : 'reference',
928
+ span,
929
+ metadata: { format: 'lsif', vertexId, resultSetId }
930
+ });
931
+ }
932
+ for (const edge of edges) {
933
+ if (edge.label === 'textDocument/definition' || edge.label === 'textDocument/references' || edge.label === 'textDocument/declaration') {
934
+ result.relations.push({
935
+ id: `rel_${idFragment(edge.id ?? `${edge.outV}_${edge.inV}_${edge.label}`)}`,
936
+ sourceId: `lsif:${edge.outV}`,
937
+ predicate: edge.label,
938
+ targetId: `lsif:${edge.inV}`,
939
+ metadata: { format: 'lsif', edge }
940
+ });
941
+ }
942
+ }
943
+ return withExternalEmptyLoss(result, context);
944
+ }
945
+
946
+ function normalizeLspPayload(payload, context) {
947
+ const result = externalSemanticBase(context, { sourceFormat: 'lsp' });
948
+ const documents = normalizeLspDocuments(payload, context);
949
+ for (const [documentIndex, document] of documents.entries()) {
950
+ const sourcePath = uriToPath(document.uri) ?? document.sourcePath ?? document.path ?? context.sourcePath ?? `lsp-document-${documentIndex + 1}`;
951
+ const language = normalizeExternalSemanticLanguage(document.languageId ?? document.language ?? context.language);
952
+ const documentId = document.id ?? `doc_${idFragment(sourcePath)}`;
953
+ result.documents.push({
954
+ id: documentId,
955
+ path: sourcePath,
956
+ language,
957
+ sourceHash: document.sourceHash ?? context.sourceHash,
958
+ metadata: { format: 'lsp', uri: document.uri, documentIndex }
959
+ });
960
+ const symbols = normalizeArray(document.documentSymbols ?? document.symbols ?? payload.documentSymbols ?? payload.symbols);
961
+ for (const symbol of symbols) addLspSymbol(result, symbol, {
962
+ context,
963
+ documentId,
964
+ sourcePath,
965
+ language,
966
+ parentName: symbol.containerName
967
+ });
968
+ const semanticTokens = document.semanticTokens ?? payload.semanticTokens;
969
+ if (semanticTokens) addLspSemanticTokens(result, semanticTokens, { context, documentId, sourcePath, language });
970
+ for (const diagnostic of normalizeArray(document.diagnostics ?? payload.diagnostics)) {
971
+ result.facts.push(externalDiagnosticFact(diagnostic, context, documentId, sourcePath, result.facts.length));
972
+ result.losses.push(externalDiagnosticLoss(diagnostic, context, sourcePath));
973
+ }
974
+ }
975
+ return withExternalEmptyLoss(result, context);
976
+ }
977
+
978
+ function normalizeSemanticDbPayload(payload, context) {
979
+ const result = externalSemanticBase(context, { sourceFormat: 'semanticdb' });
980
+ const documents = normalizeArray(payload.documents ?? payload.textDocuments ?? payload);
981
+ for (const [documentIndex, document] of documents.entries()) {
982
+ const sourcePath = uriToPath(document.uri) ?? document.uri ?? document.path ?? context.sourcePath ?? `semanticdb-document-${documentIndex + 1}`;
983
+ const language = normalizeExternalSemanticLanguage(document.language ?? context.language ?? 'scala');
984
+ const documentId = document.id ?? `doc_${idFragment(sourcePath)}`;
985
+ result.documents.push({
986
+ id: documentId,
987
+ path: sourcePath,
988
+ language,
989
+ sourceHash: document.md5 ?? document.sourceHash ?? context.sourceHash,
990
+ metadata: { format: 'semanticdb', schema: document.schema, documentIndex }
991
+ });
992
+ for (const [symbolIndex, symbolInfo] of normalizeArray(document.symbols).entries()) {
993
+ const symbolId = semanticDbSymbolId(symbolInfo.symbol, context, documentId);
994
+ result.symbols.push({
995
+ id: symbolId,
996
+ scheme: 'semanticdb',
997
+ name: symbolInfo.display_name ?? symbolInfo.displayName ?? nameFromExternalSymbol(symbolInfo.symbol),
998
+ kind: normalizeExternalSymbolKind(symbolInfo.kind),
999
+ language,
1000
+ signatureHash: hashSemanticValue(symbolInfo.signature ?? symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation ?? symbolInfo),
1001
+ metadata: { format: 'semanticdb', symbolIndex, rawSymbol: symbolInfo.symbol, properties: symbolInfo.properties }
1002
+ });
1003
+ result.facts.push(...semanticDbSymbolFacts(symbolInfo, symbolId));
1004
+ }
1005
+ for (const [occurrenceIndex, occurrence] of normalizeArray(document.occurrences).entries()) {
1006
+ const symbolId = semanticDbSymbolId(occurrence.symbol, context, documentId);
1007
+ if (!result.symbols.some((symbol) => symbol.id === symbolId)) {
1008
+ result.symbols.push({
1009
+ id: symbolId,
1010
+ scheme: 'semanticdb',
1011
+ name: nameFromExternalSymbol(occurrence.symbol),
1012
+ kind: 'symbol',
1013
+ language,
1014
+ metadata: { format: 'semanticdb', inferredFromOccurrence: true, rawSymbol: occurrence.symbol }
1015
+ });
1016
+ }
1017
+ result.occurrences.push({
1018
+ id: occurrence.id ?? `occ_${idFragment(documentId)}_${occurrenceIndex + 1}`,
1019
+ documentId,
1020
+ symbolId,
1021
+ role: semanticDbOccurrenceRole(occurrence.role),
1022
+ span: spanFromSemanticDbRange(occurrence.range, sourcePath, document.md5 ?? context.sourceHash),
1023
+ metadata: { format: 'semanticdb', role: occurrence.role }
1024
+ });
1025
+ }
1026
+ for (const diagnostic of normalizeArray(document.diagnostics)) {
1027
+ result.facts.push(externalDiagnosticFact(diagnostic, context, documentId, sourcePath, result.facts.length));
1028
+ result.losses.push(externalDiagnosticLoss(diagnostic, context, sourcePath));
1029
+ }
1030
+ }
1031
+ return withExternalEmptyLoss(result, context);
1032
+ }
1033
+
1034
+ function normalizeGenericExternalSemanticIndexPayload(payload, context) {
1035
+ const result = externalSemanticBase(context, { sourceFormat: context.format, genericPayload: true });
1036
+ result.losses.push({
1037
+ id: `loss_${context.idPart}_${idFragment(context.format)}_unsupported_payload`,
1038
+ severity: 'warning',
1039
+ phase: 'index',
1040
+ sourceFormat: context.format,
1041
+ kind: 'unsupportedSemantic',
1042
+ message: `External semantic index format ${context.format} is not recognized; payload hash is preserved as evidence only.`,
1043
+ metadata: { format: context.format, payloadHash: hashSemanticValue(payload) }
1044
+ });
1045
+ return result;
1046
+ }
1047
+
1048
+ function normalizeArray(value) {
1049
+ if (value === undefined || value === null) return [];
1050
+ return Array.isArray(value) ? value : [value];
1051
+ }
1052
+
1053
+ function normalizeExternalSemanticLanguage(value) {
1054
+ if (value === undefined || value === null || value === '') return undefined;
1055
+ const raw = typeof value === 'number' ? externalLanguageNameByNumber[value] : String(value);
1056
+ return normalizeNativeLanguageId(raw);
1057
+ }
1058
+
1059
+ function externalDocument(document, context, index) {
1060
+ const path = document.path ?? document.uri ?? document.relative_path ?? document.relativePath ?? context.sourcePath ?? `external-document-${index + 1}`;
1061
+ return {
1062
+ id: document.id ?? `doc_${idFragment(path)}`,
1063
+ path: uriToPath(path) ?? path,
1064
+ language: normalizeExternalSemanticLanguage(document.language ?? document.languageId ?? context.language),
1065
+ sourceHash: document.sourceHash ?? document.md5 ?? context.sourceHash,
1066
+ metadata: { format: context.format, ...document.metadata }
1067
+ };
1068
+ }
1069
+
1070
+ function externalSymbol(symbol, context, index) {
1071
+ const id = symbol.id ?? symbol.symbolId ?? symbol.symbol ?? `symbol:${context.format}:${index + 1}`;
1072
+ return {
1073
+ ...symbol,
1074
+ id: String(id),
1075
+ scheme: symbol.scheme ?? context.format,
1076
+ name: symbol.name ?? symbol.display_name ?? symbol.displayName ?? nameFromExternalSymbol(id),
1077
+ kind: normalizeExternalSymbolKind(symbol.kind),
1078
+ language: normalizeExternalSemanticLanguage(symbol.language ?? context.language),
1079
+ definitionSpan: normalizeExternalSpan(symbol.definitionSpan ?? symbol.span, context.sourcePath, context.sourceHash),
1080
+ metadata: { format: context.format, rawSymbol: symbol.symbol, ...symbol.metadata }
1081
+ };
1082
+ }
1083
+
1084
+ function externalOccurrence(occurrence, context, index) {
1085
+ return {
1086
+ ...occurrence,
1087
+ id: occurrence.id ?? `occ_${context.idPart}_${index + 1}`,
1088
+ documentId: occurrence.documentId ?? occurrence.document_id ?? `doc_${idFragment(occurrence.path ?? context.sourcePath ?? context.format)}`,
1089
+ symbolId: occurrence.symbolId ?? occurrence.symbol_id ?? occurrence.symbol ?? `symbol:${context.format}:unknown`,
1090
+ role: normalizeExternalOccurrenceRole(occurrence.role),
1091
+ span: normalizeExternalSpan(occurrence.span ?? occurrence.range, occurrence.path ?? context.sourcePath, context.sourceHash),
1092
+ metadata: { format: context.format, ...occurrence.metadata }
1093
+ };
1094
+ }
1095
+
1096
+ function externalRelation(relation, context, index) {
1097
+ return {
1098
+ ...relation,
1099
+ id: relation.id ?? `rel_${context.idPart}_${index + 1}`,
1100
+ sourceId: relation.sourceId ?? relation.source_id ?? relation.subjectId ?? relation.subject_id ?? `external:${context.format}`,
1101
+ predicate: relation.predicate ?? relation.label ?? relation.kind ?? 'related',
1102
+ targetId: relation.targetId ?? relation.target_id ?? relation.objectId ?? relation.object_id ?? relation.symbol ?? `external:${context.format}`,
1103
+ metadata: { format: context.format, ...relation.metadata }
1104
+ };
1105
+ }
1106
+
1107
+ function externalFact(fact, context, index) {
1108
+ return {
1109
+ ...fact,
1110
+ id: fact.id ?? `fact_${context.idPart}_${index + 1}`,
1111
+ predicate: fact.predicate ?? fact.kind ?? 'externalFact',
1112
+ subjectId: fact.subjectId ?? fact.subject_id ?? fact.symbolId ?? fact.symbol_id ?? `external:${context.format}`,
1113
+ value: fact.value ?? fact.data ?? fact,
1114
+ metadata: { format: context.format, ...fact.metadata }
1115
+ };
1116
+ }
1117
+
1118
+ function externalSemanticEvidence(context, status, summary, metadata = {}) {
1119
+ return {
1120
+ id: `evidence_${context.idPart}_${idFragment(context.format)}_external_semantic_index`,
1121
+ kind: 'import',
1122
+ status,
1123
+ path: context.sourcePath,
1124
+ summary,
1125
+ metadata: {
1126
+ format: context.format,
1127
+ parser: context.parser,
1128
+ projectRoot: context.projectRoot,
1129
+ ...metadata
1130
+ }
1131
+ };
1132
+ }
1133
+
1134
+ function externalSemanticCoverageLoss(context) {
1135
+ return {
1136
+ id: `loss_${context.idPart}_${idFragment(context.format)}_partial_semantic_index`,
1137
+ severity: 'info',
1138
+ phase: 'index',
1139
+ sourceFormat: context.format,
1140
+ kind: 'partialSemanticIndex',
1141
+ message: `${context.format} payload imported symbols, occurrences, and facts as external semantic evidence; full parser AST, comments, trivia, and executable semantics still require a native parser adapter.`,
1142
+ semanticIndexId: context.semanticIndexId,
1143
+ metadata: {
1144
+ format: context.format,
1145
+ parser: context.parser,
1146
+ source: 'external-semantic-index'
1147
+ }
1148
+ };
1149
+ }
1150
+
1151
+ function withExternalEmptyLoss(result, context) {
1152
+ if (!result.documents.length) {
1153
+ result.documents.push({
1154
+ id: `doc_${context.idPart}_${idFragment(context.format)}`,
1155
+ path: context.sourcePath ?? `${context.format}:memory`,
1156
+ language: context.language,
1157
+ sourceHash: context.sourceHash,
1158
+ metadata: { format: context.format, inferred: true }
1159
+ });
1160
+ }
1161
+ if (!result.symbols.length && !result.occurrences.length) {
1162
+ result.losses.push({
1163
+ id: `loss_${context.idPart}_${idFragment(context.format)}_empty_semantic_index`,
1164
+ severity: 'warning',
1165
+ phase: 'index',
1166
+ sourceFormat: context.format,
1167
+ kind: 'partialSemanticIndex',
1168
+ message: `${context.format} payload did not contain symbols or occurrences that Frontier can map.`,
1169
+ metadata: { format: context.format }
1170
+ });
1171
+ }
1172
+ attachExternalOwnership(result, context);
1173
+ result.symbols = uniqueRecordsById(result.symbols);
1174
+ result.occurrences = uniqueRecordsById(result.occurrences);
1175
+ result.relations = uniqueRecordsById(result.relations);
1176
+ result.facts = uniqueRecordsById(result.facts);
1177
+ result.losses = uniqueByLossId(result.losses);
1178
+ result.evidence = uniqueByEvidenceId(result.evidence);
1179
+ return result;
1180
+ }
1181
+
1182
+ function attachExternalOwnership(result, context) {
1183
+ const occurrencesBySymbol = new Map();
1184
+ for (const occurrence of result.occurrences) {
1185
+ if (!occurrencesBySymbol.has(occurrence.symbolId)) occurrencesBySymbol.set(occurrence.symbolId, []);
1186
+ occurrencesBySymbol.get(occurrence.symbolId).push(occurrence);
1187
+ result.relations.push({
1188
+ id: `rel_${idFragment(occurrence.documentId)}_${idFragment(occurrence.id)}_${idFragment(occurrence.role)}`,
1189
+ sourceId: occurrence.documentId,
1190
+ predicate: externalRelationPredicateForOccurrence(occurrence),
1191
+ targetId: occurrence.symbolId,
1192
+ metadata: {
1193
+ format: context.format,
1194
+ source: 'external-semantic-index',
1195
+ occurrenceId: occurrence.id,
1196
+ role: occurrence.role
1197
+ }
1198
+ });
1199
+ }
1200
+ result.symbols = result.symbols.map((symbol) => {
1201
+ const occurrences = occurrencesBySymbol.get(symbol.id) ?? [];
1202
+ const definition = occurrences.find((occurrence) => occurrence.role === 'definition') ?? occurrences[0];
1203
+ const sourceSpan = symbol.definitionSpan ?? definition?.span;
1204
+ const regionKind = semanticRegionKindForSymbol(symbol, undefined, undefined);
1205
+ const key = [
1206
+ 'external',
1207
+ symbol.language ?? context.language ?? 'unknown',
1208
+ sourceSpan?.path ?? context.sourcePath ?? 'memory',
1209
+ regionKind,
1210
+ symbol.name ?? symbol.id
1211
+ ].join('#');
1212
+ const region = {
1213
+ id: `region_${idFragment(key)}`,
1214
+ key,
1215
+ regionKind,
1216
+ granularity: 'symbol',
1217
+ language: symbol.language ?? context.language,
1218
+ documentId: definition?.documentId,
1219
+ sourcePath: sourceSpan?.path ?? context.sourcePath,
1220
+ sourceHash: context.sourceHash,
1221
+ symbolId: symbol.id,
1222
+ symbolName: symbol.name,
1223
+ symbolKind: symbol.kind,
1224
+ sourceSpan,
1225
+ precision: sourceSpan ? 'declaration' : 'unknown',
1226
+ mergePolicy: semanticRegionMergePolicy(regionKind),
1227
+ metadata: {
1228
+ format: context.format,
1229
+ source: 'external-semantic-index'
1230
+ }
1231
+ };
1232
+ result.facts.push({
1233
+ id: `fact_${idFragment(symbol.id)}_ownership_region`,
1234
+ predicate: 'semanticOwnershipRegion',
1235
+ subjectId: symbol.id,
1236
+ value: region
1237
+ }, {
1238
+ id: `fact_${idFragment(symbol.id)}_ownership_region_taxonomy`,
1239
+ predicate: 'semanticOwnershipRegionTaxonomy',
1240
+ subjectId: symbol.id,
1241
+ value: {
1242
+ regionKind: region.regionKind,
1243
+ granularity: region.granularity,
1244
+ key: region.key
1245
+ }
1246
+ });
1247
+ return {
1248
+ ...symbol,
1249
+ definitionSpan: symbol.definitionSpan ?? definition?.span,
1250
+ metadata: {
1251
+ ...symbol.metadata,
1252
+ ownershipRegionId: symbol.metadata?.ownershipRegionId ?? region.id,
1253
+ ownershipRegionKey: symbol.metadata?.ownershipRegionKey ?? region.key,
1254
+ ownershipRegionKind: symbol.metadata?.ownershipRegionKind ?? region.regionKind
1255
+ }
1256
+ };
1257
+ });
1258
+ }
1259
+
1260
+ function externalRelationPredicateForOccurrence(occurrence) {
1261
+ const role = String(occurrence.role ?? '').toLowerCase();
1262
+ if (role === 'definition' || role === 'declaration') return 'defines';
1263
+ if (role === 'import') return 'imports';
1264
+ if (role === 'write') return 'writes';
1265
+ if (role === 'read') return 'reads';
1266
+ return 'references';
1267
+ }
1268
+
1269
+ function externalSemanticSourceMapMappings(semanticIndex, context) {
1270
+ const symbolsById = new Map((semanticIndex.symbols ?? []).map((symbol) => [symbol.id, symbol]));
1271
+ const evidenceIds = (context.evidence ?? []).map((record) => record.id).filter(Boolean);
1272
+ const lossIds = (context.losses ?? []).map((loss) => loss.id).filter(Boolean);
1273
+ return (semanticIndex.occurrences ?? [])
1274
+ .filter((occurrence) => occurrence.span)
1275
+ .map((occurrence, index) => {
1276
+ const symbol = symbolsById.get(occurrence.symbolId);
1277
+ return {
1278
+ id: `map_${idFragment(occurrence.id ?? `${occurrence.symbolId}_${index + 1}`)}`,
1279
+ semanticSymbolId: occurrence.symbolId,
1280
+ semanticOccurrenceId: occurrence.id,
1281
+ sourceSpan: occurrence.span,
1282
+ evidenceIds,
1283
+ lossIds,
1284
+ ownershipRegionId: symbol?.metadata?.ownershipRegionId,
1285
+ ownershipRegionKey: symbol?.metadata?.ownershipRegionKey,
1286
+ ownershipRegionKind: symbol?.metadata?.ownershipRegionKind,
1287
+ precision: occurrence.span ? 'declaration' : 'unknown',
1288
+ metadata: {
1289
+ source: 'external-semantic-index'
1290
+ }
1291
+ };
1292
+ });
1293
+ }
1294
+
1295
+ function scipSymbolId(symbol, context, documentId) {
1296
+ if (!symbol) return undefined;
1297
+ const raw = String(symbol);
1298
+ if (raw.startsWith('symbol:')) return raw;
1299
+ const scope = /^local\b/i.test(raw) ? `${documentId ?? context.idPart}:` : '';
1300
+ return `symbol:scip:${idFragment(scope + raw)}`;
1301
+ }
1302
+
1303
+ function semanticDbSymbolId(symbol, context, documentId) {
1304
+ if (!symbol) return `symbol:semanticdb:${context.idPart}:unknown`;
1305
+ if (String(symbol).startsWith('symbol:')) return String(symbol);
1306
+ const scope = /^local\d+$/i.test(String(symbol)) ? `${documentId}:` : '';
1307
+ return `symbol:semanticdb:${idFragment(scope + symbol)}`;
1308
+ }
1309
+
1310
+ function nameFromExternalSymbol(symbol) {
1311
+ const value = String(symbol ?? 'symbol');
1312
+ const cleaned = value
1313
+ .replace(/^symbol:[^:]+:/, '')
1314
+ .replace(/[`'"]/g, '')
1315
+ .split(/[\/#.:() +]+/)
1316
+ .filter(Boolean)
1317
+ .at(-1);
1318
+ return cleaned || value;
1319
+ }
1320
+
1321
+ const externalSymbolKindByNumber = Object.freeze({
1322
+ 1: 'array',
1323
+ 2: 'assertion',
1324
+ 3: 'associatedType',
1325
+ 4: 'attribute',
1326
+ 7: 'class',
1327
+ 8: 'constant',
1328
+ 9: 'constructor',
1329
+ 11: 'enum',
1330
+ 12: 'enumMember',
1331
+ 13: 'event',
1332
+ 15: 'field',
1333
+ 16: 'file',
1334
+ 17: 'function',
1335
+ 21: 'interface',
1336
+ 25: 'macro',
1337
+ 26: 'method',
1338
+ 28: 'message',
1339
+ 29: 'module',
1340
+ 30: 'namespace',
1341
+ 35: 'package',
1342
+ 37: 'parameter',
1343
+ 41: 'property',
1344
+ 42: 'protocol',
1345
+ 49: 'struct',
1346
+ 53: 'trait',
1347
+ 54: 'type',
1348
+ 55: 'typeAlias',
1349
+ 58: 'typeParameter',
1350
+ 61: 'variable',
1351
+ 66: 'abstractMethod'
1352
+ });
1353
+
1354
+ const lspSymbolKindByNumber = Object.freeze({
1355
+ 1: 'file',
1356
+ 2: 'module',
1357
+ 3: 'namespace',
1358
+ 4: 'package',
1359
+ 5: 'class',
1360
+ 6: 'method',
1361
+ 7: 'property',
1362
+ 8: 'field',
1363
+ 9: 'constructor',
1364
+ 10: 'enum',
1365
+ 11: 'interface',
1366
+ 12: 'function',
1367
+ 13: 'variable',
1368
+ 14: 'constant',
1369
+ 22: 'enumMember',
1370
+ 23: 'struct',
1371
+ 26: 'typeParameter'
1372
+ });
1373
+
1374
+ const externalLanguageNameByNumber = Object.freeze({
1375
+ 1: 'csharp',
1376
+ 2: 'swift',
1377
+ 3: 'dart',
1378
+ 4: 'kotlin',
1379
+ 5: 'scala',
1380
+ 6: 'java',
1381
+ 15: 'python',
1382
+ 16: 'ruby',
1383
+ 17: 'elixir',
1384
+ 18: 'erlang',
1385
+ 19: 'php',
1386
+ 22: 'javascript',
1387
+ 23: 'typescript',
1388
+ 33: 'go',
1389
+ 34: 'c',
1390
+ 35: 'cpp',
1391
+ 38: 'zig',
1392
+ 40: 'rust',
1393
+ 44: 'haskell',
1394
+ 54: 'r',
1395
+ 69: 'sql'
1396
+ });
1397
+
1398
+ function normalizeExternalSymbolKind(kind) {
1399
+ if (kind === undefined || kind === null || kind === '') return 'symbol';
1400
+ if (typeof kind === 'number') return externalSymbolKindByNumber[kind] ?? lspSymbolKindByNumber[kind] ?? `kind${kind}`;
1401
+ return String(kind).replace(/^[A-Z_]+_/, '').replace(/^[A-Z]/, (letter) => letter.toLowerCase());
1402
+ }
1403
+
1404
+ function normalizeLspSymbolKind(kind) {
1405
+ if (typeof kind === 'number') return lspSymbolKindByNumber[kind] ?? `kind${kind}`;
1406
+ return normalizeExternalSymbolKind(kind);
1407
+ }
1408
+
1409
+ function scipSyntaxKind(kind) {
1410
+ const normalized = typeof kind === 'number' ? kind : Number(kind);
1411
+ if (normalized === 15 || normalized === 16) return 'function';
1412
+ if (normalized === 19 || normalized === 20) return 'type';
1413
+ if (normalized === 25 || normalized === 26) return 'module';
1414
+ if (normalized === 9 || normalized === 10 || normalized === 12) return 'variable';
1415
+ return 'symbol';
1416
+ }
1417
+
1418
+ function normalizeExternalOccurrenceRole(role) {
1419
+ const value = String(role ?? 'reference').toLowerCase();
1420
+ if (value.includes('def')) return 'definition';
1421
+ if (value.includes('decl')) return 'declaration';
1422
+ if (value.includes('import')) return 'import';
1423
+ if (value.includes('write')) return 'write';
1424
+ if (value.includes('read')) return 'read';
1425
+ return value === '2' ? 'definition' : value === '1' ? 'reference' : 'reference';
1426
+ }
1427
+
1428
+ function scipOccurrenceRole(value) {
1429
+ const role = Number(value ?? 0);
1430
+ if ((role & 0x1) > 0) return 'definition';
1431
+ if ((role & 0x2) > 0) return 'import';
1432
+ if ((role & 0x4) > 0) return 'write';
1433
+ if ((role & 0x8) > 0) return 'read';
1434
+ return 'reference';
1435
+ }
1436
+
1437
+ function scipOccurrenceRoleSet(value) {
1438
+ const role = Number(value ?? 0);
1439
+ const roles = [];
1440
+ if ((role & 0x1) > 0) roles.push('definition');
1441
+ if ((role & 0x2) > 0) roles.push('import');
1442
+ if ((role & 0x4) > 0) roles.push('write');
1443
+ if ((role & 0x8) > 0) roles.push('read');
1444
+ if ((role & 0x10) > 0) roles.push('generated');
1445
+ if ((role & 0x20) > 0) roles.push('test');
1446
+ if ((role & 0x40) > 0) roles.push('forwardDefinition');
1447
+ return roles.length ? roles : ['reference'];
1448
+ }
1449
+
1450
+ function semanticDbOccurrenceRole(value) {
1451
+ const role = String(value ?? 'reference').toLowerCase();
1452
+ if (role === '2' || role.includes('definition')) return 'definition';
1453
+ return 'reference';
1454
+ }
1455
+
1456
+ function scipRelationshipRelations(symbolInfo, symbolId, context) {
1457
+ return normalizeArray(symbolInfo.relationships).flatMap((relationship, index) => {
1458
+ const targetId = scipSymbolId(relationship.symbol, context);
1459
+ if (!targetId) return [];
1460
+ const predicates = [];
1461
+ if (relationship.is_reference ?? relationship.isReference) predicates.push('references');
1462
+ if (relationship.is_implementation ?? relationship.isImplementation) predicates.push('implements');
1463
+ if (relationship.is_type_definition ?? relationship.isTypeDefinition) predicates.push('typeDefinition');
1464
+ if (relationship.is_definition ?? relationship.isDefinition) predicates.push('definitionOf');
1465
+ return (predicates.length ? predicates : ['related']).map((predicate) => ({
1466
+ id: `rel_${idFragment(symbolId)}_${idFragment(targetId)}_${idFragment(predicate)}_${index + 1}`,
1467
+ sourceId: symbolId,
1468
+ predicate,
1469
+ targetId,
1470
+ metadata: { format: 'scip', relationship }
1471
+ }));
1472
+ });
1473
+ }
1474
+
1475
+ function scipSymbolFacts(symbolInfo, symbolId) {
1476
+ const facts = [];
1477
+ if (symbolInfo.documentation) {
1478
+ facts.push({
1479
+ id: `fact_${idFragment(symbolId)}_documentation`,
1480
+ predicate: 'documentation',
1481
+ subjectId: symbolId,
1482
+ value: normalizeArray(symbolInfo.documentation)
1483
+ });
1484
+ }
1485
+ const signature = symbolInfo.signature_documentation ?? symbolInfo.signatureDocumentation;
1486
+ if (signature) {
1487
+ facts.push({
1488
+ id: `fact_${idFragment(symbolId)}_signature`,
1489
+ predicate: 'signature',
1490
+ subjectId: symbolId,
1491
+ value: signature
1492
+ });
1493
+ }
1494
+ for (const [index, relationship] of normalizeArray(symbolInfo.relationships).entries()) {
1495
+ facts.push({
1496
+ id: `fact_${idFragment(symbolId)}_relationship_${index + 1}`,
1497
+ predicate: 'relationship',
1498
+ subjectId: symbolId,
1499
+ value: relationship
1500
+ });
1501
+ }
1502
+ return facts;
1503
+ }
1504
+
1505
+ function semanticDbSymbolFacts(symbolInfo, symbolId) {
1506
+ const facts = [];
1507
+ for (const key of ['signature', 'properties', 'annotations', 'access', 'language']) {
1508
+ if (symbolInfo[key] !== undefined) {
1509
+ facts.push({
1510
+ id: `fact_${idFragment(symbolId)}_${idFragment(key)}`,
1511
+ predicate: key,
1512
+ subjectId: symbolId,
1513
+ value: symbolInfo[key]
1514
+ });
1515
+ }
1516
+ }
1517
+ return facts;
1518
+ }
1519
+
1520
+ function normalizeLspDocuments(payload, context) {
1521
+ if (Array.isArray(payload.documents)) return payload.documents;
1522
+ if (payload.textDocument || payload.uri || payload.documentSymbols || payload.symbols || payload.semanticTokens || payload.diagnostics) {
1523
+ return [{
1524
+ ...payload,
1525
+ uri: payload.uri ?? payload.textDocument?.uri,
1526
+ languageId: payload.languageId ?? payload.language ?? context.language,
1527
+ documentSymbols: payload.documentSymbols,
1528
+ symbols: payload.symbols,
1529
+ semanticTokens: payload.semanticTokens,
1530
+ diagnostics: payload.diagnostics
1531
+ }];
1532
+ }
1533
+ return [{ uri: context.sourcePath, languageId: context.language }];
1534
+ }
1535
+
1536
+ function addLspSymbol(result, symbol, input) {
1537
+ const location = symbol.location ?? {};
1538
+ const range = symbol.range ?? location.range ?? symbol.selectionRange;
1539
+ const sourcePath = uriToPath(location.uri ?? symbol.uri) ?? input.sourcePath;
1540
+ const symbolName = symbol.name ?? symbol.containerName ?? `symbol_${result.symbols.length + 1}`;
1541
+ const symbolId = symbol.id ?? `symbol:lsp:${idFragment(input.language ?? 'unknown')}:${idFragment([input.parentName, symbolName].filter(Boolean).join('.'))}`;
1542
+ const ownershipSpan = spanFromLspRange(range, sourcePath, input.context.sourceHash, 0);
1543
+ const selectionSpan = spanFromLspRange(symbol.selectionRange ?? range, sourcePath, input.context.sourceHash, 0);
1544
+ if (!result.symbols.some((entry) => entry.id === symbolId)) {
1545
+ result.symbols.push({
1546
+ id: symbolId,
1547
+ scheme: 'lsp',
1548
+ name: symbolName,
1549
+ kind: normalizeLspSymbolKind(symbol.kind),
1550
+ language: input.language,
1551
+ definitionSpan: ownershipSpan,
1552
+ signatureHash: hashSemanticValue([symbolName, symbol.kind, symbol.detail]),
1553
+ metadata: {
1554
+ format: 'lsp',
1555
+ detail: symbol.detail,
1556
+ tags: symbol.tags,
1557
+ deprecated: symbol.deprecated,
1558
+ containerName: symbol.containerName,
1559
+ parentName: input.parentName
1560
+ }
1561
+ });
1562
+ }
1563
+ result.occurrences.push({
1564
+ id: `occ_${idFragment(symbolId)}_${result.occurrences.length + 1}`,
1565
+ documentId: input.documentId,
1566
+ symbolId,
1567
+ role: 'definition',
1568
+ span: selectionSpan,
1569
+ metadata: { format: 'lsp', range, selectionRange: symbol.selectionRange }
1570
+ });
1571
+ for (const child of normalizeArray(symbol.children)) {
1572
+ addLspSymbol(result, child, {
1573
+ ...input,
1574
+ parentName: [input.parentName, symbolName].filter(Boolean).join('.')
1575
+ });
1576
+ }
1577
+ }
1578
+
1579
+ function addLspSemanticTokens(result, semanticTokens, input) {
1580
+ const data = normalizeArray(semanticTokens.data);
1581
+ const legend = semanticTokens.legend ?? {};
1582
+ let line = 0;
1583
+ let character = 0;
1584
+ for (let index = 0; index + 4 < data.length; index += 5) {
1585
+ line += Number(data[index] ?? 0);
1586
+ character = Number(data[index] ?? 0) === 0 ? character + Number(data[index + 1] ?? 0) : Number(data[index + 1] ?? 0);
1587
+ const length = Number(data[index + 2] ?? 0);
1588
+ const tokenType = legend.tokenTypes?.[Number(data[index + 3] ?? 0)] ?? `tokenType${data[index + 3] ?? 0}`;
1589
+ const span = {
1590
+ path: input.sourcePath,
1591
+ startLine: line + 1,
1592
+ startColumn: character + 1,
1593
+ endLine: line + 1,
1594
+ endColumn: character + length + 1
1595
+ };
1596
+ result.facts.push({
1597
+ id: `fact_${idFragment(input.documentId)}_semantic_token_${index / 5 + 1}`,
1598
+ predicate: 'semanticToken',
1599
+ subjectId: input.documentId,
1600
+ value: {
1601
+ tokenType,
1602
+ tokenModifiers: semanticTokenModifiers(Number(data[index + 4] ?? 0), legend.tokenModifiers),
1603
+ span
1604
+ },
1605
+ metadata: { format: 'lsp' }
1606
+ });
1607
+ }
1608
+ }
1609
+
1610
+ function semanticTokenModifiers(bitset, modifiers = []) {
1611
+ const result = [];
1612
+ for (let index = 0; index < modifiers.length; index += 1) {
1613
+ if ((bitset & (1 << index)) > 0) result.push(modifiers[index]);
1614
+ }
1615
+ return result;
1616
+ }
1617
+
1618
+ function externalDiagnosticFact(diagnostic, context, documentId, sourcePath, index) {
1619
+ return {
1620
+ id: diagnostic.id ? `fact_${idFragment(diagnostic.id)}_diagnostic` : `fact_${context.idPart}_${idFragment(sourcePath)}_diagnostic_${index + 1}`,
1621
+ predicate: `${context.format}.diagnostic`,
1622
+ subjectId: documentId,
1623
+ value: {
1624
+ severity: diagnostic.severity,
1625
+ code: diagnostic.code,
1626
+ message: diagnostic.message,
1627
+ source: diagnostic.source,
1628
+ tags: diagnostic.tags,
1629
+ range: diagnostic.range,
1630
+ span: normalizeExternalSpan(diagnostic.range ?? diagnostic.span, sourcePath, context.sourceHash)
1631
+ },
1632
+ metadata: {
1633
+ format: context.format,
1634
+ source: 'external-semantic-index'
1635
+ }
1636
+ };
1637
+ }
1638
+
1639
+ function externalDiagnosticLoss(diagnostic, context, sourcePath) {
1640
+ const severity = externalDiagnosticSeverity(diagnostic.severity);
1641
+ return {
1642
+ id: diagnostic.id ?? `loss_${context.idPart}_${idFragment(diagnostic.code ?? diagnostic.message ?? severity)}_${idFragment(sourcePath)}`,
1643
+ severity,
1644
+ phase: 'index',
1645
+ sourceFormat: context.format,
1646
+ kind: severity === 'error' ? 'unsupportedSemantic' : 'partialSemanticIndex',
1647
+ message: String(diagnostic.message ?? `${context.format} diagnostic reported ${severity}.`),
1648
+ span: normalizeExternalSpan(diagnostic.range ?? diagnostic.span, sourcePath, context.sourceHash),
1649
+ metadata: {
1650
+ format: context.format,
1651
+ code: diagnostic.code,
1652
+ source: diagnostic.source,
1653
+ tags: diagnostic.tags
1654
+ }
1655
+ };
1656
+ }
1657
+
1658
+ function externalDiagnosticSeverity(value) {
1659
+ if (value === undefined || value === null || value === '') return 'error';
1660
+ const raw = String(value).toLowerCase();
1661
+ if (raw === '1' || raw.includes('error')) return 'error';
1662
+ if (raw === '3' || raw.includes('info') || raw.includes('hint')) return 'info';
1663
+ return 'warning';
1664
+ }
1665
+
1666
+ function spanFromScipOccurrence(occurrence, sourcePath, sourceHash) {
1667
+ if (occurrence.single_line_range || occurrence.singleLineRange) {
1668
+ const range = occurrence.single_line_range ?? occurrence.singleLineRange;
1669
+ return {
1670
+ sourceId: sourceHash,
1671
+ path: sourcePath,
1672
+ startLine: Number(range.line ?? 0) + 1,
1673
+ startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
1674
+ endLine: Number(range.line ?? 0) + 1,
1675
+ endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
1676
+ };
1677
+ }
1678
+ if (occurrence.multi_line_range || occurrence.multiLineRange) {
1679
+ const range = occurrence.multi_line_range ?? occurrence.multiLineRange;
1680
+ return {
1681
+ sourceId: sourceHash,
1682
+ path: sourcePath,
1683
+ startLine: Number(range.start_line ?? range.startLine ?? 0) + 1,
1684
+ startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
1685
+ endLine: Number(range.end_line ?? range.endLine ?? 0) + 1,
1686
+ endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
1687
+ };
1688
+ }
1689
+ const range = occurrence.range;
1690
+ if (Array.isArray(range) && range.length >= 3) {
1691
+ const startLine = Number(range[0] ?? 0);
1692
+ const startColumn = Number(range[1] ?? 0);
1693
+ const endLine = range.length >= 4 ? Number(range[2] ?? startLine) : startLine;
1694
+ const endColumn = range.length >= 4 ? Number(range[3] ?? startColumn) : Number(range[2] ?? startColumn);
1695
+ return {
1696
+ sourceId: sourceHash,
1697
+ path: sourcePath,
1698
+ startLine: startLine + 1,
1699
+ startColumn: startColumn + 1,
1700
+ endLine: endLine + 1,
1701
+ endColumn: endColumn + 1
1702
+ };
1703
+ }
1704
+ return undefined;
1705
+ }
1706
+
1707
+ function spanFromSemanticDbRange(range, sourcePath, sourceHash) {
1708
+ if (!range) return undefined;
1709
+ return {
1710
+ sourceId: sourceHash,
1711
+ path: sourcePath,
1712
+ startLine: Number(range.start_line ?? range.startLine ?? 0) + 1,
1713
+ startColumn: Number(range.start_character ?? range.startCharacter ?? 0) + 1,
1714
+ endLine: Number(range.end_line ?? range.endLine ?? 0) + 1,
1715
+ endColumn: Number(range.end_character ?? range.endCharacter ?? 0) + 1
1716
+ };
1717
+ }
1718
+
1719
+ function spanFromLspRange(range, sourcePath, sourceHash, base = 0) {
1720
+ if (!range) return undefined;
1721
+ const source = range.start && range.end ? range : { start: range, end: range.end ?? range };
1722
+ return {
1723
+ sourceId: sourceHash,
1724
+ path: sourcePath,
1725
+ startLine: Number(source.start?.line ?? source.startLine ?? 0) + (base === 0 ? 1 : 0),
1726
+ startColumn: Number(source.start?.character ?? source.startColumn ?? 0) + (base === 0 ? 1 : 0),
1727
+ endLine: Number(source.end?.line ?? source.endLine ?? source.start?.line ?? 0) + (base === 0 ? 1 : 0),
1728
+ endColumn: Number(source.end?.character ?? source.endColumn ?? source.start?.character ?? 0) + (base === 0 ? 1 : 0)
1729
+ };
1730
+ }
1731
+
1732
+ function normalizeExternalSpan(value, sourcePath, sourceHash) {
1733
+ if (!value) return undefined;
1734
+ if (Array.isArray(value)) {
1735
+ return spanFromScipOccurrence({ range: value }, sourcePath, sourceHash);
1736
+ }
1737
+ if (value.start || value.end || value.line !== undefined) return spanFromLspRange(value, sourcePath, sourceHash, 0);
1738
+ if (value.startLine !== undefined || value.start_line !== undefined) {
1739
+ return {
1740
+ sourceId: value.sourceId ?? sourceHash,
1741
+ path: value.path ?? sourcePath,
1742
+ start: value.start,
1743
+ end: value.end,
1744
+ startLine: Number(value.startLine ?? value.start_line),
1745
+ startColumn: value.startColumn ?? value.start_character,
1746
+ endLine: value.endLine ?? value.end_line,
1747
+ endColumn: value.endColumn ?? value.end_character
1748
+ };
1749
+ }
1750
+ return undefined;
1751
+ }
1752
+
1753
+ function uriToPath(uri) {
1754
+ if (typeof uri !== 'string') return undefined;
1755
+ if (uri.startsWith('file://')) {
1756
+ try {
1757
+ return decodeURIComponent(new URL(uri).pathname);
1758
+ } catch {
1759
+ return uri.replace(/^file:\/\//, '');
1760
+ }
1761
+ }
1762
+ return uri;
1763
+ }
1764
+
547
1765
  export function projectFrontierAst(document, target = 'typescript', options = {}) {
548
1766
  const normalized = normalizeCompileTarget(target);
549
1767
  const projector = projectors[normalized];