@svelte-vitals/core 0.34.0 → 0.35.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
@@ -1462,6 +1462,10 @@ interface ScoreResult {
1462
1462
  */
1463
1463
  rawScore: number;
1464
1464
  scoreModel: ScoreModel;
1465
+ /** Keys this result set touched. */
1466
+ keys: number;
1467
+ /** Keys carrying at least one penalized finding. */
1468
+ affectedKeys: number;
1465
1469
  }
1466
1470
  interface ScoreOptions {
1467
1471
  applyCriticalCap?: boolean;
@@ -1512,6 +1516,8 @@ interface JsonReport {
1512
1516
  categories: Record<string, {
1513
1517
  score: number;
1514
1518
  scoreModel: ScoreModel;
1519
+ keys: number;
1520
+ affectedKeys: number;
1515
1521
  }>;
1516
1522
  summary: Summary;
1517
1523
  rules: Record<string, RuleEvidence>;
@@ -1522,6 +1528,14 @@ interface JsonReport {
1522
1528
  issues: JsonIssue[];
1523
1529
  }>;
1524
1530
  siteIssues: JsonIssue[];
1531
+ /**
1532
+ * Floored severity weight per `"<category>::<scope>"` pair. Reproduces a `routes[].categories` entry
1533
+ * (`100 - 100 * failed / inventories[pair]`): a key is either a route id or a source file path, and
1534
+ * those two key spaces never overlap, so a category's results on one key always draw on a single scope.
1535
+ * It does not reproduce `routes[].score`: a route spanning more than one pair sums their *raw* weights
1536
+ * and floors that sum once, so adding this map's already-floored entries and re-dividing can disagree.
1537
+ */
1538
+ inventories: Record<string, number>;
1525
1539
  }
1526
1540
  /** Build the structured JSON report object (design §7). The shape the `json` reporter emits (issue #24). */
1527
1541
  declare function buildJsonReport(results: Result[], config: Config, meta: {
package/dist/index.js CHANGED
@@ -5823,6 +5823,7 @@ function ruleScopes(rules) {
5823
5823
 
5824
5824
  // src/scoring/score.ts
5825
5825
  var CRITICAL_CAP = 79;
5826
+ var INVENTORY_FLOOR = 25;
5826
5827
  function clamp(n) {
5827
5828
  return Math.max(0, Math.min(100, n));
5828
5829
  }
@@ -5848,14 +5849,16 @@ function computeScore(results, config, options = {}) {
5848
5849
  const prev = perRule.get(r.id) ?? 0;
5849
5850
  if (DEDUCTION[sev] > prev) perRule.set(r.id, DEDUCTION[sev]);
5850
5851
  }
5852
+ let affectedKeys = 0;
5851
5853
  let totalDeficit = 0;
5852
5854
  for (const [key, pairs] of observed) {
5853
5855
  let failed = 0;
5854
5856
  for (const d of ruleMax.get(key)?.values() ?? []) failed += d;
5857
+ if (failed > 0) affectedKeys += 1;
5855
5858
  let inventoryWeight = 0;
5856
5859
  for (const p of pairs) inventoryWeight += inventory.get(p) ?? 0;
5857
- inventoryWeight = Math.max(inventoryWeight, failed);
5858
- totalDeficit += inventoryWeight === 0 ? 0 : 100 * failed / inventoryWeight;
5860
+ inventoryWeight = Math.max(inventoryWeight, failed, INVENTORY_FLOOR);
5861
+ totalDeficit += 100 * failed / inventoryWeight;
5859
5862
  }
5860
5863
  const keyCount = observed.size;
5861
5864
  const rawRouteAverage = keyCount === 0 ? 100 : 100 - totalDeficit / keyCount;
@@ -5875,7 +5878,13 @@ function computeScore(results, config, options = {}) {
5875
5878
  const capBinds = applyCap && anyCritical && rawUncapped > CRITICAL_CAP;
5876
5879
  const criticalCap = capBinds ? CRITICAL_CAP : null;
5877
5880
  const rawScore = clamp(capBinds ? CRITICAL_CAP : rawUncapped);
5878
- return { score: Math.floor(rawScore), rawScore, scoreModel: { routeAverage, sitePenalty, criticalCap } };
5881
+ return {
5882
+ score: Math.floor(rawScore),
5883
+ rawScore,
5884
+ scoreModel: { routeAverage, sitePenalty, criticalCap },
5885
+ keys: keyCount,
5886
+ affectedKeys
5887
+ };
5879
5888
  }
5880
5889
  function scoresByCategory(results, config, options = {}) {
5881
5890
  const byCat = /* @__PURE__ */ new Map();
@@ -6093,7 +6102,10 @@ function buildJsonReport(results, config, meta, ruleIds) {
6093
6102
  const summary = summarize(results, config);
6094
6103
  const rules = ruleEvidence(results, config, ruleIds);
6095
6104
  const categories = Object.fromEntries(
6096
- Object.entries(byCat).map(([cat, sr]) => [cat, { score: sr.score, scoreModel: sr.scoreModel }])
6105
+ Object.entries(byCat).map(([cat, sr]) => [
6106
+ cat,
6107
+ { score: sr.score, scoreModel: sr.scoreModel, keys: sr.keys, affectedKeys: sr.affectedKeys }
6108
+ ])
6097
6109
  );
6098
6110
  const routeMap = /* @__PURE__ */ new Map();
6099
6111
  for (const r of results) {
@@ -6112,7 +6124,10 @@ function buildJsonReport(results, config, meta, ruleIds) {
6112
6124
  issues: rs.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }))
6113
6125
  }));
6114
6126
  const siteIssues = results.filter((r) => r.route === void 0 && isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }));
6115
- return { version: meta.version, score: health, weights, categories, summary, rules, routes, siteIssues };
6127
+ const inventories = Object.fromEntries(
6128
+ [...buildInventory(config)].map(([pair, weight]) => [pair, Math.max(weight, INVENTORY_FLOOR)])
6129
+ );
6130
+ return { version: meta.version, score: health, weights, categories, summary, rules, routes, siteIssues, inventories };
6116
6131
  }
6117
6132
  function formatJsonReport(results, config, meta, ruleIds) {
6118
6133
  return JSON.stringify(buildJsonReport(results, config, meta, ruleIds), null, 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/core",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Shared, runtime-agnostic core for svelte-vitals (types, rule engine, scorer, reporter).",
5
5
  "type": "module",
6
6
  "license": "MIT",