@svelte-vitals/core 0.33.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 +16 -1
- package/dist/index.js +27 -7
- package/package.json +1 -1
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;
|
|
@@ -1471,7 +1475,7 @@ interface ScoreOptions {
|
|
|
1471
1475
|
/** Compute the headline score and its breakdown (design §12). */
|
|
1472
1476
|
declare function computeScore(results: Result[], config: Config, options?: ScoreOptions): ScoreResult;
|
|
1473
1477
|
/** Compute an independent score per category present in `results` (issue #10). */
|
|
1474
|
-
declare function scoresByCategory(results: Result[], config: Config): Partial<Record<Category, ScoreResult>>;
|
|
1478
|
+
declare function scoresByCategory(results: Result[], config: Config, options?: ScoreOptions): Partial<Record<Category, ScoreResult>>;
|
|
1475
1479
|
interface HealthResult {
|
|
1476
1480
|
/** Weighted overall score across present categories (0–100). */
|
|
1477
1481
|
health: number;
|
|
@@ -1512,15 +1516,26 @@ 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>;
|
|
1518
1524
|
routes: Array<{
|
|
1519
1525
|
route: string;
|
|
1520
1526
|
score: number;
|
|
1527
|
+
categories: Record<string, number>;
|
|
1521
1528
|
issues: JsonIssue[];
|
|
1522
1529
|
}>;
|
|
1523
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>;
|
|
1524
1539
|
}
|
|
1525
1540
|
/** Build the structured JSON report object (design §7). The shape the `json` reporter emits (issue #24). */
|
|
1526
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 +=
|
|
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,9 +5878,15 @@ 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 {
|
|
5881
|
+
return {
|
|
5882
|
+
score: Math.floor(rawScore),
|
|
5883
|
+
rawScore,
|
|
5884
|
+
scoreModel: { routeAverage, sitePenalty, criticalCap },
|
|
5885
|
+
keys: keyCount,
|
|
5886
|
+
affectedKeys
|
|
5887
|
+
};
|
|
5879
5888
|
}
|
|
5880
|
-
function scoresByCategory(results, config) {
|
|
5889
|
+
function scoresByCategory(results, config, options = {}) {
|
|
5881
5890
|
const byCat = /* @__PURE__ */ new Map();
|
|
5882
5891
|
for (const r of results) {
|
|
5883
5892
|
const cat = r.category ?? "seo";
|
|
@@ -5886,7 +5895,7 @@ function scoresByCategory(results, config) {
|
|
|
5886
5895
|
bucket.push(r);
|
|
5887
5896
|
}
|
|
5888
5897
|
const out = {};
|
|
5889
|
-
for (const [cat, rs] of byCat) out[cat] = computeScore(rs, config);
|
|
5898
|
+
for (const [cat, rs] of byCat) out[cat] = computeScore(rs, config, options);
|
|
5890
5899
|
return out;
|
|
5891
5900
|
}
|
|
5892
5901
|
function computeHealth(results, config) {
|
|
@@ -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]) => [
|
|
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) {
|
|
@@ -6104,10 +6116,18 @@ function buildJsonReport(results, config, meta, ruleIds) {
|
|
|
6104
6116
|
const routes = [...routeMap.values()].sort((a, b) => a.route.localeCompare(b.route)).map(({ route, results: rs }) => ({
|
|
6105
6117
|
route,
|
|
6106
6118
|
score: computeScore(rs, config, { applyCriticalCap: false }).score,
|
|
6119
|
+
// Per category, scored against that category's own inventory, so this is not guaranteed to average to
|
|
6120
|
+
// `score` — which is one ratio over the union of the pairs the route touched.
|
|
6121
|
+
categories: Object.fromEntries(
|
|
6122
|
+
Object.entries(scoresByCategory(rs, config, { applyCriticalCap: false })).map(([cat, sr]) => [cat, sr.score])
|
|
6123
|
+
),
|
|
6107
6124
|
issues: rs.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }))
|
|
6108
6125
|
}));
|
|
6109
6126
|
const siteIssues = results.filter((r) => r.route === void 0 && isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }));
|
|
6110
|
-
|
|
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 };
|
|
6111
6131
|
}
|
|
6112
6132
|
function formatJsonReport(results, config, meta, ruleIds) {
|
|
6113
6133
|
return JSON.stringify(buildJsonReport(results, config, meta, ruleIds), null, 2);
|