@memlab/mcp-server 2.0.3 → 2.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 (133) hide show
  1. package/README.md +13 -13
  2. package/dist/heap-state.d.ts +12 -1
  3. package/dist/heap-state.d.ts.map +1 -1
  4. package/dist/heap-state.js +10 -4
  5. package/dist/heap-state.js.map +1 -1
  6. package/dist/index.js +27 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/tools/aggregate-nodes.d.ts.map +1 -1
  9. package/dist/tools/aggregate-nodes.js +2 -2
  10. package/dist/tools/aggregate-nodes.js.map +1 -1
  11. package/dist/tools/auto-investigate.d.ts +12 -0
  12. package/dist/tools/auto-investigate.d.ts.map +1 -0
  13. package/dist/tools/auto-investigate.js +683 -0
  14. package/dist/tools/auto-investigate.js.map +1 -0
  15. package/dist/tools/cache-analysis.d.ts +12 -0
  16. package/dist/tools/cache-analysis.d.ts.map +1 -0
  17. package/dist/tools/cache-analysis.js +341 -0
  18. package/dist/tools/cache-analysis.js.map +1 -0
  19. package/dist/tools/check-health.d.ts +12 -0
  20. package/dist/tools/check-health.d.ts.map +1 -0
  21. package/dist/tools/check-health.js +289 -0
  22. package/dist/tools/check-health.js.map +1 -0
  23. package/dist/tools/class-histogram.d.ts.map +1 -1
  24. package/dist/tools/class-histogram.js +70 -14
  25. package/dist/tools/class-histogram.js.map +1 -1
  26. package/dist/tools/closure-inspection.d.ts.map +1 -1
  27. package/dist/tools/closure-inspection.js +5 -5
  28. package/dist/tools/closure-inspection.js.map +1 -1
  29. package/dist/tools/detached-dom.d.ts.map +1 -1
  30. package/dist/tools/detached-dom.js +151 -5
  31. package/dist/tools/detached-dom.js.map +1 -1
  32. package/dist/tools/diff-snapshots.d.ts +12 -0
  33. package/dist/tools/diff-snapshots.d.ts.map +1 -0
  34. package/dist/tools/diff-snapshots.js +224 -0
  35. package/dist/tools/diff-snapshots.js.map +1 -0
  36. package/dist/tools/dominator-subtree.d.ts.map +1 -1
  37. package/dist/tools/dominator-subtree.js +29 -4
  38. package/dist/tools/dominator-subtree.js.map +1 -1
  39. package/dist/tools/duplicated-strings.d.ts.map +1 -1
  40. package/dist/tools/duplicated-strings.js +62 -9
  41. package/dist/tools/duplicated-strings.js.map +1 -1
  42. package/dist/tools/eval.d.ts.map +1 -1
  43. package/dist/tools/eval.js +111 -7
  44. package/dist/tools/eval.js.map +1 -1
  45. package/dist/tools/event-listener-leaks.d.ts +12 -0
  46. package/dist/tools/event-listener-leaks.d.ts.map +1 -0
  47. package/dist/tools/event-listener-leaks.js +266 -0
  48. package/dist/tools/event-listener-leaks.js.map +1 -0
  49. package/dist/tools/find-by-property.d.ts.map +1 -1
  50. package/dist/tools/find-by-property.js +38 -4
  51. package/dist/tools/find-by-property.js.map +1 -1
  52. package/dist/tools/find-nodes-by-class.d.ts.map +1 -1
  53. package/dist/tools/find-nodes-by-class.js +4 -4
  54. package/dist/tools/find-nodes-by-class.js.map +1 -1
  55. package/dist/tools/for-each.d.ts.map +1 -1
  56. package/dist/tools/for-each.js +2 -2
  57. package/dist/tools/for-each.js.map +1 -1
  58. package/dist/tools/get-node.d.ts.map +1 -1
  59. package/dist/tools/get-node.js +2 -2
  60. package/dist/tools/get-node.js.map +1 -1
  61. package/dist/tools/get-property.d.ts.map +1 -1
  62. package/dist/tools/get-property.js +4 -4
  63. package/dist/tools/get-property.js.map +1 -1
  64. package/dist/tools/get-references.d.ts.map +1 -1
  65. package/dist/tools/get-references.js +11 -5
  66. package/dist/tools/get-references.js.map +1 -1
  67. package/dist/tools/get-referrers.d.ts.map +1 -1
  68. package/dist/tools/get-referrers.js +11 -5
  69. package/dist/tools/get-referrers.js.map +1 -1
  70. package/dist/tools/global-variables.d.ts.map +1 -1
  71. package/dist/tools/global-variables.js +10 -6
  72. package/dist/tools/global-variables.js.map +1 -1
  73. package/dist/tools/largest-objects.d.ts.map +1 -1
  74. package/dist/tools/largest-objects.js +78 -8
  75. package/dist/tools/largest-objects.js.map +1 -1
  76. package/dist/tools/load-snapshot.d.ts.map +1 -1
  77. package/dist/tools/load-snapshot.js +118 -4
  78. package/dist/tools/load-snapshot.js.map +1 -1
  79. package/dist/tools/object-shape.d.ts.map +1 -1
  80. package/dist/tools/object-shape.js +66 -47
  81. package/dist/tools/object-shape.js.map +1 -1
  82. package/dist/tools/pinch-points.d.ts +12 -0
  83. package/dist/tools/pinch-points.d.ts.map +1 -0
  84. package/dist/tools/pinch-points.js +124 -0
  85. package/dist/tools/pinch-points.js.map +1 -0
  86. package/dist/tools/quick-diagnosis.d.ts +12 -0
  87. package/dist/tools/quick-diagnosis.d.ts.map +1 -0
  88. package/dist/tools/quick-diagnosis.js +213 -0
  89. package/dist/tools/quick-diagnosis.js.map +1 -0
  90. package/dist/tools/referrer-summary.d.ts +12 -0
  91. package/dist/tools/referrer-summary.d.ts.map +1 -0
  92. package/dist/tools/referrer-summary.js +119 -0
  93. package/dist/tools/referrer-summary.js.map +1 -0
  94. package/dist/tools/reports.d.ts.map +1 -1
  95. package/dist/tools/reports.js +5 -5
  96. package/dist/tools/reports.js.map +1 -1
  97. package/dist/tools/retainer-summary.d.ts +12 -0
  98. package/dist/tools/retainer-summary.d.ts.map +1 -0
  99. package/dist/tools/retainer-summary.js +307 -0
  100. package/dist/tools/retainer-summary.js.map +1 -0
  101. package/dist/tools/retainer-trace.d.ts.map +1 -1
  102. package/dist/tools/retainer-trace.js +88 -14
  103. package/dist/tools/retainer-trace.js.map +1 -1
  104. package/dist/tools/search-nodes.d.ts.map +1 -1
  105. package/dist/tools/search-nodes.js +2 -2
  106. package/dist/tools/search-nodes.js.map +1 -1
  107. package/dist/tools/shape-histogram.d.ts +12 -0
  108. package/dist/tools/shape-histogram.d.ts.map +1 -0
  109. package/dist/tools/shape-histogram.js +119 -0
  110. package/dist/tools/shape-histogram.js.map +1 -0
  111. package/dist/tools/sliced-strings.d.ts +12 -0
  112. package/dist/tools/sliced-strings.d.ts.map +1 -0
  113. package/dist/tools/sliced-strings.js +126 -0
  114. package/dist/tools/sliced-strings.js.map +1 -0
  115. package/dist/tools/snapshot-summary.d.ts.map +1 -1
  116. package/dist/tools/snapshot-summary.js +42 -3
  117. package/dist/tools/snapshot-summary.js.map +1 -1
  118. package/dist/tools/stale-collections.d.ts.map +1 -1
  119. package/dist/tools/stale-collections.js +30 -6
  120. package/dist/tools/stale-collections.js.map +1 -1
  121. package/dist/tools/string-patterns.d.ts +12 -0
  122. package/dist/tools/string-patterns.d.ts.map +1 -0
  123. package/dist/tools/string-patterns.js +115 -0
  124. package/dist/tools/string-patterns.js.map +1 -0
  125. package/dist/tools/trace-dominators.d.ts +12 -0
  126. package/dist/tools/trace-dominators.d.ts.map +1 -0
  127. package/dist/tools/trace-dominators.js +237 -0
  128. package/dist/tools/trace-dominators.js.map +1 -0
  129. package/dist/utils.d.ts +9 -1
  130. package/dist/utils.d.ts.map +1 -1
  131. package/dist/utils.js +67 -5
  132. package/dist/utils.js.map +1 -1
  133. package/package.json +1 -1
