react-doctor 0.9.7-dev.13138a4 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,7 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="38bfb0f3-70c5-54f3-b1bc-3d859552dacc")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="adb1b48c-bc53-5c98-982a-aeab790b4798")}catch(e){}}();
3
3
  import { n as __require, r as __toESM, t as __commonJSMin } from "./chunk-q7NCDQ7-.js";
4
- import { Et as buildRuleDocsUrl, F as isGitHookEnvironment, Gt as getCategoryImpact, Jn as runPromise, Jt as getDiffInfo, Kt as getChangedLineRanges, M as TUI_REPORT_DETAIL_WIDTH_FRACTION, Ot as calculateScore, Pn as yieldToEventLoop, Qn as void_, Tn as resolveScanTarget, Wt as getBaselineDiffPlan, Xn as sync, Y as DIAGNOSTIC_CATEGORY_BUCKETS, Zt as hasPublishedFixRecipe, _ as CI_TRUST_COMPANIES, an as isPathInsideDirectory, bn as remainingDeadlineBudgetMs, en as highlighter, et as GITHUB_ACTIONS_SETUP_URL, gt as Reporter, hn as mapWithConcurrency, j as TUI_REPORT_COMPACT_MAX_ROWS, mt as Progress, n as recordDistribution, nr as succeed, t as recordCount, y as METRIC, z as isCiEnvironment } from "./record-metric-CtlQma6P.js";
5
- import { n as registerActiveTuiRenderer, t as exitGracefully } from "./exit-gracefully-CddufLS0.js";
6
- import { L as cliLogger, d as stripAnsi, f as isUnicodeSupported, l as isFullWidth$1, n as findNearestPackageDirectory, p as chalk, u as isWide$1 } from "./install-doctor-script-DxrteuEw.js";
7
- import { i as isReactDoctorWorkflowInstalled } from "./install-github-workflow-BP6t3YBA.js";
8
- import { r as resolveScope, s as resolveBlockingLevel, t as finalizeScope, u as toForwardSlashes } from "./resolve-scope-gy72JH3h.js";
9
- import { A as resolveMergeBaseRef, B as buildEmptyReportMessage, C as copyToClipboard, D as deduplicateProjectScans, E as collectProjectSourceFileCounts, F as printDiagnosticsDump, G as formatLearnMoreLine, H as buildRulePriorityMap, I as formatDiagnosticSite, L as hasIncompleteScoreAnalysis, M as pluralize, N as formatElapsedTime, O as createInvocationInspect, P as printFooter, R as filterDiagnosticsByCategories, S as CLI_AGENT_BINARIES, T as buildHandoffPayload, U as buildSortedRuleGroups, V as hasLintHardFailure, W as formatFixRecipeLine, _ as resolveCliInspectOptions, a as projectManifestChanged, b as setUpGitHubActions, c as selectProjects, d as resolveProjectChangedLineRanges, f as resolveProjectDiffIncludePaths, g as resolveWorkspaceDeadCodeOwner, h as retryMissingProjectScores, l as buildCodeFrame, n as formatSkippedProjectsMessage, o as partitionProjectScanOutcomes, p as resolveProjectSourceFilePaths, r as validateIncludeUntrackedScope, s as discoverWorkspacePackages, t as shouldFailScanGate, u as resolveProjectScan, v as filterScansForSurface, w as launchCliAgent, x as detectLaunchableAgents, y as countUniqueScannedFiles, z as buildNoScoreMessage } from "./should-fail-scan-gate-DImKUuu-.js";
10
- import { a as recordGate, f as ONBOARDING_EVENT, n as openUrl, r as isGatePending } from "./report-workflow-result-D3GCowCN.js";
4
+ import { $ as filterDiagnosticsByCategories, $n as GITHUB_ACTIONS_SETUP_URL, B as buildHandoffPayload, C as isGatePending, Cr as highlighter, Dt as recordDistribution, Et as recordCount, F as CLI_AGENT_BINARIES, Gr as sync, Gt as chalk, H as collectProjectSourceFileCounts, Ht as isWide$1, I as copyToClipboard, J as formatElapsedTime, Jr as succeed, Kr as void_, L as launchCliAgent, Ln as TUI_REPORT_COMPACT_MAX_ROWS, M as ONBOARDING_EVENT, Mn as METRIC, Mt as toForwardSlashes, Nr as remainingDeadlineBudgetMs, P as detectLaunchableAgents, Pr as resolveScanTarget, Pt as findNearestPackageDirectory, Q as formatDiagnosticSite, Qn as DIAGNOSTIC_CATEGORY_BUCKETS, Rn as TUI_REPORT_DETAIL_WIDTH_FRACTION, Rr as yieldToEventLoop, S as openUrl, T as recordGate, Tr as isPathInsideDirectory, U as deduplicateProjectScans, Un as isCiEnvironment, Ur as runPromise, Ut as stripAnsi, V as formatSkippedProjectsMessage, Vn as isGitHookEnvironment, Vt as isFullWidth$1, W as createInvocationInspect, Wt as isUnicodeSupported, Y as printFooter, Z as printDiagnosticsDump, _ as resolveCliInspectOptions, _n as cliLogger, _r as getCategoryImpact, _t as resolveBlockingLevel, a as shouldFailScanGate, at as buildRulePriorityMap, b as setUpGitHubActions, c as selectProjects, cr as buildRuleDocsUrl, ct as formatLearnMoreLine, d as resolveProjectChangedLineRanges, et as buildEmptyReportMessage, f as resolveProjectDiffIncludePaths, ft as finalizeScope, g as resolveWorkspaceDeadCodeOwner, gr as getBaselineDiffPlan, h as retryMissingProjectScores, it as hasLintHardFailure, jn as CI_TRUST_COMPANIES, jr as mapWithConcurrency, l as buildCodeFrame, lr as calculateScore, mt as resolveScope, n as registerActiveTuiRenderer, nr as Progress, nt as buildNoScoreMessage, o as projectManifestChanged, ot as buildSortedRuleGroups, p as resolveProjectSourceFilePaths, q as pluralize, r as validateIncludeUntrackedScope, rr as Reporter, rt as resolveMergeBaseRef, s as discoverWorkspacePackages, st as formatFixRecipeLine, t as exitGracefully, tt as hasIncompleteScoreAnalysis, u as resolveProjectScan, v as filterScansForSurface, vr as getChangedLineRanges, xr as hasPublishedFixRecipe, xt as isReactDoctorWorkflowInstalled, y as countUniqueScannedFiles, yr as getDiffInfo } from "./exit-gracefully-BJRsqdaK.js";
11
5
  import { EventEmitter } from "node:events";
