diff --git a/.changeset/ten-comics-guess.md b/.changeset/ten-comics-guess.md new file mode 100644 index 00000000..e7937dd9 --- /dev/null +++ b/.changeset/ten-comics-guess.md @@ -0,0 +1,8 @@ +--- +'@spotlightjs/overlay': minor +'@spotlightjs/spotlight': minor +--- + +Add `__spotlight.initOptions` and initialEvents support allowing providing a list of "initial events" when Spotlight +loads, not requiring the sidecar to be working. Mostly going to be used when replacing default error pages in +frameworks. diff --git a/packages/overlay/src/App.tsx b/packages/overlay/src/App.tsx index a1266cdb..68ef87eb 100644 --- a/packages/overlay/src/App.tsx +++ b/packages/overlay/src/App.tsx @@ -13,6 +13,8 @@ import type { NotificationCount, SpotlightOverlayOptions } from './types'; type AppProps = Omit & Required>; +type EventData = { contentType: string; data: string | Uint8Array }; + export default function App({ openOnInit = false, showTriggerButton = true, @@ -21,6 +23,7 @@ export default function App({ anchor, fullPage = false, showClearEventsButton = true, + initialEvents = {}, }: AppProps) { const [integrationData, setIntegrationData] = useState>({}); const [isOnline, setOnline] = useState(false); @@ -141,7 +144,6 @@ export default function App({ navigate(e.detail); }; - type EventData = { contentType: string; data: string }; const dispatchToContentTypeListener = ({ contentType, data }: EventData) => { const listener = contentTypeListeners[contentType]; if (!listener) { @@ -163,8 +165,16 @@ export default function App({ } }); + // Populate from initial events + for (const contentType in initialEvents) { + log(`Injecting initial events for ${contentType}`); + for (const data of initialEvents[contentType]) { + dispatchToContentTypeListener({ contentType, data }); + } + } + return { clearEvents, onEvent, onOpen, onClose, onNavigate, onToggle }; - }, [integrations, navigate, sidecarUrl, contentTypeListeners]); + }, [integrations, navigate, sidecarUrl, contentTypeListeners, initialEvents]); useKeyPress(['ctrlKey', 'F12'], eventHandlers.onToggle); diff --git a/packages/overlay/src/index.tsx b/packages/overlay/src/index.tsx index bfd4ad48..61eafa49 100644 --- a/packages/overlay/src/index.tsx +++ b/packages/overlay/src/index.tsx @@ -60,7 +60,7 @@ export async function onClose(cb: EventListener) { /** * Send an event to spotlight without the sidecar */ -export async function sendEvent(contentType: string, data: string) { +export async function sendEvent(contentType: string, data: string | Uint8Array) { trigger('event', { contentType, data }); } @@ -88,18 +88,21 @@ function isSpotlightInjected() { return false; } -export async function init({ - openOnInit = false, - showTriggerButton = true, - injectImmediately = false, - sidecarUrl = DEFAULT_SIDECAR_URL, - anchor = DEFAULT_ANCHOR, - debug = false, - integrations, - experiments = DEFAULT_EXPERIMENTS, - fullPage = false, - showClearEventsButton = true, -}: SpotlightOverlayOptions = {}) { +export async function init( + { + openOnInit = false, + showTriggerButton = true, + injectImmediately = false, + sidecarUrl = DEFAULT_SIDECAR_URL, + anchor = DEFAULT_ANCHOR, + debug = false, + integrations, + experiments = DEFAULT_EXPERIMENTS, + fullPage = false, + showClearEventsButton = true, + initialEvents = undefined, + }: SpotlightOverlayOptions = (window as WindowWithSpotlight).__spotlight?.initOptions || {}, +) { // The undefined document guard is to avoid being initialized in a Worker // @see https://github.com/vitejs/vite/discussions/17644#discussioncomment-10026390 if (typeof document === 'undefined') return; @@ -173,6 +176,7 @@ export async function init({ anchor={anchor} fullPage={fullPage} showClearEventsButton={showClearEventsButton} + initialEvents={initialEvents} /> , diff --git a/packages/overlay/src/types.ts b/packages/overlay/src/types.ts index eff27182..519974ea 100644 --- a/packages/overlay/src/types.ts +++ b/packages/overlay/src/types.ts @@ -1,4 +1,4 @@ -import { type Integration } from './integrations/integration'; +import type { Integration } from './integrations/integration'; export type ExperimentName = 'sentry:focus-local-events'; @@ -86,7 +86,7 @@ export type SpotlightOverlayOptions = { /** * If set to `true`, the Spotlight overlay will be rendered in full page mode. * It can't be closed nor minimized. - * This is useful for replaceing error page or in when directly rendered as an html page + * This is useful for replacing error page or in when directly rendered as an html page */ fullPage?: boolean; @@ -97,6 +97,13 @@ export type SpotlightOverlayOptions = { * @default true */ showClearEventsButton?: boolean; + + /** + * Events to be sent into Spotlight (bypassing the sidecar) right after init + * + * This is useful when replacing error pages of frameworks etc. Implies "injectImmediately". + */ + initialEvents?: Record; }; export type NotificationCount = { @@ -115,5 +122,6 @@ export type NotificationCount = { export type WindowWithSpotlight = Window & { __spotlight?: { eventTarget?: EventTarget; + initOptions?: SpotlightOverlayOptions; }; };