@djangocfg/layouts 2.1.541 → 2.1.543

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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/package.json +17 -17
  3. package/src/components/core/JsonLd.tsx +1 -1
  4. package/src/components/errors/ErrorsTracker/components/ErrorButtons.tsx +4 -7
  5. package/src/components/errors/ErrorsTracker/components/ErrorToast.tsx +3 -41
  6. package/src/components/errors/ErrorsTracker/index.ts +1 -4
  7. package/src/components/errors/ErrorsTracker/providers/ErrorTrackingProvider.tsx +40 -59
  8. package/src/components/errors/ErrorsTracker/types.ts +2 -53
  9. package/src/components/errors/ErrorsTracker/utils/curl-generator.ts +2 -2
  10. package/src/components/errors/ErrorsTracker/utils/formatters.ts +2 -29
  11. package/src/components/errors/README.md +1 -1
  12. package/src/configurator/auth/schema.ts +1 -1
  13. package/src/configurator/index.ts +1 -1
  14. package/src/configurator/private/schema.ts +1 -1
  15. package/src/layouts/AppLayout/BaseApp.tsx +25 -43
  16. package/src/layouts/AppLayout/README.md +1 -2
  17. package/src/layouts/AuthLayout/AuthLayout.tsx +6 -6
  18. package/src/layouts/AuthLayout/components/oauth/OAuthCallback.tsx +2 -1
  19. package/src/layouts/AuthLayout/context.tsx +0 -1
  20. package/src/layouts/AuthLayout/types.ts +6 -3
  21. package/src/layouts/PrivateLayout/PrivateLayout.tsx +2 -7
  22. package/src/layouts/PrivateLayout/components/PrivateSidebarAccount.tsx +1 -1
  23. package/src/layouts/PrivateLayout/components/SidebarBrand.tsx +3 -8
  24. package/src/layouts/PrivateLayout/components/SidebarFeatured.tsx +10 -6
  25. package/src/layouts/PrivateLayout/components/SidebarNavGroup.tsx +7 -7
  26. package/src/layouts/PublicLayout/components/UserMenu.tsx +0 -1
  27. package/src/layouts/PublicLayout/footers/DefaultFooter/DefaultFooter.tsx +1 -1
  28. package/src/layouts/PublicLayout/hooks/useResponsiveOverflow.ts +13 -10
  29. package/src/layouts/PublicLayout/primitives/NavActions.tsx +7 -9
  30. package/src/layouts/PublicLayout/primitives/NavDesktopItems.tsx +1 -22
  31. package/src/layouts/PublicLayout/shared/MobileDrawerShell.tsx +5 -1
  32. package/src/layouts/PublicLayout/shared/NavbarShell.tsx +7 -25
  33. package/src/layouts/types/index.ts +0 -1
  34. package/src/layouts/types/layout.types.ts +1 -4
  35. package/src/layouts/types/providers.types.ts +1 -14
  36. package/src/snippets/Breadcrumbs.tsx +3 -3
  37. package/src/testing/MockAuthFormProvider.tsx +0 -1
  38. package/src/utils/navItemsShallowEqual.ts +34 -0
  39. package/src/utils/shallowEqualBy.ts +29 -0
package/README.md CHANGED
@@ -193,7 +193,7 @@ send analytics directly from a layout.
193
193
  | `@djangocfg/layouts/layouts` | Layout components. |
194
194
  | `@djangocfg/layouts/components` | Misc + `ErrorLayout`, `RedirectPage`. |
195
195
  | `@djangocfg/layouts/pages/legal` | Legal page templates. |
196
- | `@djangocfg/layouts/configurator` | JSON Schemas for `<JsonSchemaForm>`-driven configurator UIs (PrivateLayout for now). Pair with `@djangocfg/ui-tools`. |
196
+ | `@djangocfg/layouts/configurator` | JSON Schemas for `<JsonSchemaForm>`-driven configurator UIs (PrivateLayout for now). Pair with `@djangocfg/widget-forms`. |
197
197
  | `@djangocfg/layouts/styles` | Base CSS. |
198
198
 
199
199
  Extensions: `@djangocfg/ext-newsletter`, `ext-knowbase`, `ext-leads`, `ext-payments`, `ext-support`, …
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.541",
3
+ "version": "2.1.543",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -81,6 +81,7 @@
81
81
  "files": [
82
82
  "dist",
83
83
  "src",
84
+ "!src/**/*.stories.tsx",
84
85
  "README.md",
85
86
  "LICENSE"
86
87
  ],
