@shapeshift-labs/frontier-lang-compiler 0.2.12 → 0.2.14

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 CHANGED
@@ -57,6 +57,7 @@ Ask the compiler what is actually covered before sending native imports into a m
57
57
  ```js
58
58
  import {
59
59
  createNativeImportCoverageMatrix,
60
+ createProjectionTargetLossMatrix,
60
61
  importNativeSource
61
62
  } from '@shapeshift-labs/frontier-lang-compiler';
62
63
 
@@ -71,8 +72,22 @@ const python = matrix.languages.find((entry) => entry.language === 'python');
71
72
 
72
73
  console.log(python.imports.readiness); // scanner imports are intentionally review-required
73
74
  console.log(python.parserAdapters); // host-owned exact parsers such as LibCST can be injected
75
+
76
+ const projectionMatrix = createProjectionTargetLossMatrix({ imports: [imported] });
77
+ const pythonProjection = projectionMatrix.languages.find((entry) => entry.language === 'python');
78
+
79
+ console.log(pythonProjection.sourceProjection.exactSource.lossClass); // "exactSourceProjection"
80
+ console.log(pythonProjection.sourceProjection.stubs.lossClass); // "nativeSourceStubs"
81
+ console.log(pythonProjection.targets.find((entry) => entry.target === 'rust').lossClass); // "missingAdapter"
74
82
  ```
75
83
 
84
+ The projection target matrix separates four runtime/API classes:
85
+
86
+ - `exactSourceProjection`: exact source can be emitted when the import carries matching source text or source-preservation evidence.
87
+ - `nativeSourceStubs`: declaration stubs can be emitted, but bodies, resolved types, and executable semantics are review-required.
88
+ - `unsupportedTargetFeatures`: a target slot exists, but the source profile or import evidence declares features such as macros, preprocessors, dynamic runtime behavior, generated code, unsupported syntax, or unresolved inference that this facade cannot prove lossless.
89
+ - `missingAdapter`: no native-to-target projection adapter is declared; preserve or stub the original source language instead, or inject host-owned parser/semantic adapter evidence.
90
+
76
91
  Preserve exact native source text, token/trivia hashes, comments, whitespace, and source directives as evidence. This does not claim full semantic understanding; it keeps round-trip material available while exact parser adapters catch up:
77
92
 
78
93
  ```js
@@ -171,6 +186,9 @@ const project = await importNativeProject({
171
186
  console.log(imported.universalAst.sourceMaps.length);
172
187
  console.log(project.semanticIndex.symbols.length);
173
188
  console.log(imported.adapter.coverage.exactness);
189
+ console.log(imported.adapter.coverage.capabilityEvidence.declared.exactness);
190
+ console.log(imported.adapter.coverage.capabilityEvidence.observed.sourceRanges);
191
+ console.log(imported.adapter.coverage.capabilityEvidence.gaps);
174
192
  console.log(imported.adapter.coverage.semanticCoverage.level);
175
193
  console.log(project.metadata.sourcePreservationSummary.total);
176
194
  ```
@@ -184,6 +202,8 @@ The built-in adapter factories are dependency-light wrappers for caller-owned pa
184
202
 
185
203
  Adapter summaries include a structured `coverage` record so merge queues can distinguish exact parser AST imports from declaration scans. The record declares exactness, parser token/trivia support, diagnostics support, source-range and generated-range support, and semantic coverage. Built-in wrappers normalize native AST/CST nodes and declaration-level semantic indexes; they do not claim resolved references, types, control flow, generated ranges, or token/trivia fidelity unless the host adapter supplies that evidence.
186
204
 
205
+ Coverage records also keep declared, observed, and effective capability evidence separate. `coverage.capabilityEvidence.gaps` highlights missing exact AST, token/trivia, parser diagnostics, source range, generated range, reference, type, and control-flow evidence for the current adapter/import. `observedOnly` means the import produced evidence the adapter did not declare, while `declaredOnly` means the adapter declared support that this run did not exercise.
206
+
187
207
  ## Related Packages
188
208
 
189
209
  The published Frontier package family is generated from one shared package catalog so READMEs stay in sync across packages:
package/bench/smoke.mjs CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  compileFrontierSource,
4
4
  createEstreeNativeImporterAdapter,
5
5
  createNativeImportCoverageMatrix,
6
+ createProjectionTargetLossMatrix,
6
7
  createNativeSourcePreservation,
7
8
  createSemanticImportSidecar,
8
9
  importNativeSource,
@@ -68,6 +69,10 @@ const matrixStart = performance.now();
68
69
  const coverageMatrix = createNativeImportCoverageMatrix({ imports: nativeImportResults });
69
70
  const matrixDurationMs = performance.now() - matrixStart;
70
71
 
72
+ const projectionMatrixStart = performance.now();
73
+ const projectionLossMatrix = createProjectionTargetLossMatrix({ imports: nativeImportResults });
74
+ const projectionMatrixDurationMs = performance.now() - projectionMatrixStart;
75
+
71
76
  const preservationStart = performance.now();
