# DevTools

Solace exposes a narrow public DevTools integration surface through `@italone/solace/devtools`. This document records the public
lifecycle, private runtime boundary, and safe constraints for future instrumentation.

## Goals

- Help developers inspect component, reactivity, scheduler, renderer, and store behavior.
- Keep instrumentation opt-in.
- Avoid stabilizing internal runtime objects as public API.
- Avoid adding measurable overhead to production builds or benchmarks.

## Non-Goals

- No network transport, storage persistence, automatic telemetry, hidden runtime inspection, or
  production distribution workflow in the current phase.
- No SSR/SSG/hydration visualization until those runtime boundaries and event payloads are designed
  separately.

## Public API

DevTools integrations should import from the `@italone/solace/devtools` subpath:

```ts
import { createDevtoolsRecorder, onDevtoolsEvent } from "@italone/solace/devtools";
import type { DevtoolsEvent } from "@italone/solace/devtools";
```

The public subpath exports listener and recorder APIs only. It does not export emit helpers, listener-state helpers,
global cleanup helpers, serializers, DOM nodes, VNode trees, component instances, props, reactive targets, store state,
action arguments, or action results.

## Public API Lifecycle

`@italone/solace/devtools` is the only supported public DevTools entry point. New runtime exports require package boundary tests,
packed consumer smoke coverage, documentation, and a project log entry before they are treated as supported API.

Event payload additions must remain small serializable summaries and must update payload stability coverage. They should
not include raw props, state, DOM nodes, VNodes, reactive targets, action arguments, action results, stack traces, or
user content.

Renames or removals require an intentional breaking-change plan. Internal helpers remain private even when public APIs
reuse them internally, and incidental runtime cleanup must not change the public subpath shape.

## Candidate Capabilities

| Area       | Useful Signals                                         | Notes                                                                        |
| ---------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| Components | mount, update, unmount, props, emits, lifecycle hooks  | Component lifecycle and emit summaries are emitted by the internal event bus |
| Reactivity | effect creation, dependency tracking, triggers, stops  | Trigger summaries are emitted without raw targets, keys, or values           |
| Scheduler  | queued jobs, flush duration, skipped stale jobs        | `scheduler:flush` summary is emitted by the internal event bus               |
| Renderer   | element mount, prop patch, child diff, unmount         | Element summaries are emitted without DOM nodes or VNode trees               |
| Store      | action calls, narrow state paths, getter recomputation | Action summaries are emitted without args, results, or state                 |

## Hook Boundary

Solace has an internal event bus in `src/devtools/events.ts`. Runtime modules emit through that internal bus, while
public integrations subscribe through `@italone/solace/devtools`. The package root intentionally does not export DevTools APIs.

```ts
type DevtoolsEvent =
  | { type: "component:mount"; id: number; name: string }
  | { type: "component:update"; id: number; name: string }
  | { type: "component:unmount"; id: number; name: string }
  | { type: "component:emit"; id: number; name: string; event: string; handlerCount: number }
  | { type: "scheduler:flush"; queuedJobs: number; dedupedJobs: number; durationMs: number }
  | {
      type: "reactivity:trigger";
      targetType: string;
      keyType: string;
      effectCount: number;
      scheduledEffects: number;
      runEffects: number;
    }
  | {
      type: "renderer:element";
      operation: "mount" | "update" | "unmount";
      tag: string;
    }
  | {
      type: "store:action";
      name: string;
      status: "success" | "error";
      durationMs: number;
    };
```

`component:emit` summaries include the component id, component name, emitted event name, and callable handler count only.
They do not include emitted arguments, raw props, handler functions, component instances, VNodes, DOM nodes, or user
content.

`scheduler:flush` summaries include executed job count, deduped queue attempt count, and flush duration only. They do
not include scheduler job functions, function names, stack traces, component instances, reactive effects, VNodes, DOM
nodes, or user data.

Future runtime modules should emit small serializable events only when a listener is registered. If no listener is registered, the runtime should do no meaningful extra work.

`serializeDevtoolsEvent(event)` is available only from the internal event bus module. It returns an explicit plain-object
copy for the current event union and is used by integration tests to lock the payload boundary. It is not exported from
the package root.

`createDevtoolsRecorder()` is public through `@italone/solace/devtools`. It installs a listener, stores serialized events in
memory, exposes `snapshot()` for a copy of collected events, exposes `clear()` to reset the current capture window, and
exposes `stop()` to remove the listener. Pass `{ limit }` to keep only the latest N events in memory. It does not
persist data, send data over the network, write to storage, or install third-party scripts.

