@nacew/performance-web
v0.7.0
Published
Browser SDK for uTestMe render performance and frontend error telemetry.
Readme
@nacew/performance-web
Browser SDK for uTestMe render suites: Web Vitals (LCP, INP, CLS, FCP, TTFB), long tasks, and frontend errors.
Install
npm install @nacew/performance-webUsage
import { initUTestMePerformance } from "@nacew/performance-web";
initUTestMePerformance({
projectKey: "lp_…", // environment ingest API key from uTestMe → Projects
endpoint: "https://logs.example.com",
environment: "production",
sampleRate: 1,
// The version these measurements come from, to compare releases:
release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
});What it measures
| Metric | When |
| ------------------------------------------- | ----------------------------------------------------------------------------------------- |
| TTFB, FCP, LCP, NAV_DCL, NAV_LOAD | the page loading (startup) |
| INP | how fast the page responds to taps, clicks and keys, and which element was tapped (0.4.1) |
| CLS | layout shifts: the worst session window per page (0.6) |
| LONG_TASK | main-thread work over 50 ms that blocks the page |
| SCROLL_FPS, SCROLL_JANK | each scroll: frames per second, and the percent of frames over 50 ms (a visible stutter) |
| ROUTE_CHANGE | in a single-page app: from the tap that opened a route to its first painted frame (ms) |
| RESOURCE_*, DOM_NODES | page weight |
trackResponsiveness: false turns off the scroll and route timing; release is attached to every event.
From 0.5 the SDK also reports incidents: requests that failed (network error or HTTP 4xx/5xx), files that would not load, freezes (a tap unanswered for 1s+, or a 2s+ lock-up) and a blank screen after an error. Each incident, and each uncaught error, carries the last steps before it: taps named by element, page changes and requests. Page text and query strings are never sent. Turn it off with trackIncidents: false.
From 0.6 the SDK is built not to cost the page it measures:
- Nothing is sent while the page loads. Events wait until the load event and a quiet moment, then go out together at most every 10 seconds (
flushIntervalMs), and once more when the page is hidden or closed (as a beacon). A batch stays under 64 KB. - No preflight. Batches are
text/plainwith the key in the body, so each one is a single request with no CORSOPTIONSbefore it. - LCP, CLS and INP are final values, one per page view, as Google counts them: LCP once per load, CLS as the worst session window, INP as the slowest interaction (98th percentile past 50), each reported when the page is hidden or the route changes.
- Page weight (
RESOURCE_*) is sampled once after load and once per route, not on a timer, and the SDK's own requests are not counted in it. Long tasks are capped at 20 per page. sampleRatekeeps or drops a whole visit, not single events.
Events are POSTed to {endpoint}/api/v1/ingest/frontend as text/plain JSON with the key in the body (X-Api-Key is still accepted for server-side callers).
Names from before 0.7
The SDK used to be called LogPulse. Every export still exists under its old
LogPulse… name (initLogPulsePerformance, LogPulsePerformanceProvider,
LogPulseProfiler, …) as the same function, component or type as the
UTestMe… one, marked @deprecated. Existing apps keep working without
changes and can switch names whenever convenient; mixing both is fine.
