memorydetective 1.8.1 → 1.10.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 (89) hide show
  1. package/CHANGELOG.md +75 -1
  2. package/README.md +84 -15
  3. package/USAGE.md +27 -4
  4. package/dist/cli.js +106 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/index.js +42 -22
  7. package/dist/index.js.map +1 -1
  8. package/dist/parsers/referenceTree.d.ts +111 -0
  9. package/dist/parsers/referenceTree.js +328 -0
  10. package/dist/parsers/referenceTree.js.map +1 -0
  11. package/dist/runtime/axe.js +6 -1
  12. package/dist/runtime/axe.js.map +1 -1
  13. package/dist/runtime/exec.d.ts +30 -0
  14. package/dist/runtime/exec.js +30 -3
  15. package/dist/runtime/exec.js.map +1 -1
  16. package/dist/runtime/fixTemplates.js +67 -0
  17. package/dist/runtime/fixTemplates.js.map +1 -1
  18. package/dist/runtime/leakReport.d.ts +62 -0
  19. package/dist/runtime/leakReport.js +138 -0
  20. package/dist/runtime/leakReport.js.map +1 -0
  21. package/dist/runtime/platformCheck.d.ts +54 -0
  22. package/dist/runtime/platformCheck.js +94 -0
  23. package/dist/runtime/platformCheck.js.map +1 -0
  24. package/dist/runtime/redact.d.ts +66 -0
  25. package/dist/runtime/redact.js +146 -0
  26. package/dist/runtime/redact.js.map +1 -0
  27. package/dist/runtime/responseFormatter.d.ts +78 -0
  28. package/dist/runtime/responseFormatter.js +307 -0
  29. package/dist/runtime/responseFormatter.js.map +1 -0
  30. package/dist/runtime/securityFlags.d.ts +74 -0
  31. package/dist/runtime/securityFlags.js +90 -0
  32. package/dist/runtime/securityFlags.js.map +1 -0
  33. package/dist/runtime/staticAnalysisHints.js +14 -1
  34. package/dist/runtime/staticAnalysisHints.js.map +1 -1
  35. package/dist/templates/leak-report.html +39 -0
  36. package/dist/templates/templates/leak-report.html +39 -0
  37. package/dist/tools/analyzeAbandonedMemory.d.ts +162 -0
  38. package/dist/tools/analyzeAbandonedMemory.js +325 -0
  39. package/dist/tools/analyzeAbandonedMemory.js.map +1 -0
  40. package/dist/tools/analyzeAllocations.d.ts +11 -2
  41. package/dist/tools/analyzeAllocations.js +4 -0
  42. package/dist/tools/analyzeAllocations.js.map +1 -1
  43. package/dist/tools/analyzeAnimationHitches.d.ts +32 -2
  44. package/dist/tools/analyzeAnimationHitches.js +25 -4
  45. package/dist/tools/analyzeAnimationHitches.js.map +1 -1
  46. package/dist/tools/analyzeAppLaunch.d.ts +3 -0
  47. package/dist/tools/analyzeAppLaunch.js +2 -0
  48. package/dist/tools/analyzeAppLaunch.js.map +1 -1
  49. package/dist/tools/analyzeHangs.d.ts +78 -2
  50. package/dist/tools/analyzeHangs.js +117 -4
  51. package/dist/tools/analyzeHangs.js.map +1 -1
  52. package/dist/tools/analyzeMemgraph.d.ts +40 -1
  53. package/dist/tools/analyzeMemgraph.js +66 -2
  54. package/dist/tools/analyzeMemgraph.js.map +1 -1
  55. package/dist/tools/analyzeTimeProfile.d.ts +11 -1
  56. package/dist/tools/analyzeTimeProfile.js +5 -0
  57. package/dist/tools/analyzeTimeProfile.js.map +1 -1
  58. package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +18 -9
  59. package/dist/tools/bootAndLaunchForLeakInvestigation.js +27 -0
  60. package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -1
  61. package/dist/tools/captureMemgraph.d.ts +22 -4
  62. package/dist/tools/captureMemgraph.js +42 -9
  63. package/dist/tools/captureMemgraph.js.map +1 -1
  64. package/dist/tools/captureScenarioState.d.ts +12 -4
  65. package/dist/tools/captureScenarioState.js +4 -0
  66. package/dist/tools/captureScenarioState.js.map +1 -1
  67. package/dist/tools/classifyCycle.js +77 -0
  68. package/dist/tools/classifyCycle.js.map +1 -1
  69. package/dist/tools/cleanupTraces.d.ts +87 -0
  70. package/dist/tools/cleanupTraces.js +232 -0
  71. package/dist/tools/cleanupTraces.js.map +1 -0
  72. package/dist/tools/compareTracesByPattern.d.ts +2 -2
  73. package/dist/tools/detectLeaksInXCTest.d.ts +116 -0
  74. package/dist/tools/detectLeaksInXCTest.js +311 -0
  75. package/dist/tools/detectLeaksInXCTest.js.map +1 -0
  76. package/dist/tools/detectLeaksInXCUITest.d.ts +8 -3
  77. package/dist/tools/detectLeaksInXCUITest.js +30 -4
  78. package/dist/tools/detectLeaksInXCUITest.js.map +1 -1
  79. package/dist/tools/diffMemgraphs.d.ts +5 -2
  80. package/dist/tools/diffMemgraphs.js +2 -0
  81. package/dist/tools/diffMemgraphs.js.map +1 -1
  82. package/dist/tools/findCycles.d.ts +1 -1
  83. package/dist/tools/recordTimeProfile.d.ts +29 -9
  84. package/dist/tools/recordTimeProfile.js +70 -7
  85. package/dist/tools/recordTimeProfile.js.map +1 -1
  86. package/dist/tools/renderCycleGraph.d.ts +1 -1
  87. package/dist/tools/verifyFix.d.ts +2 -2
  88. package/dist/types.d.ts +24 -1
  89. package/package.json +3 -3
