@shapeshift-labs/frontier-lang-compiler 0.2.13 → 0.2.15
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 +15 -0
- package/bench/smoke.mjs +9 -0
- package/dist/index.d.ts +95 -2
- package/dist/index.js +294 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -57,6 +57,7 @@ Ask the compiler what is actually covered before sending native imports into a m
|
|
|
57
57
|
```js
|
|
58
58
|
import {
|
|
59
59
|
createNativeImportCoverageMatrix,
|
|
60
|
+
createProjectionTargetLossMatrix,
|
|
60
61
|
importNativeSource
|
|
61
62
|
} from '@shapeshift-labs/frontier-lang-compiler';
|
|
62
63
|
|
|
@@ -71,8 +72,22 @@ const python = matrix.languages.find((entry) => entry.language === 'python');
|
|
|
71
72
|
|
|
72
73
|
console.log(python.imports.readiness); // scanner imports are intentionally review-required
|
|
73
74
|
console.log(python.parserAdapters); // host-owned exact parsers such as LibCST can be injected
|
|
75
|
+
|
|
76
|
+
const projectionMatrix = createProjectionTargetLossMatrix({ imports: [imported] });
|
|
77
|
+
const pythonProjection = projectionMatrix.languages.find((entry) => entry.language === 'python');
|
|
78
|
+
|
|
79
|
+
console.log(pythonProjection.sourceProjection.exactSource.lossClass); // "exactSourceProjection"
|
|
80
|
+
console.log(pythonProjection.sourceProjection.stubs.lossClass); // "nativeSourceStubs"
|
|
81
|
+
console.log(pythonProjection.targets.find((entry) => entry.target === 'rust').lossClass); // "missingAdapter"
|
|
74
82
|
```
|
|
75
83
|
|
|
84
|
+
The projection target matrix separates four runtime/API classes:
|
|
85
|
+
|
|
86
|
+
- `exactSourceProjection`: exact source can be emitted when the import carries matching source text or source-preservation evidence.
|
|
87
|
+
- `nativeSourceStubs`: declaration stubs can be emitted, but bodies, resolved types, and executable semantics are review-required.
|
|
88
|
+
- `unsupportedTargetFeatures`: a target slot exists, but the source profile or import evidence declares features such as macros, preprocessors, dynamic runtime behavior, generated code, unsupported syntax, or unresolved inference that this facade cannot prove lossless.
|
|
89
|
+
- `missingAdapter`: no native-to-target projection adapter is declared; preserve or stub the original source language instead, or inject host-owned parser/semantic adapter evidence.
|
|
90
|
+
|
|
76
91
|
Preserve exact native source text, token/trivia hashes, comments, whitespace, and source directives as evidence. This does not claim full semantic understanding; it keeps round-trip material available while exact parser adapters catch up:
|
|
77
92
|
|
|
78
93
|
```js
|
package/bench/smoke.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
compileFrontierSource,
|
|
4
4
|
createEstreeNativeImporterAdapter,
|
|
5
5
|
createNativeImportCoverageMatrix,
|
|
6
|
+
createProjectionTargetLossMatrix,
|
|
6
7
|
createNativeSourcePreservation,
|
|
7
8
|
createSemanticImportSidecar,
|
|
8
9
|
importNativeSource,
|
|
@@ -68,6 +69,10 @@ const matrixStart = performance.now();
|
|
|
68
69
|
const coverageMatrix = createNativeImportCoverageMatrix({ imports: nativeImportResults });
|
|
69
70
|
const matrixDurationMs = performance.now() - matrixStart;
|
|
70
71
|
|
|
72
|
+
const projectionMatrixStart = performance.now();
|
|
73
|
+
const projectionLossMatrix = createProjectionTargetLossMatrix({ imports: nativeImportResults });
|
|
74
|
+
const projectionMatrixDurationMs = performance.now() - projectionMatrixStart;
|
|
75
|
+
|
|
71
76
|
const preservationStart = performance.now();
|
|
72
77
|
const preservationRecords = nativeImportResults.map((imported) => imported.metadata.sourcePreservation ?? createNativeSourcePreservation({
|
|
73
78
|
language: imported.language,
|
|
@@ -101,6 +106,10 @@ console.log(JSON.stringify({
|
|
|
101
106
|
adapterCoverageTokenGaps: coverageMatrix.summary.adapterCoverage.gaps.tokens ?? 0,
|
|
102
107
|
adapterCoverageReferenceGaps: coverageMatrix.summary.adapterCoverage.gaps.references ?? 0,
|
|
103
108
|
coverageMatrixDurationMs: Number(matrixDurationMs.toFixed(2)),
|
|
109
|
+
projectionMatrixLanguages: projectionLossMatrix.summary.languages,
|
|
110
|
+
projectionMatrixMissingAdapters: projectionLossMatrix.summary.missingAdapters,
|
|
111
|
+
projectionMatrixUnsupportedTargetFeatures: projectionLossMatrix.summary.unsupportedTargetFeatures,
|
|
112
|
+
projectionMatrixDurationMs: Number(projectionMatrixDurationMs.toFixed(2)),
|
|
104
113
|
sourcePreservationRecords: preservationRecords.length,
|
|
105
114
|
sourcePreservationTokens: preservationTokens,
|
|
106
115
|
sourcePreservationDurationMs: Number(preservationDurationMs.toFixed(2)),
|
package/dist/index.d.ts
CHANGED
|
@@ -168,7 +168,7 @@ export interface NativeImportLanguageProfile {
|
|
|
168
168
|
readonly extensions: readonly string[];
|
|
169
169
|
readonly supportsLightweightScan: boolean;
|
|
170
170
|
readonly parserAdapters: readonly string[];
|
|
171
|
-
readonly projectionTargets: readonly FrontierCompileTarget[];
|
|
171
|
+
readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
|
|
172
172
|
readonly knownLossKinds: readonly NativeImportKnownLossKind[];
|
|
173
173
|
readonly defaultReadiness: SemanticMergeReadiness;
|
|
174
174
|
readonly notes: readonly string[];
|
|
@@ -202,7 +202,7 @@ export interface NativeImportCoverageLanguage {
|
|
|
202
202
|
readonly extensions: readonly string[];
|
|
203
203
|
readonly supportsLightweightScan: boolean;
|
|
204
204
|
readonly parserAdapters: readonly string[];
|
|
205
|
-
readonly projectionTargets: readonly FrontierCompileTarget[];
|
|
205
|
+
readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
|
|
206
206
|
readonly knownLossKinds: readonly NativeImportKnownLossKind[];
|
|
207
207
|
readonly defaultReadiness: SemanticMergeReadiness;
|
|
208
208
|
readonly notes: readonly string[];
|
|
@@ -241,6 +241,7 @@ export interface NativeImportCoverageMatrix {
|
|
|
241
241
|
};
|
|
242
242
|
readonly metadata: {
|
|
243
243
|
readonly compileTargets: readonly FrontierCompileTarget[];
|
|
244
|
+
readonly projectionTargetLossClasses: readonly ProjectionTargetLossClass[];
|
|
244
245
|
readonly note: string;
|
|
245
246
|
};
|
|
246
247
|
}
|
|
@@ -252,6 +253,96 @@ export interface NativeImportCoverageMatrixOptions {
|
|
|
252
253
|
readonly generatedAt?: number;
|
|
253
254
|
}
|
|
254
255
|
|
|
256
|
+
export type ProjectionTargetLossClass =
|
|
257
|
+
| 'exactSourceProjection'
|
|
258
|
+
| 'nativeSourceStubs'
|
|
259
|
+
| 'unsupportedTargetFeatures'
|
|
260
|
+
| 'missingAdapter'
|
|
261
|
+
| string;
|
|
262
|
+
|
|
263
|
+
export interface ProjectionSourceProjectionCoverage {
|
|
264
|
+
readonly lossClass: ProjectionTargetLossClass;
|
|
265
|
+
readonly mode: NativeSourceProjectionMode;
|
|
266
|
+
readonly supported: boolean;
|
|
267
|
+
readonly readiness: SemanticMergeReadiness;
|
|
268
|
+
readonly lossKinds: readonly NativeImportKnownLossKind[];
|
|
269
|
+
readonly categories: readonly NativeImportTaxonomyKind[];
|
|
270
|
+
readonly reason: string;
|
|
271
|
+
readonly evidence: {
|
|
272
|
+
readonly imports: number;
|
|
273
|
+
readonly importsWithExactSource?: number;
|
|
274
|
+
readonly importsWithDeclarations?: number;
|
|
275
|
+
};
|
|
276
|
+
readonly notes: readonly string[];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface ProjectionTargetCoverageEntry {
|
|
280
|
+
readonly target: FrontierCompileTarget | string;
|
|
281
|
+
readonly lossClass: ProjectionTargetLossClass;
|
|
282
|
+
readonly supported: boolean;
|
|
283
|
+
readonly readiness: SemanticMergeReadiness;
|
|
284
|
+
readonly lossKinds: readonly NativeImportKnownLossKind[];
|
|
285
|
+
readonly categories: readonly NativeImportTaxonomyKind[];
|
|
286
|
+
readonly reason: string;
|
|
287
|
+
readonly adapter?: string;
|
|
288
|
+
readonly notes: readonly string[];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface ProjectionTargetLanguageCoverage {
|
|
292
|
+
readonly language: FrontierSourceLanguage | string;
|
|
293
|
+
readonly aliases: readonly string[];
|
|
294
|
+
readonly extensions: readonly string[];
|
|
295
|
+
readonly supportsLightweightScan: boolean;
|
|
296
|
+
readonly parserAdapters: readonly string[];
|
|
297
|
+
readonly projectionTargets: readonly (FrontierCompileTarget | string)[];
|
|
298
|
+
readonly knownLossKinds: readonly NativeImportKnownLossKind[];
|
|
299
|
+
readonly defaultReadiness: SemanticMergeReadiness;
|
|
300
|
+
readonly notes: readonly string[];
|
|
301
|
+
readonly sourceProjection: {
|
|
302
|
+
readonly exactSource: ProjectionSourceProjectionCoverage;
|
|
303
|
+
readonly stubs: ProjectionSourceProjectionCoverage;
|
|
304
|
+
};
|
|
305
|
+
readonly targets: readonly ProjectionTargetCoverageEntry[];
|
|
306
|
+
readonly summary: {
|
|
307
|
+
readonly imports: number;
|
|
308
|
+
readonly parserAdapters: number;
|
|
309
|
+
readonly targetEntries: number;
|
|
310
|
+
readonly byLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
|
|
311
|
+
readonly exactSourceImports: number;
|
|
312
|
+
readonly stubDeclarationImports: number;
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export interface ProjectionTargetLossMatrix {
|
|
317
|
+
readonly kind: 'frontier.lang.projectionTargetLossMatrix';
|
|
318
|
+
readonly version: 1;
|
|
319
|
+
readonly generatedAt: number;
|
|
320
|
+
readonly languages: readonly ProjectionTargetLanguageCoverage[];
|
|
321
|
+
readonly summary: {
|
|
322
|
+
readonly languages: number;
|
|
323
|
+
readonly targetEntries: number;
|
|
324
|
+
readonly byLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
|
|
325
|
+
readonly sourceProjectionByLossClass: Readonly<Record<ProjectionTargetLossClass, number>>;
|
|
326
|
+
readonly exactSourceProjection: number;
|
|
327
|
+
readonly nativeSourceStubs: number;
|
|
328
|
+
readonly unsupportedTargetFeatures: number;
|
|
329
|
+
readonly missingAdapters: number;
|
|
330
|
+
};
|
|
331
|
+
readonly metadata: {
|
|
332
|
+
readonly compileTargets: readonly (FrontierCompileTarget | string)[];
|
|
333
|
+
readonly lossClasses: readonly ProjectionTargetLossClass[];
|
|
334
|
+
readonly note: string;
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface ProjectionTargetLossMatrixOptions {
|
|
339
|
+
readonly languages?: readonly NativeImportLanguageProfile[];
|
|
340
|
+
readonly imports?: readonly NativeSourceImportResult[];
|
|
341
|
+
readonly adapters?: readonly NativeImporterAdapter[];
|
|
342
|
+
readonly targets?: readonly (FrontierCompileTarget | string)[];
|
|
343
|
+
readonly generatedAt?: number;
|
|
344
|
+
}
|
|
345
|
+
|
|
255
346
|
export interface NativeImportContractSource {
|
|
256
347
|
readonly id: string;
|
|
257
348
|
readonly language?: FrontierSourceLanguage | string;
|
|
@@ -1081,6 +1172,7 @@ export declare const NativeImportRoundtripReadinessStatuses: readonly NativeImpo
|
|
|
1081
1172
|
export declare const NativeImportTaxonomyKinds: readonly NativeImportTaxonomyKind[];
|
|
1082
1173
|
export declare const NativeImportLossKinds: readonly NativeImportKnownLossKind[];
|
|
1083
1174
|
export declare const NativeImportRegionTaxonomyKinds: readonly NativeImportRegionTaxonomyKind[];
|
|
1175
|
+
export declare const ProjectionTargetLossClasses: readonly ProjectionTargetLossClass[];
|
|
1084
1176
|
export declare const NativeImportReadinessBySeverity: Readonly<Record<NativeImportLossSummary['highestSeverity'], SemanticMergeReadiness>>;
|
|
1085
1177
|
export declare const NativeImportLanguageProfiles: readonly NativeImportLanguageProfile[];
|
|
1086
1178
|
export declare function normalizeCompileTarget(target?: string): FrontierCompileTarget;
|
|
@@ -1093,6 +1185,7 @@ export declare function summarizeNativeImportLosses(losses?: readonly NativeAstL
|
|
|
1093
1185
|
export declare function classifyNativeImportReadiness(losses?: readonly NativeAstLossRecord[], options?: NativeImportLossSummaryOptions): NativeImportReadinessClassification;
|
|
1094
1186
|
export declare function classifyNativeImportRoundtripReadiness(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportRoundtripReadinessOptions): NativeImportRoundtripReadinessClassification;
|
|
1095
1187
|
export declare function createNativeImportCoverageMatrix(options?: NativeImportCoverageMatrixOptions): NativeImportCoverageMatrix;
|
|
1188
|
+
export declare function createProjectionTargetLossMatrix(options?: ProjectionTargetLossMatrixOptions): ProjectionTargetLossMatrix;
|
|
1096
1189
|
export declare function createNativeSourcePreservation(options: CreateNativeSourcePreservationOptions): NativeSourcePreservation;
|
|
1097
1190
|
export declare function createSemanticImportSidecar(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: SemanticImportSidecarOptions): SemanticImportSidecar;
|
|
1098
1191
|
export declare function createNativeImportResultContract(importResult: NativeSourceImportResult | NativeProjectImportResult, options?: NativeImportResultContractOptions): NativeImportResultContract;
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,13 @@ export const NativeImportRegionTaxonomyKinds = Object.freeze([
|
|
|
138
138
|
'generatedOutput'
|
|
139
139
|
]);
|
|
140
140
|
|
|
141
|
+
export const ProjectionTargetLossClasses = Object.freeze([
|
|
142
|
+
'exactSourceProjection',
|
|
143
|
+
'nativeSourceStubs',
|
|
144
|
+
'unsupportedTargetFeatures',
|
|
145
|
+
'missingAdapter'
|
|
146
|
+
]);
|
|
147
|
+
|
|
141
148
|
export const NativeImportLanguageProfiles = Object.freeze([
|
|
142
149
|
nativeImportLanguageProfile('javascript', {
|
|
143
150
|
aliases: ['js', 'mjs', 'cjs', 'jsx'],
|
|
@@ -516,11 +523,37 @@ export function createNativeImportCoverageMatrix(input = {}) {
|
|
|
516
523
|
summary,
|
|
517
524
|
metadata: {
|
|
518
525
|
compileTargets: [...FrontierCompileTargets],
|
|
526
|
+
projectionTargetLossClasses: [...ProjectionTargetLossClasses],
|
|
519
527
|
note: 'Coverage is evidence and capability metadata, not a claim that every language feature is losslessly portable.'
|
|
520
528
|
}
|
|
521
529
|
};
|
|
522
530
|
}
|
|
523
531
|
|
|
532
|
+
export function createProjectionTargetLossMatrix(input = {}) {
|
|
533
|
+
const imports = input.imports ?? [];
|
|
534
|
+
const adapters = input.adapters ?? [];
|
|
535
|
+
const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
|
|
536
|
+
const targets = normalizeProjectionMatrixTargets(input.targets ?? FrontierCompileTargets);
|
|
537
|
+
const languages = profiles.map((profile) => projectionTargetCoverageForProfile(profile, {
|
|
538
|
+
imports,
|
|
539
|
+
adapters,
|
|
540
|
+
targets
|
|
541
|
+
}));
|
|
542
|
+
const summary = projectionTargetLossMatrixSummary(languages);
|
|
543
|
+
return {
|
|
544
|
+
kind: 'frontier.lang.projectionTargetLossMatrix',
|
|
545
|
+
version: 1,
|
|
546
|
+
generatedAt: input.generatedAt ?? Date.now(),
|
|
547
|
+
languages,
|
|
548
|
+
summary,
|
|
549
|
+
metadata: {
|
|
550
|
+
compileTargets: targets,
|
|
551
|
+
lossClasses: [...ProjectionTargetLossClasses],
|
|
552
|
+
note: 'Projection target coverage separates exact source preservation, declaration stubs, known unsupported target features, and missing native-to-target adapters.'
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
524
557
|
export function createNativeSourcePreservation(options) {
|
|
525
558
|
if (!options || typeof options.sourceText !== 'string') {
|
|
526
559
|
throw new Error('createNativeSourcePreservation requires sourceText');
|
|
@@ -3371,15 +3404,273 @@ function nativeImportCategoryForLossKind(kind) {
|
|
|
3371
3404
|
return String(kind ?? 'opaqueNative');
|
|
3372
3405
|
}
|
|
3373
3406
|
|
|
3407
|
+
function normalizeProjectionMatrixTargets(targets) {
|
|
3408
|
+
return uniqueStrings((Array.isArray(targets) ? targets : [targets])
|
|
3409
|
+
.map((target) => {
|
|
3410
|
+
if (target === undefined || target === null) return undefined;
|
|
3411
|
+
try {
|
|
3412
|
+
return normalizeCompileTarget(target);
|
|
3413
|
+
} catch {
|
|
3414
|
+
return String(target).trim().toLowerCase();
|
|
3415
|
+
}
|
|
3416
|
+
})
|
|
3417
|
+
.filter(Boolean));
|
|
3418
|
+
}
|
|
3419
|
+
|
|
3420
|
+
function projectionTargetCoverageForProfile(profile, context) {
|
|
3421
|
+
const aliases = new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
|
|
3422
|
+
const matchingImports = (context.imports ?? []).filter((imported) => aliases.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language)));
|
|
3423
|
+
const matchingAdapters = (context.adapters ?? []).filter((adapter) => aliases.has(normalizeNativeLanguageId(adapter?.language)));
|
|
3424
|
+
const importedLossKinds = uniqueStrings(matchingImports.flatMap((imported) => (imported?.losses ?? []).map((loss) => loss.kind).filter(Boolean)));
|
|
3425
|
+
const knownLossKinds = uniqueStrings([...(profile.knownLossKinds ?? []), ...importedLossKinds]);
|
|
3426
|
+
const parserAdapters = uniqueStrings([
|
|
3427
|
+
...(profile.parserAdapters ?? []),
|
|
3428
|
+
...matchingAdapters.map((adapter) => adapter.parser ?? adapter.id).filter(Boolean)
|
|
3429
|
+
]);
|
|
3430
|
+
const sourceProjection = sourceProjectionCoverageForProfile(profile, matchingImports, knownLossKinds);
|
|
3431
|
+
const targets = (context.targets ?? FrontierCompileTargets).map((target) => projectionTargetCoverageEntry(profile, target, {
|
|
3432
|
+
matchingImports,
|
|
3433
|
+
matchingAdapters,
|
|
3434
|
+
knownLossKinds
|
|
3435
|
+
}));
|
|
3436
|
+
return {
|
|
3437
|
+
language: profile.language,
|
|
3438
|
+
aliases: profile.aliases,
|
|
3439
|
+
extensions: profile.extensions,
|
|
3440
|
+
supportsLightweightScan: profile.supportsLightweightScan,
|
|
3441
|
+
parserAdapters,
|
|
3442
|
+
projectionTargets: profile.projectionTargets,
|
|
3443
|
+
knownLossKinds,
|
|
3444
|
+
defaultReadiness: profile.defaultReadiness,
|
|
3445
|
+
notes: profile.notes,
|
|
3446
|
+
sourceProjection,
|
|
3447
|
+
targets,
|
|
3448
|
+
summary: {
|
|
3449
|
+
imports: matchingImports.length,
|
|
3450
|
+
parserAdapters: parserAdapters.length,
|
|
3451
|
+
targetEntries: targets.length,
|
|
3452
|
+
byLossClass: countProjectionLossClasses(targets),
|
|
3453
|
+
exactSourceImports: sourceProjection.exactSource.evidence.importsWithExactSource,
|
|
3454
|
+
stubDeclarationImports: sourceProjection.stubs.evidence.importsWithDeclarations
|
|
3455
|
+
}
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
|
|
3459
|
+
function sourceProjectionCoverageForProfile(profile, imports, knownLossKinds) {
|
|
3460
|
+
const exactSourceImports = imports.filter(hasExactSourceProjectionEvidence).length;
|
|
3461
|
+
const declarationImports = imports.filter(hasNativeProjectionDeclarations).length;
|
|
3462
|
+
return {
|
|
3463
|
+
exactSource: {
|
|
3464
|
+
lossClass: 'exactSourceProjection',
|
|
3465
|
+
mode: 'preserved-source',
|
|
3466
|
+
supported: true,
|
|
3467
|
+
readiness: 'ready',
|
|
3468
|
+
lossKinds: [],
|
|
3469
|
+
categories: [],
|
|
3470
|
+
reason: exactSourceImports
|
|
3471
|
+
? 'At least one import carries matching source-preservation evidence, so projectNativeImportToSource can emit the original source exactly.'
|
|
3472
|
+
: 'Exact source projection is available when the import carries sourceText or source-preservation evidence whose hash matches the native source hash.',
|
|
3473
|
+
evidence: {
|
|
3474
|
+
imports: imports.length,
|
|
3475
|
+
importsWithExactSource: exactSourceImports
|
|
3476
|
+
},
|
|
3477
|
+
notes: ['Preserved source is the only currently lossless native-source projection mode in this facade.']
|
|
3478
|
+
},
|
|
3479
|
+
stubs: {
|
|
3480
|
+
lossClass: 'nativeSourceStubs',
|
|
3481
|
+
mode: 'native-source-stubs',
|
|
3482
|
+
supported: profile.supportsLightweightScan || declarationImports > 0,
|
|
3483
|
+
readiness: 'needs-review',
|
|
3484
|
+
lossKinds: uniqueStrings([
|
|
3485
|
+
'targetProjectionLoss',
|
|
3486
|
+
...(declarationImports || profile.supportsLightweightScan ? [] : ['declarationOnlyCoverage'])
|
|
3487
|
+
]),
|
|
3488
|
+
categories: uniqueStrings([
|
|
3489
|
+
'targetProjectionLoss',
|
|
3490
|
+
...(declarationImports || profile.supportsLightweightScan ? [] : ['declarationsOnly'])
|
|
3491
|
+
]),
|
|
3492
|
+
reason: 'Declaration stubs are emitted when exact source is unavailable or disabled; executable bodies and full type semantics remain unavailable.',
|
|
3493
|
+
evidence: {
|
|
3494
|
+
imports: imports.length,
|
|
3495
|
+
importsWithDeclarations: declarationImports
|
|
3496
|
+
},
|
|
3497
|
+
notes: uniqueStrings([
|
|
3498
|
+
'Stub projection is review-required and should not be treated as a round-trip proof.',
|
|
3499
|
+
...(projectionUnsupportedFeatureLossKinds(knownLossKinds).length
|
|
3500
|
+
? ['Known source-language feature losses may still be present behind preserved source or stubs.']
|
|
3501
|
+
: [])
|
|
3502
|
+
])
|
|
3503
|
+
}
|
|
3504
|
+
};
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
function projectionTargetCoverageEntry(profile, target, context) {
|
|
3508
|
+
const normalizedTarget = normalizeProjectionMatrixTargets([target])[0] ?? String(target);
|
|
3509
|
+
const declaredTargets = new Set(normalizeProjectionMatrixTargets(profile.projectionTargets ?? []));
|
|
3510
|
+
const adapterTargets = new Set((context.matchingAdapters ?? []).flatMap(adapterProjectionTargets));
|
|
3511
|
+
const sameSourceTarget = nativeLanguageCompileTarget(profile.language, profile.aliases) === normalizedTarget;
|
|
3512
|
+
const hasProjectionAdapter = declaredTargets.has(normalizedTarget) || adapterTargets.has(normalizedTarget);
|
|
3513
|
+
if (!hasProjectionAdapter) {
|
|
3514
|
+
return {
|
|
3515
|
+
target: normalizedTarget,
|
|
3516
|
+
lossClass: 'missingAdapter',
|
|
3517
|
+
supported: false,
|
|
3518
|
+
readiness: 'blocked',
|
|
3519
|
+
lossKinds: ['targetProjectionLoss'],
|
|
3520
|
+
categories: ['targetProjectionLoss'],
|
|
3521
|
+
reason: `No native-to-${normalizedTarget} projection adapter is declared for ${profile.language}.`,
|
|
3522
|
+
adapter: undefined,
|
|
3523
|
+
notes: ['The source can still be preserved or stubbed in its original language when import evidence supports that mode.']
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
const featureLossKinds = projectionUnsupportedFeatureLossKinds(context.knownLossKinds);
|
|
3528
|
+
if (featureLossKinds.length) {
|
|
3529
|
+
return {
|
|
3530
|
+
target: normalizedTarget,
|
|
3531
|
+
lossClass: 'unsupportedTargetFeatures',
|
|
3532
|
+
supported: true,
|
|
3533
|
+
readiness: 'needs-review',
|
|
3534
|
+
lossKinds: featureLossKinds,
|
|
3535
|
+
categories: uniqueStrings(featureLossKinds.map(nativeImportCategoryForLossKind)),
|
|
3536
|
+
reason: `${profile.language} coverage declares source features that this facade cannot prove lossless for ${normalizedTarget}: ${featureLossKinds.join(', ')}.`,
|
|
3537
|
+
adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
|
|
3538
|
+
notes: ['Use exact parser or semantic adapter evidence before treating this target projection as merge-ready.']
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
if (sameSourceTarget) {
|
|
3543
|
+
return {
|
|
3544
|
+
target: normalizedTarget,
|
|
3545
|
+
lossClass: 'exactSourceProjection',
|
|
3546
|
+
supported: true,
|
|
3547
|
+
readiness: 'ready',
|
|
3548
|
+
lossKinds: [],
|
|
3549
|
+
categories: [],
|
|
3550
|
+
reason: `${profile.language} can project to its source language exactly when source preservation evidence is available.`,
|
|
3551
|
+
adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
|
|
3552
|
+
notes: ['Without exact source text, the source projection falls back to declaration stubs.']
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
|
|
3556
|
+
return {
|
|
3557
|
+
target: normalizedTarget,
|
|
3558
|
+
lossClass: 'nativeSourceStubs',
|
|
3559
|
+
supported: true,
|
|
3560
|
+
readiness: 'needs-review',
|
|
3561
|
+
lossKinds: ['targetProjectionLoss'],
|
|
3562
|
+
categories: ['targetProjectionLoss'],
|
|
3563
|
+
reason: `${profile.language} declares a ${normalizedTarget} target slot, but this facade only exposes declaration-level native import projection evidence.`,
|
|
3564
|
+
adapter: projectionTargetAdapterName(profile, normalizedTarget, context.matchingAdapters),
|
|
3565
|
+
notes: ['Host-owned semantic adapters can upgrade this cell with stronger evidence.']
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
function projectionTargetLossMatrixSummary(languages) {
|
|
3570
|
+
const byLossClass = {};
|
|
3571
|
+
const sourceProjectionByLossClass = {};
|
|
3572
|
+
let targetEntries = 0;
|
|
3573
|
+
for (const language of languages) {
|
|
3574
|
+
for (const projection of [language.sourceProjection?.exactSource, language.sourceProjection?.stubs].filter(Boolean)) {
|
|
3575
|
+
sourceProjectionByLossClass[projection.lossClass] = (sourceProjectionByLossClass[projection.lossClass] ?? 0) + 1;
|
|
3576
|
+
}
|
|
3577
|
+
for (const target of language.targets ?? []) {
|
|
3578
|
+
targetEntries += 1;
|
|
3579
|
+
byLossClass[target.lossClass] = (byLossClass[target.lossClass] ?? 0) + 1;
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
return {
|
|
3583
|
+
languages: languages.length,
|
|
3584
|
+
targetEntries,
|
|
3585
|
+
byLossClass,
|
|
3586
|
+
sourceProjectionByLossClass,
|
|
3587
|
+
exactSourceProjection: (sourceProjectionByLossClass.exactSourceProjection ?? 0) + (byLossClass.exactSourceProjection ?? 0),
|
|
3588
|
+
nativeSourceStubs: (sourceProjectionByLossClass.nativeSourceStubs ?? 0) + (byLossClass.nativeSourceStubs ?? 0),
|
|
3589
|
+
unsupportedTargetFeatures: byLossClass.unsupportedTargetFeatures ?? 0,
|
|
3590
|
+
missingAdapters: byLossClass.missingAdapter ?? 0
|
|
3591
|
+
};
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
function countProjectionLossClasses(entries) {
|
|
3595
|
+
const counts = {};
|
|
3596
|
+
for (const entry of entries ?? []) {
|
|
3597
|
+
counts[entry.lossClass] = (counts[entry.lossClass] ?? 0) + 1;
|
|
3598
|
+
}
|
|
3599
|
+
return counts;
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
function hasExactSourceProjectionEvidence(imported) {
|
|
3603
|
+
const preservation = imported?.metadata?.sourcePreservation
|
|
3604
|
+
?? imported?.nativeSource?.metadata?.sourcePreservation
|
|
3605
|
+
?? imported?.nativeAst?.metadata?.sourcePreservation;
|
|
3606
|
+
const expectedHash = imported?.nativeSource?.sourceHash ?? imported?.nativeAst?.sourceHash ?? imported?.sourceHash;
|
|
3607
|
+
return Boolean(preservation?.summary?.exactSourceAvailable && (!expectedHash || preservation.sourceHash === expectedHash));
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
function hasNativeProjectionDeclarations(imported) {
|
|
3611
|
+
const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
|
|
3612
|
+
return (semanticIndex?.symbols?.length ?? 0) > 0;
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
function projectionUnsupportedFeatureLossKinds(lossKinds) {
|
|
3616
|
+
const unsupported = new Set([
|
|
3617
|
+
'macroExpansion',
|
|
3618
|
+
'macroHygiene',
|
|
3619
|
+
'preprocessor',
|
|
3620
|
+
'conditionalCompilation',
|
|
3621
|
+
'metaprogramming',
|
|
3622
|
+
'reflection',
|
|
3623
|
+
'dynamicRuntime',
|
|
3624
|
+
'dynamicDispatch',
|
|
3625
|
+
'generatedCode',
|
|
3626
|
+
'overloadResolution',
|
|
3627
|
+
'typeInference',
|
|
3628
|
+
'unsupportedSyntax',
|
|
3629
|
+
'unsupportedSemantic'
|
|
3630
|
+
]);
|
|
3631
|
+
return uniqueStrings((lossKinds ?? []).filter((kind) => unsupported.has(kind)));
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
function adapterProjectionTargets(adapter) {
|
|
3635
|
+
return normalizeProjectionMatrixTargets(
|
|
3636
|
+
adapter?.projectionTargets
|
|
3637
|
+
?? adapter?.coverage?.projectionTargets
|
|
3638
|
+
?? adapter?.metadata?.projectionTargets
|
|
3639
|
+
?? []
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
function projectionTargetAdapterName(profile, target, adapters = []) {
|
|
3644
|
+
const adapter = adapters.find((candidate) => adapterProjectionTargets(candidate).includes(target));
|
|
3645
|
+
if (adapter) return adapter.id ?? adapter.parser;
|
|
3646
|
+
return profile.projectionTargets?.includes(target) ? `frontier-native-source-${target}` : undefined;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
function nativeLanguageCompileTarget(language, aliases = []) {
|
|
3650
|
+
const ids = [language, ...aliases].map(normalizeNativeLanguageId);
|
|
3651
|
+
if (ids.includes('typescript')) return 'typescript';
|
|
3652
|
+
if (ids.includes('javascript')) return 'javascript';
|
|
3653
|
+
if (ids.includes('rust')) return 'rust';
|
|
3654
|
+
if (ids.includes('python')) return 'python';
|
|
3655
|
+
if (ids.includes('c')) return 'c';
|
|
3656
|
+
return undefined;
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
function nativeProjectionTargetsForLanguage(language, aliases = []) {
|
|
3660
|
+
const target = nativeLanguageCompileTarget(language, aliases);
|
|
3661
|
+
return target ? [target] : [];
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3374
3664
|
function nativeImportLanguageProfile(language, input = {}) {
|
|
3375
3665
|
const lossKinds = input.lossKinds ?? ['declarationOnlyCoverage', 'opaqueNative', 'sourceMapApproximation', 'sourcePreservation'];
|
|
3666
|
+
const aliases = uniqueStrings(input.aliases ?? []);
|
|
3376
3667
|
return Object.freeze({
|
|
3377
3668
|
language,
|
|
3378
|
-
aliases: Object.freeze(
|
|
3669
|
+
aliases: Object.freeze(aliases),
|
|
3379
3670
|
extensions: Object.freeze(uniqueStrings(input.extensions ?? [])),
|
|
3380
3671
|
supportsLightweightScan: input.supportsLightweightScan !== false,
|
|
3381
3672
|
parserAdapters: Object.freeze(uniqueStrings(input.parserAdapters ?? ['tree-sitter'])),
|
|
3382
|
-
projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ??
|
|
3673
|
+
projectionTargets: Object.freeze(uniqueStrings(input.projectionTargets ?? nativeProjectionTargetsForLanguage(language, aliases))),
|
|
3383
3674
|
knownLossKinds: Object.freeze(uniqueStrings(lossKinds)),
|
|
3384
3675
|
defaultReadiness: input.defaultReadiness ?? 'needs-review',
|
|
3385
3676
|
notes: Object.freeze(uniqueStrings(input.notes ?? ['lightweight scanner records declarations only; exact parser adapters must be injected by the host']))
|
|
@@ -3423,7 +3714,7 @@ function normalizeNativeImportLanguageProfile(profile, fallbackLanguage) {
|
|
|
3423
3714
|
extensions: uniqueStrings(profile.extensions ?? []),
|
|
3424
3715
|
supportsLightweightScan: profile.supportsLightweightScan !== false,
|
|
3425
3716
|
parserAdapters: uniqueStrings(profile.parserAdapters ?? []),
|
|
3426
|
-
projectionTargets: uniqueStrings(profile.projectionTargets ??
|
|
3717
|
+
projectionTargets: uniqueStrings(profile.projectionTargets ?? nativeProjectionTargetsForLanguage(language, profile.aliases ?? [])),
|
|
3427
3718
|
knownLossKinds: uniqueStrings(profile.knownLossKinds ?? profile.lossKinds ?? []),
|
|
3428
3719
|
defaultReadiness: profile.defaultReadiness ?? 'needs-review',
|
|
3429
3720
|
notes: uniqueStrings(profile.notes ?? [])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshift-labs/frontier-lang-compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@shapeshift-labs/frontier-lang-parser": "0.3.3",
|
|
64
64
|
"@shapeshift-labs/frontier-lang-python": "0.2.3",
|
|
65
65
|
"@shapeshift-labs/frontier-lang-rust": "0.2.3",
|
|
66
|
-
"@shapeshift-labs/frontier-lang-typescript": "0.3.
|
|
66
|
+
"@shapeshift-labs/frontier-lang-typescript": "0.3.4"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"typescript": "^5.9.3"
|