devlensio 1.0.3 → 1.1.0

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 (75) hide show
  1. package/dist/config/backcompat.test.d.ts +1 -0
  2. package/dist/config/backcompat.test.js +188 -0
  3. package/dist/config/index.d.ts +5 -3
  4. package/dist/config/index.js +37 -54
  5. package/dist/config/providers/file.d.ts +3 -1
  6. package/dist/config/providers/file.js +47 -53
  7. package/dist/config/providers/providers.default.js +0 -1
  8. package/dist/config/providers/request.js +1 -1
  9. package/dist/config/types.d.ts +0 -1
  10. package/dist/config/types.js +2 -18
  11. package/dist/graph/edges/callEdges.js +97 -5
  12. package/dist/graph/edges/callEdges.test.js +154 -0
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/jobs/queue/memory.js +12 -1
  16. package/dist/parser/extractors/classes.js +23 -4
  17. package/dist/parser/extractors/functions.d.ts +10 -3
  18. package/dist/parser/extractors/functions.js +135 -65
  19. package/dist/parser/extractors/hooks.js +5 -29
  20. package/dist/parser/extractors/objectMethods.js +3 -4
  21. package/dist/parser/index.js +11 -6
  22. package/dist/parser/overloads.d.ts +2 -0
  23. package/dist/parser/overloads.js +96 -0
  24. package/dist/parser/overloads.test.d.ts +1 -0
  25. package/dist/parser/overloads.test.js +175 -0
  26. package/dist/parser/typeUtils.d.ts +2 -0
  27. package/dist/parser/typeUtils.js +2 -0
  28. package/dist/summarizer/providers/index.js +4 -4
  29. package/dist/summarizer/providers/openai.js +1 -1
  30. package/dist/types.d.ts +6 -0
  31. package/extractors/go/bin/darwin-amd64/devlens_go_extractor +0 -0
  32. package/extractors/go/bin/darwin-arm64/devlens_go_extractor +0 -0
  33. package/extractors/go/bin/linux-amd64/devlens_go_extractor +0 -0
  34. package/extractors/go/bin/linux-arm64/devlens_go_extractor +0 -0
  35. package/extractors/go/bin/windows-amd64/devlens_go_extractor.exe +0 -0
  36. package/extractors/go/calls.go +134 -2
  37. package/extractors/go/contract.go +1 -0
  38. package/extractors/go/exports.go +107 -0
  39. package/extractors/go/exports_test.go +64 -0
  40. package/extractors/go/extractor.go +1 -0
  41. package/extractors/go/lookup.go +63 -7
  42. package/extractors/go/nodes.go +28 -0
  43. package/extractors/go/parser.go +51 -8
  44. package/extractors/java/devlens_java_extractor.jar +0 -0
  45. package/extractors/java/src/devlens/extractor/ExportsMapBuilder.java +148 -0
  46. package/extractors/java/src/devlens/extractor/Extractor.java +34 -0
  47. package/extractors/java/src/devlens/extractor/LookupMaps.java +12 -0
  48. package/extractors/java/src/devlens/extractor/Overloads.java +290 -0
  49. package/extractors/java/src/devlens/extractor/Parser.java +39 -0
  50. package/extractors/java/src/devlens/extractor/TypeSolverFactory.java +9 -0
  51. package/extractors/java/src/devlens/extractor/edges/Calls.java +42 -36
  52. package/extractors/java/src/devlens/extractor/edges/Routes.java +10 -3
  53. package/extractors/python/devlens_extractors_python/edges/calls.py +127 -26
  54. package/extractors/python/devlens_extractors_python/exports_map.py +263 -0
  55. package/extractors/python/devlens_extractors_python/extractor.py +7 -1
  56. package/extractors/python/devlens_extractors_python/lookup.py +6 -0
  57. package/extractors/python/devlens_extractors_python/parser/__init__.py +6 -0
  58. package/extractors/python/devlens_extractors_python/parser/functions.py +103 -6
  59. package/extractors/python/devlens_extractors_python/parser/overloads.py +105 -0
  60. package/extractors/rust/bin/darwin-amd64/devlens_rust_extractor +0 -0
  61. package/extractors/rust/bin/darwin-arm64/devlens_rust_extractor +0 -0
  62. package/extractors/rust/bin/linux-amd64/devlens_rust_extractor +0 -0
  63. package/extractors/rust/bin/linux-arm64/devlens_rust_extractor +0 -0
  64. package/extractors/rust/bin/windows-amd64/devlens_rust_extractor.exe +0 -0
  65. package/extractors/rust/src/calls.rs +27 -16
  66. package/extractors/rust/src/contract.rs +2 -0
  67. package/extractors/rust/src/exports.rs +387 -0
  68. package/extractors/rust/src/exports_test.rs +105 -0
  69. package/extractors/rust/src/extractor.rs +2 -0
  70. package/extractors/rust/src/lookup.rs +60 -0
  71. package/extractors/rust/src/main.rs +4 -0
  72. package/extractors/rust/src/nodes.rs +19 -0
  73. package/extractors/rust/src/parser.rs +66 -1
  74. package/extractors/rust/src/routes.rs +2 -1
  75. package/package.json +10 -3
