Files
Aiden Bai 8c2f03aea9 feat: make React cleanup first-class (#1624)
* feat: make React cleanup first-class

* refactor: remove editor integrations

* fix: harden React cleanup analysis

* fix: detect default export duplication roots

* fix: unwrap typed duplication roots

* feat: add opt-in project analysis rules

* fix: canonicalize project analysis paths

* fix: harden project analysis precision

* fix: recognize cross-platform project entries

* fix: eliminate project analysis false positives

* fix: harden project analysis reachability

* fix: canonicalize project analysis inputs

* fix: resolve project analysis review findings

* fix: eliminate residual project analysis false positives

* fix: ignore commented registry previews

* fix: eliminate project analysis false positives

* fix: normalize project analysis paths across platforms

* fix: normalize Nextra theme path identity

* test: canonicalize convention fixture paths

* fix: preserve project analysis provenance

* fix: harden project analysis precision

* fix: honor project analysis boundaries

* fix: recognize conditional config plugins

* fix: recognize executable project references

* fix: recognize Stencil tool contracts

* fix: recognize nested tool references

* fix: recognize project setup contracts

* fix: recognize generated and local package consumers

* fix: recognize static template package references

* fix: recognize nested package runtime contracts

* fix: close project analysis parser gaps

* fix: parse project conventions structurally

* refactor: replace structural scanners with parsers

* fix: recognize functional Next CSS config

* fix: close remaining project analysis gaps

* fix: apply tag filters to project analysis

* fix: preserve embedded source positions

* fix: validate static config helper bindings

* fix: bound runtime directory discovery

* fix: close final dependency analysis gaps

* fix: preserve declaration dependency references

* chore: refresh generated rule metadata

* fix: make project analysis portable and bounded

* test: stabilize cleanup scaling guard

* refactor: parse project syntax with oxc

* fix: normalize native filesystem paths

* fix: separate path identity from report paths

* fix: match project files by filesystem identity

* fix: match build glob files by package identity

* fix: use native path keys for file identity

* fix: canonicalize Windows file identities

* fix: canonicalize package ownership paths

* test: inspect Windows path identities

* test: trace Windows package ownership

* fix: keep Windows path identities consistent

* fix: classify test contracts by normalized path

* fix: scope test contracts by canonical package path

* fix: keep test package graphs conservative

* test: keep React complexity advisory
2026-08-13 16:26:08 -07:00

79 lines
3.6 KiB
TypeScript

import * as Sentry from "@sentry/node";
import { buildSentryScope } from "./cli/utils/build-sentry-scope.js";
import { SENTRY_DSN, SENTRY_FLUSH_TIMEOUT_MS } from "./cli/utils/constants.js";
import { isEnvFlagEnabled } from "./cli/utils/is-env-flag-enabled.js";
import { isExpectedUserError } from "./cli/utils/is-expected-user-error.js";
import { isTelemetryEnabled } from "./cli/utils/is-telemetry-enabled.js";
import { scrubSentryEvent } from "./cli/utils/scrub-sentry-event.js";
import { resolveSentryEnvironment, resolveSentryRelease } from "./cli/utils/sentry-config.js";
// Re-exported for back-compat after these resolvers moved to `sentry-config.ts`.
export { resolveSentryEnvironment, resolveSentryRelease };
let isInitialized = false;
/**
* Flushes queued Sentry events (errors + transactions) before the CLI exits, so
* the success-path transaction is delivered. A no-op when Sentry was never
* initialized, and it swallows transport failures so telemetry can never mask
* the user's result.
*/
export const flushSentry = async (): Promise<void> => {
if (!Sentry.isInitialized()) return;
try {
await Sentry.flush(SENTRY_FLUSH_TIMEOUT_MS);
} catch {}
};
/**
* Initializes the Sentry Node SDK for CLI crash reporting and performance
* crash reporting. Invoked as the first statement of the CLI entry
* (`cli/index.ts`) so the SDK's global `uncaughtException` /
* `unhandledRejection` handlers are armed before any command runs.
*
* Performance tracing is off: spans are exported to Axiom instead (see
* `telemetry-runtime.ts`), and Effect has a single `Tracer` reference, so the
* two backends are mutually exclusive. Sentry keeps what it is good at —
* symbolicated stack traces, issue grouping, and a quotable event id — while
* crashes carry the Axiom trace id as a tag so an issue pivots into its trace.
*
* Exported as a function rather than a bare side-effecting import because the
* package declares `"sideEffects": false`, which lets the bundler tree-shake
* side-effect-only modules. An explicit call keeps the initialization in the
* published `dist/cli.js`.
*
* Scoped to the CLI application only — the programmatic `@react-doctor/api`
* library never initializes Sentry, so importing `diagnose()` into a consumer
* app can't hijack their telemetry.
*
* Configuration is environment-overridable for self-hosting and tuning:
* `SENTRY_DSN`, `SENTRY_ENVIRONMENT`, `SENTRY_RELEASE`, and `SENTRY_DEBUG`.
*/
export const initializeSentry = (): void => {
if (isInitialized || !isTelemetryEnabled()) return;
isInitialized = true;
const { tags, contexts } = buildSentryScope();
try {
Sentry.init({
dsn: process.env.SENTRY_DSN || SENTRY_DSN,
release: resolveSentryRelease(),
environment: resolveSentryEnvironment(),
// Anonymized telemetry: never attach the user's IP address.
sendDefaultPii: false,
// Spans go to Axiom; Sentry captures exceptions only.
tracesSampleRate: 0,
debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
// Seed the scope so the run snapshot rides along with every event.
// (Only `run` exists at init; the scanned `project` context is added later
// once a scan discovers it.)
initialScope: { tags, contexts },
// Anonymize every outgoing event: strip hostname/IP/device identity, drop
// captured local variables, and scrub home-directory paths and known
// secrets from all remaining strings. Returns `null` to drop the event if
// scrubbing fails, so un-anonymized data is never sent.
beforeSend: (event, hint) =>
isExpectedUserError(hint.originalException) ? null : scrubSentryEvent(event),
});
} catch {}
};