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