@@ -1,8 +1,89 @@
1
1
  import { closestByPath } from "./utils.js";
2
+ // Arity compatibility: a candidate matches when the call provides at least all
3
+ // required (non-optional, non-rest) params, and does not exceed the declared
4
+ // count unless the candidate declares a rest parameter. Candidates without
5
+ // parameters metadata are permissive (unknown shape).
6
+ function arityMatches(candidate, argCount) {
7
+ const parameters = candidate.metadata?.parameters;
8
+ const params = candidate.metadata?.params;
9
+ if (!params && !parameters)
10
+ return true;
11
+ const total = parameters?.length ?? params?.length ?? 0;
12
+ const hasRest = parameters?.some((p) => p.isRest) ?? false;
13
+ const required = parameters
14
+ ? parameters.filter((p) => !p.isOptional && !p.isRest).length
15
+ : total;
16
+ return argCount >= required && (hasRest || argCount <= total);
17
+ }
18
+ // Loose type agreement between a call-site arg type tag and a declared param
19
+ // type text. Both sides are cheap/heuristic (no type checker), so this scores
20
+ // rather than gates: unknown tags never match, text containment counts.
21
+ function typeAgrees(argType, paramType) {
22
+ if (!argType || argType === "unknown" || !paramType)
23
+ return false;
24
+ const p = paramType.trim();
25
+ return p === argType || p.includes(argType);
26
+ }
27
+ function typeScore(candidate, argTypes) {
28
+ const parameters = candidate.metadata?.parameters;
29
+ if (!parameters)
30
+ return 0;
31
+ let score = 0;
32
+ for (let i = 0; i < argTypes.length; i++) {
33
+ if (parameters[i] && typeAgrees(argTypes[i], parameters[i].type))
34
+ score++;
35
+ }
36
+ return score;
37
+ }
38
+ // Picks the right overload among same-name candidates using the call site's
39
+ // argument shape. Ladder:
40
+ // 1. no usable call shape (USES edges, spread calls, missing callSites)
41
+ // → legacy closestByPath behavior
42
+ // 2. arity filter — survivors whose declared params accept argCount
43
+ // 3. type tiebreak — among arity survivors, prefer the candidate with the
44
+ // highest arg-type agreement (handles same arity, different types)
45
+ // 4. still ambiguous → closestByPath among survivors
46
+ function resolveOverloadTarget(candidates, caller, callSite) {
47
+ if (candidates.length === 1)
48
+ return { target: candidates[0], matchedBy: "name" };
49
+ const usableShape = callSite
50
+ && typeof callSite.argCount === "number"
51
+ && !callSite.hasSpread;
52
+ if (!usableShape)
53
+ return { target: closestByPath(candidates, caller.filePath), matchedBy: "name" };
54
+ let pool = candidates.filter((c) => arityMatches(c, callSite.argCount));
55
+ if (pool.length === 0)
56
+ pool = candidates; // nothing accepts this arity — keep all
57
+ if (pool.length === 1)
58
+ return { target: pool[0], matchedBy: "arity" };
59
+ const argTypes = callSite.argTypes ?? [];
60
+ if (argTypes.some((t) => t && t !== "unknown")) {
61
+ let best = pool[0];
62
+ let bestScore = typeScore(pool[0], argTypes);
63
+ let tieAtBest = 1;
64
+ for (let i = 1; i < pool.length; i++) {
65
+ const s = typeScore(pool[i], argTypes);
66
+ if (s > bestScore) {
67
+ best = pool[i];
68
+ bestScore = s;
69
+ tieAtBest = 1;
70
+ }
71
+ else if (s === bestScore)
72
+ tieAtBest++;
73
+ }
74
+ if (bestScore > 0 && tieAtBest === 1) {
75
+ return { target: best, matchedBy: "signature" };
76
+ }
77
+ }
78
+ return { target: closestByPath(pool, caller.filePath), matchedBy: "name" };
79
+ }
2
80
  export function detectCallEdges(nodes, lookupMp) {
3
81
  const edges = [];
4
82
  // Dedup for THIRD_PARTY CALLS edges — one per (caller, target) pair
5
83
  const createdThirdPartyEdges = new Set();
84
+ // Dedup for local CALLS/USES edges — overload call sites can resolve to
85
+ // the same target through different (name, arity) pairs
86
+ const createdLocalEdges = new Set();
6
87
  // Accumulate resolved calls per node — written back to metadata after edge loop
7
88
  const resolvedCallsMap = new Map();
8
89
  // Lazily-created method nodes for default/namespace import member-access calls
@@ -34,7 +115,16 @@ export function detectCallEdges(nodes, lookupMp) {
34
115
  if (!hasPrimary && !hasHooks)
35
116
  continue;
36
117
  // ── Primary names: both third-party and local edges ──────────────
37
- for (const calledName of (primaryNames ?? [])) {
118
+ // Structured callSites carry the argument shape for overload-aware
119
+ // resolution; fall back to bare calls/uses strings when absent.
120
+ const callSites = node.metadata.callSites;
121
+ // Each (name, arity) call site iterates separately — a caller that
122
+ // invokes two overloads of one name gets two edges.
123
+ const primaryCallSites = (callSites && callSites.length > 0)
124
+ ? callSites
125
+ : (primaryNames ?? []).map((name) => ({ name, argCount: -1 }));
126
+ for (const callSite of primaryCallSites) {
127
+ const calledName = callSite.name;
38
128
  // ── Third-party guard ─────────────────────────────────────────
39
129
  // The alias map is keyed by node.filePath (relative) and populated
40
130
  // by importEdges.ts (which runs first).
@@ -120,16 +210,18 @@ export function detectCallEdges(nodes, lookupMp) {
120
210
  }
121
211
  if (!targets || targets.length === 0)
122
212
  continue;
123
- const target = targets.length === 1
124
- ? targets[0]
125
- : closestByPath(targets, node.filePath);
213
+ const { target, matchedBy } = resolveOverloadTarget(targets, node, callSite.argCount >= 0 ? callSite : undefined);
126
214
  if (target.id === node.id)
127
215
  continue; // skip self-reference
216
+ const localEdgeKey = `${node.id}→${target.id}:${edgeType}`;
217
+ if (createdLocalEdges.has(localEdgeKey))
218
+ continue;
219
+ createdLocalEdges.add(localEdgeKey);
128
220
  edges.push({
129
221
  from: node.id,
130
222
  to: target.id,
131
223
  type: edgeType,
132
- metadata: { calledName: resolvedName },
224
+ metadata: { calledName: resolvedName, matchedBy },
133
225
  });
134
226
  if (!resolvedCallsMap.has(node.id))
135
227
  resolvedCallsMap.set(node.id, []);
@@ -198,3 +198,157 @@ describe("detectCallEdges with class methods", () => {
198
198
  deleteFakeRepo(repo);
199
199
  });
200
200
  });
201
+ // ─── Overload-aware resolution (same-name candidates in one file) ─────────────
202
+ describe("detectCallEdges with overloads", () => {
203
+ // Arity disambiguates same-name functions in one file
204
+ it("routes each call to the overload matching its argument count", () => {
205
+ const repo = createFakeRepo({
206
+ "src/fmt.ts": `
207
+ export function formatValue(v: string) { return v.trim(); }
208
+ export function formatValue(v: string, n: number) { return v.repeat(n); }
209
+ export function caller() {
210
+ formatValue("a");
211
+ formatValue("a", 3);
212
+ }
213
+ `,
214
+ });
215
+ const { edges } = detectCalls(repo);
216
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "formatValue");
217
+ const targets = callEdges.map((e) => e.to).sort();
218
+ expect(targets).toEqual([
219
+ "src/fmt.ts::formatValue#1",
220
+ "src/fmt.ts::formatValue#2",
221
+ ]);
222
+ // resolution confidence recorded on the edge
223
+ expect(callEdges.every((e) => e.metadata?.matchedBy === "arity")).toBe(true);
224
+ deleteFakeRepo(repo);
225
+ });
226
+ // Same arity, different param types — literal arg types break the tie
227
+ it("resolves same-arity overloads via argument type tags", () => {
228
+ const repo = createFakeRepo({
229
+ "src/dec.ts": `
230
+ export function decode(input: string, mode: string) { return input; }
231
+ export function decode(input: number, mode: string) { return String(input); }
232
+ export function caller() {
233
+ decode("abc", "utf8");
234
+ decode(42, "utf8");
235
+ }
236
+ `,
237
+ });
238
+ const { edges } = detectCalls(repo);
239
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "decode");
240
+ // first call (string literal) → string overload; second (number literal) → number overload
241
+ const stringTargets = callEdges.filter((e) => e.metadata?.matchedBy === "signature").map((e) => e.to).sort();
242
+ expect(stringTargets.length).toBe(2);
243
+ expect(stringTargets[0]).not.toBe(stringTargets[1]);
244
+ deleteFakeRepo(repo);
245
+ });
246
+ // A caller can invoke two overloads of one name — two distinct edges
247
+ it("emits one edge per overload call site, not one per name", () => {
248
+ const repo = createFakeRepo({
249
+ "src/multi.ts": `
250
+ export function send(x: string) { return x; }
251
+ export function send(x: string, y: string) { return x + y; }
252
+ export function client() {
253
+ send("a");
254
+ send("a", "b");
255
+ }
256
+ `,
257
+ });
258
+ const { edges } = detectCalls(repo);
259
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "send");
260
+ expect(callEdges.length).toBe(2);
261
+ expect(new Set(callEdges.map((e) => e.to)).size).toBe(2);
262
+ deleteFakeRepo(repo);
263
+ });
264
+ // Spread calls have unknowable arity — must NOT confidently pick an overload
265
+ it("falls back to path proximity for spread calls", () => {
266
+ const repo = createFakeRepo({
267
+ "src/spread.ts": `
268
+ export function combine(a: string) { return a; }
269
+ export function combine(a: string, b: string) { return a + b; }
270
+ export function caller(...args: string[]) {
271
+ return combine(...args);
272
+ }
273
+ `,
274
+ });
275
+ const { edges } = detectCalls(repo);
276
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "combine");
277
+ expect(callEdges.length).toBe(1);
278
+ expect(callEdges[0].metadata?.matchedBy).toBe("name");
279
+ deleteFakeRepo(repo);
280
+ });
281
+ // Optional params: a 1-arg call may hit a 2-param overload with optional 2nd
282
+ it("lets optional parameters accept shorter calls via arity fallback", () => {
283
+ const repo = createFakeRepo({
284
+ "src/opt.ts": `
285
+ export function render(template: string, opts?: object) { return template; }
286
+ export function render(template: string, opts: object, mode: string) { return template; }
287
+ export function caller() {
288
+ render("t");
289
+ }
290
+ `,
291
+ });
292
+ const { edges } = detectCalls(repo);
293
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "render");
294
+ // 1-arg call: renderStrict requires 3 → excluded; render (optional) → match
295
+ expect(callEdges.length).toBe(1);
296
+ expect(callEdges[0].to).toBe("src/opt.ts::render#2");
297
+ deleteFakeRepo(repo);
298
+ });
299
+ // Rest params accept any extra arguments
300
+ it("lets rest parameters accept longer calls", () => {
301
+ const repo = createFakeRepo({
302
+ "src/rest.ts": `
303
+ export function sum(first: number, ...rest: number[]) { return first; }
304
+ export function pair(a: number, b: number) { return a; }
305
+ export function caller() {
306
+ sum(1, 2, 3, 4);
307
+ }
308
+ `,
309
+ });
310
+ const { edges } = detectCalls(repo);
311
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "sum");
312
+ expect(callEdges.length).toBe(1);
313
+ expect(callEdges[0].to).toContain("sum");
314
+ deleteFakeRepo(repo);
315
+ });
316
+ // Legacy behavior preserved when no overload shape exists
317
+ it("still resolves plain single-candidate calls without callSites metadata", () => {
318
+ const repo = createFakeRepo({
319
+ "src/plain.ts": `
320
+ export function helper(x: number) { return x; }
321
+ export function caller() { return helper(1); }
322
+ `,
323
+ });
324
+ const { edges } = detectCalls(repo);
325
+ expect(edges).toContainEqual(expect.objectContaining({
326
+ from: "src/plain.ts::caller",
327
+ to: "src/plain.ts::helper",
328
+ type: "CALLS",
329
+ }));
330
+ deleteFakeRepo(repo);
331
+ });
332
+ // Class method overloads resolve through dotted names
333
+ it("resolves class method overloads by arity through dotted names", () => {
334
+ const repo = createFakeRepo({
335
+ "src/svc.ts": `
336
+ export class Repo {
337
+ find(id: string) { return id; }
338
+ find(id: string, depth: number) { return id; }
339
+ }
340
+ export function caller() {
341
+ Repo.find("a");
342
+ Repo.find("a", 2);
343
+ }
344
+ `,
345
+ });
346
+ const { edges } = detectCalls(repo);
347
+ const callEdges = edges.filter((e) => e.type === "CALLS" && e.metadata?.calledName === "Repo.find");
348
+ expect(callEdges.map((e) => e.to).sort()).toEqual([
349
+ "src/svc.ts::Repo.find#1",
350
+ "src/svc.ts::Repo.find#2",
351
+ ]);
352
+ deleteFakeRepo(repo);
353
+ });
354
+ });
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export * from "./pipeline/index.js";
9
9
  export * from "./config/types.js";
