@shapeshift-labs/frontier-lang-compiler 0.2.66 → 0.2.68
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 +6 -2
- package/bench/smoke.mjs +15 -1
- package/bench/universal-fixture-suite.mjs +183 -0
- package/dist/declarations/native-project-admission.d.ts +115 -0
- package/dist/declarations/roundtrip-audit.d.ts +186 -0
- package/dist/declarations/roundtrip.d.ts +2 -53
- package/dist/declarations/runtime.d.ts +0 -11
- package/dist/declarations/semantic-history-records.d.ts +277 -0
- package/dist/declarations/semantic-history.d.ts +45 -92
- package/dist/declarations/semantic-slice-admission.d.ts +111 -0
- package/dist/declarations/semantic-slice.d.ts +36 -1
- package/dist/declarations/universal-conversion-plan.d.ts +59 -0
- package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
- package/dist/internal/index-impl/createSemanticSlice.js +1 -1
- package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
- package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
- package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
- package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
- package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
- package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
- package/dist/internal/index-impl/projectImportAdmissionSummaries.js +46 -111
- package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
- package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
- package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
- package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
- package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
- package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
- package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
- package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
- package/dist/internal/index-impl/testSemanticSlice.js +4 -1
- package/dist/language-adapter-package-contracts.js +12 -57
- package/dist/language-adapter-package-rows.js +116 -0
- package/dist/universal-conversion-plan-summary.js +42 -0
- package/dist/universal-conversion-plan.js +46 -40
- package/dist/universal-runtime-capabilities.js +92 -0
- package/dist/universal-runtime-host-selectors.js +192 -0
- package/dist/universal-runtime-profiles.js +109 -0
- package/dist/universal-runtime-route-records.js +162 -0
- package/examples/js-frontier-rust-workbench-client.mjs +58 -1
- package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
- package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
- package/examples/js-frontier-rust-workbench-route.mjs +190 -0
- package/examples/js-frontier-rust-workbench-styles.mjs +3 -38
- package/examples/js-frontier-rust-workbench.mjs +22 -128
- package/package.json +1 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
idFragment,
|
|
3
|
+
uniqueStrings
|
|
4
|
+
} from './native-import-utils.js';
|
|
5
|
+
|
|
6
|
+
export function runtimeRoute(sourceHost, targetHost, input) {
|
|
7
|
+
const requiredCapabilities = requirementsForRoute(input.requirements, sourceHost, targetHost);
|
|
8
|
+
const requirementRows = requiredCapabilities.map((capability) => runtimeCapabilityRequirement(capability, sourceHost, targetHost, input));
|
|
9
|
+
const adapterRequirements = requirementRows.filter((row) => row.adapterRequired).map((row) => row.adapterRequirement);
|
|
10
|
+
const satisfiedCapabilities = requirementRows.filter((row) => !row.adapterRequired).map((row) => row.capability);
|
|
11
|
+
const missingCapabilities = requirementRows.filter((row) => row.targetSupport === 'unavailable').map((row) => row.capability);
|
|
12
|
+
const blockers = requirementRows.filter((row) => row.sourceSupport === 'unavailable')
|
|
13
|
+
.map((row) => `Source host ${sourceHost.id} does not declare required runtime capability ${row.capability}.`);
|
|
14
|
+
const review = adapterRequirements.map((requirement) => requirement.reason);
|
|
15
|
+
const id = `runtime_${idFragment(sourceHost.id)}_to_${idFragment(targetHost.id)}_${idFragment(targetHost.target)}`;
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
source: runtimeHostSummary(sourceHost),
|
|
19
|
+
target: runtimeHostSummary(targetHost),
|
|
20
|
+
requiredCapabilities,
|
|
21
|
+
satisfiedCapabilities,
|
|
22
|
+
adapterRequirements,
|
|
23
|
+
missingCapabilities,
|
|
24
|
+
readiness: blockers.length ? 'blocked' : adapterRequirements.length ? 'needs-review' : 'ready',
|
|
25
|
+
blockers,
|
|
26
|
+
review: uniqueStrings(review),
|
|
27
|
+
metadata: {
|
|
28
|
+
generatedAt: input.generatedAt,
|
|
29
|
+
note: 'Runtime adapter requirements describe host API obligations only; parser, syntax, and target-projection evidence remain separate.'
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function requirementsForRoute(requirements, sourceHost, targetHost) {
|
|
35
|
+
return uniqueStrings((requirements ?? []).filter((requirement) => requirementApplies(requirement, sourceHost, targetHost))
|
|
36
|
+
.map((requirement) => requirement.capability));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function requirementApplies(requirement, sourceHost, targetHost) {
|
|
40
|
+
if (requirement.sourceHostId && requirement.sourceHostId !== sourceHost.id) return false;
|
|
41
|
+
if (requirement.targetHostId && requirement.targetHostId !== targetHost.id) return false;
|
|
42
|
+
if (requirement.sourceLanguage && !sourceHost.languageIds.includes(requirement.sourceLanguage)) return false;
|
|
43
|
+
if (requirement.sourceRuntime && requirement.sourceRuntime !== sourceHost.runtime && requirement.sourceRuntime !== sourceHost.id) return false;
|
|
44
|
+
if (requirement.target && requirement.target !== targetHost.target) return false;
|
|
45
|
+
if (requirement.targetRuntime && requirement.targetRuntime !== targetHost.runtime && requirement.targetRuntime !== targetHost.id) return false;
|
|
46
|
+
return Boolean(requirement.capability);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function runtimeCapabilityRequirement(capability, sourceHost, targetHost, input) {
|
|
50
|
+
const sourceCapability = sourceHost.capabilities[capability] ?? unavailableCapability(capability, sourceHost.id);
|
|
51
|
+
const targetCapability = targetHost.capabilities[capability] ?? unavailableCapability(capability, targetHost.id);
|
|
52
|
+
const compatible = runtimeCapabilityCompatible(sourceHost, targetHost, sourceCapability, targetCapability);
|
|
53
|
+
const reason = runtimeAdapterReason(capability, sourceHost, targetHost, sourceCapability, targetCapability);
|
|
54
|
+
return {
|
|
55
|
+
capability,
|
|
56
|
+
sourceSupport: sourceCapability.support,
|
|
57
|
+
targetSupport: targetCapability.support,
|
|
58
|
+
sourceBinding: sourceCapability.binding,
|
|
59
|
+
targetBinding: targetCapability.binding,
|
|
60
|
+
adapterRequired: !compatible,
|
|
61
|
+
adapterRequirement: compatible ? undefined : {
|
|
62
|
+
id: `runtime_adapter_${idFragment(sourceHost.id)}_${idFragment(targetHost.id)}_${idFragment(capability)}`,
|
|
63
|
+
capability,
|
|
64
|
+
adapterKind: runtimeAdapterKind(capability, sourceHost, targetHost),
|
|
65
|
+
sourceHost: sourceHost.id,
|
|
66
|
+
targetHost: targetHost.id,
|
|
67
|
+
sourceBinding: sourceCapability.binding,
|
|
68
|
+
targetBinding: targetCapability.binding,
|
|
69
|
+
required: true,
|
|
70
|
+
reason,
|
|
71
|
+
evidenceIds: uniqueStrings(input.requirements
|
|
72
|
+
.filter((requirement) => requirement.capability === capability && requirementApplies(requirement, sourceHost, targetHost))
|
|
73
|
+
.flatMap((requirement) => requirement.evidenceIds ?? []))
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runtimeCapabilityCompatible(sourceHost, targetHost, sourceCapability, targetCapability) {
|
|
79
|
+
if (sourceHost.id === targetHost.id) return true;
|
|
80
|
+
if (sourceCapability.support === 'unavailable' || targetCapability.support === 'unavailable') return false;
|
|
81
|
+
return sourceCapability.binding === targetCapability.binding;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function unavailableCapability(kind, hostId) {
|
|
85
|
+
return {
|
|
86
|
+
kind,
|
|
87
|
+
support: 'unavailable',
|
|
88
|
+
binding: `${hostId}.${kind}.unavailable`,
|
|
89
|
+
notes: []
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function runtimeHostSummary(host) {
|
|
94
|
+
return {
|
|
95
|
+
id: host.id,
|
|
96
|
+
language: host.language,
|
|
97
|
+
aliases: host.aliases,
|
|
98
|
+
languageIds: host.languageIds,
|
|
99
|
+
runtime: host.runtime,
|
|
100
|
+
host: host.host,
|
|
101
|
+
target: host.target,
|
|
102
|
+
capabilities: host.capabilities
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function runtimeAdapterKind(capability, sourceHost, targetHost) {
|
|
107
|
+
if (capability === 'fetch' && sourceHost.runtime === 'web' && targetHost.runtime === 'cli') return 'browser-fetch-to-cli-http-client';
|
|
108
|
+
if (capability === 'fetch' && sourceHost.runtime === 'cli' && targetHost.runtime === 'web') return 'cli-http-client-to-browser-fetch';
|
|
109
|
+
if (capability === 'timers') return `${sourceHost.runtime}-timers-to-${targetHost.runtime}-timers`;
|
|
110
|
+
if (capability === 'storage' && targetHost.runtime === 'cli') return 'browser-storage-to-cli-persistence';
|
|
111
|
+
if (capability === 'storage' && targetHost.runtime === 'web') return 'cli-storage-to-browser-storage';
|
|
112
|
+
if (capability === 'filesystem' && targetHost.runtime === 'web') return 'filesystem-to-browser-storage';
|
|
113
|
+
if (capability === 'filesystem') return `${sourceHost.runtime}-filesystem-to-${targetHost.runtime}-filesystem`;
|
|
114
|
+
if (capability === 'threading' && targetHost.runtime === 'web') return 'native-threading-to-web-workers';
|
|
115
|
+
if (capability === 'threading') return `${sourceHost.runtime}-threading-to-${targetHost.runtime}-threading`;
|
|
116
|
+
if (capability === 'dom' && targetHost.runtime === 'cli') return 'dom-to-headless-host';
|
|
117
|
+
if (capability === 'dom') return `${sourceHost.runtime}-dom-to-${targetHost.runtime}-dom`;
|
|
118
|
+
if (capability === 'async') return `${sourceHost.runtime}-async-to-${targetHost.runtime}-async`;
|
|
119
|
+
if (capability === 'ffi' && targetHost.runtime === 'web') return 'native-ffi-to-web-boundary';
|
|
120
|
+
if (capability === 'ffi') return `${sourceHost.runtime}-ffi-to-${targetHost.runtime}-ffi`;
|
|
121
|
+
return `${sourceHost.runtime}-${capability}-to-${targetHost.runtime}-${capability}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function runtimeAdapterReason(capability, sourceHost, targetHost, sourceCapability, targetCapability) {
|
|
125
|
+
if (targetCapability.support === 'unavailable') {
|
|
126
|
+
return `${sourceHost.id} requires ${capability}, but ${targetHost.id} does not provide a compatible host capability.`;
|
|
127
|
+
}
|
|
128
|
+
if (sourceCapability.support === 'unavailable') {
|
|
129
|
+
return `${sourceHost.id} declared ${capability}, but the source host profile marks it unavailable.`;
|
|
130
|
+
}
|
|
131
|
+
return `${sourceHost.id} uses ${sourceCapability.binding} for ${capability}; ${targetHost.id} exposes ${targetCapability.binding}, so a host adapter must preserve the runtime effect boundary.`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function runtimeCapabilityMatrixSummary(routes) {
|
|
135
|
+
const byReadiness = {};
|
|
136
|
+
const byCapability = {};
|
|
137
|
+
const byAdapterKind = {};
|
|
138
|
+
let adapterRequirements = 0;
|
|
139
|
+
let routesWithAdapters = 0;
|
|
140
|
+
let missingCapabilities = 0;
|
|
141
|
+
for (const route of routes) {
|
|
142
|
+
byReadiness[route.readiness] = (byReadiness[route.readiness] ?? 0) + 1;
|
|
143
|
+
if (route.adapterRequirements.length) routesWithAdapters += 1;
|
|
144
|
+
adapterRequirements += route.adapterRequirements.length;
|
|
145
|
+
missingCapabilities += route.missingCapabilities.length;
|
|
146
|
+
for (const capability of route.requiredCapabilities) {
|
|
147
|
+
byCapability[capability] = (byCapability[capability] ?? 0) + 1;
|
|
148
|
+
}
|
|
149
|
+
for (const requirement of route.adapterRequirements) {
|
|
150
|
+
byAdapterKind[requirement.adapterKind] = (byAdapterKind[requirement.adapterKind] ?? 0) + 1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
routes: routes.length,
|
|
155
|
+
routesWithAdapters,
|
|
156
|
+
adapterRequirements,
|
|
157
|
+
missingCapabilities,
|
|
158
|
+
byReadiness,
|
|
159
|
+
byCapability,
|
|
160
|
+
byAdapterKind
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -100,7 +100,17 @@ async function convertFromTypescript() {
|
|
|
100
100
|
sourceHash: payload.result.sourceHash,
|
|
101
101
|
symbolCount: payload.result.summary.symbols,
|
|
102
102
|
readiness: payload.result.summary.readiness,
|
|
103
|
-
projections: Object.keys(projections)
|
|
103
|
+
projections: Object.keys(projections),
|
|
104
|
+
semanticMergeReadiness: payload.result.routeExplanation?.semanticMerge?.readiness,
|
|
105
|
+
missingEvidence: payload.result.routeExplanation?.semanticMerge?.missingEvidence ?? [],
|
|
106
|
+
routeExplanation: (payload.result.routeExplanation?.routes ?? []).map((route) => ({
|
|
107
|
+
target: route.target,
|
|
108
|
+
mode: route.mode,
|
|
109
|
+
routeAction: route.routeAction,
|
|
110
|
+
readiness: route.readiness,
|
|
111
|
+
admissionAction: route.semanticMerge?.admissionAction,
|
|
112
|
+
missingEvidence: route.missingEvidence
|
|
113
|
+
}))
|
|
104
114
|
});
|
|
105
115
|
} catch (error) {
|
|
106
116
|
state.result = errorResult(error);
|
|
@@ -155,15 +165,21 @@ function projectionSummaryText(projection) {
|
|
|
155
165
|
function graphHtml(result) {
|
|
156
166
|
const summary = result.summary || {};
|
|
157
167
|
const frontier = result.frontier || {};
|
|
168
|
+
const routeExplanation = result.routeExplanation || {};
|
|
169
|
+
const semanticMerge = routeExplanation.semanticMerge || {};
|
|
158
170
|
const symbols = frontier.symbols || [];
|
|
159
171
|
const relations = frontier.relations || [];
|
|
172
|
+
const missingEvidence = semanticMerge.missingEvidence || [];
|
|
160
173
|
return [
|
|
161
174
|
'<div class="chipRow">',
|
|
162
175
|
chip(summary.readiness, readinessClass(summary.readiness)),
|
|
176
|
+
chip('merge ' + (semanticMerge.readiness || 'unknown'), readinessClass(semanticMerge.readiness)),
|
|
177
|
+
chip(String(missingEvidence.length) + ' missing evidence', missingEvidence.length ? 'review' : 'ready'),
|
|
163
178
|
chip(String(summary.losses || 0) + ' losses', 'review'),
|
|
164
179
|
chip(String(summary.patchHints || 0) + ' patch hints', 'ready'),
|
|
165
180
|
chip(projectionTargets(result), 'review'),
|
|
166
181
|
'</div>',
|
|
182
|
+
routeExplanationHtml(routeExplanation),
|
|
167
183
|
'<div class="sectionTitle">Symbols</div>',
|
|
168
184
|
'<div class="graphGrid">',
|
|
169
185
|
...symbols.map((symbol) => nodeCard(symbol)),
|
|
@@ -182,6 +198,47 @@ function projectionTargets(result) {
|
|
|
182
198
|
return (result.sourceLanguage || '-') + ' -> ' + (targets.length ? targets.join('/') : '-');
|
|
183
199
|
}
|
|
184
200
|
|
|
201
|
+
function routeExplanationHtml(explanation) {
|
|
202
|
+
const routes = explanation.routes || [];
|
|
203
|
+
if (!routes.length) return '';
|
|
204
|
+
const semanticMerge = explanation.semanticMerge || {};
|
|
205
|
+
return [
|
|
206
|
+
'<div class="sectionTitle">Route Explanation</div>',
|
|
207
|
+
'<section class="routeSummary">',
|
|
208
|
+
'<strong>' + escapeHtml('Semantic merge: ' + (semanticMerge.readiness || 'unknown')) + '</strong>',
|
|
209
|
+
'<span>' + escapeHtml((semanticMerge.admissionAction || 'prioritize') + ' / score ' + String(semanticMerge.score ?? 0)) + '</span>',
|
|
210
|
+
'</section>',
|
|
211
|
+
'<div class="routeGrid">',
|
|
212
|
+
...routes.map((route) => routeCard(route)),
|
|
213
|
+
'</div>'
|
|
214
|
+
].join('');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function routeCard(route) {
|
|
218
|
+
const semanticMerge = route.semanticMerge || {};
|
|
219
|
+
const missing = route.missingEvidenceDetails || [];
|
|
220
|
+
return '<article class="routeCard">' +
|
|
221
|
+
'<div class="routeCardHeader">' +
|
|
222
|
+
'<strong>' + escapeHtml((route.sourceLanguage || '-') + ' -> ' + (route.target || '-')) + '</strong>' +
|
|
223
|
+
chip(semanticMerge.readiness || route.readiness, readinessClass(semanticMerge.readiness || route.readiness)) +
|
|
224
|
+
'</div>' +
|
|
225
|
+
'<p>' + escapeHtml(route.explanation || '') + '</p>' +
|
|
226
|
+
'<dl class="routeFacts">' +
|
|
227
|
+
factHtml('Mode', route.mode) +
|
|
228
|
+
factHtml('Action', route.routeAction) +
|
|
229
|
+
factHtml('Adapter', route.adapter || 'none') +
|
|
230
|
+
factHtml('Admission', semanticMerge.admissionAction || 'prioritize') +
|
|
231
|
+
'</dl>' +
|
|
232
|
+
'<div class="missingEvidenceList">' +
|
|
233
|
+
missing.map((gap) => '<span title="' + escapeHtml(gap.summary) + '">' + escapeHtml(gap.label) + '</span>').join('') +
|
|
234
|
+
'</div>' +
|
|
235
|
+
'</article>';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function factHtml(label, value) {
|
|
239
|
+
return '<div><dt>' + escapeHtml(label) + '</dt><dd>' + escapeHtml(value || '-') + '</dd></div>';
|
|
240
|
+
}
|
|
241
|
+
|
|
185
242
|
function nodeCard(symbol) {
|
|
186
243
|
return '<article class="nodeCard" data-graph-node="' + escapeHtml(symbol.id || symbol.name) + '">' +
|
|
187
244
|
'<strong>' + escapeHtml(symbol.name) + '</strong>' +
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compileNativeSource,
|
|
3
|
+
createSemanticImportSidecar,
|
|
4
|
+
createUniversalConversionPlan,
|
|
5
|
+
importNativeSource,
|
|
6
|
+
queryUniversalConversionPlan,
|
|
7
|
+
writeUniversalAstJson
|
|
8
|
+
} from '../dist/index.js';
|
|
9
|
+
import {
|
|
10
|
+
createTsToPythonWorkbenchAdapter,
|
|
11
|
+
createTsToRustWorkbenchAdapter
|
|
12
|
+
} from './js-frontier-rust-workbench-adapters.mjs';
|
|
13
|
+
import { conversionBounds } from './js-frontier-rust-workbench-bounds.mjs';
|
|
14
|
+
import {
|
|
15
|
+
explainRoute,
|
|
16
|
+
routeExplanationSummary
|
|
17
|
+
} from './js-frontier-rust-workbench-route.mjs';
|
|
18
|
+
|
|
19
|
+
export function convertSource(source, options = {}) {
|
|
20
|
+
const sourceLanguage = normalizeSourceLanguage(options.sourceLanguage);
|
|
21
|
+
const targets = ['rust', 'python'];
|
|
22
|
+
const imported = importNativeSource({
|
|
23
|
+
language: sourceLanguage,
|
|
24
|
+
sourcePath: `workbench/input.${sourceExtension(sourceLanguage)}`,
|
|
25
|
+
sourceText: source
|
|
26
|
+
});
|
|
27
|
+
const targetAdapters = [createTsToRustWorkbenchAdapter(), createTsToPythonWorkbenchAdapter()];
|
|
28
|
+
const projections = Object.fromEntries(targets.map((target) => {
|
|
29
|
+
const projection = compileNativeSource(imported, {
|
|
30
|
+
target,
|
|
31
|
+
targetPath: `workbench/output.${targetExtension(target)}`,
|
|
32
|
+
targetAdapters,
|
|
33
|
+
emitOnBlocked: true
|
|
34
|
+
});
|
|
35
|
+
return [target, projectionSummary(projection, { sourceLanguage, target })];
|
|
36
|
+
}));
|
|
37
|
+
const conversionPlan = createUniversalConversionPlan({
|
|
38
|
+
generatedAt: 0,
|
|
39
|
+
imports: [imported],
|
|
40
|
+
targetAdapters,
|
|
41
|
+
targets
|
|
42
|
+
});
|
|
43
|
+
const routes = targets.map((target) => explainRoute(
|
|
44
|
+
queryUniversalConversionPlan(conversionPlan, { sourceLanguage, target }).bestRoute,
|
|
45
|
+
{ projection: projections[target], sourceLanguage, target }
|
|
46
|
+
));
|
|
47
|
+
const sidecar = createSemanticImportSidecar(imported, { generatedAt: 0, targetPath: projections.rust?.targetPath });
|
|
48
|
+
return {
|
|
49
|
+
sourceHash: imported.nativeSource.sourceHash,
|
|
50
|
+
sourceLanguage,
|
|
51
|
+
summary: {
|
|
52
|
+
readiness: sidecar.summary.readiness,
|
|
53
|
+
symbols: imported.semanticIndex.symbols.length,
|
|
54
|
+
sourceMapMappings: imported.sourceMaps[0]?.mappings.length ?? 0,
|
|
55
|
+
losses: imported.losses.length,
|
|
56
|
+
patchHints: sidecar.patchHints.length
|
|
57
|
+
},
|
|
58
|
+
frontier: {
|
|
59
|
+
universalAst: universalAstSummary(imported),
|
|
60
|
+
semanticIndexId: imported.semanticIndex.id,
|
|
61
|
+
sidecarId: sidecar.id,
|
|
62
|
+
symbols: imported.semanticIndex.symbols.map(symbolSummary),
|
|
63
|
+
relations: relationSummaries(imported),
|
|
64
|
+
losses: imported.losses.map(lossSummary),
|
|
65
|
+
patchHints: sidecar.patchHints.map((hint) => ({
|
|
66
|
+
id: hint.id,
|
|
67
|
+
readiness: hint.readiness,
|
|
68
|
+
operations: hint.supportedOperations,
|
|
69
|
+
ownershipKey: hint.ownershipKey
|
|
70
|
+
}))
|
|
71
|
+
},
|
|
72
|
+
bounds: conversionBounds(sourceLanguage, targets.join('/')),
|
|
73
|
+
routeExplanation: routeExplanationSummary(conversionPlan, routes),
|
|
74
|
+
projection: projections.rust,
|
|
75
|
+
projections
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function projectionSummary(projection, { sourceLanguage, target }) {
|
|
80
|
+
return {
|
|
81
|
+
target,
|
|
82
|
+
targetLanguage: target,
|
|
83
|
+
sourceLanguage,
|
|
84
|
+
targetPath: projection.sourceMap?.targetPath,
|
|
85
|
+
mode: projection.outputMode,
|
|
86
|
+
readiness: projection.readiness.readiness,
|
|
87
|
+
ok: projection.ok,
|
|
88
|
+
output: projection.output,
|
|
89
|
+
sourceMapMappings: projection.sourceMap?.mappings.length ?? 0,
|
|
90
|
+
targetAdapter: projection.metadata?.targetProjectionAdapterId,
|
|
91
|
+
evidence: (projection.evidence ?? []).map(evidenceSummary)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function evidenceSummary(record) {
|
|
96
|
+
return {
|
|
97
|
+
id: record.id,
|
|
98
|
+
kind: record.kind,
|
|
99
|
+
status: record.status,
|
|
100
|
+
summary: record.summary,
|
|
101
|
+
adapterId: record.metadata?.adapterId
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function universalAstSummary(imported) {
|
|
106
|
+
const summary = { valid: true, validationError: undefined };
|
|
107
|
+
let parsed = imported.universalAst;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(writeUniversalAstJson(imported.universalAst));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
summary.valid = false;
|
|
112
|
+
summary.validationError = String(error.message || error);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
...summary,
|
|
116
|
+
kind: parsed.kind,
|
|
117
|
+
id: parsed.id,
|
|
118
|
+
nativeSources: parsed.nativeSources?.length ?? 0,
|
|
119
|
+
semanticSymbols: parsed.semanticIndex?.symbols?.length ?? 0,
|
|
120
|
+
sourceMaps: parsed.sourceMaps?.length ?? 0,
|
|
121
|
+
layers: Object.keys(parsed.layers ?? {})
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function symbolSummary(symbol) {
|
|
126
|
+
return {
|
|
127
|
+
id: symbol.id,
|
|
128
|
+
name: symbol.name,
|
|
129
|
+
kind: symbol.kind,
|
|
130
|
+
language: symbol.language,
|
|
131
|
+
region: symbol.metadata?.ownershipRegionKind,
|
|
132
|
+
ownershipKey: symbol.metadata?.ownershipRegionKey
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function relationSummaries(imported) {
|
|
137
|
+
const names = new Map(imported.semanticIndex.symbols.map((symbol) => [symbol.id, symbol.name]));
|
|
138
|
+
return imported.semanticIndex.relations.map((relation) => ({
|
|
139
|
+
id: relation.id,
|
|
140
|
+
predicate: relation.predicate,
|
|
141
|
+
label: `${names.get(relation.sourceId) ?? relation.sourceId} ${relation.predicate} ${names.get(relation.targetId) ?? relation.targetId}`
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function lossSummary(loss) {
|
|
146
|
+
return { id: loss.id, kind: loss.kind, severity: loss.severity, message: loss.message };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeSourceLanguage(language) {
|
|
150
|
+
return 'typescript';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function targetExtension(target) {
|
|
154
|
+
if (target === 'rust') return 'rs';
|
|
155
|
+
if (target === 'python') return 'py';
|
|
156
|
+
return 'txt';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function sourceExtension(language) {
|
|
160
|
+
return 'ts';
|
|
161
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export function workbenchRouteStyles() {
|
|
2
|
+
return `
|
|
3
|
+
.routeSummary {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
gap: 10px;
|
|
8
|
+
border: 1px solid var(--line);
|
|
9
|
+
border-radius: 8px;
|
|
10
|
+
background: #12171a;
|
|
11
|
+
padding: 10px;
|
|
12
|
+
}
|
|
13
|
+
.routeSummary strong {
|
|
14
|
+
min-width: 0;
|
|
15
|
+
overflow-wrap: anywhere;
|
|
16
|
+
font-size: 13px;
|
|
17
|
+
}
|
|
18
|
+
.routeSummary span {
|
|
19
|
+
flex: 0 0 auto;
|
|
20
|
+
color: var(--muted);
|
|
21
|
+
font-family: var(--mono);
|
|
22
|
+
font-size: 11px;
|
|
23
|
+
}
|
|
24
|
+
.routeGrid {
|
|
25
|
+
display: grid;
|
|
26
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
27
|
+
gap: 8px;
|
|
28
|
+
margin-top: 8px;
|
|
29
|
+
}
|
|
30
|
+
.routeCard {
|
|
31
|
+
border: 1px solid var(--line);
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
background: #14191d;
|
|
34
|
+
padding: 10px;
|
|
35
|
+
}
|
|
36
|
+
.routeCardHeader {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
gap: 8px;
|
|
41
|
+
}
|
|
42
|
+
.routeCardHeader strong {
|
|
43
|
+
min-width: 0;
|
|
44
|
+
overflow-wrap: anywhere;
|
|
45
|
+
font-size: 13px;
|
|
46
|
+
}
|
|
47
|
+
.routeCard p {
|
|
48
|
+
margin: 8px 0;
|
|
49
|
+
color: var(--muted);
|
|
50
|
+
font-size: 12px;
|
|
51
|
+
line-height: 1.45;
|
|
52
|
+
overflow-wrap: anywhere;
|
|
53
|
+
}
|
|
54
|
+
.routeFacts {
|
|
55
|
+
display: grid;
|
|
56
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
57
|
+
gap: 6px;
|
|
58
|
+
margin: 0 0 8px;
|
|
59
|
+
}
|
|
60
|
+
.routeFacts div {
|
|
61
|
+
min-width: 0;
|
|
62
|
+
}
|
|
63
|
+
.routeFacts dt {
|
|
64
|
+
color: var(--faint);
|
|
65
|
+
font-size: 10px;
|
|
66
|
+
text-transform: uppercase;
|
|
67
|
+
}
|
|
68
|
+
.routeFacts dd {
|
|
69
|
+
margin: 2px 0 0;
|
|
70
|
+
color: var(--text);
|
|
71
|
+
font-family: var(--mono);
|
|
72
|
+
font-size: 11px;
|
|
73
|
+
overflow-wrap: anywhere;
|
|
74
|
+
}
|
|
75
|
+
.missingEvidenceList {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-wrap: wrap;
|
|
78
|
+
gap: 5px;
|
|
79
|
+
}
|
|
80
|
+
.missingEvidenceList span {
|
|
81
|
+
border: 1px solid rgba(231, 183, 95, .45);
|
|
82
|
+
border-radius: 999px;
|
|
83
|
+
padding: 3px 6px;
|
|
84
|
+
color: var(--amber);
|
|
85
|
+
font-size: 10px;
|
|
86
|
+
}
|
|
87
|
+
.edgeList {
|
|
88
|
+
display: grid;
|
|
89
|
+
gap: 6px;
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 0;
|
|
92
|
+
list-style: none;
|
|
93
|
+
}
|
|
94
|
+
.edgeList li {
|
|
95
|
+
border-left: 2px solid var(--blue);
|
|
96
|
+
background: rgba(109, 180, 232, .08);
|
|
97
|
+
padding: 6px 8px;
|
|
98
|
+
color: var(--muted);
|
|
99
|
+
font-size: 12px;
|
|
100
|
+
overflow-wrap: anywhere;
|
|
101
|
+
}
|
|
102
|
+
.boundsGrid {
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
105
|
+
gap: 8px;
|
|
106
|
+
}
|
|
107
|
+
.boundCard {
|
|
108
|
+
border: 1px solid var(--line);
|
|
109
|
+
border-radius: 8px;
|
|
110
|
+
background: #12171a;
|
|
111
|
+
padding: 10px;
|
|
112
|
+
}
|
|
113
|
+
.boundCard strong {
|
|
114
|
+
display: block;
|
|
115
|
+
margin-bottom: 8px;
|
|
116
|
+
font-size: 12px;
|
|
117
|
+
}
|
|
118
|
+
.boundCard ul {
|
|
119
|
+
margin: 0;
|
|
120
|
+
padding-left: 16px;
|
|
121
|
+
color: var(--muted);
|
|
122
|
+
font-size: 11px;
|
|
123
|
+
line-height: 1.45;
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
}
|