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

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/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,6 +17,7 @@ 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);
20
21
  const readiness = mergeCandidates.reduce(
21
22
  (current, candidate) => maxSemanticMergeReadiness(current, candidate.readiness),
22
23
  lossSummary.semanticMergeReadiness
@@ -51,6 +52,7 @@ export function createSemanticImportSidecar(importResult, options = {}) {
51
52
  universalAstLayers,
52
53
  proofSpec,
53
54
  paradigmSemantics,
55
+ dependencies,
54
56
  patchHints,
55
57
  quality,
56
58
  admission,
@@ -92,6 +94,8 @@ export function createSemanticImportSidecar(importResult, options = {}) {
92
94
  paradigmSemanticsRecords: paradigmSemantics.total,
93
95
  paradigmSemanticsGroups: paradigmSemantics.groups.length,
94
96
  paradigmSemanticsLoweringRecords: paradigmSemantics.loweringRecords,
97
+ dependencyRelations: dependencies.total,
98
+ dependencyPredicates: dependencies.predicates,
95
99
  patchHints: patchHints.length,
96
100
  evidenceWarnings: quality.emptyEvidenceWarnings.length,
97
101
  readiness,
@@ -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,7 @@ 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 ?? []);
19
21
  const mappingsBySymbolId = new Map();
20
22
  for (const mapping of sourceMapMappings) {
21
23
  if (mapping.semanticSymbolId && !mappingsBySymbolId.has(mapping.semanticSymbolId)) {
@@ -67,6 +69,8 @@ function semanticImportSidecarEntry(imported, index, options) {
67
69
  universalAstLayerIds: universalAstLayers.ids,
68
70
  proofSpec,
69
71
  paradigmSemantics,
72
+ dependencyRelationCount: dependencies.total,
73
+ dependencyPredicates: dependencies.predicates,
70
74
  readiness: imported?.metadata?.semanticMergeReadiness ?? imported?.mergeCandidates?.[0]?.readiness ?? 'needs-review',
71
75
  emptySemanticIndex: symbols.length === 0,
72
76
  regionTaxonomy,
@@ -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;
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.54",
4
4
  "description": "Compiler facade for Frontier Lang source documents and language projection adapters.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",