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

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
@@ -64,6 +64,25 @@ console.log(readiness.readiness);
64
64
 
65
65
  The loss taxonomy separates broad scanner limits from specific round-trip risks such as conditional compilation, reflection, overload/type-inference gaps, comments/trivia preservation, source-map approximation, parser diagnostics, and target projection loss. These records are evidence labels for merge admission; they are not claims that the lightweight scanner expanded macros, evaluated inactive branches, resolved overloads, or ran a type checker.
66
66
 
67
+ High-risk native features also have explicit evidence policies. These policies are advisory in this package: they tell a swarm or admission queue what evidence is missing without silently changing the existing readiness classification.
68
+
69
+ ```js
70
+ import {
71
+ getNativeImportFeatureEvidencePolicy,
72
+ summarizeNativeImportFeatureEvidence
73
+ } from '@shapeshift-labs/frontier-lang-compiler';
74
+
75
+ const policy = getNativeImportFeatureEvidencePolicy('preprocessor');
76
+ console.log(policy.requiredEvidenceKeys); // ["preprocessedOutputHash", "definesHash"]
77
+
78
+ const featureEvidence = summarizeNativeImportFeatureEvidence(imported.losses, {
79
+ evidence: imported.evidence
80
+ });
81
+
82
+ console.log(featureEvidence.highestRisk);
83
+ console.log(featureEvidence.missingRequiredEvidence);
84
+ ```
85
+
67
86
  Ask the compiler what is actually covered before sending native imports into a merge queue:
68
87
 
69
88
  ```js
@@ -183,15 +202,18 @@ console.log(compiledJs.readiness.readiness); // scanner imports can still be "ne
183
202
 
184
203
  const rustCandidate = compileNativeSource(compiledJs.importResult, {
185
204
  target: 'rust',
205
+ targetPath: 'dist/runtime.rs',
186
206
  emitOnBlocked: true
187
207
  });
188
208
 
189
209
  console.log(rustCandidate.outputMode); // "target-stubs"
190
210
  console.log(rustCandidate.targetCoverage.lossClass); // "missingAdapter" without a JS-to-Rust adapter
191
211
  console.log(rustCandidate.ok); // true only because emitOnBlocked requested code anyway
212
+ console.log(rustCandidate.sourceMap.targetPath); // "dist/runtime.rs"
213
+ console.log(rustCandidate.sourceMap.mappings[0]?.semanticSymbolId); // generated span -> source symbol
192
214
  ```
193
215
 
194
- `compileNativeSource` returns the import result, projection, target loss matrix cell, combined losses, readiness, evidence, and output hash. Admission queues should treat `ok` as "code was emitted", not as merge approval; `readiness` and `targetCoverage` carry the merge signal.
216
+ `compileNativeSource` returns the import result, projection, target loss matrix cell, combined losses, readiness, evidence, output hash, and generated-output source maps. Same-language preserved output uses exact source mappings when the hash matches; generated stubs use declaration-level spans; adapter output uses adapter-supplied maps when present and otherwise gets an estimated fallback. Admission queues should treat `ok` as "code was emitted", not as merge approval; `readiness`, `targetCoverage`, and source-map precision carry the merge signal.
195
217
 
196
218
  Provide a target projection adapter when the host owns real native-to-target translation semantics:
197
219
 
@@ -220,12 +242,14 @@ const jsToRustAdapter = {
220
242
 
221
243
  const rustWithAdapter = compileNativeSource(compiledJs.importResult, {
222
244
  target: 'rust',
245
+ targetPath: 'dist/runtime.rs',
223
246
  targetAdapters: [jsToRustAdapter]
224
247
  });
225
248
 
226
249
  console.log(rustWithAdapter.outputMode); // "target-adapter"
227
250
  console.log(rustWithAdapter.targetCoverage.lossClass); // "targetAdapterProjection"
228
251
  console.log(rustWithAdapter.targetProjection.adapter.id); // "app-js-to-rust"
252
+ console.log(rustWithAdapter.sourceMaps.length); // adapter maps or compiler fallback map
229
253
  ```
230
254
 
231
255
  Project a native import back to source. Exact source is preserved when the import carries matching source-preservation evidence or when supplied text matches the import hash; otherwise the compiler emits declaration stubs with review-required loss evidence:
package/bench/smoke.mjs CHANGED
@@ -9,7 +9,8 @@ import {
9
9
  createSemanticImportSidecar,
10
10
  importNativeSource,
11
11
  projectNativeImportToSource,
12
- runNativeImporterAdapter
12
+ runNativeImporterAdapter,
13
+ summarizeNativeImportFeatureEvidence
13
14
  } from '../dist/index.js';
14
15
 
15
16
  const source = `
@@ -88,6 +89,13 @@ const semanticSidecars = nativeImportResults.map((imported) => createSemanticImp
88
89
  const sidecarDurationMs = performance.now() - sidecarStart;
89
90
  const sidecarOwnershipRegions = semanticSidecars.reduce((sum, sidecar) => sum + sidecar.ownershipRegions.length, 0);
90
91
 
92
+ const featureEvidenceStart = performance.now();
93
+ const featureEvidenceSummaries = nativeImportResults.map((imported) => summarizeNativeImportFeatureEvidence(imported.losses, {
94
+ evidence: imported.evidence
95
+ }));
96
+ const featureEvidenceDurationMs = performance.now() - featureEvidenceStart;
97
+ const featureEvidencePolicyMatches = featureEvidenceSummaries.reduce((sum, summary) => sum + summary.total, 0);
98
+
91
99
  const projectionStart = performance.now();
92
100
  const nativeProjections = nativeImportResults.map((imported) => projectNativeImportToSource(imported));
93
101
  const projectionDurationMs = performance.now() - projectionStart;
@@ -100,6 +108,8 @@ const nativeCompiles = nativeImportResults.map((imported, index) => compileNativ
100
108
  }));
101
109
  const nativeCompileDurationMs = performance.now() - nativeCompileStart;