72
77
  const preservationRecords = nativeImportResults.map((imported) => imported.metadata.sourcePreservation ?? createNativeSourcePreservation({
73
78
  language: imported.language,
@@ -96,7 +101,15 @@ console.log(JSON.stringify({
96
101
  nativeImportDurationMs: Number(importDurationMs.toFixed(2)),
97
102
  coverageMatrixLanguages: coverageMatrix.summary.languages,
98
103
  coverageMatrixImports: coverageMatrix.summary.imports,
104
+ adapterCoverageSummaries: coverageMatrix.summary.adapterCoverage.total,
105
+ adapterCoverageSourceRanges: coverageMatrix.summary.adapterCoverage.effective.sourceRanges ?? 0,
106
+ adapterCoverageTokenGaps: coverageMatrix.summary.adapterCoverage.gaps.tokens ?? 0,
107
+ adapterCoverageReferenceGaps: coverageMatrix.summary.adapterCoverage.gaps.references ?? 0,
99
108
  coverageMatrixDurationMs: Number(matrixDurationMs.toFixed(2)),
109
+ projectionMatrixLanguages: projectionLossMatrix.summary.languages,
110
+ projectionMatrixMissingAdapters: projectionLossMatrix.summary.missingAdapters,
111
+ projectionMatrixUnsupportedTargetFeatures: projectionLossMatrix.summary.unsupportedTargetFeatures,
112
+ projectionMatrixDurationMs: Number(projectionMatrixDurationMs.toFixed(2)),
100
113
  sourcePreservationRecords: preservationRecords.length,
101
114
  sourcePreservationTokens: preservationTokens,
102
115
  sourcePreservationDurationMs: Number(preservationDurationMs.toFixed(2)),
package/dist/index.d.ts CHANGED
@@ -168,22 +168,45 @@ export interface NativeImportLanguageProfile {
168
168
  readonly extensions: readonly string[];
169
169
  readonly supportsLightweightScan: boolean;
170
170
  readonly parserAdapters: readonly string[];
171
- readonly projectionTargets: readonly FrontierCompileTarget[];
171
+ readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
172
172
  readonly knownLossKinds: readonly NativeImportKnownLossKind[];
173
173
  readonly defaultReadiness: SemanticMergeReadiness;
174
174
  readonly notes: readonly string[];
175
175
  }
176
176
 
177
+ export interface NativeImporterAdapterCoverageAggregate {
178
+ readonly total: number;
179
+ readonly declared: Readonly<Record<string, number>>;
180
+ readonly observed: Readonly<Record<string, number>>;
181
+ readonly effective: Readonly<Record<string, number>>;
182
+ readonly gaps: Readonly<Record<string, number>>;
183
+ readonly declaredOnly: Readonly<Record<string, number>>;
184
+ readonly observedOnly: Readonly<Record<string, number>>;
185
+ readonly summaries: readonly {
186
+ readonly adapterId?: string;
187
+ readonly language?: FrontierSourceLanguage | string;
188
+ readonly parser?: string;
189
+ readonly exactness?: NativeImporterAdapterExactness;
190
+ readonly declared: readonly string[];
191
+ readonly observed: readonly string[];
192
+ readonly effective: readonly string[];
193
+ readonly gaps: readonly string[];
194
+ readonly declaredOnly: readonly string[];
195
+ readonly observedOnly: readonly string[];
196
+ }[];
197
+ }
198
+
177
199
  export interface NativeImportCoverageLanguage {
178
200
  readonly language: FrontierSourceLanguage;
179
201
  readonly aliases: readonly string[];
180
202
  readonly extensions: readonly string[];
181
203
  readonly supportsLightweightScan: boolean;
182
204
  readonly parserAdapters: readonly string[];
183
- readonly projectionTargets: readonly FrontierCompileTarget[];
205
+ readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
184
206
  readonly knownLossKinds: readonly NativeImportKnownLossKind[];
185
207
  readonly defaultReadiness: SemanticMergeReadiness;
186
208
  readonly notes: readonly string[];
209
+ readonly adapterCoverage: NativeImporterAdapterCoverageAggregate;
187
210
  readonly imports: {
188
211
  readonly total: number;
189
212
  readonly parsers: readonly string[];
@@ -214,9 +237,11 @@ export interface NativeImportCoverageMatrix {
214
237
  readonly losses: number;
215
238
  readonly byReadiness: Readonly<Record<string, number>>;
216
239
  readonly lossKinds: Readonly<Record<string, number>>;
240
+ readonly adapterCoverage: NativeImporterAdapterCoverageAggregate;
217
241
  };
218
242
  readonly metadata: {
219
243
  readonly compileTargets: readonly FrontierCompileTarget[];
244
+ readonly projectionTargetLossClasses: readonly ProjectionTargetLossClass[];
220
245
  readonly note: string;
221
246
  };
222
247
  }
@@ -228,6 +253,96 @@ export interface NativeImportCoverageMatrixOptions {
228
253
  readonly generatedAt?: number;
229
254
  }
230
255
 
256
+ export type ProjectionTargetLossClass =
257
+ | 'exactSourceProjection'
258
+ | 'nativeSourceStubs'
259
+ | 'unsupportedTargetFeatures'
260
+ | 'missingAdapter'
261
+ | string;
262
+
263
+ export interface ProjectionSourceProjectionCoverage {
264
+ readonly lossClass: ProjectionTargetLossClass;
265
+ readonly mode: NativeSourceProjectionMode;
266
+ readonly supported: boolean;
267
+ readonly readiness: SemanticMergeReadiness;
268
+ readonly lossKinds: readonly NativeImportKnownLossKind[];
269
+ readonly categories: readonly NativeImportTaxonomyKind[];
270
+ readonly reason: string;
271
+ readonly evidence: {
272
+ readonly imports: number;
273
+ readonly importsWithExactSource?: number;
274
+ readonly importsWithDeclarations?: number;
275
+ };
276
+ readonly notes: readonly string[];
277
+ }
278
+
279
+ export interface ProjectionTargetCoverageEntry {
280
+ readonly target: FrontierCompileTarget | string;
281
+ readonly lossClass: ProjectionTargetLossClass;
282
+ readonly supported: boolean;
283
+ readonly readiness: SemanticMergeReadiness;
284
+ readonly lossKinds: readonly NativeImportKnownLossKind[];
285
+ readonly categories: readonly NativeImportTaxonomyKind[];
286
+ readonly reason: string;
287
+ readonly adapter?: string;
288
+ readonly notes: readonly string[];
289
+ }
290
+
291
+ export interface ProjectionTargetLanguageCoverage {
292
+ readonly language: FrontierSourceLanguage | string;
293
+ readonly aliases: readonly string[];
294
+ readonly extensions: readonly string[];
295
+ readonly supportsLightweightScan: boolean;
296
+ readonly parserAdapters: readonly string[];
297
+ readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
298
+ readonly knownLossKinds: readonly NativeImportKnownLossKind[];
299
+ readonly defaultReadiness: SemanticMergeReadiness;
300
+ readonly notes: readonly string[];
301
+ readonly sourceProjection: {
302
+ readonly exactSource: ProjectionSourceProjectionCoverage;
303
+ readonly stubs: ProjectionSourceProjectionCoverage;
304
+ };
305
+ readonly targets: readonly ProjectionTargetCoverageEntry[];
306
+ readonly summary: {
307
+ readonly imports: number;
308
+ readonly parserAdapters: number;
309
+ readonly targetEntries: number;
310
+ readonly byLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
311
+ readonly exactSourceImports: number;
312
+ readonly stubDeclarationImports: number;
313
+ };
314
+ }
315
+
316
+ export interface ProjectionTargetLossMatrix {
317
+ readonly kind: 'frontier.lang.projectionTargetLossMatrix';
318
+ readonly version: 1;
319
+ readonly generatedAt: number;
320
+ readonly languages: readonly ProjectionTargetLanguageCoverage[];
321
+ readonly summary: {
322
+ readonly languages: number;
323
+ readonly targetEntries: number;
324
+ readonly byLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
325
+ readonly sourceProjectionByLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
326
+ readonly exactSourceProjection: number;
327
+ readonly nativeSourceStubs: number;
328
+ readonly unsupportedTargetFeatures: number;
329
+ readonly missingAdapters: number;
330
+ };
331
+ readonly metadata: {
332
+ readonly compileTargets: readonly (FrontierCompileTarget | string)[];
333
+ readonly lossClasses: readonly ProjectionTargetLossClass[];
334
+ readonly note: string;
335
+ };
336
+ }
337
+
338
+ export interface ProjectionTargetLossMatrixOptions {
339
+ readonly languages?: readonly NativeImportLanguageProfile[];
340
+ readonly imports?: readonly NativeSourceImportResult[];
341
+ readonly adapters?: readonly NativeImporterAdapter[];
342
+ readonly targets?: readonly (FrontierCompileTarget | string)[];
343
+ readonly generatedAt?: number;
344
+ }
345
+
231
346
  export interface NativeImportContractSource {
232
347
  readonly id: string;
233
348
  readonly language?: FrontierSourceLanguage | string;
@@ -618,14 +733,97 @@ export interface NativeImporterAdapterSemanticCoverage {
618
733
  readonly controlFlow: boolean;
619
734
  }
620
735
 
736
+ export interface NativeImporterAdapterCoverageSnapshot {
737
+ readonly exactness: NativeImporterAdapterExactness;
738
+ readonly exactAst: boolean;
739
+ readonly tokens: boolean;
740
+ readonly trivia: boolean;
741
+ readonly diagnostics: boolean;
742
+ readonly sourceRanges: boolean;
743
+ readonly generatedRanges: boolean;
744
+ readonly semanticCoverage: NativeImporterAdapterSemanticCoverage;
745
+ }
746
+
621
747
  export interface NativeImporterAdapterCoverageObserved {
748
+ readonly exactness?: NativeImporterAdapterExactness;
749
+ readonly exactAst?: boolean;
750
+ readonly tokens?: boolean;
751
+ readonly tokenCount?: number;
752
+ readonly trivia?: boolean;
753
+ readonly triviaCount?: number;
622
754
  readonly diagnostics: number;
755
+ readonly parserDiagnostics?: number;
756
+ readonly diagnosticErrors?: number;
757
+ readonly diagnosticWarnings?: number;
758
+ readonly diagnosticInfos?: number;
623
759
  readonly losses: number;
624
760
  readonly nativeAstNodes: number;
625
761
  readonly semanticSymbols: number;
762
+ readonly semanticReferences?: number;
763
+ readonly semanticTypes?: number;
764
+ readonly semanticControlFlow?: number;
765
+ readonly references?: boolean;
766
+ readonly types?: boolean;
767
+ readonly controlFlow?: boolean;
626
768
  readonly sourceMapMappings: number;
627
769
  readonly sourceRanges: boolean;
770
+ readonly sourceRangeNodes?: number;
771
+ readonly sourceRangeMappings?: number;
628
772
  readonly generatedRanges: boolean;
773
+ readonly generatedRangeMappings?: number;
774
+ readonly semanticCoverage?: NativeImporterAdapterSemanticCoverage;
775
+ }
776
+
777
+ export interface NativeImporterAdapterCoverageCapabilityRow {
778
+ readonly capability: string;
779
+ readonly declared: boolean;
780
+ readonly observed: boolean;
781
+ readonly effective: boolean;
782
+ readonly count: number;
783
+ readonly status: 'declared-and-observed' | 'declared-unobserved' | 'observed-undeclared' | 'absent';
784
+ }
785
+
786
+ export interface NativeImporterAdapterCoverageCapabilityEvidence {
787
+ readonly declared: NativeImporterAdapterCoverageSnapshot;
788
+ readonly observed: NativeImporterAdapterCoverageObserved;
789
+ readonly effective: NativeImporterAdapterCoverageSnapshot;
790
+ readonly capabilities: readonly NativeImporterAdapterCoverageCapabilityRow[];
791
+ readonly gaps: readonly string[];
792
+ readonly declaredOnly: readonly string[];
793
+ readonly observedOnly: readonly string[];
794
+ readonly parserDiagnostics: {
795
+ readonly declared: boolean;
796
+ readonly observed: boolean;
797
+ readonly count: number;
798
+ readonly errors: number;
799
+ readonly warnings: number;
800
+ readonly infos: number;
801
+ };
802
+ readonly sourceRanges: {
803
+ readonly declared: boolean;
804
+ readonly observed: boolean;
805
+ readonly nativeAstNodes: number;
806
+ readonly sourceRangeNodes: number;
807
+ readonly sourceMapMappings: number;
808
+ readonly sourceRangeMappings: number;
809
+ readonly generatedRangeMappings: number;
810
+ };
811
+ readonly tokensTrivia: {
812
+ readonly tokens: { readonly declared: boolean; readonly observed: boolean; readonly count: number };
813
+ readonly trivia: { readonly declared: boolean; readonly observed: boolean; readonly count: number };
814
+ };
815
+ readonly semantic: {
816
+ readonly level: {
817
+ readonly declared: string;
818
+ readonly observed: string;
819
+ readonly effective: string;
820
+ };
821
+ readonly declarations: NativeImporterAdapterCoverageCapabilityRow;
822
+ readonly symbols: NativeImporterAdapterCoverageCapabilityRow;
823
+ readonly references: NativeImporterAdapterCoverageCapabilityRow;
824
+ readonly types: NativeImporterAdapterCoverageCapabilityRow;
825
+ readonly controlFlow: NativeImporterAdapterCoverageCapabilityRow;
826
+ };
629
827
  }
630
828
 
631
829
  export interface NativeImporterAdapterCoverageSummary {
@@ -638,11 +836,13 @@ export interface NativeImporterAdapterCoverageSummary {
638
836
  readonly generatedRanges: boolean;
639
837
  readonly semanticCoverage: NativeImporterAdapterSemanticCoverage;
640
838
  readonly notes: readonly string[];
839
+ readonly declared?: NativeImporterAdapterCoverageSnapshot;
641
840
  readonly observed?: NativeImporterAdapterCoverageObserved;
841
+ readonly capabilityEvidence?: NativeImporterAdapterCoverageCapabilityEvidence;
642
842
  }
643
843
 
644
844
  export type NativeImporterAdapterCoverageInput =
645
- Omit<Partial<NativeImporterAdapterCoverageSummary>, 'semanticCoverage' | 'observed'> & {
845
+ Omit<Partial<NativeImporterAdapterCoverageSummary>, 'semanticCoverage' | 'observed' | 'declared' | 'capabilityEvidence'> & {
646
846
  readonly semanticCoverage?: Partial<NativeImporterAdapterSemanticCoverage>;
647
847
  readonly observed?: Partial<NativeImporterAdapterCoverageObserved>;
648
848
  };
@@ -972,6 +1172,7 @@ export declare const NativeImportRoundtripReadinessStatuses: readonly NativeImpo
972
1172
  export declare const NativeImportTaxonomyKinds: readonly NativeImportTaxonomyKind[];
973
1173
  export declare const NativeImportLossKinds: readonly NativeImportKnownLossKind[];
974
1174
  export declare const NativeImportRegionTaxonomyKinds: readonly NativeImportRegionTaxonomyKind[];
1175
+ export declare const ProjectionTargetLossClasses: readonly ProjectionTargetLossClass[];
975
1176
  export declare const NativeImportReadinessBySeverity: Readonly<Record<NativeImportLossSummary['highestSeverity'], SemanticMergeReadiness>>;
976
1177
  export declare const NativeImportLanguageProfiles: readonly NativeImportLanguageProfile[];
977
1178
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
@@ -984,6 +1185,7 @@ export declare function summarizeNativeImportLosses(losses?: readonly NativeAstL
984
1185
  export declare function classifyNativeImportReadiness(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportReadinessClassification;
985
1186
  export declare function classifyNativeImportRoundtripReadiness(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportRoundtripReadinessOptions): NativeImportRoundtripReadinessClassification;
986
1187
  export declare function createNativeImportCoverageMatrix(options?: NativeImportCoverageMatrixOptions): NativeImportCoverageMatrix;
1188
+ export declare function createProjectionTargetLossMatrix(options?: ProjectionTargetLossMatrixOptions): ProjectionTargetLossMatrix;
987
1189
  export declare function createNativeSourcePreservation(options: CreateNativeSourcePreservationOptions): NativeSourcePreservation;
988
1190
  export declare function createSemanticImportSidecar(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: SemanticImportSidecarOptions): SemanticImportSidecar;
989
1191
  export declare function createNativeImportResultContract(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportResultContractOptions): NativeImportResultContract;
package/dist/index.js CHANGED
@@ -138,6 +138,13 @@ export const NativeImportRegionTaxonomyKinds = Object.freeze([
138
138
  'generatedOutput'
139
139
  ]);
140
140
 
141
+ export const ProjectionTargetLossClasses = Object.freeze([
142
+ 'exactSourceProjection',
143
+ 'nativeSourceStubs',
144
+ 'unsupportedTargetFeatures',
145
+ 'missingAdapter'
146
+ ]);
147
+
141
148
  export const NativeImportLanguageProfiles = Object.freeze([
142
149
  nativeImportLanguageProfile('javascript', {
143
150
  aliases: ['js', 'mjs', 'cjs', 'jsx'],
@@ -493,6 +500,7 @@ export function createNativeImportCoverageMatrix(input = {}) {
493
500
  for (const [kind, count] of Object.entries(entry.imports.lossKinds)) {
494
501
  totals.lossKinds[kind] = (totals.lossKinds[kind] ?? 0) + count;
495
502
  }
503
+ totals.adapterCoverage = mergeNativeImporterAdapterCoverageAggregates(totals.adapterCoverage, entry.adapterCoverage);
496
504
  return totals;
497
505
  }, {
498
506
  languages: 0,
@@ -504,7 +512,8 @@ export function createNativeImportCoverageMatrix(input = {}) {
504
512
  sourceMapMappings: 0,
505
513
  losses: 0,
506
514
  byReadiness: {},
507
- lossKinds: {}
515
+ lossKinds: {},
516
+ adapterCoverage: emptyNativeImporterAdapterCoverageAggregate()
508
517
  });
509
518
  return {
510
519
  kind: 'frontier.lang.nativeImportCoverageMatrix',
@@ -514,11 +523,37 @@ export function createNativeImportCoverageMatrix(input = {}) {
514
523
  summary,
515
524
  metadata: {
516
525
  compileTargets: [...FrontierCompileTargets],
526
+ projectionTargetLossClasses: [...ProjectionTargetLossClasses],
517
527
  note: 'Coverage is evidence and capability metadata, not a claim that every language feature is losslessly portable.'
518
528
  }
519
529
  };
520
530
  }
521
531
 
532
+ export function createProjectionTargetLossMatrix(input = {}) {
533
+ const imports = input.imports ?? [];
534
+ const adapters = input.adapters ?? [];
535
+ const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
536
+ const targets = normalizeProjectionMatrixTargets(input.targets ?? FrontierCompileTargets);
537
+ const languages = profiles.map((profile) => projectionTargetCoverageForProfile(profile, {
538
+ imports,
539
+ adapters,
540
+ targets
541
+ }));
542
+ const summary = projectionTargetLossMatrixSummary(languages);
543
+ return {
544
+ kind: 'frontier.lang.projectionTargetLossMatrix',
545
+ version: 1,
546
+ generatedAt: input.generatedAt ?? Date.now(),
547
+ languages,
548
+ summary,
549
+ metadata: {
550
+ compileTargets: targets,
551
+ lossClasses: [...ProjectionTargetLossClasses],
552
+ note: 'Projection target coverage separates exact source preservation, declaration stubs, known unsupported target features, and missing native-to-target adapters.'
553
+ }
554
+ };
555
+ }
556
+
522
557
  export function createNativeSourcePreservation(options) {
523
558
  if (!options || typeof options.sourceText !== 'string') {
524
559
  throw new Error('createNativeSourcePreservation requires sourceText');
@@ -3369,15 +3404,273 @@ function nativeImportCategoryForLossKind(kind) {
3369
3404
  return String(kind ?? 'opaqueNative');
3370
3405
  }
3371
3406
 
3407
+ function normalizeProjectionMatrixTargets(targets) {
3408
+ return uniqueStrings((Array.isArray(targets) ? targets : [targets])
3409
+ .map((target) => {
3410
+ if (target === undefined || target === null) return undefined;
3411
+ try {
3412
+ return normalizeCompileTarget(target);
3413
+ } catch {
3414
+ return String(target).trim().toLowerCase();
3415
+ }
3416
+ })
3417
+ .filter(Boolean));
3418
+ }
3419
+
3420
+ function projectionTargetCoverageForProfile(profile, context) {
3421
+ const aliases = new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
3422
+ const matchingImports = (context.imports ?? []).filter((imported) => aliases.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language)));
3423
+ const matchingAdapters = (context.adapters ?? []).filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.language)));
3424
+ const importedLossKinds = uniqueStrings(matchingImports.flatMap((imported) => (imported?.losses ?? []).map((loss) => loss.kind).filter(Boolean)));
3425
+ const knownLossKinds = uniqueStrings([...(profile.knownLossKinds ?? []), ...importedLossKinds]);
3426
+ const parserAdapters = uniqueStrings([
3427
+ ...(profile.parserAdapters ?? []),
3428
+ ...matchingAdapters.map((adapter) => adapter.parser ?? adapter.id).filter(Boolean)
3429
+ ]);
3430
+ const sourceProjection = sourceProjectionCoverageForProfile(profile, matchingImports, knownLossKinds);
3431
+ const targets = (context.targets ?? FrontierCompileTargets).map((target) => projectionTargetCoverageEntry(profile, target, {
3432
+ matchingImports,
3433
+ matchingAdapters,
3434
+ knownLossKinds
3435
+ }));
3436
+ return {
3437
+ language: profile.language,
3438
+ aliases: profile.aliases,
3439
+ extensions: profile.extensions,
3440
+ supportsLightweightScan: profile.supportsLightweightScan,
3441
+ parserAdapters,
3442
+ projectionTargets: profile.projectionTargets,
3443
+ knownLossKinds,
3444
+ defaultReadiness: profile.defaultReadiness,
3445
+ notes: profile.notes,
3446
+ sourceProjection,
3447
+ targets,
3448
+ summary: {
3449
+ imports: matchingImports.length,
3450
+ parserAdapters: parserAdapters.length,
3451
+ targetEntries: targets.length,
3452
+ byLossClass: countProjectionLossClasses(targets),
3453
+ exactSourceImports: sourceProjection.exactSource.evidence.importsWithExactSource,
3454
+ stubDeclarationImports: sourceProjection.stubs.evidence.importsWithDeclarations
3455
+ }
3456
+ };
3457
+ }
3458
+
3459
+ function sourceProjectionCoverageForProfile(profile, imports, knownLossKinds) {
3460
+ const exactSourceImports = imports.filter(hasExactSourceProjectionEvidence).length;
3461
+ const declarationImports = imports.filter(hasNativeProjectionDeclarations).length;
3462
+ return {
3463
+ exactSource: {
3464
+ lossClass: 'exactSourceProjection',
3465
+ mode: 'preserved-source',
3466
+ supported: true,
3467
+ readiness: 'ready',
3468
+ lossKinds: [],
3469
+ categories: [],
3470
+ reason: exactSourceImports
3471
+ ? 'At least one import carries matching source-preservation evidence, so projectNativeImportToSource can emit the original source exactly.'
3472
+ : 'Exact source projection is available when the import carries sourceText or source-preservation evidence whose hash matches the native source hash.',
3473
+ evidence: {
3474
+ imports: imports.length,
3475
+ importsWithExactSource: exactSourceImports
3476
+ },
3477
+ notes: ['Preserved source is the only currently lossless native-source projection mode in this facade.']
3478
+ },
3479
+ stubs: {
3480
+ lossClass: 'nativeSourceStubs',
3481
+ mode: 'native-source-stubs',
3482
+ supported: profile.supportsLightweightScan || declarationImports > 0,
3483
+ readiness: 'needs-review',
3484
+ lossKinds: uniqueStrings([
3485
+ 'targetProjectionLoss',
3486
+ ...(declarationImports || profile.supportsLightweightScan ? [] : ['declarationOnlyCoverage'])
3487
+ ]),
3488
+ categories: uniqueStrings([
3489
+ 'targetProjectionLoss',
3490
+ ...(declarationImports || profile.supportsLightweightScan ? [] : ['declarationsOnly'])
3491
+ ]),
3492
+ reason: 'Declaration stubs are emitted when exact source is unavailable or disabled; executable bodies and full type semantics remain unavailable.',
3493
+ evidence: {
3494
+ imports: imports.length,
3495
+ importsWithDeclarations: declarationImports
3496
+ },
3497
+ notes: uniqueStrings([
3498
+ 'Stub projection is review-required and should not be treated as a round-trip proof.',
3499
+ ...(projectionUnsupportedFeatureLossKinds(knownLossKinds).length
3500
+ ? ['Known source-language feature losses may still be present behind preserved source or stubs.']
3501
+ : [])
3502
+ ])
3503
+ }
3504
+ };
3505
+ }
3506
+
3507
+ function projectionTargetCoverageEntry(profile, target, context) {
3508
+ const normalizedTarget = normalizeProjectionMatrixTargets([target])[0] ?? String(target);
3509
+ const declaredTargets = new Set(normalizeProjectionMatrixTargets(profile.projectionTargets ?? []));
3510
+ const adapterTargets = new Set((context.matchingAdapters ?? []).flatMap(adapterProjectionTargets));
3511
+ const sameSourceTarget = nativeLanguageCompileTarget(profile.language, profile.aliases) === normalizedTarget;
3512
+ const hasProjectionAdapter = declaredTargets.has(normalizedTarget) || adapterTargets.has(normalizedTarget);
3513
+ if (!hasProjectionAdapter) {
3514
+ return {
3515
+ target: normalizedTarget,
3516
+ lossClass: 'missingAdapter',
3517
+ supported: false,
3518
+ readiness: 'blocked',
3519
+ lossKinds: ['targetProjectionLoss'],
3520
+ categories: ['targetProjectionLoss'],
3521
+ reason: `No native-to-${normalizedTarget} projection adapter is declared for ${profile.language}.`,
3522
+ adapter: undefined,
3523
+ notes: ['The source can still be preserved or stubbed in its original language when import evidence supports that mode.']
3524
+ };
3525
+ }
3526
+
3527
+ const featureLossKinds = projectionUnsupportedFeatureLossKinds(context.knownLossKinds);
3528
+ if (featureLossKinds.length) {
3529
+ return {
3530
+ target: normalizedTarget,
3531
+ lossClass: 'unsupportedTargetFeatures',
3532
+ supported: true,
3533
+ readiness: 'needs-review',
3534
+ lossKinds: featureLossKinds,
3535
+ categories: uniqueStrings(featureLossKinds.map(nativeImportCategoryForLossKind)),
3536
+ reason: `${profile.language} coverage declares source features that this facade cannot prove lossless for ${normalizedTarget}: ${featureLossKinds.join(', ')}.`,
3537
+ adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
3538
+ notes: ['Use exact parser or semantic adapter evidence before treating this target projection as merge-ready.']
3539
+ };
3540
+ }
3541
+
3542
+ if (sameSourceTarget) {
3543
+ return {
3544
+ target: normalizedTarget,
3545
+ lossClass: 'exactSourceProjection',
3546
+ supported: true,
3547
+ readiness: 'ready',
3548
+ lossKinds: [],
3549
+ categories: [],
3550
+ reason: `${profile.language} can project to its source language exactly when source preservation evidence is available.`,
3551
+ adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
3552
+ notes: ['Without exact source text, the source projection falls back to declaration stubs.']
3553
+ };
3554
+ }
3555
+
3556
+ return {
3557
+ target: normalizedTarget,
3558
+ lossClass: 'nativeSourceStubs',
3559
+ supported: true,
3560
+ readiness: 'needs-review',
3561
+ lossKinds: ['targetProjectionLoss'],
3562
+ categories: ['targetProjectionLoss'],
3563
+ reason: `${profile.language} declares a ${normalizedTarget} target slot, but this facade only exposes declaration-level native import projection evidence.`,
3564
+ adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
3565
+ notes: ['Host-owned semantic adapters can upgrade this cell with stronger evidence.']
3566
+ };
3567
+ }
3568
+
3569
+ function projectionTargetLossMatrixSummary(languages) {
3570
+ const byLossClass = {};
3571
+ const sourceProjectionByLossClass = {};
3572
+ let targetEntries = 0;
3573
+ for (const language of languages) {
3574
+ for (const projection of [language.sourceProjection?.exactSource, language.sourceProjection?.stubs].filter(Boolean)) {
3575
+ sourceProjectionByLossClass[projection.lossClass] = (sourceProjectionByLossClass[projection.lossClass] ?? 0) + 1;
3576
+ }
3577
+ for (const target of language.targets ?? []) {
3578
+ targetEntries += 1;
3579
+ byLossClass[target.lossClass] = (byLossClass[target.lossClass] ?? 0) + 1;
3580
+ }
3581
+ }
3582
+ return {
3583
+ languages: languages.length,
3584
+ targetEntries,
3585
+ byLossClass,
3586
+ sourceProjectionByLossClass,
3587
+ exactSourceProjection: (sourceProjectionByLossClass.exactSourceProjection ?? 0) + (byLossClass.exactSourceProjection ?? 0),
3588
+ nativeSourceStubs: (sourceProjectionByLossClass.nativeSourceStubs ?? 0) + (byLossClass.nativeSourceStubs ?? 0),
3589
+ unsupportedTargetFeatures: byLossClass.unsupportedTargetFeatures ?? 0,
3590
+ missingAdapters: byLossClass.missingAdapter ?? 0
3591
+ };
3592
+ }
3593
+
3594
+ function countProjectionLossClasses(entries) {
3595
+ const counts = {};
3596
+ for (const entry of entries ?? []) {
3597
+ counts[entry.lossClass] = (counts[entry.lossClass] ?? 0) + 1;
3598
+ }
3599
+ return counts;
3600
+ }
3601
+
3602
+ function hasExactSourceProjectionEvidence(imported) {
3603
+ const preservation = imported?.metadata?.sourcePreservation
3604
+ ?? imported?.nativeSource?.metadata?.sourcePreservation
3605
+ ?? imported?.nativeAst?.metadata?.sourcePreservation;
3606
+ const expectedHash = imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? imported?.sourceHash;
3607
+ return Boolean(preservation?.summary?.exactSourceAvailable && (!expectedHash || preservation.sourceHash === expectedHash));
3608
+ }
3609
+
3610
+ function hasNativeProjectionDeclarations(imported) {
3611
+ const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
3612
+ return (semanticIndex?.symbols?.length ?? 0) > 0;
3613
+ }
3614
+
3615
+ function projectionUnsupportedFeatureLossKinds(lossKinds) {
3616
+ const unsupported = new Set([
3617
+ 'macroExpansion',
3618
+ 'macroHygiene',
3619
+ 'preprocessor',
3620
+ 'conditionalCompilation',
3621
+ 'metaprogramming',
3622
+ 'reflection',
3623
+ 'dynamicRuntime',
3624
+ 'dynamicDispatch',
3625
+ 'generatedCode',
3626
+ 'overloadResolution',
3627
+ 'typeInference',
3628
+ 'unsupportedSyntax',
3629
+ 'unsupportedSemantic'
3630
+ ]);
3631
+ return uniqueStrings((lossKinds ?? []).filter((kind) => unsupported.has(kind)));
3632
+ }
3633
+
3634
+ function adapterProjectionTargets(adapter) {
3635
+ return normalizeProjectionMatrixTargets(
3636
+ adapter?.projectionTargets
3637
+ ?? adapter?.coverage?.projectionTargets
3638
+ ?? adapter?.metadata?.projectionTargets
3639
+ ?? []
3640
+ );
3641
+ }
3642
+
3643
+ function projectionTargetAdapterName(profile, target, adapters = []) {
3644
+ const adapter = adapters.find((candidate) => adapterProjectionTargets(candidate).includes(target));
3645
+ if (adapter) return adapter.id ?? adapter.parser;
3646
+ return profile.projectionTargets?.includes(target) ? `frontier-native-source-${target}` : undefined;
3647
+ }
3648
+
3649
+ function nativeLanguageCompileTarget(language, aliases = []) {
3650
+ const ids = [language, ...aliases].map(normalizeNativeLanguageId);
3651
+ if (ids.includes('typescript')) return 'typescript';
3652
+ if (ids.includes('javascript')) return 'javascript';
3653
+ if (ids.includes('rust')) return 'rust';
3654
+ if (ids.includes('python')) return 'python';
3655
+ if (ids.includes('c')) return 'c';
3656
+ return undefined;
3657
+ }
3658
+
3659
+ function nativeProjectionTargetsForLanguage(language, aliases = []) {
3660
+ const target = nativeLanguageCompileTarget(language, aliases);
3661
+ return target ? [target] : [];
3662
+ }
3663
+
3372
3664
  function nativeImportLanguageProfile(language, input = {}) {
3373
3665
  const lossKinds = input.lossKinds ?? ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation'];
3666
+ const aliases = uniqueStrings(input.aliases ?? []);
3374
3667
  return Object.freeze({
3375
3668
  language,
3376
- aliases: Object.freeze(uniqueStrings(input.aliases ?? [])),
3669
+ aliases: Object.freeze(aliases),
3377
3670
  extensions: Object.freeze(uniqueStrings(input.extensions ?? [])),
3378
3671
  supportsLightweightScan: input.supportsLightweightScan !== false,
3379
3672
  parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? ['tree-sitter'])),
3380
- projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? FrontierCompileTargets)),
3673
+ projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? nativeProjectionTargetsForLanguage(language, aliases))),
3381
3674
  knownLossKinds: Object.freeze(uniqueStrings(lossKinds)),
3382
3675
  defaultReadiness: input.defaultReadiness ?? 'needs-review',
3383
3676
  notes: Object.freeze(uniqueStrings(input.notes ?? ['lightweight scanner records declarations only; exact parser adapters must be injected by the host']))
@@ -3421,7 +3714,7 @@ function normalizeNativeImportLanguageProfile(profile, fallbackLanguage) {
3421
3714
  extensions: uniqueStrings(profile.extensions ?? []),
3422
3715
  supportsLightweightScan: profile.supportsLightweightScan !== false,
3423
3716
  parserAdapters: uniqueStrings(profile.parserAdapters ?? []),
3424
- projectionTargets: uniqueStrings(profile.projectionTargets ?? FrontierCompileTargets),
3717
+ projectionTargets: uniqueStrings(profile.projectionTargets ?? nativeProjectionTargetsForLanguage(language, profile.aliases ?? [])),
3425
3718
  knownLossKinds: uniqueStrings(profile.knownLossKinds ?? profile.lossKinds ?? []),
3426
3719
  defaultReadiness: profile.defaultReadiness ?? 'needs-review',
3427
3720
  notes: uniqueStrings(profile.notes ?? [])
@@ -3440,6 +3733,10 @@ function nativeImportCoverageForProfile(profile, imports, adapters) {
3440
3733
  : profile.supportsLightweightScan ? profile.defaultReadiness : 'blocked';
3441
3734
  const importedParsers = uniqueStrings(matchingImports.map((imported) => imported?.nativeAst?.parser ?? imported?.parser ?? imported?.metadata?.parser).filter(Boolean));
3442
3735
  const sourceMaps = matchingImports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
3736
+ const adapterCoverage = summarizeNativeImporterAdapterCoverageEntries([
3737
+ ...matchingImports.map((imported) => nativeImporterAdapterCoverageEntryFromImport(imported)).filter(Boolean),
3738
+ ...matchingAdapters.map((adapter) => nativeImporterAdapterCoverageEntryFromAdapter(adapter)).filter(Boolean)
3739
+ ]);
3443
3740
  return {
3444
3741
  language: profile.language,
3445
3742
  aliases: profile.aliases,
@@ -3450,6 +3747,7 @@ function nativeImportCoverageForProfile(profile, imports, adapters) {
3450
3747
  knownLossKinds: uniqueStrings([...(profile.knownLossKinds ?? []), ...Object.keys(lossSummary.byKind)]),
3451
3748
  defaultReadiness: profile.defaultReadiness,
3452
3749
  notes: profile.notes,
3750
+ adapterCoverage,
3453
3751
  imports: {
3454
3752
  total: matchingImports.length,
3455
3753
  parsers: importedParsers,
@@ -3465,6 +3763,125 @@ function nativeImportCoverageForProfile(profile, imports, adapters) {
3465
3763
  };
3466
3764
  }
3467
3765
 
3766
+ function nativeImporterAdapterCoverageEntryFromImport(imported) {
3767
+ const coverage = imported?.adapter?.coverage
3768
+ ?? imported?.metadata?.adapterCoverage
3769
+ ?? imported?.nativeAst?.metadata?.adapterCoverage
3770
+ ?? imported?.nativeSource?.metadata?.adapterCoverage;
3771
+ if (!coverage) return undefined;
3772
+ return {
3773
+ adapterId: imported?.adapter?.id ?? imported?.metadata?.adapterId ?? imported?.nativeAst?.metadata?.adapterId,
3774
+ language: imported?.adapter?.language ?? imported?.language ?? imported?.nativeAst?.language,
3775
+ parser: imported?.adapter?.parser ?? imported?.nativeAst?.parser ?? imported?.nativeSource?.parser,
3776
+ coverage
3777
+ };
3778
+ }
3779
+
3780
+ function nativeImporterAdapterCoverageEntryFromAdapter(adapter) {
3781
+ if (!adapter) return undefined;
3782
+ const summary = normalizeNativeImporterAdapter(adapter);
3783
+ return {
3784
+ adapterId: summary.id,
3785
+ language: summary.language,
3786
+ parser: summary.parser,
3787
+ coverage: summary.coverage
3788
+ };
3789
+ }
3790
+
3791
+ function summarizeNativeImporterAdapterCoverageEntries(entries = []) {
3792
+ const aggregate = emptyNativeImporterAdapterCoverageAggregate();
3793
+ for (const entry of entries) {
3794
+ const coverage = normalizeNativeImporterAdapterCoverageForEvidence(entry.coverage);
3795
+ const capabilityEvidence = coverage.capabilityEvidence;
3796
+ const summary = {
3797
+ adapterId: entry.adapterId,
3798
+ language: entry.language,
3799
+ parser: entry.parser,
3800
+ exactness: coverage.exactness,
3801
+ declared: capabilityNamesByBoolean(capabilityEvidence.capabilities, 'declared'),
3802
+ observed: capabilityNamesByBoolean(capabilityEvidence.capabilities, 'observed'),
3803
+ effective: capabilityNamesByBoolean(capabilityEvidence.capabilities, 'effective'),
3804
+ gaps: capabilityEvidence.gaps,
3805
+ declaredOnly: capabilityEvidence.declaredOnly,
3806
+ observedOnly: capabilityEvidence.observedOnly
3807
+ };
3808
+ aggregate.total += 1;
3809
+ aggregate.summaries.push(summary);
3810
+ incrementCoverageCapabilityCounts(aggregate.declared, summary.declared);
3811
+ incrementCoverageCapabilityCounts(aggregate.observed, summary.observed);
3812
+ incrementCoverageCapabilityCounts(aggregate.effective, summary.effective);
3813
+ incrementCoverageCapabilityCounts(aggregate.gaps, summary.gaps);
3814
+ incrementCoverageCapabilityCounts(aggregate.declaredOnly, summary.declaredOnly);
3815
+ incrementCoverageCapabilityCounts(aggregate.observedOnly, summary.observedOnly);
3816
+ }
3817
+ return {
3818
+ ...aggregate,
3819
+ summaries: Object.freeze(aggregate.summaries)
3820
+ };
3821
+ }
3822
+
3823
+ function normalizeNativeImporterAdapterCoverageForEvidence(coverage = {}) {
3824
+ if (coverage.capabilityEvidence) return coverage;
3825
+ const declared = coverage.declared ?? adapterCoverageSnapshotFromSummary(coverage);
3826
+ const observed = normalizeNativeImporterAdapterObservedCoverage(coverage.observed, declared);
3827
+ const effective = effectiveNativeImporterAdapterCoverage(declared, observed);
3828
+ return {
3829
+ ...coverage,
3830
+ ...effective,
3831
+ declared,
3832
+ observed,
3833
+ capabilityEvidence: nativeImporterAdapterCapabilityEvidence(declared, observed, effective)
3834
+ };
3835
+ }
3836
+
3837
+ function emptyNativeImporterAdapterCoverageAggregate() {
3838
+ return {
3839
+ total: 0,
3840
+ declared: {},
3841
+ observed: {},
3842
+ effective: {},
3843
+ gaps: {},
3844
+ declaredOnly: {},
3845
+ observedOnly: {},
3846
+ summaries: []
3847
+ };
3848
+ }
3849
+
3850
+ function mergeNativeImporterAdapterCoverageAggregates(left, right) {
3851
+ const merged = emptyNativeImporterAdapterCoverageAggregate();
3852
+ for (const aggregate of [left, right]) {
3853
+ if (!aggregate) continue;
3854
+ merged.total += aggregate.total ?? 0;
3855
+ incrementCoverageCapabilityCounts(merged.declared, aggregate.declared ?? {});
3856
+ incrementCoverageCapabilityCounts(merged.observed, aggregate.observed ?? {});
3857
+ incrementCoverageCapabilityCounts(merged.effective, aggregate.effective ?? {});
3858
+ incrementCoverageCapabilityCounts(merged.gaps, aggregate.gaps ?? {});
3859
+ incrementCoverageCapabilityCounts(merged.declaredOnly, aggregate.declaredOnly ?? {});
3860
+ incrementCoverageCapabilityCounts(merged.observedOnly, aggregate.observedOnly ?? {});
3861
+ merged.summaries.push(...(aggregate.summaries ?? []));
3862
+ }
3863
+ return {
3864
+ ...merged,
3865
+ summaries: Object.freeze(merged.summaries)
3866
+ };
3867
+ }
3868
+
3869
+ function capabilityNamesByBoolean(rows, property) {
3870
+ return rows.filter((row) => row[property]).map((row) => row.capability);
3871
+ }
3872
+
3873
+ function incrementCoverageCapabilityCounts(target, capabilities) {
3874
+ if (Array.isArray(capabilities)) {
3875
+ for (const capability of capabilities) {
3876
+ target[capability] = (target[capability] ?? 0) + 1;
3877
+ }
3878
+ return;
3879
+ }
3880
+ for (const [capability, count] of Object.entries(capabilities ?? {})) {
3881
+ target[capability] = (target[capability] ?? 0) + count;
3882
+ }
3883
+ }
3884
+
3468
3885
  function semanticImportSidecarEntry(imported, index, options) {
3469
3886
  const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
3470
3887
  const nativeAst = imported?.nativeAst ?? imported?.nativeSource?.ast;
@@ -4828,7 +5245,7 @@ function normalizeNativeImporterAdapterCoverage(value = {}, context = {}) {
4828
5245
  const sourceRanges = Boolean(value.sourceRanges ?? hasCapability('sourceRanges', 'sourceRange', 'ranges', 'sourceMaps'));
4829
5246
  const generatedRanges = Boolean(value.generatedRanges ?? hasCapability('generatedRanges', 'generatedRange', 'generatedSourceMaps'));
4830
5247
  const diagnostics = Boolean(value.diagnostics ?? hasCapability('diagnostics', 'parserDiagnostics'));
4831
- return Object.freeze({
5248
+ const declared = freezeNativeImporterAdapterCoverageSnapshot({
4832
5249
  exactness: String(value.exactness ?? inferredAdapterExactness(exactAst, capabilities)),
4833
5250
  exactAst,
4834
5251
  tokens: Boolean(value.tokens ?? hasCapability('tokens', 'tokenStream')),
@@ -4840,13 +5257,21 @@ function normalizeNativeImporterAdapterCoverage(value = {}, context = {}) {
4840
5257
  capabilities,
4841
5258
  sourceRanges,
4842
5259
  generatedRanges
4843
- }),
5260
+ })
5261
+ });
5262
+ const observed = normalizeNativeImporterAdapterObservedCoverage(value.observed, declared);
5263
+ const effective = effectiveNativeImporterAdapterCoverage(declared, observed);
5264
+ return Object.freeze({
5265
+ ...effective,
5266
+ declared,
5267
+ observed,
4844
5268
  notes: uniqueStrings(value.notes ?? inferredAdapterCoverageNotes(context, {
4845
5269
  exactAst,
4846
5270
  sourceRanges,
4847
5271
  generatedRanges,
4848
5272
  diagnostics
4849
- }))
5273
+ })),
5274
+ capabilityEvidence: nativeImporterAdapterCapabilityEvidence(declared, observed, effective)
4850
5275
  });
4851
5276
  }
4852
5277
 
@@ -4856,31 +5281,297 @@ function observeNativeImporterAdapterCoverage(coverage, parseResult = {}, contex
4856
5281
  const sourceMapMappings = parseResult.sourceMaps?.flatMap((sourceMap) => sourceMap.mappings ?? []) ?? parseResult.mappings ?? [];
4857
5282
  const semanticIndex = parseResult.semanticIndex;
4858
5283
  const semanticSymbols = semanticIndex?.symbols?.length ?? 0;
4859
- const observedSourceRanges = nodeList.some((node) => Boolean(node?.span)) || sourceMapMappings.some((mapping) => Boolean(mapping?.sourceSpan));
4860
- const observedGeneratedRanges = sourceMapMappings.some((mapping) => Boolean(mapping?.generatedSpan));
4861
- const observedSemanticCoverage = normalizeNativeImporterSemanticCoverage({
4862
- ...coverage.semanticCoverage,
4863
- level: semanticSymbols
4864
- ? maxSemanticCoverageLevel(coverage.semanticCoverage?.level, 'declaration-index')
4865
- : coverage.semanticCoverage?.level,
4866
- declarations: coverage.semanticCoverage?.declarations || semanticSymbols > 0,
4867
- symbols: coverage.semanticCoverage?.symbols || semanticSymbols > 0
4868
- }, {});
5284
+ const declared = coverage.declared ?? adapterCoverageSnapshotFromSummary(coverage);
5285
+ const observed = observeNativeImporterAdapterCoverageDetails(parseResult, context, {
5286
+ declared,
5287
+ nodeList,
5288
+ sourceMapMappings,
5289
+ semanticIndex,
5290
+ semanticSymbols
5291
+ });
5292
+ const effective = effectiveNativeImporterAdapterCoverage(declared, observed);
4869
5293
  return Object.freeze({
4870
5294
  ...coverage,
4871
- diagnostics: coverage.diagnostics || (context.diagnostics?.length ?? 0) > 0,
4872
- sourceRanges: coverage.sourceRanges || observedSourceRanges,
4873
- generatedRanges: coverage.generatedRanges || observedGeneratedRanges,
4874
- semanticCoverage: observedSemanticCoverage,
4875
- observed: {
4876
- diagnostics: context.diagnostics?.length ?? 0,
4877
- losses: context.losses?.length ?? 0,
4878
- nativeAstNodes: nodeList.length,
4879
- semanticSymbols,
4880
- sourceMapMappings: sourceMapMappings.length,
4881
- sourceRanges: observedSourceRanges,
4882
- generatedRanges: observedGeneratedRanges
4883
- }
5295
+ ...effective,
5296
+ declared,
5297
+ observed,
5298
+ capabilityEvidence: nativeImporterAdapterCapabilityEvidence(declared, observed, effective)
5299
+ });
5300
+ }
5301
+
5302
+ function adapterCoverageSnapshotFromSummary(coverage = {}) {
5303
+ return freezeNativeImporterAdapterCoverageSnapshot({
5304
+ exactness: coverage.exactness ?? 'unknown',
5305
+ exactAst: coverage.exactAst,
5306
+ tokens: coverage.tokens,
5307
+ trivia: coverage.trivia,
5308
+ diagnostics: coverage.diagnostics,
5309
+ sourceRanges: coverage.sourceRanges,
5310
+ generatedRanges: coverage.generatedRanges,
5311
+ semanticCoverage: coverage.semanticCoverage
5312
+ });
5313
+ }
5314
+
5315
+ function freezeNativeImporterAdapterCoverageSnapshot(value = {}) {
5316
+ return Object.freeze({
5317
+ exactness: String(value.exactness ?? 'unknown'),
5318
+ exactAst: Boolean(value.exactAst),
5319
+ tokens: Boolean(value.tokens),
5320
+ trivia: Boolean(value.trivia),
5321
+ diagnostics: Boolean(value.diagnostics),
5322
+ sourceRanges: Boolean(value.sourceRanges),
5323
+ generatedRanges: Boolean(value.generatedRanges),
5324
+ semanticCoverage: normalizeNativeImporterSemanticCoverage(value.semanticCoverage, {})
5325
+ });
5326
+ }
5327
+
5328
+ function normalizeNativeImporterAdapterObservedCoverage(value = {}, declared = {}) {
5329
+ const diagnostics = Number(value.diagnostics ?? value.parserDiagnostics ?? 0) || 0;
5330
+ const semanticCoverage = normalizeNativeImporterSemanticCoverage({
5331
+ ...(value.semanticCoverage ?? {}),
5332
+ declarations: value.semanticCoverage?.declarations ?? value.declarations,
5333
+ symbols: value.semanticCoverage?.symbols ?? value.symbols,
5334
+ references: value.semanticCoverage?.references ?? value.references,
5335
+ types: value.semanticCoverage?.types ?? value.types,
5336
+ controlFlow: value.semanticCoverage?.controlFlow ?? value.controlFlow
5337
+ }, {});
5338
+ const nativeAstNodes = Number(value.nativeAstNodes ?? 0) || 0;
5339
+ const exactness = String(value.exactness ?? observedAdapterExactness(declared, nativeAstNodes));
5340
+ return Object.freeze({
5341
+ exactness,
5342
+ exactAst: Boolean(value.exactAst ?? (declared.exactAst && nativeAstNodes > 0)),
5343
+ tokens: Boolean(value.tokens),
5344
+ tokenCount: Number(value.tokenCount ?? 0) || 0,
5345
+ trivia: Boolean(value.trivia),
5346
+ triviaCount: Number(value.triviaCount ?? 0) || 0,
5347
+ diagnostics,
5348
+ parserDiagnostics: diagnostics,
5349
+ diagnosticErrors: Number(value.diagnosticErrors ?? 0) || 0,
5350
+ diagnosticWarnings: Number(value.diagnosticWarnings ?? 0) || 0,
5351
+ diagnosticInfos: Number(value.diagnosticInfos ?? 0) || 0,
5352
+ losses: Number(value.losses ?? 0) || 0,
5353
+ nativeAstNodes,
5354
+ semanticSymbols: Number(value.semanticSymbols ?? 0) || 0,
5355
+ semanticReferences: Number(value.semanticReferences ?? 0) || 0,
5356
+ semanticTypes: Number(value.semanticTypes ?? 0) || 0,
5357
+ semanticControlFlow: Number(value.semanticControlFlow ?? 0) || 0,
5358
+ references: Boolean(value.references ?? semanticCoverage.references),
5359
+ types: Boolean(value.types ?? semanticCoverage.types),
5360
+ controlFlow: Boolean(value.controlFlow ?? semanticCoverage.controlFlow),
5361
+ sourceMapMappings: Number(value.sourceMapMappings ?? 0) || 0,
5362
+ sourceRanges: Boolean(value.sourceRanges),
5363
+ sourceRangeNodes: Number(value.sourceRangeNodes ?? 0) || 0,
5364
+ sourceRangeMappings: Number(value.sourceRangeMappings ?? 0) || 0,
5365
+ generatedRanges: Boolean(value.generatedRanges),
5366
+ generatedRangeMappings: Number(value.generatedRangeMappings ?? 0) || 0,
5367
+ semanticCoverage
5368
+ });
5369
+ }
5370
+
5371
+ function observeNativeImporterAdapterCoverageDetails(parseResult = {}, context = {}, observedContext = {}) {
5372
+ const declared = observedContext.declared ?? {};
5373
+ const nodeList = observedContext.nodeList ?? Object.values(parseResult.nativeAst?.nodes ?? parseResult.nodes ?? {});
5374
+ const sourceMapMappings = observedContext.sourceMapMappings
5375
+ ?? parseResult.sourceMaps?.flatMap((sourceMap) => sourceMap.mappings ?? [])
5376
+ ?? parseResult.mappings
5377
+ ?? [];
5378
+ const semanticIndex = observedContext.semanticIndex ?? parseResult.semanticIndex;
5379
+ const semanticSymbols = observedContext.semanticSymbols ?? semanticIndex?.symbols?.length ?? 0;
5380
+ const diagnostics = context.diagnostics ?? [];
5381
+ const diagnosticErrors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
5382
+ const diagnosticWarnings = diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;
5383
+ const diagnosticInfos = diagnostics.filter((diagnostic) => diagnostic.severity === 'info').length;
5384
+ const sourceRangeNodes = nodeList.filter((node) => Boolean(node?.span)).length;
5385
+ const sourceRangeMappings = sourceMapMappings.filter((mapping) => Boolean(mapping?.sourceSpan)).length;
5386
+ const generatedRangeMappings = sourceMapMappings.filter((mapping) => Boolean(mapping?.generatedSpan)).length;
5387
+ const preservation = adapterCoverageSourcePreservation(parseResult);
5388
+ const tokenCount = preservation?.summary?.tokens ?? preservation?.tokens?.length ?? 0;
5389
+ const triviaCount = preservation?.summary?.trivia ?? preservation?.trivia?.length ?? 0;
5390
+ const semanticEvidence = observeNativeImporterSemanticEvidence(semanticIndex);
5391
+ const semanticCoverage = normalizeNativeImporterSemanticCoverage({
5392
+ level: maxSemanticCoverageLevel(
5393
+ semanticSymbols || semanticEvidence.declarations
5394
+ ? 'declaration-index'
5395
+ : 'native-ast',
5396
+ semanticEvidence.references || semanticEvidence.types || semanticEvidence.controlFlow
5397
+ ? 'semantic-index'
5398
+ : semanticSymbols ? 'declaration-index' : 'native-ast'
5399
+ ),
5400
+ declarations: semanticSymbols > 0 || semanticEvidence.declarations > 0,
5401
+ symbols: semanticSymbols > 0,
5402
+ references: semanticEvidence.references > 0,
5403
+ types: semanticEvidence.types > 0,
5404
+ controlFlow: semanticEvidence.controlFlow > 0
5405
+ }, {});
5406
+ return normalizeNativeImporterAdapterObservedCoverage({
5407
+ exactness: observedAdapterExactness(declared, nodeList.length),
5408
+ exactAst: Boolean(declared.exactAst && nodeList.length > 0),
5409
+ tokens: tokenCount > 0,
5410
+ tokenCount,
5411
+ trivia: triviaCount > 0,
5412
+ triviaCount,
5413
+ diagnostics: diagnostics.length,
5414
+ parserDiagnostics: diagnostics.length,
5415
+ diagnosticErrors,
5416
+ diagnosticWarnings,
5417
+ diagnosticInfos,
5418
+ losses: context.losses?.length ?? 0,
5419
+ nativeAstNodes: nodeList.length,
5420
+ semanticSymbols,
5421
+ semanticReferences: semanticEvidence.references,
5422
+ semanticTypes: semanticEvidence.types,
5423
+ semanticControlFlow: semanticEvidence.controlFlow,
5424
+ references: semanticEvidence.references > 0,
5425
+ types: semanticEvidence.types > 0,
5426
+ controlFlow: semanticEvidence.controlFlow > 0,
5427
+ sourceMapMappings: sourceMapMappings.length,
5428
+ sourceRanges: sourceRangeNodes > 0 || sourceRangeMappings > 0,
5429
+ sourceRangeNodes,
5430
+ sourceRangeMappings,
5431
+ generatedRanges: generatedRangeMappings > 0,
5432
+ generatedRangeMappings,
5433
+ semanticCoverage
5434
+ }, declared);
5435
+ }
5436
+
5437
+ function adapterCoverageSourcePreservation(parseResult = {}) {
5438
+ return parseResult.sourcePreservation
5439
+ ?? parseResult.nativeAst?.metadata?.sourcePreservation
5440
+ ?? parseResult.nativeAstMetadata?.sourcePreservation
5441
+ ?? parseResult.metadata?.sourcePreservation;
5442
+ }
5443
+
5444
+ function observeNativeImporterSemanticEvidence(semanticIndex = {}) {
5445
+ const occurrences = semanticIndex?.occurrences ?? [];
5446
+ const relations = semanticIndex?.relations ?? [];
5447
+ const facts = semanticIndex?.facts ?? [];
5448
+ const symbols = semanticIndex?.symbols ?? [];
5449
+ const referenceRelations = relations.filter((relation) => semanticPredicateMatches(relation?.predicate, ['reference', 'call', 'read', 'write', 'use']));
5450
+ const typeFacts = facts.filter((fact) => semanticPredicateMatches(fact?.predicate, ['type', 'declaredtype', 'inferredtype', 'typeof']));
5451
+ const typedSymbols = symbols.filter((symbol) => Boolean(symbol?.declaredType ?? symbol?.inferredType ?? symbol?.typeId ?? symbol?.valueType));
5452
+ const controlFlowRecords = [
5453
+ ...relations.filter((relation) => semanticPredicateMatches(relation?.predicate, ['controlflow', 'cfg', 'flow'])),
5454
+ ...facts.filter((fact) => semanticPredicateMatches(fact?.predicate, ['controlflow', 'cfg', 'flow']))
5455
+ ];
5456
+ return {
5457
+ declarations: occurrences.filter((occurrence) => occurrence?.role === 'definition' || occurrence?.role === 'declaration').length,
5458
+ references: occurrences.filter((occurrence) => {
5459
+ const role = String(occurrence?.role ?? '').toLowerCase();
5460
+ return role && role !== 'definition' && role !== 'declaration';
5461
+ }).length + referenceRelations.length,
5462
+ types: typeFacts.length + typedSymbols.length,
5463
+ controlFlow: controlFlowRecords.length
5464
+ };
5465
+ }
5466
+
5467
+ function semanticPredicateMatches(value, fragments) {
5468
+ const predicate = String(value ?? '').toLowerCase().replace(/[^a-z0-9]+/g, '');
5469
+ return fragments.some((fragment) => predicate.includes(fragment));
5470
+ }
5471
+
5472
+ function observedAdapterExactness(declared = {}, nativeAstNodes = 0) {
5473
+ if (!nativeAstNodes) return 'unknown';
5474
+ if (declared.exactAst) return declared.exactness ?? 'exact-parser-ast';
5475
+ return 'adapter-reported-native-ast';
5476
+ }
5477
+
5478
+ function effectiveNativeImporterAdapterCoverage(declared, observed) {
5479
+ const semanticCoverage = normalizeNativeImporterSemanticCoverage({
5480
+ level: maxSemanticCoverageLevel(declared.semanticCoverage?.level, observed.semanticCoverage?.level),
5481
+ declarations: declared.semanticCoverage?.declarations || observed.semanticCoverage?.declarations,
5482
+ symbols: declared.semanticCoverage?.symbols || observed.semanticCoverage?.symbols,
5483
+ references: declared.semanticCoverage?.references || observed.semanticCoverage?.references,
5484
+ types: declared.semanticCoverage?.types || observed.semanticCoverage?.types,
5485
+ controlFlow: declared.semanticCoverage?.controlFlow || observed.semanticCoverage?.controlFlow
5486
+ }, {});
5487
+ return freezeNativeImporterAdapterCoverageSnapshot({
5488
+ exactness: effectiveAdapterExactness(declared, observed),
5489
+ exactAst: declared.exactAst || observed.exactAst,
5490
+ tokens: declared.tokens || observed.tokens,
5491
+ trivia: declared.trivia || observed.trivia,
5492
+ diagnostics: declared.diagnostics || observed.parserDiagnostics > 0,
5493
+ sourceRanges: declared.sourceRanges || observed.sourceRanges,
5494
+ generatedRanges: declared.generatedRanges || observed.generatedRanges,
5495
+ semanticCoverage
5496
+ });
5497
+ }
5498
+
5499
+ function effectiveAdapterExactness(declared, observed) {
5500
+ if (declared.exactAst || observed.exactAst) return declared.exactness ?? 'exact-parser-ast';
5501
+ if (observed.nativeAstNodes > 0) return observed.exactness ?? 'adapter-reported-native-ast';
5502
+ return declared.exactness ?? 'unknown';
5503
+ }
5504
+
5505
+ function nativeImporterAdapterCapabilityEvidence(declared, observed, effective) {
5506
+ const capabilityRows = [
5507
+ adapterCoverageCapabilityRow('exactAst', declared.exactAst, observed.exactAst, effective.exactAst, observed.nativeAstNodes),
5508
+ adapterCoverageCapabilityRow('tokens', declared.tokens, observed.tokens, effective.tokens, observed.tokenCount),
5509
+ adapterCoverageCapabilityRow('trivia', declared.trivia, observed.trivia, effective.trivia, observed.triviaCount),
5510
+ adapterCoverageCapabilityRow('parserDiagnostics', declared.diagnostics, observed.parserDiagnostics > 0, effective.diagnostics, observed.parserDiagnostics),
5511
+ adapterCoverageCapabilityRow('sourceRanges', declared.sourceRanges, observed.sourceRanges, effective.sourceRanges, observed.sourceRangeNodes + observed.sourceRangeMappings),
5512
+ adapterCoverageCapabilityRow('generatedRanges', declared.generatedRanges, observed.generatedRanges, effective.generatedRanges, observed.generatedRangeMappings),
5513
+ adapterCoverageCapabilityRow('semanticDeclarations', declared.semanticCoverage.declarations, observed.semanticCoverage.declarations, effective.semanticCoverage.declarations, observed.semanticSymbols),
5514
+ adapterCoverageCapabilityRow('semanticSymbols', declared.semanticCoverage.symbols, observed.semanticCoverage.symbols, effective.semanticCoverage.symbols, observed.semanticSymbols),
5515
+ adapterCoverageCapabilityRow('references', declared.semanticCoverage.references, observed.semanticCoverage.references, effective.semanticCoverage.references, observed.semanticReferences),
5516
+ adapterCoverageCapabilityRow('types', declared.semanticCoverage.types, observed.semanticCoverage.types, effective.semanticCoverage.types, observed.semanticTypes),
5517
+ adapterCoverageCapabilityRow('controlFlow', declared.semanticCoverage.controlFlow, observed.semanticCoverage.controlFlow, effective.semanticCoverage.controlFlow, observed.semanticControlFlow)
5518
+ ];
5519
+ const reviewCapabilities = new Set(['exactAst', 'tokens', 'trivia', 'parserDiagnostics', 'sourceRanges', 'generatedRanges', 'references', 'types', 'controlFlow']);
5520
+ return Object.freeze({
5521
+ declared,
5522
+ observed,
5523
+ effective,
5524
+ capabilities: Object.freeze(capabilityRows),
5525
+ gaps: Object.freeze(capabilityRows.filter((row) => reviewCapabilities.has(row.capability) && !row.effective).map((row) => row.capability)),
5526
+ declaredOnly: Object.freeze(capabilityRows.filter((row) => row.declared && !row.observed).map((row) => row.capability)),
5527
+ observedOnly: Object.freeze(capabilityRows.filter((row) => !row.declared && row.observed).map((row) => row.capability)),
5528
+ parserDiagnostics: Object.freeze({
5529
+ declared: declared.diagnostics,
5530
+ observed: observed.parserDiagnostics > 0,
5531
+ count: observed.parserDiagnostics,
5532
+ errors: observed.diagnosticErrors,
5533
+ warnings: observed.diagnosticWarnings,
5534
+ infos: observed.diagnosticInfos
5535
+ }),
5536
+ sourceRanges: Object.freeze({
5537
+ declared: declared.sourceRanges,
5538
+ observed: observed.sourceRanges,
5539
+ nativeAstNodes: observed.nativeAstNodes,
5540
+ sourceRangeNodes: observed.sourceRangeNodes,
5541
+ sourceMapMappings: observed.sourceMapMappings,
5542
+ sourceRangeMappings: observed.sourceRangeMappings,
5543
+ generatedRangeMappings: observed.generatedRangeMappings
5544
+ }),
5545
+ tokensTrivia: Object.freeze({
5546
+ tokens: Object.freeze({ declared: declared.tokens, observed: observed.tokens, count: observed.tokenCount }),
5547
+ trivia: Object.freeze({ declared: declared.trivia, observed: observed.trivia, count: observed.triviaCount })
5548
+ }),
5549
+ semantic: Object.freeze({
5550
+ level: Object.freeze({
5551
+ declared: declared.semanticCoverage.level,
5552
+ observed: observed.semanticCoverage.level,
5553
+ effective: effective.semanticCoverage.level
5554
+ }),
5555
+ declarations: adapterCoverageCapabilityRow('semanticDeclarations', declared.semanticCoverage.declarations, observed.semanticCoverage.declarations, effective.semanticCoverage.declarations, observed.semanticSymbols),
5556
+ symbols: adapterCoverageCapabilityRow('semanticSymbols', declared.semanticCoverage.symbols, observed.semanticCoverage.symbols, effective.semanticCoverage.symbols, observed.semanticSymbols),
5557
+ references: adapterCoverageCapabilityRow('references', declared.semanticCoverage.references, observed.semanticCoverage.references, effective.semanticCoverage.references, observed.semanticReferences),
5558
+ types: adapterCoverageCapabilityRow('types', declared.semanticCoverage.types, observed.semanticCoverage.types, effective.semanticCoverage.types, observed.semanticTypes),
5559
+ controlFlow: adapterCoverageCapabilityRow('controlFlow', declared.semanticCoverage.controlFlow, observed.semanticCoverage.controlFlow, effective.semanticCoverage.controlFlow, observed.semanticControlFlow)
5560
+ })
5561
+ });
5562
+ }
5563
+
5564
+ function adapterCoverageCapabilityRow(capability, declared, observed, effective, count = 0) {
5565
+ const status = declared && observed
5566
+ ? 'declared-and-observed'
5567
+ : declared ? 'declared-unobserved' : observed ? 'observed-undeclared' : 'absent';
5568
+ return Object.freeze({
5569
+ capability,
5570
+ declared: Boolean(declared),
5571
+ observed: Boolean(observed),
5572
+ effective: Boolean(effective),
5573
+ count: Number(count ?? 0) || 0,
5574
+ status
4884
5575
  });
4885
5576
  }
4886
5577
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",