@shapeshift-labs/frontier-lang-compiler 0.2.66 → 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 (47) 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 +177 -0
  6. package/dist/declarations/roundtrip.d.ts +2 -53
  7. package/dist/declarations/semantic-history-records.d.ts +277 -0
  8. package/dist/declarations/semantic-history.d.ts +45 -92
  9. package/dist/declarations/semantic-slice-admission.d.ts +111 -0
  10. package/dist/declarations/semantic-slice.d.ts +36 -1
  11. package/dist/declarations/universal-conversion-plan.d.ts +59 -0
  12. package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
  16. package/dist/internal/index-impl/createSemanticSlice.js +1 -1
  17. package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
  18. package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
  19. package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
  20. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
  21. package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
  22. package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
  23. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +46 -111
  24. package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
  25. package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
  26. package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
  27. package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
  28. package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
  29. package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
  30. package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
  31. package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
  32. package/dist/internal/index-impl/testSemanticSlice.js +4 -1
  33. package/dist/language-adapter-package-contracts.js +12 -57
  34. package/dist/language-adapter-package-rows.js +116 -0
  35. package/dist/universal-conversion-plan-summary.js +42 -0
  36. package/dist/universal-conversion-plan.js +46 -40
  37. package/dist/universal-runtime-capabilities.js +92 -0
  38. package/dist/universal-runtime-host-selectors.js +192 -0
  39. package/dist/universal-runtime-profiles.js +109 -0
  40. package/dist/universal-runtime-route-records.js +162 -0
  41. package/examples/js-frontier-rust-workbench-client.mjs +58 -1
  42. package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
  43. package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
  44. package/examples/js-frontier-rust-workbench-route.mjs +190 -0
  45. package/examples/js-frontier-rust-workbench-styles.mjs +3 -38
  46. package/examples/js-frontier-rust-workbench.mjs +22 -128
  47. package/package.json +1 -1
