AbsoluteJS

Performance

@absolutejs/performancev0.1.1-beta.0betaObservability

Core Web Vitals, server route latency, static asset timing and Postgres health for AbsoluteJS applications — four measurements, one console, Drizzle-native.

#Installation

BASH
bun add @absolutejs/performance

#Capabilities

Overview

Core 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.

Schema

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.

Server route latency

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.

Web Vitals

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.

Static assets

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.

The console

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.

Database health

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

What you can build

Overview

Core Web Vitals, server route latency, static asset timing and Postgres health — the four measurements behind a performance console, Drizzle-native.

Install

Peer: drizzle-orm >= 1.0.0-rc.4. Postgres.

Schema

Five tables, exported as Drizzle definitions — register them in your own schema so your migrations own them:

Hardening checklist

Production guidance

Make every external boundary explicitPin the deployed @absolutejs/performance version, replace example or memory-backed dependencies with durable implementations, bound external calls, protect credentials, and emit enough evidence to retry or recover safely.

Follow in order

Troubleshooting path

1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/performance example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.

#Schema

Partial snippet

Five tables, exported as Drizzle definitions — register them in your own schema so your migrations own them:

TS
export {
  webVitals,
  webVitalDaily,
  resourcePerformance,
  routeLatencyWindows,
  perfIssues,
} from "@absolutejs/performance";

#Server route latency

Partial snippet

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.

TS
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,
  });
});

#Public entry points

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.

@absolutejs/performance@absolutejs/performance/client@absolutejs/performance/drizzle

#Package commands

Scripts declared by this project’s package manifest.

bun run buildrm -rf dist && bun build src/index.ts src/drizzle.ts --outdir dist --root ./src --sourcemap --target=bun --external drizzle-orm --external 'drizzle-orm/*' && bun build src/client.ts --outdir dist --root ./src --sourcemap --target=browser --format esm && tsc --project tsconfig.build.json
bun run check:packagebun run format && bun run typecheck && bun run test && bun run build && absolute-changelog check
bun run formatprettier --write "./**/*.{ts,json,md}"
bun run testbun test
bun run typechecktsc --noEmit

#API reference

Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.

46 symbols
perfIssuesexportPermalink
TS
perfIssues
Exported from @absolutejs/performance