@vestig/next 0.6.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/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/package.json +28 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/error/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/error/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Metrics Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for capturing route-level performance metrics
|
|
5
|
+
* including render time, hydration time, and data fetching.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { MetricEntry } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Options for useRouteMetrics hook
|
|
12
|
+
*/
|
|
13
|
+
interface UseRouteMetricsOptions {
|
|
14
|
+
/** Enable route metrics collection */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
/** Report endpoint */
|
|
17
|
+
reportEndpoint?: string;
|
|
18
|
+
/** Debug mode */
|
|
19
|
+
debug?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Hook to capture route-level performance metrics
|
|
23
|
+
*
|
|
24
|
+
* Automatically tracks:
|
|
25
|
+
* - Route render time
|
|
26
|
+
* - Hydration time (client-side)
|
|
27
|
+
* - Navigation timing
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* function MyLayout({ children }) {
|
|
32
|
+
* useRouteMetrics({
|
|
33
|
+
* enabled: process.env.NODE_ENV === 'development',
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* return <div>{children}</div>
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function useRouteMetrics(options?: UseRouteMetricsOptions): void;
|
|
41
|
+
/**
|
|
42
|
+
* Hook to get route metrics from the store
|
|
43
|
+
*
|
|
44
|
+
* @returns Array of route metrics
|
|
45
|
+
*/
|
|
46
|
+
export declare function useRouteMetricsData(): MetricEntry[];
|
|
47
|
+
/**
|
|
48
|
+
* Hook to manually measure render time
|
|
49
|
+
*
|
|
50
|
+
* Useful for measuring specific component render times.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```tsx
|
|
54
|
+
* function ExpensiveComponent() {
|
|
55
|
+
* const { startMeasure, endMeasure } = useRenderTiming('ExpensiveComponent')
|
|
56
|
+
*
|
|
57
|
+
* useEffect(() => {
|
|
58
|
+
* startMeasure()
|
|
59
|
+
* return () => endMeasure()
|
|
60
|
+
* }, [])
|
|
61
|
+
*
|
|
62
|
+
* return <div>...</div>
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function useRenderTiming(componentName: string): {
|
|
67
|
+
startMeasure: () => void;
|
|
68
|
+
endMeasure: () => void;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Hook to measure data fetching time
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* function DataComponent() {
|
|
76
|
+
* const { measureFetch } = useDataFetchTiming()
|
|
77
|
+
*
|
|
78
|
+
* useEffect(() => {
|
|
79
|
+
* measureFetch('users', async () => {
|
|
80
|
+
* const res = await fetch('/api/users')
|
|
81
|
+
* return res.json()
|
|
82
|
+
* })
|
|
83
|
+
* }, [])
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function useDataFetchTiming(): {
|
|
88
|
+
measureFetch: <T>(name: string, fetchFn: () => Promise<T>) => Promise<T>;
|
|
89
|
+
};
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=use-route-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-route-metrics.d.ts","sourceRoot":"","sources":["../../../src/metrics/hooks/use-route-metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,UAAU,CAAA;AAGxD;;GAEG;AACH,UAAU,sBAAsB;IAC/B,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iBAAiB;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,IAAI,CA6F1E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,WAAW,EAAE,CAcnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM;;;EA0BpD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB;mBAEzB,CAAC,QAAQ,MAAM,WAAW,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;EAwC/D"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Metrics Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for capturing route-level performance metrics
|
|
5
|
+
* including render time, hydration time, and data fetching.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
'use client';
|
|
10
|
+
import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react';
|
|
11
|
+
import { usePathname } from 'next/navigation';
|
|
12
|
+
import { metricsStore } from '../store';
|
|
13
|
+
import { MetricsReporter } from '../reporter';
|
|
14
|
+
/**
|
|
15
|
+
* Hook to capture route-level performance metrics
|
|
16
|
+
*
|
|
17
|
+
* Automatically tracks:
|
|
18
|
+
* - Route render time
|
|
19
|
+
* - Hydration time (client-side)
|
|
20
|
+
* - Navigation timing
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* function MyLayout({ children }) {
|
|
25
|
+
* useRouteMetrics({
|
|
26
|
+
* enabled: process.env.NODE_ENV === 'development',
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* return <div>{children}</div>
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function useRouteMetrics(options = {}) {
|
|
34
|
+
const { enabled = true, reportEndpoint = '/api/vestig/metrics', debug = false } = options;
|
|
35
|
+
const pathname = usePathname();
|
|
36
|
+
const renderStartRef = useRef(performance.now());
|
|
37
|
+
const lastPathnameRef = useRef(null);
|
|
38
|
+
const hydrationMeasuredRef = useRef(false);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!enabled)
|
|
41
|
+
return;
|
|
42
|
+
const reporter = new MetricsReporter({
|
|
43
|
+
endpoint: reportEndpoint,
|
|
44
|
+
debug,
|
|
45
|
+
batchInterval: 10000, // Longer interval for route metrics
|
|
46
|
+
});
|
|
47
|
+
// Measure hydration time on initial mount
|
|
48
|
+
if (!hydrationMeasuredRef.current && typeof window !== 'undefined') {
|
|
49
|
+
hydrationMeasuredRef.current = true;
|
|
50
|
+
// Use requestIdleCallback or setTimeout to measure after hydration
|
|
51
|
+
const measureHydration = () => {
|
|
52
|
+
const hydrationTime = performance.now() - renderStartRef.current;
|
|
53
|
+
if (debug) {
|
|
54
|
+
console.log(`[vestig-metrics] Hydration time for ${pathname}:`, hydrationTime.toFixed(2), 'ms');
|
|
55
|
+
}
|
|
56
|
+
const entry = {
|
|
57
|
+
type: 'route',
|
|
58
|
+
name: 'hydration',
|
|
59
|
+
value: hydrationTime,
|
|
60
|
+
metadata: {
|
|
61
|
+
pathname,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
metricsStore.addMetric(entry);
|
|
65
|
+
};
|
|
66
|
+
if ('requestIdleCallback' in window) {
|
|
67
|
+
;
|
|
68
|
+
window.requestIdleCallback(measureHydration);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
setTimeout(measureHydration, 0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return () => {
|
|
75
|
+
reporter.destroy();
|
|
76
|
+
};
|
|
77
|
+
}, [enabled, reportEndpoint, debug, pathname]);
|
|
78
|
+
// Track route changes
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!enabled)
|
|
81
|
+
return;
|
|
82
|
+
// Skip initial render
|
|
83
|
+
if (lastPathnameRef.current === null) {
|
|
84
|
+
lastPathnameRef.current = pathname;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Route changed - measure navigation time
|
|
88
|
+
if (pathname !== lastPathnameRef.current) {
|
|
89
|
+
const navigationTime = performance.now() - renderStartRef.current;
|
|
90
|
+
if (debug) {
|
|
91
|
+
console.log(`[vestig-metrics] Navigation to ${pathname}:`, navigationTime.toFixed(2), 'ms');
|
|
92
|
+
}
|
|
93
|
+
const entry = {
|
|
94
|
+
type: 'route',
|
|
95
|
+
name: 'navigation',
|
|
96
|
+
value: navigationTime,
|
|
97
|
+
metadata: {
|
|
98
|
+
pathname,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
metricsStore.addMetric(entry);
|
|
102
|
+
// Reset for next navigation
|
|
103
|
+
lastPathnameRef.current = pathname;
|
|
104
|
+
renderStartRef.current = performance.now();
|
|
105
|
+
}
|
|
106
|
+
}, [enabled, pathname, debug]);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Hook to get route metrics from the store
|
|
110
|
+
*
|
|
111
|
+
* @returns Array of route metrics
|
|
112
|
+
*/
|
|
113
|
+
export function useRouteMetricsData() {
|
|
114
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
115
|
+
return metricsStore.subscribe(onStoreChange);
|
|
116
|
+
}, []);
|
|
117
|
+
const getSnapshot = useCallback(() => {
|
|
118
|
+
return metricsStore.getSnapshot().metrics.filter((m) => m.type === 'route');
|
|
119
|
+
}, []);
|
|
120
|
+
const getServerSnapshot = useCallback(() => {
|
|
121
|
+
return [];
|
|
122
|
+
}, []);
|
|
123
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Hook to manually measure render time
|
|
127
|
+
*
|
|
128
|
+
* Useful for measuring specific component render times.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```tsx
|
|
132
|
+
* function ExpensiveComponent() {
|
|
133
|
+
* const { startMeasure, endMeasure } = useRenderTiming('ExpensiveComponent')
|
|
134
|
+
*
|
|
135
|
+
* useEffect(() => {
|
|
136
|
+
* startMeasure()
|
|
137
|
+
* return () => endMeasure()
|
|
138
|
+
* }, [])
|
|
139
|
+
*
|
|
140
|
+
* return <div>...</div>
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function useRenderTiming(componentName) {
|
|
145
|
+
const startTimeRef = useRef(null);
|
|
146
|
+
const startMeasure = useCallback(() => {
|
|
147
|
+
startTimeRef.current = performance.now();
|
|
148
|
+
}, []);
|
|
149
|
+
const endMeasure = useCallback(() => {
|
|
150
|
+
if (startTimeRef.current === null)
|
|
151
|
+
return;
|
|
152
|
+
const renderTime = performance.now() - startTimeRef.current;
|
|
153
|
+
startTimeRef.current = null;
|
|
154
|
+
const entry = {
|
|
155
|
+
type: 'custom',
|
|
156
|
+
name: `render:${componentName}`,
|
|
157
|
+
value: renderTime,
|
|
158
|
+
metadata: {
|
|
159
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
metricsStore.addMetric(entry);
|
|
163
|
+
}, [componentName]);
|
|
164
|
+
return { startMeasure, endMeasure };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Hook to measure data fetching time
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```tsx
|
|
171
|
+
* function DataComponent() {
|
|
172
|
+
* const { measureFetch } = useDataFetchTiming()
|
|
173
|
+
*
|
|
174
|
+
* useEffect(() => {
|
|
175
|
+
* measureFetch('users', async () => {
|
|
176
|
+
* const res = await fetch('/api/users')
|
|
177
|
+
* return res.json()
|
|
178
|
+
* })
|
|
179
|
+
* }, [])
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export function useDataFetchTiming() {
|
|
184
|
+
const measureFetch = useCallback(async (name, fetchFn) => {
|
|
185
|
+
const start = performance.now();
|
|
186
|
+
try {
|
|
187
|
+
const result = await fetchFn();
|
|
188
|
+
const duration = performance.now() - start;
|
|
189
|
+
const entry = {
|
|
190
|
+
type: 'custom',
|
|
191
|
+
name: `fetch:${name}`,
|
|
192
|
+
value: duration,
|
|
193
|
+
metadata: {
|
|
194
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
metricsStore.addMetric(entry);
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
const duration = performance.now() - start;
|
|
202
|
+
const entry = {
|
|
203
|
+
type: 'custom',
|
|
204
|
+
name: `fetch:${name}:error`,
|
|
205
|
+
value: duration,
|
|
206
|
+
metadata: {
|
|
207
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
metricsStore.addMetric(entry);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}, []);
|
|
214
|
+
return { measureFetch };
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=use-route-metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-route-metrics.js","sourceRoot":"","sources":["../../../src/metrics/hooks/use-route-metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Vitals Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for capturing Core Web Vitals metrics.
|
|
5
|
+
* Integrates with the web-vitals library and the metrics store.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import type { MetricEntry, WebVitalName } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Options for useWebVitals hook
|
|
12
|
+
*/
|
|
13
|
+
interface UseWebVitalsOptions {
|
|
14
|
+
/** Enable Web Vitals collection */
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
/** Sampling rate (0.0 - 1.0) */
|
|
17
|
+
sampleRate?: number;
|
|
18
|
+
/** Report endpoint */
|
|
19
|
+
reportEndpoint?: string;
|
|
20
|
+
/** Report immediately when rating is poor */
|
|
21
|
+
reportPoorImmediately?: boolean;
|
|
22
|
+
/** Debug mode */
|
|
23
|
+
debug?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Hook to capture Core Web Vitals
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* function MyComponent() {
|
|
31
|
+
* useWebVitals({
|
|
32
|
+
* enabled: process.env.NODE_ENV === 'development',
|
|
33
|
+
* sampleRate: 0.1,
|
|
34
|
+
* reportPoorImmediately: true,
|
|
35
|
+
* })
|
|
36
|
+
*
|
|
37
|
+
* return <div>My App</div>
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function useWebVitals(options?: UseWebVitalsOptions): void;
|
|
42
|
+
/**
|
|
43
|
+
* Hook to get the current Web Vitals from the store
|
|
44
|
+
*
|
|
45
|
+
* @returns Latest Web Vitals values
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* function MetricsDisplay() {
|
|
50
|
+
* const vitals = useWebVitalsData()
|
|
51
|
+
*
|
|
52
|
+
* return (
|
|
53
|
+
* <div>
|
|
54
|
+
* <p>LCP: {vitals.LCP?.value ?? 'N/A'}</p>
|
|
55
|
+
* <p>CLS: {vitals.CLS?.value ?? 'N/A'}</p>
|
|
56
|
+
* <p>INP: {vitals.INP?.value ?? 'N/A'}</p>
|
|
57
|
+
* </div>
|
|
58
|
+
* )
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function useWebVitalsData(): Partial<Record<WebVitalName, MetricEntry>>;
|
|
63
|
+
/**
|
|
64
|
+
* Hook to get Web Vitals summary statistics
|
|
65
|
+
*
|
|
66
|
+
* @returns Summary statistics for all Web Vitals
|
|
67
|
+
*/
|
|
68
|
+
export declare function useWebVitalsSummary(): Partial<Record<WebVitalName, import('../types').MetricSummary>>;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=use-web-vitals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-web-vitals.d.ts","sourceRoot":"","sources":["../../../src/metrics/hooks/use-web-vitals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAkB,YAAY,EAAE,MAAM,UAAU,CAAA;AAGzE;;GAEG;AACH,UAAU,mBAAmB;IAC5B,mCAAmC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sBAAsB;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,iBAAiB;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf;AAmBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,IAAI,CA8DpE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAc7E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAC7C,MAAM,CAAC,YAAY,EAAE,OAAO,UAAU,EAAE,aAAa,CAAC,CACtD,CAgBA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Vitals Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for capturing Core Web Vitals metrics.
|
|
5
|
+
* Integrates with the web-vitals library and the metrics store.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
'use client';
|
|
10
|
+
import { useCallback, useEffect, useSyncExternalStore } from 'react';
|
|
11
|
+
import { onCLS, onFCP, onINP, onLCP, onTTFB } from 'web-vitals';
|
|
12
|
+
import { metricsStore } from '../store';
|
|
13
|
+
import { MetricsReporter } from '../reporter';
|
|
14
|
+
/**
|
|
15
|
+
* Convert web-vitals Metric to our MetricEntry format
|
|
16
|
+
*/
|
|
17
|
+
function toMetricEntry(metric) {
|
|
18
|
+
return {
|
|
19
|
+
type: 'web-vital',
|
|
20
|
+
name: metric.name,
|
|
21
|
+
value: metric.value,
|
|
22
|
+
rating: metric.rating,
|
|
23
|
+
metadata: {
|
|
24
|
+
navigationType: metric.navigationType,
|
|
25
|
+
delta: metric.delta,
|
|
26
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Hook to capture Core Web Vitals
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* function MyComponent() {
|
|
36
|
+
* useWebVitals({
|
|
37
|
+
* enabled: process.env.NODE_ENV === 'development',
|
|
38
|
+
* sampleRate: 0.1,
|
|
39
|
+
* reportPoorImmediately: true,
|
|
40
|
+
* })
|
|
41
|
+
*
|
|
42
|
+
* return <div>My App</div>
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function useWebVitals(options = {}) {
|
|
47
|
+
const { enabled = true, sampleRate = 1.0, reportEndpoint = '/api/vestig/metrics', reportPoorImmediately = true, debug = false, } = options;
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
// Skip if disabled or failed sample
|
|
50
|
+
if (!enabled)
|
|
51
|
+
return;
|
|
52
|
+
if (Math.random() > sampleRate)
|
|
53
|
+
return;
|
|
54
|
+
// Create reporter for this session
|
|
55
|
+
const reporter = new MetricsReporter({
|
|
56
|
+
endpoint: reportEndpoint,
|
|
57
|
+
debug,
|
|
58
|
+
});
|
|
59
|
+
// Handler for all metrics
|
|
60
|
+
const handleMetric = (metric) => {
|
|
61
|
+
const entry = toMetricEntry(metric);
|
|
62
|
+
// Add to store
|
|
63
|
+
metricsStore.addMetric(entry);
|
|
64
|
+
if (debug) {
|
|
65
|
+
console.log(`[vestig-metrics] ${metric.name}:`, {
|
|
66
|
+
value: metric.value,
|
|
67
|
+
rating: metric.rating,
|
|
68
|
+
delta: metric.delta,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Create full entry with ID for reporter
|
|
72
|
+
const fullEntry = {
|
|
73
|
+
...entry,
|
|
74
|
+
id: `m_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
};
|
|
77
|
+
// Report immediately if poor, otherwise batch
|
|
78
|
+
if (reportPoorImmediately && metric.rating === 'poor') {
|
|
79
|
+
reporter.reportImmediate(fullEntry);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
reporter.report(fullEntry);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Subscribe to all Core Web Vitals
|
|
86
|
+
// Note: onFID is deprecated, use onINP instead
|
|
87
|
+
onLCP(handleMetric);
|
|
88
|
+
onCLS(handleMetric);
|
|
89
|
+
onINP(handleMetric);
|
|
90
|
+
onTTFB(handleMetric);
|
|
91
|
+
onFCP(handleMetric);
|
|
92
|
+
return () => {
|
|
93
|
+
reporter.destroy();
|
|
94
|
+
};
|
|
95
|
+
}, [enabled, sampleRate, reportEndpoint, reportPoorImmediately, debug]);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Hook to get the current Web Vitals from the store
|
|
99
|
+
*
|
|
100
|
+
* @returns Latest Web Vitals values
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```tsx
|
|
104
|
+
* function MetricsDisplay() {
|
|
105
|
+
* const vitals = useWebVitalsData()
|
|
106
|
+
*
|
|
107
|
+
* return (
|
|
108
|
+
* <div>
|
|
109
|
+
* <p>LCP: {vitals.LCP?.value ?? 'N/A'}</p>
|
|
110
|
+
* <p>CLS: {vitals.CLS?.value ?? 'N/A'}</p>
|
|
111
|
+
* <p>INP: {vitals.INP?.value ?? 'N/A'}</p>
|
|
112
|
+
* </div>
|
|
113
|
+
* )
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function useWebVitalsData() {
|
|
118
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
119
|
+
return metricsStore.subscribe(onStoreChange);
|
|
120
|
+
}, []);
|
|
121
|
+
const getSnapshot = useCallback(() => {
|
|
122
|
+
return metricsStore.getSnapshot().latestVitals;
|
|
123
|
+
}, []);
|
|
124
|
+
const getServerSnapshot = useCallback(() => {
|
|
125
|
+
return {};
|
|
126
|
+
}, []);
|
|
127
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Hook to get Web Vitals summary statistics
|
|
131
|
+
*
|
|
132
|
+
* @returns Summary statistics for all Web Vitals
|
|
133
|
+
*/
|
|
134
|
+
export function useWebVitalsSummary() {
|
|
135
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
136
|
+
return metricsStore.subscribe(onStoreChange);
|
|
137
|
+
}, []);
|
|
138
|
+
const getSnapshot = useCallback(() => {
|
|
139
|
+
return metricsStore.getVitalsSummary();
|
|
140
|
+
}, []);
|
|
141
|
+
const getServerSnapshot = useCallback(() => {
|
|
142
|
+
return {};
|
|
143
|
+
}, []);
|
|
144
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=use-web-vitals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-web-vitals.js","sourceRoot":"","sources":["../../../src/metrics/hooks/use-web-vitals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,CAAA;AAEZ,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAEpE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAkB7C;;GAEG;AACH,SAAS,aAAa,CAAC,MAAc;IACpC,OAAO;QACN,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,MAAM,CAAC,IAAoB;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE;YACT,cAAc,EAAE,MAAM,CAAC,cAAgC;YACvD,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;SAC9E;KACD,CAAA;AACF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC7D,MAAM,EACL,OAAO,GAAG,IAAI,EACd,UAAU,GAAG,GAAG,EAChB,cAAc,GAAG,qBAAqB,EACtC,qBAAqB,GAAG,IAAI,EAC5B,KAAK,GAAG,KAAK,GACb,GAAG,OAAO,CAAA;IAEX,SAAS,CAAC,GAAG,EAAE;QACd,oCAAoC;QACpC,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU;YAAE,OAAM;QAEtC,mCAAmC;QACnC,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;YACpC,QAAQ,EAAE,cAAc;YACxB,KAAK;SACL,CAAC,CAAA;QAEF,0BAA0B;QAC1B,MAAM,YAAY,GAAG,CAAC,MAAc,EAAQ,EAAE;YAC7C,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;YAEnC,eAAe;YACf,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YAE7B,IAAI,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,GAAG,CAAC,oBAAoB,MAAM,CAAC,IAAI,GAAG,EAAE;oBAC/C,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,KAAK,EAAE,MAAM,CAAC,KAAK;iBACnB,CAAC,CAAA;YACH,CAAC;YAED,yCAAyC;YACzC,MAAM,SAAS,GAAgB;gBAC9B,GAAG,KAAK;gBACR,EAAE,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;gBAC5E,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACnC,CAAA;YAED,8CAA8C;YAC9C,IAAI,qBAAqB,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACvD,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAC,CAAA;YACpC,CAAC;iBAAM,CAAC;gBACP,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;YAC3B,CAAC;QACF,CAAC,CAAA;QAED,mCAAmC;QACnC,+CAA+C;QAC/C,KAAK,CAAC,YAAY,CAAC,CAAA;QACnB,KAAK,CAAC,YAAY,CAAC,CAAA;QACnB,KAAK,CAAC,YAAY,CAAC,CAAA;QACnB,MAAM,CAAC,YAAY,CAAC,CAAA;QACpB,KAAK,CAAC,YAAY,CAAC,CAAA;QAEnB,OAAO,GAAG,EAAE;YACX,QAAQ,CAAC,OAAO,EAAE,CAAA;QACnB,CAAC,CAAA;IACF,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,qBAAqB,EAAE,KAAK,CAAC,CAAC,CAAA;AACxE,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB;IAC/B,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,aAAyB,EAAE,EAAE;QAC3D,OAAO,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;IAC7C,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,OAAO,YAAY,CAAC,WAAW,EAAE,CAAC,YAAY,CAAA;IAC/C,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC1C,OAAO,EAAgD,CAAA;IACxD,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IAGlC,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,aAAyB,EAAE,EAAE;QAC3D,OAAO,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;IAC7C,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,OAAO,YAAY,CAAC,gBAAgB,EAAE,CAAA;IACvC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAEpC,EAAE;QACH,OAAO,EAAE,CAAA;IACV,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAA;AACvE,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
export { VestigMetrics, type VestigMetricsProps } from './web-vitals';
|
|
45
|
+
export { metricsStore } from './store';
|
|
46
|
+
export { useWebVitals, useWebVitalsData, useWebVitalsSummary, } from './hooks/use-web-vitals';
|
|
47
|
+
export { useRouteMetrics, useRouteMetricsData, useRenderTiming, useDataFetchTiming, } from './hooks/use-route-metrics';
|
|
48
|
+
export { THRESHOLDS, RATING_COLORS, getRating, getRatingColors, getMetricDescription, getMetricUnit, formatMetricValue, } from './thresholds';
|
|
49
|
+
export { MetricsReporter } from './reporter';
|
|
50
|
+
export type { WebVitalName, MetricRating, NavigationType, WebVitalMetric, RouteMetric, MetricEntry, MetricSummary, HistogramBucket, MetricsState, MetricsStore, VestigMetricsConfig, MetricsReportPayload, } from './types';
|
|
51
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAKH,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAGtC,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;AAGlC,OAAO,EACN,UAAU,EACV,aAAa,EACb,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAG5C,YAAY,EACX,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACX,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,GACpB,MAAM,SAAS,CAAA"}
|
|
@@ -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
|