/** * Home bootstrap failures are not one "the API is slow" bucket. * * `api-error` is the in-bundle account/session path (HTTP status or the 8s * cloud timeout). The other categories are what the inline dead-screen script * can see before that bundle runs. Observation records only category, build * id, and duration — never a payload, exception string, or birth data. */ export const HOME_BOOTSTRAP_FAILURE = { hydrateTimeout: "hydrate-timeout", chunk404: "chunk-404", chunkParse: "chunk-parse", staleCache: "stale-cache", weakNetwork: "weak-network", apiError: "api-error", } as const; export type HomeBootstrapFailureCategory = (typeof HOME_BOOTSTRAP_FAILURE)[keyof typeof HOME_BOOTSTRAP_FAILURE]; export type HomeBootstrapFailureInput = { /** `false` when the browser reports offline. `null` when the signal is absent. */ online: boolean | null; scriptMessage?: string; scriptSource?: string; /** A failed script whose resource timing says it was served from cache. */ resourceFromCache?: boolean; /** HTTP status from an account/session/model request the bundle actually made. */ apiStatus?: number | null; /** Bundle-side cloud timeout (page.tsx 8000ms), not the dead-screen timer. */ apiTimedOut?: boolean; }; const PARSE_FAILURE = /SyntaxError|Unexpected token|Invalid regular expression/i; const CHUNK_LOAD_FAILURE = /ChunkLoadError|Loading chunk|Failed to fetch dynamically imported module|Failed to load/i; export function sanitizeBootstrapBuildId(value: string | null | undefined): string { if (typeof value !== "string") return "unknown"; const trimmed = value.trim(); return /^[0-9a-fA-F]{7,64}$/.test(trimmed) ? trimmed : "unknown"; } export function classifyHomeBootstrapFailure( input: HomeBootstrapFailureInput, ): HomeBootstrapFailureCategory { if (typeof input.apiStatus === "number" && input.apiStatus >= 400) { return HOME_BOOTSTRAP_FAILURE.apiError; } const message = input.scriptMessage ?? ""; const source = input.scriptSource ?? ""; const parseFailed = PARSE_FAILURE.test(message); const chunkFailed = CHUNK_LOAD_FAILURE.test(`${message} ${source}`) || /\/_next\/static\//.test(source); if (parseFailed) { return input.resourceFromCache ? HOME_BOOTSTRAP_FAILURE.staleCache : HOME_BOOTSTRAP_FAILURE.chunkParse; } if (chunkFailed) { if (input.resourceFromCache) return HOME_BOOTSTRAP_FAILURE.staleCache; if (input.online === false) return HOME_BOOTSTRAP_FAILURE.weakNetwork; return HOME_BOOTSTRAP_FAILURE.chunk404; } if (input.online === false) return HOME_BOOTSTRAP_FAILURE.weakNetwork; if (input.apiTimedOut) return HOME_BOOTSTRAP_FAILURE.apiError; return HOME_BOOTSTRAP_FAILURE.hydrateTimeout; } export type BootstrapObservation = { category: HomeBootstrapFailureCategory; buildId: string; durationMs: number; }; export function buildBootstrapObservation( category: HomeBootstrapFailureCategory, buildId: string, durationMs: number, ): BootstrapObservation { const safeDuration = Number.isFinite(durationMs) ? Math.max(0, Math.round(durationMs)) : 0; return { category, buildId: sanitizeBootstrapBuildId(buildId), durationMs: safeDuration, }; }