12
6
  import { execFileSync } from "node:child_process";
13
7
  import path from "node:path";
@@ -20448,7 +20442,7 @@ if (process$1.env["DEV"] === "true") {
20448
20442
  import.meta.resolve("react-devtools-core");
20449
20443
  isDevtoolsInstalled = true;
20450
20444
  } catch {}
20451
- if (isDevtoolsInstalled) await import("./devtools-DPRVRu3S.js");
20445
+ if (isDevtoolsInstalled) await import("./devtools-BQATi33M.js");
20452
20446
  }
20453
20447
  const diff = (before, after) => {
20454
20448
  if (before === after) return;
@@ -27726,7 +27720,7 @@ const ScanApp = ({ store, displayMode = "report", launchableAgents, onHandoff, c
27726
27720
  onQuit?.();
27727
27721
  exit();
27728
27722
  };
27729
- if (displayMode === "report" && snapshot.phase === "summary") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Summary, {
27723
+ if (displayMode === "report" && snapshot.phase === "summary" && snapshot.summary) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Summary, {
27730
27724
  summary: snapshot.summary,
27731
27725
  launchableAgents,
27732
27726
  onHandoff,
@@ -27735,7 +27729,7 @@ const ScanApp = ({ store, displayMode = "report", launchableAgents, onHandoff, c
27735
27729
  onExit: exit,
27736
27730
  onQuit: handleQuit
27737
27731
  });
27738
- if (displayMode === "report" && snapshot.phase === "report") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Report, {
27732
+ if (displayMode === "report" && snapshot.phase === "report" && snapshot.report) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Report, {
27739
27733
  report: snapshot.report,
27740
27734
  launchableAgents,
27741
27735
  onHandoff,
@@ -27771,7 +27765,9 @@ const progressLayerForStore = (store, options = {}) => Progress.layerOra(progres
27771
27765
  const INITIAL_SNAPSHOT = {
27772
27766
  phase: "scanning",
27773
27767
  liveDiagnostics: [],
27774
- progress: null
27768
+ progress: null,
27769
+ report: null,
27770
+ summary: null
27775
27771
  };
27776
27772
  const createScanStore = () => {
27777
27773
  let snapshot = INITIAL_SNAPSHOT;
@@ -27780,7 +27776,7 @@ const createScanStore = () => {
27780
27776
  let progressTimer = null;
27781
27777
  const commit = (next) => {
27782
27778
  snapshot = next;
27783
- for (const listener of [...listeners]) listener();
27779
+ for (const listener of listeners) listener();
27784
27780
  };
27785
27781
  const cancelPendingProgress = () => {
27786
27782
  pendingProgress = null;
@@ -27825,19 +27821,17 @@ const createScanStore = () => {
27825
27821
  setReport: (report) => {
27826
27822
  cancelPendingProgress();
27827
27823
  commit({
27828
- phase: "report",
27829
- liveDiagnostics: snapshot.liveDiagnostics,
27830
- progress: snapshot.progress,
27831
- report
27824
+ ...snapshot,
27825
+ report,
27826
+ phase: "report"
27832
27827
  });
27833
27828
  },
27834
27829
  setSummary: (summary) => {
27835
27830
  cancelPendingProgress();
27836
27831
  commit({
27837
- phase: "summary",
27838
- liveDiagnostics: snapshot.liveDiagnostics,
27839
- progress: snapshot.progress,
27840
- summary
27832
+ ...snapshot,
27833
+ summary,
27834
+ phase: "summary"
27841
27835
  });
27842
27836
  }
27843
27837
  };
@@ -28164,16 +28158,14 @@ const runMultiProjectScan = async (rootScanTarget, directories, input, scopePlan
28164
28158
  context.store.setProgress(`Scanning ${projectCount} projects…`);
28165
28159
  await yieldToEventLoop();
28166
28160
  const precomputedSourceFileCounts = scopePlan.scope === "full" ? await collectProjectSourceFileCounts(rootDirectory, projectScans.map(({ projectScan }) => projectScan.directory)) : null;
28167
- const { completedScans, skippedScans } = partitionProjectScanOutcomes(await mapWithConcurrency(projectScans, 4, async ({ projectScan, scopeOptions }) => {
28161
+ const scanOutcomes = await mapWithConcurrency(projectScans, 4, async ({ projectScan, scopeOptions }) => {
28168
28162
  if (input.options?.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(input.options.deadlineEpochMs) === 0) {
28169
28163
  finishedCount += 1;
28170
28164
  context.store.setProgress(`Scanning ${projectCount} projects… (${finishedCount}/${projectCount})`);
28171
28165
  return {
28172
28166
  status: "skipped",
28173
- value: {
28174
- directory: projectScan.directory,
28175
- reason: "max-duration"
28176
- }
28167
+ directory: projectScan.directory,
28168
+ reason: "max-duration"
28177
28169
  };
28178
28170
  }
28179
28171
  const projectLabel = path.relative(rootDirectory, projectScan.directory) || path.basename(rootDirectory);
@@ -28204,18 +28196,19 @@ const runMultiProjectScan = async (rootScanTarget, directories, input, scopePlan
28204
28196
  await yieldToEventLoop();
28205
28197
  return {
28206
28198
  status: "completed",
28207
- value: {
28208
- directory: projectScan.directory,
28209
- result,
28210
- config: projectScan.config
28211
- }
28199
+ directory: projectScan.directory,
28200
+ result,
28201
+ config: projectScan.config
28212
28202
  };
28213
- }));
28214
- const results = await retryMissingProjectScores(completedScans.map((completedScan) => ({
28203
+ });
28204
+ const results = await retryMissingProjectScores(scanOutcomes.filter((scanOutcome) => scanOutcome.status === "completed").map((completedScan) => ({
28215
28205
  ...completedScan,
28216
28206
  isScoreDisabled: input.options?.noScore ?? completedScan.config?.noScore ?? false
28217
28207
  })));
28218
- const skippedProjects = skippedScans.sort((left, right) => left.directory.localeCompare(right.directory));
28208
+ const skippedProjects = scanOutcomes.filter((scanOutcome) => scanOutcome.status === "skipped").map(({ directory, reason }) => ({
28209
+ directory,
28210
+ reason
28211
+ })).sort((left, right) => left.directory.localeCompare(right.directory));
28219
28212
  if (skippedProjects.length > 0) recordCount(METRIC.scanProjectSkipped, skippedProjects.length, { reason: "max-duration" });
28220
28213
  const projectEntries = results.map(({ directory, result, config }) => {
28221
28214
  const reportSelection = selectReportDiagnostics({
@@ -28314,5 +28307,5 @@ const runScanApp = async (input) => {
28314
28307
  //#endregion
28315
28308
  export { runScanApp };
28316
28309
 
28317
- //# sourceMappingURL=run-scan-app-Drlqxtzl.js.map
28318
- //# debugId=38bfb0f3-70c5-54f3-b1bc-3d859552dacc
28310
+ //# sourceMappingURL=run-scan-app-DawD5rhU.js.map
28311
+ //# debugId=adb1b48c-bc53-5c98-982a-aeab790b4798
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.9.7-dev.13138a4",
3
+ "version": "0.9.7",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -70,12 +70,13 @@
70
70
  "yaml": "^2.9.0",
71
71
  "yoga-layout": "~3.2.1",
72
72
  "deslop-js": "0.9.7",
73
- "oxlint-plugin-react-doctor": "0.9.7-dev.13138a4"
73
+ "oxlint-plugin-react-doctor": "0.9.7"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/babel__code-frame": "^7.27.0",
77
77
  "@types/prompts": "^2.4.9",
78
78
  "@types/react": "^19.2.14",
79
+ "@xterm/headless": "^6.0.0",
79
80
  "commander": "^14.0.3",
80
81
  "ink": "^7.1.0",
81
82
  "ink-spinner": "^5.0.0",
@@ -1,312 +0,0 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8e93a83f-d2d8-5db5-9431-c9699a4d2a7d")}catch(e){}}();
3
- import { D as SENTRY_FLUSH_TIMEOUT_MS, I as isDebugFlagEnabled, On as scrubSensitivePaths, U as CANONICAL_GITHUB_URL, X as DOCS_URL, en as highlighter, i as buildSentryScope, on as isPlainObject, yn as redactSensitiveText } from "./record-metric-CtlQma6P.js";
4
- import { o as isExpectedUserError } from "./unref-stdin-B5RnVeLa.js";
5
- import * as Sentry from "@sentry/node";
6
- //#region src/cli/utils/is-env-flag-enabled.ts
7
- const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
8
- //#endregion
9
- //#region src/cli/utils/anonymize-text.ts
10
- /**
11
- * Free-text fields can carry both a home-directory path (the OS username) and a
12
- * secret/email echoed from user code, so run both scrubbers: strip the username
13
- * from paths, then mask any known credential/PII shape. Shared by the Sentry
14
- * event scrubber ({@link scrubSentryEvent}) and metric scrubber
15
- * ({@link scrubSentryMetric}).
16
- */
17
- const anonymizeText = (text) => redactSensitiveText(scrubSensitivePaths(text));
18
- /**
19
- * Recursively rewrites every string within an arbitrary value (object / array /
20
- * primitive) through {@link anonymizeText}, mutating in place. Used to sweep the
21
- * unstructured corners of a Sentry payload (event contexts/extra/tags, breadcrumb
22
- * data, span attributes, metric attributes) where a path or secret could hide.
23
- */
24
- const anonymizeInPlace = (value) => {
25
- if (Array.isArray(value)) {
26
- for (let index = 0; index < value.length; index += 1) {
27
- const item = value[index];
28
- if (typeof item === "string") value[index] = anonymizeText(item);
29
- else anonymizeInPlace(item);
30
- }
31
- return;
32
- }
33
- if (!isPlainObject(value)) return;
34
- for (const key of Object.keys(value)) {
35
- const inner = value[key];
36
- if (typeof inner === "string") value[key] = anonymizeText(inner);
37
- else anonymizeInPlace(inner);
38
- }
39
- };
40
- //#endregion
41
- //#region src/cli/utils/scrub-sentry-event.ts
42
- /**
43
- * Anonymizes a Sentry event (error or transaction) before it leaves the
44
- * machine. Strips identity the SDK attaches automatically — the IP-bearing
45
- * `user`, the `server_name`, and the device `name` (all hostnames) — drops
46
- * captured local variables (unbounded, un-anonymizable user data), and scrubs
47
- * home-directory paths + known secrets/emails from every remaining string:
48
- * messages, stack frames, breadcrumbs, contexts/extra/tags, and span
49
- * attributes (e.g. the `inspect.directory` path on the bridged `runInspect`
50
- * span).
51
- *
52
- * Wired into both `beforeSend` and `beforeSendTransaction`. If scrubbing ever
53
- * throws, the event is dropped (`null`) rather than risk sending un-anonymized
54
- * data — telemetry is best-effort, privacy is not.
55
- */
56
- const scrubSentryEvent = (event) => {
57
- try {
58
- delete event.server_name;
59
- delete event.user;
60
- const device = event.contexts?.device;
61
- if (device) delete device.name;
62
- if (event.contexts) anonymizeInPlace(event.contexts);
63
- if (event.extra) anonymizeInPlace(event.extra);
64
- if (event.tags) anonymizeInPlace(event.tags);
65
- if (typeof event.message === "string") event.message = anonymizeText(event.message);
66
- for (const breadcrumb of event.breadcrumbs ?? []) {
67
- if (typeof breadcrumb.message === "string") breadcrumb.message = anonymizeText(breadcrumb.message);
68
- if (breadcrumb.data) anonymizeInPlace(breadcrumb.data);
69
- }
70
- for (const exception of event.exception?.values ?? []) {
71
- if (typeof exception.value === "string") exception.value = anonymizeText(exception.value);
72
- for (const frame of exception.stacktrace?.frames ?? []) {
73
- delete frame.vars;
74
- if (typeof frame.filename === "string") frame.filename = scrubSensitivePaths(frame.filename);
75
- if (typeof frame.abs_path === "string") frame.abs_path = scrubSensitivePaths(frame.abs_path);
76
- if (typeof frame.module === "string") frame.module = scrubSensitivePaths(frame.module);
77
- }
78
- }
79
- for (const span of event.spans ?? []) {
80
- if (typeof span.description === "string") span.description = anonymizeText(span.description);
81
- if (span.data) anonymizeInPlace(span.data);
82
- }
83
- return event;
84
- } catch {
85
- return null;
86
- }
87
- };
88
- //#endregion
89
- //#region src/cli/utils/scrub-sentry-metric.ts
90
- /**
91
- * Anonymizes a Sentry metric before it leaves the machine, mirroring
92
- * {@link scrubSentryEvent}. Drops the `server.address` default attribute (the
93
- * hostname) and scrubs home-directory paths + known secrets/emails from every
94
- * remaining attribute value (metric names are our own constants, so they're
95
- * left intact to avoid splitting a series). Returns `null` on any failure so an
96
- * un-anonymized metric is never sent. Wired into `beforeSendMetric`.
97
- */
98
- const scrubSentryMetric = (metric) => {
99
- try {
100
- if (metric.attributes) {
101
- delete metric.attributes["server.address"];
102
- anonymizeInPlace(metric.attributes);
103
- }
104
- return metric;
105
- } catch {
106
- return null;
107
- }
108
- };
109
- //#endregion
110
- //#region src/cli/utils/sentry-config.ts
111
- /**
112
- * Shared Sentry configuration resolution — release, environment, and tracing
113
- * sample rate — derived from `VERSION` and the standard `SENTRY_*` env
114
- * overrides. Lives apart from `instrument.ts` (the CLI's run-context-aware
115
- * init) so both the CLI and the editor language server can resolve identical
116
- * release/environment values without the LSP pulling in CLI run-context code.
117
- */
118
- /**
119
- * A version is a "dev" build when it's the unbuilt placeholder (`0.0.0`) or
120
- * carries a prerelease suffix (e.g. the `-dev.<sha>` snapshots published from
121
- * CI). Everything else is a real, tagged release.
122
- */
123
- const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
124
- /**
125
- * Sentry release identifier. `react-doctor@<version>` keeps it unique within
126
- * the org and — crucially — matches the value `scripts/sentry-sourcemaps.mjs`
127
- * uploads source-map artifacts under, so stack frames symbolicate. Honors the
128
- * standard `SENTRY_RELEASE` override.
129
- */
130
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.7-dev.13138a4`;
131
- /**
132
- * Deployment environment shown in Sentry's environment filter. Defaults to
133
- * `production` for tagged releases and `development` for dev/unbuilt versions,
134
- * overridable via the standard `SENTRY_ENVIRONMENT` env var.
135
- */
136
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.7-dev.13138a4") ? "development" : "production");
137
- /**
138
- * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
139
- * (set to `0` to disable tracing) and falls back to
140
- * {@link SENTRY_DEFAULT_TRACES_SAMPLE_RATE}. Invalid / out-of-range values fall
141
- * back to the default rather than silently disabling tracing.
142
- */
143
- const resolveTracesSampleRate = () => {
144
- const raw = process.env.SENTRY_TRACES_SAMPLE_RATE;
145
- if (raw === void 0 || raw.trim() === "") return 1;
146
- const parsed = Number(raw);
147
- if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) return 1;
148
- return parsed;
149
- };
150
- //#endregion
151
- //#region src/instrument.ts
152
- let isInitialized = false;
153
- let resolvedTracesSampleRate = 0;
154
- const shouldEnableSentry = () => {
155
- if (process.argv.includes("--no-score") || process.argv.includes("--no-telemetry")) return false;
156
- if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
157
- return true;
158
- };
159
- /**
160
- * Whether performance traces will actually be recorded — Sentry is live and the
161
- * resolved sample rate is above zero. Used to gate the per-run root span and
162
- * the Effect→Sentry tracer bridge so they're true no-ops when tracing is off.
163
- */
164
- const isSentryTracingEnabled = () => Sentry.isInitialized() && resolvedTracesSampleRate > 0;
165
- /**
166
- * Flushes queued Sentry events (errors + transactions) before the CLI exits, so
167
- * the success-path transaction is delivered. A no-op when Sentry was never
168
- * initialized, and it swallows transport failures so telemetry can never mask
169
- * the user's result.
170
- */
171
- const flushSentry = async () => {
172
- if (!Sentry.isInitialized()) return;
173
- try {
174
- await Sentry.flush(SENTRY_FLUSH_TIMEOUT_MS);
175
- } catch {}
176
- };
177
- /**
178
- * Initializes the Sentry Node SDK for CLI crash reporting and performance
179
- * tracing. Invoked as the first statement of the CLI entry (`cli/index.ts`) so
180
- * the SDK's global `uncaughtException` / `unhandledRejection` handlers and OTel
181
- * auto-instrumentation are armed before any command runs.
182
- *
183
- * Exported as a function rather than a bare side-effecting import because the
184
- * package declares `"sideEffects": false`, which lets the bundler tree-shake
185
- * side-effect-only modules. An explicit call keeps the initialization in the
186
- * published `dist/cli.js`.
187
- *
188
- * Scoped to the CLI application only — the programmatic `@react-doctor/api`
189
- * library never initializes Sentry, so importing `diagnose()` into a consumer
190
- * app can't hijack their telemetry.
191
- *
192
- * Configuration is environment-overridable for self-hosting and tuning:
193
- * `SENTRY_DSN`, `SENTRY_ENVIRONMENT`, `SENTRY_RELEASE`,
194
- * `SENTRY_TRACES_SAMPLE_RATE` (`0` disables tracing), and `SENTRY_DEBUG`.
195
- */
196
- const initializeSentry = () => {
197
- if (isInitialized || !shouldEnableSentry()) return;
198
- isInitialized = true;
199
- resolvedTracesSampleRate = isDebugFlagEnabled() ? 1 : resolveTracesSampleRate();
200
- const { tags, contexts } = buildSentryScope();
201
- try {
202
- Sentry.init({
203
- dsn: process.env.SENTRY_DSN || "https://f253d570240a59b8dbd77b7a548ef133@o4510226365743104.ingest.us.sentry.io/4511487817809920",
204
- release: resolveSentryRelease(),
205
- environment: resolveSentryEnvironment(),
206
- sendDefaultPii: false,
207
- tracesSampleRate: resolvedTracesSampleRate,
208
- debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
209
- initialScope: {
210
- tags,
211
- contexts
212
- },
213
- beforeSend: (event, hint) => isExpectedUserError(hint.originalException) ? null : scrubSentryEvent(event),
214
- beforeSendTransaction: (event) => scrubSentryEvent(event),
215
- beforeSendMetric: (metric) => scrubSentryMetric(metric)
216
- });
217
- } catch {
218
- resolvedTracesSampleRate = 0;
219
- }
220
- };
221
- //#endregion
222
- //#region src/cli/utils/active-scan-abort-registry.ts
223
- const activeScanAbortControllers = /* @__PURE__ */ new Set();
224
- const activeScanAbortRegistry = {
225
- register: (controller) => {
226
- activeScanAbortControllers.add(controller);
227
- return () => activeScanAbortControllers.delete(controller);
228
- },
229
- abortAll: () => {
230
- const controllers = [...activeScanAbortControllers];
231
- activeScanAbortControllers.clear();
232
- for (const controller of controllers) controller.abort();
233
- }
234
- };
235
- //#endregion
236
- //#region src/cli/utils/resolve-measure-width.ts
237
- const resolveClampedWidth = (input) => {
238
- const terminalColumns = process.stdout.columns;
239
- if (!terminalColumns || terminalColumns <= 0) return input.fullWidth;
240
- const availableColumns = terminalColumns - input.reservedColumns;
241
- return Math.max(input.minWidth, Math.min(input.fullWidth, availableColumns));
242
- };
243
- const resolveMeasureWidth = (reservedColumns = 0) => resolveClampedWidth({
244
- reservedColumns,
245
- fullWidth: 60,
246
- minWidth: 24
247
- });
248
- //#endregion
249
- //#region src/cli/utils/wrap-indented-text.ts
250
- const wrapLine = (lineText, width, breakLongWords) => {
251
- if (lineText.length <= width) return [lineText];
252
- const wrappedLines = [];
253
- let remainingText = lineText.trim();
254
- while (remainingText.length > width) {
255
- const candidateText = remainingText.slice(0, width);
256
- const breakIndex = candidateText.lastIndexOf(" ");
257
- if (breakIndex <= 0) {
258
- if (breakLongWords) {
259
- wrappedLines.push(candidateText);
260
- remainingText = remainingText.slice(width).trimStart();
261
- continue;
262
- }
263
- const nextSpace = remainingText.indexOf(" ");
264
- if (nextSpace === -1) break;
265
- wrappedLines.push(remainingText.slice(0, nextSpace));
266
- remainingText = remainingText.slice(nextSpace + 1).trimStart();
267
- continue;
268
- }
269
- wrappedLines.push(remainingText.slice(0, breakIndex));
270
- remainingText = remainingText.slice(breakIndex + 1).trimStart();
271
- }
272
- if (remainingText.length > 0) wrappedLines.push(remainingText);
273
- return wrappedLines;
274
- };
275
- /**
276
- * Greedy word-wrap to a character width, returning one string per line
277
- * (no indentation applied). Preserves existing `\n` boundaries.
278
- */
279
- const wrapTextToWidth = (text, width, options = {}) => {
280
- if (width <= 0) return text.split("\n");
281
- const breakLongWords = options.breakLongWords ?? true;
282
- return text.split("\n").flatMap((lineText) => wrapLine(lineText, width, breakLongWords));
283
- };
284
- //#endregion
285
- //#region src/cli/utils/build-footer-description-lines.ts
286
- const FOOTER_DESCRIPTION_INDENT = " ";
287
- const buildFooterDescriptionLines = (description) => {
288
- return wrapTextToWidth(description, resolveMeasureWidth(2)).map((line) => highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
289
- };
290
- //#endregion
291
- //#region src/cli/utils/build-footer-link-lines.ts
292
- const buildFooterLinkLines = ({ shareUrl }) => [
293
- ...shareUrl === null ? [] : [
294
- ` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`,
295
- ...buildFooterDescriptionLines("Tell others how you did on socials"),
296
- ""
297
- ],
298
- ` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`,
299
- ...buildFooterDescriptionLines("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file"),
300
- "",
301
- ` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`,
302
- ...buildFooterDescriptionLines("Report issues and star the repository!")
303
- ];
304
- //#endregion
305
- //#region src/cli/utils/build-section-divider.ts
306
- const DIVIDER_INDENT = " ";
307
- const buildSectionDivider = () => highlighter.dim(`${DIVIDER_INDENT}${"─".repeat(resolveMeasureWidth(2))}`);
308
- //#endregion
309
- export { initializeSentry as a, flushSentry as i, buildFooterLinkLines as n, isSentryTracingEnabled as o, activeScanAbortRegistry as r, buildSectionDivider as t };
310
-
311
- //# sourceMappingURL=build-section-divider-QykRnrlR.js.map
312
- //# debugId=8e93a83f-d2d8-5db5-9431-c9699a4d2a7d