@svelte-vitals/core 0.43.1 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,1953 +1 @@
1
- import { AST } from 'svelte/compiler';
2
-
3
- /**
4
- * Core type definitions shared across modes. This module is pure: no I/O, no
5
- * `node:` imports, no runtime-specific globals (design §8).
6
- */
7
- type Severity = 'critical' | 'warning' | 'info';
8
- /** Where a head tag is set, relative to the route being evaluated (design §4). */
9
- type Presence = 'own' | 'inherited' | 'none';
10
- /** How a tag's value is determined (design §4). */
11
- type Value = 'static' | 'dynamic' | 'absent';
12
- /**
13
- * Two-axis detection result. Kept independent so combinations such as
14
- * "inherited + dynamic" remain expressible (design §4).
15
- */
16
- interface Detection {
17
- presence: Presence;
18
- value: Value;
19
- }
20
- /**
21
- * One compiled SvelteKit alias entry, in the order Kit builds them (`get_config_aliases` in
22
- * `@sveltejs/kit/src/exports/vite/utils.js`): `$lib` first, then `kit.alias` in declaration
23
- * order. Resolution takes the FIRST matching entry, exactly as Vite's alias plugin does, so
24
- * **position is precedence** — the list is never sorted and a longer `find` never wins on
25
- * length alone.
26
- */
27
- interface KitAlias {
28
- /** The alias key, with any trailing `/*` removed. */
29
- find: string;
30
- /**
31
- * The project-relative target: posixified, with any trailing `/*` and any trailing slashes
32
- * removed. `null` when the config's value is not a string literal — such an entry still
33
- * matches (holding its position and its mode) but resolves to undefined, so a specifier we
34
- * cannot resolve stays unresolved instead of falling through to a later entry.
35
- */
36
- replacement: string | null;
37
- /**
38
- * How `find` matches a specifier, mirroring Kit's three compiled entry shapes:
39
- * - `prefix` — `spec === find` or `spec.startsWith(find + '/')`; a plain key.
40
- * - `contents` — `spec.startsWith(find + '/')` only; from a `key/*` key, which Kit
41
- * documents as matching "the contents of a directory, not the directory itself".
42
- * - `exact` — `spec === find` only; a plain key whose `key/*` form is ALSO declared, which
43
- * is how Kit stops the plain key from swallowing the nested specifiers.
44
- */
45
- match: 'prefix' | 'contents' | 'exact';
46
- }
47
- /** Project-wide facts precomputed by the runtime layer for project-scope rules (design §10). */
48
- interface Project {
49
- hasRobotsTxt: boolean;
50
- hasSitemap: boolean;
51
- /** <html lang> from app.html: presence 'own' when the attribute exists ('none' otherwise); value 'static' if non-empty, 'absent' if empty. */
52
- htmlLang: Detection;
53
- /** Whether the static static/robots.txt references a sitemap (`Sitemap:` line). Undefined for a +server endpoint / absent / unreadable. */
54
- robotsReferencesSitemap?: boolean;
55
- /**
56
- * Set when the Vite config disables minification for production builds (performance/minify-disabled).
57
- * `file` is the config path relative to the analyzed root (posix, may start with `../`
58
- * in monorepos); unset for inline programmatic configs. `line` is 1-based and set only
59
- * when the literal `minify: false` was located in that file; unset when the value was
60
- * resolved at build time (plugin/conditional config).
61
- */
62
- viteMinifyDisabled?: {
63
- file?: string;
64
- line?: number;
65
- };
66
- /**
67
- * Set when the project configures a non-empty `kit.paths.base` — read from the `sveltekit()`
68
- * Vite plugin config, else `svelte.config.{js,ts}` (correctness/base-path-navigation).
69
- * `value` is the literal base when statically resolvable, unset when the config computes it
70
- * (e.g. `dev ? '' : '/repo'`). `file` is the config path relative to the analyzed root (posix).
71
- * Absent means the app is served at the root — the rule stays silent.
72
- */
73
- kitPathsBase?: {
74
- value?: string;
75
- file: string;
76
- };
77
- /**
78
- * The project's compiled SvelteKit alias entries, in Kit's own order (`$lib` first), read from
79
- * `svelte.config.{js,ts}`. Absent means no config was read — resolution then falls back to
80
- * `$lib` → `src/lib`, which is what this analyzer assumed unconditionally before. A collected
81
- * list is never empty: `$lib` is always prepended.
82
- */
83
- kitAliases?: KitAlias[];
84
- /**
85
- * Whether `src/app.html` opens with `<!doctype html>` (a11y/doctype). Set from the same read
86
- * as `htmlLang`; absent when the file wasn't read (missing or unreadable) — the rule stays
87
- * silent then, like `viteMinifyDisabled`'s absent convention.
88
- */
89
- appHtmlDoctype?: boolean;
90
- /**
91
- * Literal `id` attributes in `src/app.html`, from the same read as `htmlLang`. The shell is part
92
- * of every rendered document, so its ids satisfy a route's id references (a11y/no-missing-id-ref);
93
- * absent when the file wasn't read.
94
- */
95
- appHtmlIds?: string[];
96
- }
97
- declare const defaultProject: Project;
98
- /** A concrete, agent-actionable remediation for a finding (design §10, issue #18). */
99
- interface Fix {
100
- /** One-line imperative instruction, e.g. 'Add a <meta name="description"> inside <svelte:head>.' */
101
- description: string;
102
- /** Concrete code to insert or a file's contents to create. */
103
- snippet?: string;
104
- /** Markdown fenced-code language for `snippet` (default 'svelte'). */
105
- lang?: string;
106
- }
107
- /** A single rule finding for one route (or the whole project). */
108
- interface Result {
109
- /** Rule id, e.g. 'seo/title-presence'. */
110
- id: string;
111
- severity: Severity;
112
- detection: Detection;
113
- /** Route path, e.g. '/blog/[slug]'. Omitted for project-scoped rules. */
114
- route?: string;
115
- /** Source location, e.g. 'src/routes/blog/[slug]/+page.svelte'. */
116
- location?: string;
117
- message: string;
118
- recommendation?: string;
119
- docsUrl?: string;
120
- /** Agent-actionable remediation (issue #18). */
121
- fix?: Fix;
122
- /** Vitals category this finding belongs to (default 'seo' when absent). */
123
- category?: Category;
124
- /** 1-based source line for element-level findings (e.g. a specific <img>). */
125
- line?: number;
126
- }
127
- type Scope = 'route' | 'project' | 'component';
128
- type Category = 'seo' | 'performance' | 'correctness' | 'security' | 'architecture' | 'a11y';
129
- /**
130
- * Every category, as a runtime list — for validating a user-supplied category
131
- * name and naming the known ones in the error. One definition so a category
132
- * added to `Category` can't be accepted by one validator and rejected by
133
- * another. Not an ordering: reporters keep their own display order.
134
- */
135
- declare const CATEGORIES: readonly Category[];
136
- /** How dynamic (`{data.title}`) values are treated by scoring (design §4, §12). */
137
- type TreatDynamicAs = 'pass' | 'warn' | 'fail';
138
- /** Resolved option values handed to a rule at check time. */
139
- type RuleOptions = Record<string, unknown>;
140
- /**
141
- * Object form of a rule setting. `severity` omitted keeps the rule's built-in
142
- * severity — the common case when only a threshold is being moved.
143
- * `{ severity: 'off', … }` disables the rule and any `options` beside it are
144
- * inert (equivalent to the bare `'off'` string, not an error).
145
- */
146
- interface RuleSettingObject {
147
- severity?: Severity | 'off';
148
- options?: RuleOptions;
149
- }
150
- /** Per-rule override: disable, change severity, and/or set options. */
151
- type RuleSetting = 'off' | Severity | RuleSettingObject;
152
- /**
153
- * Scoped rule override (design 2026-07-18), applied to results after analysis.
154
- * An entry matches a finding when any `route` glob matches its route id or any
155
- * `files` glob matches its source location; at least one of the two must be
156
- * set. Glob syntax: `*` matches within a segment, `**` across segments, a
157
- * trailing `/**` also matches the bare prefix, and all other characters
158
- * (including SvelteKit's `(`, `)`, `[`, `]`) are literal.
159
- */
160
- interface RuleOverride {
161
- /**
162
- * Route-id glob(s), e.g. '/admin/**'. Note route ids drop `(group)` segments
163
- * (`src/routes/(app)/dashboard` reports as '/dashboard') — target a group
164
- * via `files` instead.
165
- */
166
- route?: string | string[];
167
- /** Source-path glob(s) matched against a finding's location, e.g. 'src/routes/(app)/**'. */
168
- files?: string | string[];
169
- /** Keys are rule ids ('seo/title-presence') or category names ('seo'). Rule id beats category within an entry. */
170
- rules: Record<string, RuleSetting>;
171
- }
172
- interface Config {
173
- treatDynamicAs: TreatDynamicAs;
174
- /** Component names treated as meta sources of unknown content (design §11 layer 4). */
175
- metaComponents: string[];
176
- /** Per-rule overrides keyed by rule id (design §6). */
177
- rules: Record<string, RuleSetting>;
178
- /** Minimum severity that fails the run / CI (design §6). */
179
- failOn: Severity;
180
- /** Per-category weights for the combined Health score (default: equal, 1 each) (#10). */
181
- weights?: Partial<Record<Category, number>>;
182
- /** Route-/file-scoped rule overrides, applied to results after analysis (later entries win). */
183
- overrides?: RuleOverride[];
184
- }
185
- declare const defaultConfig: Config;
186
- /** Merge user config over defaults. Identity helper for config files (design §6). */
187
- declare function defineConfig(config?: Partial<Config>): Config;
188
-
189
- /**
190
- * Runtime abstraction (design §8). Core defines only the interface; concrete
191
- * adapters (Node / Deno / Bun) live in the CLI package and are the only place
192
- * allowed to touch runtime-specific I/O APIs. Providers and rules use this
193
- * interface exclusively, which keeps them runtime-agnostic and lets tests inject
194
- * an in-memory implementation.
195
- */
196
- interface Runtime {
197
- /** Read a UTF-8 text file. Rejects if the file does not exist. */
198
- readFile(path: string): Promise<string>;
199
- /** Whether a path exists. */
200
- exists(path: string): Promise<boolean>;
201
- /**
202
- * Paths matching `pattern`, relative to `cwd`.
203
- *
204
- * **Dot files and dot directories are excluded**, and an adapter must keep it that way: the
205
- * directory-shaped Architecture rules derive their directory set from these paths, and one of them
206
- * enumerates a parent's children exhaustively, so a `.server/` appearing here would be reported as
207
- * an undeclared name. Both shipped adapters pass `dot: false`.
208
- *
209
- * **Every returned path is a file, never a directory**, and an adapter must keep that true too:
210
- * `architecture/reserved-directory-names`' unit test takes a directory's immediate children from
211
- * this same inventory and asks whether one of them is a file named after the directory, so an
212
- * adapter that let a directory through here would let a bare `Card/Card` satisfy that test as if it
213
- * were an entry file. Both shipped adapters get this for free from their glob library's default,
214
- * which returns files only unless asked to include directories.
215
- */
216
- glob(pattern: string, cwd: string): Promise<string[]>;
217
- /** Join path segments without depending on `node:path`. */
218
- join(...parts: string[]): string;
219
- }
220
-
221
- /**
222
- * A normalized head tag. The mode-independent boundary (design §8): the static
223
- * SourceHeadProvider (CLI, via the runtime-abstracted `HeadProvider` below) and
224
- * the rendered collector (`@svelte-vitals/vite`, build-time Node) both emit
225
- * these, so rules never need to know which mode produced them.
226
- */
227
- interface HeadTag {
228
- kind: 'title' | 'meta' | 'link' | 'jsonld' | 'script';
229
- /** <meta name="...">. */
230
- name?: string;
231
- /** <meta property="..."> (e.g. og:image). */
232
- property?: string;
233
- /** <link rel="...">. */
234
- rel?: string;
235
- /** <link as="..."> keyword (e.g. 'font') when statically literal; undefined when absent or dynamically bound. */
236
- as?: string;
237
- /** True when a <link> has an `as` attribute at all (literal or dynamic). Distinguishes "no as" from "dynamic as". */
238
- hasAs?: boolean;
239
- /** True when a <link> has a `crossorigin` attribute (presence only; value is irrelevant to the checks). */
240
- hasCrossorigin?: boolean;
241
- /** True when a <meta name="robots"> literal content contains `noindex`/`none`. Undefined when dynamic or absent. */
242
- noindex?: boolean;
243
- /** Literal `<script type="application/ld+json">` content, set only when the script is static. Undefined when dynamic. */
244
- jsonld?: string;
245
- /** Literal visible text of a static <title> or <meta name="description"> content, set only when static. Undefined when dynamic. */
246
- text?: string;
247
- /** Literal `hreflang` of a `<link rel="alternate">` (e.g. 'en', 'en-US', 'x-default'). Undefined when dynamic/absent. */
248
- hreflang?: string;
249
- /** Literal href (link) / src (script) URL when static — used for third-party origin analysis (performance/preconnect). */
250
- href?: string;
251
- /** True for a render-blocking `<script src>` in <head> (no defer/async/module) (performance/render-blocking-script). */
252
- blocking?: boolean;
253
- /** Where this tag was set relative to the route. Never 'none' (absence = no tag). */
254
- presence: Exclude<Presence, 'none'>;
255
- /** Whether the tag's value is static/dynamic/absent (design §4). */
256
- value: Value;
257
- /** Source file the tag came from (static mode only). */
258
- file?: string;
259
- }
260
- /** Resolved effective head for a single route (design §8). */
261
- interface ResolvedHead {
262
- /** Route path, e.g. '/blog/[slug]'. */
263
- route: string;
264
- /** Which provider produced this. */
265
- source: 'static' | 'rendered';
266
- /** Effective head tags after layout-chain composition. */
267
- tags: HeadTag[];
268
- /** Representative source file for the route (used for issue locations). */
269
- file: string;
270
- }
271
- /**
272
- * Supplies ResolvedHead[] for a project through the runtime abstraction. The
273
- * static (CLI) mode implements this; rendered mode reads prerendered HTML at
274
- * build time and emits the same ResolvedHead[] without the runtime indirection.
275
- */
276
- interface HeadProvider {
277
- mode: 'static' | 'rendered';
278
- collect(rt: Runtime, cwd: string, config?: Config): Promise<ResolvedHead[]>;
279
- }
280
-
281
- /**
282
- * A normalized <img> occurrence — the mode-independent boundary for Performance
283
- * rules (mirrors head.ts). Attribute presence only: a dynamically-bound attribute
284
- * (width={w}) still counts as present, so dynamic values are never flagged.
285
- */
286
- interface ImageInfo {
287
- hasWidth: boolean;
288
- hasHeight: boolean;
289
- hasLoading: boolean;
290
- /** True when the <img> has an `alt` attribute at all (incl. empty `alt=""` decorative; seo/image-alt). */
291
- hasAlt: boolean;
292
- /** True when the <img> has a literal `loading="lazy"` (performance/lcp-image). Dynamic/spread → false. */
293
- lazy: boolean;
294
- /** True when the <img> has a `srcset` attribute (performance/responsive-image). */
295
- hasSrcset: boolean;
296
- /** 1-based source line, or 0 if unknown. */
297
- line: number;
298
- /** Source file the <img> came from. */
299
- file: string;
300
- }
301
- /** Resolved <img> elements for a single route (page + layout chain). */
302
- interface ResolvedImages {
303
- route: string;
304
- images: ImageInfo[];
305
- }
306
-
307
- /**
308
- * A normalized page-body heading occurrence — the mode-independent boundary for
309
- * the heading-hierarchy rule (mirrors images.ts). Both providers collect these
310
- * so seo/single-h1 never needs to know which mode produced them.
311
- */
312
- interface HeadingInfo {
313
- /** Heading level 1–6 (the `n` in <hn>). */
314
- level: number;
315
- /** 1-based source line, or 0 if unknown (rendered mode does not track lines). */
316
- line: number;
317
- /** Source file the heading came from. */
318
- file: string;
319
- }
320
- /** Resolved page-body headings for a single route (page + layout chain). */
321
- interface ResolvedHeadings {
322
- route: string;
323
- headings: HeadingInfo[];
324
- /**
325
- * Headings found in child components rendered (transitively) by this route's
326
- * chain files — source mode only; absent in rendered mode. Kept separate from
327
- * `headings` because their position in document order is unknown: safe for
328
- * counting (seo/single-h1), unusable for outline order (seo/heading-level-skip).
329
- */
330
- componentHeadings?: HeadingInfo[];
331
- }
332
-
333
- /** One step of a template branch address: which exclusive block, and which arm of it. */
334
- interface BranchStep {
335
- /** index of the {#if}/{#await} block among its file's blocks (document order) */
336
- group: number;
337
- /** branch index within the group (if: 0..n consequent→else; await: 0=pending,1=then,2=catch) */
338
- branch: number;
339
- }
340
- /** Where a folded occurrence sits, for the finding location. */
341
- interface A11yOccurrenceInfo {
342
- file: string;
343
- line: number;
344
- }
345
- /**
346
- * Route-scoped a11y facts, the mode-independent boundary for the landmark/id rules
347
- * (mirrors headings.ts). Source mode composes the layout chain plus its resolved
348
- * components; rendered mode reads the prerendered document.
349
- */
350
- interface ResolvedA11y {
351
- route: string;
352
- /** representatives per landmark kind after the branch-aware fold ('main' | 'banner' | 'contentinfo' | 'complementary') */
353
- landmarks: Record<string, A11yOccurrenceInfo[]>;
354
- /** landmark occurrences nested inside another landmark after composition */
355
- nestedLandmarks: {
356
- kind: string;
357
- within: string;
358
- file: string;
359
- line: number;
360
- }[];
361
- /** representatives per literal id */
362
- ids: Record<string, A11yOccurrenceInfo[]>;
363
- /** literal id references */
364
- idRefs: {
365
- id: string;
366
- attr: string;
367
- file: string;
368
- line: number;
369
- }[];
370
- /** optimistic candidates: every literal id anywhere (all branches, each/snippet bodies, components, app.html) */
371
- idCandidates: string[];
372
- /** closed world holds: every component resolved, no depth truncation, no {@html}/spread, no dynamic id */
373
- fullyResolved: boolean;
374
- }
375
- type Foldable = {
376
- key: string;
377
- path: BranchStep[];
378
- repeatable: boolean;
379
- };
380
- /**
381
- * Branch-aware occurrence fold (design "Control-flow semantics"): within a branch
382
- * occurrences sum, across the arms of one exclusive block the arm with the most
383
- * occurrences wins (tie → lowest branch index) and ITS occurrences are the group's
384
- * representatives — so a caller's count is always `list.length`, with a location per
385
- * representative. `{#each}`/`{#snippet}` occurrences render 0..N times and drop out.
386
- * The max is per key: there is no scalar total to maximize.
387
- */
388
- declare function foldOccurrences<T extends Foldable>(nodes: T[]): Map<string, T[]>;
389
- /**
390
- * Decode a fragment identifier the way navigation does before matching an element id
391
- * (`href="#caf%C3%A9"` targets `id="café"`). Malformed escapes are kept verbatim —
392
- * the browser would also fail to decode them, so the raw text is the comparable form.
393
- */
394
- declare function decodeFragmentId(fragment: string): string;
395
- /** Whitespace-split tokens of a (possibly undefined) literal attribute value. */
396
- declare function splitTokens(value: string | undefined): string[];
397
- /** Explicit `role` values that map to the landmark kinds the route rules inspect. */
398
- declare const LANDMARK_ROLES: ReadonlySet<string>;
399
- /** Attributes whose (whitespace-tokenized) values reference element ids. */
400
- declare const IDREF_ATTRS: readonly string[];
401
- /**
402
- * Whether a decoded URL fragment is HTML's "top of the document" indicator: `#top` (ASCII
403
- * case-insensitive) scrolls to the top when no element has that id, so it is never a missing
404
- * reference. Compare AFTER percent-decoding — `#%74op` navigates identically to `#top`.
405
- */
406
- declare function isTopFragment(id: string): boolean;
407
-
408
- /**
409
- * Component-body facts for the Correctness category — the source-analysis boundary
410
- * (mirrors images.ts / headings.ts). Collected by the static (CLI) provider only;
411
- * the rendered provider can't see reactivity, so correctness rules no-op there.
412
- */
413
- /** An `{#each}` block in a component template. */
414
- interface EachBlockFact {
415
- /** True when the block has a key, e.g. `{#each items as item (item.id)}`. */
416
- hasKey: boolean;
417
- /** 1-based source line, or 0 if unknown. */
418
- line: number;
419
- /** 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. */
420
- indexKey?: boolean;
421
- }
422
- /** An `$effect(...)` / `$effect.pre(...)` call in a component's instance script. */
423
- interface EffectFact {
424
- /** 1-based source line, or 0 if unknown. */
425
- line: number;
426
- /** True when the effect body only assigns to `$state` variables (the "use $derived" smell). */
427
- assignsOnlyState: boolean;
428
- /** 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). */
429
- mountOnly: boolean;
430
- }
431
- /** A `$effect` guaranteed to run outside component initialisation — it throws `effect_orphan` at runtime (correctness/orphan-effect). */
432
- interface OrphanEffectFact {
433
- /** 1-based source line, or 0 if unknown. For 'constructor-instantiated', the module-scope `new` site. */
434
- line: number;
435
- /** 'top-level' = runs at module evaluation; 'constructor-instantiated' = module-scope `new` of a same-file class whose constructor creates a bare effect. */
436
- kind: 'top-level' | 'constructor-instantiated';
437
- /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
438
- className?: string;
439
- }
440
- /** A svelte lifecycle/context call guaranteed to run outside component initialisation — it throws `lifecycle_outside_component` at runtime (correctness/orphan-lifecycle). */
441
- interface OrphanLifecycleCallFact {
442
- /** Canonical svelte export name (alias-resolved), e.g. 'onMount'. */
443
- name: string;
444
- /** 1-based source line, or 0 if unknown. For 'constructor-instantiated', the module-scope `new` site. */
445
- line: number;
446
- /** 'top-level' = runs at module evaluation; 'constructor-instantiated' = module-scope `new` of a same-file class whose constructor calls a tracked function. */
447
- kind: 'top-level' | 'constructor-instantiated';
448
- /** Class name when kind is 'constructor-instantiated' (used in the finding message). */
449
- className?: string;
450
- }
451
- /** 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). */
452
- interface BrowserGlobalRefFact {
453
- /** The global's name, e.g. 'window'. */
454
- name: string;
455
- /** 1-based source line, or 0 if unknown. */
456
- line: number;
457
- /** '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). */
458
- context: 'module' | 'instance';
459
- }
460
- /** A flagged source position in a component (e.g. an `{@html}` tag or a `javascript:` URL). */
461
- interface SourceSpan {
462
- /** 1-based source line, or 0 if unknown. */
463
- line: number;
464
- }
465
- /** An inline `svelte-vitals-disable-next-line` directive found in the component's source (issue #92). */
466
- interface SuppressionDirective {
467
- /** 1-based line the directive suppresses (the line immediately after the comment). */
468
- line: number;
469
- /** Rule ids suppressed on that line; undefined = suppress every rule on that line. */
470
- ruleIds?: string[];
471
- }
472
- /** An `<input type="checkbox">` / `<input type="radio">` element carrying a `bind:value`
473
- * directive — `bind:value` observes the DOM `value` property, which checkbox/radio
474
- * interaction never changes, so the bound state silently never updates
475
- * (correctness/checkable-bind-value). */
476
- interface CheckableBindValueFact {
477
- /** Which checkable input type was flagged — selects the message wording. */
478
- kind: 'checkbox' | 'radio';
479
- /** 1-based source line, or 0 if unknown. */
480
- line: number;
481
- }
482
- /** A root-relative navigation literal — broken when the app is served under `kit.paths.base`
483
- * (correctness/base-path-navigation). Shared by the component and Kit-module channels. */
484
- interface BasePathLinkFact {
485
- /** Which navigation surface it was written on — selects the message wording. */
486
- kind: 'href' | 'goto' | 'redirect';
487
- /** The literal path as written, e.g. '/about'. */
488
- path: string;
489
- /** 1-based source line, or 0 if unknown. */
490
- line: number;
491
- }
492
- /** An interactive element (e.g. `<button>`) found nested inside another interactive
493
- * container (e.g. `<a href>`) (a11y/interactive-nesting). */
494
- interface InteractiveNestingFact {
495
- containerTag: string;
496
- descendantTag: string;
497
- /** 1-based source line of the descendant, or 0 if unknown. */
498
- line: number;
499
- }
500
- /** A `button`/`a href`/`input type="image"` with no computable accessible name (a11y/accessible-name). */
501
- interface UnnamedInteractiveFact {
502
- tag: string;
503
- /** 1-based source line, or 0 if unknown. */
504
- line: number;
505
- }
506
- /** An element carrying a `role` and/or `aria-*` attribute(s) (a11y ARIA rules). */
507
- interface AriaElementFact {
508
- tag: string;
509
- /** 1-based source line, or 0 if unknown. */
510
- line: number;
511
- /** literal role value; undefined = no role attr; { expression: true } = dynamic */
512
- role?: {
513
- literal?: string;
514
- expression?: boolean;
515
- };
516
- /** every aria-* attribute on the element */
517
- aria: {
518
- name: string;
519
- literal?: string;
520
- expression?: boolean;
521
- line: number;
522
- }[];
523
- /** literal `type` of an `<input>`, lowercased; undefined for non-inputs or a dynamic type */
524
- inputType?: string;
525
- /** Set when the element also carries a spread attribute — its full attribute set is
526
- * unknowable, so required-prop presence checks must treat it as satisfied (a11y/required-aria-props). */
527
- hasSpread?: true;
528
- }
529
- /** Reactivity/correctness + security + architecture facts parsed from one `.svelte` component. */
530
- interface ComponentFacts {
531
- /** Source file the component came from. */
532
- file: string;
533
- eachBlocks: EachBlockFact[];
534
- effects: EffectFact[];
535
- /** `{@html …}` occurrences — raw-HTML render surfaces (security/raw-html). */
536
- htmlTags: SourceSpan[];
537
- /** Element attributes with a literal `javascript:` URL (security/javascript-url). */
538
- javascriptUrls: SourceSpan[];
539
- /** Source line count of the component file (architecture/component-size). */
540
- loc: number;
541
- /** Named props destructured from `$props()`; 0 when unknowable (rest / non-destructured) (architecture/prop-count). */
542
- propCount: number;
543
- /** Module specifiers of every `import` in the instance + module scripts (performance/heavy-import). */
544
- imports: string[];
545
- /**
546
- * Module specifiers of every `import`, each with its source line (performance/heavy-import,
547
- * architecture/route-component-import). `type` marks a declaration that contributes **no runtime
548
- * value binding** — either `import type …`, or one whose every specifier is inline-typed
549
- * (`import { type A } from …`). A specifier-less side-effect import is not marked: it still loads
550
- * the module. Optional, so existing external constructors of `ComponentFacts` are unaffected.
551
- */
552
- importSpans: {
553
- source: string;
554
- line: number;
555
- type?: true;
556
- }[];
557
- /** Value `import * as X from '<bare pkg>'` namespace imports (type-only excluded) — performance/namespace-import. */
558
- namespaceImports: {
559
- source: string;
560
- line: number;
561
- }[];
562
- /** `$state` declarations never written or escaped anywhere in the component — candidates for const (correctness/unmutated-state). */
563
- constableStates: {
564
- name: string;
565
- line: number;
566
- }[];
567
- /** 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. */
568
- mutatedProps: {
569
- name: string;
570
- line: number;
571
- legacy?: boolean;
572
- }[];
573
- /** 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. */
574
- stalePropDerivations: {
575
- name: string;
576
- line: number;
577
- legacy?: boolean;
578
- }[];
579
- /** Object/array-literal $state bindings reassigned at least once but never mutated, escaped, aliased, or item-edited — $state.raw candidates (performance/state-raw). */
580
- rawableStates: {
581
- name: string;
582
- line: number;
583
- }[];
584
- /** 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). */
585
- nonreactiveBuiltinStates: {
586
- name: string;
587
- type: string;
588
- line: number;
589
- }[];
590
- /** `<input type="checkbox">` / `<input type="radio">` elements bound with `bind:value`
591
- * instead of `bind:checked`/`bind:group` (correctness/checkable-bind-value). */
592
- checkableBindValues: CheckableBindValueFact[];
593
- /** Root-relative `<a href>` and `goto()` literals in this component (correctness/base-path-navigation). */
594
- basePathLinks: BasePathLinkFact[];
595
- /** `$effect` calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-effect). */
596
- orphanEffects: OrphanEffectFact[];
597
- /** Svelte lifecycle/context calls guaranteed to run outside component initialisation — module scope in `.svelte.ts`/`.svelte.js` or `<script module>` (correctness/orphan-lifecycle). */
598
- orphanLifecycleCalls: OrphanLifecycleCallFact[];
599
- /** Browser-global reads in server-executed positions of this file (correctness/server-browser-global, correctness/instance-browser-global). */
600
- browserGlobalRefs: BrowserGlobalRefFact[];
601
- /** 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. */
602
- moduleStateDecls: {
603
- name: string;
604
- line: number;
605
- }[];
606
- /** Inline `svelte-vitals-disable-next-line` directives found in this file's source — component-rule escape hatch (issue #92). Optional: absent is equivalent to no directives, so existing external constructors of `ComponentFacts` are unaffected. */
607
- suppressions?: SuppressionDirective[];
608
- /** Markdown links `[label](url)` appearing inside a comment (architecture/doc-link-target). */
609
- commentLinks: {
610
- url: string;
611
- line: number;
612
- }[];
613
- /** Elements carrying a role or any aria-* attribute (a11y ARIA rules). */
614
- ariaElements?: AriaElementFact[];
615
- /** Interactive elements nested inside another interactive container (a11y/interactive-nesting). */
616
- interactiveNestings?: InteractiveNestingFact[];
617
- /** `button`/`a href`/`input type="image"` elements with no computable accessible name (a11y/accessible-name). */
618
- unnamedInteractive?: UnnamedInteractiveFact[];
619
- /** `<label>` elements with neither a `for` attribute nor a wrapped labelable descendant (a11y/label-has-control). */
620
- unassociatedLabels?: {
621
- line: number;
622
- }[];
623
- /** Text nodes whose trimmed content opens with a bullet character followed by whitespace, outside any `li` (a11y/use-list). */
624
- bulletTexts?: {
625
- line: number;
626
- char: string;
627
- }[];
628
- /** `<select required>` (no `multiple`, display size absent or ≤ 1) whose first `option` element
629
- * child is not a placeholder label option (a11y/placeholder-label-option). */
630
- selectsMissingPlaceholder?: {
631
- line: number;
632
- }[];
633
- /** `<time>` with no `datetime` attribute whose literal text content is not machine-readable (a11y/require-datetime). */
634
- timesMissingDatetime?: {
635
- line: number;
636
- text: string;
637
- }[];
638
- /** Set when the file failed to read or parse and these facts are the empty fallback — the file was NOT analyzed. */
639
- parseFailed?: true;
640
- }
641
-
642
- /** What the per-file parsers produce — `ComponentFacts` minus `file`, with `suppressions` always present. */
643
- type ParsedFacts = Omit<ComponentFacts, 'file' | 'suppressions'> & {
644
- suppressions: SuppressionDirective[];
645
- };
646
- /**
647
- * Parse one source file's facts (CLI/static + vite build mode): a `.svelte` component's
648
- * reactivity/correctness + security + architecture facts, or a `.svelte.ts`/`.svelte.js`
649
- * runes module's orphan-$effect facts (correctness/orphan-effect).
650
- */
651
- declare function parseComponentFacts(source: string, filename: string): ParsedFacts;
652
-
653
- /**
654
- * Fallback facts for a file that fails to read or parse (dev tooling must never
655
- * throw). This is the single source of truth for the empty-facts shape — add new
656
- * `ComponentFacts` fields HERE so TypeScript catches every call site that still
657
- * needs updating.
658
- */
659
- declare function emptyComponentFacts(file: string): ComponentFacts;
660
- /**
661
- * Scan every `.svelte` component and `.svelte.ts`/`.svelte.js` runes module under `src/`
662
- * for Correctness/Security/Architecture/Bundle-Performance/Accessibility facts. Independent
663
- * of route resolution — covers `$lib` and non-route components too. A file that fails to
664
- * read or parse contributes empty facts instead of aborting the whole scan (dev tooling
665
- * must never throw).
666
- */
667
- declare function collectComponentFacts(rt: Runtime, cwd: string): Promise<ComponentFacts[]>;
668
-
669
- /**
670
- * Every file under `src/`, as project-relative paths, sorted. Paths only — nothing is
671
- * read, so this is the cheaper of the two passes over `src/` (the component collector
672
- * already walks the same tree and reads every `.svelte`).
673
- *
674
- * Directory-shaped rules derive their directory set from these paths' ancestor prefixes
675
- * rather than globbing a second time; see `architecture/unit-entry-file`. The list is
676
- * sorted so anything that picks "the first file under a directory" is deterministic.
677
- *
678
- * Two properties of the result the directory-shaped rules depend on: a directory containing no file
679
- * at any depth does not appear among these paths' ancestor prefixes and so does not exist as far as
680
- * those rules are concerned, and dot directories never appear at all (see `Runtime.glob`).
681
- */
682
- declare function collectSourceFiles(rt: Runtime, cwd: string): Promise<string[]>;
683
-
684
- /**
685
- * Facts parsed from one SvelteKit route/hooks file for the SSR shared-state rules
686
- * (the security kit-module rules). Collected by `collectKitModuleFacts` (static/CLI + vite build mode).
687
- */
688
- interface KitModuleFacts {
689
- /** Repo-relative source file. */
690
- file: string;
691
- /** 'server' = runs only on the server (+*.server, +server, hooks.server); 'universal' = +page.ts/+layout.ts (still runs on the server during SSR). */
692
- kind: 'server' | 'universal';
693
- /** Module-scope let/var reassigned from inside a function (security/server-module-state). */
694
- moduleStateReassignments: {
695
- name: string;
696
- line: number;
697
- inHandler: boolean;
698
- }[];
699
- /** Writes to an imported binding from inside an exported handler (security/handler-state-write). */
700
- importedStateWrites: {
701
- name: string;
702
- line: number;
703
- via: 'assignment' | 'set-call';
704
- }[];
705
- /** Writes to an imported binding outside handlers — top level or helper functions (security/shared-state-import's write flavour). */
706
- importedStateWritesOutsideHandlers: {
707
- name: string;
708
- line: number;
709
- }[];
710
- /**
711
- * `.set()`/`.update()` in a handler on an import resolving under the `$lib` server root.
712
- * The call shape alone cannot tell a persistence client (`db.set(…)`) from a hand-rolled
713
- * in-memory store, so the decision needs the target module — which this pure parse cannot
714
- * read. `collectKitModuleFacts` resolves each one and promotes the in-memory ones into
715
- * `importedStateWrites`; a consumer that ignores this field sees the pre-arbitration
716
- * behaviour, i.e. every one of these exempt.
717
- */
718
- pendingServerStoreWrites: {
719
- name: string;
720
- imported: string;
721
- resolved: string;
722
- line: number;
723
- }[];
724
- /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (security/shared-state-import). */
725
- runesModuleImports: {
726
- source: string;
727
- resolved: string;
728
- names: string[];
729
- line: number;
730
- }[];
731
- /** Svelte lifecycle/context calls that run outside component initialisation — top level, handler bodies, or the `init` hook (correctness/orphan-lifecycle). */
732
- lifecycleCalls: {
733
- name: string;
734
- line: number;
735
- inHandler: boolean;
736
- }[];
737
- /** 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`. */
738
- browserGlobalRefs: {
739
- name: string;
740
- line: number;
741
- inHandler: boolean;
742
- }[];
743
- /** Root-relative `redirect()` literals in this Kit module (correctness/base-path-navigation). */
744
- basePathLinks: BasePathLinkFact[];
745
- /** Set when this file disables SSR via `export const ssr = false` (inline or same-file alias export) — the declaration's line (seo/ssr-disabled). */
746
- ssrDisabled?: {
747
- line: number;
748
- };
749
- /** 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. */
750
- csrDisabled?: {
751
- line: number;
752
- };
753
- /** 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. */
754
- loadWaterfalls?: {
755
- dependentLines: number[];
756
- independentLines: number[];
757
- };
758
- /** Inline `svelte-vitals-disable-next-line` directives in this file. */
759
- suppressions: SuppressionDirective[];
760
- /** Set when the file failed to read or parse and these facts are the empty fallback — the file was NOT analyzed. */
761
- parseFailed?: true;
762
- }
763
-
764
- /**
765
- * Resolve an import specifier to a path relative to the analyzed project's root (the
766
- * cwd svelte-vitals runs from — not necessarily a repo root; in a monorepo the project
767
- * may live at e.g. `apps/web/`) against the importing file, or undefined when it cannot
768
- * be a project-local module: the caller's `aliases` list decides which non-relative
769
- * specifiers resolve, defaulting to `$lib` → `src/lib`; `./`/`../` resolve against the
770
- * importing file's directory; bare packages and other aliases are skipped (they can't
771
- * be resolved to a project-local path at all). Also undefined when a relative
772
- * specifier's `..` segments escape the project root (see `normalizePosix`), or when a
773
- * matched alias's value is itself absolute (e.g. `/opt/shared/src`, or a posixified Windows
774
- * drive-letter path like `C:/shared/src`): an absolute target is outside the analyzed project by
775
- * definition, and without this check `normalizePosix` would quietly drop the leading empty
776
- * segment and hand back a project-relative-LOOKING path that actually names a different file.
777
- *
778
- * Exported from the package's public barrel because `architecture/private-scope-import`
779
- * and `architecture/route-component-import` (inside `packages/core`) both need resolution
780
- * that is not restricted to runes modules, unlike `resolveRunesModuleSpecifier` — and
781
- * because `resolveComponentPath` (`packages/cli/src/providers/source/resolve.ts`), which
782
- * drives transitive `<head>`/heading resolution, delegates its alias/`$lib`/relative
783
- * mapping here too, rather than duplicating it. This is the single site for every
784
- * repo-local specifier resolution in the repo.
785
- */
786
- declare function resolveRepoLocalPath(spec: string, importerFile: string, aliases?: readonly KitAlias[]): string | undefined;
787
- /**
788
- * Resolve an import specifier to a repo-relative `.svelte.ts`/`.svelte.js` path, or
789
- * undefined when it cannot be a runes module: delegates to `resolveRepoLocalPath` with
790
- * `aliases` (defaulting to `$lib` → `src/lib` when omitted), so a project's declared
791
- * `kit.alias`/`kit.files.lib` resolve here exactly as they do at rule time; `./`/`../`
792
- * resolve against the importing file's directory; bare packages, unmatched aliases, and
793
- * a relative specifier whose `..` segments escape the repo root are skipped. An
794
- * extensionless `…/x.svelte` specifier canonicalises to `….svelte.ts` (security/shared-state-import also
795
- * tries the `.js` sibling when matching).
796
- */
797
- declare function resolveRunesModuleSpecifier(spec: string, importerFile: string, aliases?: readonly KitAlias[]): string | undefined;
798
- /**
799
- * Parse one SvelteKit route/hooks file's SSR shared-state facts (the security kit-module rules). Uses
800
- * the shared wrap parser (`parseModuleProgram`), so reported lines subtract the
801
- * 1-line wrap prefix; suppressions are scanned on the unwrapped source.
802
- */
803
- declare function parseKitModuleFacts(source: string, filename: string, aliases?: readonly KitAlias[]): Omit<KitModuleFacts, 'file' | 'kind'>;
804
-
805
- /** Fallback facts for a Kit file that fails to read or parse (dev tooling must never throw). */
806
- declare function emptyKitModuleFacts(file: string, kind: KitModuleFacts['kind']): KitModuleFacts;
807
- /**
808
- * Scan SvelteKit route/hooks files for SSR shared-state facts (the security kit-module rules): route
809
- * `+page`/`+layout` server and universal modules, `+server` endpoints, and
810
- * `src/hooks.server`. `src/lib/server/**` is deliberately NOT scanned — legitimate
811
- * module singletons (DB connections, clients) live there (design). A file that
812
- * fails to read or parse contributes empty facts instead of aborting the scan.
813
- *
814
- * `aliases` is the project's compiled alias list (`Project.kitAliases`); omitted,
815
- * specifiers resolve through `$lib` → `src/lib` only.
816
- */
817
- declare function collectKitModuleFacts(rt: Runtime, cwd: string, aliases?: readonly KitAlias[]): Promise<KitModuleFacts[]>;
818
-
819
- /**
820
- * The `build: { minify: false }` override, when present as a literal: returns the
821
- * `minify` property's 1-based line in the ORIGINAL source. Undefined for clean,
822
- * dynamic, or unparsable configs (never throws).
823
- */
824
- declare function findMinifyDisabled(source: string): {
825
- line: number;
826
- } | undefined;
827
-
828
- /** What a Vite config says about SvelteKit's own configuration. */
829
- type ViteKitConfigResult =
830
- /** No `sveltekit()` call, or one with no argument — `svelte.config` still applies. */
831
- {
832
- kind: 'no-plugin-config';
833
- }
834
- /** `sveltekit(<something we can't resolve>)` — the effective config is unknowable AND
835
- * `svelte.config` is provably ignored, so the caller must stay quiet. */
836
- | {
837
- kind: 'unresolvable';
838
- }
839
- /** `sveltekit({…})` resolved. `base` is unset when the config declares no non-empty base. */
840
- | {
841
- kind: 'resolved';
842
- base?: {
843
- value?: string;
844
- };
845
- };
846
- /** `kit.alias` and `kit.files.lib` as written, before Kit compiles them into ordered entries. */
847
- type RawKitAliases = {
848
- /**
849
- * `kit.alias` entries in declaration order, `value: null` where the config's value is not a
850
- * string literal. **Undefined means the key set is unknowable** — a spread or a computed key
851
- * puts an unknown key at a known position, and an unknown key could shadow anything after it,
852
- * with no `find` to record that with. The caller then discards every user entry.
853
- */
854
- entries?: {
855
- key: string;
856
- value: string | null;
857
- }[];
858
- /**
859
- * `kit.files.lib`, in three distinct states: **absent** (`undefined`) — there is no `lib`
860
- * property, or `files` itself does not resolve to an object literal; a **literal** (the
861
- * string) — `files.lib` is a string literal; **present but unreadable** (`null`) — the `lib`
862
- * property exists but its value is not statically a string (e.g. a computed expression). The
863
- * `null` state must not collapse into "absent": the caller cannot fall back to `src/lib`
864
- * without risking a wrong answer, because the project may have moved `$lib` to something this
865
- * parser simply couldn't read.
866
- */
867
- filesLib?: string | null;
868
- };
869
- /** `kit.alias` and `kit.files.lib` from a `svelte.config.{js,ts}` source. */
870
- declare function findKitAliasesInSvelteConfig(source: string): RawKitAliases;
871
- /**
872
- * The project's compiled alias list, following SvelteKit's config precedence: options passed to
873
- * the `sveltekit()` Vite plugin make `svelte.config` irrelevant (Kit logs "svelte.config.js is
874
- * ignored when options are passed via your Vite config"), so aliases are read from
875
- * `svelte.config` only when the Vite config carries no plugin config. Reading `kit.alias` out of
876
- * a plugin config is deliberately not done — that costs reach, not correctness, and such a
877
- * project keeps the resolver's default `$lib` behaviour. Undefined means "no config was read".
878
- */
879
- declare function resolveKitAliases(viteConfig: {
880
- source: string;
881
- } | undefined, svelteConfig: {
882
- source: string;
883
- } | undefined): KitAlias[] | undefined;
884
- /** `kit.paths.base` from a `svelte.config.{js,ts}` source. */
885
- declare function findKitPathsBaseInSvelteConfig(source: string): {
886
- value?: string;
887
- } | undefined;
888
- /** SvelteKit config passed to the `sveltekit()` plugin in a Vite config source (since Kit 2.62). */
889
- declare function findKitPathsBaseInViteConfig(source: string): ViteKitConfigResult;
890
- /**
891
- * The project's effective `kit.paths.base`, following SvelteKit's precedence: the `sveltekit()`
892
- * plugin config when it carries one, otherwise `svelte.config`. `file` is the config the base
893
- * came from (as passed in by the caller). Undefined means "no base path" — the gate stays shut.
894
- */
895
- declare function resolveKitPathsBase(viteConfig: {
896
- file: string;
897
- source: string;
898
- } | undefined, svelteConfig: {
899
- file: string;
900
- source: string;
901
- } | undefined): {
902
- value?: string;
903
- file: string;
904
- } | undefined;
905
-
906
- /** A template fragment's child node relevant to value classification: literal text or a `{expr}`. */
907
- type TextOrExpr = AST.Text | AST.ExpressionTag;
908
- /**
909
- * All keys that can bear child nodes in a Svelte AST node.
910
- * Covers if/each/await blocks (pending/then/catch/fallback) as well as
911
- * the standard fragment, nodes, consequent, alternate, and body keys.
912
- */
913
- declare const CHILD_NODE_KEYS: string[];
914
- declare function valueFromNodes(nodes: TextOrExpr[]): Value;
915
- /** The literal text of a node list when fully static (no ExpressionTag), else undefined. */
916
- declare function textFromNodes(nodes: TextOrExpr[]): string | undefined;
917
- /** Static string of an attribute (e.g. name="description"), or undefined if dynamic/absent. */
918
- declare function attrText(attributes: AST.Attribute[], name: string): string | undefined;
919
- /** Value kind of an attribute's content (e.g. the `content` of a <meta>). */
920
- declare function attrValue(attributes: AST.Attribute[], name: string): Value;
921
- declare function lineOf(source: string, offset: unknown): number;
922
- declare function findAttr(attributes: AST.Attribute[], name: string): AST.Attribute | undefined;
923
- /** Value kind of a single attribute (e.g. a component prop). */
924
- declare function attrValueOf(attr: AST.Attribute): Value;
925
- /** Literal static text of a single attribute node (e.g. a component prop), or undefined if dynamic/absent. */
926
- declare function attrTextOf(attr: AST.Attribute): string | undefined;
927
-
928
- /**
929
- * Source-file locations that satisfy the project-scope rules, shared by every
930
- * mode so the static (CLI) and rendered (plugin) collectors never drift. This
931
- * module is pure data: no I/O, no `node:` imports (design §8).
932
- */
933
- /** Locations that satisfy the robots.txt project rule (seo/robots-txt). */
934
- declare const ROBOTS_SOURCE_PATHS: readonly ["static/robots.txt", "src/routes/robots.txt/+server.ts", "src/routes/robots.txt/+server.js"];
935
- /** Locations that satisfy the sitemap.xml project rule (seo/sitemap-xml). */
936
- declare const SITEMAP_SOURCE_PATHS: readonly ["static/sitemap.xml", "src/routes/sitemap.xml/+server.ts", "src/routes/sitemap.xml/+server.js"];
937
- /** Vite's own config resolution order — only the first existing file is the one Vite loads. */
938
- declare const VITE_CONFIG_FILES: readonly ["vite.config.js", "vite.config.mjs", "vite.config.ts", "vite.config.cjs", "vite.config.mts", "vite.config.cts"];
939
- /** SvelteKit's config resolution order (`@sveltejs/kit` checks js before ts). */
940
- declare const SVELTE_CONFIG_FILES: readonly ["svelte.config.js", "svelte.config.ts"];
941
-
942
- /** The severity a setting selects: `'off'`, an explicit severity, or undefined (leave the built-in). */
943
- declare function settingSeverity(setting: RuleSetting | undefined): Severity | 'off' | undefined;
944
- /** The options a setting carries, or undefined for the string forms. */
945
- declare function settingOptions(setting: RuleSetting | undefined): RuleOptions | undefined;
946
- /** Drop rules disabled via config (design §6). */
947
- declare function selectRules(rules: Rule[], config: Config): Rule[];
948
- /**
949
- * `config` with `failedRuleIds` (from `runRules`' `failedRules`) forced `'off'`: a rule that threw
950
- * examined nothing, so leaving it in the inventory would score it as if it had run clean, silently
951
- * inflating Health. Reuses the exact mechanism a `rules: { id: 'off' }` config entry already gets —
952
- * `selectRules`/`buildInventory` both drop an `'off'` id from the denominator — rather than adding a
953
- * second, parallel notion of "not counted" for callers to keep in sync.
954
- */
955
- declare function withFailedRulesOff(config: Config, failedRuleIds: readonly string[]): Config;
956
- /** One-line "rule failed and was skipped" warning; capped to the message's first line so a stack trace can't flood a terminal. */
957
- declare function formatFailedRuleWarning(f: {
958
- id: string;
959
- message: string;
960
- }): string;
961
- /** Apply per-rule severity overrides to results (design §6). */
962
- declare function applyRuleSeverities(results: Result[], config: Config): Result[];
963
- /** An override entry with its globs compiled once. Build with `compileOverrides`. */
964
- interface CompiledOverride {
965
- routes: RegExp[];
966
- files: RegExp[];
967
- rules: Record<string, RuleSetting>;
968
- }
969
- /**
970
- * Compile every override entry's globs to RegExp, once. Callers that match many
971
- * targets (every component, every route) must hoist this out of their loop.
972
- */
973
- declare function compileOverrides(config: Config): CompiledOverride[];
974
- /**
975
- * Whether an override entry applies to a target. THE single definition of that
976
- * question — the result post-pass and in-run option resolution both call it.
977
- * Sharing this matcher is necessary but not sufficient for a severity override
978
- * and an option override to select the same files: each caller must also pass
979
- * the same `target` (route and, critically, `file`) the other path effectively
980
- * matches against. See Finding 1, docs/superpowers/specs/2026-07-26-rule-options-design.md.
981
- */
982
- declare function overrideMatches(o: CompiledOverride, target: {
983
- route?: string;
984
- file?: string;
985
- }): boolean;
986
- /**
987
- * Apply route-/file-scoped overrides to results (design 2026-07-18). An entry
988
- * matches when any `route` glob matches the finding's route id or any `files`
989
- * glob matches its location (OR). `'off'` removes a matched result entirely —
990
- * passing seeds included, so scoring and "checks passed" counts behave as if
991
- * the rule never ran there. A severity value rewrites the result's severity.
992
- * Entries are evaluated in order (later entries win); within one entry, a
993
- * rule-id key beats a category key only when it specifies a `severity` — an
994
- * options-only rule-id key (no `severity`) contributes its options but leaves
995
- * the category key's severity in force, rather than shadowing it (design
996
- * 2026-07-26, Finding 2 / second review Finding E).
997
- */
998
- declare function applyOverrides(results: Result[], config: Config): Result[];
999
-
1000
- /**
1001
- * Per-rule options: their declaration, resolution, and validation (design
1002
- * 2026-07-26). Deliberately does not import `rule.ts` — `rule.ts` imports
1003
- * `RuleOptionsSpec` from here, so taking `Rule` as a parameter would cycle.
1004
- * Callers pass the id and the spec instead.
1005
- */
1006
-
1007
- /**
1008
- * One configurable option. `kind` decides the merge semantics, so no rule
1009
- * writes merge code of its own: `integer` replaces, and the two collection
1010
- * kinds ADD to the built-in default (never replace — see the design doc).
1011
- */
1012
- type RuleOptionSpec = {
1013
- kind: 'integer';
1014
- default: number;
1015
- min?: number;
1016
- max?: number;
1017
- } | {
1018
- kind: 'string-list';
1019
- default: readonly string[];
1020
- } | {
1021
- kind: 'string-map';
1022
- default: Readonly<Record<string, string>>;
1023
- };
1024
- /** A rule's configurable options, keyed by option name. */
1025
- type RuleOptionsSpec = Record<string, RuleOptionSpec>;
1026
- /**
1027
- * Typed reads of a resolved options object. `RuleOptions` values are `unknown`
1028
- * (the map is open-ended by design), so without these every rule would carry
1029
- * its own `o.max as number` cast and the "resolution guarantees the declared
1030
- * kind" invariant would live in a dozen places instead of one. `resolveRuleOptions`
1031
- * always seeds every declared key from the spec default and validation rejects a
1032
- * wrongly-typed value up front, so a mismatch here means a rule read a key it
1033
- * never declared — the `fallback` keeps that a wrong number rather than a crash.
1034
- */
1035
- declare function intOption(options: RuleOptions, key: string, fallback?: number): number;
1036
- /** As `intOption`, for a `string-list` option. */
1037
- declare function listOption(options: RuleOptions, key: string): string[];
1038
- /** As `intOption`, for a `string-map` option. */
1039
- declare function mapOption(options: RuleOptions, key: string): Record<string, string>;
1040
- /**
1041
- * Whether any config layer so much as mentions `ruleId` — its `rules` entry, or any `overrides`
1042
- * entry's.
1043
- *
1044
- * A rule that is inert until declared can return early on `false` instead of resolving options once
1045
- * per target and discarding the result. That waste is not hypothetical: the three directory-shaped
1046
- * Architecture rules resolve per directory, so an unconfigured project pays it for every directory
1047
- * under `src/` three times over, on every dev-server save. Measured 2026-07-30 over a synthetic tree
1048
- * of 1,523 directories: 5.4 ms per analysis, for rules that are off by default and therefore produce
1049
- * nothing.
1050
- *
1051
- * Deliberately conservative. It asks only whether the rule is *mentioned*, not whether the mention
1052
- * resolves to a non-empty value, so a `'off'` severity with no options still answers `true` and the
1053
- * caller does its normal work. A cheaper-but-wrong version of this would make a rule skip work it
1054
- * owed; this one can only ever fail to save time.
1055
- */
1056
- declare function isMentionedAnywhere(config: Config, ruleId: string): boolean;
1057
- /**
1058
- * Effective options for a rule at a target: built-in defaults, then
1059
- * `config.rules[ruleId].options`, then every matching `config.overrides` entry
1060
- * in order. Integers take the last value; lists and maps accumulate.
1061
- *
1062
- * `target` omitted skips overrides entirely (project-scoped rules). Callers
1063
- * resolving many targets should hoist `compileOverrides(config)` and pass it as
1064
- * `compiled` — otherwise every call recompiles the globs.
1065
- */
1066
- declare function resolveRuleOptions(ruleId: string, spec: RuleOptionsSpec | undefined, config: Config, target?: {
1067
- route?: string;
1068
- file?: string;
1069
- }, compiled?: CompiledOverride[]): RuleOptions;
1070
- /**
1071
- * Problems with a user-supplied options object, as human-readable sentences
1072
- * (empty = valid). Callers treat any result as fatal: a typo that silently
1073
- * leaves the config inert is the failure this exists to prevent.
1074
- *
1075
- * `baseline`, when given, is the already-resolved value this `options` layer
1076
- * is being merged onto — built-in defaults merged with any earlier layer(s)
1077
- * (e.g. the global `config.rules[id].options`, when `options` is an
1078
- * `overrides[]` entry). The min/max cross-check below compares against it
1079
- * instead of the spec's own default, so a layer that only sets one side of a
1080
- * range is checked against what it actually inherits (design 2026-07-26
1081
- * review, Finding A). Omit it to check `options` against the spec defaults
1082
- * alone, as when validating the global layer itself. A `baseline` that is
1083
- * only partially resolved (missing `min` or `max`) is treated as "can't
1084
- * determine that side" rather than silently comparing against `undefined` —
1085
- * see the `typeof` guard below.
1086
- *
1087
- * `skipRangeCheck`, when true, skips the min/max cross-check entirely
1088
- * regardless of `baseline`. A caller sets this when it statically cannot
1089
- * rule out that some *other* config layer narrows the opposite side of the
1090
- * range at the same target — see the CLI's and the Vite plugin's
1091
- * `overrides[]` validation (design 2026-07-26 review, Finding A, third
1092
- * pass).
1093
- */
1094
- declare function validateRuleOptions(ruleId: string, spec: RuleOptionsSpec | undefined, options: RuleOptions, baseline?: RuleOptions, skipRangeCheck?: boolean): string[];
1095
- /**
1096
- * Whether `validateRuleOptions` should skip the min/max cross-check for
1097
- * `overrides[selfIndex].rules[key]` — the whole decision, so the CLI's
1098
- * config-file loader and the Vite plugin can't drift apart on it (they held
1099
- * line-for-line copies of it before).
1100
- *
1101
- * An entry that sets both sides, or neither, is judged against its baseline as
1102
- * usual. An entry that sets only one side is skipped when some *other* entry
1103
- * sets the opposite side, since the two may co-apply at a shared target and be
1104
- * valid there — see `otherOverrideNarrowsOppositeSide` for why that is
1105
- * conservative by necessity and what it lets through.
1106
- */
1107
- declare function shouldSkipRangeCheck(overrides: readonly unknown[], selfIndex: number, key: string, setting: unknown): boolean;
1108
- /**
1109
- * Problems with one user-supplied rule setting — the bare severity string or the
1110
- * object form — as human-readable sentences prefixed with `label` (empty = valid).
1111
- * THE single definition of what a setting may look like: the CLI's config-file
1112
- * loader and the Vite plugin both funnel through it, so a config file and the
1113
- * equivalent plugin option are accepted or rejected identically. Callers treat any
1114
- * result as fatal, on the same reasoning as an unknown rule id — a typo that
1115
- * silently leaves the config inert is the failure being prevented.
1116
- *
1117
- * `label` names the setting in the message (e.g. `rules.seo/title-length`,
1118
- * `overrides[0].rules.architecture`); `ruleId` is the key options messages quote.
1119
- * `allowOptions` is false for a category key: a category may carry a severity, but
1120
- * options are rule-specific and meaningless there. `baseline` and `skipRangeCheck`
1121
- * are passed through to `validateRuleOptions`.
1122
- */
1123
- declare function validateRuleSetting(label: string, ruleId: string, setting: unknown, spec: RuleOptionsSpec | undefined, opts: {
1124
- allowOptions: boolean;
1125
- baseline?: RuleOptions;
1126
- skipRangeCheck?: boolean;
1127
- }): string[];
1128
-
1129
- /** Input given to every rule. Mode-independent: rules see only ResolvedHead[] (design §8, §10). */
1130
- interface RuleContext {
1131
- heads: ResolvedHead[];
1132
- /** Per-route <img> elements for Performance rules (absent in modes that don't collect them). */
1133
- images?: ResolvedImages[];
1134
- /** Per-route page-body headings for seo/single-h1 (absent in modes that don't collect them). */
1135
- headings?: ResolvedHeadings[];
1136
- /** Per-route composed landmark/id occurrences for the route-scoped a11y rules (absent in modes that don't collect them). */
1137
- a11y?: ResolvedA11y[];
1138
- /** Per-file component-body facts for Correctness rules (static/CLI mode only). */
1139
- components?: ComponentFacts[];
1140
- /** Per-file SvelteKit route/hooks facts for the SSR shared-state rules (static/CLI + vite build mode only). */
1141
- kitModules?: KitModuleFacts[];
1142
- /**
1143
- * Every file under `src/`, as project-relative paths, for directory-shaped Architecture rules
1144
- * (static/CLI + vite build mode only). Sorted — see `collectSourceFiles`, which is what both
1145
- * adapters use to build it.
1146
- */
1147
- sourceFiles?: string[];
1148
- project: Project;
1149
- config: Config;
1150
- /**
1151
- * Report per-declaration counts of places this rule examined. The engine supplies it and keys the
1152
- * result by rule id; a rule that does not call it gets no entry, which is distinct from an entry of
1153
- * zeros. Absent in contexts a caller builds directly. Silent last-write-wins: calling it more than
1154
- * once keeps only the most recent map, with no merge and no error — call it once, with the complete
1155
- * counts, at the end of `check()`.
1156
- */
1157
- recordExamined?: (counts: Record<string, number>) => void;
1158
- }
1159
- interface Rule {
1160
- id: string;
1161
- title: string;
1162
- category: Category;
1163
- /** Default severity (overridable by config in later slices). */
1164
- severity: Severity;
1165
- /** 'route' = evaluated per route, 'project' = site-wide, 'component' = evaluated per source file (design §10, §12). */
1166
- scope: Scope;
1167
- /** Why this rule matters — one or two sentences, surfaced by `svelte-vitals explain` (issue #24). */
1168
- rationale: string;
1169
- /** Canonical remediation template, shared by findings and `svelte-vitals explain` (issue #24). */
1170
- fix?: Fix;
1171
- /** Configurable options for this rule; absent means the rule takes none. */
1172
- options?: RuleOptionsSpec;
1173
- /**
1174
- * Evaluate the resolved heads. A single rule may return one Result per route,
1175
- * so it always returns an array. Project-scoped rules return a single element.
1176
- */
1177
- check(ctx: RuleContext): Promise<Result[]>;
1178
- }
1179
- /** Documentation URL for a rule id. Single source so no per-rule URL can drift (issue #24). */
1180
- declare function docsUrlFor(id: string): string;
1181
- /**
1182
- * Whether a detection should be penalized by scoring (design §12). Shared by the
1183
- * future Scorer and by the Slice 0 reporter so pass/fail is decided in one place.
1184
- *
1185
- * presence 'none' → penalized (nothing set anywhere)
1186
- * value 'absent' → penalized (tag present but empty)
1187
- * value 'dynamic' → penalized when treatDynamicAs is not 'pass' (warn or fail)
1188
- * otherwise (static/inherited) → not penalized
1189
- */
1190
- declare function isPenalized(detection: Detection, treatDynamicAs: TreatDynamicAs): boolean;
1191
-
1192
- interface FailedRule {
1193
- id: string;
1194
- message: string;
1195
- }
1196
- /**
1197
- * Run a set of rules against a shared context and collect their findings.
1198
- * Rules are independent, so they run concurrently; results are flattened in
1199
- * rule order for stable output. A rule that throws (sync or async) contributes
1200
- * no results instead of taking the whole run down with it — dev tooling must
1201
- * never throw — and is reported in `failedRules` instead.
1202
- */
1203
- declare function runRules(rules: Rule[], ctx: RuleContext): Promise<{
1204
- results: Result[];
1205
- examined: Record<string, Record<string, number>>;
1206
- failedRules: FailedRule[];
1207
- }>;
1208
-
1209
- /**
1210
- * seo/title-presence — every route should resolve a non-empty <title> (design §11).
1211
- * A dynamic title (`{data.title}`) is the most common correct pattern and must
1212
- * never be flagged as missing; it surfaces as value 'dynamic' (design §4).
1213
- */
1214
- declare const seoTitlePresence: Rule;
1215
-
1216
- declare const seoDescriptionPresence: Rule;
1217
-
1218
- declare const seoCanonicalUrl: Rule;
1219
-
1220
- declare const seoOgImage: Rule;
1221
-
1222
- declare const seoOgTitle: Rule;
1223
-
1224
- declare const seoJsonLd: Rule;
1225
-
1226
- declare const seoRobotsTxt: Rule;
1227
-
1228
- declare const seoSitemapXml: Rule;
1229
-
1230
- declare const seoHtmlLang: Rule;
1231
-
1232
- declare const performanceImageDimensions: Rule;
1233
-
1234
- declare const performanceImageLoadingHint: Rule;
1235
-
1236
- declare const performanceResponsiveImage: Rule;
1237
-
1238
- declare const performancePreloadMissingAs: Rule;
1239
-
1240
- declare const performanceFontPreloadCrossorigin: Rule;
1241
-
1242
- /**
1243
- * performance/lcp-image — LCP image not lazy-loaded. Lazy-loading the largest contentful paint
1244
- * image delays it. Analysis approximates the LCP as the first <img> in document
1245
- * order for the route; if that image is loading="lazy", flag it. Runs in both
1246
- * static (CLI) and rendered (vite) mode, since both providers collect <img>.
1247
- */
1248
- declare const performanceLcpImage: Rule;
1249
-
1250
- /**
1251
- * performance/render-blocking-script — Render-blocking <script> in <head>. A <script src> without
1252
- * defer/async/type=module blocks the parser. SvelteKit's own scripts are
1253
- * module/deferred, so this catches hand-added blocking scripts — in app.html
1254
- * (rendered mode) or in <svelte:head> (static mode). A head with no <script>
1255
- * emits nothing (no signal), like the image rules.
1256
- */
1257
- declare const performanceRenderBlockingScript: Rule;
1258
-
1259
- /**
1260
- * performance/preconnect — Preconnect for third-party origins. A resource from a well-known
1261
- * third-party origin (e.g. Google Fonts) without a preconnect/dns-prefetch pays a
1262
- * connection-setup round-trip. Opt-in by construction: only origins in the
1263
- * allowlist are checked; routes referencing none emit nothing.
1264
- */
1265
- declare const performancePreconnect: Rule;
1266
-
1267
- declare const seoIndexability: Rule;
1268
-
1269
- declare const seoTwitterCard: Rule;
1270
-
1271
- declare const seoOgDescription: Rule;
1272
-
1273
- declare const seoOgUrl: Rule;
1274
-
1275
- declare const seoViewport: Rule;
1276
-
1277
- declare const seoSitemapInRobots: Rule;
1278
-
1279
- declare const seoJsonLdValidity: Rule;
1280
-
1281
- declare const seoJsonLdDeprecatedType: Rule;
1282
-
1283
- declare const seoJsonLdRelativeUrl: Rule;
1284
-
1285
- declare const seoJsonLdDateFormat: Rule;
1286
-
1287
- declare const seoJsonLdPlaceholder: Rule;
1288
-
1289
- declare const seoJsonLdRequiredProps: Rule;
1290
-
1291
- declare const seoTitleLength: Rule;
1292
-
1293
- declare const seoDescriptionLength: Rule;
1294
-
1295
- /**
1296
- * seo/charset — Character encoding. The charset meta lives in `src/app.html`, so it is
1297
- * only visible to rendered analysis (`appliesTo: rendered`), exactly like seo/viewport
1298
- * (viewport). Static route analysis emits nothing instead of false-flagging it.
1299
- */
1300
- declare const seoCharset: Rule;
1301
-
1302
- /**
1303
- * seo/image-alt — Image alt text. Reuses the <img> collection from both providers — the
1304
- * static (CLI) source parser and the rendered (vite) HTML parser — like performance/image-dimensions, performance/image-loading-hint.
1305
- * Presence only: an explicit empty `alt=""` is a valid decorative-image signal and
1306
- * passes; a spread `{...rest}` may supply alt, so it is not flagged.
1307
- */
1308
- declare const seoImageAlt: Rule;
1309
-
1310
- /**
1311
- * seo/hreflang — hreflang / x-default validity. Opt-in: a route with no
1312
- * `<link rel="alternate" hreflang>` emits nothing (monolingual sites are never
1313
- * flagged). When alternates exist, every code must be well-formed and a set of
1314
- * two or more must declare an x-default. Works in both modes.
1315
- */
1316
- declare const seoHreflang: Rule;
1317
-
1318
- /**
1319
- * seo/single-h1 — Heading hierarchy (single H1). Reads the per-route page-body headings
1320
- * channel (collected by both providers), counting `headings` plus `componentHeadings`
1321
- * (static mode only — headings found transitively in rendered child components) as one
1322
- * combined list. Zero <h1> (no primary heading) is a `warning`: defensible, a page needs
1323
- * a primary heading. Two or more is only `info`: a single <h1> is the conventional
1324
- * signal, but no official source documents a ranking penalty for several (2026-08-09 v1
1325
- * rule-validity review, P2 #11) — so it's flagged as a style nit, not a defect. Exactly
1326
- * one passes. A route whose headings were not collected (channel unset) emits nothing. A
1327
- * global `rules: { 'seo/single-h1': <severity> }` override flattens both arms to one
1328
- * severity (design, `applyRuleSeverities`).
1329
- */
1330
- declare const seoSingleH1: Rule;
1331
-
1332
- declare const seoDuplicateTitle: Rule;
1333
-
1334
- declare const seoDuplicateDescription: Rule;
1335
-
1336
- /**
1337
- * seo/heading-level-skip — Skipped heading level. Walking a route's body headings in
1338
- * document order, a level that jumps more than +1 over the previous heading (e.g.
1339
- * h2 → h4) breaks the outline. The first heading has no predecessor (missing/multiple
1340
- * <h1> stays seo/single-h1's concern). A route with no headings emits nothing.
1341
- */
1342
- declare const seoHeadingLevelSkip: Rule;
1343
-
1344
- declare const seoSsrDisabled: Rule;
1345
-
1346
- declare const correctnessEachKey: Rule;
1347
-
1348
- declare const correctnessEachIndexKey: Rule;
1349
-
1350
- declare const correctnessEffectAsDerived: Rule;
1351
-
1352
- declare const correctnessEffectAsOnMount: Rule;
1353
-
1354
- declare const correctnessUnmutatedState: Rule;
1355
-
1356
- /**
1357
- * correctness/prop-mutation — mutating a prop directly is a silent bug in both Svelte modes,
1358
- * for different reasons: in runes mode, a non-$bindable prop mutation doesn't propagate to the
1359
- * parent; in legacy mode (export let), Svelte's reactivity is assignment-based, so a mutating
1360
- * method call (`.push(...)`, etc.) doesn't trigger an update at all without a following
1361
- * reassignment. The two modes can't be mixed in one component, so a given finding is always
1362
- * exactly one or the other — see `legacy` on `ComponentFacts.mutatedProps` (component-parse.ts).
1363
- */
1364
- declare const correctnessPropMutation: Rule;
1365
-
1366
- /**
1367
- * correctness/stale-prop-derivation — a value computed from a prop without $derived (runes
1368
- * mode) or $: (legacy mode) is evaluated once, at init, and silently stops tracking the
1369
- * parent. Svelte's own guidance: treat props as though they will change. The two modes can't
1370
- * be mixed in one component, so a given finding is always exactly one or the other — see
1371
- * `legacy` on `ComponentFacts.stalePropDerivations` (component-parse.ts).
1372
- */
1373
- declare const correctnessStalePropDerivation: Rule;
1374
-
1375
- /**
1376
- * correctness/nonreactive-builtin-state — $state's deep proxy covers plain
1377
- * objects and arrays only. A plain Map/Set/Date/URL/URLSearchParams in $state
1378
- * keeps working as data, but its mutations never reach effects, deriveds, or
1379
- * the template: the UI silently stops updating. svelte/reactivity ships
1380
- * drop-in reactive equivalents for exactly this.
1381
- */
1382
- declare const correctnessNonreactiveBuiltinState: Rule;
1383
-
1384
- /**
1385
- * correctness/checkable-bind-value — bind:value binds the DOM value property. A
1386
- * checkbox/radio's user interaction toggles checkedness, which bind:value never observes. A
1387
- * checkbox throws bind_invalid_checkbox_value in dev (silently tracks value instead of
1388
- * checkedness in prod); a radio throws nothing and its bound state silently never updates.
1389
- * bind:checked (single checkbox) / bind:group (checkbox list, radio group) are the correct
1390
- * bindings.
1391
- */
1392
- declare const correctnessCheckableBindValue: Rule;
1393
-
1394
- declare const correctnessOrphanEffect: Rule;
1395
-
1396
- /**
1397
- * correctness/orphan-lifecycle — svelte lifecycle/context calls guaranteed to run outside component
1398
- * initialisation: module scope in runes modules / `<script module>`, the constructor of
1399
- * a module-scope-instantiated class, and Kit load/handler/`init` bodies. A custom check
1400
- * because the facts live on BOTH the component channel and the Kit-module channel.
1401
- */
1402
- declare const correctnessOrphanLifecycle: Rule;
1403
-
1404
- /**
1405
- * correctness/base-path-navigation — root-relative navigation literals in a project that sets
1406
- * `kit.paths.base`. A custom check because it is gated on a PROJECT fact and its own facts live
1407
- * on BOTH the component channel (`<a href>`, `goto()`) and the Kit-module channel (`redirect()`).
1408
- * With no base path configured the rule emits nothing at all — the gate is the whole point.
1409
- */
1410
- declare const correctnessBasePathNavigation: Rule;
1411
-
1412
- /**
1413
- * correctness/server-browser-global — browser globals read in server-executed MODULE code: module scope of
1414
- * runes modules / `<script module>`, and Kit route/hooks files (top level, handler
1415
- * bodies, the `init` hook). All of it runs on the server, where these globals do not
1416
- * exist — SSR crashes with a ReferenceError. Instance-script reads are correctness/instance-browser-global's
1417
- * (warning) territory. A custom check because the facts live on both channels.
1418
- */
1419
- declare const correctnessServerBrowserGlobal: Rule;
1420
-
1421
- declare const correctnessInstanceBrowserGlobal: Rule;
1422
-
1423
- declare const securityRawHtml: Rule;
1424
-
1425
- declare const securityJavascriptUrl: Rule;
1426
-
1427
- declare const securityHandlerStateWrite: Rule;
1428
-
1429
- declare const securityServerModuleState: Rule;
1430
-
1431
- declare const securitySharedStateImport: Rule;
1432
-
1433
- declare const architectureComponentSize: Rule;
1434
-
1435
- declare const architecturePropCount: Rule;
1436
-
1437
- /**
1438
- * architecture/private-scope-import — a unit inside a declared private scope must not be
1439
- * imported from outside that scope (design 2026-07-28). L3: the scopes are declared by the
1440
- * project via the `scopes` option and never inferred, so the rule is inert until then.
1441
- *
1442
- * Findings are reported at the import site, not at the imported unit: `--diff` filters
1443
- * results to the files that changed, and the author of the violation edited the importer.
1444
- */
1445
- declare const architecturePrivateScopeImport: Rule;
1446
-
1447
- /**
1448
- * architecture/unit-entry-file — a directory declared to be a unit must contain a file named
1449
- * after it (design 2026-07-28). L3: the declarations come from the project's own `units`,
1450
- * `pascalCaseUnits` and `exclude` options and are never inferred, so the rule is inert until then.
1451
- *
1452
- * The directory set is every ancestor path prefix of every file, so a directory holding only
1453
- * subdirectories is checked too. Violations report at a file inside the directory rather than at
1454
- * the directory, because `filterToChangedFiles` keeps only locations git lists as changed.
1455
- */
1456
- declare const architectureUnitEntryFile: Rule;
1457
-
1458
- /**
1459
- * architecture/directory-naming — a directory must be named in the casing its location declares
1460
- * (design 2026-07-29). L3: the declarations come from the project's own `directories` and `exclude`
1461
- * options and are never inferred, so the rule is inert until then.
1462
- *
1463
- * Violations report at a file inside the directory rather than at the directory, because
1464
- * `filterToChangedFiles` keeps only locations git lists as changed and git never lists a directory.
1465
- *
1466
- * There are no pass results. `architecture/unit-entry-file` emits one per conforming unit and can
1467
- * afford to, because it keys the pass on the unit's entry file — a `.svelte` path already present as
1468
- * a score key. This rule's subject is the directory itself, with no such pre-existing key, and
1469
- * `computeScore` seeds every distinct `route` at 100 and averages: a pass per directory would add
1470
- * hundreds of 100s from one `'src/routes/**'` declaration and dilute every real finding.
1471
- */
1472
- declare const architectureDirectoryNaming: Rule;
1473
-
1474
- /**
1475
- * architecture/reserved-directory-names — a directory's immediate subdirectories may only take names
1476
- * the project declared for that position (design 2026-07-29, extended 2026-08-08 for lowercase units —
1477
- * issue #386).
1478
- *
1479
- * The option maps differ in what their keys name. A `scopes` key names the parent directly. A
1480
- * `unitScopes` key names a root, and the rule governs the children of whichever directories beneath
1481
- * it are units whose name begins A–Z — the shape a glob cannot reach, because units nest to arbitrary
1482
- * depth. An `anyCaseUnitScopes` key names a root the same way, but governs units of *either* case:
1483
- * `isUnitDir`'s letter test — A–Z plus a same-stemmed entry file, whatever its extension — excludes a
1484
- * lowercase unit, so without this map no generic unit-map declaration governed one's children (a
1485
- * `scopes` key naming the parent directly could still reach one) — measured at 129 of 299 units (43%)
1486
- * on a real tree. Neither map is named with the bare word "unit": the
1487
- * sibling rule `architecture/reserved-name-placement` records why that word alone is ambiguous between
1488
- * the two predicates once both exist.
1489
- *
1490
- * There are no pass results. `computeScore` seeds every distinct `route` at 100 and averages, and the
1491
- * subject here is a directory with no pre-existing score key, so a pass per directory would add
1492
- * hundreds of 100s from one broad declaration and dilute every real finding.
1493
- */
1494
- declare const architectureReservedDirectoryNames: Rule;
1495
-
1496
- /**
1497
- * architecture/reserved-name-placement — a reserved directory name may appear only in the places
1498
- * declared for it (design 2026-08-06). L3: inert until a placement is declared.
1499
- *
1500
- * The sibling `architecture/reserved-directory-names` says "at this position, only these names"; it
1501
- * cannot say "this name, only at these positions", which for a name appearing in several kinds of
1502
- * place is what a convention actually states.
1503
- *
1504
- * All three maps match the same directory — the reserved-name directory's parent — and differ only in
1505
- * what else they require of it: nothing, that it is a capitalised unit, that it is a unit of either
1506
- * case. A name's permitted positions are the UNION of its entries across the three, because a real
1507
- * convention permits one name under a unit, under a grouping and under a route directory at once.
1508
- *
1509
- * There are no pass results, for the reason the sibling records: `computeScore` seeds every distinct
1510
- * `route` at 100 and averages, and a directory has no pre-existing score key.
1511
- */
1512
- declare const architectureReservedNamePlacement: Rule;
1513
-
1514
- declare const architectureRouteComponentImport: Rule;
1515
-
1516
- declare const architectureDocLinkTarget: Rule;
1517
-
1518
- declare const performanceHeavyImport: Rule;
1519
-
1520
- declare const performanceNamespaceImport: Rule;
1521
-
1522
- /**
1523
- * performance/minify-disabled — a `build.minify: false` left in vite.config ships unminified JS/CSS
1524
- * to production. Project-scope: the fact is produced by the CLI's static parse
1525
- * of vite.config.* (literal-only) or by the Vite plugin's resolved config
1526
- * (exact). Emits a finding only when the fact is set — no pass result.
1527
- */
1528
- declare const performanceMinifyDisabled: Rule;
1529
-
1530
- /**
1531
- * performance/load-waterfall — dependent await chains in universal loads. Server loads are exempt:
1532
- * a dependent chain cannot be parallelized, and on the server there is no better
1533
- * placement to suggest. csr = false files are exempt too — without a client
1534
- * runtime the universal load only runs during SSR.
1535
- */
1536
- declare const performanceLoadWaterfall: Rule;
1537
-
1538
- /**
1539
- * performance/sequential-awaits — independent sequential awaits in any load. Info severity: static
1540
- * data flow cannot see side-effect ordering (e.g. a setup call an API relies
1541
- * on), so the parallelize suggestion stays advisory.
1542
- */
1543
- declare const performanceSequentialAwaits: Rule;
1544
-
1545
- /**
1546
- * performance/state-raw — deep $state proxies every property access; a binding
1547
- * that is only ever reassigned never uses that machinery. Svelte's guidance:
1548
- * large reassign-only objects (API responses, canonically) belong in $state.raw.
1549
- * "Large" is not statically knowable, so a non-primitive literal initializer is
1550
- * the proxy condition.
1551
- */
1552
- declare const performanceStateRaw: Rule;
1553
-
1554
- declare const a11yInvalidRole: Rule;
1555
-
1556
- declare const a11yUnknownAriaAttribute: Rule;
1557
-
1558
- declare const a11yRequiredAriaProps: Rule;
1559
-
1560
- declare const a11yInvalidAriaValue: Rule;
1561
-
1562
- declare const a11yInteractiveNesting: Rule;
1563
-
1564
- declare const a11yAccessibleName: Rule;
1565
-
1566
- declare const a11yLabelHasControl: Rule;
1567
-
1568
- declare const a11yUseList: Rule;
1569
-
1570
- declare const a11yPlaceholderLabelOption: Rule;
1571
-
1572
- declare const a11yRequireDatetime: Rule;
1573
-
1574
- declare const a11yDoctype: Rule;
1575
-
1576
- /**
1577
- * a11y/duplicate-landmark — a composed route (layout chain + page) yields more than one
1578
- * `main` / `banner` / `contentinfo` landmark. `ctx.a11y[].landmarks` already holds the
1579
- * branch-aware-folded representatives, so this rule only counts them per kind, in the
1580
- * fixed KINDS order (it decides emission order and the PASS anchor).
1581
- */
1582
- declare const a11yDuplicateLandmark: Rule;
1583
-
1584
- /**
1585
- * a11y/top-level-landmark — a landmark (`main`/`banner`/`complementary`/`contentinfo`) that
1586
- * composition places inside another landmark. `ctx.a11y[].nestedLandmarks` already carries one
1587
- * entry per nested occurrence, so this rule only reports them.
1588
- */
1589
- declare const a11yTopLevelLandmark: Rule;
1590
-
1591
- /**
1592
- * a11y/id-duplication — a literal id repeated within a composed route. `ctx.a11y[].ids`
1593
- * already holds the branch-aware-folded representatives per id, so this rule only counts them.
1594
- */
1595
- declare const a11yIdDuplication: Rule;
1596
-
1597
- /**
1598
- * a11y/no-missing-id-ref — a `for`/`aria-labelledby`/`aria-describedby`/`aria-controls`/
1599
- * `aria-activedescendant`/same-page `href="#…"` referencing an `id` absent from the composed
1600
- * route. Universal ("no element anywhere defines this id") needs a closed world, so this rule
1601
- * runs only on routes `ctx.a11y[].fullyResolved` marks fully resolved — see the rule docs.
1602
- */
1603
- declare const a11yNoMissingIdRef: Rule;
1604
-
1605
- declare const allRules: Rule[];
1606
-
1607
- /** One configurable option of a rule, flattened for `svelte-vitals explain`'s output. */
1608
- interface RuleOptionInfo {
1609
- name: string;
1610
- /** `integer` replaces the default; `string-list`/`string-map` are ADDED to it. */
1611
- kind: RuleOptionSpec['kind'];
1612
- default: number | readonly string[] | Readonly<Record<string, string>>;
1613
- min?: number;
1614
- max?: number;
1615
- }
1616
- interface RuleInfo {
1617
- id: string;
1618
- title: string;
1619
- category: Category;
1620
- severity: Severity;
1621
- rationale: string;
1622
- docsUrl: string;
1623
- fix?: Fix;
1624
- /**
1625
- * The rule's configurable options, omitted when it takes none. An agent that
1626
- * judges a finding to be a threshold disagreement rather than a defect needs
1627
- * to know the knob exists and what it is called before it can suggest one.
1628
- */
1629
- options?: RuleOptionInfo[];
1630
- }
1631
- /** Look up a rule's static metadata, as `svelte-vitals explain` renders it (issue #24). Rule ids are matched exactly (case-sensitive, e.g. "seo/ssr-disabled"). */
1632
- declare function explainRule(id: string): RuleInfo | undefined;
1633
-
1634
- interface HeadTagRuleOptions {
1635
- id: string;
1636
- title: string;
1637
- severity: Severity;
1638
- /** Identifies the tag this rule looks for. */
1639
- match: (tag: HeadTag) => boolean;
1640
- /** Short human label, e.g. 'description'. */
1641
- label: string;
1642
- recommendation: string;
1643
- /** Why this rule matters — surfaced by `svelte-vitals explain` (issue #24). */
1644
- rationale: string;
1645
- /** Agent-actionable remediation attached to every finding (issue #18). */
1646
- fix?: Fix;
1647
- /**
1648
- * When set, only heads for which this returns true are evaluated; others emit
1649
- * nothing. Use for tags whose canonical location is invisible to a given mode
1650
- * (e.g. viewport lives in app.html → only checkable in rendered mode), so the
1651
- * rule stays silent instead of false-flagging "missing".
1652
- */
1653
- appliesTo?: (head: ResolvedHead) => boolean;
1654
- }
1655
- /** Build a route-scope rule asserting the presence of a single head tag (design §11). */
1656
- declare function headTagRule(opts: HeadTagRuleOptions): Rule;
1657
-
1658
- interface ImageRuleOptions {
1659
- id: string;
1660
- title: string;
1661
- severity: Severity;
1662
- /** Vitals category (default 'performance'); seo/image-alt (alt text) reports under 'seo'. */
1663
- category?: Category;
1664
- /** Noun phrase for messages, e.g. '<img> width/height'. */
1665
- label: string;
1666
- recommendation: string;
1667
- rationale: string;
1668
- fix?: Fix;
1669
- /** Returns true when the image satisfies the rule (passes). */
1670
- ok: (img: ImageInfo) => boolean;
1671
- }
1672
- /** Build a route-scoped <img> rule that checks each image against `ok` (issue #10). */
1673
- declare function imageRule(opts: ImageRuleOptions): Rule;
1674
-
1675
- interface LinkRuleOptions {
1676
- id: string;
1677
- title: string;
1678
- severity: Severity;
1679
- /** Noun phrase for messages, e.g. '`as` on a preloaded `<link>`'. */
1680
- label: string;
1681
- recommendation: string;
1682
- rationale: string;
1683
- fix?: Fix;
1684
- /** Which link tags this rule evaluates (e.g. rel === 'preload'). */
1685
- relevant: (tag: HeadTag) => boolean;
1686
- /** Returns true when a relevant link satisfies the rule (passes). */
1687
- ok: (tag: HeadTag) => boolean;
1688
- }
1689
- /** Build a route-scoped Performance rule that checks each relevant <link> in the effective head. */
1690
- declare function linkRule(opts: LinkRuleOptions): Rule;
1691
-
1692
- interface Summary {
1693
- critical: number;
1694
- warning: number;
1695
- info: number;
1696
- /** Passed (not penalized), including dynamic. */
1697
- passed: number;
1698
- /** Subset of passed that resolved dynamically (↯). */
1699
- dynamic: number;
1700
- }
1701
- /** Classify a single result for display/scoring (design §7, §12). */
1702
- type Classification = 'fail' | 'pass' | 'dynamic';
1703
- declare function classify(result: Result, config: Config): Classification;
1704
- /** A penalized dynamic finding is a warning under treatDynamicAs 'warn'; otherwise the rule's severity. */
1705
- declare function effectiveSeverity(result: Result, config: Config): Severity;
1706
- declare function summarize(results: Result[], config: Config): Summary;
1707
- /** Whether the run should fail the build/CI per the minimum failing severity. */
1708
- declare function hasFailureAtOrAbove(summary: Summary, min: Severity): boolean;
1709
-
1710
- /** String decorators for the console reporter. Injected so core stays pure/dep-free. */
1711
- interface Palette {
1712
- bold: (s: string) => string;
1713
- dim: (s: string) => string;
1714
- red: (s: string) => string;
1715
- yellow: (s: string) => string;
1716
- green: (s: string) => string;
1717
- cyan: (s: string) => string;
1718
- }
1719
- /** Default: no decoration (identity) — output is byte-identical to plain text. */
1720
- declare const noColorPalette: Palette;
1721
- /** Green ≥ 90, yellow ≥ 70, red otherwise — for a 0–100 score. */
1722
- declare function scoreColor(p: Palette, score: number): (s: string) => string;
1723
-
1724
- interface ConsoleReportOptions {
1725
- byRoute?: boolean;
1726
- /** Mode label shown in the header (default 'static mode'). */
1727
- mode?: string;
1728
- /** Color decorators; defaults to no color. */
1729
- palette?: Palette;
1730
- /** Show every failing/passed/route entry uncapped and ungrouped, exactly as before this option existed. Default false (capped, grouped by rule). */
1731
- verbose?: boolean;
1732
- /** Internal: set by the CLI when it has already animated the Health header itself — skips the brand/Health lines (category score lines still print). Default false. */
1733
- omitHeader?: boolean;
1734
- }
1735
- /**
1736
- * Render results as a console report string (design §7). Pure: returns a string,
1737
- * the caller is responsible for printing. Prepends a score header; when byRoute is
1738
- * set, adds a per-route score tree.
1739
- */
1740
- declare function formatConsoleReport(results: Result[], config: Config, options?: ConsoleReportOptions): string;
1741
-
1742
- /**
1743
- * Strip ANSI/OSC escape sequences and C0 control characters (except `\n`/`\t`) from a
1744
- * string before it reaches a terminal. POSIX file/route names can contain almost any
1745
- * byte, so a hostile repo can smuggle a terminal-title rewrite, cursor move, or other
1746
- * escape-sequence trick into what looks like plain report text.
1747
- *
1748
- * Only OSC and CSI sequences are pattern-matched and removed whole (payload included) —
1749
- * those cover title-bar writes and cursor/screen control, the two classes with a real
1750
- * blast radius. Any other `ESC` byte (rarer single/two-byte forms like reset or
1751
- * save-cursor) falls through to the final C0 sweep below, which drops the lone `ESC`
1752
- * but — deliberately, not swallowing an adjacent legitimate character — leaves whatever
1753
- * printable byte follows it as stray text.
1754
- * ponytail: doesn't special-case every Fe escape form; broaden the CSI/OSC patterns if a
1755
- * concrete non-CSI/OSC sequence turns out to matter.
1756
- */
1757
- declare function terminalSafe(text: string): string;
1758
-
1759
- interface ScoreModel {
1760
- routeAverage: number;
1761
- sitePenalty: number;
1762
- /** Headline cap value when it actually lowered the score, else null. */
1763
- criticalCap: number | null;
1764
- }
1765
- interface ScoreResult {
1766
- /** The score as displayed: `Math.floor(rawScore)`, so 100 means the deduction was exactly zero. */
1767
- score: number;
1768
- /**
1769
- * The same score before flooring, after `sitePenalty` and the cap, clamped to `[0, 100]`. Exposed so
1770
- * `computeHealth` can average unrounded values and floor once — averaging the displayed scores would
1771
- * compose two roundings and move Health by up to two points.
1772
- */
1773
- rawScore: number;
1774
- scoreModel: ScoreModel;
1775
- /** Keys this result set touched. */
1776
- keys: number;
1777
- /** Keys carrying at least one penalized finding. */
1778
- affectedKeys: number;
1779
- }
1780
- interface ScoreOptions {
1781
- applyCriticalCap?: boolean;
1782
- /** The rules that ran. Defaults to the selected registry; supplied by tests and custom rule sets. */
1783
- rules?: readonly Rule[];
1784
- }
1785
- /** Compute the headline score and its breakdown (design §12). */
1786
- declare function computeScore(results: Result[], config: Config, options?: ScoreOptions): ScoreResult;
1787
- /** Compute an independent score per category present in `results` (issue #10). */
1788
- declare function scoresByCategory(results: Result[], config: Config, options?: ScoreOptions): Partial<Record<Category, ScoreResult>>;
1789
- interface HealthResult {
1790
- /** Weighted overall score across present categories (0–100). */
1791
- health: number;
1792
- categories: Partial<Record<Category, ScoreResult>>;
1793
- /** Effective weight used per present category. */
1794
- weights: Partial<Record<Category, number>>;
1795
- }
1796
- /** Combined weighted Health score over the categories present in `results` (#10). */
1797
- declare function computeHealth(results: Result[], config: Config): HealthResult;
1798
-
1799
- declare function issueOf(result: Result): {
1800
- fix?: Fix | undefined;
1801
- docsUrl?: string | undefined;
1802
- recommendation: string | undefined;
1803
- line?: number | undefined;
1804
- id: string;
1805
- category: Category;
1806
- title: string;
1807
- detection: Detection;
1808
- location: string | undefined;
1809
- };
1810
- type JsonIssue = ReturnType<typeof issueOf> & {
1811
- severity: ReturnType<typeof effectiveSeverity>;
1812
- };
1813
- /**
1814
- * Per-rule counts. A rule present with `findings: 0` ran and reported nothing, and an absent rule was not
1815
- * selected — but only when the caller supplied `ruleIds`. Without it the map is seeded from results alone,
1816
- * so absence means "produced nothing" rather than "not selected".
1817
- */
1818
- interface RuleEvidence {
1819
- findings: number;
1820
- passed: number;
1821
- }
1822
- interface JsonReport {
1823
- version: string;
1824
- score: number;
1825
- weights: Partial<Record<Category, number>>;
1826
- categories: Record<string, {
1827
- score: number;
1828
- scoreModel: ScoreModel;
1829
- keys: number;
1830
- affectedKeys: number;
1831
- }>;
1832
- summary: Summary;
1833
- rules: Record<string, RuleEvidence>;
1834
- routes: Array<{
1835
- route: string;
1836
- score: number;
1837
- categories: Record<string, number>;
1838
- issues: JsonIssue[];
1839
- }>;
1840
- siteIssues: JsonIssue[];
1841
- /**
1842
- * Floored severity weight per `"<category>::<scope>"` pair. Reproduces a `routes[].categories` entry
1843
- * (`100 - 100 * failed / inventories[pair]`): a key is either a route id or a source file path, and
1844
- * those two key spaces never overlap, so a category's results on one key always draw on a single scope.
1845
- * It does not reproduce `routes[].score`: a route spanning more than one pair sums their *raw* weights
1846
- * and floors that sum once, so adding this map's already-floored entries and re-dividing can disagree.
1847
- */
1848
- inventories: Record<string, number>;
1849
- /**
1850
- * Per-rule, per-declaration counts of places examined. Unlike `rules`, this describes the analysis
1851
- * rather than the report: `--diff`, `--baseline` and suppressions do not narrow it. Three states: a
1852
- * rule that reports no counts has no entry; a rule that counts but whose configuration declares
1853
- * nothing has an empty entry; a declaration that judged nothing has an entry of `0`.
1854
- */
1855
- examined?: Record<string, Record<string, number>>;
1856
- }
1857
- /** Build the structured JSON report object (design §7). The shape the `json` reporter emits (issue #24). */
1858
- declare function buildJsonReport(results: Result[], config: Config, meta: {
1859
- version: string;
1860
- }, ruleIds?: readonly string[], examined?: Record<string, Record<string, number>>): JsonReport;
1861
- /** Render results as the documented JSON report string (design §7). */
1862
- declare function formatJsonReport(results: Result[], config: Config, meta: {
1863
- version: string;
1864
- }, ruleIds?: readonly string[], examined?: Record<string, Record<string, number>>): string;
1865
-
1866
- /** Render failing findings as an agent-actionable Markdown remediation document (issue #18). */
1867
- declare function formatAgentReport(results: Result[], config: Config): string;
1868
-
1869
- /** Render penalized findings as a SARIF 2.1.0 log string (issue #18, design slice 5). */
1870
- declare function formatSarifReport(results: Result[], config: Config, meta: {
1871
- version: string;
1872
- }): string;
1873
-
1874
- /**
1875
- * Render penalized findings as GitHub Actions workflow commands (issue #18, design slice 5).
1876
- * GitHub turns these into inline PR annotations and run-annotation entries. Returns '' when clean.
1877
- */
1878
- declare function formatGithubReport(results: Result[], config: Config): string;
1879
-
1880
- /**
1881
- * Render a compact Markdown summary — Health score, per-category table, severity counts, and
1882
- * a findings table — suitable for a GitHub Actions job summary or a sticky PR comment
1883
- * (`svelte-vitals ci install`). Delegates all aggregation to `buildJsonReport` so the numbers
1884
- * never drift from the JSON/console reporters.
1885
- */
1886
- declare function formatMarkdownReport(results: Result[], config: Config, meta: {
1887
- version: string;
1888
- }): string;
1889
-
1890
- type Band = 'good' | 'warn' | 'poor';
1891
- declare const BAND_COLOR: Record<Band, string>;
1892
- declare function scoreBand(score: number): Band;
1893
- declare function escapeHtml(s: string): string;
1894
- /**
1895
- * Return the URL only when it uses a safe http/https scheme, else null.
1896
- * Guards a finding's `docsUrl` against `javascript:`/`data:` hrefs — escapeHtml
1897
- * neutralizes attribute breakout but not a malicious scheme. Browsers strip
1898
- * ASCII whitespace (tab/newline/CR) from a URL before resolving its scheme (so
1899
- * `java\tscript:` runs as `javascript:`), so strip whitespace first; anything not
1900
- * plainly http(s):// afterward is rejected. Pure string work — no `URL` global,
1901
- * keeping core runtime-agnostic and lib-minimal.
1902
- */
1903
- declare function safeHref(url: string): string | null;
1904
- /** Provenance of a route's findings: real rendered page vs. source-only analysis. */
1905
- type RouteBadge = 'measured' | 'static';
1906
- interface AppSnapshot {
1907
- report: JsonReport;
1908
- badges: Record<string, RouteBadge>;
1909
- analyzing: boolean;
1910
- /** Monotonically increasing; lets the client discard an out-of-order /data.json response. */
1911
- sequence: number;
1912
- /** Whether a dev server is behind this page (SSE updates, /data.json refetch, connection dot). */
1913
- live: boolean;
1914
- meta: {
1915
- version: string;
1916
- coreVersion?: string;
1917
- };
1918
- }
1919
- /**
1920
- * Hand-authored CSS for the master/detail shell. Reuses the same design-token
1921
- * names/values as the rest of the project, and adds a dark theme via
1922
- * `:root[data-theme="dark"]` plus a `prefers-color-scheme` fallback for a
1923
- * first-ever visit with no stored preference.
1924
- */
1925
- declare const APP_STYLE: string;
1926
- /**
1927
- * Hand-authored client script for the shell — no bundler, no framework. Parses the
1928
- * AppSnapshot embedded by renderAppShell, then owns all rendering: sidebar
1929
- * (search/sort/route list) and detail pane (Overview or a selected route). When the
1930
- * snapshot says `live`, it additionally re-fetches /data.json on every SSE `update`
1931
- * and on the EventSource's `open` event (covers the initial connection and every
1932
- * auto-reconnect, since EventSource replays no missed events) — discarding any
1933
- * response whose `sequence` isn't newer than what's already rendered.
1934
- */
1935
- declare const APP_SCRIPT: string;
1936
- /** The shell HTML: empty sidebar/detail/topbar containers, the stylesheet, the
1937
- * client script, and the snapshot embedded as JSON for the client's first paint. */
1938
- declare function renderAppShell(snapshot: AppSnapshot): string;
1939
- /**
1940
- * Static (non-live) document over a prebuilt JsonReport — kept as the public name the
1941
- * html reporter has always exported.
1942
- */
1943
- declare function buildHtmlDocument(report: JsonReport, meta: {
1944
- version: string;
1945
- coreVersion?: string;
1946
- }): string;
1947
- /** Render results as the self-contained HTML report (the CLI's `--reporter html`). */
1948
- declare function formatHtmlReport(results: Result[], config: Config, meta: {
1949
- version: string;
1950
- coreVersion?: string;
1951
- }): string;
1952
-
1953
- export { type A11yOccurrenceInfo, APP_SCRIPT, APP_STYLE, type AppSnapshot, BAND_COLOR, type BranchStep, CATEGORIES, CHILD_NODE_KEYS, type Category, type Classification, type CompiledOverride, type ComponentFacts, type Config, type ConsoleReportOptions, type Detection, type EachBlockFact, type EffectFact, type FailedRule, type Fix, type HeadProvider, type HeadTag, type HeadingInfo, type HealthResult, IDREF_ATTRS, type ImageInfo, type JsonReport, type KitAlias, type KitModuleFacts, LANDMARK_ROLES, type OrphanEffectFact, type Palette, type Presence, type Project, ROBOTS_SOURCE_PATHS, type RawKitAliases, type ResolvedA11y, type ResolvedHead, type ResolvedHeadings, type ResolvedImages, type Result, type RouteBadge, type Rule, type RuleContext, type RuleEvidence, type RuleInfo, type RuleOptionInfo, type RuleOptionSpec, type RuleOptions, type RuleOptionsSpec, type RuleOverride, type RuleSetting, type RuleSettingObject, type Runtime, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type SourceSpan, type Summary, type SuppressionDirective, type TreatDynamicAs, VITE_CONFIG_FILES, type Value, type ViteKitConfigResult, a11yAccessibleName, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoMissingIdRef, a11yPlaceholderLabelOption, a11yRequireDatetime, a11yRequiredAriaProps, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUseList, allRules, applyOverrides, applyRuleSeverities, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, compileOverrides, computeHealth, computeScore, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitAliasesInSvelteConfig, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isMentionedAnywhere, isPenalized, isTopFragment, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRepoLocalPath, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, splitTokens, summarize, terminalSafe, textFromNodes, validateRuleOptions, validateRuleSetting, valueFromNodes, withFailedRulesOff };
1
+ export { ap as CATEGORIES, e as Category, i as Config, aq as Detection, F as Fix, J as JsonReport, ar as Presence, d as Result, as as RuleEvidence, at as RuleOptions, au as RuleOverride, av as RuleSetting, aw as RuleSettingObject, ax as ScoreModel, f as Severity, ay as Summary, az as TreatDynamicAs, V as Value, aA as defineConfig, a0 as formatGithubReport, a2 as formatMarkdownReport, a3 as hasFailureAtOrAbove, ak as summarize } from './index-DbUx4tlY.js';