@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,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 {
@@ -223,44 +225,7 @@ textarea[readonly] {
223
225
  font-size: 11px;
224
226
  text-transform: uppercase;
225
227
  }
226
- .edgeList {
227
- display: grid;
228
- gap: 6px;
229
- margin: 0;
230
- padding: 0;
231
- list-style: none;
232
- }
233
- .edgeList li {
234
- border-left: 2px solid var(--blue);
235
- background: rgba(109, 180, 232, .08);
236
- padding: 6px 8px;
237
- color: var(--muted);
238
- font-size: 12px;
239
- overflow-wrap: anywhere;
240
- }
241
- .boundsGrid {
242
- display: grid;
243
- grid-template-columns: repeat(3, minmax(0, 1fr));
244
- gap: 8px;
245
- }
246
- .boundCard {
247
- border: 1px solid var(--line);
248
- border-radius: 8px;
249
- background: #12171a;
250
- padding: 10px;
251
- }
252
- .boundCard strong {
253
- display: block;
254
- margin-bottom: 8px;
255
- font-size: 12px;
256
- }
257
- .boundCard ul {
258
- margin: 0;
259
- padding-left: 16px;
260
- color: var(--muted);
261
- font-size: 11px;
262
- line-height: 1.45;
263
- }
228
+ ${workbenchRouteStyles()}
264
229
  .segmented { display: flex; gap: 0; }
265
230
  .segmented button {
266
231
  width: 58px;
@@ -1,11 +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 {
5
- createTsToPythonWorkbenchAdapter,
6
- createTsToRustWorkbenchAdapter
7
- } from './js-frontier-rust-workbench-adapters.mjs';
8
- import { conversionBounds } from './js-frontier-rust-workbench-bounds.mjs';
3
+ import { convertSource } from './js-frontier-rust-workbench-convert.mjs';
9
4
  import { renderWorkbenchHtml } from './js-frontier-rust-workbench-html.mjs';
10
5
  import { workbenchClientScript } from './js-frontier-rust-workbench-client.mjs';
11
6
  import { workbenchStyles } from './js-frontier-rust-workbench-styles.mjs';
@@ -35,6 +30,11 @@ if (args.smoke) {
35
30
  assert(result.frontier.universalAst.valid, 'expected valid universal AST summary');
36
31
  assert(result.projections.rust.output.includes('pub'), 'expected Rust projection');
37
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
38
  const html = renderWorkbenchHtml({
39
39
  sourceLanguage: 'typescript',
40
40
  sources: { typescript: sampleSource },
@@ -48,8 +48,11 @@ if (args.smoke) {
48
48
  assert(html.includes('id="rustOutput"'), 'expected Rust output pane');
49
49
  assert(html.includes('id="pythonOutput"'), 'expected Python output pane');
50
50
  assert(client.includes("addEventListener('submit'"), 'expected submit-based conversion listener');
51
+ assert(client.includes('Route Explanation'), 'expected route explanation rendering');
51
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');
52
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');
53
56
  console.log(JSON.stringify({
54
57
  ok: true,
55
58
  summary: result.summary,
@@ -57,10 +60,22 @@ if (args.smoke) {
57
60
  rust: { ok: result.projections.rust.ok, readiness: result.projections.rust.readiness },
58
61
  python: { ok: result.projections.python.ok, readiness: result.projections.python.readiness }
59
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
+ },
60
74
  layout: {
61
75
  submitBased: true,
62
76
  panes: ['typescript', 'frontier', 'rust', 'python'],
63
- independentScrollRegions: 4
77
+ independentScrollRegions: 4,
78
+ fixedSections: ['topbar', 'statusStrip', 'workspace']
64
79
  }
65
80
  }, null, 2));
66
81
  } else {
@@ -100,127 +115,6 @@ async function routeRequest(request, response) {
100
115
  }
101
116
  }
102
117
 
103
- function convertSource(source, options = {}) {
104
- const sourceLanguage = normalizeSourceLanguage(options.sourceLanguage);
105
- const targets = ['rust', 'python'];
106
- const imported = importNativeSource({
107
- language: sourceLanguage,
108
- sourcePath: `workbench/input.${sourceExtension(sourceLanguage)}`,
109
- sourceText: source
110
- });
111
- const targetAdapters = [createTsToRustWorkbenchAdapter(), createTsToPythonWorkbenchAdapter()];
112
- const projections = Object.fromEntries(targets.map((target) => {
113
- const projection = compileNativeSource(imported, {
114
- target,
115
- targetPath: `workbench/output.${targetExtension(target)}`,
116
- targetAdapters,
117
- emitOnBlocked: true
118
- });
119
- return [target, projectionSummary(projection, { sourceLanguage, target })];
120
- }));
121
- const sidecar = createSemanticImportSidecar(imported, { generatedAt: 0, targetPath: projections.rust?.targetPath });
122
- return {
123
- sourceHash: imported.nativeSource.sourceHash,
124
- sourceLanguage,
125
- summary: {
126
- readiness: sidecar.summary.readiness,
127
- symbols: imported.semanticIndex.symbols.length,
128
- sourceMapMappings: imported.sourceMaps[0]?.mappings.length ?? 0,
129
- losses: imported.losses.length,
130
- patchHints: sidecar.patchHints.length
131
- },
132
- frontier: {
133
- universalAst: universalAstSummary(imported),
134
- semanticIndexId: imported.semanticIndex.id,
135
- sidecarId: sidecar.id,
136
- symbols: imported.semanticIndex.symbols.map(symbolSummary),
137
- relations: relationSummaries(imported),
138
- losses: imported.losses.map(lossSummary),
139
- patchHints: sidecar.patchHints.map((hint) => ({
140
- id: hint.id,
141
- readiness: hint.readiness,
142
- operations: hint.supportedOperations,
143
- ownershipKey: hint.ownershipKey
144
- }))
145
- },
146
- bounds: conversionBounds(sourceLanguage, targets.join('/')),
147
- projection: projections.rust,
148
- projections
149
- };
150
- }
151
-
152
- function projectionSummary(projection, { sourceLanguage, target }) {
153
- return {
154
- target,
155
- targetLanguage: target,
156
- sourceLanguage,
157
- targetPath: projection.sourceMap?.targetPath,
158
- mode: projection.outputMode,
159
- readiness: projection.readiness.readiness,
160
- ok: projection.ok,
161
- output: projection.output,
162
- sourceMapMappings: projection.sourceMap?.mappings.length ?? 0
163
- };
164
- }
165
-
166
- function universalAstSummary(imported) {
167
- const summary = { valid: true, validationError: undefined };
168
- let parsed = imported.universalAst;
169
- try {
170
- parsed = JSON.parse(writeUniversalAstJson(imported.universalAst));
171
- } catch (error) {
172
- summary.valid = false;
173
- summary.validationError = String(error.message || error);
174
- }
175
- return {
176
- ...summary,
177
- kind: parsed.kind,
178
- id: parsed.id,
179
- nativeSources: parsed.nativeSources?.length ?? 0,
180
- semanticSymbols: parsed.semanticIndex?.symbols?.length ?? 0,
181
- sourceMaps: parsed.sourceMaps?.length ?? 0,
182
- layers: Object.keys(parsed.layers ?? {})
183
- };
184
- }
185
-
186
- function symbolSummary(symbol) {
187
- return {
188
- id: symbol.id,
189
- name: symbol.name,
190
- kind: symbol.kind,
191
- language: symbol.language,
192
- region: symbol.metadata?.ownershipRegionKind,
193
- ownershipKey: symbol.metadata?.ownershipRegionKey
194
- };
195
- }
196
-
197
- function relationSummaries(imported) {
198
- const names = new Map(imported.semanticIndex.symbols.map((symbol) => [symbol.id, symbol.name]));
199
- return imported.semanticIndex.relations.map((relation) => ({
200
- id: relation.id,
201
- predicate: relation.predicate,
202
- label: `${names.get(relation.sourceId) ?? relation.sourceId} ${relation.predicate} ${names.get(relation.targetId) ?? relation.targetId}`
203
- }));
204
- }
205
-
206
- function lossSummary(loss) {
207
- return { id: loss.id, kind: loss.kind, severity: loss.severity, message: loss.message };
208
- }
209
-
210
- function normalizeSourceLanguage(language) {
211
- return 'typescript';
212
- }
213
-
214
- function targetExtension(target) {
215
- if (target === 'rust') return 'rs';
216
- if (target === 'python') return 'py';
217
- return 'txt';
218
- }
219
-
220
- function sourceExtension(language) {
221
- return 'ts';
222
- }
223
-
224
118
  function sendJson(response, status, value) {
225
119
  return send(response, status, JSON.stringify(value), 'application/json; charset=utf-8');
226
120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.66",
3
+ "version": "0.2.68",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",