@shapeshift-labs/frontier-lang-compiler 0.2.53 → 0.2.55

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.
package/README.md CHANGED
@@ -19,6 +19,19 @@ const result = compileFrontierSource(source, { target: 'typescript' });
19
19
  if (result.ok) console.log(result.output);
20
20
  ```
21
21
 
22
+ Run a small end-to-end demo after installing or building the package:
23
+
24
+ ```sh
25
+ npm run build
26
+ node examples/native-js-to-rust-demo.mjs
27
+ ```
28
+
29
+ The demo prints JavaScript source, the Frontier universal AST/semantic-index summary,
30
+ Rust declaration stubs, a host-adapter Rust projection, and a direct Frontier-source
31
+ to Rust projection. Native JavaScript projection remains loss-aware: without a
32
+ target adapter the compiler emits review-required stubs rather than claiming a
33
+ lossless JS-to-Rust transpilation.
34
+
22
35
  Emit code with declaration-level source-map sidecars for semantic review and merge admission:
23
36
 
24
37
  ```js
package/bench/smoke.mjs CHANGED
@@ -97,6 +97,7 @@ console.log(JSON.stringify({
97
97
  nativeTargetAdapterDurationMs: Number(transformMetrics.nativeTargetAdapterDurationMs.toFixed(2)),
98
98
  regionScanImports: sourceChangeMetrics.regionScanImports,
99
99
  regionScanSymbols: sourceChangeMetrics.regionScanSymbols,
100
+ regionScanDependencyRelations: sourceChangeMetrics.regionScanDependencyRelations,
100
101
  regionScanOwnershipRegions: sourceChangeMetrics.regionScanOwnershipRegions,
101
102
  regionScanDurationMs: Number(sourceChangeMetrics.regionScanDurationMs.toFixed(2)),
102
103
  changeProjectionSets: sourceChangeMetrics.changeProjectionSets,
@@ -22,6 +22,7 @@ function measureRegionScan() {
22
22
  language: 'typescript',
23
23
  sourcePath: `src/regions-${index}.ts`,
24
24
  sourceText: `
25
+ export function normalizeCount${index}(value) { return value; }
25
26
  export const appRoutes${index} = [
26
27
  { path: "/${index}", component: Screen${index} },
27
28
  { path: "/${index}/settings", component: Settings${index} }
@@ -31,7 +32,7 @@ function measureRegionScan() {
31
32
  legal: { title: "Legal ${index}" }
32
33
  };
33
34
  export const runtimeConfig${index} = {
34
- limits: { count: ${index} },
35
+ limits: { count: normalizeCount${index}(${index}) },
35
36
  resolve(id) { return id; }
36
37
  };
37
38
  export const helpers${index} = {
@@ -45,6 +46,7 @@ function measureRegionScan() {
45
46
  return {
46
47
  regionScanImports: regionScanImports.length,
47
48
  regionScanSymbols: regionScanImports.reduce((sum, entry) => sum + entry.imported.semanticIndex.symbols.length, 0),
49
+ regionScanDependencyRelations: regionScanImports.reduce((sum, entry) => sum + entry.sidecar.dependencies.total, 0),
48
50
  regionScanOwnershipRegions: regionScanImports.reduce((sum, entry) => sum + entry.sidecar.ownershipRegions.length, 0),
49
51
  regionScanDurationMs
50
52
  };
@@ -132,6 +132,7 @@ export interface SemanticImportSidecarImportEntry {
132
132
  readonly universalAstLayerIds: readonly string[];
133
133
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
134
134
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
135
+ readonly dependencyRelationCount: number; readonly dependencyPredicates: readonly string[];
135
136
  readonly readiness: SemanticMergeReadiness;
136
137
  readonly emptySemanticIndex: boolean;
137
138
  readonly regionTaxonomy?: SemanticImportRegionTaxonomySummary;
@@ -228,6 +229,13 @@ export interface SemanticImportSidecarParadigmSemanticsSummary {
228
229
  readonly empty: boolean;
229
230
  }
230
231
 
232
+ export interface SemanticImportDependencySummary {
233
+ readonly total: number; readonly calls: number; readonly uses: number; readonly references: number;
234
+ readonly imports: number; readonly extends: number; readonly implements: number; readonly includes: number; readonly requires: number;
235
+ readonly byPredicate: Readonly<Record<string, number>>; readonly predicates: readonly string[];
236
+ readonly ids: readonly string[]; readonly sourceSymbolIds: readonly string[]; readonly targetSymbolIds: readonly string[];
237
+ }
238
+
231
239
  export interface SemanticImportSidecar {
232
240
  readonly kind: 'frontier.lang.semanticImportSidecar';
233
241
  readonly version: 1;
@@ -238,11 +246,7 @@ export interface SemanticImportSidecar {
238
246
  readonly imports: readonly SemanticImportSidecarImportEntry[];
239
247
  readonly symbols: readonly SemanticImportSidecarSymbol[];
240
248
  readonly ownershipRegions: readonly SemanticImportOwnershipRegion[];
241
- readonly sourceMaps: {
242
- readonly total: number;
243
- readonly mappings: number;
244
- readonly ids: readonly string[];
245
- };
249
+ readonly sourceMaps: { readonly total: number; readonly mappings: number; readonly ids: readonly string[] };
246
250
  readonly sourcePreservation: {
247
251
  readonly total: number;
248
252
  readonly ids: readonly string[];
@@ -259,6 +263,7 @@ export interface SemanticImportSidecar {
259
263
  readonly universalAstLayers: SemanticImportSidecarUniversalAstLayerSummary;
260
264
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
261
265
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
266
+ readonly dependencies: SemanticImportDependencySummary;
262
267
  readonly patchHints: readonly SemanticImportPatchHint[];
263
268
  readonly quality: SemanticImportSidecarQuality;
264
269
  readonly admission: SemanticImportSidecarAdmission;
@@ -280,11 +285,7 @@ export interface SemanticImportSidecar {
280
285
  readonly reviewLossIds: readonly string[];
281
286
  };
282
287
  readonly regionTaxonomy: SemanticImportRegionTaxonomySummary;
283
- readonly evidence: {
284
- readonly total: number;
285
- readonly failed: readonly string[];
286
- readonly ids: readonly string[];
287
- };
288
+ readonly evidence: { readonly total: number; readonly failed: readonly string[]; readonly ids: readonly string[] };
288
289
  readonly summary: {
289
290
  readonly imports: number;
290
291
  readonly symbols: number;
@@ -300,6 +301,7 @@ export interface SemanticImportSidecar {
300
301
  readonly paradigmSemanticsRecords: number;
301
302
  readonly paradigmSemanticsGroups: number;
302
303
  readonly paradigmSemanticsLoweringRecords: number;
304
+ readonly dependencyRelations: number; readonly dependencyPredicates: readonly string[];
303
305
  readonly patchHints: number;
304
306
  readonly evidenceWarnings: number;
305
307
  readonly readiness: SemanticMergeReadiness;
@@ -1,4 +1,4 @@
1
- import{idFragment}from'../../native-import-utils.js';import{lightweightCoverageLosses,scanNativeDeclarations}from'../../native-region-scanner.js';import{semanticOwnershipRegionForDeclaration}from'../../semantic-import-regions.js';import{createSemanticIndexRecord,hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
1
+ import{idFragment}from'../../native-import-utils.js';import{lightweightDependencyRelations}from'../../lightweight-dependency-relations.js';import{lightweightCoverageLosses,scanNativeDeclarations}from'../../native-region-scanner.js';import{semanticOwnershipRegionForDeclaration}from'../../semantic-import-regions.js';import{createSemanticIndexRecord,hashSemanticValue}from'@shapeshift-labs/frontier-lang-kernel';
2
2
  export function createLightweightNativeImport(input) {
3
3
  const parser = input.parser ?? `${input.language}.lightweight-declaration-scan`;
4
4
  const rootId = 'native_root';
@@ -20,6 +20,7 @@ export function createLightweightNativeImport(input) {
20
20
  const facts = [];
21
21
  const mappings = [];
22
22
  const evidenceId = `evidence_${idFragment(input.sourcePath ?? input.language)}_lightweight_scan`;
23
+ const dependencies = lightweightDependencyRelations(input, declarations, documentId);
23
24
 
24
25
  for (const declaration of declarations) {
25
26
  const ownershipRegion = semanticOwnershipRegionForDeclaration(input, declaration, documentId);
@@ -105,6 +106,9 @@ export function createLightweightNativeImport(input) {
105
106
  }
106
107
  if (declaration.loss) losses.push(declaration.loss);
107
108
  }
109
+ occurrences.push(...dependencies.occurrences);
110
+ relations.push(...dependencies.relations);
111
+ facts.push(...dependencies.facts);
108
112
  losses.push(...lightweightCoverageLosses(input, declarations, input.sourcePreservation));
109
113
 
110
114
  const semanticIndex = createSemanticIndexRecord({
@@ -124,12 +128,13 @@ export function createLightweightNativeImport(input) {
124
128
  kind: 'import',
125
129
  status: 'passed',
126
130
  path: input.sourcePath,
127
- summary: `Lightweight declaration scan found ${symbols.length} symbol(s).`,
128
- metadata: { parser }
131
+ summary: `Lightweight declaration scan found ${symbols.length} symbol(s) and ${dependencies.summary.total} dependency edge(s).`,
132
+ metadata: { parser, dependencyRelations: dependencies.summary.total }
129
133
  }],
130
134
  metadata: {
131
135
  parser,
132
136
  coverage: 'declarations-only',
137
+ dependencyRelations: dependencies.summary,
133
138
  unsupported: ['full expression AST', 'type checking', 'control flow', 'comments and formatting preservation']
134
139
  }
135
140
  });
@@ -145,6 +150,8 @@ export function createLightweightNativeImport(input) {
145
150
  parser,
146
151
  scanKind: 'lightweight-declaration-scan',
147
152
  declarationCount: declarations.length,
153
+ dependencyRelationCount: dependencies.summary.total,
154
+ dependencyOccurrenceCount: dependencies.occurrences.length,
148
155
  ...(input.sourcePreservation ? {
149
156
  sourcePreservationId: input.sourcePreservation.id,
150
157
  sourcePreservationSummary: input.sourcePreservation.summary
@@ -1,4 +1,4 @@
1
- import{idFragment,maxSemanticMergeReadiness,uniqueRecordsById}from'../../native-import-utils.js';import{summarizeSemanticImportSidecarParadigmSemantics,summarizeSemanticImportSidecarProofSpec,summarizeSemanticImportSidecarUniversalAstLayers}from'../../semantic-import-layers.js';import{semanticPatchHintForRegion,summarizeSemanticImportRegionTaxonomy}from'../../semantic-import-regions.js';import{semanticImportSidecarEntry}from'../../semantic-import-sidecar-entry.js';import{summarizeKernelSourcePreservation}from'../../semantic-import-source-preservation.js';
1
+ import{idFragment,maxSemanticMergeReadiness,uniqueRecordsById}from'../../native-import-utils.js';import{summarizeSemanticImportDependencies}from'../../semantic-import-dependencies.js';import{summarizeSemanticImportSidecarParadigmSemantics,summarizeSemanticImportSidecarProofSpec,summarizeSemanticImportSidecarUniversalAstLayers}from'../../semantic-import-layers.js';import{semanticPatchHintForRegion,summarizeSemanticImportRegionTaxonomy}from'../../semantic-import-regions.js';import{semanticImportSidecarEntry}from'../../semantic-import-sidecar-entry.js';import{summarizeKernelSourcePreservation}from'../../semantic-import-source-preservation.js';
2
2
  import{createSemanticImportSidecarAdmission,createSemanticImportSidecarQuality}from'./createSemanticImportSidecarAdmission.js';
3
3
  import{summarizeNativeImportLosses}from'./summarizeNativeImportLosses.js';
4
4
  export function createSemanticImportSidecar(importResult, options = {}) {
@@ -17,9 +17,14 @@ export function createSemanticImportSidecar(importResult, options = {}) {
17
17
  const universalAstLayers = summarizeSemanticImportSidecarUniversalAstLayers(importEntries);
18
18
  const proofSpec = summarizeSemanticImportSidecarProofSpec(importEntries);
19
19
  const paradigmSemantics = summarizeSemanticImportSidecarParadigmSemantics(importEntries);
20
+ const dependencies = summarizeSemanticImportDependencies(imports);
21
+ const entryReadiness = importEntries.reduce(
22
+ (current, entry) => maxSemanticMergeReadiness(current, entry.readiness),
23
+ lossSummary.semanticMergeReadiness
24
+ );
20
25
  const readiness = mergeCandidates.reduce(
21
26
  (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
22
- lossSummary.semanticMergeReadiness
27
+ entryReadiness
23
28
  );
24
29
  const patchHints = ownershipRegions.map((region) => semanticPatchHintForRegion(region, readiness, options));
25
30
  const quality = createSemanticImportSidecarQuality({
@@ -51,6 +56,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
51
56
  universalAstLayers,
52
57
  proofSpec,
53
58
  paradigmSemantics,
59
+ dependencies,
54
60
  patchHints,
55
61
  quality,
56
62
  admission,
@@ -92,6 +98,8 @@ export function createSemanticImportSidecar(importResult, options = {}) {
92
98
  paradigmSemanticsRecords: paradigmSemantics.total,
93
99
  paradigmSemanticsGroups: paradigmSemantics.groups.length,
94
100
  paradigmSemanticsLoweringRecords: paradigmSemantics.loweringRecords,
101
+ dependencyRelations: dependencies.total,
102
+ dependencyPredicates: dependencies.predicates,
95
103
  patchHints: patchHints.length,
96
104
  evidenceWarnings: quality.emptyEvidenceWarnings.length,
97
105
  readiness,
@@ -1,24 +1,32 @@
1
- import{declarationRecord}from'./declarationRecord.js';import{nativeNodeId}from'./nativeNodeId.js';import{shortNodeText}from'./shortNodeText.js';import{treeSitterFieldText}from'./treeSitterFieldText.js';
1
+ import{declarationRecord}from'./declarationRecord.js';import{shortNodeText}from'./shortNodeText.js';import{treeSitterChildText,treeSitterFieldText}from'./treeSitterNodeAccess.js';
2
2
  export function treeSitterDeclaration(node, kind, nativeNodeId, input) {
3
3
  if (/import|include|use/.test(kind)) {
4
- const name = treeSitterFieldText(node, 'path') ?? treeSitterFieldText(node, 'source') ?? shortNodeText(node);
4
+ const name = treeSitterNamedText(node, ['string', 'string_fragment', 'identifier']) ?? shortNodeText(node);
5
5
  if (name) return declarationRecord(input, nativeNodeId, name, 'module', 'import');
6
6
  }
7
7
  if (/function|method|fn_item|function_declaration/.test(kind)) {
8
- const name = treeSitterFieldText(node, 'name');
8
+ const name = treeSitterNamedText(node, ['identifier', 'property_identifier']);
9
9
  if (name) return declarationRecord(input, nativeNodeId, name, 'function', 'definition');
10
10
  }
11
11
  if (/class/.test(kind)) {
12
- const name = treeSitterFieldText(node, 'name');
12
+ const name = treeSitterNamedText(node, ['type_identifier', 'identifier']);
13
13
  if (name) return declarationRecord(input, nativeNodeId, name, 'class', 'definition');
14
14
  }
15
15
  if (/interface/.test(kind)) {
16
- const name = treeSitterFieldText(node, 'name');
16
+ const name = treeSitterNamedText(node, ['type_identifier', 'identifier']);
17
17
  if (name) return declarationRecord(input, nativeNodeId, name, 'interface', 'definition');
18
18
  }
19
19
  if (/struct|enum|type/.test(kind)) {
20
- const name = treeSitterFieldText(node, 'name');
20
+ const name = treeSitterNamedText(node, ['type_identifier', 'identifier']);
21
21
  if (name) return declarationRecord(input, nativeNodeId, name, 'type', 'definition');
22
22
  }
23
+ if (/variable_declarator|property_declaration|field_declaration/.test(kind)) {
24
+ const name = treeSitterNamedText(node, ['identifier', 'property_identifier', 'field_identifier']);
25
+ if (name) return declarationRecord(input, nativeNodeId, name, 'variable', 'definition');
26
+ }
23
27
  return undefined;
24
28
  }
29
+
30
+ function treeSitterNamedText(node, childKinds) {
31
+ return treeSitterFieldText(node, 'name') ?? treeSitterFieldText(node, 'declarator') ?? treeSitterChildText(node, childKinds);
32
+ }
@@ -1,5 +1 @@
1
- import{shortNodeText}from'./shortNodeText.js';
2
- export function treeSitterFieldText(node, field) {
3
- if (typeof node.childForFieldName !== 'function') return undefined;
4
- return shortNodeText(node.childForFieldName(field));
5
- }
1
+ export{treeSitterFieldText}from'./treeSitterNodeAccess.js';
@@ -0,0 +1,75 @@
1
+ import{shortNodeText}from'./shortNodeText.js';
2
+ export function treeSitterNodeKind(node) {
3
+ return String(node?.type ?? node?.kind ?? 'node');
4
+ }
5
+
6
+ export function treeSitterChildren(node) {
7
+ const children = treeSitterArrayChildren(node, 'children');
8
+ if (children.length) return children;
9
+ const childCountChildren = treeSitterIndexedChildren(node, 'childCount', 'child');
10
+ if (childCountChildren.length) return childCountChildren;
11
+ const namedChildren = treeSitterArrayChildren(node, 'namedChildren');
12
+ if (namedChildren.length) return namedChildren;
13
+ return treeSitterIndexedChildren(node, 'namedChildCount', 'namedChild');
14
+ }
15
+
16
+ export function treeSitterChildText(node, kinds) {
17
+ const wanted = new Set(kinds);
18
+ const stack = [...treeSitterChildren(node)];
19
+ while (stack.length) {
20
+ const child = stack.shift();
21
+ if (!child || typeof child !== 'object') continue;
22
+ if (wanted.has(treeSitterNodeKind(child))) {
23
+ const text = shortNodeText(child);
24
+ if (text) return text;
25
+ }
26
+ stack.unshift(...treeSitterChildren(child));
27
+ }
28
+ return undefined;
29
+ }
30
+
31
+ export function treeSitterFieldText(node, field) {
32
+ const exact = treeSitterFieldNode(node, field);
33
+ if (exact) return shortNodeText(exact);
34
+ return treeSitterNamedFieldText(node, field);
35
+ }
36
+
37
+ function treeSitterFieldNode(node, field) {
38
+ if (typeof node?.childForFieldName === 'function') return node.childForFieldName(field);
39
+ if (typeof node?.child_by_field_name === 'function') return node.child_by_field_name(field);
40
+ return undefined;
41
+ }
42
+
43
+ function treeSitterNamedFieldText(node, field) {
44
+ for (const child of treeSitterChildren(node)) {
45
+ if (treeSitterFieldName(child) === field) {
46
+ const text = shortNodeText(child);
47
+ if (text) return text;
48
+ }
49
+ }
50
+ return undefined;
51
+ }
52
+
53
+ function treeSitterFieldName(node) {
54
+ if (typeof node?.fieldName === 'string') return node.fieldName;
55
+ if (typeof node?.field_name === 'string') return node.field_name;
56
+ if (typeof node?.fieldName === 'function') return node.fieldName();
57
+ if (typeof node?.field_name === 'function') return node.field_name();
58
+ return undefined;
59
+ }
60
+
61
+ function treeSitterArrayChildren(node, field) {
62
+ const value = node?.[field];
63
+ return Array.isArray(value) ? value.filter((child) => child && typeof child === 'object') : [];
64
+ }
65
+
66
+ function treeSitterIndexedChildren(node, countField, childMethod) {
67
+ const count = Number(node?.[countField]);
68
+ if (!Number.isFinite(count) || count <= 0 || typeof node?.[childMethod] !== 'function') return [];
69
+ const children = [];
70
+ for (let index = 0; index < count; index += 1) {
71
+ const child = node[childMethod](index);
72
+ if (child && typeof child === 'object') children.push(child);
73
+ }
74
+ return children;
75
+ }
@@ -1,5 +1,5 @@
1
1
  import{idFragment}from'../../native-import-utils.js';
2
- import{nativeNodeId}from'./nativeNodeId.js';import{numberOrUndefined}from'./numberOrUndefined.js';import{shortNodeText}from'./shortNodeText.js';import{spanFromTreeSitterNode}from'./spanFromTreeSitterNode.js';import{treeSitterDeclaration}from'./treeSitterDeclaration.js';
2
+ import{nativeNodeId}from'./nativeNodeId.js';import{numberOrUndefined}from'./numberOrUndefined.js';import{shortNodeText}from'./shortNodeText.js';import{spanFromTreeSitterNode}from'./spanFromTreeSitterNode.js';import{treeSitterDeclaration}from'./treeSitterDeclaration.js';import{treeSitterChildren,treeSitterNodeKind}from'./treeSitterNodeAccess.js';
3
3
  export function visitTreeSitterNode(node, context, propertyPath, depth = 0) {
4
4
  if (!node || typeof node !== 'object' || context.truncated) return undefined;
5
5
  if (context.objectIds.has(node)) return context.objectIds.get(node);
@@ -8,7 +8,7 @@ export function visitTreeSitterNode(node, context, propertyPath, depth = 0) {
8
8
  return undefined;
9
9
  }
10
10
  context.counter += 1;
11
- const kind = String(node.type ?? node.kind ?? 'node');
11
+ const kind = treeSitterNodeKind(node);
12
12
  const span = spanFromTreeSitterNode(node, context.input);
13
13
  const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
14
14
  context.objectIds.set(node, id);
@@ -82,32 +82,6 @@ export function visitTreeSitterNode(node, context, propertyPath, depth = 0) {
82
82
  return id;
83
83
  }
84
84
 
85
- function treeSitterChildren(node) {
86
- const children = treeSitterArrayChildren(node, 'children');
87
- if (children.length) return children;
88
- const childCountChildren = treeSitterIndexedChildren(node, 'childCount', 'child');
89
- if (childCountChildren.length) return childCountChildren;
90
- const namedChildren = treeSitterArrayChildren(node, 'namedChildren');
91
- if (namedChildren.length) return namedChildren;
92
- return treeSitterIndexedChildren(node, 'namedChildCount', 'namedChild');
93
- }
94
-
95
- function treeSitterArrayChildren(node, field) {
96
- const value = node[field];
97
- return Array.isArray(value) ? value.filter((child) => child && typeof child === 'object') : [];
98
- }
99
-
100
- function treeSitterIndexedChildren(node, countField, childMethod) {
101
- const count = Number(node[countField]);
102
- if (!Number.isFinite(count) || count <= 0 || typeof node[childMethod] !== 'function') return [];
103
- const children = [];
104
- for (let index = 0; index < count; index += 1) {
105
- const child = node[childMethod](index);
106
- if (child && typeof child === 'object') children.push(child);
107
- }
108
- return children;
109
- }
110
-
111
85
  function treeSitterBoolean(node, ...fields) {
112
86
  for (const field of fields) {
113
87
  const value = node[field];
@@ -0,0 +1,271 @@
1
+ import { idFragment } from './native-import-utils.js';
2
+ import { sourceLines } from './native-region-scanner-core.js';
3
+
4
+ const jsLikeLanguages = new Set(['javascript', 'typescript']);
5
+ const ignoredIdentifiers = new Set([
6
+ 'async', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'default',
7
+ 'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for', 'from',
8
+ 'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null', 'of',
9
+ 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof',
10
+ 'undefined', 'var', 'void', 'while', 'with', 'yield'
11
+ ]);
12
+
13
+ export function lightweightDependencyRelations(input, declarations, documentId) {
14
+ if (!jsLikeLanguages.has(String(input.language ?? '').toLowerCase())) {
15
+ return { relations: [], occurrences: [], facts: [], summary: emptySummary() };
16
+ }
17
+ const lines = sourceLines(input.sourceText);
18
+ const identifiers = declarationIdentifierMap(input, declarations, lines);
19
+ const records = { relations: [], occurrences: [], facts: [], seen: new Set() };
20
+ for (const scan of declarationScanRanges(declarations, lines)) {
21
+ scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records);
22
+ }
23
+ return {
24
+ relations: records.relations,
25
+ occurrences: records.occurrences,
26
+ facts: records.facts,
27
+ summary: {
28
+ total: records.relations.length,
29
+ calls: records.relations.filter((relation) => relation.predicate === 'calls').length,
30
+ uses: records.relations.filter((relation) => relation.predicate === 'uses').length
31
+ }
32
+ };
33
+ }
34
+
35
+ function declarationIdentifierMap(input, declarations, lines) {
36
+ const identifiers = new Map();
37
+ for (const declaration of declarations ?? []) {
38
+ if (!declaration?.symbolId) continue;
39
+ const target = {
40
+ name: declaration.name,
41
+ symbolId: declaration.symbolId,
42
+ symbolKind: declaration.symbolKind,
43
+ nodeId: declaration.nodeId,
44
+ role: declaration.role
45
+ };
46
+ for (const identifier of identifiersForDeclaration(input, declaration, lines)) {
47
+ addIdentifierTarget(identifiers, identifier, target);
48
+ }
49
+ }
50
+ return identifiers;
51
+ }
52
+
53
+ function identifiersForDeclaration(input, declaration, lines) {
54
+ const identifiers = new Set();
55
+ addIdentifier(identifiers, declaration.name);
56
+ const parts = String(declaration.name ?? '').split('.');
57
+ addIdentifier(identifiers, parts[parts.length - 1]);
58
+ if (declaration.role === 'import') {
59
+ const line = lines[(declaration.span?.startLine ?? 1) - 1]?.line ?? '';
60
+ for (const identifier of importLocalIdentifiers(line)) addIdentifier(identifiers, identifier);
61
+ addIdentifier(identifiers, packageIdentifier(declaration.importPath ?? declaration.name));
62
+ }
63
+ return [...identifiers];
64
+ }
65
+
66
+ function importLocalIdentifiers(line) {
67
+ const source = String(line ?? '');
68
+ const identifiers = [];
69
+ let match = source.match(/^import\s+([A-Za-z_$][\w$]*)\s+from\b/);
70
+ if (match) identifiers.push(match[1]);
71
+ match = source.match(/^import\s+\*\s+as\s+([A-Za-z_$][\w$]*)\s+from\b/);
72
+ if (match) identifiers.push(match[1]);
73
+ match = source.match(/^import\s+[^,{]+,\s*\{([^}]+)\}\s+from\b/);
74
+ if (match) identifiers.push(...namedImportIdentifiers(match[1]));
75
+ match = source.match(/^import\s+\{([^}]+)\}\s+from\b/);
76
+ if (match) identifiers.push(...namedImportIdentifiers(match[1]));
77
+ return identifiers;
78
+ }
79
+
80
+ function namedImportIdentifiers(raw) {
81
+ return String(raw ?? '')
82
+ .split(',')
83
+ .map((part) => part.trim().split(/\s+as\s+/i).pop()?.trim())
84
+ .filter(Boolean);
85
+ }
86
+
87
+ function packageIdentifier(value) {
88
+ const parts = String(value ?? '').split('/').filter(Boolean);
89
+ const last = parts[parts.length - 1] ?? value;
90
+ return String(last).replace(/[^A-Za-z0-9_$]/g, '');
91
+ }
92
+
93
+ function addIdentifierTarget(map, identifier, target) {
94
+ if (!isIdentifier(identifier)) return;
95
+ const existing = map.get(identifier) ?? [];
96
+ if (!existing.some((entry) => entry.symbolId === target.symbolId)) existing.push(target);
97
+ map.set(identifier, existing);
98
+ }
99
+
100
+ function addIdentifier(set, value) {
101
+ if (isIdentifier(value)) set.add(value);
102
+ }
103
+
104
+ function isIdentifier(value) {
105
+ const text = String(value ?? '');
106
+ return /^[A-Za-z_$][\w$]*$/.test(text) && !ignoredIdentifiers.has(text);
107
+ }
108
+
109
+ function declarationScanRanges(declarations, lines) {
110
+ return (declarations ?? [])
111
+ .filter((declaration) => declaration?.symbolId && declaration.role !== 'import')
112
+ .map((declaration) => ({
113
+ declaration,
114
+ startLine: declaration.span?.startLine ?? 1,
115
+ endLine: declarationScanEndLine(declaration, lines)
116
+ }));
117
+ }
118
+
119
+ function declarationScanEndLine(declaration, lines) {
120
+ const startLine = declaration.span?.startLine ?? 1;
121
+ if (!declaration.metadata?.hasBody || declaration.kind === 'ClassDeclaration') return startLine;
122
+ return balancedRegionEndLine(lines, startLine);
123
+ }
124
+
125
+ function balancedRegionEndLine(lines, startLine) {
126
+ const state = { inBlockComment: false, inTemplateString: false };
127
+ let depth = 0;
128
+ let opened = false;
129
+ for (let index = Math.max(0, startLine - 1); index < lines.length; index += 1) {
130
+ for (const char of maskReferenceLine(lines[index].line, state)) {
131
+ if (char === '{' || char === '[' || char === '(') {
132
+ depth += 1;
133
+ opened = true;
134
+ } else if (char === '}' || char === ']' || char === ')') {
135
+ depth -= 1;
136
+ }
137
+ }
138
+ if (opened && depth <= 0) return lines[index].number;
139
+ }
140
+ return startLine;
141
+ }
142
+
143
+ function scanDeclarationDependencies(input, documentId, scan, identifiers, lines, records) {
144
+ const state = { inBlockComment: false, inTemplateString: false };
145
+ for (let lineNumber = scan.startLine; lineNumber <= scan.endLine; lineNumber += 1) {
146
+ const scanLine = maskReferenceLine(lines[lineNumber - 1]?.line ?? '', state);
147
+ for (const match of scanLine.matchAll(/[A-Za-z_$][\w$]*/g)) {
148
+ const name = match[0];
149
+ if (ignoredIdentifiers.has(name) || !identifiers.has(name)) continue;
150
+ const targets = identifiers.get(name).filter((target) => target.symbolId !== scan.declaration.symbolId);
151
+ for (const target of targets) {
152
+ addDependencyRecord(input, documentId, scan.declaration, target, {
153
+ line: scanLine,
154
+ lineNumber,
155
+ startColumn: match.index + 1,
156
+ name
157
+ }, records);
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ function addDependencyRecord(input, documentId, caller, target, occurrence, records) {
164
+ const predicate = isCallReference(occurrence.line, occurrence.startColumn + occurrence.name.length - 1) ? 'calls' : 'uses';
165
+ const key = `${caller.symbolId}|${predicate}|${target.symbolId}|${occurrence.lineNumber}|${occurrence.startColumn}`;
166
+ if (records.seen.has(key)) return;
167
+ records.seen.add(key);
168
+ const relationId = `rel_${idFragment(caller.symbolId)}_${predicate}_${idFragment(target.symbolId)}_${occurrence.lineNumber}_${occurrence.startColumn}`;
169
+ const occurrenceId = `occ_${idFragment(caller.nodeId)}_${predicate}_${idFragment(target.symbolId)}_${occurrence.lineNumber}_${occurrence.startColumn}`;
170
+ const span = {
171
+ sourceId: input.sourceHash,
172
+ path: input.sourcePath,
173
+ startLine: occurrence.lineNumber,
174
+ endLine: occurrence.lineNumber,
175
+ startColumn: occurrence.startColumn,
176
+ endColumn: occurrence.startColumn + occurrence.name.length
177
+ };
178
+ records.relations.push({
179
+ id: relationId,
180
+ sourceId: caller.symbolId,
181
+ predicate,
182
+ targetId: target.symbolId,
183
+ metadata: {
184
+ scan: 'lightweight-dependency',
185
+ confidence: 'lexical-reference',
186
+ sourceDocumentId: documentId,
187
+ sourceName: caller.name,
188
+ targetName: target.name
189
+ }
190
+ });
191
+ records.occurrences.push({
192
+ id: occurrenceId,
193
+ documentId,
194
+ symbolId: target.symbolId,
195
+ role: 'reference',
196
+ span,
197
+ nativeAstNodeId: caller.nodeId
198
+ });
199
+ records.facts.push({
200
+ id: `fact_${idFragment(relationId)}_lightweight_dependency`,
201
+ predicate: 'lightweightDependency',
202
+ subjectId: caller.symbolId,
203
+ value: {
204
+ relationId,
205
+ predicate,
206
+ targetId: target.symbolId,
207
+ line: occurrence.lineNumber,
208
+ confidence: 'lexical-reference'
209
+ }
210
+ });
211
+ }
212
+
213
+ function isCallReference(line, afterIdentifierIndex) {
214
+ return /^\s*\(/.test(String(line ?? '').slice(afterIdentifierIndex));
215
+ }
216
+
217
+ function maskReferenceLine(line, state) {
218
+ const text = String(line ?? '');
219
+ let output = '';
220
+ let quote;
221
+ let escaped = false;
222
+ for (let index = 0; index < text.length; index += 1) {
223
+ const char = text[index];
224
+ const next = text[index + 1];
225
+ if (state.inBlockComment) {
226
+ output += ' ';
227
+ if (char === '*' && next === '/') {
228
+ output += ' ';
229
+ index += 1;
230
+ state.inBlockComment = false;
231
+ }
232
+ continue;
233
+ }
234
+ if (state.inTemplateString) {
235
+ output += ' ';
236
+ if (char === '`' && !escaped) state.inTemplateString = false;
237
+ escaped = char === '\\' && !escaped;
238
+ continue;
239
+ }
240
+ if (quote) {
241
+ output += ' ';
242
+ if (escaped) escaped = false;
243
+ else if (char === '\\') escaped = true;
244
+ else if (char === quote) quote = undefined;
245
+ continue;
246
+ }
247
+ if (char === '/' && next === '/') break;
248
+ if (char === '/' && next === '*') {
249
+ output += ' ';
250
+ index += 1;
251
+ state.inBlockComment = true;
252
+ continue;
253
+ }
254
+ if (char === '\'' || char === '"') {
255
+ output += ' ';
256
+ quote = char;
257
+ continue;
258
+ }
259
+ if (char === '`') {
260
+ output += ' ';
261
+ state.inTemplateString = true;
262
+ continue;
263
+ }
264
+ output += char;
265
+ }
266
+ return output;
267
+ }
268
+
269
+ function emptySummary() {
270
+ return { total: 0, calls: 0, uses: 0 };
271
+ }
@@ -0,0 +1,62 @@
1
+ import { countBy, uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
+
3
+ function summarizeSemanticImportDependencies(imports) {
4
+ return summarizeSemanticImportDependencyRelations((imports ?? [])
5
+ .flatMap((imported) => imported?.semanticIndex?.relations ?? imported?.universalAst?.semanticIndex?.relations ?? []));
6
+ }
7
+
8
+ function summarizeSemanticImportDependencyRelations(relations) {
9
+ const dependencyRelations = uniqueRecordsById((relations ?? []).filter(isDependencyRelation));
10
+ const predicateKeys = dependencyRelations.map((relation) => semanticDependencyPredicateKey(relation.predicate));
11
+ const byPredicate = countBy(predicateKeys);
12
+ return {
13
+ total: dependencyRelations.length,
14
+ calls: byPredicate.calls ?? 0,
15
+ uses: byPredicate.uses ?? 0,
16
+ references: byPredicate.references ?? 0,
17
+ imports: byPredicate.imports ?? 0,
18
+ extends: byPredicate.extends ?? 0,
19
+ implements: byPredicate.implements ?? 0,
20
+ includes: byPredicate.includes ?? 0,
21
+ requires: byPredicate.requires ?? 0,
22
+ byPredicate,
23
+ predicates: uniqueStrings(predicateKeys),
24
+ ids: dependencyRelations.map((relation) => relation.id).filter(Boolean),
25
+ sourceSymbolIds: uniqueStrings(dependencyRelations.map((relation) => relation.sourceId).filter(Boolean)),
26
+ targetSymbolIds: uniqueStrings(dependencyRelations.map((relation) => relation.targetId).filter(Boolean))
27
+ };
28
+ }
29
+
30
+ function isDependencyRelation(relation) {
31
+ const predicate = String(relation?.predicate ?? '').toLowerCase();
32
+ if (!predicate || predicate === 'defines' || predicate === 'definitionof') return false;
33
+ return predicate === 'imports'
34
+ || predicate === 'calls'
35
+ || predicate === 'uses'
36
+ || predicate.includes('reference')
37
+ || predicate.includes('depend')
38
+ || predicate.includes('require')
39
+ || predicate.includes('include')
40
+ || predicate.includes('extend')
41
+ || predicate.includes('implement');
42
+ }
43
+
44
+ function semanticDependencyPredicateKey(predicate) {
45
+ const value = String(predicate ?? 'unknown').toLowerCase();
46
+ if (value.includes('call')) return 'calls';
47
+ if (value.includes('reference')) return 'references';
48
+ if (value.includes('import')) return 'imports';
49
+ if (value.includes('depend')) return 'depends';
50
+ if (value.includes('require')) return 'requires';
51
+ if (value.includes('include')) return 'includes';
52
+ if (value.includes('extend')) return 'extends';
53
+ if (value.includes('implement')) return 'implements';
54
+ if (value === 'uses') return 'uses';
55
+ return value || 'unknown';
56
+ }
57
+
58
+ export {
59
+ semanticDependencyPredicateKey,
60
+ summarizeSemanticImportDependencies,
61
+ summarizeSemanticImportDependencyRelations
62
+ };
@@ -1,4 +1,5 @@
1
1
  import { uniqueRecordsById, uniqueStrings } from './native-import-utils.js';
2
+ import { summarizeSemanticImportDependencyRelations } from './semantic-import-dependencies.js';
2
3
  import { semanticOwnershipRegionForSymbol, summarizeSemanticImportRegionTaxonomy } from './semantic-import-regions.js';
3
4
  import { collectKernelSourcePreservationFromImport } from './semantic-import-source-preservation.js';
4
5
  import {
@@ -16,6 +17,8 @@ function semanticImportSidecarEntry(imported, index, options) {
16
17
  const universalAstLayers = summarizeUniversalAstLayers(imported?.universalAst);
17
18
  const proofSpec = summarizeProofSpecLayer(imported?.universalAst?.proof ?? imported?.proof);
18
19
  const paradigmSemantics = summarizeParadigmSemanticsLayer(imported?.universalAst?.paradigmSemantics ?? imported?.paradigmSemantics);
20
+ const dependencies = summarizeSemanticImportDependencyRelations(semanticIndex?.relations ?? []);
21
+ const readiness = semanticImportEntryReadiness(imported);
19
22
  const mappingsBySymbolId = new Map();
20
23
  for (const mapping of sourceMapMappings) {
21
24
  if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
@@ -42,7 +45,7 @@ function semanticImportSidecarEntry(imported, index, options) {
42
45
  ownershipRegionId: region.id,
43
46
  ownershipKey: region.key,
44
47
  ownershipRegionKind: region.regionKind,
45
- readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review'
48
+ readiness
46
49
  });
47
50
  }
48
51
  const ownershipRegions = uniqueRecordsById(regions);
@@ -67,7 +70,9 @@ function semanticImportSidecarEntry(imported, index, options) {
67
70
  universalAstLayerIds: universalAstLayers.ids,
68
71
  proofSpec,
69
72
  paradigmSemantics,
70
- readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
73
+ dependencyRelationCount: dependencies.total,
74
+ dependencyPredicates: dependencies.predicates,
75
+ readiness,
71
76
  emptySemanticIndex: symbols.length === 0,
72
77
  regionTaxonomy,
73
78
  symbols,
@@ -75,4 +80,14 @@ function semanticImportSidecarEntry(imported, index, options) {
75
80
  };
76
81
  }
77
82
 
83
+ function semanticImportEntryReadiness(imported) {
84
+ const readiness = imported?.metadata?.semanticMergeReadiness
85
+ ?? imported?.metadata?.nativeImportLossSummary?.semanticMergeReadiness
86
+ ?? imported?.readiness?.semanticMergeReadiness
87
+ ?? imported?.readiness?.readiness
88
+ ?? (typeof imported?.readiness === 'string' ? imported.readiness : undefined)
89
+ ?? imported?.mergeCandidates?.[0]?.readiness;
90
+ return readiness ?? 'needs-review';
91
+ }
92
+
78
93
  export { semanticImportSidecarEntry };
@@ -90,6 +90,8 @@ export interface SemanticImportSidecarImportEntry {
90
90
  readonly universalAstLayerIds: readonly string[];
91
91
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
92
92
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
93
+ readonly dependencyRelationCount: number;
94
+ readonly dependencyPredicates: readonly string[];
93
95
  readonly readiness: SemanticMergeReadiness;
94
96
  readonly emptySemanticIndex: boolean;
95
97
  readonly regionTaxonomy?: SemanticImportRegionTaxonomySummary;
@@ -186,6 +188,23 @@ export interface SemanticImportSidecarParadigmSemanticsSummary {
186
188
  readonly empty: boolean;
187
189
  }
188
190
 
191
+ export interface SemanticImportDependencySummary {
192
+ readonly total: number;
193
+ readonly calls: number;
194
+ readonly uses: number;
195
+ readonly references: number;
196
+ readonly imports: number;
197
+ readonly extends: number;
198
+ readonly implements: number;
199
+ readonly includes: number;
200
+ readonly requires: number;
201
+ readonly byPredicate: Readonly<Record<string, number>>;
202
+ readonly predicates: readonly string[];
203
+ readonly ids: readonly string[];
204
+ readonly sourceSymbolIds: readonly string[];
205
+ readonly targetSymbolIds: readonly string[];
206
+ }
207
+
189
208
  export interface SemanticImportSidecar {
190
209
  readonly kind: 'frontier.lang.semanticImportSidecar';
191
210
  readonly version: 1;
@@ -217,6 +236,7 @@ export interface SemanticImportSidecar {
217
236
  readonly universalAstLayers: SemanticImportSidecarUniversalAstLayerSummary;
218
237
  readonly proofSpec: SemanticImportSidecarProofSpecSummary;
219
238
  readonly paradigmSemantics: SemanticImportSidecarParadigmSemanticsSummary;
239
+ readonly dependencies: SemanticImportDependencySummary;
220
240
  readonly patchHints: readonly SemanticImportPatchHint[];
221
241
  readonly quality: SemanticImportSidecarQuality;
222
242
  readonly admission: SemanticImportSidecarAdmission;
@@ -258,6 +278,8 @@ export interface SemanticImportSidecar {
258
278
  readonly paradigmSemanticsRecords: number;
259
279
  readonly paradigmSemanticsGroups: number;
260
280
  readonly paradigmSemanticsLoweringRecords: number;
281
+ readonly dependencyRelations: number;
282
+ readonly dependencyPredicates: readonly string[];
261
283
  readonly patchHints: number;
262
284
  readonly evidenceWarnings: number;
263
285
  readonly readiness: SemanticMergeReadiness;
@@ -0,0 +1,148 @@
1
+ import {
2
+ compileFrontierSource,
3
+ compileNativeSource,
4
+ importNativeSource,
5
+ writeUniversalAstJson
6
+ } from '../dist/index.js';
7
+
8
+ const javascriptSource = `import { nanoid } from "nanoid";
9
+
10
+ export function addTodo(title) {
11
+ return { id: nanoid(), title };
12
+ }
13
+
14
+ export class TodoStore {
15
+ save(title) {
16
+ return addTodo(title);
17
+ }
18
+ }
19
+ `;
20
+
21
+ const imported = importNativeSource({
22
+ language: 'javascript',
23
+ sourcePath: 'src/todo.js',
24
+ sourceText: javascriptSource
25
+ });
26
+
27
+ const graphSummary = {
28
+ universalAst: imported.universalAst.kind,
29
+ semanticIndexId: imported.semanticIndex.id,
30
+ symbols: imported.semanticIndex.symbols.map((symbol) => ({
31
+ name: symbol.name,
32
+ kind: symbol.kind,
33
+ region: symbol.metadata?.ownershipRegionKind
34
+ })),
35
+ sourceMapMappings: imported.sourceMaps[0]?.mappings.length ?? 0,
36
+ mergeReadiness: imported.metadata.nativeImportLossSummary.semanticMergeReadiness,
37
+ losses: imported.losses.map((loss) => loss.kind)
38
+ };
39
+
40
+ console.log('--- JavaScript source ---');
41
+ console.log(javascriptSource.trim());
42
+ console.log('\n--- Frontier graph summary ---');
43
+ console.log(JSON.stringify(graphSummary, null, 2));
44
+ console.log('\n--- Universal AST envelope excerpt ---');
45
+ console.log(JSON.stringify(universalAstExcerpt(imported), null, 2));
46
+
47
+ const stubProjection = compileNativeSource(imported, {
48
+ target: 'rust',
49
+ targetPath: 'src/todo.rs'
50
+ });
51
+
52
+ console.log('\n--- Rust declaration stubs without a target adapter ---');
53
+ console.log(`ok=${stubProjection.ok} readiness=${stubProjection.readiness.readiness} mode=${stubProjection.outputMode}`);
54
+ console.log(stubProjection.output.trim());
55
+
56
+ const adapterProjection = compileNativeSource(imported, {
57
+ target: 'rust',
58
+ targetPath: 'src/todo.rs',
59
+ targetAdapters: [createDemoJsToRustAdapter()]
60
+ });
61
+
62
+ console.log('\n--- Rust output with a host-owned target adapter ---');
63
+ console.log(`ok=${adapterProjection.ok} readiness=${adapterProjection.readiness.readiness} mode=${adapterProjection.outputMode}`);
64
+ console.log(adapterProjection.output.trim());
65
+
66
+ const frontierSource = `module TodoApp @id("mod_todo")
67
+
68
+ type TodoInput @id("type_todo_input") {
69
+ title: Text
70
+ }
71
+
72
+ entity Todo @id("ent_todo") {
73
+ title @id("field_title"): Text
74
+ }
75
+
76
+ action addTodo @id("action_add") {
77
+ input TodoInput
78
+ writes field_title
79
+ returns Patch
80
+ }
81
+ `;
82
+
83
+ const canonicalRust = compileFrontierSource(frontierSource, { target: 'rust' });
84
+
85
+ console.log('\n--- Frontier source projected directly to Rust ---');
86
+ console.log(`ok=${canonicalRust.ok} target=${canonicalRust.target}`);
87
+ console.log(canonicalRust.output.trim());
88
+
89
+ function universalAstExcerpt(importResult) {
90
+ const parsed = JSON.parse(writeUniversalAstJson(importResult.universalAst));
91
+ return {
92
+ kind: parsed.kind,
93
+ id: parsed.id,
94
+ documentCount: parsed.documents?.length ?? 0,
95
+ symbolCount: parsed.semanticIndex?.symbols?.length ?? 0,
96
+ sourceMapCount: parsed.sourceMaps?.length ?? 0
97
+ };
98
+ }
99
+
100
+ function createDemoJsToRustAdapter() {
101
+ return {
102
+ id: 'demo-js-to-rust-target-adapter',
103
+ sourceLanguage: 'javascript',
104
+ target: 'rust',
105
+ version: '0.0.0-demo',
106
+ capabilities: ['declaration-stubs'],
107
+ coverage: {
108
+ readiness: 'ready',
109
+ handledLossKinds: ['opaqueNative', 'declarationOnlyCoverage', 'partialSemanticIndex', 'sourceMapApproximation', 'sourcePreservation'],
110
+ notes: ['Demo adapter turns Frontier semantic symbols into deterministic Rust scaffolding.']
111
+ },
112
+ project(input) {
113
+ const symbols = input.importResult.semanticIndex?.symbols ?? [];
114
+ return {
115
+ output: renderRustScaffold(symbols),
116
+ readiness: 'ready',
117
+ evidence: [{
118
+ id: 'evidence_demo_js_to_rust_adapter',
119
+ kind: 'projection',
120
+ status: 'passed',
121
+ summary: 'Demo adapter projected JavaScript semantic symbols to Rust scaffolding.'
122
+ }]
123
+ };
124
+ }
125
+ };
126
+ }
127
+
128
+ function renderRustScaffold(symbols) {
129
+ const lines = ['// Generated from Frontier semantic graph evidence.'];
130
+ for (const symbol of symbols) {
131
+ if (symbol.kind === 'class') {
132
+ lines.push(`pub struct ${symbol.name};`, '');
133
+ } else if (symbol.kind === 'function' || symbol.kind === 'method') {
134
+ lines.push(`pub fn ${rustName(symbol.name)}() {`);
135
+ lines.push(' todo!("port body from native source evidence");');
136
+ lines.push('}', '');
137
+ }
138
+ }
139
+ return `${lines.join('\n').trim()}\n`;
140
+ }
141
+
142
+ function rustName(name) {
143
+ return String(name)
144
+ .replace(/\./g, '_')
145
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
146
+ .replace(/[^A-Za-z0-9_]/g, '_')
147
+ .toLowerCase();
148
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-compiler",
3
- "version": "0.2.53",
3
+ "version": "0.2.55",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",