@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,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stack Trace Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses JavaScript error stack traces into structured frames.
|
|
5
|
+
* Supports Chrome, Firefox, Safari, and Edge stack trace formats.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Regex patterns for different browser stack trace formats
|
|
11
|
+
*/
|
|
12
|
+
const CHROME_STACK_REGEX = /^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?$/;
|
|
13
|
+
const FIREFOX_STACK_REGEX = /^(?:(.+)@)?(.+?):(\d+):(\d+)$/;
|
|
14
|
+
const SAFARI_STACK_REGEX = /^(?:(.+)@)?(.+?):(\d+):(\d+)$/;
|
|
15
|
+
/**
|
|
16
|
+
* Check if a file path is from node_modules
|
|
17
|
+
*/
|
|
18
|
+
function isNodeModulePath(path) {
|
|
19
|
+
return path.includes('node_modules') || path.includes('node:');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if a file path is from app source code
|
|
23
|
+
*/
|
|
24
|
+
function isAppCodePath(path) {
|
|
25
|
+
// Not from node_modules
|
|
26
|
+
if (isNodeModulePath(path))
|
|
27
|
+
return false;
|
|
28
|
+
// Common app directories
|
|
29
|
+
const appPatterns = ['/src/', '/app/', '/pages/', '/components/', '/lib/', '/utils/', '/hooks/'];
|
|
30
|
+
return appPatterns.some((pattern) => path.includes(pattern));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse a single stack frame line
|
|
34
|
+
*/
|
|
35
|
+
function parseStackLine(line) {
|
|
36
|
+
const trimmedLine = line.trim();
|
|
37
|
+
if (!trimmedLine || trimmedLine === 'Error')
|
|
38
|
+
return null;
|
|
39
|
+
// Try Chrome format first (most common)
|
|
40
|
+
let match = CHROME_STACK_REGEX.exec(trimmedLine);
|
|
41
|
+
if (match) {
|
|
42
|
+
const [, functionName, fileName, lineNumber, columnNumber] = match;
|
|
43
|
+
return {
|
|
44
|
+
functionName: functionName?.trim(),
|
|
45
|
+
fileName: fileName,
|
|
46
|
+
lineNumber: Number.parseInt(lineNumber ?? '0', 10),
|
|
47
|
+
columnNumber: Number.parseInt(columnNumber ?? '0', 10),
|
|
48
|
+
isNodeModule: isNodeModulePath(fileName ?? ''),
|
|
49
|
+
isAppCode: isAppCodePath(fileName ?? ''),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Try Firefox/Safari format
|
|
53
|
+
match = FIREFOX_STACK_REGEX.exec(trimmedLine);
|
|
54
|
+
if (match) {
|
|
55
|
+
const [, functionName, fileName, lineNumber, columnNumber] = match;
|
|
56
|
+
return {
|
|
57
|
+
functionName: functionName?.trim(),
|
|
58
|
+
fileName: fileName,
|
|
59
|
+
lineNumber: Number.parseInt(lineNumber ?? '0', 10),
|
|
60
|
+
columnNumber: Number.parseInt(columnNumber ?? '0', 10),
|
|
61
|
+
isNodeModule: isNodeModulePath(fileName ?? ''),
|
|
62
|
+
isAppCode: isAppCodePath(fileName ?? ''),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse an error stack trace into structured frames
|
|
69
|
+
*
|
|
70
|
+
* @param stack - The error.stack string
|
|
71
|
+
* @returns Array of parsed stack frames
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* try {
|
|
76
|
+
* throw new Error('Test')
|
|
77
|
+
* } catch (e) {
|
|
78
|
+
* const frames = parseStackTrace(e.stack)
|
|
79
|
+
* // [{ functionName: 'test', fileName: '/app/src/test.ts', lineNumber: 5, ... }]
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export function parseStackTrace(stack) {
|
|
84
|
+
if (!stack)
|
|
85
|
+
return [];
|
|
86
|
+
const lines = stack.split('\n');
|
|
87
|
+
const frames = [];
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
const frame = parseStackLine(line);
|
|
90
|
+
if (frame) {
|
|
91
|
+
frames.push(frame);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return frames;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse React component stack into readable format
|
|
98
|
+
*/
|
|
99
|
+
export function parseComponentStack(componentStack) {
|
|
100
|
+
if (!componentStack)
|
|
101
|
+
return [];
|
|
102
|
+
const lines = componentStack.split('\n');
|
|
103
|
+
const components = [];
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
const trimmed = line.trim();
|
|
106
|
+
if (!trimmed)
|
|
107
|
+
continue;
|
|
108
|
+
// Extract component name from "at ComponentName (file:line:col)"
|
|
109
|
+
const match = /^at\s+(\w+)/.exec(trimmed);
|
|
110
|
+
if (match?.[1]) {
|
|
111
|
+
components.push(match[1]);
|
|
112
|
+
}
|
|
113
|
+
else if (trimmed.startsWith('in ')) {
|
|
114
|
+
// Alternative format: "in ComponentName"
|
|
115
|
+
const componentName = trimmed.slice(3).split(' ')[0];
|
|
116
|
+
if (componentName)
|
|
117
|
+
components.push(componentName);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return components;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get the most relevant stack frame (first app code frame)
|
|
124
|
+
*/
|
|
125
|
+
export function getMostRelevantFrame(frames) {
|
|
126
|
+
// First, try to find an app code frame
|
|
127
|
+
const appFrame = frames.find((f) => f.isAppCode);
|
|
128
|
+
if (appFrame)
|
|
129
|
+
return appFrame;
|
|
130
|
+
// Fall back to first non-node_modules frame
|
|
131
|
+
const nonModuleFrame = frames.find((f) => !f.isNodeModule);
|
|
132
|
+
if (nonModuleFrame)
|
|
133
|
+
return nonModuleFrame;
|
|
134
|
+
// Last resort: first frame
|
|
135
|
+
return frames[0] ?? null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Format a stack frame for display
|
|
139
|
+
*/
|
|
140
|
+
export function formatStackFrame(frame) {
|
|
141
|
+
const parts = [];
|
|
142
|
+
if (frame.functionName) {
|
|
143
|
+
parts.push(frame.functionName);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
parts.push('<anonymous>');
|
|
147
|
+
}
|
|
148
|
+
if (frame.fileName) {
|
|
149
|
+
let location = frame.fileName;
|
|
150
|
+
if (frame.lineNumber) {
|
|
151
|
+
location += `:${frame.lineNumber}`;
|
|
152
|
+
if (frame.columnNumber) {
|
|
153
|
+
location += `:${frame.columnNumber}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
parts.push(`(${location})`);
|
|
157
|
+
}
|
|
158
|
+
return parts.join(' ');
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Format entire stack trace for display
|
|
162
|
+
*/
|
|
163
|
+
export function formatStackTrace(frames) {
|
|
164
|
+
return frames.map((frame, i) => ` at ${formatStackFrame(frame)}`).join('\n');
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=stack-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack-parser.js","sourceRoot":"","sources":["../../src/error/stack-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,MAAM,kBAAkB,GAAG,+CAA+C,CAAA;AAC1E,MAAM,mBAAmB,GAAG,+BAA+B,CAAA;AAC3D,MAAM,kBAAkB,GAAG,+BAA+B,CAAA;AAE1D;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AAC/D,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAY;IAClC,wBAAwB;IACxB,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAExC,yBAAyB;IACzB,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;IAChG,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,IAAY;IACnC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,WAAW,IAAI,WAAW,KAAK,OAAO;QAAE,OAAO,IAAI,CAAA;IAExD,wCAAwC;IACxC,IAAI,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAChD,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,CAAC,GAAG,KAAK,CAAA;QAClE,OAAO;YACN,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE;YAClC,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,EAAE,CAAC;YAClD,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,IAAI,GAAG,EAAE,EAAE,CAAC;YACtD,YAAY,EAAE,gBAAgB,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC9C,SAAS,EAAE,aAAa,CAAC,QAAQ,IAAI,EAAE,CAAC;SACxC,CAAA;IACF,CAAC;IAED,4BAA4B;IAC5B,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC7C,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,CAAC,GAAG,KAAK,CAAA;QAClE,OAAO;YACN,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE;YAClC,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,EAAE,CAAC;YAClD,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,IAAI,GAAG,EAAE,EAAE,CAAC;YACtD,YAAY,EAAE,gBAAgB,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC9C,SAAS,EAAE,aAAa,CAAC,QAAQ,IAAI,EAAE,CAAC;SACxC,CAAA;IACF,CAAC;IAED,OAAO,IAAI,CAAA;AACZ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,KAAyB;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,MAAM,GAAiB,EAAE,CAAA;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAA;QAClC,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnB,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAA;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,cAAkC;IACrE,IAAI,CAAC,cAAc;QAAE,OAAO,EAAE,CAAA;IAE9B,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACxC,MAAM,UAAU,GAAa,EAAE,CAAA;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,CAAC,OAAO;YAAE,SAAQ;QAEtB,iEAAiE;QACjE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACzC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1B,CAAC;aAAM,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,yCAAyC;YACzC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;YACpD,IAAI,aAAa;gBAAE,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QAClD,CAAC;IACF,CAAC;IAED,OAAO,UAAU,CAAA;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACxD,uCAAuC;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAChD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAE7B,4CAA4C;IAC5C,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAA;IAC1D,IAAI,cAAc;QAAE,OAAO,cAAc,CAAA;IAEzC,2BAA2B;IAC3B,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IAC/B,CAAC;SAAM,CAAC;QACP,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,IAAI,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;QAC7B,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACtB,QAAQ,IAAI,IAAI,KAAK,CAAC,UAAU,EAAE,CAAA;YAClC,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;gBACxB,QAAQ,IAAI,IAAI,KAAK,CAAC,YAAY,EAAE,CAAA;YACrC,CAAC;QACF,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAA;IAC5B,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACpD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC9E,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Types for Better Error DX
|
|
3
|
+
*
|
|
4
|
+
* Types for enhanced error handling with source maps,
|
|
5
|
+
* breadcrumbs, and fingerprinting.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Breadcrumb categories for filtering
|
|
11
|
+
*/
|
|
12
|
+
export type BreadcrumbCategory = 'log' | 'navigation' | 'click' | 'input' | 'fetch' | 'error' | 'custom';
|
|
13
|
+
/**
|
|
14
|
+
* Enhanced breadcrumb with more context
|
|
15
|
+
*/
|
|
16
|
+
export interface Breadcrumb {
|
|
17
|
+
/** Unique ID */
|
|
18
|
+
id: string;
|
|
19
|
+
/** ISO timestamp */
|
|
20
|
+
timestamp: string;
|
|
21
|
+
/** Breadcrumb category */
|
|
22
|
+
category: BreadcrumbCategory;
|
|
23
|
+
/** Log level (for log breadcrumbs) */
|
|
24
|
+
level?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
|
|
25
|
+
/** Human-readable message */
|
|
26
|
+
message: string;
|
|
27
|
+
/** Logger namespace */
|
|
28
|
+
namespace?: string;
|
|
29
|
+
/** Additional data */
|
|
30
|
+
data?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parsed stack frame from error stack trace
|
|
34
|
+
*/
|
|
35
|
+
export interface StackFrame {
|
|
36
|
+
/** Function name (if available) */
|
|
37
|
+
functionName?: string;
|
|
38
|
+
/** File path */
|
|
39
|
+
fileName?: string;
|
|
40
|
+
/** Line number (1-indexed) */
|
|
41
|
+
lineNumber?: number;
|
|
42
|
+
/** Column number (1-indexed) */
|
|
43
|
+
columnNumber?: number;
|
|
44
|
+
/** Whether this is from node_modules */
|
|
45
|
+
isNodeModule: boolean;
|
|
46
|
+
/** Whether this is from the app's source code */
|
|
47
|
+
isAppCode: boolean;
|
|
48
|
+
/** Source code context (if available via source maps) */
|
|
49
|
+
sourceContext?: {
|
|
50
|
+
/** Lines before the error line */
|
|
51
|
+
pre: string[];
|
|
52
|
+
/** The error line */
|
|
53
|
+
line: string;
|
|
54
|
+
/** Lines after the error line */
|
|
55
|
+
post: string[];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Enhanced error with parsed stack and fingerprint
|
|
60
|
+
*/
|
|
61
|
+
export interface EnhancedError {
|
|
62
|
+
/** Original error */
|
|
63
|
+
error: Error;
|
|
64
|
+
/** Error fingerprint for grouping */
|
|
65
|
+
fingerprint: string;
|
|
66
|
+
/** Parsed stack frames */
|
|
67
|
+
frames: StackFrame[];
|
|
68
|
+
/** React component stack */
|
|
69
|
+
componentStack?: string;
|
|
70
|
+
/** Breadcrumbs leading up to the error */
|
|
71
|
+
breadcrumbs: Breadcrumb[];
|
|
72
|
+
/** Timestamp when error occurred */
|
|
73
|
+
timestamp: string;
|
|
74
|
+
/** Browser/environment info */
|
|
75
|
+
environment: {
|
|
76
|
+
userAgent: string;
|
|
77
|
+
url: string;
|
|
78
|
+
pathname: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Props for enhanced error boundary
|
|
83
|
+
*/
|
|
84
|
+
export interface EnhancedErrorBoundaryProps {
|
|
85
|
+
children: React.ReactNode;
|
|
86
|
+
/**
|
|
87
|
+
* Custom fallback UI
|
|
88
|
+
*/
|
|
89
|
+
fallback?: React.ReactNode | ((error: EnhancedError) => React.ReactNode);
|
|
90
|
+
/**
|
|
91
|
+
* Callback when error is caught
|
|
92
|
+
*/
|
|
93
|
+
onError?: (error: EnhancedError) => void;
|
|
94
|
+
/**
|
|
95
|
+
* Maximum breadcrumbs to keep
|
|
96
|
+
* @default 50
|
|
97
|
+
*/
|
|
98
|
+
maxBreadcrumbs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Show source code context in development
|
|
101
|
+
* @default true in development
|
|
102
|
+
*/
|
|
103
|
+
showSourceContext?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Show breadcrumbs in error UI
|
|
106
|
+
* @default true in development
|
|
107
|
+
*/
|
|
108
|
+
showBreadcrumbs?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Categories of breadcrumbs to capture
|
|
111
|
+
* @default all categories
|
|
112
|
+
*/
|
|
113
|
+
breadcrumbCategories?: BreadcrumbCategory[];
|
|
114
|
+
/**
|
|
115
|
+
* Report errors to endpoint
|
|
116
|
+
*/
|
|
117
|
+
reportEndpoint?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* State for enhanced error boundary
|
|
121
|
+
*/
|
|
122
|
+
export interface EnhancedErrorBoundaryState {
|
|
123
|
+
hasError: boolean;
|
|
124
|
+
enhancedError: EnhancedError | null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Options for error fingerprinting
|
|
128
|
+
*/
|
|
129
|
+
export interface FingerprintOptions {
|
|
130
|
+
/** Include file paths in fingerprint */
|
|
131
|
+
includeFilePaths?: boolean;
|
|
132
|
+
/** Include line numbers in fingerprint */
|
|
133
|
+
includeLineNumbers?: boolean;
|
|
134
|
+
/** Maximum stack frames to consider */
|
|
135
|
+
maxFrames?: number;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Breadcrumb store interface
|
|
139
|
+
*/
|
|
140
|
+
export interface BreadcrumbStore {
|
|
141
|
+
/** Add a breadcrumb */
|
|
142
|
+
add: (breadcrumb: Omit<Breadcrumb, 'id' | 'timestamp'>) => void;
|
|
143
|
+
/** Get all breadcrumbs */
|
|
144
|
+
getAll: () => Breadcrumb[];
|
|
145
|
+
/** Get breadcrumbs by category */
|
|
146
|
+
getByCategory: (category: BreadcrumbCategory) => Breadcrumb[];
|
|
147
|
+
/** Clear all breadcrumbs */
|
|
148
|
+
clear: () => void;
|
|
149
|
+
/** Set maximum breadcrumbs */
|
|
150
|
+
setMaxSize: (size: number) => void;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -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,93 @@
|
|
|
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
|
+
* Uses simple useState + useEffect pattern - React Compiler handles memoization.
|
|
45
|
+
*
|
|
46
|
+
* @returns Array of route metrics
|
|
47
|
+
*/
|
|
48
|
+
export declare function useRouteMetricsData(): MetricEntry[];
|
|
49
|
+
/**
|
|
50
|
+
* Hook to manually measure render time
|
|
51
|
+
*
|
|
52
|
+
* Useful for measuring specific component render times.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* function ExpensiveComponent() {
|
|
57
|
+
* const { startMeasure, endMeasure } = useRenderTiming('ExpensiveComponent')
|
|
58
|
+
*
|
|
59
|
+
* useEffect(() => {
|
|
60
|
+
* startMeasure()
|
|
61
|
+
* return () => endMeasure()
|
|
62
|
+
* }, [])
|
|
63
|
+
*
|
|
64
|
+
* return <div>...</div>
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function useRenderTiming(componentName: string): {
|
|
69
|
+
startMeasure: () => void;
|
|
70
|
+
endMeasure: () => void;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Hook to measure data fetching time
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* function DataComponent() {
|
|
78
|
+
* const { measureFetch } = useDataFetchTiming()
|
|
79
|
+
*
|
|
80
|
+
* useEffect(() => {
|
|
81
|
+
* measureFetch('users', async () => {
|
|
82
|
+
* const res = await fetch('/api/users')
|
|
83
|
+
* return res.json()
|
|
84
|
+
* })
|
|
85
|
+
* }, [])
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function useDataFetchTiming(): {
|
|
90
|
+
measureFetch: <T>(name: string, fetchFn: () => Promise<T>) => Promise<T>;
|
|
91
|
+
};
|
|
92
|
+
export {};
|
|
93
|
+
//# 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;AAQH,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,UAAU,CAAA;AAExD;;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;;;;;;GAMG;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,217 @@
|
|
|
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 { usePathname } from 'next/navigation';
|
|
11
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { MetricsReporter } from '../reporter';
|
|
13
|
+
import { metricsStore } from '../store';
|
|
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
|
+
* Uses simple useState + useEffect pattern - React Compiler handles memoization.
|
|
112
|
+
*
|
|
113
|
+
* @returns Array of route metrics
|
|
114
|
+
*/
|
|
115
|
+
export function useRouteMetricsData() {
|
|
116
|
+
const [routeMetrics, setRouteMetrics] = useState([]);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
setRouteMetrics(metricsStore.getRouteMetrics());
|
|
119
|
+
const unsubscribe = metricsStore.subscribe(() => {
|
|
120
|
+
setRouteMetrics(metricsStore.getRouteMetrics());
|
|
121
|
+
});
|
|
122
|
+
return unsubscribe;
|
|
123
|
+
}, []);
|
|
124
|
+
return routeMetrics;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Hook to manually measure render time
|
|
128
|
+
*
|
|
129
|
+
* Useful for measuring specific component render times.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* function ExpensiveComponent() {
|
|
134
|
+
* const { startMeasure, endMeasure } = useRenderTiming('ExpensiveComponent')
|
|
135
|
+
*
|
|
136
|
+
* useEffect(() => {
|
|
137
|
+
* startMeasure()
|
|
138
|
+
* return () => endMeasure()
|
|
139
|
+
* }, [])
|
|
140
|
+
*
|
|
141
|
+
* return <div>...</div>
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export function useRenderTiming(componentName) {
|
|
146
|
+
const startTimeRef = useRef(null);
|
|
147
|
+
const startMeasure = useCallback(() => {
|
|
148
|
+
startTimeRef.current = performance.now();
|
|
149
|
+
}, []);
|
|
150
|
+
const endMeasure = useCallback(() => {
|
|
151
|
+
if (startTimeRef.current === null)
|
|
152
|
+
return;
|
|
153
|
+
const renderTime = performance.now() - startTimeRef.current;
|
|
154
|
+
startTimeRef.current = null;
|
|
155
|
+
const entry = {
|
|
156
|
+
type: 'custom',
|
|
157
|
+
name: `render:${componentName}`,
|
|
158
|
+
value: renderTime,
|
|
159
|
+
metadata: {
|
|
160
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
metricsStore.addMetric(entry);
|
|
164
|
+
}, [componentName]);
|
|
165
|
+
return { startMeasure, endMeasure };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Hook to measure data fetching time
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```tsx
|
|
172
|
+
* function DataComponent() {
|
|
173
|
+
* const { measureFetch } = useDataFetchTiming()
|
|
174
|
+
*
|
|
175
|
+
* useEffect(() => {
|
|
176
|
+
* measureFetch('users', async () => {
|
|
177
|
+
* const res = await fetch('/api/users')
|
|
178
|
+
* return res.json()
|
|
179
|
+
* })
|
|
180
|
+
* }, [])
|
|
181
|
+
* }
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export function useDataFetchTiming() {
|
|
185
|
+
const measureFetch = useCallback(async (name, fetchFn) => {
|
|
186
|
+
const start = performance.now();
|
|
187
|
+
try {
|
|
188
|
+
const result = await fetchFn();
|
|
189
|
+
const duration = performance.now() - start;
|
|
190
|
+
const entry = {
|
|
191
|
+
type: 'custom',
|
|
192
|
+
name: `fetch:${name}`,
|
|
193
|
+
value: duration,
|
|
194
|
+
metadata: {
|
|
195
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
metricsStore.addMetric(entry);
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const duration = performance.now() - start;
|
|
203
|
+
const entry = {
|
|
204
|
+
type: 'custom',
|
|
205
|
+
name: `fetch:${name}:error`,
|
|
206
|
+
value: duration,
|
|
207
|
+
metadata: {
|
|
208
|
+
pathname: typeof window !== 'undefined' ? window.location.pathname : undefined,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
metricsStore.addMetric(entry);
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}, []);
|
|
215
|
+
return { measureFetch };
|
|
216
|
+
}
|
|
217
|
+
//# 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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"}
|