|
| 1 | +--- |
| 2 | +name: devtools-inspector |
| 3 | +description: Add or fix how an inspector collects data, from the page-side overlay through the devframe server to the panel and the MCP tools. Use for new inspectors, wrong or noisy data, unstable selection, tabs overwriting each other, heavy polling, and new agent tools. |
| 4 | +--- |
| 5 | + |
| 6 | +# Inspector data pipeline |
| 7 | + |
| 8 | +Every inspector follows the same path: |
| 9 | + |
| 10 | +``` |
| 11 | +app page (overlay.ts + <area>-collector.ts) |
| 12 | + -> rpc.call('push-<area>', { pageId, ... }) |
| 13 | + -> devframe.ts: per-page Map, shared state '<area>', expiry, forget-<area>-page |
| 14 | + -> panel page (app/src/pages/<area>.ts) via rpc.sharedState('<area>') |
| 15 | + -> agent tools (ctx.agent.registerTool) and MCP resources |
| 16 | +``` |
| 17 | + |
| 18 | +Read `docs/contributing/coding-standards.md` ("Reading data from the page") before you start. |
| 19 | + |
| 20 | +## Page side (`packages/ng-devtools/src`) |
| 21 | + |
| 22 | +- Put collection logic in its own module (`<area>-collector.ts`) and keep `overlay.ts` changes to wiring: import, attach, push, `leave()` and the returned cleanup. |
| 23 | +- Read Angular through the debug APIs on `window.ng` and verify each field against `node_modules/@angular/core/fesm2022` (or the library's fesm build). Known helpers: |
| 24 | + - `element-id.ts`: stable `WeakMap` ids for elements (`elementId`, `elementById`). |
| 25 | + - `injector-tree.ts`: `className()` strips bundler `_` prefixes, `tokenName()`, `dependenciesOf()`, environment injector walk. |
| 26 | + - `serialize.ts`: safe, size-limited serialization. |
| 27 | + - `router.ts` `providerOf()`: find a service through the injector resolution path. |
| 28 | +- Never write attributes into the app's DOM. Never run app code (validators, guards) on a timer unless the user turned recording on. |
| 29 | +- Pushes: send on change, skip unchanged payloads (compare with the last JSON), re-send every few cycles so the server doesn't expire the page, and avoid full DOM scans on a timer (cache, rescan after a `MutationObserver` signal). |
| 30 | +- Every report carries `pageId` from `claimPageId()`. Call `forget-<area>-page` from `leave()`. |
| 31 | + |
| 32 | +## Server side (`devframe.ts`, `rpc/`) |
| 33 | + |
| 34 | +- Keep a `Map<pageId, report>` with `reportedAt`, drop pages older than 15 seconds in the shared expiry interval, and write the combined value into the shared state. |
| 35 | +- Page actions that the panel triggers (restore, highlight, run) go panel -> `request-<area>-action` -> broadcast `<area>-action` to the page -> `<area>-action-result`, keyed by `requestId`, with a timeout. |
| 36 | +- Source scans in `rpc/` enrich or stand in for live data. They must return a `kind` for anything the Dashboard counts. |
| 37 | + |
| 38 | +## Panel side |
| 39 | + |
| 40 | +- Subscribe with `const state = await rpc.sharedState('<area>'); apply(state.value()); state.on('updated', apply)` and remove the listener through `DestroyRef`. There is no `subscribe()` on shared state. |
| 41 | +- Filter to the current page with the `?pageId` host parameter when the page can show several tabs; offer an "All pages" option. |
| 42 | +- Then follow the `devtools-ui` skill for the page itself. |
| 43 | + |
| 44 | +## Agent tools |
| 45 | + |
| 46 | +- Describe what the tool returns, where the data comes from and what an empty answer means. Answer in markdown. |
| 47 | +- Update descriptions in `devframe.ts` when data shapes change, and the README tool list. |
| 48 | + |
| 49 | +## Tests |
| 50 | + |
| 51 | +- Collector tests run in jsdom with a fake `ng` (see `__tests__/injector-tree.test.ts`, `component-tree.test.ts`, `ngrx-collector.test.ts`). Cover stable ids, dedupe, expiry and the Angular shapes you rely on. |
| 52 | +- Server tests call the RPC handlers directly (see `http-server.test.ts`, `agent-tools.test.ts`). |
| 53 | +- `pnpm test:devtools` must stay green. |
0 commit comments