10
10
  export { queue } from "./jobs/index.js";
11
11
  export { storage } from "./storage/index.js";
12
- export { resolveConfig, initConfig, maskConfig, writeConfig, resolveAllProviders, setActiveProvider, removeProvider as removeProviderConfig, } from "./config/index.js";
12
+ export { resolveConfig, hasSummarizationConfigured, initConfig, maskConfig, writeConfig, resolveAllProviders, setActiveProvider, removeProvider as removeProviderConfig, } from "./config/index.js";
13
13
  export type { AllProvidersResult } from "./config/index.js";
14
14
  export { readPackageDependencies, categorizeLibrary } from "./graph/thirdPartyLibs.js";
15
15
  export { analyzePipeline } from "./pipeline/index.js";
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export * from "./config/types.js";
11
11
  export { queue } from "./jobs/index.js";
12
12
  export { storage } from "./storage/index.js";
13
13
  // Config helpers
14
- export { resolveConfig, initConfig, maskConfig, writeConfig, resolveAllProviders, setActiveProvider, removeProvider as removeProviderConfig, } from "./config/index.js";
14
+ export { resolveConfig, hasSummarizationConfigured, initConfig, maskConfig, writeConfig, resolveAllProviders, setActiveProvider, removeProvider as removeProviderConfig, } from "./config/index.js";
15
15
  // Pre-scan helpers
