@shapeshift-labs/frontier-lang-compiler 0.2.65 → 0.2.67

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 (67) hide show
  1. package/README.md +37 -8
  2. package/bench/smoke.mjs +15 -1
  3. package/bench/universal-fixture-suite.mjs +183 -0
  4. package/dist/declarations/import-adapter-core.d.ts +3 -0
  5. package/dist/declarations/native-project-admission.d.ts +133 -0
  6. package/dist/declarations/roundtrip-audit.d.ts +177 -0
  7. package/dist/declarations/roundtrip.d.ts +2 -53
  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-merge-candidates.d.ts +200 -0
  11. package/dist/declarations/semantic-merge-conflicts.d.ts +12 -0
  12. package/dist/declarations/semantic-sidecar.d.ts +8 -3
  13. package/dist/declarations/semantic-slice-admission.d.ts +111 -0
  14. package/dist/declarations/semantic-slice.d.ts +36 -1
  15. package/dist/declarations/universal-conversion-plan.d.ts +59 -0
  16. package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +2 -0
  19. package/dist/internal/index-impl/attachExternalOwnership.js +18 -10
  20. package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
  21. package/dist/internal/index-impl/createSemanticImportSidecar.js +6 -0
  22. package/dist/internal/index-impl/createSemanticSlice.js +4 -3
  23. package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
  24. package/dist/internal/index-impl/diffNativeSourceImports.js +3 -2
  25. package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
  26. package/dist/internal/index-impl/externalSemanticBase.js +1 -0
  27. package/dist/internal/index-impl/importExternalSemanticIndex.js +4 -0
  28. package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
  29. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
  30. package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
  31. package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
  32. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +77 -117
  33. package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
  34. package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
  35. package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
  36. package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
  37. package/dist/internal/index-impl/semanticMergeCandidateRecordInternals.js +314 -0
  38. package/dist/internal/index-impl/semanticMergeCandidateRecords.js +241 -0
  39. package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
  40. package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
  41. package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
  42. package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
  43. package/dist/internal/index-impl/testSemanticSlice.js +4 -1
  44. package/dist/internal/index-impl/withExternalEmptyLoss.js +1 -0
  45. package/dist/language-adapter-package-contracts.js +12 -57
  46. package/dist/language-adapter-package-rows.js +116 -0
  47. package/dist/lightweight-dependency-language.js +8 -0
  48. package/dist/native-region-scanner-core.js +42 -10
  49. package/dist/native-region-scanner-js-helpers.js +2 -0
  50. package/dist/native-region-scanner-js-imports.js +111 -0
  51. package/dist/native-region-scanner-js.js +111 -28
  52. package/dist/universal-conversion-plan-summary.js +42 -0
  53. package/dist/universal-conversion-plan.js +46 -40
  54. package/dist/universal-runtime-capabilities.js +92 -0
  55. package/dist/universal-runtime-host-selectors.js +192 -0
  56. package/dist/universal-runtime-profiles.js +109 -0
  57. package/dist/universal-runtime-route-records.js +162 -0
  58. package/examples/js-frontier-rust-workbench-adapters.mjs +89 -0
  59. package/examples/js-frontier-rust-workbench-bounds.mjs +4 -3
  60. package/examples/js-frontier-rust-workbench-client.mjs +135 -59
  61. package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
  62. package/examples/js-frontier-rust-workbench-html.mjs +20 -13
  63. package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
  64. package/examples/js-frontier-rust-workbench-route.mjs +190 -0
  65. package/examples/js-frontier-rust-workbench-styles.mjs +12 -54
  66. package/examples/js-frontier-rust-workbench.mjs +54 -214
  67. package/package.json +1 -1