@@ -89,12 +90,11 @@
89
90
  "check": "tsc --noEmit"
90
91
  },
91
92
  "peerDependencies": {
92
- "@djangocfg/analytics": "^2.1.541",
93
- "@djangocfg/api": "^2.1.541",
94
- "@djangocfg/centrifugo": "^2.1.541",
95
- "@djangocfg/devtools": "^2.1.541",
96
- "@djangocfg/i18n": "^2.1.541",
97
- "@djangocfg/ui-core": "^2.1.541",
93
+ "@djangocfg/analytics": "^2.1.543",
94
+ "@djangocfg/api": "^2.1.543",
95
+ "@djangocfg/devtools": "^2.1.543",
96
+ "@djangocfg/i18n": "^2.1.543",
97
+ "@djangocfg/ui-core": "^2.1.543",
98
98
  "@hookform/resolvers": "^5.2.2",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -102,8 +102,8 @@
102
102
  "next": "^16.2.2",
103
103
  "next-intl": "^4.9.1",
104
104
  "p-retry": "^7.0.0",
105
- "react": "^19.2.4",
106
- "react-dom": "^19.2.4",
105
+ "react": "^19.0.0",
106
+ "react-dom": "^19.0.0",
107
107
  "react-hook-form": "^7.69.0",
108
108
  "sonner": "2.0.7",
109
109
  "swr": "^2.3.7",
@@ -124,19 +124,19 @@
124
124
  "uuid": "^11.1.1"
125
125
  },
126
126
  "devDependencies": {
127
- "@djangocfg/analytics": "^2.1.541",
128
- "@djangocfg/api": "^2.1.541",
129
- "@djangocfg/centrifugo": "^2.1.541",
130
- "@djangocfg/devtools": "^2.1.541",
131
- "@djangocfg/i18n": "^2.1.541",
132
- "@djangocfg/typescript-config": "^2.1.541",
133
- "@djangocfg/ui-core": "^2.1.541",
134
- "@djangocfg/ui-tools": "^2.1.541",
127
+ "@djangocfg/analytics": "^2.1.543",
128
+ "@djangocfg/api": "^2.1.543",
129
+ "@djangocfg/devtools": "^2.1.543",
130
+ "@djangocfg/i18n": "^2.1.543",
131
+ "@djangocfg/typescript-config": "^2.1.543",
132
+ "@djangocfg/ui-core": "^2.1.543",
133
+ "@storybook/react-vite": "^10.5.0",
135
134
  "@types/node": "^25.9.5",
136
135
  "@types/react": "19.2.15",
137
136
  "@types/react-dom": "19.2.3",
138
137
  "eslint": "^9.39.5",
139
138
  "next-intl": "^4.13.2",
139
+ "storybook": "^10.5.0",
140
140
  "typescript": "^5.9.3",
141
141
  "zustand": "^5.0.14"
142
142
  },
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface JsonLdProps {
4
- data: Record<string, any> | Record<string, any>[];
4
+ data: Record<string, unknown> | Record<string, unknown>[];
5
5
  }
6
6
 
7
7
  /**
@@ -14,21 +14,20 @@ import { useCopy } from '@djangocfg/ui-core/hooks';
14
14
 
15
15
  import { generateCurlFromError } from '../utils/curl-generator';
16
16
  import {
17
- formatCentrifugoErrorForClipboard,
18
17
  formatCORSErrorForClipboard,
19
18
  formatNetworkErrorForClipboard,
20
19
  formatValidationErrorForClipboard
21
20
  } from '../utils/formatters';
22
21
 
23
- import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail } from '../types';
22
+ import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail } from '../types';
24
23
  export interface ErrorButtonsProps {
25
- detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail;
24
+ detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail;
26
25
  }
27
26
 
28
27
  /**
29
28
  * Format error for clipboard based on type
30
29
  */
