Skip to content

feat: track errors on all platforms to ensure stability across all environments - #1882

Merged
paustint merged 3 commits into
mainfrom
feat/track-error-analytics-all-platforms
Aug 2, 2026
Merged

feat: track errors on all platforms to ensure stability across all environments#1882
paustint merged 3 commits into
mainfrom
feat/track-error-analytics-all-platforms

Conversation

@paustint

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI review requested due to automatic review settings July 27, 2026 15:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires up error/crash reporting across Jetstream’s non-web surfaces (Desktop renderer + Electron main process, browser extension pages, and Canvas) using per-app public DSNs, and adds user-controlled opt-out that propagates immediately. It also tightens/clarifies deviceId handling (desktop deep link compatibility + API request logging context) and ensures Canvas CSP allows the error tracker’s ingest origin when configured.

Changes:

  • Add per-platform DSN env vars and initialize the error tracker in the extension (app + popup), Canvas, and the Electron main process (via renderer→main IPC).
  • Add crashReportingEnabled preference with UI controls (extension settings + desktop menu checkbox) and live opt-out propagation (IPC event).
  • Improve deviceId flow: make desktop deep-link deviceId optional for backwards compatibility, and enrich API request-scoped logging context with resolved deviceId; update Canvas CSP connect-src dynamically from DSN.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
libs/desktop-types/src/lib/desktop-app.types.ts Adds IPC channel + API surface for crash reporting config and opt-out change notifications; adds crashReportingEnabled preference.
apps/landing/hooks/desktop-auth.hooks.ts Documents deep-link parameter backwards compatibility behavior for desktop auth.
apps/jetstream-web-extension/src/utils/extension.types.ts Extends stored options to include crashReportingEnabled.
apps/jetstream-web-extension/src/utils/extension.store.ts Normalizes persisted extension options to default crash reporting to enabled.
apps/jetstream-web-extension/src/pages/popup/Popup.tsx Initializes error tracking in the popup page and applies opt-out based on settings.
apps/jetstream-web-extension/src/pages/additional-settings/AdditionalSettings.tsx Adds a UI toggle to opt out of crash reporting.
apps/jetstream-web-extension/src/hooks/useExtensionSettings.ts Persists partial option updates without clobbering other option fields; adds crash reporting setters.
apps/jetstream-web-extension/src/environments/environment.ts Switches extension DSN env var to NX_PUBLIC_SENTRY_DSN_EXTENSION.
apps/jetstream-web-extension/src/environments/environment.staging.ts Switches extension DSN env var to NX_PUBLIC_SENTRY_DSN_EXTENSION.
apps/jetstream-web-extension/src/environments/environment.prod.ts Switches extension DSN env var to NX_PUBLIC_SENTRY_DSN_EXTENSION.
apps/jetstream-web-extension/src/core/AppInitializer.tsx Initializes error tracking for the main extension app, applies opt-out, and sets user context.
apps/jetstream-desktop/src/services/menu.service.ts Adds desktop menu checkbox to toggle crash reporting and broadcasts changes to renderers.
apps/jetstream-desktop/src/services/ipc.service.ts Adds IPC handler to configure the main-process crash reporter; makes deep-link deviceId optional.
apps/jetstream-desktop/src/preload.ts Exposes crash reporting IPC methods/events to the renderer.
apps/jetstream-desktop/src/config/error-tracker.ts Implements Electron main-process error tracker init/opt-out/capture helpers.
apps/jetstream-desktop/src/config/auto-updater.ts Captures auto-updater errors into the main-process error tracker.
apps/jetstream-desktop/project.json Marks @sentry/node as external for the desktop build bundling configuration.
apps/jetstream-desktop-client/src/environments/environment.ts Wires desktop renderer DSN from NX_PUBLIC_SENTRY_DSN_DESKTOP.
apps/jetstream-desktop-client/src/environments/environment.prod.ts Wires desktop renderer DSN from NX_PUBLIC_SENTRY_DSN_DESKTOP.
apps/jetstream-desktop-client/src/app/components/core/AppInitializer.tsx Configures main-process crash reporting from the renderer and reacts to opt-out changes.
apps/jetstream-desktop-client/src/app/components/core/AppDesktopState.ts Defaults desktop preferences to crash reporting enabled when unavailable.
apps/jetstream-canvas/vite.config.mts Inlines Canvas DSN at build time (no envPrefix) for runtime access.
apps/jetstream-canvas/src/environments/environment.ts Uses NX_PUBLIC_SENTRY_DSN_CANVAS for Canvas error tracking DSN.
apps/jetstream-canvas/src/environments/environment.staging.ts Uses NX_PUBLIC_SENTRY_DSN_CANVAS for Canvas error tracking DSN.
apps/jetstream-canvas/src/environments/environment.prod.ts Uses NX_PUBLIC_SENTRY_DSN_CANVAS for Canvas error tracking DSN.
apps/jetstream-canvas/src/app/core/AppInitializer.tsx Initializes error tracking in Canvas and best-effort sets user context from signed request.
apps/api/src/app/services/external-auth.service.ts Documents/standardizes deviceId resolution and enriches request-scoped logger context with deviceId.
apps/api/src/app/routes/canvas.routes.ts Adds DSN-derived ingest origin to Canvas CSP connect-src when configured.
.env.example Adds per-platform public DSN env vars for desktop/extension/canvas.

