react-doctor 0.5.8-dev.ea00b1b → 0.5.8-dev.f69f216

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/dist/index.d.ts CHANGED
@@ -9726,7 +9726,7 @@ interface InspectResult {
9726
9726
  * Per-file lint cache outcome: files served from cache, and total files
9727
9727
  * considered. Both absent when the cache was off or bypassed (audit mode,
9728
9728
  * adopted `extends`, user plugins). The CLI projects these onto the Sentry
9729
- * wide event as `lintCacheHitRatio`.
9729
+ * wide event as `lint.cacheHitRatio`.
9730
9730
  */
9731
9731
  lintCacheHitFileCount?: number | null;
9732
9732
  lintCacheTotalFileCount?: number | null;
@@ -10123,7 +10123,7 @@ declare const summarizeDiagnostics: (diagnostics: Diagnostic[], worstScore?: num
10123
10123
  * untouched so the loader sees the same plain object either way.
10124
10124
  */
10125
10125
  declare const defineConfig: (config: ReactDoctorConfig) => ReactDoctorConfig; //#endregion
10126
- //#region src/utils/group-by.d.ts
10126
+ //#region src/utils/detect-ai-training-environment.d.ts
10127
10127
  //#endregion
10128
10128
  //#region ../api/dist/index.d.ts
10129
10129
  //#region src/diagnose.d.ts
package/dist/index.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]="dccb6d63-63d4-5e91-af21-2756a08fe93a")}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]="9807b1bf-1e62-5421-ad35-d1f4129c1538")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as NFS from "node:fs";
@@ -5996,7 +5996,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
5996
5996
  * @since 2.0.0
5997
5997
  */
5998
5998
  const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
5999
- const setImmediate = "setImmediate" in globalThis ? (f) => {
5999
+ const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
6000
6000
  const timer = globalThis.setImmediate(f);
6001
6001
  return () => globalThis.clearImmediate(timer);
6002
6002
  } : (f) => {
@@ -6040,7 +6040,7 @@ var PriorityBuckets = class {
6040
6040
  var MixedScheduler = class {
6041
6041
  executionMode;
6042
6042
  setImmediate;
6043
- constructor(executionMode = "async", setImmediateFn = setImmediate) {
6043
+ constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
6044
6044
  this.executionMode = executionMode;
6045
6045
  this.setImmediate = setImmediateFn;
6046
6046
  }
@@ -6065,7 +6065,7 @@ var MixedSchedulerDispatcher = class {
6065
6065
  tasks = /* @__PURE__ */ new PriorityBuckets();
6066
6066
  running = void 0;
6067
6067
  setImmediate;
6068
- constructor(setImmediateFn = setImmediate) {
6068
+ constructor(setImmediateFn = setImmediate$1) {
6069
6069
  this.setImmediate = setImmediateFn;
6070
6070
  }
6071
6071
  /**
@@ -33752,6 +33752,7 @@ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
33752
33752
  const LINT_PHASE_TIMEOUT_MS = 3e5;
33753
33753
  const SCAN_TOTAL_DEADLINE_MS = 9e5;
33754
33754
  const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
33755
+ const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
33755
33756
  const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
33756
33757
  const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
33757
33758
  const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
@@ -35369,7 +35370,7 @@ const resolveScanConcurrency = (requested) => {
35369
35370
  if (!Number.isFinite(requested) || requested < 1) return 1;
35370
35371
  return Math.min(Math.floor(requested), 32);
35371
35372
  };
35372
- const readSystemFacts = () => ({
35373
+ const readSystemFacts$1 = () => ({
35373
35374
  availableCores: os.availableParallelism(),
35374
35375
  totalMemoryBytes: os.totalmem(),
35375
35376
  cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
@@ -35390,7 +35391,7 @@ const readSystemFacts = () => ({
35390
35391
  * `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
35391
35392
  * limited, and ceiling cases without mocking `os` or the filesystem.
35392
35393
  */
35393
- const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
35394
+ const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
35394
35395
  const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
35395
35396
  const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
35396
35397
  return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
@@ -36923,7 +36924,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
36923
36924
  }
36924
36925
  return true;
36925
36926
  };
36926
- const checkSecurityScan = (rootDirectory, options = {}) => {
36927
+ const yieldToEventLoop = () => new Promise((resolve) => {
36928
+ setImmediate(resolve);
36929
+ });
36930
+ const createSecurityScanSession = (rootDirectory, options) => {
36927
36931
  const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
36928
36932
  const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
36929
36933
  const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
@@ -36938,7 +36942,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
36938
36942
  committedFilesOnly: rule.committedFilesOnly === true
36939
36943
  }];
36940
36944
  });
36941
- if (enabledScanRules.length === 0) return [];
36945
+ if (enabledScanRules.length === 0) return null;
36942
36946
  const diagnostics = [];
36943
36947
  const seen = /* @__PURE__ */ new Set();
36944
36948
  const gitIgnoredCache = /* @__PURE__ */ new Map();
@@ -36950,15 +36954,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
36950
36954
  }
