memorydetective 1.8.0 → 1.9.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 +64 -1
- package/README.md +75 -9
- 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 +73 -0
- package/dist/parsers/referenceTree.js +145 -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 +55 -0
- package/dist/runtime/responseFormatter.js +200 -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 +140 -0
- package/dist/tools/analyzeAbandonedMemory.js +287 -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 +21 -1
- package/dist/tools/analyzeMemgraph.js +52 -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 +4 -3
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redact sensitive substrings from tool responses before they leave the
|
|
3
|
+
* MCP boundary.
|
|
4
|
+
*
|
|
5
|
+
* Tool outputs frequently include filesystem paths (with the user's home
|
|
6
|
+
* directory in them), token-shaped strings, hostnames, IP addresses, and
|
|
7
|
+
* bundle identifiers. None of those are useful to an AI agent reasoning
|
|
8
|
+
* about an iOS leak, and all of them are footguns when the output ends
|
|
9
|
+
* up in a Slack message, a PR comment, or a screenshot. This module
|
|
10
|
+
* scrubs them at the formatter boundary so by-default outputs are safe
|
|
11
|
+
* to share without manual sweeping.
|
|
12
|
+
*
|
|
13
|
+
* Three modes are selected via the `MEMORYDETECTIVE_REDACTION` env var:
|
|
14
|
+
*
|
|
15
|
+
* - `balanced` (default): home-directory absolute paths become `~/...`,
|
|
16
|
+
* common secret-shaped tokens (AWS keys, GitHub PATs, Stripe secrets,
|
|
17
|
+
* Slack tokens, Bearer auth) are masked. Hostnames, IPs, bundle IDs,
|
|
18
|
+
* process names, and class names are preserved (they are usually
|
|
19
|
+
* useful for debugging).
|
|
20
|
+
*
|
|
21
|
+
* - `strict`: everything in `balanced`, plus hostnames, IPv4 addresses,
|
|
22
|
+
* and bundle identifiers. Use when the output is going to be pasted
|
|
23
|
+
* into a public artifact (issue tracker, blog post, social) and you
|
|
24
|
+
* want a wide safety margin.
|
|
25
|
+
*
|
|
26
|
+
* - `off`: no redaction. Default behavior is preserved for legacy
|
|
27
|
+
* workflows and for local-only debugging where the noise is genuinely
|
|
28
|
+
* helpful. The startup banner logs the active mode so an operator
|
|
29
|
+
* running `off` knows the responses are unfiltered.
|
|
30
|
+
*
|
|
31
|
+
* Redaction is structural: the value passed in keeps its shape (same
|
|
32
|
+
* object keys, same array lengths, same scalar types), only string
|
|
33
|
+
* leaves are rewritten. Numbers, booleans, null/undefined, and dates
|
|
34
|
+
* pass through unchanged.
|
|
35
|
+
*/
|
|
36
|
+
import os from "node:os";
|
|
37
|
+
const TOKEN_PATTERNS = [
|
|
38
|
+
// AWS access key id (AKIA + 16 chars)
|
|
39
|
+
{ re: /\bAKIA[0-9A-Z]{16}\b/g, keepPrefix: 4 },
|
|
40
|
+
// GitHub classic PAT
|
|
41
|
+
{ re: /\bghp_[A-Za-z0-9]{36,}\b/g, keepPrefix: 4 },
|
|
42
|
+
// GitHub fine-grained PAT
|
|
43
|
+
{ re: /\bgithub_pat_[A-Za-z0-9_]+\b/g, keepPrefix: 11 },
|
|
44
|
+
// Stripe live + test secret
|
|
45
|
+
{ re: /\bsk_(?:live|test)_[A-Za-z0-9]{20,}\b/g, keepPrefix: 8 },
|
|
46
|
+
// Slack tokens
|
|
47
|
+
{ re: /\bxox[bpoasr]-[A-Za-z0-9-]+\b/g, keepPrefix: 5 },
|
|
48
|
+
// Bearer auth tokens
|
|
49
|
+
{ re: /\bBearer\s+[A-Za-z0-9._\-]{20,}\b/gi, keepPrefix: 7 },
|
|
50
|
+
];
|
|
51
|
+
// Hostnames: 1+ labels followed by a TLD (2+ alpha chars). Excludes the
|
|
52
|
+
// .swift / .ts / .js / .json / .trace / .memgraph "extensions" we see in
|
|
53
|
+
// paths, since those would false-positive otherwise.
|
|
54
|
+
const HOST_PATTERN = /\b(?!(?:[A-Za-z0-9-]+)\.(?:swift|ts|js|json|trace|memgraph|md|yaml|yml|html|png|jpg|jpeg|gif|m4v|mp4|mov|css|sh|py|rb|go|rs|c|cpp|h|hpp|m|mm|plist|xcframework|xcconfig|xcassets)\b)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
|
|
55
|
+
const IP_PATTERN = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
|
|
56
|
+
const BUNDLE_ID_PATTERN = /\bcom\.[a-z0-9_-]+(?:\.[a-z0-9_-]+)+\b/gi;
|
|
57
|
+
/**
|
|
58
|
+
* Pure: read the active redaction mode from an env-like object.
|
|
59
|
+
* Defaults to `balanced` when unset or set to an unrecognized value.
|
|
60
|
+
*
|
|
61
|
+
* Threaded as a parameter for testability; production callers omit it
|
|
62
|
+
* and get `process.env`.
|
|
63
|
+
*/
|
|
64
|
+
export function getRedactionMode(env = process.env) {
|
|
65
|
+
const raw = (env.MEMORYDETECTIVE_REDACTION ?? "balanced").toLowerCase();
|
|
66
|
+
if (raw === "off" || raw === "strict" || raw === "balanced") {
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
69
|
+
return "balanced";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Pure: scrub a single string per the active mode. `off` returns the
|
|
73
|
+
* input unchanged; the other modes apply the rules described in the
|
|
74
|
+
* module doc.
|
|
75
|
+
*
|
|
76
|
+
* Threaded `homeDir` for testability so unit tests can pass a fake
|
|
77
|
+
* `/Users/test/` without depending on the real home directory.
|
|
78
|
+
*/
|
|
79
|
+
export function redactString(input, mode, homeDir = os.homedir()) {
|
|
80
|
+
if (mode === "off")
|
|
81
|
+
return input;
|
|
82
|
+
let result = input;
|
|
83
|
+
// Home dir collapse first so subsequent host/IP rules don't mistakenly
|
|
84
|
+
// grab parts of paths.
|
|
85
|
+
if (homeDir && homeDir.length > 1 && result.includes(homeDir)) {
|
|
86
|
+
result = result.split(homeDir).join("~");
|
|
87
|
+
}
|
|
88
|
+
// Always mask token-shaped secrets, even in `balanced`.
|
|
89
|
+
for (const { re, keepPrefix } of TOKEN_PATTERNS) {
|
|
90
|
+
result = result.replace(re, (match) => match.slice(0, keepPrefix) + "***REDACTED***");
|
|
91
|
+
}
|
|
92
|
+
if (mode === "strict") {
|
|
93
|
+
result = result.replace(BUNDLE_ID_PATTERN, "***BUNDLE_ID***");
|
|
94
|
+
result = result.replace(IP_PATTERN, "***IP***");
|
|
95
|
+
result = result.replace(HOST_PATTERN, "***HOST***");
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Pure: recursively redact strings inside an arbitrary JSON-shaped
|
|
101
|
+
* value. Object key names are preserved unchanged (they are part of
|
|
102
|
+
* the schema, not data); only string VALUES and string ARRAY items
|
|
103
|
+
* are scrubbed.
|
|
104
|
+
*
|
|
105
|
+
* Non-string scalars (number, boolean, null) pass through. Functions,
|
|
106
|
+
* symbols, and other non-JSON values are returned as-is.
|
|
107
|
+
*/
|
|
108
|
+
export function redact(value, mode, homeDir = os.homedir()) {
|
|
109
|
+
if (mode === "off")
|
|
110
|
+
return value;
|
|
111
|
+
if (value == null)
|
|
112
|
+
return value;
|
|
113
|
+
if (typeof value === "string")
|
|
114
|
+
return redactString(value, mode, homeDir);
|
|
115
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
116
|
+
return value;
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
return value.map((v) => redact(v, mode, homeDir));
|
|
119
|
+
}
|
|
120
|
+
if (typeof value === "object") {
|
|
121
|
+
const out = {};
|
|
122
|
+
for (const [k, v] of Object.entries(value)) {
|
|
123
|
+
out[k] = redact(v, mode, homeDir);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Side-effecting: log the active redaction mode once per server
|
|
131
|
+
* startup so an operator running with `off` knows the responses
|
|
132
|
+
* are unfiltered.
|
|
133
|
+
*/
|
|
134
|
+
let advisoryLogged = false;
|
|
135
|
+
export function maybeLogRedactionModeOnce(mode, writer = (line) => process.stderr.write(line)) {
|
|
136
|
+
if (advisoryLogged)
|
|
137
|
+
return;
|
|
138
|
+
writer(`[memorydetective] redaction mode: ${mode}. ` +
|
|
139
|
+
`Set MEMORYDETECTIVE_REDACTION to balanced (default), strict, or off.\n`);
|
|
140
|
+
advisoryLogged = true;
|
|
141
|
+
}
|
|
142
|
+
/** Test-only: reset the once-per-instance log flag. */
|
|
143
|
+
export function resetRedactionAdvisoryFlagForTests() {
|
|
144
|
+
advisoryLogged = false;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/runtime/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
export declare const outputFormatField: z.ZodOptional<z.ZodEnum<["markdown", "json", "both"]>>;
|
|
22
|
+
export type OutputFormat = z.infer<typeof outputFormatField>;
|
|
23
|
+
export interface McpContentItem {
|
|
24
|
+
type: "text";
|
|
25
|
+
text: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Shape of the MCP tool response. Matches the SDK's expected type
|
|
29
|
+
* (which has an open index signature for arbitrary extension fields like
|
|
30
|
+
* `_meta`); we model it explicitly here so the formatter's return type
|
|
31
|
+
* can flow through `server.registerTool` without a cast.
|
|
32
|
+
*/
|
|
33
|
+
export interface McpResponse {
|
|
34
|
+
content: McpContentItem[];
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Pure: shape the MCP response based on the caller's `outputFormat`.
|
|
39
|
+
*
|
|
40
|
+
* For `json` and `both`, the JSON is `JSON.stringify(result, null, 2)`. For
|
|
41
|
+
* `markdown` and `both`, the markdown is rendered via {@link renderAsMarkdown}.
|
|
42
|
+
*/
|
|
43
|
+
export declare function formatMcpResponse(result: unknown, toolName: string, format: OutputFormat | undefined): McpResponse;
|
|
44
|
+
/**
|
|
45
|
+
* Pure: render an arbitrary JSON-shaped value as markdown.
|
|
46
|
+
*
|
|
47
|
+
* The rendering is intentionally generic: it does not have per-tool
|
|
48
|
+
* templates. A `# Tool name` header, a `## Key` for each top-level field, and
|
|
49
|
+
* smart formatting for arrays of objects (tables when the rows share a
|
|
50
|
+
* schema) and scalars. Per-tool overrides can land in v1.9.1+ if any
|
|
51
|
+
* specific tool's output deserves a more curated view.
|
|
52
|
+
*
|
|
53
|
+
* Exposed for tests.
|
|
54
|
+
*/
|
|
55
|
+
export declare function renderAsMarkdown(value: unknown, toolName: string): string;
|
|
@@ -0,0 +1,200 @@
|
|
|
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"])
|
|
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.");
|
|
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
|
+
const markdown = renderAsMarkdown(redacted, toolName);
|
|
45
|
+
if (mode === "markdown") {
|
|
46
|
+
return { content: [{ type: "text", text: markdown }] };
|
|
47
|
+
}
|
|
48
|
+
// "both": markdown first so a UI that picks content[0] gets the readable
|
|
49
|
+
// view, then JSON so an agent looking for the structured data finds it
|
|
50
|
+
// without having to parse the markdown.
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{ type: "text", text: markdown },
|
|
54
|
+
{ type: "text", text: json },
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Pure: render an arbitrary JSON-shaped value as markdown.
|
|
60
|
+
*
|
|
61
|
+
* The rendering is intentionally generic: it does not have per-tool
|
|
62
|
+
* templates. A `# Tool name` header, a `## Key` for each top-level field, and
|
|
63
|
+
* smart formatting for arrays of objects (tables when the rows share a
|
|
64
|
+
* schema) and scalars. Per-tool overrides can land in v1.9.1+ if any
|
|
65
|
+
* specific tool's output deserves a more curated view.
|
|
66
|
+
*
|
|
67
|
+
* Exposed for tests.
|
|
68
|
+
*/
|
|
69
|
+
export function renderAsMarkdown(value, toolName) {
|
|
70
|
+
const lines = [`# ${toolName}`, ""];
|
|
71
|
+
if (value == null || typeof value !== "object") {
|
|
72
|
+
lines.push(formatScalar(value));
|
|
73
|
+
return lines.join("\n");
|
|
74
|
+
}
|
|
75
|
+
const obj = value;
|
|
76
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
77
|
+
lines.push(`## ${key}`);
|
|
78
|
+
lines.push("");
|
|
79
|
+
lines.push(formatValue(val, 0));
|
|
80
|
+
lines.push("");
|
|
81
|
+
}
|
|
82
|
+
return lines.join("\n").trim() + "\n";
|
|
83
|
+
}
|
|
84
|
+
function formatValue(value, depth) {
|
|
85
|
+
if (value == null)
|
|
86
|
+
return "_(null)_";
|
|
87
|
+
if (typeof value === "string")
|
|
88
|
+
return value || "_(empty)_";
|
|
89
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
90
|
+
return formatScalar(value);
|
|
91
|
+
if (Array.isArray(value))
|
|
92
|
+
return formatArray(value, depth);
|
|
93
|
+
if (typeof value === "object")
|
|
94
|
+
return formatObject(value, depth);
|
|
95
|
+
return String(value);
|
|
96
|
+
}
|
|
97
|
+
function formatArray(arr, depth) {
|
|
98
|
+
if (arr.length === 0)
|
|
99
|
+
return "_(empty array)_";
|
|
100
|
+
// Table if all entries are objects with a shared key set.
|
|
101
|
+
if (arr.length > 0 &&
|
|
102
|
+
arr.every((e) => e != null && typeof e === "object" && !Array.isArray(e))) {
|
|
103
|
+
const objects = arr;
|
|
104
|
+
const cols = collectCommonKeys(objects);
|
|
105
|
+
if (cols.length > 0 && cols.length <= 8) {
|
|
106
|
+
const header = `| ${cols.join(" | ")} |`;
|
|
107
|
+
const sep = `| ${cols.map(() => "---").join(" | ")} |`;
|
|
108
|
+
const rows = objects.slice(0, 50).map((o) => {
|
|
109
|
+
const cells = cols.map((c) => formatCell(o[c]));
|
|
110
|
+
return `| ${cells.join(" | ")} |`;
|
|
111
|
+
});
|
|
112
|
+
const tail = objects.length > 50 ? `\n_(${objects.length - 50} more rows omitted)_` : "";
|
|
113
|
+
return [header, sep, ...rows].join("\n") + tail;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Otherwise bullet list of scalars / mixed.
|
|
117
|
+
return arr
|
|
118
|
+
.slice(0, 50)
|
|
119
|
+
.map((e) => `- ${formatCell(e)}`)
|
|
120
|
+
.join("\n");
|
|
121
|
+
}
|
|
122
|
+
function formatObject(obj, depth) {
|
|
123
|
+
const entries = Object.entries(obj);
|
|
124
|
+
if (entries.length === 0)
|
|
125
|
+
return "_(empty object)_";
|
|
126
|
+
if (depth >= 2) {
|
|
127
|
+
// Deeply nested: collapse to inline JSON to keep the markdown tidy.
|
|
128
|
+
return "```json\n" + JSON.stringify(obj, null, 2) + "\n```";
|
|
129
|
+
}
|
|
130
|
+
return entries
|
|
131
|
+
.map(([k, v]) => `- **${k}**: ${formatInline(v, depth + 1)}`)
|
|
132
|
+
.join("\n");
|
|
133
|
+
}
|
|
134
|
+
function formatInline(value, depth) {
|
|
135
|
+
if (value == null)
|
|
136
|
+
return "_(null)_";
|
|
137
|
+
if (typeof value === "string")
|
|
138
|
+
return value || "_(empty)_";
|
|
139
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
140
|
+
return formatScalar(value);
|
|
141
|
+
if (Array.isArray(value)) {
|
|
142
|
+
if (value.length === 0)
|
|
143
|
+
return "_(empty array)_";
|
|
144
|
+
if (value.length <= 5 && value.every((e) => typeof e !== "object" || e == null)) {
|
|
145
|
+
return `[${value.map((e) => formatCell(e)).join(", ")}]`;
|
|
146
|
+
}
|
|
147
|
+
return `\n${formatArray(value, depth)}`;
|
|
148
|
+
}
|
|
149
|
+
if (typeof value === "object") {
|
|
150
|
+
return `\n${formatObject(value, depth)}`;
|
|
151
|
+
}
|
|
152
|
+
return String(value);
|
|
153
|
+
}
|
|
154
|
+
function formatScalar(value) {
|
|
155
|
+
if (value == null)
|
|
156
|
+
return "_(null)_";
|
|
157
|
+
if (typeof value === "boolean")
|
|
158
|
+
return value ? "`true`" : "`false`";
|
|
159
|
+
if (typeof value === "number")
|
|
160
|
+
return `\`${value}\``;
|
|
161
|
+
return String(value);
|
|
162
|
+
}
|
|
163
|
+
function formatCell(value) {
|
|
164
|
+
if (value == null)
|
|
165
|
+
return "_";
|
|
166
|
+
if (typeof value === "string") {
|
|
167
|
+
// Escape pipes for table safety, truncate long strings.
|
|
168
|
+
const escaped = value.replace(/\|/g, "\\|");
|
|
169
|
+
return escaped.length > 80 ? escaped.slice(0, 77) + "..." : escaped;
|
|
170
|
+
}
|
|
171
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
172
|
+
return formatScalar(value);
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(value))
|
|
175
|
+
return `[${value.length}]`;
|
|
176
|
+
if (typeof value === "object") {
|
|
177
|
+
// Compact inline JSON, truncated.
|
|
178
|
+
const s = JSON.stringify(value);
|
|
179
|
+
return s.length > 60 ? s.slice(0, 57) + "..." : s;
|
|
180
|
+
}
|
|
181
|
+
return String(value);
|
|
182
|
+
}
|
|
183
|
+
function collectCommonKeys(objects) {
|
|
184
|
+
// Use the keys of the FIRST object as the column set, filtered to those
|
|
185
|
+
// present in at least half of the rows. Keeps the table compact when rows
|
|
186
|
+
// have optional fields.
|
|
187
|
+
if (objects.length === 0)
|
|
188
|
+
return [];
|
|
189
|
+
const firstKeys = Object.keys(objects[0]);
|
|
190
|
+
const threshold = Math.ceil(objects.length / 2);
|
|
191
|
+
return firstKeys.filter((k) => {
|
|
192
|
+
let hits = 0;
|
|
193
|
+
for (const o of objects) {
|
|
194
|
+
if (Object.prototype.hasOwnProperty.call(o, k))
|
|
195
|
+
hits += 1;
|
|
196
|
+
}
|
|
197
|
+
return hits >= threshold;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
//# 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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"}
|