@@ -9,15 +9,25 @@
9
9
  */
10
10
  import { z } from 'zod';
11
11
  import { getSnapshot } from '../heap-state.js';
12
- import { formatBytes, errorResult, textResult } from '../utils.js';
12
+ import { formatBytes, errorResult, toolResult } from '../utils.js';
13
13
  export function registerDuplicatedStrings(server) {
14
- server.tool('memlab_duplicated_strings', 'Find duplicated string instances in the heap. Shows strings that appear multiple times, ranked by total retained size — a common source of memory waste.', {
14
+ server.tool('memlab_duplicated_strings', 'Find duplicated string instances in the heap. Shows strings that appear multiple times, ranked by total retained size — a common source of memory waste. Use after memlab_class_histogram shows high string counts.', {
15
15
  limit: z
16
16
  .number()
17
17
  .optional()
18
18
  .default(15)
19
19
  .describe('Maximum number of results (default 15)'),
20
- }, async ({ limit }) => {
20
+ min_count: z
21
+ .number()
22
+ .optional()
23
+ .default(2)
24
+ .describe('Minimum number of copies to include (default 2). Increase to focus on heavily duplicated strings (e.g., 100).'),
25
+ include_node_ids: z
26
+ .boolean()
27
+ .optional()
28
+ .default(false)
29
+ .describe('Include example node IDs in the output for follow-up with retainer_summary. Omitted by default to save ~20-30 tokens per entry.'),
30
+ }, async ({ limit, min_count, include_node_ids }) => {
21
31
  try {
22
32
  const snapshot = getSnapshot();
23
33
  // Build frequency map: string value -> { count, totalSize, exampleIds }
@@ -48,9 +58,8 @@ export function registerDuplicatedStrings(server) {
48
58
  });
49
59
  }
50
60
  });
51
- // Filter to only duplicated strings (count > 1) and rank by total size
52
61
  const duplicated = Array.from(stringMap.entries())
53
- .filter(([, stats]) => stats.count > 1)
62
+ .filter(([, stats]) => stats.count >= min_count)
54
63
  .sort((a, b) => b[1].total_size - a[1].total_size)
55
64
  .slice(0, limit)
56
65
  .map(([value, stats]) => ({
@@ -59,16 +68,60 @@ export function registerDuplicatedStrings(server) {
59
68
  total_size: stats.total_size,
60
69
  total_size_formatted: formatBytes(stats.total_size),
61
70
  example_node_ids: stats.example_node_ids,
71
+ field_context: null,
62
72
  }));
63
73
  if (duplicated.length === 0) {
64
- return textResult('No duplicated strings found.');
74
+ return toolResult('No duplicated strings found.');
75
+ }
76
+ // For top entries, sample referrer objects to show field context
77
+ for (const d of duplicated.slice(0, 10)) {
78
+ const propCounts = new Map();
79
+ for (const nodeId of d.example_node_ids) {
80
+ const node = snapshot.getNodeById(nodeId);
81
+ if (!node)
82
+ continue;
83
+ for (const ref of node.referrers) {
84
+ if (ref.type === 'property' || ref.type === 'context') {
85
+ const propName = String(ref.name_or_index);
86
+ const entry = propCounts.get(propName);
87
+ if (entry) {
88
+ entry.count++;
89
+ }
90
+ else {
91
+ propCounts.set(propName, {
92
+ count: 1,
93
+ ownerName: ref.fromNode.name,
94
+ });
95
+ }
96
+ }
97
+ }
98
+ }
99
+ if (propCounts.size > 0) {
100
+ const topProp = [...propCounts.entries()].sort((a, b) => b[1].count - a[1].count)[0];
101
+ d.field_context = `.${topProp[0]} on \`${topProp[1].ownerName}\` instances`;
102
+ }
65
103
  }
66
104
  const lines = duplicated.map((d, i) => {
67
105
  const val = d.value.length > 80 ? d.value.slice(0, 80) + '...' : d.value;
68
- const nodeIds = d.example_node_ids.map(id => `@${id}`).join(', ');
69
- return `${i + 1}. "${val}" x ${d.count} copies, ${d.total_size_formatted} total (nodes: ${nodeIds})`;
106
+ const nodeIdsPart = include_node_ids
107
+ ? ` (nodes: ${d.example_node_ids.map(id => `@${id}`).join(', ')})`
108
+ : '';
109
+ const context = d.field_context
110
+ ? `\n commonly held as: ${d.field_context}`
111
+ : '';
112
+ return `${i + 1}. "${val}" x ${d.count} copies, ${d.total_size_formatted} total${nodeIdsPart}${context}`;
70
113
  });
71
- return textResult(`Duplicated strings (${duplicated.length} entries):\n\n${lines.join('\n')}`);
114
+ const hasHeavyDups = duplicated.some(d => d.count >= 1000);
115
+ const suggestions = [];
116
+ if (hasHeavyDups) {
117
+ suggestions.push('**Suggested action:** Heavily duplicated strings often come from `JSON.parse()` or API responses. ' +
118
+ 'Consider string interning with a `Map<string, string>` pool applied at ingestion time, ' +
119
+ 'or deduplicating at the data source.');
120
+ }
121
+ const body = `Duplicated strings (${duplicated.length} entries):\n\n${lines.join('\n')}`;
122
+ return toolResult(suggestions.length > 0
123
+ ? `${body}\n\n---\n\n${suggestions.join('\n')}`
124
+ : body);
72
125
  }
73
126
  catch (err) {
74
127
  return errorResult(err);
@@ -1 +1 @@
1
- {"version":3,"file":"duplicated-strings.js","sourceRoot":"","sources":["../../src/tools/duplicated-strings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"duplicated-strings.js","sourceRoot":"","sources":["../../src/tools/duplicated-strings.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../src/tools/eval.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AA0BvE,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwHpD"}
1
+ {"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../../src/tools/eval.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAmHvE,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4IpD"}
@@ -12,27 +12,131 @@ import vm from 'node:vm';
12
12
  import memlabCore from '@memlab/core';
13
13
  const { utils } = memlabCore;
14
14
  import { getSnapshot } from '../heap-state.js';
15
- import { errorResult, textResult, serializeNodeSummary, serializeNodeDetail, formatBytes, formatNumber, markdownTable, isNodeWorthInspecting, filterLargestObjects, queryNodes, } from '../utils.js';
15
+ import { errorResult, toolResult, serializeNodeSummary, serializeNodeDetail, formatBytes, formatNumber, markdownTable, isNodeWorthInspecting, filterLargestObjects, queryNodes, } from '../utils.js';
16
16
  const MAX_OUTPUT_SIZE = 50 * 1024; // 50KB
17
17
  function truncate(str, max) {
18
18
  if (str.length <= max)
19
19
  return str;
20
20
  return str.slice(0, max) + '\n... [truncated, output exceeded 50KB]';
21
21
  }
22
+ const NODE_PROPERTY_ALIASES = {
23
+ retained_size: 'retainedSize',
24
+ referrer_count: 'numOfReferrers',
25
+ };
26
+ function wrapNode(node) {
27
+ if (node == null)
28
+ return node;
29
+ return new Proxy(node, {
30
+ get(target, prop, receiver) {
31
+ if (typeof prop === 'string' && prop in NODE_PROPERTY_ALIASES) {
32
+ return target[NODE_PROPERTY_ALIASES[prop]];
33
+ }
34
+ const val = Reflect.get(target, prop, receiver);
35
+ if (prop === 'references' || prop === 'referrers') {
36
+ return wrapEdgeIterable(val);
37
+ }
38
+ if (prop === 'dominatorNode' || prop === 'pathEdge') {
39
+ if (val != null && typeof val === 'object' && 'fromNode' in val) {
40
+ return wrapEdge(val);
41
+ }
42
+ if (val != null && typeof val === 'object' && 'id' in val) {
43
+ return wrapNode(val);
44
+ }
45
+ }
46
+ return val;
47
+ },
48
+ });
49
+ }
50
+ function wrapEdge(edge) {
51
+ if (edge == null)
52
+ return edge;
53
+ return new Proxy(edge, {
54
+ get(target, prop, receiver) {
55
+ const val = Reflect.get(target, prop, receiver);
56
+ if (prop === 'toNode' || prop === 'fromNode') {
57
+ return wrapNode(val);
58
+ }
59
+ return val;
60
+ },
61
+ });
62
+ }
63
+ function wrapEdgeIterable(iterable) {
64
+ if (iterable == null)
65
+ return iterable;
66
+ const original = iterable;
67
+ return {
68
+ [Symbol.iterator]() {
69
+ const iter = original[Symbol.iterator]();
70
+ return {
71
+ next() {
72
+ const result = iter.next();
73
+ if (result.done)
74
+ return result;
75
+ return { done: false, value: wrapEdge(result.value) };
76
+ },
77
+ };
78
+ },
79
+ };
80
+ }
81
+ function wrapSnapshot(snapshot) {
82
+ return new Proxy(snapshot, {
83
+ get(target, prop, receiver) {
84
+ if (prop === 'getNodeById') {
85
+ const orig = target.getNodeById.bind(target);
86
+ return (id) => wrapNode(orig(id));
87
+ }
88
+ if (prop === 'nodes') {
89
+ const nodes = Reflect.get(target, prop, receiver);
90
+ return new Proxy(nodes, {
91
+ get(nodesTarget, nodesProp, nodesReceiver) {
92
+ if (nodesProp === 'forEach') {
93
+ const origForEach = nodesTarget.forEach.bind(nodesTarget);
94
+ return (cb) => {
95
+ origForEach((node) => cb(wrapNode(node)));
96
+ };
97
+ }
98
+ return Reflect.get(nodesTarget, nodesProp, nodesReceiver);
99
+ },
100
+ });
101
+ }
102
+ return Reflect.get(target, prop, receiver);
103
+ },
104
+ });
105
+ }
22
106
  export function registerEval(server) {
23
107
  server.tool('memlab_eval', 'Execute arbitrary JavaScript code against the loaded heap snapshot. ' +
24
108
  'The code runs in a sandboxed VM with access to `snapshot` (IHeapSnapshot), ' +
25
109
  '`utils` (@memlab/core utils), and `helpers` (plugin utility functions). ' +
26
110
  'Assign your result to the `result` variable. ' +
27
- 'No require/process/fs/network access. Read-only heap analysis only.', {
111
+ 'No require/process/fs/network access. Read-only heap analysis only.\n\n' +
112
+ '**IHeapNode API:** Each node has: `.id`, `.name`, `.type`, `.self_size`, ' +
113
+ '`.retained_size` (alias: `.retainedSize`), `.edge_count`, `.is_detached`, ' +
114
+ '`.referrer_count` (alias: `.numOfReferrers`), `.isString`, `.hasPathEdge`, ' +
115
+ '`.pathEdge`, `.dominatorNode`, `.location` (script_id/line/column).\n' +
116
+ '**Traversal:** Use `node.references` (outgoing edges, iterable with for-of) and ' +
117
+ '`node.referrers` (incoming edges, iterable with for-of). ' +
118
+ 'Each edge has: `.name_or_index`, `.type` (property/element/context/internal/hidden/shortcut), ' +
119
+ '`.toNode`, `.fromNode`.\n' +
120
+ '**Iterating all nodes:** `snapshot.nodes.forEach(node => { ... })` — NOT for-of.\n' +
121
+ '**Get node by ID:** `snapshot.getNodeById(id)` returns IHeapNode or null.\n' +
122
+ '**String values:** `node.toStringNode()?.stringValue` for string nodes.\n\n' +
123
+ '**Example — inspect Map entries:**\n' +
124
+ '```\nconst map = snapshot.getNodeById(12345);\nconst entries = [];\n' +
125
+ 'for (const edge of map.references) {\n' +
126
+ ' if (edge.name_or_index === "table") {\n' +
127
+ ' for (const te of edge.toNode.references) {\n' +
128
+ ' entries.push({name: te.toNode.name, type: te.toNode.type});\n' +
129
+ ' }\n }\n}\nresult = entries.slice(0, 10);\n```', {
28
130
  code: z
29
131
  .string()
30
132
  .describe('JavaScript code to execute. Must assign the output to a `result` variable. ' +
31
- 'Available globals: snapshot (IHeapSnapshot), utils (@memlab/core utils with ' +
32
- 'aggregateDominatorMetrics, isFiberNode, isDetachedDOMNode, etc.), ' +
133
+ 'Available globals: snapshot (IHeapSnapshot — use .nodes.forEach(), .getNodeById(), .edges.forEach()), ' +
134
+ 'utils (@memlab/core utils with aggregateDominatorMetrics, isFiberNode, isDetachedDOMNode), ' +
33
135
  'helpers ({ serializeNodeSummary, serializeNodeDetail, formatBytes, formatNumber, ' +
34
136
  'markdownTable, isNodeWorthInspecting, filterLargestObjects, queryNodes }), ' +
35
- 'and standard JS built-ins (Array, Object, Map, Set, JSON, Math, RegExp, console).'),
137
+ 'and standard JS built-ins. ' +
138
+ 'Node traversal: use node.references (outgoing) and node.referrers (incoming) with for-of. ' +
139
+ 'Edge properties: .name_or_index, .type, .toNode, .fromNode.'),
36
140
  timeout_ms: z
37
141
  .number()
38
142
  .optional()
@@ -59,7 +163,7 @@ export function registerEval(server) {
59
163
  queryNodes,
60
164
  };
61
165
  const sandbox = {
62
- snapshot,
166
+ snapshot: wrapSnapshot(snapshot),
63
167
  utils,
64
168
  helpers,
65
169
  console: capturedConsole,
@@ -105,7 +209,7 @@ export function registerEval(server) {
105
209
  const consolePart = truncate(consoleOutput.join('\n'), MAX_OUTPUT_SIZE - output.length > 1024 ? 4096 : 1024);
106
210
  output += '\n\n--- console output ---\n' + consolePart;
107
211
  }
108
- return textResult(output);
212
+ return toolResult(output);
109
213
  }
110
214
  catch (err) {
111
215
  if (err instanceof Error &&
@@ -1 +1 @@
1
- {"version":3,"file":"eval.js","sourceRoot":"","sources":["../../src/tools/eval.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eval.js","sourceRoot":"","sources":["../../src/tools/eval.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,MAAM,EAAC,KAAK,EAAC,GAAG,UAAU,CAAC;AAC3B,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO;AAE1C,SAAS,QAAQ,CAAC,GAAW,EAAE,GAAW;IACxC,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAClC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,yCAAyC,CAAC;AACvE,CAAC;AAED,MAAM,qBAAqB,GAA2B;IACpD,aAAa,EAAE,cAAc;IAC7B,cAAc,EAAE,gBAAgB;CACjC,CAAC;AAEF,SAAS,QAAQ,CAAC,IAAa;IAC7B,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,IAAI,KAAK,CAAC,IAAc,EAAE;QAC/B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,qBAAqB,EAAE,CAAC;gBAC9D,OAAQ,MAAkC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAChD,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBAClD,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;YACD,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBACpD,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;oBAChE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;gBACD,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;oBAC1D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,IAAa;IAC7B,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,IAAI,KAAK,CAAC,IAAc,EAAE;QAC/B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAChD,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC7C,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;YACvB,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAiB;IACzC,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,QAAQ,CAAC;IACtC,MAAM,QAAQ,GAAG,QAA6B,CAAC;IAC/C,OAAO;QACL,CAAC,MAAM,CAAC,QAAQ,CAAC;YACf,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,OAAO;gBACL,IAAI;oBACF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;oBAC3B,IAAI,MAAM,CAAC,IAAI;wBAAE,OAAO,MAAM,CAAC;oBAC/B,OAAO,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAC,CAAC;gBACtD,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,QAAiB;IACrC,OAAO,IAAI,KAAK,CAAC,QAAkB,EAAE;QACnC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACxB,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;gBAC3B,MAAM,IAAI,GACR,MACD,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3B,OAAO,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC;YACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAClD,OAAO,IAAI,KAAK,CAAC,KAAe,EAAE;oBAChC,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa;wBACvC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;4BAC5B,MAAM,WAAW,GACf,WACD,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;4BAC5B,OAAO,CAAC,EAA2B,EAAE,EAAE;gCACrC,WAAW,CAAC,CAAC,IAAa,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;4BACrD,CAAC,CAAC;wBACJ,CAAC;wBACD,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;oBAC5D,CAAC;iBACF,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAiB;IAC5C,MAAM,CAAC,IAAI,CACT,aAAa,EACb,sEAAsE;QACpE,6EAA6E;QAC7E,0EAA0E;QAC1E,+CAA+C;QAC/C,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,6EAA6E;QAC7E,uEAAuE;QACvE,kFAAkF;QAClF,2DAA2D;QAC3D,gGAAgG;QAChG,2BAA2B;QAC3B,oFAAoF;QACpF,6EAA6E;QAC7E,6EAA6E;QAC7E,sCAAsC;QACtC,sEAAsE;QACtE,wCAAwC;QACxC,2CAA2C;QAC3C,kDAAkD;QAClD,qEAAqE;QACrE,oDAAoD,EACtD;QACE,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,CACP,6EAA6E;YAC3E,wGAAwG;YACxG,6FAA6F;YAC7F,mFAAmF;YACnF,6EAA6E;YAC7E,6BAA6B;YAC7B,4FAA4F;YAC5F,6DAA6D,CAChE;QACH,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,OAAO,CAAC,KAAK,CAAC;aACd,QAAQ,CAAC,mDAAmD,CAAC;KACjE,EACD,KAAK,EAAE,EAAC,IAAI,EAAE,UAAU,EAAC,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;YAE/B,MAAM,aAAa,GAAa,EAAE,CAAC;YACnC,MAAM,eAAe,GAAG;gBACtB,GAAG,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAC1B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChD,IAAI,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAC3B,aAAa,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC5D,KAAK,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAC5B,aAAa,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7D,IAAI,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAC3B,aAAa,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aAC7D,CAAC;YAEF,MAAM,OAAO,GAAG;gBACd,oBAAoB;gBACpB,mBAAmB;gBACnB,WAAW;gBACX,YAAY;gBACZ,aAAa;gBACb,qBAAqB;gBACrB,oBAAoB;gBACpB,UAAU;aACX,CAAC;YAEF,MAAM,OAAO,GAAG;gBACd,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC;gBAChC,KAAK;gBACL,OAAO;gBACP,OAAO,EAAE,eAAe;gBACxB,MAAM,EAAE,SAAoB;gBAC5B,sBAAsB;gBACtB,KAAK;gBACL,MAAM;gBACN,GAAG;gBACH,GAAG;gBACH,IAAI;gBACJ,IAAI;gBACJ,MAAM;gBACN,MAAM;gBACN,MAAM;gBACN,OAAO;gBACP,IAAI;gBACJ,KAAK;gBACL,SAAS;gBACT,UAAU;gBACV,OAAO;gBACP,OAAO;gBACP,MAAM;gBACN,QAAQ;gBACR,UAAU;gBACV,KAAK;gBACL,QAAQ;gBACR,QAAQ;gBACR,GAAG;gBACH,SAAS;aACV,CAAC;YAEF,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAC,QAAQ,EAAE,aAAa,EAAC,CAAC,CAAC;YAC9D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,EAAC,OAAO,EAAE,UAAU,EAAC,CAAC,CAAC;YAEpD,IAAI,MAAc,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,WAAW,CAAC;YAClE,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClC,CAAC;YAED,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;YAE3C,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,WAAW,GAAG,QAAQ,CAC1B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EACxB,eAAe,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CACrD,CAAC;gBACF,MAAM,IAAI,8BAA8B,GAAG,WAAW,CAAC;YACzD,CAAC;YAED,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IACE,GAAG,YAAY,KAAK;gBACpB,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAClD,CAAC;gBACD,OAAO,WAAW,CAChB,IAAI,KAAK,CAAC,6BAA6B,UAAU,IAAI,CAAC,CACvD,CAAC;YACJ,CAAC;YACD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ export declare function registerEventListenerLeaks(server: McpServer): void;
12
+ //# sourceMappingURL=event-listener-leaks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-listener-leaks.d.ts","sourceRoot":"","sources":["../../src/tools/event-listener-leaks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AA0JvE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2LlE"}
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import { z } from 'zod';
11
+ import { getSnapshot } from '../heap-state.js';
12
+ import { formatBytes, formatNumber, markdownTable, truncateNodeName, errorResult, toolResult, } from '../utils.js';
13
+ const EVENT_PROPERTY_NAMES = new Set([
14
+ '_events',
15
+ '_listeners',
16
+ 'eventMap',
17
+ '__events',
18
+ '_eventHandlers',
19
+ 'listeners',
20
+ '__listeners',
21
+ ]);
22
+ function inspectEventContainer(container) {
23
+ const events = [];
24
+ for (const edge of container.references) {
25
+ const eventName = String(edge.name_or_index);
26
+ if (edge.toNode.type === 'hidden' || edge.toNode.id <= 3)
27
+ continue;
28
+ const listeners = [];
29
+ if (edge.toNode.type === 'object' && edge.toNode.name === 'Array') {
30
+ for (const arrEdge of edge.toNode.references) {
31
+ if (arrEdge.type !== 'element')
32
+ continue;
33
+ const entry = arrEdge.toNode;
34
+ if (entry.id <= 3)
35
+ continue;
36
+ if (entry.type === 'closure') {
37
+ listeners.push({
38
+ callbackId: entry.id,
39
+ callbackName: entry.name,
40
+ contextId: null,
41
+ contextName: null,
42
+ });
43
+ }
44
+ else if (entry.type === 'object') {
45
+ let callbackId = 0;
46
+ let callbackName = '';
47
+ let contextId = null;
48
+ let contextName = null;
49
+ for (const propEdge of entry.references) {
50
+ const pName = String(propEdge.name_or_index);
51
+ if (pName === 'callback' ||
52
+ pName === 'fn' ||
53
+ pName === 'handler' ||
54
+ pName === 'listener') {
55
+ callbackId = propEdge.toNode.id;
56
+ callbackName = propEdge.toNode.name;
57
+ }
58
+ if (pName === 'context' ||
59
+ pName === 'ctx' ||
60
+ pName === 'this' ||
61
+ pName === 'target') {
62
+ contextId = propEdge.toNode.id;
63
+ contextName = propEdge.toNode.name;
64
+ }
65
+ }
66
+ if (callbackId > 0) {
67
+ listeners.push({
68
+ callbackId,
69
+ callbackName,
70
+ contextId,
71
+ contextName,
72
+ });
73
+ }
74
+ }
75
+ }
76
+ }
77
+ else if (edge.toNode.type === 'closure') {
78
+ listeners.push({
79
+ callbackId: edge.toNode.id,
80
+ callbackName: edge.toNode.name,
81
+ contextId: null,
82
+ contextName: null,
83
+ });
84
+ }
85
+ if (listeners.length > 0) {
86
+ events.push({ eventName, listeners });
87
+ }
88
+ }
89
+ return events;
90
+ }
91
+ function countZombies(listeners, snapshot) {
92
+ let zombies = 0;
93
+ for (const l of listeners) {
94
+ if (l.contextId == null)
95
+ continue;
96
+ const ctxNode = snapshot.getNodeById(l.contextId);
97
+ if (!ctxNode)
98
+ continue;
99
+ if (ctxNode.numOfReferrers <= 2) {
100
+ zombies++;
101
+ }
102
+ }
103
+ return zombies;
104
+ }
105
+ function countDuplicateCallbacks(listeners) {
106
+ const callbackContextPairs = new Map();
107
+ for (const l of listeners) {
108
+ const key = `${l.callbackName}\0${l.contextName ?? ''}`;
109
+ callbackContextPairs.set(key, (callbackContextPairs.get(key) ?? 0) + 1);
110
+ }
111
+ let duplicates = 0;
112
+ for (const count of callbackContextPairs.values()) {
113
+ if (count > 1)
114
+ duplicates += count - 1;
115
+ }
116
+ return duplicates;
117
+ }
118
+ export function registerEventListenerLeaks(server) {
119
+ server.tool('memlab_event_listener_leaks', 'Detect EventEmitter-style listener accumulation — the #1 cause of memory leaks in ' +
120
+ 'Backbone/Node.js/EventEmitter-based apps. Scans for objects with _events/_listeners ' +
121
+ 'properties and flags: arrays with >N entries (accumulation), entries where the ' +
122
+ 'context/ctx object has no other referrers (zombie listeners from missed .off() calls), ' +
123
+ 'and multiple entries with the same callback but different contexts (mount/unmount leaks). ' +
124
+ 'Use after memlab_auto_investigate flags subscription accumulation.', {
125
+ min_listeners: z
126
+ .number()
127
+ .optional()
128
+ .default(50)
129
+ .describe('Minimum total listener count on a single host to report (default 50)'),
130
+ limit: z
131
+ .number()
132
+ .optional()
133
+ .default(15)
134
+ .describe('Maximum number of hosts to return (default 15)'),
135
+ check_zombies: z
136
+ .boolean()
137
+ .optional()
138
+ .default(true)
139
+ .describe('Check for zombie listeners whose context objects have no other referrers (default true). Can be slow on very large snapshots.'),
140
+ }, async ({ min_listeners, limit, check_zombies }) => {
141
+ try {
142
+ const snapshot = getSnapshot();
143
+ const accumulations = [];
144
+ snapshot.nodes.forEach(node => {
145
+ if (node.id <= 3)
146
+ return;
147
+ if (node.type !== 'object')
148
+ return;
149
+ for (const edge of node.references) {
150
+ const eName = String(edge.name_or_index);
151
+ if (!EVENT_PROPERTY_NAMES.has(eName))
152
+ continue;
153
+ if (edge.toNode.id <= 3)
154
+ continue;
155
+ const events = inspectEventContainer(edge.toNode);
156
+ const totalListeners = events.reduce((s, e) => s + e.listeners.length, 0);
157
+ if (totalListeners < min_listeners)
158
+ continue;
159
+ const allListeners = events.flatMap(e => e.listeners);
160
+ const zombieCount = check_zombies
161
+ ? countZombies(allListeners, snapshot)
162
+ : 0;
163
+ const duplicateCallbackCount = countDuplicateCallbacks(allListeners);
164
+ const entry = {
165
+ hostNodeId: node.id,
166
+ hostName: node.name,
167
+ hostType: node.type,
168
+ eventPropertyName: eName,
169
+ eventContainerId: edge.toNode.id,
170
+ totalListeners,
171
+ eventBreakdown: events
172
+ .sort((a, b) => b.listeners.length - a.listeners.length)
173
+ .slice(0, 10)
174
+ .map(e => ({
175
+ eventName: e.eventName,
176
+ listenerCount: e.listeners.length,
177
+ sampleListeners: e.listeners.slice(0, 3),
178
+ })),
179
+ zombieCount,
180
+ duplicateCallbackCount,
181
+ totalRetainedSize: node.retainedSize,
182
+ };
183
+ let inserted = false;
184
+ for (let i = 0; i < accumulations.length; i++) {
185
+ if (totalListeners > accumulations[i].totalListeners) {
186
+ accumulations.splice(i, 0, entry);
187
+ inserted = true;
188
+ break;
189
+ }
190
+ }
191
+ if (!inserted)
192
+ accumulations.push(entry);
193
+ if (accumulations.length > limit)
194
+ accumulations.length = limit;
195
+ break;
196
+ }
197
+ });
198
+ if (accumulations.length === 0) {
199
+ return toolResult(`No event listener accumulation found with >= ${min_listeners} listeners. ` +
200
+ `Try lowering min_listeners or check \`memlab_find_by_property\` with property_name="_events".`);
201
+ }
202
+ const lines = [
203
+ `## Event Listener Leak Detection`,
204
+ `Found ${accumulations.length} object(s) with potential listener accumulation`,
205
+ '',
206
+ ];
207
+ const headers = [
208
+ 'Host',
209
+ 'Class',
210
+ 'Property',
211
+ 'Listeners',
212
+ 'Zombies',
213
+ 'Duplicates',
214
+ 'Retained',
215
+ ];
216
+ const rightCols = new Set([3, 4, 5, 6]);
217
+ const rows = accumulations.map(a => [
218
+ `@${a.hostNodeId}`,
219
+ truncateNodeName(a.hostName, a.hostType, 0, 30),
220
+ a.eventPropertyName,
221
+ formatNumber(a.totalListeners),
222
+ check_zombies ? formatNumber(a.zombieCount) : '-',
223
+ formatNumber(a.duplicateCallbackCount),
224
+ formatBytes(a.totalRetainedSize),
225
+ ]);
226
+ lines.push(markdownTable(headers, rows, rightCols));
227
+ lines.push('');
228
+ for (const a of accumulations.slice(0, 5)) {
229
+ lines.push(`### @${a.hostNodeId} \`${truncateNodeName(a.hostName, a.hostType, 0, 40)}\` — ${formatNumber(a.totalListeners)} listeners`);
230
+ lines.push('');
231
+ if (a.zombieCount > 0) {
232
+ lines.push(`**${formatNumber(a.zombieCount)} zombie listener(s)** — context objects with no other referrers (likely from missing \`.off()\` calls)`);
233
+ lines.push('');
234
+ }
235
+ if (a.duplicateCallbackCount > 0) {
236
+ lines.push(`**${formatNumber(a.duplicateCallbackCount)} duplicate callback(s)** — same callback registered multiple times (classic mount/unmount leak)`);
237
+ lines.push('');
238
+ }
239
+ lines.push('Event breakdown:');
240
+ for (const ev of a.eventBreakdown) {
241
+ lines.push(`- \`${ev.eventName}\`: ${formatNumber(ev.listenerCount)} listener(s)`);
242
+ for (const l of ev.sampleListeners) {
243
+ const ctx = l.contextName
244
+ ? `, context: \`${l.contextName}\` @${l.contextId}`
245
+ : '';
246
+ lines.push(` - callback: \`${l.callbackName}\` @${l.callbackId}${ctx}`);
247
+ }
248
+ }
249
+ lines.push('');
250
+ }
251
+ lines.push('**Suggested next steps:**');
252
+ const top = accumulations[0];
253
+ lines.push(`- Inspect host: \`memlab_object_shape(${top.hostNodeId})\``);
254
+ lines.push(`- Trace retention: \`memlab_retainer_trace(${top.hostNodeId})\``);
255
+ lines.push(`- Inspect event container: \`memlab_get_references(${top.eventContainerId})\``);
256
+ if (top.zombieCount > 0) {
257
+ lines.push(`- Find zombie contexts: use \`memlab_referrer_summary(${top.eventContainerId})\` to identify which context classes dominate`);
258
+ }
259
+ return toolResult(lines.join('\n'));
260
+ }
261
+ catch (err) {
262
+ return errorResult(err);
263
+ }
264
+ });
265
+ }
266
+ //# sourceMappingURL=event-listener-leaks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-listener-leaks.js","sourceRoot":"","sources":["../../src/tools/event-listener-leaks.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"find-by-property.d.ts","sourceRoot":"","sources":["../../src/tools/find-by-property.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAYvE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8E9D"}
1
+ {"version":3,"file":"find-by-property.d.ts","sourceRoot":"","sources":["../../src/tools/find-by-property.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAavE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+G9D"}