Comment thread apps/jetstream-desktop-client/src/app/components/core/AppInitializer.tsx Outdated
Comment thread apps/api/src/app/services/external-auth.service.ts
@paustint
paustint force-pushed the feat/track-error-analytics-all-platforms branch from fbc2ca0 to 0080677 Compare July 28, 2026 23:01
Copilot AI review requested due to automatic review settings July 28, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

apps/jetstream-web-extension/src/pages/popup/Popup.tsx:54

  • Crash reporting opt-out is applied after initErrorTracker() runs. Since initErrorTracker() bails out when opted out, this ordering can initialize the tracker even when the user has disabled crash reporting. Seed opt-out first (and allow enabling later to initialize on demand).
  useEffect(() => {
    initErrorTracker({
      dsn: environment.sentryDsn,
      environment: environment.production ? 'production' : 'development',
      version: getBrowserExtensionVersion(),
    });
  }, []);

  useEffect(() => {
    setErrorTrackerOptOut(crashReportingEnabled === false);
  }, [crashReportingEnabled]);

apps/jetstream-web-extension/src/core/AppInitializer.tsx:89

  • The error tracker is initialized before applying the user's crash-reporting preference. Because initErrorTracker() returns early when opted out, calling setErrorTrackerOptOut() first avoids initializing telemetry for users who have disabled it, and still allows later opt-in to initialize on demand.
  useEffect(() => {
    initErrorTracker({
      dsn: environment.sentryDsn,
      environment: environment.production ? 'production' : 'development',
      version: getBrowserExtensionVersion(),
    });
  }, []);

  // Honor the "Send crash reports to Jetstream" extension setting (runs after init so it can toggle at runtime).
  useEffect(() => {
    setErrorTrackerOptOut(options.crashReportingEnabled === false);
  }, [options.crashReportingEnabled]);

apps/jetstream-desktop-client/src/app/components/core/AppInitializer.tsx:133

  • The renderer initializes initErrorTracker() before it reads the persisted crash-reporting preference from the main process. Since initErrorTracker() skips initialization when opted out, seed setErrorTrackerOptOut() first and only then call initErrorTracker(), so users who opt out don’t initialize telemetry during startup.
  // Hand the (build-time-baked) DSN to the main process for main-process crash reporting, and seed
  // the renderer's opt-out from the persisted "Send crash reports to Jetstream" menu preference.
  useEffect(() => {
    if (!window.electronAPI) {
      return;
    }
    window.electronAPI.configureCrashReporter(environment.sentryDsn).catch((ex) => {
      logger.error('[CRASH REPORTER] Error configuring main process crash reporter', ex);
    });
    window.electronAPI
      .getPreferences()
      .then((preferences) => setErrorTrackerOptOut(preferences.crashReportingEnabled === false))
      .catch((ex) => {
        logger.error('[CRASH REPORTER] Error reading crash reporting preference', ex);
      });
  }, []);

paustint and others added 2 commits July 30, 2026 09:01
The desktop already knows its own deviceId — it generates the login URL from
`dataService.getAppData()` and passes that same value to `verifyAuthToken`. The
callback URL only echoes it back, so requiring it as a literal added no security
while forcing the browser relay page to keep the deviceId in the deep-link URL.

Accept `deviceId` when present and still assert it matches, but make it optional.
The per-login `token` nonce (generated in the same closure) remains required and is
what proves the callback belongs to this login attempt.

The landing page still sends the parameter for older desktop builds; it can be
dropped once those have aged out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…vironments

Wire the error tracker into the desktop app (renderer + main process), the browser
extension (app pages + popup), and the Salesforce canvas app, each reporting to its
own Better Stack project via a separate public DSN.

- Desktop: renderer hands the build-time DSN to the main process over IPC, which
  initializes @sentry/node. Opt out from the "Send crash reports to Jetstream" menu
  checkbox; the persisted preference is the single source of truth (read on every
  event) and a change re-reads preferences in the renderer so the settings page can
  never write a stale value back over it.
- Extension: one `useExtensionErrorTracker` hook shared by the app pages and the
  popup, driven by a new "Send crash reports to Jetstream" setting.
