ts-common-lib
v0.0.5
Published
TypeScript common combining type utilities, validation patterns, and testing infrastructure
Maintainers
Readme
TypeScript common
A comprehensive TypeScript common combining type utilities, validation patterns, and testing infrastructure. This package aggregates reusable mapped types, validation schemas, and driver patterns to avoid re-writing them across different projects.
Features
- Type Utilities: Advanced TypeScript type utilities for objects, tuples, functions, classes, and operands
- Validation Patterns: Schema validation using ArkType with driver pattern support
- Testing Infrastructure: Vitest configuration with coverage reporting
Performance & profiling (perf/)
The perf/ folder provides reusable performance testing and profiling for Node/TypeScript projects. Use it from any repo by depending on the common and the perf export (e.g. "ts-common-lib": "file:../../ts-common-lib" and import … from 'ts-common-lib/perf').
- No build: Exports point to TypeScript source (
.ts). Consumers (e.g. gateway) run with tsx and importts-common-lib/perf; nodist/or compiled output. - API: Import
runScenarios,parseArgs, reporter helpers, and types fromts-common-lib/perf. The package is client-agnostic:ScenarioContextis generic (Record<string, unknown>). Your project registers its own client (e.g. REST, gRPC) and context shape; scenarios use that client and typed context. - Profiling: Consumer runs the
.tsscripts underperf/profiling/via tsx (e.g. aperf-profilingwrapper or directtsx path/to/analyze.ts). Use--port=NNNor--service=namewith envPERF_INSPECTOR_PORTS. - Layout: Sources under
perf/(e.g.perf/performance/,perf/profiling/,perf/hooks.ts). Noperf/package.json, noperf/tsconfig.json, noperf/dist. Profiling scripts are grouped underperf/profiling/:common/(shared CLI args),heap/(snapshot, analyze, diff, allocation-sampling, baseline, snapshot-signal, memory-over-time, utils),cpu/(profile, analyze, flamegraph, utils); at root:event-loop-delay.ts,gc-trace.ts. Names avoid redundant prefix (e.g.heap/diff.tsnotheap/heap-diff.tsper CODING_STANDARDS). Run via npm scripts ortsx perf/profiling/<group>/<script>.ts. - Why
exportsin package.json: Node needs the subpath mapping so thatimport … from 'ts-common-lib/perf'resolves;baseUrlin tsconfig only applies to in-repo resolution. Consumers that run the profiling scripts (CDP/WebSocket) needwsin their own dependencies. - Hooks:
perf/hooks.tsexposes a generic layer overperf_hooks:markStart/markEnd,measureAsync,measureEventLoopDelay,createMeasureObserver. Use in tests or microservices to measure spans and event loop lag. Seetest/perf.spec.tsfor a full demo (hooks +runScenarios+MetricsCollector). - Single CLI: All commands go through
perf/cli.ts. Runnpm run perf -- <command> [args...](e.g.perf -- flamegraph file.cpuprofile,perf -- heap --service=auth). Commands:demo:run|capture:cpu|capture:heap|capture:mem|capture:allocation|cpuprofile|heap|memory-over-time|allocation|event-loop-delay|baseline-heap|analyze|analyze-heap|heap-diff|flamegraph|gc-trace|heap-signal. - Real demo capture: Same expensive + leak cases in
perf/demo-run.ts. Terminal Anpm run perf -- demo:run; Terminal Bnpm run perf -- capture:cpu(or capture:heap, capture:mem, capture:allocation). Output →perf/fixtures/(git/npm ignored). Thennpm run perf -- analyze <file.cpuprofile>orperf -- analyze-heap <file.heapsnapshot>. - Test all commands: Run
npm run perf:test-all(implemented inperf/test-all.ts, cross-platform Windows + Linux). It starts the demo, captures CPU/heap/memory/allocation toperf/fixtures/, then runs analyze, analyze-heap, flamegraph, heap-diff, gc-trace, heap-signal, event-loop-delay, and baseline-heap. You can also run analyze/flamegraph/analyze-heap/heap-diff on existing fixture files without the demo.
Profiling tools (CPU, memory, and beyond)
Beyond CPU profiling, other dimensions help explain slowdowns, OOMs, and odd behaviour. Summary of what the perf scripts support and when to use them:
| Tool | What it shows | Typical use |
|------|----------------|-------------|
| CPU profile (.cpuprofile) | Where CPU time is spent (self-time per function) | Capture during load; analyse with perf-analyze path/to/file.cpuprofile or Chrome DevTools → Performance → Load profile. |
| Flamegraph (SVG) | Same data as CPU profile, visual bars | perf-flamegraph path/to/file.cpuprofile [--top=N] [--out=flame.svg]. Share or embed the SVG. |
| Heap snapshot (.heapsnapshot) | Who holds memory (object graph, retainers) | Capture via inspector script or Node --heapsnapshot-signal=SIGUSR2 + perf-heap-signal --pid=NNN. Analyse: perf-analyze-heap path/to/file.heapsnapshot [--top=25]. |
| Heap diff | What grew between two snapshots (leak check) | Take two snapshots (before/after load). perf-heap-diff before.heapsnapshot after.heapsnapshot [--top=20]. |
| Memory over time | heapUsed / rss sampled during a run | perf-memory-over-time [--interval=5] [--duration=120]. Optionally --out=csv --out-file=.... Run stress in another terminal to see heap under load. |
| Allocation sampling | Who allocates (not just who holds) | perf-allocation [--duration=30]. Run load during capture. Open JSON in Chrome Memory or parse for top allocators. |
| Event loop delay | Lag between scheduled and actual run of setImmediate | perf-event-loop-delay [--duration=20] [--samples=50]. High p95/p99 → event loop blocked. |
| GC trace | GC events (Scavenge, Mark-sweep) over time | perf-gc-trace --run="node app.js" [--duration=30] [--out=gc-summary.txt]. Correlate P99 spikes with GC. |
| Heap snapshot on signal | Capture heap in prod without inspector | Target: node --heapsnapshot-signal=SIGUSR2 app.js. Trigger: perf-heap-signal --pid=NNN. Snapshot written by Node in target cwd. |
| Baseline heap | CI regression: fail if heap exceeds baseline | perf-baseline-heap [--duration=15] [--baseline=BYTES] [--margin=1.2] [--out=baseline.txt]. Or set PERF_HEAP_BASELINE_BYTES. |
Memory / heap — when and how
- Heap snapshot: Use when RSS or heap grows over time (possible leak), OOM crashes, or you want to see what’s retaining memory. Capture via script (inspector + CDP
HeapProfiler.takeHeapSnapshot), Chrome DevTools, or Node--heapsnapshot-signal=SIGUSR2+kill -SIGUSR2 <pid>. Analyse in Chrome → Memory → Load, or useperf-analyze-heapfor a text summary. - Memory over time: Use
process.memoryUsage()(or the script) to confirm “heap is growing” before taking snapshots. - Allocation sampling: Use
perf-allocation(CDP) or Chrome DevTools → Memory → “Allocation instrumentation on timeline” or “Allocation sampling”. Use when you want to reduce allocations (lower GC or improve latency), not necessarily to find a leak.
Event loop / async
When latency is high and CPU profile shows a lot of “idle” or “(program)”, suspect I/O or event loop lag. Measure event loop delay (time between when a timer/tick was scheduled and when it ran). Options: perf_hooks, a small script with setTimeout/setImmediate, or tools like Clinic.js (clinic doctor). High delay → too much sync work per tick or slow I/O; then use CPU profile for sync work or DB/HTTP metrics for I/O.
Other dimensions
| Dimension | When to use | How (short) |
|-----------|-------------|-------------|
| DB / driver | Queries slow or pool saturated | Slow query log, driver metrics (pool size, queue). Add timing in resolvers or use APM. |
| Redis | Cache/session latency | Redis INFO, LATENCY, client metrics. |
| Network | Downstream HTTP or external APIs slow | HTTP client metrics, tracing (e.g. OpenTelemetry). |
| Garbage collection | GC pauses visible in latency (P99 spikes) | Node --trace-gc or v8 flags; CPU profile shows “(garbage collector)”. Reduce allocations in hot paths if GC % is high. |
Quick decision table
| Symptom | First step | Then | |---------|------------|------| | High CPU, slow requests | CPU profile | Optimise hot functions; consider parse cache, projection. | | RSS/heap grows; OOM or suspicion of leak | Heap snapshot (before/after or over time) | Compare snapshots; find retainers; fix leaks or cap caches. | | High latency but CPU mostly idle | Event loop delay + DB/Redis latency | Find I/O or sync bottleneck; tune pool, indexes, or move work off the loop. | | P99 spikes, GC high in CPU profile | Allocation profile or trace-gc | Reduce allocations in hot paths; reuse objects. | | “Who allocates the most?” | Allocation sampling | DevTools or Clinic.js; then refactor hot allocators. |
References
- Chrome DevTools: Memory tab (heap snapshots, allocation sampling); Performance tab (CPU, load .cpuprofile).
- Node: Diagnostics (heap snapshot, etc.).
