/** * Pure parser for Apple MetricKit `.mxdiagnostic` payloads. v1.18 (item #79). * * MetricKit (`MXMetricManager`) delivers JSON payloads to a developer-readable * directory on real-device TestFlight / App Store builds. The format is * generated by `MXDiagnosticPayload.jsonRepresentation()`: * * ```jsonc * { * "timeStampBegin": "ISO-8601", * "timeStampEnd": "ISO-8601", * "crashDiagnostics": [ MXCrashDiagnostic, ... ], * "hangDiagnostics": [ MXHangDiagnostic, ... ], * "cpuExceptionDiagnostics": [ MXCPUExceptionDiagnostic, ... ], * "diskWriteExceptionDiagnostics": [ MXDiskWriteExceptionDiagnostic, ... ] * } * ``` * * Apple does not publish a schema. ChimeHQ/Meter (the canonical community * parser, ~222 stars on GitHub) is the de-facto reference. This module is * the TypeScript counterpart, deliberately TOLERANT: * * - Unknown top-level keys pass through (Apple has silently added fields * between iOS minors). * - `hangDuration`, `totalCPUTime`, `writesCaused` come as LOCALIZED * STRINGS (e.g. `"5.4 sec"`, `"20秒"`, `"1.2 GB"`). We extract the * leading number with a regex; never `parseFloat` the whole string. * - Missing diagnostic arrays are treated as empty, not as errors. * - `payload.version` (per-diagnostic) is echoed in the analyzer result so * a future caller can branch on a schema bump without us shipping a new * type. * * The parser is split from the analyzer for the same reason `xctraceXml` is * separate from the trace analyzers: pure functions, no node:fs / node:child_process, * trivially testable. */ export interface MetricKitFrame { binaryUUID?: string; binaryName?: string; offsetIntoBinaryTextSegment?: number; address?: number; sampleCount?: number; subFrames?: MetricKitFrame[]; /** Pass-through for anything Apple ships that we don't model yet. */ raw?: unknown; } export interface MetricKitCallStackTree { callStacks: Array<{ threadAttributed?: boolean; callStackRootFrames: MetricKitFrame[]; }>; } export interface MetricKitDiagnostic { version?: string; callStackTree: MetricKitCallStackTree; diagnosticMetaData: Record; } export interface MetricKitPayload { timeStampBegin?: string; timeStampEnd?: string; crashDiagnostics: MetricKitDiagnostic[]; hangDiagnostics: MetricKitDiagnostic[]; cpuExceptionDiagnostics: MetricKitDiagnostic[]; diskWriteExceptionDiagnostics: MetricKitDiagnostic[]; /** Anything we did not model — Apple has been adding fields silently. */ raw?: Record; } /** * Parse a single `.mxdiagnostic` JSON string into a normalized payload. * Missing arrays default to `[]`, missing scalars stay `undefined`. * Throws only on invalid JSON. Throwing on a known-shape mismatch would * defeat the whole point of the tolerant design. */ export declare function parseMetricKitPayload(jsonText: string): MetricKitPayload; /** * Extract the top (deepest-most-sampled) frame label from a diagnostic. * * Strategy: pick the first `callStacks[0].callStackRootFrames[0]` that has * a `binaryName`. Fall back to `binaryName + 0x` for * unsymbolicated frames. Returns `""` when the stack is empty — * we do not silently elide diagnostics with missing stacks because their * count still matters for aggregation. */ export declare function extractTopFrameLabel(d: MetricKitDiagnostic): string; /** * Extract the leading number from a localized hang-duration / CPU-time / * disk-bytes string. MetricKit ships values like: * * "5.4 sec" "20秒" (Japanese) "1.2 GB" "500 ms" "80 %" * * Strategy: capture the first numeric run (with optional decimal). Unit * inference is best-effort — caller passes the expected unit family * (`"ms" | "MB"`) and we convert. Returns `undefined` when no number is * present (Apple has shipped malformed strings too). */ export declare function extractLeadingNumber(s: string): number | undefined; /** * Convert a MetricKit time string ("5.4 sec", "500 ms", "20秒") to * milliseconds. Unknown units default to seconds (the iOS / Japanese * default) per Apple's typical output. */ export declare function metricKitTimeToMs(s: string): number | undefined; /** * Convert a MetricKit disk string ("1.2 GB", "500 MB", "12 KB") to MB. * Unknown units default to bytes (rare but safer than dropping the value). */ export declare function metricKitDiskToMB(s: string): number | undefined;