@@ -0,0 +1,162 @@
1
+ /**
2
+ * `analyzeAbandonedMemory(beforePath, afterPath)`
3
+ *
4
+ * Diff two `.memgraph` snapshots on the reference-tree class counts (not the
5
+ * cycle list) and classify the GROWTH shape per class. Surfaces the family
6
+ * of bugs that the standard `diffMemgraphs` (cycle-focused) misses:
7
+ * orphaned KVO observers, never-removed NotificationCenter handlers, caches
8
+ * that never evict, singletons that retain payloads, and the long tail of
9
+ * "unknown growth" that warrants further inspection.
10
+ *
11
+ * The tool is the natural pair for the v1.8 verify-fix loop: capture a
12
+ * `before.memgraph`, ship the fix, capture an `after.memgraph`, then run
13
+ * this to confirm the suspect class went from N to <= 1. Validated end
14
+ * to end on the notelet investigation 2026-05-12 where AVPlayerItem went
15
+ * 342 to 0 across a fix that was invisible in `leaks` output but obvious
16
+ * in the reference tree.
17
+ *
18
+ * The classifier is pattern-catalog driven, same shape as `classifyCycle`:
19
+ * each grown class is matched against a small set of heuristics and tagged
20
+ * with a stable `classification` id + confidence tier. The agent can chain
21
+ * the result into `swiftSearchPattern` with the class name to locate the
22
+ * source.
23
+ */
24
+ import { z } from "zod";
25
+ import { type ReferenceTreeEntry } from "../parsers/referenceTree.js";
26
+ import type { NextCallSuggestion } from "../types.js";
27
+ export declare const analyzeAbandonedMemoryShape: {
28
+ readonly beforePath: z.ZodString;
29
+ readonly afterPath: z.ZodString;
30
+ readonly topN: z.ZodDefault<z.ZodNumber>;
31
+ readonly classFilter: z.ZodOptional<z.ZodString>;
32
+ readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
33
+ };
34
+ export declare const analyzeAbandonedMemorySchema: z.ZodObject<{
35
+ readonly beforePath: z.ZodString;
36
+ readonly afterPath: z.ZodString;
37
+ readonly topN: z.ZodDefault<z.ZodNumber>;
38
+ readonly classFilter: z.ZodOptional<z.ZodString>;
39
+ readonly outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
40
+ }, "strip", z.ZodTypeAny, {
41
+ topN: number;
42
+ beforePath: string;
43
+ afterPath: string;
44
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
45
+ classFilter?: string | undefined;
46
+ }, {
47
+ beforePath: string;
48
+ afterPath: string;
49
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
50
+ topN?: number | undefined;
51
+ classFilter?: string | undefined;
52
+ }>;
53
+ export type AnalyzeAbandonedMemoryInput = z.infer<typeof analyzeAbandonedMemorySchema>;
54
+ export type AbandonedMemoryClassification = "kvo-observer-orphaned" | "notificationcenter-observer-leaked" | "cache-too-aggressive" | "singleton-retains-payload" | "unknown-growth";
55
+ export interface AbandonedMemoryEntry {
56
+ className: string;
57
+ beforeCount: number;
58
+ afterCount: number;
59
+ delta: number;
60
+ beforeBytes: number;
61
+ afterBytes: number;
62
+ bytesDelta: number;
63
+ classification: AbandonedMemoryClassification;
64
+ confidence: "high" | "medium" | "low";
65
+ hint?: string;
66
+ }
67
+ export interface AnalyzeAbandonedMemoryResult {
68
+ ok: boolean;
69
+ beforePath: string;
70
+ afterPath: string;
71
+ totals: {
72
+ classesGrown: number;
73
+ classesShrunk: number;
74
+ classesUnchanged: number;
75
+ netInstancesDelta: number;
76
+ netBytesDelta: number;
77
+ };
78
+ /**
79
+ * Classes that grew between before and after, ranked by absolute delta
80
+ * descending. Each entry carries a `classification` from the catalog plus
81
+ * a `confidence` tier. The agent can branch on `classification` to choose
82
+ * the right `swiftSearchPattern` / fix template.
83
+ *
84
+ * **Raw view.** Includes framework noise (NSMutableDictionary, CFString,
85
+ * libMainThreadChecker bss, etc.). Useful for cache-bloat investigations.
86
+ */
87
+ growthByClass: AbandonedMemoryEntry[];
88
+ /**
89
+ * Classes that shrunk between before and after. Surfaced so the caller
90
+ * can confirm the fix freed the suspect class (e.g. AVPlayerItem in the
91
+ * notelet case went from 342 to 0). Sorted by absolute delta desc.
92
+ *
93
+ * **Raw view.** See `actionableShrinkage` for the filtered "what fix
94
+ * verifiably freed" view.
95
+ */
96
+ shrinkageByClass: AbandonedMemoryEntry[];
97
+ /**
98
+ * `growthByClass` with framework noise filtered out (Foundation collection
99
+ * types, ObjC metadata, __DATA sections, allocator stacks, etc.). The
100
+ * remaining entries are user-actionable classes. New in v1.10.
101
+ *
102
+ * Use this when answering "what new bug just appeared?". Use the raw
103
+ * `growthByClass` when answering "what does the heap look like now?".
104
+ */
105
+ actionableGrowth: AbandonedMemoryEntry[];
106
+ /**
107
+ * `shrinkageByClass` with framework noise filtered out. Use this in the
108
+ * verify-fix loop to confirm which app-level classes the fix actually
109
+ * freed. AVPlayerItem dropping from 342 to 0 shows up here at the top.
110
+ * New in v1.10.
111
+ */
112
+ actionableShrinkage: AbandonedMemoryEntry[];
113
+ /** Plain-English diagnosis tying the highest-confidence growth to a fix hint. */
114
+ diagnosis: string;
115
+ /** Pipeline hints: chain into `swiftSearchPattern` against the top growth class. */
116
+ suggestedNextCalls?: NextCallSuggestion[];
117
+ }
118
+ /**
119
+ * Pure: diff two reference-tree entry lists by class name, classify each
120
+ * class with a delta != 0, and return the structured result minus the
121
+ * filesystem header fields.
122
+ *
123
+ * Exposed so tests can drive it without subprocess spawning. The async
124
+ * wrapper around it handles the leaks invocations.
125
+ */
126
+ export declare function buildAbandonedMemoryDiff(before: ReferenceTreeEntry[], after: ReferenceTreeEntry[], options: {
127
+ topN: number;
128
+ classFilter?: string;
129
+ }): Omit<AnalyzeAbandonedMemoryResult, "ok" | "beforePath" | "afterPath">;
130
+ /**
131
+ * Pure: classify a single class's growth shape based on its name + the
132
+ * presence of co-occurring NSKeyValueObservance growth.
133
+ *
134
+ * Heuristics (highest specificity first):
135
+ *
136
+ * - NSKeyValueObservance / NSKeyValueObservationInfo growth: high-confidence
137
+ * `kvo-observer-orphaned`. The KVO subsystem only allocates these tokens
138
+ * when `obj.observe(\.x) { ... }` is called; growth here means tokens
139
+ * never invalidated.
140
+ *
141
+ * - When KVO observation infrastructure grew, escalate any other class
142
+ * with delta >= 5 to `kvo-observer-orphaned` (medium confidence). These
143
+ * are typically the observed types being retained by orphaned observers
144
+ * (AVPlayerItem in the notelet case).
145
+ *
146
+ * - NSCache / NSCountedSet / NSMapTable / NSMutable{Array,Dictionary,Set}
147
+ * growth: medium-confidence `cache-too-aggressive`. Collection classes
148
+ * that grow across a workflow typically indicate missing eviction.
149
+ *
150
+ * - NotificationCenter observer block growth (NSConcreteNotification,
151
+ * __NSObserver, and similar): medium-confidence
152
+ * `notificationcenter-observer-leaked`.
153
+ *
154
+ * - Everything else: low-confidence `unknown-growth`. The agent should
155
+ * chain into `swiftSearchPattern` with the class name to confirm.
156
+ */
157
+ export declare function classifyGrowth(className: string, delta: number, hasKvoCoOccurrence: boolean, kvoObservanceDelta: number): {
158
+ classification: AbandonedMemoryClassification;
159
+ confidence: "high" | "medium" | "low";
160
+ hint?: string;
161
+ };
162
+ export declare function analyzeAbandonedMemory(input: AnalyzeAbandonedMemoryInput): Promise<AnalyzeAbandonedMemoryResult>;
@@ -0,0 +1,325 @@
1
+ /**
2
+ * `analyzeAbandonedMemory(beforePath, afterPath)`
3
+ *
4
+ * Diff two `.memgraph` snapshots on the reference-tree class counts (not the
5
+ * cycle list) and classify the GROWTH shape per class. Surfaces the family
6
+ * of bugs that the standard `diffMemgraphs` (cycle-focused) misses:
7
+ * orphaned KVO observers, never-removed NotificationCenter handlers, caches
8
+ * that never evict, singletons that retain payloads, and the long tail of
9
+ * "unknown growth" that warrants further inspection.
10
+ *
11
+ * The tool is the natural pair for the v1.8 verify-fix loop: capture a
12
+ * `before.memgraph`, ship the fix, capture an `after.memgraph`, then run
13
+ * this to confirm the suspect class went from N to <= 1. Validated end
14
+ * to end on the notelet investigation 2026-05-12 where AVPlayerItem went
15
+ * 342 to 0 across a fix that was invisible in `leaks` output but obvious
16
+ * in the reference tree.
17
+ *
18
+ * The classifier is pattern-catalog driven, same shape as `classifyCycle`:
19
+ * each grown class is matched against a small set of heuristics and tagged
20
+ * with a stable `classification` id + confidence tier. The agent can chain
21
+ * the result into `swiftSearchPattern` with the class name to locate the
22
+ * source.
23
+ */
24
+ import { z } from "zod";
25
+ import { existsSync } from "node:fs";
26
+ import { resolve as resolvePath } from "node:path";
27
+ import { runCommand } from "../runtime/exec.js";
28
+ import { parseReferenceTreeText, isFrameworkNoise, } from "../parsers/referenceTree.js";
29
+ import { outputFormatField } from "../runtime/responseFormatter.js";
30
+ export const analyzeAbandonedMemoryShape = {
31
+ beforePath: z
32
+ .string()
33
+ .min(1)
34
+ .describe("Absolute path to the baseline `.memgraph` (the BEFORE snapshot). Use `captureScenarioState({ label: 'before' })` to produce one in the standard verify-fix flow."),
35
+ afterPath: z
36
+ .string()
37
+ .min(1)
38
+ .describe("Absolute path to the post-fix `.memgraph` (the AFTER snapshot). Same workflow as `beforePath`, after applying the candidate fix."),
39
+ topN: z
40
+ .number()
41
+ .int()
42
+ .positive()
43
+ .max(200)
44
+ .default(25)
45
+ .describe("Cap on `growthByClass[]` length. Default 25, max 200. Classes are ranked by absolute instance-count delta descending."),
46
+ classFilter: z
47
+ .string()
48
+ .optional()
49
+ .describe("Optional substring filter. When set, only classes whose name contains this substring are included in the response. Useful for verifying a specific class went to baseline without seeing the surrounding noise."),
50
+ outputFormat: outputFormatField,
51
+ };
52
+ export const analyzeAbandonedMemorySchema = z.object(analyzeAbandonedMemoryShape);
53
+ /**
54
+ * Pure: diff two reference-tree entry lists by class name, classify each
55
+ * class with a delta != 0, and return the structured result minus the
56
+ * filesystem header fields.
57
+ *
58
+ * Exposed so tests can drive it without subprocess spawning. The async
59
+ * wrapper around it handles the leaks invocations.
60
+ */
61
+ export function buildAbandonedMemoryDiff(before, after, options) {
62
+ const beforeByName = new Map(before.map((e) => [e.className, e]));
63
+ const afterByName = new Map(after.map((e) => [e.className, e]));
64
+ const allNames = new Set([...beforeByName.keys(), ...afterByName.keys()]);
65
+ const raw = [];
66
+ for (const name of allNames) {
67
+ if (options.classFilter && !name.includes(options.classFilter))
68
+ continue;
69
+ const b = beforeByName.get(name);
70
+ const a = afterByName.get(name);
71
+ const beforeCount = b?.instanceCount ?? 0;
72
+ const afterCount = a?.instanceCount ?? 0;
73
+ const beforeBytes = b?.totalBytes ?? 0;
74
+ const afterBytes = a?.totalBytes ?? 0;
75
+ raw.push({
76
+ className: name,
77
+ beforeCount,
78
+ afterCount,
79
+ delta: afterCount - beforeCount,
80
+ beforeBytes,
81
+ afterBytes,
82
+ bytesDelta: afterBytes - beforeBytes,
83
+ });
84
+ }
85
+ // Co-occurrence signal: did the KVO observation infrastructure grow?
86
+ // If yes, the same-direction growth of other large classes is best
87
+ // explained as "those classes are the observed types being retained
88
+ // by orphaned observers", and the classifier should escalate them to
89
+ // `kvo-observer-orphaned` instead of leaving them as `unknown-growth`.
90
+ const kvoObservanceGrowth = raw.find((r) => r.className === "NSKeyValueObservance")?.delta ?? 0;
91
+ const kvoObservationInfoGrowth = raw.find((r) => r.className === "NSKeyValueObservationInfo")?.delta ?? 0;
92
+ const hasKvoCoOccurrence = kvoObservanceGrowth >= 3 || kvoObservationInfoGrowth >= 3;
93
+ const grown = raw.filter((r) => r.delta > 0);
94
+ const shrunk = raw.filter((r) => r.delta < 0);
95
+ const unchanged = raw.filter((r) => r.delta === 0).length;
96
+ const growthByClass = grown
97
+ .map((r) => {
98
+ const { classification, confidence, hint } = classifyGrowth(r.className, r.delta, hasKvoCoOccurrence, kvoObservanceGrowth);
99
+ return {
100
+ className: r.className,
101
+ beforeCount: r.beforeCount,
102
+ afterCount: r.afterCount,
103
+ delta: r.delta,
104
+ beforeBytes: r.beforeBytes,
105
+ afterBytes: r.afterBytes,
106
+ bytesDelta: r.bytesDelta,
107
+ classification,
108
+ confidence,
109
+ ...(hint ? { hint } : {}),
110
+ };
111
+ })
112
+ .sort((a, b) => b.delta - a.delta || b.bytesDelta - a.bytesDelta);
113
+ const shrinkageByClass = shrunk
114
+ .map((r) => ({
115
+ className: r.className,
116
+ beforeCount: r.beforeCount,
117
+ afterCount: r.afterCount,
118
+ delta: r.delta,
119
+ beforeBytes: r.beforeBytes,
120
+ afterBytes: r.afterBytes,
121
+ bytesDelta: r.bytesDelta,
122
+ // Shrinkage entries are still classified for symmetry, but the
123
+ // classification reflects what the suspect-shaped class WAS doing
124
+ // before; the fix freed it, which is what we want to confirm.
125
+ classification: classifyGrowth(r.className, Math.abs(r.delta), false, 0).classification,
126
+ confidence: "high",
127
+ }))
128
+ .sort((a, b) => a.delta - b.delta || a.bytesDelta - b.bytesDelta);
129
+ const netInstancesDelta = raw.reduce((acc, r) => acc + r.delta, 0);
130
+ const netBytesDelta = raw.reduce((acc, r) => acc + r.bytesDelta, 0);
131
+ const totals = {
132
+ classesGrown: grown.length,
133
+ classesShrunk: shrunk.length,
134
+ classesUnchanged: unchanged,
135
+ netInstancesDelta,
136
+ netBytesDelta,
137
+ };
138
+ const diagnosis = buildDiagnosis(growthByClass, shrinkageByClass);
139
+ const suggestedNextCalls = buildSuggestedNextCalls(growthByClass);
140
+ // Actionable views: drop framework noise so the caller's first-look list
141
+ // surfaces app-level + AV + KVO classes instead of NSMutableDictionary +
142
+ // CFString + ObjC runtime data. Same ranking, just filtered. Falls back
143
+ // to topN, so the actionable list can be SHORTER than the raw view when
144
+ // most of the top entries are noise.
145
+ const actionableGrowth = growthByClass
146
+ .filter((e) => !isFrameworkNoise(e.className))
147
+ .slice(0, options.topN);
148
+ const actionableShrinkage = shrinkageByClass
149
+ .filter((e) => !isFrameworkNoise(e.className))
150
+ .slice(0, options.topN);
151
+ return {
152
+ totals,
153
+ growthByClass: growthByClass.slice(0, options.topN),
154
+ shrinkageByClass: shrinkageByClass.slice(0, options.topN),
155
+ actionableGrowth,
156
+ actionableShrinkage,
157
+ diagnosis,
158
+ ...(suggestedNextCalls.length > 0 ? { suggestedNextCalls } : {}),
159
+ };
160
+ }
161
+ /**
162
+ * Pure: classify a single class's growth shape based on its name + the
163
+ * presence of co-occurring NSKeyValueObservance growth.
164
+ *
165
+ * Heuristics (highest specificity first):
166
+ *
167
+ * - NSKeyValueObservance / NSKeyValueObservationInfo growth: high-confidence
168
+ * `kvo-observer-orphaned`. The KVO subsystem only allocates these tokens
169
+ * when `obj.observe(\.x) { ... }` is called; growth here means tokens
170
+ * never invalidated.
171
+ *
172
+ * - When KVO observation infrastructure grew, escalate any other class
173
+ * with delta >= 5 to `kvo-observer-orphaned` (medium confidence). These
174
+ * are typically the observed types being retained by orphaned observers
175
+ * (AVPlayerItem in the notelet case).
176
+ *
177
+ * - NSCache / NSCountedSet / NSMapTable / NSMutable{Array,Dictionary,Set}
178
+ * growth: medium-confidence `cache-too-aggressive`. Collection classes
179
+ * that grow across a workflow typically indicate missing eviction.
180
+ *
181
+ * - NotificationCenter observer block growth (NSConcreteNotification,
182
+ * __NSObserver, and similar): medium-confidence
183
+ * `notificationcenter-observer-leaked`.
184
+ *
185
+ * - Everything else: low-confidence `unknown-growth`. The agent should
186
+ * chain into `swiftSearchPattern` with the class name to confirm.
187
+ */
188
+ export function classifyGrowth(className, delta, hasKvoCoOccurrence, kvoObservanceDelta) {
189
+ if (className.includes("NSKeyValueObservance") ||
190
+ className.includes("NSKeyValueObservationInfo")) {
191
+ return {
192
+ classification: "kvo-observer-orphaned",
193
+ confidence: "high",
194
+ hint: "NSKeyValueObservance growth indicates `observe(\\.x) { ... }` tokens that were never invalidated. The token strongly retains the change closure (which usually captures self), and the closure is anchored in the KVO global observer registry. Use `[weak self]` inside the observe closure and call `token?.invalidate()` in `deinit`, or invalidate-then-nil before reassigning the token. See `classifyCycle` pattern `kvo.observation-not-invalidated`.",
195
+ };
196
+ }
197
+ // KVO co-occurrence escalation. Tightened in v1.10 after the v1.9 retro
198
+ // showed the broad `delta >= 5` rule was painting 25 unrelated classes
199
+ // as `kvo-observer-orphaned high` whenever NSKeyValueObservance grew at
200
+ // all. Three guards now:
201
+ // 1. Skip classes that look like framework noise (allocator stacks,
202
+ // memory zones, __DATA sections, summary rows, etc.)
203
+ // 2. Skip classes that are not "object-shaped" (anonymous bracket
204
+ // forms `<... 0xADDR> [size]`, byte-offset prefixes, etc.). These
205
+ // slip past Phase A's extractClassName when the leaks output uses
206
+ // a non-canonical form for them.
207
+ // 3. Require the candidate's delta to be a meaningful FRACTION of
208
+ // the KVO growth, not just any positive delta. A class with
209
+ // delta=5 when NSKeyValueObservance grew by +200 is statistical
210
+ // noise; the proportion thresholds below capture this.
211
+ if (hasKvoCoOccurrence && delta >= 5) {
212
+ const isObjectShaped = !isFrameworkNoise(className) &&
213
+ !/^<.*0x[0-9a-fA-F]+.*>/.test(className) &&
214
+ !/^\d+ bytes into\b/.test(className);
215
+ // Proportional thresholds. A class whose delta is below half of the
216
+ // NSKeyValueObservance delta is rejected as too small to be the
217
+ // observed type. Above 5x the KVO delta the confidence escalates
218
+ // to high (the class is the dominant observed type by a wide margin).
219
+ const mediumThreshold = Math.max(5, Math.floor(kvoObservanceDelta * 0.5));
220
+ const highThreshold = Math.max(50, Math.floor(kvoObservanceDelta * 5));
221
+ if (isObjectShaped && delta >= mediumThreshold) {
222
+ const confidence = delta >= highThreshold ? "high" : "medium";
223
+ return {
224
+ classification: "kvo-observer-orphaned",
225
+ confidence,
226
+ hint: `Co-occurring NSKeyValueObservance growth (+${kvoObservanceDelta}) suggests this type is the value being observed via \`observe(\\.x) { ... }\`. The orphaned observer holds the value alive. Fixing the observer (\`token.invalidate()\` on teardown) will free this class too. See \`classifyCycle\` pattern \`kvo.observation-not-invalidated\`.`,
227
+ };
228
+ }
229
+ }
230
+ if (/^NS(Cache|CountedSet|MapTable|MutableArray|MutableDictionary|MutableSet|HashTable)/.test(className)) {
231
+ return {
232
+ classification: "cache-too-aggressive",
233
+ confidence: "medium",
234
+ hint: "Bulk-storage class is growing across the workflow. Likely a cache or collection without eviction. For `NSCache`, set `countLimit` or `totalCostLimit`. For `NSMutable*`, audit the producer to confirm it is not appending without bounds. For domain caches, prefer `NSCache` over `NSMutableDictionary` when you want OS-driven eviction under memory pressure.",
235
+ };
236
+ }
237
+ if (/NotificationToken|NotificationObserver|__NSObserver|NSConcreteNotification|NSNotificationCenterObserver/.test(className)) {
238
+ return {
239
+ classification: "notificationcenter-observer-leaked",
240
+ confidence: "medium",
241
+ hint: "NotificationCenter observer block (added via `addObserver(forName:object:queue:using:)`) growth indicates the returned token was never passed to `removeObserver(_:)`. Store the token on `self` and remove it in `deinit`, or use the selector-based variant which auto-deregisters on dealloc.",
242
+ };
243
+ }
244
+ return {
245
+ classification: "unknown-growth",
246
+ confidence: "low",
247
+ hint: "Class grew between before and after but the catalog did not recognize a known abandoned-memory shape. Chain into `swiftSearchPattern` with this class name to locate the allocation sites, then inspect for missing teardown (observers, timers, dispatch sources, weak ownership invariants).",
248
+ };
249
+ }
250
+ function buildDiagnosis(grown, shrunk) {
251
+ if (grown.length === 0 && shrunk.length === 0) {
252
+ return "No class-count changes between before and after. Either the fix had no effect on the heap composition, or the workflow did not exercise the code path.";
253
+ }
254
+ if (grown.length === 0) {
255
+ const top = shrunk[0];
256
+ return `No growth detected. ${shrunk.length} class${shrunk.length === 1 ? "" : "es"} shrunk. Largest: ${top.className} (${top.beforeCount} to ${top.afterCount}, delta ${top.delta}). The fix appears to have closed an abandoned-memory chain.`;
257
+ }
258
+ const highConfidence = grown.filter((e) => e.confidence === "high");
259
+ const top = grown[0];
260
+ if (highConfidence.length > 0) {
261
+ const hc = highConfidence[0];
262
+ return `${grown.length} class${grown.length === 1 ? "" : "es"} grew. Top suspect: ${hc.className} (${hc.beforeCount} to ${hc.afterCount}, delta +${hc.delta}). Classification: ${hc.classification} (high confidence). ${hc.hint ?? ""}`;
263
+ }
264
+ return `${grown.length} class${grown.length === 1 ? "" : "es"} grew. Largest: ${top.className} (${top.beforeCount} to ${top.afterCount}, delta +${top.delta}). Classification: ${top.classification} (${top.confidence} confidence). Chain into swiftSearchPattern with the class name to locate the allocation site.`;
265
+ }
266
+ function buildSuggestedNextCalls(grown) {
267
+ if (grown.length === 0)
268
+ return [];
269
+ // Prefer the highest-confidence + largest-delta entry for the suggestion.
270
+ const ranked = [...grown].sort((a, b) => {
271
+ const confRank = (c) => c === "high" ? 2 : c === "medium" ? 1 : 0;
272
+ const diff = confRank(b.confidence) - confRank(a.confidence);
273
+ if (diff !== 0)
274
+ return diff;
275
+ return b.delta - a.delta;
276
+ });
277
+ const target = ranked[0];
278
+ return [
279
+ {
280
+ tool: "swiftSearchPattern",
281
+ args: {
282
+ pattern: target.className,
283
+ scope: "<your project root>",
284
+ },
285
+ why: `Locate the allocation site for ${target.className} (grew by +${target.delta} between snapshots; classified as ${target.classification}, ${target.confidence} confidence). The class name + a project-wide pattern search usually narrows to one or two files.`,
286
+ },
287
+ ];
288
+ }
289
+ /**
290
+ * Spawn `leaks --referenceTree --groupByType --noContent` against a .memgraph
291
+ * and return parsed entries. Wide topN here (1000) so the diff has the full
292
+ * picture; the user-facing `topN` only applies to the FINAL response slice.
293
+ */
294
+ async function loadReferenceTree(path) {
295
+ const result = await runCommand("leaks", [path, "--referenceTree", "--groupByType", "--noContent"], { timeoutMs: 5 * 60_000 });
296
+ if (result.code !== 0 && result.code !== 1) {
297
+ throw new Error(`leaks --referenceTree failed (code ${result.code}) on ${path}: ${result.stderr || result.stdout}`);
298
+ }
299
+ return parseReferenceTreeText(result.stdout, 1000);
300
+ }
301
+ export async function analyzeAbandonedMemory(input) {
302
+ const beforePath = resolvePath(input.beforePath);
303
+ const afterPath = resolvePath(input.afterPath);
304
+ if (!existsSync(beforePath)) {
305
+ throw new Error(`Before memgraph not found: ${beforePath}`);
306
+ }
307
+ if (!existsSync(afterPath)) {
308
+ throw new Error(`After memgraph not found: ${afterPath}`);
309
+ }
310
+ const [before, after] = await Promise.all([
311
+ loadReferenceTree(beforePath),
312
+ loadReferenceTree(afterPath),
313
+ ]);
314
+ const diff = buildAbandonedMemoryDiff(before, after, {
315
+ topN: input.topN ?? 25,
316
+ ...(input.classFilter ? { classFilter: input.classFilter } : {}),
317
+ });
318
+ return {
319
+ ok: true,
320
+ beforePath,
321
+ afterPath,
322
+ ...diff,
323
+ };
324
+ }
325
+ //# sourceMappingURL=analyzeAbandonedMemory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzeAbandonedMemory.js","sourceRoot":"","sources":["../../src/tools/analyzeAbandonedMemory.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,18 @@
1
1
  import { z } from "zod";
