@svelte-vitals/core 0.27.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 +240 -124
  2. package/dist/index.js +1280 -537
  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,53 @@ 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()` — member writes, `delete`, or a mutating method call (correctness/prop-mutation). */
317
332
  mutatedProps: {
318
333
  name: string;
319
334
  line: number;
320
335
  }[];
321
- /** `$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). */
322
347
  orphanEffects: OrphanEffectFact[];
323
- /** 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). */
324
349
  orphanLifecycleCalls: OrphanLifecycleCallFact[];
325
- /** 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). */
326
351
  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. */
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. */
328
353
  moduleStateDecls: {
329
354
  name: string;
330
355
  line: number;
@@ -340,7 +365,7 @@ type ParsedFacts = Omit<ComponentFacts, 'file' | 'suppressions'> & {
340
365
  /**
341
366
  * Parse one source file's facts (CLI/static + vite build mode): a `.svelte` component's
342
367
  * reactivity/correctness + security + architecture facts, or a `.svelte.ts`/`.svelte.js`
343
- * runes module's orphan-$effect facts (CORRECT006).
368
+ * runes module's orphan-$effect facts (correctness/orphan-effect).
344
369
  */
345
370
  declare function parseComponentFacts(source: string, filename: string): ParsedFacts;
346
371
 
@@ -362,49 +387,62 @@ declare function collectComponentFacts(rt: Runtime, cwd: string): Promise<Compon
362
387
 
363
388
  /**
364
389
  * 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).
390
+ * (the security kit-module rules). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
366
391
  */
367
392
  interface KitModuleFacts {
368
393
  /** Repo-relative source file. */
369
394
  file: string;
370
395
  /** 'server' = runs only on the server (+*.server, +server, hooks.server); 'universal' = +page.ts/+layout.ts (still runs on the server during SSR). */
371
396
  kind: 'server' | 'universal';
372
- /** Module-scope let/var reassigned from inside a function (SEC004). */
397
+ /** Module-scope let/var reassigned from inside a function (security/server-module-state). */
373
398
  moduleStateReassignments: {
374
399
  name: string;
375
400
  line: number;
376
401
  inHandler: boolean;
377
402
  }[];
378
- /** 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). */
379
404
  importedStateWrites: {
380
405
  name: string;
381
406
  line: number;
382
407
  via: 'assignment' | 'set-call';
383
408
  }[];
384
- /** 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). */
385
410
  importedStateWritesOutsideHandlers: {
386
411
  name: string;
387
412
  line: number;
388
413
  }[];
389
- /** 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). */
390
415
  runesModuleImports: {
391
416
  source: string;
392
417
  resolved: string;
393
418
  names: string[];
394
419
  line: number;
395
420
  }[];
396
- /** 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). */
397
422
  lifecycleCalls: {
398
423
  name: string;
399
424
  line: number;
400
425
  inHandler: boolean;
401
426
  }[];
402
- /** 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`. */
403
428
  browserGlobalRefs: {
404
429
  name: string;
405
430
  line: number;
406
431
  inHandler: boolean;
407
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
+ };
408
446
  /** Inline `svelte-vitals-disable-next-line` directives in this file. */
409
447
  suppressions: SuppressionDirective[];
410
448
  }
@@ -414,12 +452,12 @@ interface KitModuleFacts {
414
452
  * undefined when it cannot be a runes module: `$lib/` maps to `src/lib/`, `./`/`../`
415
453
  * resolve against the importing file's directory; bare packages, other aliases, and
416
454
  * a relative specifier whose `..` segments escape the repo root are skipped. An
417
- * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (SEC005 also
455
+ * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (security/shared-state-import also
418
456
  * tries the `.js` sibling when matching).
419
457
  */
420
458
  declare function resolveRunesModuleSpecifier(spec: string, importerFile: string): string | undefined;
421
459
  /**
422
- * 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
423
461
  * the shared wrap parser (`parseModuleProgram`), so reported lines subtract the
424
462
  * 1-line wrap prefix; suppressions are scanned on the unwrapped source.
425
463
  */
@@ -428,7 +466,7 @@ declare function parseKitModuleFacts(source: string, filename: string): Omit<Kit
428
466
  /** Fallback facts for a Kit file that fails to read or parse (dev tooling must never throw). */
429
467
  declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind']): KitModuleFacts;
430
468
  /**
431
- * 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
432
470
  * `+page`/`+layout` server and universal modules, `+server` endpoints, and
433
471
  * `src/hooks.server`. `src/lib/server/**` is deliberately NOT scanned — legitimate
434
472
  * module singletons (DB connections, clients) live there (design). A file that
@@ -436,7 +474,17 @@ declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind'])
436
474
  */
437
475
  declare function collectKitModuleFacts(rt: Runtime, cwd: string): Promise<KitModuleFacts[]>;
438
476
 
439
- 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;
440
488
  /**
441
489
  * All keys that can bear child nodes in a Svelte AST node.
442
490
  * Covers if/each/await blocks (pending/then/catch/fallback) as well as
@@ -450,28 +498,28 @@ declare const CHILD_NODE_KEYS: string[];
450
498
  * - non-whitespace Text only → 'static'
451
499
  * - empty / whitespace only → 'absent'
452
500
  */
453
- declare function valueFromNodes(nodes: Node[]): Value;
501
+ declare function valueFromNodes(nodes: TextOrExpr[]): Value;
454
502
  /** The literal text of a node list when fully static (no ExpressionTag), else undefined. */
455
- declare function textFromNodes(nodes: Node[]): string | undefined;
503
+ declare function textFromNodes(nodes: TextOrExpr[]): string | undefined;
456
504
  /** Static string of an attribute (e.g. name="description"), or undefined if dynamic/absent. */
457
- declare function attrText(attributes: Node[], name: string): string | undefined;
505
+ declare function attrText(attributes: AST.Attribute[], name: string): string | undefined;
458
506
  /** Value kind of an attribute's content (e.g. the `content` of a <meta>). */
459
- declare function attrValue(attributes: Node[], name: string): Value;
507
+ declare function attrValue(attributes: AST.Attribute[], name: string): Value;
460
508
  declare function lineOf(source: string, offset: unknown): number;
461
- declare function findAttr(attributes: Node[], name: string): Node | undefined;
509
+ declare function findAttr(attributes: AST.Attribute[], name: string): AST.Attribute | undefined;
462
510
  /** Value kind of a single attribute (e.g. a component prop). */
463
- declare function attrValueOf(attr: Node): Value;
511
+ declare function attrValueOf(attr: AST.Attribute): Value;
464
512
  /** 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;
513
+ declare function attrTextOf(attr: AST.Attribute): string | undefined;
466
514
 
467
515
  /**
468
516
  * Source-file locations that satisfy the project-scope rules, shared by every
469
517
  * mode so the static (CLI) and rendered (plugin) collectors never drift. This
470
518
  * module is pure data: no I/O, no `node:` imports (design §8).
471
519
  */
472
- /** Locations that satisfy the robots.txt project rule (SEO006). */
520
+ /** Locations that satisfy the robots.txt project rule (seo/robots-txt). */
473
521
  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). */
522
+ /** Locations that satisfy the sitemap.xml project rule (seo/sitemap-xml). */
475
523
  declare const SITEMAP_SOURCE_PATHS: readonly ["static/sitemap.xml", "src/routes/sitemap.xml/+server.ts", "src/routes/sitemap.xml/+server.js"];
476
524
 
477
525
  /** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */
@@ -479,7 +527,7 @@ interface RuleContext {
479
527
  heads: ResolvedHead[];
480
528
  /** Per-route <img> elements for Performance rules (absent in modes that don't collect them). */
481
529
  images?: ResolvedImages[];
482
- /** 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). */
483
531
  headings?: ResolvedHeadings[];
484
532
  /** Per-file component-body facts for Correctness rules (static/CLI mode only). */
485
533
  components?: ComponentFacts[];
@@ -527,157 +575,225 @@ declare function isPenalized(detection: Detection, treatDynamicAs: TreatDynamicA
527
575
  declare function runRules(rules: Rule[], ctx: RuleContext): Promise<Result[]>;
528
576
 
529
577
  /**
530
- * 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).
531
579
  * A dynamic title (`{data.title}`) is the most common correct pattern and must
532
580
  * never be flagged as missing; it surfaces as value 'dynamic' (design §4).
533
581
  */
534
- declare const seo001Title: Rule;
582
+ declare const seoTitlePresence: Rule;
583
+
584
+ declare const seoDescriptionPresence: Rule;
585
+
586
+ declare const seoCanonicalUrl: Rule;
587
+
588
+ declare const seoOgImage: Rule;
589
+
590
+ declare const seoOgTitle: Rule;
591
+
592
+ declare const seoJsonLd: Rule;
535
593
 
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;
594
+ declare const seoRobotsTxt: Rule;
541
595
 
542
- declare const seo006Robots: Rule;
543
- declare const seo007Sitemap: Rule;
544
- declare const seo009HtmlLang: Rule;
596
+ declare const seoSitemapXml: Rule;
545
597
 
546
- declare const perf001ImageDimensions: Rule;
547
- declare const perf002ImageLoading: Rule;
548
- declare const perf006ResponsiveImage: Rule;
598
+ declare const seoHtmlLang: Rule;
549
599
 
550
- declare const perf003PreloadAs: Rule;
551
- declare const perf004FontPreloadCrossorigin: Rule;
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;
552
609
 
553
610
  /**
554
- * 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
555
612
  * image delays it. Analysis approximates the LCP as the first <img> in document
556
613
  * order for the route; if that image is loading="lazy", flag it. Runs in both
557
614
  * static (CLI) and rendered (vite) mode, since both providers collect <img>.
558
615
  */
559
- declare const perf005LcpImage: Rule;
616
+ declare const performanceLcpImage: Rule;
560
617
 
561
618
  /**
562
- * PERF007 — Render-blocking <script> in <head>. A <script src> without
619
+ * performance/render-blocking-script — Render-blocking <script> in <head>. A <script src> without
563
620
  * defer/async/type=module blocks the parser. SvelteKit's own scripts are
564
621
  * module/deferred, so this catches hand-added blocking scripts — in app.html
565
622
  * (rendered mode) or in <svelte:head> (static mode). A head with no <script>
566
623
  * emits nothing (no signal), like the image rules.
567
624
  */
568
- declare const perf007RenderBlockingScript: Rule;
625
+ declare const performanceRenderBlockingScript: Rule;
569
626
 
570
627
  /**
571
- * 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
572
629
  * third-party origin (e.g. Google Fonts) without a preconnect/dns-prefetch pays a
573
630
  * connection-setup round-trip. Opt-in by construction: only origins in the
574
631
  * allowlist are checked; routes referencing none emit nothing.
575
632
  */
576
- declare const perf008Preconnect: Rule;
633
+ declare const performancePreconnect: Rule;
634
+
635
+ declare const seoIndexability: Rule;
636
+
637
+ declare const seoTwitterCard: Rule;
638
+
639
+ declare const seoOgDescription: Rule;
577
640
 
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;
641
+ declare const seoOgUrl: Rule;
584
642
 
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;
643
+ declare const seoViewport: Rule;
591
644
 
592
- declare const seo022TitleLength: Rule;
593
- declare const seo023DescriptionLength: Rule;
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;
594
662
 
595
663
  /**
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
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
598
666
  * (viewport). Static route analysis emits nothing instead of false-flagging it.
599
667
  */
600
- declare const seo024Charset: Rule;
668
+ declare const seoCharset: Rule;
601
669
 
602
670
  /**
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.
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.
605
673
  * Presence only: an explicit empty `alt=""` is a valid decorative-image signal and
606
674
  * passes; a spread `{...rest}` may supply alt, so it is not flagged.
607
675
  */
608
- declare const seo025ImageAlt: Rule;
676
+ declare const seoImageAlt: Rule;
609
677
 
610
678
  /**
611
- * SEO026 — hreflang / x-default validity. Opt-in: a route with no
679
+ * seo/hreflang — hreflang / x-default validity. Opt-in: a route with no
612
680
  * `<link rel="alternate" hreflang>` emits nothing (monolingual sites are never
613
681
  * flagged). When alternates exist, every code must be well-formed and a set of
614
682
  * two or more must declare an x-default. Works in both modes.
615
683
  */
616
- declare const seo026Hreflang: Rule;
684
+ declare const seoHreflang: Rule;
617
685
 
618
686
  /**
619
- * 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
620
688
  * channel (collected by both providers). Zero <h1> (no primary heading) and two
621
689
  * or more (diluted topic) are both flagged; exactly one passes. A route whose
622
690
  * headings were not collected (channel unset) emits nothing.
623
691
  */
624
- declare const seo027Heading: Rule;
692
+ declare const seoSingleH1: Rule;
625
693
 
626
- declare const seo028TitleUnique: Rule;
627
- declare const seo029DescriptionUnique: Rule;
694
+ declare const seoDuplicateTitle: Rule;
695
+
696
+ declare const seoDuplicateDescription: Rule;
628
697
 
629
698
  /**
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.
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.
634
703
  */
635
- declare const seo030HeadingOrder: Rule;
704
+ declare const seoHeadingLevelSkip: Rule;
705
+
706
+ declare const seoSsrDisabled: Rule;
707
+
708
+ declare const correctnessEachKey: Rule;
709
+
710
+ declare const correctnessEachIndexKey: Rule;
636
711
 
637
- declare const correct001EachKey: Rule;
638
- declare const correct002EffectDerived: Rule;
639
- declare const correct003EffectAsOnMount: Rule;
712
+ declare const correctnessEffectAsDerived: Rule;
640
713
 
641
- declare const correct004UnmutatedState: Rule;
714
+ declare const correctnessEffectAsOnMount: Rule;
642
715
 
643
- declare const correct005PropMutation: Rule;
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;
644
726
 
645
- declare const correct006OrphanEffect: Rule;
727
+ declare const correctnessOrphanEffect: Rule;
646
728
 
647
729
  /**
648
- * CORRECT007 — svelte lifecycle/context calls guaranteed to run outside component
730
+ * correctness/orphan-lifecycle — svelte lifecycle/context calls guaranteed to run outside component
649
731
  * initialisation: module scope in runes modules / `<script module>`, the constructor of
650
732
  * a module-scope-instantiated class, and Kit load/handler/`init` bodies. A custom check
651
733
  * because the facts live on BOTH the component channel and the Kit-module channel.
652
734
  */
653
- declare const correct007OrphanLifecycle: Rule;
735
+ declare const correctnessOrphanLifecycle: Rule;
654
736
 
655
737
  /**
656
- * 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
657
739
  * runes modules / `<script module>`, and Kit route/hooks files (top level, handler
658
740
  * 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
741
+ * exist — SSR crashes with a ReferenceError. Instance-script reads are correctness/instance-browser-global's
660
742
  * (warning) territory. A custom check because the facts live on both channels.
661
743
  */
662
- declare const correct008BrowserGlobals: Rule;
744
+ declare const correctnessServerBrowserGlobal: Rule;
745
+
746
+ declare const correctnessInstanceBrowserGlobal: Rule;
747
+
748
+ declare const securityRawHtml: Rule;
749
+
750
+ declare const securityJavascriptUrl: Rule;
663
751
 
664
- declare const correct009InstanceBrowserGlobals: Rule;
752
+ declare const securityHandlerStateWrite: Rule;
665
753
 
666
- declare const sec001Html: Rule;
667
- declare const sec002JavascriptUrl: Rule;
754
+ declare const securityServerModuleState: Rule;
668
755
 
669
- declare const sec003LoadStateWrite: Rule;
756
+ declare const securitySharedStateImport: Rule;
670
757
 
671
- declare const sec004ServerModuleState: Rule;
758
+ declare const architectureComponentSize: Rule;
672
759
 
673
- declare const sec005SharedStateImport: Rule;
760
+ declare const architecturePropCount: Rule;
674
761
 
675
- declare const arch001ComponentSize: Rule;
676
- declare const arch002PropCount: Rule;
762
+ declare const performanceHeavyImport: Rule;
677
763
 
678
- declare const perf009HeavyImport: Rule;
764
+ declare const performanceNamespaceImport: Rule;
679
765
 
680
- declare const perf010NamespaceImport: Rule;
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;
681
797
 
682
798
  declare const allRules: Rule[];
683
799
 
@@ -690,7 +806,7 @@ interface RuleInfo {
690
806
  docsUrl: string;
691
807
  fix?: Fix;
692
808
  }
693
- /** 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"). */
694
810
  declare function explainRule(id: string): RuleInfo | undefined;
695
811
 
696
812
  interface HeadTagRuleOptions {
@@ -721,7 +837,7 @@ interface ImageRuleOptions {
721
837
  id: string;
722
838
  title: string;
723
839
  severity: Severity;
724
- /** Vitals category (default 'performance'); SEO025 (alt text) reports under 'seo'. */
840
+ /** Vitals category (default 'performance'); seo/image-alt (alt text) reports under 'seo'. */
725
841
  category?: Category;
726
842
  /** Noun phrase for messages, e.g. '<img> width/height'. */
727
843
  label: string;
@@ -972,4 +1088,4 @@ declare function applyRuleSeverities(results: Result[], config: Config): Result[
972
1088
  */
973
1089
  declare function applyOverrides(results: Result[], config: Config): Result[];
974
1090
 
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 };
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 };