@djangocfg/devtools 2.1.459

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/src/types.ts ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @djangocfg/devtools — shared types.
3
+ *
4
+ * `DevtoolsEvent` is the hand-written contract of `POST /cfg/monitor/ingest/`
5
+ * (django-cfg's django_monitor extension). The endpoint is a stable, additive
6
+ * API — a generated OpenAPI client here would be 2k+ lines to describe one
7
+ * POST, so we don't. If the backend contract changes shape (it shouldn't),
8
+ * this file is the single place to update.
9
+ */
10
+
11
+ // ─── Backend event contract ──────────────────────────────────────────────────
12
+
13
+ export const EventType = {
14
+ JS_ERROR: 'JS_ERROR',
15
+ NETWORK_ERROR: 'NETWORK_ERROR',
16
+ ERROR: 'ERROR',
17
+ WARNING: 'WARNING',
18
+ CONSOLE: 'CONSOLE',
19
+ } as const
20
+ export type EventType = (typeof EventType)[keyof typeof EventType]
21
+
22
+ export const EventLevel = {
23
+ ERROR: 'error',
24
+ WARNING: 'warning',
25
+ INFO: 'info',
26
+ DEBUG: 'debug',
27
+ } as const
28
+ export type EventLevel = (typeof EventLevel)[keyof typeof EventLevel]
29
+
30
+ /** One ingest event. Field limits mirror the backend serializer. */
31
+ export interface DevtoolsEvent {
32
+ event_type: EventType
33
+ message: string
34
+ level?: EventLevel
35
+ stack_trace?: string
36
+ url?: string
37
+ fingerprint?: string
38
+ http_status?: number | null
39
+ http_method?: string
40
+ http_url?: string
41
+ session_id?: string
42
+ user_agent?: string
43
+ build_id?: string
44
+ environment?: string
45
+ extra?: unknown
46
+ project_name?: string
47
+ }
48
+
49
+ // ─── Config ──────────────────────────────────────────────────────────────────
50
+
51
+ export interface DevtoolsConfig {
52
+ /** Base URL for the django-cfg backend. Default: NEXT_PUBLIC_API_URL or same origin */
53
+ baseUrl?: string
54
+ /** Project name sent with every event */
55
+ project?: string
56
+ /** Environment tag: production / staging / development */
57
+ environment?: string
58
+ /** Next.js BUILD_ID for server-side source map deminification */
59
+ buildId?: string
60
+ /** Send captured errors/warnings to the backend ingest endpoint. Default: true */
61
+ ingest?: boolean
62
+ /** Capture window.onerror + unhandledrejection. Default: true */
63
+ captureJsErrors?: boolean
64
+ /** Capture console.warn / console.error. Default: true */
65
+ captureConsole?: boolean
66
+ /** Ingest flush interval in ms. Default: 5000 */
67
+ flushInterval?: number
68
+ /** Max events in the outbox before immediate flush. Default: 20 */
69
+ maxBufferSize?: number
70
+ /** Deduplication TTL in ms (same event captured twice). Default: 30000 */
71
+ dedupeTtl?: number
72
+ /** Log devtools internals to console. Default: false */
73
+ debug?: boolean
74
+ }
75
+
76
+ export interface ServerDevtoolsConfig {
77
+ /** Base URL for the django-cfg backend (absolute URL required on server) */
78
+ baseUrl?: string
79
+ project?: string
80
+ environment?: string
81
+ }
82
+
83
+ // ─── Panel log entries (local, never sent anywhere) ─────────────────────────
84
+
85
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success'
86
+
87
+ export interface LogEntry {
88
+ id: string
89
+ timestamp: Date
90
+ level: LogLevel
91
+ /** Where the entry came from: a component name, or `capture:<event_type>` */
92
+ source: string
93
+ message: string
94
+ data?: Record<string, unknown>
95
+ stack?: string
96
+ }
97
+
98
+ export interface Logger {
99
+ debug: (message: string, data?: Record<string, unknown>) => void
100
+ info: (message: string, data?: Record<string, unknown>) => void
101
+ warn: (message: string, data?: Record<string, unknown>) => void
102
+ error: (message: string, data?: Record<string, unknown>) => void
103
+ success: (message: string, data?: Record<string, unknown>) => void
104
+ }
105
+
106
+ // ─── Panel customization ─────────────────────────────────────────────────────
107
+
108
+ export interface CustomDebugTab {
109
+ id: string
110
+ label: string
111
+ icon: React.ElementType
112
+ panel: React.ComponentType<{ isActive: boolean }>
113
+ }
@@ -0,0 +1,58 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * useDebugMode — debug mode detection.
5
+ *
6
+ * - development: always true
7
+ * - production: `?debug=1` persists to localStorage (param removed from URL),
8
+ * `?debug=0` clears it; otherwise the stored flag decides.
9
+ */
10
+
11
+ import { useEffect, useState } from 'react'
12
+
13
+ import { isDevelopment } from './internal'
14
+
15
+ const LS_KEY = '__debug_mode__'
16
+
17
+ function readStored(): boolean {
18
+ try {
19
+ return localStorage.getItem(LS_KEY) === '1'
20
+ } catch {
21
+ return false
22
+ }
23
+ }
24
+
25
+ function consumeDebugParam(): boolean | null {
26
+ if (typeof window === 'undefined') return null
27
+ const params = new URLSearchParams(window.location.search)
28
+ const value = params.get('debug')
29
+ if (value === null) return null
30
+
31
+ try {
32
+ if (value === '1') localStorage.setItem(LS_KEY, '1')
33
+ else localStorage.removeItem(LS_KEY)
34
+ } catch {
35
+ /* private browsing */
36
+ }
37
+
38
+ params.delete('debug')
39
+ const qs = params.toString()
40
+ window.history.replaceState(
41
+ null,
42
+ '',
43
+ `${window.location.pathname}${qs ? `?${qs}` : ''}${window.location.hash}`,
44
+ )
45
+ return value === '1'
46
+ }
47
+
48
+ export function useDebugMode(): boolean {
49
+ const [isDebug, setIsDebug] = useState(isDevelopment)
50
+
51
+ useEffect(() => {
52
+ if (isDevelopment) return
53
+ const fromUrl = consumeDebugParam()
54
+ setIsDebug(fromUrl ?? readStored())
55
+ }, [])
56
+
57
+ return isDebug
58
+ }