LiveEventEmitter
The single place every emit point in this package's component/render/ HTTP lifecycle goes through — one small set of static helpers instead of duplicating the "construct a slot, dispatch, read it back" dance at each of the call sites (`AutocompleteComponent`, `AbstractDashboardComponent` + `DataTableComponent`, `AbstractFieldComponent`, the four HTML/TUI renderers, `LiveEndpoint`). Every method here is nullable-safe: `$dispatcher === null` means every `with*()` helper below runs its `$compute` closure directly and no event is ever constructed or dispatched — byte-identical to this package's pre-event behavior. This is the load-bearing property the whole retrofit rests on (see `docs/superpowers/specs/2026-07-08-event-driven-familia-design.md` §milpa/live: "el dispatcher entra como dep opcional; sin él los * componentes corren igual"). This package has no boot site: the dispatcher enters per call, so the emitter declares its events lazily — the first time a given dispatcher that implements {@see DeclaredEvents} reaches any helper here, every name in {@see LiveEvents::declarations()} is declared to it, once per dispatcher instance (greenhouse decisions/0228). A dispatcher that does not implement the contract is asked nothing; `null` declares nothing.
LiveEventEmitter::declareTo()
public static function declareTo(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher): voidDeclares every event of this package to `$dispatcher`, once per instance — a silent no-op unless it implements {@see DeclaredEvents}. Every `with*()` helper calls this before dispatching, so a host never has to; a host that wants the declarations visible BEFORE the first dispatch (a catalogue read at boot) may call it eagerly with the same dispatcher, and the lazy path then finds nothing left to do.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface |
LiveEventEmitter::withMounting()
public static function withMounting(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher, string $componentName, array $props, Milpa\Live\ValueObjects\ComponentContext $context, Closure $compute): Milpa\Live\ValueObjects\StateSnapshotWraps a `mount()` call with `component.mounting` (PRE, no slot) / `component.mounted` (POST, no slot). Mount has no interception seam in this catalog — see {@see ComponentMountingEvent}.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface | |
| $componentName | string | |
| $props | array<string, mixed> | |
| $context | Milpa\Live\ValueObjects\ComponentContext | |
| $compute | \Closure(): StateSnapshot |
LiveEventEmitter::withHandling()
public static function withHandling(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher, Milpa\Live\ValueObjects\InteractionRequest $request, Closure $compute): Milpa\Live\ValueObjects\InteractionResultWraps a `handle()` call with `component.handling` (PRE, slot) / `component.handled` (POST, no slot). A `component.handling` listener may: - short-circuit (`InterceptionSlot::shortCircuit($interactionResult)`) to answer on the component's behalf — `$compute` never runs; - veto (`InterceptionSlot::stop()`) — `$compute` never runs; the veto is reported back to the caller via `InteractionResult::$errors` rather than silently doing nothing, per {@see \Milpa\Live\Contracts\Component\ComponentDefinitionInterface::handle()}'s own "SHOULD report via errors, not throw" contract; - do neither — `$compute` runs exactly as it would with no dispatcher wired.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface | |
| $request | Milpa\Live\ValueObjects\InteractionRequest | |
| $compute | \Closure(): InteractionResult |
Throws
\LogicException If a listener short-circuits with a value that is not an
{@see InteractionResult} — the documented contract for this slot.
LiveEventEmitter::withRendering()
public static function withRendering(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher, string $componentName, Milpa\Live\ValueObjects\RenderRequest $request, Closure $compute): Milpa\Live\ValueObjects\RenderResultWraps a `render()` call with `component.rendering` (PRE, slot) / `component.rendered` (POST, no slot). See {@see withHandling()} for the short-circuit/veto/passthrough shape — identical here, just for {@see RenderResult} instead of {@see InteractionResult}. A pure veto (`stop()` with no result) yields an empty {@see RenderResult} for this target rather than running the renderer.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface | |
| $componentName | string | |
| $request | Milpa\Live\ValueObjects\RenderRequest | |
| $compute | \Closure(): RenderResult |
Throws
\LogicException If a listener short-circuits with a value that is not a
{@see RenderResult} — the documented contract for this slot.
LiveEventEmitter::liveRequest()
public static function liveRequest(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher, Milpa\Live\ValueObjects\InteractionRequest $interaction, ?Milpa\Live\ValueObjects\SecurityPrincipal $principal): Milpa\Events\InterceptionSlotDispatches `live.request` (PRE, slot) — see {@see LiveRequestEvent} for the security-anchor placement contract. Returns the slot for {@see \Milpa\Live\Http\LiveEndpoint::handle()} to read back; unlike the other `with*()` helpers here, `LiveEndpoint` needs to branch on the outcome itself (a short-circuit skips `handle()` but still re-renders with the intercepted state; a veto skips both and returns an error response directly), so this one hands back the raw slot instead of taking a `$compute` closure.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface | |
| $interaction | Milpa\Live\ValueObjects\InteractionRequest | |
| $principal | ?Milpa\Live\ValueObjects\SecurityPrincipal |
LiveEventEmitter::liveResponded()
public static function liveResponded(?Milpa\Interfaces\Event\MilpaEventDispatcherInterface $dispatcher, Milpa\Live\ValueObjects\InteractionRequest $interaction, Milpa\Live\Http\LiveHttpResponse $response, bool $intercepted): voidDispatches `live.responded` (POST, no slot) — see {@see LiveRespondedEvent}.
Parameters
| Name | Type | Description |
|---|---|---|
| $dispatcher | ?Milpa\Interfaces\Event\MilpaEventDispatcherInterface | |
| $interaction | Milpa\Live\ValueObjects\InteractionRequest | |
| $response | Milpa\Live\Http\LiveHttpResponse | |
| $intercepted | bool |