2
+ import type { DataStatus } from "../types.js";
2
3
  export declare const analyzeAllocationsSchema: z.ZodObject<{
3
4
  tracePath: z.ZodString;
4
5
  topN: z.ZodDefault<z.ZodNumber>;
5
6
  minBytes: z.ZodDefault<z.ZodNumber>;
7
+ outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
6
8
  }, "strip", z.ZodTypeAny, {
7
- topN: number;
8
9
  tracePath: string;
10
+ topN: number;
9
11
  minBytes: number;
12
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
10
13
  }, {
11
14
  tracePath: string;
15
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
12
16
  topN?: number | undefined;
13
17
  minBytes?: number | undefined;
14
18
  }>;
@@ -38,9 +42,14 @@ export interface AnalyzeAllocationsResult {
38
42
  };
39
43
  /** Top categories by cumulative bytes. */
40
44
  topByBytes: AllocationEntry[];
41
- /** Top categories by allocation count (different signal small frequent allocations). */
45
+ /** Top categories by allocation count (different signal, small frequent allocations). */
42
46
  topByCount: AllocationEntry[];
43
47
  diagnosis: string;
48
+ /**
49
+ * Disambiguates empty arrays into "no data in the trace" vs "trace could
50
+ * not be exported" vs "data was exported partially". See {@link DataStatus}.
51
+ */
52
+ status: DataStatus;
44
53
  }
