Files
Agent-System/web/src/lib/initialLoadMetrics.ts
T
weichenandCursor eabaadd2a0 test(web): record login waterfall and init request counts
Give the initial-load baseline a repeatable probe that strips query strings and can assert the login member fanout stays on the current project.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-19 12:57:12 +00:00

64 lines
2.0 KiB
TypeScript

/** 首次加载证据的纯函数。URL 查询串可能带会话参数,证据里只留路径。 */
export interface ResourceSample {
path: string;
initiator: string;
durationMs: number;
transferBytes: number;
decodedBytes: number;
}
export interface RequestSample {
method: string;
path: string;
}
const HEAVY_CHUNK = /katex|mermaid|docx|xlsx|AdminConsole|BlockEditor|FilePreview/i;
export function redactResourceUrl(raw: string): string {
const noQuery = raw.split('?')[0] ?? raw;
try {
const parsed = new URL(noQuery);
return parsed.pathname || '/';
} catch {
return noQuery.replace(/^https?:\/\/[^/]+/i, '') || '/';
}
}
export function percentile(values: number[], p: number): number | null {
if (values.length === 0) return null;
const sorted = [...values].sort((a, b) => a - b);
const rank = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
return sorted[rank] ?? null;
}
export function summarizeResources(resources: ResourceSample[]) {
const transferBytes = resources.reduce((sum, row) => sum + (row.transferBytes || 0), 0);
const js = resources.filter((row) => row.path.endsWith('.js'));
const heavy = resources.filter((row) => HEAVY_CHUNK.test(row.path)).map((row) => row.path);
return {
requestCount: resources.length,
transferBytes,
jsTransferBytes: js.reduce((sum, row) => sum + (row.transferBytes || 0), 0),
jsCount: js.length,
heavyChunks: [...new Set(heavy)],
};
}
export function countInitRequests(requests: RequestSample[]) {
const members = requests.filter(
(row) => row.method === 'GET' && /^\/api\/v1\/projects\/\d+\/members$/.test(row.path),
);
const sessions = requests.filter(
(row) => row.method === 'GET' && row.path === '/api/v1/chat/sessions',
);
const projectIds = new Set(
members.map((row) => row.path.split('/')[4]).filter((id): id is string => Boolean(id)),
);
return {
memberGetCount: members.length,
distinctMemberProjects: projectIds.size,
sessionListGetCount: sessions.length,
};
}