@@ -0,0 +1,190 @@
1
+ export function routeExplanationSummary(plan, routes) {
2
+ const missingEvidence = uniqueStrings(routes.flatMap((route) => route.missingEvidence));
3
+ const blockers = uniqueStrings(routes.flatMap((route) => route.blockers));
4
+ const review = uniqueStrings(routes.flatMap((route) => route.review));
5
+ const readiness = aggregateReadiness(routes.map((route) => route.semanticMerge.readiness));
6
+ const routeScores = routes.map((route) => route.semanticMerge.score).filter((score) => Number.isFinite(score));
7
+ return {
8
+ kind: 'frontier.workbench.routeExplanation',
9
+ version: 1,
10
+ planId: plan.id,
11
+ summary: {
12
+ routes: routes.length,
13
+ targetAdapterRoutes: plan.summary.targetAdapterRoutes,
14
+ missingEvidence: missingEvidence.length,
15
+ blockers: blockers.length,
16
+ reviewReasons: review.length,
17
+ autoMergeClaims: plan.summary.autoMergeClaims,
18
+ semanticEquivalenceClaims: plan.summary.semanticEquivalenceClaims
19
+ },
20
+ semanticMerge: {
21
+ readiness,
22
+ ready: readiness === 'ready' && missingEvidence.length === 0 && blockers.length === 0,
23
+ admissionAction: aggregateAdmissionAction(routes.map((route) => route.semanticMerge.admissionAction)),
24
+ score: routeScores.length ? Math.min(...routeScores) : 0,
25
+ risk: aggregateRisk(routes.map((route) => route.semanticMerge.risk)),
26
+ missingEvidence,
27
+ missingEvidenceDetails: missingEvidence.map(evidenceGapDetail),
28
+ blockers,
29
+ review,
30
+ autoMergeClaim: false,
31
+ semanticEquivalenceClaim: false
32
+ },
33
+ routes
34
+ };
35
+ }
36
+
37
+ export function explainRoute(route, context) {
38
+ const missingEvidence = routeMissingEvidence(route, context.projection);
39
+ const semanticReadiness = routeSemanticMergeReadiness(route, missingEvidence);
40
+ const semanticAdmissionAction = routeSemanticAdmissionAction(route, semanticReadiness, missingEvidence);
41
+ return {
42
+ id: route?.id ?? `conversion_${context.sourceLanguage}_to_${context.target}`,
43
+ sourceLanguage: route?.sourceLanguage ?? context.sourceLanguage,
44
+ target: route?.target ?? context.target,
45
+ mode: route?.mode ?? 'blocked',
46
+ routeAction: route?.routeAction ?? 'blocked',
47
+ readiness: route?.readiness ?? 'blocked',
48
+ priority: route?.priority ?? 'blocker',
49
+ adapter: route?.adapter,
50
+ adapterKind: route?.adapterKind,
51
+ targetPath: context.projection?.targetPath,
52
+ missingEvidence,
53
+ missingEvidenceDetails: missingEvidence.map(evidenceGapDetail),
54
+ blockers: route?.blockers ?? ['No conversion route was planned.'],
55
+ review: route?.review ?? [],
56
+ evidence: {
57
+ imports: route?.evidence?.imports ?? 0,
58
+ importReadiness: route?.evidence?.importReadiness,
59
+ symbols: route?.evidence?.symbols ?? 0,
60
+ sourceMapMappings: route?.evidence?.sourceMapMappings ?? 0,
61
+ parserRows: route?.evidence?.parserRows ?? 0,
62
+ mergeReadyParsers: route?.evidence?.mergeReadyParsers ?? 0,
63
+ targetSupported: route?.evidence?.targetSupported === true,
64
+ targetAdapter: route?.evidence?.targetAdapter ?? route?.adapter,
65
+ projectionEvidenceIds: (context.projection?.evidence ?? []).map((record) => record.id).filter(Boolean)
66
+ },
67
+ semanticMerge: {
68
+ readiness: semanticReadiness,
69
+ score: route?.mergeScore?.value ?? 0,
70
+ risk: routeSemanticRisk(route, semanticReadiness),
71
+ admissionAction: semanticAdmissionAction,
72
+ penalties: uniqueStrings([
73
+ ...(route?.mergeScore?.penalties ?? []),
74
+ ...missingEvidence.map((key) => `Missing evidence: ${key}`)
75
+ ]).slice(0, 6),
76
+ autoMergeClaim: route?.autoMergeClaim === true,
77
+ semanticEquivalenceClaim: route?.semanticEquivalenceClaim === true
78
+ },
79
+ explanation: routeExplanationText(route, missingEvidence, semanticReadiness)
80
+ };
81
+ }
82
+
83
+ function routeMissingEvidence(route, projection) {
84
+ const missing = new Set(route?.missingEvidence ?? []);
85
+ if ((route?.evidence?.mergeReadyParsers ?? 0) === 0) missing.add('merge-ready-parser');
86
+ if (!hasProofEvidence(projection?.evidence ?? [])) missing.add('proof-or-replay-evidence');
87
+ if (!hasRuntimeAdapterEvidence(projection?.evidence ?? [])) missing.add('runtime-adapter-evidence');
88
+ return [...missing].sort();
89
+ }
90
+
91
+ function routeExplanationText(route, missingEvidence, semanticReadiness) {
92
+ if (!route) return 'No conversion route was planned for this target.';
93
+ const adapter = route.adapter ? ` through ${route.adapter}` : '';
94
+ const evidence = missingEvidence.length ? ` missing ${missingEvidence.join(', ')}` : ' with required evidence attached';
95
+ return `${route.sourceLanguage} -> ${route.target} uses ${route.mode}${adapter}; semantic merge readiness is ${semanticReadiness}${evidence}.`;
96
+ }
97
+
98
+ function evidenceGapDetail(key) {
99
+ const details = {
100
+ 'merge-ready-parser': {
101
+ key,
102
+ label: 'Merge-ready parser evidence',
103
+ status: 'missing',
104
+ summary: 'Attach exact parser, source range, token/trivia, and feature coverage evidence before merge admission.'
105
+ },
106
+ 'proof-or-replay-evidence': {
107
+ key,
108
+ label: 'Proof or replay evidence',
109
+ status: 'missing',
110
+ summary: 'Attach proof, oracle, replay, or behavior test evidence for the projected route.'
111
+ },
112
+ 'runtime-adapter-evidence': {
113
+ key,
114
+ label: 'Runtime adapter evidence',
115
+ status: 'missing',
116
+ summary: 'Attach runtime adapter evidence for imports, effects, host APIs, package modules, and target runtime shims.'
117
+ },
118
+ 'source-preservation-hash': {
119
+ key,
120
+ label: 'Source preservation hash',
121
+ status: 'missing',
122
+ summary: 'Attach exact source preservation evidence so source text can be traced to merge candidates.'
123
+ },
124
+ 'target-adapter': {
125
+ key,
126
+ label: 'Target adapter',
127
+ status: 'missing',
128
+ summary: 'Add a source-to-target projection adapter for emitted target code.'
129
+ },
130
+ 'target-adapter-evidence': {
131
+ key,
132
+ label: 'Target adapter evidence',
133
+ status: 'missing',
134
+ summary: 'Attach run evidence from the host-owned target projection adapter.'
135
+ }
136
+ };
137
+ return details[key] ?? { key, label: key, status: 'missing', summary: 'Attach route evidence before merge admission.' };
138
+ }
139
+
140
+ function hasProofEvidence(evidence) {
141
+ return evidence.some((record) => /proof|oracle|replay|behavior|test|verification/.test(String(record.kind ?? record.metadata?.kind ?? record.type ?? '')));
142
+ }
143
+
144
+ function hasRuntimeAdapterEvidence(evidence) {
145
+ return evidence.some((record) => {
146
+ const capabilities = record.metadata?.capabilities ?? [];
147
+ return record.metadata?.runtimeAdapterEvidence === true
148
+ || capabilities.includes('runtime-adapter')
149
+ || /runtime/.test(String(record.kind ?? record.metadata?.kind ?? record.type ?? ''));
150
+ });
151
+ }
152
+
153
+ function uniqueStrings(values) {
154
+ return [...new Set(values.filter((value) => typeof value === 'string' && value.length > 0))];
155
+ }
156
+
157
+ function aggregateReadiness(values) {
158
+ const rank = { ready: 0, 'ready-with-losses': 1, 'needs-review': 2, blocked: 3 };
159
+ return values.reduce((current, value) => (rank[value] ?? 3) > (rank[current] ?? 3) ? value : current, 'ready');
160
+ }
161
+
162
+ function routeSemanticMergeReadiness(route, missingEvidence) {
163
+ const base = route?.mergeScore?.readiness ?? route?.readiness ?? 'blocked';
164
+ if (!missingEvidence.length || base === 'blocked') return base;
165
+ return aggregateReadiness([base, 'needs-review']);
166
+ }
167
+
168
+ function routeSemanticAdmissionAction(route, readiness, missingEvidence) {
169
+ if ((route?.mergeScore?.action ?? route?.admissionAction) === 'reject' || readiness === 'blocked') return 'reject';
170
+ if (readiness === 'ready' && missingEvidence.length === 0) return route?.mergeScore?.action ?? 'admit';
171
+ return 'prioritize';
172
+ }
173
+
174
+ function routeSemanticRisk(route, readiness) {
175
+ if (readiness === 'blocked') return 'high';
176
+ if (readiness === 'needs-review') return 'medium';
177
+ return route?.mergeScore?.risk ?? 'low';
178
+ }
179
+
180
+ function aggregateAdmissionAction(values) {
181
+ if (values.includes('reject')) return 'reject';
182
+ if (values.includes('prioritize')) return 'prioritize';
183
+ return values[0] ?? 'prioritize';
184
+ }
185
+
186
+ function aggregateRisk(values) {
187
+ if (values.includes('high')) return 'high';
188
+ if (values.includes('medium')) return 'medium';
189
+ return values[0] ?? 'low';
190
+ }
@@ -1,3 +1,5 @@
1
+ import { workbenchRouteStyles } from './js-frontier-rust-workbench-route-styles.mjs';
2
+
1
3
  export function workbenchStyles() {
2
4
  return `
3
5
  :root {
@@ -69,8 +71,8 @@ body {
69
71
  .brand h1 { margin: 0; font-size: 15px; line-height: 1.1; letter-spacing: 0; }
70
72
  .brand p { margin: 4px 0 0; color: var(--muted); font-size: 12px; }
71
73
 
72
- .topActions { display: flex; align-items: center; gap: 8px; }
73
- .iconButton, .segmented button, .modeGroup button, .runButton {
74
+ .topActions { display: flex; align-items: center; gap: 8px; margin: 0; }
75
+ .iconButton, .segmented button, .runButton {
74
76
  height: 32px;
75
77
  border: 1px solid var(--line);
76
78
  background: var(--panel-2);
@@ -79,17 +81,7 @@ body {
79
81
  }
80
82
  .iconButton { width: 34px; font-size: 16px; cursor: pointer; }
81
83
  .runButton { min-width: 64px; padding: 0 14px; cursor: pointer; color: var(--green); }
82
- .modeGroup { display: flex; }
83
- .modeGroup button {
84
- width: 84px;
85
- border-radius: 0;
86
- font-size: 12px;
87
- cursor: pointer;
88
- }
89
- .modeGroup button:first-child { border-radius: 7px 0 0 7px; }
90
- .modeGroup button:last-child { border-radius: 0 7px 7px 0; border-left: 0; }
91
- .modeGroup button[aria-pressed="true"] { background: #213127; color: var(--green); }
92
- .iconButton:hover, .segmented button:hover, .modeGroup button:hover, .runButton:hover { background: var(--panel-3); }
84
+ .iconButton:hover, .segmented button:hover, .runButton:hover { background: var(--panel-3); }
93
85
 
94
86
  .statusStrip {
95
87
  position: fixed;
@@ -97,7 +89,7 @@ body {
97
89
  z-index: 19;
98
90
  height: var(--status-height);
99
91
  display: grid;
100
- grid-template-columns: repeat(4, minmax(0, 1fr));
92
+ grid-template-columns: repeat(5, minmax(0, 1fr));
101
93
  background: #0f1214;
102
94
  overflow: hidden;
103
95
  }
@@ -132,7 +124,7 @@ body {
132
124
  height: calc(100dvh - var(--chrome-height));
133
125
  min-height: 0;
134
126
  display: grid;
135
- grid-template-columns: minmax(270px, 0.95fr) minmax(330px, 1.1fr) minmax(300px, 1fr);
127
+ grid-template-columns: minmax(260px, 0.9fr) minmax(320px, 1.1fr) minmax(280px, 1fr) minmax(280px, 1fr);
136
128
  overflow: hidden;
137
129
  }
138
130
  .pane {
@@ -189,6 +181,9 @@ textarea, .codeBlock {
189
181
  overscroll-behavior: contain;
190
182
  white-space: pre;
191
183
  }
184
+ textarea[readonly] {
185
+ cursor: default;
186
+ }
192
187
 
193
188
  .graphView {
194
189
  padding: 12px;
@@ -230,44 +225,7 @@ textarea, .codeBlock {
230
225
  font-size: 11px;
231
226
  text-transform: uppercase;
232
227
  }
233
- .edgeList {
234
- display: grid;
235
- gap: 6px;
236
- margin: 0;
237
- padding: 0;
238
- list-style: none;
239
- }
240
- .edgeList li {
241
- border-left: 2px solid var(--blue);
242
- background: rgba(109, 180, 232, .08);
243
- padding: 6px 8px;
244
- color: var(--muted);
245
- font-size: 12px;
246
- overflow-wrap: anywhere;
247
- }
248
- .boundsGrid {
249
- display: grid;
250
- grid-template-columns: repeat(3, minmax(0, 1fr));
251
- gap: 8px;
252
- }
253
- .boundCard {
254
- border: 1px solid var(--line);
255
- border-radius: 8px;
256
- background: #12171a;
257
- padding: 10px;
258
- }
259
- .boundCard strong {
260
- display: block;
261
- margin-bottom: 8px;
262
- font-size: 12px;
263
- }
264
- .boundCard ul {
265
- margin: 0;
266
- padding-left: 16px;
267
- color: var(--muted);
268
- font-size: 11px;
269
- line-height: 1.45;
270
- }
228
+ ${workbenchRouteStyles()}
271
229
  .segmented { display: flex; gap: 0; }
272
230
  .segmented button {
273
231
  width: 58px;
@@ -291,7 +249,7 @@ textarea, .codeBlock {
291
249
  }
292
250
  .workspace {
293
251
  grid-template-columns: 1fr;
294
- grid-template-rows: repeat(3, minmax(0, 1fr));
252
+ grid-template-rows: repeat(4, minmax(0, 1fr));
295
253
  overflow: hidden;
296
254
  }
297
255
  .pane {
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import http from 'node:http';
3
- import { compileNativeSource, createSemanticImportSidecar, importNativeSource, writeUniversalAstJson } from '../dist/index.js';
4
- import { conversionBounds } from './js-frontier-rust-workbench-bounds.mjs';
3
+ import { convertSource } from './js-frontier-rust-workbench-convert.mjs';
5
4
  import { renderWorkbenchHtml } from './js-frontier-rust-workbench-html.mjs';
6
5
  import { workbenchClientScript } from './js-frontier-rust-workbench-client.mjs';
7
6
  import { workbenchStyles } from './js-frontier-rust-workbench-styles.mjs';
@@ -24,21 +23,61 @@ export class TodoStore {
24
23
  }
25
24
  }
26
25
  `;
27
- const sampleRustSource = `// Generated from Frontier semantic graph evidence.
28
- pub fn add_todo() {
29
- // port body from preserved TypeScript source
30
- }
31
-
32
- pub struct TodoStore;
33
- `;
34
26
 
35
27
  if (args.smoke) {
36
28
  const result = convertSource(sampleSource, { sourceLanguage: 'typescript' });
37
29
  assert(result.summary.symbols >= 2, 'expected semantic symbols');
38
- assert(result.projection.output.includes('pub'), 'expected Rust projection');
39
- const reverse = convertSource(result.projection.output, { sourceLanguage: 'rust' });
40
- assert(reverse.projection.output.includes('export'), 'expected TypeScript projection');
41
- console.log(JSON.stringify({ ok: true, summary: result.summary }, null, 2));
30
+ assert(result.frontier.universalAst.valid, 'expected valid universal AST summary');
31
+ assert(result.projections.rust.output.includes('pub'), 'expected Rust projection');
32
+ assert(result.projections.python.output.includes('def add_todo'), 'expected Python projection');
33
+ assert(result.routeExplanation.routes.length === 2, 'expected route explanation for both targets');
34
+ assert(result.routeExplanation.semanticMerge.readiness === 'blocked', 'expected blocked semantic merge readiness');
35
+ assert(result.routeExplanation.semanticMerge.missingEvidence.includes('proof-or-replay-evidence'), 'expected missing proof evidence');
36
+ assert(result.routeExplanation.semanticMerge.missingEvidence.includes('merge-ready-parser'), 'expected missing parser evidence');
37
+ assert(result.routeExplanation.semanticMerge.missingEvidence.includes('runtime-adapter-evidence'), 'expected missing runtime adapter evidence');
38
+ const html = renderWorkbenchHtml({
39
+ sourceLanguage: 'typescript',
40
+ sources: { typescript: sampleSource },
41
+ result
42
+ });
43
+ const client = workbenchClientScript();
44
+ const styles = workbenchStyles();
45
+ assert(html.includes('id="convertForm"'), 'expected submit form');
46
+ assert(html.includes('id="typescriptInput"'), 'expected TypeScript input pane');
47
+ assert(html.includes('id="frontierJson"'), 'expected Frontier graph JSON pane');
48
+ assert(html.includes('id="rustOutput"'), 'expected Rust output pane');
49
+ assert(html.includes('id="pythonOutput"'), 'expected Python output pane');
50
+ assert(client.includes("addEventListener('submit'"), 'expected submit-based conversion listener');
51
+ assert(client.includes('Route Explanation'), 'expected route explanation rendering');
52
+ assert(styles.includes('grid-template-columns: minmax(260px, 0.9fr) minmax(320px, 1.1fr) minmax(280px, 1fr) minmax(280px, 1fr);'), 'expected four-pane desktop scaffold');
53
+ assert(styles.includes('overflow: auto'), 'expected scrollable pane bodies');
54
+ assert(styles.includes('.workspace {'), 'expected fixed workspace section');
55
+ assert(styles.includes('.routeCard'), 'expected route explanation styles');
56
+ console.log(JSON.stringify({
57
+ ok: true,
58
+ summary: result.summary,
59
+ projections: {
60
+ rust: { ok: result.projections.rust.ok, readiness: result.projections.rust.readiness },
61
+ python: { ok: result.projections.python.ok, readiness: result.projections.python.readiness }
62
+ },
63
+ routeExplanation: {
64
+ semanticMerge: result.routeExplanation.semanticMerge,
65
+ routes: result.routeExplanation.routes.map((route) => ({
66
+ target: route.target,
67
+ mode: route.mode,
68
+ routeAction: route.routeAction,
69
+ readiness: route.readiness,
70
+ admissionAction: route.semanticMerge.admissionAction,
71
+ missingEvidence: route.missingEvidence
72
+ }))
73
+ },
74
+ layout: {
75
+ submitBased: true,
76
+ panes: ['typescript', 'frontier', 'rust', 'python'],
77
+ independentScrollRegions: 4,
78
+ fixedSections: ['topbar', 'statusStrip', 'workspace']
79
+ }
80
+ }, null, 2));
42
81
  } else {
43
82
  const server = http.createServer(routeRequest);
44
83
  server.listen(port, '127.0.0.1', () => {
@@ -51,7 +90,7 @@ async function routeRequest(request, response) {
51
90
  if (request.method === 'GET' && request.url === '/') {
52
91
  return send(response, 200, renderWorkbenchHtml({
53
92
  sourceLanguage: 'typescript',
54
- sources: { typescript: sampleSource, rust: sampleRustSource },
93
+ sources: { typescript: sampleSource },
55
94
  result: convertSource(sampleSource, { sourceLanguage: 'typescript' })
56
95
  }), 'text/html; charset=utf-8');
57
96
  }
@@ -66,7 +105,7 @@ async function routeRequest(request, response) {
66
105
  return sendJson(response, 200, {
67
106
  ok: true,
68
107
  result: convertSource(String(body.source ?? ''), {
69
- sourceLanguage: body.sourceLanguage ?? 'typescript'
108
+ sourceLanguage: 'typescript'
70
109
  })
71
110
  });
72
111
  }
@@ -76,205 +115,6 @@ async function routeRequest(request, response) {
76
115
  }
77
116
  }
78
117
 
79
- function convertSource(source, options = {}) {
80
- const sourceLanguage = normalizeSourceLanguage(options.sourceLanguage);
81
- const target = sourceLanguage === 'rust' ? 'typescript' : 'rust';
82
- const imported = importNativeSource({
83
- language: sourceLanguage,
84
- sourcePath: `workbench/input.${sourceExtension(sourceLanguage)}`,
85
- sourceText: source
86
- });
87
- const projection = compileNativeSource(imported, {
88
- target,
89
- targetPath: `workbench/output.${targetExtension(target)}`,
90
- targetAdapters: [createTsToRustWorkbenchAdapter(), createRustToTsWorkbenchAdapter()],
91
- emitOnBlocked: true
92
- });
93
- const sidecar = createSemanticImportSidecar(imported, { generatedAt: 0, targetPath: projection.sourceMap?.targetPath });
94
- return {
95
- sourceHash: imported.nativeSource.sourceHash,
96
- sourceLanguage,
97
- summary: {
98
- readiness: sidecar.summary.readiness,
99
- symbols: imported.semanticIndex.symbols.length,
100
- sourceMapMappings: imported.sourceMaps[0]?.mappings.length ?? 0,
101
- losses: imported.losses.length,
102
- patchHints: sidecar.patchHints.length
103
- },
104
- frontier: {
105
- universalAst: universalAstSummary(imported),
106
- semanticIndexId: imported.semanticIndex.id,
107
- sidecarId: sidecar.id,
108
- symbols: imported.semanticIndex.symbols.map(symbolSummary),
109
- relations: relationSummaries(imported),
110
- losses: imported.losses.map(lossSummary),
111
- patchHints: sidecar.patchHints.map((hint) => ({
112
- id: hint.id,
113
- readiness: hint.readiness,
114
- operations: hint.supportedOperations,
115
- ownershipKey: hint.ownershipKey
116
- }))
117
- },
118
- bounds: conversionBounds(sourceLanguage, target),
119
- projection: {
120
- target,
121
- targetLanguage: target,
122
- sourceLanguage,
123
- mode: projection.outputMode,
124
- readiness: projection.readiness.readiness,
125
- ok: projection.ok,
126
- output: projection.output,
127
- sourceMapMappings: projection.sourceMap?.mappings.length ?? 0
128
- }
129
- };
130
- }
131
-
132
- function createTsToRustWorkbenchAdapter() {
133
- return {
134
- id: 'frontier-lang-workbench-ts-to-rust',
135
- sourceLanguage: 'typescript',
136
- target: 'rust',
137
- version: '0.0.0-demo',
138
- capabilities: ['semantic-symbol-scaffold'],
139
- coverage: {
140
- readiness: 'ready',
141
- handledLossKinds: ['opaqueNative', 'declarationOnlyCoverage', 'partialSemanticIndex', 'sourceMapApproximation', 'sourcePreservation'],
142
- notes: ['Workbench adapter lowers TypeScript semantic symbols to Rust scaffolding and leaves bodies explicit.']
143
- },
144
- project(input) {
145
- return {
146
- output: rustFromSymbols(input.importResult.semanticIndex?.symbols ?? []),
147
- readiness: 'ready',
148
- evidence: [{
149
- id: 'evidence_workbench_js_to_rust',
150
- kind: 'projection',
151
- status: 'passed',
152
- summary: 'Workbench adapter projected TypeScript semantic symbols to Rust scaffolding.'
153
- }]
154
- };
155
- }
156
- };
157
- }
158
-
159
- function createRustToTsWorkbenchAdapter() {
160
- return {
161
- id: 'frontier-lang-workbench-rust-to-ts',
162
- sourceLanguage: 'rust',
163
- target: 'typescript',
164
- version: '0.0.0-demo',
165
- capabilities: ['semantic-symbol-scaffold'],
166
- coverage: {
167
- readiness: 'ready',
168
- handledLossKinds: ['opaqueNative', 'macroExpansion', 'declarationOnlyCoverage', 'partialSemanticIndex', 'sourceMapApproximation', 'sourcePreservation'],
169
- notes: ['Workbench adapter lowers Rust semantic symbols to TypeScript scaffolding and leaves bodies explicit.']
170
- },
171
- project(input) {
172
- return {
173
- output: tsFromSymbols(input.importResult.semanticIndex?.symbols ?? []),
174
- readiness: 'ready',
175
- evidence: [{
176
- id: 'evidence_workbench_rust_to_ts',
177
- kind: 'projection',
178
- status: 'passed',
179
- summary: 'Workbench adapter projected Rust semantic symbols to TypeScript scaffolding.'
180
- }]
181
- };
182
- }
183
- };
184
- }
185
-
186
- function rustFromSymbols(symbols) {
187
- const lines = ['// Generated from Frontier semantic graph evidence.'];
188
- for (const symbol of symbols) {
189
- if (symbol.kind === 'class') lines.push('', `pub struct ${safeTypeName(symbol.name)};`);
190
- if (symbol.kind === 'function' || symbol.kind === 'method') {
191
- lines.push('', `pub fn ${safeRustName(symbol.name)}() {`, ' // port body from preserved TypeScript source', '}');
192
- }
193
- }
194
- return `${lines.join('\n').trim()}\n`;
195
- }
196
-
197
- function tsFromSymbols(symbols) {
198
- const lines = ['// Generated from Frontier semantic graph evidence.'];
199
- for (const symbol of symbols) {
200
- if (symbol.kind === 'type' || symbol.kind === 'class') lines.push('', `export class ${safeTypeName(symbol.name)} {}`);
201
- if (symbol.kind === 'function' || symbol.kind === 'method') {
202
- lines.push('', `export function ${safeJsName(symbol.name)}(...args: unknown[]) {`, " throw new Error('port body from preserved Rust source');", '}');
203
- }
204
- }
205
- return `${lines.join('\n').trim()}\n`;
206
- }
207
-
208
- function universalAstSummary(imported) {
209
- const summary = { valid: true, validationError: undefined };
210
- let parsed = imported.universalAst;
211
- try {
212
- parsed = JSON.parse(writeUniversalAstJson(imported.universalAst));
213
- } catch (error) {
214
- summary.valid = false;
215
- summary.validationError = String(error.message || error);
216
- }
217
- return {
218
- ...summary,
219
- kind: parsed.kind,
220
- id: parsed.id,
221
- nativeSources: parsed.nativeSources?.length ?? 0,
222
- semanticSymbols: parsed.semanticIndex?.symbols?.length ?? 0,
223
- sourceMaps: parsed.sourceMaps?.length ?? 0,
224
- layers: Object.keys(parsed.layers ?? {})
225
- };
226
- }
227
-
228
- function symbolSummary(symbol) {
229
- return {
230
- id: symbol.id,
231
- name: symbol.name,
232
- kind: symbol.kind,
233
- language: symbol.language,
234
- region: symbol.metadata?.ownershipRegionKind,
235
- ownershipKey: symbol.metadata?.ownershipRegionKey
236
- };
237
- }
238
-
239
- function relationSummaries(imported) {
240
- const names = new Map(imported.semanticIndex.symbols.map((symbol) => [symbol.id, symbol.name]));
241
- return imported.semanticIndex.relations.map((relation) => ({
242
- id: relation.id,
243
- predicate: relation.predicate,
244
- label: `${names.get(relation.sourceId) ?? relation.sourceId} ${relation.predicate} ${names.get(relation.targetId) ?? relation.targetId}`
245
- }));
246
- }
247
-
248
- function lossSummary(loss) {
249
- return { id: loss.id, kind: loss.kind, severity: loss.severity, message: loss.message };
250
- }
251
-
252
- function normalizeSourceLanguage(language) {
253
- return String(language || 'typescript').toLowerCase() === 'rust' ? 'rust' : 'typescript';
254
- }
255
-
256
- function targetExtension(target) {
257
- return target === 'rust' ? 'rs' : 'ts';
258
- }
259
-
260
- function sourceExtension(language) {
261
- return language === 'rust' ? 'rs' : 'ts';
262
- }
263
-
264
- function safeTypeName(name) {
265
- const clean = String(name).replace(/[^A-Za-z0-9_]/g, '');
266
- return clean && /^[A-Z]/.test(clean) ? clean : `Frontier${clean || 'Type'}`;
267
- }
268
-
269
- function safeRustName(name) {
270
- return String(name).replace(/\./g, '_').replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/[^A-Za-z0-9_]/g, '_').toLowerCase();
271
- }
272
-
273
- function safeJsName(name) {
274
- const clean = String(name).replace(/[^A-Za-z0-9_$]/g, '_');
275
- return /^[A-Za-z_$]/.test(clean) ? clean : `frontier_${clean}`;
276
- }
277
-
278
118
  function sendJson(response, status, value) {
279
119
  return send(response, status, JSON.stringify(value), 'application/json; charset=utf-8');
280
120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.65",
3
+ "version": "0.2.67",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",