@shapeshift-labs/frontier-lang-compiler 0.2.18 → 0.2.19

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
@@ -166,6 +166,32 @@ console.log(changeSet.mergeCandidate.readiness); // merge-admission classificati
166
166
 
167
167
  Use `diffNativeSourceImports` when the worker or runner already produced `importNativeSource` results. Body-only edits that the lightweight scanner cannot anchor to a symbol are still reported as file-level changed regions instead of being silently treated as safe.
168
168
 
169
+ Compile native source imports through the same reader/IR/writer facade that swarms use for sidecar evidence. Same-language targets preserve exact source when hashes match; cross-language targets emit declaration stubs until a real adapter provides stronger evidence:
170
+
171
+ ```js
172
+ import { compileNativeSource } from '@shapeshift-labs/frontier-lang-compiler';
173
+
174
+ const compiledJs = compileNativeSource({
175
+ language: 'javascript',
176
+ sourcePath: 'src/runtime.js',
177
+ sourceText: 'export function step(frame) { return frame + 1; }\n'
178
+ });
179
+
180
+ console.log(compiledJs.outputMode); // "preserved-source"
181
+ console.log(compiledJs.readiness.readiness); // scanner imports can still be "needs-review"
182
+
183
+ const rustCandidate = compileNativeSource(compiledJs.importResult, {
184
+ target: 'rust',
185
+ emitOnBlocked: true
186
+ });
187
+
188
+ console.log(rustCandidate.outputMode); // "target-stubs"
189
+ console.log(rustCandidate.targetCoverage.lossClass); // "missingAdapter" without a JS-to-Rust adapter
190
+ console.log(rustCandidate.ok); // true only because emitOnBlocked requested code anyway
191
+ ```
192
+
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.
194
+
169
195
  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:
170
196
 
