@svelte-vitals/core 0.26.0 → 0.28.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 +271 -123
  2. package/dist/index.js +1326 -546
  3. package/package.json +5 -2
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { AST } from 'svelte/compiler';
2
+
1
3
  /**
2
4
  * Core type definitions shared across modes. This module is pure: no I/O, no
3
5
  * `node:` imports, no runtime-specific globals (design §8).
@@ -23,6 +25,17 @@ interface Project {
23
25
  htmlLang: Detection;
24
26
  /** Whether the static static/robots.txt references a sitemap (`Sitemap:` line). Undefined for a +server endpoint / absent / unreadable. */
25
27
  robotsReferencesSitemap?: boolean;
28
+ /**
29
+ * Set when the Vite config disables minification for production builds (performance/minify-disabled).
30
+ * `file` is the config path relative to the analyzed root (posix, may start with `../`
31
+ * in monorepos); unset for inline programmatic configs. `line` is 1-based and set only
32
+ * when the literal `minify: false` was located in that file; unset when the value was
33
+ * resolved at build time (plugin/conditional config).
34
+ */
35
+ viteMinifyDisabled?: {
36
+ file?: string;
37
+ line?: number;
38
+ };
26
39
  }
27
40
  declare const defaultProject: Project;
28
41
  /** A concrete, agent-actionable remediation for a finding (design §10, issue #18). */
@@ -36,7 +49,7 @@ interface Fix {
36
49
  }
37
50
  /** A single rule finding for one route (or the whole project). */
