memorydetective 1.6.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.
- package/CHANGELOG.md +49 -0
- package/README.md +76 -34
- package/USAGE.md +112 -41
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/axe.d.ts +86 -0
- package/dist/runtime/axe.js +249 -0
- package/dist/runtime/axe.js.map +1 -0
- package/dist/runtime/buildSettings.d.ts +27 -0
- package/dist/runtime/buildSettings.js +88 -0
- package/dist/runtime/buildSettings.js.map +1 -0
- package/dist/runtime/exec.d.ts +8 -1
- package/dist/runtime/exec.js +8 -2
- package/dist/runtime/exec.js.map +1 -1
- package/dist/runtime/fixTemplates.d.ts +27 -0
- package/dist/runtime/fixTemplates.js +757 -0
- package/dist/runtime/fixTemplates.js.map +1 -0
- package/dist/runtime/simctl.d.ts +68 -0
- package/dist/runtime/simctl.js +194 -0
- package/dist/runtime/simctl.js.map +1 -0
- package/dist/runtime/staticAnalysisHints.js +8 -0
- package/dist/runtime/staticAnalysisHints.js.map +1 -1
- package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +166 -0
- package/dist/tools/bootAndLaunchForLeakInvestigation.js +367 -0
- package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -0
- package/dist/tools/captureMemgraph.d.ts +29 -1
- package/dist/tools/captureMemgraph.js +148 -6
- package/dist/tools/captureMemgraph.js.map +1 -1
- package/dist/tools/captureScenarioState.d.ts +77 -0
- package/dist/tools/captureScenarioState.js +159 -0
- package/dist/tools/captureScenarioState.js.map +1 -0
- package/dist/tools/classifyCycle.d.ts +7 -0
- package/dist/tools/classifyCycle.js +31 -0
- package/dist/tools/classifyCycle.js.map +1 -1
- package/dist/tools/compareTracesByPattern.d.ts +112 -0
- package/dist/tools/compareTracesByPattern.js +312 -0
- package/dist/tools/compareTracesByPattern.js.map +1 -0
- package/dist/tools/detectLeaksInXCUITest.d.ts +2 -2
- package/dist/tools/getInvestigationPlaybook.d.ts +15 -0
- package/dist/tools/getInvestigationPlaybook.js +24 -1
- package/dist/tools/getInvestigationPlaybook.js.map +1 -1
- package/dist/tools/replayScenario.d.ts +243 -0
- package/dist/tools/replayScenario.js +187 -0
- package/dist/tools/replayScenario.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal infrastructure for driving the iOS Simulator UI from inside leak/perf
|
|
3
|
+
* workflows (replayScenario, captureScenarioState). NOT a generic UI automation
|
|
4
|
+
* surface, the public tool contract stays scoped to leak/perf debug.
|
|
5
|
+
*
|
|
6
|
+
* Wraps Cameron Cooke's `axe` CLI (https://github.com/cameroncooke/AXe).
|
|
7
|
+
* `axe` is a soft dependency: we detect it on first use and emit a structured
|
|
8
|
+
* install hint when missing, instead of hard-failing or bundling it.
|
|
9
|
+
*/
|
|
10
|
+
import { runCommand } from "./exec.js";
|
|
11
|
+
const AXE_INSTALL_HINT = "axe CLI not found in PATH. Install with `brew install cameroncooke/axe/axe` (https://github.com/cameroncooke/AXe). axe is a soft dependency, only required for replayScenario and captureScenarioState.";
|
|
12
|
+
/**
|
|
13
|
+
* Check whether `axe` is available on the system. Cached at module load via
|
|
14
|
+
* `which` lookup so callers can skip the call when they already know.
|
|
15
|
+
*/
|
|
16
|
+
export async function checkAxeAvailable() {
|
|
17
|
+
const result = await runCommand("which", ["axe"], { timeoutMs: 5_000 });
|
|
18
|
+
if (result.code !== 0) {
|
|
19
|
+
return { available: false, installHint: AXE_INSTALL_HINT };
|
|
20
|
+
}
|
|
21
|
+
const binaryPath = result.stdout.trim();
|
|
22
|
+
if (!binaryPath) {
|
|
23
|
+
return { available: false, installHint: AXE_INSTALL_HINT };
|
|
24
|
+
}
|
|
25
|
+
return { available: true, binaryPath };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run `axe describe-ui --udid <udid>` and parse the tree into normalized
|
|
29
|
+
* `UIElement` nodes. Returns the root element.
|
|
30
|
+
*/
|
|
31
|
+
export async function describeUI(udid) {
|
|
32
|
+
const result = await runCommand("axe", ["describe-ui", "--udid", udid], {
|
|
33
|
+
timeoutMs: 30_000,
|
|
34
|
+
});
|
|
35
|
+
if (result.code !== 0) {
|
|
36
|
+
throw new Error(`axe describe-ui --udid ${udid} failed (code ${result.code}): ${result.stderr || result.stdout}`);
|
|
37
|
+
}
|
|
38
|
+
return parseAxeDescribeUI(result.stdout);
|
|
39
|
+
}
|
|
40
|
+
/** Pure: parse `axe describe-ui` JSON output into normalized UIElement tree. */
|
|
41
|
+
export function parseAxeDescribeUI(stdout) {
|
|
42
|
+
const sliced = sliceJsonValue(stdout);
|
|
43
|
+
if (!sliced) {
|
|
44
|
+
throw new Error("axe describe-ui output did not contain a JSON object/array. Stdout begins: " +
|
|
45
|
+
stdout.slice(0, 200));
|
|
46
|
+
}
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = JSON.parse(sliced);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new Error(`axe describe-ui output failed JSON.parse: ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
return normalizeAxeNode(parsed);
|
|
55
|
+
}
|
|
56
|
+
function sliceJsonValue(stdout) {
|
|
57
|
+
const trimmed = stdout.trim();
|
|
58
|
+
if (!trimmed)
|
|
59
|
+
return null;
|
|
60
|
+
// axe emits a JSON object as the root.
|
|
61
|
+
const objStart = trimmed.indexOf("{");
|
|
62
|
+
const objEnd = trimmed.lastIndexOf("}");
|
|
63
|
+
if (objStart !== -1 && objEnd > objStart) {
|
|
64
|
+
return trimmed.slice(objStart, objEnd + 1);
|
|
65
|
+
}
|
|
66
|
+
const arrStart = trimmed.indexOf("[");
|
|
67
|
+
const arrEnd = trimmed.lastIndexOf("]");
|
|
68
|
+
if (arrStart !== -1 && arrEnd > arrStart) {
|
|
69
|
+
return trimmed.slice(arrStart, arrEnd + 1);
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function normalizeAxeNode(raw) {
|
|
74
|
+
if (raw == null || typeof raw !== "object") {
|
|
75
|
+
return { raw: {} };
|
|
76
|
+
}
|
|
77
|
+
const obj = raw;
|
|
78
|
+
const out = { raw: obj };
|
|
79
|
+
const labelKey = pickFirstString(obj, [
|
|
80
|
+
"AXLabel",
|
|
81
|
+
"label",
|
|
82
|
+
"name",
|
|
83
|
+
"title",
|
|
84
|
+
]);
|
|
85
|
+
if (labelKey != null)
|
|
86
|
+
out.label = labelKey;
|
|
87
|
+
const idKey = pickFirstString(obj, ["AXIdentifier", "identifier", "id"]);
|
|
88
|
+
if (idKey != null)
|
|
89
|
+
out.identifier = idKey;
|
|
90
|
+
const roleKey = pickFirstString(obj, ["AXRole", "role", "type"]);
|
|
91
|
+
if (roleKey != null)
|
|
92
|
+
out.role = roleKey;
|
|
93
|
+
const frame = parseAxFrame(obj);
|
|
94
|
+
if (frame)
|
|
95
|
+
out.frame = frame;
|
|
96
|
+
const childrenRaw = obj.children ??
|
|
97
|
+
obj.AXChildren ??
|
|
98
|
+
obj.subviews;
|
|
99
|
+
if (Array.isArray(childrenRaw)) {
|
|
100
|
+
out.children = childrenRaw.map((c) => normalizeAxeNode(c));
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function pickFirstString(obj, keys) {
|
|
105
|
+
for (const key of keys) {
|
|
106
|
+
const value = obj[key];
|
|
107
|
+
if (typeof value === "string" && value.length > 0) {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Pure: parse an `AXFrame`-style frame from a node. Accepts both AppKit-style
|
|
115
|
+
* dictionaries `{AXX: 0, AXY: 0, AXWidth: 100, AXHeight: 100}` and CGRect
|
|
116
|
+
* strings `"{{0,0},{100,100}}"`.
|
|
117
|
+
*/
|
|
118
|
+
export function parseAxFrame(obj) {
|
|
119
|
+
const direct = obj.frame ?? obj.AXFrame;
|
|
120
|
+
if (typeof direct === "string") {
|
|
121
|
+
return parseFrameString(direct);
|
|
122
|
+
}
|
|
123
|
+
if (direct && typeof direct === "object") {
|
|
124
|
+
const f = direct;
|
|
125
|
+
const x = pickFirstNumber(f, ["x", "AXX", "X"]);
|
|
126
|
+
const y = pickFirstNumber(f, ["y", "AXY", "Y"]);
|
|
127
|
+
const w = pickFirstNumber(f, ["width", "AXWidth", "Width"]);
|
|
128
|
+
const h = pickFirstNumber(f, ["height", "AXHeight", "Height"]);
|
|
129
|
+
if (x != null && y != null && w != null && h != null) {
|
|
130
|
+
return { x, y, width: w, height: h };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Direct top-level keys, AppKit/AX style flat.
|
|
134
|
+
const x = pickFirstNumber(obj, ["AXX", "frameX"]);
|
|
135
|
+
const y = pickFirstNumber(obj, ["AXY", "frameY"]);
|
|
136
|
+
const w = pickFirstNumber(obj, ["AXWidth", "frameWidth"]);
|
|
137
|
+
const h = pickFirstNumber(obj, ["AXHeight", "frameHeight"]);
|
|
138
|
+
if (x != null && y != null && w != null && h != null) {
|
|
139
|
+
return { x, y, width: w, height: h };
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
function pickFirstNumber(obj, keys) {
|
|
144
|
+
for (const key of keys) {
|
|
145
|
+
const value = obj[key];
|
|
146
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function parseFrameString(s) {
|
|
153
|
+
const match = s.match(/\{\{\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\}\s*,\s*\{\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\}\s*\}/);
|
|
154
|
+
if (!match)
|
|
155
|
+
return undefined;
|
|
156
|
+
return {
|
|
157
|
+
x: parseFloat(match[1]),
|
|
158
|
+
y: parseFloat(match[2]),
|
|
159
|
+
width: parseFloat(match[3]),
|
|
160
|
+
height: parseFloat(match[4]),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Pure: find the first descendant whose label or identifier matches `query`
|
|
165
|
+
* (exact match preferred, falls back to substring). Walks the tree
|
|
166
|
+
* depth-first.
|
|
167
|
+
*/
|
|
168
|
+
export function findElementByLabel(root, query) {
|
|
169
|
+
const exact = findFirst(root, (el) => el.label === query || el.identifier === query);
|
|
170
|
+
if (exact)
|
|
171
|
+
return exact;
|
|
172
|
+
return findFirst(root, (el) => (el.label != null && el.label.includes(query)) ||
|
|
173
|
+
(el.identifier != null && el.identifier.includes(query)));
|
|
174
|
+
}
|
|
175
|
+
function findFirst(root, predicate) {
|
|
176
|
+
if (predicate(root))
|
|
177
|
+
return root;
|
|
178
|
+
if (!root.children)
|
|
179
|
+
return null;
|
|
180
|
+
for (const child of root.children) {
|
|
181
|
+
const hit = findFirst(child, predicate);
|
|
182
|
+
if (hit)
|
|
183
|
+
return hit;
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
/** Pure: compute the center point of an `AxFrame` for tap targeting. */
|
|
188
|
+
export function centerOf(frame) {
|
|
189
|
+
return {
|
|
190
|
+
x: Math.round(frame.x + frame.width / 2),
|
|
191
|
+
y: Math.round(frame.y + frame.height / 2),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Tap on the simulator. Resolves label/elementId targets via `describe-ui`
|
|
196
|
+
* before issuing the tap.
|
|
197
|
+
*/
|
|
198
|
+
export async function tap(udid, target) {
|
|
199
|
+
let coords;
|
|
200
|
+
if (target.kind === "coords") {
|
|
201
|
+
coords = { x: target.x, y: target.y };
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const tree = await describeUI(udid);
|
|
205
|
+
const query = target.value;
|
|
206
|
+
const el = findElementByLabel(tree, query);
|
|
207
|
+
if (!el || !el.frame) {
|
|
208
|
+
throw new Error(`Could not locate element matching "${query}" in the current UI tree, or the element has no frame metadata.`);
|
|
209
|
+
}
|
|
210
|
+
coords = centerOf(el.frame);
|
|
211
|
+
}
|
|
212
|
+
const result = await runCommand("axe", [
|
|
213
|
+
"tap",
|
|
214
|
+
"--udid",
|
|
215
|
+
udid,
|
|
216
|
+
"-x",
|
|
217
|
+
String(coords.x),
|
|
218
|
+
"-y",
|
|
219
|
+
String(coords.y),
|
|
220
|
+
], { timeoutMs: 10_000 });
|
|
221
|
+
if (result.code !== 0) {
|
|
222
|
+
throw new Error(`axe tap (${coords.x},${coords.y}) failed (code ${result.code}): ${result.stderr || result.stdout}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** Swipe between two coordinates with optional duration in milliseconds. */
|
|
226
|
+
export async function swipe(udid, from, to, durationMs = 250) {
|
|
227
|
+
const result = await runCommand("axe", [
|
|
228
|
+
"swipe",
|
|
229
|
+
"--udid",
|
|
230
|
+
udid,
|
|
231
|
+
"--from",
|
|
232
|
+
`${from.x},${from.y}`,
|
|
233
|
+
"--to",
|
|
234
|
+
`${to.x},${to.y}`,
|
|
235
|
+
"--duration",
|
|
236
|
+
String(durationMs),
|
|
237
|
+
], { timeoutMs: 15_000 });
|
|
238
|
+
if (result.code !== 0) {
|
|
239
|
+
throw new Error(`axe swipe failed (code ${result.code}): ${result.stderr || result.stdout}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** Type a string into the currently focused field. */
|
|
243
|
+
export async function typeText(udid, text) {
|
|
244
|
+
const result = await runCommand("axe", ["type", "--udid", udid, "--text", text], { timeoutMs: 15_000 });
|
|
245
|
+
if (result.code !== 0) {
|
|
246
|
+
throw new Error(`axe type failed (code ${result.code}): ${result.stderr || result.stdout}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=axe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"axe.js","sourceRoot":"","sources":["../../src/runtime/axe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parser for `xcodebuild -showBuildSettings -json` output.
|
|
3
|
+
*
|
|
4
|
+
* `xcodebuild` emits a JSON array of `{action, target, buildSettings}` objects
|
|
5
|
+
* on stdout, but may print non-JSON warnings on stderr/stdout before or after
|
|
6
|
+
* the array. We slice from the first `[` to the last `]` to extract the JSON
|
|
7
|
+
* payload defensively.
|
|
8
|
+
*
|
|
9
|
+
* When multiple targets exist (e.g. an aggregate target alongside the app
|
|
10
|
+
* target), we pick the one whose build settings declare `WRAPPER_EXTENSION === "app"`,
|
|
11
|
+
* which is the actual application bundle.
|
|
12
|
+
*/
|
|
13
|
+
export interface BuildSettings {
|
|
14
|
+
/** Absolute path to the build products directory (where the .app lands). */
|
|
15
|
+
builtProductsDir: string;
|
|
16
|
+
/** Bundle wrapper name, e.g. `MyApp.app`. */
|
|
17
|
+
wrapperName: string;
|
|
18
|
+
/** Executable name inside the bundle, e.g. `MyApp`. Used for `pgrep -ax`. */
|
|
19
|
+
executableName: string;
|
|
20
|
+
/** App bundle identifier, e.g. `com.example.MyApp`. */
|
|
21
|
+
productBundleIdentifier: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse `xcodebuild -showBuildSettings -json` stdout. Throws when the output
|
|
25
|
+
* cannot be parsed or when the matched target is missing required keys.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseBuildSettingsJson(stdout: string): BuildSettings;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parser for `xcodebuild -showBuildSettings -json` output.
|
|
3
|
+
*
|
|
4
|
+
* `xcodebuild` emits a JSON array of `{action, target, buildSettings}` objects
|
|
5
|
+
* on stdout, but may print non-JSON warnings on stderr/stdout before or after
|
|
6
|
+
* the array. We slice from the first `[` to the last `]` to extract the JSON
|
|
7
|
+
* payload defensively.
|
|
8
|
+
*
|
|
9
|
+
* When multiple targets exist (e.g. an aggregate target alongside the app
|
|
10
|
+
* target), we pick the one whose build settings declare `WRAPPER_EXTENSION === "app"`,
|
|
11
|
+
* which is the actual application bundle.
|
|
12
|
+
*/
|
|
13
|
+
const REQUIRED_KEYS = [
|
|
14
|
+
"builtProductsDir",
|
|
15
|
+
"wrapperName",
|
|
16
|
+
"executableName",
|
|
17
|
+
"productBundleIdentifier",
|
|
18
|
+
];
|
|
19
|
+
const KEY_MAP = {
|
|
20
|
+
builtProductsDir: "BUILT_PRODUCTS_DIR",
|
|
21
|
+
wrapperName: "WRAPPER_NAME",
|
|
22
|
+
executableName: "EXECUTABLE_NAME",
|
|
23
|
+
productBundleIdentifier: "PRODUCT_BUNDLE_IDENTIFIER",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Parse `xcodebuild -showBuildSettings -json` stdout. Throws when the output
|
|
27
|
+
* cannot be parsed or when the matched target is missing required keys.
|
|
28
|
+
*/
|
|
29
|
+
export function parseBuildSettingsJson(stdout) {
|
|
30
|
+
const sliced = sliceJsonArray(stdout);
|
|
31
|
+
if (!sliced) {
|
|
32
|
+
throw new Error("xcodebuild -showBuildSettings -json output did not contain a JSON array. Stdout begins: " +
|
|
33
|
+
stdout.slice(0, 200));
|
|
34
|
+
}
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(sliced);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw new Error(`xcodebuild -showBuildSettings -json output failed JSON.parse: ${err.message}`);
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
43
|
+
throw new Error("xcodebuild -showBuildSettings -json returned an empty array — no targets found for the given scheme/destination.");
|
|
44
|
+
}
|
|
45
|
+
const entries = parsed;
|
|
46
|
+
const appEntry = pickAppTarget(entries);
|
|
47
|
+
if (!appEntry || !appEntry.buildSettings) {
|
|
48
|
+
throw new Error("Could not find a target with WRAPPER_EXTENSION=app in -showBuildSettings output. Verify the scheme builds an iOS application bundle.");
|
|
49
|
+
}
|
|
50
|
+
const settings = appEntry.buildSettings;
|
|
51
|
+
const result = {};
|
|
52
|
+
for (const key of REQUIRED_KEYS) {
|
|
53
|
+
const xcodeKey = KEY_MAP[key];
|
|
54
|
+
const value = settings[xcodeKey];
|
|
55
|
+
if (!value || value.length === 0) {
|
|
56
|
+
throw new Error(`xcodebuild -showBuildSettings missing required key '${xcodeKey}' for target '${appEntry.target ?? "<unknown>"}'. Cannot proceed without this value.`);
|
|
57
|
+
}
|
|
58
|
+
result[key] = value;
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Locate the JSON array within stdout. Returns the substring `[...]` or null
|
|
64
|
+
* when no balanced array is found.
|
|
65
|
+
*/
|
|
66
|
+
function sliceJsonArray(stdout) {
|
|
67
|
+
const start = stdout.indexOf("[");
|
|
68
|
+
if (start === -1)
|
|
69
|
+
return null;
|
|
70
|
+
const end = stdout.lastIndexOf("]");
|
|
71
|
+
if (end === -1 || end <= start)
|
|
72
|
+
return null;
|
|
73
|
+
return stdout.slice(start, end + 1);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Pick the entry whose build settings indicate an application bundle. When
|
|
77
|
+
* multiple match, the first wins — this is rare in practice and the caller
|
|
78
|
+
* can always pass a more specific scheme to disambiguate.
|
|
79
|
+
*/
|
|
80
|
+
function pickAppTarget(entries) {
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
if (entry.buildSettings?.WRAPPER_EXTENSION === "app") {
|
|
83
|
+
return entry;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=buildSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildSettings.js","sourceRoot":"","sources":["../../src/runtime/buildSettings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAmBH,MAAM,aAAa,GAA+B;IAChD,kBAAkB;IAClB,aAAa;IACb,gBAAgB;IAChB,yBAAyB;CAC1B,CAAC;AAEF,MAAM,OAAO,GAAwC;IACnD,gBAAgB,EAAE,oBAAoB;IACtC,WAAW,EAAE,cAAc;IAC3B,cAAc,EAAE,iBAAiB;IACjC,uBAAuB,EAAE,2BAA2B;CACrD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CACvB,CAAC;IACJ,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,iEAAkE,GAAa,CAAC,OAAO,EAAE,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,kHAAkH,CACnH,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAmC,CAAC;IACpD,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CACb,sIAAsI,CACvI,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC;IACxC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,uDAAuD,QAAQ,iBAAiB,QAAQ,CAAC,MAAM,IAAI,WAAW,uCAAuC,CACtJ,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC;IACD,OAAO,MAAuB,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CACpB,OAAkC;IAElC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,aAAa,EAAE,iBAAiB,KAAK,KAAK,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/runtime/exec.d.ts
CHANGED
|
@@ -8,9 +8,16 @@ export interface RunCommandOptions {
|
|
|
8
8
|
cwd?: string;
|
|
9
9
|
/** Timeout in ms (kill the child if it exceeds this). */
|
|
10
10
|
timeoutMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Extra environment variables to expose to the child process. When provided,
|
|
13
|
+
* these are MERGED on top of `process.env` (PATH, DEVELOPER_DIR, HOME and
|
|
14
|
+
* other inherited vars are preserved). Pass an empty object to inherit
|
|
15
|
+
* unchanged; pass `undefined` (or omit) for the same default.
|
|
16
|
+
*/
|
|
17
|
+
env?: Record<string, string>;
|
|
11
18
|
}
|
|
12
19
|
/**
|
|
13
|
-
* Run a command and collect stdout/stderr. Does not throw on non-zero exit code
|
|
20
|
+
* Run a command and collect stdout/stderr. Does not throw on non-zero exit code,
|
|
14
21
|
* the caller decides what's acceptable (e.g. `leaks` exits 1 when leaks are found,
|
|
15
22
|
* which is normal).
|
|
16
23
|
*/
|
package/dist/runtime/exec.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
/**
|
|
3
|
-
* Run a command and collect stdout/stderr. Does not throw on non-zero exit code
|
|
3
|
+
* Run a command and collect stdout/stderr. Does not throw on non-zero exit code,
|
|
4
4
|
* the caller decides what's acceptable (e.g. `leaks` exits 1 when leaks are found,
|
|
5
5
|
* which is normal).
|
|
6
6
|
*/
|
|
7
7
|
export function runCommand(cmd, args, opts = {}) {
|
|
8
8
|
return new Promise((resolve, reject) => {
|
|
9
|
-
const
|
|
9
|
+
const env = opts.env
|
|
10
|
+
? { ...process.env, ...opts.env }
|
|
11
|
+
: undefined;
|
|
12
|
+
const child = spawn(cmd, args, {
|
|
13
|
+
cwd: opts.cwd,
|
|
14
|
+
...(env ? { env } : {}),
|
|
15
|
+
});
|
|
10
16
|
let stdout = "";
|
|
11
17
|
let stderr = "";
|
|
12
18
|
let killedByTimeout = false;
|
package/dist/runtime/exec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/runtime/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/runtime/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAsB3C;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,GAAW,EACX,IAAc,EACd,OAA0B,EAAE;IAE5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG;YAClB,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;YACjC,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxB,CAAC,CAAC;QACH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,KAAiC,CAAC;QAEtC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;YACzC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,eAAe,GAAG,IAAI,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,eAAe,EAAE,CAAC;gBACpB,MAAM,CACJ,IAAI,KAAK,CACP,2BAA2B,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACxE,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-pattern fix templates: Swift code snippets showing the typical
|
|
3
|
+
* before/after for each cycle pattern in the catalog. Pairs with
|
|
4
|
+
* `staticAnalysisHints.ts` and `classifyCycle.PATTERNS` to give the
|
|
5
|
+
* agent a concrete code example it can adapt to the user's context.
|
|
6
|
+
*
|
|
7
|
+
* Templates are deliberately minimal — just enough to demonstrate the
|
|
8
|
+
* shape of the fix. The agent fills in real type/method names from the
|
|
9
|
+
* surrounding code via the SourceKit-LSP tools.
|
|
10
|
+
*/
|
|
11
|
+
export interface FixTemplate {
|
|
12
|
+
/** Optional one-line description if the snippet alone needs framing. */
|
|
13
|
+
context?: string;
|
|
14
|
+
/** Code that shows the leak shape. */
|
|
15
|
+
before: string;
|
|
16
|
+
/** Code that fixes it. */
|
|
17
|
+
after: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional notes — e.g. "this only works on iOS 14+" or
|
|
20
|
+
* "the WeakProxy class is a one-time helper you add to your codebase".
|
|
21
|
+
*/
|
|
22
|
+
notes?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Returns the fix template for a given pattern, or null if unknown. */
|
|
25
|
+
export declare function getFixTemplate(patternId: string): FixTemplate | null;
|
|
26
|
+
/** All known pattern ids that have templates. Used in tests for coverage assertion. */
|
|
27
|
+
export declare function knownTemplatePatternIds(): string[];
|