@shapeshift-labs/frontier-lang-compiler 0.2.103 → 0.2.104

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 (124) hide show
  1. package/README.md +13 -0
  2. package/dist/declarations/bidirectional-target-change-source-edit.d.ts +30 -0
  3. package/dist/declarations/bidirectional-target-change.d.ts +10 -0
  4. package/dist/declarations/js-ts-safe-member-merge.d.ts +58 -0
  5. package/dist/declarations/js-ts-safe-merge.d.ts +120 -0
  6. package/dist/declarations/js-ts-semantic-conflict-sidecars.d.ts +235 -0
  7. package/dist/declarations/js-ts-semantic-merge-contracts.d.ts +287 -0
  8. package/dist/declarations/js-ts-semantic-merge.d.ts +4 -0
  9. package/dist/declarations/native-import-losses.d.ts +3 -0
  10. package/dist/declarations/semantic-edit-replay-diagnostics.d.ts +12 -0
  11. package/dist/declarations/semantic-edit-script.d.ts +7 -4
  12. package/dist/declarations/semantic-patch-bundle-index.d.ts +45 -0
  13. package/dist/declarations/semantic-patch-bundle.d.ts +6 -4
  14. package/dist/declarations/semantic-sidecar-example.d.ts +18 -0
  15. package/dist/declarations/semantic-transform-identity.d.ts +3 -0
  16. package/dist/declarations/source-preservation.d.ts +72 -0
  17. package/dist/declarations/universal-capability.d.ts +4 -0
  18. package/dist/declarations/universal-conversion-artifacts.d.ts +61 -1
  19. package/dist/declarations/universal-conversion-compact-counts.d.ts +51 -0
  20. package/dist/declarations/universal-conversion-plan.d.ts +6 -1
  21. package/dist/declarations/universal-representation-coverage.d.ts +90 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.js +3 -0
  24. package/dist/internal/index-impl/bidirectionalExactSourceBackprojection.js +199 -0
  25. package/dist/internal/index-impl/bidirectionalSameLanguageSourceProjection.js +112 -0
  26. package/dist/internal/index-impl/bidirectionalSourceEditProjection.js +319 -0
  27. package/dist/internal/index-impl/bidirectionalSourceEditProjectionArtifacts.js +67 -0
  28. package/dist/internal/index-impl/bidirectionalTargetChangeRecordInternals.js +17 -5
  29. package/dist/internal/index-impl/bidirectionalTargetRoundtripEvidence.js +58 -20
  30. package/dist/internal/index-impl/createBidirectionalTargetChangeRecord.js +60 -7
  31. package/dist/internal/index-impl/createLightweightNativeImport.js +1 -0
  32. package/dist/internal/index-impl/createNativeSourcePreservation.js +28 -2
  33. package/dist/internal/index-impl/diffNativeSymbols.js +3 -3
  34. package/dist/internal/index-impl/nativeChangeProjectionSourceMapLinks.js +2 -0
  35. package/dist/internal/index-impl/projectSemanticEditScriptToSource.js +43 -8
  36. package/dist/internal/index-impl/replaySemanticEditLineEndings.js +34 -0
  37. package/dist/internal/index-impl/replaySemanticEditProjection.js +39 -19
  38. package/dist/internal/index-impl/semanticEditBundleAdmission.js +7 -3
  39. package/dist/internal/index-impl/semanticEditBundleIndex.js +47 -1
  40. package/dist/internal/index-impl/semanticEditExplicitSourceReplacement.js +40 -0
  41. package/dist/internal/index-impl/semanticEditOperationCoverage.js +33 -3
  42. package/dist/internal/index-impl/semanticEditProjectionRecord.js +29 -0
  43. package/dist/internal/index-impl/semanticEditReplayDiagnostics.js +39 -0
  44. package/dist/internal/index-impl/semanticEditReplaySourceReplacement.js +85 -0
  45. package/dist/internal/index-impl/semanticEditScripts.js +4 -0
  46. package/dist/internal/index-impl/semanticEditSourceRanges.js +27 -0
  47. package/dist/internal/index-impl/semanticIndexFromNativeDeclarations.js +1 -0
  48. package/dist/internal/index-impl/semanticPatchBundleAdmission.js +41 -7
  49. package/dist/internal/index-impl/semanticPatchBundleRecords.js +16 -0
  50. package/dist/internal/index-impl/semanticPatchBundleSourceRecords.js +2 -0
  51. package/dist/internal/index-impl/semanticSidecarQuality.js +111 -0
  52. package/dist/internal/index-impl/semanticSourceEditDedupe.js +69 -9
  53. package/dist/internal/index-impl/semanticTransformIdentityRecords.js +85 -9
  54. package/dist/js-ts-safe-member-merge-result.js +158 -0
  55. package/dist/js-ts-safe-member-merge.js +202 -0
  56. package/dist/js-ts-safe-merge-analyze.js +279 -0
  57. package/dist/js-ts-safe-merge-constants.js +50 -0
  58. package/dist/js-ts-safe-merge-context.js +118 -0
  59. package/dist/js-ts-safe-merge-ledger-validation.js +92 -0
  60. package/dist/js-ts-safe-merge-ledger.js +85 -0
  61. package/dist/js-ts-safe-merge-parse-declarations.js +210 -0
  62. package/dist/js-ts-safe-merge-parse-statements.js +155 -0
  63. package/dist/js-ts-safe-merge-plan.js +190 -0
  64. package/dist/js-ts-safe-merge.js +175 -0
  65. package/dist/js-ts-semantic-conflict-sidecar-constants.js +77 -0
  66. package/dist/js-ts-semantic-conflict-sidecar-detectors.js +195 -0
  67. package/dist/js-ts-semantic-conflict-sidecar-normalize.js +203 -0
  68. package/dist/js-ts-semantic-conflict-sidecar-utils.js +190 -0
  69. package/dist/js-ts-semantic-conflict-sidecars.js +81 -0
  70. package/dist/js-ts-semantic-merge-contract-helpers.js +128 -0
  71. package/dist/js-ts-semantic-merge-contracts.js +217 -0
  72. package/dist/js-ts-semantic-merge-member-containers.js +100 -0
  73. package/dist/js-ts-semantic-merge-member-keys.js +142 -0
  74. package/dist/js-ts-semantic-merge-member-segments.js +185 -0
  75. package/dist/js-ts-semantic-merge-member-source.js +64 -0
  76. package/dist/js-ts-semantic-merge-member-utils.js +18 -0
  77. package/dist/js-ts-semantic-merge-parse.js +15 -0
  78. package/dist/js-ts-semantic-merge.js +21 -0
  79. package/dist/lightweight-dependency-effects.js +51 -0
  80. package/dist/lightweight-dependency-language.js +12 -1
  81. package/dist/lightweight-dependency-relations.js +14 -27
  82. package/dist/native-region-scanner-core.js +33 -1
  83. package/dist/native-region-scanner-csharp.js +151 -0
  84. package/dist/native-region-scanner-dart.js +91 -0
  85. package/dist/native-region-scanner-dynamic.js +21 -151
  86. package/dist/native-region-scanner-functional.js +40 -13
  87. package/dist/native-region-scanner-java.js +97 -0
  88. package/dist/native-region-scanner-js-class.js +100 -0
  89. package/dist/native-region-scanner-js-helpers.js +28 -86
  90. package/dist/native-region-scanner-js-imports.js +121 -1
  91. package/dist/native-region-scanner-js-nested.js +96 -8
  92. package/dist/native-region-scanner-js-structure.js +27 -0
  93. package/dist/native-region-scanner-js-types.js +99 -0
  94. package/dist/native-region-scanner-js.js +70 -118
  95. package/dist/native-region-scanner-kotlin.js +94 -0
  96. package/dist/native-region-scanner-main.js +15 -181
  97. package/dist/native-region-scanner-php.js +80 -0
  98. package/dist/native-region-scanner-python.js +62 -0
  99. package/dist/native-region-scanner-ruby.js +72 -0
  100. package/dist/native-region-scanner-scala.js +91 -0
  101. package/dist/native-region-scanner-spans.js +74 -0
  102. package/dist/native-region-scanner-swift.js +155 -0
  103. package/dist/native-region-scanner.js +14 -10
  104. package/dist/native-source-ledger-helpers.js +195 -0
  105. package/dist/native-source-ledger.js +306 -0
  106. package/dist/native-source-preservation-scanner.js +4 -0
  107. package/dist/semantic-import-callsite-regions.js +136 -0
  108. package/dist/semantic-import-effect-regions.js +283 -0
  109. package/dist/semantic-import-regions.js +11 -2
  110. package/dist/semantic-import-sidecar-entry.js +16 -2
  111. package/dist/semantic-import-sidecar-types.d.ts +2 -0
  112. package/dist/semantic-sidecar-example.js +68 -0
  113. package/dist/universal-capability-matrix.js +23 -0
  114. package/dist/universal-conversion-artifact-query.js +79 -2
  115. package/dist/universal-conversion-artifact-semantic-edit.js +103 -0
  116. package/dist/universal-conversion-artifact-summary.js +33 -1
  117. package/dist/universal-conversion-artifacts.js +13 -48
  118. package/dist/universal-conversion-plan-scoring.js +21 -1
  119. package/dist/universal-conversion-plan-summary.js +30 -0
  120. package/dist/universal-conversion-plan.js +25 -9
  121. package/dist/universal-conversion-route-metadata.js +96 -0
  122. package/dist/universal-conversion-route-operations.js +7 -0
  123. package/dist/universal-representation-coverage.js +193 -0
  124. package/package.json +1 -1
