@geoql/doctor-core 1.1.0 → 1.2.0

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
@@ -1,3 +1,5 @@
1
+ import { z } from "zod";
2
+
1
3
  //#region src/score.d.ts
2
4
  interface ScoreBreakdownEntry {
3
5
  ruleId: string;
@@ -139,46 +141,102 @@ declare function checkBuildQuality(projectInfo: ProjectInfo): Promise<Diagnostic
139
141
  //#region src/check-deps.d.ts
140
142
  declare function checkDeps(projectInfo: ProjectInfo): Promise<Diagnostic[]>;
141
143
  //#endregion
144
+ //#region src/config/schema.d.ts
145
+ /**
146
+ * A rule severity level as accepted in user config. `off` disables a rule
147
+ * (removing it from the resolved preset base); the other three mirror the
148
+ * internal {@link import('../types.js').Severity} vocabulary.
149
+ */
150
+ declare const LevelSchema: z.ZodEnum<{
151
+ error: "error";
152
+ warn: "warn";
153
+ info: "info";
154
+ off: "off";
155
+ }>;
156
+ /**
157
+ * The accepted shape of a single `rules` entry. Doctor config only ever takes
158
+ * a bare severity string (no `[level, options]` tuple, no `{ level }` object) —
159
+ * this matches the historical hand-rolled validator exactly.
160
+ */
161
+ declare const RuleEntrySchema: z.ZodEnum<{
162
+ error: "error";
163
+ warn: "warn";
164
+ info: "info";
165
+ off: "off";
166
+ }>;
167
+ /**
168
+ * The single source of truth for doctor config validation. Wraps the base
169
+ * object in a `superRefine` that rejects any unknown rule id with a
170
+ * `rules.<id>` path, so a typo'd rule surfaces as an `InvalidConfigError`
171
+ * rather than silently doing nothing.
172
+ */
173
+ declare const DoctorUserConfigSchema: z.ZodObject<{
174
+ $schema: z.ZodOptional<z.ZodString>;
175
+ rootDir: z.ZodOptional<z.ZodString>;
176
+ include: z.ZodOptional<z.ZodArray<z.ZodString>>;
177
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
178
+ failOn: z.ZodOptional<z.ZodEnum<{
179
+ error: "error";
180
+ warn: "warn";
181
+ none: "none";
182
+ }>>;
183
+ threshold: z.ZodOptional<z.ZodInt>;
184
+ preset: z.ZodOptional<z.ZodEnum<{
185
+ minimal: "minimal";
186
+ recommended: "recommended";
187
+ strict: "strict";
188
+ all: "all";
189
+ }>>;
190
+ extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
191
+ fixExcludes: z.ZodOptional<z.ZodArray<z.ZodString>>;
192
+ rules: z.ZodOptional<z.ZodObject<{
193
+ [x: string]: z.ZodOptional<z.ZodEnum<{
194
+ error: "error";
195
+ warn: "warn";
196
+ info: "info";
197
+ off: "off";
198
+ }>>;
199
+ }, z.core.$catchall<z.ZodOptional<z.ZodEnum<{
200
+ error: "error";
201
+ warn: "warn";
202
+ info: "info";
203
+ off: "off";
204
+ }>>>>>;
205
+ }, z.core.$loose>;
206
+ /** The inferred user-config type — the single source of truth for the shape. */
207
+ type DoctorUserConfigInput = z.infer<typeof DoctorUserConfigSchema>;
208
+ /**
209
+ * Generate the JSON Schema for the user config. Built from the base object
210
+ * (not the refined wrapper) so `z.toJSONSchema` emits the full property set,
211
+ * including every enumerated rule id. The result is what gets written to
212
+ * `schema.json` and served from the docs site.
213
+ */
214
+ declare function buildJsonSchema(): Record<string, unknown>;
215
+ //#endregion
142
216
  //#region src/config/types.d.ts
143
- type ConfigSource = 'flag' | 'ts' | 'mjs' | 'js' | 'json' | 'package.json' | 'built-in';
144
- interface DoctorUserConfig {
145
- rootDir?: string;
146
- include?: string[];
147
- exclude?: string[];
148
- failOn?: 'error' | 'warn' | 'none';
149
- threshold?: number;
150
- preset?: string;
151
- rules?: Record<string, Severity | 'off'>;
152
- extends?: string[];
153
- fixExcludes?: string[];
154
- }
155
- interface ResolvedDoctorConfig {
156
- rootDir: string;
157
- include: string[];
158
- exclude: string[];
159
- failOn: 'error' | 'warn' | 'none';
160
- threshold: number;
161
- rules: Record<string, Severity>;
162
- preset: 'minimal' | 'recommended' | 'strict' | 'all';
163
- source: ConfigSource;
164
- configFile?: string;
165
- fixExcludes?: string[];
166
- }
217
+ type ConfigSource = 'flag' | 'ts' | 'mjs' | 'js' | 'json' | 'jsonc' | 'package.json' | 'built-in';
218
+ /**
219
+ * User-authored config shape, inferred from {@link DoctorUserConfigSchema} so
220
+ * the zod schema is the single source of truth for the accepted fields.
221
+ */
222
+ type DoctorUserConfig = DoctorUserConfigInput;
167
223
  /**
168
- * Resolved + normalized config that audit consumes.
169
- * - `rules` is the merged effective severity map (preset base + user overrides - explicit offs).
170
- * - `preset` is the preset name that was applied as the base.
224
+ * Resolved + normalized config that audit consumes. `failOn` and `preset` are
225
+ * narrowed from the schema-derived user shape; `rules` is the merged effective
226
+ * severity map (preset base + user overrides minus explicit offs), so it never
227
+ * carries `'off'`.
171
228
  */
172
229
  interface ResolvedDoctorConfig {
173
230
  rootDir: string;
174
231
  include: string[];
175
232
  exclude: string[];
176
- failOn: 'error' | 'warn' | 'none';
233
+ failOn: NonNullable<DoctorUserConfig['failOn']>;
177
234
  threshold: number;
178
235
  rules: Record<string, Severity>;
179
- preset: 'minimal' | 'recommended' | 'strict' | 'all';
236
+ preset: NonNullable<DoctorUserConfig['preset']>;
180
237
  source: ConfigSource;
181
238
  configFile?: string;
239
+ fixExcludes?: string[];
182
240
  }
183
241
  //#endregion
184
242
  //#region src/dead-code/dedupe.d.ts
@@ -231,6 +289,8 @@ declare class ConfigCycleError extends Error {
231
289
  }
232
290
  declare class InvalidConfigError extends Error {
233
291
  name: "InvalidConfigError";
292
+ readonly issues: readonly z.core.$ZodIssue[];
293
+ constructor(message: string, issues?: readonly z.core.$ZodIssue[]);
234
294
  }
235
295
  //#endregion
236
296
  //#region src/config/define-config.d.ts
@@ -244,6 +304,12 @@ interface LoadDoctorConfigOptions {
244
304
  declare function loadDoctorConfig(rootDir: string, explicitPathOrOptions?: string | LoadDoctorConfigOptions): Promise<ResolvedDoctorConfig>;
245
305
  //#endregion
246
306
  //#region src/config/validate.d.ts
307
+ /**
308
+ * Validate a raw, untrusted config object against the zod schema — the single
309
+ * source of truth. Throws {@link InvalidConfigError} (carrying the full zod
310
+ * issue list) on the first failure, mirroring the legacy validator's
311
+ * throw-on-first-error contract.
312
+ */
247
313
  declare function validateConfig(raw: unknown): void;
248
314
  //#endregion
249
315
  //#region src/detect-project.d.ts
@@ -466,8 +532,22 @@ interface LoadRuleDocOptions {
466
532
  declare function loadRuleDoc(ruleId: string, options?: LoadRuleDocOptions): RuleDoc | null;
467
533
  declare function loadAllRuleDocs(options?: LoadRuleDocOptions): RuleDoc[];
468
534
  //#endregion
535
+ //#region src/rule-prompt.d.ts
536
+ /**
537
+ * Render a self-contained "fix + validate" markdown recipe for a single rule.
538
+ * Returns `null` for an unknown rule id. Pure: derives everything from the
539
+ * rule's `RuleDoc` (id, severity, category, source, recommended, helpUri).
540
+ */
541
+ declare function renderRulePrompt(ruleId: string): string | null;
542
+ /**
543
+ * Render the canonical agent playbook: the scan → filter → triage → fix →
544
+ * validate loop an AI agent follows when cleaning up doctor diagnostics. Pure
545
+ * markdown; fetched on demand by the agent skill at runtime.
546
+ */
547
+ declare function renderAgentPlaybook(): string;
548
+ //#endregion
469
549
  //#region src/rule-registry.d.ts
470
- type RuleCategory = 'ai-slop' | 'reactivity' | 'composition' | 'performance' | 'template' | 'template-perf' | 'build-quality' | 'deps' | 'dead-code' | 'sfc' | 'vue-builtin' | 'structure' | 'modules-deps' | 'nitro' | 'seo' | 'cloudflare' | 'server-routes' | 'hydration' | 'data-fetching';
550
+ type RuleCategory = 'ai-slop' | 'reactivity' | 'composition' | 'performance' | 'template' | 'template-perf' | 'build-quality' | 'deps' | 'dead-code' | 'sfc' | 'vue-builtin' | 'structure' | 'modules-deps' | 'nitro' | 'seo' | 'cloudflare' | 'server-routes' | 'hydration' | 'data-fetching' | 'security' | 'design';
471
551
  type RuleSource = 'doctor' | 'oxlint-builtin' | 'eslint-plugin-vue';
472
552
  interface RegisteredRule {
473
553
  readonly id: string;
@@ -485,6 +565,29 @@ interface ListRulesFilter {
485
565
  }
486
566
  declare function listRules(filter?: ListRulesFilter): RegisteredRule[];
487
567
  //#endregion
568
+ //#region src/audit-filter.d.ts
569
+ /**
570
+ * Filter `report.diagnostics` to a known set of allowed rule ids, then
571
+ * recompute `score`, `errorCount`, `warnCount`, `infoCount`, `scoreResult`,
572
+ * `ruleCounts`, and `exitCode` from the filtered array.
573
+ *
574
+ * Why: the CLI layer calls `audit()` and then applies its own
575
+ * `allowedRuleIds` filter to drop suppressed (`off`) and preset-excluded
576
+ * (`info` rules not in `recommended`) diagnostics. The audit's own
577
+ * `score`/`counts` are computed BEFORE the CLI filter, so they still
578
+ * include the suppressed findings. That desync surfaces to consumers
579
+ * (the SaaS `--push` payload, the dashboard's counts-but-no-findings bug)
580
+ * and to the `score` itself (suppressed findings still penalize).
581
+ *
582
+ * Invariant enforced here:
583
+ * report.errorCount + report.warnCount + report.infoCount === report.diagnostics.length
584
+ * report.scoreResult.totalFindings === report.diagnostics.length
585
+ *
586
+ * `exitCode=2` (oxlint crash) is preserved verbatim; `exitCode=1` is
587
+ * recomputed from the surviving counts + the `failOn` gate.
588
+ */
589
+ declare function filterReportByRules(report: AuditReport, allowedRuleIds: ReadonlySet<string>, failOn?: 'error' | 'warn' | 'none', oxlintStderr?: string): AuditReport;
590
+ //#endregion
488
591
  //#region src/push.d.ts
489
592
  /**
490
593
  * The 6 fields the SaaS receives for each finding.
@@ -519,6 +622,8 @@ interface PushOptions {
519
622
  warnCount: number;
520
623
  infoCount: number;
521
624
  diagnostics: Diagnostic[];
625
+ /** Project root used to relativize absolute finding file paths. */
626
+ rootDir?: string;
522
627
  url: string;
523
628
  apiKey: string;
524
629
  fetchImpl?: typeof fetch;
@@ -538,8 +643,11 @@ type PushResult = {
538
643
  * Strip a list of Diagnostics down to the 6 allowed push fields.
539
644
  * Privacy boundary: this function is the single point of enforcement.
540
645
  * If you add a new privacy-sensitive field to Diagnostic, add it to the deny list here.
646
+ *
647
+ * Absolute file paths are relativized against rootDir so dashboards show
648
+ * `src/Foo.vue` rather than the CI runner's `/home/runner/work/<repo>/…`.
541
649
  */
542
- declare function stripFindings(diagnostics: Diagnostic[]): PushedFinding[];
650
+ declare function stripFindings(diagnostics: Diagnostic[], rootDir?: string): PushedFinding[];
543
651
  /**
544
652
  * Build the top-level POST body. CI fields (commitSha, branch, prNumber) are
545
653
  * included ONLY when the matching GITHUB_* env var is set.
@@ -552,5 +660,5 @@ declare function buildPushPayload(input: PushPayloadInput): PushPayload;
552
660
  */
553
661
  declare function pushFindings(opts: PushOptions): Promise<PushResult>;
554
662
  //#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 };
663
+ 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 DoctorUserConfigInput, DoctorUserConfigSchema, type Framework, type GitScopeMode, type InitConfigFormat, type InitFileWrite, type InitOptions, type InitPlan, type InitPreset, InvalidConfigError, LevelSchema, 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, RuleEntrySchema, type RuleSource, type ScoreBreakdownEntry, type ScoreConfig, type ScoreResult, type Severity, type VerboseTraceOptions, type WorkspacePackage, agentReport, applyInlineDisables, audit, buildDoctorReport, buildJsonSchema, 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, renderAgentPlaybook, renderDetectSummary, renderRulePrompt, renderVerboseTrace, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
556
664
  //# sourceMappingURL=index.d.ts.map