@djangocfg/layouts 2.1.167 → 2.1.169
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 +26 -5
- package/package.json +14 -14
- package/src/components/errors/ErrorsTracker/components/ErrorToast.tsx +26 -2
- package/src/components/errors/ErrorsTracker/hooks/useErrorEmitter.ts +72 -0
- package/src/components/errors/ErrorsTracker/hooks.ts +2 -0
- package/src/components/errors/ErrorsTracker/index.ts +14 -1
- package/src/components/errors/ErrorsTracker/providers/ErrorTrackingProvider.tsx +35 -2
- package/src/components/errors/ErrorsTracker/types.ts +46 -2
- package/src/components/errors/ErrorsTracker/utils/formatters.ts +24 -2
package/README.md
CHANGED
|
@@ -466,16 +466,37 @@ export default function Page() {
|
|
|
466
466
|
import {
|
|
467
467
|
ErrorTrackingProvider,
|
|
468
468
|
useErrors,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
} from '@djangocfg/layouts
|
|
469
|
+
useErrorEmitter,
|
|
470
|
+
emitRuntimeError,
|
|
471
|
+
} from '@djangocfg/layouts';
|
|
472
472
|
|
|
473
|
+
// Wrap your app
|
|
473
474
|
<ErrorTrackingProvider>
|
|
474
475
|
<YourApp />
|
|
475
476
|
</ErrorTrackingProvider>
|
|
476
477
|
|
|
477
|
-
// In components
|
|
478
|
-
|
|
478
|
+
// In React components - use hook
|
|
479
|
+
function MyComponent() {
|
|
480
|
+
const { emitError } = useErrorEmitter('MyComponent');
|
|
481
|
+
|
|
482
|
+
try {
|
|
483
|
+
doSomething();
|
|
484
|
+
} catch (error) {
|
|
485
|
+
emitError('Something failed', error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Outside React (utilities, libraries) - use standalone function
|
|
490
|
+
import { emitRuntimeError } from '@djangocfg/layouts';
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
doSomething();
|
|
494
|
+
} catch (error) {
|
|
495
|
+
emitRuntimeError('MyUtility', 'Something failed', error);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Access errors
|
|
499
|
+
const { errors, clearErrors } = useErrors();
|
|
479
500
|
```
|
|
480
501
|
|
|
481
502
|
### Update Notifier
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.169",
|
|
4
4
|
"description": "Simple, straightforward layout components for Next.js - import and use with props",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"layouts",
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"check": "tsc --noEmit"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@djangocfg/api": "^2.1.
|
|
78
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
79
|
-
"@djangocfg/i18n": "^2.1.
|
|
80
|
-
"@djangocfg/ui-core": "^2.1.
|
|
81
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
82
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
77
|
+
"@djangocfg/api": "^2.1.169",
|
|
78
|
+
"@djangocfg/centrifugo": "^2.1.169",
|
|
79
|
+
"@djangocfg/i18n": "^2.1.169",
|
|
80
|
+
"@djangocfg/ui-core": "^2.1.169",
|
|
81
|
+
"@djangocfg/ui-nextjs": "^2.1.169",
|
|
82
|
+
"@djangocfg/ui-tools": "^2.1.169",
|
|
83
83
|
"@hookform/resolvers": "^5.2.2",
|
|
84
84
|
"consola": "^3.4.2",
|
|
85
85
|
"lucide-react": "^0.545.0",
|
|
@@ -102,13 +102,13 @@
|
|
|
102
102
|
"uuid": "^11.1.0"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
|
-
"@djangocfg/api": "^2.1.
|
|
106
|
-
"@djangocfg/i18n": "^2.1.
|
|
107
|
-
"@djangocfg/centrifugo": "^2.1.
|
|
108
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
109
|
-
"@djangocfg/ui-core": "^2.1.
|
|
110
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
111
|
-
"@djangocfg/ui-tools": "^2.1.
|
|
105
|
+
"@djangocfg/api": "^2.1.169",
|
|
106
|
+
"@djangocfg/i18n": "^2.1.169",
|
|
107
|
+
"@djangocfg/centrifugo": "^2.1.169",
|
|
108
|
+
"@djangocfg/typescript-config": "^2.1.169",
|
|
109
|
+
"@djangocfg/ui-core": "^2.1.169",
|
|
110
|
+
"@djangocfg/ui-nextjs": "^2.1.169",
|
|
111
|
+
"@djangocfg/ui-tools": "^2.1.169",
|
|
112
112
|
"@types/node": "^24.7.2",
|
|
113
113
|
"@types/react": "^19.1.0",
|
|
114
114
|
"@types/react-dom": "^19.1.0",
|
|
@@ -16,10 +16,12 @@ import type {
|
|
|
16
16
|
CORSErrorDetail,
|
|
17
17
|
NetworkErrorDetail,
|
|
18
18
|
CentrifugoErrorDetail,
|
|
19
|
+
RuntimeErrorDetail,
|
|
19
20
|
ValidationErrorConfig,
|
|
20
21
|
CORSErrorConfig,
|
|
21
22
|
NetworkErrorConfig,
|
|
22
23
|
CentrifugoErrorConfig,
|
|
24
|
+
RuntimeErrorConfig,
|
|
23
25
|
} from '../types';
|
|
24
26
|
/**
|
|
25
27
|
* Build validation error description
|
|
@@ -151,12 +153,32 @@ function buildCentrifugoDescription(
|
|
|
151
153
|
);
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Build runtime error description
|
|
158
|
+
*/
|
|
159
|
+
function buildRuntimeDescription(
|
|
160
|
+
detail: RuntimeErrorDetail,
|
|
161
|
+
config: Required<RuntimeErrorConfig>
|
|
162
|
+
): React.ReactNode {
|
|
163
|
+
return (
|
|
164
|
+
<div className="flex flex-col gap-2 text-sm">
|
|
165
|
+
{config.showSource && (
|
|
166
|
+
<div className="font-mono text-xs opacity-90">
|
|
167
|
+
Source: {detail.source}
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
<div className="opacity-90">{detail.message}</div>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
154
176
|
/**
|
|
155
177
|
* Create toast options for any error type
|
|
156
178
|
*/
|
|
157
179
|
export function createErrorToast(
|
|
158
|
-
detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail,
|
|
159
|
-
config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig>
|
|
180
|
+
detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail,
|
|
181
|
+
config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig | RuntimeErrorConfig>
|
|
160
182
|
) {
|
|
161
183
|
let description: React.ReactNode;
|
|
162
184
|
|
|
@@ -167,6 +189,8 @@ export function createErrorToast(
|
|
|
167
189
|
description = buildCORSDescription(detail, config as Required<CORSErrorConfig>);
|
|
168
190
|
} else if (detail.type === 'centrifugo') {
|
|
169
191
|
description = buildCentrifugoDescription(detail, config as Required<CentrifugoErrorConfig>);
|
|
192
|
+
} else if (detail.type === 'runtime') {
|
|
193
|
+
description = buildRuntimeDescription(detail, config as Required<RuntimeErrorConfig>);
|
|
170
194
|
} else {
|
|
171
195
|
description = buildNetworkDescription(detail, config as Required<NetworkErrorConfig>);
|
|
172
196
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useErrorEmitter - Universal error emitter hook
|
|
3
|
+
*
|
|
4
|
+
* Emits runtime errors that are caught by ErrorTrackingProvider
|
|
5
|
+
* and displayed as toasts.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* function MyComponent() {
|
|
10
|
+
* const { emitError } = useErrorEmitter('tour');
|
|
11
|
+
*
|
|
12
|
+
* useEffect(() => {
|
|
13
|
+
* try {
|
|
14
|
+
* document.querySelectorAll(invalidSelector);
|
|
15
|
+
* } catch (error) {
|
|
16
|
+
* emitError('Invalid selector', error);
|
|
17
|
+
* }
|
|
18
|
+
* }, []);
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
'use client';
|
|
24
|
+
|
|
25
|
+
import { useCallback } from 'react';
|
|
26
|
+
|
|
27
|
+
import { ERROR_EVENTS } from '../types';
|
|
28
|
+
|
|
29
|
+
export interface EmitErrorOptions {
|
|
30
|
+
/** Additional context data */
|
|
31
|
+
context?: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ErrorEmitter {
|
|
35
|
+
/**
|
|
36
|
+
* Emit an error to be shown as a toast
|
|
37
|
+
* @param message - Human-readable error message
|
|
38
|
+
* @param error - Original Error object (optional)
|
|
39
|
+
* @param options - Additional options
|
|
40
|
+
*/
|
|
41
|
+
emitError: (message: string, error?: Error, options?: EmitErrorOptions) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hook to emit runtime errors
|
|
46
|
+
*
|
|
47
|
+
* @param source - Error source identifier (e.g., 'tour', 'form', 'api')
|
|
48
|
+
*/
|
|
49
|
+
export function useErrorEmitter(source: string): ErrorEmitter {
|
|
50
|
+
const emitError = useCallback(
|
|
51
|
+
(message: string, error?: Error, options?: EmitErrorOptions) => {
|
|
52
|
+
if (typeof window === 'undefined') return;
|
|
53
|
+
|
|
54
|
+
const event = new CustomEvent(ERROR_EVENTS.RUNTIME, {
|
|
55
|
+
detail: {
|
|
56
|
+
source,
|
|
57
|
+
message,
|
|
58
|
+
error,
|
|
59
|
+
context: options?.context,
|
|
60
|
+
timestamp: new Date(),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
window.dispatchEvent(event);
|
|
65
|
+
},
|
|
66
|
+
[source]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return { emitError };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default useErrorEmitter;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { useErrors } from './providers/ErrorTrackingProvider';
|
|
8
|
+
export { useErrorEmitter } from './hooks/useErrorEmitter';
|
|
9
|
+
export type { ErrorEmitter, EmitErrorOptions } from './hooks/useErrorEmitter';
|
|
8
10
|
|
|
9
11
|
// Type exports for convenience
|
|
10
12
|
export type { ErrorTrackingContextValue } from './types';
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Error Tracking - Unified error tracking for all error types
|
|
3
3
|
*
|
|
4
|
-
* Single provider and hook for validation, CORS, network, and
|
|
4
|
+
* Single provider and hook for validation, CORS, network, Centrifugo, and runtime errors
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Main provider and hook
|
|
8
8
|
export { ErrorTrackingProvider } from './providers/ErrorTrackingProvider';
|
|
9
9
|
export { useErrors } from './hooks';
|
|
10
10
|
|
|
11
|
+
// Error emitter hook (for emitting runtime errors from React components)
|
|
12
|
+
export { useErrorEmitter } from './hooks';
|
|
13
|
+
export type { ErrorEmitter, EmitErrorOptions } from './hooks';
|
|
14
|
+
|
|
15
|
+
// Re-export standalone emitRuntimeError from ui-core (for use outside React)
|
|
16
|
+
export { emitRuntimeError } from '@djangocfg/ui-core/utils';
|
|
17
|
+
|
|
11
18
|
// Types
|
|
12
19
|
export type {
|
|
13
20
|
ErrorDetail,
|
|
@@ -15,15 +22,20 @@ export type {
|
|
|
15
22
|
CORSErrorDetail,
|
|
16
23
|
NetworkErrorDetail,
|
|
17
24
|
CentrifugoErrorDetail,
|
|
25
|
+
RuntimeErrorDetail,
|
|
18
26
|
StoredError,
|
|
19
27
|
ErrorTrackingConfig,
|
|
20
28
|
ValidationErrorConfig,
|
|
21
29
|
CORSErrorConfig,
|
|
22
30
|
NetworkErrorConfig,
|
|
23
31
|
CentrifugoErrorConfig,
|
|
32
|
+
RuntimeErrorConfig,
|
|
24
33
|
ErrorTrackingContextValue,
|
|
25
34
|
} from './types';
|
|
26
35
|
|
|
36
|
+
// Event names (for manual event dispatching)
|
|
37
|
+
export { ERROR_EVENTS } from './types';
|
|
38
|
+
|
|
27
39
|
// Components
|
|
28
40
|
export { ErrorButtons } from './components/ErrorButtons';
|
|
29
41
|
export { createErrorToast } from './components/ErrorToast';
|
|
@@ -35,6 +47,7 @@ export {
|
|
|
35
47
|
formatCORSErrorForClipboard,
|
|
36
48
|
formatNetworkErrorForClipboard,
|
|
37
49
|
formatCentrifugoErrorForClipboard,
|
|
50
|
+
formatRuntimeErrorForClipboard,
|
|
38
51
|
formatErrorTitle,
|
|
39
52
|
extractDomain,
|
|
40
53
|
} from './utils/formatters';
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
DEFAULT_CENTRIFUGO_CONFIG,
|
|
38
38
|
DEFAULT_CORS_CONFIG,
|
|
39
39
|
DEFAULT_NETWORK_CONFIG,
|
|
40
|
+
DEFAULT_RUNTIME_CONFIG,
|
|
40
41
|
DEFAULT_VALIDATION_CONFIG,
|
|
41
42
|
ERROR_EVENTS
|
|
42
43
|
} from '../types';
|
|
@@ -48,10 +49,12 @@ import type {
|
|
|
48
49
|
CORSErrorConfig,
|
|
49
50
|
NetworkErrorConfig,
|
|
50
51
|
CentrifugoErrorConfig,
|
|
52
|
+
RuntimeErrorConfig,
|
|
51
53
|
ValidationErrorDetail,
|
|
52
54
|
CORSErrorDetail,
|
|
53
55
|
NetworkErrorDetail,
|
|
54
56
|
CentrifugoErrorDetail,
|
|
57
|
+
RuntimeErrorDetail,
|
|
55
58
|
ErrorTrackingContextValue,
|
|
56
59
|
} from '../types';
|
|
57
60
|
const ErrorTrackingContext = createContext<ErrorTrackingContextValue | undefined>(undefined);
|
|
@@ -78,6 +81,8 @@ function getErrorDedupeKey(detail: ErrorDetail): string {
|
|
|
78
81
|
return `network:${detail.url}:${detail.statusCode || 'unknown'}`;
|
|
79
82
|
case 'centrifugo':
|
|
80
83
|
return `centrifugo:${detail.method}:${detail.code || 'unknown'}`;
|
|
84
|
+
case 'runtime':
|
|
85
|
+
return `runtime:${detail.source}:${detail.message.slice(0, 50)}`;
|
|
81
86
|
default:
|
|
82
87
|
return `unknown:${Date.now()}`;
|
|
83
88
|
}
|
|
@@ -148,6 +153,7 @@ export interface ErrorTrackingProviderProps {
|
|
|
148
153
|
cors?: Partial<CORSErrorConfig>;
|
|
149
154
|
network?: Partial<NetworkErrorConfig>;
|
|
150
155
|
centrifugo?: Partial<CentrifugoErrorConfig>;
|
|
156
|
+
runtime?: Partial<RuntimeErrorConfig>;
|
|
151
157
|
onError?: (error: ErrorDetail) => boolean | void;
|
|
152
158
|
}
|
|
153
159
|
|
|
@@ -162,6 +168,7 @@ export function ErrorTrackingProvider({
|
|
|
162
168
|
cors: userCorsConfig,
|
|
163
169
|
network: userNetworkConfig,
|
|
164
170
|
centrifugo: userCentrifugoConfig,
|
|
171
|
+
runtime: userRuntimeConfig,
|
|
165
172
|
onError,
|
|
166
173
|
}: ErrorTrackingProviderProps) {
|
|
167
174
|
const [errors, setErrors] = useState<StoredError[]>([]);
|
|
@@ -187,6 +194,11 @@ export function ErrorTrackingProvider({
|
|
|
187
194
|
...userCentrifugoConfig,
|
|
188
195
|
};
|
|
189
196
|
|
|
197
|
+
const runtimeConfig: Required<RuntimeErrorConfig> = {
|
|
198
|
+
...DEFAULT_RUNTIME_CONFIG,
|
|
199
|
+
...userRuntimeConfig,
|
|
200
|
+
};
|
|
201
|
+
|
|
190
202
|
/**
|
|
191
203
|
* Clear all errors
|
|
192
204
|
*/
|
|
@@ -212,7 +224,7 @@ export function ErrorTrackingProvider({
|
|
|
212
224
|
* Handle any error event
|
|
213
225
|
*/
|
|
214
226
|
const handleError = useCallback(
|
|
215
|
-
(detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig>) => {
|
|
227
|
+
(detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig | RuntimeErrorConfig>) => {
|
|
216
228
|
// Check for duplicate errors (within deduplication window)
|
|
217
229
|
const isUnique = shouldShowError(detail);
|
|
218
230
|
|
|
@@ -314,19 +326,38 @@ export function ErrorTrackingProvider({
|
|
|
314
326
|
handlers.push({ event: ERROR_EVENTS.CENTRIFUGO, handler });
|
|
315
327
|
}
|
|
316
328
|
|
|
329
|
+
// Runtime errors (generic JS errors from anywhere in the app)
|
|
330
|
+
if (runtimeConfig.enabled) {
|
|
331
|
+
const handler = (event: Event) => {
|
|
332
|
+
if (!(event instanceof CustomEvent)) return;
|
|
333
|
+
const detail: RuntimeErrorDetail = {
|
|
334
|
+
type: 'runtime' as const,
|
|
335
|
+
source: event.detail.source || 'unknown',
|
|
336
|
+
message: event.detail.message || 'Unknown error',
|
|
337
|
+
error: event.detail.error,
|
|
338
|
+
context: event.detail.context,
|
|
339
|
+
timestamp: event.detail.timestamp || new Date(),
|
|
340
|
+
};
|
|
341
|
+
handleError(detail, runtimeConfig);
|
|
342
|
+
};
|
|
343
|
+
window.addEventListener(ERROR_EVENTS.RUNTIME, handler);
|
|
344
|
+
handlers.push({ event: ERROR_EVENTS.RUNTIME, handler });
|
|
345
|
+
}
|
|
346
|
+
|
|
317
347
|
// Cleanup
|
|
318
348
|
return () => {
|
|
319
349
|
handlers.forEach(({ event, handler }) => {
|
|
320
350
|
window.removeEventListener(event, handler);
|
|
321
351
|
});
|
|
322
352
|
};
|
|
323
|
-
}, [handleError, validationConfig, corsConfig, networkConfig, centrifugoConfig]);
|
|
353
|
+
}, [handleError, validationConfig, corsConfig, networkConfig, centrifugoConfig, runtimeConfig]);
|
|
324
354
|
|
|
325
355
|
// Filter errors by type
|
|
326
356
|
const validationErrors = errors.filter((e) => e.type === 'validation') as StoredError<ValidationErrorDetail>[];
|
|
327
357
|
const corsErrors = errors.filter((e) => e.type === 'cors') as StoredError<CORSErrorDetail>[];
|
|
328
358
|
const networkErrors = errors.filter((e) => e.type === 'network') as StoredError<NetworkErrorDetail>[];
|
|
329
359
|
const centrifugoErrors = errors.filter((e) => e.type === 'centrifugo') as StoredError<CentrifugoErrorDetail>[];
|
|
360
|
+
const runtimeErrors = errors.filter((e) => e.type === 'runtime') as StoredError<RuntimeErrorDetail>[];
|
|
330
361
|
|
|
331
362
|
const value: ErrorTrackingContextValue = {
|
|
332
363
|
errors,
|
|
@@ -334,6 +365,7 @@ export function ErrorTrackingProvider({
|
|
|
334
365
|
corsErrors,
|
|
335
366
|
networkErrors,
|
|
336
367
|
centrifugoErrors,
|
|
368
|
+
runtimeErrors,
|
|
337
369
|
clearErrors,
|
|
338
370
|
clearErrorsByType,
|
|
339
371
|
clearError,
|
|
@@ -344,6 +376,7 @@ export function ErrorTrackingProvider({
|
|
|
344
376
|
cors: corsConfig,
|
|
345
377
|
network: networkConfig,
|
|
346
378
|
centrifugo: centrifugoConfig,
|
|
379
|
+
runtime: runtimeConfig,
|
|
347
380
|
},
|
|
348
381
|
};
|
|
349
382
|
|
|
@@ -74,10 +74,26 @@ export interface CentrifugoErrorDetail extends BaseErrorDetail {
|
|
|
74
74
|
data?: any;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Runtime/generic error detail (from runtime-error event)
|
|
79
|
+
* Used for any JS errors that need to be shown to user (e.g., Tour selector errors)
|
|
80
|
+
*/
|
|
81
|
+
export interface RuntimeErrorDetail extends BaseErrorDetail {
|
|
82
|
+
type: 'runtime';
|
|
83
|
+
/** Error source/category (e.g., 'tour', 'component', etc.) */
|
|
84
|
+
source: string;
|
|
85
|
+
/** Error message */
|
|
86
|
+
message: string;
|
|
87
|
+
/** Original error object if available */
|
|
88
|
+
error?: Error;
|
|
89
|
+
/** Additional context data */
|
|
90
|
+
context?: Record<string, unknown>;
|
|
91
|
+
}
|
|
92
|
+
|
|
77
93
|
/**
|
|
78
94
|
* Union type of all error details
|
|
79
95
|
*/
|
|
80
|
-
export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail;
|
|
96
|
+
export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail;
|
|
81
97
|
|
|
82
98
|
/**
|
|
83
99
|
* Stored error with unique ID
|
|
@@ -202,6 +218,17 @@ export interface CentrifugoErrorConfig extends ErrorTypeConfig {
|
|
|
202
218
|
showCode?: boolean;
|
|
203
219
|
}
|
|
204
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Runtime error specific config
|
|
223
|
+
*/
|
|
224
|
+
export interface RuntimeErrorConfig extends ErrorTypeConfig {
|
|
225
|
+
/**
|
|
226
|
+
* Show error source in toast
|
|
227
|
+
* @default true
|
|
228
|
+
*/
|
|
229
|
+
showSource?: boolean;
|
|
230
|
+
}
|
|
231
|
+
|
|
205
232
|
/**
|
|
206
233
|
* Complete error tracking configuration
|
|
207
234
|
*/
|
|
@@ -226,6 +253,11 @@ export interface ErrorTrackingConfig {
|
|
|
226
253
|
*/
|
|
227
254
|
centrifugo?: CentrifugoErrorConfig;
|
|
228
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Runtime error tracking configuration
|
|
258
|
+
*/
|
|
259
|
+
runtime?: RuntimeErrorConfig;
|
|
260
|
+
|
|
229
261
|
/**
|
|
230
262
|
* Custom error handler (called before toast for all errors)
|
|
231
263
|
* Return false to prevent default toast notification
|
|
@@ -252,11 +284,14 @@ export interface ErrorTrackingContextValue {
|
|
|
252
284
|
/** Centrifugo errors only */
|
|
253
285
|
centrifugoErrors: StoredError<CentrifugoErrorDetail>[];
|
|
254
286
|
|
|
287
|
+
/** Runtime errors only */
|
|
288
|
+
runtimeErrors: StoredError<RuntimeErrorDetail>[];
|
|
289
|
+
|
|
255
290
|
/** Clear all errors */
|
|
256
291
|
clearErrors: () => void;
|
|
257
292
|
|
|
258
293
|
/** Clear errors by type */
|
|
259
|
-
clearErrorsByType: (type: 'validation' | 'cors' | 'network' | 'centrifugo') => void;
|
|
294
|
+
clearErrorsByType: (type: 'validation' | 'cors' | 'network' | 'centrifugo' | 'runtime') => void;
|
|
260
295
|
|
|
261
296
|
/** Clear specific error by ID */
|
|
262
297
|
clearError: (id: string) => void;
|
|
@@ -273,6 +308,7 @@ export interface ErrorTrackingContextValue {
|
|
|
273
308
|
cors: Required<CORSErrorConfig>;
|
|
274
309
|
network: Required<NetworkErrorConfig>;
|
|
275
310
|
centrifugo: Required<CentrifugoErrorConfig>;
|
|
311
|
+
runtime: Required<RuntimeErrorConfig>;
|
|
276
312
|
};
|
|
277
313
|
}
|
|
278
314
|
|
|
@@ -285,6 +321,8 @@ export const ERROR_EVENTS = {
|
|
|
285
321
|
NETWORK: 'network-error',
|
|
286
322
|
/** Unified Centrifugo event - filter by detail.type === 'error' */
|
|
287
323
|
CENTRIFUGO: 'centrifugo',
|
|
324
|
+
/** Runtime/generic errors (Tour, components, etc.) */
|
|
325
|
+
RUNTIME: 'runtime-error',
|
|
288
326
|
} as const;
|
|
289
327
|
|
|
290
328
|
/**
|
|
@@ -326,3 +364,9 @@ export const DEFAULT_CENTRIFUGO_CONFIG: Required<CentrifugoErrorConfig> = {
|
|
|
326
364
|
showMethod: true,
|
|
327
365
|
showCode: true,
|
|
328
366
|
};
|
|
367
|
+
|
|
368
|
+
export const DEFAULT_RUNTIME_CONFIG: Required<RuntimeErrorConfig> = {
|
|
369
|
+
...DEFAULT_ERROR_CONFIG,
|
|
370
|
+
duration: 8000,
|
|
371
|
+
showSource: true,
|
|
372
|
+
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ZodError } from 'zod';
|
|
8
|
-
import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail } from '../types';
|
|
8
|
+
import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail, RuntimeErrorDetail } from '../types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Format Zod error issues for display
|
|
@@ -111,10 +111,30 @@ export function extractDomain(url: string): string {
|
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Format runtime error for clipboard
|
|
116
|
+
*/
|
|
117
|
+
export function formatRuntimeErrorForClipboard(detail: RuntimeErrorDetail): string {
|
|
118
|
+
const errorData = {
|
|
119
|
+
type: 'runtime',
|
|
120
|
+
timestamp: detail.timestamp.toISOString(),
|
|
121
|
+
source: detail.source,
|
|
122
|
+
message: detail.message,
|
|
123
|
+
...(detail.error && {
|
|
124
|
+
errorName: detail.error.name,
|
|
125
|
+
errorMessage: detail.error.message,
|
|
126
|
+
stack: detail.error.stack,
|
|
127
|
+
}),
|
|
128
|
+
...(detail.context && { context: detail.context }),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return JSON.stringify(errorData, null, 2);
|
|
132
|
+
}
|
|
133
|
+
|
|
114
134
|
/**
|
|
115
135
|
* Format error title based on type
|
|
116
136
|
*/
|
|
117
|
-
export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): string {
|
|
137
|
+
export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail): string {
|
|
118
138
|
switch (detail.type) {
|
|
119
139
|
case 'validation':
|
|
120
140
|
return `❌ Validation Error in ${detail.operation}`;
|
|
@@ -128,6 +148,8 @@ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail
|
|
|
128
148
|
return detail.code !== undefined
|
|
129
149
|
? `🔌 Centrifugo Error (${detail.code})`
|
|
130
150
|
: '🔌 Centrifugo Error';
|
|
151
|
+
case 'runtime':
|
|
152
|
+
return `⚠️ ${detail.source} Error`;
|
|
131
153
|
default:
|
|
132
154
|
return '❌ Error';
|
|
133
155
|
}
|