Repository navigation
injectors: fix and improve the DI inspector from the October 2026 audit #318
Copy link
Copy link
Closed as duplicate
Closed as duplicate
Copy link
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
This comes from a code audit of the DI inspector (collector, overlays, devframe RPCs, agent tools and panel) done during a comparison with other devtools in October 2026. Roadmap items live in #310, #311, #312 and #313. Every problem below was checked against the code on main; none is marked (to confirm).
Problems
Medium
- Element entries never refresh:
readElementcaches providers, dependencies andprovidedByinelementEntriesfor the life of the element, so a dependency resolved later (lazyinjector.get, or recorded after the profiler caught up) never shows up. Store a cheap version key (provider record count and dependency count) with the entry and re-read when it changes. Update the test "reads element providers and dependencies once per element" (packages/devtools/src/injector-tree.ts:308,:326-327,:377;packages/devtools/src/__tests__/injector-tree.test.ts:146). - Environment injectors off the rendered path are missing:
noteEnvironmentis only fed fromentry.environments, so route injectors for routes not shown,createEnvironmentInjector()injectors and injectors kept alive after navigation never appear. Also discover them from the Router's loaded route configs (_injector/_loadedInjector) and label them "inactive" (packages/devtools/src/injector-tree.ts:390-415,:435).
Low
- Service dependencies skip InjectionTokens:
environmentDependenciesonly handles class tokens (typeof token !== 'function'), souseFactoryInjectionToken providers never list what they inject. Handle created non-multi token records with a factory too, keeping theisCreatedguard (packages/devtools/src/injector-tree.ts:291-292). - No validation on
push-injector-tree: the handler castsrootsandenvironmenttoInjectorTreeNode[]with no shape or size check, then fans them out to the panel and agents. Validate with a valibot schema, cap node count and string lengths, and drop malformed nodes (packages/devtools/src/devframe.ts:1492-1520). - Angular-native overlay keeps a failed push as sent:
pushInjectorTreesetslastInjectorJsonbeforepush-injector-treeand has no try/catch, so a failed push is only retried after the keepalive. Reset it on failure asoverlay.tsdoes, ideally through one shared helper for the three overlays (packages/devtools/src/overlay-angular-native.ts:280-282,packages/devtools/src/overlay.ts:319-323). - NativeScript overlay resends the whole tree on keepalive: it skips
pingKnows('ping-injector-tree')and pushes the full report instead (packages/devtools/src/overlay-nativescript.ts:237, comparepackages/devtools/src/overlay.ts:315). - Zone mode never shown: the collector reports
zoneand the server stores it, but the panel never displays it. Add a badge on the Root node or the toolbar (zoneless / zone.js / zone.js loaded but unused) (app/src/pages/di-inspector.ts,packages/devtools/src/devframe.ts:356). - Panel types drift from the package: the panel's own
DependencyInfohas noprovidedByNameandInjectorPagehas nozone. Import the shared types so type checking catches drift (app/src/pages/di-inspector.ts:21-79,packages/devtools/src/types.ts:224,:241). - Hardcoded caps: the 2000 element cap is hardcoded in the panel text, and "Provided by" chips stop at 12 with no "+N more". Send the cap in the report and add a "+N more" chip (
app/src/pages/di-inspector.ts:193,:1533). - Search results re-announced on every keystroke: the "Provided by" and no-match rows are
role="status". Drop the role from the chip row and announce a debounced summary ("N injectors provide X") through the existingannouncementsignal (app/src/pages/di-inspector.ts:201,:212). - Tokens matched by name only: two tokens with the same class name or InjectionToken description (two
Storeclasses, minified_a) are merged in token queries and panel search. Give tokens a stableWeakMapid and group or disambiguate by it (packages/devtools/src/injector-tree.ts:56-65,packages/devtools/src/rpc/injector-tools.ts:161-165). - Live view hides source data: once a live tree exists, the
get-providersfile:line data is no longer shown. Join live tokens tosourceProvidersby name and show a file:line link in the Provides and Injected here lists (app/src/pages/di-inspector.ts:123,:477-560). - Unknown
provide*()helpers are dropped: source mode maps helpers through the fixedPROVIDE_FN_TO_TOKENtable, soprovideStore,provideFirestoreand custom helpers are invisible. List unknownprovide*()calls in providers arrays andappConfigas "provider function" entries (packages/devtools/src/rpc/get-providers.ts:60-76,:173).
Improvements
- Draw the selected element's resolution path (element injectors up to environment injectors) as a vertical graph, highlighting where the queried token is found or where it would throw NullInjectorError (
app/src/pages/di-inspector.ts). - Show orphaned providers: declared in source (
get-providers) but never created at runtime, and services created in Root that nothing on the page injects (packages/devtools/src/rpc/get-providers.ts,packages/devtools/src/injector-tree.ts,app/src/pages/di-inspector.ts). - Show a warning count badge on the Injectors dock when an optional dependency resolves to "not provided anywhere" (
packages/devtools/src/devframe.ts,app/src/pages/di-inspector.ts).
Already tracked elsewhere
inspect-providerswith no arguments returns JSON cut atMAX_TOOL_CHARS: phase 1 of 4: safe by default, open in editor and cheap shell wins #310.- The collector walks and stringifies the whole tree every tick; event-driven collection and deltas: phase 3 of 4: a devframe shell as good as Vue and Nuxt DevTools #312.
- Ideas already on the roadmap: open in editor (phase 1 of 4: safe by default, open in editor and cheap shell wins #310), summary-first
inspect-providers(phase 1 of 4: safe by default, open in editor and cheap shell wins #310), Components and Injectors cross-links (phase 2 of 4: match the official Angular DevTools #311), event-driven deltas (phase 3 of 4: a devframe shell as good as Vue and Nuxt DevTools #312), provider value peek and anexplain-injectiontool (phase 4 of 4: lead where no Angular devtools goes today #313).
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