diff --git a/.changeset/nasty-lizards-sleep.md b/.changeset/nasty-lizards-sleep.md new file mode 100644 index 00000000..39c30daf --- /dev/null +++ b/.changeset/nasty-lizards-sleep.md @@ -0,0 +1,6 @@ +--- +'@spotlightjs/overlay': minor +--- + +Add direct event ingestion through `Spotlight.sendEvent('', )` to allow sending events without the +sidecar diff --git a/packages/overlay/src/App.tsx b/packages/overlay/src/App.tsx index 9334002c..f243a521 100644 --- a/packages/overlay/src/App.tsx +++ b/packages/overlay/src/App.tsx @@ -43,7 +43,7 @@ export default function App({ } } - const result: [contentType: string, listener: (event: MessageEvent) => void][] = []; + const result: Record void> = Object.create(null); for (const [contentType, integrations] of contentTypeToIntegrations.entries()) { const listener = (event: MessageEvent): void => { log(`Received new ${contentType} event`); @@ -67,10 +67,10 @@ export default function App({ } }; - log('Adding listener for', contentType, 'sum', result.length); + log('Adding listener for', contentType); // `contentType` could for example be "application/x-sentry-envelope" - result.push([contentType, listener]); + result[contentType] = listener; } return result; }, [integrations]); @@ -139,8 +139,11 @@ export default function App({ navigate(e.detail); }; - return { clearEvents, onOpen, onClose, onNavigate, onToggle }; - }, [integrations, navigate, sidecarUrl]); + const onEvent = ({ detail }: CustomEvent<{ contentType: string; event: MessageEvent }>) => + contentTypeListeners[detail.contentType]?.(detail.event); + + return { clearEvents, onEvent, onOpen, onClose, onNavigate, onToggle }; + }, [integrations, navigate, sidecarUrl, contentTypeListeners]); useKeyPress(['ctrlKey', 'F12'], eventHandlers.onToggle); @@ -150,6 +153,7 @@ export default function App({ spotlightEventTarget.addEventListener('close', eventHandlers.onClose); spotlightEventTarget.addEventListener('navigate', eventHandlers.onNavigate as EventListener); spotlightEventTarget.addEventListener('clearEvents', eventHandlers.clearEvents as EventListener); + spotlightEventTarget.addEventListener('event', eventHandlers.onEvent as EventListener); return (): undefined => { log('useEffect[destructor]: Removing event listeners'); @@ -157,6 +161,7 @@ export default function App({ spotlightEventTarget.removeEventListener('close', eventHandlers.onClose); spotlightEventTarget.removeEventListener('navigate', eventHandlers.onNavigate as EventListener); spotlightEventTarget.removeEventListener('clearEvents', eventHandlers.clearEvents as EventListener); + spotlightEventTarget.removeEventListener('event', eventHandlers.onEvent as EventListener); }; }, [spotlightEventTarget, eventHandlers]); diff --git a/packages/overlay/src/index.tsx b/packages/overlay/src/index.tsx index 5ad35a86..7a02d2b8 100644 --- a/packages/overlay/src/index.tsx +++ b/packages/overlay/src/index.tsx @@ -39,28 +39,35 @@ function createStyleSheet(styles: string) { /** * Open the Spotlight debugger Window */ -export async function openSpotlight(path?: string | undefined) { +export function openSpotlight(path?: string | undefined) { trigger('open', { path }); } /** * Close the Spotlight debugger Window */ -export async function closeSpotlight() { +export function closeSpotlight() { trigger('close'); } /** * Invokes the passed in callback when the Spotlight debugger Window is closed */ -export async function onClose(cb: EventListener) { +export function onClose(cb: EventListener) { on('closed', cb); } +/** + * Send an event to spotlight without the sidecar + */ +export function sendEvent(contentType: string, event: MessageEvent) { + trigger('event', { contentType, event }); +} + /** * Invokes the passed in callback when the Spotlight debugger Window is opened */ -export async function onOpen(cb: EventListener) { +export function onOpen(cb: EventListener) { on('opened', cb); } @@ -69,10 +76,8 @@ export async function onOpen(cb: EventListener) { * by a Spotlight integration. * A count of the number of collected severe events is passed to the callback. */ -export async function onSevereEvent(cb: (count: number) => void) { - on('severeEventCount', e => { - cb((e as CustomEvent).detail?.count ?? 1); - }); +export function onSevereEvent(cb: (count: number) => void) { + on('severeEventCount', e => cb((e as CustomEvent).detail?.count ?? 1)); } function isSpotlightInjected() { diff --git a/packages/overlay/src/sidecar.ts b/packages/overlay/src/sidecar.ts index 6e9c2d61..30ebef9b 100644 --- a/packages/overlay/src/sidecar.ts +++ b/packages/overlay/src/sidecar.ts @@ -3,13 +3,14 @@ import { log } from './lib/logger'; export function connectToSidecar( sidecarUrl: string, - contentTypeListeners: [contentType: string, listener: (event: MessageEvent) => void][], + // Content Type to listener + contentTypeListeners: Record void>, setOnline: React.Dispatch>, ): () => void { log('Connecting to sidecar at', sidecarUrl); const source = new EventSource(sidecarUrl); - for (const [contentType, listener] of contentTypeListeners) { + for (const [contentType, listener] of Object.entries(contentTypeListeners)) { source.addEventListener(contentType, listener); } @@ -24,10 +25,10 @@ export function connectToSidecar( }); return () => { - log(`Removing ${contentTypeListeners.length} listeners`); - for (const typeAndListener of contentTypeListeners) { - source.removeEventListener(typeAndListener[0], typeAndListener[1]); - log('Removed listener for type', typeAndListener[0]); + log('Removing all content type listeners'); + for (const [contentType, listener] of Object.entries(contentTypeListeners)) { + source.removeEventListener(contentType, listener); + log('Removed listener for type', contentType); } }; }