36951
36955
  return status === true;
36952
36956
  };
36953
- for (const file of collectSecurityScanFiles(rootDirectory)) for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
36954
- if (committedFilesOnly && isFileGitIgnored(file)) continue;
36955
- const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
36956
- const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
36957
- if (seen.has(key)) continue;
36958
- seen.add(key);
36959
- diagnostics.push(diagnostic);
36957
+ const scanFile = (file) => {
36958
+ for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
36959
+ if (committedFilesOnly && isFileGitIgnored(file)) continue;
36960
+ const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
36961
+ const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
36962
+ if (seen.has(key)) continue;
36963
+ seen.add(key);
36964
+ diagnostics.push(diagnostic);
36965
+ }
36966
+ };
36967
+ return {
36968
+ scanFile,
36969
+ diagnostics
36970
+ };
36971
+ };
36972
+ const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
36973
+ const session = createSecurityScanSession(rootDirectory, options);
36974
+ if (session === null) return [];
36975
+ let filesSinceYield = 0;
36976
+ for (const file of collectSecurityScanFiles(rootDirectory)) {
36977
+ session.scanFile(file);
36978
+ filesSinceYield += 1;
36979
+ if (filesSinceYield >= 16) {
36980
+ filesSinceYield = 0;
36981
+ await yieldToEventLoop();
36982
+ }
36960
36983
  }
36961
- return diagnostics;
36984
+ return session.diagnostics;
36962
36985
  };
36963
36986
  var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
36964
36987
  let p = process || {}, argv = p.argv || [], env = p.env || {};
@@ -37175,6 +37198,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
37175
37198
  return [...seen].filter((pattern) => pattern.length > 0);
37176
37199
  };
37177
37200
  const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
