@shapeshift-labs/frontier-lang-compiler 0.2.19 → 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
@@ -93,11 +93,12 @@ console.log(pythonProjection.sourceProjection.stubs.lossClass); // "nativeSource
93
93
  console.log(pythonProjection.targets.find((entry) => entry.target === 'rust').lossClass); // "missingAdapter"
94
94
  ```
95
95
 
96
- The projection target matrix separates four runtime/API classes:
96
+ The projection target matrix separates five runtime/API classes:
97
97
 
98
98
  - `exactSourceProjection`: exact source can be emitted when the import carries matching source text or source-preservation evidence.
99
99
  - `nativeSourceStubs`: declaration stubs can be emitted, but bodies, resolved types, and executable semantics are review-required.
100
100
  - `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.
101
+ - `targetAdapterProjection`: a host-owned native-to-target adapter is present and produced target output with its own evidence/readiness.
101
102
  - `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.
102
103
 
103
104
  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:
@@ -182,15 +183,55 @@ console.log(compiledJs.readiness.readiness); // scanner imports can still be "ne
182
183
 
183
184
  const rustCandidate = compileNativeSource(compiledJs.importResult, {
184
185
  target: 'rust',
186
+ targetPath: 'dist/runtime.rs',
185
187
  emitOnBlocked: true
186
188
  });
187
189
 
188
190
  console.log(rustCandidate.outputMode); // "target-stubs"
189
191
  console.log(rustCandidate.targetCoverage.lossClass); // "missingAdapter" without a JS-to-Rust adapter
190
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
191
195
  ```
192
196
 
193
- `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.
198
+
199
+ Provide a target projection adapter when the host owns real native-to-target translation semantics:
200
+
201
+ ```js
202
+ const jsToRustAdapter = {
203
+ id: 'app-js-to-rust',
204
+ sourceLanguage: 'javascript',
205
+ target: 'rust',
206
+ coverage: {
207
+ readiness: 'needs-review',
208
+ handledLossKinds: ['dynamicRuntime']
209
+ },
210
+ project(input) {
211
+ return {
212
+ output: `// projected from ${input.sourceLanguage}\npub fn add_todo() {}\n`,
213
+ readiness: 'needs-review',
214
+ evidence: [{
215
+ id: 'evidence_app_js_to_rust',
216
+ kind: 'projection',
217
+ status: 'passed',
218
+ summary: 'Host JS-to-Rust adapter emitted declaration-compatible Rust.'
219
+ }]
220
+ };
221
+ }
222
+ };
223
+
224
+ const rustWithAdapter = compileNativeSource(compiledJs.importResult, {
225
+ target: 'rust',
226
+ targetPath: 'dist/runtime.rs',
227
+ targetAdapters: [jsToRustAdapter]
228
+ });
229
+
230
+ console.log(rustWithAdapter.outputMode); // "target-adapter"
231
+ console.log(rustWithAdapter.targetCoverage.lossClass); // "targetAdapterProjection"
232
+ console.log(rustWithAdapter.targetProjection.adapter.id); // "app-js-to-rust"
233
+ console.log(rustWithAdapter.sourceMaps.length); // adapter maps or compiler fallback map
234
+ ```
194
235
 
195
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:
196
237
 
package/bench/smoke.mjs CHANGED
@@ -100,7 +100,31 @@ 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;
106
+ const nativeTargetAdapterStart = performance.now();
107
+ const nativeTargetAdapterCompiles = nativeImportResults.slice(0, 25).map((imported, index) => {
108
+ const target = index % 2 === 0 ? 'rust' : 'python';
109
+ return compileNativeSource(imported, {
110
+ target,
111
+ targetAdapters: [{
112
+ id: `bench-target-adapter-${index}`,
113
+ sourceLanguage: imported.language,
114
+ target,
115
+ coverage: {
116
+ readiness: 'needs-review',
117
+ handledLossKinds: ['dynamicRuntime', 'dynamicDispatch', 'typeInference', 'overloadResolution']
118
+ },
119
+ project() {
120
+ return { output: `// bench target adapter ${index}\n`, readiness: 'needs-review' };
121
+ }
122
+ }]
123
+ });
124
+ });
125
+ const nativeTargetAdapterDurationMs = performance.now() - nativeTargetAdapterStart;
126
+ const nativeTargetAdapterBytes = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.output.length, 0);
127
+ const nativeTargetAdapterSourceMaps = nativeTargetAdapterCompiles.reduce((sum, result) => sum + result.sourceMaps.length, 0);
104
128
 