45
54
  /** Pure: turn parsed XML into the analyzed result. */
46
55
  export declare function analyzeAllocationsFromXml(xml: string, tracePath: string, topN?: number, minBytes?: number): AnalyzeAllocationsResult;
@@ -3,6 +3,7 @@ import { existsSync } from "node:fs";
3
3
  import { resolve as resolvePath } from "node:path";
4
4
  import { runCommand } from "../runtime/exec.js";
5
5
  import { parseXctraceXml, asNumber, asFormatted, } from "../parsers/xctraceXml.js";
6
+ import { outputFormatField } from "../runtime/responseFormatter.js";
6
7
  export const analyzeAllocationsSchema = z.object({
7
8
  tracePath: z
8
9
  .string()
@@ -19,6 +20,7 @@ export const analyzeAllocationsSchema = z.object({
19
20
  .nonnegative()
20
21
  .default(0)
21
22
  .describe("Filter out individual allocations smaller than this size in bytes (default 0). Use 1024 to focus on >1KB allocations."),
23
+ outputFormat: outputFormatField,
22
24
  });
23
25
  /** Pure: turn parsed XML into the analyzed result. */
24
26
  export function analyzeAllocationsFromXml(xml, tracePath, topN = 15, minBytes = 0) {
@@ -38,6 +40,7 @@ export function analyzeAllocationsFromXml(xml, tracePath, topN = 15, minBytes =
38
40
  topByBytes: [],
39
41
  topByCount: [],
40
42
  diagnosis: "No allocations table found in the trace.",
43
+ status: "not_present",
41
44
  };
42
45
  }
43
46
  // The xctrace `allocations` schema has columns roughly like:
@@ -126,6 +129,7 @@ export function analyzeAllocationsFromXml(xml, tracePath, topN = 15, minBytes =
126
129
  topByBytes,
127
130
  topByCount,
128
131
  diagnosis,
132
+ status: "available",
129
133
  };
130
134
  }
131
135
  function buildDiagnosis(rows, cumulativeBytes, topByBytes) {
@@ -1 +1 @@
1
- {"version":3,"file":"analyzeAllocations.js","sourceRoot":"","sources":["../../src/tools/analyzeAllocations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EACL,eAAe,EACf,QAAQ,EACR,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAElC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,+IAA+I,CAChJ;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,8DAA8D,CAAC;IAC3E,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,WAAW,EAAE;SACb,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CACP,uHAAuH,CACxH;CACJ,CAAC,CAAC;AAyCH,sDAAsD;AACtD,MAAM,UAAU,yBAAyB,CACvC,GAAW,EACX,SAAiB,EACjB,IAAI,GAAG,EAAE,EACT,QAAQ,GAAG,CAAC;IAEZ,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,EAAE,EAAE,IAAI;YACR,SAAS;YACT,MAAM,EAAE;gBACN,IAAI,EAAE,CAAC;gBACP,eAAe,EAAE,CAAC;gBAClB,qBAAqB,EAAE,CAAC;gBACxB,eAAe,EAAE,CAAC;gBAClB,cAAc,EAAE,CAAC;aAClB;YACD,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,EAAE;YACd,SAAS,EAAE,0CAA0C;SACtD,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,0DAA0D;IAC1D,0EAA0E;IAC1E,yDAAyD;IACzD,MAAM,IAAI,GAAuB,EAAE,CAAC;IACpC,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,qBAAqB,GAAG,CAAC,CAAC;IAE9B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,QAAQ,GACZ,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;YACzB,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;YAC1B,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YACjC,SAAS,CAAC;QACZ,MAAM,IAAI,GACR,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7D,IAAI,IAAI,GAAG,QAAQ;YAAE,SAAS;QAC9B,MAAM,SAAS,GACb,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC;YACR,QAAQ;YACR,IAAI;YACJ,SAAS,EAAE,SAAS,KAAK,OAAO,IAAI,SAAS,KAAK,MAAM;SACzD,CAAC,CAAC;QACH,eAAe,IAAI,IAAI,CAAC;QACxB,qBAAqB,IAAI,CAAC,CAAC;IAC7B,CAAC;IASD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAe,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,eAAe,IAAI,CAAC,CAAC;YAC9B,QAAQ,CAAC,eAAe,IAAI,CAAC,CAAC,IAAI,CAAC;YACnC,IAAI,CAAC,CAAC,SAAS;gBAAE,QAAQ,CAAC,SAAS,IAAI,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE;gBAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,eAAe,EAAE,CAAC;gBAClB,eAAe,EAAE,CAAC,CAAC,IAAI;gBACvB,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAsB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACpE,MAAM,GAAG,GAAG,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9E,IAAI,SAA+C,CAAC;QACpD,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC;YAAE,SAAS,GAAG,WAAW,CAAC;aAC1C,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,eAAe;YAAE,SAAS,GAAG,YAAY,CAAC;;YAChE,SAAS,GAAG,OAAO,CAAC;QACzB,OAAO;YACL,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,eAAe,EAAE,CAAC,CAAC,eAAe;YAClC,eAAe,EAAE,CAAC,CAAC,eAAe;YAClC,YAAY,EAAE,GAAG;YACjB,SAAS;SACV,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,OAAO;SAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,YAAY,CAAC;SAC3C,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,OAAO;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,WAAW,CAAC;SAC1C,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC;SAC5B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;SACrD,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAClB,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC;SAC5B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;SACrD,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAElB,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;IAE3E,OAAO;QACL,EAAE,EAAE,IAAI;QACR,SAAS;QACT,MAAM,EAAE;YACN,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,eAAe;YACf,qBAAqB;YACrB,eAAe;YACf,cAAc;SACf;QACD,UAAU;QACV,UAAU;QACV,SAAS;KACV,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CACrB,IAAY,EACZ,eAAuB,EACvB,UAA6B;IAE7B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,OAAO,yDAAyD,CAAC;IACnE,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,CAAC,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACtD,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,yBAAyB,EAAE,uBAAuB,GAAG,CAAC,QAAQ,KAAK,CAAC,GAAG,CAAC,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,eAAe,CAAC,cAAc,EAAE,2BAA2B,GAAG,CAAC,SAAS,IAAI,CAAC;AACjP,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAA8B;IAE9B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,OAAO,EACP;QACE,SAAS;QACT,QAAQ;QACR,SAAS;QACT,SAAS;QACT,SAAS;QACT,kDAAkD;KACnD,EACD,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,EAAE,CAC1B,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,+BAA+B,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,yBAAyB,CAC9B,MAAM,CAAC,MAAM,EACb,SAAS,EACT,KAAK,CAAC,IAAI,IAAI,EAAE,EAChB,KAAK,CAAC,QAAQ,IAAI,CAAC,CACpB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"analyzeAllocations.js","sourceRoot":"","sources":["../../src/tools/analyzeAllocations.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,38 @@
1
1
  import { z } from "zod";
2
+ import type { DataStatus } from "../types.js";
2
3
  export declare const analyzeAnimationHitchesSchema: z.ZodObject<{
3
4
  tracePath: z.ZodString;
4
5
  topN: z.ZodDefault<z.ZodNumber>;
5
6
  minDurationMs: z.ZodDefault<z.ZodNumber>;
7
+ timeRangeMs: z.ZodOptional<z.ZodObject<{
8
+ startMs: z.ZodNumber;
9
+ endMs: z.ZodNumber;
10
+ }, "strip", z.ZodTypeAny, {
11
+ startMs: number;
12
+ endMs: number;
13
+ }, {
14
+ startMs: number;
15
+ endMs: number;
16
+ }>>;
17
+ outputFormat: z.ZodOptional<z.ZodEnum<["markdown", "json", "both", "verify-fix-table"]>>;
6
18
  }, "strip", z.ZodTypeAny, {
7
- topN: number;
8
19
  tracePath: string;
20
+ topN: number;
9
21
  minDurationMs: number;
22
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
23
+ timeRangeMs?: {
24
+ startMs: number;
25
+ endMs: number;
26
+ } | undefined;
10
27
  }, {
11
28
  tracePath: string;
29
+ outputFormat?: "markdown" | "json" | "both" | "verify-fix-table" | undefined;
12
30
  topN?: number | undefined;
13
31
  minDurationMs?: number | undefined;
32
+ timeRangeMs?: {
33
+ startMs: number;
34
+ endMs: number;
35
+ } | undefined;
14
36
  }>;
15
37
  export type AnalyzeAnimationHitchesInput = z.infer<typeof analyzeAnimationHitchesSchema>;
16
38
  export interface HitchEntry {
@@ -37,7 +59,15 @@ export interface AnalyzeAnimationHitchesResult {
37
59
  byType: Record<string, number>;
38
60
  top: HitchEntry[];
39
61
  diagnosis: string;
62
+ /**
63
+ * Disambiguates empty arrays into "no data in the trace" vs "trace could
64
+ * not be exported" vs "data was exported partially". See {@link DataStatus}.
65
+ */
66
+ status: DataStatus;
40
67
  }
41
68
  /** Pure: turn parsed XML into the analyzed result. */
42
- export declare function analyzeAnimationHitchesFromXml(xml: string, tracePath: string, topN?: number, minDurationMs?: number): AnalyzeAnimationHitchesResult;
69
+ export declare function analyzeAnimationHitchesFromXml(xml: string, tracePath: string, topN?: number, minDurationMs?: number, timeRangeMs?: {
70
+ startMs: number;
71
+ endMs: number;
72
+ }): AnalyzeAnimationHitchesResult;
43
73
  export declare function analyzeAnimationHitches(input: AnalyzeAnimationHitchesInput): Promise<AnalyzeAnimationHitchesResult>;