Download src/lib/telemetry.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 2.76 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/telemetry.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/telemetry.ts
-
curl -L -o telemetry.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/telemetry.ts
2.76 kB
| // Forwards unhandled UI errors to the backend so they land in the same | |
| // error-tracking pane as backend errors. Fire-and-forget: never throws, never | |
| // blocks render. No-op on the server. | |
| import { track } from "@vercel/analytics"; | |
| import { getApiBaseUrl } from "@/lib/api/client"; | |
| const SENSITIVE_ASSIGNMENT = /\b(authorization|cookie|password|token|secret|api[_-]?key|service[_-]?role)\b(\s*[:=]\s*)([^\s,;]+)/gi; | |
| const BEARER_VALUE = /\bbearer\s+[a-z0-9._~+/=-]+/gi; | |
| export function sanitizeClientErrorText(value: unknown, limit: number): string { | |
| return String(value || "Unknown client error") | |
| .replace(BEARER_VALUE, "Bearer [REDACTED]") | |
| .replace(SENSITIVE_ASSIGNMENT, (_match, key: string, separator: string) => `${key}${separator}[REDACTED]`) | |
| .slice(0, limit); | |
| } | |
| export function reportClientError( | |
| error: unknown, | |
| context: string, | |
| extra?: { digest?: string }, | |
| ): void { | |
| if (typeof window === "undefined") return; | |
| try { | |
| const err = error as { message?: string; stack?: string } | undefined; | |
| const body = JSON.stringify({ | |
| message: sanitizeClientErrorText(err?.message || error, 2000), | |
| stack: sanitizeClientErrorText(err?.stack || "", 8000), | |
| url: `${window.location.origin}${window.location.pathname}`, | |
| digest: extra?.digest, | |
| user_agent: navigator.userAgent, | |
| context, | |
| }); | |
| // A relative fallback keeps same-origin deployments observable. If the | |
| // API is intentionally hosted elsewhere, configure the public base URL; | |
| // never send an error to an accidental empty/undefined host. | |
| const url = `${getApiBaseUrl()}/telemetry/client-error`; | |
| // sendBeacon survives page unload; fall back to fetch keepalive. | |
| if (navigator.sendBeacon) { | |
| navigator.sendBeacon(url, new Blob([body], { type: "application/json" })); | |
| } else { | |
| void fetch(url, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body, | |
| keepalive: true, | |
| }).catch(() => {}); | |
| } | |
| } catch { | |
| // swallow — telemetry must never break the error UI | |
| } | |
| } | |
| /** Privacy-safe funnel telemetry for non-payment product actions. */ | |
| export function trackClientEvent( | |
| name: string, | |
| properties?: Record<string, string | number | boolean>, | |
| ): void { | |
| if (typeof window === "undefined") return; | |
| try { | |
| const safeName = name.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80); | |
| const safeProperties = Object.fromEntries( | |
| Object.entries(properties ?? {}).filter( | |
| ([key, value]) => | |
| /^[a-zA-Z0-9_.-]{1,40}$/.test(key) && | |
| (typeof value === "string" ? value.length <= 120 : true), | |
| ), | |
| ); | |
| track(safeName, safeProperties); | |
| } catch { | |
| // Analytics must never affect the student workflow. | |
| } | |
| } | |