@geoql/doctor-core 1.1.0 → 1.1.1

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
@@ -485,6 +485,29 @@ interface ListRulesFilter {
485
485
  }
486
486
  declare function listRules(filter?: ListRulesFilter): RegisteredRule[];
487
487
  //#endregion
488
+ //#region src/audit-filter.d.ts
489
+ /**
490
+ * Filter `report.diagnostics` to a known set of allowed rule ids, then
491
+ * recompute `score`, `errorCount`, `warnCount`, `infoCount`, `scoreResult`,
492
+ * `ruleCounts`, and `exitCode` from the filtered array.
493
+ *
494
+ * Why: the CLI layer calls `audit()` and then applies its own
495
+ * `allowedRuleIds` filter to drop suppressed (`off`) and preset-excluded
496
+ * (`info` rules not in `recommended`) diagnostics. The audit's own
497
+ * `score`/`counts` are computed BEFORE the CLI filter, so they still
498
+ * include the suppressed findings. That desync surfaces to consumers
499
+ * (the SaaS `--push` payload, the dashboard's counts-but-no-findings bug)
500
+ * and to the `score` itself (suppressed findings still penalize).
501
+ *
502
+ * Invariant enforced here:
503
+ * report.errorCount + report.warnCount + report.infoCount === report.diagnostics.length
504
+ * report.scoreResult.totalFindings === report.diagnostics.length
505
+ *
506
+ * `exitCode=2` (oxlint crash) is preserved verbatim; `exitCode=1` is
507
+ * recomputed from the surviving counts + the `failOn` gate.
508
+ */
509
+ declare function filterReportByRules(report: AuditReport, allowedRuleIds: ReadonlySet<string>, failOn?: 'error' | 'warn' | 'none', oxlintStderr?: string): AuditReport;
510
+ //#endregion
488
511
  //#region src/push.d.ts
489
512
  /**
490
513
  * The 6 fields the SaaS receives for each finding.
@@ -519,6 +542,8 @@ interface PushOptions {
519
542
  warnCount: number;
520
543
  infoCount: number;
521
544
  diagnostics: Diagnostic[];
545
+ /** Project root used to relativize absolute finding file paths. */
546
+ rootDir?: string;
522
547
  url: string;
523
548
  apiKey: string;
524
549
  fetchImpl?: typeof fetch;
