@vestig/next 0.5.0 → 0.8.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 +42 -0
- package/dist/client/transport.d.ts.map +1 -1
- package/dist/client/transport.js +143 -2
- 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 +96 -0
- package/dist/dev/filters.js.map +1 -0
- package/dist/dev/hooks/use-logs.d.ts +53 -0
- package/dist/dev/hooks/use-logs.d.ts.map +1 -0
- package/dist/dev/hooks/use-logs.js +205 -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 +204 -0
- package/dist/dev/overlay.js.map +1 -0
- package/dist/dev/store.d.ts +186 -0
- package/dist/dev/store.d.ts.map +1 -0
- package/dist/dev/store.js +214 -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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/metrics/hooks/use-route-metrics.d.ts +91 -0
- package/dist/metrics/hooks/use-route-metrics.d.ts.map +1 -0
- package/dist/metrics/hooks/use-route-metrics.js +216 -0
- package/dist/metrics/hooks/use-route-metrics.js.map +1 -0
- package/dist/metrics/hooks/use-web-vitals.d.ts +70 -0
- package/dist/metrics/hooks/use-web-vitals.d.ts.map +1 -0
- package/dist/metrics/hooks/use-web-vitals.js +146 -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 +34 -0
- package/dist/metrics/store.d.ts.map +1 -0
- package/dist/metrics/store.js +172 -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 +211 -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/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +51 -37
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-handler.js +61 -40
- package/dist/server/route-handler.js.map +1 -1
- package/dist/server/server-action.d.ts.map +1 -1
- package/dist/server/server-action.js +54 -33
- package/dist/server/server-action.js.map +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +28 -6
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vestig/next/metrics - Performance Metrics Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides Core Web Vitals and route-level performance
|
|
5
|
+
* monitoring for Next.js applications.
|
|
6
|
+
*
|
|
7
|
+
* @example Quick Start
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/layout.tsx
|
|
10
|
+
* import { VestigMetrics } from '@vestig/next/metrics'
|
|
11
|
+
*
|
|
12
|
+
* export default function RootLayout({ children }) {
|
|
13
|
+
* return (
|
|
14
|
+
* <html>
|
|
15
|
+
* <body>
|
|
16
|
+
* {children}
|
|
17
|
+
* <VestigMetrics />
|
|
18
|
+
* </body>
|
|
19
|
+
* </html>
|
|
20
|
+
* )
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example With Dev Overlay
|
|
25
|
+
* ```tsx
|
|
26
|
+
* import { VestigDevOverlay } from '@vestig/next/dev'
|
|
27
|
+
* import { VestigMetrics } from '@vestig/next/metrics'
|
|
28
|
+
*
|
|
29
|
+
* export default function RootLayout({ children }) {
|
|
30
|
+
* return (
|
|
31
|
+
* <html>
|
|
32
|
+
* <body>
|
|
33
|
+
* {children}
|
|
34
|
+
* {process.env.NODE_ENV === 'development' && <VestigDevOverlay />}
|
|
35
|
+
* <VestigMetrics />
|
|
36
|
+
* </body>
|
|
37
|
+
* </html>
|
|
38
|
+
* )
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @packageDocumentation
|
|
43
|
+
*/
|
|
44
|
+
'use client';
|
|
45
|
+
// Main component
|
|
46
|
+
export { VestigMetrics } from './web-vitals';
|
|
47
|
+
// Store for programmatic access
|
|
48
|
+
export { metricsStore } from './store';
|
|
49
|
+
// Hooks
|
|
50
|
+
export { useWebVitals, useWebVitalsData, useWebVitalsSummary, } from './hooks/use-web-vitals';
|
|
51
|
+
export { useRouteMetrics, useRouteMetricsData, useRenderTiming, useDataFetchTiming, } from './hooks/use-route-metrics';
|
|
52
|
+
// Thresholds and utilities
|
|
53
|
+
export { THRESHOLDS, RATING_COLORS, getRating, getRatingColors, getMetricDescription, getMetricUnit, formatMetricValue, } from './thresholds';
|
|
54
|
+
// Reporter for custom integrations
|
|
55
|
+
export { MetricsReporter } from './reporter';
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,YAAY,CAAA;AAEZ,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAA;AAErE,gCAAgC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAEtC,QAAQ;AACR,OAAO,EACN,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACnB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GAClB,MAAM,2BAA2B,CAAA;AAElC,2BAA2B;AAC3B,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,cAAc,CAAA;AAErB,mCAAmC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Reporter
|
|
3
|
+
*
|
|
4
|
+
* Handles batched reporting of metrics to the server.
|
|
5
|
+
* Uses sendBeacon for reliable delivery on page unload.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { MetricEntry } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* Reporter configuration
|
|
12
|
+
*/
|
|
13
|
+
interface ReporterConfig {
|
|
14
|
+
/** Endpoint to report metrics to */
|
|
15
|
+
endpoint: string;
|
|
16
|
+
/** Batch interval in milliseconds */
|
|
17
|
+
batchInterval: number;
|
|
18
|
+
/** Maximum batch size before forced flush */
|
|
19
|
+
maxBatchSize: number;
|
|
20
|
+
/** Enable debug logging */
|
|
21
|
+
debug: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Metrics Reporter class
|
|
25
|
+
*
|
|
26
|
+
* Collects metrics and sends them in batches to minimize network overhead.
|
|
27
|
+
* Uses sendBeacon on page unload to ensure metrics are not lost.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const reporter = new MetricsReporter({
|
|
32
|
+
* endpoint: '/api/vestig/metrics',
|
|
33
|
+
* batchInterval: 5000,
|
|
34
|
+
* maxBatchSize: 50,
|
|
35
|
+
* debug: false
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* reporter.report(metric)
|
|
39
|
+
* reporter.reportImmediate(criticalMetric) // Bypass batch
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class MetricsReporter {
|
|
43
|
+
private config;
|
|
44
|
+
private queue;
|
|
45
|
+
private flushTimer;
|
|
46
|
+
private isDestroyed;
|
|
47
|
+
constructor(config?: Partial<ReporterConfig>);
|
|
48
|
+
/**
|
|
49
|
+
* Add a metric to the batch queue
|
|
50
|
+
*/
|
|
51
|
+
report(metric: MetricEntry): void;
|
|
52
|
+
/**
|
|
53
|
+
* Report a metric immediately (bypasses batch)
|
|
54
|
+
* Use for critical metrics like "poor" ratings
|
|
55
|
+
*/
|
|
56
|
+
reportImmediate(metric: MetricEntry): void;
|
|
57
|
+
/**
|
|
58
|
+
* Flush all queued metrics
|
|
59
|
+
*/
|
|
60
|
+
flush(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Destroy the reporter and clean up
|
|
63
|
+
*/
|
|
64
|
+
destroy(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Start the batch timer
|
|
67
|
+
*/
|
|
68
|
+
private startBatchTimer;
|
|
69
|
+
/**
|
|
70
|
+
* Set up page unload handler to flush metrics
|
|
71
|
+
*/
|
|
72
|
+
private setupUnloadHandler;
|
|
73
|
+
/**
|
|
74
|
+
* Flush metrics using sendBeacon (for page unload)
|
|
75
|
+
*/
|
|
76
|
+
private flushWithBeacon;
|
|
77
|
+
/**
|
|
78
|
+
* Send metrics via fetch
|
|
79
|
+
*/
|
|
80
|
+
private sendMetrics;
|
|
81
|
+
/**
|
|
82
|
+
* Create the report payload
|
|
83
|
+
*/
|
|
84
|
+
private createPayload;
|
|
85
|
+
}
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/metrics/reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,SAAS,CAAA;AAEhE;;GAEG;AACH,UAAU,cAAc;IACvB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAA;IACpB,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAA;CACd;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,WAAW,CAAQ;gBAEf,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM;IAYhD;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAejC;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAU1C;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;OAEG;IACH,OAAO,IAAI,IAAI;IAYf;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAsBvB;;OAEG;YACW,WAAW;IAwBzB;;OAEG;IACH,OAAO,CAAC,aAAa;CAUrB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Reporter
|
|
3
|
+
*
|
|
4
|
+
* Handles batched reporting of metrics to the server.
|
|
5
|
+
* Uses sendBeacon for reliable delivery on page unload.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Metrics Reporter class
|
|
11
|
+
*
|
|
12
|
+
* Collects metrics and sends them in batches to minimize network overhead.
|
|
13
|
+
* Uses sendBeacon on page unload to ensure metrics are not lost.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const reporter = new MetricsReporter({
|
|
18
|
+
* endpoint: '/api/vestig/metrics',
|
|
19
|
+
* batchInterval: 5000,
|
|
20
|
+
* maxBatchSize: 50,
|
|
21
|
+
* debug: false
|
|
22
|
+
* })
|
|
23
|
+
*
|
|
24
|
+
* reporter.report(metric)
|
|
25
|
+
* reporter.reportImmediate(criticalMetric) // Bypass batch
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class MetricsReporter {
|
|
29
|
+
config;
|
|
30
|
+
queue = [];
|
|
31
|
+
flushTimer = null;
|
|
32
|
+
isDestroyed = false;
|
|
33
|
+
constructor(config = {}) {
|
|
34
|
+
this.config = {
|
|
35
|
+
endpoint: config.endpoint ?? '/api/vestig/metrics',
|
|
36
|
+
batchInterval: config.batchInterval ?? 5000,
|
|
37
|
+
maxBatchSize: config.maxBatchSize ?? 50,
|
|
38
|
+
debug: config.debug ?? false,
|
|
39
|
+
};
|
|
40
|
+
this.startBatchTimer();
|
|
41
|
+
this.setupUnloadHandler();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Add a metric to the batch queue
|
|
45
|
+
*/
|
|
46
|
+
report(metric) {
|
|
47
|
+
if (this.isDestroyed)
|
|
48
|
+
return;
|
|
49
|
+
this.queue.push(metric);
|
|
50
|
+
if (this.config.debug) {
|
|
51
|
+
console.log('[vestig-metrics] Queued:', metric.name, metric.value);
|
|
52
|
+
}
|
|
53
|
+
// Force flush if queue is full
|
|
54
|
+
if (this.queue.length >= this.config.maxBatchSize) {
|
|
55
|
+
this.flush();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Report a metric immediately (bypasses batch)
|
|
60
|
+
* Use for critical metrics like "poor" ratings
|
|
61
|
+
*/
|
|
62
|
+
reportImmediate(metric) {
|
|
63
|
+
if (this.isDestroyed)
|
|
64
|
+
return;
|
|
65
|
+
if (this.config.debug) {
|
|
66
|
+
console.log('[vestig-metrics] Immediate report:', metric.name, metric.value);
|
|
67
|
+
}
|
|
68
|
+
this.sendMetrics([metric]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Flush all queued metrics
|
|
72
|
+
*/
|
|
73
|
+
flush() {
|
|
74
|
+
if (this.queue.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const metrics = [...this.queue];
|
|
77
|
+
this.queue = [];
|
|
78
|
+
if (this.config.debug) {
|
|
79
|
+
console.log('[vestig-metrics] Flushing', metrics.length, 'metrics');
|
|
80
|
+
}
|
|
81
|
+
this.sendMetrics(metrics);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Destroy the reporter and clean up
|
|
85
|
+
*/
|
|
86
|
+
destroy() {
|
|
87
|
+
this.isDestroyed = true;
|
|
88
|
+
if (this.flushTimer) {
|
|
89
|
+
clearInterval(this.flushTimer);
|
|
90
|
+
this.flushTimer = null;
|
|
91
|
+
}
|
|
92
|
+
// Final flush
|
|
93
|
+
this.flush();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Start the batch timer
|
|
97
|
+
*/
|
|
98
|
+
startBatchTimer() {
|
|
99
|
+
this.flushTimer = setInterval(() => {
|
|
100
|
+
this.flush();
|
|
101
|
+
}, this.config.batchInterval);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Set up page unload handler to flush metrics
|
|
105
|
+
*/
|
|
106
|
+
setupUnloadHandler() {
|
|
107
|
+
if (typeof document === 'undefined')
|
|
108
|
+
return;
|
|
109
|
+
const handleVisibilityChange = () => {
|
|
110
|
+
if (document.visibilityState === 'hidden') {
|
|
111
|
+
this.flushWithBeacon();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
// visibilitychange is more reliable than beforeunload
|
|
115
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Flush metrics using sendBeacon (for page unload)
|
|
119
|
+
*/
|
|
120
|
+
flushWithBeacon() {
|
|
121
|
+
if (this.queue.length === 0)
|
|
122
|
+
return;
|
|
123
|
+
const metrics = [...this.queue];
|
|
124
|
+
this.queue = [];
|
|
125
|
+
const payload = this.createPayload(metrics);
|
|
126
|
+
// Use sendBeacon for reliable delivery during page unload
|
|
127
|
+
if (typeof navigator !== 'undefined' && navigator.sendBeacon) {
|
|
128
|
+
const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
|
|
129
|
+
navigator.sendBeacon(this.config.endpoint, blob);
|
|
130
|
+
if (this.config.debug) {
|
|
131
|
+
console.log('[vestig-metrics] Beacon sent:', metrics.length, 'metrics');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Fallback to fetch
|
|
136
|
+
this.sendMetrics(metrics);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Send metrics via fetch
|
|
141
|
+
*/
|
|
142
|
+
async sendMetrics(metrics) {
|
|
143
|
+
if (metrics.length === 0)
|
|
144
|
+
return;
|
|
145
|
+
const payload = this.createPayload(metrics);
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(this.config.endpoint, {
|
|
148
|
+
method: 'POST',
|
|
149
|
+
headers: { 'Content-Type': 'application/json' },
|
|
150
|
+
body: JSON.stringify(payload),
|
|
151
|
+
// Use keepalive for reliability during page transitions
|
|
152
|
+
keepalive: true,
|
|
153
|
+
});
|
|
154
|
+
if (!response.ok && this.config.debug) {
|
|
155
|
+
console.error('[vestig-metrics] Report failed:', response.status);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (this.config.debug) {
|
|
160
|
+
console.error('[vestig-metrics] Report error:', error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Create the report payload
|
|
166
|
+
*/
|
|
167
|
+
createPayload(metrics) {
|
|
168
|
+
return {
|
|
169
|
+
metrics,
|
|
170
|
+
client: {
|
|
171
|
+
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',
|
|
172
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : 'unknown',
|
|
173
|
+
timestamp: new Date().toISOString(),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/metrics/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Store
|
|
3
|
+
*
|
|
4
|
+
* Global store for capturing and managing performance metrics.
|
|
5
|
+
* Follows the same pub/sub pattern as the log store for consistency.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { MetricsStore } from './types';
|
|
10
|
+
/**
|
|
11
|
+
* Global metrics store singleton
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { metricsStore } from '@vestig/next/metrics'
|
|
16
|
+
*
|
|
17
|
+
* // Subscribe to changes
|
|
18
|
+
* const unsubscribe = metricsStore.subscribe(() => {
|
|
19
|
+
* const summary = metricsStore.getVitalsSummary()
|
|
20
|
+
* console.log('LCP p75:', summary.LCP?.p75)
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // Add a metric
|
|
24
|
+
* metricsStore.addMetric({
|
|
25
|
+
* type: 'web-vital',
|
|
26
|
+
* name: 'LCP',
|
|
27
|
+
* value: 2500,
|
|
28
|
+
* rating: 'needs-improvement',
|
|
29
|
+
* metadata: { pathname: '/dashboard' }
|
|
30
|
+
* })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare const metricsStore: MetricsStore;
|
|
34
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/metrics/store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAMX,YAAY,EAEZ,MAAM,SAAS,CAAA;AAsKhB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,YAAY,cAAuB,CAAA"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Store
|
|
3
|
+
*
|
|
4
|
+
* Global store for capturing and managing performance metrics.
|
|
5
|
+
* Follows the same pub/sub pattern as the log store for consistency.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { getRating, THRESHOLDS } 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
|
+
* Create the metrics store singleton
|
|
28
|
+
*/
|
|
29
|
+
function createMetricsStore() {
|
|
30
|
+
const state = {
|
|
31
|
+
metrics: [],
|
|
32
|
+
maxMetrics: 500,
|
|
33
|
+
latestVitals: {},
|
|
34
|
+
};
|
|
35
|
+
const listeners = new Set();
|
|
36
|
+
function notify() {
|
|
37
|
+
for (const listener of listeners) {
|
|
38
|
+
try {
|
|
39
|
+
listener();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('[vestig-metrics] Listener error:', error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
subscribe(listener) {
|
|
48
|
+
listeners.add(listener);
|
|
49
|
+
return () => listeners.delete(listener);
|
|
50
|
+
},
|
|
51
|
+
getSnapshot() {
|
|
52
|
+
return state;
|
|
53
|
+
},
|
|
54
|
+
addMetric(entry) {
|
|
55
|
+
const metric = {
|
|
56
|
+
...entry,
|
|
57
|
+
id: createMetricId(),
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
state.metrics.push(metric);
|
|
61
|
+
// Update latest vitals cache
|
|
62
|
+
if (entry.type === 'web-vital') {
|
|
63
|
+
state.latestVitals[entry.name] = metric;
|
|
64
|
+
}
|
|
65
|
+
// Trim if over limit
|
|
66
|
+
if (state.metrics.length > state.maxMetrics) {
|
|
67
|
+
state.metrics = state.metrics.slice(-state.maxMetrics);
|
|
68
|
+
}
|
|
69
|
+
notify();
|
|
70
|
+
},
|
|
71
|
+
getHistogram(name, bucketCount = 10) {
|
|
72
|
+
const values = state.metrics.filter((m) => m.name === name).map((m) => m.value);
|
|
73
|
+
if (values.length === 0)
|
|
74
|
+
return [];
|
|
75
|
+
const min = Math.min(...values);
|
|
76
|
+
const max = Math.max(...values);
|
|
77
|
+
const range = max - min || 1;
|
|
78
|
+
const bucketSize = range / bucketCount;
|
|
79
|
+
const buckets = Array.from({ length: bucketCount }, (_, i) => ({
|
|
80
|
+
min: min + i * bucketSize,
|
|
81
|
+
max: min + (i + 1) * bucketSize,
|
|
82
|
+
count: 0,
|
|
83
|
+
percentage: 0,
|
|
84
|
+
}));
|
|
85
|
+
for (const value of values) {
|
|
86
|
+
const bucketIndex = Math.min(Math.floor((value - min) / bucketSize), bucketCount - 1);
|
|
87
|
+
const bucket = buckets[bucketIndex];
|
|
88
|
+
if (bucket)
|
|
89
|
+
bucket.count++;
|
|
90
|
+
}
|
|
91
|
+
// Calculate percentages
|
|
92
|
+
for (const bucket of buckets) {
|
|
93
|
+
bucket.percentage = (bucket.count / values.length) * 100;
|
|
94
|
+
}
|
|
95
|
+
return buckets;
|
|
96
|
+
},
|
|
97
|
+
getSummary(name) {
|
|
98
|
+
const values = state.metrics.filter((m) => m.name === name).map((m) => m.value);
|
|
99
|
+
if (values.length === 0)
|
|
100
|
+
return null;
|
|
101
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
102
|
+
const sum = values.reduce((acc, v) => acc + v, 0);
|
|
103
|
+
const avg = sum / values.length;
|
|
104
|
+
const p75 = percentile(sorted, 75);
|
|
105
|
+
// Determine rating based on p75 for web vitals
|
|
106
|
+
let rating = 'needs-improvement';
|
|
107
|
+
if (name in THRESHOLDS) {
|
|
108
|
+
rating = getRating(name, p75);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
name,
|
|
112
|
+
count: values.length,
|
|
113
|
+
avg,
|
|
114
|
+
min: sorted[0] ?? 0,
|
|
115
|
+
max: sorted[sorted.length - 1] ?? 0,
|
|
116
|
+
p50: percentile(sorted, 50),
|
|
117
|
+
p75,
|
|
118
|
+
p95: percentile(sorted, 95),
|
|
119
|
+
p99: percentile(sorted, 99),
|
|
120
|
+
rating,
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
getVitalsSummary() {
|
|
124
|
+
const result = {};
|
|
125
|
+
const vitals = ['LCP', 'CLS', 'INP', 'TTFB', 'FCP'];
|
|
126
|
+
for (const name of vitals) {
|
|
127
|
+
const summary = this.getSummary(name);
|
|
128
|
+
if (summary) {
|
|
129
|
+
result[name] = summary;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
},
|
|
134
|
+
getLatest(name) {
|
|
135
|
+
if (name in state.latestVitals) {
|
|
136
|
+
return state.latestVitals[name] ?? null;
|
|
137
|
+
}
|
|
138
|
+
const metrics = state.metrics.filter((m) => m.name === name);
|
|
139
|
+
return metrics[metrics.length - 1] ?? null;
|
|
140
|
+
},
|
|
141
|
+
clear() {
|
|
142
|
+
state.metrics = [];
|
|
143
|
+
state.latestVitals = {};
|
|
144
|
+
notify();
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Global metrics store singleton
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { metricsStore } from '@vestig/next/metrics'
|
|
154
|
+
*
|
|
155
|
+
* // Subscribe to changes
|
|
156
|
+
* const unsubscribe = metricsStore.subscribe(() => {
|
|
157
|
+
* const summary = metricsStore.getVitalsSummary()
|
|
158
|
+
* console.log('LCP p75:', summary.LCP?.p75)
|
|
159
|
+
* })
|
|
160
|
+
*
|
|
161
|
+
* // Add a metric
|
|
162
|
+
* metricsStore.addMetric({
|
|
163
|
+
* type: 'web-vital',
|
|
164
|
+
* name: 'LCP',
|
|
165
|
+
* value: 2500,
|
|
166
|
+
* rating: 'needs-improvement',
|
|
167
|
+
* metadata: { pathname: '/dashboard' }
|
|
168
|
+
* })
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export const metricsStore = createMetricsStore();
|
|
172
|
+
//# 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"}
|