@@ -0,0 +1,64 @@
1
+ function canonicalizeSourceBodies(sourceText, preparedRegions, side) {
2
+ let output = sourceText;
3
+ const replacements = preparedRegions
4
+ .map((region) => ({ range: region[side], replacement: region.base.body }))
5
+ .sort((left, right) => right.range.bodyStart - left.range.bodyStart);
6
+ for (const { range, replacement } of replacements) {
7
+ output = `${output.slice(0, range.bodyStart)}${replacement}${output.slice(range.bodyEnd)}`;
8
+ }
9
+ return output;
10
+ }
11
+
12
+ function applyMemberAdditions(headSourceText, preparedRegions) {
13
+ let output = headSourceText;
14
+ const replacements = preparedRegions
15
+ .filter((region) => region.workerAddedMembers.length)
16
+ .map((region) => ({ range: region.head, replacement: appendMembersToBody(region.head.body, region.workerAddedMembers) }))
17
+ .sort((left, right) => right.range.bodyStart - left.range.bodyStart);
18
+ for (const { range, replacement } of replacements) {
19
+ output = `${output.slice(0, range.bodyStart)}${replacement}${output.slice(range.bodyEnd)}`;
20
+ }
21
+ return output;
22
+ }
23
+
24
+ function appendMembersToBody(body, members) {
25
+ if (!members.length) return body;
26
+ const indent = inferMemberIndent(body) ?? inferMemberIndent(members.map((member) => member.text).join('\n')) ?? ' ';
27
+ const addedText = members.map((member) => normalizeMemberForInsertion(member.text, indent)).join('\n');
28
+ const trailing = body.match(/\s*$/)?.[0] ?? '';
29
+ const before = body.slice(0, body.length - trailing.length);
30
+ if (!before.trim()) {
31
+ const closingIndent = trailing.includes('\n') ? trailing.slice(trailing.lastIndexOf('\n') + 1) : '';
32
+ return `\n${addedText}\n${closingIndent}`;
33
+ }
34
+ return `${before}${before.endsWith('\n') ? '' : '\n'}${addedText}${trailing.includes('\n') ? trailing : `\n${trailing}`}`;
35
+ }
36
+
37
+ function inferMemberIndent(text) {
38
+ return String(text ?? '').match(/\n([ \t]*)\S/)?.[1];
39
+ }
40
+
41
+ function normalizeMemberForInsertion(text, indent) {
42
+ const lines = String(text ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
43
+ while (lines.length && !lines[0].trim()) lines.shift();
44
+ while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
45
+ const commonIndent = minimumIndent(lines);
46
+ return lines.map((line) => {
47
+ const normalized = commonIndent ? line.slice(Math.min(commonIndent, leadingWhitespace(line))) : line;
48
+ return normalized.trim() ? `${indent}${normalized}` : normalized;
49
+ }).join('\n');
50
+ }
51
+
52
+ function minimumIndent(lines) {
53
+ const indents = lines.filter((line) => line.trim()).map(leadingWhitespace);
54
+ return indents.length ? Math.min(...indents) : 0;
55
+ }
56
+
57
+ function leadingWhitespace(line) {
58
+ return line.match(/^[ \t]*/)?.[0].length ?? 0;
59
+ }
60
+
61
+ export {
62
+ applyMemberAdditions,
63
+ canonicalizeSourceBodies
64
+ };
@@ -0,0 +1,18 @@
1
+ function normalizeMemberText(text) {
2
+ return String(text ?? '')
3
+ .replace(/\r\n/g, '\n')
4
+ .replace(/\r/g, '\n')
5
+ .split('\n')
6
+ .map((line) => line.trimEnd())
7
+ .join('\n')
8
+ .trim();
9
+ }
10
+
11
+ function uniqueStrings(values) {
12
+ return [...new Set(values.filter(Boolean).map(String))];
13
+ }
14
+
15
+ export {
16
+ normalizeMemberText,
17
+ uniqueStrings
18
+ };
@@ -0,0 +1,15 @@
1
+ export {
2
+ findContainer,
3
+ normalizeKind
4
+ } from './js-ts-semantic-merge-member-containers.js';
5
+ export {
6
+ parseMembers
7
+ } from './js-ts-semantic-merge-member-segments.js';
8
+ export {
9
+ applyMemberAdditions,
10
+ canonicalizeSourceBodies
11
+ } from './js-ts-semantic-merge-member-source.js';
12
+ export {
13
+ normalizeMemberText,
14
+ uniqueStrings
15
+ } from './js-ts-semantic-merge-member-utils.js';
@@ -0,0 +1,21 @@
1
+ export {
2
+ JsTsSafeMergeConflictCodes,
3
+ JsTsSafeMergeGateIds,
4
+ JsTsSafeMergeStatuses,
5
+ safeMergeJsTsImportsAndDeclarations
6
+ } from './js-ts-safe-merge.js';
7
+ export {
8
+ mergeJsTsSafeMemberAdditions,
9
+ safeMergeJsTsMembers
10
+ } from './js-ts-safe-member-merge.js';
11
+ export {
12
+ createJsTsSemanticMergeConflictExplanation,
13
+ createJsTsSemanticMergeGateResult,
14
+ JsTsSemanticMergeConflictClasses,
15
+ JsTsSemanticMergeGateStatuses
16
+ } from './js-ts-semantic-merge-contracts.js';
17
+ export {
18
+ createJsTsSemanticConflictSidecars,
19
+ JsTsSemanticConflictSidecarClasses,
20
+ summarizeJsTsSemanticConflictSidecars
21
+ } from './js-ts-semantic-conflict-sidecars.js';
@@ -0,0 +1,51 @@
1
+ function lightweightEffectKinds(line) {
2
+ const kinds = [];
3
+ if (/\bawait\b|import\s*\(/.test(line)) kinds.push('async');
4
+ if (hasGlobalNetworkCall(line)) kinds.push('network');
5
+ if (/\b(localStorage|sessionStorage|indexedDB|caches|cookie)\b/.test(line)) kinds.push('storage');
6
+ if (hasGlobalSchedulerCall(line)) kinds.push('scheduler');
7
+ if (/\b(console|process|Deno|Bun)\s*\./.test(line)) kinds.push('host');
8
+ if (hasBrowserEffect(line)) kinds.push('browser');
9
+ return kinds;
10
+ }
11
+
12
+ function hasGlobalNetworkCall(line) {
13
+ return hasBareCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'])
14
+ || hasGlobalPropertyCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource']);
15
+ }
16
+
17
+ function hasGlobalSchedulerCall(line) {
18
+ const names = [
19
+ 'setTimeout',
20
+ 'setInterval',
21
+ 'clearTimeout',
22
+ 'clearInterval',
23
+ 'requestAnimationFrame',
24
+ 'cancelAnimationFrame',
25
+ 'requestIdleCallback',
26
+ 'cancelIdleCallback',
27
+ 'queueMicrotask',
28
+ 'setImmediate',
29
+ 'clearImmediate'
30
+ ];
31
+ return hasBareCall(line, names) || hasGlobalPropertyCall(line, names);
32
+ }
33
+
34
+ function hasBrowserEffect(line) {
35
+ return /\b(document|window|navigator|location|history)\s*\./.test(line)
36
+ || hasGlobalConstructorCall(line, ['Worker', 'SharedWorker']);
37
+ }
38
+
39
+ function hasBareCall(line, names) {
40
+ return names.some((name) => new RegExp(`(?:^|[^\\w$.])${name}\\s*\\(`).test(line));
41
+ }
42
+
43
+ function hasGlobalPropertyCall(line, names) {
44
+ return names.some((name) => new RegExp(`\\b(?:window|globalThis|self)\\s*\\.\\s*${name}\\s*\\(`).test(line));
45
+ }
46
+
47
+ function hasGlobalConstructorCall(line, names) {
48
+ return names.some((name) => new RegExp(`(?:^|[^\\w$.])new\\s+(?:(?:window|globalThis|self)\\s*\\.\\s*)?${name}\\s*\\(`).test(line));
49
+ }
50
+
51
+ export { lightweightEffectKinds };
@@ -41,7 +41,7 @@ export function isDependencyIdentifier(value) {
41
41
  export function dependencyScanRanges(input, declarations, lines) {
42
42
  const ordered = [...(declarations ?? [])].sort((left, right) => (left.span?.startLine ?? 0) - (right.span?.startLine ?? 0));
43
43
  return ordered
44
- .filter((declaration) => declaration?.symbolId && declaration.role !== 'import')
44
+ .filter((declaration) => declaration?.symbolId && declaration.role !== 'import' && shouldScanDependencyDeclaration(input, declaration))
45
45
  .map((declaration, index) => ({
46
46
  declaration,
47
47
  startLine: declaration.span?.startLine ?? 1,
@@ -49,6 +49,17 @@ export function dependencyScanRanges(input, declarations, lines) {
49
49
  }));
50
50
  }
51
51
 
52
+ function shouldScanDependencyDeclaration(input, declaration) {
53
+ if (!isJavaScriptLikeLanguage(input)) return true;
54
+ if (declaration.metadata?.signatureOnly || declaration.fields?.typeKind) return false;
55
+ if (/^Type(?:Alias|Method|Property|FunctionProperty)/.test(String(declaration?.kind ?? ''))) return false;
56
+ return !['interface', 'type'].includes(String(declaration?.symbolKind ?? '').toLowerCase());
57
+ }
58
+
59
+ function isJavaScriptLikeLanguage(input) {
60
+ return ['javascript', 'typescript'].includes(String(input?.language ?? '').toLowerCase());
61
+ }
62
+
52
63
  export function maskDependencyLine(input, line, state) {
53
64
  const language = String(input.language ?? '').toLowerCase();
54
65
  const text = String(line ?? '');
@@ -7,6 +7,7 @@ import {
7
7
  isDependencyIdentifier,
8
8
  maskDependencyLine
9
9
  } from './lightweight-dependency-language.js';
10
+ import { lightweightEffectKinds } from './lightweight-dependency-effects.js';
10
11
  import { sourceLines } from './native-region-scanner-core.js';
11
12
 
12
13
  export function lightweightDependencyRelations(input, declarations, documentId) {
@@ -108,6 +109,7 @@ function addLightweightSemanticFacts(input, documentId, declaration, line, lineN
108
109
  function shouldScanRuntimeFacts(input, declaration) {
109
110
  if (!isJavaScriptLike(input)) return true;
110
111
  if (declaration?.fields?.typeKind) return false;
112
+ if (['object', 'array'].includes(declaration?.fields?.initializerKind)) return false;
111
113
  if (/^Type(?:Alias|Method|Property|FunctionProperty)/.test(String(declaration?.kind ?? ''))) return false;
112
114
  return !['interface', 'type'].includes(String(declaration?.symbolKind ?? '').toLowerCase());
113
115
  }
@@ -119,6 +121,10 @@ function isJavaScriptLike(input) {
119
121
  function isIgnoredDependencyOccurrence(input, line, startIndex, name) {
120
122
  if (!isJavaScriptLike(input)) return false;
121
123
  const endIndex = startIndex + String(name).length;
124
+ const beforeText = line.slice(0, startIndex);
125
+ if (/\b(?:async\s+)?function\*?\s*$/.test(beforeText) || /\bclass\s*$/.test(beforeText)) return true;
126
+ const afterText = line.slice(endIndex);
127
+ if (/^\s*\([^)]*\)\s*\{/.test(afterText) && /^\s*(?:async\s+)?(?:get\s+|set\s+)?$/.test(beforeText)) return true;
122
128
  const previous = previousNonSpace(line, startIndex - 1);
123
129
  const next = nextNonSpace(line, endIndex);
124
130
  return previous === '.' || next === ':';
@@ -148,17 +154,6 @@ function lightweightControlFlowKinds(line, state = {}) {
148
154
  return kinds;
149
155
  }
150
156
 
151
- function lightweightEffectKinds(line) {
152
- const kinds = [];
153
- if (/\bawait\b|import\s*\(/.test(line)) kinds.push('async');
154
- if (hasGlobalNetworkCall(line)) kinds.push('network');
155
- if (/\b(localStorage|sessionStorage|indexedDB|caches|cookie)\b/.test(line)) kinds.push('storage');
156
- if (/\b(setTimeout|setInterval|requestAnimationFrame|queueMicrotask)\s*\(/.test(line)) kinds.push('scheduler');
157
- if (/\b(console|process|Deno|Bun)\s*\./.test(line)) kinds.push('host');
158
- if (/\b(document|window|navigator|location|history)\s*\./.test(line)) kinds.push('browser');
159
- return kinds;
160
- }
161
-
162
157
  function lightweightMutationKinds(line) {
163
158
  const kinds = [];
164
159
  if (/\bdelete\s+[A-Za-z_$][\w$.[\]]*/.test(line)) kinds.push('delete');
@@ -199,24 +194,11 @@ function blockBraceDelta(line) {
199
194
  return delta;
200
195
  }
201
196
 
202
- function hasGlobalNetworkCall(line) {
203
- return hasBareCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'])
204
- || hasGlobalPropertyCall(line, ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource']);
205
- }
206
-
207
- function hasBareCall(line, names) {
208
- return names.some((name) => new RegExp(`(?:^|[^\\w$.])${name}\\s*\\(`).test(line));
209
- }
210
-
211
- function hasGlobalPropertyCall(line, names) {
212
- return names.some((name) => new RegExp(`\\b(?:window|globalThis|self)\\s*\\.\\s*${name}\\s*\\(`).test(line));
213
- }
214
-
215
197
  function hasRuntimeAssignment(line) {
216
198
  const text = String(line ?? '');
217
199
  for (let index = 0; index < text.length; index += 1) {
218
200
  if (text[index] !== '=' || !isPlainAssignmentOperator(text, index)) continue;
219
- if (!isLocalDeclarationInitializer(text, index)) return true;
201
+ if (!isLocalDeclarationInitializer(text, index) && !isJsxAttributeInitializer(text, index)) return true;
220
202
  }
221
203
  return false;
222
204
  }
@@ -237,6 +219,8 @@ function isLocalDeclarationInitializer(text, index) {
237
219
  || /^(?:export\s+)?type\s+[A-Za-z_$][\w$]*(?:\s*<[^>]+>)?\s*$/.test(statement);
238
220
  }
239
221
 
222
+ function isJsxAttributeInitializer(text, index) { const before = text.slice(0, index); const open = before.lastIndexOf('<'); return open >= 0 && before.lastIndexOf('>') < open && /<[A-Za-z][^<>{}]*\s[A-Za-z_$][\w:.-]*\s*$/.test(before.slice(open)); }
223
+
240
224
  function addFactRecord(input, documentId, declaration, predicate, factKind, lineNumber, records) {
241
225
  const key = `${declaration.symbolId}|${predicate}|${factKind}|${lineNumber}`;
242
226
  if (records.seen.has(key)) return;
@@ -280,7 +264,9 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
280
264
  confidence: 'lexical-reference',
281
265
  sourceDocumentId: documentId,
282
266
  sourceName: caller.name,
283
- targetName: target.name
267
+ targetName: target.name,
268
+ occurrenceId,
269
+ sourceSpan: span
284
270
  }
285
271
  });
286
272
  records.occurrences.push({
@@ -306,5 +292,6 @@ function addDependencyRecord(input, documentId, caller, target, occurrence, reco
306
292
  }
307
293
 
308
294
  function isCallReference(line, afterIdentifierIndex) {
309
- return /^\s*\(/.test(String(line ?? '').slice(afterIdentifierIndex));
295
+ const rest = String(line ?? '').slice(afterIdentifierIndex);
296
+ return /^\s*(?:\(|\?\.\s*\(|\?\.\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\])(?:\s*(?:\.|\?\.)\s*(?:[A-Za-z_$][\w$]*|\[[^\]]+\]))*\s*(?:\?\.)?\s*\()/.test(rest);
310
297
  }
@@ -8,7 +8,7 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
8
8
  languageKind: `${input.language}.${languageKind}`,
9
9
  name,
10
10
  symbolKind,
11
- symbolId: `symbol:${input.language}:${idFragment(name)}`,
11
+ symbolId: options.symbolId ?? `symbol:${input.language}:${idFragment(name)}`,
12
12
  span: options.span ?? spanForLine(input, lineNumber),
13
13
  fields,
14
14
  metadata: { scan: 'lightweight-declaration', hasBody, ...options.metadata },
@@ -18,6 +18,22 @@ function nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fi
18
18
  };
19
19
  }
20
20
 
21
+ function nativeSignatureDeclaration(input, lineNumber, languageKind, symbolKind, name, fields = {}, _hasBody = false, options = {}) {
22
+ const signatureParts = [name, fields.signature, fields.returnType, fields.valueType, ...(fields.parameters ?? [])];
23
+ const signatureKey = idFragment(signatureParts.filter(Boolean).join(':') || `${name}:${lineNumber}`);
24
+ const sourcePath = input.sourcePath ?? `${input.language}:memory`;
25
+ const regionKind = options.regionKind ?? 'declaration';
26
+ return nativeDeclaration(input, lineNumber, languageKind, symbolKind, name, fields, false, {
27
+ regionKind,
28
+ symbolId: options.symbolId ?? `symbol:${input.language}:signature:${idFragment(name)}:${signatureKey}`,
29
+ metadata: {
30
+ signatureOnly: true,
31
+ ownershipRegionKey: options.ownershipRegionKey ?? `source#${sourcePath}#${regionKind}#${name}#${signatureKey}`,
32
+ ...options.metadata
33
+ }
34
+ });
35
+ }
36
+
21
37
  function nativeImportDeclaration(input, lineNumber, importPath, languageKind, symbolKind, options = {}) {
22
38
  const name = String(options.name ?? importPath);
23
39
  const nodeId = `native_${idFragment(languageKind)}_${lineNumber}_${idFragment(name)}`;
@@ -65,6 +81,20 @@ function nativeImportBindingDeclaration(input, lineNumber, importPath, binding,
65
81
  });
66
82
  }
67
83
 
84
+ function nativeExportDeclaration(input, lineNumber, exportedName, languageKind = 'ExportDeclaration', fields = {}, options = {}) {
85
+ const name = String(options.name ?? exportedName ?? 'default');
86
+ return nativeDeclaration(input, lineNumber, languageKind, options.symbolKind ?? 'export', name, {
87
+ exportedName: String(exportedName ?? name),
88
+ ...fields
89
+ }, false, {
90
+ role: options.role ?? 'export',
91
+ regionKind: options.regionKind ?? 'export',
92
+ span: options.span,
93
+ symbolId: options.symbolId ?? `symbol:${input.language}:export:${idFragment(name)}`,
94
+ metadata: { scan: 'lightweight-export', ...options.metadata }
95
+ });
96
+ }
97
+
68
98
  function nativeMacroLoss(input, lineNumber, source, kind, name = idFragment(source).slice(0, 40)) {
69
99
  const nodeId = `native_${kind}_${lineNumber}_${idFragment(name)}`;
70
100
  return {
@@ -202,9 +232,11 @@ export {
202
232
  jsControlKeyword,
203
233
  lightweightCoverageLosses,
204
234
  nativeDeclaration,
235
+ nativeExportDeclaration,
205
236
  nativeImportDeclaration,
206
237
  nativeImportBindingDeclaration,
207
238
  nativeMacroLoss,
239
+ nativeSignatureDeclaration,
208
240
  sourceLines,
209
241
  splitParameters,
210
242
  splitTypeParameters
@@ -0,0 +1,151 @@
1
+ import { uniqueStrings, upperFirst } from './native-import-utils.js';
2
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
3
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
4
+
5
+ function scanCSharp(input) {
6
+ const declarations = [];
7
+ const lines = sourceLines(input.sourceText);
8
+ const blockStack = [];
9
+ let braceDepth = 0;
10
+ for (const [index, { line, number }] of lines.entries()) {
11
+ const trimmed = line.trim();
12
+ const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
13
+ while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
14
+
15
+ let match;
16
+ if ((match = trimmed.match(/^using\s+([A-Za-z_]\w*)\s*=\s*(.+?)\s*;/))) {
17
+ declarations.push(nativeDeclaration(input, number, 'UsingAliasDirective', 'type', match[1], { target: match[2].trim() }, false));
18
+ } else if ((match = trimmed.match(/^using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UsingDirective', 'namespace'));
20
+ } else if ((match = trimmed.match(/^namespace\s+([A-Za-z_][\w.]*)/))) {
21
+ const hasBody = trimmed.includes('{');
22
+ declarations.push(nativeDeclaration(input, number, 'NamespaceDeclaration', 'namespace', match[1], {}, hasBody, spanOptions(input, lines, index, hasBody)));
23
+ if (hasBody) blockStack.push({ kind: 'namespace', name: match[1], bodyDepth: braceDepth + 1 });
24
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|unsafe|new)\s+)*)delegate\s+(.+?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*;/))) {
25
+ declarations.push(nativeDeclaration(input, number, 'DelegateDeclaration', 'type', match[3], {
26
+ returnType: match[2].trim(),
27
+ parameters: splitParameters(match[4]),
28
+ modifiers: csharpModifiers(match[1])
29
+ }, false));
30
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|abstract|sealed|static|partial|readonly|ref|unsafe)\s+)*)(class|interface|struct|enum|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/))) {
31
+ const owner = nearestType(blockStack);
32
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
33
+ const hasBody = trimmed.includes('{');
34
+ declarations.push(nativeDeclaration(input, number, csharpDeclarationKind(match[2]), csharpSymbolKind(match[2]), name, {
35
+ csharpKind: match[2].replace(/\s+/g, ' '),
36
+ modifiers: csharpModifiers(match[1]),
37
+ ...(owner ? { owner: owner.name } : {})
38
+ }, hasBody, spanOptions(input, lines, index, hasBody)));
39
+ if (hasBody) blockStack.push({ kind: csharpTypeStackKind(match[2]), name, bodyDepth: braceDepth + 1 });
40
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|async|partial|sealed|abstract|extern|new|unsafe|readonly)\s+)*)(?:[A-Za-z_][\w<>\[\].?,\s]*\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:=>.*|\{.*|;)?$/))) {
41
+ const parameters = splitParameters(match[3]);
42
+ const owner = nearestType(blockStack);
43
+ const extensionReceiver = csharpExtensionReceiver(parameters);
44
+ const target = csharpMethodTarget(owner, match[1], match[2], extensionReceiver);
45
+ const hasBody = trimmed.includes('{') || trimmed.includes('=>');
46
+ declarations.push(nativeDeclaration(input, number, extensionReceiver ? 'ExtensionMethodDeclaration' : 'MethodDeclaration', target.owner ? 'method' : 'function', target.name, {
47
+ parameters,
48
+ methodName: match[2],
49
+ modifiers: csharpModifiers(match[1]),
50
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
51
+ ...(extensionReceiver ? { extensionReceiver } : {})
52
+ }, hasBody, {
53
+ ...spanOptions(input, lines, index, trimmed.includes('{')),
54
+ metadata: {
55
+ methodName: match[2],
56
+ modifiers: csharpModifiers(match[1]),
57
+ ...(target.owner ? { owner: target.owner, receiverKind: target.receiverKind } : {}),
58
+ ...(extensionReceiver ? { extensionReceiver } : {})
59
+ }
60
+ }));
61
+ if (trimmed.includes('{')) blockStack.push({ kind: 'method', name: target.name, bodyDepth: braceDepth + 1 });
62
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|unsafe)\s+)*)event\s+(.+?)\s+([A-Za-z_]\w*)\s*(?:[;{=]|=>)/))) {
63
+ const owner = nearestType(blockStack);
64
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
65
+ declarations.push(nativeDeclaration(input, number, 'EventDeclaration', 'event', name, {
66
+ eventType: match[2].trim(),
67
+ accessors: csharpAccessors(trimmed),
68
+ modifiers: csharpModifiers(match[1]),
69
+ ...(owner ? { owner: owner.name, eventName: match[3] } : {})
70
+ }, trimmed.includes('{'), spanOptions(input, lines, index, trimmed.includes('{'))));
71
+ } else if ((match = trimmed.match(/^((?:(?:public|protected|private|internal|static|virtual|override|abstract|sealed|new|required|readonly|unsafe)\s+)*)([A-Za-z_][\w<>\[\].?,\s]*\??)\s+([A-Za-z_]\w*)\s*(?:\{|=>)/))) {
72
+ const owner = nearestType(blockStack);
73
+ const name = owner ? `${owner.name}.${match[3]}` : match[3];
74
+ declarations.push(nativeDeclaration(input, number, 'PropertyDeclaration', 'property', name, {
75
+ propertyType: match[2].trim(),
76
+ accessors: csharpAccessors(trimmed),
77
+ modifiers: csharpModifiers(match[1]),
78
+ ...(owner ? { owner: owner.name, propertyName: match[3] } : {})
79
+ }, trimmed.includes('{') || trimmed.includes('=>'), spanOptions(input, lines, index, trimmed.includes('{'))));
80
+ }
81
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
82
+ }
83
+ return declarations;
84
+ }
85
+
86
+ function spanOptions(input, lines, index, hasBraceBody) {
87
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
88
+ }
89
+
90
+ function depthAfterLeadingClosers(trimmed, depth) {
91
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
92
+ return Math.max(0, depth - closers);
93
+ }
94
+
95
+ function nearestType(blockStack) {
96
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
97
+ if (blockStack[index].kind === 'method') return undefined;
98
+ if (['class', 'interface', 'struct', 'record'].includes(blockStack[index].kind)) return blockStack[index];
99
+ }
100
+ return undefined;
101
+ }
102
+
103
+ function csharpMethodTarget(owner, modifiers, methodName, extensionReceiver) {
104
+ if (extensionReceiver) return { name: `${extensionReceiver.type}.extension.${methodName}`, owner: extensionReceiver.type, receiverKind: 'extension' };
105
+ if (!owner) return { name: methodName };
106
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
107
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
108
+ }
109
+
110
+ function csharpModifiers(raw) {
111
+ return splitParameters(String(raw ?? '').trim().replace(/\s+/g, ','));
112
+ }
113
+
114
+ function csharpTypeStackKind(kind) {
115
+ const normalized = String(kind).replace(/\s+/g, ' ');
116
+ if (normalized.startsWith('record')) return 'record';
117
+ if (normalized === 'enum') return 'enum';
118
+ if (normalized === 'struct') return 'struct';
119
+ if (normalized === 'interface') return 'interface';
120
+ return 'class';
121
+ }
122
+
123
+ function csharpSymbolKind(kind) {
124
+ const normalized = String(kind).replace(/\s+/g, ' ');
125
+ if (normalized === 'interface') return 'interface';
126
+ if (normalized === 'struct' || normalized === 'enum' || normalized.startsWith('record')) return 'type';
127
+ return 'class';
128
+ }
129
+
130
+ function csharpDeclarationKind(kind) {
131
+ const normalized = String(kind).replace(/\s+/g, ' ');
132
+ if (normalized === 'record struct') return 'RecordStructDeclaration';
133
+ if (normalized === 'record class') return 'RecordClassDeclaration';
134
+ if (normalized === 'record') return 'RecordDeclaration';
135
+ return `${upperFirst(normalized)}Declaration`;
136
+ }
137
+
138
+ function csharpExtensionReceiver(parameters) {
139
+ const match = String(parameters?.[0] ?? '').match(/^this\s+(.+?)\s+([A-Za-z_]\w*)$/);
140
+ return match ? { type: match[1].trim(), name: match[2] } : undefined;
141
+ }
142
+
143
+ function csharpAccessors(source) {
144
+ return uniqueStrings([...String(source ?? '').matchAll(/\b(get|set|init|add|remove)\b/g)].map((match) => match[1]));
145
+ }
146
+
147
+ function braceDelta(source) {
148
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
149
+ }
150
+
151
+ export { scanCSharp };
@@ -0,0 +1,91 @@
1
+ import { upperFirst } from './native-import-utils.js';
2
+ import { nativeDeclaration, nativeImportDeclaration, sourceLines, splitParameters } from './native-region-scanner-core.js';
3
+ import { braceBlockSpan } from './native-region-scanner-spans.js';
4
+
5
+ function scanDart(input) {
6
+ const declarations = [];
7
+ const lines = sourceLines(input.sourceText);
8
+ const blockStack = [];
9
+ let braceDepth = 0;
10
+ for (const [index, { line, number }] of lines.entries()) {
11
+ const trimmed = line.trim();
12
+ const lineStartDepth = depthAfterLeadingClosers(trimmed, braceDepth);
13
+ while (blockStack.length && blockStack[blockStack.length - 1].bodyDepth > lineStartDepth) blockStack.pop();
14
+
15
+ let match;
16
+ if ((match = trimmed.match(/^(?:import|export)\s+['"]([^'"]+)['"]/))) {
17
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'UriBasedDirective', 'library'));
18
+ } else if ((match = trimmed.match(/^part\s+['"]([^'"]+)['"]/))) {
19
+ declarations.push(nativeImportDeclaration(input, number, match[1], 'PartDirective', 'library'));
20
+ } else if ((match = trimmed.match(/^(?:(?:abstract|base|final|interface|sealed)\s+)*(class|mixin|enum)\s+([A-Za-z_]\w*)/))) {
21
+ const hasBody = trimmed.includes('{');
22
+ declarations.push(nativeDeclaration(input, number, `${upperFirst(match[1])}Declaration`, dartSymbolKind(match[1]), match[2], {}, hasBody, spanOptions(input, lines, index, hasBody)));
23
+ if (hasBody) blockStack.push({ kind: match[1], name: match[2], bodyDepth: braceDepth + 1 });
24
+ } else if ((match = trimmed.match(/^extension(?:\s+([A-Za-z_]\w*))?\s+on\s+([A-Za-z_]\w*(?:<[^>]+>)?)\s*\{/))) {
25
+ const name = match[1] ? `${match[1]}.extension` : `${match[2]}.extension`;
26
+ declarations.push(nativeDeclaration(input, number, 'ExtensionDeclaration', 'implementation', name, {
27
+ receiverType: match[2],
28
+ ...(match[1] ? { extensionName: match[1] } : {})
29
+ }, true, spanOptions(input, lines, index, true)));
30
+ blockStack.push({ kind: 'extension', name, receiverType: match[2], bodyDepth: braceDepth + 1 });
31
+ } else if ((match = trimmed.match(/^typedef\s+([A-Za-z_]\w*)\b/))) {
32
+ declarations.push(nativeDeclaration(input, number, 'TypeAlias', 'type', match[1], {}, false));
33
+ } else if ((match = trimmed.match(/^((?:(?:external|static)\s+)*)(?:[A-Za-z_]\w*(?:<[^>]+>)?\??|void)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*(?:async\s*)?(?:\{|=>|;)/))) {
34
+ const owner = nearestContainer(blockStack);
35
+ const target = dartFunctionTarget(owner, match[1], match[2]);
36
+ const hasBraceBody = trimmed.includes('{');
37
+ declarations.push(nativeDeclaration(input, number, 'FunctionDeclaration', target.owner ? 'method' : 'function', target.name, {
38
+ parameters: splitParameters(match[3]),
39
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
40
+ ...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
41
+ }, hasBraceBody || trimmed.includes('=>'), {
42
+ ...spanOptions(input, lines, index, hasBraceBody),
43
+ metadata: {
44
+ ...(target.owner ? { owner: target.owner, methodName: match[2], receiverKind: target.receiverKind } : {}),
45
+ ...(owner?.receiverType ? { receiverType: owner.receiverType } : {})
46
+ }
47
+ }));
48
+ if (hasBraceBody) blockStack.push({ kind: 'function', name: target.name, bodyDepth: braceDepth + 1 });
49
+ } else if ((match = trimmed.match(/^(?:(?:static|external|late)\s+)*(?:const|final|var)\s+(?:[A-Za-z_]\w*(?:<[^>]+>)?\??\s+)?([A-Za-z_]\w*)\b/))) {
50
+ declarations.push(nativeDeclaration(input, number, 'VariableDeclaration', 'variable', match[1], {}, false));
51
+ }
52
+ braceDepth = Math.max(0, braceDepth + braceDelta(line));
53
+ }
54
+ return declarations;
55
+ }
56
+
57
+ function spanOptions(input, lines, index, hasBraceBody) {
58
+ return hasBraceBody ? { span: braceBlockSpan(input, lines, index) } : {};
59
+ }
60
+
61
+ function depthAfterLeadingClosers(trimmed, depth) {
62
+ const closers = String(trimmed).match(/^}+/)?.[0].length ?? 0;
63
+ return Math.max(0, depth - closers);
64
+ }
65
+
66
+ function nearestContainer(blockStack) {
67
+ for (let index = blockStack.length - 1; index >= 0; index -= 1) {
68
+ if (blockStack[index].kind === 'function') return undefined;
69
+ if (['class', 'mixin', 'enum', 'extension'].includes(blockStack[index].kind)) return blockStack[index];
70
+ }
71
+ return undefined;
72
+ }
73
+
74
+ function dartFunctionTarget(owner, modifiers, methodName) {
75
+ if (!owner) return { name: methodName };
76
+ if (owner.kind === 'extension') return { name: `${owner.name}.${methodName}`, owner: owner.name, receiverKind: 'extension' };
77
+ const receiverKind = /\bstatic\b/.test(modifiers) ? 'static' : 'member';
78
+ return { name: receiverKind === 'static' ? `${owner.name}.static.${methodName}` : `${owner.name}.${methodName}`, owner: owner.name, receiverKind };
79
+ }
80
+
81
+ function braceDelta(source) {
82
+ return [...String(source ?? '')].reduce((delta, char) => delta + (char === '{' ? 1 : char === '}' ? -1 : 0), 0);
83
+ }
84
+
85
+ function dartSymbolKind(kind) {
86
+ if (kind === 'mixin') return 'trait';
87
+ if (kind === 'enum') return 'type';
88
+ return 'class';
89
+ }
90
+
91
+ export { scanDart };