@@ -538,8 +563,11 @@ type PushResult = {
538
563
  * Strip a list of Diagnostics down to the 6 allowed push fields.
539
564
  * Privacy boundary: this function is the single point of enforcement.
540
565
  * If you add a new privacy-sensitive field to Diagnostic, add it to the deny list here.
566
+ *
567
+ * Absolute file paths are relativized against rootDir so dashboards show
568
+ * `src/Foo.vue` rather than the CI runner's `/home/runner/work/<repo>/…`.
541
569
  */
542
- declare function stripFindings(diagnostics: Diagnostic[]): PushedFinding[];
570
+ declare function stripFindings(diagnostics: Diagnostic[], rootDir?: string): PushedFinding[];
543
571
  /**
544
572
  * Build the top-level POST body. CI fields (commitSha, branch, prNumber) are
545
573
  * included ONLY when the matching GITHUB_* env var is set.
@@ -552,5 +580,5 @@ declare function buildPushPayload(input: PushPayloadInput): PushPayload;
552
580
  */
553
581
  declare function pushFindings(opts: PushOptions): Promise<PushResult>;
554
582
  //#endregion
555
- export { type ApplyInlineDisablesOptions, type AuditConfig, type AuditReport, type AuditTimings, BUILT_IN_RECOMMENDED, type Capability, type CliOverrides, ConfigCycleError, ConfigFileNotFoundError, type ConfigSource, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, type Diagnostic, type DiagnosticSource, type DirectiveLine, type DirectiveRange, type DirectiveSet, type DoctorReport, type DoctorUserConfig, type Framework, type GitScopeMode, type InitConfigFormat, type InitFileWrite, type InitOptions, type InitPlan, type InitPreset, InvalidConfigError, type ListRulesFilter, type LoadRuleDocOptions, type MonorepoKind, type MonorepoResult, OxlintOutputTooLarge, OxlintSpawnFailed, type ProjectInfo, type ProjectInfoLite, type PushOptions, type PushPayload, type PushPayloadInput, type PushResult, type PushedFinding, RULE_REGISTRY, type RawInitAnswers, type RegisteredRule, type ReporterFormat, type ReporterInput, type ReporterOptions, type ResolvedDoctorConfig, type ResolvedInitAnswers, type RuleCategory, type RuleDoc, type RuleSource, type ScoreBreakdownEntry, type ScoreConfig, type ScoreResult, type Severity, type VerboseTraceOptions, type WorkspacePackage, agentReport, applyInlineDisables, audit, buildDoctorReport, buildPushPayload, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, docsUrl, encodeAnnotation, encodeAnnotations, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderDetectSummary, renderVerboseTrace, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
583
+ export { type ApplyInlineDisablesOptions, type AuditConfig, type AuditReport, type AuditTimings, BUILT_IN_RECOMMENDED, type Capability, type CliOverrides, ConfigCycleError, ConfigFileNotFoundError, type ConfigSource, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, type Diagnostic, type DiagnosticSource, type DirectiveLine, type DirectiveRange, type DirectiveSet, type DoctorReport, type DoctorUserConfig, type Framework, type GitScopeMode, type InitConfigFormat, type InitFileWrite, type InitOptions, type InitPlan, type InitPreset, InvalidConfigError, type ListRulesFilter, type LoadRuleDocOptions, type MonorepoKind, type MonorepoResult, OxlintOutputTooLarge, OxlintSpawnFailed, type ProjectInfo, type ProjectInfoLite, type PushOptions, type PushPayload, type PushPayloadInput, type PushResult, type PushedFinding, RULE_REGISTRY, type RawInitAnswers, type RegisteredRule, type ReporterFormat, type ReporterInput, type ReporterOptions, type ResolvedDoctorConfig, type ResolvedInitAnswers, type RuleCategory, type RuleDoc, type RuleSource, type ScoreBreakdownEntry, type ScoreConfig, type ScoreResult, type Severity, type VerboseTraceOptions, type WorkspacePackage, agentReport, applyInlineDisables, audit, buildDoctorReport, buildPushPayload, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderDetectSummary, renderVerboseTrace, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
556
584
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as listRules, c as InvalidConfigError, i as RULE_REGISTRY, l as BUILT_IN_RECOMMENDED, o as ConfigCycleError, r as validateConfig, s as ConfigFileNotFoundError, t as loadDoctorConfig } from "./load-yXDi-5ti.js";
2
- import { dirname, join, relative, resolve } from "node:path";
2
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { performance } from "node:perf_hooks";
4
4
  import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
5
  import { createRequire } from "node:module";
@@ -3011,7 +3011,7 @@ async function listWorkspacePackages(rootDir) {
3011
3011
  //#endregion
3012
3012
  //#region src/reporters/docs-url.ts
3013
3013
  function docsUrl(ruleId) {
3014
- return `https://docs.doctor.geoql.in/rules/${ruleId}`;
3014
+ return `https://docs.the-doctor.report/rules/${ruleId}`;
3015
3015
  }
3016
3016
  //#endregion
3017
3017
  //#region src/reporters/render.ts
@@ -3374,7 +3374,7 @@ function jsonCompactReport(input) {
3374
3374
  }
3375
3375
  //#endregion
3376
3376
  //#region src/reporters/pr-comment.ts
3377
- const DOCS_BASE = "https://docs.doctor.geoql.in/rules/";
3377
+ const DOCS_BASE = "https://docs.the-doctor.report/rules/";
3378
3378
  const MAX_PER_SECTION = 5;
3379
3379
  function binName(toolName) {
3380
3380
  return toolName.replace("@geoql/", "");
@@ -3474,23 +3474,76 @@ function format(input, kind = "agent", options) {
3474
3474
  return agentReport(input, options);
3475
3475
  }
3476
3476
  //#endregion
3477
+ //#region src/audit-filter.ts
3478
+ /**
3479
+ * Filter `report.diagnostics` to a known set of allowed rule ids, then
3480
+ * recompute `score`, `errorCount`, `warnCount`, `infoCount`, `scoreResult`,
3481
+ * `ruleCounts`, and `exitCode` from the filtered array.
3482
+ *
3483
+ * Why: the CLI layer calls `audit()` and then applies its own
3484
+ * `allowedRuleIds` filter to drop suppressed (`off`) and preset-excluded
3485
+ * (`info` rules not in `recommended`) diagnostics. The audit's own
3486
+ * `score`/`counts` are computed BEFORE the CLI filter, so they still
3487
+ * include the suppressed findings. That desync surfaces to consumers
3488
+ * (the SaaS `--push` payload, the dashboard's counts-but-no-findings bug)
3489
+ * and to the `score` itself (suppressed findings still penalize).
3490
+ *
3491
+ * Invariant enforced here:
3492
+ * report.errorCount + report.warnCount + report.infoCount === report.diagnostics.length
3493
+ * report.scoreResult.totalFindings === report.diagnostics.length
3494
+ *
3495
+ * `exitCode=2` (oxlint crash) is preserved verbatim; `exitCode=1` is
3496
+ * recomputed from the surviving counts + the `failOn` gate.
3497
+ */
3498
+ function filterReportByRules(report, allowedRuleIds, failOn = "error", oxlintStderr) {
3499
+ const filtered = [];
3500
+ for (const d of report.diagnostics) if (allowedRuleIds.has(d.ruleId)) filtered.push(d);
3501
+ const threshold = report.scoreResult.threshold;
3502
+ const scoreResult = scoreDiagnostics(filtered, { threshold });
3503
+ const ruleCounts = {};
3504
+ for (const d of filtered) ruleCounts[d.ruleId] = (ruleCounts[d.ruleId] ?? 0) + 1;
3505
+ let exitCode = report.exitCode;
3506
+ if (exitCode !== 2) if (failOn === "none") exitCode = 0;
3507
+ else exitCode = (failOn === "warn" ? scoreResult.errorCount + scoreResult.warnCount : scoreResult.errorCount) > 0 ? 1 : 0;
3508
+ return {
3509
+ ...report,
3510
+ diagnostics: filtered,
3511
+ score: scoreResult.score,
3512
+ errorCount: scoreResult.errorCount,
3513
+ warnCount: scoreResult.warnCount,
3514
+ infoCount: scoreResult.infoCount,
3515
+ scoreResult,
3516
+ ruleCounts,
3517
+ exitCode
3518
+ };
3519
+ }
3520
+ //#endregion
3477
3521
  //#region src/push.ts
3478
3522
  const PUSH_TIMEOUT_MS = 5e3;
3479
3523
  const categoryByRuleId = new Map(RULE_REGISTRY.map((r) => [r.id, r.category]));
3480
3524
  function lookupCategory(ruleId) {
3481
3525
  return categoryByRuleId.get(ruleId) ?? "unknown";
3482
3526
  }
3527
+ function toRepoRelative(file, rootDir) {
3528
+ if (!rootDir || !isAbsolute(file)) return file;
3529
+ const rel = relative(rootDir, file);
3530
+ if (rel.startsWith("..")) return file;
3531
+ return rel;
3532
+ }
3483
3533
  /**
3484
3534
  * Strip a list of Diagnostics down to the 6 allowed push fields.
3485
3535
  * Privacy boundary: this function is the single point of enforcement.
3486
3536
  * If you add a new privacy-sensitive field to Diagnostic, add it to the deny list here.
3537
+ *
3538
+ * Absolute file paths are relativized against rootDir so dashboards show
3539
+ * `src/Foo.vue` rather than the CI runner's `/home/runner/work/<repo>/…`.
3487
3540
  */
3488
- function stripFindings(diagnostics) {
3541
+ function stripFindings(diagnostics, rootDir) {
3489
3542
  const out = new Array(diagnostics.length);
3490
3543
  for (let i = 0; i < diagnostics.length; i++) {
3491
3544
  const d = diagnostics[i];
3492
3545
  out[i] = {
3493
- file: d.file,
3546
+ file: toRepoRelative(d.file, rootDir),
3494
3547
  line: d.line,
3495
3548
  col: d.column,
3496
3549
  ruleId: d.ruleId,
@@ -3537,7 +3590,7 @@ function buildPushPayload(input) {
3537
3590
  * exit code is not affected by SaaS downtime.
3538
3591
  */
3539
3592
  async function pushFindings(opts) {
3540
- const findings = stripFindings(opts.diagnostics);
3593
+ const findings = stripFindings(opts.diagnostics, opts.rootDir);
3541
3594
  const payload = buildPushPayload({
3542
3595
  project: opts.project,
3543
3596
  score: opts.score,
@@ -3580,6 +3633,6 @@ async function pushFindings(opts) {
3580
3633
  }
3581
3634
  }
3582
3635
  //#endregion
3583
- export { BUILT_IN_RECOMMENDED, ConfigCycleError, ConfigFileNotFoundError, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, InvalidConfigError, OxlintOutputTooLarge, OxlintSpawnFailed, RULE_REGISTRY, agentReport, applyInlineDisables, audit, buildDoctorReport, buildPushPayload, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, docsUrl, encodeAnnotation, encodeAnnotations, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderDetectSummary, renderVerboseTrace, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
3636
+ export { BUILT_IN_RECOMMENDED, ConfigCycleError, ConfigFileNotFoundError, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, InvalidConfigError, OxlintOutputTooLarge, OxlintSpawnFailed, RULE_REGISTRY, agentReport, applyInlineDisables, audit, buildDoctorReport, buildPushPayload, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderDetectSummary, renderVerboseTrace, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
3584
3637
 
3585
3638
  //# sourceMappingURL=index.js.map