@djangocfg/debuger 2.1.233 → 2.1.235
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/dist/{chunk-ESIQODSI.mjs → chunk-5LBHQJ2O.mjs} +5 -4
- package/dist/chunk-5LBHQJ2O.mjs.map +1 -0
- package/dist/index.cjs +23 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +24 -28
- package/dist/index.mjs.map +1 -1
- package/dist/logger/index.cjs +3 -2
- package/dist/logger/index.cjs.map +1 -1
- package/dist/logger/index.mjs +1 -1
- package/package.json +6 -6
- package/src/DebugButton.tsx +45 -47
- package/src/logger/logger.ts +1 -1
- package/dist/chunk-ESIQODSI.mjs.map +0 -1
package/src/DebugButton.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
TooltipContent,
|
|
8
8
|
TooltipTrigger,
|
|
9
9
|
} from '@djangocfg/ui-core/components';
|
|
10
|
-
import { cn } from '@djangocfg/ui-core/lib';
|
|
10
|
+
import { cn, isDev } from '@djangocfg/ui-core/lib';
|
|
11
11
|
import { useDebugMode } from '@djangocfg/monitor/client';
|
|
12
12
|
import { useDebugStore } from './store/debugStore';
|
|
13
13
|
import { useDebugShortcut } from './hooks/useDebugShortcut';
|
|
@@ -23,87 +23,86 @@ export interface DebugButtonProps {
|
|
|
23
23
|
className?: string;
|
|
24
24
|
/** Props forwarded to DebugPanel */
|
|
25
25
|
panel?: Omit<DebugPanelProps, never>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ============================================================================
|
|
31
|
-
// Easter-egg: 5 clicks in 2 seconds → unlock
|
|
32
|
-
// ============================================================================
|
|
33
|
-
|
|
34
|
-
function useEasterEgg(onTriggered: () => void) {
|
|
35
|
-
const clicks = useRef<number[]>([]);
|
|
36
|
-
|
|
37
|
-
return useCallback(() => {
|
|
38
|
-
const now = Date.now();
|
|
39
|
-
clicks.current = [...clicks.current.filter((t) => now - t < 2000), now];
|
|
40
|
-
if (clicks.current.length >= 5) {
|
|
41
|
-
clicks.current = [];
|
|
42
|
-
onTriggered();
|
|
43
|
-
}
|
|
44
|
-
}, [onTriggered]);
|
|
26
|
+
/** Explicitly disable. Default: undefined (auto — visible in dev, hidden in prod, ?debug=1 always works) */
|
|
27
|
+
enabled?: boolean;
|
|
45
28
|
}
|
|
46
29
|
|
|
47
30
|
// ============================================================================
|
|
48
31
|
// DebugButton
|
|
32
|
+
//
|
|
33
|
+
// Visibility rules (simple):
|
|
34
|
+
// 1. enabled === false AND no ?debug=1 → hidden completely
|
|
35
|
+
// 2. isDev OR ?debug=1 (localStorage) → visible (unlocked)
|
|
36
|
+
// 3. prod without ?debug=1 → invisible click trap (easter egg: 5 clicks)
|
|
49
37
|
// ============================================================================
|
|
50
38
|
|
|
51
|
-
export function DebugButton({ className, panel = {},
|
|
39
|
+
export function DebugButton({ className, panel = {}, enabled }: DebugButtonProps) {
|
|
40
|
+
// All hooks called unconditionally (React rules of hooks)
|
|
52
41
|
const isOpen = useDebugStore((s) => s.isOpen);
|
|
53
42
|
const isUnlocked = useDebugStore((s) => s.isUnlocked);
|
|
54
43
|
const unlock = useDebugStore((s) => s.unlock);
|
|
55
44
|
const toggle = useDebugStore((s) => s.toggle);
|
|
56
|
-
|
|
57
45
|
const isDebugMode = useDebugMode();
|
|
58
|
-
|
|
59
|
-
// Auto-unlock when defaultUnlocked or debug mode is active
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (defaultUnlocked || isDebugMode) unlock('1');
|
|
62
|
-
}, [defaultUnlocked, isDebugMode, unlock]);
|
|
63
46
|
const errorCount = useDebugErrorCount();
|
|
64
|
-
|
|
65
|
-
// Keyboard shortcut
|
|
66
47
|
useDebugShortcut();
|
|
67
48
|
|
|
68
|
-
//
|
|
69
|
-
|
|
49
|
+
// Should this button be visible (unlocked)?
|
|
50
|
+
const shouldUnlock = isDev || isDebugMode;
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
if (typeof window !== 'undefined') console.info('[DebugButton]', { isDev, isDebugMode, shouldUnlock, isUnlocked, enabled });
|
|
54
|
+
|
|
55
|
+
// Auto-unlock in dev or when ?debug=1
|
|
70
56
|
useEffect(() => {
|
|
71
|
-
if (
|
|
57
|
+
if (shouldUnlock) unlock('1');
|
|
58
|
+
}, [shouldUnlock, unlock]);
|
|
72
59
|
|
|
60
|
+
// Hide Next.js dev indicator while panel is open
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (typeof document === 'undefined') return;
|
|
73
63
|
const apply = () => {
|
|
74
64
|
document.querySelectorAll<HTMLElement>('nextjs-portal').forEach((el) => {
|
|
75
65
|
el.style.display = isOpen ? 'none' : '';
|
|
76
66
|
});
|
|
77
67
|
};
|
|
78
|
-
|
|
79
|
-
apply(); // handle already-mounted elements
|
|
80
|
-
|
|
68
|
+
apply();
|
|
81
69
|
const observer = new MutationObserver(apply);
|
|
82
70
|
observer.observe(document.body, { childList: true });
|
|
83
|
-
|
|
84
71
|
return () => observer.disconnect();
|
|
85
72
|
}, [isOpen]);
|
|
86
73
|
|
|
87
|
-
// Easter
|
|
88
|
-
const
|
|
89
|
-
const
|
|
74
|
+
// Easter egg: 5 clicks in 2s → unlock
|
|
75
|
+
const clicks = useRef<number[]>([]);
|
|
76
|
+
const handleEasterEgg = useCallback(() => {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
clicks.current = [...clicks.current.filter((t) => now - t < 2000), now];
|
|
79
|
+
if (clicks.current.length >= 5) {
|
|
80
|
+
clicks.current = [];
|
|
81
|
+
unlock('1');
|
|
82
|
+
toggle();
|
|
83
|
+
}
|
|
84
|
+
}, [unlock, toggle]);
|
|
85
|
+
|
|
86
|
+
// === Render decision (after all hooks) ===
|
|
90
87
|
|
|
91
|
-
//
|
|
92
|
-
|
|
88
|
+
// Explicitly disabled AND no debug override → don't render at all
|
|
89
|
+
if (enabled === false && !isDebugMode) return null;
|
|
90
|
+
|
|
91
|
+
// Not unlocked → invisible click trap for easter egg
|
|
93
92
|
if (!isUnlocked) {
|
|
94
93
|
return (
|
|
95
94
|
<div
|
|
96
|
-
className="fixed bottom-5 left-5 w-9 h-9 z-[99999] opacity-0"
|
|
97
|
-
onClick={
|
|
95
|
+
className="fixed bottom-5 left-5 w-9 h-9 z-[99999] opacity-0 cursor-default"
|
|
96
|
+
onClick={handleEasterEgg}
|
|
98
97
|
/>
|
|
99
98
|
);
|
|
100
99
|
}
|
|
101
100
|
|
|
101
|
+
// Unlocked → visible debug button
|
|
102
102
|
return (
|
|
103
103
|
<>
|
|
104
|
-
{/* Trigger button — hidden when panel is open */}
|
|
105
104
|
{!isOpen && (
|
|
106
|
-
<div className=
|
|
105
|
+
<div className={cn('fixed bottom-5 z-[99999]', isDev ? 'left-16' : 'left-5')}>
|
|
107
106
|
<Tooltip>
|
|
108
107
|
<TooltipTrigger asChild>
|
|
109
108
|
<button
|
|
@@ -115,7 +114,7 @@ export function DebugButton({ className, panel = {}, defaultUnlocked = false }:
|
|
|
115
114
|
'transition-all duration-150 hover:scale-105',
|
|
116
115
|
'focus:outline-none focus-visible:ring-2 focus-visible:ring-white/50',
|
|
117
116
|
errorCount > 0 && 'bg-red-600/90',
|
|
118
|
-
className
|
|
117
|
+
className,
|
|
119
118
|
)}
|
|
120
119
|
onClick={toggle}
|
|
121
120
|
>
|
|
@@ -131,7 +130,6 @@ export function DebugButton({ className, panel = {}, defaultUnlocked = false }:
|
|
|
131
130
|
</Tooltip>
|
|
132
131
|
</div>
|
|
133
132
|
)}
|
|
134
|
-
|
|
135
133
|
<DebugPanel {...panel} />
|
|
136
134
|
</>
|
|
137
135
|
);
|
package/src/logger/logger.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { Logger, LogLevel } from './types';
|
|
|
12
12
|
// Helpers
|
|
13
13
|
// ============================================================================
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
import { isDev } from '@djangocfg/ui-core/lib';
|
|
16
16
|
const isBrowser = typeof window !== 'undefined';
|
|
17
17
|
|
|
18
18
|
function extractStack(data?: Record<string, unknown>): {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logger/logStore.ts","../src/logger/logger.ts"],"names":[],"mappings":"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","file":"chunk-ESIQODSI.mjs","sourcesContent":["'use client';\n\nimport { create } from 'zustand';\nimport type { LogStore, LogEntry, LogFilter } from './types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst MAX_LOGS = 1000;\n\nconst DEFAULT_FILTER: LogFilter = {\n levels: ['debug', 'info', 'warn', 'error', 'success'],\n};\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nlet _counter = 0;\n\nfunction generateId(): string {\n return `log-${Date.now()}-${++_counter}`;\n}\n\nfunction matchesFilter(entry: LogEntry, filter: LogFilter): boolean {\n if (!filter.levels.includes(entry.level)) return false;\n\n if (filter.component) {\n if (!entry.component.toLowerCase().includes(filter.component.toLowerCase())) return false;\n }\n\n if (filter.search) {\n const s = filter.search.toLowerCase();\n const inMsg = entry.message.toLowerCase().includes(s);\n const inData = entry.data ? JSON.stringify(entry.data).toLowerCase().includes(s) : false;\n if (!inMsg && !inData) return false;\n }\n\n return true;\n}\n\n// ============================================================================\n// Store\n// ============================================================================\n\nexport const useDebugLogStore = create<LogStore>((set, get) => ({\n logs: [],\n filter: DEFAULT_FILTER,\n\n addLog: (entry) => {\n const newEntry: LogEntry = {\n ...entry,\n id: generateId(),\n timestamp: new Date(),\n };\n set((state) => {\n const next = [...state.logs, newEntry];\n return { logs: next.length > MAX_LOGS ? next.slice(-MAX_LOGS) : next };\n });\n },\n\n clearLogs: () => set({ logs: [] }),\n\n setFilter: (filter) =>\n set((state) => ({ filter: { ...state.filter, ...filter } })),\n\n getFilteredLogs: () => {\n const { logs, filter } = get();\n return logs.filter((e) => matchesFilter(e, filter));\n },\n\n exportLogs: () => JSON.stringify(get().logs, null, 2),\n}));\n\n// ============================================================================\n// Selector hooks\n// ============================================================================\n\nexport const useDebugFilteredLogs = () => {\n const logs = useDebugLogStore((s) => s.logs);\n const filter = useDebugLogStore((s) => s.filter);\n return logs.filter((e) => matchesFilter(e, filter));\n};\n\nexport const useDebugLogCount = () => useDebugLogStore((s) => s.logs.length);\n\nexport const useDebugErrorCount = () =>\n useDebugLogStore((s) => s.logs.filter((l) => l.level === 'error').length);\n","/**\n * Debug Logger\n *\n * Standalone logger for @djangocfg/debuger — no ui-core dependency.\n * Writes to useDebugLogStore (shown in LogsPanel) and to console in dev.\n */\n\nimport { useDebugLogStore } from './logStore';\nimport type { Logger, LogLevel } from './types';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst isDev = process.env.NODE_ENV !== 'production';\nconst isBrowser = typeof window !== 'undefined';\n\nfunction extractStack(data?: Record<string, unknown>): {\n cleanData: Record<string, unknown> | undefined;\n stack: string | undefined;\n} {\n if (!data) return { cleanData: undefined, stack: undefined };\n\n const cleanData = { ...data };\n let stack: string | undefined;\n\n if (data.error instanceof Error) {\n stack = data.error.stack;\n cleanData.error = { name: data.error.name, message: data.error.message };\n } else if (typeof data.error === 'object' && data.error !== null) {\n const e = data.error as Record<string, unknown>;\n if (typeof e.stack === 'string') stack = e.stack;\n if (typeof e.message === 'string') cleanData.error = e.message;\n }\n\n return { cleanData, stack };\n}\n\n// ============================================================================\n// createLogger\n// ============================================================================\n\nexport function createDebugLogger(component: string): Logger {\n const write = (level: LogLevel, message: string, data?: Record<string, unknown>) => {\n const { cleanData, stack } = extractStack(data);\n\n if (isBrowser) {\n useDebugLogStore.getState().addLog({ level, component, message, data: cleanData, stack });\n }\n\n if (isDev) {\n const method = level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log';\n const prefix = `[${component}]`;\n if (cleanData) {\n console[method](prefix, message, cleanData);\n } else {\n console[method](prefix, message);\n }\n }\n };\n\n return {\n debug: (msg, data) => write('debug', msg, data),\n info: (msg, data) => write('info', msg, data),\n warn: (msg, data) => write('warn', msg, data),\n error: (msg, data) => write('error', msg, data),\n success: (msg, data) => write('success', msg, data),\n };\n}\n\n// ============================================================================\n// debugLog — one-shot helper (creates logger on each call, for non-hot paths)\n// ============================================================================\n\nexport function debugLog(\n component: string,\n level: LogLevel,\n message: string,\n data?: Record<string, unknown>,\n): void {\n const { cleanData, stack } = extractStack(data);\n\n if (isBrowser) {\n useDebugLogStore.getState().addLog({ level, component, message, data: cleanData, stack });\n }\n\n if (isDev) {\n const method = level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log';\n if (cleanData) {\n console[method](`[${component}]`, message, cleanData);\n } else {\n console[method](`[${component}]`, message);\n }\n }\n}\n"]}
|