// Slot / SlotRegistry — named extension points the host renders and addons // contribute to at register() time. Keyed by a slot id (e.g. "dashboard.widgets", // "invoice.footer"). Each contribution is an arbitrary React element factory. import React, { useSyncExternalStore } from 'react' export type SlotComponent

= React.ComponentType

interface SlotEntry { id: string component: SlotComponent priority: number source?: string } type Listener = () => void class SlotRegistryImpl { private slots = new Map() private listeners = new Set() register(slotId: string, component: SlotComponent, opts?: { priority?: number; source?: string }): () => void { const entry: SlotEntry = { id: slotId, component, priority: opts?.priority ?? 0, source: opts?.source } const list = this.slots.get(slotId) ?? [] list.push(entry) // Higher priority renders first — canonical across SDK and runtime-react. // See docs/slot-priority.md. list.sort((a, b) => b.priority - a.priority) this.slots.set(slotId, list) this.emit() return () => { const arr = this.slots.get(slotId) if (!arr) return const idx = arr.indexOf(entry) if (idx >= 0) { arr.splice(idx, 1) this.emit() } } } get(slotId: string): SlotEntry[] { return this.slots.get(slotId) ?? [] } subscribe(listener: Listener): () => void { this.listeners.add(listener) return () => { this.listeners.delete(listener) } } private emit() { this.listeners.forEach(l => l()) } } export const slotRegistry = new SlotRegistryImpl() export interface SlotProps { /** Slot id. */ name: string /** Props forwarded to each contribution component. */ props?: Record /** Fallback element shown when no contribution is registered. */ fallback?: React.ReactNode } export function Slot({ name, props, fallback = null }: SlotProps) { const entries = useSyncExternalStore( (cb) => slotRegistry.subscribe(cb), () => slotRegistry.get(name), () => slotRegistry.get(name), ) if (entries.length === 0) return <>{fallback} return ( <> {entries.map((entry, i) => { const C = entry.component return })} ) }