@shapeshift-labs/frontier-lang-compiler 0.2.13 → 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
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,
@@ -101,6 +106,10 @@ console.log(JSON.stringify({
101
106
  adapterCoverageTokenGaps: coverageMatrix.summary.adapterCoverage.gaps.tokens ?? 0,
102
107
  adapterCoverageReferenceGaps: coverageMatrix.summary.adapterCoverage.gaps.references ?? 0,
103
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)),
104
113
  sourcePreservationRecords: preservationRecords.length,
105
114
  sourcePreservationTokens: preservationTokens,
106
115
  sourcePreservationDurationMs: Number(preservationDurationMs.toFixed(2)),
package/dist/index.d.ts CHANGED
@@ -168,7 +168,7 @@ 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[];
@@ -202,7 +202,7 @@ export interface NativeImportCoverageLanguage {
202
202
  readonly extensions: readonly string[];
203
203
  readonly supportsLightweightScan: boolean;
204
204
  readonly parserAdapters: readonly string[];
205
- readonly projectionTargets: readonly FrontierCompileTarget[];
205
+ readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
206
206
  readonly knownLossKinds: readonly NativeImportKnownLossKind[];
207
207
  readonly defaultReadiness: SemanticMergeReadiness;
208
208
  readonly notes: readonly string[];
@@ -241,6 +241,7 @@ export interface NativeImportCoverageMatrix {
241
241
  };
242
242
  readonly metadata: {
243
243
  readonly compileTargets: readonly FrontierCompileTarget[];
244
+ readonly projectionTargetLossClasses: readonly ProjectionTargetLossClass[];
244
245
  readonly note: string;
245
246
  };
246
247
  }
@@ -252,6 +253,96 @@ export interface NativeImportCoverageMatrixOptions {
252
253
  readonly generatedAt?: number;
253
254
  }
254
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
+
255
346
  export interface NativeImportContractSource {
256
347
  readonly id: string;
257
348
  readonly language?: FrontierSourceLanguage | string;
@@ -1081,6 +1172,7 @@ export declare const NativeImportRoundtripReadinessStatuses: readonly NativeImpo
1081
1172
  export declare const NativeImportTaxonomyKinds: readonly NativeImportTaxonomyKind[];
1082
1173
  export declare const NativeImportLossKinds: readonly NativeImportKnownLossKind[];
1083
1174
  export declare const NativeImportRegionTaxonomyKinds: readonly NativeImportRegionTaxonomyKind[];
1175
+ export declare const ProjectionTargetLossClasses: readonly ProjectionTargetLossClass[];
1084
1176
  export declare const NativeImportReadinessBySeverity: Readonly<Record<NativeImportLossSummary['highestSeverity'], SemanticMergeReadiness>>;
1085
1177
  export declare const NativeImportLanguageProfiles: readonly NativeImportLanguageProfile[];
1086
1178
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
@@ -1093,6 +1185,7 @@ export declare function summarizeNativeImportLosses(losses?: readonly NativeAstL
1093
1185
  export declare function classifyNativeImportReadiness(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportReadinessClassification;
1094
1186
  export declare function classifyNativeImportRoundtripReadiness(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportRoundtripReadinessOptions): NativeImportRoundtripReadinessClassification;
1095
1187
  export declare function createNativeImportCoverageMatrix(options?: NativeImportCoverageMatrixOptions): NativeImportCoverageMatrix;
1188
+ export declare function createProjectionTargetLossMatrix(options?: ProjectionTargetLossMatrixOptions): ProjectionTargetLossMatrix;
1096
1189
  export declare function createNativeSourcePreservation(options: CreateNativeSourcePreservationOptions): NativeSourcePreservation;
1097
1190
  export declare function createSemanticImportSidecar(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: SemanticImportSidecarOptions): SemanticImportSidecar;
1098
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'],
@@ -516,11 +523,37 @@ export function createNativeImportCoverageMatrix(input = {}) {
516
523
  summary,
517
524
  metadata: {
518
525
  compileTargets: [...FrontierCompileTargets],
526
+ projectionTargetLossClasses: [...ProjectionTargetLossClasses],
519
527
  note: 'Coverage is evidence and capability metadata, not a claim that every language feature is losslessly portable.'
520
528
  }
521
529
  };
522
530
  }
523
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
+
524
557
  export function createNativeSourcePreservation(options) {
525
558
  if (!options || typeof options.sourceText !== 'string') {
526
559
  throw new Error('createNativeSourcePreservation requires sourceText');
@@ -3371,15 +3404,273 @@ function nativeImportCategoryForLossKind(kind) {
3371
3404
  return String(kind ?? 'opaqueNative');
3372
3405
  }
3373
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
+
3374
3664
  function nativeImportLanguageProfile(language, input = {}) {
3375
3665
  const lossKinds = input.lossKinds ?? ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation'];
3666
+ const aliases = uniqueStrings(input.aliases ?? []);
3376
3667
  return Object.freeze({
3377
3668
  language,
3378
- aliases: Object.freeze(uniqueStrings(input.aliases ?? [])),
3669
+ aliases: Object.freeze(aliases),
3379
3670
  extensions: Object.freeze(uniqueStrings(input.extensions ?? [])),
3380
3671
  supportsLightweightScan: input.supportsLightweightScan !== false,
3381
3672
  parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? ['tree-sitter'])),
3382
- projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? FrontierCompileTargets)),
3673
+ projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? nativeProjectionTargetsForLanguage(language, aliases))),
3383
3674
  knownLossKinds: Object.freeze(uniqueStrings(lossKinds)),
3384
3675
  defaultReadiness: input.defaultReadiness ?? 'needs-review',
3385
3676
  notes: Object.freeze(uniqueStrings(input.notes ?? ['lightweight scanner records declarations only; exact parser adapters must be injected by the host']))
@@ -3423,7 +3714,7 @@ function normalizeNativeImportLanguageProfile(profile, fallbackLanguage) {
3423
3714
  extensions: uniqueStrings(profile.extensions ?? []),
3424
3715
  supportsLightweightScan: profile.supportsLightweightScan !== false,
3425
3716
  parserAdapters: uniqueStrings(profile.parserAdapters ?? []),
3426
- projectionTargets: uniqueStrings(profile.projectionTargets ?? FrontierCompileTargets),
3717
+ projectionTargets: uniqueStrings(profile.projectionTargets ?? nativeProjectionTargetsForLanguage(language, profile.aliases ?? [])),
3427
3718
  knownLossKinds: uniqueStrings(profile.knownLossKinds ?? profile.lossKinds ?? []),
3428
3719
  defaultReadiness: profile.defaultReadiness ?? 'needs-review',
3429
3720
  notes: uniqueStrings(profile.notes ?? [])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.13",
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",