@vestig/next 0.6.0 → 0.9.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/README.md +1 -1
- package/dist/__tests__/mocks/next-server.d.ts.map +1 -1
- package/dist/__tests__/mocks/next-server.js.map +1 -1
- package/dist/client/error-boundary.d.ts +80 -0
- package/dist/client/error-boundary.d.ts.map +1 -0
- package/dist/client/error-boundary.js +182 -0
- package/dist/client/error-boundary.js.map +1 -0
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/transport.d.ts +50 -0
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/client/transport.js +200 -3
- package/dist/client/transport.js.map +1 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/db/drizzle.d.ts +115 -0
- package/dist/db/drizzle.d.ts.map +1 -0
- package/dist/db/drizzle.js +174 -0
- package/dist/db/drizzle.js.map +1 -0
- package/dist/db/index.d.ts +49 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +51 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/prisma.d.ts +114 -0
- package/dist/db/prisma.d.ts.map +1 -0
- package/dist/db/prisma.js +144 -0
- package/dist/db/prisma.js.map +1 -0
- package/dist/db/query-logger.d.ts +30 -0
- package/dist/db/query-logger.d.ts.map +1 -0
- package/dist/db/query-logger.js +169 -0
- package/dist/db/query-logger.js.map +1 -0
- package/dist/db/types.d.ts +102 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +28 -0
- package/dist/db/types.js.map +1 -0
- package/dist/dev/api/index.d.ts +13 -0
- package/dist/dev/api/index.d.ts.map +1 -0
- package/dist/dev/api/index.js +13 -0
- package/dist/dev/api/index.js.map +1 -0
- package/dist/dev/api/logs-stream.d.ts +119 -0
- package/dist/dev/api/logs-stream.d.ts.map +1 -0
- package/dist/dev/api/logs-stream.js +156 -0
- package/dist/dev/api/logs-stream.js.map +1 -0
- package/dist/dev/filters.d.ts +17 -0
- package/dist/dev/filters.d.ts.map +1 -0
- package/dist/dev/filters.js +100 -0
- package/dist/dev/filters.js.map +1 -0
- package/dist/dev/hooks/use-logs.d.ts +55 -0
- package/dist/dev/hooks/use-logs.d.ts.map +1 -0
- package/dist/dev/hooks/use-logs.js +202 -0
- package/dist/dev/hooks/use-logs.js.map +1 -0
- package/dist/dev/index.d.ts +35 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +41 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/log-entry.d.ts +12 -0
- package/dist/dev/log-entry.d.ts.map +1 -0
- package/dist/dev/log-entry.js +152 -0
- package/dist/dev/log-entry.js.map +1 -0
- package/dist/dev/log-viewer.d.ts +11 -0
- package/dist/dev/log-viewer.d.ts.map +1 -0
- package/dist/dev/log-viewer.js +49 -0
- package/dist/dev/log-viewer.js.map +1 -0
- package/dist/dev/metrics-card.d.ts +18 -0
- package/dist/dev/metrics-card.d.ts.map +1 -0
- package/dist/dev/metrics-card.js +75 -0
- package/dist/dev/metrics-card.js.map +1 -0
- package/dist/dev/metrics-histogram.d.ts +12 -0
- package/dist/dev/metrics-histogram.d.ts.map +1 -0
- package/dist/dev/metrics-histogram.js +69 -0
- package/dist/dev/metrics-histogram.js.map +1 -0
- package/dist/dev/metrics-panel.d.ts +10 -0
- package/dist/dev/metrics-panel.d.ts.map +1 -0
- package/dist/dev/metrics-panel.js +84 -0
- package/dist/dev/metrics-panel.js.map +1 -0
- package/dist/dev/overlay.d.ts +55 -0
- package/dist/dev/overlay.d.ts.map +1 -0
- package/dist/dev/overlay.js +216 -0
- package/dist/dev/overlay.js.map +1 -0
- package/dist/dev/store.d.ts +126 -0
- package/dist/dev/store.d.ts.map +1 -0
- package/dist/dev/store.js +210 -0
- package/dist/dev/store.js.map +1 -0
- package/dist/error/boundary.d.ts +36 -0
- package/dist/error/boundary.d.ts.map +1 -0
- package/dist/error/boundary.js +263 -0
- package/dist/error/boundary.js.map +1 -0
- package/dist/error/breadcrumbs.d.ts +95 -0
- package/dist/error/breadcrumbs.d.ts.map +1 -0
- package/dist/error/breadcrumbs.js +273 -0
- package/dist/error/breadcrumbs.js.map +1 -0
- package/dist/error/fingerprint.d.ts +42 -0
- package/dist/error/fingerprint.d.ts.map +1 -0
- package/dist/error/fingerprint.js +135 -0
- package/dist/error/fingerprint.js.map +1 -0
- package/dist/error/index.d.ts +52 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +56 -0
- package/dist/error/index.js.map +1 -0
- package/dist/error/stack-parser.d.ts +43 -0
- package/dist/error/stack-parser.d.ts.map +1 -0
- package/dist/error/stack-parser.js +166 -0
- package/dist/error/stack-parser.js.map +1 -0
- package/dist/error/types.d.ts +152 -0
- package/dist/error/types.d.ts.map +1 -0
- package/dist/error/types.js +10 -0
- package/dist/error/types.js.map +1 -0
- package/dist/metrics/hooks/use-route-metrics.d.ts +93 -0
- package/dist/metrics/hooks/use-route-metrics.d.ts.map +1 -0
- package/dist/metrics/hooks/use-route-metrics.js +217 -0
- package/dist/metrics/hooks/use-route-metrics.js.map +1 -0
- package/dist/metrics/hooks/use-web-vitals.d.ts +73 -0
- package/dist/metrics/hooks/use-web-vitals.d.ts.map +1 -0
- package/dist/metrics/hooks/use-web-vitals.js +141 -0
- package/dist/metrics/hooks/use-web-vitals.js.map +1 -0
- package/dist/metrics/index.d.ts +51 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +56 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/reporter.d.ts +87 -0
- package/dist/metrics/reporter.d.ts.map +1 -0
- package/dist/metrics/reporter.js +178 -0
- package/dist/metrics/reporter.js.map +1 -0
- package/dist/metrics/store.d.ts +67 -0
- package/dist/metrics/store.d.ts.map +1 -0
- package/dist/metrics/store.js +187 -0
- package/dist/metrics/store.js.map +1 -0
- package/dist/metrics/thresholds.d.ts +84 -0
- package/dist/metrics/thresholds.d.ts.map +1 -0
- package/dist/metrics/thresholds.js +148 -0
- package/dist/metrics/thresholds.js.map +1 -0
- package/dist/metrics/types.d.ts +215 -0
- package/dist/metrics/types.d.ts.map +1 -0
- package/dist/metrics/types.js +10 -0
- package/dist/metrics/types.js.map +1 -0
- package/dist/metrics/web-vitals.d.ts +72 -0
- package/dist/metrics/web-vitals.d.ts.map +1 -0
- package/dist/metrics/web-vitals.js +89 -0
- package/dist/metrics/web-vitals.js.map +1 -0
- package/package.json +28 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/metrics/store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACX,eAAe,EACf,WAAW,EAEX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,MAAM,SAAS,CAAA;AAmBhB,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAE1B;;GAEG;AACH,cAAM,kBAAkB;IACvB,OAAO,CAAC,KAAK,CAIZ;IAED,OAAO,CAAC,SAAS,CAAsB;IAEvC;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI;IAKzC;;OAEG;IACH,OAAO,CAAC,MAAM;IAUd;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,IAAI;IAsB7D;;OAEG;IACH,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAI7D;;OAEG;IACH,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IAchE;;OAEG;IACH,eAAe,IAAI,WAAW,EAAE;IAIhC;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,eAAe,EAAE;IA8B/D;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA6B9C;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAS3C;;OAEG;IACH,WAAW,IAAI,YAAY;IAI3B;;OAEG;IACH,KAAK,IAAI,IAAI;CAKb;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,oBAA2B,CAAA"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Store
|
|
3
|
+
*
|
|
4
|
+
* Simple pub/sub store for performance metrics.
|
|
5
|
+
* Designed for reliability over cleverness - no useSyncExternalStore magic.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { THRESHOLDS, getRating } from './thresholds';
|
|
10
|
+
/**
|
|
11
|
+
* Create a unique ID for metrics
|
|
12
|
+
*/
|
|
13
|
+
function createMetricId() {
|
|
14
|
+
return `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Calculate percentile from sorted array
|
|
18
|
+
*/
|
|
19
|
+
function percentile(sorted, p) {
|
|
20
|
+
if (sorted.length === 0)
|
|
21
|
+
return 0;
|
|
22
|
+
const index = Math.ceil((p / 100) * sorted.length) - 1;
|
|
23
|
+
const clampedIndex = Math.max(0, Math.min(index, sorted.length - 1));
|
|
24
|
+
return sorted[clampedIndex] ?? 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Simple metrics store - no React magic, just data + subscriptions
|
|
28
|
+
*/
|
|
29
|
+
class SimpleMetricsStore {
|
|
30
|
+
state = {
|
|
31
|
+
metrics: [],
|
|
32
|
+
maxMetrics: 500,
|
|
33
|
+
latestVitals: {},
|
|
34
|
+
};
|
|
35
|
+
listeners = new Set();
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to store changes
|
|
38
|
+
*/
|
|
39
|
+
subscribe(listener) {
|
|
40
|
+
this.listeners.add(listener);
|
|
41
|
+
return () => this.listeners.delete(listener);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Notify all listeners of a change
|
|
45
|
+
*/
|
|
46
|
+
notify() {
|
|
47
|
+
for (const listener of this.listeners) {
|
|
48
|
+
try {
|
|
49
|
+
listener();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('[vestig-metrics] Listener error:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Add a new metric to the store
|
|
58
|
+
*/
|
|
59
|
+
addMetric(entry) {
|
|
60
|
+
const metric = {
|
|
61
|
+
...entry,
|
|
62
|
+
id: createMetricId(),
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
};
|
|
65
|
+
this.state.metrics.push(metric);
|
|
66
|
+
// Update latest vitals
|
|
67
|
+
if (entry.type === 'web-vital') {
|
|
68
|
+
this.state.latestVitals[entry.name] = metric;
|
|
69
|
+
}
|
|
70
|
+
// Trim if over limit
|
|
71
|
+
if (this.state.metrics.length > this.state.maxMetrics) {
|
|
72
|
+
this.state.metrics = this.state.metrics.slice(-this.state.maxMetrics);
|
|
73
|
+
}
|
|
74
|
+
this.notify();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get latest vitals - returns a new object each time (React will handle memoization)
|
|
78
|
+
*/
|
|
79
|
+
getLatestVitals() {
|
|
80
|
+
return { ...this.state.latestVitals };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get vitals summary
|
|
84
|
+
*/
|
|
85
|
+
getVitalsSummary() {
|
|
86
|
+
const result = {};
|
|
87
|
+
const vitals = ['LCP', 'CLS', 'INP', 'TTFB', 'FCP'];
|
|
88
|
+
for (const name of vitals) {
|
|
89
|
+
const summary = this.getSummary(name);
|
|
90
|
+
if (summary) {
|
|
91
|
+
result[name] = summary;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get route metrics
|
|
98
|
+
*/
|
|
99
|
+
getRouteMetrics() {
|
|
100
|
+
return this.state.metrics.filter((m) => m.type === 'route');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get histogram for a specific metric
|
|
104
|
+
*/
|
|
105
|
+
getHistogram(name, bucketCount = 10) {
|
|
106
|
+
const values = this.state.metrics.filter((m) => m.name === name).map((m) => m.value);
|
|
107
|
+
if (values.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
const min = Math.min(...values);
|
|
110
|
+
const max = Math.max(...values);
|
|
111
|
+
const range = max - min || 1;
|
|
112
|
+
const bucketSize = range / bucketCount;
|
|
113
|
+
const buckets = Array.from({ length: bucketCount }, (_, i) => ({
|
|
114
|
+
min: min + i * bucketSize,
|
|
115
|
+
max: min + (i + 1) * bucketSize,
|
|
116
|
+
count: 0,
|
|
117
|
+
percentage: 0,
|
|
118
|
+
}));
|
|
119
|
+
for (const value of values) {
|
|
120
|
+
const bucketIndex = Math.min(Math.floor((value - min) / bucketSize), bucketCount - 1);
|
|
121
|
+
const bucket = buckets[bucketIndex];
|
|
122
|
+
if (bucket)
|
|
123
|
+
bucket.count++;
|
|
124
|
+
}
|
|
125
|
+
for (const bucket of buckets) {
|
|
126
|
+
bucket.percentage = (bucket.count / values.length) * 100;
|
|
127
|
+
}
|
|
128
|
+
return buckets;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get summary statistics for a metric
|
|
132
|
+
*/
|
|
133
|
+
getSummary(name) {
|
|
134
|
+
const values = this.state.metrics.filter((m) => m.name === name).map((m) => m.value);
|
|
135
|
+
if (values.length === 0)
|
|
136
|
+
return null;
|
|
137
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
138
|
+
const sum = values.reduce((acc, v) => acc + v, 0);
|
|
139
|
+
const avg = sum / values.length;
|
|
140
|
+
const p75 = percentile(sorted, 75);
|
|
141
|
+
let rating = 'needs-improvement';
|
|
142
|
+
if (name in THRESHOLDS) {
|
|
143
|
+
rating = getRating(name, p75);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
name,
|
|
147
|
+
count: values.length,
|
|
148
|
+
avg,
|
|
149
|
+
min: sorted[0] ?? 0,
|
|
150
|
+
max: sorted[sorted.length - 1] ?? 0,
|
|
151
|
+
p50: percentile(sorted, 50),
|
|
152
|
+
p75,
|
|
153
|
+
p95: percentile(sorted, 95),
|
|
154
|
+
p99: percentile(sorted, 99),
|
|
155
|
+
rating,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the latest metric for a given name
|
|
160
|
+
*/
|
|
161
|
+
getLatest(name) {
|
|
162
|
+
if (name in this.state.latestVitals) {
|
|
163
|
+
return this.state.latestVitals[name] ?? null;
|
|
164
|
+
}
|
|
165
|
+
const metrics = this.state.metrics.filter((m) => m.name === name);
|
|
166
|
+
return metrics[metrics.length - 1] ?? null;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get current snapshot (for debugging)
|
|
170
|
+
*/
|
|
171
|
+
getSnapshot() {
|
|
172
|
+
return this.state;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Clear all metrics
|
|
176
|
+
*/
|
|
177
|
+
clear() {
|
|
178
|
+
this.state.metrics = [];
|
|
179
|
+
this.state.latestVitals = {};
|
|
180
|
+
this.notify();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Global metrics store singleton
|
|
185
|
+
*/
|
|
186
|
+
export const metricsStore = new SimpleMetricsStore();
|
|
187
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/metrics/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Web Vitals Thresholds
|
|
3
|
+
*
|
|
4
|
+
* Official thresholds from Google's Core Web Vitals program.
|
|
5
|
+
* These determine whether a metric is rated as "good", "needs-improvement", or "poor".
|
|
6
|
+
*
|
|
7
|
+
* @see https://web.dev/articles/vitals
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
import type { MetricRating, WebVitalName } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Threshold definition for a metric
|
|
13
|
+
*/
|
|
14
|
+
interface MetricThreshold {
|
|
15
|
+
/** Upper bound for "good" rating (exclusive) */
|
|
16
|
+
good: number;
|
|
17
|
+
/** Upper bound for "needs-improvement" rating (exclusive) */
|
|
18
|
+
poor: number;
|
|
19
|
+
/** Unit of measurement */
|
|
20
|
+
unit: 'ms' | 'score';
|
|
21
|
+
/** Human-readable description */
|
|
22
|
+
description: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Official Core Web Vitals thresholds
|
|
26
|
+
*
|
|
27
|
+
* Values represent the upper bounds:
|
|
28
|
+
* - good: value < threshold.good
|
|
29
|
+
* - needs-improvement: threshold.good <= value < threshold.poor
|
|
30
|
+
* - poor: value >= threshold.poor
|
|
31
|
+
*/
|
|
32
|
+
export declare const THRESHOLDS: Record<WebVitalName, MetricThreshold>;
|
|
33
|
+
/**
|
|
34
|
+
* Calculate rating for a metric value
|
|
35
|
+
*
|
|
36
|
+
* @param name - Web Vital metric name
|
|
37
|
+
* @param value - Metric value
|
|
38
|
+
* @returns Rating based on thresholds
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* getRating('LCP', 2000) // 'good'
|
|
43
|
+
* getRating('LCP', 3000) // 'needs-improvement'
|
|
44
|
+
* getRating('LCP', 5000) // 'poor'
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function getRating(name: WebVitalName, value: number): MetricRating;
|
|
48
|
+
/**
|
|
49
|
+
* Get human-readable description for a metric
|
|
50
|
+
*/
|
|
51
|
+
export declare function getMetricDescription(name: WebVitalName): string;
|
|
52
|
+
/**
|
|
53
|
+
* Get unit for a metric
|
|
54
|
+
*/
|
|
55
|
+
export declare function getMetricUnit(name: WebVitalName): 'ms' | 'score';
|
|
56
|
+
/**
|
|
57
|
+
* Format a metric value for display
|
|
58
|
+
*
|
|
59
|
+
* @param name - Metric name
|
|
60
|
+
* @param value - Metric value
|
|
61
|
+
* @returns Formatted string
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* formatMetricValue('LCP', 2500) // '2.50s'
|
|
66
|
+
* formatMetricValue('CLS', 0.15) // '0.15'
|
|
67
|
+
* formatMetricValue('INP', 150) // '150ms'
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function formatMetricValue(name: WebVitalName, value: number): string;
|
|
71
|
+
/**
|
|
72
|
+
* Rating colors for UI display
|
|
73
|
+
*/
|
|
74
|
+
export declare const RATING_COLORS: Record<MetricRating, {
|
|
75
|
+
bg: string;
|
|
76
|
+
text: string;
|
|
77
|
+
border: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Get color scheme for a rating
|
|
81
|
+
*/
|
|
82
|
+
export declare function getRatingColors(rating: MetricRating): (typeof RATING_COLORS)[MetricRating];
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=thresholds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thresholds.d.ts","sourceRoot":"","sources":["../../src/metrics/thresholds.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEzD;;GAEG;AACH,UAAU,eAAe;IACxB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAA;IACZ,0BAA0B;IAC1B,IAAI,EAAE,IAAI,GAAG,OAAO,CAAA;IACpB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,YAAY,EAAE,eAAe,CAuD5D,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAOzE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAe3E;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAI5F,CAAA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,YAAY,CAAC,CAE1F"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Web Vitals Thresholds
|
|
3
|
+
*
|
|
4
|
+
* Official thresholds from Google's Core Web Vitals program.
|
|
5
|
+
* These determine whether a metric is rated as "good", "needs-improvement", or "poor".
|
|
6
|
+
*
|
|
7
|
+
* @see https://web.dev/articles/vitals
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Official Core Web Vitals thresholds
|
|
12
|
+
*
|
|
13
|
+
* Values represent the upper bounds:
|
|
14
|
+
* - good: value < threshold.good
|
|
15
|
+
* - needs-improvement: threshold.good <= value < threshold.poor
|
|
16
|
+
* - poor: value >= threshold.poor
|
|
17
|
+
*/
|
|
18
|
+
export const THRESHOLDS = {
|
|
19
|
+
/**
|
|
20
|
+
* Largest Contentful Paint
|
|
21
|
+
* Measures loading performance - when the largest content element becomes visible
|
|
22
|
+
*/
|
|
23
|
+
LCP: {
|
|
24
|
+
good: 2500,
|
|
25
|
+
poor: 4000,
|
|
26
|
+
unit: 'ms',
|
|
27
|
+
description: 'Largest Contentful Paint - Loading performance',
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Cumulative Layout Shift
|
|
31
|
+
* Measures visual stability - how much the page layout shifts unexpectedly
|
|
32
|
+
*/
|
|
33
|
+
CLS: {
|
|
34
|
+
good: 0.1,
|
|
35
|
+
poor: 0.25,
|
|
36
|
+
unit: 'score',
|
|
37
|
+
description: 'Cumulative Layout Shift - Visual stability',
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Interaction to Next Paint
|
|
41
|
+
* Measures interactivity - time from user interaction to visual feedback
|
|
42
|
+
*/
|
|
43
|
+
INP: {
|
|
44
|
+
good: 200,
|
|
45
|
+
poor: 500,
|
|
46
|
+
unit: 'ms',
|
|
47
|
+
description: 'Interaction to Next Paint - Interactivity',
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Time to First Byte
|
|
51
|
+
* Measures server responsiveness - time until first byte of response
|
|
52
|
+
*/
|
|
53
|
+
TTFB: {
|
|
54
|
+
good: 800,
|
|
55
|
+
poor: 1800,
|
|
56
|
+
unit: 'ms',
|
|
57
|
+
description: 'Time to First Byte - Server responsiveness',
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* First Contentful Paint
|
|
61
|
+
* Measures perceived load speed - when first content is rendered
|
|
62
|
+
*/
|
|
63
|
+
FCP: {
|
|
64
|
+
good: 1800,
|
|
65
|
+
poor: 3000,
|
|
66
|
+
unit: 'ms',
|
|
67
|
+
description: 'First Contentful Paint - Perceived load speed',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Calculate rating for a metric value
|
|
72
|
+
*
|
|
73
|
+
* @param name - Web Vital metric name
|
|
74
|
+
* @param value - Metric value
|
|
75
|
+
* @returns Rating based on thresholds
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* getRating('LCP', 2000) // 'good'
|
|
80
|
+
* getRating('LCP', 3000) // 'needs-improvement'
|
|
81
|
+
* getRating('LCP', 5000) // 'poor'
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function getRating(name, value) {
|
|
85
|
+
const threshold = THRESHOLDS[name];
|
|
86
|
+
if (!threshold)
|
|
87
|
+
return 'needs-improvement';
|
|
88
|
+
if (value < threshold.good)
|
|
89
|
+
return 'good';
|
|
90
|
+
if (value < threshold.poor)
|
|
91
|
+
return 'needs-improvement';
|
|
92
|
+
return 'poor';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get human-readable description for a metric
|
|
96
|
+
*/
|
|
97
|
+
export function getMetricDescription(name) {
|
|
98
|
+
return THRESHOLDS[name]?.description ?? name;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get unit for a metric
|
|
102
|
+
*/
|
|
103
|
+
export function getMetricUnit(name) {
|
|
104
|
+
return THRESHOLDS[name]?.unit ?? 'ms';
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Format a metric value for display
|
|
108
|
+
*
|
|
109
|
+
* @param name - Metric name
|
|
110
|
+
* @param value - Metric value
|
|
111
|
+
* @returns Formatted string
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* formatMetricValue('LCP', 2500) // '2.50s'
|
|
116
|
+
* formatMetricValue('CLS', 0.15) // '0.15'
|
|
117
|
+
* formatMetricValue('INP', 150) // '150ms'
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export function formatMetricValue(name, value) {
|
|
121
|
+
const unit = getMetricUnit(name);
|
|
122
|
+
if (unit === 'score') {
|
|
123
|
+
return value.toFixed(3);
|
|
124
|
+
}
|
|
125
|
+
// For timing metrics
|
|
126
|
+
if (value < 1) {
|
|
127
|
+
return `${(value * 1000).toFixed(0)}μs`;
|
|
128
|
+
}
|
|
129
|
+
if (value < 1000) {
|
|
130
|
+
return `${value.toFixed(0)}ms`;
|
|
131
|
+
}
|
|
132
|
+
return `${(value / 1000).toFixed(2)}s`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Rating colors for UI display
|
|
136
|
+
*/
|
|
137
|
+
export const RATING_COLORS = {
|
|
138
|
+
good: { bg: '#dcfce7', text: '#166534', border: '#86efac' },
|
|
139
|
+
'needs-improvement': { bg: '#fef3c7', text: '#92400e', border: '#fcd34d' },
|
|
140
|
+
poor: { bg: '#fee2e2', text: '#991b1b', border: '#fca5a5' },
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Get color scheme for a rating
|
|
144
|
+
*/
|
|
145
|
+
export function getRatingColors(rating) {
|
|
146
|
+
return RATING_COLORS[rating];
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=thresholds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thresholds.js","sourceRoot":"","sources":["../../src/metrics/thresholds.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Web Vitals and Route Metrics Types
|
|
3
|
+
*
|
|
4
|
+
* These types define the structure for capturing and reporting
|
|
5
|
+
* performance metrics in Next.js applications.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Web Vital metric names
|
|
11
|
+
* - LCP: Largest Contentful Paint (loading performance)
|
|
12
|
+
* - CLS: Cumulative Layout Shift (visual stability)
|
|
13
|
+
* - INP: Interaction to Next Paint (interactivity)
|
|
14
|
+
* - TTFB: Time to First Byte (server responsiveness)
|
|
15
|
+
* - FCP: First Contentful Paint (perceived load speed)
|
|
16
|
+
*/
|
|
17
|
+
export type WebVitalName = 'LCP' | 'CLS' | 'INP' | 'TTFB' | 'FCP';
|
|
18
|
+
/**
|
|
19
|
+
* Rating for a metric based on Core Web Vitals thresholds
|
|
20
|
+
*/
|
|
21
|
+
export type MetricRating = 'good' | 'needs-improvement' | 'poor';
|
|
22
|
+
/**
|
|
23
|
+
* Navigation type from Performance API
|
|
24
|
+
*/
|
|
25
|
+
export type NavigationType = 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' | 'restore';
|
|
26
|
+
/**
|
|
27
|
+
* Web Vital metric captured from the browser
|
|
28
|
+
*/
|
|
29
|
+
export interface WebVitalMetric {
|
|
30
|
+
/** Metric name (LCP, CLS, INP, TTFB, FCP) */
|
|
31
|
+
name: WebVitalName;
|
|
32
|
+
/** Metric value (ms for timing metrics, score for CLS) */
|
|
33
|
+
value: number;
|
|
34
|
+
/** Rating based on Core Web Vitals thresholds */
|
|
35
|
+
rating: MetricRating;
|
|
36
|
+
/** Change since last measurement */
|
|
37
|
+
delta: number;
|
|
38
|
+
/** Unique identifier for this metric instance */
|
|
39
|
+
id: string;
|
|
40
|
+
/** How the page was navigated to */
|
|
41
|
+
navigationType: NavigationType;
|
|
42
|
+
/** Entries that contributed to this metric */
|
|
43
|
+
entries: PerformanceEntry[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Route-specific performance metric
|
|
47
|
+
*/
|
|
48
|
+
export interface RouteMetric {
|
|
49
|
+
/** Route pathname (e.g., /dashboard, /users/[id]) */
|
|
50
|
+
pathname: string;
|
|
51
|
+
/** Time to render the route in ms */
|
|
52
|
+
renderTime: number;
|
|
53
|
+
/** Time to hydrate the route in ms */
|
|
54
|
+
hydrationTime: number;
|
|
55
|
+
/** Time to fetch route data (if applicable) */
|
|
56
|
+
dataFetchTime?: number;
|
|
57
|
+
/** ISO timestamp when metric was captured */
|
|
58
|
+
timestamp: string;
|
|
59
|
+
/** Request ID for correlation with server logs */
|
|
60
|
+
requestId?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Stored metric entry for Dev Overlay and reporting
|
|
64
|
+
*/
|
|
65
|
+
export interface MetricEntry {
|
|
66
|
+
/** Unique identifier */
|
|
67
|
+
id: string;
|
|
68
|
+
/** ISO timestamp */
|
|
69
|
+
timestamp: string;
|
|
70
|
+
/** Type of metric */
|
|
71
|
+
type: 'web-vital' | 'route' | 'custom';
|
|
72
|
+
/** Metric name */
|
|
73
|
+
name: string;
|
|
74
|
+
/** Metric value */
|
|
75
|
+
value: number;
|
|
76
|
+
/** Rating (for web vitals) */
|
|
77
|
+
rating?: MetricRating;
|
|
78
|
+
/** Additional metadata */
|
|
79
|
+
metadata: {
|
|
80
|
+
pathname?: string;
|
|
81
|
+
requestId?: string;
|
|
82
|
+
traceId?: string;
|
|
83
|
+
userAgent?: string;
|
|
84
|
+
navigationType?: NavigationType;
|
|
85
|
+
delta?: number;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Histogram bucket for metric visualization
|
|
90
|
+
*/
|
|
91
|
+
export interface HistogramBucket {
|
|
92
|
+
/** Lower bound of bucket (inclusive) */
|
|
93
|
+
min: number;
|
|
94
|
+
/** Upper bound of bucket (exclusive) */
|
|
95
|
+
max: number;
|
|
96
|
+
/** Count of values in this bucket */
|
|
97
|
+
count: number;
|
|
98
|
+
/** Percentage of total */
|
|
99
|
+
percentage: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Summary statistics for a metric
|
|
103
|
+
*/
|
|
104
|
+
export interface MetricSummary {
|
|
105
|
+
/** Metric name */
|
|
106
|
+
name: string;
|
|
107
|
+
/** Number of samples */
|
|
108
|
+
count: number;
|
|
109
|
+
/** Average value */
|
|
110
|
+
avg: number;
|
|
111
|
+
/** Minimum value */
|
|
112
|
+
min: number;
|
|
113
|
+
/** Maximum value */
|
|
114
|
+
max: number;
|
|
115
|
+
/** 50th percentile (median) */
|
|
116
|
+
p50: number;
|
|
117
|
+
/** 75th percentile */
|
|
118
|
+
p75: number;
|
|
119
|
+
/** 95th percentile */
|
|
120
|
+
p95: number;
|
|
121
|
+
/** 99th percentile */
|
|
122
|
+
p99: number;
|
|
123
|
+
/** Overall rating based on p75 */
|
|
124
|
+
rating: MetricRating;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Configuration for VestigMetrics component
|
|
128
|
+
*/
|
|
129
|
+
export interface VestigMetricsConfig {
|
|
130
|
+
/**
|
|
131
|
+
* Sampling rate for metrics collection (0.0 - 1.0)
|
|
132
|
+
* @default 1.0 in development, 0.1 in production
|
|
133
|
+
*/
|
|
134
|
+
sampleRate?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Endpoint to report metrics to
|
|
137
|
+
* @default '/api/vestig/metrics'
|
|
138
|
+
*/
|
|
139
|
+
reportEndpoint?: string;
|
|
140
|
+
/**
|
|
141
|
+
* Enable/disable metrics collection
|
|
142
|
+
* @default true
|
|
143
|
+
*/
|
|
144
|
+
enabled?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Batch interval for reporting in milliseconds
|
|
147
|
+
* @default 5000
|
|
148
|
+
*/
|
|
149
|
+
batchInterval?: number;
|
|
150
|
+
/**
|
|
151
|
+
* Report immediately when a metric has poor rating
|
|
152
|
+
* @default true
|
|
153
|
+
*/
|
|
154
|
+
reportPoorImmediately?: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Capture route metrics (render time, hydration)
|
|
157
|
+
* @default true
|
|
158
|
+
*/
|
|
159
|
+
captureRouteMetrics?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Debug mode - logs metrics to console
|
|
162
|
+
* @default false
|
|
163
|
+
*/
|
|
164
|
+
debug?: boolean;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Metrics store state
|
|
168
|
+
*/
|
|
169
|
+
export interface MetricsState {
|
|
170
|
+
/** All captured metrics */
|
|
171
|
+
metrics: MetricEntry[];
|
|
172
|
+
/** Maximum number of metrics to keep in memory */
|
|
173
|
+
maxMetrics: number;
|
|
174
|
+
/** Latest value for each web vital */
|
|
175
|
+
latestVitals: Partial<Record<WebVitalName, MetricEntry>>;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Metrics store interface
|
|
179
|
+
*/
|
|
180
|
+
export interface MetricsStore {
|
|
181
|
+
/** Subscribe to store changes */
|
|
182
|
+
subscribe: (listener: () => void) => () => void;
|
|
183
|
+
/** Get current snapshot of state (for debugging) */
|
|
184
|
+
getSnapshot: () => MetricsState;
|
|
185
|
+
/** Add a new metric */
|
|
186
|
+
addMetric: (entry: Omit<MetricEntry, 'id' | 'timestamp'>) => void;
|
|
187
|
+
/** Get histogram for a specific metric */
|
|
188
|
+
getHistogram: (name: string, bucketCount?: number) => HistogramBucket[];
|
|
189
|
+
/** Get summary statistics for a metric */
|
|
190
|
+
getSummary: (name: string) => MetricSummary | null;
|
|
191
|
+
/** Get all summaries for web vitals */
|
|
192
|
+
getVitalsSummary: () => Partial<Record<WebVitalName, MetricSummary>>;
|
|
193
|
+
/** Get latest vitals */
|
|
194
|
+
getLatestVitals: () => Partial<Record<WebVitalName, MetricEntry>>;
|
|
195
|
+
/** Get route metrics */
|
|
196
|
+
getRouteMetrics: () => MetricEntry[];
|
|
197
|
+
/** Get the latest metric for a given name */
|
|
198
|
+
getLatest: (name: string) => MetricEntry | null;
|
|
199
|
+
/** Clear all metrics */
|
|
200
|
+
clear: () => void;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Payload for metrics reporting
|
|
204
|
+
*/
|
|
205
|
+
export interface MetricsReportPayload {
|
|
206
|
+
/** Metrics to report */
|
|
207
|
+
metrics: MetricEntry[];
|
|
208
|
+
/** Client metadata */
|
|
209
|
+
client: {
|
|
210
|
+
userAgent: string;
|
|
211
|
+
pathname: string;
|
|
212
|
+
timestamp: string;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/metrics/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/metrics/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|