Repository navigation
pipes: fixes and improvements for the pipes inspector #350
Copy link
Copy link
Closed
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Description
Context
Fixes and improvements from a code audit of the pipes inspector, done in October 2026. Roadmap work lives in #340, #341, #342 and #343. Items marked (to confirm) were not reproduced; check them before fixing.
Problems
Medium
- Pages mixed in the panel: the
pipe-usageshared state is the aggregate of every connected page (counts summed, last call taken from the newest page,asyncrows flattened), and the panel applies it as is. The panel for tab A shows tab B's instances, calls andasyncrows with no page label, while recording targetshostPageId(). Publish per-page state (likepipePageFordoes for agents) and filter byhostPageId(), or add a page picker. At least tag eachasyncrow and instance with itspageId(packages/devtools/src/rpc/pipes-tools.ts:150-222,app/src/pages/pipes-inspector.ts:1193-1205). -
impure-pipe-in-foris noisy for built-ins: the purity map is seeded fromBUILTIN_PIPES, whereasync,json,keyvalueandsliceare impure, soitem.price$ | asyncinside@forraises a warning although it is idiomatic and cheap. Excludeasync(andjson, which has its own rule) or downgrade built-ins to info, and add a test for@for { {{ x$ | async }} }(packages/devtools/src/rpc/pipe-lint.ts:62-63,125-134). - No transform timing: the wrapper calls
original.applywithout measuring it, yet theexplain-pipetool description says it answers "why is this pipe slow?". Time each call withperformance.now(), keeptotalMs,maxMsandlastMsper instance, and report them inPipeCallInfo, the panel andexplain-pipe(packages/devtools/src/pipes-runtime.ts:257-271,packages/devtools/src/devframe.ts:2664). - No usage sites for custom pipes:
recordUsagesskips any name not inBUILTIN_PIPES, and the panel shows "Used in templates" only whenp.builtin. Your own pipes never get usage sites or the+N morelink. Record usages for every name and attachusageCountandusagesto customPipeInfoentries (packages/devtools/src/rpc/get-pipes.ts:268-280,app/src/pages/pipes-inspector.ts:304).
Low
- Thrown transforms are not recorded:
original.applyruns before thetry, so whentransformthrows,onCallnever runs and a failing pipe looks like "no calls". Wrap it intry/catch, record an error count and the last (redacted) message, and rethrow (packages/devtools/src/pipes-runtime.ts:258). - Full source scan on every request: each
explain-pipecall runsscanPipesandlintPipes, which both walk and read the whole tree and re-runtemplatesIn; the panel refresh runsget-pipesandpipe-linttoo. Nothing is cached. Share one file and template index keyed on mtime and invalidate it from the Vite watcher (packages/devtools/src/rpc/pipe-explain.ts:37-39,packages/devtools/src/rpc/get-pipes.ts:97-130). - Own-property and subclass transforms: only
proto.transformis patched, so a class-field arrowtransformis never recorded. A subclass whose inheritedtransformis the parent's wrapper is skipped as already wrapped, and its calls are reported under the parent'sslot.name. Patch the own property when there is one, and resolve the name fromthis(aWeakMapfrom instance to name filled byaddPipe) instead of the closure (packages/devtools/src/pipes-runtime.ts:251-262). - Stale check silently inactive:
staleCheckForregex-scanstView.template.toString()and returnsnullin minified or mangled builds; the collector caches thatnullper instance and never says the check is off. ReportstaleCheck: 'unavailable'so the panel andexplain-pipecan say so (packages/devtools/src/pipes-runtime.ts:335-361,packages/devtools/src/pipes-collector.ts:247-252). - False duplicate
asyncsubscriptions: duplicates are counted by source reference only, so aSubject,shareReplaystream orstore.selectused twice is flagged, andexplain-pipesays the work repeats. Mark obviously shared sources assharedand show them as info (packages/devtools/src/pipes-collector.ts:120-125,packages/devtools/src/rpc/pipe-explain.ts:60). - Silent truncation:
asyncrows stop at 200, component targets at 10 and per-instance calls at 5, and the merged instances are sliced again, with no flag in the payload. AddasyncTotalandtruncatedcounts toPipePageReportand show "showing 200 of N" (packages/devtools/src/pipes-collector.ts:43-44,133,195,packages/devtools/src/rpc/pipes-tools.ts:198). - Per-instance rows lack the component: the payload carries
component(andlastCaller) for each instance, but the panel renders only args, result and count, and itsPipeInstanceCalltype omitslastCaller. Show the component with a highlight target and the caller (app/src/pages/pipes-inspector.ts:46-51,370-374). -
callandstaleare not validated:isPipeUsageInfoignorescallandstale, so a page can push any size oflastArgs,instancesorlastCallerinto shared state and agent markdown;capJsoncaps only at render time. ValidatePipeCallInfoand clamp arrays and string lengths inisPipePageReportormergePipePageReport(packages/devtools/src/rpc/pipes-tools.ts:106-117). - First calls of new pipe classes are missed:
addPiperuns only insidepushPipes, so a pipe class that first appears while recording is not patched until the next push and its first calls are lost, while the docs say each recording starts from zero. Patch right after discovery in the mutation path, or document the delay (packages/devtools/src/pipes-collector.ts:493-495,apps/docs/src/content/inspectors/pipes.md:84).
Improvements
- "Hot pipes" sort and view: calls per second and total ms per pipe, ranked, once timing exists (
pipes-collector.ts,app/src/pages/pipes-inspector.ts). - Per-template pipe list in the Components inspector: selecting a component lists the pipes its template uses with live values, linked to the Pipes tab (
component-tree.ts,app/src/pages/pipes-inspector.ts). - Migration suggestions as agent actions with a patch preview: a pure pipe reading signals becomes
computed(),obs$ | asyncbecomestoSignal()or@let, an impure custom pipe gets a memoized pure version (rpc/pipe-lint.ts,rpc/pipes-agent.ts,config.ts). - Detect identical-result churn: an impure pipe whose result is deep-equal on more than 95% of recorded calls should probably be pure (
pipes-collector.ts,rpc/pipe-explain.ts). - Label library pipes by resolving the class to its
node_modulespackage, in the list and inexplain-pipe(pipes-collector.ts,rpc/pipe-explain.ts). - A
pipe-usagesagent resource that returns the usage sites of custom pipes, so agents can refactor every call site (rpc/pipes-agent.ts,rpc/get-pipes.ts,config.ts).
Already tracked elsewhere
- Pipes seen only at runtime (translate,
ngrxPush, Material) missing from the panel list: phase 1 of 4: safe by default and wired into your workflow #340. - Open in editor for declaration and usage sites: phase 1 of 4: safe by default and wired into your workflow #340.
- Pipe calls on a shared timeline lane: phase 3 of 4: a shell you live in #342.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething is broken or shows wrong dataSomething is broken or shows wrong datafeatureA feature request or a pull request that adds oneA feature request or a pull request that adds one
Activity
fix: audit findings for components, injectors, signals, pipes and HTTP (