Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 9x 9x 6x 6x 3x 3x 3x 9x 3x 3x 1x 1x 1x 13x 13x 13x 13x 13x 13x 13x 13x 4x 4x 9x 9x 9x 9x 9x 9x 9x 9x 9x 13x 13x 13x 8x 13x 13x 13x 13x 5x 3x 2x 1x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 9x 3x 3x 9x 1x 1x 1x 1x 9x 1x 1x 9x 1x 1x 9x 1x 1x 9x 1x 1x 1x 1x 1x 1x | import { DyE2E_CWVMetric_Type } from '../../contracts/_enums/cwv-metric-type.enum';
import { DyE2E_CWVThresholds_Interface } from '../../contracts/_models/interfaces/cwv-thresholds.interface';
import { DyE2E_CWVMeasurement_DataModel } from '../_models/cwv-measurement.data-model';
/**
* MP-05/Wave-3 — Attribution-based diagnose util.
*
* Bemenet: measurement-tömb + thresholds. Kimenet: human-readable diagnose
* sorok — "LCP p75 = 4200ms (cél: 2500ms). A leglassabb LCP target:
* `IMG#hero-banner`. Optimalizálás: preload + dimension-set."
*/
export interface DyE2E_CWVDiagnose_Hint {
metric: DyE2E_CWVMetric_Type;
/** A measured p75 érték. */
p75: number;
/** A threshold (ha definiált). */
threshold?: number;
/** A legrosszabb futás target-info (a `measurement.attribution`-ből). */
worstTarget?: string;
/** Human-readable summary. */
summary: string;
/** Optimalizálási javaslat. */
suggestion: string;
}
const LCP_SUGGESTIONS: Record<string, string> = {
IMG: 'IMG → preload + explicit width/height + responsive-srcset',
VIDEO: 'VIDEO → poster-image + preload="metadata"',
H1: 'H1 → font-display:swap + preload critical CSS',
DIV: 'DIV background-image → image preload + CSS containment',
};
export class DyE2E_CWVDiagnose_Util {
/**
* Per-metric percentile-számolás (lásd `DyE2E_CWVAggregator_Util` is —
* itt egy-self-contained verzió a diagnose-flow-hoz).
*/
private static percentile(values: number[], p: number): number {
if (values.length === 0) {
return NaN;
}
const sorted: number[] = [...values].sort((a: number, b: number): number => a - b);
if (sorted.length === 1) {
return sorted[0];
}
const rank: number = p * (sorted.length - 1);
const lo: number = Math.floor(rank);
const hi: number = Math.ceil(rank);
if (lo === hi) {
return sorted[lo];
}
return sorted[lo] * (1 - (rank - lo)) + sorted[hi] * (rank - lo);
}
/** Diagnose egy metricre — visszaadja a hint-et vagy null-t ha nincs adat. */
static diagnoseMetric(
measurements: DyE2E_CWVMeasurement_DataModel[],
metric: DyE2E_CWVMetric_Type,
thresholds: DyE2E_CWVThresholds_Interface,
): DyE2E_CWVDiagnose_Hint | null {
const filtered: DyE2E_CWVMeasurement_DataModel[] = measurements.filter(
(m: DyE2E_CWVMeasurement_DataModel): boolean => m.metric === metric,
);
if (filtered.length === 0) {
return null;
}
const values: number[] = filtered.map((m: DyE2E_CWVMeasurement_DataModel): number => m.value);
const p75: number = DyE2E_CWVDiagnose_Util.percentile(values, 0.75);
// Az "legrosszabb" futás attribution-ja a target-info forrása.
const worst: DyE2E_CWVMeasurement_DataModel = filtered.reduce(
(acc: DyE2E_CWVMeasurement_DataModel, m: DyE2E_CWVMeasurement_DataModel): DyE2E_CWVMeasurement_DataModel => m.value > acc.value ? m : acc,
filtered[0],
);
const worstTarget: string | undefined = typeof (worst.attribution as { element?: unknown })?.element === 'string'
? (worst.attribution as { element: string }).element
: typeof (worst.attribution as { target?: unknown })?.target === 'string'
? (worst.attribution as { target: string }).target
: undefined;
const thresholdKey: keyof DyE2E_CWVThresholds_Interface =
metric === DyE2E_CWVMetric_Type.lcp ? 'lcp_p75_ms' :
metric === DyE2E_CWVMetric_Type.cls ? 'cls_p75' :
metric === DyE2E_CWVMetric_Type.inp ? 'inp_p75_ms' :
metric === DyE2E_CWVMetric_Type.ttfb ? 'ttfb_p75_ms' :
metric === DyE2E_CWVMetric_Type.fcp ? 'fcp_p75_ms' :
'lcp_p75_ms';
const threshold: number | undefined = thresholds[thresholdKey];
const fails: boolean = typeof threshold === 'number' && p75 > threshold;
const suggestion: string = DyE2E_CWVDiagnose_Util.suggest(metric, worstTarget, fails);
const summary: string = `${metric.toUpperCase()} p75 = ${p75.toFixed(2)}${
metric === DyE2E_CWVMetric_Type.cls ? '' : 'ms'
}${typeof threshold === 'number' ? ` (cél: ${threshold}${metric === DyE2E_CWVMetric_Type.cls ? '' : 'ms'})` : ''}${
fails ? ' ⚠️ FAIL' : ''
}${worstTarget ? `. Legrosszabb target: \`${worstTarget}\`` : ''}.`;
return {
metric: metric,
p75: p75,
threshold: threshold,
worstTarget: worstTarget,
summary: summary,
suggestion: suggestion,
};
}
/** Diagnose minden metric-re egyszerre. */
static diagnoseAll(
measurements: DyE2E_CWVMeasurement_DataModel[],
thresholds: DyE2E_CWVThresholds_Interface,
): DyE2E_CWVDiagnose_Hint[] {
const metrics: DyE2E_CWVMetric_Type[] = [
DyE2E_CWVMetric_Type.lcp,
DyE2E_CWVMetric_Type.cls,
DyE2E_CWVMetric_Type.inp,
DyE2E_CWVMetric_Type.ttfb,
DyE2E_CWVMetric_Type.fcp,
];
return metrics
.map((m: DyE2E_CWVMetric_Type): DyE2E_CWVDiagnose_Hint | null => DyE2E_CWVDiagnose_Util.diagnoseMetric(measurements, m, thresholds))
.filter((h: DyE2E_CWVDiagnose_Hint | null): h is DyE2E_CWVDiagnose_Hint => h !== null);
}
/** Egy metric-szintű optimalizálási javaslat. */
private static suggest(metric: DyE2E_CWVMetric_Type, target: string | undefined, fails: boolean): string {
if (!fails) {
return 'OK — threshold tartva.';
}
if (metric === DyE2E_CWVMetric_Type.lcp && target) {
// Az IMG/H1/DIV stb. prefixet vegye ki
const tag: string = target.split(/[#.]/)[0].toUpperCase();
return LCP_SUGGESTIONS[tag] ?? `LCP target ${target} — optimalizálás: preload + critical CSS + lazy-load other content`;
}
if (metric === DyE2E_CWVMetric_Type.lcp) {
return 'LCP — preload critical resources + reduce render-blocking JS + use CDN';
}
if (metric === DyE2E_CWVMetric_Type.cls) {
return 'CLS — set explicit dimensions on IMG/IFRAME/AD-slots + avoid font-swap layout-shift + use `font-display: optional`';
}
if (metric === DyE2E_CWVMetric_Type.inp) {
return 'INP — break up long tasks (yield to main thread) + remove unused JS + defer non-critical interactivity';
}
if (metric === DyE2E_CWVMetric_Type.ttfb) {
return 'TTFB — server-side caching + CDN + reduce DB query time + edge-deploy';
}
if (metric === DyE2E_CWVMetric_Type.fcp) {
return 'FCP — inline critical CSS + preconnect to third-party origins + reduce render-blocking JS';
}
return 'Optimalizálási javaslat — lásd web.dev metric-specific docs.';
}
}
|