38
51
  interface Result {
39
- /** Rule id, e.g. 'SEO001'. */
52
+ /** Rule id, e.g. 'seo/title-presence'. */
40
53
  id: string;
41
54
  severity: Severity;
42
55
  detection: Detection;
@@ -60,6 +73,26 @@ type Category = 'seo' | 'performance' | 'correctness' | 'security' | 'architectu
60
73
  type TreatDynamicAs = 'pass' | 'warn' | 'fail';
61
74
  /** Per-rule override: disable, or change severity. */
62
75
  type RuleSetting = 'off' | Severity;
76
+ /**
77
+ * Scoped rule override (design 2026-07-18), applied to results after analysis.
78
+ * An entry matches a finding when any `route` glob matches its route id or any
79
+ * `files` glob matches its source location; at least one of the two must be
80
+ * set. Glob syntax: `*` matches within a segment, `**` across segments, a
81
+ * trailing `/**` also matches the bare prefix, and all other characters
82
+ * (including SvelteKit's `(`, `)`, `[`, `]`) are literal.
83
+ */
84
+ interface RuleOverride {
85
+ /**
86
+ * Route-id glob(s), e.g. '/admin/**'. Note route ids drop `(group)` segments
87
+ * (`src/routes/(app)/dashboard` reports as '/dashboard') — target a group
88
+ * via `files` instead.
89
+ */
90
+ route?: string | string[];
91
+ /** Source-path glob(s) matched against a finding's location, e.g. 'src/routes/(app)/**'. */
92
+ files?: string | string[];
93
+ /** Keys are rule ids ('seo/title-presence') or category names ('seo'). Rule id beats category within an entry. */
94
+ rules: Record<string, RuleSetting>;
95
+ }
63
96
  interface Config {
64
97
  treatDynamicAs: TreatDynamicAs;
65
98
  /** Component names treated as meta sources of unknown content (design §11 layer 4). */
@@ -70,6 +103,8 @@ interface Config {
70
103
  failOn: Severity;
71
104
  /** Per-category weights for the combined Health score (default: equal, 1 each) (#10). */
72
105
  weights?: Partial<Record<Category, number>>;
106
+ /** Route-/file-scoped rule overrides, applied to results after analysis (later entries win). */
107
+ overrides?: RuleOverride[];
73
108
  }
74
109
  declare const defaultConfig: Config;
75
110
  /** Merge user config over defaults. Identity helper for config files (design §6). */
@@ -121,9 +156,9 @@ interface HeadTag {
121
156
  text?: string;
122
157
  /** Literal `hreflang` of a `<link rel="alternate">` (e.g. 'en', 'en-US', 'x-default'). Undefined when dynamic/absent. */
123
158
  hreflang?: string;
124
- /** Literal href (link) / src (script) URL when static — used for third-party origin analysis (PERF008). */
159
+ /** Literal href (link) / src (script) URL when static — used for third-party origin analysis (performance/preconnect). */
125
160
  href?: string;
126
- /** True for a render-blocking `<script src>` in <head> (no defer/async/module) (PERF007). */
161
+ /** True for a render-blocking `<script src>` in <head> (no defer/async/module) (performance/render-blocking-script). */
127
162
  blocking?: boolean;
128
163
  /** Where this tag was set relative to the route. Never 'none' (absence = no tag). */
129
164
  presence: Exclude<Presence, 'none'>;
@@ -162,11 +197,11 @@ interface ImageInfo {
162
197
  hasWidth: boolean;
163
198
  hasHeight: boolean;
164
199
  hasLoading: boolean;
165
- /** True when the <img> has an `alt` attribute at all (incl. empty `alt=""` decorative; SEO025). */
200
+ /** True when the <img> has an `alt` attribute at all (incl. empty `alt=""` decorative; seo/image-alt). */
166
201
  hasAlt: boolean;
167
- /** True when the <img> has a literal `loading="lazy"` (PERF005). Dynamic/spread → false. */
202
+ /** True when the <img> has a literal `loading="lazy"` (performance/lcp-image). Dynamic/spread → false. */
168
203
  lazy: boolean;
169
- /** True when the <img> has a `srcset` attribute (PERF006). */
204
+ /** True when the <img> has a `srcset` attribute (performance/responsive-image). */
170
205
  hasSrcset: boolean;
171
206
  /** 1-based source line, or 0 if unknown. */
172
207
  line: number;
@@ -182,7 +217,7 @@ interface ResolvedImages {
182
217
  /**
183
218
  * A normalized page-body heading occurrence — the mode-independent boundary for
184
219
  * the heading-hierarchy rule (mirrors images.ts). Both providers collect these
185
- * so SEO027 never needs to know which mode produced them.
220
+ * so seo/single-h1 never needs to know which mode produced them.
186
221
  */
187
222
  interface HeadingInfo {
188
223
  /** Heading level 1–6 (the `n` in <hn>). */
@@ -209,6 +244,8 @@ interface EachBlockFact {
209
244
  hasKey: boolean;
210
245
  /** 1-based source line, or 0 if unknown. */
211
246
  line: number;
247
+ /** Set when the block's key expression is its index binding or a trivial coercion of it — `(i)`, `(String(i))`, `(Number(i))`, `` (`${i}`) ``, `(i.toString())`, `(i + '')` — correctness/each-index-key. */
248
+ indexKey?: boolean;
212
249
  }
213
250
  /** An `$effect(...)` / `$effect.pre(...)` call in a component's instance script. */
214
251
  interface EffectFact {
@@ -216,10 +253,10 @@ interface EffectFact {
216
253
  line: number;
217
254
  /** True when the effect body only assigns to `$state` variables (the "use $derived" smell). */
218
255
  assignsOnlyState: boolean;
219
- /** True when this $effect has a NON-EMPTY body that reads no reactive value and makes no bare call — it never re-runs, so it should be onMount (CORRECT003). */
256
+ /** True when this $effect has a NON-EMPTY body that reads no reactive value and makes no bare call — it never re-runs, so it should be onMount (correctness/effect-as-onmount). */
220
257
  mountOnly: boolean;
221
258
  }
222
- /** A `$effect` guaranteed to run outside component initialisation — it throws `effect_orphan` at runtime (CORRECT006). */
259
+ /** A `$effect` guaranteed to run outside component initialisation — it throws `effect_orphan` at runtime (correctness/orphan-effect). */
223
260
  interface OrphanEffectFact {
224
261
  /** 1-based source line, or 0 if unknown. For 'constructor-instantiated', the module-scope `new` site. */
225
262
  line: number;
@@ -228,7 +265,7 @@ interface OrphanEffectFact {
228
265
  /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
229
266
  className?: string;
230
267
  }
231
- /** A svelte lifecycle/context call guaranteed to run outside component initialisation — it throws `lifecycle_outside_component` at runtime (CORRECT007). */
268
+ /** A svelte lifecycle/context call guaranteed to run outside component initialisation — it throws `lifecycle_outside_component` at runtime (correctness/orphan-lifecycle). */
232
269
  interface OrphanLifecycleCallFact {
233
270
  /** Canonical svelte export name (alias-resolved), e.g. 'onMount'. */
234
271
  name: string;
@@ -239,13 +276,13 @@ interface OrphanLifecycleCallFact {
239
276
  /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
240
277
  className?: string;
241
278
  }
242
- /** A browser-only global read in code that runs on the server — SSR crashes with "<name> is not defined" (CORRECT008/009). */
279
+ /** A browser-only global read in code that runs on the server — SSR crashes with "<name> is not defined" (correctness/server-browser-global, correctness/instance-browser-global). */
243
280
  interface BrowserGlobalRefFact {
244
281
  /** The global's name, e.g. 'window'. */
245
282
  name: string;
246
283
  /** 1-based source line, or 0 if unknown. */
247
284
  line: number;
248
- /** 'module' = module evaluation (script module / runes module — CORRECT008); 'instance' = component-init top level (runs on the server during SSR — CORRECT009). */
285
+ /** 'module' = module evaluation (script module / runes module — correctness/server-browser-global); 'instance' = component-init top level (runs on the server during SSR — correctness/instance-browser-global). */
249
286
  context: 'module' | 'instance';
250
287
  }
251
288
  /** A flagged source position in a component (e.g. an `{@html}` tag or a `javascript:` URL). */
@@ -266,43 +303,53 @@ interface ComponentFacts {
266
303
  file: string;
267
304
  eachBlocks: EachBlockFact[];
268
305
  effects: EffectFact[];
269
- /** `{@html …}` occurrences — raw-HTML render surfaces (Security SEC001). */
306
+ /** `{@html …}` occurrences — raw-HTML render surfaces (security/raw-html). */
270
307
  htmlTags: SourceSpan[];
271
- /** Element attributes with a literal `javascript:` URL (Security SEC002). */
308
+ /** Element attributes with a literal `javascript:` URL (security/javascript-url). */
272
309
  javascriptUrls: SourceSpan[];
273
- /** Source line count of the component file (Architecture ARCH001). */
310
+ /** Source line count of the component file (architecture/component-size). */
274
311
  loc: number;
275
- /** Named props destructured from `$props()`; 0 when unknowable (rest / non-destructured) (Architecture ARCH002). */
312
+ /** Named props destructured from `$props()`; 0 when unknowable (rest / non-destructured) (architecture/prop-count). */
276
313
  propCount: number;
277
- /** Module specifiers of every `import` in the instance + module scripts (Bundle PERF009). */
314
+ /** Module specifiers of every `import` in the instance + module scripts (performance/heavy-import). */
278
315
  imports: string[];
279
- /** Module specifiers of every `import`, each with its source line (Bundle PERF009). */
316
+ /** Module specifiers of every `import`, each with its source line (performance/heavy-import). */
280
317
  importSpans: {
281
318
  source: string;
282
319
  line: number;
283
320
  }[];
284
- /** Value `import * as X from '<bare pkg>'` namespace imports (type-only excluded) — Bundle PERF010. */
321
+ /** Value `import * as X from '<bare pkg>'` namespace imports (type-only excluded) — performance/namespace-import. */
285
322
  namespaceImports: {
286
323
  source: string;
287
324
  line: number;
288
325
  }[];
289
- /** `$state` declarations never written or escaped anywhere in the component — candidates for const (CORRECT004). */
326
+ /** `$state` declarations never written or escaped anywhere in the component — candidates for const (correctness/unmutated-state). */
290
327
  constableStates: {
291
328
  name: string;
292
329
  line: number;
293
330
  }[];
294
- /** Mutations of a non-`$bindable` prop from `$props()` — member writes, `delete`, or a mutating method call (CORRECT005). */
331
+ /** Mutations of a non-`$bindable` prop from `$props()` — member writes, `delete`, or a mutating method call (correctness/prop-mutation). */
295
332
  mutatedProps: {
296
333
  name: string;
297
334
  line: number;
298
335
  }[];
299
- /** `$effect` calls guaranteed to run outside component initialisation module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (CORRECT006). */
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). */
337
+ stalePropDerivations: {
338
+ name: string;
339
+ line: number;
340
+ }[];
341
+ /** Object/array-literal $state bindings reassigned at least once but never mutated, escaped, aliased, or item-edited — $state.raw candidates (performance/state-raw). */
342
+ rawableStates: {
343
+ name: string;
344
+ line: number;
345
+ }[];
346
+ /** `$effect` calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-effect). */
300
347
  orphanEffects: OrphanEffectFact[];
301
- /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (CORRECT007). */
348
+ /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-lifecycle). */
302
349
  orphanLifecycleCalls: OrphanLifecycleCallFact[];
303
- /** Browser-global reads in server-executed positions of this file (CORRECT008/009). */
350
+ /** Browser-global reads in server-executed positions of this file (correctness/server-browser-global, correctness/instance-browser-global). */
304
351
  browserGlobalRefs: BrowserGlobalRefFact[];
305
- /** Module-scope `$state` declarations in a `.svelte.ts`/`.svelte.js` runes module — on a server, one instance shared by every request (SEC005). Always empty for `.svelte` files. */
352
+ /** Module-scope `$state` declarations in a `.svelte.ts`/`.svelte.js` runes module — on a server, one instance shared by every request (security/shared-state-import). Always empty for `.svelte` files. */
306
353
  moduleStateDecls: {
307
354
  name: string;
308
355
  line: number;
@@ -318,7 +365,7 @@ type ParsedFacts = Omit<ComponentFacts, 'file' | 'suppressions'> & {
318
365
  /**
319
366
  * Parse one source file's facts (CLI/static + vite build mode): a `.svelte` component's
320
367
  * reactivity/correctness + security + architecture facts, or a `.svelte.ts`/`.svelte.js`
321
- * runes module's orphan-$effect facts (CORRECT006).
368
+ * runes module's orphan-$effect facts (correctness/orphan-effect).
322
369
  */
323
370
  declare function parseComponentFacts(source: string, filename: string): ParsedFacts;
324
371
 
@@ -340,49 +387,62 @@ declare function collectComponentFacts(rt: Runtime, cwd: string): Promise<Compon
340
387
 
341
388
  /**
342
389
  * Facts parsed from one SvelteKit route/hooks file for the SSR shared-state rules
343
- * (SEC003–005). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
390
+ * (the security kit-module rules). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
344
391
  */
345
392
  interface KitModuleFacts {
346
393
  /** Repo-relative source file. */
347
394
  file: string;
348
395
  /** 'server' = runs only on the server (+*.server, +server, hooks.server); 'universal' = +page.ts/+layout.ts (still runs on the server during SSR). */
349
396
  kind: 'server' | 'universal';
350
- /** Module-scope let/var reassigned from inside a function (SEC004). */
397
+ /** Module-scope let/var reassigned from inside a function (security/server-module-state). */
351
398
  moduleStateReassignments: {
352
399
  name: string;
353
400
  line: number;
354
401
  inHandler: boolean;
355
402
  }[];
356
- /** Writes to an imported binding from inside an exported handler (SEC003). */
403
+ /** Writes to an imported binding from inside an exported handler (security/handler-state-write). */
357
404
  importedStateWrites: {
358
405
  name: string;
359
406
  line: number;
360
407
  via: 'assignment' | 'set-call';
361
408
  }[];
362
- /** Writes to an imported binding outside handlers — top level or helper functions (SEC005's write flavour). */
409
+ /** Writes to an imported binding outside handlers — top level or helper functions (security/shared-state-import's write flavour). */
363
410
  importedStateWritesOutsideHandlers: {
364
411
  name: string;
365
412
  line: number;
366
413
  }[];
367
- /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (SEC005). */
414
+ /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (security/shared-state-import). */
368
415
  runesModuleImports: {
369
416
  source: string;
370
417
  resolved: string;
371
418
  names: string[];
372
419
  line: number;
373
420
  }[];
374
- /** Svelte lifecycle/context calls that run outside component initialisation — top level, handler bodies, or the `init` hook (CORRECT007). */
421
+ /** Svelte lifecycle/context calls that run outside component initialisation — top level, handler bodies, or the `init` hook (correctness/orphan-lifecycle). */
375
422
  lifecycleCalls: {
376
423
  name: string;
377
424
  line: number;
378
425
  inHandler: boolean;
379
426
  }[];
380
- /** Browser-global reads in server-executed positions — top level, handler bodies, the `init` hook (CORRECT008). Empty when the file itself exports `ssr = false`. */
427
+ /** Browser-global reads in server-executed positions — top level, handler bodies, the `init` hook (correctness/server-browser-global). Empty when the file itself exports `ssr = false`. */
381
428
  browserGlobalRefs: {
382
429
  name: string;
383
430
  line: number;
384
431
  inHandler: boolean;
385
432
  }[];
433
+ /** 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
+ ssrDisabled?: {
435
+ line: number;
436
+ };
437
+ /** Set when this file disables client-side rendering via `export const csr = false` (inline or same-file alias export). With no client runtime, a universal load only runs during SSR — performance/load-waterfall's browser-waterfall premise doesn't hold. */
438
+ csrDisabled?: {
439
+ line: number;
440
+ };
441
+ /** Sequential-await analysis of the exported `load` function (performance/load-waterfall, performance/sequential-awaits): 1-based lines of await sites that depend on an earlier await's result, and of sites independent of all earlier awaits. Set only when at least one list is non-empty. */
442
+ loadWaterfalls?: {
443
+ dependentLines: number[];
444
+ independentLines: number[];
445
+ };
386
446
  /** Inline `svelte-vitals-disable-next-line` directives in this file. */
387
447
  suppressions: SuppressionDirective[];
388
448
  }
@@ -392,12 +452,12 @@ interface KitModuleFacts {
392
452
  * undefined when it cannot be a runes module: `$lib/` maps to `src/lib/`, `./`/`../`
393
453
  * resolve against the importing file's directory; bare packages, other aliases, and
394
454
  * a relative specifier whose `..` segments escape the repo root are skipped. An
395
- * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (SEC005 also
455
+ * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (security/shared-state-import also
396
456
  * tries the `.js` sibling when matching).
397
457
  */
398
458
  declare function resolveRunesModuleSpecifier(spec: string, importerFile: string): string | undefined;
399
459
  /**
400
- * Parse one SvelteKit route/hooks file's SSR shared-state facts (SEC003–005). Uses
460
+ * Parse one SvelteKit route/hooks file's SSR shared-state facts (the security kit-module rules). Uses
401
461
  * the shared wrap parser (`parseModuleProgram`), so reported lines subtract the
402
462
  * 1-line wrap prefix; suppressions are scanned on the unwrapped source.
403
463
  */
@@ -406,7 +466,7 @@ declare function parseKitModuleFacts(source: string, filename: string): Omit<Kit
406
466
  /** Fallback facts for a Kit file that fails to read or parse (dev tooling must never throw). */
407
467
  declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind']): KitModuleFacts;
408
468
  /**
409
- * Scan SvelteKit route/hooks files for SSR shared-state facts (SEC003–005): route
469
+ * Scan SvelteKit route/hooks files for SSR shared-state facts (the security kit-module rules): route
410
470
  * `+page`/`+layout` server and universal modules, `+server` endpoints, and
411
471
  * `src/hooks.server`. `src/lib/server/**` is deliberately NOT scanned — legitimate
412
472
  * module singletons (DB connections, clients) live there (design). A file that
@@ -414,7 +474,17 @@ declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind'])
414
474
  */
415
475
  declare function collectKitModuleFacts(rt: Runtime, cwd: string): Promise<KitModuleFacts[]>;
416
476
 
417
- type Node = any;
477
+ /**
478
+ * The `build: { minify: false }` override, when present as a literal: returns the
479
+ * `minify` property's 1-based line in the ORIGINAL source. Undefined for clean,
480
+ * dynamic, or unparsable configs (never throws).
481
+ */
482
+ declare function findMinifyDisabled(source: string): {
483
+ line: number;
484
+ } | undefined;
485
+
486
+ /** A template fragment's child node relevant to value classification: literal text or a `{expr}`. */
487
+ type TextOrExpr = AST.Text | AST.ExpressionTag;
418
488
  /**
419
489
  * All keys that can bear child nodes in a Svelte AST node.
420
490
  * Covers if/each/await blocks (pending/then/catch/fallback) as well as
@@ -428,28 +498,28 @@ declare const CHILD_NODE_KEYS: string[];
428
498
  * - non-whitespace Text only → 'static'
429
499
  * - empty / whitespace only → 'absent'
430
500
  */
431
- declare function valueFromNodes(nodes: Node[]): Value;
501
+ declare function valueFromNodes(nodes: TextOrExpr[]): Value;
432
502
  /** The literal text of a node list when fully static (no ExpressionTag), else undefined. */
433
- declare function textFromNodes(nodes: Node[]): string | undefined;
503
+ declare function textFromNodes(nodes: TextOrExpr[]): string | undefined;
434
504
  /** Static string of an attribute (e.g. name="description"), or undefined if dynamic/absent. */
435
- declare function attrText(attributes: Node[], name: string): string | undefined;
505
+ declare function attrText(attributes: AST.Attribute[], name: string): string | undefined;
436
506
  /** Value kind of an attribute's content (e.g. the `content` of a <meta>). */
437
- declare function attrValue(attributes: Node[], name: string): Value;
507
+ declare function attrValue(attributes: AST.Attribute[], name: string): Value;
438
508
  declare function lineOf(source: string, offset: unknown): number;
439
- declare function findAttr(attributes: Node[], name: string): Node | undefined;
509
+ declare function findAttr(attributes: AST.Attribute[], name: string): AST.Attribute | undefined;
440
510
  /** Value kind of a single attribute (e.g. a component prop). */
441
- declare function attrValueOf(attr: Node): Value;
511
+ declare function attrValueOf(attr: AST.Attribute): Value;
442
512
  /** Literal static text of a single attribute node (e.g. a component prop), or undefined if dynamic/absent. */
443
- declare function attrTextOf(attr: Node): string | undefined;
513
+ declare function attrTextOf(attr: AST.Attribute): string | undefined;
444
514
 
445
515
  /**
446
516
  * Source-file locations that satisfy the project-scope rules, shared by every
447
517
  * mode so the static (CLI) and rendered (plugin) collectors never drift. This
448
518
  * module is pure data: no I/O, no `node:` imports (design §8).
449
519
  */
450
- /** Locations that satisfy the robots.txt project rule (SEO006). */
520
+ /** Locations that satisfy the robots.txt project rule (seo/robots-txt). */
451
521
  declare const ROBOTS_SOURCE_PATHS: readonly ["static/robots.txt", "src/routes/robots.txt/+server.ts", "src/routes/robots.txt/+server.js"];
452
- /** Locations that satisfy the sitemap.xml project rule (SEO007). */
522
+ /** Locations that satisfy the sitemap.xml project rule (seo/sitemap-xml). */
453
523
  declare const SITEMAP_SOURCE_PATHS: readonly ["static/sitemap.xml", "src/routes/sitemap.xml/+server.ts", "src/routes/sitemap.xml/+server.js"];
454
524
 
455
525
  /** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */
@@ -457,7 +527,7 @@ interface RuleContext {
457
527
  heads: ResolvedHead[];
458
528
  /** Per-route <img> elements for Performance rules (absent in modes that don't collect them). */
459
529
  images?: ResolvedImages[];
460
- /** Per-route page-body headings for SEO027 (absent in modes that don't collect them). */
530
+ /** Per-route page-body headings for seo/single-h1 (absent in modes that don't collect them). */
461
531
  headings?: ResolvedHeadings[];
462
532
  /** Per-file component-body facts for Correctness rules (static/CLI mode only). */
463
533
  components?: ComponentFacts[];
@@ -505,157 +575,225 @@ declare function isPenalized(detection: Detection, treatDynamicAs: TreatDynamicA
505
575
  declare function runRules(rules: Rule[], ctx: RuleContext): Promise<Result[]>;
506
576
 
507
577
  /**
508
- * SEO001 — every route should resolve a non-empty <title> (design §11).
578
+ * seo/title-presence — every route should resolve a non-empty <title> (design §11).
509
579
  * A dynamic title (`{data.title}`) is the most common correct pattern and must
510
580
  * never be flagged as missing; it surfaces as value 'dynamic' (design §4).
511
581
  */
512
- declare const seo001Title: Rule;
582
+ declare const seoTitlePresence: Rule;
583
+
584
+ declare const seoDescriptionPresence: Rule;
585
+
586
+ declare const seoCanonicalUrl: Rule;
513
587
 
514
- declare const seo002Description: Rule;
515
- declare const seo003Canonical: Rule;
516
- declare const seo004OgImage: Rule;
517
- declare const seo005OgTitle: Rule;
518
- declare const seo008JsonLd: Rule;
588
+ declare const seoOgImage: Rule;
519
589
 
520
- declare const seo006Robots: Rule;
521
- declare const seo007Sitemap: Rule;
522
- declare const seo009HtmlLang: Rule;
590
+ declare const seoOgTitle: Rule;
523
591
 
524
- declare const perf001ImageDimensions: Rule;
525
- declare const perf002ImageLoading: Rule;
526
- declare const perf006ResponsiveImage: Rule;
592
+ declare const seoJsonLd: Rule;
527
593
 
528
- declare const perf003PreloadAs: Rule;
529
- declare const perf004FontPreloadCrossorigin: Rule;
594
+ declare const seoRobotsTxt: Rule;
595
+
596
+ declare const seoSitemapXml: Rule;
597
+
598
+ declare const seoHtmlLang: Rule;
599
+
600
+ declare const performanceImageDimensions: Rule;
601
+
602
+ declare const performanceImageLoadingHint: Rule;
603
+
604
+ declare const performanceResponsiveImage: Rule;
605
+
606
+ declare const performancePreloadMissingAs: Rule;
607
+
608
+ declare const performanceFontPreloadCrossorigin: Rule;
530
609
 
531
610
  /**
532
- * PERF005 — LCP image not lazy-loaded. Lazy-loading the largest contentful paint
611
+ * performance/lcp-image — LCP image not lazy-loaded. Lazy-loading the largest contentful paint
533
612
  * image delays it. Analysis approximates the LCP as the first <img> in document
534
613
  * order for the route; if that image is loading="lazy", flag it. Runs in both
535
614
  * static (CLI) and rendered (vite) mode, since both providers collect <img>.
536
615
  */
537
- declare const perf005LcpImage: Rule;
616
+ declare const performanceLcpImage: Rule;
538
617
 
539
618
  /**
540
- * PERF007 — Render-blocking <script> in <head>. A <script src> without
619
+ * performance/render-blocking-script — Render-blocking <script> in <head>. A <script src> without
541
620
  * defer/async/type=module blocks the parser. SvelteKit's own scripts are
542
621
  * module/deferred, so this catches hand-added blocking scripts — in app.html
543
622
  * (rendered mode) or in <svelte:head> (static mode). A head with no <script>
544
623
  * emits nothing (no signal), like the image rules.
545
624
  */
546
- declare const perf007RenderBlockingScript: Rule;
625
+ declare const performanceRenderBlockingScript: Rule;
547
626
 
548
627
  /**
549
- * PERF008 — Preconnect for third-party origins. A resource from a well-known
628
+ * performance/preconnect — Preconnect for third-party origins. A resource from a well-known
550
629
  * third-party origin (e.g. Google Fonts) without a preconnect/dns-prefetch pays a
551
630
  * connection-setup round-trip. Opt-in by construction: only origins in the
552
631
  * allowlist are checked; routes referencing none emit nothing.
553
632
  */
554
- declare const perf008Preconnect: Rule;
633
+ declare const performancePreconnect: Rule;
634
+
635
+ declare const seoIndexability: Rule;
555
636
 
556
- declare const seo010Indexability: Rule;
557
- declare const seo011TwitterCard: Rule;
558
- declare const seo012OgDescription: Rule;
559
- declare const seo013OgUrl: Rule;
560
- declare const seo014Viewport: Rule;
561
- declare const seo015SitemapInRobots: Rule;
637
+ declare const seoTwitterCard: Rule;
562
638
 
563
- declare const seo016JsonLdValidity: Rule;
564
- declare const seo017DeprecatedType: Rule;
565
- declare const seo018RelativeUrl: Rule;
566
- declare const seo019DateFormat: Rule;
567
- declare const seo020Placeholder: Rule;
568
- declare const seo021RequiredProps: Rule;
639
+ declare const seoOgDescription: Rule;
569
640
 
570
- declare const seo022TitleLength: Rule;
571
- declare const seo023DescriptionLength: Rule;
641
+ declare const seoOgUrl: Rule;
642
+
643
+ declare const seoViewport: Rule;
644
+
645
+ declare const seoSitemapInRobots: Rule;
646
+
647
+ declare const seoJsonLdValidity: Rule;
648
+
649
+ declare const seoJsonLdDeprecatedType: Rule;
650
+
651
+ declare const seoJsonLdRelativeUrl: Rule;
652
+
653
+ declare const seoJsonLdDateFormat: Rule;
654
+
655
+ declare const seoJsonLdPlaceholder: Rule;
656
+
657
+ declare const seoJsonLdRequiredProps: Rule;
658
+
659
+ declare const seoTitleLength: Rule;
660
+
661
+ declare const seoDescriptionLength: Rule;
572
662
 
573
663
  /**
574
- * SEO024 — Character encoding. The charset meta lives in `src/app.html`, so it is
575
- * only visible to rendered analysis (`appliesTo: rendered`), exactly like SEO014
664
+ * seo/charset — Character encoding. The charset meta lives in `src/app.html`, so it is
665
+ * only visible to rendered analysis (`appliesTo: rendered`), exactly like seo/viewport
576
666
  * (viewport). Static route analysis emits nothing instead of false-flagging it.
577
667
  */
578
- declare const seo024Charset: Rule;
668
+ declare const seoCharset: Rule;
579
669
 
580
670
  /**
581
- * SEO025 — Image alt text. Reuses the <img> collection from both providers — the
582
- * static (CLI) source parser and the rendered (vite) HTML parser — like PERF001/002.
671
+ * seo/image-alt — Image alt text. Reuses the <img> collection from both providers — the
672
+ * static (CLI) source parser and the rendered (vite) HTML parser — like performance/image-dimensions, performance/image-loading-hint.
583
673
  * Presence only: an explicit empty `alt=""` is a valid decorative-image signal and
584
674
  * passes; a spread `{...rest}` may supply alt, so it is not flagged.
585
675
  */
586
- declare const seo025ImageAlt: Rule;
676
+ declare const seoImageAlt: Rule;
587
677
 
588
678
  /**
589
- * SEO026 — hreflang / x-default validity. Opt-in: a route with no
679
+ * seo/hreflang — hreflang / x-default validity. Opt-in: a route with no
590
680
  * `<link rel="alternate" hreflang>` emits nothing (monolingual sites are never
591
681
  * flagged). When alternates exist, every code must be well-formed and a set of
592
682
  * two or more must declare an x-default. Works in both modes.
593
683
  */
594
- declare const seo026Hreflang: Rule;
684
+ declare const seoHreflang: Rule;
595
685
 
596
686
  /**
597
- * SEO027 — Heading hierarchy (single H1). Reads the per-route page-body headings
687
+ * seo/single-h1 — Heading hierarchy (single H1). Reads the per-route page-body headings
598
688
  * channel (collected by both providers). Zero <h1> (no primary heading) and two
599
689
  * or more (diluted topic) are both flagged; exactly one passes. A route whose
600
690
  * headings were not collected (channel unset) emits nothing.
601
691
  */
602
- declare const seo027Heading: Rule;
692
+ declare const seoSingleH1: Rule;
693
+
694
+ declare const seoDuplicateTitle: Rule;
603
695
 
604
- declare const seo028TitleUnique: Rule;
605
- declare const seo029DescriptionUnique: Rule;
696
+ declare const seoDuplicateDescription: Rule;
606
697
 
607
698
  /**
608
- * SEO030 — Skipped heading level. Walking a route's body headings in document
609
- * order, a level that jumps more than +1 over the previous heading (e.g. h2 → h4)
610
- * breaks the outline. The first heading has no predecessor (missing/multiple
611
- * <h1> stays SEO027's concern). A route with no headings emits nothing.
699
+ * seo/heading-level-skip — Skipped heading level. Walking a route's body headings in
700
+ * document order, a level that jumps more than +1 over the previous heading (e.g.
701
+ * h2 → h4) breaks the outline. The first heading has no predecessor (missing/multiple
702
+ * <h1> stays seo/single-h1's concern). A route with no headings emits nothing.
612
703
  */
613
- declare const seo030HeadingOrder: Rule;
704
+ declare const seoHeadingLevelSkip: Rule;
705
+
706
+ declare const seoSsrDisabled: Rule;
707
+
708
+ declare const correctnessEachKey: Rule;
614
709
 
615
- declare const correct001EachKey: Rule;
616
- declare const correct002EffectDerived: Rule;
617
- declare const correct003EffectAsOnMount: Rule;
710
+ declare const correctnessEachIndexKey: Rule;
618
711
 
619
- declare const correct004UnmutatedState: Rule;
712
+ declare const correctnessEffectAsDerived: Rule;
620
713
 
621
- declare const correct005PropMutation: Rule;
714
+ declare const correctnessEffectAsOnMount: Rule;
715
+
716
+ declare const correctnessUnmutatedState: Rule;
717
+
718
+ declare const correctnessPropMutation: Rule;
719
+
720
+ /**
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.
724
+ */
725
+ declare const correctnessStalePropDerivation: Rule;
622
726
 
623
- declare const correct006OrphanEffect: Rule;
727
+ declare const correctnessOrphanEffect: Rule;
624
728
 
625
729
  /**
626
- * CORRECT007 — svelte lifecycle/context calls guaranteed to run outside component
730
+ * correctness/orphan-lifecycle — svelte lifecycle/context calls guaranteed to run outside component
627
731
  * initialisation: module scope in runes modules / `<script module>`, the constructor of
628
732
  * a module-scope-instantiated class, and Kit load/handler/`init` bodies. A custom check
629
733
  * because the facts live on BOTH the component channel and the Kit-module channel.
630
734
  */
631
- declare const correct007OrphanLifecycle: Rule;
735
+ declare const correctnessOrphanLifecycle: Rule;
632
736
 
633
737
  /**
634
- * CORRECT008 — browser globals read in server-executed MODULE code: module scope of
738
+ * correctness/server-browser-global — browser globals read in server-executed MODULE code: module scope of
635
739
  * runes modules / `<script module>`, and Kit route/hooks files (top level, handler
636
740
  * bodies, the `init` hook). All of it runs on the server, where these globals do not
637
- * exist — SSR crashes with a ReferenceError. Instance-script reads are CORRECT009's
741
+ * exist — SSR crashes with a ReferenceError. Instance-script reads are correctness/instance-browser-global's
638
742
  * (warning) territory. A custom check because the facts live on both channels.
639
743
  */
640
- declare const correct008BrowserGlobals: Rule;
744
+ declare const correctnessServerBrowserGlobal: Rule;
745
+
746
+ declare const correctnessInstanceBrowserGlobal: Rule;
747
+
748
+ declare const securityRawHtml: Rule;
641
749
 
642
- declare const correct009InstanceBrowserGlobals: Rule;
750
+ declare const securityJavascriptUrl: Rule;
643
751
 
644
- declare const sec001Html: Rule;
645
- declare const sec002JavascriptUrl: Rule;
752
+ declare const securityHandlerStateWrite: Rule;
646
753
 
647
- declare const sec003LoadStateWrite: Rule;
754
+ declare const securityServerModuleState: Rule;
648
755
 
649
- declare const sec004ServerModuleState: Rule;
756
+ declare const securitySharedStateImport: Rule;
650
757
 
651
- declare const sec005SharedStateImport: Rule;
758
+ declare const architectureComponentSize: Rule;
652
759
 
653
- declare const arch001ComponentSize: Rule;
654
- declare const arch002PropCount: Rule;
760
+ declare const architecturePropCount: Rule;
655
761
 
656
- declare const perf009HeavyImport: Rule;
762
+ declare const performanceHeavyImport: Rule;
657
763
 
658
- declare const perf010NamespaceImport: Rule;
764
+ declare const performanceNamespaceImport: Rule;
765
+
766
+ /**
767
+ * performance/minify-disabled — a `build.minify: false` left in vite.config ships unminified JS/CSS
768
+ * to production. Project-scope: the fact is produced by the CLI's static parse
769
+ * of vite.config.* (literal-only) or by the Vite plugin's resolved config
770
+ * (exact). Emits a finding only when the fact is set — no pass result.
771
+ */
772
+ declare const performanceMinifyDisabled: Rule;
773
+
774
+ /**
775
+ * performance/load-waterfall — dependent await chains in universal loads. Server loads are exempt:
776
+ * a dependent chain cannot be parallelized, and on the server there is no better
777
+ * placement to suggest. csr = false files are exempt too — without a client
778
+ * runtime the universal load only runs during SSR.
779
+ */
780
+ declare const performanceLoadWaterfall: Rule;
781
+
782
+ /**
783
+ * performance/sequential-awaits — independent sequential awaits in any load. Info severity: static
784
+ * data flow cannot see side-effect ordering (e.g. a setup call an API relies
785
+ * on), so the parallelize suggestion stays advisory.
786
+ */
787
+ declare const performanceSequentialAwaits: Rule;
788
+
789
+ /**
790
+ * performance/state-raw — deep $state proxies every property access; a binding
791
+ * that is only ever reassigned never uses that machinery. Svelte's guidance:
792
+ * large reassign-only objects (API responses, canonically) belong in $state.raw.
793
+ * "Large" is not statically knowable, so a non-primitive literal initializer is
794
+ * the proxy condition.
795
+ */
796
+ declare const performanceStateRaw: Rule;
659
797
 
660
798
  declare const allRules: Rule[];
661
799
 
@@ -668,7 +806,7 @@ interface RuleInfo {
668
806
  docsUrl: string;
669
807
  fix?: Fix;
670
808
  }
671
- /** Look up a rule's static metadata for the MCP explain_rule tool (issue #24). Rule ids are matched case-insensitively. */
809
+ /** 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"). */
672
810
  declare function explainRule(id: string): RuleInfo | undefined;
673
811
 
674
812
  interface HeadTagRuleOptions {
@@ -699,7 +837,7 @@ interface ImageRuleOptions {
699
837
  id: string;
700
838
  title: string;
701
839
  severity: Severity;
702
- /** Vitals category (default 'performance'); SEO025 (alt text) reports under 'seo'. */
840
+ /** Vitals category (default 'performance'); seo/image-alt (alt text) reports under 'seo'. */
703
841
  category?: Category;
704
842
  /** Noun phrase for messages, e.g. '<img> width/height'. */
705
843
  label: string;
@@ -939,5 +1077,15 @@ declare function safeHref(url: string): string | null;
939
1077
  declare function selectRules(rules: Rule[], config: Config): Rule[];
940
1078
  /** Apply per-rule severity overrides to results (design §6). */
941
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[];
942
1090
 
943
- 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 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, applyRuleSeverities, arch001ComponentSize, arch002PropCount, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, computeHealth, computeScore, correct001EachKey, correct002EffectDerived, correct003EffectAsOnMount, correct004UnmutatedState, correct005PropMutation, correct006OrphanEffect, correct007OrphanLifecycle, correct008BrowserGlobals, correct009InstanceBrowserGlobals, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, formatAgentReport, formatConsoleReport, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, lineOf, linkRule, noColorPalette, parseComponentFacts, parseKitModuleFacts, perf001ImageDimensions, perf002ImageLoading, perf003PreloadAs, perf004FontPreloadCrossorigin, perf005LcpImage, perf006ResponsiveImage, perf007RenderBlockingScript, perf008Preconnect, perf009HeavyImport, perf010NamespaceImport, renderAppShell, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, sec001Html, sec002JavascriptUrl, sec003LoadStateWrite, sec004ServerModuleState, sec005SharedStateImport, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, seo010Indexability, seo011TwitterCard, seo012OgDescription, seo013OgUrl, seo014Viewport, seo015SitemapInRobots, seo016JsonLdValidity, seo017DeprecatedType, seo018RelativeUrl, seo019DateFormat, seo020Placeholder, seo021RequiredProps, seo022TitleLength, seo023DescriptionLength, seo024Charset, seo025ImageAlt, seo026Hreflang, seo027Heading, seo028TitleUnique, seo029DescriptionUnique, seo030HeadingOrder, summarize, textFromNodes, valueFromNodes };
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 };