@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.
Files changed (48) hide show
  1. package/README.md +6 -2
  2. package/bench/smoke.mjs +15 -1
  3. package/bench/universal-fixture-suite.mjs +183 -0
  4. package/dist/declarations/native-project-admission.d.ts +115 -0
  5. package/dist/declarations/roundtrip-audit.d.ts +186 -0
  6. package/dist/declarations/roundtrip.d.ts +2 -53
  7. package/dist/declarations/runtime.d.ts +0 -11
  8. package/dist/declarations/semantic-history-records.d.ts +277 -0
  9. package/dist/declarations/semantic-history.d.ts +45 -92
  10. package/dist/declarations/semantic-slice-admission.d.ts +111 -0
  11. package/dist/declarations/semantic-slice.d.ts +36 -1
  12. package/dist/declarations/universal-conversion-plan.d.ts +59 -0
  13. package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +1 -0
  16. package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
  17. package/dist/internal/index-impl/createSemanticSlice.js +1 -1
  18. package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
  19. package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
  20. package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
  21. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
  22. package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
  23. package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
  24. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +46 -111
  25. package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
  26. package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
  27. package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
  28. package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
  29. package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
  30. package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
  31. package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
  32. package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
  33. package/dist/internal/index-impl/testSemanticSlice.js +4 -1
  34. package/dist/language-adapter-package-contracts.js +12 -57
  35. package/dist/language-adapter-package-rows.js +116 -0
  36. package/dist/universal-conversion-plan-summary.js +42 -0
  37. package/dist/universal-conversion-plan.js +46 -40
  38. package/dist/universal-runtime-capabilities.js +92 -0
  39. package/dist/universal-runtime-host-selectors.js +192 -0
  40. package/dist/universal-runtime-profiles.js +109 -0
  41. package/dist/universal-runtime-route-records.js +162 -0
  42. package/examples/js-frontier-rust-workbench-client.mjs +58 -1
  43. package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
  44. package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
  45. package/examples/js-frontier-rust-workbench-route.mjs +190 -0
  46. package/examples/js-frontier-rust-workbench-styles.mjs +3 -38
  47. package/examples/js-frontier-rust-workbench.mjs +22 -128
  48. 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
+ }