- Canvas: reports user context from the signed request. The API allow-lists the
  ingest origin in the canvas CSP `connect-src` (validated in `ENV` at boot so a
  malformed DSN fails loudly instead of silently blocking reports in an iframe).

In every case the opt-out is applied before init, so nothing is reported while the
preference is still loading, and re-enabling starts reporting without a restart.

`fileReplacements` are not applied by the @nx/vite build, so `environment.ts` ships
in every build for canvas and the desktop renderer - `production` now derives from
`import.meta.env.PROD` so error reports are not all tagged `development`. The
identifiers that production has always shipped (the desktop `name` used for the local
database, the canvas `serverUrl`) are deliberately left alone.

Also: `crashReportingEnabled` defaults to true in the preferences schema rather than
relying on an "absence means enabled" convention, and `saveAuthResponseToAppData`
takes the server-shaped `UserProfileUi` it actually persists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 31, 2026 00:29
@paustint
paustint force-pushed the feat/track-error-analytics-all-platforms branch from 0080677 to b2c49cb Compare July 31, 2026 00:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated 1 comment.

Comment thread libs/api-config/src/lib/env-config.ts
Copilot AI review requested due to automatic review settings July 31, 2026 02:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 36 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

apps/jetstream-canvas/vite.config.mts:36

  • define currently uses JSON.stringify(process.env.NX_PUBLIC_SENTRY_DSN_CANVAS ?? null), which will inline the literal string "null" when the env var is unset. That makes environment.sentryDsn truthy and initErrorTracker will attempt to initialize with an invalid DSN instead of treating it as not configured.
    // Canvas vite has no `envPrefix`, so inline the DSN explicitly (mirrors the amplitude handling above).
    'import.meta.env.NX_PUBLIC_SENTRY_DSN_CANVAS': JSON.stringify(process.env.NX_PUBLIC_SENTRY_DSN_CANVAS ?? null),
    'globalThis.__IS_CANVAS_APP__': true,

@paustint
paustint marked this pull request as ready for review July 31, 2026 14:59
Copilot AI review requested due to automatic review settings July 31, 2026 14:59
@paustint
paustint force-pushed the feat/track-error-analytics-all-platforms branch from 8ce2590 to bcef7bf Compare July 31, 2026 14:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated no new comments.

Suppressed comments (2)

apps/jetstream-desktop-client/src/app/components/core/useDesktopErrorTracker.ts:26

  • window.electronAPI?.configureCrashReporter(...) is optionally invoked, but .catch(...) is still called on the result. When electronAPI is undefined (e.g. browser dev), this will throw TypeError: Cannot read properties of undefined (reading 'catch') on mount.
  useEffect(() => {
    window.electronAPI?.configureCrashReporter(environment.sentryDsn).catch((ex) => {
      logger.error('[CRASH REPORTER] Error configuring main process crash reporter', ex);
    });

apps/jetstream-desktop-client/src/app/components/core/useDesktopErrorTracker.ts:40

  • The crash-reporting toggle listener closes over preferences and writes { ...preferences, crashReportingEnabled }. If preferences change between renders (e.g. other settings updated) and the IPC event arrives before this effect re-subscribes, this can overwrite newer preference fields with stale values. Prefer a functional state update so the latest preferences are always merged.
  useEffect(
    () => window.electronAPI?.onCrashReportingChanged((crashReportingEnabled) => setPreferences({ ...preferences, crashReportingEnabled })),
    [preferences, setPreferences],
  );

…iling boot

`z.url().optional()` rejects an empty string, and both an unset CI secret and the
`NX_PUBLIC_SENTRY_DSN_CANVAS=''` line in `.env.example` produce one — so the API
exited at startup with an env parse error rather than simply disabling canvas error
reporting. Coerce empty to undefined; a non-empty but malformed DSN still fails the
boot-time parse, which is the point of validating it there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 31, 2026 15:13
@paustint
paustint force-pushed the feat/track-error-analytics-all-platforms branch from bcef7bf to 61c7840 Compare July 31, 2026 15:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 37 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/jetstream-desktop-client/src/app/components/core/useDesktopErrorTracker.ts:26

  • window.electronAPI?.configureCrashReporter(...) can evaluate to undefined when running outside Electron (browser dev), but .catch(...) is still invoked on the result. That will throw Cannot read properties of undefined (reading 'catch') and can break the initializer in non-Electron contexts.
  useEffect(() => {
    window.electronAPI?.configureCrashReporter(environment.sentryDsn).catch((ex) => {
      logger.error('[CRASH REPORTER] Error configuring main process crash reporter', ex);
    });
  }, []);

@paustint
paustint merged commit 5bf4402 into main Aug 2, 2026
9 checks passed
@paustint
paustint deleted the feat/track-error-analytics-all-platforms branch August 2, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants