npm.io
0.0.15 • Published 2 weeks ago

ts-common-lib

Licence
MIT
Version
0.0.15
Deps
12
Size
199 kB
Vulns
0
Weekly
0

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 import ts-common-lib/perf; no dist/ or compiled output.
  • API: Import runScenarios, parseArgs, reporter helpers, and types from ts-common-lib/perf. The package is client-agnostic: ScenarioContext is 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 .ts scripts under perf/profiling/ via tsx (e.g. a perf-profiling wrapper or direct tsx path/to/analyze.ts). Use --port=NNN or --service=name with env PERF_INSPECTOR_PORTS.
  • Layout: Sources under perf/ (e.g. perf/performance/, perf/profiling/, perf/hooks.ts). No perf/package.json, no perf/tsconfig.json, no perf/dist. Profiling scripts are grouped under perf/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.ts not heap/heap-diff.ts per CODING_STANDARDS). Run via npm scripts or tsx perf/profiling/<group>/<script>.ts.
  • Why exports in package.json: Node needs the subpath mapping so that import … from 'ts-common-lib/perf' resolves; baseUrl in tsconfig only applies to in-repo resolution. CDP profiling uses Node’s native WebSocket client (Node >= 24 per engines).
  • Hooks: perf/hooks.ts exposes a generic layer over perf_hooks: markStart/markEnd, measureAsync, measureEventLoopDelay, createMeasureObserver. Use in tests or microservices to measure spans and event loop lag. See test/perf.spec.ts for a full demo (hooks + runScenarios + MetricsCollector).
  • Single CLI: All commands go through perf/cli.ts. Run npm 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 A npm run perf -- demo:run; Terminal B npm run perf -- capture:cpu (or capture:heap, capture:mem, capture:allocation). Output → perf/fixtures/ (git/npm ignored). Then npm run perf -- analyze <file.cpuprofile> or perf -- analyze-heap <file.heapsnapshot>.
  • Test all commands: Run npm run perf:test-all (implemented in perf/test-all.ts, cross-platform Windows + Linux). It starts the demo, captures CPU/heap/memory/allocation to perf/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 use perf-analyze-heap for 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

Keywords