16
16
  export { readPackageDependencies, categorizeLibrary } from "./graph/thirdPartyLibs.js";
17
17
  // Pipeline & analysis
@@ -116,7 +116,18 @@ export class InMemoryQueue {
116
116
  this._markCancelled(jobId, true);
117
117
  return true;
118
118
  }
119
- // Running or paused — signal the runner
119
+ // Paused: NO runner loop is active (the summarizer returned at the
120
+ // pause checkpoint), so a cancelRequested flag would sit forever —
121
+ // resumeJob() even clears it. Mark cancelled right here. The
122
+ // checkpoint file stays on disk (cleanedUp=false), so a later
123
+ // summarize resumes from where it stopped.
124
+ if (job.status === "paused") {
125
+ job.cancelRequested = true;
126
+ job.pauseRequested = false;
127
+ this._markCancelled(jobId, false);
128
+ return true;
129
+ }
130
+ // Running — signal the runner (checked between batches)
120
131
  job.cancelRequested = true;
121
132
  job.pauseRequested = false; // clear pause signal if both were set
122
133
  console.log(`🚫 Cancel requested for job ${jobId}`);
@@ -17,7 +17,7 @@
17
17
  // Out of scope (deliberate): INTERFACE / ENUM / STRUCT nodes for JS/TS.
