memorydetective 1.7.0 → 1.8.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 (34) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +61 -29
  3. package/USAGE.md +87 -41
  4. package/dist/index.js +34 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/runtime/axe.d.ts +86 -0
  7. package/dist/runtime/axe.js +249 -0
  8. package/dist/runtime/axe.js.map +1 -0
  9. package/dist/runtime/buildSettings.d.ts +27 -0
  10. package/dist/runtime/buildSettings.js +88 -0
  11. package/dist/runtime/buildSettings.js.map +1 -0
  12. package/dist/runtime/exec.d.ts +8 -1
  13. package/dist/runtime/exec.js +8 -2
  14. package/dist/runtime/exec.js.map +1 -1
  15. package/dist/runtime/simctl.d.ts +68 -0
  16. package/dist/runtime/simctl.js +194 -0
  17. package/dist/runtime/simctl.js.map +1 -0
  18. package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +166 -0
  19. package/dist/tools/bootAndLaunchForLeakInvestigation.js +367 -0
  20. package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -0
  21. package/dist/tools/captureMemgraph.d.ts +29 -1
  22. package/dist/tools/captureMemgraph.js +148 -6
  23. package/dist/tools/captureMemgraph.js.map +1 -1
  24. package/dist/tools/captureScenarioState.d.ts +77 -0
  25. package/dist/tools/captureScenarioState.js +159 -0
  26. package/dist/tools/captureScenarioState.js.map +1 -0
  27. package/dist/tools/detectLeaksInXCUITest.d.ts +2 -2
  28. package/dist/tools/getInvestigationPlaybook.d.ts +15 -0
  29. package/dist/tools/getInvestigationPlaybook.js +24 -1
  30. package/dist/tools/getInvestigationPlaybook.js.map +1 -1
  31. package/dist/tools/replayScenario.d.ts +243 -0
  32. package/dist/tools/replayScenario.js +187 -0
  33. package/dist/tools/replayScenario.js.map +1 -0
  34. package/package.json +2 -2
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Thin wrappers around `xcrun simctl` for booting, installing, and launching
3
+ * iOS apps on the iOS Simulator. Used by `bootAndLaunchForLeakInvestigation`
4
+ * to set up a leak-investigation environment with `MallocStackLogging=1`.
5
+ *
6
+ * Design notes:
7
+ * - All operations are idempotent where possible (boot ignores "already booted",
8
+ * launch uses --terminate-running-process to atomically replace any prior instance).
9
+ * - Env vars are propagated to the child via the `SIMCTL_CHILD_*` prefix
10
+ * convention enforced by simctl. The caller passes plain `MallocStackLogging=1`
11
+ * and we prefix it before invoking simctl.
12
+ */
13
+ import { runCommand } from "./exec.js";
14
+ /**
15
+ * Find the first booted simulator. Returns null when none are booted.
16
+ * Used as a fallback when the caller doesn't specify a simulator.
17
+ */
18
+ export async function findBootedSimulator() {
19
+ const result = await runCommand("xcrun", ["simctl", "list", "devices", "booted", "--json"], { timeoutMs: 15_000 });
20
+ if (result.code !== 0) {
21
+ throw new Error(`xcrun simctl list failed (code ${result.code}): ${result.stderr || result.stdout}`);
22
+ }
23
+ const devices = parseSimctlDevices(result.stdout);
24
+ return devices.find((d) => d.state === "Booted") ?? null;
25
+ }
26
+ /**
27
+ * Find a simulator by display name (e.g. "iPhone 15"), optionally constrained
28
+ * to a runtime version. Returns null when no match.
29
+ *
30
+ * @param os Either "latest" (highest available runtime), an explicit version
31
+ * like "17.5", or undefined (any runtime).
32
+ */
33
+ export async function findSimulatorByName(name, os) {
34
+ const result = await runCommand("xcrun", ["simctl", "list", "devices", "--json"], { timeoutMs: 15_000 });
35
+ if (result.code !== 0) {
36
+ throw new Error(`xcrun simctl list failed (code ${result.code}): ${result.stderr || result.stdout}`);
37
+ }
38
+ const devices = parseSimctlDevices(result.stdout);
39
+ const matches = devices.filter((d) => d.name === name);
40
+ if (matches.length === 0)
41
+ return null;
42
+ if (!os)
43
+ return matches[0];
44
+ if (os === "latest") {
45
+ return pickLatestByRuntime(matches);
46
+ }
47
+ // Runtime keys come back like "com.apple.CoreSimulator.SimRuntime.iOS-17-5".
48
+ // Match either "17.5" or "iOS 17.5" from the user.
49
+ const normalized = os.replace(/^iOS\s+/i, "").replace(/\./g, "-");
50
+ return matches.find((d) => d.runtime.includes(normalized)) ?? null;
51
+ }
52
+ /**
53
+ * Boot a simulator. Idempotent: if the device is already booted, returns
54
+ * cleanly. Then waits via `simctl bootstatus -b` until SpringBoard is ready,
55
+ * which prevents flaky `install` calls right after boot.
56
+ */
57
+ export async function bootSimulator(udid) {
58
+ const boot = await runCommand("xcrun", ["simctl", "boot", udid], {
59
+ timeoutMs: 60_000,
60
+ });
61
+ // CoreSimulator returns non-zero with "Unable to boot device in current state: Booted"
62
+ // when already booted. Treat that as success.
63
+ const alreadyBooted = /current state:\s*Booted/i.test(boot.stderr + boot.stdout);
64
+ if (boot.code !== 0 && !alreadyBooted) {
65
+ throw new Error(`simctl boot ${udid} failed (code ${boot.code}): ${boot.stderr || boot.stdout}`);
66
+ }
67
+ // -b blocks until the system is fully booted (SpringBoard up).
68
+ const status = await runCommand("xcrun", ["simctl", "bootstatus", udid, "-b"], { timeoutMs: 120_000 });
69
+ if (status.code !== 0) {
70
+ throw new Error(`simctl bootstatus ${udid} -b failed (code ${status.code}): ${status.stderr || status.stdout}`);
71
+ }
72
+ }
73
+ /**
74
+ * Install (or reinstall) an app bundle on a simulator. Idempotent by design;
75
+ * simctl overwrites a prior install at the same bundle identifier.
76
+ */
77
+ export async function installApp(udid, appPath) {
78
+ const result = await runCommand("xcrun", ["simctl", "install", udid, appPath], { timeoutMs: 120_000 });
79
+ if (result.code !== 0) {
80
+ throw new Error(`simctl install ${udid} ${appPath} failed (code ${result.code}): ${result.stderr || result.stdout}`);
81
+ }
82
+ }
83
+ /**
84
+ * Launch an app on a booted simulator with the given env vars and launch args.
85
+ * Uses `--terminate-running-process` so any existing instance is replaced
86
+ * atomically (no race where stale env vars persist).
87
+ *
88
+ * Env vars are propagated to the child via the `SIMCTL_CHILD_*` prefix that
89
+ * simctl honors. Pass plain keys (e.g. `MallocStackLogging`); we prefix.
90
+ */
91
+ export async function launchApp(udid, bundleId, env = {}, launchArgs = []) {
92
+ const args = [
93
+ "simctl",
94
+ "launch",
95
+ "--terminate-running-process",
96
+ udid,
97
+ bundleId,
98
+ ...launchArgs,
99
+ ];
100
+ const prefixedEnv = {};
101
+ for (const [k, v] of Object.entries(env)) {
102
+ prefixedEnv[`SIMCTL_CHILD_${k}`] = v;
103
+ }
104
+ const result = await runCommand("xcrun", args, {
105
+ timeoutMs: 60_000,
106
+ env: prefixedEnv,
107
+ });
108
+ if (result.code !== 0) {
109
+ throw new Error(`simctl launch ${bundleId} on ${udid} failed (code ${result.code}): ${result.stderr || result.stdout}`);
110
+ }
111
+ // simctl prints "<bundleId>: <pid>" on success.
112
+ const simPid = parseLaunchPid(result.stdout, bundleId);
113
+ if (simPid === null) {
114
+ throw new Error(`simctl launch succeeded but PID line was not parseable. stdout: ${result.stdout}`);
115
+ }
116
+ return { simPid, bundleId };
117
+ }
118
+ /**
119
+ * Take a screenshot of the simulator's screen and write it to `outputPath`.
120
+ * Wraps `xcrun simctl io <udid> screenshot <path>`.
121
+ */
122
+ export async function takeScreenshot(udid, outputPath) {
123
+ const result = await runCommand("xcrun", ["simctl", "io", udid, "screenshot", outputPath], { timeoutMs: 30_000 });
124
+ if (result.code !== 0) {
125
+ throw new Error(`simctl io ${udid} screenshot failed (code ${result.code}): ${result.stderr || result.stdout}`);
126
+ }
127
+ }
128
+ /** Pure: parse the simulator-internal PID from `simctl launch` stdout. Exposed for tests. */
129
+ export function parseLaunchPid(stdout, bundleId) {
130
+ const escaped = bundleId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
131
+ const re = new RegExp(`${escaped}\\s*:\\s*(\\d+)`);
132
+ const match = stdout.match(re);
133
+ if (!match)
134
+ return null;
135
+ const pid = parseInt(match[1], 10);
136
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
137
+ }
138
+ /** Pure: parse a `simctl list devices --json` payload into a flat list. Exposed for tests. */
139
+ export function parseSimctlDevices(stdout) {
140
+ let parsed;
141
+ try {
142
+ parsed = JSON.parse(stdout);
143
+ }
144
+ catch (err) {
145
+ throw new Error(`simctl list devices --json output failed JSON.parse: ${err.message}`);
146
+ }
147
+ const out = [];
148
+ if (!parsed.devices || typeof parsed.devices !== "object") {
149
+ return out;
150
+ }
151
+ for (const [runtime, list] of Object.entries(parsed.devices)) {
152
+ if (!Array.isArray(list))
153
+ continue;
154
+ for (const dev of list) {
155
+ const udid = typeof dev.udid === "string" ? dev.udid : null;
156
+ const name = typeof dev.name === "string" ? dev.name : null;
157
+ const state = typeof dev.state === "string" ? dev.state : "Unknown";
158
+ if (!udid || !name)
159
+ continue;
160
+ out.push({ udid, name, state, runtime });
161
+ }
162
+ }
163
+ return out;
164
+ }
165
+ /**
166
+ * Pick the simulator with the highest runtime version. Used when the caller
167
+ * asks for `os: "latest"`. Comparison is on the digits inside the runtime
168
+ * key (e.g. "iOS-17-5" → 17.5).
169
+ */
170
+ function pickLatestByRuntime(devices) {
171
+ if (devices.length === 0)
172
+ return null;
173
+ let best = devices[0];
174
+ let bestKey = runtimeSortKey(best.runtime);
175
+ for (const dev of devices.slice(1)) {
176
+ const key = runtimeSortKey(dev.runtime);
177
+ if (key > bestKey) {
178
+ best = dev;
179
+ bestKey = key;
180
+ }
181
+ }
182
+ return best;
183
+ }
184
+ /** Pure: produce a sortable numeric key from a runtime identifier. */
185
+ export function runtimeSortKey(runtime) {
186
+ const match = runtime.match(/(\d+)[-.](\d+)(?:[-.](\d+))?/);
187
+ if (!match)
188
+ return 0;
189
+ const major = parseInt(match[1], 10);
190
+ const minor = parseInt(match[2], 10);
191
+ const patch = match[3] ? parseInt(match[3], 10) : 0;
192
+ return major * 10_000 + minor * 100 + patch;
193
+ }
194
+ //# sourceMappingURL=simctl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simctl.js","sourceRoot":"","sources":["../../src/runtime/simctl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Build, boot, install, and launch an iOS app on the iOS Simulator with
3
+ * `MallocStackLogging=1` (and any caller-supplied env vars) applied. Returns
4
+ * the host PID + simulator UDID + bundle id ready to chain into
5
+ * `captureMemgraph`.
6
+ *
7
+ * Why this exists: `leaks --outputGraph` regressed on macOS 26.x and aborts
8
+ * with `Failed to get DYLD info for task` when the target was not launched
9
+ * with malloc-stack-logging in its environment. Capturing a memgraph after
10
+ * the fact does not work; the env vars must be set at launch. This tool
11
+ * gives users a single MCP call that produces a leak-investigable process
12
+ * without requiring them to wire up xcodebuild + simctl manually.
13
+ *
14
+ * Out of scope: UI driving (Phase 3), composite snapshots (Phase 3), physical
15
+ * iOS devices (`leaks --outputGraph` does not support them).
16
+ */
17
+ import { z } from "zod";
18
+ import type { NextCallSuggestion } from "../types.js";
19
+ export declare const bootAndLaunchForLeakInvestigationShape: {
20
+ readonly workspace: z.ZodOptional<z.ZodString>;
21
+ readonly project: z.ZodOptional<z.ZodString>;
22
+ readonly scheme: z.ZodString;
23
+ readonly configuration: z.ZodDefault<z.ZodString>;
24
+ readonly bundleId: z.ZodOptional<z.ZodString>;
25
+ readonly derivedDataPath: z.ZodOptional<z.ZodString>;
26
+ readonly simulator: z.ZodOptional<z.ZodObject<{
27
+ udid: z.ZodOptional<z.ZodString>;
28
+ name: z.ZodOptional<z.ZodString>;
29
+ os: z.ZodOptional<z.ZodString>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ name?: string | undefined;
32
+ udid?: string | undefined;
33
+ os?: string | undefined;
34
+ }, {
35
+ name?: string | undefined;
36
+ udid?: string | undefined;
37
+ os?: string | undefined;
38
+ }>>;
39
+ readonly envVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
40
+ readonly launchArgs: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
41
+ readonly buildBeforeLaunch: z.ZodDefault<z.ZodBoolean>;
42
+ readonly warmupSeconds: z.ZodDefault<z.ZodNumber>;
43
+ };
44
+ export declare const bootAndLaunchForLeakInvestigationSchema: z.ZodEffects<z.ZodObject<{
45
+ readonly workspace: z.ZodOptional<z.ZodString>;
46
+ readonly project: z.ZodOptional<z.ZodString>;
47
+ readonly scheme: z.ZodString;
48
+ readonly configuration: z.ZodDefault<z.ZodString>;
49
+ readonly bundleId: z.ZodOptional<z.ZodString>;
50
+ readonly derivedDataPath: z.ZodOptional<z.ZodString>;
51
+ readonly simulator: z.ZodOptional<z.ZodObject<{
52
+ udid: z.ZodOptional<z.ZodString>;
53
+ name: z.ZodOptional<z.ZodString>;
54
+ os: z.ZodOptional<z.ZodString>;
55
+ }, "strip", z.ZodTypeAny, {
56
+ name?: string | undefined;
57
+ udid?: string | undefined;
58
+ os?: string | undefined;
59
+ }, {
60
+ name?: string | undefined;
61
+ udid?: string | undefined;
62
+ os?: string | undefined;
63
+ }>>;
64
+ readonly envVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
65
+ readonly launchArgs: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
66
+ readonly buildBeforeLaunch: z.ZodDefault<z.ZodBoolean>;
67
+ readonly warmupSeconds: z.ZodDefault<z.ZodNumber>;
68
+ }, "strip", z.ZodTypeAny, {
69
+ scheme: string;
70
+ configuration: string;
71
+ launchArgs: string[];
72
+ buildBeforeLaunch: boolean;
73
+ warmupSeconds: number;
74
+ simulator?: {
75
+ name?: string | undefined;
76
+ udid?: string | undefined;
77
+ os?: string | undefined;
78
+ } | undefined;
79
+ bundleId?: string | undefined;
80
+ workspace?: string | undefined;
81
+ project?: string | undefined;
82
+ derivedDataPath?: string | undefined;
83
+ envVars?: Record<string, string> | undefined;
84
+ }, {
85
+ scheme: string;
86
+ simulator?: {
87
+ name?: string | undefined;
88
+ udid?: string | undefined;
89
+ os?: string | undefined;
90
+ } | undefined;
91
+ bundleId?: string | undefined;
92
+ workspace?: string | undefined;
93
+ project?: string | undefined;
94
+ configuration?: string | undefined;
95
+ derivedDataPath?: string | undefined;
96
+ envVars?: Record<string, string> | undefined;
97
+ launchArgs?: string[] | undefined;
98
+ buildBeforeLaunch?: boolean | undefined;
99
+ warmupSeconds?: number | undefined;
100
+ }>, {
101
+ scheme: string;
102
+ configuration: string;
103
+ launchArgs: string[];
104
+ buildBeforeLaunch: boolean;
105
+ warmupSeconds: number;
106
+ simulator?: {
107
+ name?: string | undefined;
108
+ udid?: string | undefined;
109
+ os?: string | undefined;
110
+ } | undefined;
111
+ bundleId?: string | undefined;
112
+ workspace?: string | undefined;
113
+ project?: string | undefined;
114
+ derivedDataPath?: string | undefined;
115
+ envVars?: Record<string, string> | undefined;
116
+ }, {
117
+ scheme: string;
118
+ simulator?: {
119
+ name?: string | undefined;
120
+ udid?: string | undefined;
121
+ os?: string | undefined;
122
+ } | undefined;
123
+ bundleId?: string | undefined;
124
+ workspace?: string | undefined;
125
+ project?: string | undefined;
126
+ configuration?: string | undefined;
127
+ derivedDataPath?: string | undefined;
128
+ envVars?: Record<string, string> | undefined;
129
+ launchArgs?: string[] | undefined;
130
+ buildBeforeLaunch?: boolean | undefined;
131
+ warmupSeconds?: number | undefined;
132
+ }>;
133
+ export type BootAndLaunchForLeakInvestigationInput = z.infer<typeof bootAndLaunchForLeakInvestigationSchema>;
134
+ export type LaunchState = "launched" | "buildFailed" | "installFailed" | "launchFailed" | "pidNotFound" | "noSimulatorAvailable";
135
+ export interface BootAndLaunchForLeakInvestigationResult {
136
+ ok: boolean;
137
+ state: LaunchState;
138
+ simulatorUDID?: string;
139
+ pid?: number;
140
+ bundleId?: string;
141
+ appName?: string;
142
+ appPath?: string;
143
+ appliedEnvVars: Record<string, string>;
144
+ steps: string[];
145
+ warnings?: string[];
146
+ failureReason?: string;
147
+ suggestedNextCalls?: NextCallSuggestion[];
148
+ }
149
+ export declare function bootAndLaunchForLeakInvestigation(input: BootAndLaunchForLeakInvestigationInput): Promise<BootAndLaunchForLeakInvestigationResult>;
150
+ /**
151
+ * Resolve the host-side PID for an app running inside the target simulator.
152
+ *
153
+ * Strategy: list every host process with `ps -Ao pid,command`, then pick lines
154
+ * whose command path includes the simulator UDID (i.e. lives under
155
+ * `~/Library/Developer/CoreSimulator/Devices/<UDID>/...`) AND whose path ends
156
+ * with `/<EXECUTABLE_NAME>`. This handles two cases natively:
157
+ * 1. Multi-simulator: filtering by UDID disambiguates from same-named apps in other simulators.
158
+ * 2. Long executable names: we match on full path, so the 15-char `comm` truncation that breaks `pgrep -x` is irrelevant.
159
+ *
160
+ * Returns the PID, or null when nothing matched (the typical reasons: app
161
+ * still launching, or executable name does not appear in the path because
162
+ * the user passed a custom override).
163
+ */
164
+ export declare function resolveHostPid(udid: string, executableName: string): Promise<number | null>;
165
+ /** Pure: filter `ps` output for the simulator's app process. Exposed for tests. */
166
+ export declare function pickHostPidFromPs(psOutput: string, udid: string, executableName: string): number | null;