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

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
@@ -183,15 +183,18 @@ console.log(compiledJs.readiness.readiness); // scanner imports can still be "ne
183
183
 
184
184
  const rustCandidate = compileNativeSource(compiledJs.importResult, {
185
185
  target: 'rust',
186
+ targetPath: 'dist/runtime.rs',
186
187
  emitOnBlocked: true
187
188
  });
188
189
 
189
190
  console.log(rustCandidate.outputMode); // "target-stubs"
190
191
  console.log(rustCandidate.targetCoverage.lossClass); // "missingAdapter" without a JS-to-Rust adapter
191
192
  console.log(rustCandidate.ok); // true only because emitOnBlocked requested code anyway
193
+ console.log(rustCandidate.sourceMap.targetPath); // "dist/runtime.rs"
194
+ console.log(rustCandidate.sourceMap.mappings[0]?.semanticSymbolId); // generated span -> source symbol
192
195
  ```
193
196
 
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.
197
+ `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
198
 
196
199
  Provide a target projection adapter when the host owns real native-to-target translation semantics:
197
200
 
@@ -220,12 +223,14 @@ const jsToRustAdapter = {
220
223
 
221
224
  const rustWithAdapter = compileNativeSource(compiledJs.importResult, {
222
225
  target: 'rust',
226
+ targetPath: 'dist/runtime.rs',
223
227
  targetAdapters: [jsToRustAdapter]
224
228
  });
225
229
 
226
230
  console.log(rustWithAdapter.outputMode); // "target-adapter"
227
231
  console.log(rustWithAdapter.targetCoverage.lossClass); // "targetAdapterProjection"
228
232
  console.log(rustWithAdapter.targetProjection.adapter.id); // "app-js-to-rust"
233
+ console.log(rustWithAdapter.sourceMaps.length); // adapter maps or compiler fallback map
229
234
  ```
230
235
 
231
236
  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
@@ -100,6 +100,8 @@ const nativeCompiles = nativeImportResults.map((imported, index) => compileNativ
100
100
  }));
101
101
  const nativeCompileDurationMs = performance.now() - nativeCompileStart;
102
102
  const nativeCompileBytes = nativeCompiles.reduce((sum, result) => sum + result.output.length, 0);
103
+ const nativeCompileSourceMaps = nativeCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
104
+ const nativeCompileSourceMapMappings = nativeCompiles.reduce((sum, result) => sum + result.sourceMaps.reduce((mapSum, sourceMap) => mapSum + sourceMap.mappings.length, 0), 0);
103
105
  const nativeCompileBlocked = nativeCompiles.filter((result) => result.readiness.readiness === 'blocked').length;
104
106
  const nativeTargetAdapterStart = performance.now();
105
107
  const nativeTargetAdapterCompiles = nativeImportResults.slice(0, 25).map((imported, index) => {
@@ -122,6 +124,7 @@ const nativeTargetAdapterCompiles = nativeImportResults.slice(0, 25).map((import
122
124
  });
123
125
  const nativeTargetAdapterDurationMs = performance.now() - nativeTargetAdapterStart;
124
126
  const nativeTargetAdapterBytes = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.output.length, 0);
127
+ const nativeTargetAdapterSourceMaps = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
125
128
 
126
129
  console.log(JSON.stringify({
127
130
  compiles: 250,
@@ -152,9 +155,12 @@ console.log(JSON.stringify({
152
155
  projectionDurationMs: Number(projectionDurationMs.toFixed(2)),
153
156
  nativeCompiles: nativeCompiles.length,
154
157
  nativeCompileBytes,
158
+ nativeCompileSourceMaps,
159
+ nativeCompileSourceMapMappings,
155
160
  nativeCompileBlocked,
156
161
  nativeCompileDurationMs: Number(nativeCompileDurationMs.toFixed(2)),
157
162
  nativeTargetAdapterCompiles: nativeTargetAdapterCompiles.length,
158
163
  nativeTargetAdapterBytes,
164
+ nativeTargetAdapterSourceMaps,
159
165
  nativeTargetAdapterDurationMs: Number(nativeTargetAdapterDurationMs.toFixed(2))
160
166
  }));
package/dist/index.d.ts CHANGED
@@ -1331,6 +1331,10 @@ export interface CompileNativeSourceOptions extends ProjectNativeImportToSourceO
1331
1331
  readonly languages?: readonly NativeImportLanguageProfile[];
1332
1332
  readonly generatedAt?: number;
1333
1333
  readonly emitOnBlocked?: boolean;
1334
+ readonly emitSourceMap?: boolean;
1335
+ readonly targetPath?: string;
1336
+ readonly targetHash?: string;
1337
+ readonly sourceMapId?: string;
1334
1338
  readonly projectionId?: string;
1335
1339
  readonly projectionEvidenceId?: string;
1336
1340
  readonly compileEvidenceId?: string;
@@ -1349,6 +1353,8 @@ export interface NativeSourceCompileResult {
1349
1353
  readonly output: string;
1350
1354
  readonly outputHash: string;
1351
1355
  readonly outputMode: NativeSourceCompileOutputMode;
1356
+ readonly sourceMap?: SourceMapRecord;
1357
+ readonly sourceMaps: readonly SourceMapRecord[];
1352
1358
  readonly importResult: NativeSourceImportResult;
1353
1359
  readonly projection: NativeSourceProjectionResult;
1354
1360
  readonly targetProjection?: NativeTargetProjectionResult;
package/dist/index.js CHANGED
@@ -359,6 +359,25 @@ export function compileNativeSource(input, options = {}) {
359
359
  scanKind: 'native-source-compile',
360
360
  semanticStatus: importResult.metadata?.semanticStatus ?? options.semanticStatus
361
361
  });
362
+ const sourceMaps = options.emitSourceMap === false
363
+ ? []
364
+ : nativeSourceCompileSourceMaps({
365
+ id: options.sourceMapId ?? `source_map_${idFragment(id)}_${idFragment(target)}`,
366
+ importResult,
367
+ projection,
368
+ targetProjection,
369
+ sourceLanguage,
370
+ target,
371
+ targetPath: options.targetPath ?? targetProjection?.targetPath,
372
+ targetHash: options.targetHash ?? outputHash,
373
+ output,
374
+ outputHash,
375
+ outputMode,
376
+ evidence,
377
+ losses,
378
+ compileResultId: id
379
+ });
380
+ const sourceMap = sourceMaps[0];
362
381
  return {
363
382
  kind: 'frontier.lang.nativeSourceCompileResult',
364
383
  version: 1,
@@ -370,6 +389,8 @@ export function compileNativeSource(input, options = {}) {
370
389
  output,
371
390
  outputHash,
372
391
  outputMode,
392
+ sourceMap,
393
+ sourceMaps,
373
394
  importResult,
374
395
  projection,
375
396
  targetProjection,
@@ -388,8 +409,13 @@ export function compileNativeSource(input, options = {}) {
388
409
  projectionId: projection.id,
389
410
  targetProjectionId: targetProjection?.id,
390
411
  targetProjectionAdapterId: targetProjection?.adapter?.id,
412
+ sourceMapId: sourceMap?.id,
413
+ sourceMapIds: sourceMaps.map((record) => record.id).filter(Boolean),
414
+ sourceMapMappings: sourceMaps.reduce((sum, record) => sum + (record.mappings?.length ?? 0), 0),
391
415
  projectionMode: projection.mode,
392
416
  outputMode,
417
+ targetPath: sourceMap?.targetPath ?? options.targetPath ?? targetProjection?.targetPath,
418
+ targetHash: sourceMap?.targetHash ?? options.targetHash ?? outputHash,
393
419
  sourceTarget,
394
420
  sameSourceTarget,
395
421
  targetLossClass: targetCoverage.lossClass,
@@ -4544,6 +4570,251 @@ function nativeSourceCompileEvidence(input) {
4544
4570
  };
4545
4571
  }
4546
4572
 
4573
+ function nativeSourceCompileSourceMaps(input) {
4574
+ const adapterSourceMaps = input.targetProjection?.sourceMaps ?? [];
4575
+ if (adapterSourceMaps.length) return adapterSourceMaps;
4576
+ const targetPath = nativeSourceCompileTargetPath(input);
4577
+ const targetHash = input.targetHash ?? input.outputHash;
4578
+ const target = nativeSourceCompileMapTarget(input, targetPath);
4579
+ const mappings = input.projection.mode === 'preserved-source'
4580
+ ? nativeSourceCompilePreservedMappings({ ...input, targetPath, targetHash, target })
4581
+ : nativeSourceCompileDeclarationMappings({ ...input, targetPath, targetHash, target });
4582
+ const resolvedMappings = mappings.length
4583
+ ? mappings
4584
+ : [nativeSourceCompileFileMapping({ ...input, targetPath, targetHash, target })];
4585
+ return [createSourceMapRecord({
4586
+ id: input.id,
4587
+ sourcePath: input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath,
4588
+ sourceHash: input.importResult.nativeSource?.sourceHash ?? input.importResult.nativeAst?.sourceHash ?? input.importResult.sourceHash,
4589
+ target,
4590
+ targetPath: targetPath ?? commonGeneratedTargetPath(resolvedMappings),
4591
+ targetHash,
4592
+ semanticIndexId: input.importResult.semanticIndex?.id ?? input.importResult.universalAst?.semanticIndex?.id,
4593
+ universalAstId: input.importResult.universalAst?.id,
4594
+ nativeAstId: input.importResult.nativeAst?.id ?? input.importResult.nativeSource?.ast?.id,
4595
+ nativeSourceId: input.importResult.nativeSource?.id,
4596
+ mappings: resolvedMappings,
4597
+ evidence: input.evidence ?? [],
4598
+ metadata: {
4599
+ compileResultId: input.compileResultId,
4600
+ importId: input.importResult.id,
4601
+ projectionId: input.projection.id,
4602
+ targetProjectionId: input.targetProjection?.id,
4603
+ targetProjectionAdapterId: input.targetProjection?.adapter?.id,
4604
+ sourceLanguage: input.sourceLanguage,
4605
+ target: input.target,
4606
+ outputMode: input.outputMode,
4607
+ outputHash: input.outputHash,
4608
+ generatedBy: 'compileNativeSource'
4609
+ }
4610
+ })];
4611
+ }
4612
+
4613
+ function nativeSourceCompilePreservedMappings(input) {
4614
+ const sourceMaps = input.importResult.sourceMaps ?? input.importResult.universalAst?.sourceMaps ?? [];
4615
+ const sourceHash = input.importResult.nativeSource?.sourceHash ?? input.importResult.nativeAst?.sourceHash ?? input.importResult.sourceHash;
4616
+ const exact = input.projection.mode === 'preserved-source' && (!input.projection.sourceHash || input.outputHash === input.projection.sourceHash);
4617
+ const usedIds = new Set();
4618
+ return sourceMaps
4619
+ .flatMap((sourceMap) => sourceMap?.mappings ?? [])
4620
+ .filter((mapping) => mapping?.sourceSpan)
4621
+ .map((mapping, index) => ({
4622
+ id: reserveUniqueId(`compile_map_${idFragment(mapping.id ?? mapping.semanticSymbolId ?? mapping.nativeAstNodeId ?? index + 1)}`, usedIds),
4623
+ nativeSourceId: mapping.nativeSourceId ?? input.importResult.nativeSource?.id,
4624
+ nativeAstNodeId: mapping.nativeAstNodeId,
4625
+ semanticSymbolId: mapping.semanticSymbolId,
4626
+ semanticOccurrenceId: mapping.semanticOccurrenceId,
4627
+ semanticNodeId: mapping.semanticNodeId,
4628
+ mergeCandidateId: mapping.mergeCandidateId,
4629
+ sourceSpan: {
4630
+ ...mapping.sourceSpan,
4631
+ sourceId: mapping.sourceSpan.sourceId ?? sourceHash,
4632
+ path: mapping.sourceSpan.path ?? input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath
4633
+ },
4634
+ generatedSpan: nativeSourceCompileGeneratedSpanFromSource(mapping.sourceSpan, input, mapping.generatedName),
4635
+ target: input.target,
4636
+ generatedName: mapping.generatedName,
4637
+ evidenceIds: uniqueStrings([
4638
+ ...(mapping.evidenceIds ?? []),
4639
+ ...(input.evidence ?? []).map((record) => record.id).filter(Boolean)
4640
+ ]),
4641
+ lossIds: uniqueStrings([
4642
+ ...(mapping.lossIds ?? []),
4643
+ ...lossIdsForNativeNode(input.losses ?? [], mapping.nativeAstNodeId)
4644
+ ]),
4645
+ ownershipRegionId: mapping.ownershipRegionId,
4646
+ ownershipRegionKey: mapping.ownershipRegionKey,
4647
+ ownershipRegionKind: mapping.ownershipRegionKind,
4648
+ precision: exact ? 'exact' : mapping.precision === 'exact' ? 'line' : mapping.precision ?? 'line',
4649
+ metadata: {
4650
+ ...mapping.metadata,
4651
+ compileResultId: input.compileResultId,
4652
+ sourceMapOrigin: 'preserved-source'
4653
+ }
4654
+ }));
4655
+ }
4656
+
4657
+ function nativeSourceCompileDeclarationMappings(input) {
4658
+ const usedIds = new Set();
4659
+ return (input.projection.declarations ?? []).map((declaration, index) => {
4660
+ const generated = nativeSourceCompileDeclarationGeneratedSpan(input, declaration);
4661
+ return {
4662
+ id: reserveUniqueId(`compile_map_${idFragment(declaration.symbolId ?? declaration.nativeAstNodeId ?? declaration.name ?? index + 1)}`, usedIds),
4663
+ nativeSourceId: input.importResult.nativeSource?.id,
4664
+ nativeAstNodeId: declaration.nativeAstNodeId,
4665
+ semanticSymbolId: declaration.symbolId,
4666
+ sourceSpan: declaration.sourceSpan,
4667
+ generatedSpan: generated.span,
4668
+ target: input.target,
4669
+ generatedName: generated.name,
4670
+ evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
4671
+ lossIds: lossIdsForNativeNode(input.losses ?? [], declaration.nativeAstNodeId),
4672
+ ownershipRegionId: declaration.ownershipRegionId,
4673
+ ownershipRegionKey: declaration.metadata?.ownershipRegionKey,
4674
+ ownershipRegionKind: declaration.metadata?.ownershipRegionKind,
4675
+ precision: generated.exactName ? 'declaration' : 'estimated',
4676
+ metadata: {
4677
+ ...declaration.metadata,
4678
+ compileResultId: input.compileResultId,
4679
+ declarationKind: declaration.kind,
4680
+ sourceMapOrigin: input.outputMode === 'target-adapter' ? 'target-adapter-fallback' : 'declaration-stub'
4681
+ }
4682
+ };
4683
+ });
4684
+ }
4685
+
4686
+ function nativeSourceCompileFileMapping(input) {
4687
+ const rootSpan = input.importResult.nativeAst?.nodes?.[input.importResult.nativeAst?.rootId]?.span
4688
+ ?? input.importResult.nativeSource?.ast?.nodes?.[input.importResult.nativeSource?.ast?.rootId]?.span
4689
+ ?? input.projection.declarations?.find((declaration) => declaration.sourceSpan)?.sourceSpan;
4690
+ return {
4691
+ id: `compile_map_${idFragment(input.compileResultId ?? input.id)}_file`,
4692
+ nativeSourceId: input.importResult.nativeSource?.id,
4693
+ sourceSpan: rootSpan,
4694
+ generatedSpan: nativeSourceCompileFullGeneratedSpan(input),
4695
+ target: input.target,
4696
+ evidenceIds: (input.evidence ?? []).map((record) => record.id).filter(Boolean),
4697
+ precision: input.projection.mode === 'preserved-source' && input.outputHash === input.projection.sourceHash ? 'line' : 'estimated',
4698
+ metadata: {
4699
+ compileResultId: input.compileResultId,
4700
+ sourceMapOrigin: 'file-fallback'
4701
+ }
4702
+ };
4703
+ }
4704
+
4705
+ function nativeSourceCompileGeneratedSpanFromSource(sourceSpan, input, generatedName) {
4706
+ if (!sourceSpan) return nativeSourceCompileFullGeneratedSpan(input, generatedName);
4707
+ return {
4708
+ ...sourceSpan,
4709
+ sourceId: input.targetHash ?? input.outputHash,
4710
+ path: input.targetPath,
4711
+ target: input.target,
4712
+ targetPath: input.targetPath,
4713
+ targetHash: input.targetHash ?? input.outputHash,
4714
+ generatedName
4715
+ };
4716
+ }
4717
+
4718
+ function nativeSourceCompileDeclarationGeneratedSpan(input, declaration) {
4719
+ const identifiers = uniqueStrings([
4720
+ declaration.name,
4721
+ safeProjectionIdentifier(declaration.name),
4722
+ upperFirst(safeProjectionIdentifier(declaration.name)),
4723
+ safeProjectionIdentifier(declaration.name).toUpperCase()
4724
+ ]).filter(Boolean);
4725
+ for (const identifier of identifiers) {
4726
+ const offset = input.output.indexOf(identifier);
4727
+ if (offset >= 0) {
4728
+ return {
4729
+ name: identifier,
4730
+ exactName: true,
4731
+ span: nativeSourceCompileGeneratedSpanForOffset(input, offset, identifier.length, identifier)
4732
+ };
4733
+ }
4734
+ }
4735
+ return {
4736
+ name: safeProjectionIdentifier(declaration.name),
4737
+ exactName: false,
4738
+ span: nativeSourceCompileFullGeneratedSpan(input, safeProjectionIdentifier(declaration.name))
4739
+ };
4740
+ }
4741
+
4742
+ function nativeSourceCompileGeneratedSpanForOffset(input, offset, length, generatedName) {
4743
+ const start = lineColumnForOffset(input.output, offset);
4744
+ const end = lineColumnForOffset(input.output, offset + Math.max(1, length));
4745
+ return {
4746
+ sourceId: input.targetHash ?? input.outputHash,
4747
+ path: input.targetPath,
4748
+ startLine: start.line,
4749
+ startColumn: start.column,
4750
+ endLine: end.line,
4751
+ endColumn: end.column,
4752
+ target: input.target,
4753
+ targetPath: input.targetPath,
4754
+ targetHash: input.targetHash ?? input.outputHash,
4755
+ generatedName
4756
+ };
4757
+ }
4758
+
4759
+ function nativeSourceCompileFullGeneratedSpan(input, generatedName) {
4760
+ const lines = String(input.output ?? '').split(/\r?\n/);
4761
+ const lastLine = lines.at(-1) ?? '';
4762
+ return {
4763
+ sourceId: input.targetHash ?? input.outputHash,
4764
+ path: input.targetPath,
4765
+ startLine: 1,
4766
+ startColumn: 1,
4767
+ endLine: Math.max(1, lines.length),
4768
+ endColumn: Math.max(1, lastLine.length + 1),
4769
+ target: input.target,
4770
+ targetPath: input.targetPath,
4771
+ targetHash: input.targetHash ?? input.outputHash,
4772
+ generatedName
4773
+ };
4774
+ }
4775
+
4776
+ function nativeSourceCompileTargetPath(input) {
4777
+ if (input.targetPath) return input.targetPath;
4778
+ const sourcePath = input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath;
4779
+ if (!sourcePath) return undefined;
4780
+ const targetExt = nativeSourceCompileTargetExtension(input.target);
4781
+ if (!targetExt) return sourcePath;
4782
+ return sourcePath.replace(/(\.[^./\\]+)?$/, targetExt);
4783
+ }
4784
+
4785
+ function nativeSourceCompileTargetExtension(target) {
4786
+ const normalized = normalizeNativeLanguageId(target);
4787
+ if (normalized === 'typescript') return '.ts';
4788
+ if (normalized === 'javascript') return '.js';
4789
+ if (normalized === 'rust') return '.rs';
4790
+ if (normalized === 'python') return '.py';
4791
+ if (normalized === 'c') return '.h';
4792
+ return undefined;
4793
+ }
4794
+
4795
+ function nativeSourceCompileMapTarget(input, targetPath) {
4796
+ return {
4797
+ language: input.target,
4798
+ emitPath: targetPath
4799
+ };
4800
+ }
4801
+
4802
+ function lineColumnForOffset(source, offset) {
4803
+ const text = String(source ?? '');
4804
+ const safeOffset = Math.max(0, Math.min(offset, text.length));
4805
+ let line = 1;
4806
+ let column = 1;
4807
+ for (let index = 0; index < safeOffset; index += 1) {
4808
+ if (text[index] === '\n') {
4809
+ line += 1;
4810
+ column = 1;
4811
+ } else {
4812
+ column += 1;
4813
+ }
4814
+ }
4815
+ return { line, column };
4816
+ }
4817
+
4547
4818
  function nativeProjectionTargetsForLanguage(language, aliases = []) {
4548
4819
  const target = nativeLanguageCompileTarget(language, aliases);
4549
4820
  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.21",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",