fix: correlate webprobe performance windows

This commit is contained in:
Codex
2026-07-02 13:54:14 +00:00
parent 9fd8fc6f9f
commit c2d3814415
5 changed files with 580 additions and 28 deletions
@@ -3,8 +3,10 @@
export function nodeWebObserveAnalyzerPerformanceSource(): string {
return String.raw`
function buildFrontendPerformanceReport(rows, artifacts) {
function buildFrontendPerformanceReport(rows, artifacts, samples, network) {
const sourceRows = Array.isArray(rows) ? rows : [];
const sourceSamples = Array.isArray(samples) ? samples : [];
const sourceNetwork = Array.isArray(network) ? network : [];
const events = [];
const drainErrors = [];
const captures = [];
@@ -46,7 +48,9 @@ function buildFrontendPerformanceReport(rows, artifacts) {
.map(finalizeProfileStackHotspot)
.sort((left, right) => Number(right.selfTimeMs ?? 0) - Number(left.selfTimeMs ?? 0))
.slice(0, 30);
const attribution = frontendPerformanceAttributionStatus({ captures, profileHotspots, profileStacks, scriptHotspots, longTasks, loafs, gaps });
const performanceWindows = buildFrontendPerformanceWindows({ events, captures, profileHotspots, profileStacks, scriptHotspots, samples: sourceSamples, network: sourceNetwork });
const sourceAttribution = buildFrontendSourceAttribution({ scriptHotspots, profileHotspots, profileStacks });
const attribution = frontendPerformanceAttributionStatus({ captures, profileHotspots, profileStacks, scriptHotspots, longTasks, loafs, gaps, performanceWindows });
return {
summary: {
rowCount: sourceRows.length,
@@ -70,6 +74,12 @@ function buildFrontendPerformanceReport(rows, artifacts) {
loafOnly: attribution.loafOnly,
cpuProfileHotspotEvidence: attribution.cpuProfileHotspotEvidence,
evidenceNote: attribution.evidenceNote,
windowCorrelationCount: performanceWindows.length,
cpuProfileWindowCoveredCount: performanceWindows.filter((item) => item.cpuProfileCoverageStatus === "covered").length,
cpuProfileWindowOverlappedCount: performanceWindows.filter((item) => item.cpuProfileCoverageStatus === "overlapped").length,
cpuProfileWindowMissedCount: performanceWindows.filter((item) => item.cpuProfileCoverageStatus === "missed").length,
cpuProfileWindowMissingCount: performanceWindows.filter((item) => item.cpuProfileCoverageStatus === "no-cpu-profile").length,
sourceMapStatus: sourceAttribution.sourceMapStatus,
captureArtifacts: performanceCaptureArtifacts(artifacts),
valuesRedacted: true,
},
@@ -79,6 +89,8 @@ function buildFrontendPerformanceReport(rows, artifacts) {
scriptHotspots,
profileHotspots,
profileStacks,
performanceWindows,
sourceAttribution,
captures: captures.slice(-20),
drainErrors: drainErrors.slice(-20),
valuesRedacted: true,
@@ -142,6 +154,7 @@ function buildFrontendPerformanceFindings(report) {
attributionMode: summary.attributionMode || "loaf-only-no-cpu-profile",
cpuProfileStatus: summary.cpuProfileStatus || "missing",
captureCount: summary.captureCount ?? 0,
windowCorrelations: (report?.performanceWindows || []).slice(0, 8),
longTaskCount: summary.longTaskCount ?? 0,
longAnimationFrameCount: summary.longAnimationFrameCount ?? 0,
eventLoopGapCount: summary.eventLoopGapCount ?? 0,
@@ -171,15 +184,20 @@ function frontendPerformanceAttributionStatus(input) {
(Array.isArray(input?.gaps) ? input.gaps.length : 0);
const hasCpuProfile = captureCount > 0;
const hasCpuProfileHotspots = profileHotspotCount > 0 || profileStackCount > 0;
const windowRows = Array.isArray(input?.performanceWindows) ? input.performanceWindows : [];
const coveredWindowCount = windowRows.filter((item) => item?.cpuProfileCoverageStatus === "covered" || item?.cpuProfileCoverageStatus === "overlapped").length;
const missedWindowCount = windowRows.filter((item) => item?.cpuProfileCoverageStatus === "missed").length;
if (hasCpuProfile) {
return {
cpuProfileStatus: hasCpuProfileHotspots ? "captured-with-hotspots" : "captured-no-hotspots",
attributionMode: "cpu-profile-and-performance-observer",
cpuProfileStatus: hasCpuProfileHotspots ? (coveredWindowCount > 0 ? "captured-with-window-hotspots" : "captured-hotspots-outside-performance-window") : "captured-no-hotspots",
attributionMode: missedWindowCount > 0 && coveredWindowCount === 0 ? "cpu-profile-missed-performance-window" : "cpu-profile-and-performance-observer",
noCpuProfile: false,
loafOnly: false,
cpuProfileHotspotEvidence: hasCpuProfileHotspots,
evidenceNote: hasCpuProfileHotspots
? "completed performanceCapture artifacts produced CPU profile hotspot evidence"
? coveredWindowCount > 0
? "completed performanceCapture artifacts overlap severe frontend performance windows; attached CPU hotspots are same-capture candidates"
: "completed performanceCapture artifacts produced CPU profile hotspots, but none overlap the severe frontend performance windows"
: "completed performanceCapture artifacts exist, but no CPU profile hotspots were extracted",
valuesRedacted: true,
};
@@ -198,6 +216,7 @@ function frontendPerformanceAttributionStatus(input) {
}
function compactPerformanceEventRow(row, perf) {
const window = performanceEventWindow(row, perf);
return {
ts: row.ts ?? null,
seq: row.seq ?? null,
@@ -213,6 +232,13 @@ function compactPerformanceEventRow(row, perf) {
blockingDurationMs: numberOrNull(perf.blockingDuration),
startTime: numberOrNull(perf.startTime),
thresholdMs: numberOrNull(perf.thresholdMs),
timeOrigin: numberOrNull(perf.timeOrigin),
observedAt: numberOrNull(perf.observedAt),
startEpochMs: window.startEpochMs,
endEpochMs: window.endEpochMs,
startAt: window.startAt,
endAt: window.endAt,
windowSource: window.source,
path: limitText(perf.path ?? "", 160),
url: safeReportUrl(perf.url),
scriptCount: Array.isArray(perf.scripts) ? perf.scripts.length : 0,
@@ -237,13 +263,21 @@ function compactPerformanceDrainError(row) {
function compactPerformanceCapture(row) {
const artifact = row.artifact && typeof row.artifact === "object" ? row.artifact : {};
const summary = row.summary && typeof row.summary === "object" ? row.summary : {};
const durationMs = numberOrNull(artifact.durationMs ?? summary.durationMs);
const endEpochMs = performanceTimestampMs(row.ts);
const startEpochMs = Number.isFinite(endEpochMs) && Number.isFinite(durationMs) ? endEpochMs - Number(durationMs) : null;
return {
ts: row.ts ?? null,
commandId: row.commandId ?? null,
captureId: row.captureId ?? artifact.captureId ?? null,
pageRole: row.pageRole ?? artifact.pageRole ?? null,
pageId: row.pageId ?? artifact.pageId ?? null,
durationMs: numberOrNull(artifact.durationMs),
durationMs,
startEpochMs,
endEpochMs,
startAt: startEpochMs === null ? null : new Date(startEpochMs).toISOString(),
endAt: endEpochMs === null ? null : new Date(endEpochMs).toISOString(),
windowSource: startEpochMs === null ? "missing-capture-window" : "completed-event-ts-minus-artifact-duration",
profileTotalTimeMs: numberOrNull(summary.totalTimeMs),
profileSampleCount: numberOrNull(summary.sampleCount),
path: artifact.path ?? null,
@@ -256,13 +290,22 @@ function compactPerformanceCapture(row) {
function compactLoafScript(script) {
const value = script && typeof script === "object" ? script : {};
const source = sourceAttributionFor(value.sourceURL, value.sourceFunctionName, value.lineNumber, value.columnNumber, value.sourceCharPosition);
return {
invoker: limitText(value.invoker ?? "", 160),
invokerType: limitText(value.invokerType ?? "", 80),
sourceURL: safeReportUrl(value.sourceURL),
sourceFunctionName: limitText(value.sourceFunctionName ?? "", 160),
sourceFile: source.sourceFile,
sourceLine: source.sourceLine,
sourceColumn: source.sourceColumn,
sourceCharPosition: numberOrNull(value.sourceCharPosition),
sourceMapStatus: source.sourceMapStatus,
sourceAttributionMode: source.sourceAttributionMode,
lineNumber: numberOrNull(value.lineNumber),
columnNumber: numberOrNull(value.columnNumber),
startTime: numberOrNull(value.startTime),
executionStart: numberOrNull(value.executionStart),
durationMs: numberOrNull(value.duration),
forcedStyleAndLayoutDurationMs: numberOrNull(value.forcedStyleAndLayoutDuration),
pauseDurationMs: numberOrNull(value.pauseDuration),
@@ -272,17 +315,22 @@ function compactLoafScript(script) {
function mergeLoafScript(groups, script, event) {
const compact = compactLoafScript(script);
const key = [compact.sourceFunctionName || compact.invoker || "(anonymous)", compact.sourceURL || "", compact.lineNumber ?? "", compact.columnNumber ?? ""].join("@");
const key = loafScriptKey(compact);
const group = groups.get(key) || { ...compact, key, count: 0, totalDurationMs: 0, maxDurationMs: 0, firstAt: event.ts, lastAt: event.ts, examples: [], valuesRedacted: true };
const duration = Number(compact.durationMs || 0);
group.count += 1;
group.totalDurationMs += duration;
group.maxDurationMs = Math.max(group.maxDurationMs, duration);
group.lastAt = event.ts;
if (group.examples.length < 8) group.examples.push({ ts: event.ts, sampleSeq: event.sampleSeq, durationMs: event.durationMs, scriptDurationMs: compact.durationMs, pageRole: event.pageRole, valuesRedacted: true });
if (group.examples.length < 8) group.examples.push({ ts: event.ts, startAt: event.startAt, endAt: event.endAt, sampleSeq: event.sampleSeq, durationMs: event.durationMs, scriptDurationMs: compact.durationMs, pageRole: event.pageRole, sourceCharPosition: compact.sourceCharPosition, valuesRedacted: true });
groups.set(key, group);
}
function loafScriptKey(script) {
if (!script || typeof script !== "object") return "";
return [script.sourceFunctionName || script.invoker || "(anonymous)", script.sourceURL || "", script.lineNumber ?? "", script.columnNumber ?? ""].join("@");
}
function finalizeScriptHotspot(group) {
return {
...group,
@@ -294,8 +342,9 @@ function finalizeScriptHotspot(group) {
function mergeProfileFunction(groups, fn, capture) {
const value = fn && typeof fn === "object" ? fn : {};
const source = sourceAttributionFor(value.url, value.functionName, value.lineNumber, value.columnNumber, null);
const key = [value.functionName || "(anonymous)", value.url || value.scriptId || "", value.lineNumber ?? "", value.columnNumber ?? ""].join("@");
const group = groups.get(key) || { functionName: value.functionName || "(anonymous)", url: safeReportUrl(value.url), scriptId: value.scriptId ?? null, lineNumber: numberOrNull(value.lineNumber), columnNumber: numberOrNull(value.columnNumber), key, captureCount: 0, sampleCount: 0, selfTimeMs: 0, totalTimeMs: 0, maxSelfTimeMs: 0, captures: [], valuesRedacted: true };
const group = groups.get(key) || { functionName: value.functionName || "(anonymous)", url: safeReportUrl(value.url), scriptId: value.scriptId ?? null, sourceFile: source.sourceFile, sourceLine: source.sourceLine, sourceColumn: source.sourceColumn, sourceMapStatus: source.sourceMapStatus, sourceAttributionMode: source.sourceAttributionMode, lineNumber: numberOrNull(value.lineNumber), columnNumber: numberOrNull(value.columnNumber), key, captureCount: 0, sampleCount: 0, selfTimeMs: 0, totalTimeMs: 0, maxSelfTimeMs: 0, captures: [], valuesRedacted: true };
const selfTime = Number(value.selfTimeMs || 0);
const totalTime = Number(value.totalTimeMs || 0);
group.captureCount += 1;
@@ -321,7 +370,8 @@ function mergeProfileStack(groups, stack, capture) {
const value = stack && typeof stack === "object" ? stack : {};
const key = String(value.key || "").slice(0, 800);
if (!key) return;
const group = groups.get(key) || { key, sampleCount: 0, selfTimeMs: 0, maxSelfTimeMs: 0, leaf: value.leaf ?? null, frames: Array.isArray(value.frames) ? value.frames.slice(-10) : [], captures: [], valuesRedacted: true };
const frames = Array.isArray(value.frames) ? value.frames.slice(-10).map(annotateProfileFrameSource) : [];
const group = groups.get(key) || { key, sampleCount: 0, selfTimeMs: 0, maxSelfTimeMs: 0, leaf: annotateProfileFrameSource(value.leaf), frames, captures: [], valuesRedacted: true };
const selfTime = Number(value.selfTimeMs || 0);
group.sampleCount += Number(value.sampleCount || 0);
group.selfTimeMs += selfTime;
@@ -346,6 +396,328 @@ function performanceCaptureArtifacts(artifacts) {
.map((item) => ({ ts: item.ts ?? null, captureId: item.captureId ?? null, commandId: item.commandId ?? null, path: item.path ?? null, summaryPath: item.summaryPath ?? null, sha256: item.sha256 ?? null, summarySha256: item.summarySha256 ?? null, valuesRedacted: true }));
}
function buildFrontendPerformanceWindows(input) {
const aggregatedLoafHotspotKeys = new Set(
(Array.isArray(input?.scriptHotspots) ? input.scriptHotspots : [])
.filter((item) => Number(item?.totalDurationMs ?? 0) >= alertThresholds.longAnimationFrameRedMs)
.map(loafScriptKey)
.filter(Boolean)
);
const events = (Array.isArray(input?.events) ? input.events : [])
.filter((item) => item && (item.kind === "longtask" || item.kind === "long-animation-frame" || item.kind === "event-loop-gap"))
.filter((item) => Number(item.durationMs ?? 0) >= frontendPerformanceWindowBudget(item) || hasAggregatedLoafHotspot(item, aggregatedLoafHotspotKeys))
.sort(descDuration)
.slice(0, 20);
return events.map((event) => correlateFrontendPerformanceWindow(event, input)).filter(Boolean);
}
function hasAggregatedLoafHotspot(event, keys) {
if (event?.kind !== "long-animation-frame" || !(keys instanceof Set) || keys.size === 0) return false;
for (const script of Array.isArray(event?.scripts) ? event.scripts : []) {
if (keys.has(loafScriptKey(script))) return true;
}
return false;
}
function correlateFrontendPerformanceWindow(event, input) {
const captures = Array.isArray(input?.captures) ? input.captures : [];
const matchingCaptures = captures.filter((capture) => samePageContext(event, capture));
const overlapping = matchingCaptures.filter((capture) => windowsOverlap(event, capture));
const covering = overlapping.filter((capture) => windowCovers(capture, event));
const selectedCapture = covering[0] || overlapping[0] || nearestCapture(event, matchingCaptures) || nearestCapture(event, captures);
const coverageStatus = selectedCapture
? covering.length > 0 ? "covered" : overlapping.length > 0 ? "overlapped" : "missed"
: "no-cpu-profile";
const profileEvidence = coverageStatus === "covered" || coverageStatus === "overlapped" ? {
topFunctions: (Array.isArray(input?.profileHotspots) ? input.profileHotspots : []).slice(0, 5),
topStacks: (Array.isArray(input?.profileStacks) ? input.profileStacks : []).slice(0, 3),
evidenceScope: "same-capture-window-candidate",
valuesRedacted: true,
} : null;
const sourceHint = event.kind === "long-animation-frame" ? topLoafScriptHint(event) : null;
return {
kind: event.kind,
ts: event.ts ?? null,
startAt: event.startAt ?? null,
endAt: event.endAt ?? null,
startEpochMs: event.startEpochMs ?? null,
endEpochMs: event.endEpochMs ?? null,
durationMs: event.durationMs ?? null,
blockingDurationMs: event.blockingDurationMs ?? null,
sampleSeq: event.sampleSeq ?? null,
pageRole: event.pageRole ?? null,
pageId: event.pageId ?? null,
pageEpoch: event.pageEpoch ?? null,
path: event.path ?? null,
url: event.url ?? null,
scriptCount: event.scriptCount ?? null,
topLoafScript: sourceHint,
cpuProfileCoverageStatus: coverageStatus,
coverageReason: cpuProfileCoverageReason(event, selectedCapture, coverageStatus),
capture: selectedCapture ? compactWindowCapture(selectedCapture, event) : null,
relatedNetwork: relatedNetworkRows(event, input?.network),
relatedSamples: relatedSampleRows(event, input?.samples),
profileEvidence,
valuesRedacted: true,
};
}
function frontendPerformanceWindowBudget(event) {
if (event?.kind === "longtask") return alertThresholds.longTaskRedMs;
if (event?.kind === "long-animation-frame") return alertThresholds.longAnimationFrameRedMs;
if (event?.kind === "event-loop-gap") return alertThresholds.eventLoopGapRedMs;
return 0;
}
function performanceEventWindow(row, perf) {
const timeOrigin = Number(perf?.timeOrigin);
const startTime = Number(perf?.startTime);
const duration = Number(perf?.duration);
if (Number.isFinite(timeOrigin) && timeOrigin > 0 && Number.isFinite(startTime)) {
const startEpochMs = timeOrigin + startTime;
const endEpochMs = startEpochMs + (Number.isFinite(duration) ? duration : 0);
return { startEpochMs: roundMs(startEpochMs), endEpochMs: roundMs(endEpochMs), startAt: new Date(startEpochMs).toISOString(), endAt: new Date(endEpochMs).toISOString(), source: "performance-timeOrigin-startTime" };
}
const observedAt = Number(perf?.observedAt);
if (Number.isFinite(observedAt) && observedAt > 0) {
const endEpochMs = observedAt;
const startEpochMs = endEpochMs - (Number.isFinite(duration) ? duration : 0);
return { startEpochMs: roundMs(startEpochMs), endEpochMs: roundMs(endEpochMs), startAt: new Date(startEpochMs).toISOString(), endAt: new Date(endEpochMs).toISOString(), source: "observedAt-minus-duration" };
}
const rowMs = performanceTimestampMs(row?.ts);
if (Number.isFinite(rowMs)) {
const endEpochMs = rowMs;
const startEpochMs = endEpochMs - (Number.isFinite(duration) ? duration : 0);
return { startEpochMs: roundMs(startEpochMs), endEpochMs: roundMs(endEpochMs), startAt: new Date(startEpochMs).toISOString(), endAt: new Date(endEpochMs).toISOString(), source: "row-ts-minus-duration" };
}
return { startEpochMs: null, endEpochMs: null, startAt: null, endAt: null, source: "missing-window" };
}
function performanceTimestampMs(value) {
const ms = Date.parse(String(value || ""));
return Number.isFinite(ms) ? ms : null;
}
function samePageContext(event, capture) {
if (!capture) return false;
if (event.pageRole && capture.pageRole && event.pageRole !== capture.pageRole) return false;
if (event.pageId && capture.pageId && event.pageId !== capture.pageId) return false;
return true;
}
function windowsOverlap(left, right) {
const leftStart = Number(left?.startEpochMs);
const leftEnd = Number(left?.endEpochMs);
const rightStart = Number(right?.startEpochMs);
const rightEnd = Number(right?.endEpochMs);
if (![leftStart, leftEnd, rightStart, rightEnd].every(Number.isFinite)) return false;
return leftStart <= rightEnd && rightStart <= leftEnd;
}
function windowCovers(outer, inner) {
const outerStart = Number(outer?.startEpochMs);
const outerEnd = Number(outer?.endEpochMs);
const innerStart = Number(inner?.startEpochMs);
const innerEnd = Number(inner?.endEpochMs);
if (![outerStart, outerEnd, innerStart, innerEnd].every(Number.isFinite)) return false;
return outerStart <= innerStart && outerEnd >= innerEnd;
}
function nearestCapture(event, captures) {
const rows = Array.isArray(captures) ? captures : [];
const eventStart = Number(event?.startEpochMs);
const eventEnd = Number(event?.endEpochMs);
if (![eventStart, eventEnd].every(Number.isFinite) || rows.length === 0) return null;
return rows
.map((capture) => ({ capture, distanceMs: captureWindowDistanceMs(eventStart, eventEnd, capture) }))
.filter((item) => Number.isFinite(item.distanceMs))
.sort((left, right) => left.distanceMs - right.distanceMs)[0]?.capture || null;
}
function captureWindowDistanceMs(eventStart, eventEnd, capture) {
const start = Number(capture?.startEpochMs);
const end = Number(capture?.endEpochMs);
if (![start, end].every(Number.isFinite)) return Number.POSITIVE_INFINITY;
if (eventEnd < start) return start - eventEnd;
if (end < eventStart) return eventStart - end;
return 0;
}
function cpuProfileCoverageReason(event, capture, status) {
if (status === "no-cpu-profile") return "no completed performanceCapture artifact is present";
if (!capture) return "no comparable capture window is present";
if (status === "covered") return "performanceCapture window fully covers this frontend performance event";
if (status === "overlapped") return "performanceCapture window overlaps this frontend performance event, but does not fully cover it";
const eventStart = Number(event?.startEpochMs);
const eventEnd = Number(event?.endEpochMs);
const captureStart = Number(capture?.startEpochMs);
const captureEnd = Number(capture?.endEpochMs);
if (Number.isFinite(captureStart) && Number.isFinite(eventEnd) && captureStart > eventEnd) return "nearest performanceCapture started after this frontend performance window ended";
if (Number.isFinite(captureEnd) && Number.isFinite(eventStart) && captureEnd < eventStart) return "nearest performanceCapture ended before this frontend performance window started";
return "completed performanceCapture exists but its window could not be matched to this frontend event";
}
function compactWindowCapture(capture, event) {
const distanceMs = captureWindowDistanceMs(Number(event?.startEpochMs), Number(event?.endEpochMs), capture);
return {
captureId: capture.captureId ?? null,
commandId: capture.commandId ?? null,
pageRole: capture.pageRole ?? null,
pageId: capture.pageId ?? null,
startAt: capture.startAt ?? null,
endAt: capture.endAt ?? null,
durationMs: capture.durationMs ?? null,
windowDistanceMs: Number.isFinite(distanceMs) ? roundMs(distanceMs) : null,
profileSampleCount: capture.profileSampleCount ?? null,
profileTotalTimeMs: capture.profileTotalTimeMs ?? null,
valuesRedacted: true,
};
}
function relatedNetworkRows(event, network) {
const rows = Array.isArray(network) ? network : [];
const start = Number(event?.startEpochMs);
const end = Number(event?.endEpochMs);
const toleranceMs = Math.max(1000, Math.min(10000, Number(event?.durationMs || 0) + 1000));
if (![start, end].every(Number.isFinite)) return [];
return rows
.filter((item) => {
const ms = performanceTimestampMs(item?.ts);
if (!Number.isFinite(ms)) return false;
return ms >= start - toleranceMs && ms <= end + toleranceMs;
})
.sort((left, right) => Math.abs(Number(performanceTimestampMs(left?.ts)) - start) - Math.abs(Number(performanceTimestampMs(right?.ts)) - start))
.slice(0, 8)
.map(compactRelatedNetworkRow);
}
function compactRelatedNetworkRow(item) {
const url = item?.url ?? item?.request?.url ?? item?.response?.url ?? item?.detail?.url ?? "";
return {
ts: item?.ts ?? null,
sampleSeq: item?.sampleSeq ?? null,
phase: item?.phase ?? item?.type ?? null,
method: item?.method ?? item?.request?.method ?? null,
status: item?.status ?? item?.response?.status ?? null,
urlPath: urlPathOnly(url),
bodyByteCount: numberOrNull(item?.bodyByteCount ?? item?.response?.bodyByteCount ?? item?.bodySummary?.byteCount ?? item?.bodySummary?.bodyByteCount),
durationMs: numberOrNull(item?.durationMs ?? item?.response?.durationMs),
routeSessionId: item?.routeSessionId ?? item?.sessionId ?? null,
traceId: item?.traceId ?? item?.bodySummary?.traceId ?? null,
valuesRedacted: true,
};
}
function relatedSampleRows(event, samples) {
const rows = Array.isArray(samples) ? samples : [];
const start = Number(event?.startEpochMs);
const end = Number(event?.endEpochMs);
const toleranceMs = Math.max(1000, Math.min(10000, Number(event?.durationMs || 0) + 1000));
if (![start, end].every(Number.isFinite)) return [];
return rows
.filter((item) => {
const ms = performanceTimestampMs(item?.ts);
if (!Number.isFinite(ms)) return false;
return ms >= start - toleranceMs && ms <= end + toleranceMs;
})
.slice(-6)
.map((sample) => ({
ts: sample?.ts ?? null,
sampleSeq: sample?.seq ?? sample?.sampleSeq ?? null,
pageRole: sample?.pageRole ?? null,
path: limitText(sample?.path ?? "", 160),
routeSessionId: sample?.routeSessionId ?? null,
activeSessionId: sample?.activeSessionId ?? null,
traceIds: sampleTraceIdsForPerformance(sample).slice(0, 6),
valuesRedacted: true,
}));
}
function sampleTraceIdsForPerformance(sample) {
const ids = new Set();
for (const group of [sample?.messages, sample?.traceRows, sample?.turns, sample?.diagnostics]) {
if (!Array.isArray(group)) continue;
for (const item of group) if (item?.traceId) ids.add(String(item.traceId));
}
if (sample?.traceId) ids.add(String(sample.traceId));
return Array.from(ids);
}
function topLoafScriptHint(event) {
const scripts = Array.isArray(event?.scripts) ? event.scripts : [];
return scripts
.slice()
.sort((left, right) => Number(right.durationMs ?? 0) - Number(left.durationMs ?? 0))[0] || null;
}
function buildFrontendSourceAttribution(input) {
const sourceFiles = new Map();
const add = (row) => {
const file = row?.sourceFile || sourceFileFromUrl(row?.sourceURL || row?.url);
if (!file) return;
const existing = sourceFiles.get(file) || { sourceFile: file, count: 0, sourceMapStatus: row?.sourceMapStatus || "missing", valuesRedacted: true };
existing.count += 1;
if (existing.sourceMapStatus !== "mapped") existing.sourceMapStatus = row?.sourceMapStatus || existing.sourceMapStatus;
sourceFiles.set(file, existing);
};
for (const row of Array.isArray(input?.scriptHotspots) ? input.scriptHotspots : []) add(row);
for (const row of Array.isArray(input?.profileHotspots) ? input.profileHotspots : []) add(row);
for (const stack of Array.isArray(input?.profileStacks) ? input.profileStacks : []) {
for (const frame of Array.isArray(stack?.frames) ? stack.frames : []) add(frame);
}
return {
sourceMapStatus: "missing",
note: "No source-map artifact is loaded by this analyzer; function/file/line fields are raw browser URLs or CPU profile callFrame locations.",
sourceFiles: Array.from(sourceFiles.values()).sort((left, right) => Number(right.count ?? 0) - Number(left.count ?? 0)).slice(0, 20),
valuesRedacted: true,
};
}
function annotateProfileFrameSource(frame) {
if (!frame || typeof frame !== "object") return frame ?? null;
const source = sourceAttributionFor(frame.url, frame.functionName, frame.lineNumber, frame.columnNumber, null);
return { ...frame, sourceFile: source.sourceFile, sourceLine: source.sourceLine, sourceColumn: source.sourceColumn, sourceMapStatus: source.sourceMapStatus, sourceAttributionMode: source.sourceAttributionMode, valuesRedacted: true };
}
function sourceAttributionFor(url, functionName, lineNumber, columnNumber, sourceCharPosition) {
return {
functionName: limitText(functionName ?? "", 160),
sourceFile: sourceFileFromUrl(url),
sourceLine: numberOrNull(lineNumber),
sourceColumn: numberOrNull(columnNumber),
sourceCharPosition: numberOrNull(sourceCharPosition),
sourceMapStatus: "missing",
sourceAttributionMode: "browser-raw-url-line-column",
valuesRedacted: true,
};
}
function sourceFileFromUrl(value) {
const text = String(value || "");
if (!text) return null;
try {
const parsed = new URL(text, "http://local.invalid");
const path = parsed.pathname || "";
const parts = path.split("/").filter(Boolean);
return limitText(parts[parts.length - 1] || path || text, 160);
} catch {
const clean = text.split(/[?#]/u)[0];
const parts = clean.split("/").filter(Boolean);
return limitText(parts[parts.length - 1] || clean, 160);
}
}
function urlPathOnly(value) {
const text = String(value || "");
if (!text) return null;
try {
const parsed = new URL(text, "http://local.invalid");
return limitText(parsed.pathname || "/", 180);
} catch {
return limitText(text.split("?")[0] || text, 180);
}
}
function frontendPerformanceMaxNumber(items, getter) {
const selected = maxByNumber(items, getter);
return selected ? numberOrNull(getter(selected)) : null;