first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
# memory
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`MemoryProfiler` captures periodic heap snapshots, records labelled allocation totals, tracks a peak-heap watermark, and runs a simple trend-based leak detector. Heap readings come from an injectable `HeapCapture` so test runs are deterministic and non-Node environments can plug their own probe. Timer source is `TimersLike`, matching `Sampler`.
## Features
- Zod-validated config (`sampleIntervalMs > 0`, `maxSnapshots > 0`, `leakThresholdBytes ≥ 0`, `leakWindowSize ≥ 2`).
- Injectable `Clock`, `TimersLike`, `HeapCapture`. Defaults read `process.memoryUsage` then `performance.memory` then return zeros.
- `start()` / `stop()` are idempotent — double-start and double-stop are no-ops.
- `takeSnapshot(label?)` records a snapshot; FIFO-evicts beyond `maxSnapshots`.
- `recordAllocation(label, bytes)` aggregates `allocations` / `totalBytes` / `peakBytes` per label.
- `detectLeak()` returns true when the last `leakWindowSize` snapshots are monotonically non-decreasing AND total growth meets `leakThresholdBytes`.
- `triggerGC()` invokes `globalThis.gc()` when Node was started with `--expose-gc`.
- `events: EventBus<MemoryProfilerEventMap>` — `memory:start`, `memory:stop`, `memory:snapshot`, `memory:leak-detected`.
- Telemetry: `profiler_memory_snapshots_total` counter on every snapshot.
## Usage: Basic
```ts
import { MemoryProfiler } from "@suaveplan/profiler";
const mp = new MemoryProfiler({ sampleIntervalMs: 1000 });
mp.start();
// ...
mp.stop();
if (mp.detectLeak()) console.warn("leak suspected");
```
## Usage: Advanced
```ts
import { MemoryProfiler } from "@suaveplan/profiler";
const fakeHeap: Array<{ heapUsed: number; heapTotal: number }> = [
{ heapUsed: 1_000_000, heapTotal: 10_000_000 },
{ heapUsed: 2_000_000, heapTotal: 10_000_000 },
{ heapUsed: 3_000_000, heapTotal: 10_000_000 },
];
let i = 0;
const mp = new MemoryProfiler({
leakWindowSize: 3,
leakThresholdBytes: 1_500_000,
heapCapture: () => fakeHeap[i++] ?? fakeHeap[fakeHeap.length - 1]!,
});
mp.events.on("memory:leak-detected", ({ snapshots }) => {
log("leak window", snapshots);
});
for (let n = 0; n < 3; n++) mp.takeSnapshot();
mp.detectLeak(); // true → emits memory:leak-detected
```
## API Reference
- `class MemoryProfiler` — `new MemoryProfiler(options?: MemoryProfilerOptions)`.
- `interface MemoryProfilerOptions extends MemoryProfilerConfig { clock?, timers?, heapCapture? }`.
- Methods: `start`, `stop`, `takeSnapshot`, `recordAllocation`, `getAllocations`, `getAllocation`, `getSnapshots`, `getPeakHeapUsed`, `isRunning`, `detectLeak`, `triggerGC`, `clear`.
## Implementation Notes
The leak detector walks the last `leakWindowSize` snapshots and counts strictly-increasing transitions; a leak is declared only when EVERY transition is non-decreasing AND end-minus-start exceeds the threshold. That eliminates the false-positive case where one large jump in an otherwise flat window would otherwise read as a leak. `triggerGC` is a best-effort hook — Node only exposes `globalThis.gc` under `--expose-gc`, so the false branch matters in production.