@svelte-vitals/core 0.8.0 → 0.10.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
@@ -53,7 +53,7 @@ interface Result {
53
53
  line?: number;
54
54
  }
55
55
  type Scope = 'route' | 'project';
56
- type Category = 'seo' | 'performance' | 'a11y' | 'maintainability';
56
+ type Category = 'seo' | 'performance';
57
57
  /** How dynamic (`{data.title}`) values are treated by scoring (design §4, §12). */
58
58
  type TreatDynamicAs = 'pass' | 'warn' | 'fail';
59
59
  /** Per-rule override: disable, or change severity. */
@@ -66,6 +66,8 @@ interface Config {
66
66
  rules: Record<string, RuleSetting>;
67
67
  /** Minimum severity that fails the run / CI (design §6). */
68
68
  failOn: Severity;
69
+ /** Per-category weights for the combined Health score (default: equal, 1 each) (#10). */
70
+ weights?: Partial<Record<Category, number>>;
69
71
  }
70
72
  declare const defaultConfig: Config;
71
73
  /** Merge user config over defaults. Identity helper for config files (design §6). */
@@ -320,6 +322,15 @@ interface ScoreOptions {
320
322
  declare function computeScore(results: Result[], config: Config, options?: ScoreOptions): ScoreResult;
321
323
  /** Compute an independent score per category present in `results` (issue #10). */
322
324
  declare function scoresByCategory(results: Result[], config: Config): Partial<Record<Category, ScoreResult>>;
325
+ interface HealthResult {
326
+ /** Weighted overall score across present categories (0–100). */
327
+ health: number;
328
+ categories: Partial<Record<Category, ScoreResult>>;
329
+ /** Effective weight used per present category. */
330
+ weights: Partial<Record<Category, number>>;
331
+ }
332
+ /** Combined weighted Health score over the categories present in `results` (#10). */
333
+ declare function computeHealth(results: Result[], config: Config): HealthResult;
323
334
 
324
335
  declare function issueOf(result: Result): {
325
336
  fix?: Fix | undefined;
@@ -338,7 +349,7 @@ type JsonIssue = ReturnType<typeof issueOf> & {
338
349
  interface JsonReport {
339
350
  version: string;
340
351
  score: number;
341
- scoreModel: ScoreModel;
352
+ weights: Partial<Record<Category, number>>;
342
353
  categories: Record<string, {
343
354
  score: number;
344
355
  scoreModel: ScoreModel;
@@ -379,4 +390,4 @@ declare function selectRules(rules: Rule[], config: Config): Rule[];
379
390
  /** Apply per-rule severity overrides to results (design §6). */
380
391
  declare function applyRuleSeverities(results: Result[], config: Config): Result[];
381
392
 
382
- export { type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type ImageInfo, type JsonReport, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedImages, type Result, type Rule, type RuleContext, type RuleInfo, type RuleSetting, type Runtime, SITEMAP_SOURCE_PATHS, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type Summary, type TreatDynamicAs, type Value, allRules, applyRuleSeverities, buildJsonReport, classify, computeScore, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, explainRule, formatAgentReport, formatConsoleReport, formatGithubReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, perf001ImageDimensions, perf002ImageLoading, runRules, scoresByCategory, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
393
+ export { type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type HealthResult, type ImageInfo, type JsonReport, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedImages, type Result, type Rule, type RuleContext, type RuleInfo, type RuleSetting, type Runtime, SITEMAP_SOURCE_PATHS, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type Summary, type TreatDynamicAs, type Value, allRules, applyRuleSeverities, buildJsonReport, classify, computeHealth, computeScore, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, explainRule, formatAgentReport, formatConsoleReport, formatGithubReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, perf001ImageDimensions, perf002ImageLoading, runRules, scoresByCategory, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
package/dist/index.js CHANGED
@@ -490,6 +490,27 @@ function scoresByCategory(results, config) {
490
490
  for (const [cat, rs] of byCat) out[cat] = computeScore(rs, config);
491
491
  return out;
492
492
  }
493
+ function computeHealth(results, config) {
494
+ const categories = scoresByCategory(results, config);
495
+ const weights = {};
496
+ let weighted = 0;
497
+ let total = 0;
498
+ for (const cat of Object.keys(categories)) {
499
+ const w = config.weights?.[cat] ?? 1;
500
+ if (!Number.isFinite(w) || w < 0) {
501
+ throw new RangeError(`invalid weight for '${cat}'; expected a finite number >= 0.`);
502
+ }
503
+ weights[cat] = w;
504
+ weighted += categories[cat].score * w;
505
+ total += w;
506
+ }
507
+ if (Object.keys(weights).length === 0) return { health: 100, categories, weights };
508
+ if (total === 0) {
509
+ throw new RangeError("Health weights sum to 0; at least one present category must have a positive weight.");
510
+ }
511
+ const health = Math.round(weighted / total);
512
+ return { health, categories, weights };
513
+ }
493
514
 
494
515
  // src/reporter/console.ts
495
516
  var RULE = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
@@ -500,11 +521,9 @@ var SEVERITY_TITLE = {
500
521
  };
501
522
  var CATEGORY_LABEL = {
502
523
  seo: "SEO",
503
- performance: "Performance",
504
- a11y: "Accessibility",
505
- maintainability: "Maintainability"
524
+ performance: "Performance"
506
525
  };
507
- var CATEGORY_ORDER = ["seo", "performance", "a11y", "maintainability"];
526
+ var CATEGORY_ORDER = ["seo", "performance"];
508
527
  function scoreLine(label, { score, scoreModel }) {
509
528
  const parts = [`route avg ${scoreModel.routeAverage}`];
510
529
  if (scoreModel.sitePenalty > 0) parts.push(`site \u2212${scoreModel.sitePenalty}`);
@@ -531,9 +550,9 @@ function byRouteTree(results, config) {
531
550
  }
532
551
  function formatConsoleReport(results, config, options = {}) {
533
552
  const summary = summarize(results, config);
534
- const byCat = scoresByCategory(results, config);
553
+ const { health, categories: byCat } = computeHealth(results, config);
535
554
  const present2 = CATEGORY_ORDER.filter((c) => byCat[c] !== void 0);
536
- const header = [`Svelte Vitals \xB7 ${options.mode ?? "static mode"}`, ""];
555
+ const header = [`Svelte Vitals \xB7 ${options.mode ?? "static mode"}`, "", `Health: ${health}/100`];
537
556
  for (const c of present2) {
538
557
  header.push(scoreLine(CATEGORY_LABEL[c] ?? c, byCat[c]));
539
558
  }
@@ -580,10 +599,8 @@ function issueOf(result) {
580
599
  };
581
600
  }
582
601
  function buildJsonReport(results, config, meta) {
583
- const seoResults = results.filter((r) => (r.category ?? "seo") === "seo");
584
- const { score, scoreModel } = computeScore(seoResults, config);
602
+ const { health, categories: byCat, weights } = computeHealth(results, config);
585
603
  const summary = summarize(results, config);
586
- const byCat = scoresByCategory(results, config);
587
604
  const categories = Object.fromEntries(
588
605
  Object.entries(byCat).map(([cat, sr]) => [cat, { score: sr.score, scoreModel: sr.scoreModel }])
589
606
  );
@@ -599,7 +616,7 @@ function buildJsonReport(results, config, meta) {
599
616
  issues: rs.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }))
600
617
  }));
601
618
  const siteIssues = results.filter((r) => r.route === void 0 && isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }));
602
- return { version: meta.version, score, scoreModel, categories, summary, routes, siteIssues };
619
+ return { version: meta.version, score: health, weights, categories, summary, routes, siteIssues };
603
620
  }
604
621
  function formatJsonReport(results, config, meta) {
605
622
  return JSON.stringify(buildJsonReport(results, config, meta), null, 2);
@@ -612,7 +629,8 @@ function mdTags(text) {
612
629
  }
613
630
  function formatAgentReport(results, config) {
614
631
  const failing = results.filter((r) => classify(r, config) === "fail");
615
- const lines = ["# svelte-vitals \u2014 fixes", ""];
632
+ const { health } = computeHealth(results, config);
633
+ const lines = ["# svelte-vitals \u2014 fixes", "", `Health: ${health}/100`, ""];
616
634
  if (failing.length === 0) {
617
635
  lines.push("No issues to fix.", "");
618
636
  return lines.join("\n").replace(/\n+$/, "\n");
@@ -771,6 +789,7 @@ export {
771
789
  applyRuleSeverities,
772
790
  buildJsonReport,
773
791
  classify,
792
+ computeHealth,
774
793
  computeScore,
775
794
  defaultConfig,
776
795
  defaultProject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/core",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Shared, runtime-agnostic core for svelte-vitals (types, rule engine, scorer, reporter).",
5
5
  "type": "module",
6
6
  "license": "MIT",