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/.vscode/settings.json b/.vscode/settings.json index e4d8b96d..c918cba7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "cSpell.words": [ "Astro", "Astro's", + "astrojs", "contextlines", "Endcaps", "fontsource", diff --git a/packages/overlay/src/App.tsx b/packages/overlay/src/App.tsx index 9334002c..ccf05799 100644 --- a/packages/overlay/src/App.tsx +++ b/packages/overlay/src/App.tsx @@ -43,9 +43,9 @@ 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 => { + const listener = (event: { data: string | Buffer }): void => { log(`Received new ${contentType} event`); for (const integration of integrations) { const newIntegrationData = integration.processEvent @@ -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,18 @@ export default function App({ navigate(e.detail); }; - return { clearEvents, onOpen, onClose, onNavigate, onToggle }; - }, [integrations, navigate, sidecarUrl]); + const onEvent = ({ detail }: CustomEvent<{ contentType: string; data: string }>) => { + const { contentType, data } = detail; + const listener = contentTypeListeners[contentType]; + if (!listener) { + log('Got event for unknown content type:', contentType); + return; + } + listener({ data }); + }; + + return { clearEvents, onEvent, onOpen, onClose, onNavigate, onToggle }; + }, [integrations, navigate, sidecarUrl, contentTypeListeners]); useKeyPress(['ctrlKey', 'F12'], eventHandlers.onToggle); @@ -150,6 +160,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 +168,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..e39abfa9 100644 --- a/packages/overlay/src/index.tsx +++ b/packages/overlay/src/index.tsx @@ -57,6 +57,13 @@ export async function onClose(cb: EventListener) { on('closed', cb); } +/** + * Send an event to spotlight without the sidecar + */ +export async function sendEvent(contentType: string, data: string) { + trigger('event', { contentType, data }); +} + /** * Invokes the passed in callback when the Spotlight debugger Window is opened */ @@ -70,9 +77,7 @@ export async function onOpen(cb: EventListener) { * 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); - }); + 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); } }; }