Production package builds do not publish JavaScript sourcemaps. This keeps internal DevTools wiring visible in source
control but out of package artifacts, so consumers do not accidentally couple to private helper names or module layout.

## Browser Extension Panel

The repository now includes a first browser DevTools extension example under
`examples/devtools-extension`. It opens a Solace panel, captures DevTools events for the inspected
tab through the public `@italone/solace/devtools` listener, and renders a local timeline view.
The runtime installs a non-exported page-local DevTools hook for browser extensions so the injected
bridge can subscribe to the inspected page event bus as a classic script without importing private
modules or bundling a second event bus.

The initial panel scope is intentionally narrow:

- Timeline rows for component, scheduler, reactivity, renderer, and store event families.
- Family filters, pause/resume, clear, selected-event details, and a bounded capture limit.
- A detail pane that displays the serialized `DevtoolsEvent` payload exactly as received.
- Extension wiring through a DevTools page, content script, page bridge, background relay, and panel
  transport.
- Tab-scoped activation: content scripts open a runtime port, but the page bridge is injected only
  after a Solace panel connects for that browser tab.

The extension does not change runtime payloads. It does not inspect component instances, DOM nodes,
VNodes, props, store state, reactive targets, user content, stack traces, action arguments, or action
results. It does not persist captured events, send them over the network, install analytics, or model
SSR/SSG/hydration state.

Run the example locally with:

```bash
pnpm dev:devtools-extension
```

Validate the extension build and browser smoke with:

```bash
pnpm build:devtools-extension
pnpm test:e2e:devtools-extension
```

## Privacy And Safety

- Do not emit full props, state, DOM nodes, or reactive targets by default.
- Redact or summarize values before exposing them to tooling.
- Keep hooks disabled unless a dev-only listener is installed.
- Do not send data over the network.

## Performance Constraints

- Production builds should not pay for DevTools instrumentation.
- Benchmark commands should run with DevTools disabled.
- Hook payload construction should be lazy or guarded by a listener check.
- Component tree and dependency graph snapshots should be explicit actions, not automatic on every update.

## Phased Roadmap

1. **Event model design**: completed for initial component and scheduler summary events.
2. **Development-only event bus**: internal event bus exists in `src/devtools/events.ts`.
3. **Scheduler flush and dedupe summary**: `scheduler:flush` reports executed jobs, deduped queue attempts, and duration.
4. **Component lifecycle summaries**: component mount/update/unmount summaries emit id and name only.
5. **Component emit summaries**: `component:emit` is emitted with event name and callable handler count only.
6. **Store action summaries**: `store:action` is emitted after action success or error without raw values.
7. **Reactivity trigger summaries**: `reactivity:trigger` is emitted without raw targets, keys, or values.
8. **Renderer element summaries**: `renderer:element` is emitted for element mount/update/unmount without DOM nodes or VNode trees.
9. **Payload stability smoke**: integrated runtime events serialize to JSON-safe payloads with allowed fields only.
10. **Internal recorder boundary**: `createDevtoolsRecorder()` captures serialized event snapshots for examples and experiments.
11. **Example-oriented recorder smoke**: a todo-style interaction validates recorder capture after clearing initial mount noise.
12. **Bounded recorder captures**: `createDevtoolsRecorder({ limit })` keeps recorder memory bounded for examples and experiments.
13. **Large-list recorder smoke**: a 10,000-row keyed update validates public recorder snapshots remain serialized summaries without DOM, VNode, raw state, or row data.
14. **Public package boundary guard**: package exports tests verify DevTools internals are not available from the package root.
15. **Public DevTools subpath**: `@italone/solace/devtools` exposes listener and recorder APIs without internal emit helpers.
16. **Production artifact boundary**: package builds do not publish JavaScript sourcemaps that expose internal wiring.
17. **Browser extension timeline panel**: `examples/devtools-extension` builds a local DevTools
    panel that consumes only the public DevTools subpath and renders the existing serialized event
    summaries.

## Recommendation

Use the browser extension panel as an example-grade inspector for the current public DevTools event
contract. Keep future UI expansion tied to explicit runtime event designs: component trees,
dependency graphs, flame charts, persisted captures, telemetry, and SSR/SSG/hydration panels should
not be added by inferring private runtime state.
