From 776fef3fc6b7950c1761f5798f8421fd332c749c Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 Nov 2023 17:09:14 +0100 Subject: [PATCH 1/3] wip: This still needs the client injection fix --- packages/astro/src/index.ts | 19 +++++++++++++++++-- packages/astro/src/snippets.ts | 11 +++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/index.ts b/packages/astro/src/index.ts index 2016744ba..f0728e80f 100644 --- a/packages/astro/src/index.ts +++ b/packages/astro/src/index.ts @@ -1,5 +1,5 @@ import type { AstroIntegration } from 'astro'; -import { SPOTLIGHT_SERVER_SNIPPET, buildClientInitSnippet, type ClientInitOptions } from './snippets'; +import { buildClientInitSnippet, buildServerSnippet, type ClientInitOptions } from './snippets'; import path from 'path'; import url from 'url'; @@ -11,7 +11,22 @@ const PKG_NAME = '@spotlightjs/astro'; export type SpotlightOptions = | { __debugOptions?: ClientInitOptions; + + /** + * If enabled, Spotlight will log additional debug output to the console. + */ debug: boolean; + + /** + * Set this URL if you're running the Spotlight sidecar on a custom URL. + * Setting this URL will cause server- and client-side events to be forwarded to the sidcar running on the passed URL. + * + * IMPORTANT: This option assumes that you manually started the sidecar outside of Astro. Therefore, if it is set, + * the spotlight Astro integration will not start its own sidecar. + * + * @default 'http://localhost:8969/stream' + */ + sidecarUrl?: string; } | undefined; @@ -51,7 +66,7 @@ const createPlugin = (options?: SpotlightOptions): AstroIntegration => { } injectScript('page', buildClientInitSnippet({ importPath: PKG_NAME, showTriggerButton, ...options })); - injectScript('page-ssr', SPOTLIGHT_SERVER_SNIPPET); + injectScript('page-ssr', buildServerSnippet({ sidecarUrl: options?.sidecarUrl })); const importPath = path.dirname(url.fileURLToPath(import.meta.url)); const pluginPath = path.join(importPath, 'overlay/index.ts'); diff --git a/packages/astro/src/snippets.ts b/packages/astro/src/snippets.ts index f8fa1cbb2..7570cd8ea 100644 --- a/packages/astro/src/snippets.ts +++ b/packages/astro/src/snippets.ts @@ -58,8 +58,15 @@ if (enableOverlay) { } `; -export const SPOTLIGHT_SERVER_SNIPPET = ` +type ServerSnippetOptions = { + sidecarUrl?: string; +}; + +export const buildServerSnippet: (options: ServerSnippetOptions) => string = ({ sidecarUrl }) => ` import * as _SentrySDKForSpotlight from '@sentry/astro'; + _SentrySDKForSpotlight.getClient().setupIntegrations(true); -_SentrySDKForSpotlight.addIntegration(new _SentrySDKForSpotlight.Integrations.Spotlight()); +_SentrySDKForSpotlight.addIntegration(new _SentrySDKForSpotlight.Integrations.Spotlight({ + ${sidecarUrl ? `sidecarUrl: '${sidecarUrl}'` : ''} +})); `; From ca0a0b02e2154f5938fc744cf2e8f6e34d32afea Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 Nov 2023 17:18:11 +0100 Subject: [PATCH 2/3] add client-side sidecarUrl option --- packages/astro/src/index.ts | 32 ++++++++------------------------ packages/astro/src/snippets.ts | 5 ++++- packages/astro/src/types.ts | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 packages/astro/src/types.ts diff --git a/packages/astro/src/index.ts b/packages/astro/src/index.ts index f0728e80f..1f93a11e5 100644 --- a/packages/astro/src/index.ts +++ b/packages/astro/src/index.ts @@ -1,36 +1,15 @@ import type { AstroIntegration } from 'astro'; -import { buildClientInitSnippet, buildServerSnippet, type ClientInitOptions } from './snippets'; +import { buildClientInitSnippet, buildServerSnippet } from './snippets'; import path from 'path'; import url from 'url'; +import type { SpotlightAstroIntegrationOptions } from './types'; import { errorPageInjectionPlugin } from './vite/error-page'; const PKG_NAME = '@spotlightjs/astro'; -export type SpotlightOptions = - | { - __debugOptions?: ClientInitOptions; - - /** - * If enabled, Spotlight will log additional debug output to the console. - */ - debug: boolean; - - /** - * Set this URL if you're running the Spotlight sidecar on a custom URL. - * Setting this URL will cause server- and client-side events to be forwarded to the sidcar running on the passed URL. - * - * IMPORTANT: This option assumes that you manually started the sidecar outside of Astro. Therefore, if it is set, - * the spotlight Astro integration will not start its own sidecar. - * - * @default 'http://localhost:8969/stream' - */ - sidecarUrl?: string; - } - | undefined; - -const createPlugin = (options?: SpotlightOptions): AstroIntegration => { +const createPlugin = (options?: SpotlightAstroIntegrationOptions): AstroIntegration => { const thisFilePath = url.fileURLToPath(import.meta.url); return { @@ -77,6 +56,11 @@ const createPlugin = (options?: SpotlightOptions): AstroIntegration => { }, 'astro:server:start': async () => { + if (options?.sidecarUrl) { + // If users set a custom sidecar URL, we assume they started the sidecar manually outside of Astro. + // So we don't setup the default sidecar instance. + return; + } // Importing this dynamically because for some reason, the top level import // caused a dev server error because the sidecar code was bundled into the server const { setupSidecar } = await import('@spotlightjs/sidecar'); diff --git a/packages/astro/src/snippets.ts b/packages/astro/src/snippets.ts index 7570cd8ea..b3e6e117d 100644 --- a/packages/astro/src/snippets.ts +++ b/packages/astro/src/snippets.ts @@ -1,3 +1,5 @@ +import type { SpotlightAstroIntegrationOptions } from './types'; + type SupportedIntegrations = 'sentry' | 'console' | 'viteInspect'; export type ClientInitOptions = { @@ -5,7 +7,7 @@ export type ClientInitOptions = { showTriggerButton?: boolean; integrationNames?: SupportedIntegrations[]; injectImmediately?: boolean; -}; +} & SpotlightAstroIntegrationOptions; const DEFAULT_INTEGRATIONS = ['sentry']; @@ -22,6 +24,7 @@ Spotlight.init({ showTriggerButton: ${options.showTriggerButton === false ? 'false' : 'true'}, injectImmediately: ${options.injectImmediately === true ? 'true' : 'false'}, debug: ${options.debug === true ? 'true' : 'false'}, + ${options.sidecarUrl ? `sidecarUrl: '${options.sidecarUrl}'` : ''} }); `; }; diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts new file mode 100644 index 000000000..f43102b02 --- /dev/null +++ b/packages/astro/src/types.ts @@ -0,0 +1,25 @@ +export type SpotlightAstroIntegrationOptions = + | { + /** + * Set this URL if you're running the Spotlight sidecar on a custom URL. + * Setting this URL will cause server- and client-side events to be forwarded to the sidcar running on the passed URL. + * + * IMPORTANT: This option assumes that you manually started the sidecar outside of Astro. Therefore, if it is set, + * the spotlight Astro integration will not start its own sidecar. + * + * @default 'http://localhost:8969/stream' + */ + sidecarUrl?: string; + + /** + * If enabled, Spotlight will log additional debug output to the console. + */ + debug: boolean; + + /** + * Additional debug options. + * WARNING: These options are not part of the public API and may change at any time. + */ + __debugOptions?: Record; + } + | undefined; From 07a87ae7a8a2214bbfd5e4dd5d3fa0864a6bc930 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 Nov 2023 18:23:44 +0100 Subject: [PATCH 3/3] feat(core): Add support for custom sidecar URL on client side Sentry integration --- .changeset/hungry-dryers-hear.md | 6 +++++ .changeset/lucky-fireants-whisper.md | 5 +++++ packages/astro/src/index.ts | 4 ++-- packages/astro/src/snippets.ts | 18 ++++++--------- packages/astro/src/types.ts | 2 +- packages/astro/src/vite/error-page.ts | 7 +++--- packages/overlay/src/index.tsx | 8 +++++-- .../overlay/src/integrations/sentry/index.ts | 15 +++++++++---- .../integrations/sentry/sentry-integration.ts | 22 ++++++++++++++++--- 9 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 .changeset/hungry-dryers-hear.md create mode 100644 .changeset/lucky-fireants-whisper.md diff --git a/.changeset/hungry-dryers-hear.md b/.changeset/hungry-dryers-hear.md new file mode 100644 index 000000000..8e645afac --- /dev/null +++ b/.changeset/hungry-dryers-hear.md @@ -0,0 +1,6 @@ +--- +'@spotlightjs/overlay': patch +'@spotlightjs/astro': patch +--- + +feat(core): Add support for custom sidecar URL in client Sentry integration diff --git a/.changeset/lucky-fireants-whisper.md b/.changeset/lucky-fireants-whisper.md new file mode 100644 index 000000000..7ced6d170 --- /dev/null +++ b/.changeset/lucky-fireants-whisper.md @@ -0,0 +1,5 @@ +--- +'@spotlightjs/astro': patch +--- + +feat(astro): Add custom `sidecarUrl` option diff --git a/packages/astro/src/index.ts b/packages/astro/src/index.ts index 1f93a11e5..540ddaf24 100644 --- a/packages/astro/src/index.ts +++ b/packages/astro/src/index.ts @@ -45,13 +45,13 @@ const createPlugin = (options?: SpotlightAstroIntegrationOptions): AstroIntegrat } injectScript('page', buildClientInitSnippet({ importPath: PKG_NAME, showTriggerButton, ...options })); - injectScript('page-ssr', buildServerSnippet({ sidecarUrl: options?.sidecarUrl })); + injectScript('page-ssr', buildServerSnippet(options)); const importPath = path.dirname(url.fileURLToPath(import.meta.url)); const pluginPath = path.join(importPath, 'overlay/index.ts'); addDevOverlayPlugin(pluginPath); } else if (options?.__debugOptions) { - injectScript('page', buildClientInitSnippet(options.__debugOptions)); + injectScript('page', buildClientInitSnippet({ importPath: PKG_NAME, ...options })); } }, diff --git a/packages/astro/src/snippets.ts b/packages/astro/src/snippets.ts index b3e6e117d..fef21baeb 100644 --- a/packages/astro/src/snippets.ts +++ b/packages/astro/src/snippets.ts @@ -9,13 +9,13 @@ export type ClientInitOptions = { injectImmediately?: boolean; } & SpotlightAstroIntegrationOptions; -const DEFAULT_INTEGRATIONS = ['sentry']; - const buildClientImport = (importPath: string) => `import * as Spotlight from '${importPath}';`; const buildClientInit = (options: ClientInitOptions) => { - const integrations = options.integrationNames || DEFAULT_INTEGRATIONS; - const integrationCalls = integrations.map(i => `Spotlight.${i}()`).join(', '); + const integrationCalls = options.integrationNames + ? options.integrationNames.map(i => `Spotlight.${i}()`).join(', ') + : `Spotlight.sentry({sidecarUrl: ${options.sidecarUrl ? `'${options.sidecarUrl}'` : undefined}})`; + return ` Spotlight.init({ integrations: [ @@ -24,7 +24,7 @@ Spotlight.init({ showTriggerButton: ${options.showTriggerButton === false ? 'false' : 'true'}, injectImmediately: ${options.injectImmediately === true ? 'true' : 'false'}, debug: ${options.debug === true ? 'true' : 'false'}, - ${options.sidecarUrl ? `sidecarUrl: '${options.sidecarUrl}'` : ''} + ${options.sidecarUrl ? `sidecar: '${options.sidecarUrl}'` : ''} }); `; }; @@ -61,15 +61,11 @@ if (enableOverlay) { } `; -type ServerSnippetOptions = { - sidecarUrl?: string; -}; - -export const buildServerSnippet: (options: ServerSnippetOptions) => string = ({ sidecarUrl }) => ` +export const buildServerSnippet: (options: SpotlightAstroIntegrationOptions) => string = options => ` import * as _SentrySDKForSpotlight from '@sentry/astro'; _SentrySDKForSpotlight.getClient().setupIntegrations(true); _SentrySDKForSpotlight.addIntegration(new _SentrySDKForSpotlight.Integrations.Spotlight({ - ${sidecarUrl ? `sidecarUrl: '${sidecarUrl}'` : ''} + ${options?.sidecarUrl ? `sidecarUrl: '${options.sidecarUrl}'` : ''} })); `; diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index f43102b02..b718ff7ea 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -14,7 +14,7 @@ export type SpotlightAstroIntegrationOptions = /** * If enabled, Spotlight will log additional debug output to the console. */ - debug: boolean; + debug?: boolean; /** * Additional debug options. diff --git a/packages/astro/src/vite/error-page.ts b/packages/astro/src/vite/error-page.ts index 77abc57bc..6a4fc51cb 100644 --- a/packages/astro/src/vite/error-page.ts +++ b/packages/astro/src/vite/error-page.ts @@ -1,12 +1,13 @@ import { buildSpotlightErrorPageSnippet } from '../snippets'; import type { Plugin } from 'vite'; +import type { SpotlightAstroIntegrationOptions } from '../types'; type ErrorPagePluginOptions = { importPath: string; -}; +} & SpotlightAstroIntegrationOptions; -export const errorPageInjectionPlugin: (options: ErrorPagePluginOptions) => Plugin = ({ importPath }) => { +export const errorPageInjectionPlugin: (options: ErrorPagePluginOptions) => Plugin = options => { return { name: 'spotlight-vite-client-snippet-plugin', transform(code, id, opts = {}) { @@ -14,7 +15,7 @@ export const errorPageInjectionPlugin: (options: ErrorPagePluginOptions) => Plug if (!id.includes('vite/dist/client/client.mjs')) return; const initSnippet = buildSpotlightErrorPageSnippet({ - importPath, + ...options, // Astro's toolbar isn't available in the error page showTriggerButton: true, // don't wait for the window.load event to be fired because in the error page, diff --git a/packages/overlay/src/index.tsx b/packages/overlay/src/index.tsx index 463e301ef..e87af4c92 100644 --- a/packages/overlay/src/index.tsx +++ b/packages/overlay/src/index.tsx @@ -48,12 +48,12 @@ const DEFAULT_SIDECAR = 'http://localhost:8969/stream'; export async function init({ fullScreen = false, showTriggerButton = true, - integrations = [sentry()], defaultEventId, injectImmediately = false, sidecar = DEFAULT_SIDECAR, anchor = DEFAULT_ANCHOR, debug = false, + integrations, }: { integrations?: Integration[]; fullScreen?: boolean; @@ -77,7 +77,11 @@ export async function init({ if (debug) { activateLogger(); } - const initializedIntegrations = await initIntegrations(integrations); + + // Sentry is enabled by default + const defaultInitegrations = [sentry({ sidecarUrl: sidecar })]; + + const initializedIntegrations = await initIntegrations(integrations ?? defaultInitegrations); // build shadow dom container to contain styles const docRoot = document.createElement('div'); diff --git a/packages/overlay/src/integrations/sentry/index.ts b/packages/overlay/src/integrations/sentry/index.ts index 70fc47119..76543e9b8 100644 --- a/packages/overlay/src/integrations/sentry/index.ts +++ b/packages/overlay/src/integrations/sentry/index.ts @@ -10,13 +10,17 @@ import TracesTab from './tabs/TracesTab'; const HEADER = 'application/x-sentry-envelope'; -export default function sentryIntegration() { +type SentryIntegrationOptions = { + sidecarUrl?: string; +}; + +export default function sentryIntegration(options?: SentryIntegrationOptions) { return { name: 'sentry', forwardedContentType: [HEADER], setup: () => { - addSpotlightIntegrationToSentry(); + addSpotlightIntegrationToSentry(options); }, processEvent: (event: RawEventContext) => processEnvelope(event), @@ -89,12 +93,15 @@ function isErrorEnvelope(envelope: Envelope) { return envelope[1].some(([itemHeader]) => itemHeader.type === 'event'); } -function addSpotlightIntegrationToSentry() { +function addSpotlightIntegrationToSentry(options?: SentryIntegrationOptions) { // A very hacky way to hook into Sentry's SDK // but we love hacks const sentryHub = (window as WindowWithSentry).__SENTRY__?.hub; const sentryClient = sentryHub?.getClient(); if (sentryClient) { - sentryClient.addIntegration(new Spotlight()); + const spotlightIntegration = new Spotlight({ + sidecarUrl: options?.sidecarUrl, + }); + sentryClient.addIntegration(spotlightIntegration); } } diff --git a/packages/overlay/src/integrations/sentry/sentry-integration.ts b/packages/overlay/src/integrations/sentry/sentry-integration.ts index 297d70c33..85a4f63ed 100644 --- a/packages/overlay/src/integrations/sentry/sentry-integration.ts +++ b/packages/overlay/src/integrations/sentry/sentry-integration.ts @@ -1,9 +1,25 @@ import { Client, Envelope, Event, EventProcessor, Hub, Integration } from '@sentry/types'; import { serializeEnvelope } from '@sentry/utils'; +type SpotlightBrowserIntegationOptions = { + /** + * The URL of the Sidecar instance to connect and forward events to. + * If not set, Spotlight will try to connect to the Sidecar running on localhost:8969. + * + * @default "http://localhost:8969/stream" + */ + sidecarUrl?: string; +}; export class Spotlight implements Integration { public name: string = 'DevServerContextLines'; + private _sidecarUrl: string; + + public constructor(options?: SpotlightBrowserIntegationOptions) { + this._sidecarUrl = options?.sidecarUrl ?? 'http://localhost:8969/stream'; + console.log('Spotlight: Using Sidecar URL', this._sidecarUrl); + } + public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { addGlobalEventProcessor(async (event: Event) => { if (event.type || !event.exception || !event.exception.values) { @@ -32,18 +48,18 @@ export class Spotlight implements Integration { const client = getCurrentHub().getClient(); if (client) { - sendEnvelopesToSidecar(client); + sendEnvelopesToSidecar(client, this._sidecarUrl); } } } -function sendEnvelopesToSidecar(client: Client) { +function sendEnvelopesToSidecar(client: Client, sidecarUrl: string) { // Ensure, integrations are initialized even if no DSN was set client?.setupIntegrations(true); if (client.on) { client?.on('beforeEnvelope', (envelope: Envelope) => { - fetch('http://localhost:8969/stream', { + fetch(sidecarUrl, { method: 'POST', body: serializeEnvelope(envelope), headers: {