Skip to content

Optional React framework addon (--framework off|auto) - #168

Merged
jantimon merged 1 commit into
mainfrom
feat/react-addon
Jul 29, 2026
Merged

Optional React framework addon (--framework off|auto)#168
jantimon merged 1 commit into
mainfrom
feat/react-addon

Conversation

@jantimon

@jantimon jantimon commented Jul 29, 2026

Copy link
Copy Markdown
Owner

What

Adds framework support as an optional addon, never core. All React code lives under src/addons/react/ and src/addons/react-dev/ behind one narrow registry interface (model/addon.ts Addon), wired in src/addons/registry.ts. The core (record/query/driver/node) calls addons only through that seam (activeAddons/addonPageInits/runEnrich); it imports no addon internals. An addon reads what the capture already recorded and attaches facts to a span's addons slot; it never changes what is captured.

--framework off|auto (default auto). off resolves to no addons, so no addon code runs and the recording is byte-identical to a pre-addon one. Every lane accepts the flag; an addon no-ops where its signals are absent.

The two addons (probe-verified scope, docs/dev/react-attribution.md)

react (build-independent):

  • Detection metadata (present/version/renderer/build) via a pre-load __REACT_DEVTOOLS_GLOBAL_HOOK__ mini-hook installed before app code (evaluateOnNewDocument, browser lanes). build is bundleType (DEV=1/PROD=0), the cheapest dev/prod signal.
  • Exact per-step commit count via onCommitFiberRoot (build-independent; run span cumulative, each driver step its own window).
  • Node-lane server-phase rollup: react-dom self-time onto a minimal allowlist of stable anchor names. React 19 production resolves them unmangled; React 18 production is mangled, so the fact is honestly absent (with a note).

react-dev (dev-build-gated): on chrome --deep, classifies the persisted TimeStamp React Performance-Track events (already stored today) into per-track facts. Gated on the react addon having detected a development build AND entries present; a production browser build emits none.

Honesty

No framework detected -> no addon vocabulary on the recording (the hook seeds detected:false, and enrichment gates on real detection). Node has no detection hook; phases absent when no anchor resolves; the node-phase rollup is gated on the node lane so a chrome run can never mislabel a client bundle as "server phases". Never a fabricated zero.

Surfaces

query span <label> renders a labeled React (addon) block; --format json anatomy gains an additive addons object. New public types: ReactFacts, ReactDevFacts, SpanAddons, FrameworkMode. README gains a "Framework addons (React)" section; docs/dev/react-attribution.md gains a "what wpd stores" subsection.

Tests

  • Unit (test/unit/react-addon.test.mjs, 13): registry contract (off feeds no addon to runEnrich), the TimeStamp classifier, the anchor rollup, detection/commit fact-shaping, the react-dev dev-build gate, and the honesty fix (a non-React run attaches nothing, even with react-dom-named frames present).
  • Measurement (test/measurement/react-addon.test.mjs, 3, browser-free node lane): the node server-phase rollup end-to-end through the built CLI on a self-contained fixture (a local package named react-dom with anchor-named functions -- no install), the --framework off guarantee (no addons slot), and honest absence on a non-React workload.

e2e fixture is option (b): the browser lane's detection/commit/track path cannot get a real React bundle without a network install (forbidden in tests) and Chrome was not available locally, so the node lane covers the addon end-to-end and the browser-lane classifier/rollup/shaping is unit-covered.

Gates

build, lint, format:check, knip, unit (680), measurement (6) all green. Chrome e2e is unchanged and runs in CI (Chrome was not downloadable in the authoring environment).

Framework support is an optional addon, never core: all React code lives
under src/addons/react + src/addons/react-dev behind one interface
(model/addon.ts Addon), wired in src/addons/registry.ts. The core calls
addons only through that seam (activeAddons/addonPageInits/runEnrich); it
imports no addon internals. --framework off|auto (default auto): off runs
zero addon code, leaving the recording byte-identical. An addon reads what
the capture recorded and attaches facts to Span.addons; it never changes
what is captured.

react addon (build-independent): detection metadata + per-step commit
counts from a pre-load __REACT_DEVTOOLS_GLOBAL_HOOK__ mini-hook (browser
lanes), and react-dom server-phase self-time rolled onto a minimal anchor
allowlist (node lane; React 19 prod resolves, React 18 prod is mangled so
the fact is absent). react-dev addon (dev-gated): classifies the persisted
TimeStamp React Performance-Track events on chrome --deep.

Every absence is honest: no framework detected -> no addon vocabulary;
node has no detection hook; phases absent when no anchor resolves; tracks
absent unless a development build was detected AND entries are present.

query span renders a labeled React (addon) block; --format json adds an
additive addons object. New public types: ReactFacts, ReactDevFacts,
SpanAddons, FrameworkMode.
@jantimon
jantimon merged commit 21b14bd into main Jul 29, 2026
4 checks passed
@jantimon
jantimon deleted the feat/react-addon branch July 29, 2026 19:21
@github-actions github-actions Bot mentioned this pull request Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant