@svelte-vitals/core 0.28.0 → 0.30.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +337 -23
  2. package/dist/index.js +910 -135
  3. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -36,6 +36,17 @@ interface Project {
36
36
  file?: string;
37
37
  line?: number;
38
38
  };
39
+ /**
40
+ * Set when the project configures a non-empty `kit.paths.base` — read from the `sveltekit()`
41
+ * Vite plugin config, else `svelte.config.{js,ts}` (correctness/base-path-navigation).
42
+ * `value` is the literal base when statically resolvable, unset when the config computes it
43
+ * (e.g. `dev ? '' : '/repo'`). `file` is the config path relative to the analyzed root (posix).
44
+ * Absent means the app is served at the root — the rule stays silent.
45
+ */
46
+ kitPathsBase?: {
47
+ value?: string;
48
+ file: string;
49
+ };
39
50
  }
40
51
  declare const defaultProject: Project;
41
52
  /** A concrete, agent-actionable remediation for a finding (design §10, issue #18). */
@@ -69,10 +80,29 @@ interface Result {
69
80
  }
70
81
  type Scope = 'route' | 'project' | 'component';
71
82
  type Category = 'seo' | 'performance' | 'correctness' | 'security' | 'architecture';
83
+ /**
84
+ * Every category, as a runtime list — for validating a user-supplied category
85
+ * name and naming the known ones in the error. One definition so a category
86
+ * added to `Category` can't be accepted by one validator and rejected by
87
+ * another. Not an ordering: reporters keep their own display order.
88
+ */
89
+ declare const CATEGORIES: readonly Category[];
72
90
  /** How dynamic (`{data.title}`) values are treated by scoring (design §4, §12). */
73
91
  type TreatDynamicAs = 'pass' | 'warn' | 'fail';
74
- /** Per-rule override: disable, or change severity. */
75
- type RuleSetting = 'off' | Severity;
92
+ /** Resolved option values handed to a rule at check time. */
93
+ type RuleOptions = Record<string, unknown>;
94
+ /**
95
+ * Object form of a rule setting. `severity` omitted keeps the rule's built-in
96
+ * severity — the common case when only a threshold is being moved.
97
+ * `{ severity: 'off', … }` disables the rule and any `options` beside it are
98
+ * inert (equivalent to the bare `'off'` string, not an error).
99
+ */
100
+ interface RuleSettingObject {
101
+ severity?: Severity | 'off';
102
+ options?: RuleOptions;
103
+ }
104
+ /** Per-rule override: disable, change severity, and/or set options. */
105
+ type RuleSetting = 'off' | Severity | RuleSettingObject;
76
106
  /**
77
107
  * Scoped rule override (design 2026-07-18), applied to results after analysis.
78
108
  * An entry matches a finding when any `route` glob matches its route id or any
@@ -297,6 +327,26 @@ interface SuppressionDirective {
297
327
  /** Rule ids suppressed on that line; undefined = suppress every rule on that line. */
298
328
  ruleIds?: string[];
299
329
  }
330
+ /** An `<input type="checkbox">` / `<input type="radio">` element carrying a `bind:value`
331
+ * directive — `bind:value` observes the DOM `value` property, which checkbox/radio
332
+ * interaction never changes, so the bound state silently never updates
333
+ * (correctness/checkable-bind-value). */
334
+ interface CheckableBindValueFact {
335
+ /** Which checkable input type was flagged — selects the message wording. */
336
+ kind: 'checkbox' | 'radio';
337
+ /** 1-based source line, or 0 if unknown. */
338
+ line: number;
339
+ }
340
+ /** A root-relative navigation literal — broken when the app is served under `kit.paths.base`
341
+ * (correctness/base-path-navigation). Shared by the component and Kit-module channels. */
342
+ interface BasePathLinkFact {
343
+ /** Which navigation surface it was written on — selects the message wording. */
344
+ kind: 'href' | 'goto' | 'redirect';
345
+ /** The literal path as written, e.g. '/about'. */
346
+ path: string;
347
+ /** 1-based source line, or 0 if unknown. */
348
+ line: number;
349
+ }
300
350
  /** Reactivity/correctness + security + architecture facts parsed from one `.svelte` component. */
