react-doctor 0.5.8-dev.c2ce298 → 0.5.8-dev.c72b560
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.
- package/LICENSE +14 -1
- package/dist/cli.js +449 -148
- package/dist/index.d.ts +2 -2
- package/dist/index.js +238 -65
- package/dist/lsp.js +178 -66
- package/package.json +7 -6
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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]="
|
|
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]="bd31e7dc-cfca-56b2-a963-9d798b3cbbe5")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -9041,7 +9041,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
|
|
|
9041
9041
|
* @since 2.0.0
|
|
9042
9042
|
*/
|
|
9043
9043
|
const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
|
|
9044
|
-
const setImmediate = "setImmediate" in globalThis ? (f) => {
|
|
9044
|
+
const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
|
|
9045
9045
|
const timer = globalThis.setImmediate(f);
|
|
9046
9046
|
return () => globalThis.clearImmediate(timer);
|
|
9047
9047
|
} : (f) => {
|
|
@@ -9085,7 +9085,7 @@ var PriorityBuckets = class {
|
|
|
9085
9085
|
var MixedScheduler = class {
|
|
9086
9086
|
executionMode;
|
|
9087
9087
|
setImmediate;
|
|
9088
|
-
constructor(executionMode = "async", setImmediateFn = setImmediate) {
|
|
9088
|
+
constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
|
|
9089
9089
|
this.executionMode = executionMode;
|
|
9090
9090
|
this.setImmediate = setImmediateFn;
|
|
9091
9091
|
}
|
|
@@ -9110,7 +9110,7 @@ var MixedSchedulerDispatcher = class {
|
|
|
9110
9110
|
tasks = /* @__PURE__ */ new PriorityBuckets();
|
|
9111
9111
|
running = void 0;
|
|
9112
9112
|
setImmediate;
|
|
9113
|
-
constructor(setImmediateFn = setImmediate) {
|
|
9113
|
+
constructor(setImmediateFn = setImmediate$1) {
|
|
9114
9114
|
this.setImmediate = setImmediateFn;
|
|
9115
9115
|
}
|
|
9116
9116
|
/**
|
|
@@ -36862,6 +36862,7 @@ const DOCS_URL = "https://react.doctor/docs";
|
|
|
36862
36862
|
const DOCS_RULES_BASE_URL = `${DOCS_URL}/rules`;
|
|
36863
36863
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
36864
36864
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
36865
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
36865
36866
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
36866
36867
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
36867
36868
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -36946,6 +36947,7 @@ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
|
36946
36947
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
36947
36948
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
36948
36949
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
36950
|
+
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
36949
36951
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
36950
36952
|
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
36951
36953
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
@@ -38556,7 +38558,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
38556
38558
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
38557
38559
|
return Math.min(Math.floor(requested), 32);
|
|
38558
38560
|
};
|
|
38559
|
-
const readSystemFacts = () => ({
|
|
38561
|
+
const readSystemFacts$1 = () => ({
|
|
38560
38562
|
availableCores: os.availableParallelism(),
|
|
38561
38563
|
totalMemoryBytes: os.totalmem(),
|
|
38562
38564
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -38577,7 +38579,7 @@ const readSystemFacts = () => ({
|
|
|
38577
38579
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
38578
38580
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
38579
38581
|
*/
|
|
38580
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
38582
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
38581
38583
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
38582
38584
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
38583
38585
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -38768,6 +38770,12 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
38768
38770
|
"adoptExistingLintConfig"
|
|
38769
38771
|
];
|
|
38770
38772
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
38773
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
38774
|
+
"projects",
|
|
38775
|
+
"textComponents",
|
|
38776
|
+
"rawTextWrapperComponents",
|
|
38777
|
+
"serverAuthFunctionNames"
|
|
38778
|
+
];
|
|
38771
38779
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
38772
38780
|
"includeTags",
|
|
38773
38781
|
"excludeTags",
|
|
@@ -38869,6 +38877,7 @@ const validateConfigTypes = (config) => {
|
|
|
38869
38877
|
const validated = { ...config };
|
|
38870
38878
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
38871
38879
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
38880
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
38872
38881
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
38873
38882
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
38874
38883
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -40131,7 +40140,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
40131
40140
|
}
|
|
40132
40141
|
return true;
|
|
40133
40142
|
};
|
|
40134
|
-
const
|
|
40143
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
40144
|
+
setImmediate(resolve);
|
|
40145
|
+
});
|
|
40146
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
40135
40147
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
40136
40148
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
40137
40149
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -40146,7 +40158,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
40146
40158
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
40147
40159
|
}];
|
|
40148
40160
|
});
|
|
40149
|
-
if (enabledScanRules.length === 0) return
|
|
40161
|
+
if (enabledScanRules.length === 0) return null;
|
|
40150
40162
|
const diagnostics = [];
|
|
40151
40163
|
const seen = /* @__PURE__ */ new Set();
|
|
40152
40164
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -40158,15 +40170,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
40158
40170
|
}
|
|
40159
40171
|
return status === true;
|
|
40160
40172
|
};
|
|
40161
|
-
|
|
40162
|
-
|
|
40163
|
-
|
|
40164
|
-
|
|
40165
|
-
|
|
40166
|
-
|
|
40167
|
-
|
|
40173
|
+
const scanFile = (file) => {
|
|
40174
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
40175
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
40176
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
40177
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
40178
|
+
if (seen.has(key)) continue;
|
|
40179
|
+
seen.add(key);
|
|
40180
|
+
diagnostics.push(diagnostic);
|
|
40181
|
+
}
|
|
40182
|
+
};
|
|
40183
|
+
return {
|
|
40184
|
+
scanFile,
|
|
40185
|
+
diagnostics
|
|
40186
|
+
};
|
|
40187
|
+
};
|
|
40188
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
40189
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
40190
|
+
if (session === null) return [];
|
|
40191
|
+
let filesSinceYield = 0;
|
|
40192
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
40193
|
+
session.scanFile(file);
|
|
40194
|
+
filesSinceYield += 1;
|
|
40195
|
+
if (filesSinceYield >= 16) {
|
|
40196
|
+
filesSinceYield = 0;
|
|
40197
|
+
await yieldToEventLoop();
|
|
40198
|
+
}
|
|
40168
40199
|
}
|
|
40169
|
-
return diagnostics;
|
|
40200
|
+
return session.diagnostics;
|
|
40170
40201
|
};
|
|
40171
40202
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
40172
40203
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -40399,6 +40430,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
40399
40430
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
40400
40431
|
};
|
|
40401
40432
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
40433
|
+
const readSystemFacts = () => ({
|
|
40434
|
+
availableCores: os.availableParallelism(),
|
|
40435
|
+
totalMemoryBytes: os.totalmem(),
|
|
40436
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
40437
|
+
});
|
|
40438
|
+
/**
|
|
40439
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
40440
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
40441
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
40442
|
+
* workers that fit in available memory, floored at 1.
|
|
40443
|
+
*
|
|
40444
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
40445
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
40446
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
40447
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
40448
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
40449
|
+
* cap the per-project spawn path lacked.
|
|
40450
|
+
*
|
|
40451
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
40452
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
40453
|
+
*/
|
|
40454
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
40455
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
40456
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
40457
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
40458
|
+
};
|
|
40459
|
+
let availableSlots = -1;
|
|
40460
|
+
const waiters = [];
|
|
40461
|
+
const releaseSlot = () => {
|
|
40462
|
+
const nextWaiter = waiters.shift();
|
|
40463
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
40464
|
+
else availableSlots += 1;
|
|
40465
|
+
};
|
|
40466
|
+
/**
|
|
40467
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
40468
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
40469
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
40470
|
+
* queue and run as slots free.
|
|
40471
|
+
*
|
|
40472
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
40473
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
40474
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
40475
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
40476
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
40477
|
+
*/
|
|
40478
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
40479
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
40480
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
40481
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
40482
|
+
else await new Promise((resolve, reject) => {
|
|
40483
|
+
const waiter = () => {
|
|
40484
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
40485
|
+
resolve();
|
|
40486
|
+
};
|
|
40487
|
+
const onAbort = () => {
|
|
40488
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
40489
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
40490
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
40491
|
+
};
|
|
40492
|
+
waiters.push(waiter);
|
|
40493
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
40494
|
+
});
|
|
40495
|
+
try {
|
|
40496
|
+
return await task();
|
|
40497
|
+
} finally {
|
|
40498
|
+
releaseSlot();
|
|
40499
|
+
}
|
|
40500
|
+
};
|
|
40402
40501
|
/**
|
|
40403
40502
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
40404
40503
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -40689,14 +40788,17 @@ const checkDeadCode = async (options) => {
|
|
|
40689
40788
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
40690
40789
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
40691
40790
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
40692
|
-
const
|
|
40693
|
-
|
|
40694
|
-
|
|
40695
|
-
|
|
40696
|
-
|
|
40697
|
-
|
|
40698
|
-
|
|
40699
|
-
|
|
40791
|
+
const spawnAndRun = () => {
|
|
40792
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
40793
|
+
rootDirectory,
|
|
40794
|
+
entryPatterns,
|
|
40795
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
40796
|
+
ignorePatterns,
|
|
40797
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
40798
|
+
parseConcurrency: options.parseConcurrency
|
|
40799
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
40800
|
+
};
|
|
40801
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
40700
40802
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
40701
40803
|
const diagnostics = [];
|
|
40702
40804
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -41097,43 +41199,46 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41097
41199
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
41098
41200
|
* sees a single failure channel.
|
|
41099
41201
|
*/
|
|
41100
|
-
const runCommand = (input) =>
|
|
41101
|
-
const
|
|
41102
|
-
cwd: input.directory,
|
|
41103
|
-
env: input.env,
|
|
41104
|
-
extendEnv: true
|
|
41105
|
-
}));
|
|
41106
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
41107
|
-
const stdoutByteCount = yield* make$13(0);
|
|
41108
|
-
const [stdout, stderr, status] = yield* all([
|
|
41109
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41110
|
-
args: [...input.args],
|
|
41111
|
-
directory: input.directory,
|
|
41112
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
41113
|
-
}) })) : void_)))))),
|
|
41114
|
-
mkString(decodeText(handle.stderr)),
|
|
41115
|
-
handle.exitCode
|
|
41116
|
-
], { concurrency: 3 });
|
|
41117
|
-
return {
|
|
41118
|
-
status,
|
|
41119
|
-
stdout,
|
|
41120
|
-
stderr
|
|
41121
|
-
};
|
|
41122
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
41123
|
-
if (input.command !== "git") return succeed$2({
|
|
41202
|
+
const runCommand = (input) => {
|
|
41203
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
41124
41204
|
status: 127,
|
|
41125
41205
|
stdout: "",
|
|
41126
41206
|
stderr: String(cause)
|
|
41127
|
-
})
|
|
41128
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41207
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41129
41208
|
args: [...input.args],
|
|
41130
41209
|
directory: input.directory,
|
|
41131
41210
|
cause
|
|
41132
|
-
}) });
|
|
41133
|
-
|
|
41134
|
-
|
|
41135
|
-
|
|
41136
|
-
|
|
41211
|
+
}) }));
|
|
41212
|
+
return scoped(gen(function* () {
|
|
41213
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
41214
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
41215
|
+
if (argvLengthChars > 24e3) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${SPAWN_ARGS_MAX_LENGTH_CHARS})`);
|
|
41216
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
41217
|
+
cwd: input.directory,
|
|
41218
|
+
env: input.env,
|
|
41219
|
+
extendEnv: true
|
|
41220
|
+
}));
|
|
41221
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
41222
|
+
const stdoutByteCount = yield* make$13(0);
|
|
41223
|
+
const [stdout, stderr, status] = yield* all([
|
|
41224
|
+
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41225
|
+
args: [...input.args],
|
|
41226
|
+
directory: input.directory,
|
|
41227
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
41228
|
+
}) })) : void_)))))),
|
|
41229
|
+
mkString(decodeText(handle.stderr)),
|
|
41230
|
+
handle.exitCode
|
|
41231
|
+
], { concurrency: 3 });
|
|
41232
|
+
return {
|
|
41233
|
+
status,
|
|
41234
|
+
stdout,
|
|
41235
|
+
stderr
|
|
41236
|
+
};
|
|
41237
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
41238
|
+
"git.command": input.command,
|
|
41239
|
+
"git.subcommand": input.args[0] ?? ""
|
|
41240
|
+
} }));
|
|
41241
|
+
};
|
|
41137
41242
|
const runGit = (directory, args) => runCommand({
|
|
41138
41243
|
command: "git",
|
|
41139
41244
|
args,
|
|
@@ -41166,7 +41271,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41166
41271
|
"rev-parse",
|
|
41167
41272
|
"--verify",
|
|
41168
41273
|
branch
|
|
41169
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
41274
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
41170
41275
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
41171
41276
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
41172
41277
|
"merge-base",
|
|
@@ -41380,7 +41485,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41380
41485
|
]);
|
|
41381
41486
|
if (result.status !== 0) return null;
|
|
41382
41487
|
return parseChangedLineRanges(result.stdout);
|
|
41383
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
41488
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
41384
41489
|
});
|
|
41385
41490
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
41386
41491
|
/**
|
|
@@ -43857,7 +43962,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
43857
43962
|
* diagnostics).
|
|
43858
43963
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
43859
43964
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
43860
|
-
* expo/react-native
|
|
43965
|
+
* expo/react-native), collected synchronously. The heavier
|
|
43966
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
43967
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
43968
|
+
* rather than blocking the event loop before it.
|
|
43861
43969
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
43862
43970
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
43863
43971
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -43877,7 +43985,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
43877
43985
|
* order, so terminal output is identical either way; supply-chain
|
|
43878
43986
|
* rides alongside without a spinner.
|
|
43879
43987
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
43880
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
43988
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
43881
43989
|
* byte-identical regardless of which fiber settled first. The
|
|
43882
43990
|
* viewer-permission fiber is joined later, during score-metadata
|
|
43883
43991
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -43938,12 +44046,12 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
43938
44046
|
...checkPnpmHardening(scanDirectory),
|
|
43939
44047
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
43940
44048
|
...checkExpoProject(scanDirectory, project),
|
|
43941
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
43942
|
-
...checkSecurityScan(scanDirectory, {
|
|
43943
|
-
project,
|
|
43944
|
-
ignoredTags: input.ignoredTags
|
|
43945
|
-
})
|
|
44049
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
43946
44050
|
])));
|
|
44051
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(promise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
44052
|
+
project,
|
|
44053
|
+
ignoredTags: input.ignoredTags
|
|
44054
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
|
|
43947
44055
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
43948
44056
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
43949
44057
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -44079,9 +44187,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44079
44187
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
44080
44188
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
44081
44189
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
44190
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
44082
44191
|
yield* reporterService.finalize;
|
|
44083
44192
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
44084
44193
|
...envCollected,
|
|
44194
|
+
...securityScanCollected,
|
|
44085
44195
|
...supplyChainCollected,
|
|
44086
44196
|
...lintCollected,
|
|
44087
44197
|
...deadCodeCollected
|
|
@@ -44620,6 +44730,61 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
44620
44730
|
}
|
|
44621
44731
|
return crypto.createHash("sha1").update(parts.join("|")).digest("hex");
|
|
44622
44732
|
};
|
|
44733
|
+
const AI_TRAINING_ENV_VARS = [
|
|
44734
|
+
["HF_DATASETS_CACHE", "huggingface"],
|
|
44735
|
+
["HF_HOME", "huggingface"],
|
|
44736
|
+
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
44737
|
+
["SPACE_ID", "huggingface-spaces"],
|
|
44738
|
+
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
44739
|
+
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
44740
|
+
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
44741
|
+
["TPU_NAME", "google-tpu"],
|
|
44742
|
+
["WANDB_RUN_ID", "wandb"],
|
|
44743
|
+
["WANDB_SWEEP_ID", "wandb"],
|
|
44744
|
+
["MLFLOW_RUN_ID", "mlflow"],
|
|
44745
|
+
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
44746
|
+
["COMET_EXPERIMENT_KEY", "comet"],
|
|
44747
|
+
["NEPTUNE_RUN_ID", "neptune"],
|
|
44748
|
+
["CLEARML_TASK_ID", "clearml"],
|
|
44749
|
+
["DVC_STAGE", "dvc"],
|
|
44750
|
+
["RAY_WORKER_PROCESS", "ray"],
|
|
44751
|
+
["RAY_ADDRESS", "ray"],
|
|
44752
|
+
["MUJOCO_GL", "mujoco"],
|
|
44753
|
+
["MUJOCO_PATH", "mujoco"],
|
|
44754
|
+
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
44755
|
+
["SM_TRAINING_ENV", "sagemaker"],
|
|
44756
|
+
["TRAINING_JOB_ARN", "sagemaker"],
|
|
44757
|
+
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
44758
|
+
["AZUREML_RUN_ID", "azure-ml"],
|
|
44759
|
+
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
44760
|
+
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
44761
|
+
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
44762
|
+
["DET_MASTER", "determined-ai"],
|
|
44763
|
+
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
44764
|
+
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
44765
|
+
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
44766
|
+
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
44767
|
+
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
44768
|
+
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
44769
|
+
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
44770
|
+
["DAYTONA_WS_ID", "daytona"],
|
|
44771
|
+
["DAYTONA_WS_NAME", "daytona"],
|
|
44772
|
+
["E2B_SANDBOX_ID", "e2b"],
|
|
44773
|
+
["MODAL_FUNCTION_ID", "modal"],
|
|
44774
|
+
["MODAL_TASK_ID", "modal"],
|
|
44775
|
+
["RUNPOD_POD_ID", "runpod"],
|
|
44776
|
+
["REPLICATE_USERNAME", "replicate"],
|
|
44777
|
+
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
44778
|
+
["HARBOR_URL", "harbor"],
|
|
44779
|
+
["HARBOR_HOSTNAME", "harbor"],
|
|
44780
|
+
["SWE_BENCH_TASK", "swe-bench"],
|
|
44781
|
+
["SWEBENCH_TASK", "swe-bench"],
|
|
44782
|
+
["SWE_AGENT_MODEL", "swe-agent"]
|
|
44783
|
+
];
|
|
44784
|
+
const detectAiTrainingEnvironment = () => {
|
|
44785
|
+
for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
|
|
44786
|
+
return null;
|
|
44787
|
+
};
|
|
44623
44788
|
const groupBy = (items, keyFn) => {
|
|
44624
44789
|
const groups = /* @__PURE__ */ new Map();
|
|
44625
44790
|
for (const item of items) {
|
|
@@ -44705,7 +44870,25 @@ const CI_PROVIDER_BY_ENVIRONMENT_VARIABLE = [
|
|
|
44705
44870
|
["TEAMCITY_VERSION", "teamcity"],
|
|
44706
44871
|
["BITBUCKET_BUILD_NUMBER", "bitbucket"],
|
|
44707
44872
|
["TRAVIS", "travis"],
|
|
44708
|
-
["DRONE", "drone"]
|
|
44873
|
+
["DRONE", "drone"],
|
|
44874
|
+
["SEMAPHORE", "semaphore"],
|
|
44875
|
+
["APPVEYOR", "appveyor"],
|
|
44876
|
+
["HARNESS_BUILD_ID", "harness"],
|
|
44877
|
+
["BUDDY", "buddy"],
|
|
44878
|
+
["CF_BUILD_ID", "codefresh"],
|
|
44879
|
+
["NETLIFY", "netlify"],
|
|
44880
|
+
["RAILWAY_SERVICE_ID", "railway"],
|
|
44881
|
+
["VERCEL", "vercel"],
|
|
44882
|
+
["CM_BUILD_ID", "codemagic"],
|
|
44883
|
+
["PROW_JOB_ID", "prow"],
|
|
44884
|
+
["AGOLA_GIT_REF", "agola"],
|
|
44885
|
+
["CIRRUS_CI", "cirrus-ci"],
|
|
44886
|
+
["BLACKSMITH_STICKYDISK_TOKEN", "blacksmith"],
|
|
44887
|
+
["WARPBUILD_RUNNER_VERIFICATION_TOKEN", "warpbuild"],
|
|
44888
|
+
["NSC_CACHE_PATH", "namespace"],
|
|
44889
|
+
["UBICLOUD_CACHE_URL", "ubicloud"],
|
|
44890
|
+
["RENDER", "render"],
|
|
44891
|
+
["FLY_APP_NAME", "fly-io"]
|
|
44709
44892
|
];
|
|
44710
44893
|
const GITHUB_ACTION_MARKER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_GITHUB_ACTION";
|
|
44711
44894
|
const ACTION_INPUT_ENVIRONMENT_VARIABLES = {
|
|
@@ -44723,7 +44906,10 @@ const CODING_AGENT_BY_ENVIRONMENT_VARIABLE = [
|
|
|
44723
44906
|
["CODEX_SANDBOX_NETWORK_DISABLED", "codex"],
|
|
44724
44907
|
["OPENCODE", "opencode"],
|
|
44725
44908
|
["GOOSE_TERMINAL", "goose"],
|
|
44726
|
-
["AMP_THREAD_ID", "amp"]
|
|
44909
|
+
["AMP_THREAD_ID", "amp"],
|
|
44910
|
+
["CLINE_ACTIVE", "cline"],
|
|
44911
|
+
["AUGMENT_AGENT", "augment"],
|
|
44912
|
+
["TRAE_AI_SHELL_ID", "trae-ai"]
|
|
44727
44913
|
];
|
|
44728
44914
|
const GENERIC_CODING_AGENT_ENVIRONMENT_VARIABLES = ["AGENT_SESSION_ID", "AGENT_THREAD_ID"];
|
|
44729
44915
|
const CODING_AGENT_ENVIRONMENT_VALUE_VARIABLES = ["AGENT"];
|
|
@@ -44842,6 +45028,7 @@ const NANOSECONDS_PER_SECOND = 1000000000n;
|
|
|
44842
45028
|
const METRIC = {
|
|
44843
45029
|
cliInvoked: "cli.invoked",
|
|
44844
45030
|
cliError: "cli.error",
|
|
45031
|
+
cliEnvironmentError: "cli.env_error",
|
|
44845
45032
|
projectDetected: "project.detected",
|
|
44846
45033
|
projectPathSelected: "project.path_selected",
|
|
44847
45034
|
projectConfigSelected: "project.config_selected",
|
|
@@ -44871,7 +45058,8 @@ const METRIC = {
|
|
|
44871
45058
|
lspSessionStarted: "lsp.session.started",
|
|
44872
45059
|
lspScanCompleted: "lsp.scan.completed",
|
|
44873
45060
|
lspScanDuration: "lsp.scan.duration",
|
|
44874
|
-
lspScanDiagnostics: "lsp.scan.diagnostics"
|
|
45061
|
+
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
45062
|
+
aiTrainingWarningShown: "ai.training.warning_shown"
|
|
44875
45063
|
};
|
|
44876
45064
|
//#endregion
|
|
44877
45065
|
//#region src/cli/utils/noop-console.ts
|
|
@@ -44914,7 +45102,7 @@ const makeNoopConsole = () => ({
|
|
|
44914
45102
|
});
|
|
44915
45103
|
//#endregion
|
|
44916
45104
|
//#region src/cli/utils/version.ts
|
|
44917
|
-
const VERSION = "0.5.8-dev.
|
|
45105
|
+
const VERSION = "0.5.8-dev.c72b560";
|
|
44918
45106
|
//#endregion
|
|
44919
45107
|
//#region src/cli/utils/json-mode.ts
|
|
44920
45108
|
let context = null;
|
|
@@ -45278,13 +45466,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
45278
45466
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
45279
45467
|
* standard `SENTRY_RELEASE` override.
|
|
45280
45468
|
*/
|
|
45281
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.
|
|
45469
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.c72b560`;
|
|
45282
45470
|
/**
|
|
45283
45471
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
45284
45472
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
45285
45473
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
45286
45474
|
*/
|
|
45287
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.
|
|
45475
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.c72b560") ? "development" : "production");
|
|
45288
45476
|
/**
|
|
45289
45477
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
45290
45478
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -48880,6 +49068,25 @@ const shouldBlockCi = (diagnostics, blockingLevel) => {
|
|
|
48880
49068
|
*/
|
|
48881
49069
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
48882
49070
|
//#endregion
|
|
49071
|
+
//#region src/cli/utils/with-namespace.ts
|
|
49072
|
+
/**
|
|
49073
|
+
* Prefixes every key in a flat attribute group with `namespace.`, so a logical
|
|
49074
|
+
* group (scan config, diagnostics rollup, score, …) carries one dotted
|
|
49075
|
+
* namespace applied in a single place rather than hand-spelled into each key —
|
|
49076
|
+
* which is how the wide event drifted into a flat, half-namespaced soup. The
|
|
49077
|
+
* dotted prefix is what makes the attributes tree up in Sentry's attribute
|
|
49078
|
+
* browser and stay group-/filter-/aggregate-able in the Spans dataset.
|
|
49079
|
+
*
|
|
49080
|
+
* Value types are preserved verbatim (numbers stay numbers so Sentry infers a
|
|
49081
|
+
* numeric attribute and `p75(...)`/`avg(...)` work; `null` is kept so
|
|
49082
|
+
* `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
49083
|
+
*/
|
|
49084
|
+
const withNamespace = (namespace, attributes) => {
|
|
49085
|
+
const namespaced = {};
|
|
49086
|
+
for (const [key, value] of Object.entries(attributes)) namespaced[`${namespace}.${key}`] = value;
|
|
49087
|
+
return namespaced;
|
|
49088
|
+
};
|
|
49089
|
+
//#endregion
|
|
48883
49090
|
//#region src/cli/utils/build-run-event.ts
|
|
48884
49091
|
const readEnvBoolean = (name) => {
|
|
48885
49092
|
const value = process.env[name];
|
|
@@ -48901,12 +49108,12 @@ const buildOutcomeAttributes = (input) => {
|
|
|
48901
49108
|
if (input.result === void 0) {
|
|
48902
49109
|
const error = input.error;
|
|
48903
49110
|
const known = isReactDoctorError(error);
|
|
48904
|
-
return {
|
|
48905
|
-
|
|
49111
|
+
return withNamespace("outcome", {
|
|
49112
|
+
status: "error",
|
|
48906
49113
|
exitCode: 1,
|
|
48907
49114
|
knownError: known,
|
|
48908
49115
|
errorTag: known ? error.reason._tag : error instanceof Error ? error.name : null
|
|
48909
|
-
};
|
|
49116
|
+
});
|
|
48910
49117
|
}
|
|
48911
49118
|
const result = input.result;
|
|
48912
49119
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
@@ -48940,66 +49147,82 @@ const buildOutcomeAttributes = (input) => {
|
|
|
48940
49147
|
}
|
|
48941
49148
|
let fixGroupedFindings = 0;
|
|
48942
49149
|
for (const count of findingsPerFixGroup.values()) fixGroupedFindings += count;
|
|
49150
|
+
const categoryRollup = {};
|
|
49151
|
+
for (const [category, count] of countByCategory) categoryRollup[`category.${toCategoryKey(category)}`] = count;
|
|
48943
49152
|
const attributes = {
|
|
48944
|
-
outcome,
|
|
48945
|
-
|
|
48946
|
-
|
|
48947
|
-
|
|
48948
|
-
|
|
48949
|
-
|
|
48950
|
-
|
|
48951
|
-
|
|
48952
|
-
|
|
48953
|
-
|
|
48954
|
-
|
|
48955
|
-
|
|
48956
|
-
|
|
48957
|
-
|
|
48958
|
-
|
|
48959
|
-
|
|
48960
|
-
|
|
48961
|
-
|
|
48962
|
-
|
|
48963
|
-
|
|
48964
|
-
|
|
48965
|
-
|
|
48966
|
-
|
|
48967
|
-
|
|
48968
|
-
|
|
48969
|
-
|
|
48970
|
-
|
|
48971
|
-
|
|
48972
|
-
|
|
48973
|
-
|
|
48974
|
-
|
|
48975
|
-
|
|
48976
|
-
|
|
48977
|
-
|
|
48978
|
-
|
|
48979
|
-
|
|
48980
|
-
|
|
48981
|
-
|
|
48982
|
-
|
|
48983
|
-
|
|
48984
|
-
|
|
48985
|
-
|
|
48986
|
-
|
|
49153
|
+
...withNamespace("outcome", {
|
|
49154
|
+
status: outcome,
|
|
49155
|
+
exitCode: wouldBlock ? 1 : 0,
|
|
49156
|
+
wouldBlock,
|
|
49157
|
+
blocking: blockingLevel,
|
|
49158
|
+
clean: isClean,
|
|
49159
|
+
skippedChecks: result.skippedChecks.length
|
|
49160
|
+
}),
|
|
49161
|
+
...withNamespace("diag", {
|
|
49162
|
+
total: summary.totalDiagnosticCount,
|
|
49163
|
+
errors: summary.errorCount,
|
|
49164
|
+
warnings: summary.warningCount,
|
|
49165
|
+
affectedFiles: summary.affectedFileCount,
|
|
49166
|
+
inTestFiles: diagnosticsInTestFiles,
|
|
49167
|
+
inStoryFiles: diagnosticsInStoryFiles,
|
|
49168
|
+
distinctRules: countByRule.size,
|
|
49169
|
+
topRule,
|
|
49170
|
+
fixGroups: findingsPerFixGroup.size,
|
|
49171
|
+
fixGroupedFindings,
|
|
49172
|
+
...categoryRollup
|
|
49173
|
+
}),
|
|
49174
|
+
...withNamespace("score", {
|
|
49175
|
+
value: result.score ? result.score.score : null,
|
|
49176
|
+
label: result.score ? result.score.label : null,
|
|
49177
|
+
available: result.score !== null
|
|
49178
|
+
}),
|
|
49179
|
+
...withNamespace("lint", {
|
|
49180
|
+
failed: input.didLintFail ?? null,
|
|
49181
|
+
failureReasonKind: input.lintFailureReasonKind ?? null,
|
|
49182
|
+
partialFailureCount: input.lintPartialFailureCount ?? null,
|
|
49183
|
+
droppedFileCount: input.lintDroppedFileCount ?? null,
|
|
49184
|
+
cacheHitFiles: result.lintCacheHitFileCount ?? null,
|
|
49185
|
+
cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
|
|
49186
|
+
cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
|
|
49187
|
+
}),
|
|
49188
|
+
...withNamespace("deadCode", {
|
|
49189
|
+
failed: input.didDeadCodeFail ?? null,
|
|
49190
|
+
overlapped: input.deadCodeOverlapped ?? null
|
|
49191
|
+
}),
|
|
49192
|
+
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
49193
|
+
...withNamespace("timing", {
|
|
49194
|
+
elapsedMs: result.elapsedMilliseconds,
|
|
49195
|
+
scanMs: result.scanElapsedMilliseconds ?? null
|
|
49196
|
+
}),
|
|
49197
|
+
...withNamespace("migration", {
|
|
49198
|
+
largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
|
|
49199
|
+
largestRuleBucketSites: largestRuleBucket ? largestRuleBucket.siteCount : null,
|
|
49200
|
+
largestRuleBucketRule: largestRuleBucket ? largestRuleBucket.ruleKey : null
|
|
49201
|
+
})
|
|
49202
|
+
};
|
|
49203
|
+
if (result.baselineDelta) Object.assign(attributes, withNamespace("baseline", {
|
|
49204
|
+
new: summary.totalDiagnosticCount,
|
|
49205
|
+
fixed: result.baselineDelta.fixedCount,
|
|
49206
|
+
baseTotal: result.baselineDelta.baseTotalCount,
|
|
49207
|
+
degraded: false
|
|
49208
|
+
}));
|
|
49209
|
+
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
48987
49210
|
return attributes;
|
|
48988
49211
|
};
|
|
48989
|
-
const
|
|
49212
|
+
const buildActionAttributes = () => {
|
|
48990
49213
|
const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
|
|
48991
|
-
return {
|
|
49214
|
+
return withNamespace("action", {
|
|
48992
49215
|
actorAssociation: githubActorAssociation ?? null,
|
|
48993
49216
|
runnerOs: detectRunnerOs(),
|
|
48994
49217
|
comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
|
|
48995
49218
|
reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
|
|
48996
49219
|
versionPin: resolveVersionPin(process.env[ACTION_INPUT_ENVIRONMENT_VARIABLES.version])
|
|
48997
|
-
};
|
|
49220
|
+
});
|
|
48998
49221
|
};
|
|
48999
|
-
const
|
|
49222
|
+
const buildScanAttributes = (input) => {
|
|
49000
49223
|
const ruleOverrides = input.userConfig?.rules ?? {};
|
|
49001
49224
|
const ruleKeys = Object.keys(ruleOverrides);
|
|
49002
|
-
return {
|
|
49225
|
+
return withNamespace("scan", {
|
|
49003
49226
|
mode: input.mode,
|
|
49004
49227
|
scope: input.scope,
|
|
49005
49228
|
parallel: input.parallel,
|
|
@@ -49014,12 +49237,18 @@ const buildConfigAttributes = (input) => {
|
|
|
49014
49237
|
ignoredTagCount: input.ignoredTagCount,
|
|
49015
49238
|
hasCustomConfig: input.hasCustomConfig,
|
|
49016
49239
|
rulesConfigured: ruleKeys.length,
|
|
49017
|
-
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length
|
|
49018
|
-
|
|
49240
|
+
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
49241
|
+
fileCount: input.result?.scannedFileCount ?? null
|
|
49242
|
+
});
|
|
49019
49243
|
};
|
|
49020
49244
|
/**
|
|
49021
|
-
* Projects a scan into the
|
|
49022
|
-
* per-scan "wide event".
|
|
49245
|
+
* Projects a scan into the namespaced attribute set for its root span — the
|
|
49246
|
+
* canonical per-scan "wide event". Every attribute carries a dotted namespace
|
|
49247
|
+
* that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
|
|
49248
|
+
* verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`,
|
|
49249
|
+
* `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
|
|
49250
|
+
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
49251
|
+
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
49023
49252
|
* precedence, rule/category rollups, CI knobs, config shape) is unit-testable
|
|
49024
49253
|
* without a live Sentry client. `null` values are dropped so absent signals
|
|
49025
49254
|
* never become misleading `"null"` attributes. The run + project base context
|
|
@@ -49028,8 +49257,8 @@ const buildConfigAttributes = (input) => {
|
|
|
49028
49257
|
* those don't carry.
|
|
49029
49258
|
*/
|
|
49030
49259
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
49031
|
-
...
|
|
49032
|
-
...
|
|
49260
|
+
...buildScanAttributes(input),
|
|
49261
|
+
...buildActionAttributes(),
|
|
49033
49262
|
...buildOutcomeAttributes(input)
|
|
49034
49263
|
});
|
|
49035
49264
|
/**
|
|
@@ -50357,7 +50586,7 @@ const readPackageJson = (projectRoot) => {
|
|
|
50357
50586
|
return null;
|
|
50358
50587
|
}
|
|
50359
50588
|
};
|
|
50360
|
-
const writeJsonFile
|
|
50589
|
+
const writeJsonFile = (filePath, value) => {
|
|
50361
50590
|
NFS.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
50362
50591
|
};
|
|
50363
50592
|
const packageHasDependency = (projectRoot, dependencyName) => {
|
|
@@ -51077,7 +51306,8 @@ const getStagedSourceFiles = async (directory) => {
|
|
|
51077
51306
|
return [...await runPromise(gen(function* () {
|
|
51078
51307
|
return yield* (yield* StagedFiles).discoverSourceFiles(directory);
|
|
51079
51308
|
}).pipe(provide(stagedFilesLayer)))];
|
|
51080
|
-
} catch {
|
|
51309
|
+
} catch (error) {
|
|
51310
|
+
cliLogger.warn(`Failed to discover staged files: ${error instanceof Error ? error.message : String(error)}`);
|
|
51081
51311
|
return [];
|
|
51082
51312
|
}
|
|
51083
51313
|
};
|
|
@@ -51096,6 +51326,35 @@ const materializeStagedFiles = async (directory, stagedFiles, tempDirectory) =>
|
|
|
51096
51326
|
};
|
|
51097
51327
|
};
|
|
51098
51328
|
//#endregion
|
|
51329
|
+
//#region src/cli/utils/is-environment-error.ts
|
|
51330
|
+
const isNodeSystemError = (error) => error instanceof Error && typeof error.code === "string";
|
|
51331
|
+
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
51332
|
+
"ENOSPC",
|
|
51333
|
+
"EIO",
|
|
51334
|
+
"EROFS",
|
|
51335
|
+
"EACCES",
|
|
51336
|
+
"EPERM",
|
|
51337
|
+
"ENOTDIR"
|
|
51338
|
+
]);
|
|
51339
|
+
const isEnvironmentError = (error) => {
|
|
51340
|
+
if (!isNodeSystemError(error)) return false;
|
|
51341
|
+
if (error.code === "ENOENT") return error.syscall?.startsWith("spawn") ?? false;
|
|
51342
|
+
return error.code !== void 0 && ENVIRONMENT_ERROR_CODES.has(error.code);
|
|
51343
|
+
};
|
|
51344
|
+
const formatEnvironmentError = (error) => {
|
|
51345
|
+
if (!isNodeSystemError(error)) return error instanceof Error ? error.message : String(error);
|
|
51346
|
+
switch (error.code) {
|
|
51347
|
+
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
51348
|
+
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
51349
|
+
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
51350
|
+
case "EACCES":
|
|
51351
|
+
case "EPERM": return error.path ? `Permission denied accessing ${error.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
51352
|
+
case "ENOTDIR": return error.path ? `A file exists at ${error.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
51353
|
+
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
51354
|
+
default: return error.message;
|
|
51355
|
+
}
|
|
51356
|
+
};
|
|
51357
|
+
//#endregion
|
|
51099
51358
|
//#region src/cli/utils/handle-error.ts
|
|
51100
51359
|
const OTLP_ENDPOINT_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_ENDPOINT";
|
|
51101
51360
|
const OTLP_AUTH_HEADER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_AUTH_HEADER";
|
|
@@ -51178,15 +51437,19 @@ const handleError = (error, options = {}) => {
|
|
|
51178
51437
|
process.exitCode = 1;
|
|
51179
51438
|
};
|
|
51180
51439
|
/**
|
|
51181
|
-
* Renderer for expected, user-actionable failures — a bad `--diff` value
|
|
51182
|
-
* a base branch that isn't fetched
|
|
51183
|
-
*
|
|
51184
|
-
*
|
|
51440
|
+
* Renderer for expected, user-actionable failures — a bad `--diff` value,
|
|
51441
|
+
* a base branch that isn't fetched, or environment errors like disk-full or
|
|
51442
|
+
* permission-denied. Prints just the (already human-readable) message — no
|
|
51443
|
+
* "Something went wrong", prefilled issue, Discord link, or Sentry reference
|
|
51444
|
+
* — because there is no bug to report.
|
|
51185
51445
|
*/
|
|
51186
51446
|
const handleUserError = (error, options = {}) => {
|
|
51447
|
+
const isEnvError = isEnvironmentError(error);
|
|
51448
|
+
if (isEnvError) recordCount(METRIC.cliEnvironmentError, 1, { code: error.code ?? "unknown" });
|
|
51449
|
+
const message = isEnvError ? formatEnvironmentError(error) : formatErrorForReport(error);
|
|
51187
51450
|
runSync(gen(function* () {
|
|
51188
51451
|
yield* error$1("");
|
|
51189
|
-
yield* error$1(highlighter.error(
|
|
51452
|
+
yield* error$1(highlighter.error(message));
|
|
51190
51453
|
yield* error$1("");
|
|
51191
51454
|
}));
|
|
51192
51455
|
if (options.shouldExit !== false) process.exit(1);
|
|
@@ -51201,7 +51464,7 @@ const handleUserError = (error, options = {}) => {
|
|
|
51201
51464
|
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
51202
51465
|
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
51203
51466
|
*
|
|
51204
|
-
*
|
|
51467
|
+
* Four distinct shapes reach the CLI's catch blocks:
|
|
51205
51468
|
*
|
|
51206
51469
|
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
51207
51470
|
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
@@ -51214,12 +51477,19 @@ const handleUserError = (error, options = {}) => {
|
|
|
51214
51477
|
* `--project` name.
|
|
51215
51478
|
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
51216
51479
|
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
51480
|
+
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
51481
|
+
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
51482
|
+
* full / failing / read-only, permission denied, or a path blocked by a
|
|
51483
|
+
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
51484
|
+
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
51485
|
+
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
51486
|
+
* Sentry).
|
|
51217
51487
|
*
|
|
51218
51488
|
* This composes the existing core narrowers rather than introducing a new
|
|
51219
51489
|
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
51220
51490
|
* knowledge of the `ReactDoctorError` shape.
|
|
51221
51491
|
*/
|
|
51222
|
-
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
51492
|
+
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
51223
51493
|
//#endregion
|
|
51224
51494
|
//#region src/cli/utils/build-handoff-payload.ts
|
|
51225
51495
|
const buildHandoffPayload = (input) => {
|
|
@@ -51395,7 +51665,7 @@ const installDoctorScript = (options) => {
|
|
|
51395
51665
|
};
|
|
51396
51666
|
})();
|
|
51397
51667
|
const scriptStatus = scriptTarget.status;
|
|
51398
|
-
if (scriptStatus === "created") writeJsonFile
|
|
51668
|
+
if (scriptStatus === "created") writeJsonFile(packageJsonPath, {
|
|
51399
51669
|
...packageJson,
|
|
51400
51670
|
scripts: {
|
|
51401
51671
|
...isRecord$1(scripts) ? scripts : {},
|
|
@@ -51989,20 +52259,34 @@ const CURSOR_HOOKS_RELATIVE_PATH = ".cursor/hooks.json";
|
|
|
51989
52259
|
const CURSOR_HOOK_RELATIVE_PATH = ".cursor/hooks/react-doctor.sh";
|
|
51990
52260
|
const CURSOR_HOOK_MATCHER = "Write|Edit|MultiEdit|ApplyPatch";
|
|
51991
52261
|
const CURSOR_HOOKS_SCHEMA_VERSION = 1;
|
|
51992
|
-
const JSON_INDENT_SPACES$1 = 2;
|
|
51993
52262
|
const isSupportedAgent = (agent) => agent === CLAUDE_AGENT || agent === CURSOR_AGENT;
|
|
51994
52263
|
const readJsonFile = (filePath, fallback) => {
|
|
51995
52264
|
if (!NFS.existsSync(filePath)) return fallback;
|
|
51996
52265
|
const content = NFS.readFileSync(filePath, "utf8").trim();
|
|
51997
52266
|
if (content.length === 0) return fallback;
|
|
51998
|
-
|
|
52267
|
+
try {
|
|
52268
|
+
return JSON.parse(content);
|
|
52269
|
+
} catch (error) {
|
|
52270
|
+
if (error instanceof SyntaxError) throw new CliInputError(`Could not parse ${filePath}: the file contains invalid JSON. Fix the syntax errors in this file and re-run the install command.`);
|
|
52271
|
+
throw error;
|
|
52272
|
+
}
|
|
51999
52273
|
};
|
|
52000
|
-
const
|
|
52001
|
-
|
|
52002
|
-
|
|
52274
|
+
const ensureDirectoryExists = (directoryPath) => {
|
|
52275
|
+
try {
|
|
52276
|
+
NFS.mkdirSync(directoryPath, { recursive: true });
|
|
52277
|
+
} catch (error) {
|
|
52278
|
+
const code = error.code;
|
|
52279
|
+
if (code === "EACCES" || code === "EPERM") throw new CliInputError(`Could not create directory ${directoryPath}: permission denied. Ensure you have write permissions for this location and re-run the install command.`);
|
|
52280
|
+
if (code === "ENOTDIR" || code === "EEXIST") throw new CliInputError(`Could not create directory ${directoryPath}: a file exists at this path or one of its parent paths. Remove the conflicting file and re-run the install command.`);
|
|
52281
|
+
throw error;
|
|
52282
|
+
}
|
|
52283
|
+
};
|
|
52284
|
+
const writeJsonFileWithDirectoryCheck = (filePath, value) => {
|
|
52285
|
+
ensureDirectoryExists(Path.dirname(filePath));
|
|
52286
|
+
writeJsonFile(filePath, value);
|
|
52003
52287
|
};
|
|
52004
52288
|
const writeHookScript = (filePath) => {
|
|
52005
|
-
|
|
52289
|
+
ensureDirectoryExists(Path.dirname(filePath));
|
|
52006
52290
|
NFS.writeFileSync(filePath, buildAgentHookScript());
|
|
52007
52291
|
NFS.chmodSync(filePath, 493);
|
|
52008
52292
|
};
|
|
@@ -52018,7 +52302,7 @@ const installClaudeHook = (projectRoot) => {
|
|
|
52018
52302
|
command: CLAUDE_HOOK_COMMAND
|
|
52019
52303
|
}] });
|
|
52020
52304
|
hooks.PostToolBatch = postToolBatchHooks;
|
|
52021
|
-
|
|
52305
|
+
writeJsonFileWithDirectoryCheck(settingsPath, {
|
|
52022
52306
|
...settings,
|
|
52023
52307
|
hooks
|
|
52024
52308
|
});
|
|
@@ -52038,7 +52322,7 @@ const installCursorHook = (projectRoot) => {
|
|
|
52038
52322
|
timeout: 120
|
|
52039
52323
|
});
|
|
52040
52324
|
hooks.postToolUse = postToolUseHooks;
|
|
52041
|
-
|
|
52325
|
+
writeJsonFileWithDirectoryCheck(configPath, {
|
|
52042
52326
|
...config,
|
|
52043
52327
|
version: config.version ?? CURSOR_HOOKS_SCHEMA_VERSION,
|
|
52044
52328
|
hooks
|
|
@@ -52274,7 +52558,7 @@ const installPackageJsonHook = (options, strategy) => {
|
|
|
52274
52558
|
parent = cloned;
|
|
52275
52559
|
}
|
|
52276
52560
|
parent[leafKey] = strategy.leafShape === "array" ? appendArrayCommand(parent[leafKey]) : appendStringCommand(parent[leafKey]);
|
|
52277
|
-
writeJsonFile
|
|
52561
|
+
writeJsonFile(packageJsonPath, nextPackageJson);
|
|
52278
52562
|
removeLegacyManagedRunner(options.projectRoot);
|
|
52279
52563
|
return {
|
|
52280
52564
|
hookPath: packageJsonPath,
|
|
@@ -53713,7 +53997,7 @@ const warnDeprecatedDiff = (flags, userConfig) => {
|
|
|
53713
53997
|
};
|
|
53714
53998
|
const warnDiffUnavailable = (requested, isQuiet) => {
|
|
53715
53999
|
if (isQuiet) return;
|
|
53716
|
-
if (typeof requested.base === "string") cliLogger.warn(`Could not compute diff against "${requested.base}" (
|
|
54000
|
+
if (typeof requested.base === "string") cliLogger.warn(`Could not compute diff against "${requested.base}" (git unavailable, ref not found, or merge-base failed). Running full scan.`);
|
|
53717
54001
|
else cliLogger.warn("No feature branch or uncommitted changes detected. Running full scan.");
|
|
53718
54002
|
cliLogger.break();
|
|
53719
54003
|
};
|
|
@@ -53910,6 +54194,7 @@ const resolveRequestedProjects = (requestedNames, workspacePackages, rootDirecto
|
|
|
53910
54194
|
return requestedNames.map((requestedName) => {
|
|
53911
54195
|
const matched = workspacePackages.find((workspacePackage) => workspacePackage.name === requestedName || Path.basename(workspacePackage.directory) === requestedName);
|
|
53912
54196
|
if (matched) return matched.directory;
|
|
54197
|
+
if (Path.basename(rootDirectory) === requestedName) return rootDirectory;
|
|
53913
54198
|
const candidateDirectory = Path.resolve(rootDirectory, requestedName);
|
|
53914
54199
|
if (isDirectory(candidateDirectory)) {
|
|
53915
54200
|
recordCount(METRIC.projectPathSelected);
|
|
@@ -54017,6 +54302,17 @@ const warnDeprecatedFailOn = (flags, userConfig) => {
|
|
|
54017
54302
|
cliLogger.warn(`${source} is deprecated; rename it to ${replacement}.`);
|
|
54018
54303
|
};
|
|
54019
54304
|
//#endregion
|
|
54305
|
+
//#region src/cli/utils/warn-ai-training-environment.ts
|
|
54306
|
+
let didWarnAiTraining = false;
|
|
54307
|
+
const warnIfAiTrainingEnvironment = () => {
|
|
54308
|
+
if (didWarnAiTraining) return;
|
|
54309
|
+
const detected = detectAiTrainingEnvironment();
|
|
54310
|
+
if (detected === null) return;
|
|
54311
|
+
didWarnAiTraining = true;
|
|
54312
|
+
process.stderr.write(`${highlighter.warn("react-doctor detected use in an AI or ML pipeline. This use requires written permission under the react-doctor license — contact founders@million.dev to request access.")}\n`);
|
|
54313
|
+
recordCount(METRIC.aiTrainingWarningShown, 1, { environment: detected });
|
|
54314
|
+
};
|
|
54315
|
+
//#endregion
|
|
54020
54316
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
54021
54317
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
54022
54318
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
@@ -54117,6 +54413,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
54117
54413
|
setJsonReportDirectory(resolvedDirectory);
|
|
54118
54414
|
warnDeprecatedFailOn(flags, userConfig);
|
|
54119
54415
|
warnDeprecatedDiff(flags, userConfig);
|
|
54416
|
+
warnIfAiTrainingEnvironment();
|
|
54120
54417
|
if (scanTarget.didRedirectViaRootDir && !isQuiet) {
|
|
54121
54418
|
cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
|
|
54122
54419
|
cliLogger.break();
|
|
@@ -54404,6 +54701,10 @@ const installAction = async (options, command) => {
|
|
|
54404
54701
|
projectRoot: options.cwd ?? process.cwd()
|
|
54405
54702
|
});
|
|
54406
54703
|
} catch (error) {
|
|
54704
|
+
if (isExpectedUserError(error)) {
|
|
54705
|
+
handleUserError(error);
|
|
54706
|
+
return;
|
|
54707
|
+
}
|
|
54407
54708
|
handleError(error, { sentryEventId: await reportErrorToSentry(error) });
|
|
54408
54709
|
}
|
|
54409
54710
|
};
|
|
@@ -55411,4 +55712,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
55411
55712
|
export {};
|
|
55412
55713
|
|
|
55413
55714
|
//# sourceMappingURL=cli.js.map
|
|
55414
|
-
//# debugId=
|
|
55715
|
+
//# debugId=bd31e7dc-cfca-56b2-a963-9d798b3cbbe5
|