RxJS leak detection · dev only
One line in main.ts, a floating widget, a local dashboard. Works with any Angular dev-mode app. No Chrome extension.
npm install --save-dev rxjs-leak-finderimport { isDevMode } from '@angular/core';
import { Observable } from 'rxjs';
import { enableRxjsLeakDetector } from 'rxjs-leak-finder';
if (isDevMode()) {
enableRxjsLeakDetector(Observable);
}npx rxjs-leak-finder dashboard30 seconds
The floating widget on your app starts capturing every subscribe() with its stack and the active route.
Visit a route, leave it, visit another. The History API hook marks every route boundary in the session. Leaks start showing up the instant a route loses an open subscription.
Leaks stream into the dashboard the moment you leave a route — no waiting. Hit Stop to freeze the full source-mapped report.
Each leak gets a kind so you know why it leaked — not just where.
nested-subscribeA subscribe() runs inside another subscribe(). Both leak.
outer$.subscribe(() => {
inner$.subscribe(...);
});outer$.pipe(switchMap(() => inner$)).subscribe(...);async-initsubscribe() runs after await in async ngOnInit. takeUntilDestroyed() no-ops.
async ngOnInit() {
await loadData();
obs$.subscribe(...);
}ngOnInit() {
obs$.pipe(takeUntilDestroyed())
.subscribe(...);
}ng-initA bare subscribe() in ngOnInit with no teardown.
ngOnInit() {
interval(1000).subscribe(...);
}ngOnInit() {
interval(1000)
.pipe(takeUntilDestroyed())
.subscribe(...);
}file:line → opens in your editor (VS Code, IntelliJ, …).
No magic, no devtools protocol, no extension. Just a stack trace, a route boundary, and a heuristic.
Replace Observable.prototype.subscribe to tag every Subscription with its stack at creation time.
Track route changes via the History API. A "leak" is a subscription created on a route you've since left.
Classify by stack-trace heuristics: nested, async-init, global-event, timer, subject, …
Full architecture → HOW_IT_WORKS.md
Today the dashboard tells you which subscriptions leaked. The next release tells you how much memory each one is holding, so you can fix the biggest ones first and prove the win.
Right after you click Stop, the widget grabs a V8 heap snapshot of your running app — same format Chrome DevTools produces.
Every leaked Subscription is tagged with a hidden id, so the analyzer can find it in the snapshot and follow every object holding it alive.
Each leak gets a retained-bytes column. Sort by it, see which service or closure is hoarding tens of MB, fix it, re-record, watch it drop to 0.
Read the full plan, trade-offs, and alternatives in the ROADMAP — or open an issue if you want this sooner.
No — wrap the call in `if (isDevMode())`. The runtime is ~5 KB gzipped and inert until `enableRxjsLeakDetector` is called.