@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
@@ -9,7 +9,7 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
9
9
  name,
10
10
  symbolKind,
11
11
  symbolId: `symbol:${input.language}:${idFragment(name)}`,
12
- span: spanForLine(input, lineNumber),
12
+ span: options.span ?? spanForLine(input, lineNumber),
13
13
  fields,
14
14
  metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
15
15
  ...(options.regionKind ? { regionKind: options.regionKind } : {}),
@@ -18,8 +18,8 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
18
18
  };
19
19
  }
20
20
 
21
- function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind) {
22
- const name = String(importPath);
21
+ function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind, options = {}) {
22
+ const name = String(options.name ?? importPath);
23
23
  const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
24
24
  return {
25
25
  nodeId,
@@ -27,15 +27,44 @@ function nativeImportDeclaration(input, lineNumber, importPath, languageKind, sy
27
27
  languageKind: `${input.language}.${languageKind}`,
28
28
  name,
29
29
  symbolKind,
30
- symbolId: `symbol:${input.language}:import:${idFragment(name)}`,
31
- role: 'import',
32
- importPath: name,
33
- span: spanForLine(input, lineNumber),
34
- fields: { importPath: name },
35
- metadata: { scan: 'lightweight-import' }
30
+ symbolId: options.symbolId ?? `symbol:${input.language}:import:${idFragment(name)}`,
31
+ role: options.role ?? 'import',
32
+ importPath: String(importPath),
33
+ span: options.span ?? spanForLine(input, lineNumber),
34
+ fields: { importPath: String(importPath), ...options.fields },
35
+ metadata: { scan: 'lightweight-import', ...options.metadata },
36
+ ...(options.regionKind ? { regionKind: options.regionKind } : {})
36
37
  };
37
38
  }
38
39
 
40
+ function nativeImportBindingDeclaration(input, lineNumber, importPath, binding, options = {}) {
41
+ const localName = String(binding.localName ?? binding.name ?? importPath);
42
+ const importedName = binding.importedName ?? localName;
43
+ const importKind = binding.importKind ?? 'named';
44
+ return nativeImportDeclaration(input, lineNumber, importPath, options.languageKind ?? 'ImportBinding', binding.symbolKind ?? 'import', {
45
+ name: localName,
46
+ symbolId: options.symbolId ?? `symbol:${input.language}:import:${idFragment(`${importPath}:${localName}`)}`,
47
+ span: options.span,
48
+ fields: {
49
+ localName,
50
+ importedName,
51
+ importKind,
52
+ importPath: String(importPath),
53
+ ...(binding.exportedName ? { exportedName: binding.exportedName } : {}),
54
+ ...(binding.typeOnly ? { typeOnly: true } : {})
55
+ },
56
+ metadata: {
57
+ scan: 'lightweight-import-binding',
58
+ localName,
59
+ importedName,
60
+ importKind,
61
+ ...(binding.exportedName ? { exportedName: binding.exportedName } : {}),
62
+ ...(binding.typeOnly ? { typeOnly: true } : {}),
63
+ ...options.metadata
64
+ }
65
+ });
66
+ }
67
+
39
68
  function nativeMacroLoss(input, lineNumber, source, kind, name = idFragment(source).slice(0, 40)) {
40
69
  const nodeId = `native_${kind}_${lineNumber}_${idFragment(name)}`;
41
70
  return {
@@ -133,12 +162,14 @@ function sourceLines(sourceText) {
133
162
  }
134
163
 
135
164
  function spanForLine(input, lineNumber) {
165
+ const line = sourceLines(input.sourceText)[lineNumber - 1]?.line ?? '';
136
166
  return {
137
167
  sourceId: input.sourceHash,
138
168
  path: input.sourcePath,
139
169
  startLine: lineNumber,
140
170
  endLine: lineNumber,
141
- startColumn: 1
171
+ startColumn: 1,
172
+ endColumn: line.length + 1
142
173
  };
143
174
  }
144
175
 
@@ -172,6 +203,7 @@ export {
172
203
  lightweightCoverageLosses,
173
204
  nativeDeclaration,
174
205
  nativeImportDeclaration,
206
+ nativeImportBindingDeclaration,
175
207
  nativeMacroLoss,
176
208
  sourceLines,
177
209
  splitParameters,
@@ -3,6 +3,7 @@ import {
3
3
  nativeDeclaration,
4
4
  splitParameters
5
5
  } from './native-region-scanner-core.js';
6
+ import { jsImportDeclarations } from './native-region-scanner-js-imports.js';
6
7
 
7
8
  function jsCommentOnlyLine(trimmed) {
8
9
  return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
@@ -247,6 +248,7 @@ export {
247
248
  jsDeclarationScanLine,
248
249
  jsExportedContainerDeclaration,
249
250
  jsInitializerKind,
251
+ jsImportDeclarations,
250
252
  jsObjectPropertyDeclaration,
251
253
  jsObjectRegionContext,
252
254
  jsRegionKindForDeclarationName,
@@ -0,0 +1,111 @@
1
+ import {
2
+ nativeImportBindingDeclaration,
3
+ nativeImportDeclaration
4
+ } from './native-region-scanner-core.js';
5
+
6
+ export function jsImportDeclarations(input, lineNumber, trimmed) {
7
+ const importEquals = trimmed.match(/^import\s+(type\s+)?([A-Za-z_$][\w$]*)\s*=\s*require\s*\(\s*(['"])([^'"]+)\3\s*\)/);
8
+ if (importEquals) return jsImportModuleDeclarations(input, lineNumber, importEquals[4], 'ImportEqualsDeclaration', [{
9
+ localName: importEquals[2],
10
+ importedName: 'default',
11
+ importKind: 'commonjs-require',
12
+ typeOnly: Boolean(importEquals[1])
13
+ }]);
14
+ const importMatch = trimmed.match(/^import\s+(type\s+)?(?:(.+?)\s+from\s+)?(['"])([^'"]+)\3/);
15
+ if (importMatch) {
16
+ const typeOnly = Boolean(importMatch[1]);
17
+ const importPath = importMatch[4];
18
+ const clause = importMatch[2]?.trim();
19
+ const bindings = clause ? jsImportBindingsFromClause(clause, { typeOnly }) : [];
20
+ return jsImportModuleDeclarations(input, lineNumber, importPath, 'ImportDeclaration', bindings, { typeOnly, sideEffectOnly: bindings.length === 0 });
21
+ }
22
+ const exportMatch = trimmed.match(/^export\s+(type\s+)?(?:(\*)\s+from|\*\s+as\s+([A-Za-z_$][\w$]*)\s+from|\{([^}]+)\}\s+from)\s+(['"])([^'"]+)\5/);
23
+ if (exportMatch) {
24
+ const typeOnly = Boolean(exportMatch[1]);
25
+ const importPath = exportMatch[6];
26
+ const bindings = exportMatch[3]
27
+ ? [{ localName: exportMatch[3], importedName: '*', exportedName: exportMatch[3], importKind: 'namespace-reexport', typeOnly }]
28
+ : exportMatch[4]
29
+ ? jsNamedImportBindings(exportMatch[4], { typeOnly, reexport: true })
30
+ : [];
31
+ return jsImportModuleDeclarations(input, lineNumber, importPath, 'ExportFromDeclaration', bindings, { typeOnly, reexport: true, exportStar: Boolean(exportMatch[2]) });
32
+ }
33
+ const requireMatch = trimmed.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:await\s+)?(?:require|import)\s*\(\s*(['"])([^'"]+)\2\s*\)/);
34
+ if (requireMatch) return jsImportModuleDeclarations(input, lineNumber, requireMatch[3], 'CommonJsRequireDeclaration', [{
35
+ localName: requireMatch[1],
36
+ importedName: 'default',
37
+ importKind: trimmed.includes('import') ? 'dynamic-import-binding' : 'commonjs-require'
38
+ }]);
39
+ return [];
40
+ }
41
+
42
+ function jsImportModuleDeclarations(input, lineNumber, importPath, languageKind, bindings = [], metadata = {}) {
43
+ const bindingSummaries = bindings.map((binding) => ({
44
+ localName: binding.localName,
45
+ importedName: binding.importedName,
46
+ importKind: binding.importKind,
47
+ ...(binding.exportedName ? { exportedName: binding.exportedName } : {}),
48
+ ...(binding.typeOnly ? { typeOnly: true } : {})
49
+ }));
50
+ return [
51
+ nativeImportDeclaration(input, lineNumber, importPath, languageKind, 'module', {
52
+ fields: {
53
+ moduleOnly: true,
54
+ importBindings: bindingSummaries,
55
+ ...(metadata.typeOnly ? { typeOnly: true } : {}),
56
+ ...(metadata.sideEffectOnly ? { sideEffectOnly: true } : {}),
57
+ ...(metadata.reexport ? { reexport: true } : {}),
58
+ ...(metadata.exportStar ? { exportStar: true } : {})
59
+ },
60
+ metadata: {
61
+ moduleOnly: true,
62
+ bindingCount: bindings.length,
63
+ ...metadata
64
+ }
65
+ }),
66
+ ...bindings.map((binding) => nativeImportBindingDeclaration(input, lineNumber, importPath, binding))
67
+ ];
68
+ }
69
+
70
+ function jsImportBindingsFromClause(clause, options = {}) {
71
+ const source = String(clause ?? '').trim();
72
+ if (!source) return [];
73
+ const namespace = source.match(/^\*\s+as\s+([A-Za-z_$][\w$]*)$/);
74
+ if (namespace) return [{ localName: namespace[1], importedName: '*', importKind: 'namespace', typeOnly: options.typeOnly }];
75
+ const bindings = [];
76
+ const named = source.match(/\{([^}]*)\}/);
77
+ const defaultPart = source.replace(/\{[^}]*\}/, '').split(',').map((part) => part.trim()).filter(Boolean)[0];
78
+ if (defaultPart && /^[A-Za-z_$][\w$]*$/.test(defaultPart)) {
79
+ bindings.push({ localName: defaultPart, importedName: 'default', importKind: 'default', typeOnly: options.typeOnly });
80
+ }
81
+ if (named) bindings.push(...jsNamedImportBindings(named[1], options));
82
+ return bindings;
83
+ }
84
+
85
+ function jsNamedImportBindings(raw, options = {}) {
86
+ return String(raw ?? '')
87
+ .split(',')
88
+ .map((part) => jsNamedImportBinding(part, options))
89
+ .filter(Boolean);
90
+ }
91
+
92
+ function jsNamedImportBinding(raw, options = {}) {
93
+ let text = String(raw ?? '').trim();
94
+ if (!text) return undefined;
95
+ let typeOnly = Boolean(options.typeOnly);
96
+ if (text.startsWith('type ')) {
97
+ typeOnly = true;
98
+ text = text.slice(5).trim();
99
+ }
100
+ const match = text.match(/^([A-Za-z_$][\w$]*|\*)\s*(?:as\s+([A-Za-z_$][\w$]*))?$/);
101
+ if (!match) return undefined;
102
+ const importedName = match[1];
103
+ const localName = match[2] ?? importedName;
104
+ return {
105
+ localName,
106
+ importedName,
107
+ exportedName: options.reexport ? localName : undefined,
108
+ importKind: options.reexport ? 'reexport' : typeOnly ? 'type-named' : 'named',
109
+ typeOnly
110
+ };
111
+ }
@@ -12,6 +12,7 @@ import {
12
12
  jsDeclarationScanLine,
13
13
  jsExportedContainerDeclaration,
14
14
  jsInitializerKind,
15
+ jsImportDeclarations,
15
16
  jsObjectPropertyDeclaration,
16
17
  jsObjectRegionContext,
17
18
  jsRegionKindForDeclarationName,
@@ -22,11 +23,18 @@ import {
22
23
 
23
24
  function scanJavaScriptLike(input) {
24
25
  const declarations = [];
26
+ const lines = sourceLines(input.sourceText);
27
+ const pushDeclaration = (declaration) => {
28
+ if (declaration) declarations.push(jsDeclarationWithSourceSpan(input, declaration, lines));
29
+ };
30
+ const pushDeclarations = (items) => {
31
+ for (const declaration of items ?? []) pushDeclaration(declaration);
32
+ };
25
33
  let currentClass;
26
34
  let classDepth = 0;
27
35
  let currentObject;
28
36
  const lexicalState = { inBlockComment: false, inTemplateString: false };
29
- for (const { line, number } of sourceLines(input.sourceText)) {
37
+ for (const { line, number } of lines) {
30
38
  const scanLine = jsDeclarationScanLine(line, lexicalState);
31
39
  const trimmed = scanLine.trim();
32
40
  if (!trimmed || jsCommentOnlyLine(trimmed)) continue;
@@ -35,42 +43,42 @@ function scanJavaScriptLike(input) {
35
43
  if (currentObject) {
36
44
  const routeRecord = jsRouteRecordDeclaration(input, number, trimmed, currentObject);
37
45
  if (routeRecord) {
38
- declarations.push(routeRecord);
46
+ pushDeclaration(routeRecord);
39
47
  } else {
40
48
  const property = jsObjectPropertyDeclaration(input, number, trimmed, currentObject);
41
- if (property) declarations.push(property);
49
+ if (property) pushDeclaration(property);
42
50
  }
43
51
  }
44
- if ((match = trimmed.match(/^import\s+(?:.+?\s+from\s+)?['"]([^'"]+)['"]/))) {
45
- declarations.push(nativeImportDeclaration(input, number, match[1], 'ImportDeclaration', 'module'));
52
+ const importDeclarations = jsImportDeclarations(input, number, trimmed);
53
+ if (importDeclarations.length) {
54
+ pushDeclarations(importDeclarations);
46
55
  } else if ((match = trimmed.match(/^import\s*\(\s*['"]([^'"]+)['"]\s*\)/))) {
47
- declarations.push(nativeImportDeclaration(input, number, match[1], 'DynamicImportExpression', 'module'));
48
- } else if ((match = trimmed.match(/^export\s+(?:\*\s+from|\{[^}]*\}\s+from)\s+['"]([^'"]+)['"]/))) {
49
- declarations.push(nativeImportDeclaration(input, number, match[1], 'ExportFromDeclaration', 'module'));
50
- } else if ((match = declarationLine.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)/))) {
51
- declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, declarationLine.includes('{')));
52
- } else if ((match = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*\(([^)]*)\)/))) {
53
- declarations.push(nativeDeclaration(input, number, 'ExportDefaultFunctionDeclaration', 'function', match[1] ?? 'default', { parameters: splitParameters(match[2]), exportDefault: true }, trimmed.includes('{')));
56
+ pushDeclaration(nativeImportDeclaration(input, number, match[1], 'DynamicImportExpression', 'module'));
57
+ } else if ((match = declarationLine.match(/^(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?/))) {
58
+ pushDeclaration(nativeDeclaration(input, number, 'FunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, declarationLine.includes('{')));
59
+ } else if ((match = trimmed.match(/^export\s+default\s+(?:async\s+)?function\*?\s*([A-Za-z_$][\w$]*)?\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?/))) {
60
+ pushDeclaration(nativeDeclaration(input, number, 'ExportDefaultFunctionDeclaration', 'function', match[1] ?? 'default', { parameters: splitParameters(match[2]), exportDefault: true }, trimmed.includes('{')));
54
61
  } else if ((match = declarationLine.match(/^(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/))) {
55
- declarations.push(nativeDeclaration(input, number, declarationLine.startsWith('default ') ? 'ExportDefaultClassDeclaration' : 'ClassDeclaration', 'class', match[1], { exportDefault: declarationLine.startsWith('default ') || undefined }, declarationLine.includes('{')));
56
- if (declarationLine.includes('{') && !declarationLine.includes('}')) {
62
+ pushDeclaration(nativeDeclaration(input, number, declarationLine.startsWith('default ') ? 'ExportDefaultClassDeclaration' : 'ClassDeclaration', 'class', match[1], { exportDefault: declarationLine.startsWith('default ') || undefined }, declarationLine.includes('{')));
63
+ pushDeclarations(jsInlineClassMemberDeclarations(input, number, declarationLine, match[1]));
64
+ if (jsStructureDelta(declarationLine).value > 0) {
57
65
  currentClass = match[1];
58
66
  classDepth = 0;
59
67
  }
60
68
  } else if ((match = declarationLine.match(/^interface\s+([A-Za-z_$][\w$]*)/))) {
61
- declarations.push(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, declarationLine.includes('{')));
69
+ pushDeclaration(nativeDeclaration(input, number, 'InterfaceDeclaration', 'interface', match[1], {}, declarationLine.includes('{')));
62
70
  } else if ((match = declarationLine.match(/^(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/))) {
63
- declarations.push(nativeDeclaration(input, number, 'EnumDeclaration', 'type', match[1], {}, declarationLine.includes('{')));
71
+ pushDeclaration(nativeDeclaration(input, number, 'EnumDeclaration', 'type', match[1], {}, declarationLine.includes('{')));
64
72
  } else if ((match = declarationLine.match(/^(?:namespace|module)\s+([A-Za-z_$][\w$.]*)/))) {
65
- declarations.push(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, declarationLine.includes('{')));
73
+ pushDeclaration(nativeDeclaration(input, number, 'ModuleDeclaration', 'module', match[1], {}, declarationLine.includes('{')));
66
74
  } else if ((match = declarationLine.match(/^type\s+([A-Za-z_$][\w$]*)\s*=/))) {
67
- declarations.push(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
68
- } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?([^=;]*)\)?\s*=>/))) {
69
- declarations.push(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
75
+ pushDeclaration(nativeDeclaration(input, number, 'TypeAliasDeclaration', 'type', match[1], {}, false));
76
+ } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^=]+)?=\s*(?:async\s*)?(?:<[^=]+>\s*)?(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*(?::\s*[^=]+)?=>/))) {
77
+ pushDeclaration(nativeDeclaration(input, number, 'VariableFunctionDeclaration', 'function', match[1], { parameters: splitParameters(match[2] ?? match[3]) }, true));
70
78
  } else if ((match = declarationLine.match(/^(?:const|let|var)\s+([A-Za-z_$][\w$]*)\b/))) {
71
79
  const initializerKind = jsInitializerKind(declarationLine, match[1]);
72
80
  const regionKind = jsRegionKindForDeclarationName(match[1], declarationLine);
73
- declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', jsVariableSymbolKind(regionKind, initializerKind), match[1], {
81
+ pushDeclaration(nativeDeclaration(input, number, 'VariableDeclaration', jsVariableSymbolKind(regionKind, initializerKind), match[1], {
74
82
  initializerKind
75
83
  }, jsVariableHasBody(initializerKind, declarationLine), {
76
84
  regionKind,
@@ -78,21 +86,21 @@ function scanJavaScriptLike(input) {
78
86
  }));
79
87
  currentObject = jsObjectRegionContext(match[1], declarationLine, number, regionKind);
80
88
  } else if ((match = jsExportedContainerDeclaration(input, number, trimmed))) {
81
- declarations.push(match.declaration);
89
+ pushDeclaration(match.declaration);
82
90
  currentObject = match.context;
83
91
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?function\*?\s*\(([^)]*)\)/))) {
84
- declarations.push(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
92
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsFunctionExport', 'function', match[1], { parameters: splitParameters(match[2]) }, true));
85
93
  } else if ((match = trimmed.match(/^(?:module\.)?exports\.([A-Za-z_$][\w$]*)\s*=/))) {
86
94
  const regionKind = jsRegionKindForDeclarationName(match[1], trimmed);
87
- declarations.push(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
88
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
89
- declarations.push(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
95
+ pushDeclaration(nativeDeclaration(input, number, 'CommonJsExport', 'variable', match[1], { export: 'commonjs' }, false, { regionKind }));
96
+ } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={]+)?(?:\{|=>|$)/)) && !jsControlKeyword(match[1])) {
97
+ pushDeclaration(nativeDeclaration(input, number, 'MethodDefinition', 'method', `${currentClass}.${match[1]}`, {
90
98
  methodName: match[1],
91
99
  owner: currentClass,
92
100
  parameters: splitParameters(match[2])
93
101
  }, declarationLine.includes('{') || declarationLine.includes('=>')));
94
- } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
95
- declarations.push(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
102
+ } else if (currentClass && (match = declarationLine.match(/^(?:(?:public|private|protected|static|readonly|declare|accessor)\s+)*([A-Za-z_$][\w$]*)[?!]?\s*(?::\s*([^=;{]+))?(?:[=;]|$)/))) {
103
+ pushDeclaration(nativeDeclaration(input, number, 'PropertyDefinition', 'property', `${currentClass}.${match[1]}`, {
96
104
  propertyName: match[1],
97
105
  owner: currentClass,
98
106
  valueType: match[2]?.trim()
@@ -113,4 +121,79 @@ function scanJavaScriptLike(input) {
113
121
  return declarations;
114
122
  }
115
123
 
124
+ function jsDeclarationWithSourceSpan(input, declaration, lines) {
125
+ const startLine = declaration.span?.startLine ?? 1;
126
+ const endLine = declaration.metadata?.hasBody ? jsBalancedDeclarationEndLine(input, lines, startLine) : startLine;
127
+ const endLineText = lines[Math.max(0, endLine - 1)]?.line ?? '';
128
+ return {
129
+ ...declaration,
130
+ span: {
131
+ ...declaration.span,
132
+ sourceId: declaration.span?.sourceId ?? input.sourceHash,
133
+ path: declaration.span?.path ?? input.sourcePath,
134
+ startLine,
135
+ endLine,
136
+ startColumn: declaration.span?.startColumn ?? 1,
137
+ endColumn: declaration.span?.endColumn ?? endLineText.length + 1
138
+ }
139
+ };
140
+ }
141
+
142
+ function jsBalancedDeclarationEndLine(input, lines, startLine) {
143
+ const state = { inBlockComment: false, inTemplateString: false };
144
+ let depth = 0;
145
+ let opened = false;
146
+ for (let index = Math.max(0, startLine - 1); index < lines.length; index += 1) {
147
+ const scanLine = jsDeclarationScanLine(lines[index].line, state);
148
+ const delta = jsStructureDelta(scanLine);
149
+ if (delta.opened) opened = true;
150
+ depth += delta.value;
151
+ if (opened && depth <= 0) return lines[index].number;
152
+ }
153
+ return startLine;
154
+ }
155
+
156
+ function jsStructureDelta(source) {
157
+ let value = 0;
158
+ let opened = false;
159
+ let quote;
160
+ let escaped = false;
161
+ for (const char of String(source ?? '')) {
162
+ if (quote) {
163
+ if (escaped) escaped = false;
164
+ else if (char === '\\') escaped = true;
165
+ else if (char === quote) quote = undefined;
166
+ continue;
167
+ }
168
+ if (char === '\'' || char === '"' || char === '`') {
169
+ quote = char;
170
+ continue;
171
+ }
172
+ if (char === '{' || char === '[' || char === '(') {
173
+ value += 1;
174
+ opened = true;
175
+ } else if (char === '}' || char === ']' || char === ')') {
176
+ value -= 1;
177
+ }
178
+ }
179
+ return { value, opened };
180
+ }
181
+
182
+ function jsInlineClassMemberDeclarations(input, lineNumber, declarationLine, className) {
183
+ const open = declarationLine.indexOf('{');
184
+ const close = declarationLine.lastIndexOf('}');
185
+ if (open < 0 || close <= open) return [];
186
+ const body = declarationLine.slice(open + 1, close);
187
+ const declarations = [];
188
+ for (const match of body.matchAll(/(?:(?:public|private|protected|static|async|override|readonly|abstract|accessor|get|set)\s+)*(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$]*)\??\s*(?:<[^({;]+>)?\s*\(([^)]*)\)\s*(?::\s*[^={;]+)?\s*(?:\{|=>)/g)) {
189
+ if (jsControlKeyword(match[1])) continue;
190
+ declarations.push(nativeDeclaration(input, lineNumber, 'MethodDefinition', 'method', `${className}.${match[1]}`, {
191
+ methodName: match[1],
192
+ owner: className,
193
+ parameters: splitParameters(match[2])
194
+ }, true));
195
+ }
196
+ return declarations;
197
+ }
198
+
116
199
  export { scanJavaScriptLike };
@@ -0,0 +1,42 @@
1
+ export function conversionPlanSummary(routes) {
2
+ const summary = {
3
+ routes: routes.length,
4
+ byMode: {},
5
+ byReadiness: {},
6
+ byAdmissionAction: {},
7
+ readyRoutes: 0,
8
+ reviewRoutes: 0,
9
+ blockedRoutes: 0,
10
+ preserveSourceRoutes: 0,
11
+ targetAdapterRoutes: 0,
12
+ stubOnlyRoutes: 0,
13
+ semanticIndexOnlyRoutes: 0,
14
+ missingEvidence: 0,
15
+ runtimeAdapterRequirements: 0,
16
+ runtimeRoutesWithAdapters: 0,
17
+ blockers: 0,
18
+ reviewReasons: 0,
19
+ autoMergeClaims: 0,
20
+ semanticEquivalenceClaims: 0
21
+ };
22
+ for (const route of routes) {
23
+ summary.byMode[route.mode] = (summary.byMode[route.mode] ?? 0) + 1;
24
+ summary.byReadiness[route.readiness] = (summary.byReadiness[route.readiness] ?? 0) + 1;
25
+ summary.byAdmissionAction[route.admissionAction] = (summary.byAdmissionAction[route.admissionAction] ?? 0) + 1;
26
+ if (route.readiness === 'ready') summary.readyRoutes += 1;
27
+ if (route.readiness === 'needs-review' || route.readiness === 'ready-with-losses') summary.reviewRoutes += 1;
28
+ if (route.readiness === 'blocked') summary.blockedRoutes += 1;
29
+ if (route.mode === 'preserve-source') summary.preserveSourceRoutes += 1;
30
+ if (route.mode === 'target-adapter') summary.targetAdapterRoutes += 1;
31
+ if (route.mode === 'stub-only') summary.stubOnlyRoutes += 1;
32
+ if (route.mode === 'semantic-index-only') summary.semanticIndexOnlyRoutes += 1;
33
+ summary.missingEvidence += route.missingEvidence.length;
34
+ summary.runtimeAdapterRequirements += route.runtimeAdapterRequirements.length;
35
+ if (route.runtimeAdapterRequirements.length) summary.runtimeRoutesWithAdapters += 1;
36
+ summary.blockers += route.blockers.length;
37
+ summary.reviewReasons += route.review.length;
38
+ if (route.autoMergeClaim) summary.autoMergeClaims += 1;
39
+ if (route.semanticEquivalenceClaim) summary.semanticEquivalenceClaims += 1;
40
+ }
41
+ return summary;
42
+ }
@@ -9,6 +9,10 @@ import {
9
9
  normalizeProjectionMatrixTargets
10
10
  } from './coverage-matrix-profiles.js';
11
11
  import { createUniversalCapabilityMatrix } from './universal-capability-matrix.js';
12
+ import {
13
+ createUniversalRuntimeCapabilityMatrix,
14
+ runtimeRouteForConversion
15
+ } from './universal-runtime-capabilities.js';
12
16
  import {
13
17
  conversionMergeRefs,
14
18
  importsForConversionLanguage
@@ -17,6 +21,7 @@ import {
17
21
  conversionMergeScore,
18
22
  conversionScoreComponents
19
23
  } from './universal-conversion-plan-scoring.js';
24
+ import { conversionPlanSummary } from './universal-conversion-plan-summary.js';
20
25
 
21
26
  export function createUniversalConversionPlan(input = {}, context = {}) {
22
27
  const generatedAt = input.generatedAt ?? Date.now();
@@ -25,12 +30,21 @@ export function createUniversalConversionPlan(input = {}, context = {}) {
25
30
  ? input.universalCapabilityMatrix
26
31
  : createUniversalCapabilityMatrix({ ...input, generatedAt }, context);
27
32
  const targets = conversionTargets(input, matrix, context);
33
+ const runtimeMatrix = input.universalRuntimeCapabilityMatrix?.kind === 'frontier.lang.universalRuntimeCapabilityMatrix'
34
+ ? input.universalRuntimeCapabilityMatrix
35
+ : createUniversalRuntimeCapabilityMatrix({
36
+ ...input,
37
+ generatedAt,
38
+ sourceLanguages: matrix.languages,
39
+ targets
40
+ }, context);
28
41
  const evidence = input.evidence ?? [];
29
42
  const routes = (matrix.languages ?? []).flatMap((language) => targets.map((target) => conversionRoute(language, target, {
30
43
  evidence,
31
44
  generatedAt,
32
45
  imports: input.imports ?? [],
33
- matrix
46
+ matrix,
47
+ runtimeMatrix
34
48
  }, id)));
35
49
  return {
36
50
  kind: 'frontier.lang.universalConversionPlan',
@@ -41,6 +55,7 @@ export function createUniversalConversionPlan(input = {}, context = {}) {
41
55
  summary: conversionPlanSummary(routes),
42
56
  matrices: {
43
57
  universalCapability: matrix,
58
+ runtimeCapabilities: runtimeMatrix,
44
59
  projectionReadiness: matrix.matrices?.projectionReadiness,
45
60
  projectionTargets: matrix.matrices?.projectionTargets
46
61
  },
@@ -90,6 +105,8 @@ function conversionRoute(language, target, input, planId) {
90
105
  const sourceTarget = nativeLanguageCompileTarget(language.language, language.aliases) ?? normalizeNativeLanguageId(language.language);
91
106
  const targetCell = (language.projection?.targets ?? []).find((entry) => entry.target === target);
92
107
  const readinessCell = projectionReadinessCell(input.matrix, language, target);
108
+ const runtimeRoute = runtimeRouteForConversion(input.runtimeMatrix, language, target);
109
+ const runtime = conversionRuntime(runtimeRoute);
93
110
  const mode = conversionMode(language, target, sourceTarget, targetCell);
94
111
  const blockers = conversionBlockers(language, targetCell, mode);
95
112
  const review = conversionReviewReasons(language, targetCell, mode);
@@ -115,6 +132,8 @@ function conversionRoute(language, target, input, planId) {
115
132
  adapterKind: targetCell?.adapterKind,
116
133
  sourceProjection: language.projection?.sourceProjection,
117
134
  projectionReadiness: readinessCell,
135
+ runtime,
136
+ runtimeAdapterRequirements: runtime.adapterRequirements,
118
137
  evidence: conversionEvidence(language, targetCell),
119
138
  missingEvidence: conversionMissingEvidence(language, targetCell, mode),
120
139
  blockers,
@@ -172,6 +191,32 @@ function conversionReviewReasons(language, targetCell, mode) {
172
191
  ]);
173
192
  }
174
193
 
194
+ function conversionRuntime(runtimeRoute) {
195
+ if (!runtimeRoute) {
196
+ return {
197
+ requiredCapabilities: [],
198
+ satisfiedCapabilities: [],
199
+ adapterRequirements: [],
200
+ missingCapabilities: [],
201
+ readiness: 'ready',
202
+ blockers: [],
203
+ review: []
204
+ };
205
+ }
206
+ return {
207
+ routeId: runtimeRoute.id,
208
+ source: runtimeRoute.source,
209
+ target: runtimeRoute.target,
210
+ requiredCapabilities: runtimeRoute.requiredCapabilities,
211
+ satisfiedCapabilities: runtimeRoute.satisfiedCapabilities,
212
+ adapterRequirements: runtimeRoute.adapterRequirements,
213
+ missingCapabilities: runtimeRoute.missingCapabilities,
214
+ readiness: runtimeRoute.readiness,
215
+ blockers: runtimeRoute.blockers,
216
+ review: runtimeRoute.review
217
+ };
218
+ }
219
+
175
220
  function projectionReadinessCell(matrix, language, target) {
176
221
  const ids = uniqueStrings([language.language, ...(language.aliases ?? [])].map(normalizeNativeLanguageId));
177
222
  return (matrix.matrices?.projectionReadiness?.languages ?? [])
@@ -238,42 +283,3 @@ function conversionTasks(language, target, mode, blockers, review) {
238
283
  ...(blockers.length || review.length ? [`collect proof, replay, or oracle evidence for ${language.language} to ${target}`] : [])
239
284
  ]);
240
285
  }
241
-
242
- function conversionPlanSummary(routes) {
243
- const summary = {
244
- routes: routes.length,
245
- byMode: {},
246
- byReadiness: {},
247
- byAdmissionAction: {},
248
- readyRoutes: 0,
249
- reviewRoutes: 0,
250
- blockedRoutes: 0,
251
- preserveSourceRoutes: 0,
252
- targetAdapterRoutes: 0,
253
- stubOnlyRoutes: 0,
254
- semanticIndexOnlyRoutes: 0,
255
- missingEvidence: 0,
256
- blockers: 0,
257
- reviewReasons: 0,
258
- autoMergeClaims: 0,
259
- semanticEquivalenceClaims: 0
260
- };
261
- for (const route of routes) {
262
- summary.byMode[route.mode] = (summary.byMode[route.mode] ?? 0) + 1;
263
- summary.byReadiness[route.readiness] = (summary.byReadiness[route.readiness] ?? 0) + 1;
264
- summary.byAdmissionAction[route.admissionAction] = (summary.byAdmissionAction[route.admissionAction] ?? 0) + 1;
265
- if (route.readiness === 'ready') summary.readyRoutes += 1;
266
- if (route.readiness === 'needs-review' || route.readiness === 'ready-with-losses') summary.reviewRoutes += 1;
267
- if (route.readiness === 'blocked') summary.blockedRoutes += 1;
268
- if (route.mode === 'preserve-source') summary.preserveSourceRoutes += 1;
269
- if (route.mode === 'target-adapter') summary.targetAdapterRoutes += 1;
270
- if (route.mode === 'stub-only') summary.stubOnlyRoutes += 1;
271
- if (route.mode === 'semantic-index-only') summary.semanticIndexOnlyRoutes += 1;
272
- summary.missingEvidence += route.missingEvidence.length;
273
- summary.blockers += route.blockers.length;
274
- summary.reviewReasons += route.review.length;
275
- if (route.autoMergeClaim) summary.autoMergeClaims += 1;
276
- if (route.semanticEquivalenceClaim) summary.semanticEquivalenceClaims += 1;
277
- }
278
- return summary;
279
- }