301
351
  interface ComponentFacts {
302
352
  /** Source file the component came from. */
@@ -328,21 +378,34 @@ interface ComponentFacts {
328
378
  name: string;
329
379
  line: number;
330
380
  }[];
331
- /** Mutations of a non-`$bindable` prop from `$props()` — member writes, `delete`, or a mutating method call (correctness/prop-mutation). */
381
+ /** Mutations of a non-`$bindable` prop from `$props()`, or a legacy `export let` prop — member writes, `delete`, or a mutating method call (correctness/prop-mutation). `legacy` distinguishes which mode the prop was declared in (absent/false: `$props()`), since the fix differs — optional so existing external constructors of `ComponentFacts` are unaffected. */
332
382
  mutatedProps: {
333
383
  name: string;
334
384
  line: number;
385
+ legacy?: boolean;
335
386
  }[];
336
- /** Top-level const/let bindings computed from a $props() prop without $derived, never reassigned or escaped, and referenced (eagerly) in the template — frozen at init (correctness/stale-prop-derivation). */
387
+ /** Top-level const/let bindings computed from a $props() or legacy `export let` prop without $derived (or `$:`), never reassigned or escaped, and referenced (eagerly) in the template — frozen at init (correctness/stale-prop-derivation). `legacy` distinguishes which mode the prop was declared in, since the fix differs — optional so existing external constructors of `ComponentFacts` are unaffected. */
337
388
  stalePropDerivations: {
338
389
  name: string;
339
390
  line: number;
391
+ legacy?: boolean;
340
392
  }[];
341
393
  /** Object/array-literal $state bindings reassigned at least once but never mutated, escaped, aliased, or item-edited — $state.raw candidates (performance/state-raw). */
342
394
  rawableStates: {
343
395
  name: string;
344
396
  line: number;
345
397
  }[];
398
+ /** Plain built-in instances (Map/Set/Date/URL/URLSearchParams) in $state whose type-specific mutations were observed inside functions, with no exempting reassignment — untracked by reactivity (correctness/nonreactive-builtin-state). */
399
+ nonreactiveBuiltinStates: {
400
+ name: string;
401
+ type: string;
402
+ line: number;
403
+ }[];
404
+ /** `<input type="checkbox">` / `<input type="radio">` elements bound with `bind:value`
405
+ * instead of `bind:checked`/`bind:group` (correctness/checkable-bind-value). */
406
+ checkableBindValues: CheckableBindValueFact[];
407
+ /** Root-relative `<a href>` and `goto()` literals in this component (correctness/base-path-navigation). */
408
+ basePathLinks: BasePathLinkFact[];
346
409
  /** `$effect` calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-effect). */
347
410
  orphanEffects: OrphanEffectFact[];
348
411
  /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-lifecycle). */
@@ -430,6 +493,8 @@ interface KitModuleFacts {
430
493
  line: number;
431
494
  inHandler: boolean;
432
495
  }[];
496
+ /** Root-relative `redirect()` literals in this Kit module (correctness/base-path-navigation). */
497
+ basePathLinks: BasePathLinkFact[];
433
498
  /** Set when this file disables SSR via `export const ssr = false` (inline or same-file alias export) — the declaration's line (seo/ssr-disabled). */