37201
+ const readSystemFacts = () => ({
37202
+ availableCores: os.availableParallelism(),
37203
+ totalMemoryBytes: os.totalmem(),
37204
+ cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
37205
+ });
37206
+ /**
37207
+ * How many real deslop dead-code child processes may run at once, across the
37208
+ * concurrent per-project `runInspect` fibers of one CLI run. The cap is the
37209
+ * smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
37210
+ * workers that fit in available memory, floored at 1.
37211
+ *
37212
+ * On a roomy dev box / CI runner this resolves high enough that every
37213
+ * concurrently-scanned project still spawns its own worker (no serialization vs
37214
+ * the prior uncapped behavior); on a memory-constrained runner it collapses
37215
+ * toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
37216
+ * instead of oversubscribing memory with N simultaneous children — the global
37217
+ * cap the per-project spawn path lacked.
37218
+ *
37219
+ * Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
37220
+ * heavier dead-code worker. `facts` is injectable for tests.
37221
+ */
37222
+ const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
37223
+ const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
37224
+ const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
37225
+ return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
37226
+ };
37227
+ let availableSlots = -1;
37228
+ const waiters = [];
37229
+ const releaseSlot = () => {
37230
+ const nextWaiter = waiters.shift();
37231
+ if (nextWaiter !== void 0) nextWaiter();
37232
+ else availableSlots += 1;
37233
+ };
37234
+ /**
37235
+ * Runs `task` once a dead-code worker slot is free, releasing the slot when the
37236
+ * task settles (success or failure). With a high cap (roomy machine) every
37237
+ * caller proceeds immediately; with a low cap (constrained runner) callers
37238
+ * queue and run as slots free.
37239
+ *
37240
+ * `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
37241
+ * this caller is queued, the call rejects without acquiring a slot or running
37242
+ * `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
37243
+ * then spawn a child only to tear it down. A queued caller that aborts removes
37244
+ * its own waiter so a later release never hands a slot to a dead request.
37245
+ */
37246
+ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
37247
+ if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
37248
+ if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
37249
+ if (availableSlots > 0) availableSlots -= 1;
37250
+ else await new Promise((resolve, reject) => {
37251
+ const waiter = () => {
37252
+ abortSignal?.removeEventListener("abort", onAbort);
37253
+ resolve();
37254
+ };
37255
+ const onAbort = () => {
37256
+ const queuedIndex = waiters.indexOf(waiter);
37257
+ if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
37258
+ reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
37259
+ };
37260
+ waiters.push(waiter);
37261
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
37262
+ });
37263
+ try {
37264
+ return await task();
37265
+ } finally {
37266
+ releaseSlot();
37267
+ }
37268
+ };
37178
37269
  /**
37179
37270
  * Resolves a path to its canonical, symlink-free form, falling back to
37180
37271
  * the input when it cannot be realpath'd (broken symlink, permission
@@ -37465,14 +37556,17 @@ const checkDeadCode = async (options) => {
37465
37556
  if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
37466
37557
  const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
37467
37558
  const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
37468
- const result = parseDeadCodeWorkerResult(await runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
37469
- rootDirectory,
37470
- entryPatterns,
37471
- tsConfigPath: resolveTsConfigPath(rootDirectory),
37472
- ignorePatterns,
37473
- deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
37474
- parseConcurrency: options.parseConcurrency
37475
- }), options.workerTimeoutMs ?? 12e4, options.abortSignal));
37559
+ const spawnAndRun = () => {
37560
+ return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
37561
+ rootDirectory,
37562
+ entryPatterns,
37563
+ tsConfigPath: resolveTsConfigPath(rootDirectory),
37564
+ ignorePatterns,
37565
+ deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
37566
+ parseConcurrency: options.parseConcurrency
37567
+ }), options.workerTimeoutMs ?? 12e4, options.abortSignal);
37568
+ };
37569
+ const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
37476
37570
  const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
37477
37571
  const diagnostics = [];
37478
37572
  for (const unusedFile of result.unusedFiles) diagnostics.push({
@@ -40636,7 +40730,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
40636
40730
  * diagnostics).
40637
40731
  * 2. beforeLint hook (e.g. CLI renders the project-detection block)
40638
40732
  * 3. environment checks (reduced-motion + pnpm hardening +
40639
- * expo/react-native + security scan), collected synchronously
40733
+ * expo/react-native), collected synchronously. The heavier
40734
+ * content-regex security scan is forked instead (like supply-chain
40735
+ * below) and joined before the concat, so its CPU overlaps lint
40736
+ * rather than blocking the event loop before it.
40640
40737
  * 4. The supply-chain check (Socket.dev) is forked onto a background
40641
40738
  * fiber so its ~100% network-bound time overlaps the ~100%
40642
40739
  * CPU/subprocess-bound lint pass below, collapsing two serial
@@ -40656,7 +40753,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
40656
40753
  * order, so terminal output is identical either way; supply-chain
40657
40754
  * rides alongside without a spinner.
40658
40755
  * 6. Join the supply-chain fiber, then assemble the diagnostics in a
40659
- * FIXED order (env, supply-chain, lint, dead-code) so the output is
40756
+ * FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
40660
40757
  * byte-identical regardless of which fiber settled first. The
40661
40758
  * viewer-permission fiber is joined later, during score-metadata
40662
40759
  * assembly (it feeds score metadata, not diagnostics). The per-element
@@ -40717,12 +40814,12 @@ const runInspect = (input, hooks = {}) => gen(function* () {
40717
40814
  ...checkPnpmHardening(scanDirectory),
40718
40815
  ...checkReactServerComponentsAdvisory(scanDirectory, project),
40719
40816
  ...checkExpoProject(scanDirectory, project),
40720
- ...checkReactNativeProject(scanDirectory, project),
40721
- ...checkSecurityScan(scanDirectory, {
40722
- project,
40723
- ignoredTags: input.ignoredTags
40724
- })
40817
+ ...checkReactNativeProject(scanDirectory, project)
40725
40818
  ])));
40819
+ const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(promise(() => checkSecurityScanCooperative(scanDirectory, {
40820
+ project,
40821
+ ignoredTags: input.ignoredTags
40822
+ })).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
40726
40823
  const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
40727
40824
  const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
40728
40825
  const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
@@ -40858,9 +40955,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
40858
40955
  else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
40859
40956
  const supplyChainResult = yield* join(supplyChainFiber);
40860
40957
  const supplyChainCollected = supplyChainResult.diagnostics;
40958
+ const securityScanCollected = yield* join(securityScanFiber);
40861
40959
  yield* reporterService.finalize;
40862
40960
  const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
40863
40961
  ...envCollected,
40962
+ ...securityScanCollected,
40864
40963
  ...supplyChainCollected,
40865
40964
  ...lintCollected,
40866
40965
  ...deadCodeCollected
@@ -41312,6 +41411,61 @@ const clearAutoSuppressionCaches = () => {};
41312
41411
  * untouched so the loader sees the same plain object either way.
41313
41412
  */