18
18
  import { SyntaxKind } from "ts-morph";
19
19
  import { detectFunctionDirective } from "../directives.js";
20
- import { extractFunctionCalls, extractHookCalls, extractApiCalls, hasErrorHandling, extractThrowStatements, } from "./functions.js";
20
+ import { extractCallsWithSites, hasErrorHandling, extractThrowStatements, } from "./functions.js";
21
21
  import { extractParams, extractReturnTypeAnnotation, extractBareTypeNames, extractReferencedInterfaces, } from "../typeUtils.js";
22
22
  // React lifecycle methods (incl. render) — flagged so consumers can tell
23
23
  // framework-registered callbacks apart from ordinary methods.
@@ -79,6 +79,12 @@ function rewriteThisCalls(calls, className, methodNames) {
79
79
  return `${className}.${rest.replace(/^#/, "")}`;
80
80
  });
81
81
  }
82
+ // Same rewrite as rewriteThisCalls but for structured CallSite records —
83
+ // the name must match the dotted METHOD node names for overload-aware
84
+ // resolution in callEdges.
85
+ function rewriteThisCallSites(callSites, className, methodNames) {
86
+ return callSites.map((cs) => ({ ...cs, name: rewriteThisCalls([cs.name], className, methodNames)[0] }));
87
+ }
82
88
  function extractDecoratorNames(cls) {
83
89
  if (typeof cls.getDecorators !== "function")
84
90
  return [];
@@ -96,9 +102,8 @@ function buildMethodNode(file, className, methodName, method, methodNameSet, fil
96
102
  const filePath = file.getFilePath();
97
103
  const dotted = `${className}.${methodName}`;
98
104
  const typedParams = extractParams(method);
99
- const calls = extractFunctionCalls(method);
100
- const hookCalls = extractHookCalls(method);
101
- const apiCalls = extractApiCalls(method);
105
+ const { calls, callSites: rawSites, hookCalls, apiCalls } = extractCallsWithSites(method);
106
+ const callSites = rewriteThisCallSites(rawSites, className, methodNameSet);
102
107
  const isAsync = typeof method.isAsync === "function" && method.isAsync();
103
108
  const hasErrors = hasErrorHandling(method);
104
109
  const throws = extractThrowStatements(method);
@@ -106,6 +111,7 @@ function buildMethodNode(file, className, methodName, method, methodNameSet, fil
106
111
  const returnType = extractReturnTypeAnnotation(method);
107
112
  const bareTypeNames = extractBareTypeNames([...typedParams.map((p) => p.type), returnType]);
108
113
  const referencedTypes = extractReferencedInterfaces(file, bareTypeNames);
114
+ const isOverloadSignature = typeof method.getBody === "function" ? !method.getBody() : false;
109
115
  return {
110
116
  id: makeId(filePath, dotted),
111
117
  name: dotted,
@@ -121,6 +127,8 @@ function buildMethodNode(file, className, methodName, method, methodNameSet, fil
121
127
  returnType,
122
128
  referencedTypes,
123
129
  calls: rewriteThisCalls(calls, className, methodNameSet),
130
+ callSites,
131
+ ...(isOverloadSignature && { isOverloadSignature: true }),
124
132
  hookCalls,
125
133
  apiCalls,
126
134
  isAsync,
@@ -216,9 +224,20 @@ export function extractClasses(file, fileDirective = null) {
216
224
  if (isHashPrivate)
217
225
  node.metadata.isPrivate = true;
218
226
  nodes.push(node);
227
+ // Overload signatures — getMethods() returns only implementations;
228
+ // signatures hang off getOverloads() and become body-less METHOD nodes.
229
+ for (const overload of m.getOverloads?.() ?? []) {
230
+ const ovNode = buildMethodNode(file, name, methodName, overload, methodNameSet, fileDirective);
231
+ if (isHashPrivate)
232
+ ovNode.metadata.isPrivate = true;
233
+ nodes.push(ovNode);
234
+ }
219
235
  }
220
236
  for (const c of cls.getConstructors()) {
221
237
  nodes.push(buildMethodNode(file, name, "constructor", c, methodNameSet, fileDirective));
238
+ for (const overload of c.getOverloads?.() ?? []) {
239
+ nodes.push(buildMethodNode(file, name, "constructor", overload, methodNameSet, fileDirective));
240
+ }
222
241
  }
223
242
  for (const acc of cls.getGetAccessors()) {
224
243
  const accName = acc.getName();
@@ -1,10 +1,17 @@
1
1
  import { SourceFile } from "ts-morph";
2
- import type { CodeNode } from "../../types.js";
2
+ import type { CallSite, CodeNode } from "../../types.js";
3
3
  import { type RenderingBoundary } from "../directives.js";
4
4
  export declare const JS_BUILTINS: Set<string>;
5
+ export interface ExtractedCallData {
6
+ calls: string[];
7
+ callSites: CallSite[];
8
+ hookCalls: string[];
9
+ apiCalls: string[];
10
+ dependencyNames: string[];
11
+ contextRefs: string[];
12
+ }
13
+ export declare function extractCallsWithSites(node: any): ExtractedCallData;
5
14
  export declare function extractFunctionCalls(node: any): string[];
6
- export declare function extractHookCalls(node: any): string[];
7
- export declare function extractApiCalls(node: any): string[];
8
15
  export declare function hasErrorHandling(node: any): boolean;
9
16
  export declare function extractThrowStatements(node: any): boolean;
10
17
  export declare function extractFunctions(file: SourceFile, fileDirective?: RenderingBoundary): CodeNode[];