434
499
  ssrDisabled?: {
435
500
  line: number;
@@ -483,6 +548,46 @@ declare function findMinifyDisabled(source: string): {
483
548
  line: number;
484
549
  } | undefined;
485
550
 
551
+ /** What a Vite config says about SvelteKit's own configuration. */
552
+ type ViteKitConfigResult =
553
+ /** No `sveltekit()` call, or one with no argument — `svelte.config` still applies. */
554
+ {
555
+ kind: 'no-plugin-config';
556
+ }
557
+ /** `sveltekit(<something we can't resolve>)` — the effective config is unknowable AND
558
+ * `svelte.config` is provably ignored, so the caller must stay quiet. */
559
+ | {
560
+ kind: 'unresolvable';
561
+ }
562
+ /** `sveltekit({…})` resolved. `base` is unset when the config declares no non-empty base. */
563
+ | {
564
+ kind: 'resolved';
565
+ base?: {
566
+ value?: string;
567
+ };
568
+ };
569
+ /** `kit.paths.base` from a `svelte.config.{js,ts}` source. */
570
+ declare function findKitPathsBaseInSvelteConfig(source: string): {
571
+ value?: string;
572
+ } | undefined;
573
+ /** SvelteKit config passed to the `sveltekit()` plugin in a Vite config source (since Kit 2.62). */
574
+ declare function findKitPathsBaseInViteConfig(source: string): ViteKitConfigResult;
575
+ /**
576
+ * The project's effective `kit.paths.base`, following SvelteKit's precedence: the `sveltekit()`
577
+ * plugin config when it carries one, otherwise `svelte.config`. `file` is the config the base
578
+ * came from (as passed in by the caller). Undefined means "no base path" — the gate stays shut.
579
+ */
580
+ declare function resolveKitPathsBase(viteConfig: {
581
+ file: string;
582
+ source: string;
583
+ } | undefined, svelteConfig: {
584
+ file: string;
585
+ source: string;
586
+ } | undefined): {
587
+ value?: string;
588
+ file: string;
589
+ } | undefined;
590
+
486
591
  /** A template fragment's child node relevant to value classification: literal text or a `{expr}`. */
487
592
  type TextOrExpr = AST.Text | AST.ExpressionTag;
488
593
  /**
@@ -521,6 +626,167 @@ declare function attrTextOf(attr: AST.Attribute): string | undefined;
521
626
  declare const ROBOTS_SOURCE_PATHS: readonly ["static/robots.txt", "src/routes/robots.txt/+server.ts", "src/routes/robots.txt/+server.js"];
522
627
  /** Locations that satisfy the sitemap.xml project rule (seo/sitemap-xml). */
523
628
  declare const SITEMAP_SOURCE_PATHS: readonly ["static/sitemap.xml", "src/routes/sitemap.xml/+server.ts", "src/routes/sitemap.xml/+server.js"];
629
+ /** Vite's own config resolution order — only the first existing file is the one Vite loads. */
630
+ declare const VITE_CONFIG_FILES: readonly ["vite.config.js", "vite.config.mjs", "vite.config.ts", "vite.config.cjs", "vite.config.mts", "vite.config.cts"];
631
+ /** SvelteKit's config resolution order (`@sveltejs/kit` checks js before ts). */
632
+ declare const SVELTE_CONFIG_FILES: readonly ["svelte.config.js", "svelte.config.ts"];
633
+
634
+ /** The severity a setting selects: `'off'`, an explicit severity, or undefined (leave the built-in). */
635
+ declare function settingSeverity(setting: RuleSetting | undefined): Severity | 'off' | undefined;
636
+ /** The options a setting carries, or undefined for the string forms. */
637
+ declare function settingOptions(setting: RuleSetting | undefined): RuleOptions | undefined;
638
+ /** Drop rules disabled via config (design §6). */
639
+ declare function selectRules(rules: Rule[], config: Config): Rule[];
640
+ /** Apply per-rule severity overrides to results (design §6). */
641
+ declare function applyRuleSeverities(results: Result[], config: Config): Result[];
642
+ /** An override entry with its globs compiled once. Build with `compileOverrides`. */
643
+ interface CompiledOverride {
644
+ routes: RegExp[];
645
+ files: RegExp[];
646
+ rules: Record<string, RuleSetting>;
647
+ }
648
+ /**
649
+ * Compile every override entry's globs to RegExp, once. Callers that match many
650
+ * targets (every component, every route) must hoist this out of their loop.
651
+ */
652
+ declare function compileOverrides(config: Config): CompiledOverride[];
653
+ /**
654
+ * Whether an override entry applies to a target. THE single definition of that
655
+ * question — the result post-pass and in-run option resolution both call it.
656
+ * Sharing this matcher is necessary but not sufficient for a severity override
657
+ * and an option override to select the same files: each caller must also pass
658
+ * the same `target` (route and, critically, `file`) the other path effectively
659
+ * matches against. See Finding 1, docs/superpowers/specs/2026-07-26-rule-options-design.md.
660
+ */
661
+ declare function overrideMatches(o: CompiledOverride, target: {
662
+ route?: string;
663
+ file?: string;
664
+ }): boolean;
665
+ /**
666
+ * Apply route-/file-scoped overrides to results (design 2026-07-18). An entry
667
+ * matches when any `route` glob matches the finding's route id or any `files`
668
+ * glob matches its location (OR). `'off'` removes a matched result entirely —
669
+ * passing seeds included, so scoring and "checks passed" counts behave as if
670
+ * the rule never ran there. A severity value rewrites the result's severity.
671
+ * Entries are evaluated in order (later entries win); within one entry, a
672
+ * rule-id key beats a category key only when it specifies a `severity` — an
673
+ * options-only rule-id key (no `severity`) contributes its options but leaves
674
+ * the category key's severity in force, rather than shadowing it (design
675
+ * 2026-07-26, Finding 2 / second review Finding E).
676
+ */
677
+ declare function applyOverrides(results: Result[], config: Config): Result[];
678
+
679
+ /**
680
+ * Per-rule options: their declaration, resolution, and validation (design
681
+ * 2026-07-26). Deliberately does not import `rule.ts` — `rule.ts` imports
682
+ * `RuleOptionsSpec` from here, so taking `Rule` as a parameter would cycle.
683
+ * Callers pass the id and the spec instead.
684
+ */
685
+
686
+ /**
687
+ * One configurable option. `kind` decides the merge semantics, so no rule
688
+ * writes merge code of its own: `integer` replaces, and the two collection
689
+ * kinds ADD to the built-in default (never replace — see the design doc).
690
+ */
691
+ type RuleOptionSpec = {
692
+ kind: 'integer';
693
+ default: number;
694
+ min?: number;
695
+ max?: number;
696
+ } | {
697
+ kind: 'string-list';
698
+ default: readonly string[];
699
+ } | {
700
+ kind: 'string-map';
701
+ default: Readonly<Record<string, string>>;
702
+ };
703
+ /** A rule's configurable options, keyed by option name. */
704
+ type RuleOptionsSpec = Record<string, RuleOptionSpec>;
705
+ /**
706
+ * Typed reads of a resolved options object. `RuleOptions` values are `unknown`
707
+ * (the map is open-ended by design), so without these every rule would carry
708
+ * its own `o.max as number` cast and the "resolution guarantees the declared
709
+ * kind" invariant would live in a dozen places instead of one. `resolveRuleOptions`
710
+ * always seeds every declared key from the spec default and validation rejects a
711
+ * wrongly-typed value up front, so a mismatch here means a rule read a key it
712
+ * never declared — the `fallback` keeps that a wrong number rather than a crash.
713
+ */
714
+ declare function intOption(options: RuleOptions, key: string, fallback?: number): number;
715
+ /** As `intOption`, for a `string-list` option. */
716
+ declare function listOption(options: RuleOptions, key: string): string[];
717
+ /** As `intOption`, for a `string-map` option. */
718
+ declare function mapOption(options: RuleOptions, key: string): Record<string, string>;
719
+ /**
720
+ * Effective options for a rule at a target: built-in defaults, then
721
+ * `config.rules[ruleId].options`, then every matching `config.overrides` entry
722
+ * in order. Integers take the last value; lists and maps accumulate.
723
+ *
724
+ * `target` omitted skips overrides entirely (project-scoped rules). Callers
725
+ * resolving many targets should hoist `compileOverrides(config)` and pass it as
726
+ * `compiled` — otherwise every call recompiles the globs.
727
+ */
728
+ declare function resolveRuleOptions(ruleId: string, spec: RuleOptionsSpec | undefined, config: Config, target?: {
729
+ route?: string;
730
+ file?: string;
731
+ }, compiled?: CompiledOverride[]): RuleOptions;
732
+ /**
733
+ * Problems with a user-supplied options object, as human-readable sentences
734
+ * (empty = valid). Callers treat any result as fatal: a typo that silently
735
+ * leaves the config inert is the failure this exists to prevent.
736
+ *
737
+ * `baseline`, when given, is the already-resolved value this `options` layer
738
+ * is being merged onto — built-in defaults merged with any earlier layer(s)
739
+ * (e.g. the global `config.rules[id].options`, when `options` is an
740
+ * `overrides[]` entry). The min/max cross-check below compares against it
741
+ * instead of the spec's own default, so a layer that only sets one side of a
742
+ * range is checked against what it actually inherits (design 2026-07-26
743
+ * review, Finding A). Omit it to check `options` against the spec defaults
744
+ * alone, as when validating the global layer itself. A `baseline` that is
745
+ * only partially resolved (missing `min` or `max`) is treated as "can't
746
+ * determine that side" rather than silently comparing against `undefined` —
747
+ * see the `typeof` guard below.
748
+ *
749
+ * `skipRangeCheck`, when true, skips the min/max cross-check entirely
750
+ * regardless of `baseline`. A caller sets this when it statically cannot
751
+ * rule out that some *other* config layer narrows the opposite side of the
752
+ * range at the same target — see the CLI's and the Vite plugin's
753
+ * `overrides[]` validation (design 2026-07-26 review, Finding A, third
754
+ * pass).
755
+ */
756
+ declare function validateRuleOptions(ruleId: string, spec: RuleOptionsSpec | undefined, options: RuleOptions, baseline?: RuleOptions, skipRangeCheck?: boolean): string[];
757
+ /**
758
+ * Whether `validateRuleOptions` should skip the min/max cross-check for
759
+ * `overrides[selfIndex].rules[key]` — the whole decision, so the CLI's
760
+ * config-file loader and the Vite plugin can't drift apart on it (they held
761
+ * line-for-line copies of it before).
762
+ *
763
+ * An entry that sets both sides, or neither, is judged against its baseline as
764
+ * usual. An entry that sets only one side is skipped when some *other* entry
765
+ * sets the opposite side, since the two may co-apply at a shared target and be
766
+ * valid there — see `otherOverrideNarrowsOppositeSide` for why that is
767
+ * conservative by necessity and what it lets through.
768
+ */
769
+ declare function shouldSkipRangeCheck(overrides: readonly unknown[], selfIndex: number, key: string, setting: unknown): boolean;
770
+ /**
771
+ * Problems with one user-supplied rule setting — the bare severity string or the
772
+ * object form — as human-readable sentences prefixed with `label` (empty = valid).
773
+ * THE single definition of what a setting may look like: the CLI's config-file
774
+ * loader and the Vite plugin both funnel through it, so a config file and the
775
+ * equivalent plugin option are accepted or rejected identically. Callers treat any
776
+ * result as fatal, on the same reasoning as an unknown rule id — a typo that
777
+ * silently leaves the config inert is the failure being prevented.
778
+ *
779
+ * `label` names the setting in the message (e.g. `rules.seo/title-length`,
780
+ * `overrides[0].rules.architecture`); `ruleId` is the key options messages quote.
781
+ * `allowOptions` is false for a category key: a category may carry a severity, but
782
+ * options are rule-specific and meaningless there. `baseline` and `skipRangeCheck`
783
+ * are passed through to `validateRuleOptions`.
784
+ */
785
+ declare function validateRuleSetting(label: string, ruleId: string, setting: unknown, spec: RuleOptionsSpec | undefined, opts: {
786
+ allowOptions: boolean;
787
+ baseline?: RuleOptions;
788
+ skipRangeCheck?: boolean;
789
+ }): string[];
524
790
 
525
791
  /** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */
526
792
  interface RuleContext {
@@ -548,6 +814,8 @@ interface Rule {
548
814
  rationale: string;
549
815
  /** Canonical remediation template, shared by findings and explain_rule (issue #24). */
550
816
  fix?: Fix;
817
+ /** Configurable options for this rule; absent means the rule takes none. */
818
+ options?: RuleOptionsSpec;
551
819
  /**
552
820
  * Evaluate the resolved heads. A single rule may return one Result per route,
553
821
  * so it always returns an array. Project-scoped rules return a single element.
@@ -715,15 +983,43 @@ declare const correctnessEffectAsOnMount: Rule;
715
983
 
716
984
  declare const correctnessUnmutatedState: Rule;
717
985
 
986
+ /**
987
+ * correctness/prop-mutation — mutating a prop directly is a silent bug in both Svelte modes,
988
+ * for different reasons: in runes mode, a non-$bindable prop mutation doesn't propagate to the
989
+ * parent; in legacy mode (export let), Svelte's reactivity is assignment-based, so a mutating
990
+ * method call (`.push(...)`, etc.) doesn't trigger an update at all without a following
991
+ * reassignment. The two modes can't be mixed in one component, so a given finding is always
992
+ * exactly one or the other — see `legacy` on `ComponentFacts.mutatedProps` (component-parse.ts).
993
+ */
718
994
  declare const correctnessPropMutation: Rule;
719
995
 
720
996
  /**
721
- * correctness/stale-prop-derivation — a value computed from a prop without
722
- * $derived is evaluated once, at init, and silently stops tracking the parent.
723
- * Svelte's own guidance: treat props as though they will change.
997
+ * correctness/stale-prop-derivation — a value computed from a prop without $derived (runes
998
+ * mode) or $: (legacy mode) is evaluated once, at init, and silently stops tracking the
999
+ * parent. Svelte's own guidance: treat props as though they will change. The two modes can't
1000
+ * be mixed in one component, so a given finding is always exactly one or the other — see
1001
+ * `legacy` on `ComponentFacts.stalePropDerivations` (component-parse.ts).
724
1002
  */
725
1003
  declare const correctnessStalePropDerivation: Rule;
726
1004
 
1005
+ /**
1006
+ * correctness/nonreactive-builtin-state — $state's deep proxy covers plain
1007
+ * objects and arrays only. A plain Map/Set/Date/URL/URLSearchParams in $state
1008
+ * keeps working as data, but its mutations never reach effects, deriveds, or
1009
+ * the template: the UI silently stops updating. svelte/reactivity ships
1010
+ * drop-in reactive equivalents for exactly this.
1011
+ */
1012
+ declare const correctnessNonreactiveBuiltinState: Rule;
1013
+
1014
+ /**
1015
+ * correctness/checkable-bind-value — bind:value binds the DOM value property. A
1016
+ * checkbox/radio's user interaction toggles checkedness, which bind:value never observes, so
1017
+ * the bound state is frozen at its initial value and silently never updates in production.
1018
+ * bind:checked (single checkbox) / bind:group (checkbox list, radio group) are the correct
1019
+ * bindings.
1020
+ */
1021
+ declare const correctnessCheckableBindValue: Rule;
1022
+
727
1023
  declare const correctnessOrphanEffect: Rule;
728
1024
 
729
1025
  /**
@@ -734,6 +1030,14 @@ declare const correctnessOrphanEffect: Rule;
734
1030
  */
735
1031
  declare const correctnessOrphanLifecycle: Rule;
736
1032
 
1033
+ /**
1034
+ * correctness/base-path-navigation — root-relative navigation literals in a project that sets
1035
+ * `kit.paths.base`. A custom check because it is gated on a PROJECT fact and its own facts live
1036
+ * on BOTH the component channel (`<a href>`, `goto()`) and the Kit-module channel (`redirect()`).
1037
+ * With no base path configured the rule emits nothing at all — the gate is the whole point.
1038
+ */
1039
+ declare const correctnessBasePathNavigation: Rule;
1040
+
737
1041
  /**
738
1042
  * correctness/server-browser-global — browser globals read in server-executed MODULE code: module scope of
739
1043
  * runes modules / `<script module>`, and Kit route/hooks files (top level, handler
@@ -759,6 +1063,16 @@ declare const architectureComponentSize: Rule;
759
1063
 
760
1064
  declare const architecturePropCount: Rule;
761
1065
 
1066
+ /**
1067
+ * architecture/private-scope-import — a unit inside a declared private scope must not be
1068
+ * imported from outside that scope (design 2026-07-28). L3: the scopes are declared by the
1069
+ * project via the `scopes` option and never inferred, so the rule is inert until then.
1070
+ *
1071
+ * Findings are reported at the import site, not at the imported unit: `--diff` filters
1072
+ * results to the files that changed, and the author of the violation edited the importer.
1073
+ */
1074
+ declare const architecturePrivateScopeImport: Rule;
1075
+
762
1076
  declare const performanceHeavyImport: Rule;
763
1077
 
764
1078
  declare const performanceNamespaceImport: Rule;
@@ -797,6 +1111,15 @@ declare const performanceStateRaw: Rule;
797
1111
 
798
1112
  declare const allRules: Rule[];
799
1113
 
1114
+ /** One configurable option of a rule, flattened for explain_rule's consumers. */
1115
+ interface RuleOptionInfo {
1116
+ name: string;
1117
+ /** `integer` replaces the default; `string-list`/`string-map` are ADDED to it. */
1118
+ kind: RuleOptionSpec['kind'];
1119
+ default: number | readonly string[] | Readonly<Record<string, string>>;
1120
+ min?: number;
1121
+ max?: number;
1122
+ }
800
1123
  interface RuleInfo {
801
1124
  id: string;
802
1125
  title: string;
@@ -805,6 +1128,12 @@ interface RuleInfo {
805
1128
  rationale: string;
806
1129
  docsUrl: string;
807
1130
  fix?: Fix;
1131
+ /**
1132
+ * The rule's configurable options, omitted when it takes none. An agent that
1133
+ * judges a finding to be a threshold disagreement rather than a defect needs
1134
+ * to know the knob exists and what it is called before it can suggest one.
1135
+ */
1136
+ options?: RuleOptionInfo[];
808
1137
  }
809
1138
  /** Look up a rule's static metadata for the MCP explain_rule tool (issue #24). Rule ids are matched exactly (case-sensitive, e.g. "seo/ssr-disabled"). */
810
1139
  declare function explainRule(id: string): RuleInfo | undefined;
@@ -1073,19 +1402,4 @@ declare function escapeHtml(s: string): string;
1073
1402
  */
1074
1403
  declare function safeHref(url: string): string | null;
1075
1404
 
1076
- /** Drop rules disabled via config (design §6). */
1077
- declare function selectRules(rules: Rule[], config: Config): Rule[];
1078
- /** Apply per-rule severity overrides to results (design §6). */
1079
- declare function applyRuleSeverities(results: Result[], config: Config): Result[];
1080
- /**
1081
- * Apply route-/file-scoped overrides to results (design 2026-07-18). An entry
1082
- * matches when any `route` glob matches the finding's route id or any `files`
1083
- * glob matches its location (OR). `'off'` removes a matched result entirely —
1084
- * passing seeds included, so scoring and "checks passed" counts behave as if
1085
- * the rule never ran there. A severity value rewrites the result's severity.
1086
- * Entries are evaluated in order (later entries win); within one entry a
1087
- * rule-id key beats a category key.
1088
- */
1089
- declare function applyOverrides(results: Result[], config: Config): Result[];
1090
-
1091
- export { APP_SCRIPT, APP_STYLE, type AppSnapshot, BAND_COLOR, CHILD_NODE_KEYS, type Category, type Classification, type ComponentFacts, type Config, type ConsoleReportOptions, type Detection, type EachBlockFact, type EffectFact, type Fix, type HeadProvider, type HeadTag, type HeadingInfo, type HealthResult, type ImageInfo, type JsonReport, type KitModuleFacts, type OrphanEffectFact, type Palette, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedHeadings, type ResolvedImages, type Result, type RouteBadge, type Rule, type RuleContext, type RuleInfo, type RuleOverride, type RuleSetting, type Runtime, SITEMAP_SOURCE_PATHS, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type SourceSpan, type Summary, type SuppressionDirective, type TreatDynamicAs, type Value, allRules, applyOverrides, applyRuleSeverities, architectureComponentSize, architecturePropCount, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, computeHealth, computeScore, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findMinifyDisabled, formatAgentReport, formatConsoleReport, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, lineOf, linkRule, noColorPalette, parseComponentFacts, parseKitModuleFacts, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, summarize, textFromNodes, valueFromNodes };
1405
+ export { APP_SCRIPT, APP_STYLE, type AppSnapshot, BAND_COLOR, CATEGORIES, CHILD_NODE_KEYS, type Category, type Classification, type CompiledOverride, type ComponentFacts, type Config, type ConsoleReportOptions, type Detection, type EachBlockFact, type EffectFact, type Fix, type HeadProvider, type HeadTag, type HeadingInfo, type HealthResult, type ImageInfo, type JsonReport, type KitModuleFacts, type OrphanEffectFact, type Palette, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedHeadings, type ResolvedImages, type Result, type RouteBadge, type Rule, type RuleContext, type RuleInfo, type RuleOptionInfo, type RuleOptionSpec, type RuleOptions, type RuleOptionsSpec, type RuleOverride, type RuleSetting, type RuleSettingObject, type Runtime, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type SourceSpan, type Summary, type SuppressionDirective, type TreatDynamicAs, VITE_CONFIG_FILES, type Value, type ViteKitConfigResult, allRules, applyOverrides, applyRuleSeverities, architectureComponentSize, architecturePrivateScopeImport, architecturePropCount, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, compileOverrides, computeHealth, computeScore, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, formatAgentReport, formatConsoleReport, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isPenalized, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitPathsBase, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, summarize, textFromNodes, validateRuleOptions, validateRuleSetting, valueFromNodes };