Overview
Core Web Vitals, server route latency, static asset timing and Postgres health — the four measurements behind a performance console, Drizzle-native.
@absolutejs/performancev0.1.1-beta.0betaObservabilityCore Web Vitals, server route latency, static asset timing and Postgres health for AbsoluteJS applications — four measurements, one console, Drizzle-native.
bun add @absolutejs/performanceCore Web Vitals, server route latency, static asset timing and Postgres health — the four measurements behind a performance console, Drizzle-native.
Four measurements of one question: is this fast enough to use? Vitals are what a person's browser felt, resources are what the page had to download, routes are what the server took, and the database numbers are where server time usually goes. They stay separate because the fix for each is different.
Five tables, exported as Drizzle definitions — register them in your own schema so your migrations own them:
The database section additionally reads pg_stat_activity, pg_stat_user_tables, pg_stat_user_indexes, and — if the extension is installed — pg_stat_statements. Each read is guarded independently, so a managed provider without the extension loses the slow-query list and keeps the rest.
Every response is timed and folded into a per-route counter. A row per request would put a database write in the path of every request — making the thing being measured slower — and leave millions of rows to scan; a counter per route per minute costs one upsert per flush and answers the same questions.
routeShape collapses ids so /orders/1042 and /orders/1043 are one route rather than two — UUIDs, long hex, numeric segments and prefixed object ids (cs_test_…) out of the box, plus any idPatterns you pass.
start installs the interval and a beforeExit flush, and deliberately does not touch SIGTERM/SIGINT. Registering a signal listener replaces the runtime's default terminate behaviour: unless the handler itself exits, the process survives the signal. A library that quietly does that to its host turns every kill, every orchestrator stop, and every build step that starts the app and signals it afterwards into a hang. If you want a true shutdown drain, you own your shutdown — call flush() from your own handler and then exit or re-raise the signal.
Budgets are Google's published thresholds — the numbers search ranking is scored against, so they are not yours to invent. ratingFor scores a value the same way the browser does, which is what makes a backfilled or synthetic sample comparable with a real one.
p75 is the headline because that is what Core Web Vitals is judged on; p50 and p95 sit either side so a page whose median is fine but whose tail is bad is visible as what it is.
Everything is sampled, not only what crossed a threshold: a threshold can say a file was slow, but never that a fast file loaded on every page is the one worth caching. cacheRate is that answer.
Findings are derived live rather than stored, so nothing on the list can be stale — a page that got fixed stops appearing because it got fixed. The only stored half is the human decision, joined on, so a known and accepted slow page stops shouting without disappearing. state: "open" deletes the decision outright rather than leaving a tombstone that would keep the finding looking handled.
Thresholds are the caller's: slowRouteMeanMs, slowResourceMs, slowQueryMeanMs, errorRateLimit, poorRateLimit, minRouteCalls, minVitalSamples.
startPerformanceRollup exists because a console that is only correct when a host crontab exists is a console that quietly goes stale — the one failure a health dashboard cannot afford. The rollup is idempotent, so an endpoint your scheduler also calls costs a duplicate scan and nothing else.
Read from Postgres' own statistics: no query wrapping, nothing on the hot path, zero cost until the page is opened. Slow queries are ordered by mean time, but mean × calls is reported too — that column is what exposes an N+1, where a 2ms query called forty thousand times is the actual problem and no single call looks slow.
Outcomes
Core Web Vitals, server route latency, static asset timing and Postgres health — the four measurements behind a performance console, Drizzle-native.
Peer: drizzle-orm >= 1.0.0-rc.4. Postgres.
Five tables, exported as Drizzle definitions — register them in your own schema so your migrations own them:
Hardening checklist
Follow in order
Five tables, exported as Drizzle definitions — register them in your own schema so your migrations own them:
export {
webVitals,
webVitalDaily,
resourcePerformance,
routeLatencyWindows,
perfIssues,
} from "@absolutejs/performance";Every response is timed and folded into a per-route counter. A row per request would put a database write in the path of every request — making the thing being measured slower — and leave millions of rows to scan; a counter per route per minute costs one upsert per flush and answers the same questions.
const timings = createRouteTimingCollector();
timings.start(db, releaseSha); // periodic flush + drain on SIGTERM/SIGINT
app.request(({ request }) => start.set(request, performance.now()));
app.afterResponse(({ request, response }) => {
timings.record({
durationMs: performance.now() - start.get(request)!,
method: request.method,
pathname: new URL(request.url).pathname,
status: response.status,
});
});Supported entry points declared by this project’s package manifest. Internal dist paths are not part of the package contract.
Public package entry point declared in package.json.
Scripts declared by this project’s package manifest.
Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.