@svelte-vitals/core 0.27.0 → 0.29.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 +267 -124
  2. package/dist/index.js +1429 -544
  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;
@@ -77,7 +90,7 @@ interface RuleOverride {
77
90
  route?: string | string[];
78
91
  /** Source-path glob(s) matched against a finding's location, e.g. 'src/routes/(app)/**'. */
79
92
  files?: string | string[];
80
- /** Keys are rule ids ('SEO001') or category names ('seo'). Rule id beats category within an entry. */
93
+ /** Keys are rule ids ('seo/title-presence') or category names ('seo'). Rule id beats category within an entry. */
81
94
  rules: Record<string, RuleSetting>;
82
95
  }
83
96
  interface Config {
@@ -143,9 +156,9 @@ interface HeadTag {
143
156
  text?: string;
144
157
  /** Literal `hreflang` of a `<link rel="alternate">` (e.g. 'en', 'en-US', 'x-default'). Undefined when dynamic/absent. */
145
158
  hreflang?: string;
146
- /** 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). */
147
160
  href?: string;
148
- /** 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). */
149
162
  blocking?: boolean;
150
163
  /** Where this tag was set relative to the route. Never 'none' (absence = no tag). */
151
164
  presence: Exclude<Presence, 'none'>;
@@ -184,11 +197,11 @@ interface ImageInfo {
184
197
  hasWidth: boolean;
185
198
  hasHeight: boolean;
186
199
  hasLoading: boolean;
187
- /** 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). */
188
201
  hasAlt: boolean;
189
- /** 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. */
190
203
  lazy: boolean;
191
- /** True when the <img> has a `srcset` attribute (PERF006). */
204
+ /** True when the <img> has a `srcset` attribute (performance/responsive-image). */
192
205
  hasSrcset: boolean;
193
206
  /** 1-based source line, or 0 if unknown. */
194
207
  line: number;
@@ -204,7 +217,7 @@ interface ResolvedImages {
204
217
  /**
205
218
  * A normalized page-body heading occurrence — the mode-independent boundary for
206
219
  * the heading-hierarchy rule (mirrors images.ts). Both providers collect these
207
- * so SEO027 never needs to know which mode produced them.
220
+ * so seo/single-h1 never needs to know which mode produced them.
208
221
  */
209
222
  interface HeadingInfo {
210
223
  /** Heading level 1–6 (the `n` in <hn>). */
@@ -231,6 +244,8 @@ interface EachBlockFact {
231
244
  hasKey: boolean;
232
245
  /** 1-based source line, or 0 if unknown. */
233
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;
234
249
  }
235
250
  /** An `$effect(...)` / `$effect.pre(...)` call in a component's instance script. */
236
251
  interface EffectFact {
@@ -238,10 +253,10 @@ interface EffectFact {
238
253
  line: number;
239
254
  /** True when the effect body only assigns to `$state` variables (the "use $derived" smell). */
240
255
  assignsOnlyState: boolean;
241
- /** 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). */
242
257
  mountOnly: boolean;
243
258
  }
244
- /** 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). */
245
260
  interface OrphanEffectFact {
246
261
  /** 1-based source line, or 0 if unknown. For 'constructor-instantiated', the module-scope `new` site. */
247
262
  line: number;
@@ -250,7 +265,7 @@ interface OrphanEffectFact {
250
265
  /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
251
266
  className?: string;
252
267
  }
253
- /** 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). */
254
269
  interface OrphanLifecycleCallFact {
255
270
  /** Canonical svelte export name (alias-resolved), e.g. 'onMount'. */
256
271
  name: string;
@@ -261,13 +276,13 @@ interface OrphanLifecycleCallFact {
261
276
  /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
262
277
  className?: string;
263
278
  }
264
- /** 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). */
265
280
  interface BrowserGlobalRefFact {
266
281
  /** The global's name, e.g. 'window'. */
267
282
  name: string;
268
283
  /** 1-based source line, or 0 if unknown. */
269
284
  line: number;
270
- /** '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). */
271
286
  context: 'module' | 'instance';
272
287
  }
273
288
  /** A flagged source position in a component (e.g. an `{@html}` tag or a `javascript:` URL). */
@@ -288,43 +303,61 @@ interface ComponentFacts {
288
303
  file: string;
289
304
  eachBlocks: EachBlockFact[];
290
305
  effects: EffectFact[];
291
- /** `{@html …}` occurrences — raw-HTML render surfaces (Security SEC001). */
306
+ /** `{@html …}` occurrences — raw-HTML render surfaces (security/raw-html). */
292
307
  htmlTags: SourceSpan[];
293
- /** Element attributes with a literal `javascript:` URL (Security SEC002). */
308
+ /** Element attributes with a literal `javascript:` URL (security/javascript-url). */
294
309
  javascriptUrls: SourceSpan[];
295
- /** Source line count of the component file (Architecture ARCH001). */
310
+ /** Source line count of the component file (architecture/component-size). */
296
311
  loc: number;
297
- /** 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). */
298
313
  propCount: number;
299
- /** 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). */
300
315
  imports: string[];
301
- /** 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). */
302
317
  importSpans: {
303
318
  source: string;
304
319
  line: number;
305
320
  }[];
306
- /** 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. */
307
322
  namespaceImports: {
308
323
  source: string;
309
324
  line: number;
310
325
  }[];
311
- /** `$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). */
312
327
  constableStates: {
313
328
  name: string;
314
329
  line: number;
315
330
  }[];
316
- /** 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()`, 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. */
317
332
  mutatedProps: {
318
333
  name: string;
319
334
  line: number;
335
+ legacy?: boolean;
336
+ }[];
337
+ /** 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. */
338
+ stalePropDerivations: {
339
+ name: string;
340
+ line: number;
341
+ legacy?: boolean;
342
+ }[];
343
+ /** Object/array-literal $state bindings reassigned at least once but never mutated, escaped, aliased, or item-edited — $state.raw candidates (performance/state-raw). */
344
+ rawableStates: {
345
+ name: string;
346
+ line: number;
320
347
  }[];
321
- /** `$effect` calls guaranteed to run outside component initialisation module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (CORRECT006). */
348
+ /** 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). */
349
+ nonreactiveBuiltinStates: {
350
+ name: string;
351
+ type: string;
352
+ line: number;
353
+ }[];
354
+ /** `$effect` calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-effect). */
322
355
  orphanEffects: OrphanEffectFact[];
323
- /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (CORRECT007). */
356
+ /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-lifecycle). */
324
357
  orphanLifecycleCalls: OrphanLifecycleCallFact[];
325
- /** Browser-global reads in server-executed positions of this file (CORRECT008/009). */
358
+ /** Browser-global reads in server-executed positions of this file (correctness/server-browser-global, correctness/instance-browser-global). */
326
359
  browserGlobalRefs: BrowserGlobalRefFact[];
327
- /** 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. */
360
+ /** 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. */
328
361
  moduleStateDecls: {
329
362
  name: string;
330
363
  line: number;
@@ -340,7 +373,7 @@ type ParsedFacts = Omit<ComponentFacts, 'file' | 'suppressions'> & {
340
373
  /**
341
374
  * Parse one source file's facts (CLI/static + vite build mode): a `.svelte` component's
342
375
  * reactivity/correctness + security + architecture facts, or a `.svelte.ts`/`.svelte.js`
343
- * runes module's orphan-$effect facts (CORRECT006).
376
+ * runes module's orphan-$effect facts (correctness/orphan-effect).
344
377
  */
345
378
  declare function parseComponentFacts(source: string, filename: string): ParsedFacts;
346
379
 
@@ -362,49 +395,62 @@ declare function collectComponentFacts(rt: Runtime, cwd: string): Promise<Compon
362
395
 
363
396
  /**
364
397
  * Facts parsed from one SvelteKit route/hooks file for the SSR shared-state rules
365
- * (SEC003–005). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
398
+ * (the security kit-module rules). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
366
399
  */
367
400
  interface KitModuleFacts {
368
401
  /** Repo-relative source file. */
369
402
  file: string;
370
403
  /** 'server' = runs only on the server (+*.server, +server, hooks.server); 'universal' = +page.ts/+layout.ts (still runs on the server during SSR). */
371
404
  kind: 'server' | 'universal';
372
- /** Module-scope let/var reassigned from inside a function (SEC004). */
405
+ /** Module-scope let/var reassigned from inside a function (security/server-module-state). */
373
406
  moduleStateReassignments: {
374
407
  name: string;
375
408
  line: number;
376
409
  inHandler: boolean;
377
410
  }[];
378
- /** Writes to an imported binding from inside an exported handler (SEC003). */
411
+ /** Writes to an imported binding from inside an exported handler (security/handler-state-write). */
379
412
  importedStateWrites: {
380
413
  name: string;
381
414
  line: number;
382
415
  via: 'assignment' | 'set-call';
383
416
  }[];
384
- /** Writes to an imported binding outside handlers — top level or helper functions (SEC005's write flavour). */
417
+ /** Writes to an imported binding outside handlers — top level or helper functions (security/shared-state-import's write flavour). */
385
418
  importedStateWritesOutsideHandlers: {
386
419
  name: string;
387
420
  line: number;
388
421
  }[];
389
- /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (SEC005). */
422
+ /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (security/shared-state-import). */
390
423
  runesModuleImports: {
391
424
  source: string;
392
425
  resolved: string;
393
426
  names: string[];
394
427
  line: number;
395
428
  }[];
396
- /** Svelte lifecycle/context calls that run outside component initialisation — top level, handler bodies, or the `init` hook (CORRECT007). */
429
+ /** Svelte lifecycle/context calls that run outside component initialisation — top level, handler bodies, or the `init` hook (correctness/orphan-lifecycle). */
397
430
  lifecycleCalls: {
398
431
  name: string;
399
432
  line: number;
400
433
  inHandler: boolean;
401
434
  }[];
402
- /** Browser-global reads in server-executed positions — top level, handler bodies, the `init` hook (CORRECT008). Empty when the file itself exports `ssr = false`. */
435
+ /** 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`. */
403
436
  browserGlobalRefs: {
404
437
  name: string;
405
438
  line: number;
406
439
  inHandler: boolean;
407
440
  }[];
441
+ /** Set when this file disables SSR via `export const ssr = false` (inline or same-file alias export) — the declaration's line (seo/ssr-disabled). */
442
+ ssrDisabled?: {
443
+ line: number;
444
+ };
445
+ /** 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. */
446
+ csrDisabled?: {
447
+ line: number;
448
+ };
449
+ /** 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. */
450
+ loadWaterfalls?: {
451
+ dependentLines: number[];
452
+ independentLines: number[];
453
+ };
408
454
  /** Inline `svelte-vitals-disable-next-line` directives in this file. */
409
455
  suppressions: SuppressionDirective[];
410
456
  }
@@ -414,12 +460,12 @@ interface KitModuleFacts {
414
460
  * undefined when it cannot be a runes module: `$lib/` maps to `src/lib/`, `./`/`../`
415
461
  * resolve against the importing file's directory; bare packages, other aliases, and
416
462
  * a relative specifier whose `..` segments escape the repo root are skipped. An
417
- * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (SEC005 also
463
+ * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (security/shared-state-import also
418
464
  * tries the `.js` sibling when matching).
419
465
  */
420
466
  declare function resolveRunesModuleSpecifier(spec: string, importerFile: string): string | undefined;
421
467
  /**
422
- * Parse one SvelteKit route/hooks file's SSR shared-state facts (SEC003–005). Uses
468
+ * Parse one SvelteKit route/hooks file's SSR shared-state facts (the security kit-module rules). Uses
423
469
  * the shared wrap parser (`parseModuleProgram`), so reported lines subtract the
424
470
  * 1-line wrap prefix; suppressions are scanned on the unwrapped source.
425
471
  */
@@ -428,7 +474,7 @@ declare function parseKitModuleFacts(source: string, filename: string): Omit<Kit
428
474
  /** Fallback facts for a Kit file that fails to read or parse (dev tooling must never throw). */
429
475
  declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind']): KitModuleFacts;
430
476
  /**
431
- * Scan SvelteKit route/hooks files for SSR shared-state facts (SEC003–005): route
477
+ * Scan SvelteKit route/hooks files for SSR shared-state facts (the security kit-module rules): route
432
478
  * `+page`/`+layout` server and universal modules, `+server` endpoints, and
433
479
  * `src/hooks.server`. `src/lib/server/**` is deliberately NOT scanned — legitimate
434
480
  * module singletons (DB connections, clients) live there (design). A file that
@@ -436,7 +482,17 @@ declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind'])
436
482
  */
437
483
  declare function collectKitModuleFacts(rt: Runtime, cwd: string): Promise<KitModuleFacts[]>;
438
484
 
439
- type Node = any;
485
+ /**
486
+ * The `build: { minify: false }` override, when present as a literal: returns the
487
+ * `minify` property's 1-based line in the ORIGINAL source. Undefined for clean,
488
+ * dynamic, or unparsable configs (never throws).
489
+ */
490
+ declare function findMinifyDisabled(source: string): {
491
+ line: number;
492
+ } | undefined;
493
+
494
+ /** A template fragment's child node relevant to value classification: literal text or a `{expr}`. */
495
+ type TextOrExpr = AST.Text | AST.ExpressionTag;
440
496
  /**
441
497
  * All keys that can bear child nodes in a Svelte AST node.
442
498
  * Covers if/each/await blocks (pending/then/catch/fallback) as well as
@@ -450,28 +506,28 @@ declare const CHILD_NODE_KEYS: string[];
450
506
  * - non-whitespace Text only → 'static'
451
507
  * - empty / whitespace only → 'absent'
452
508
  */
453
- declare function valueFromNodes(nodes: Node[]): Value;
509
+ declare function valueFromNodes(nodes: TextOrExpr[]): Value;
454
510
  /** The literal text of a node list when fully static (no ExpressionTag), else undefined. */
455
- declare function textFromNodes(nodes: Node[]): string | undefined;
511
+ declare function textFromNodes(nodes: TextOrExpr[]): string | undefined;
456
512
  /** Static string of an attribute (e.g. name="description"), or undefined if dynamic/absent. */
457
- declare function attrText(attributes: Node[], name: string): string | undefined;
513
+ declare function attrText(attributes: AST.Attribute[], name: string): string | undefined;
458
514
  /** Value kind of an attribute's content (e.g. the `content` of a <meta>). */
459
- declare function attrValue(attributes: Node[], name: string): Value;
515
+ declare function attrValue(attributes: AST.Attribute[], name: string): Value;
460
516
  declare function lineOf(source: string, offset: unknown): number;
461
- declare function findAttr(attributes: Node[], name: string): Node | undefined;
517
+ declare function findAttr(attributes: AST.Attribute[], name: string): AST.Attribute | undefined;
462
518
  /** Value kind of a single attribute (e.g. a component prop). */
463
- declare function attrValueOf(attr: Node): Value;
519
+ declare function attrValueOf(attr: AST.Attribute): Value;
464
520
  /** Literal static text of a single attribute node (e.g. a component prop), or undefined if dynamic/absent. */
465
- declare function attrTextOf(attr: Node): string | undefined;
521
+ declare function attrTextOf(attr: AST.Attribute): string | undefined;
466
522
 
467
523
  /**
468
524
  * Source-file locations that satisfy the project-scope rules, shared by every
469
525
  * mode so the static (CLI) and rendered (plugin) collectors never drift. This
470
526
  * module is pure data: no I/O, no `node:` imports (design §8).
471
527
  */
472
- /** Locations that satisfy the robots.txt project rule (SEO006). */
528
+ /** Locations that satisfy the robots.txt project rule (seo/robots-txt). */
473
529
  declare const ROBOTS_SOURCE_PATHS: readonly ["static/robots.txt", "src/routes/robots.txt/+server.ts", "src/routes/robots.txt/+server.js"];
474
- /** Locations that satisfy the sitemap.xml project rule (SEO007). */
530
+ /** Locations that satisfy the sitemap.xml project rule (seo/sitemap-xml). */
475
531
  declare const SITEMAP_SOURCE_PATHS: readonly ["static/sitemap.xml", "src/routes/sitemap.xml/+server.ts", "src/routes/sitemap.xml/+server.js"];
476
532
 
477
533
  /** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */
@@ -479,7 +535,7 @@ interface RuleContext {
479
535
  heads: ResolvedHead[];
480
536
  /** Per-route <img> elements for Performance rules (absent in modes that don't collect them). */
481
537
  images?: ResolvedImages[];
482
- /** Per-route page-body headings for SEO027 (absent in modes that don't collect them). */
538
+ /** Per-route page-body headings for seo/single-h1 (absent in modes that don't collect them). */
483
539
  headings?: ResolvedHeadings[];
484
540
  /** Per-file component-body facts for Correctness rules (static/CLI mode only). */
485
541
  components?: ComponentFacts[];
@@ -527,157 +583,244 @@ declare function isPenalized(detection: Detection, treatDynamicAs: TreatDynamicA
527
583
  declare function runRules(rules: Rule[], ctx: RuleContext): Promise<Result[]>;
528
584
 
529
585
  /**
530
- * SEO001 — every route should resolve a non-empty <title> (design §11).
586
+ * seo/title-presence — every route should resolve a non-empty <title> (design §11).
531
587
  * A dynamic title (`{data.title}`) is the most common correct pattern and must
532
588
  * never be flagged as missing; it surfaces as value 'dynamic' (design §4).
533
589
  */
534
- declare const seo001Title: Rule;
590
+ declare const seoTitlePresence: Rule;
591
+
592
+ declare const seoDescriptionPresence: Rule;
593
+
594
+ declare const seoCanonicalUrl: Rule;
595
+
596
+ declare const seoOgImage: Rule;
597
+
598
+ declare const seoOgTitle: Rule;
599
+
600
+ declare const seoJsonLd: Rule;
535
601
 
536
- declare const seo002Description: Rule;
537
- declare const seo003Canonical: Rule;
538
- declare const seo004OgImage: Rule;
539
- declare const seo005OgTitle: Rule;
540
- declare const seo008JsonLd: Rule;
602
+ declare const seoRobotsTxt: Rule;
541
603
 
542
- declare const seo006Robots: Rule;
543
- declare const seo007Sitemap: Rule;
544
- declare const seo009HtmlLang: Rule;
604
+ declare const seoSitemapXml: Rule;
545
605
 
546
- declare const perf001ImageDimensions: Rule;
547
- declare const perf002ImageLoading: Rule;
548
- declare const perf006ResponsiveImage: Rule;
606
+ declare const seoHtmlLang: Rule;
549
607
 
550
- declare const perf003PreloadAs: Rule;
551
- declare const perf004FontPreloadCrossorigin: Rule;
608
+ declare const performanceImageDimensions: Rule;
609
+
610
+ declare const performanceImageLoadingHint: Rule;
611
+
612
+ declare const performanceResponsiveImage: Rule;
613
+
614
+ declare const performancePreloadMissingAs: Rule;
615
+
616
+ declare const performanceFontPreloadCrossorigin: Rule;
552
617
 
553
618
  /**
554
- * PERF005 — LCP image not lazy-loaded. Lazy-loading the largest contentful paint
619
+ * performance/lcp-image — LCP image not lazy-loaded. Lazy-loading the largest contentful paint
555
620
  * image delays it. Analysis approximates the LCP as the first <img> in document
556
621
  * order for the route; if that image is loading="lazy", flag it. Runs in both
557
622
  * static (CLI) and rendered (vite) mode, since both providers collect <img>.
558
623
  */
559
- declare const perf005LcpImage: Rule;
624
+ declare const performanceLcpImage: Rule;
560
625
 
561
626
  /**
562
- * PERF007 — Render-blocking <script> in <head>. A <script src> without
627
+ * performance/render-blocking-script — Render-blocking <script> in <head>. A <script src> without
563
628
  * defer/async/type=module blocks the parser. SvelteKit's own scripts are
564
629
  * module/deferred, so this catches hand-added blocking scripts — in app.html
565
630
  * (rendered mode) or in <svelte:head> (static mode). A head with no <script>
566
631
  * emits nothing (no signal), like the image rules.
567
632
  */
568
- declare const perf007RenderBlockingScript: Rule;
633
+ declare const performanceRenderBlockingScript: Rule;
569
634
 
570
635
  /**
571
- * PERF008 — Preconnect for third-party origins. A resource from a well-known
636
+ * performance/preconnect — Preconnect for third-party origins. A resource from a well-known
572
637
  * third-party origin (e.g. Google Fonts) without a preconnect/dns-prefetch pays a
573
638
  * connection-setup round-trip. Opt-in by construction: only origins in the
574
639
  * allowlist are checked; routes referencing none emit nothing.
575
640
  */
576
- declare const perf008Preconnect: Rule;
641
+ declare const performancePreconnect: Rule;
642
+
643
+ declare const seoIndexability: Rule;
644
+
645
+ declare const seoTwitterCard: Rule;
646
+
647
+ declare const seoOgDescription: Rule;
648
+
649
+ declare const seoOgUrl: Rule;
577
650
 
578
- declare const seo010Indexability: Rule;
579
- declare const seo011TwitterCard: Rule;
580
- declare const seo012OgDescription: Rule;
581
- declare const seo013OgUrl: Rule;
582
- declare const seo014Viewport: Rule;
583
- declare const seo015SitemapInRobots: Rule;
651
+ declare const seoViewport: Rule;
584
652
 
585
- declare const seo016JsonLdValidity: Rule;
586
- declare const seo017DeprecatedType: Rule;
587
- declare const seo018RelativeUrl: Rule;
588
- declare const seo019DateFormat: Rule;
589
- declare const seo020Placeholder: Rule;
590
- declare const seo021RequiredProps: Rule;
653
+ declare const seoSitemapInRobots: Rule;
591
654
 
592
- declare const seo022TitleLength: Rule;
593
- declare const seo023DescriptionLength: Rule;
655
+ declare const seoJsonLdValidity: Rule;
656
+
657
+ declare const seoJsonLdDeprecatedType: Rule;
658
+
659
+ declare const seoJsonLdRelativeUrl: Rule;
660
+
661
+ declare const seoJsonLdDateFormat: Rule;
662
+
663
+ declare const seoJsonLdPlaceholder: Rule;
664
+
665
+ declare const seoJsonLdRequiredProps: Rule;
666
+
667
+ declare const seoTitleLength: Rule;
668
+
669
+ declare const seoDescriptionLength: Rule;
594
670
 
595
671
  /**
596
- * SEO024 — Character encoding. The charset meta lives in `src/app.html`, so it is
597
- * only visible to rendered analysis (`appliesTo: rendered`), exactly like SEO014
672
+ * seo/charset — Character encoding. The charset meta lives in `src/app.html`, so it is
673
+ * only visible to rendered analysis (`appliesTo: rendered`), exactly like seo/viewport
598
674
  * (viewport). Static route analysis emits nothing instead of false-flagging it.
599
675
  */
600
- declare const seo024Charset: Rule;
676
+ declare const seoCharset: Rule;
601
677
 
602
678
  /**
603
- * SEO025 — Image alt text. Reuses the <img> collection from both providers — the
604
- * static (CLI) source parser and the rendered (vite) HTML parser — like PERF001/002.
679
+ * seo/image-alt — Image alt text. Reuses the <img> collection from both providers — the
680
+ * static (CLI) source parser and the rendered (vite) HTML parser — like performance/image-dimensions, performance/image-loading-hint.
605
681
  * Presence only: an explicit empty `alt=""` is a valid decorative-image signal and
606
682
  * passes; a spread `{...rest}` may supply alt, so it is not flagged.
607
683
  */
608
- declare const seo025ImageAlt: Rule;
684
+ declare const seoImageAlt: Rule;
609
685
 
610
686
  /**
611
- * SEO026 — hreflang / x-default validity. Opt-in: a route with no
687
+ * seo/hreflang — hreflang / x-default validity. Opt-in: a route with no
612
688
  * `<link rel="alternate" hreflang>` emits nothing (monolingual sites are never
613
689
  * flagged). When alternates exist, every code must be well-formed and a set of
614
690
  * two or more must declare an x-default. Works in both modes.
615
691
  */
616
- declare const seo026Hreflang: Rule;
692
+ declare const seoHreflang: Rule;
617
693
 
618
694
  /**
619
- * SEO027 — Heading hierarchy (single H1). Reads the per-route page-body headings
695
+ * seo/single-h1 — Heading hierarchy (single H1). Reads the per-route page-body headings
620
696
  * channel (collected by both providers). Zero <h1> (no primary heading) and two
621
697
  * or more (diluted topic) are both flagged; exactly one passes. A route whose
622
698
  * headings were not collected (channel unset) emits nothing.
623
699
  */
624
- declare const seo027Heading: Rule;
700
+ declare const seoSingleH1: Rule;
625
701
 
626
- declare const seo028TitleUnique: Rule;
627
- declare const seo029DescriptionUnique: Rule;
702
+ declare const seoDuplicateTitle: Rule;
703
+
704
+ declare const seoDuplicateDescription: Rule;
628
705
 
629
706
  /**
630
- * SEO030 — Skipped heading level. Walking a route's body headings in document
631
- * order, a level that jumps more than +1 over the previous heading (e.g. h2 → h4)
632
- * breaks the outline. The first heading has no predecessor (missing/multiple
633
- * <h1> stays SEO027's concern). A route with no headings emits nothing.
707
+ * seo/heading-level-skip — Skipped heading level. Walking a route's body headings in
708
+ * document order, a level that jumps more than +1 over the previous heading (e.g.
709
+ * h2 → h4) breaks the outline. The first heading has no predecessor (missing/multiple
710
+ * <h1> stays seo/single-h1's concern). A route with no headings emits nothing.
634
711
  */
635
- declare const seo030HeadingOrder: Rule;
712
+ declare const seoHeadingLevelSkip: Rule;
713
+
714
+ declare const seoSsrDisabled: Rule;
636
715
 
637
- declare const correct001EachKey: Rule;
638
- declare const correct002EffectDerived: Rule;
639
- declare const correct003EffectAsOnMount: Rule;
716
+ declare const correctnessEachKey: Rule;
640
717
 
641
- declare const correct004UnmutatedState: Rule;
718
+ declare const correctnessEachIndexKey: Rule;
719
+
720
+ declare const correctnessEffectAsDerived: Rule;
721
+
722
+ declare const correctnessEffectAsOnMount: Rule;
723
+
724
+ declare const correctnessUnmutatedState: Rule;
725
+
726
+ /**
727
+ * correctness/prop-mutation — mutating a prop directly is a silent bug in both Svelte modes,
728
+ * for different reasons: in runes mode, a non-$bindable prop mutation doesn't propagate to the
729
+ * parent; in legacy mode (export let), Svelte's reactivity is assignment-based, so a mutating
730
+ * method call (`.push(...)`, etc.) doesn't trigger an update at all without a following
731
+ * reassignment. The two modes can't be mixed in one component, so a given finding is always
732
+ * exactly one or the other — see `legacy` on `ComponentFacts.mutatedProps` (component-parse.ts).
733
+ */
734
+ declare const correctnessPropMutation: Rule;
735
+
736
+ /**
737
+ * correctness/stale-prop-derivation — a value computed from a prop without $derived (runes
738
+ * mode) or $: (legacy mode) is evaluated once, at init, and silently stops tracking the
739
+ * parent. Svelte's own guidance: treat props as though they will change. The two modes can't
740
+ * be mixed in one component, so a given finding is always exactly one or the other — see
741
+ * `legacy` on `ComponentFacts.stalePropDerivations` (component-parse.ts).
742
+ */
743
+ declare const correctnessStalePropDerivation: Rule;
642
744
 
643
- declare const correct005PropMutation: Rule;
745
+ /**
746
+ * correctness/nonreactive-builtin-state — $state's deep proxy covers plain
747
+ * objects and arrays only. A plain Map/Set/Date/URL/URLSearchParams in $state
748
+ * keeps working as data, but its mutations never reach effects, deriveds, or
749
+ * the template: the UI silently stops updating. svelte/reactivity ships
750
+ * drop-in reactive equivalents for exactly this.
751
+ */
752
+ declare const correctnessNonreactiveBuiltinState: Rule;
644
753
 
645
- declare const correct006OrphanEffect: Rule;
754
+ declare const correctnessOrphanEffect: Rule;
646
755
 
647
756
  /**
648
- * CORRECT007 — svelte lifecycle/context calls guaranteed to run outside component
757
+ * correctness/orphan-lifecycle — svelte lifecycle/context calls guaranteed to run outside component
649
758
  * initialisation: module scope in runes modules / `<script module>`, the constructor of
650
759
  * a module-scope-instantiated class, and Kit load/handler/`init` bodies. A custom check
651
760
  * because the facts live on BOTH the component channel and the Kit-module channel.
652
761
  */
653
- declare const correct007OrphanLifecycle: Rule;
762
+ declare const correctnessOrphanLifecycle: Rule;
654
763
 
655
764
  /**
656
- * CORRECT008 — browser globals read in server-executed MODULE code: module scope of
765
+ * correctness/server-browser-global — browser globals read in server-executed MODULE code: module scope of
657
766
  * runes modules / `<script module>`, and Kit route/hooks files (top level, handler
658
767
  * bodies, the `init` hook). All of it runs on the server, where these globals do not
659
- * exist — SSR crashes with a ReferenceError. Instance-script reads are CORRECT009's
768
+ * exist — SSR crashes with a ReferenceError. Instance-script reads are correctness/instance-browser-global's
660
769
  * (warning) territory. A custom check because the facts live on both channels.
661
770
  */
662
- declare const correct008BrowserGlobals: Rule;
771
+ declare const correctnessServerBrowserGlobal: Rule;
772
+
773
+ declare const correctnessInstanceBrowserGlobal: Rule;
774
+
775
+ declare const securityRawHtml: Rule;
663
776
 
664
- declare const correct009InstanceBrowserGlobals: Rule;
777
+ declare const securityJavascriptUrl: Rule;
665
778
 
666
- declare const sec001Html: Rule;
667
- declare const sec002JavascriptUrl: Rule;
779
+ declare const securityHandlerStateWrite: Rule;
668
780
 
669
- declare const sec003LoadStateWrite: Rule;
781
+ declare const securityServerModuleState: Rule;
670
782
 
671
- declare const sec004ServerModuleState: Rule;
783
+ declare const securitySharedStateImport: Rule;
672
784
 
673
- declare const sec005SharedStateImport: Rule;
785
+ declare const architectureComponentSize: Rule;
674
786
 
675
- declare const arch001ComponentSize: Rule;
676
- declare const arch002PropCount: Rule;
787
+ declare const architecturePropCount: Rule;
677
788
 
678
- declare const perf009HeavyImport: Rule;
789
+ declare const performanceHeavyImport: Rule;
790
+
791
+ declare const performanceNamespaceImport: Rule;
792
+
793
+ /**
794
+ * performance/minify-disabled — a `build.minify: false` left in vite.config ships unminified JS/CSS
795
+ * to production. Project-scope: the fact is produced by the CLI's static parse
796
+ * of vite.config.* (literal-only) or by the Vite plugin's resolved config
797
+ * (exact). Emits a finding only when the fact is set — no pass result.
798
+ */
799
+ declare const performanceMinifyDisabled: Rule;
679
800
 
680
- declare const perf010NamespaceImport: Rule;
801
+ /**
802
+ * performance/load-waterfall — dependent await chains in universal loads. Server loads are exempt:
803
+ * a dependent chain cannot be parallelized, and on the server there is no better
804
+ * placement to suggest. csr = false files are exempt too — without a client
805
+ * runtime the universal load only runs during SSR.
806
+ */
807
+ declare const performanceLoadWaterfall: Rule;
808
+
809
+ /**
810
+ * performance/sequential-awaits — independent sequential awaits in any load. Info severity: static
811
+ * data flow cannot see side-effect ordering (e.g. a setup call an API relies
812
+ * on), so the parallelize suggestion stays advisory.
813
+ */
814
+ declare const performanceSequentialAwaits: Rule;
815
+
816
+ /**
817
+ * performance/state-raw — deep $state proxies every property access; a binding
818
+ * that is only ever reassigned never uses that machinery. Svelte's guidance:
819
+ * large reassign-only objects (API responses, canonically) belong in $state.raw.
820
+ * "Large" is not statically knowable, so a non-primitive literal initializer is
821
+ * the proxy condition.
822
+ */
823
+ declare const performanceStateRaw: Rule;
681
824
 
682
825
  declare const allRules: Rule[];
683
826
 
@@ -690,7 +833,7 @@ interface RuleInfo {
690
833
  docsUrl: string;
691
834
  fix?: Fix;
692
835
  }
693
- /** Look up a rule's static metadata for the MCP explain_rule tool (issue #24). Rule ids are matched case-insensitively. */
836
+ /** 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"). */
694
837
  declare function explainRule(id: string): RuleInfo | undefined;
695
838
 
696
839
  interface HeadTagRuleOptions {
@@ -721,7 +864,7 @@ interface ImageRuleOptions {
721
864
  id: string;
722
865
  title: string;
723
866
  severity: Severity;
724
- /** Vitals category (default 'performance'); SEO025 (alt text) reports under 'seo'. */
867
+ /** Vitals category (default 'performance'); seo/image-alt (alt text) reports under 'seo'. */
725
868
  category?: Category;
726
869
  /** Noun phrase for messages, e.g. '<img> width/height'. */
727
870
  label: string;
@@ -972,4 +1115,4 @@ declare function applyRuleSeverities(results: Result[], config: Config): Result[
972
1115
  */
973
1116
  declare function applyOverrides(results: Result[], config: Config): Result[];
974
1117
 
975
- 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, 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 };
1118
+ 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, correctnessNonreactiveBuiltinState, 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 };