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.
- package/CHANGELOG.md +75 -1
- package/README.md +84 -15
- package/USAGE.md +27 -4
- package/dist/cli.js +106 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +42 -22
- package/dist/index.js.map +1 -1
- package/dist/parsers/referenceTree.d.ts +111 -0
- package/dist/parsers/referenceTree.js +328 -0
- package/dist/parsers/referenceTree.js.map +1 -0
- package/dist/runtime/axe.js +6 -1
- package/dist/runtime/axe.js.map +1 -1
- package/dist/runtime/exec.d.ts +30 -0
- package/dist/runtime/exec.js +30 -3
- package/dist/runtime/exec.js.map +1 -1
- package/dist/runtime/fixTemplates.js +67 -0
- package/dist/runtime/fixTemplates.js.map +1 -1
- package/dist/runtime/leakReport.d.ts +62 -0
- package/dist/runtime/leakReport.js +138 -0
- package/dist/runtime/leakReport.js.map +1 -0
- package/dist/runtime/platformCheck.d.ts +54 -0
- package/dist/runtime/platformCheck.js +94 -0
- package/dist/runtime/platformCheck.js.map +1 -0
- package/dist/runtime/redact.d.ts +66 -0
- package/dist/runtime/redact.js +146 -0
- package/dist/runtime/redact.js.map +1 -0
- package/dist/runtime/responseFormatter.d.ts +78 -0
- package/dist/runtime/responseFormatter.js +307 -0
- package/dist/runtime/responseFormatter.js.map +1 -0
- package/dist/runtime/securityFlags.d.ts +74 -0
- package/dist/runtime/securityFlags.js +90 -0
- package/dist/runtime/securityFlags.js.map +1 -0
- package/dist/runtime/staticAnalysisHints.js +14 -1
- package/dist/runtime/staticAnalysisHints.js.map +1 -1
- package/dist/templates/leak-report.html +39 -0
- package/dist/templates/templates/leak-report.html +39 -0
- package/dist/tools/analyzeAbandonedMemory.d.ts +162 -0
- package/dist/tools/analyzeAbandonedMemory.js +325 -0
- package/dist/tools/analyzeAbandonedMemory.js.map +1 -0
- package/dist/tools/analyzeAllocations.d.ts +11 -2
- package/dist/tools/analyzeAllocations.js +4 -0
- package/dist/tools/analyzeAllocations.js.map +1 -1
- package/dist/tools/analyzeAnimationHitches.d.ts +32 -2
- package/dist/tools/analyzeAnimationHitches.js +25 -4
- package/dist/tools/analyzeAnimationHitches.js.map +1 -1
- package/dist/tools/analyzeAppLaunch.d.ts +3 -0
- package/dist/tools/analyzeAppLaunch.js +2 -0
- package/dist/tools/analyzeAppLaunch.js.map +1 -1
- package/dist/tools/analyzeHangs.d.ts +78 -2
- package/dist/tools/analyzeHangs.js +117 -4
- package/dist/tools/analyzeHangs.js.map +1 -1
- package/dist/tools/analyzeMemgraph.d.ts +40 -1
- package/dist/tools/analyzeMemgraph.js +66 -2
- package/dist/tools/analyzeMemgraph.js.map +1 -1
- package/dist/tools/analyzeTimeProfile.d.ts +11 -1
- package/dist/tools/analyzeTimeProfile.js +5 -0
- package/dist/tools/analyzeTimeProfile.js.map +1 -1
- package/dist/tools/bootAndLaunchForLeakInvestigation.d.ts +18 -9
- package/dist/tools/bootAndLaunchForLeakInvestigation.js +27 -0
- package/dist/tools/bootAndLaunchForLeakInvestigation.js.map +1 -1
- package/dist/tools/captureMemgraph.d.ts +22 -4
- package/dist/tools/captureMemgraph.js +42 -9
- package/dist/tools/captureMemgraph.js.map +1 -1
- package/dist/tools/captureScenarioState.d.ts +12 -4
- package/dist/tools/captureScenarioState.js +4 -0
- package/dist/tools/captureScenarioState.js.map +1 -1
- package/dist/tools/classifyCycle.js +77 -0
- package/dist/tools/classifyCycle.js.map +1 -1
- package/dist/tools/cleanupTraces.d.ts +87 -0
- package/dist/tools/cleanupTraces.js +232 -0
- package/dist/tools/cleanupTraces.js.map +1 -0
- package/dist/tools/compareTracesByPattern.d.ts +2 -2
- package/dist/tools/detectLeaksInXCTest.d.ts +116 -0
- package/dist/tools/detectLeaksInXCTest.js +311 -0
- package/dist/tools/detectLeaksInXCTest.js.map +1 -0
- package/dist/tools/detectLeaksInXCUITest.d.ts +8 -3
- package/dist/tools/detectLeaksInXCUITest.js +30 -4
- package/dist/tools/detectLeaksInXCUITest.js.map +1 -1
- package/dist/tools/diffMemgraphs.d.ts +5 -2
- package/dist/tools/diffMemgraphs.js +2 -0
- package/dist/tools/diffMemgraphs.js.map +1 -1
- package/dist/tools/findCycles.d.ts +1 -1
- package/dist/tools/recordTimeProfile.d.ts +29 -9
- package/dist/tools/recordTimeProfile.js +70 -7
- package/dist/tools/recordTimeProfile.js.map +1 -1
- package/dist/tools/renderCycleGraph.d.ts +1 -1
- package/dist/tools/verifyFix.d.ts +2 -2
- package/dist/types.d.ts +24 -1
- package/package.json +3 -3
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared response formatter for MCP tool outputs.
|
|
3
|
+
*
|
|
4
|
+
* Tools can declare `outputFormat?: "markdown" | "json" | "both"` (defaults to
|
|
5
|
+
* `"json"`, preserving v1.8 behavior). When the caller asks for a different
|
|
6
|
+
* shape, the registration wrapper in `src/index.ts` calls
|
|
7
|
+
* {@link formatMcpResponse} to produce the actual response content.
|
|
8
|
+
*
|
|
9
|
+
* Two audiences are served at once:
|
|
10
|
+
* - **AI agents** (the typical caller) want raw JSON they can parse and chain
|
|
11
|
+
* into the next call without re-reasoning.
|
|
12
|
+
* - **Humans reading the same response** (the typical second audience: the
|
|
13
|
+
* dev pasting the result into a PR comment, a Slack thread, or a Jira
|
|
14
|
+
* ticket) want a markdown view that highlights the key fields.
|
|
15
|
+
*
|
|
16
|
+
* `outputFormat: "both"` returns BOTH content items in a single response, so a
|
|
17
|
+
* client can display the markdown to the user AND parse the JSON for the
|
|
18
|
+
* agent loop without an extra round-trip.
|
|
19
|
+
*/
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { getRedactionMode, redact } from "./redact.js";
|
|
22
|
+
export const outputFormatField = z
|
|
23
|
+
.enum(["markdown", "json", "both", "verify-fix-table"])
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Response format. Omitted or `json` (default, preserves v1.8 behavior) returns JSON.stringify of the result. `markdown` renders a human-readable view of the same data. `both` returns both content items in one response, so a client can display markdown to the user and parse JSON for the agent loop without a second call. `verify-fix-table` (v1.10, applies to `analyzeAbandonedMemory` and `diffMemgraphs`) emits a focused 4-column markdown comparison table (Class | Before | After | Delta) of the actionable rows; other tools fall back to `markdown` for this value.");
|
|
26
|
+
/**
|
|
27
|
+
* Pure: shape the MCP response based on the caller's `outputFormat`.
|
|
28
|
+
*
|
|
29
|
+
* For `json` and `both`, the JSON is `JSON.stringify(result, null, 2)`. For
|
|
30
|
+
* `markdown` and `both`, the markdown is rendered via {@link renderAsMarkdown}.
|
|
31
|
+
*/
|
|
32
|
+
export function formatMcpResponse(result, toolName, format) {
|
|
33
|
+
const mode = format ?? "json";
|
|
34
|
+
// Redaction happens at the structured-value level so both the JSON
|
|
35
|
+
// and the markdown views are scrubbed consistently. `off` short-circuits
|
|
36
|
+
// and returns the input unchanged; `balanced` (default) masks home-dir
|
|
37
|
+
// paths and common secret-shaped tokens; `strict` also masks hostnames,
|
|
38
|
+
// IPs, and bundle identifiers. See src/runtime/redact.ts.
|
|
39
|
+
const redacted = redact(result, getRedactionMode());
|
|
40
|
+
const json = JSON.stringify(redacted, null, 2);
|
|
41
|
+
if (mode === "json") {
|
|
42
|
+
return { content: [{ type: "text", text: json }] };
|
|
43
|
+
}
|
|
44
|
+
// `verify-fix-table` is a focused renderer that takes precedence over
|
|
45
|
+
// the generic markdown one. Falls back to standard markdown when the
|
|
46
|
+
// tool does not implement a verify-fix view.
|
|
47
|
+
if (mode === "verify-fix-table") {
|
|
48
|
+
const focused = renderVerifyFixTable(redacted, toolName);
|
|
49
|
+
if (focused != null) {
|
|
50
|
+
return { content: [{ type: "text", text: focused }] };
|
|
51
|
+
}
|
|
52
|
+
// Fall through to markdown for tools that don't implement it.
|
|
53
|
+
const fallback = renderAsMarkdown(redacted, toolName);
|
|
54
|
+
return { content: [{ type: "text", text: fallback }] };
|
|
55
|
+
}
|
|
56
|
+
const markdown = renderAsMarkdown(redacted, toolName);
|
|
57
|
+
if (mode === "markdown") {
|
|
58
|
+
return { content: [{ type: "text", text: markdown }] };
|
|
59
|
+
}
|
|
60
|
+
// "both": markdown first so a UI that picks content[0] gets the readable
|
|
61
|
+
// view, then JSON so an agent looking for the structured data finds it
|
|
62
|
+
// without having to parse the markdown.
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{ type: "text", text: markdown },
|
|
66
|
+
{ type: "text", text: json },
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pure: render a verify-fix focused markdown table for tools that support
|
|
72
|
+
* it. Returns `null` if the tool's result does not match the expected
|
|
73
|
+
* verify-fix shape, signaling the caller to fall back to standard markdown.
|
|
74
|
+
*
|
|
75
|
+
* Supported tools:
|
|
76
|
+
*
|
|
77
|
+
* - `analyzeAbandonedMemory`: reads `actionableShrinkage[]` (the v1.10
|
|
78
|
+
* verify-fix-default direction: classes that the fix freed) and
|
|
79
|
+
* `actionableGrowth[]` (regressions the fix didn't address). Emits one
|
|
80
|
+
* table for shrinkage and, when non-empty, a second smaller table for
|
|
81
|
+
* growth. Threshold: |delta| >= 10 by default to filter cosmetic noise.
|
|
82
|
+
*
|
|
83
|
+
* - `diffMemgraphs`: reads `classCountChanges[]` (positive + negative).
|
|
84
|
+
* Future expansion; for now returns null and falls back to standard
|
|
85
|
+
* markdown.
|
|
86
|
+
*
|
|
87
|
+
* The 4-column layout is deliberately compact (Class | Before | After |
|
|
88
|
+
* Delta) so it renders cleanly in GitHub's markdown preview, dev.to, and
|
|
89
|
+
* agent chat contexts. A trailing `> Diagnosis: ...` blockquote carries
|
|
90
|
+
* the structured `diagnosis` field when present.
|
|
91
|
+
*/
|
|
92
|
+
export function renderVerifyFixTable(result, toolName) {
|
|
93
|
+
if (toolName !== "analyzeAbandonedMemory") {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (result == null || typeof result !== "object") {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const obj = result;
|
|
100
|
+
const shrinkage = extractVerifyFixRows(obj["actionableShrinkage"]);
|
|
101
|
+
const growth = extractVerifyFixRows(obj["actionableGrowth"]);
|
|
102
|
+
const diagnosis = typeof obj["diagnosis"] === "string" ? obj["diagnosis"] : null;
|
|
103
|
+
// Threshold: filter cosmetic noise.
|
|
104
|
+
const DELTA_THRESHOLD = 10;
|
|
105
|
+
const filteredShrinkage = shrinkage.filter((r) => Math.abs(r.delta) >= DELTA_THRESHOLD);
|
|
106
|
+
const filteredGrowth = growth.filter((r) => Math.abs(r.delta) >= DELTA_THRESHOLD);
|
|
107
|
+
if (filteredShrinkage.length === 0 && filteredGrowth.length === 0) {
|
|
108
|
+
return [
|
|
109
|
+
"# analyzeAbandonedMemory: verify-fix",
|
|
110
|
+
"",
|
|
111
|
+
"_No class counts crossed the actionable threshold (|delta| >= 10)._",
|
|
112
|
+
diagnosis ? `\n> ${diagnosis}` : "",
|
|
113
|
+
]
|
|
114
|
+
.join("\n")
|
|
115
|
+
.trim();
|
|
116
|
+
}
|
|
117
|
+
const sections = ["# analyzeAbandonedMemory: verify-fix", ""];
|
|
118
|
+
if (filteredShrinkage.length > 0) {
|
|
119
|
+
sections.push("## What the fix freed");
|
|
120
|
+
sections.push("");
|
|
121
|
+
sections.push("| Class | Before | After | Delta |");
|
|
122
|
+
sections.push("|---|---:|---:|---:|");
|
|
123
|
+
for (const row of filteredShrinkage) {
|
|
124
|
+
sections.push(`| \`${row.className}\` | ${row.beforeCount} | ${row.afterCount} | ${row.delta} |`);
|
|
125
|
+
}
|
|
126
|
+
sections.push("");
|
|
127
|
+
}
|
|
128
|
+
if (filteredGrowth.length > 0) {
|
|
129
|
+
sections.push("## Classes that grew (regressions or unrelated)");
|
|
130
|
+
sections.push("");
|
|
131
|
+
sections.push("| Class | Before | After | Delta |");
|
|
132
|
+
sections.push("|---|---:|---:|---:|");
|
|
133
|
+
for (const row of filteredGrowth) {
|
|
134
|
+
sections.push(`| \`${row.className}\` | ${row.beforeCount} | ${row.afterCount} | +${row.delta} |`);
|
|
135
|
+
}
|
|
136
|
+
sections.push("");
|
|
137
|
+
}
|
|
138
|
+
if (diagnosis) {
|
|
139
|
+
sections.push(`> ${diagnosis}`);
|
|
140
|
+
}
|
|
141
|
+
return sections.join("\n").trim();
|
|
142
|
+
}
|
|
143
|
+
function extractVerifyFixRows(value) {
|
|
144
|
+
if (!Array.isArray(value))
|
|
145
|
+
return [];
|
|
146
|
+
const rows = [];
|
|
147
|
+
for (const item of value) {
|
|
148
|
+
if (item == null || typeof item !== "object")
|
|
149
|
+
continue;
|
|
150
|
+
const r = item;
|
|
151
|
+
if (typeof r["className"] === "string" &&
|
|
152
|
+
typeof r["beforeCount"] === "number" &&
|
|
153
|
+
typeof r["afterCount"] === "number" &&
|
|
154
|
+
typeof r["delta"] === "number") {
|
|
155
|
+
rows.push({
|
|
156
|
+
className: r["className"],
|
|
157
|
+
beforeCount: r["beforeCount"],
|
|
158
|
+
afterCount: r["afterCount"],
|
|
159
|
+
delta: r["delta"],
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return rows;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Pure: render an arbitrary JSON-shaped value as markdown.
|
|
167
|
+
*
|
|
168
|
+
* The rendering is intentionally generic: it does not have per-tool
|
|
169
|
+
* templates. A `# Tool name` header, a `## Key` for each top-level field, and
|
|
170
|
+
* smart formatting for arrays of objects (tables when the rows share a
|
|
171
|
+
* schema) and scalars. Per-tool overrides can land in v1.9.1+ if any
|
|
172
|
+
* specific tool's output deserves a more curated view.
|
|
173
|
+
*
|
|
174
|
+
* Exposed for tests.
|
|
175
|
+
*/
|
|
176
|
+
export function renderAsMarkdown(value, toolName) {
|
|
177
|
+
const lines = [`# ${toolName}`, ""];
|
|
178
|
+
if (value == null || typeof value !== "object") {
|
|
179
|
+
lines.push(formatScalar(value));
|
|
180
|
+
return lines.join("\n");
|
|
181
|
+
}
|
|
182
|
+
const obj = value;
|
|
183
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
184
|
+
lines.push(`## ${key}`);
|
|
185
|
+
lines.push("");
|
|
186
|
+
lines.push(formatValue(val, 0));
|
|
187
|
+
lines.push("");
|
|
188
|
+
}
|
|
189
|
+
return lines.join("\n").trim() + "\n";
|
|
190
|
+
}
|
|
191
|
+
function formatValue(value, depth) {
|
|
192
|
+
if (value == null)
|
|
193
|
+
return "_(null)_";
|
|
194
|
+
if (typeof value === "string")
|
|
195
|
+
return value || "_(empty)_";
|
|
196
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
197
|
+
return formatScalar(value);
|
|
198
|
+
if (Array.isArray(value))
|
|
199
|
+
return formatArray(value, depth);
|
|
200
|
+
if (typeof value === "object")
|
|
201
|
+
return formatObject(value, depth);
|
|
202
|
+
return String(value);
|
|
203
|
+
}
|
|
204
|
+
function formatArray(arr, depth) {
|
|
205
|
+
if (arr.length === 0)
|
|
206
|
+
return "_(empty array)_";
|
|
207
|
+
// Table if all entries are objects with a shared key set.
|
|
208
|
+
if (arr.length > 0 &&
|
|
209
|
+
arr.every((e) => e != null && typeof e === "object" && !Array.isArray(e))) {
|
|
210
|
+
const objects = arr;
|
|
211
|
+
const cols = collectCommonKeys(objects);
|
|
212
|
+
if (cols.length > 0 && cols.length <= 8) {
|
|
213
|
+
const header = `| ${cols.join(" | ")} |`;
|
|
214
|
+
const sep = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
215
|
+
const rows = objects.slice(0, 50).map((o) => {
|
|
216
|
+
const cells = cols.map((c) => formatCell(o[c]));
|
|
217
|
+
return `| ${cells.join(" | ")} |`;
|
|
218
|
+
});
|
|
219
|
+
const tail = objects.length > 50 ? `\n_(${objects.length - 50} more rows omitted)_` : "";
|
|
220
|
+
return [header, sep, ...rows].join("\n") + tail;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Otherwise bullet list of scalars / mixed.
|
|
224
|
+
return arr
|
|
225
|
+
.slice(0, 50)
|
|
226
|
+
.map((e) => `- ${formatCell(e)}`)
|
|
227
|
+
.join("\n");
|
|
228
|
+
}
|
|
229
|
+
function formatObject(obj, depth) {
|
|
230
|
+
const entries = Object.entries(obj);
|
|
231
|
+
if (entries.length === 0)
|
|
232
|
+
return "_(empty object)_";
|
|
233
|
+
if (depth >= 2) {
|
|
234
|
+
// Deeply nested: collapse to inline JSON to keep the markdown tidy.
|
|
235
|
+
return "```json\n" + JSON.stringify(obj, null, 2) + "\n```";
|
|
236
|
+
}
|
|
237
|
+
return entries
|
|
238
|
+
.map(([k, v]) => `- **${k}**: ${formatInline(v, depth + 1)}`)
|
|
239
|
+
.join("\n");
|
|
240
|
+
}
|
|
241
|
+
function formatInline(value, depth) {
|
|
242
|
+
if (value == null)
|
|
243
|
+
return "_(null)_";
|
|
244
|
+
if (typeof value === "string")
|
|
245
|
+
return value || "_(empty)_";
|
|
246
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
247
|
+
return formatScalar(value);
|
|
248
|
+
if (Array.isArray(value)) {
|
|
249
|
+
if (value.length === 0)
|
|
250
|
+
return "_(empty array)_";
|
|
251
|
+
if (value.length <= 5 && value.every((e) => typeof e !== "object" || e == null)) {
|
|
252
|
+
return `[${value.map((e) => formatCell(e)).join(", ")}]`;
|
|
253
|
+
}
|
|
254
|
+
return `\n${formatArray(value, depth)}`;
|
|
255
|
+
}
|
|
256
|
+
if (typeof value === "object") {
|
|
257
|
+
return `\n${formatObject(value, depth)}`;
|
|
258
|
+
}
|
|
259
|
+
return String(value);
|
|
260
|
+
}
|
|
261
|
+
function formatScalar(value) {
|
|
262
|
+
if (value == null)
|
|
263
|
+
return "_(null)_";
|
|
264
|
+
if (typeof value === "boolean")
|
|
265
|
+
return value ? "`true`" : "`false`";
|
|
266
|
+
if (typeof value === "number")
|
|
267
|
+
return `\`${value}\``;
|
|
268
|
+
return String(value);
|
|
269
|
+
}
|
|
270
|
+
function formatCell(value) {
|
|
271
|
+
if (value == null)
|
|
272
|
+
return "_";
|
|
273
|
+
if (typeof value === "string") {
|
|
274
|
+
// Escape pipes for table safety, truncate long strings.
|
|
275
|
+
const escaped = value.replace(/\|/g, "\\|");
|
|
276
|
+
return escaped.length > 80 ? escaped.slice(0, 77) + "..." : escaped;
|
|
277
|
+
}
|
|
278
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
279
|
+
return formatScalar(value);
|
|
280
|
+
}
|
|
281
|
+
if (Array.isArray(value))
|
|
282
|
+
return `[${value.length}]`;
|
|
283
|
+
if (typeof value === "object") {
|
|
284
|
+
// Compact inline JSON, truncated.
|
|
285
|
+
const s = JSON.stringify(value);
|
|
286
|
+
return s.length > 60 ? s.slice(0, 57) + "..." : s;
|
|
287
|
+
}
|
|
288
|
+
return String(value);
|
|
289
|
+
}
|
|
290
|
+
function collectCommonKeys(objects) {
|
|
291
|
+
// Use the keys of the FIRST object as the column set, filtered to those
|
|
292
|
+
// present in at least half of the rows. Keeps the table compact when rows
|
|
293
|
+
// have optional fields.
|
|
294
|
+
if (objects.length === 0)
|
|
295
|
+
return [];
|
|
296
|
+
const firstKeys = Object.keys(objects[0]);
|
|
297
|
+
const threshold = Math.ceil(objects.length / 2);
|
|
298
|
+
return firstKeys.filter((k) => {
|
|
299
|
+
let hits = 0;
|
|
300
|
+
for (const o of objects) {
|
|
301
|
+
if (Object.prototype.hasOwnProperty.call(o, k))
|
|
302
|
+
hits += 1;
|
|
303
|
+
}
|
|
304
|
+
return hits >= threshold;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=responseFormatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseFormatter.js","sourceRoot":"","sources":["../../src/runtime/responseFormatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security and resource-limit flags read from the environment.
|
|
3
|
+
*
|
|
4
|
+
* These flags exist to make memorydetective safer to install in shared
|
|
5
|
+
* setups (CI runners, teammate's machine, automation pipelines) by
|
|
6
|
+
* gating operations that can execute arbitrary local programs, bound
|
|
7
|
+
* recording durations so an unattended agent does not pile up
|
|
8
|
+
* multi-GB traces, and centralize where `.trace` bundles get written
|
|
9
|
+
* by default so cleanup is predictable.
|
|
10
|
+
*
|
|
11
|
+
* The flags are read from `process.env` lazily (each tool call calls
|
|
12
|
+
* `getSecurityFlags()` fresh). Defaults preserve the v1.8 behavior
|
|
13
|
+
* for every existing caller; the new behavior is opt-in via the env
|
|
14
|
+
* var.
|
|
15
|
+
*
|
|
16
|
+
* - `MEMORYDETECTIVE_ALLOW_LAUNCH=1`: gates
|
|
17
|
+
* `bootAndLaunchForLeakInvestigation`. Without it, the tool returns
|
|
18
|
+
* `ok: false` with an explanation. With it, the tool runs normally.
|
|
19
|
+
* Default is OFF because that tool executes `xcodebuild` and
|
|
20
|
+
* `xcrun simctl launch`, which are "run arbitrary local program"
|
|
21
|
+
* in a trusted-input sense.
|
|
22
|
+
*
|
|
23
|
+
* - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS=300` (default 300): caps
|
|
24
|
+
* `durationSec` for `recordTimeProfile`. A caller asking for a
|
|
25
|
+
* recording longer than this gets a clear error rather than the
|
|
26
|
+
* tool silently agreeing to a 10-minute trace.
|
|
27
|
+
*
|
|
28
|
+
* - `MEMORYDETECTIVE_TRACE_ROOT=<path>` (default
|
|
29
|
+
* `~/Library/Application Support/memorydetective/traces`):
|
|
30
|
+
* directory where `.trace` bundles are written when the caller
|
|
31
|
+
* provides a relative `output` path. Absolute `output` paths
|
|
32
|
+
* bypass this default, preserving existing behavior. Also used by
|
|
33
|
+
* the upcoming `cleanup_traces` tool as the default scan path.
|
|
34
|
+
*/
|
|
35
|
+
export interface SecurityFlags {
|
|
36
|
+
allowLaunch: boolean;
|
|
37
|
+
maxRecordingSeconds: number;
|
|
38
|
+
traceRoot: string;
|
|
39
|
+
}
|
|
40
|
+
export declare const DEFAULT_MAX_RECORDING_SECONDS = 300;
|
|
41
|
+
export declare function defaultTraceRoot(homeDir?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Pure: read the security flags from an env-like object. Threaded as
|
|
44
|
+
* a parameter for testability; production callers omit it and get
|
|
45
|
+
* `process.env`.
|
|
46
|
+
*
|
|
47
|
+
* Parse rules:
|
|
48
|
+
*
|
|
49
|
+
* - `MEMORYDETECTIVE_ALLOW_LAUNCH` is truthy only when the value is
|
|
50
|
+
* literally `"1"`. Any other value (including `"true"` and `"yes"`)
|
|
51
|
+
* leaves it off, to keep the gate explicit.
|
|
52
|
+
*
|
|
53
|
+
* - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS` accepts a positive
|
|
54
|
+
* integer string; anything else (missing, zero, negative, NaN)
|
|
55
|
+
* falls back to the default 300. The cap is bounded at 3600s (1h)
|
|
56
|
+
* to prevent obviously-bad configs from disabling the gate via
|
|
57
|
+
* absurd values.
|
|
58
|
+
*
|
|
59
|
+
* - `MEMORYDETECTIVE_TRACE_ROOT` accepts any non-empty string; empty
|
|
60
|
+
* or missing values fall back to the default location.
|
|
61
|
+
*/
|
|
62
|
+
export declare function getSecurityFlags(env?: Readonly<Record<string, string | undefined>>, homeDir?: string): SecurityFlags;
|
|
63
|
+
/**
|
|
64
|
+
* Build the error message returned when `bootAndLaunchForLeakInvestigation`
|
|
65
|
+
* is invoked without `MEMORYDETECTIVE_ALLOW_LAUNCH=1`. Exposed as a
|
|
66
|
+
* named export so the same wording is used everywhere (and so the
|
|
67
|
+
* unit tests can assert on it).
|
|
68
|
+
*/
|
|
69
|
+
export declare const ALLOW_LAUNCH_REQUIRED_MESSAGE = "bootAndLaunchForLeakInvestigation requires MEMORYDETECTIVE_ALLOW_LAUNCH=1 in the environment, because this tool executes xcodebuild + xcrun simctl launch against the host. Set the env var only when you trust the inputs (workspace/project paths and bundle ids) the agent will pass.";
|
|
70
|
+
/**
|
|
71
|
+
* Build the error message returned when `recordTimeProfile` is asked
|
|
72
|
+
* for a duration above the cap.
|
|
73
|
+
*/
|
|
74
|
+
export declare function maxRecordingExceededMessage(requested: number, cap: number): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security and resource-limit flags read from the environment.
|
|
3
|
+
*
|
|
4
|
+
* These flags exist to make memorydetective safer to install in shared
|
|
5
|
+
* setups (CI runners, teammate's machine, automation pipelines) by
|
|
6
|
+
* gating operations that can execute arbitrary local programs, bound
|
|
7
|
+
* recording durations so an unattended agent does not pile up
|
|
8
|
+
* multi-GB traces, and centralize where `.trace` bundles get written
|
|
9
|
+
* by default so cleanup is predictable.
|
|
10
|
+
*
|
|
11
|
+
* The flags are read from `process.env` lazily (each tool call calls
|
|
12
|
+
* `getSecurityFlags()` fresh). Defaults preserve the v1.8 behavior
|
|
13
|
+
* for every existing caller; the new behavior is opt-in via the env
|
|
14
|
+
* var.
|
|
15
|
+
*
|
|
16
|
+
* - `MEMORYDETECTIVE_ALLOW_LAUNCH=1`: gates
|
|
17
|
+
* `bootAndLaunchForLeakInvestigation`. Without it, the tool returns
|
|
18
|
+
* `ok: false` with an explanation. With it, the tool runs normally.
|
|
19
|
+
* Default is OFF because that tool executes `xcodebuild` and
|
|
20
|
+
* `xcrun simctl launch`, which are "run arbitrary local program"
|
|
21
|
+
* in a trusted-input sense.
|
|
22
|
+
*
|
|
23
|
+
* - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS=300` (default 300): caps
|
|
24
|
+
* `durationSec` for `recordTimeProfile`. A caller asking for a
|
|
25
|
+
* recording longer than this gets a clear error rather than the
|
|
26
|
+
* tool silently agreeing to a 10-minute trace.
|
|
27
|
+
*
|
|
28
|
+
* - `MEMORYDETECTIVE_TRACE_ROOT=<path>` (default
|
|
29
|
+
* `~/Library/Application Support/memorydetective/traces`):
|
|
30
|
+
* directory where `.trace` bundles are written when the caller
|
|
31
|
+
* provides a relative `output` path. Absolute `output` paths
|
|
32
|
+
* bypass this default, preserving existing behavior. Also used by
|
|
33
|
+
* the upcoming `cleanup_traces` tool as the default scan path.
|
|
34
|
+
*/
|
|
35
|
+
import os from "node:os";
|
|
36
|
+
import { join as joinPath } from "node:path";
|
|
37
|
+
export const DEFAULT_MAX_RECORDING_SECONDS = 300;
|
|
38
|
+
export function defaultTraceRoot(homeDir = os.homedir()) {
|
|
39
|
+
return joinPath(homeDir, "Library", "Application Support", "memorydetective", "traces");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Pure: read the security flags from an env-like object. Threaded as
|
|
43
|
+
* a parameter for testability; production callers omit it and get
|
|
44
|
+
* `process.env`.
|
|
45
|
+
*
|
|
46
|
+
* Parse rules:
|
|
47
|
+
*
|
|
48
|
+
* - `MEMORYDETECTIVE_ALLOW_LAUNCH` is truthy only when the value is
|
|
49
|
+
* literally `"1"`. Any other value (including `"true"` and `"yes"`)
|
|
50
|
+
* leaves it off, to keep the gate explicit.
|
|
51
|
+
*
|
|
52
|
+
* - `MEMORYDETECTIVE_MAX_RECORDING_SECONDS` accepts a positive
|
|
53
|
+
* integer string; anything else (missing, zero, negative, NaN)
|
|
54
|
+
* falls back to the default 300. The cap is bounded at 3600s (1h)
|
|
55
|
+
* to prevent obviously-bad configs from disabling the gate via
|
|
56
|
+
* absurd values.
|
|
57
|
+
*
|
|
58
|
+
* - `MEMORYDETECTIVE_TRACE_ROOT` accepts any non-empty string; empty
|
|
59
|
+
* or missing values fall back to the default location.
|
|
60
|
+
*/
|
|
61
|
+
export function getSecurityFlags(env = process.env, homeDir = os.homedir()) {
|
|
62
|
+
const allowLaunch = env.MEMORYDETECTIVE_ALLOW_LAUNCH === "1";
|
|
63
|
+
const rawMax = env.MEMORYDETECTIVE_MAX_RECORDING_SECONDS;
|
|
64
|
+
let maxRecordingSeconds = DEFAULT_MAX_RECORDING_SECONDS;
|
|
65
|
+
if (rawMax != null && rawMax !== "") {
|
|
66
|
+
const parsed = Number.parseInt(rawMax, 10);
|
|
67
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
68
|
+
maxRecordingSeconds = Math.min(parsed, 3600);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const rawRoot = env.MEMORYDETECTIVE_TRACE_ROOT;
|
|
72
|
+
const traceRoot = rawRoot != null && rawRoot.length > 0 ? rawRoot : defaultTraceRoot(homeDir);
|
|
73
|
+
return { allowLaunch, maxRecordingSeconds, traceRoot };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the error message returned when `bootAndLaunchForLeakInvestigation`
|
|
77
|
+
* is invoked without `MEMORYDETECTIVE_ALLOW_LAUNCH=1`. Exposed as a
|
|
78
|
+
* named export so the same wording is used everywhere (and so the
|
|
79
|
+
* unit tests can assert on it).
|
|
80
|
+
*/
|
|
81
|
+
export const ALLOW_LAUNCH_REQUIRED_MESSAGE = "bootAndLaunchForLeakInvestigation requires MEMORYDETECTIVE_ALLOW_LAUNCH=1 in the environment, because this tool executes xcodebuild + xcrun simctl launch against the host. Set the env var only when you trust the inputs (workspace/project paths and bundle ids) the agent will pass.";
|
|
82
|
+
/**
|
|
83
|
+
* Build the error message returned when `recordTimeProfile` is asked
|
|
84
|
+
* for a duration above the cap.
|
|
85
|
+
*/
|
|
86
|
+
export function maxRecordingExceededMessage(requested, cap) {
|
|
87
|
+
return (`recordTimeProfile durationSec=${requested} exceeds the configured cap of ${cap}s ` +
|
|
88
|
+
`(MEMORYDETECTIVE_MAX_RECORDING_SECONDS). Lower durationSec, or raise the cap with the env var.`);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=securityFlags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securityFlags.js","sourceRoot":"","sources":["../../src/runtime/securityFlags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,WAAW,CAAC;AAQ7C,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAEjD,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,CAAC,OAAO,EAAE;IAC7D,OAAO,QAAQ,CACb,OAAO,EACP,SAAS,EACT,qBAAqB,EACrB,iBAAiB,EACjB,QAAQ,CACT,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAAoD,OAAO,CAAC,GAAG,EAC/D,UAAkB,EAAE,CAAC,OAAO,EAAE;IAE9B,MAAM,WAAW,GAAG,GAAG,CAAC,4BAA4B,KAAK,GAAG,CAAC;IAE7D,MAAM,MAAM,GAAG,GAAG,CAAC,qCAAqC,CAAC;IACzD,IAAI,mBAAmB,GAAG,6BAA6B,CAAC;IACxD,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,0BAA0B,CAAC;IAC/C,MAAM,SAAS,GACb,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE9E,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,0RAA0R,CAAC;AAE7R;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAiB,EACjB,GAAW;IAEX,OAAO,CACL,iCAAiC,SAAS,kCAAkC,GAAG,IAAI;QACnF,gGAAgG,CACjG,CAAC;AACJ,CAAC"}
|
|
@@ -203,7 +203,20 @@ const HINTS = {
|
|
|
203
203
|
"swiftdata.modelcontext-actor-cycle": {
|
|
204
204
|
rule: null,
|
|
205
205
|
url: "https://developer.apple.com/forums/thread/748042",
|
|
206
|
-
explanation: "No static rule. The cycle is between Apple-provided types (`Actor` / `DefaultSerialModelExecutor` / `ModelContext`)
|
|
206
|
+
explanation: "No static rule. The cycle is between Apple-provided types (`Actor` / `DefaultSerialModelExecutor` / `ModelContext`); SwiftLint can't reason about Apple-framework retain semantics. Apple fixed the framework-level shape in iOS 18 beta 1 (FB13844786). Until your minimum target is iOS 18+, the user-code shape persists.",
|
|
207
|
+
},
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
209
|
+
// v1.9 catalog (DebugSwift borrow)
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
211
|
+
"uikit.viewcontroller-retained-after-pop": {
|
|
212
|
+
rule: "weak_delegate",
|
|
213
|
+
url: "https://realm.github.io/SwiftLint/weak_delegate.html",
|
|
214
|
+
explanation: "`weak_delegate` catches the most common shape (delegate property without `weak`), and the project-wide `weak_self` rule covers closure-captured self in `viewDidLoad`/`viewWillAppear`. Neither catches Combine sinks stored on the VC nor KVO observations that never `invalidate()`; for those you need the offline catalog match.",
|
|
215
|
+
},
|
|
216
|
+
"swiftui.observable-write-on-every-render": {
|
|
217
|
+
rule: null,
|
|
218
|
+
url: "https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app",
|
|
219
|
+
explanation: "No static rule. SwiftLint cannot prove that an `@Observable` mutation happens inside a `View.body` chain (it would need to reason about View body resolution, which is compiler-internal). The cycle catalog catches the heap shape it leaves behind; the trace-side perf signal (excessive body re-evaluations on the same view) is a parallel detector.",
|
|
207
220
|
},
|
|
208
221
|
};
|
|
209
222
|
/** Returns the static-analysis hint for a given pattern, or null if unknown. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staticAnalysisHints.js","sourceRoot":"","sources":["../../src/runtime/staticAnalysisHints.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"staticAnalysisHints.js","sourceRoot":"","sources":["../../src/runtime/staticAnalysisHints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="generator" content="memorydetective">
|
|
6
|
+
<title>{{TITLE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: light dark; }
|
|
9
|
+
body { font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; margin: 0; padding: 2rem; max-width: 960px; background: #fafafa; color: #1c1c1e; }
|
|
10
|
+
@media (prefers-color-scheme: dark) { body { background: #1c1c1e; color: #f2f2f7; } .card { background: #2c2c2e; } th { background: #3a3a3c; } code { background: #3a3a3c; } }
|
|
11
|
+
h1 { font-size: 1.6rem; margin: 0 0 .5rem; }
|
|
12
|
+
h2 { font-size: 1.1rem; margin: 2rem 0 .5rem; }
|
|
13
|
+
.meta { font-size: .8rem; opacity: .65; margin-bottom: 1.5rem; }
|
|
14
|
+
.card { background: #fff; border: 1px solid rgba(127,127,127,.2); border-radius: 8px; padding: 1rem 1.25rem; margin-bottom: 1rem; }
|
|
15
|
+
.verdict { display: inline-block; padding: .25rem .75rem; border-radius: 999px; font-weight: 600; font-size: .8rem; letter-spacing: .02em; }
|
|
16
|
+
.verdict.pass { background: #34c759; color: #fff; }
|
|
17
|
+
.verdict.fail { background: #ff3b30; color: #fff; }
|
|
18
|
+
.verdict.skip { background: #8e8e93; color: #fff; }
|
|
19
|
+
table { border-collapse: collapse; width: 100%; margin: .5rem 0; font-size: .85rem; }
|
|
20
|
+
th, td { text-align: left; padding: .4rem .6rem; border-bottom: 1px solid rgba(127,127,127,.2); }
|
|
21
|
+
th { background: #f2f2f7; font-weight: 600; }
|
|
22
|
+
code { font: .85rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; background: #f2f2f7; padding: .15rem .35rem; border-radius: 4px; }
|
|
23
|
+
.badge { font-size: .7rem; padding: .1rem .4rem; border-radius: 4px; }
|
|
24
|
+
.badge.allow { background: rgba(142,142,147,.2); }
|
|
25
|
+
.badge.fail { background: rgba(255,59,48,.15); color: #ff3b30; }
|
|
26
|
+
.stat { display: inline-block; margin-right: 1.5rem; }
|
|
27
|
+
.stat .n { font-size: 1.4rem; font-weight: 600; }
|
|
28
|
+
.stat .label { font-size: .75rem; opacity: .65; text-transform: uppercase; letter-spacing: .05em; }
|
|
29
|
+
.steps { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .75rem; opacity: .7; white-space: pre-wrap; word-break: break-all; }
|
|
30
|
+
details { margin-top: .5rem; }
|
|
31
|
+
summary { cursor: pointer; font-size: .8rem; opacity: .7; }
|
|
32
|
+
</style>
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<h1>{{TITLE}}</h1>
|
|
36
|
+
<div class="meta">Generated by memorydetective {{VERSION}} at {{TIMESTAMP}}</div>
|
|
37
|
+
{{BODY}}
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="generator" content="memorydetective">
|
|
6
|
+
<title>{{TITLE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: light dark; }
|
|
9
|
+
body { font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; margin: 0; padding: 2rem; max-width: 960px; background: #fafafa; color: #1c1c1e; }
|
|
10
|
+
@media (prefers-color-scheme: dark) { body { background: #1c1c1e; color: #f2f2f7; } .card { background: #2c2c2e; } th { background: #3a3a3c; } code { background: #3a3a3c; } }
|
|
11
|
+
h1 { font-size: 1.6rem; margin: 0 0 .5rem; }
|
|
12
|
+
h2 { font-size: 1.1rem; margin: 2rem 0 .5rem; }
|
|
13
|
+
.meta { font-size: .8rem; opacity: .65; margin-bottom: 1.5rem; }
|
|
14
|
+
.card { background: #fff; border: 1px solid rgba(127,127,127,.2); border-radius: 8px; padding: 1rem 1.25rem; margin-bottom: 1rem; }
|
|
15
|
+
.verdict { display: inline-block; padding: .25rem .75rem; border-radius: 999px; font-weight: 600; font-size: .8rem; letter-spacing: .02em; }
|
|
16
|
+
.verdict.pass { background: #34c759; color: #fff; }
|
|
17
|
+
.verdict.fail { background: #ff3b30; color: #fff; }
|
|
18
|
+
.verdict.skip { background: #8e8e93; color: #fff; }
|
|
19
|
+
table { border-collapse: collapse; width: 100%; margin: .5rem 0; font-size: .85rem; }
|
|
20
|
+
th, td { text-align: left; padding: .4rem .6rem; border-bottom: 1px solid rgba(127,127,127,.2); }
|
|
21
|
+
th { background: #f2f2f7; font-weight: 600; }
|
|
22
|
+
code { font: .85rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; background: #f2f2f7; padding: .15rem .35rem; border-radius: 4px; }
|
|
23
|
+
.badge { font-size: .7rem; padding: .1rem .4rem; border-radius: 4px; }
|
|
24
|
+
.badge.allow { background: rgba(142,142,147,.2); }
|
|
25
|
+
.badge.fail { background: rgba(255,59,48,.15); color: #ff3b30; }
|
|
26
|
+
.stat { display: inline-block; margin-right: 1.5rem; }
|
|
27
|
+
.stat .n { font-size: 1.4rem; font-weight: 600; }
|
|
28
|
+
.stat .label { font-size: .75rem; opacity: .65; text-transform: uppercase; letter-spacing: .05em; }
|
|
29
|
+
.steps { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .75rem; opacity: .7; white-space: pre-wrap; word-break: break-all; }
|
|
30
|
+
details { margin-top: .5rem; }
|
|
31
|
+
summary { cursor: pointer; font-size: .8rem; opacity: .7; }
|
|
32
|
+
</style>
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<h1>{{TITLE}}</h1>
|
|
36
|
+
<div class="meta">Generated by memorydetective {{VERSION}} at {{TIMESTAMP}}</div>
|
|
37
|
+
{{BODY}}
|
|
38
|
+
</body>
|
|
39
|
+
</html>
|