31
- function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): string {
30
+ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail): string {
32
31
  switch (detail.type) {
33
32
  case 'validation':
34
33
  return formatValidationErrorForClipboard(detail);
@@ -36,8 +35,6 @@ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail
36
35
  return formatCORSErrorForClipboard(detail);
37
36
  case 'network':
38
37
  return formatNetworkErrorForClipboard(detail);
39
- case 'centrifugo':
40
- return formatCentrifugoErrorForClipboard(detail);
41
38
  default:
42
39
  return JSON.stringify(detail, null, 2);
43
40
  }
@@ -46,7 +43,7 @@ function formatErrorForClipboard(detail: ValidationErrorDetail | CORSErrorDetail
46
43
  /**
47
44
  * Check if error supports cURL generation
48
45
  */
49
- function supportsCurl(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail): boolean {
46
+ function supportsCurl(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail): boolean {
50
47
  return detail.type === 'validation';
51
48
  }
52
49
 
@@ -17,12 +17,10 @@ import type {
17
17
  ValidationErrorDetail,
18
18
  CORSErrorDetail,
19
19
  NetworkErrorDetail,
20
- CentrifugoErrorDetail,
21
20
  RuntimeErrorDetail,
22
21
  ValidationErrorConfig,
23
22
  CORSErrorConfig,
24
23
  NetworkErrorConfig,
25
- CentrifugoErrorConfig,
26
24
  RuntimeErrorConfig,
27
25
  } from '../types';
28
26
  /**
@@ -130,7 +128,7 @@ function buildNetworkDescription(
130
128
  <div className="opacity-90">{detail.error}</div>
131
129
 
132
130
  {dpopHint && (
133
- <div className="rounded-md bg-amber-500/10 px-2 py-1 text-xs text-amber-600 dark:text-amber-400">
131
+ <div className="rounded-md bg-warning/10 px-2 py-1 text-xs text-warning-foreground">
134
132
  {dpopHint}
135
133
  </div>
136
134
  )}
@@ -140,40 +138,6 @@ function buildNetworkDescription(
140
138
  );
141
139
  }
142
140
 
143
- /**
144
- * Build centrifugo error description
145
- */
146
- function buildCentrifugoDescription(
147
- detail: CentrifugoErrorDetail,
148
- config: Required<CentrifugoErrorConfig>
149
- ): React.ReactNode {
150
- const parts: string[] = [];
151
-
152
- // Add method info
153
- if (config.showMethod) {
154
- parts.push(`RPC: ${detail.method}`);
155
- }
156
-
157
- // Add error code
158
- if (config.showCode && detail.code !== undefined) {
159
- parts.push(`Code: ${detail.code}`);
160
- }
161
-
162
- return (
163
- <div className="flex flex-col gap-2 text-sm">
164
- {parts.length > 0 && (
165
- <div className="font-mono text-xs opacity-90">
166
- {parts.join(' • ')}
167
- </div>
168
- )}
169
-
170
- <div className="opacity-90">{detail.error}</div>
171
-
172
- <ErrorButtons detail={detail} />
173
- </div>
174
- );
175
- }
176
-
177
141
  /**
178
142
  * Build runtime error description
179
143
  */
@@ -198,8 +162,8 @@ function buildRuntimeDescription(
198
162
  * Create toast options for any error type
199
163
  */
200
164
  export function createErrorToast(
201
- detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail,
202
- config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig | RuntimeErrorConfig>
165
+ detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | RuntimeErrorDetail,
166
+ config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | RuntimeErrorConfig>
203
167
  ) {
204
168
  let description: React.ReactNode;
205
169
 
@@ -208,8 +172,6 @@ export function createErrorToast(
208
172
  description = buildValidationDescription(detail, config as Required<ValidationErrorConfig>);
209
173
  } else if (detail.type === 'cors') {
210
174
  description = buildCORSDescription(detail, config as Required<CORSErrorConfig>);
211
- } else if (detail.type === 'centrifugo') {
212
- description = buildCentrifugoDescription(detail, config as Required<CentrifugoErrorConfig>);
213
175
  } else if (detail.type === 'runtime') {
214
176
  description = buildRuntimeDescription(detail, config as Required<RuntimeErrorConfig>);
215
177
  } else {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Error Tracking - Unified error tracking for all error types
3
3
  *
4
- * Single provider and hook for validation, CORS, network, Centrifugo, and runtime errors
4
+ * Single provider and hook for validation, CORS, network, and runtime errors
5
5
  */
6
6
 
7
7
  // Main provider and hook
@@ -21,14 +21,12 @@ export type {
21
21
  ValidationErrorDetail,
22
22
  CORSErrorDetail,
23
23
  NetworkErrorDetail,
24
- CentrifugoErrorDetail,
25
24
  RuntimeErrorDetail,
26
25
  StoredError,
27
26
  ErrorTrackingConfig,
28
27
  ValidationErrorConfig,
29
28
  CORSErrorConfig,
30
29
  NetworkErrorConfig,
31
- CentrifugoErrorConfig,
32
30
  RuntimeErrorConfig,
33
31
  ErrorTrackingContextValue,
34
32
  TrackedErrorType,
@@ -47,7 +45,6 @@ export {
47
45
  formatValidationErrorForClipboard,
48
46
  formatCORSErrorForClipboard,
49
47
  formatNetworkErrorForClipboard,
50
- formatCentrifugoErrorForClipboard,
51
48
  formatRuntimeErrorForClipboard,
52
49
  formatErrorTitle,
53
50
  extractDomain,
@@ -27,7 +27,7 @@
27
27
  'use client';
28
28
 
29
29
  import React, {
30
- createContext, ReactNode, useCallback, useContext, useEffect, useState
30
+ createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState
31
31
  } from 'react';
32
32
 
33
33
  import { toast } from '@djangocfg/ui-core/hooks';
@@ -36,7 +36,6 @@ import { useDebugMode, isProduction } from '@djangocfg/devtools';
36
36
  import { createErrorToast } from '../components/ErrorToast';
37
37
  import { generateShortErrorId, getProductionToast } from '../utils/formatters';
38
38
  import {
39
- DEFAULT_CENTRIFUGO_CONFIG,
40
39
  DEFAULT_CORS_CONFIG,
41
40
  DEFAULT_NETWORK_CONFIG,
42
41
  DEFAULT_RUNTIME_CONFIG,
@@ -50,12 +49,10 @@ import type {
50
49
  ValidationErrorConfig,
51
50
  CORSErrorConfig,
52
51
  NetworkErrorConfig,
53
- CentrifugoErrorConfig,
54
52
  RuntimeErrorConfig,
55
53
  ValidationErrorDetail,
56
54
  CORSErrorDetail,
57
55
  NetworkErrorDetail,
58
- CentrifugoErrorDetail,
59
56
  RuntimeErrorDetail,
60
57
  ErrorTrackingContextValue,
61
58
  TrackedErrorType,
@@ -82,8 +79,6 @@ function getErrorDedupeKey(detail: ErrorDetail): string {
82
79
  return `cors:${detail.url}`;
83
80
  case 'network':
84
81
  return `network:${detail.url}:${detail.statusCode || 'unknown'}`;
85
- case 'centrifugo':
86
- return `centrifugo:${detail.method}:${detail.code || 'unknown'}`;
87
82
  case 'runtime':
88
83
  return `runtime:${detail.source}:${detail.message.slice(0, 50)}`;
89
84
  default:
@@ -169,7 +164,6 @@ export interface ErrorTrackingProviderProps {
169
164
  validation?: Partial<ValidationErrorConfig>;
170
165
  cors?: Partial<CORSErrorConfig>;
171
166
  network?: Partial<NetworkErrorConfig>;
172
- centrifugo?: Partial<CentrifugoErrorConfig>;
173
167
  runtime?: Partial<RuntimeErrorConfig>;
174
168
  onError?: (error: ErrorDetail) => boolean | void;
175
169
  /** Called with the raw ErrorDetail for every tracked error. Wire to devtools.capture() (via errorDetailToDevtoolsEvent) to forward errors to the backend. */
@@ -186,7 +180,6 @@ export function ErrorTrackingProvider({
186
180
  validation: userValidationConfig,
187
181
  cors: userCorsConfig,
188
182
  network: userNetworkConfig,
189
- centrifugo: userCentrifugoConfig,
190
183
  runtime: userRuntimeConfig,
191
184
  onError,
192
185
  onMonitorCapture,
@@ -194,31 +187,29 @@ export function ErrorTrackingProvider({
194
187
  const [errors, setErrors] = useState<StoredError[]>([]);
195
188
  const isDebugMode = useDebugMode();
196
189
 
197
- // Merge user configs with defaults
198
- const validationConfig: Required<ValidationErrorConfig> = {
199
- ...DEFAULT_VALIDATION_CONFIG,
200
- ...userValidationConfig,
201
- };
202
-
203
- const corsConfig: Required<CORSErrorConfig> = {
204
- ...DEFAULT_CORS_CONFIG,
205
- ...userCorsConfig,
206
- };
190
+ // Merged configs must be referentially stable: they feed the listener effect
191
+ // below, and a fresh object each render would detach and re-attach every
192
+ // window listener on every render — including the re-render each tracked
193
+ // error causes, dropping any error dispatched inside that gap.
194
+ const validationConfig: Required<ValidationErrorConfig> = useMemo(
195
+ () => ({ ...DEFAULT_VALIDATION_CONFIG, ...userValidationConfig }),
196
+ [userValidationConfig]
197
+ );
207
198
 
208
- const networkConfig: Required<NetworkErrorConfig> = {
209
- ...DEFAULT_NETWORK_CONFIG,
210
- ...userNetworkConfig,
211
- };
199
+ const corsConfig: Required<CORSErrorConfig> = useMemo(
200
+ () => ({ ...DEFAULT_CORS_CONFIG, ...userCorsConfig }),
201
+ [userCorsConfig]
202
+ );
212
203
 
213
- const centrifugoConfig: Required<CentrifugoErrorConfig> = {
214
- ...DEFAULT_CENTRIFUGO_CONFIG,
215
- ...userCentrifugoConfig,
216
- };
204
+ const networkConfig: Required<NetworkErrorConfig> = useMemo(
205
+ () => ({ ...DEFAULT_NETWORK_CONFIG, ...userNetworkConfig }),
206
+ [userNetworkConfig]
207
+ );
217
208
 
218
- const runtimeConfig: Required<RuntimeErrorConfig> = {
219
- ...DEFAULT_RUNTIME_CONFIG,
220
- ...userRuntimeConfig,
221
- };
209
+ const runtimeConfig: Required<RuntimeErrorConfig> = useMemo(
210
+ () => ({ ...DEFAULT_RUNTIME_CONFIG, ...userRuntimeConfig }),
211
+ [userRuntimeConfig]
212
+ );
222
213
 
223
214
  /**
224
215
  * Clear all errors
@@ -245,7 +236,7 @@ export function ErrorTrackingProvider({
245
236
  * Handle any error event
246
237
  */
247
238
  const handleError = useCallback(
248
- (detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | CentrifugoErrorConfig | RuntimeErrorConfig>) => {
239
+ (detail: ErrorDetail, config: Required<ValidationErrorConfig | CORSErrorConfig | NetworkErrorConfig | RuntimeErrorConfig>) => {
249
240
  // Check for duplicate errors (within deduplication window)
250
241
  const isUnique = shouldShowError(detail);
251
242
 
@@ -287,9 +278,19 @@ export function ErrorTrackingProvider({
287
278
  }
288
279
  }
289
280
  },
290
- [onError, onMonitorCapture, isProduction, isDebugMode]
281
+ // `isProduction` is a module constant, not reactive state.
282
+ [onError, onMonitorCapture, isDebugMode]
291
283
  );
292
284
 
285
+ // The listener effect must not re-run when the handler changes identity: hosts
286
+ // pass `onError`/`onMonitorCapture` as inline arrows, so `handleError` is new
287
+ // on every render. Listeners read the current handler through this ref and
288
+ // stay attached for the provider's whole lifetime.
289
+ const handleErrorRef = useRef(handleError);
290
+ useEffect(() => {
291
+ handleErrorRef.current = handleError;
292
+ }, [handleError]);
293
+
293
294
  /**
294
295
  * Setup event listeners
295
296
  */
@@ -318,7 +319,7 @@ export function ErrorTrackingProvider({
318
319
  error: error as ValidationErrorDetail['error'],
319
320
  response: raw.response ?? raw.data,
320
321
  };
321
- handleError(detail, validationConfig);
322
+ handleErrorRef.current(detail, validationConfig);
322
323
  };
323
324
  window.addEventListener(ERROR_EVENTS.VALIDATION, handler);
324
325
  handlers.push({ event: ERROR_EVENTS.VALIDATION, handler });
@@ -332,32 +333,12 @@ export function ErrorTrackingProvider({
332
333
  ...event.detail,
333
334
  type: 'network' as const,
334
335
  };
335
- handleError(detail, networkConfig);
336
+ handleErrorRef.current(detail, networkConfig);
336
337
  };
337
338
  window.addEventListener(ERROR_EVENTS.NETWORK, handler);
338
339
  handlers.push({ event: ERROR_EVENTS.NETWORK, handler });
339
340
  }
340
341
 
341
- // Centrifugo errors (unified event with type discriminator)
342
- if (centrifugoConfig.enabled) {
343
- const handler = (event: Event) => {
344
- if (!(event instanceof CustomEvent)) return;
345
- // Filter: only handle 'error' type from unified centrifugo event
346
- if (event.detail?.type !== 'error') return;
347
- const detail: CentrifugoErrorDetail = {
348
- type: 'centrifugo' as const,
349
- method: event.detail.data?.method || 'unknown',
350
- error: event.detail.data?.error || 'Unknown error',
351
- code: event.detail.data?.code,
352
- data: event.detail.data?.data,
353
- timestamp: event.detail.timestamp || new Date(),
354
- };
355
- handleError(detail, centrifugoConfig);
356
- };
357
- window.addEventListener(ERROR_EVENTS.CENTRIFUGO, handler);
358
- handlers.push({ event: ERROR_EVENTS.CENTRIFUGO, handler });
359
- }
360
-
361
342
  // Runtime errors (generic JS errors from anywhere in the app)
362
343
  if (runtimeConfig.enabled) {
363
344
  const handler = (event: Event) => {
@@ -370,7 +351,7 @@ export function ErrorTrackingProvider({
370
351
  context: event.detail.context,
371
352
  timestamp: event.detail.timestamp || new Date(),
372
353
  };
373
- handleError(detail, runtimeConfig);
354
+ handleErrorRef.current(detail, runtimeConfig);
374
355
  };
375
356
  window.addEventListener(ERROR_EVENTS.RUNTIME, handler);
376
357
  handlers.push({ event: ERROR_EVENTS.RUNTIME, handler });
@@ -382,13 +363,15 @@ export function ErrorTrackingProvider({
382
363
  window.removeEventListener(event, handler);
383
364
  });
384
365
  };
385
- }, [handleError, validationConfig, corsConfig, networkConfig, centrifugoConfig, runtimeConfig]);
366
+ // No `cors` listener: nothing dispatches `cors-error`. A blocked request
367
+ // surfaces as a network error carrying `possibly_cors`, so `corsConfig`
368
+ // configures presentation only and is not a dependency here.
369
+ }, [validationConfig, networkConfig, runtimeConfig]);
386
370
 
387
371
  // Filter errors by type
388
372
  const validationErrors = errors.filter((e) => e.type === 'validation') as StoredError<ValidationErrorDetail>[];
389
373
  const corsErrors = errors.filter((e) => e.type === 'cors') as StoredError<CORSErrorDetail>[];
390
374
  const networkErrors = errors.filter((e) => e.type === 'network') as StoredError<NetworkErrorDetail>[];
391
- const centrifugoErrors = errors.filter((e) => e.type === 'centrifugo') as StoredError<CentrifugoErrorDetail>[];
392
375
  const runtimeErrors = errors.filter((e) => e.type === 'runtime') as StoredError<RuntimeErrorDetail>[];
393
376
 
394
377
  const value: ErrorTrackingContextValue = {
@@ -396,7 +379,6 @@ export function ErrorTrackingProvider({
396
379
  validationErrors,
397
380
  corsErrors,
398
381
  networkErrors,
399
- centrifugoErrors,
400
382
  runtimeErrors,
401
383
  clearErrors,
402
384
  clearErrorsByType,
@@ -407,7 +389,6 @@ export function ErrorTrackingProvider({
407
389
  validation: validationConfig,
408
390
  cors: corsConfig,
409
391
  network: networkConfig,
410
- centrifugo: centrifugoConfig,
411
392
  runtime: runtimeConfig,
412
393
  },
413
394
  };
@@ -28,7 +28,7 @@ export interface ValidationErrorDetail extends BaseErrorDetail {
28
28
  /** Zod validation error */
29
29
  error: ZodError;
30
30
  /** Raw response data that failed validation */
31
- response: any;
31
+ response: unknown;
32
32
  }
33
33
 
34
34
  /**
@@ -65,21 +65,6 @@ export interface NetworkErrorDetail extends BaseErrorDetail {
65
65
  possibly_cors?: boolean;
66
66
  }
67
67
 
68
- /**
69
- * Centrifugo error detail (from centrifugo-error event)
70
- */
71
- export interface CentrifugoErrorDetail extends BaseErrorDetail {
72
- type: 'centrifugo';
73
- /** RPC method that failed */
74
- method: string;
75
- /** Error message */
76
- error: string;
77
- /** Error code from Centrifugo */
78
- code?: number;
79
- /** Additional data sent with the request */
80
- data?: any;
81
- }
82
-
83
68
  /**
84
69
  * Every error category the tracker stores.
85
70
  *
@@ -94,7 +79,6 @@ export type TrackedErrorType =
94
79
  | 'validation'
95
80
  | 'cors'
96
81
  | 'network'
97
- | 'centrifugo'
98
82
  | 'runtime';
99
83
 
100
84
  /**
@@ -116,7 +100,7 @@ export interface RuntimeErrorDetail extends BaseErrorDetail {
116
100
  /**
117
101
  * Union type of all error details
118
102
  */
119
- export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail;
103
+ export type ErrorDetail = ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | RuntimeErrorDetail;
120
104
 
121
105
  /**
122
106
  * Stored error with unique ID
@@ -224,23 +208,6 @@ export interface NetworkErrorConfig extends ErrorTypeConfig {
224
208
  showStatusCode?: boolean;
225
209
  }
226
210
 
227
- /**
228
- * Centrifugo error specific config
229
- */
230
- export interface CentrifugoErrorConfig extends ErrorTypeConfig {
231
- /**
232
- * Show RPC method in toast
233
- * @default true
234
- */
235
- showMethod?: boolean;
236
-
237
- /**
238
- * Show error code in toast
239
- * @default true
240
- */
241
- showCode?: boolean;
242
- }
243
-
244
211
  /**
245
212
  * Runtime error specific config
246
213
  */
@@ -271,11 +238,6 @@ export interface ErrorTrackingConfig {
271
238
  */
272
239
  network?: NetworkErrorConfig;
273
240
 
274
- /**
275
- * Centrifugo error tracking configuration
276
- */
277
- centrifugo?: CentrifugoErrorConfig;
278
-
279
241
  /**
280
242
  * Runtime error tracking configuration
281
243
  */
@@ -304,9 +266,6 @@ export interface ErrorTrackingContextValue {
304
266
  /** Network errors only */
305
267
  networkErrors: StoredError<NetworkErrorDetail>[];
306
268
 
307
- /** Centrifugo errors only */
308
- centrifugoErrors: StoredError<CentrifugoErrorDetail>[];
309
-
310
269
  /** Runtime errors only */
311
270
  runtimeErrors: StoredError<RuntimeErrorDetail>[];
312
271
 
@@ -330,7 +289,6 @@ export interface ErrorTrackingContextValue {
330
289
  validation: Required<ValidationErrorConfig>;
331
290
  cors: Required<CORSErrorConfig>;
332
291
  network: Required<NetworkErrorConfig>;
333
- centrifugo: Required<CentrifugoErrorConfig>;
334
292
  runtime: Required<RuntimeErrorConfig>;
335
293
  };
336
294
  }
@@ -342,8 +300,6 @@ export const ERROR_EVENTS = {
342
300
  VALIDATION: 'zod-validation-error',
343
301
  CORS: 'cors-error',
344
302
  NETWORK: 'network-error',
345
- /** Unified Centrifugo event - filter by detail.type === 'error' */
346
- CENTRIFUGO: 'centrifugo',
347
303
  /** Runtime/generic errors (Tour, components, etc.) */
348
304
  RUNTIME: 'runtime-error',
349
305
  } as const;
@@ -381,13 +337,6 @@ export const DEFAULT_NETWORK_CONFIG: Required<NetworkErrorConfig> = {
381
337
  showStatusCode: true,
382
338
  };
383
339
 
384
- export const DEFAULT_CENTRIFUGO_CONFIG: Required<CentrifugoErrorConfig> = {
385
- ...DEFAULT_ERROR_CONFIG,
386
- duration: 0, // Don't auto-dismiss centrifugo errors
387
- showMethod: true,
388
- showCode: true,
389
- };
390
-
391
340
  export const DEFAULT_RUNTIME_CONFIG: Required<RuntimeErrorConfig> = {
392
341
  ...DEFAULT_ERROR_CONFIG,
393
342
  duration: 8000,
@@ -13,7 +13,7 @@ export interface CurlOptions {
13
13
  method: string;
14
14
  path: string;
15
15
  token?: string;
16
- body?: any;
16
+ body?: unknown;
17
17
  headers?: Record<string, string>;
18
18
  baseUrl?: string;
19
19
  queryParams?: Record<string, string>;
@@ -126,7 +126,7 @@ export function generateCurl(options: CurlOptions): string {
126
126
  export function generateCurlFromError(detail: {
127
127
  method: string;
128
128
  path: string;
129
- response?: any;
129
+ response?: unknown;
130
130
  }): string {
131
131
  return generateCurl({
132
132
  method: detail.method,
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { ZodError } from 'zod';
8
- import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, CentrifugoErrorDetail, RuntimeErrorDetail, ErrorDetail } from '../types';
8
+ import type { ValidationErrorDetail, CORSErrorDetail, NetworkErrorDetail, RuntimeErrorDetail, ErrorDetail } from '../types';
9
9
  import type { DevtoolsEvent } from '@djangocfg/devtools';
10
10
  import { EventType, EventLevel } from '@djangocfg/devtools';
11
11
 
@@ -99,22 +99,6 @@ export function formatNetworkErrorForClipboard(detail: NetworkErrorDetail): stri
99
99
  return JSON.stringify(errorData, null, 2);
100
100
  }
101
101
 
102
- /**
103
- * Format centrifugo error for clipboard
104
- */
105
- export function formatCentrifugoErrorForClipboard(detail: CentrifugoErrorDetail): string {
106
- const errorData = {
107
- type: 'centrifugo',
108
- timestamp: detail.timestamp.toISOString(),
109
- method: detail.method,
110
- error: detail.error,
111
- ...(detail.code !== undefined && { code: detail.code }),
112
- ...(detail.data && { data: detail.data }),
113
- };
114
-
115
- return JSON.stringify(errorData, null, 2);
116
- }
117
-
118
102
  /**
119
103
  * Extract domain from URL
120
104
  */
@@ -160,8 +144,6 @@ export function errorDetailToDevtoolsEvent(detail: ErrorDetail): DevtoolsEvent {
160
144
  return { event_type: EventType.NETWORK_ERROR, level: EventLevel.ERROR, message: `CORS: ${detail.method} ${detail.url}`, http_method: detail.method, http_url: detail.url, url }
161
145
  case 'network':
162
146
  return { event_type: EventType.NETWORK_ERROR, level: detail.statusCode && detail.statusCode >= 500 ? EventLevel.ERROR : EventLevel.WARNING, message: `${detail.method} ${detail.url} → ${detail.statusCode ?? 'ERR'}`, http_method: detail.method, http_url: detail.url, http_status: detail.statusCode, url }
163
- case 'centrifugo':
164
- return { event_type: EventType.ERROR, level: EventLevel.ERROR, message: `Centrifugo ${detail.method}: ${detail.error}`, url }
165
147
  case 'runtime':
166
148
  return { event_type: EventType.JS_ERROR, level: EventLevel.ERROR, message: detail.message, stack_trace: detail.error?.stack, url }
167
149
  }
@@ -170,7 +152,7 @@ export function errorDetailToDevtoolsEvent(detail: ErrorDetail): DevtoolsEvent {
170
152
  /**
171
153
  * Format error title based on type (dev/debug mode — full details)
172
154
  */
173
- export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | CentrifugoErrorDetail | RuntimeErrorDetail): string {
155
+ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail | NetworkErrorDetail | RuntimeErrorDetail): string {
174
156
  switch (detail.type) {
175
157
  case 'validation':
176
158
  return `❌ Validation Error in ${detail.operation}`;
@@ -180,10 +162,6 @@ export function formatErrorTitle(detail: ValidationErrorDetail | CORSErrorDetail
180
162
  return detail.statusCode
181
163
  ? `⚠️ Network Error (${detail.statusCode})`
182
164
  : '⚠️ Network Error';
183
- case 'centrifugo':
184
- return detail.code !== undefined
185
- ? `🔌 Centrifugo Error (${detail.code})`
186
- : '🔌 Centrifugo Error';
187
165
  case 'runtime':
188
166
  return `⚠️ ${detail.source} Error`;
189
167
  default:
@@ -242,11 +220,6 @@ export function getProductionToast(detail: ErrorDetail, errorId: string): { titl
242
220
  description: `Please check your connection and try again. Reference: ${errorId}`,
243
221
  };
244
222
  }
245
- case 'centrifugo':
246
- return {
247
- title: 'Real-time connection error',
248
- description: `Live updates may be delayed. Reference: ${errorId}`,
249
- };
250
223
  case 'runtime':
251
224
  return {
252
225
  title: 'Something went wrong',