@@ -0,0 +1,142 @@
1
+ import{idFragment,uniqueStrings}from'../../native-import-utils.js';
2
+
3
+ export function semanticSliceAdmissionSelectedSurface(slice){
4
+ return{
5
+ entryRefs:[...(slice?.entryRefs??[])],
6
+ matchedEntryRefs:[...(slice?.matchedEntryRefs??[])],
7
+ unresolvedEntryRefs:[...(slice?.unresolvedEntryRefs??[])],
8
+ symbols:(slice?.symbols??[]).map(compactAdmissionSymbol),
9
+ ownershipRegions:(slice?.ownershipRegions??[]).map(compactAdmissionRegion),
10
+ nativeNodes:(slice?.nativeNodes??[]).map(compactAdmissionNativeNode),
11
+ relations:(slice?.relations??[]).map(compactAdmissionRelation),
12
+ occurrences:(slice?.occurrences??[]).map(compactAdmissionOccurrence),
13
+ sourceMapLinks:(slice?.sourceMapLinks??[]).map(compactAdmissionSourceMapLink),
14
+ sourceSpans:(slice?.sourceSpans??[]).map(compactAdmissionSpan),
15
+ sourceFiles:(slice?.sourceFiles??[]).map((file)=>({
16
+ path:file.path,
17
+ sourceHash:file.sourceHash,
18
+ spanCount:file.spanCount,
19
+ excerptCount:file.excerptCount,
20
+ sourceTextAvailable:file.sourceTextAvailable
21
+ })),
22
+ sourceHashes:slice?.mergeAdmission?.sourceHashes??[],
23
+ conflictKeys:uniqueStrings(slice?.mergeAdmission?.conflictKeys??[]),
24
+ ownershipKeys:uniqueStrings(slice?.mergeAdmission?.ownershipKeys??[])
25
+ };
26
+ }
27
+
28
+ export function semanticSliceSelectedSurfaceEvidence(slice,selectedSurface,testResult,readiness,action){
29
+ return{
30
+ id:`evidence_${idFragment(slice?.id??'slice')}_selected_surface`,
31
+ kind:'semantic-slice-selected-surface',
32
+ status:readiness==='blocked'||testResult?.status==='failed'?'failed':'passed',
33
+ path:slice?.sourcePath,
34
+ summary:`Semantic slice admission selected ${selectedSurface.symbols.length} symbol(s), ${selectedSurface.ownershipRegions.length} ownership region(s), ${selectedSurface.relations.length} relation(s), and ${selectedSurface.sourceFiles.length} source file(s).`,
35
+ metadata:{
36
+ sliceId:slice?.id,
37
+ action,
38
+ readiness,
39
+ testStatus:testResult?.status,
40
+ selectedSurface
41
+ }
42
+ };
43
+ }
44
+
45
+ function compactAdmissionSymbol(symbol){
46
+ return{
47
+ id:symbol.id,
48
+ name:symbol.name,
49
+ displayName:symbol.displayName,
50
+ kind:symbol.kind,
51
+ signature:symbol.signature,
52
+ nativeAstNodeId:symbol.nativeAstNodeId,
53
+ sourcePath:symbol.sourcePath,
54
+ sourceSpan:compactAdmissionSpan(symbol.sourceSpan),
55
+ ownershipRegionId:symbol.ownershipRegionId??symbol.metadata?.ownershipRegionId,
56
+ ownershipRegionKey:symbol.ownershipRegionKey??symbol.metadata?.ownershipRegionKey,
57
+ ownershipRegionKind:symbol.ownershipRegionKind??symbol.metadata?.ownershipRegionKind
58
+ };
59
+ }
60
+
61
+ function compactAdmissionRegion(region){
62
+ return{
63
+ id:region.id,
64
+ key:region.key,
65
+ conflictKey:region.conflictKey,
66
+ kind:region.kind??region.regionKind,
67
+ granularity:region.granularity,
68
+ symbolId:region.symbolId,
69
+ symbolName:region.symbolName,
70
+ nativeAstNodeId:region.nativeAstNodeId,
71
+ sourcePath:region.sourcePath,
72
+ sourceSpan:compactAdmissionSpan(region.sourceSpan)
73
+ };
74
+ }
75
+
76
+ function compactAdmissionNativeNode(node){
77
+ return{
78
+ id:node.id,
79
+ kind:node.kind,
80
+ languageKind:node.languageKind,
81
+ sourcePath:node.sourcePath,
82
+ sourceSpan:compactAdmissionSpan(node.sourceSpan??node.span),
83
+ parentId:node.parentId,
84
+ childCount:(node.children??[]).length
85
+ };
86
+ }
87
+
88
+ function compactAdmissionRelation(relation){
89
+ return{
90
+ id:relation.id,
91
+ predicate:relation.predicate,
92
+ kind:relation.kind,
93
+ sourceId:relation.sourceId,
94
+ targetId:relation.targetId
95
+ };
96
+ }
97
+
98
+ function compactAdmissionOccurrence(occurrence){
99
+ return{
100
+ id:occurrence.id,
101
+ symbolId:occurrence.symbolId,
102
+ nativeAstNodeId:occurrence.nativeAstNodeId,
103
+ role:occurrence.role,
104
+ sourcePath:occurrence.sourcePath,
105
+ sourceSpan:compactAdmissionSpan(occurrence.sourceSpan??occurrence.span)
106
+ };
107
+ }
108
+
109
+ function compactAdmissionSourceMapLink(link){
110
+ return{
111
+ id:link.id,
112
+ sourceMapId:link.sourceMapId,
113
+ sourcePath:link.sourcePath,
114
+ sourceHash:link.sourceHash,
115
+ targetPath:link.targetPath,
116
+ targetHash:link.targetHash,
117
+ semanticSymbolId:link.semanticSymbolId,
118
+ semanticOccurrenceId:link.semanticOccurrenceId,
119
+ semanticNodeId:link.semanticNodeId,
120
+ nativeAstNodeId:link.nativeAstNodeId,
121
+ ownershipRegionId:link.ownershipRegionId,
122
+ ownershipRegionKey:link.ownershipRegionKey,
123
+ ownershipRegionKind:link.ownershipRegionKind,
124
+ precision:link.precision,
125
+ sourceSpan:compactAdmissionSpan(link.sourceSpan)
126
+ };
127
+ }
128
+
129
+ function compactAdmissionSpan(span){
130
+ if(!span)return undefined;
131
+ return{
132
+ path:span.path,
133
+ sourceId:span.sourceId,
134
+ sourceHash:span.sourceHash,
135
+ startLine:span.startLine,
136
+ startColumn:span.startColumn,
137
+ endLine:span.endLine,
138
+ endColumn:span.endColumn,
139
+ startOffset:span.startOffset,
140
+ endOffset:span.endOffset
141
+ };
142
+ }
@@ -0,0 +1,100 @@
1
+ import{semanticSliceAssertion}from'./semanticSliceAssertion.js';import{semanticSliceExpectationRecords}from'./semanticSliceExpectationRecords.js';import{semanticSliceRegionMatchesRef}from'./semanticSliceRegionMatchesRef.js';import{semanticSliceSymbolMatchesRef}from'./semanticSliceSymbolMatchesRef.js';
2
+
3
+ export function semanticSliceExpectationAssertions(slice, options = {}) {
4
+ const assertions = [];
5
+ for (const expectation of semanticSliceTestExpectations(slice, options)) {
6
+ if (expectation.category === 'symbol') {
7
+ const ok = (slice?.symbols ?? []).some((symbol) => semanticSliceSymbolMatchesRef(symbol, expectation.ref));
8
+ assertions.push(semanticSliceAssertion(expectation.id, ok, ok ? `Expected symbol ${expectation.ref} is present.` : `Expected symbol ${expectation.ref} is missing.`, {
9
+ expectedRef: expectation.ref,
10
+ selectedSymbols: compactSymbols(slice?.symbols)
11
+ }));
12
+ continue;
13
+ }
14
+ if (expectation.category === 'region') {
15
+ const ok = (slice?.ownershipRegions ?? []).some((region) => semanticSliceRegionMatchesRef(region, expectation.ref));
16
+ assertions.push(semanticSliceAssertion(expectation.id, ok, ok ? `Expected region ${expectation.ref} is present.` : `Expected region ${expectation.ref} is missing.`, {
17
+ expectedRef: expectation.ref,
18
+ selectedRegions: compactRegions(slice?.ownershipRegions)
19
+ }));
20
+ continue;
21
+ }
22
+ if (expectation.category === 'sourceHash') {
23
+ const actual = semanticSliceActualSourceHash(slice, expectation.path);
24
+ const ok = Boolean(actual) && actual === expectation.expected;
25
+ assertions.push(semanticSliceAssertion(expectation.id, ok, ok ? `Expected source hash matched for ${expectation.path ?? 'the selected source file'}.` : `Expected source hash did not match for ${expectation.path ?? 'the selected source file'}.`, {
26
+ path: expectation.path,
27
+ expectedSourceHash: expectation.expected,
28
+ actualSourceHash: actual,
29
+ selectedSourceHashes: slice?.mergeAdmission?.sourceHashes ?? []
30
+ }));
31
+ continue;
32
+ }
33
+ if (expectation.category === 'summaryCount') {
34
+ const actual = semanticSliceSummaryCount(slice, expectation.key);
35
+ const ok = actual === expectation.expected;
36
+ assertions.push(semanticSliceAssertion(expectation.id, ok, ok ? `Expected ${expectation.key} count matched.` : `Expected ${expectation.key} count did not match.`, {
37
+ key: expectation.key,
38
+ expected: expectation.expected,
39
+ actual
40
+ }));
41
+ }
42
+ }
43
+ return assertions;
44
+ }
45
+
46
+ function semanticSliceTestExpectations(slice, options) {
47
+ const known = new Set(['symbol', 'region', 'sourceHash', 'summaryCount']);
48
+ const records = [
49
+ ...(slice?.verification?.expectedAssertions ?? []).filter((record) => known.has(record?.category)),
50
+ ...semanticSliceExpectationRecords(options)
51
+ ];
52
+ const seen = new Set();
53
+ const result = [];
54
+ for (const record of records) {
55
+ const key = `${record.id}:${record.expected}`;
56
+ if (seen.has(key)) continue;
57
+ seen.add(key);
58
+ result.push(record);
59
+ }
60
+ return result;
61
+ }
62
+
63
+ function semanticSliceActualSourceHash(slice, path) {
64
+ const hashes = slice?.mergeAdmission?.sourceHashes ?? [];
65
+ if (path) return hashes.find((entry) => entry.path === path)?.sourceHash ?? (slice?.sourceFiles ?? []).find((file) => file.path === path)?.sourceHash;
66
+ if (hashes.length === 1) return hashes[0]?.sourceHash;
67
+ if ((slice?.sourceFiles?.length ?? 0) === 1) return slice.sourceFiles[0]?.sourceHash;
68
+ return undefined;
69
+ }
70
+
71
+ function semanticSliceSummaryCount(slice, key) {
72
+ if (key === 'symbols') return slice?.summary?.symbols ?? slice?.symbols?.length ?? 0;
73
+ if (key === 'ownershipRegions') return slice?.summary?.ownershipRegions ?? slice?.ownershipRegions?.length ?? 0;
74
+ if (key === 'sourceFiles') return slice?.summary?.sourceFiles ?? slice?.sourceFiles?.length ?? 0;
75
+ return undefined;
76
+ }
77
+
78
+ function compactSymbols(symbols) {
79
+ return (symbols ?? []).map((symbol) => ({
80
+ id: symbol.id,
81
+ name: symbol.name,
82
+ displayName: symbol.displayName,
83
+ nativeAstNodeId: symbol.nativeAstNodeId,
84
+ ownershipRegionId: symbol.ownershipRegionId ?? symbol.metadata?.ownershipRegionId,
85
+ ownershipRegionKey: symbol.ownershipRegionKey ?? symbol.metadata?.ownershipRegionKey
86
+ }));
87
+ }
88
+
89
+ function compactRegions(regions) {
90
+ return (regions ?? []).map((region) => ({
91
+ id: region.id,
92
+ key: region.key,
93
+ kind: region.kind ?? region.regionKind,
94
+ granularity: region.granularity,
95
+ symbolId: region.symbolId,
96
+ symbolName: region.symbolName,
97
+ sourcePath: region.sourcePath,
98
+ nativeAstNodeId: region.nativeAstNodeId
99
+ }));
100
+ }
@@ -0,0 +1,75 @@
1
+ import{readStringArray}from'./readStringArray.js';
2
+
3
+ export function semanticSliceExpectationRecords(options={}) {
4
+ const records = [];
5
+ for (const ref of readStringArray(options.expectedSymbols ?? options.expectedSymbolRefs)) {
6
+ records.push({ id: `expectedSymbol:${ref}`, category: 'symbol', ref, expected: true });
7
+ }
8
+ for (const ref of readStringArray(options.expectedRegions ?? options.expectedRegionRefs)) {
9
+ records.push({ id: `expectedRegion:${ref}`, category: 'region', ref, expected: true });
10
+ }
11
+ for (const entry of semanticSliceSourceHashExpectationRecords(options.expectedSourceHashes)) {
12
+ records.push(entry);
13
+ }
14
+ addSummaryCount(records, 'symbols', options.expectedSymbolCount);
15
+ addSummaryCount(records, 'ownershipRegions', options.expectedRegionCount);
16
+ addSummaryCount(records, 'sourceFiles', options.expectedSourceFileCount);
17
+ return uniqueExpectationRecords(records);
18
+ }
19
+
20
+ export function semanticSliceSourceHashExpectationRecords(value) {
21
+ const records = [];
22
+ const add = (path, sourceHash) => {
23
+ if (!sourceHash) return;
24
+ const normalizedPath = path === undefined || path === null || path === '' ? undefined : String(path);
25
+ records.push({
26
+ id: `expectedSourceHash:${normalizedPath ?? '*'}`,
27
+ category: 'sourceHash',
28
+ path: normalizedPath,
29
+ expected: String(sourceHash)
30
+ });
31
+ };
32
+ if (!value) return records;
33
+ if (Array.isArray(value)) {
34
+ for (const entry of value) {
35
+ if (Array.isArray(entry)) {
36
+ add(entry[0], entry[1]);
37
+ continue;
38
+ }
39
+ if (entry && typeof entry === 'object') {
40
+ add(entry.path ?? entry.sourcePath, entry.sourceHash ?? entry.hash ?? entry.expected);
41
+ }
42
+ }
43
+ return records;
44
+ }
45
+ if (typeof value.entries === 'function') {
46
+ for (const [path, sourceHash] of value.entries()) add(path, sourceHash);
47
+ return records;
48
+ }
49
+ if (typeof value === 'object') {
50
+ for (const [path, sourceHash] of Object.entries(value)) add(path, sourceHash);
51
+ }
52
+ return records;
53
+ }
54
+
55
+ function addSummaryCount(records, key, value) {
56
+ if (!Number.isFinite(value)) return;
57
+ records.push({
58
+ id: `expectedCount:${key}`,
59
+ category: 'summaryCount',
60
+ key,
61
+ expected: Math.max(0, Math.floor(value))
62
+ });
63
+ }
64
+
65
+ function uniqueExpectationRecords(records) {
66
+ const seen = new Set();
67
+ const result = [];
68
+ for (const record of records) {
69
+ const key = `${record.id}:${record.expected}`;
70
+ if (seen.has(key)) continue;
71
+ seen.add(key);
72
+ result.push(record);
73
+ }
74
+ return result;
75
+ }
@@ -1,8 +1,11 @@
1
- export function semanticSliceExpectedAssertions(selection, unresolvedEntryRefs) {
1
+ import{semanticSliceExpectationRecords}from'./semanticSliceExpectationRecords.js';
2
+
3
+ export function semanticSliceExpectedAssertions(selection, unresolvedEntryRefs, options = {}) {
2
4
  return [
3
5
  { id: 'entryRefsResolved', expected: unresolvedEntryRefs.length === 0 },
4
6
  { id: 'nonEmptySelection', expected: selection.symbols.length + selection.regions.length + selection.nativeNodes.length > 0 },
5
7
  { id: 'sourceMapLinks', expected: selection.mappings.length > 0 },
6
- { id: 'conflictKeys', expected: true }
8
+ { id: 'conflictKeys', expected: true },
9
+ ...semanticSliceExpectationRecords(options)
7
10
  ];
8
11
  }
@@ -1,5 +1,5 @@
1
1
  import{idFragment,maxSemanticMergeReadiness}from'../../native-import-utils.js';
2
- import{semanticSliceAssertion}from'./semanticSliceAssertion.js';import{semanticSliceSourceHashAssertions}from'./semanticSliceSourceHashAssertions.js';
2
+ import{semanticSliceAssertion}from'./semanticSliceAssertion.js';import{semanticSliceExpectationAssertions}from'./semanticSliceExpectationAssertions.js';import{semanticSliceSourceHashAssertions}from'./semanticSliceSourceHashAssertions.js';
3
3
  export function testSemanticSlice(slice, options = {}) {
4
4
  const assertions = [];
5
5
  assertions.push(semanticSliceAssertion('kind', slice?.kind === 'frontier.lang.semanticSlice', 'Input is a Frontier semantic slice.'));
@@ -11,6 +11,8 @@ export function testSemanticSlice(slice, options = {}) {
11
11
  assertions.push(semanticSliceAssertion('conflictKeys', (slice?.mergeAdmission?.conflictKeys?.length ?? 0) > 0, 'Slice exposes merge-admission conflict keys.'));
12
12
  const sourceHashAssertions = semanticSliceSourceHashAssertions(slice, options.currentSources);
13
13
  assertions.push(...sourceHashAssertions);
14
+ const expectationAssertions = semanticSliceExpectationAssertions(slice, options);
15
+ assertions.push(...expectationAssertions);
14
16
  const failed = assertions.filter((assertion) => assertion.status === 'failed');
15
17
  const warnings = assertions.filter((assertion) => assertion.status === 'warning');
16
18
  const readiness = failed.length
@@ -31,6 +33,7 @@ export function testSemanticSlice(slice, options = {}) {
31
33
  warnings: warnings.length,
32
34
  failed: failed.length,
33
35
  sourceHashChecks: sourceHashAssertions.length,
36
+ expectedAssertions: expectationAssertions.length,
34
37
  symbols: slice?.summary?.symbols ?? slice?.symbols?.length ?? 0,
35
38
  ownershipRegions: slice?.summary?.ownershipRegions ?? slice?.ownershipRegions?.length ?? 0,
36
39
  sourceMapLinks: slice?.summary?.sourceMapLinks ?? slice?.sourceMapLinks?.length ?? 0
@@ -10,6 +10,7 @@ import {
10
10
  normalizeNativeLanguageId,
11
11
  uniqueStrings
12
12
  } from './native-import-utils.js';
13
+ import { packageRows } from './language-adapter-package-rows.js';
13
14
 
14
15
  export const LanguageAdapterPackageReleaseReadinessStatuses = Object.freeze([
15
16
  'ready',
@@ -18,23 +19,6 @@ export const LanguageAdapterPackageReleaseReadinessStatuses = Object.freeze([
18
19
  'blocked'
19
20
  ]);
20
21
 
21
- const packageRows = [
22
- row('@shapeshift-labs/frontier-lang-typescript', '0.3.8', 'typescript', 'typescript-compiler-api', { target: 'typescript' }),
23
- row('@shapeshift-labs/frontier-lang-javascript', '0.2.8', 'javascript', 'estree', { target: 'javascript', formats: ['estree', 'babel'] }),
24
- row('@shapeshift-labs/frontier-lang-rust', '0.2.8', 'rust', 'rust-syn', { target: 'rust', proofKeys: ['parserAst', 'sourceMap', 'semanticSidecar', 'macroExpansionEvidence'] }),
25
- row('@shapeshift-labs/frontier-lang-python', '0.2.8', 'python', 'python-ast', { target: 'python', formats: ['python-ast', 'libcst'] }),
26
- row('@shapeshift-labs/frontier-lang-c', '0.2.8', 'c', 'clang-ast-json', { target: 'c', proofKeys: ['parserAst', 'sourceMap', 'semanticSidecar', 'compileCommandsHash', 'preprocessorRecordsHash'] }),
27
- platform('@shapeshift-labs/frontier-lang-java', '0.1.8', 'java', 'java-ast', ['semanticdb', 'lsp']),
28
- platform('@shapeshift-labs/frontier-lang-kotlin', '0.1.8', 'kotlin', 'kotlin-psi', ['semanticdb', 'lsp']),
29
- platform('@shapeshift-labs/frontier-lang-swift', '0.1.8', 'swift', 'swift-syntax', ['sourcekit-lsp', 'lsp']),
30
- platform('@shapeshift-labs/frontier-lang-csharp', '0.1.8', 'csharp', 'roslyn-csharp', ['lsp']),
31
- platform('@shapeshift-labs/frontier-lang-go', '0.1.8', 'go', 'go-ast', ['lsp']),
32
- platform('@shapeshift-labs/frontier-lang-clang', '0.1.8', 'c', 'clang-ast-json', ['lsp'], {
33
- supportedLanguages: ['c', 'cpp'],
34
- proofKeys: ['parserAst', 'semanticIndex', 'compileCommandsHash', 'preprocessorRecordsHash']
35
- })
36
- ];
37
-
38
22
  export const LanguageAdapterPackageContracts = Object.freeze(packageRows.map(createLanguageAdapterPackageContract));
39
23
 
40
24
  export function createLanguageAdapterPackageContract(input = {}) {
@@ -131,7 +115,7 @@ export function getLanguageAdapterPackageContract(ref, contracts = LanguageAdapt
131
115
  contract.packageName === text ||
132
116
  contract.package.name === text ||
133
117
  contract.package.adapterId === text ||
134
- contract.sourceParser.language === normalized
118
+ (normalized && contractSupportsLanguage(contract, normalized))
135
119
  );
136
120
  }
137
121
 
@@ -142,7 +126,7 @@ export function queryLanguageAdapterPackageContracts(query = {}, contracts = Lan
142
126
  return (contracts ?? []).filter((contract) =>
143
127
  (!query.packageName || contract.packageName === query.packageName) &&
144
128
  (!query.packageClass || contract.package.packageClass === query.packageClass) &&
145
- (!language || contract.sourceParser.language === language) &&
129
+ contractSupportsLanguage(contract, language) &&
146
130
  (!target || contract.targetProjection.targets.includes(target)) &&
147
131
  (!semanticFormat || contract.semanticIndex.formats.map((format) => format.toLowerCase()).includes(semanticFormat)) &&
148
132
  (query.releaseReady === undefined || contract.releaseReadiness.releaseReady === query.releaseReady) &&
@@ -168,44 +152,6 @@ export function summarizeLanguageAdapterPackageContracts(contracts = LanguageAda
168
152
  });
169
153
  }
170
154
 
171
- function row(packageName, packageVersion, language, parser, input = {}) {
172
- return {
173
- packageName,
174
- packageVersion,
175
- language,
176
- parser,
177
- supportedFormats: input.formats,
178
- target: input.target,
179
- packageClass: 'target-projection',
180
- releaseReadiness: { status: 'ready-with-losses', releaseReady: true, versionSource: 'package-json-dependency' },
181
- semanticIndex: { formats: ['frontier-semantic-index', 'scip', 'lsif', 'lsp'], hostEvidenceRequired: false },
182
- proofKeys: input.proofKeys
183
- };
184
- }
185
-
186
- function platform(packageName, packageVersion, language, parser, semanticFormats, input = {}) {
187
- const published = packageVersion !== '0.0.0';
188
- return {
189
- packageName,
190
- packageVersion,
191
- language,
192
- parser,
193
- supportedLanguages: input.supportedLanguages,
194
- supportedFormats: input.formats,
195
- packageClass: 'platform-importer',
196
- targetProjection: { targets: [], caveats: input.targetCaveats },
197
- releaseReadiness: {
198
- status: published ? 'ready-with-losses' : 'needs-review',
199
- releaseReady: published,
200
- versionSource: published ? 'static-package-catalog' : 'related-package-catalog-placeholder',
201
- signals: input.signals
202
- },
203
- semanticIndex: { formats: ['frontier-semantic-index', ...semanticFormats], hostEvidenceRequired: true },
204
- proofEvidence: { hostEvidenceRequired: true, requiredEvidenceKeys: input.proofKeys ?? ['parserAst', 'semanticIndex', 'buildGraphEvidence'] },
205
- parserCaveats: input.parserCaveats
206
- };
207
- }
208
-
209
155
  function projectionRows(input, language) {
210
156
  const targets = input.targetProjection?.targets ?? input.targets ?? input.target ?? nativeProjectionTargetsForLanguage(language);
211
157
  const normalizedTargets = freezeStrings(targets);
@@ -274,6 +220,15 @@ function targetProjectionCaveats(targetRows, packageClass) {
274
220
  return caveats;
275
221
  }
276
222
 
223
+ function contractSupportsLanguage(contract, language) {
224
+ if (!language) return true;
225
+ const languageIds = [
226
+ contract.sourceParser?.language,
227
+ ...(contract.sourceParser?.supportedLanguages ?? [])
228
+ ].map(normalizeNativeLanguageId).filter(Boolean);
229
+ return languageIds.includes(language);
230
+ }
231
+
277
232
  function adapterFamily(language, parser) {
278
233
  if (language === 'javascript' || language === 'typescript') return 'javascript-typescript';
279
234
  if (language === 'java' || language === 'kotlin') return 'jvm';
@@ -0,0 +1,116 @@
1
+ const packageRows = [
2
+ row('@shapeshift-labs/frontier-lang-typescript', '0.3.8', 'typescript', 'typescript-compiler-api', { target: 'typescript' }),
3
+ row('@shapeshift-labs/frontier-lang-javascript', '0.2.8', 'javascript', 'estree', { target: 'javascript', formats: ['estree', 'babel'] }),
4
+ row('@shapeshift-labs/frontier-lang-rust', '0.2.8', 'rust', 'rust-syn', { target: 'rust', proofKeys: ['parserAst', 'sourceMap', 'semanticSidecar', 'macroExpansionEvidence'] }),
5
+ row('@shapeshift-labs/frontier-lang-python', '0.2.8', 'python', 'python-ast', { target: 'python', formats: ['python-ast', 'libcst'] }),
6
+ row('@shapeshift-labs/frontier-lang-c', '0.2.8', 'c', 'clang-ast-json', { target: 'c', proofKeys: ['parserAst', 'sourceMap', 'semanticSidecar', 'compileCommandsHash', 'preprocessorRecordsHash'] }),
7
+ platform('@shapeshift-labs/frontier-lang-java', '0.1.8', 'java', 'java-ast', ['semanticdb', 'lsp']),
8
+ platform('@shapeshift-labs/frontier-lang-kotlin', '0.1.8', 'kotlin', 'kotlin-psi', ['semanticdb', 'lsp']),
9
+ platform('@shapeshift-labs/frontier-lang-swift', '0.1.8', 'swift', 'swift-syntax', ['sourcekit-lsp', 'lsp']),
10
+ platform('@shapeshift-labs/frontier-lang-csharp', '0.1.8', 'csharp', 'roslyn-csharp', ['lsp']),
11
+ platform('@shapeshift-labs/frontier-lang-go', '0.1.8', 'go', 'go-ast', ['lsp']),
12
+ platform('@shapeshift-labs/frontier-lang-clang', '0.1.8', 'c', 'clang-ast-json', ['lsp'], {
13
+ supportedLanguages: ['c', 'cpp'],
14
+ proofKeys: ['parserAst', 'semanticIndex', 'compileCommandsHash', 'preprocessorRecordsHash']
15
+ }),
16
+ plannedPlatform('@shapeshift-labs/frontier-lang-lisp', 'lisp', 'tree-sitter-lisp', ['lsp'], {
17
+ supportedLanguages: ['lisp', 'common-lisp', 'scheme', 'racket'],
18
+ formats: ['tree-sitter'],
19
+ family: 'lisp-scheme',
20
+ parserCaveats: ['Lisp and Scheme contracts need host evidence for reader conditionals, package/module systems, macros, quoting semantics, source ranges, and comment/trivia preservation.']
21
+ }),
22
+ plannedPlatform('@shapeshift-labs/frontier-lang-haskell', 'haskell', 'ghc-api', ['lsp'], {
23
+ parserCaveats: ['GHC API parse trees require host evidence for CPP, Template Haskell, quasiquotes, package flags, renamer/typechecker facts, source ranges, and layout-sensitive source preservation.']
24
+ }),
25
+ plannedPlatform('@shapeshift-labs/frontier-lang-erlang', 'erlang', 'erl_parse', ['lsp'], {
26
+ parserCaveats: ['Erlang parser contracts require host evidence for include paths, record definitions, macros, parse transforms, specs, source ranges, and comments/trivia preservation.']
27
+ }),
28
+ plannedPlatform('@shapeshift-labs/frontier-lang-elixir', 'elixir', 'elixir-quoted', ['lsp'], {
29
+ parserCaveats: ['Elixir quoted AST contracts require host evidence for macro expansion, aliases/imports, protocols, Mix compile context, generated code, source ranges, and formatter trivia.']
30
+ }),
31
+ plannedPlatform('@shapeshift-labs/frontier-lang-ruby', 'ruby', 'prism', ['lsp'], {
32
+ parserCaveats: ['Ruby parser contracts require host evidence for dynamic constant lookup, refinements, metaprogramming, comments/trivia, source ranges, and optional type signature ecosystems.']
33
+ }),
34
+ plannedPlatform('@shapeshift-labs/frontier-lang-php', 'php', 'php-parser', ['lsp'], {
35
+ parserCaveats: ['PHP parser contracts require host evidence for Composer/autoload context, conditional declarations, attributes, PHPDoc, generated code, source ranges, and comments/trivia.']
36
+ }),
37
+ plannedPlatform('@shapeshift-labs/frontier-lang-lua', 'lua', 'luaparse', ['lsp'], {
38
+ parserCaveats: ['Lua parser contracts require host evidence for dynamic module loading, metatables, embedded DSLs, comments/trivia, source ranges, and version-specific syntax.']
39
+ }),
40
+ plannedPlatform('@shapeshift-labs/frontier-lang-r', 'r', 'r-parser', ['lsp'], {
41
+ parserCaveats: ['R parser contracts require host evidence for non-standard evaluation, library search paths, generated code, comments/trivia, source ranges, and version-specific syntax.']
42
+ }),
43
+ plannedPlatform('@shapeshift-labs/frontier-lang-julia', 'julia', 'julia-syntax', ['lsp'], {
44
+ parserCaveats: ['Julia parser contracts require host evidence for macro expansion, generated functions, multiple dispatch/type facts, package environments, source ranges, and comments/trivia.']
45
+ }),
46
+ plannedPlatform('@shapeshift-labs/frontier-lang-zig', 'zig', 'zig-ast', ['lsp'], {
47
+ parserCaveats: ['Zig parser contracts require host evidence for comptime evaluation, build.zig context, imports, generated code, source ranges, and comments/trivia preservation.']
48
+ }),
49
+ plannedPlatform('@shapeshift-labs/frontier-lang-ocaml', 'ocaml', 'ocaml-parsetree', ['lsp'], {
50
+ parserCaveats: ['OCaml parsetree contracts require host evidence for PPX rewrites, module/functor resolution, typedtree facts, Dune context, source ranges, and comments/trivia.']
51
+ }),
52
+ plannedPlatform('@shapeshift-labs/frontier-lang-scala', 'scala', 'scalameta', ['semanticdb', 'lsp'], {
53
+ parserCaveats: ['Scala parser contracts require host evidence for Scala 2/3 dialects, macros, givens/implicits, SemanticDB/TASTy facts, build targets, source ranges, and comments/trivia.']
54
+ }),
55
+ plannedPlatform('@shapeshift-labs/frontier-lang-dart', 'dart', 'dart-analyzer', ['lsp'], {
56
+ parserCaveats: ['Dart analyzer contracts require host evidence for analysis context, null-safety mode, package config, build_runner generated sources, source ranges, and comments/trivia.']
57
+ }),
58
+ plannedPlatform('@shapeshift-labs/frontier-lang-sql', 'sql', 'sqlparser', ['lsp'], {
59
+ supportedLanguages: ['sql', 'postgresql', 'postgres', 'mysql', 'sqlite', 'tsql'],
60
+ parserCaveats: ['SQL parser contracts require host evidence for dialect selection, schema/catalog metadata, migrations, vendor extensions, procedural SQL blocks, source ranges, and comments/trivia.']
61
+ }),
62
+ plannedPlatform('@shapeshift-labs/frontier-lang-shader', 'shader', 'tree-sitter-shader', ['lsp', 'spirv-reflect'], {
63
+ supportedLanguages: ['shader', 'glsl', 'hlsl', 'wgsl', 'slang'],
64
+ formats: ['tree-sitter'],
65
+ family: 'shader',
66
+ parserCaveats: ['Shader parser contracts require host evidence for dialect selection, preprocessor/includes, entry points, resource layouts, generated variants, source ranges, and reflection data.']
67
+ })
68
+ ];
69
+
70
+ export { packageRows };
71
+
72
+ function row(packageName, packageVersion, language, parser, input = {}) {
73
+ return {
74
+ packageName,
75
+ packageVersion,
76
+ language,
77
+ parser,
78
+ supportedFormats: input.formats,
79
+ target: input.target,
80
+ packageClass: 'target-projection',
81
+ releaseReadiness: { status: 'ready-with-losses', releaseReady: true, versionSource: 'package-json-dependency' },
82
+ semanticIndex: { formats: ['frontier-semantic-index', 'scip', 'lsif', 'lsp'], hostEvidenceRequired: false },
83
+ proofKeys: input.proofKeys
84
+ };
85
+ }
86
+
87
+ function platform(packageName, packageVersion, language, parser, semanticFormats, input = {}) {
88
+ const published = packageVersion !== '0.0.0';
89
+ return {
90
+ packageName,
91
+ packageVersion,
92
+ language,
93
+ parser,
94
+ family: input.family,
95
+ supportedLanguages: input.supportedLanguages,
96
+ supportedFormats: input.formats,
97
+ packageClass: 'platform-importer',
98
+ targetProjection: { targets: [], caveats: input.targetCaveats },
99
+ releaseReadiness: {
100
+ status: published ? 'ready-with-losses' : 'needs-review',
101
+ releaseReady: published,
102
+ versionSource: published ? 'static-package-catalog' : 'related-package-catalog-placeholder',
103
+ signals: input.signals
104
+ },
105
+ semanticIndex: { formats: ['frontier-semantic-index', ...semanticFormats], hostEvidenceRequired: true },
106
+ proofEvidence: { hostEvidenceRequired: true, requiredEvidenceKeys: input.proofKeys ?? ['parserAst', 'semanticIndex', 'buildGraphEvidence'] },
107
+ parserCaveats: input.parserCaveats
108
+ };
109
+ }
110
+
111
+ function plannedPlatform(packageName, language, parser, semanticFormats, input = {}) {
112
+ return platform(packageName, '0.0.0', language, parser, semanticFormats, {
113
+ proofKeys: ['parserAst', 'semanticIndex', 'sourceMap', 'sourcePreservationEvidence', 'projectionCaveats'],
114
+ ...input
115
+ });
116
+ }
@@ -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
+ }