171
197
  ```js
package/bench/smoke.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { performance } from 'node:perf_hooks';
2
2
  import {
3
+ compileNativeSource,
3
4
  compileFrontierSource,
4
5
  createEstreeNativeImporterAdapter,
5
6
  createNativeImportCoverageMatrix,
@@ -92,6 +93,15 @@ const nativeProjections = nativeImportResults.map((imported) => projectNativeImp
92
93
  const projectionDurationMs = performance.now() - projectionStart;
93
94
  const projectionBytes = nativeProjections.reduce((sum, projection) => sum + projection.sourceText.length, 0);
94
95
 
96
+ const nativeCompileStart = performance.now();
97
+ const nativeCompiles = nativeImportResults.map((imported, index) => compileNativeSource(imported, {
98
+ target: index % 2 === 0 ? 'javascript' : 'rust',
99
+ emitOnBlocked: true
100
+ }));
101
+ const nativeCompileDurationMs = performance.now() - nativeCompileStart;
102
+ const nativeCompileBytes = nativeCompiles.reduce((sum, result) => sum + result.output.length, 0);
103
+ const nativeCompileBlocked = nativeCompiles.filter((result) => result.readiness.readiness === 'blocked').length;
104
+
95
105
  console.log(JSON.stringify({
96
106
  compiles: 250,
97
107
  bytes,
@@ -118,5 +128,9 @@ console.log(JSON.stringify({
118
128
  sidecarDurationMs: Number(sidecarDurationMs.toFixed(2)),
119
129
  nativeProjections: nativeProjections.length,
120
130
  projectionBytes,
121
- projectionDurationMs: Number(projectionDurationMs.toFixed(2))
131
+ projectionDurationMs: Number(projectionDurationMs.toFixed(2)),
132
+ nativeCompiles: nativeCompiles.length,
133
+ nativeCompileBytes,
134
+ nativeCompileBlocked,
135
+ nativeCompileDurationMs: Number(nativeCompileDurationMs.toFixed(2))
122
136
  }));
package/dist/index.d.ts CHANGED
@@ -1221,6 +1221,43 @@ export interface NativeSourceProjectionResult {
1221
1221
  readonly metadata: Record<string, unknown>;
1222
1222
  }
1223
1223
 
1224
+ export type NativeSourceCompileOutputMode = NativeSourceProjectionMode | 'target-stubs';
1225
+
1226
+ export interface CompileNativeSourceOptions extends ProjectNativeImportToSourceOptions {
1227
+ readonly target?: FrontierCompileTarget | string;
1228
+ readonly adapters?: readonly NativeImporterAdapter[];
1229
+ readonly languages?: readonly NativeImportLanguageProfile[];
1230
+ readonly generatedAt?: number;
1231
+ readonly emitOnBlocked?: boolean;
1232
+ readonly projectionId?: string;
1233
+ readonly projectionEvidenceId?: string;
1234
+ readonly compileEvidenceId?: string;
1235
+ readonly evidence?: readonly EvidenceRecord[];
1236
+ readonly losses?: readonly NativeAstLossRecord[];
1237
+ }
1238
+
1239
+ export interface NativeSourceCompileResult {
1240
+ readonly kind: 'frontier.lang.nativeSourceCompileResult';
1241
+ readonly version: 1;
1242
+ readonly id: string;
1243
+ readonly ok: boolean;
1244
+ readonly target: FrontierCompileTarget | string;
1245
+ readonly language: FrontierSourceLanguage | string;
1246
+ readonly sourcePath?: string;
1247
+ readonly output: string;
1248
+ readonly outputHash: string;
1249
+ readonly outputMode: NativeSourceCompileOutputMode;
1250
+ readonly importResult: NativeSourceImportResult;
1251
+ readonly projection: NativeSourceProjectionResult;
1252
+ readonly targetCoverage: ProjectionTargetCoverageEntry;
1253
+ readonly projectionMatrix: ProjectionTargetLossMatrix;
1254
+ readonly losses: readonly NativeAstLossRecord[];
1255
+ readonly lossSummary: NativeImportLossSummary;
1256
+ readonly readiness: NativeImportReadinessClassification;
1257
+ readonly evidence: readonly EvidenceRecord[];
1258
+ readonly metadata: Record<string, unknown>;
1259
+ }
1260
+
1224
1261
  export type NativeImportRoundtripReadinessStatus =
1225
1262
  | 'exact'
1226
1263
  | 'preserved-source'
@@ -1286,6 +1323,7 @@ export declare const NativeImportLanguageProfiles: readonly NativeImportLanguage
1286
1323
  export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
1287
1324
  export declare function compileFrontierSource(source: string, options?: FrontierCompileOptions): FrontierCompileResult;
1288
1325
  export declare function compileFrontierDocument(document: FrontierLangDocument, options?: FrontierCompileOptions): FrontierCompileResult;
1326
+ export declare function compileNativeSource(input: ImportNativeSourceOptions | NativeSourceImportResult, options?: CompileNativeSourceOptions): NativeSourceCompileResult;
1289
1327
  export declare function projectFrontierAst(document: FrontierLangDocument, target?: FrontierCompileOptions['target'], options?: FrontierCompileEmitOptions): FrontierTargetAst;
1290
1328
  export declare function renderTargetAst(ast: FrontierTargetAst, target?: FrontierCompileOptions['target']): string;
1291
1329
  export declare function renderTargetAstWithSourceMap(ast: FrontierTargetAst, target?: FrontierCompileOptions['target'], options?: FrontierCompileEmitOptions): FrontierTargetSourceMapResult;
package/dist/index.js CHANGED
@@ -253,6 +253,116 @@ export function compileFrontierDocument(document, options = {}) {
253
253
  };
254
254
  }
255
255
 
256
+ export function compileNativeSource(input, options = {}) {
257
+ const importResult = isNativeSourceImportResult(input) ? input : importNativeSource(input);
258
+ const sourceLanguage = nativeCompileSourceLanguage(importResult, input);
259
+ const target = nativeCompileTarget(input, importResult, options);
260
+ const sourceTarget = nativeLanguageCompileTarget(sourceLanguage);
261
+ const sameSourceTarget = sourceTarget === target;
262
+ const idPart = idFragment(options.id ?? importResult.id ?? importResult.sourcePath ?? sourceLanguage ?? target);
263
+ const id = options.id ?? `native_source_compile_${idPart}_${idFragment(target)}`;
264
+ const projection = projectNativeImportToSource(importResult, {
265
+ ...options,
266
+ id: options.projectionId ?? `${id}_projection`,
267
+ language: sameSourceTarget ? sourceLanguage : target,
268
+ preferPreservedSource: sameSourceTarget ? options.preferPreservedSource : false,
269
+ evidenceId: options.projectionEvidenceId ?? options.evidenceId,
270
+ metadata: {
271
+ sourceLanguage,
272
+ target,
273
+ nativeCompileResultId: id,
274
+ ...options.metadata
275
+ }
276
+ });
277
+ const projectionMatrix = createProjectionTargetLossMatrix({
278
+ imports: [importResult],
279
+ adapters: options.adapters,
280
+ languages: options.languages,
281
+ targets: [target],
282
+ generatedAt: options.generatedAt
283
+ });
284
+ const targetCoverage = nativeSourceCompileTargetCoverage(projectionMatrix, sourceLanguage, target);
285
+ const targetLosses = nativeSourceCompileTargetLosses({
286
+ importResult,
287
+ projection,
288
+ targetCoverage,
289
+ sourceLanguage,
290
+ target,
291
+ idPart
292
+ });
293
+ const outputMode = sameSourceTarget ? projection.mode : 'target-stubs';
294
+ const compileEvidence = nativeSourceCompileEvidence({
295
+ id: options.compileEvidenceId ?? `evidence_${idPart}_${idFragment(target)}_native_source_compile`,
296
+ importResult,
297
+ projection,
298
+ targetCoverage,
299
+ targetLosses,
300
+ sourceLanguage,
301
+ target,
302
+ outputMode
303
+ });
304
+ const evidence = uniqueByEvidenceId([
305
+ ...(importResult.evidence ?? []),
306
+ ...(projection.evidence ?? []),
307
+ compileEvidence,
308
+ ...(options.evidence ?? [])
309
+ ]);
310
+ const losses = uniqueByLossId([
311
+ ...(importResult.losses ?? []),
312
+ ...(projection.losses ?? []),
313
+ ...targetLosses,
314
+ ...(options.losses ?? [])
315
+ ]);
316
+ const lossSummary = summarizeNativeImportLosses(losses, {
317
+ evidence,
318
+ parser: importResult.nativeAst?.parser ?? importResult.nativeSource?.parser ?? options.parser,
319
+ scanKind: 'native-source-compile',
320
+ semanticStatus: importResult.metadata?.semanticStatus ?? options.semanticStatus
321
+ });
322
+ const readiness = classifyNativeImportReadiness(losses, {
323
+ evidence,
324
+ parser: importResult.nativeAst?.parser ?? importResult.nativeSource?.parser ?? options.parser,
325
+ scanKind: 'native-source-compile',
326
+ semanticStatus: importResult.metadata?.semanticStatus ?? options.semanticStatus
327
+ });
328
+ return {
329
+ kind: 'frontier.lang.nativeSourceCompileResult',
330
+ version: 1,
331
+ id,
332
+ ok: readiness.readiness !== 'blocked' || options.emitOnBlocked === true,
333
+ target,
334
+ language: sourceLanguage,
335
+ sourcePath: importResult.sourcePath ?? importResult.nativeSource?.sourcePath,
336
+ output: projection.sourceText,
337
+ outputHash: projection.outputHash,
338
+ outputMode,
339
+ importResult,
340
+ projection,
341
+ targetCoverage,
342
+ projectionMatrix,
343
+ losses,
344
+ lossSummary,
345
+ readiness,
346
+ evidence,
347
+ metadata: {
348
+ nativeImportId: importResult.id,
349
+ nativeSourceId: importResult.nativeSource?.id,
350
+ nativeAstId: importResult.nativeAst?.id ?? importResult.nativeSource?.ast?.id,
351
+ semanticIndexId: importResult.semanticIndex?.id ?? importResult.universalAst?.semanticIndex?.id,
352
+ universalAstId: importResult.universalAst?.id,
353
+ projectionId: projection.id,
354
+ projectionMode: projection.mode,
355
+ outputMode,
356
+ sourceTarget,
357
+ sameSourceTarget,
358
+ targetLossClass: targetCoverage.lossClass,
359
+ targetReadiness: targetCoverage.readiness,
360
+ targetSupported: targetCoverage.supported,
361
+ ...options.metadata
362
+ }
363
+ };
364
+ }
365
+
256
366
  export function projectFrontierAst(document, target = 'typescript', options = {}) {
257
367
  const normalized = normalizeCompileTarget(target);
258
368
  const projector = projectors[normalized];
@@ -4075,6 +4185,159 @@ function nativeLanguageCompileTarget(language, aliases = []) {
4075
4185
  return undefined;
4076
4186
  }
4077
4187
 
4188
+ function isNativeSourceImportResult(input) {
4189
+ return Boolean(input && typeof input === 'object' && input.kind === 'frontier.lang.importResult' && input.nativeSource && input.universalAst);
4190
+ }
4191
+
4192
+ function nativeCompileSourceLanguage(importResult, input) {
4193
+ return normalizeNativeLanguageId(
4194
+ importResult.language
4195
+ ?? importResult.nativeSource?.language
4196
+ ?? importResult.nativeAst?.language
4197
+ ?? importResult.nativeSource?.ast?.language
4198
+ ?? input?.language
4199
+ ) || String(importResult.language ?? input?.language ?? 'source');
4200
+ }
4201
+
4202
+ function nativeCompileTarget(input, importResult, options) {
4203
+ const targetInput = options.target
4204
+ ?? compileTargetLanguage(input?.target)
4205
+ ?? compileTargetLanguage(importResult.nativeSource?.target)
4206
+ ?? nativeLanguageCompileTarget(nativeCompileSourceLanguage(importResult, input))
4207
+ ?? 'typescript';
4208
+ return normalizeProjectionMatrixTargets([targetInput])[0] ?? String(targetInput ?? 'typescript').toLowerCase();
4209
+ }
4210
+
4211
+ function compileTargetLanguage(target) {
4212
+ if (!target) return undefined;
4213
+ if (typeof target === 'string') return target;
4214
+ return target.language ?? target.emitLanguage ?? target.target ?? target.name;
4215
+ }
4216
+
4217
+ function nativeSourceCompileTargetCoverage(matrix, language, target) {
4218
+ const sourceLanguage = normalizeNativeLanguageId(language);
4219
+ const entry = (matrix.languages ?? []).find((candidate) => {
4220
+ const ids = [candidate.language, ...(candidate.aliases ?? [])].map(normalizeNativeLanguageId);
4221
+ return ids.includes(sourceLanguage);
4222
+ });
4223
+ const coverage = entry?.targets?.find((candidate) => candidate.target === target);
4224
+ if (coverage) return coverage;
4225
+ return {
4226
+ target,
4227
+ lossClass: 'missingAdapter',
4228
+ supported: false,
4229
+ readiness: 'blocked',
4230
+ lossKinds: ['targetProjectionLoss'],
4231
+ categories: ['targetProjectionLoss'],
4232
+ reason: `No native-to-${target} projection coverage is available for ${language}.`,
4233
+ adapter: undefined,
4234
+ notes: ['Inject a source-language parser and target projection adapter before treating this cross-language output as merge-ready.']
4235
+ };
4236
+ }
4237
+
4238
+ function nativeSourceCompileTargetLosses(input) {
4239
+ const { importResult, projection, targetCoverage, sourceLanguage, target, idPart } = input;
4240
+ if (!targetCoverage || targetCoverage.lossClass === 'exactSourceProjection') return [];
4241
+ if (targetCoverage.lossClass === 'missingAdapter') {
4242
+ return [nativeSourceCompileTargetLoss({
4243
+ id: `loss_${idPart}_${idFragment(target)}_missing_projection_adapter`,
4244
+ severity: 'error',
4245
+ message: targetCoverage.reason,
4246
+ importResult,
4247
+ projection,
4248
+ targetCoverage,
4249
+ sourceLanguage,
4250
+ target
4251
+ })];
4252
+ }
4253
+ if (targetCoverage.lossClass === 'unsupportedTargetFeatures') {
4254
+ return [nativeSourceCompileTargetLoss({
4255
+ id: `loss_${idPart}_${idFragment(target)}_unsupported_target_features`,
4256
+ severity: 'warning',
4257
+ message: targetCoverage.reason,
4258
+ importResult,
4259
+ projection,
4260
+ targetCoverage,
4261
+ sourceLanguage,
4262
+ target
4263
+ })];
4264
+ }
4265
+ if (targetCoverage.lossClass === 'nativeSourceStubs') {
4266
+ return [nativeSourceCompileTargetLoss({
4267
+ id: `loss_${idPart}_${idFragment(target)}_target_stubs`,
4268
+ severity: 'warning',
4269
+ message: targetCoverage.reason,
4270
+ importResult,
4271
+ projection,
4272
+ targetCoverage,
4273
+ sourceLanguage,
4274
+ target
4275
+ })];
4276
+ }
4277
+ return [];
4278
+ }
4279
+
4280
+ function nativeSourceCompileTargetLoss(input) {
4281
+ const rootSpan = input.importResult.nativeAst?.nodes?.[input.importResult.nativeAst?.rootId]?.span
4282
+ ?? input.importResult.nativeSource?.ast?.nodes?.[input.importResult.nativeSource?.ast?.rootId]?.span;
4283
+ return {
4284
+ id: input.id,
4285
+ severity: input.severity,
4286
+ phase: 'emit',
4287
+ sourceFormat: input.sourceLanguage,
4288
+ kind: 'targetProjectionLoss',
4289
+ message: input.message,
4290
+ span: rootSpan ?? {
4291
+ sourceId: input.importResult.nativeSource?.sourceHash ?? input.importResult.sourceHash,
4292
+ path: input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath,
4293
+ startLine: 1,
4294
+ startColumn: 1
4295
+ },
4296
+ metadata: {
4297
+ target: input.target,
4298
+ sourceLanguage: input.sourceLanguage,
4299
+ projectionId: input.projection.id,
4300
+ projectionMode: input.projection.mode,
4301
+ targetLossClass: input.targetCoverage.lossClass,
4302
+ targetReadiness: input.targetCoverage.readiness,
4303
+ targetSupported: input.targetCoverage.supported,
4304
+ targetAdapter: input.targetCoverage.adapter,
4305
+ targetLossKinds: input.targetCoverage.lossKinds,
4306
+ lossCategory: 'targetProjectionLoss',
4307
+ semanticMergeAdmission: semanticMergeAdmissionForSeverity(input.severity)
4308
+ }
4309
+ };
4310
+ }
4311
+
4312
+ function nativeSourceCompileEvidence(input) {
4313
+ const failed = input.targetLosses.some((loss) => loss.severity === 'error')
4314
+ || input.projection.evidence?.some((record) => record.status === 'failed')
4315
+ || input.targetCoverage.readiness === 'blocked';
4316
+ return {
4317
+ id: input.id,
4318
+ kind: 'projection',
4319
+ status: failed ? 'failed' : 'passed',
4320
+ path: input.importResult.sourcePath ?? input.importResult.nativeSource?.sourcePath,
4321
+ summary: failed
4322
+ ? `Compiled ${input.sourceLanguage} native source to ${input.target} with blocked projection evidence.`
4323
+ : `Compiled ${input.sourceLanguage} native source to ${input.target} as ${input.outputMode}.`,
4324
+ metadata: {
4325
+ importId: input.importResult.id,
4326
+ projectionId: input.projection.id,
4327
+ sourceLanguage: input.sourceLanguage,
4328
+ target: input.target,
4329
+ outputHash: input.projection.outputHash,
4330
+ outputMode: input.outputMode,
4331
+ projectionMode: input.projection.mode,
4332
+ targetLossClass: input.targetCoverage.lossClass,
4333
+ targetReadiness: input.targetCoverage.readiness,
4334
+ targetSupported: input.targetCoverage.supported,
4335
+ targetReason: input.targetCoverage.reason,
4336
+ targetLossIds: input.targetLosses.map((loss) => loss.id)
4337
+ }
4338
+ };
4339
+ }
4340
+
4078
4341
  function nativeProjectionTargetsForLanguage(language, aliases = []) {
4079
4342
  const target = nativeLanguageCompileTarget(language, aliases);
4080
4343
  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.18",
3
+ "version": "0.2.19",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",