105
129
  console.log(JSON.stringify({
106
130
  compiles: 250,
@@ -131,6 +155,12 @@ console.log(JSON.stringify({
131
155
  projectionDurationMs: Number(projectionDurationMs.toFixed(2)),
132
156
  nativeCompiles: nativeCompiles.length,
133
157
  nativeCompileBytes,
158
+ nativeCompileSourceMaps,
159
+ nativeCompileSourceMapMappings,
134
160
  nativeCompileBlocked,
135
- nativeCompileDurationMs: Number(nativeCompileDurationMs.toFixed(2))
161
+ nativeCompileDurationMs: Number(nativeCompileDurationMs.toFixed(2)),
162
+ nativeTargetAdapterCompiles: nativeTargetAdapterCompiles.length,
163
+ nativeTargetAdapterBytes,
164
+ nativeTargetAdapterSourceMaps,
165
+ nativeTargetAdapterDurationMs: Number(nativeTargetAdapterDurationMs.toFixed(2))
136
166
  }));
package/dist/index.d.ts CHANGED
@@ -271,6 +271,7 @@ export type ProjectionTargetLossClass =
271
271
  | 'exactSourceProjection'
272
272
  | 'nativeSourceStubs'
273
273
  | 'unsupportedTargetFeatures'
274
+ | 'targetAdapterProjection'
274
275
  | 'missingAdapter'
275
276
  | string;
276
277
 
@@ -299,6 +300,9 @@ export interface ProjectionTargetCoverageEntry {
299
300
  readonly categories: readonly NativeImportTaxonomyKind[];
300
301
  readonly reason: string;
301
302
  readonly adapter?: string;
303
+ readonly adapterKind?: 'importer' | 'targetProjection' | string;
304
+ readonly adapterVersion?: string;
305
+ readonly adapterCoverage?: NativeTargetProjectionAdapterCoverageInput;
302
306
  readonly notes: readonly string[];
303
307
  }
304
308
 
@@ -339,6 +343,7 @@ export interface ProjectionTargetLossMatrix {
339
343
  readonly sourceProjectionByLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
340
344
  readonly exactSourceProjection: number;
341
345
  readonly nativeSourceStubs: number;
346
+ readonly targetAdapterProjection: number;
342
347
  readonly unsupportedTargetFeatures: number;
343
348
  readonly missingAdapters: number;
344
349
  };
@@ -353,6 +358,7 @@ export interface ProjectionTargetLossMatrixOptions {
353
358
  readonly languages?: readonly NativeImportLanguageProfile[];
354
359
  readonly imports?: readonly NativeSourceImportResult[];
355
360
  readonly adapters?: readonly NativeImporterAdapter[];
361
+ readonly targetAdapters?: readonly NativeTargetProjectionAdapter[];
356
362
  readonly targets?: readonly (FrontierCompileTarget | string)[];
357
363
  readonly generatedAt?: number;
358
364
  }
@@ -1124,6 +1130,94 @@ export type NativeImporterAdapterImportResult = NativeSourceImportResult & {
1124
1130
  readonly diagnostics: readonly NativeImporterAdapterDiagnostic[];
1125
1131
  };
1126
1132
 
1133
+ export interface NativeTargetProjectionAdapterCoverageInput {
1134
+ readonly readiness?: SemanticMergeReadiness;
1135
+ readonly lossKinds?: readonly NativeImportKnownLossKind[];
1136
+ readonly handledLossKinds?: readonly NativeImportKnownLossKind[];
1137
+ readonly sourceMapPrecision?: SourceMapMappingRecord['precision'] | 'none' | string;
1138
+ readonly semanticCoverage?: Partial<NativeImporterAdapterSemanticCoverage>;
1139
+ readonly notes?: readonly string[];
1140
+ }
1141
+
1142
+ export interface NativeTargetProjectionAdapterSummary {
1143
+ readonly id: string;
1144
+ readonly sourceLanguage: FrontierSourceLanguage | string;
1145
+ readonly target: FrontierCompileTarget | string;
1146
+ readonly version?: string;
1147
+ readonly capabilities: readonly string[];
1148
+ readonly supportedParsers: readonly string[];
1149
+ readonly supportedExtensions: readonly string[];
1150
+ readonly coverage: Required<Pick<NativeTargetProjectionAdapterCoverageInput, 'readiness' | 'lossKinds' | 'handledLossKinds' | 'notes'>> & NativeTargetProjectionAdapterCoverageInput;
1151
+ readonly diagnostics: readonly NativeImporterAdapterDiagnostic[];
1152
+ }
1153
+
1154
+ export interface NativeTargetProjectionAdapterInput {
1155
+ readonly importResult: NativeSourceImportResult;
1156
+ readonly sourceProjection: NativeSourceProjectionResult;
1157
+ readonly sourceLanguage: FrontierSourceLanguage | string;
1158
+ readonly target: FrontierCompileTarget | string;
1159
+ readonly targetPath?: string;
1160
+ readonly targetCoverage?: ProjectionTargetCoverageEntry;
1161
+ readonly options: Record<string, unknown>;
1162
+ readonly metadata: Record<string, unknown>;
1163
+ }
1164
+
1165
+ export interface NativeTargetProjectionAdapterResult {
1166
+ readonly id?: string;
1167
+ readonly output?: string;
1168
+ readonly outputHash?: string;
1169
+ readonly sourceMaps?: readonly SourceMapRecord[];
1170
+ readonly losses?: readonly NativeAstLossRecord[];
1171
+ readonly evidence?: readonly EvidenceRecord[];
1172
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
1173
+ readonly readiness?: SemanticMergeReadiness;
1174
+ readonly metadata?: Record<string, unknown>;
1175
+ }
1176
+
1177
+ export interface NativeTargetProjectionAdapter {
1178
+ readonly id: string;
1179
+ readonly sourceLanguage?: FrontierSourceLanguage | string;
1180
+ readonly language?: FrontierSourceLanguage | string;
1181
+ readonly target: FrontierCompileTarget | string;
1182
+ readonly targetLanguage?: FrontierCompileTarget | string;
1183
+ readonly version?: string;
1184
+ readonly capabilities?: readonly string[];
1185
+ readonly supportedParsers?: readonly string[];
1186
+ readonly supportedExtensions?: readonly string[];
1187
+ readonly coverage?: NativeTargetProjectionAdapterCoverageInput;
1188
+ readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
1189
+ readonly project: (input: NativeTargetProjectionAdapterInput) => NativeTargetProjectionAdapterResult;
1190
+ }
1191
+
1192
+ export interface NativeTargetProjectionAdapterResolverInput {
1193
+ readonly importResult: NativeSourceImportResult;
1194
+ readonly sourceProjection: NativeSourceProjectionResult;
1195
+ readonly sourceLanguage: FrontierSourceLanguage | string;
1196
+ readonly target: FrontierCompileTarget | string;
1197
+ readonly sourcePath?: string;
1198
+ readonly parser?: string;
1199
+ }
1200
+
1201
+ export interface NativeTargetProjectionResult {
1202
+ readonly kind: 'frontier.lang.nativeTargetProjection';
1203
+ readonly version: 1;
1204
+ readonly id: string;
1205
+ readonly sourceLanguage: FrontierSourceLanguage | string;
1206
+ readonly target: FrontierCompileTarget | string;
1207
+ readonly targetPath?: string;
1208
+ readonly adapter: NativeTargetProjectionAdapterSummary;
1209
+ readonly output: string;
1210
+ readonly outputHash: string;
1211
+ readonly outputMode: 'target-adapter';
1212
+ readonly sourceMaps: readonly SourceMapRecord[];
1213
+ readonly losses: readonly NativeAstLossRecord[];
1214
+ readonly lossSummary: NativeImportLossSummary;
1215
+ readonly readiness: NativeImportReadinessClassification;
1216
+ readonly evidence: readonly EvidenceRecord[];
1217
+ readonly diagnostics: readonly NativeImporterAdapterDiagnostic[];
1218
+ readonly metadata: Record<string, unknown>;
1219
+ }
1220
+
1127
1221
  export interface NativeProjectSourceInput extends ImportNativeSourceOptions {
1128
1222
  readonly adapter?: NativeImporterAdapter | string;
1129
1223
  readonly adapterOptions?: Record<string, unknown>;
@@ -1221,14 +1315,26 @@ export interface NativeSourceProjectionResult {
1221
1315
  readonly metadata: Record<string, unknown>;
1222
1316
  }
1223
1317
 
1224
- export type NativeSourceCompileOutputMode = NativeSourceProjectionMode | 'target-stubs';
1318
+ export type NativeSourceCompileOutputMode = NativeSourceProjectionMode | 'target-stubs' | 'target-adapter';
1225
1319
 
1226
1320
  export interface CompileNativeSourceOptions extends ProjectNativeImportToSourceOptions {
1227
1321
  readonly target?: FrontierCompileTarget | string;
1228
1322
  readonly adapters?: readonly NativeImporterAdapter[];
1323
+ readonly targetAdapters?: readonly NativeTargetProjectionAdapter[];
1324
+ readonly targetAdapter?: NativeTargetProjectionAdapter | string;
1325
+ readonly targetAdapterResolver?: (
1326
+ input: NativeTargetProjectionAdapterResolverInput,
1327
+ adapters: readonly NativeTargetProjectionAdapter[]
1328
+ ) => NativeTargetProjectionAdapter | undefined;
1329
+ readonly targetAdapterOptions?: Record<string, unknown>;
1330
+ readonly targetAdapterMetadata?: Record<string, unknown>;
1229
1331
  readonly languages?: readonly NativeImportLanguageProfile[];
1230
1332
  readonly generatedAt?: number;
1231
1333
  readonly emitOnBlocked?: boolean;
1334
+ readonly emitSourceMap?: boolean;
1335
+ readonly targetPath?: string;
1336
+ readonly targetHash?: string;
1337
+ readonly sourceMapId?: string;
1232
1338
  readonly projectionId?: string;
1233
1339
  readonly projectionEvidenceId?: string;
1234
1340
  readonly compileEvidenceId?: string;
@@ -1247,8 +1353,11 @@ export interface NativeSourceCompileResult {
1247
1353
  readonly output: string;
1248
1354
  readonly outputHash: string;
1249
1355
  readonly outputMode: NativeSourceCompileOutputMode;
1356
+ readonly sourceMap?: SourceMapRecord;
1357
+ readonly sourceMaps: readonly SourceMapRecord[];
1250
1358
  readonly importResult: NativeSourceImportResult;
1251
1359
  readonly projection: NativeSourceProjectionResult;
1360
+ readonly targetProjection?: NativeTargetProjectionResult;
1252
1361
  readonly targetCoverage: ProjectionTargetCoverageEntry;
1253
1362
  readonly projectionMatrix: ProjectionTargetLossMatrix;
1254
1363
  readonly losses: readonly NativeAstLossRecord[];
@@ -1342,6 +1451,7 @@ export declare function createBabelNativeImporterAdapter(options?: JavaScriptNat
1342
1451
  export declare function createTypeScriptCompilerNativeImporterAdapter(options?: TypeScriptCompilerNativeImporterAdapterOptions): NativeImporterAdapter;
1343
1452
  export declare function createTreeSitterNativeImporterAdapter(options?: TreeSitterNativeImporterAdapterOptions): NativeImporterAdapter;
1344
1453
  export declare function runNativeImporterAdapter(adapter: NativeImporterAdapter, input: RunNativeImporterAdapterOptions): Promise<NativeImporterAdapterImportResult>;
1454
+ export declare function runNativeTargetProjectionAdapter(adapter: NativeTargetProjectionAdapter, input: NativeTargetProjectionAdapterInput): NativeTargetProjectionResult;
1345
1455
  export declare function projectNativeImportToSource(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: ProjectNativeImportToSourceOptions): NativeSourceProjectionResult;
1346
1456
  export declare function importNativeSource(input: ImportNativeSourceOptions): NativeSourceImportResult;
1347
1457
  export declare function diffNativeSources(input: DiffNativeSourcesOptions): NativeSourceChangeSet;
package/dist/index.js CHANGED
@@ -151,6 +151,7 @@ export const ProjectionTargetLossClasses = Object.freeze([
151
151
  'exactSourceProjection',
152
152
  'nativeSourceStubs',
153
153
  'unsupportedTargetFeatures',
154
+ 'targetAdapterProjection',
154
155
  'missingAdapter'
155
156
  ]);
156
157
 
@@ -277,11 +278,38 @@ export function compileNativeSource(input, options = {}) {
277
278
  const projectionMatrix = createProjectionTargetLossMatrix({
278
279
  imports: [importResult],
279
280
  adapters: options.adapters,
281
+ targetAdapters: options.targetAdapters,
280
282
  languages: options.languages,
281
283
  targets: [target],
282
284
  generatedAt: options.generatedAt
283
285
  });
284
286
  const targetCoverage = nativeSourceCompileTargetCoverage(projectionMatrix, sourceLanguage, target);
287
+ const targetAdapter = resolveNativeTargetProjectionAdapter({
288
+ importResult,
289
+ sourceProjection: projection,
290
+ sourceLanguage,
291
+ target,
292
+ sourcePath: importResult.sourcePath ?? importResult.nativeSource?.sourcePath,
293
+ parser: importResult.nativeAst?.parser ?? importResult.nativeSource?.parser ?? options.parser
294
+ }, options);
295
+ const targetProjection = targetAdapter
296
+ ? runNativeTargetProjectionAdapter(targetAdapter, {
297
+ importResult,
298
+ sourceProjection: projection,
299
+ sourceLanguage,
300
+ target,
301
+ targetPath: options.targetPath,
302
+ targetCoverage,
303
+ options: options.targetAdapterOptions ?? {},
304
+ metadata: {
305
+ nativeCompileResultId: id,
306
+ sourceLanguage,
307
+ target,
308
+ projectionId: projection.id,
309
+ ...options.targetAdapterMetadata
310
+ }
311
+ })
312
+ : undefined;
285
313
  const targetLosses = nativeSourceCompileTargetLosses({
286
314
  importResult,
287
315
  projection,
@@ -290,26 +318,32 @@ export function compileNativeSource(input, options = {}) {
290
318
  target,
291
319
  idPart
292
320
  });
293
- const outputMode = sameSourceTarget ? projection.mode : 'target-stubs';
321
+ const output = targetProjection?.output ?? projection.sourceText;
322
+ const outputHash = targetProjection?.outputHash ?? projection.outputHash;
323
+ const outputMode = targetProjection ? targetProjection.outputMode : sameSourceTarget ? projection.mode : 'target-stubs';
294
324
  const compileEvidence = nativeSourceCompileEvidence({
295
325
  id: options.compileEvidenceId ?? `evidence_${idPart}_${idFragment(target)}_native_source_compile`,
296
326
  importResult,
297
327
  projection,
328
+ targetProjection,
298
329
  targetCoverage,
299
330
  targetLosses,
300
331
  sourceLanguage,
301
332
  target,
333
+ outputHash,
302
334
  outputMode
303
335
  });
304
336
  const evidence = uniqueByEvidenceId([
305
337
  ...(importResult.evidence ?? []),
306
338
  ...(projection.evidence ?? []),
339
+ ...(targetProjection?.evidence ?? []),
307
340
  compileEvidence,
308
341
  ...(options.evidence ?? [])
309
342
  ]);
310
343
  const losses = uniqueByLossId([
311
344
  ...(importResult.losses ?? []),
312
345
  ...(projection.losses ?? []),
346
+ ...(targetProjection?.losses ?? []),
313
347
  ...targetLosses,
314
348
  ...(options.losses ?? [])
315
349
  ]);
@@ -325,6 +359,25 @@ export function compileNativeSource(input, options = {}) {
325
359
  scanKind: 'native-source-compile',
326
360
  semanticStatus: importResult.metadata?.semanticStatus ?? options.semanticStatus
327
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];
328
381
  return {
329
382
  kind: 'frontier.lang.nativeSourceCompileResult',
330
383
  version: 1,
@@ -333,11 +386,14 @@ export function compileNativeSource(input, options = {}) {
333
386
  target,
334
387
  language: sourceLanguage,
335
388
  sourcePath: importResult.sourcePath ?? importResult.nativeSource?.sourcePath,
336
- output: projection.sourceText,
337
- outputHash: projection.outputHash,
389
+ output,
390
+ outputHash,
338
391
  outputMode,
392
+ sourceMap,
393
+ sourceMaps,
339
394
  importResult,
340
395
  projection,
396
+ targetProjection,
341
397
  targetCoverage,
342
398
  projectionMatrix,
343
399
  losses,
@@ -351,8 +407,15 @@ export function compileNativeSource(input, options = {}) {
351
407
  semanticIndexId: importResult.semanticIndex?.id ?? importResult.universalAst?.semanticIndex?.id,
352
408
  universalAstId: importResult.universalAst?.id,
353
409
  projectionId: projection.id,
410
+ targetProjectionId: targetProjection?.id,
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),
354
415
  projectionMode: projection.mode,
355
416
  outputMode,
417
+ targetPath: sourceMap?.targetPath ?? options.targetPath ?? targetProjection?.targetPath,
418
+ targetHash: sourceMap?.targetHash ?? options.targetHash ?? outputHash,
356
419
  sourceTarget,
357
420
  sameSourceTarget,
358
421
  targetLossClass: targetCoverage.lossClass,
@@ -657,11 +720,13 @@ export function createNativeImportCoverageMatrix(input = {}) {
657
720
  export function createProjectionTargetLossMatrix(input = {}) {
658
721
  const imports = input.imports ?? [];
659
722
  const adapters = input.adapters ?? [];
660
- const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
723
+ const targetAdapters = input.targetAdapters ?? [];
724
+ const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters, targetAdapters);
661
725
  const targets = normalizeProjectionMatrixTargets(input.targets ?? FrontierCompileTargets);
662
726
  const languages = profiles.map((profile) => projectionTargetCoverageForProfile(profile, {
663
727
  imports,
664
728
  adapters,
729
+ targetAdapters,
665
730
  targets
666
731
  }));
667
732
  const summary = projectionTargetLossMatrixSummary(languages);
@@ -674,7 +739,7 @@ export function createProjectionTargetLossMatrix(input = {}) {
674
739
  metadata: {
675
740
  compileTargets: targets,
676
741
  lossClasses: [...ProjectionTargetLossClasses],
677
- note: 'Projection target coverage separates exact source preservation, declaration stubs, known unsupported target features, and missing native-to-target adapters.'
742
+ note: 'Projection target coverage separates exact source preservation, declaration stubs, host-owned target adapters, known unsupported target features, and missing native-to-target adapters.'
678
743
  }
679
744
  };
680
745
  }
@@ -1159,6 +1224,120 @@ export async function runNativeImporterAdapter(adapter, input = {}) {
1159
1224
  };
1160
1225
  }
1161
1226
 
1227
+ export function runNativeTargetProjectionAdapter(adapter, input = {}) {
1228
+ const summary = normalizeNativeTargetProjectionAdapter(adapter);
1229
+ const sourceLanguage = normalizeNativeLanguageId(input.sourceLanguage ?? summary.sourceLanguage);
1230
+ const target = normalizeProjectionMatrixTargets([input.target ?? summary.target])[0] ?? summary.target;
1231
+ const diagnosticContext = {
1232
+ sourcePath: input.importResult?.sourcePath ?? input.importResult?.nativeSource?.sourcePath,
1233
+ sourceHash: input.importResult?.sourceHash ?? input.importResult?.nativeSource?.sourceHash,
1234
+ language: sourceLanguage,
1235
+ parser: `target:${target}`,
1236
+ parserVersion: summary.version
1237
+ };
1238
+ const projectInput = {
1239
+ importResult: input.importResult,
1240
+ sourceProjection: input.sourceProjection,
1241
+ sourceLanguage,
1242
+ target,
1243
+ targetPath: input.targetPath,
1244
+ targetCoverage: input.targetCoverage,
1245
+ options: input.options ?? {},
1246
+ metadata: input.metadata ?? {}
1247
+ };
1248
+ let projected;
1249
+ let thrownDiagnostic;
1250
+ try {
1251
+ projected = adapter.project(projectInput) ?? {};
1252
+ } catch (error) {
1253
+ thrownDiagnostic = {
1254
+ severity: 'error',
1255
+ code: 'targetAdapter.project.threw',
1256
+ phase: 'emit',
1257
+ kind: 'targetProjectionLoss',
1258
+ message: error instanceof Error ? error.message : String(error),
1259
+ metadata: {
1260
+ errorName: error instanceof Error ? error.name : undefined
1261
+ }
1262
+ };
1263
+ projected = {};
1264
+ }
1265
+ const diagnostics = [
1266
+ ...normalizeAdapterDiagnostics(summary.diagnostics, summary, diagnosticContext, 'target-adapter'),
1267
+ ...(thrownDiagnostic ? normalizeAdapterDiagnostics([thrownDiagnostic], summary, diagnosticContext, 'throw') : []),
1268
+ ...normalizeAdapterDiagnostics(projected.diagnostics, summary, diagnosticContext, 'project')
1269
+ ];
1270
+ const output = typeof projected.output === 'string' ? projected.output : input.sourceProjection?.sourceText ?? '';
1271
+ const outputHash = projected.outputHash ?? hashSemanticValue(output);
1272
+ const adapterEvidence = nativeTargetProjectionAdapterEvidence(summary, diagnostics, {
1273
+ ...diagnosticContext,
1274
+ sourceLanguage,
1275
+ target,
1276
+ outputHash,
1277
+ targetPath: input.targetPath
1278
+ });
1279
+ const evidence = uniqueByEvidenceId([
1280
+ ...(projected.evidence ?? []),
1281
+ adapterEvidence
1282
+ ]);
1283
+ const losses = uniqueByLossId([
1284
+ ...(projected.losses ?? []),
1285
+ ...diagnostics.map((diagnostic, index) => nativeTargetProjectionDiagnosticToLoss(diagnostic, index, summary, {
1286
+ ...diagnosticContext,
1287
+ sourceLanguage,
1288
+ target
1289
+ }))
1290
+ ]);
1291
+ const lossSummary = summarizeNativeImportLosses(losses, {
1292
+ evidence,
1293
+ parser: diagnosticContext.parser,
1294
+ scanKind: 'native-target-projection',
1295
+ semanticStatus: input.importResult?.metadata?.semanticStatus
1296
+ });
1297
+ const classifiedReadiness = classifyNativeImportReadiness(losses, {
1298
+ evidence,
1299
+ parser: diagnosticContext.parser,
1300
+ scanKind: 'native-target-projection',
1301
+ semanticStatus: input.importResult?.metadata?.semanticStatus
1302
+ });
1303
+ const declaredReadiness = normalizeSemanticMergeReadiness(projected.readiness ?? summary.coverage.readiness);
1304
+ const readiness = declaredReadiness
1305
+ ? {
1306
+ ...classifiedReadiness,
1307
+ readiness: maxSemanticMergeReadiness(classifiedReadiness.readiness, declaredReadiness),
1308
+ reasons: uniqueStrings([
1309
+ ...classifiedReadiness.reasons,
1310
+ ...(declaredReadiness === classifiedReadiness.readiness ? [] : [`Target adapter declared readiness ${declaredReadiness}.`])
1311
+ ])
1312
+ }
1313
+ : classifiedReadiness;
1314
+ return {
1315
+ kind: 'frontier.lang.nativeTargetProjection',
1316
+ version: 1,
1317
+ id: projected.id ?? `native_target_projection_${idFragment(summary.id)}_${idFragment(sourceLanguage)}_${idFragment(target)}`,
1318
+ sourceLanguage,
1319
+ target,
1320
+ targetPath: input.targetPath,
1321
+ adapter: summary,
1322
+ output,
1323
+ outputHash,
1324
+ outputMode: 'target-adapter',
1325
+ sourceMaps: projected.sourceMaps ?? [],
1326
+ losses,
1327
+ lossSummary,
1328
+ readiness,
1329
+ evidence,
1330
+ diagnostics: diagnostics.map(serializableDiagnostic),
1331
+ metadata: {
1332
+ importId: input.importResult?.id,
1333
+ projectionId: input.sourceProjection?.id,
1334
+ targetCoverageLossClass: input.targetCoverage?.lossClass,
1335
+ targetCoverageReadiness: input.targetCoverage?.readiness,
1336
+ ...projected.metadata
1337
+ }
1338
+ };
1339
+ }
1340
+
1162
1341
  export function projectNativeImportToSource(importResult, options = {}) {
1163
1342
  if (!importResult || typeof importResult !== 'object') {
1164
1343
  throw new Error('projectNativeImportToSource requires a native import result');
@@ -3950,6 +4129,7 @@ function projectionTargetCoverageForProfile(profile, context) {
3950
4129
  const aliases = new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
3951
4130
  const matchingImports = (context.imports ?? []).filter((imported) => aliases.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language)));
3952
4131
  const matchingAdapters = (context.adapters ?? []).filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.language)));
4132
+ const matchingTargetAdapters = (context.targetAdapters ?? []).filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.sourceLanguage ?? adapter?.language)));
3953
4133
  const importedLossKinds = uniqueStrings(matchingImports.flatMap((imported) => (imported?.losses ?? []).map((loss) => loss.kind).filter(Boolean)));
3954
4134
  const knownLossKinds = uniqueStrings([...(profile.knownLossKinds ?? []), ...importedLossKinds]);
3955
4135
  const parserAdapters = uniqueStrings([
@@ -3960,6 +4140,7 @@ function projectionTargetCoverageForProfile(profile, context) {
3960
4140
  const targets = (context.targets ?? FrontierCompileTargets).map((target) => projectionTargetCoverageEntry(profile, target, {
3961
4141
  matchingImports,
3962
4142
  matchingAdapters,
4143
+ matchingTargetAdapters,
3963
4144
  knownLossKinds
3964
4145
  }));
3965
4146
  return {
@@ -4037,8 +4218,14 @@ function projectionTargetCoverageEntry(profile, target, context) {
4037
4218
  const normalizedTarget = normalizeProjectionMatrixTargets([target])[0] ?? String(target);
4038
4219
  const declaredTargets = new Set(normalizeProjectionMatrixTargets(profile.projectionTargets ?? []));
4039
4220
  const adapterTargets = new Set((context.matchingAdapters ?? []).flatMap(adapterProjectionTargets));
4221
+ const targetAdapter = matchingNativeTargetProjectionAdapter({
4222
+ sourceLanguage: profile.language,
4223
+ target: normalizedTarget,
4224
+ sourcePath: context.matchingImports?.[0]?.sourcePath ?? context.matchingImports?.[0]?.nativeSource?.sourcePath
4225
+ }, context.matchingTargetAdapters ?? []);
4226
+ const targetAdapterSummary = targetAdapter ? normalizeNativeTargetProjectionAdapter(targetAdapter) : undefined;
4040
4227
  const sameSourceTarget = nativeLanguageCompileTarget(profile.language, profile.aliases) === normalizedTarget;
4041
- const hasProjectionAdapter = declaredTargets.has(normalizedTarget) || adapterTargets.has(normalizedTarget);
4228
+ const hasProjectionAdapter = Boolean(targetAdapterSummary) || declaredTargets.has(normalizedTarget) || adapterTargets.has(normalizedTarget);
4042
4229
  if (!hasProjectionAdapter) {
4043
4230
  return {
4044
4231
  target: normalizedTarget,
@@ -4054,6 +4241,48 @@ function projectionTargetCoverageEntry(profile, target, context) {
4054
4241
  }
4055
4242
 
4056
4243
  const featureLossKinds = projectionUnsupportedFeatureLossKinds(context.knownLossKinds);
4244
+ if (targetAdapterSummary) {
4245
+ const handledLossKinds = new Set(targetAdapterSummary.coverage.handledLossKinds ?? []);
4246
+ const unhandledFeatureLossKinds = featureLossKinds.filter((kind) => !handledLossKinds.has(kind));
4247
+ if (unhandledFeatureLossKinds.length) {
4248
+ return {
4249
+ target: normalizedTarget,
4250
+ lossClass: 'unsupportedTargetFeatures',
4251
+ supported: true,
4252
+ readiness: 'needs-review',
4253
+ lossKinds: unhandledFeatureLossKinds,
4254
+ categories: uniqueStrings(unhandledFeatureLossKinds.map(nativeImportCategoryForLossKind)),
4255
+ reason: `${profile.language} has target adapter ${targetAdapterSummary.id}, but source feature losses remain unhandled for ${normalizedTarget}: ${unhandledFeatureLossKinds.join(', ')}.`,
4256
+ adapter: targetAdapterSummary.id,
4257
+ adapterKind: 'targetProjection',
4258
+ adapterVersion: targetAdapterSummary.version,
4259
+ adapterCoverage: targetAdapterSummary.coverage,
4260
+ notes: uniqueStrings([
4261
+ ...(targetAdapterSummary.coverage.notes ?? []),
4262
+ 'Adapter output is available, but merge readiness still requires review for unhandled source-language feature losses.'
4263
+ ])
4264
+ };
4265
+ }
4266
+ const adapterLossKinds = uniqueStrings(targetAdapterSummary.coverage.lossKinds ?? []);
4267
+ return {
4268
+ target: normalizedTarget,
4269
+ lossClass: 'targetAdapterProjection',
4270
+ supported: true,
4271
+ readiness: targetAdapterSummary.coverage.readiness ?? 'needs-review',
4272
+ lossKinds: adapterLossKinds,
4273
+ categories: uniqueStrings(adapterLossKinds.map(nativeImportCategoryForLossKind)),
4274
+ reason: `${profile.language} can project to ${normalizedTarget} through host target adapter ${targetAdapterSummary.id}.`,
4275
+ adapter: targetAdapterSummary.id,
4276
+ adapterKind: 'targetProjection',
4277
+ adapterVersion: targetAdapterSummary.version,
4278
+ adapterCoverage: targetAdapterSummary.coverage,
4279
+ notes: uniqueStrings([
4280
+ ...(targetAdapterSummary.coverage.notes ?? []),
4281
+ 'The host adapter owns native-to-target translation semantics and must provide evidence for merge admission.'
4282
+ ])
4283
+ };
4284
+ }
4285
+
4057
4286
  if (featureLossKinds.length) {
4058
4287
  return {
4059
4288
  target: normalizedTarget,
@@ -4115,6 +4344,7 @@ function projectionTargetLossMatrixSummary(languages) {
4115
4344
  sourceProjectionByLossClass,
4116
4345
  exactSourceProjection: (sourceProjectionByLossClass.exactSourceProjection ?? 0) + (byLossClass.exactSourceProjection ?? 0),
4117
4346
  nativeSourceStubs: (sourceProjectionByLossClass.nativeSourceStubs ?? 0) + (byLossClass.nativeSourceStubs ?? 0),
4347
+ targetAdapterProjection: byLossClass.targetAdapterProjection ?? 0,
4118
4348
  unsupportedTargetFeatures: byLossClass.unsupportedTargetFeatures ?? 0,
4119
4349
  missingAdapters: byLossClass.missingAdapter ?? 0
4120
4350
  };
@@ -4324,9 +4554,11 @@ function nativeSourceCompileEvidence(input) {
4324
4554
  metadata: {
4325
4555
  importId: input.importResult.id,
4326
4556
  projectionId: input.projection.id,
4557
+ targetProjectionId: input.targetProjection?.id,
4558
+ targetProjectionAdapterId: input.targetProjection?.adapter?.id,
4327
4559
  sourceLanguage: input.sourceLanguage,
4328
4560
  target: input.target,
4329
- outputHash: input.projection.outputHash,
4561
+ outputHash: input.outputHash ?? input.projection.outputHash,
4330
4562
  outputMode: input.outputMode,
4331
4563
  projectionMode: input.projection.mode,
4332
4564
  targetLossClass: input.targetCoverage.lossClass,
@@ -4338,6 +4570,251 @@ function nativeSourceCompileEvidence(input) {
4338
4570
  };
4339
4571
  }
4340
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
+
4341
4818
  function nativeProjectionTargetsForLanguage(language, aliases = []) {
4342
4819
  const target = nativeLanguageCompileTarget(language, aliases);
4343
4820
  return target ? [target] : [];
@@ -4359,7 +4836,7 @@ function nativeImportLanguageProfile(language, input = {}) {
4359
4836
  });
4360
4837
  }
4361
4838
 
4362
- function mergeNativeImportProfiles(languages, imports, adapters) {
4839
+ function mergeNativeImportProfiles(languages, imports, adapters, targetAdapters = []) {
4363
4840
  const profilesByLanguage = new Map();
4364
4841
  for (const profile of languages) {
4365
4842
  const normalized = normalizeNativeLanguageId(profile.language ?? profile);
@@ -4385,6 +4862,16 @@ function mergeNativeImportProfiles(languages, imports, adapters) {
4385
4862
  parserAdapters: uniqueStrings([...(existing.parserAdapters ?? []), adapter.parser ?? adapter.id].filter(Boolean))
4386
4863
  });
4387
4864
  }
4865
+ for (const adapter of targetAdapters) {
4866
+ const summary = safeNativeTargetProjectionAdapterSummary(adapter);
4867
+ const normalized = normalizeNativeLanguageId(summary?.sourceLanguage);
4868
+ if (!normalized) continue;
4869
+ const existing = profilesByLanguage.get(normalized) ?? nativeImportLanguageProfile(normalized, { supportsLightweightScan: false, parserAdapters: [] });
4870
+ profilesByLanguage.set(normalized, {
4871
+ ...existing,
4872
+ projectionTargets: uniqueStrings([...(existing.projectionTargets ?? []), summary.target].filter(Boolean))
4873
+ });
4874
+ }
4388
4875
  return [...profilesByLanguage.values()].sort((left, right) => left.language.localeCompare(right.language));
4389
4876
  }
4390
4877
 
@@ -5217,6 +5704,11 @@ function maxSemanticMergeReadiness(left, right) {
5217
5704
  return leftRank >= rightRank ? left : right;
5218
5705
  }
5219
5706
 
5707
+ function normalizeSemanticMergeReadiness(value) {
5708
+ const readiness = String(value ?? '').toLowerCase();
5709
+ return Object.prototype.hasOwnProperty.call(semanticMergeReadinessRank, readiness) ? readiness : undefined;
5710
+ }
5711
+
5220
5712
  export function createUniversalAstFromDocument(document, input = {}) {
5221
5713
  return createUniversalAstEnvelope({
5222
5714
  id: input.id ?? `universal_ast_${idFragment(document.id)}`,
@@ -5886,6 +6378,150 @@ function resolveNativeProjectAdapter(source, adapters, input) {
5886
6378
  });
5887
6379
  }
5888
6380
 
6381
+ function resolveNativeTargetProjectionAdapter(input, options = {}) {
6382
+ const adapters = options.targetAdapters ?? [];
6383
+ if (options.targetAdapter && typeof options.targetAdapter === 'object') return options.targetAdapter;
6384
+ if (typeof options.targetAdapter === 'string') {
6385
+ const explicit = adapters.find((adapter) => adapter?.id === options.targetAdapter);
6386
+ if (explicit) return explicit;
6387
+ }
6388
+ if (typeof options.targetAdapterResolver === 'function') {
6389
+ const resolved = options.targetAdapterResolver(input, adapters);
6390
+ if (resolved) return resolved;
6391
+ }
6392
+ return matchingNativeTargetProjectionAdapter(input, adapters);
6393
+ }
6394
+
6395
+ function matchingNativeTargetProjectionAdapter(input, adapters = []) {
6396
+ return adapters.find((adapter) => nativeTargetProjectionAdapterMatches(adapter, input));
6397
+ }
6398
+
6399
+ function nativeTargetProjectionAdapterMatches(adapter, input = {}) {
6400
+ const summary = safeNativeTargetProjectionAdapterSummary(adapter);
6401
+ if (!summary) return false;
6402
+ if (input.sourceLanguage && normalizeNativeLanguageId(input.sourceLanguage) !== summary.sourceLanguage) return false;
6403
+ const target = normalizeProjectionMatrixTargets([input.target])[0] ?? String(input.target ?? '').toLowerCase();
6404
+ if (target && target !== summary.target) return false;
6405
+ const parser = input.parser ? String(input.parser).toLowerCase() : undefined;
6406
+ if (parser && summary.supportedParsers.length && !summary.supportedParsers.map((item) => item.toLowerCase()).includes(parser)) return false;
6407
+ const sourcePath = String(input.sourcePath ?? '').toLowerCase();
6408
+ if (sourcePath && summary.supportedExtensions.length) {
6409
+ return summary.supportedExtensions.some((extension) => sourcePath.endsWith(extension));
6410
+ }
6411
+ return true;
6412
+ }
6413
+
6414
+ function safeNativeTargetProjectionAdapterSummary(adapter) {
6415
+ try {
6416
+ return normalizeNativeTargetProjectionAdapter(adapter);
6417
+ } catch {
6418
+ return undefined;
6419
+ }
6420
+ }
6421
+
6422
+ function normalizeNativeTargetProjectionAdapter(adapter) {
6423
+ if (!adapter || typeof adapter !== 'object') {
6424
+ throw new Error('Native target projection adapter must be an object');
6425
+ }
6426
+ if (!adapter.id) throw new Error('Native target projection adapter requires an id');
6427
+ const sourceLanguage = normalizeNativeLanguageId(adapter.sourceLanguage ?? adapter.language);
6428
+ if (!sourceLanguage) throw new Error(`Native target projection adapter ${adapter.id} requires a sourceLanguage`);
6429
+ const target = normalizeProjectionMatrixTargets([adapter.target ?? adapter.targetLanguage])[0];
6430
+ if (!target) throw new Error(`Native target projection adapter ${adapter.id} requires a target`);
6431
+ if (typeof adapter.project !== 'function') throw new Error(`Native target projection adapter ${adapter.id} requires a project function`);
6432
+ const capabilities = normalizeStringList(adapter.capabilities);
6433
+ const summaryInput = {
6434
+ id: String(adapter.id),
6435
+ sourceLanguage,
6436
+ language: sourceLanguage,
6437
+ target,
6438
+ parser: `target:${target}`,
6439
+ version: adapter.version === undefined ? undefined : String(adapter.version)
6440
+ };
6441
+ return Object.freeze({
6442
+ id: summaryInput.id,
6443
+ sourceLanguage,
6444
+ target,
6445
+ version: summaryInput.version,
6446
+ capabilities,
6447
+ supportedParsers: normalizeStringList(adapter.supportedParsers),
6448
+ supportedExtensions: normalizeStringList(adapter.supportedExtensions).map((extension) => extension.startsWith('.') ? extension.toLowerCase() : `.${extension.toLowerCase()}`),
6449
+ coverage: normalizeNativeTargetProjectionAdapterCoverage(adapter.coverage, { capabilities }),
6450
+ diagnostics: normalizeAdapterDiagnostics(adapter.diagnostics, summaryInput, {
6451
+ language: sourceLanguage,
6452
+ parser: summaryInput.parser,
6453
+ parserVersion: summaryInput.version
6454
+ }, 'target-adapter')
6455
+ });
6456
+ }
6457
+
6458
+ function normalizeNativeTargetProjectionAdapterCoverage(value = {}, context = {}) {
6459
+ const capabilities = new Set(normalizeStringList(context.capabilities).map((capability) => capability.toLowerCase()));
6460
+ const lossKinds = uniqueStrings(value.lossKinds ?? []);
6461
+ const handledLossKinds = uniqueStrings([
6462
+ ...(value.handledLossKinds ?? []),
6463
+ ...(capabilities.has('macros') || capabilities.has('macroexpansion') ? ['macroExpansion', 'macroHygiene'] : []),
6464
+ ...(capabilities.has('preprocessor') ? ['preprocessor', 'conditionalCompilation'] : []),
6465
+ ...(capabilities.has('dynamicruntime') ? ['dynamicRuntime', 'dynamicDispatch'] : []),
6466
+ ...(capabilities.has('typeinference') ? ['typeInference', 'overloadResolution'] : [])
6467
+ ]);
6468
+ return Object.freeze({
6469
+ readiness: normalizeSemanticMergeReadiness(value.readiness) ?? 'needs-review',
6470
+ lossKinds,
6471
+ handledLossKinds,
6472
+ sourceMapPrecision: value.sourceMapPrecision,
6473
+ semanticCoverage: value.semanticCoverage ?? {},
6474
+ notes: uniqueStrings(value.notes ?? ['Target projection adapter output is host-owned evidence and should be reviewed unless declared ready.'])
6475
+ });
6476
+ }
6477
+
6478
+ function nativeTargetProjectionAdapterEvidence(adapter, diagnostics, input) {
6479
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;
6480
+ const warnings = diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;
6481
+ return {
6482
+ id: `evidence_${idFragment(adapter.id)}_native_target_projection_adapter`,
6483
+ kind: 'projection',
6484
+ status: errors ? 'failed' : 'passed',
6485
+ path: input.sourcePath,
6486
+ summary: `Ran ${adapter.id} native target projection adapter from ${input.sourceLanguage} to ${input.target} with ${diagnostics.length} diagnostic(s).`,
6487
+ metadata: {
6488
+ adapterId: adapter.id,
6489
+ adapterVersion: adapter.version,
6490
+ sourceLanguage: input.sourceLanguage,
6491
+ target: input.target,
6492
+ targetPath: input.targetPath,
6493
+ outputHash: input.outputHash,
6494
+ capabilities: adapter.capabilities,
6495
+ coverage: adapter.coverage,
6496
+ diagnostics: diagnostics.map(serializableDiagnostic),
6497
+ errors,
6498
+ warnings
6499
+ }
6500
+ };
6501
+ }
6502
+
6503
+ function nativeTargetProjectionDiagnosticToLoss(diagnostic, index, adapter, input) {
6504
+ return {
6505
+ id: `loss_${idFragment(diagnostic.id ?? `${adapter.id}_${index}_${diagnostic.code ?? diagnostic.severity}`)}`,
6506
+ severity: diagnostic.severity,
6507
+ phase: diagnostic.phase ?? 'emit',
6508
+ sourceFormat: input.sourceLanguage,
6509
+ kind: diagnostic.kind ?? 'targetProjectionLoss',
6510
+ message: diagnostic.message,
6511
+ span: diagnostic.span,
6512
+ metadata: {
6513
+ adapterId: adapter.id,
6514
+ adapterVersion: adapter.version,
6515
+ diagnosticId: diagnostic.id,
6516
+ diagnosticCode: diagnostic.code,
6517
+ sourceLanguage: input.sourceLanguage,
6518
+ target: input.target,
6519
+ path: diagnostic.path,
6520
+ ...diagnostic.metadata
6521
+ }
6522
+ };
6523
+ }
6524
+
5889
6525
  function normalizeNativeImporterAdapter(adapter) {
5890
6526
  if (!adapter || typeof adapter !== 'object') {
5891
6527
  throw new Error('Native importer adapter must be an object');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.19",
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",