102
110
  const nativeCompileBytes = nativeCompiles.reduce((sum, result) => sum + result.output.length, 0);
111
+ const nativeCompileSourceMaps = nativeCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
112
+ const nativeCompileSourceMapMappings = nativeCompiles.reduce((sum, result) => sum + result.sourceMaps.reduce((mapSum, sourceMap) => mapSum + sourceMap.mappings.length, 0), 0);
103
113
  const nativeCompileBlocked = nativeCompiles.filter((result) => result.readiness.readiness === 'blocked').length;
104
114
  const nativeTargetAdapterStart = performance.now();
105
115
  const nativeTargetAdapterCompiles = nativeImportResults.slice(0, 25).map((imported, index) => {
@@ -122,6 +132,7 @@ const nativeTargetAdapterCompiles = nativeImportResults.slice(0, 25).map((import
122
132
  });
123
133
  const nativeTargetAdapterDurationMs = performance.now() - nativeTargetAdapterStart;
124
134
  const nativeTargetAdapterBytes = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.output.length, 0);
135
+ const nativeTargetAdapterSourceMaps = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
125
136
 
126
137
  console.log(JSON.stringify({
127
138
  compiles: 250,
@@ -147,14 +158,19 @@ console.log(JSON.stringify({
147
158
  semanticSidecars: semanticSidecars.length,
148
159
  sidecarOwnershipRegions,
149
160
  sidecarDurationMs: Number(sidecarDurationMs.toFixed(2)),
161
+ featureEvidencePolicyMatches,
162
+ featureEvidenceDurationMs: Number(featureEvidenceDurationMs.toFixed(2)),
150
163
  nativeProjections: nativeProjections.length,
151
164
  projectionBytes,
152
165
  projectionDurationMs: Number(projectionDurationMs.toFixed(2)),
153
166
  nativeCompiles: nativeCompiles.length,
154
167
  nativeCompileBytes,
168
+ nativeCompileSourceMaps,
169
+ nativeCompileSourceMapMappings,
155
170
  nativeCompileBlocked,
156
171
  nativeCompileDurationMs: Number(nativeCompileDurationMs.toFixed(2)),
157
172
  nativeTargetAdapterCompiles: nativeTargetAdapterCompiles.length,
158
173
  nativeTargetAdapterBytes,
174
+ nativeTargetAdapterSourceMaps,
159
175
  nativeTargetAdapterDurationMs: Number(nativeTargetAdapterDurationMs.toFixed(2))
160
176
  }));
package/dist/index.d.ts CHANGED
@@ -151,6 +151,49 @@ export interface NativeImportLossSummaryOptions {
151
151
  readonly semanticStatus?: string;
152
152
  }
153
153
 
154
+ export type NativeImportFeatureEvidenceRisk = 'low' | 'medium' | 'high' | 'critical' | string;
155
+
156
+ export interface NativeImportFeatureEvidencePolicy {
157
+ readonly kind: NativeImportKnownLossKind;
158
+ readonly category: NativeImportTaxonomyKind;
159
+ readonly risk: NativeImportFeatureEvidenceRisk;
160
+ readonly minimumReadiness: SemanticMergeReadiness;
161
+ readonly missingEvidenceReadiness: SemanticMergeReadiness;
162
+ readonly requiredEvidenceKeys: readonly string[];
163
+ readonly recommendedEvidenceKeys: readonly string[];
164
+ readonly notes: readonly string[];
165
+ }
166
+
167
+ export interface NativeImportFeatureEvidenceIssue {
168
+ readonly lossId: string;
169
+ readonly kind: NativeImportKnownLossKind;
170
+ readonly policyKind: NativeImportKnownLossKind;
171
+ readonly risk: NativeImportFeatureEvidenceRisk;
172
+ readonly category: NativeImportTaxonomyKind;
173
+ readonly readiness: SemanticMergeReadiness;
174
+ readonly missingRequiredEvidence: readonly string[];
175
+ readonly presentRequiredEvidence: readonly string[];
176
+ readonly presentRecommendedEvidence: readonly string[];
177
+ readonly evidenceIds: readonly string[];
178
+ }
179
+
180
+ export interface NativeImportFeatureEvidenceSummary {
181
+ readonly total: number;
182
+ readonly policyKinds: readonly NativeImportKnownLossKind[];
183
+ readonly byKind: Readonly<Record<string, number>>;
184
+ readonly byRisk: Readonly<Record<string, number>>;
185
+ readonly highestRisk: NativeImportFeatureEvidenceRisk;
186
+ readonly semanticMergeReadiness: SemanticMergeReadiness;
187
+ readonly missingRequiredEvidence: readonly {
188
+ readonly lossId: string;
189
+ readonly kind: NativeImportKnownLossKind;
190
+ readonly policyKind: NativeImportKnownLossKind;
191
+ readonly evidenceKey: string;
192
+ }[];
193
+ readonly issues: readonly NativeImportFeatureEvidenceIssue[];
194
+ readonly reasons: readonly string[];
195
+ }
196
+
154
197
  export interface NativeImportLossSummary {
155
198
  readonly total: number;
156
199
  readonly hasLosses: boolean;
@@ -165,6 +208,7 @@ export interface NativeImportLossSummary {
165
208
  readonly reviewLossIds: readonly string[];
166
209
  readonly informationalLossIds: readonly string[];
167
210
  readonly failedEvidenceIds: readonly string[];
211
+ readonly featureEvidence: NativeImportFeatureEvidenceSummary;
168
212
  readonly parser?: string;
169
213
  readonly scanKind?: string;
170
214
  readonly semanticStatus?: string;
@@ -1331,6 +1375,10 @@ export interface CompileNativeSourceOptions extends ProjectNativeImportToSourceO
1331
1375
  readonly languages?: readonly NativeImportLanguageProfile[];
1332
1376
  readonly generatedAt?: number;
1333
1377
  readonly emitOnBlocked?: boolean;
1378
+ readonly emitSourceMap?: boolean;
1379
+ readonly targetPath?: string;
1380
+ readonly targetHash?: string;
1381
+ readonly sourceMapId?: string;
1334
1382
  readonly projectionId?: string;
1335
1383
  readonly projectionEvidenceId?: string;
1336
1384
  readonly compileEvidenceId?: string;
@@ -1349,6 +1397,8 @@ export interface NativeSourceCompileResult {
1349
1397
  readonly output: string;
1350
1398
  readonly outputHash: string;
1351
1399
  readonly outputMode: NativeSourceCompileOutputMode;
1400
+ readonly sourceMap?: SourceMapRecord;
1401
+ readonly sourceMaps: readonly SourceMapRecord[];
1352
1402
  readonly importResult: NativeSourceImportResult;
1353
1403
  readonly projection: NativeSourceProjectionResult;
1354
1404
  readonly targetProjection?: NativeTargetProjectionResult;
@@ -1422,6 +1472,7 @@ export declare const NativeImportLossKinds: readonly NativeImportKnownLossKind[]
1422
1472
  export declare const NativeImportRegionTaxonomyKinds: readonly NativeImportRegionTaxonomyKind[];
1423
1473
  export declare const ProjectionTargetLossClasses: readonly ProjectionTargetLossClass[];
1424
1474
  export declare const NativeImportReadinessBySeverity: Readonly<Record<NativeImportLossSummary['highestSeverity'], SemanticMergeReadiness>>;
1475
+ export declare const NativeImportFeatureEvidencePolicies: Readonly<Record<string, NativeImportFeatureEvidencePolicy>>;
1425
1476
  export declare const NativeImportLanguageProfiles: readonly NativeImportLanguageProfile[];
1426
1477
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
1427
1478
  export declare function compileFrontierSource(source: string, options?: FrontierCompileOptions): FrontierCompileResult;
@@ -1432,6 +1483,8 @@ export declare function renderTargetAst(ast: FrontierTargetAst, target?: Frontie
1432
1483
  export declare function renderTargetAstWithSourceMap(ast: FrontierTargetAst, target?: FrontierCompileOptions['target'], options?: FrontierCompileEmitOptions): FrontierTargetSourceMapResult;
1433
1484
  export declare function emitForTargetWithSourceMap(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: FrontierCompileEmitOptions): FrontierTargetDocumentSourceMapResult;
1434
1485
  export declare function resolveCapabilityAdapters(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: { readonly platform?: string }): readonly CapabilityResolution[];
1486
+ export declare function getNativeImportFeatureEvidencePolicy(kind: NativeImportKnownLossKind | string): NativeImportFeatureEvidencePolicy | undefined;
1487
+ export declare function summarizeNativeImportFeatureEvidence(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportFeatureEvidenceSummary;
1435
1488
  export declare function summarizeNativeImportLosses(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportLossSummary;
1436
1489
  export declare function classifyNativeImportReadiness(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportReadinessClassification;
1437
1490
  export declare function classifyNativeImportRoundtripReadiness(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportRoundtripReadinessOptions): NativeImportRoundtripReadinessClassification;
package/dist/index.js CHANGED
@@ -76,6 +76,13 @@ const semanticMergeReadinessRank = Object.freeze({
76
76
  blocked: 3
77
77
  });
78
78
 
79
+ const nativeFeatureEvidenceRiskRank = Object.freeze({
80
+ low: 0,
81
+ medium: 1,
82
+ high: 2,
83
+ critical: 3
84
+ });
85
+
79
86
  export const NativeImportRoundtripReadinessStatuses = Object.freeze([
80
87
  'exact',
81
88
  'preserved-source',
@@ -136,6 +143,117 @@ export const NativeImportReadinessBySeverity = Object.freeze({
136
143
  error: 'blocked'
137
144
  });
138
145
 
146
+ export const NativeImportFeatureEvidencePolicies = Object.freeze({
147
+ macroExpansion: nativeImportFeatureEvidencePolicy('macroExpansion', {
148
+ category: 'macroExpansion',
149
+ risk: 'high',
150
+ minimumReadiness: 'needs-review',
151
+ requiredEvidenceKeys: ['macroDefinitionsHash', 'expandedSourceHash'],
152
+ recommendedEvidenceKeys: ['expansionMapId', 'sourceMapId', 'macroCallSites'],
153
+ notes: ['Macro-expanded code must retain a link from generated output back to macro call sites before semantic merges can be trusted.']
154
+ }),
155
+ macroHygiene: nativeImportFeatureEvidencePolicy('macroHygiene', {
156
+ category: 'macroExpansion',
157
+ risk: 'critical',
158
+ minimumReadiness: 'needs-review',
159
+ missingEvidenceReadiness: 'blocked',
160
+ requiredEvidenceKeys: ['hygieneContextHash', 'bindingMapId'],
161
+ recommendedEvidenceKeys: ['expansionMapId', 'captureSetHash'],
162
+ notes: ['Hygiene-sensitive macros can change binding identity even when emitted text looks equivalent.']
163
+ }),
164
+ preprocessor: nativeImportFeatureEvidencePolicy('preprocessor', {
165
+ category: 'preprocessor',
166
+ risk: 'high',
167
+ minimumReadiness: 'needs-review',
168
+ requiredEvidenceKeys: ['preprocessedOutputHash', 'definesHash'],
169
+ recommendedEvidenceKeys: ['includeGraphHash', 'conditionalBranches', 'sourceMapId'],
170
+ notes: ['Preprocessor imports need the active defines/includes and preprocessed output hash to make replayable claims.']
171
+ }),
172
+ conditionalCompilation: nativeImportFeatureEvidencePolicy('conditionalCompilation', {
173
+ category: 'conditionalCompilation',
174
+ risk: 'high',
175
+ minimumReadiness: 'needs-review',
176
+ requiredEvidenceKeys: ['activeBranches', 'inactiveBranchesHash'],
177
+ recommendedEvidenceKeys: ['compileTarget', 'featureFlags', 'preprocessedOutputHash'],
178
+ notes: ['Conditional branches that were not active still affect portability and conflict review.']
179
+ }),
180
+ metaprogramming: nativeImportFeatureEvidencePolicy('metaprogramming', {
181
+ category: 'metaprogramming',
182
+ risk: 'critical',
183
+ minimumReadiness: 'needs-review',
184
+ missingEvidenceReadiness: 'blocked',
185
+ requiredEvidenceKeys: ['generatedArtifactHash', 'generatorIdentity'],
186
+ recommendedEvidenceKeys: ['generatorInputsHash', 'generatedRanges', 'replayCommand'],
187
+ notes: ['Generated or metaprogrammed declarations need replayable generator identity and input evidence.']
188
+ }),
189
+ reflection: nativeImportFeatureEvidencePolicy('reflection', {
190
+ category: 'reflection',
191
+ risk: 'high',
192
+ minimumReadiness: 'needs-review',
193
+ requiredEvidenceKeys: ['reflectionSurface', 'runtimeContract'],
194
+ recommendedEvidenceKeys: ['observedMembers', 'fixtureIds', 'runtimeVersion'],
195
+ notes: ['Reflection-heavy code needs a declared runtime contract because static AST evidence is incomplete.']
196
+ }),
197
+ dynamicRuntime: nativeImportFeatureEvidencePolicy('dynamicRuntime', {
198
+ category: 'reflection',
199
+ risk: 'high',
200
+ minimumReadiness: 'needs-review',
201
+ requiredEvidenceKeys: ['runtimeContract'],
202
+ recommendedEvidenceKeys: ['fixtureIds', 'observedEffects', 'runtimeVersion'],
203
+ notes: ['Dynamic runtime behavior should stay review-required until fixtures or traces describe the observed contract.']
204
+ }),
205
+ dynamicDispatch: nativeImportFeatureEvidencePolicy('dynamicDispatch', {
206
+ category: 'overloadTypeInference',
207
+ risk: 'medium',
208
+ minimumReadiness: 'needs-review',
209
+ requiredEvidenceKeys: ['dispatchTargets'],
210
+ recommendedEvidenceKeys: ['callGraphId', 'typeEvidenceId', 'fixtureIds'],
211
+ notes: ['Dynamic dispatch needs candidate target evidence before call graph or porting claims are merge-ready.']
212
+ }),
213
+ generatedCode: nativeImportFeatureEvidencePolicy('generatedCode', {
214
+ category: 'generatedCode',
215
+ risk: 'high',
216
+ minimumReadiness: 'needs-review',
217
+ requiredEvidenceKeys: ['generatedArtifactHash', 'generatedRanges'],
218
+ recommendedEvidenceKeys: ['generatorIdentity', 'generatorInputsHash', 'sourceMapId'],
219
+ notes: ['Generated code must preserve generated ranges and artifact hashes so workers can avoid editing derived output blindly.']
220
+ }),
221
+ overloadResolution: nativeImportFeatureEvidencePolicy('overloadResolution', {
222
+ category: 'overloadTypeInference',
223
+ risk: 'medium',
224
+ minimumReadiness: 'needs-review',
225
+ requiredEvidenceKeys: ['resolvedOverloads'],
226
+ recommendedEvidenceKeys: ['typeEvidenceId', 'compilerVersion', 'callSiteSpans'],
227
+ notes: ['Overload-sensitive imports should record compiler/type evidence for each call site.']
228
+ }),
229
+ typeInference: nativeImportFeatureEvidencePolicy('typeInference', {
230
+ category: 'overloadTypeInference',
231
+ risk: 'medium',
232
+ minimumReadiness: 'needs-review',
233
+ requiredEvidenceKeys: ['inferredTypesHash'],
234
+ recommendedEvidenceKeys: ['typeEvidenceId', 'compilerVersion', 'symbolTableHash'],
235
+ notes: ['Inferred types need a stable type-evidence hash before cross-language projection can claim fidelity.']
236
+ }),
237
+ unsupportedSyntax: nativeImportFeatureEvidencePolicy('unsupportedSyntax', {
238
+ category: 'unsupportedSyntax',
239
+ risk: 'high',
240
+ minimumReadiness: 'needs-review',
241
+ missingEvidenceReadiness: 'blocked',
242
+ requiredEvidenceKeys: ['unsupportedSyntaxKind', 'sourceSpan'],
243
+ recommendedEvidenceKeys: ['parserDiagnosticId', 'nativeAstNodeId', 'sourceSnippetHash'],
244
+ notes: ['Unsupported syntax must remain anchored to source spans and parser diagnostics for later adapter work.']
245
+ }),
246
+ unsupportedSemantic: nativeImportFeatureEvidencePolicy('unsupportedSemantic', {
247
+ category: 'unsupportedSyntax',
248
+ risk: 'high',
249
+ minimumReadiness: 'needs-review',
250
+ missingEvidenceReadiness: 'blocked',
251
+ requiredEvidenceKeys: ['unsupportedSemanticKind', 'semanticSymbolId'],
252
+ recommendedEvidenceKeys: ['semanticIndexId', 'sourceMapId', 'reason'],
253
+ notes: ['Unsupported semantics should name the affected symbol so merge tools can isolate the unsafe region.']
254
+ })
255
+ });
256
+
139
257
  export const NativeImportRegionTaxonomyKinds = Object.freeze([
140
258
  'symbol',
141
259
  'declaration',
@@ -359,6 +477,25 @@ export function compileNativeSource(input, options = {}) {
359
477
  scanKind: 'native-source-compile',
360
478
  semanticStatus: importResult.metadata?.semanticStatus ?? options.semanticStatus
361
479
  });
480
+ const sourceMaps = options.emitSourceMap === false
481
+ ? []
482
+ : nativeSourceCompileSourceMaps({
483
+ id: options.sourceMapId ?? `source_map_${idFragment(id)}_${idFragment(target)}`,
484
+ importResult,
485
+ projection,
486
+ targetProjection,
487
+ sourceLanguage,
488
+ target,
489
+ targetPath: options.targetPath ?? targetProjection?.targetPath,
490
+ targetHash: options.targetHash ?? outputHash,
491
+ output,
492
+ outputHash,
493
+ outputMode,
494
+ evidence,
495
+ losses,
496
+ compileResultId: id
497
+ });
498
+ const sourceMap = sourceMaps[0];
362
499
  return {
363
500
  kind: 'frontier.lang.nativeSourceCompileResult',
364
501
  version: 1,
@@ -370,6 +507,8 @@ export function compileNativeSource(input, options = {}) {
370
507
  output,
371
508
  outputHash,
372
509
  outputMode,
510
+ sourceMap,
511
+ sourceMaps,
373
512
  importResult,
374
513
  projection,
375
514
  targetProjection,
@@ -388,8 +527,13 @@ export function compileNativeSource(input, options = {}) {
388
527
  projectionId: projection.id,
389
528
  targetProjectionId: targetProjection?.id,
390
529
  targetProjectionAdapterId: targetProjection?.adapter?.id,
530
+ sourceMapId: sourceMap?.id,
531
+ sourceMapIds: sourceMaps.map((record) => record.id).filter(Boolean),
532
+ sourceMapMappings: sourceMaps.reduce((sum, record) => sum + (record.mappings?.length ?? 0), 0),
391
533
  projectionMode: projection.mode,
392
534
  outputMode,
535
+ targetPath: sourceMap?.targetPath ?? options.targetPath ?? targetProjection?.targetPath,
536
+ targetHash: sourceMap?.targetHash ?? options.targetHash ?? outputHash,
393
537
  sourceTarget,
394
538
  sameSourceTarget,
395
539
  targetLossClass: targetCoverage.lossClass,
@@ -447,6 +591,70 @@ export function resolveCapabilityAdapters(document, target = 'typescript', optio
447
591
  });
448
592
  }
449
593
 
594
+ export function getNativeImportFeatureEvidencePolicy(kind) {
595
+ const normalized = normalizeNativeLossKind({ kind }, 'warning');
596
+ return NativeImportFeatureEvidencePolicies[normalized] ?? NativeImportFeatureEvidencePolicies[String(kind ?? '')];
597
+ }
598
+
599
+ export function summarizeNativeImportFeatureEvidence(losses = [], options = {}) {
600
+ const normalizedLosses = normalizeNativeLossRecords(losses);
601
+ const evidence = options.evidence ?? [];
602
+ const issues = [];
603
+ const byKind = {};
604
+ const byRisk = {};
605
+ const policyKinds = [];
606
+ let highestRisk = 'low';
607
+ let semanticMergeReadiness = 'ready';
608
+
609
+ for (const loss of normalizedLosses) {
610
+ const policy = getNativeImportFeatureEvidencePolicy(loss.kind);
611
+ if (!policy) continue;
612
+ byKind[policy.kind] = (byKind[policy.kind] ?? 0) + 1;
613
+ byRisk[policy.risk] = (byRisk[policy.risk] ?? 0) + 1;
614
+ if ((nativeFeatureEvidenceRiskRank[policy.risk] ?? 0) > (nativeFeatureEvidenceRiskRank[highestRisk] ?? 0)) {
615
+ highestRisk = policy.risk;
616
+ }
617
+ policyKinds.push(policy.kind);
618
+ semanticMergeReadiness = maxSemanticMergeReadiness(semanticMergeReadiness, policy.minimumReadiness);
619
+ const missingRequiredEvidence = policy.requiredEvidenceKeys.filter((key) => !nativeImportFeatureEvidenceHasKey(loss, evidence, key));
620
+ const presentRequiredEvidence = policy.requiredEvidenceKeys.filter((key) => !missingRequiredEvidence.includes(key));
621
+ const presentRecommendedEvidence = policy.recommendedEvidenceKeys.filter((key) => nativeImportFeatureEvidenceHasKey(loss, evidence, key));
622
+ if (missingRequiredEvidence.length) {
623
+ semanticMergeReadiness = maxSemanticMergeReadiness(semanticMergeReadiness, policy.missingEvidenceReadiness);
624
+ }
625
+ issues.push({
626
+ lossId: loss.id,
627
+ kind: loss.kind,
628
+ policyKind: policy.kind,
629
+ risk: policy.risk,
630
+ category: policy.category,
631
+ readiness: missingRequiredEvidence.length ? policy.missingEvidenceReadiness : policy.minimumReadiness,
632
+ missingRequiredEvidence,
633
+ presentRequiredEvidence,
634
+ presentRecommendedEvidence,
635
+ evidenceIds: nativeImportFeatureEvidenceIds(loss, evidence, policy)
636
+ });
637
+ }
638
+
639
+ const missingRequiredEvidence = issues.flatMap((issue) => issue.missingRequiredEvidence.map((key) => ({
640
+ lossId: issue.lossId,
641
+ kind: issue.kind,
642
+ policyKind: issue.policyKind,
643
+ evidenceKey: key
644
+ })));
645
+ return {
646
+ total: issues.length,
647
+ policyKinds: uniqueStrings(policyKinds),
648
+ byKind,
649
+ byRisk,
650
+ highestRisk: issues.length ? highestRisk : 'low',
651
+ semanticMergeReadiness,
652
+ missingRequiredEvidence,
653
+ issues,
654
+ reasons: nativeImportFeatureEvidenceReasons(issues)
655
+ };
656
+ }
657
+
450
658
  export function summarizeNativeImportLosses(losses = [], options = {}) {
451
659
  const normalizedLosses = normalizeNativeLossRecords(losses);
452
660
  const bySeverity = { info: 0, warning: 0, error: 0 };
@@ -486,6 +694,9 @@ export function summarizeNativeImportLosses(losses = [], options = {}) {
486
694
  reviewLossIds,
487
695
  informationalLossIds
488
696
  });
697
+ const featureEvidence = summarizeNativeImportFeatureEvidence(normalizedLosses, {
698
+ evidence: options.evidence
699
+ });
489
700
 
490
701
  return {
491
702
  total: normalizedLosses.length,
@@ -501,6 +712,7 @@ export function summarizeNativeImportLosses(losses = [], options = {}) {
501
712
  reviewLossIds,
502
713
  informationalLossIds,
503
714
  failedEvidenceIds,
715
+ featureEvidence,
504
716
  parser: options.parser,
505
717
  scanKind: options.scanKind,
506
718
  semanticStatus: options.semanticStatus
@@ -4086,6 +4298,61 @@ function nativeImportCategoryForLossKind(kind) {
4086
4298
  return String(kind ?? 'opaqueNative');
4087
4299
  }
4088
4300
 
4301
+ function nativeImportFeatureEvidencePolicy(kind, input = {}) {
4302
+ return Object.freeze({
4303
+ kind,
4304
+ category: input.category ?? nativeImportCategoryForLossKind(kind),
4305
+ risk: input.risk ?? 'medium',
4306
+ minimumReadiness: normalizeSemanticMergeReadiness(input.minimumReadiness) ?? 'needs-review',
4307
+ missingEvidenceReadiness: normalizeSemanticMergeReadiness(input.missingEvidenceReadiness) ?? 'needs-review',
4308
+ requiredEvidenceKeys: Object.freeze(uniqueStrings(input.requiredEvidenceKeys ?? [])),
4309
+ recommendedEvidenceKeys: Object.freeze(uniqueStrings(input.recommendedEvidenceKeys ?? [])),
4310
+ notes: Object.freeze(uniqueStrings(input.notes ?? []))
4311
+ });
4312
+ }
4313
+
4314
+ function nativeImportFeatureEvidenceHasKey(loss, evidence, key) {
4315
+ return nativeImportFeatureEvidenceValuePresent(nativeImportFeatureEvidenceValue(loss, key))
4316
+ || (evidence ?? []).some((record) => nativeImportFeatureEvidenceValuePresent(nativeImportFeatureEvidenceValue(record, key)));
4317
+ }
4318
+
4319
+ function nativeImportFeatureEvidenceValue(record, key) {
4320
+ if (!record || !key) return undefined;
4321
+ const candidates = [record, record.metadata].filter(Boolean);
4322
+ for (const candidate of candidates) {
4323
+ const direct = candidate[key];
4324
+ if (direct !== undefined) return direct;
4325
+ const dotted = String(key).split('.').reduce((current, part) => current?.[part], candidate);
4326
+ if (dotted !== undefined) return dotted;
4327
+ }
4328
+ return undefined;
4329
+ }
4330
+
4331
+ function nativeImportFeatureEvidenceValuePresent(value) {
4332
+ if (value === undefined || value === null) return false;
4333
+ if (typeof value === 'string') return value.trim().length > 0;
4334
+ if (Array.isArray(value)) return value.length > 0;
4335
+ if (typeof value === 'object') return Object.keys(value).length > 0;
4336
+ return true;
4337
+ }
4338
+
4339
+ function nativeImportFeatureEvidenceIds(loss, evidence, policy) {
4340
+ const keys = [...policy.requiredEvidenceKeys, ...policy.recommendedEvidenceKeys];
4341
+ return uniqueStrings((evidence ?? [])
4342
+ .filter((record) => keys.some((key) => nativeImportFeatureEvidenceValuePresent(nativeImportFeatureEvidenceValue(record, key))))
4343
+ .map((record) => record.id)
4344
+ .filter(Boolean)
4345
+ .concat(loss.evidenceIds ?? []));
4346
+ }
4347
+
4348
+ function nativeImportFeatureEvidenceReasons(issues) {
4349
+ return uniqueStrings((issues ?? []).flatMap((issue) => {
4350
+ const missing = issue.missingRequiredEvidence ?? [];
4351
+ if (!missing.length) return [];
4352
+ return [`${issue.kind} loss ${issue.lossId} is missing required evidence: ${missing.join(', ')}.`];
4353
+ }));
4354
+ }
4355
+
4089
4356
  function normalizeProjectionMatrixTargets(targets) {
4090
4357
  return uniqueStrings((Array.isArray(targets) ? targets : [targets])
4091
4358
  .map((target) => {
@@ -4544,6 +4811,251 @@ function nativeSourceCompileEvidence(input) {
4544
4811
  };
4545
4812
  }
4546
4813
 
4814
+ function nativeSourceCompileSourceMaps(input) {
4815
+ const adapterSourceMaps = input.targetProjection?.sourceMaps ?? [];
4816
+ if (adapterSourceMaps.length) return adapterSourceMaps;
4817
+ const targetPath = nativeSourceCompileTargetPath(input);
4818
+ const targetHash = input.targetHash ?? input.outputHash;
4819
+ const target = nativeSourceCompileMapTarget(input, targetPath);
4820
+ const mappings = input.projection.mode === 'preserved-source'
4821
+ ? nativeSourceCompilePreservedMappings({ ...input, targetPath, targetHash, target })
4822
+ : nativeSourceCompileDeclarationMappings({ ...input, targetPath, targetHash, target });
4823
+ const resolvedMappings = mappings.length
4824
+ ? mappings
4825
+ : [nativeSourceCompileFileMapping({ ...input, targetPath, targetHash, target })];
4826
+ return [createSourceMapRecord({
4827
+ id: input.id,
4828
+ sourcePath: input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath,
4829
+ sourceHash: input.importResult.nativeSource?.sourceHash ?? input.importResult.nativeAst?.sourceHash ?? input.importResult.sourceHash,
4830
+ target,
4831
+ targetPath: targetPath ?? commonGeneratedTargetPath(resolvedMappings),
4832
+ targetHash,
4833
+ semanticIndexId: input.importResult.semanticIndex?.id ?? input.importResult.universalAst?.semanticIndex?.id,
4834
+ universalAstId: input.importResult.universalAst?.id,
4835
+ nativeAstId: input.importResult.nativeAst?.id ?? input.importResult.nativeSource?.ast?.id,
4836
+ nativeSourceId: input.importResult.nativeSource?.id,
4837
+ mappings: resolvedMappings,
4838
+ evidence: input.evidence ?? [],
4839
+ metadata: {
4840
+ compileResultId: input.compileResultId,
4841
+ importId: input.importResult.id,
4842
+ projectionId: input.projection.id,
4843
+ targetProjectionId: input.targetProjection?.id,
4844
+ targetProjectionAdapterId: input.targetProjection?.adapter?.id,
4845
+ sourceLanguage: input.sourceLanguage,
4846
+ target: input.target,
4847
+ outputMode: input.outputMode,
4848
+ outputHash: input.outputHash,
4849
+ generatedBy: 'compileNativeSource'
4850
+ }
4851
+ })];
4852
+ }
4853
+
4854
+ function nativeSourceCompilePreservedMappings(input) {
4855
+ const sourceMaps = input.importResult.sourceMaps ?? input.importResult.universalAst?.sourceMaps ?? [];
4856
+ const sourceHash = input.importResult.nativeSource?.sourceHash ?? input.importResult.nativeAst?.sourceHash ?? input.importResult.sourceHash;
4857
+ const exact = input.projection.mode === 'preserved-source' && (!input.projection.sourceHash || input.outputHash === input.projection.sourceHash);
4858
+ const usedIds = new Set();
4859
+ return sourceMaps
4860
+ .flatMap((sourceMap) => sourceMap?.mappings ?? [])
4861
+ .filter((mapping) => mapping?.sourceSpan)
4862
+ .map((mapping, index) => ({
4863
+ id: reserveUniqueId(`compile_map_${idFragment(mapping.id ?? mapping.semanticSymbolId ?? mapping.nativeAstNodeId ?? index + 1)}`, usedIds),
4864
+ nativeSourceId: mapping.nativeSourceId ?? input.importResult.nativeSource?.id,
4865
+ nativeAstNodeId: mapping.nativeAstNodeId,
4866
+ semanticSymbolId: mapping.semanticSymbolId,
4867
+ semanticOccurrenceId: mapping.semanticOccurrenceId,
4868
+ semanticNodeId: mapping.semanticNodeId,
4869
+ mergeCandidateId: mapping.mergeCandidateId,
4870
+ sourceSpan: {
4871
+ ...mapping.sourceSpan,
4872
+ sourceId: mapping.sourceSpan.sourceId ?? sourceHash,
4873
+ path: mapping.sourceSpan.path ?? input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath
4874
+ },
4875
+ generatedSpan: nativeSourceCompileGeneratedSpanFromSource(mapping.sourceSpan, input, mapping.generatedName),
4876
+ target: input.target,
4877
+ generatedName: mapping.generatedName,
4878
+ evidenceIds: uniqueStrings([
4879
+ ...(mapping.evidenceIds ?? []),
4880
+ ...(input.evidence ?? []).map((record) => record.id).filter(Boolean)
4881
+ ]),
4882
+ lossIds: uniqueStrings([
4883
+ ...(mapping.lossIds ?? []),
4884
+ ...lossIdsForNativeNode(input.losses ?? [], mapping.nativeAstNodeId)
4885
+ ]),
4886
+ ownershipRegionId: mapping.ownershipRegionId,
4887
+ ownershipRegionKey: mapping.ownershipRegionKey,
4888
+ ownershipRegionKind: mapping.ownershipRegionKind,
4889
+ precision: exact ? 'exact' : mapping.precision === 'exact' ? 'line' : mapping.precision ?? 'line',
4890
+ metadata: {
4891
+ ...mapping.metadata,
4892
+ compileResultId: input.compileResultId,
4893
+ sourceMapOrigin: 'preserved-source'
4894
+ }
4895
+ }));
4896
+ }
4897
+
4898
+ function nativeSourceCompileDeclarationMappings(input) {
4899
+ const usedIds = new Set();
4900
+ return (input.projection.declarations ?? []).map((declaration, index) => {
4901
+ const generated = nativeSourceCompileDeclarationGeneratedSpan(input, declaration);
4902
+ return {
4903
+ id: reserveUniqueId(`compile_map_${idFragment(declaration.symbolId ?? declaration.nativeAstNodeId ?? declaration.name ?? index + 1)}`, usedIds),
4904
+ nativeSourceId: input.importResult.nativeSource?.id,
4905
+ nativeAstNodeId: declaration.nativeAstNodeId,
4906
+ semanticSymbolId: declaration.symbolId,
4907
+ sourceSpan: declaration.sourceSpan,
4908
+ generatedSpan: generated.span,
4909
+ target: input.target,
4910
+ generatedName: generated.name,
4911
+ evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
4912
+ lossIds: lossIdsForNativeNode(input.losses ?? [], declaration.nativeAstNodeId),
4913
+ ownershipRegionId: declaration.ownershipRegionId,
4914
+ ownershipRegionKey: declaration.metadata?.ownershipRegionKey,
4915
+ ownershipRegionKind: declaration.metadata?.ownershipRegionKind,
4916
+ precision: generated.exactName ? 'declaration' : 'estimated',
4917
+ metadata: {
4918
+ ...declaration.metadata,
4919
+ compileResultId: input.compileResultId,
4920
+ declarationKind: declaration.kind,
4921
+ sourceMapOrigin: input.outputMode === 'target-adapter' ? 'target-adapter-fallback' : 'declaration-stub'
4922
+ }
4923
+ };
4924
+ });
4925
+ }
4926
+
4927
+ function nativeSourceCompileFileMapping(input) {
4928
+ const rootSpan = input.importResult.nativeAst?.nodes?.[input.importResult.nativeAst?.rootId]?.span
4929
+ ?? input.importResult.nativeSource?.ast?.nodes?.[input.importResult.nativeSource?.ast?.rootId]?.span
4930
+ ?? input.projection.declarations?.find((declaration) => declaration.sourceSpan)?.sourceSpan;
4931
+ return {
4932
+ id: `compile_map_${idFragment(input.compileResultId ?? input.id)}_file`,
4933
+ nativeSourceId: input.importResult.nativeSource?.id,
4934
+ sourceSpan: rootSpan,
4935
+ generatedSpan: nativeSourceCompileFullGeneratedSpan(input),
4936
+ target: input.target,
4937
+ evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
4938
+ precision: input.projection.mode === 'preserved-source' && input.outputHash === input.projection.sourceHash ? 'line' : 'estimated',
4939
+ metadata: {
4940
+ compileResultId: input.compileResultId,
4941
+ sourceMapOrigin: 'file-fallback'
4942
+ }
4943
+ };
4944
+ }
4945
+
4946
+ function nativeSourceCompileGeneratedSpanFromSource(sourceSpan, input, generatedName) {
4947
+ if (!sourceSpan) return nativeSourceCompileFullGeneratedSpan(input, generatedName);
4948
+ return {
4949
+ ...sourceSpan,
4950
+ sourceId: input.targetHash ?? input.outputHash,
4951
+ path: input.targetPath,
4952
+ target: input.target,
4953
+ targetPath: input.targetPath,
4954
+ targetHash: input.targetHash ?? input.outputHash,
4955
+ generatedName
4956
+ };
4957
+ }
4958
+
4959
+ function nativeSourceCompileDeclarationGeneratedSpan(input, declaration) {
4960
+ const identifiers = uniqueStrings([
4961
+ declaration.name,
4962
+ safeProjectionIdentifier(declaration.name),
4963
+ upperFirst(safeProjectionIdentifier(declaration.name)),
4964
+ safeProjectionIdentifier(declaration.name).toUpperCase()
4965
+ ]).filter(Boolean);
4966
+ for (const identifier of identifiers) {
4967
+ const offset = input.output.indexOf(identifier);
4968
+ if (offset >= 0) {
4969
+ return {
4970
+ name: identifier,
4971
+ exactName: true,
4972
+ span: nativeSourceCompileGeneratedSpanForOffset(input, offset, identifier.length, identifier)
4973
+ };
4974
+ }
4975
+ }
4976
+ return {
4977
+ name: safeProjectionIdentifier(declaration.name),
4978
+ exactName: false,
4979
+ span: nativeSourceCompileFullGeneratedSpan(input, safeProjectionIdentifier(declaration.name))
4980
+ };
4981
+ }
4982
+
4983
+ function nativeSourceCompileGeneratedSpanForOffset(input, offset, length, generatedName) {
4984
+ const start = lineColumnForOffset(input.output, offset);
4985
+ const end = lineColumnForOffset(input.output, offset + Math.max(1, length));
4986
+ return {
4987
+ sourceId: input.targetHash ?? input.outputHash,
4988
+ path: input.targetPath,
4989
+ startLine: start.line,
4990
+ startColumn: start.column,
4991
+ endLine: end.line,
4992
+ endColumn: end.column,
4993
+ target: input.target,
4994
+ targetPath: input.targetPath,
4995
+ targetHash: input.targetHash ?? input.outputHash,
4996
+ generatedName
4997
+ };
4998
+ }
4999
+
5000
+ function nativeSourceCompileFullGeneratedSpan(input, generatedName) {
5001
+ const lines = String(input.output ?? '').split(/\r?\n/);
5002
+ const lastLine = lines.at(-1) ?? '';
5003
+ return {
5004
+ sourceId: input.targetHash ?? input.outputHash,
5005
+ path: input.targetPath,
5006
+ startLine: 1,
5007
+ startColumn: 1,
5008
+ endLine: Math.max(1, lines.length),
5009
+ endColumn: Math.max(1, lastLine.length + 1),
5010
+ target: input.target,
5011
+ targetPath: input.targetPath,
5012
+ targetHash: input.targetHash ?? input.outputHash,
5013
+ generatedName
5014
+ };
5015
+ }
5016
+
5017
+ function nativeSourceCompileTargetPath(input) {
5018
+ if (input.targetPath) return input.targetPath;
5019
+ const sourcePath = input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath;
5020
+ if (!sourcePath) return undefined;
5021
+ const targetExt = nativeSourceCompileTargetExtension(input.target);
5022
+ if (!targetExt) return sourcePath;
5023
+ return sourcePath.replace(/(\.[^./\\]+)?$/, targetExt);
5024
+ }
5025
+
5026
+ function nativeSourceCompileTargetExtension(target) {
5027
+ const normalized = normalizeNativeLanguageId(target);
5028
+ if (normalized === 'typescript') return '.ts';
5029
+ if (normalized === 'javascript') return '.js';
5030
+ if (normalized === 'rust') return '.rs';
5031
+ if (normalized === 'python') return '.py';
5032
+ if (normalized === 'c') return '.h';
5033
+ return undefined;
5034
+ }
5035
+
5036
+ function nativeSourceCompileMapTarget(input, targetPath) {
5037
+ return {
5038
+ language: input.target,
5039
+ emitPath: targetPath
5040
+ };
5041
+ }
5042
+
5043
+ function lineColumnForOffset(source, offset) {
5044
+ const text = String(source ?? '');
5045
+ const safeOffset = Math.max(0, Math.min(offset, text.length));
5046
+ let line = 1;
5047
+ let column = 1;
5048
+ for (let index = 0; index < safeOffset; index += 1) {
5049
+ if (text[index] === '\n') {
5050
+ line += 1;
5051
+ column = 1;
5052
+ } else {
5053
+ column += 1;
5054
+ }
5055
+ }
5056
+ return { line, column };
5057
+ }
5058
+
4547
5059
  function nativeProjectionTargetsForLanguage(language, aliases = []) {
4548
5060
  const target = nativeLanguageCompileTarget(language, aliases);
4549
5061
  return target ? [target] : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.20",
3
+ "version": "0.2.22",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",