41314
41413
  const defineConfig = (config) => config;
41414
+ const AI_TRAINING_ENV_VARS = [
41415
+ ["HF_DATASETS_CACHE", "huggingface"],
41416
+ ["HF_HOME", "huggingface"],
41417
+ ["HUGGINGFACE_HUB_CACHE", "huggingface"],
41418
+ ["SPACE_ID", "huggingface-spaces"],
41419
+ ["CUDA_VISIBLE_DEVICES", "cuda"],
41420
+ ["NVIDIA_VISIBLE_DEVICES", "nvidia"],
41421
+ ["ROCR_VISIBLE_DEVICES", "rocm"],
41422
+ ["TPU_NAME", "google-tpu"],
41423
+ ["WANDB_RUN_ID", "wandb"],
41424
+ ["WANDB_SWEEP_ID", "wandb"],
41425
+ ["MLFLOW_RUN_ID", "mlflow"],
41426
+ ["MLFLOW_TRACKING_URI", "mlflow"],
41427
+ ["COMET_EXPERIMENT_KEY", "comet"],
41428
+ ["NEPTUNE_RUN_ID", "neptune"],
41429
+ ["CLEARML_TASK_ID", "clearml"],
41430
+ ["DVC_STAGE", "dvc"],
41431
+ ["RAY_WORKER_PROCESS", "ray"],
41432
+ ["RAY_ADDRESS", "ray"],
41433
+ ["MUJOCO_GL", "mujoco"],
41434
+ ["MUJOCO_PATH", "mujoco"],
41435
+ ["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
41436
+ ["SM_TRAINING_ENV", "sagemaker"],
41437
+ ["TRAINING_JOB_ARN", "sagemaker"],
41438
+ ["SAGEMAKER_BASE_DIR", "sagemaker"],
41439
+ ["AZUREML_RUN_ID", "azure-ml"],
41440
+ ["AZURE_ML_MODEL_DIR", "azure-ml"],
41441
+ ["CLOUD_ML_PROJECT_ID", "vertex-ai"],
41442
+ ["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
41443
+ ["DET_MASTER", "determined-ai"],
41444
+ ["LIGHTNING_USER_ID", "lightning-ai"],
41445
+ ["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
41446
+ ["ARGO_WORKFLOW_NAME", "argo-workflows"],
41447
+ ["KFP_POD_NAME", "kubeflow-pipelines"],
41448
+ ["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
41449
+ ["COLAB_BACKEND_VERSION", "google-colab"],
41450
+ ["DATABRICKS_RUNTIME_VERSION", "databricks"],
41451
+ ["DAYTONA_WS_ID", "daytona"],
41452
+ ["DAYTONA_WS_NAME", "daytona"],
41453
+ ["E2B_SANDBOX_ID", "e2b"],
41454
+ ["MODAL_FUNCTION_ID", "modal"],
41455
+ ["MODAL_TASK_ID", "modal"],
41456
+ ["RUNPOD_POD_ID", "runpod"],
41457
+ ["REPLICATE_USERNAME", "replicate"],
41458
+ ["VAST_CONTAINERLABEL", "vast-ai"],
41459
+ ["HARBOR_URL", "harbor"],
41460
+ ["HARBOR_HOSTNAME", "harbor"],
41461
+ ["SWE_BENCH_TASK", "swe-bench"],
41462
+ ["SWEBENCH_TASK", "swe-bench"],
41463
+ ["SWE_AGENT_MODEL", "swe-agent"]
41464
+ ];
41465
+ const detectAiTrainingEnvironment = () => {
41466
+ for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
41467
+ return null;
41468
+ };
41315
41469
  const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
41316
41470
  const mergeIgnores = (baseIgnore, overrideIgnore) => {
41317
41471
  const mergedIgnore = {
@@ -41356,6 +41510,12 @@ const mergeReactDoctorConfigs = (baseConfig, overrideConfig) => {
41356
41510
  };
41357
41511
  //#endregion
41358
41512
  //#region ../api/dist/index.js
41513
+ let didWarnAiTraining = false;
41514
+ const warnIfAiTrainingEnvironment = () => {
41515
+ if (didWarnAiTraining || detectAiTrainingEnvironment() === null) return;
41516
+ didWarnAiTraining = true;
41517
+ console.warn("[react-doctor] Use in an AI or ML pipeline requires written permission under the react-doctor license. Contact founders@million.dev to request access.");
41518
+ };
41359
41519
  const buildDiagnoseLayer = (config, configOverrideTarget) => {
41360
41520
  const configLayer = configOverrideTarget === void 0 ? Config.layerNode : Config.layerOf({
41361
41521
  config,
@@ -41393,6 +41553,7 @@ const outputToDiagnoseResult = (output, elapsedMilliseconds) => {
41393
41553
  };
41394
41554
  };
41395
41555
  const diagnoseDirectory = async (directory, options) => {
41556
+ warnIfAiTrainingEnvironment();
41396
41557
  const startTime = globalThis.performance.now();
41397
41558
  const scanTarget = await resolveScanTarget(directory);
41398
41559
  return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(buildInspectProgram(scanTarget, options).pipe(provide(buildDiagnoseLayer(scanTarget.userConfig)), provide(layerOtlp)))), globalThis.performance.now() - startTime);
@@ -41439,6 +41600,7 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
41439
41600
  }
41440
41601
  };
41441
41602
  const diagnoseProjectBatch = async (input) => {
41603
+ warnIfAiTrainingEnvironment();
41442
41604
  const startTime = globalThis.performance.now();
41443
41605
  const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
41444
41606
  const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig));
@@ -41483,4 +41645,4 @@ const toJsonReport = (result, options) => buildJsonReport({
41483
41645
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
41484
41646
 
41485
41647
  //# sourceMappingURL=index.js.map
41486
- //# debugId=dccb6d63-63d4-5e91-af21-2756a08fe93a
41648
+ //# debugId=9807b1bf-1e62-5421-ad35-d1f4129c1538