hazo_debug
v3.1.0
Published
Debug toolkit for hazo_* packages - permission-gated UI for inspecting runtime state, API calls, and configuration
Maintainers
Readme
hazo_debug
Permission-gated debug toolkit for hazo_* packages. Provides a floating DevTools-like panel with 10 pluggable feature tabs for inspecting runtime state, API calls, errors, database queries, LLM prompts, file operations, and more.
Installation
npm install hazo_debugPeer Dependencies (required)
npm install hazo_ui react react-domOptional Peer Dependencies
hazo_auth— Permission-gated access in productionhazo_config— INI config file readinghazo_connect— Database query logginghazo_files— File operation logginghazo_llm_api— LLM prompt/response logginghazo_logs(>=1.0.13) — Structured logging withon_logcallbacklucide-react— Icons
Quick Start
1. Wrap your app with DebugProvider
// app/layout.tsx
import { DebugProvider } from 'hazo_debug/client';
export default function Layout({ children }) {
return (
<DebugProvider
force_enabled={process.env.NODE_ENV === 'development'}
hazo_logs_endpoint="/api/hazo_logs/client-log"
hazo_logs_api="/api/logs"
>
{children}
</DebugProvider>
);
}2. The debug panel appears automatically
Press Ctrl+Shift+D or click the floating bug icon in the bottom-right corner.
3. Log with hazo_debug_console
import { use_debug_log } from 'hazo_debug/client';
function MyComponent({ data }) {
const { hazo_debug_console, log_event } = use_debug_log('MyComponent');
function handleClick() {
hazo_debug_console('info', 'Button clicked', { data });
log_event('user_action', 'Button clicked', { data });
}
return <button onClick={handleClick}>Do something</button>;
}hazo_debug_console() entries appear in both the hazo_logs and hazo_console tabs, and are dual-written to the server when hazo_logs_endpoint is configured.
4. Add debug icons to components
import { DebugIcon } from 'hazo_debug/client';
function MyComponent({ data }) {
return (
<div>
<DebugIcon data={data} label="Props" component_name="MyComponent" />
</div>
);
}5. Set up the server API (optional)
// app/api/hazo_debug/[...path]/route.ts
import { create_debug_api_handler } from 'hazo_debug/server';
export const { GET } = create_debug_api_handler();Debug Panel Tabs
| Tab | Description |
|-----|-------------|
| Network | Intercepts all fetch() calls. Shows URL, method, status, timing, headers, body. |
| Events | Chronological log of user actions, navigation, state changes. Filterable by category. |
| Errors | Catches errors via <DebugErrorBoundary>, window.onerror, and unhandledrejection. |
| hazo_auth | Displays current auth state, permissions, and user info. |
| hazo_config | Shows INI config values (with secrets masked) and environment variables. |
| hazo_connect | Database query log — operation, table, WHERE conditions, timing, rows, errors. |
| hazo_llm_api | LLM prompt/response viewer — provider, model, timing, errors. Click to see full prompt and response. |
| hazo_files | File operation log — upload, download, delete, move, extract with size, storage, timing. |
| hazo_logs | Server-side logs (polled via hazo_logs_api) + hazo_debug_console() entries with level filter. |
| hazo_console | Filtered view showing only hazo_debug_console() entries — the console.log replacement. |
Copy Debug Context for AI
The toolbar's clipboard button (📋) — or the keyboard shortcut Ctrl/Cmd+Shift+X — opens a dialog that captures a snapshot of every tab within a chosen time window and copies it to the clipboard. Use this when you want to hand a bug to Claude/ChatGPT/a teammate without manually screenshotting tabs.
The dialog:
- Surfaces the most recent error (across
error_log,debug_log[level=error], andrequest_log[status>=500]) and uses its timestamp as the default anchor. - Lets you pick a window: 5s / 30s / 60s / since panel opened / all buffered.
- Includes a Notes field — free-text so the receiver knows what you were trying to do.
- Redacts
authorization,cookie,set-cookie, andproxy-authorizationheaders/keys recursively (toggleable). - Supports two output formats:
- AI prompt (markdown) (default) — wraps the JSON in a Markdown frame with role/instruction text and a self-describing
descriptionfield, ready to paste into a chat. - JSON only — raw snapshot.
- AI prompt (markdown) (default) — wraps the JSON in a Markdown frame with role/instruction text and a self-describing
- Copy to clipboard + Download .md / .json.
Custom tabs in snapshots
To include a custom tab's data in copy-context output, provide get_snapshot on the registration. It receives the full DebugContextState and a { since, until } window and returns any JSON-serializable value — typically { entries: [...], total_in_buffer: N }.
use_register_debug_tab({
id: 'orders',
label: 'Orders',
render: () => <OrdersPanel />,
get_snapshot: (state, { since, until }) => ({
entries: orders_log.filter(o => o.timestamp >= since && o.timestamp <= until),
total_in_buffer: orders_log.length,
}),
});Tabs without get_snapshot appear in the snapshot as null so the receiver knows they exist but didn't expose state.
Hazo Package Integration Hooks
import {
use_debug_query, // hazo_connect: SQL queries
use_debug_llm, // hazo_llm_api: prompts/responses
use_debug_files, // hazo_files: file operations
use_debug_log, // hazo_logs: structured logging
} from 'hazo_debug/client';
// In components:
const { log_query } = use_debug_query();
const { log_llm_call } = use_debug_llm();
const { log_file_op } = use_debug_files();
const { hazo_debug_console } = use_debug_log('MyComponent');Wire hazo_logs on_log callback
import { createClientLogger } from 'hazo_logs/ui';
import { use_debug_log } from 'hazo_debug/client';
const { hazo_debug_console } = use_debug_log('MyApp');
const logger = createClientLogger({
packageName: 'my_app',
on_log: hazo_debug_console, // pipes all logs to debug panel + server
});Registering Custom Panel Tabs
Any consumer package can add its own tab to the debug panel — or replace a
built-in with a richer version — by calling use_register_debug_tab from a
component mounted inside a DebugProvider.
'use client';
import { use_register_debug_tab } from 'hazo_debug/client';
import { HazoAuthDebugTab } from '../components/hazo_auth_debug_tab';
export function HazoAuthDebugBridge() {
use_register_debug_tab({
id: 'auth_display',
label: 'hazo_auth',
icon: 'Shield',
priority: 50,
overrides_builtin: true,
badge: { count: 3, severity: 'warn' },
render: () => <HazoAuthDebugTab />,
});
return null;
}DebugTabRegistration
| Field | Type | Default | Notes |
|---|---|---|---|
| id | string | required | Unique identifier. Built-in ids: request_inspector, event_log, error_boundary, auth_display, config_viewer, query_log, llm_log, file_log, hazo_logs, hazo_console. |
| label | string | required | Tab strip text. When overriding a built-in, an empty string preserves the built-in label. |
| icon | string \| ComponentType | undefined | Lucide icon name (e.g. 'Shield') or a React component accepting { className, size }. |
| priority | number | 200 | Lower is earlier. Built-ins use 100..199. |
| overrides_builtin | boolean | false | Required to replace a built-in id. Without it the registration is rejected with a console warning. |
| badge | { count?: number; severity?: 'info' \| 'warn' \| 'error' } | undefined | Static badge. Wins over get_badge. Omit count for a colored dot. |
| get_badge | (state) => DebugTabBadge \| undefined | undefined | Dynamic badge resolver. Built-ins use this to derive counts from buffers. |
| render | (ctx) => ReactNode | required | Called only when the tab is active. Receives the debug context value. |
| visible | boolean | true | Hide without unregistering. |
Lifecycle
- The hook dispatches
REGISTER_TABon mount andUNREGISTER_TABon unmount. - When unmounting an override, the original built-in is restored automatically.
- Field changes (badge count, label, etc.) are dispatched as
UPDATE_TABand do not re-mount the active tab content. - When
DebugProvideris absent or debug is disabled, the hook is a no-op — no DOM, no state. - Two simultaneous registrations with the same
idlog a warning; the first one wins.
Declarative wrapper
If you prefer JSX:
import { RegisterDebugTab } from 'hazo_debug/client';
<RegisterDebugTab id="metrics" label="Metrics" priority={50}>
<MyMetricsContent />
</RegisterDebugTab>Permission Model
| Environment | Behavior |
|-------------|----------|
| NODE_ENV=development | Debug tools always available |
| NODE_ENV=production | Requires debug_permission on user's hazo_auth role |
| Config override | Set require_permission = false in hazo_debug_config.ini |
| Prop override | Pass force_enabled={true} to <DebugProvider> |
When debug is disabled, all components render nothing and no data is captured (zero overhead).
Configuration
Copy config/hazo_debug_config.example.ini to your project's config/hazo_debug_config.ini:
[hazo_debug]
enabled = true
require_permission = true
permission_name = debug_permission
max_request_entries = 200
max_event_entries = 500
keyboard_shortcut = ctrl+shift+d
panel_position = bottomTailwind CSS
No configuration needed. hazo_debug injects its own self-contained stylesheet at runtime, scoped under [data-hazo-debug]. The debug panel works regardless of the consumer's Tailwind setup — no @source directive required.
API Reference
Components
<DebugProvider>— Context provider. Wrap your app root. Acceptsforce_enabled,features,hazo_logs_endpoint,hazo_logs_api,keyboard_shortcut,api_base_path.<DebugIcon>— Bright blue bug icon that opens a debug dialog on click.<DebugDialog>— Modal for structured debug data.<DebugCopyDialog>— Copy-context dialog (rendered automatically insideDebugProvider; opens via toolbar📋orCtrl/Cmd+Shift+X).<DebugJsonView>— Collapsible JSON viewer.<DebugErrorBoundary>— Error boundary that reports to the debug panel.<DebugBadge>— Status/method/count pill (variants: default, success, warning, error, info).
Hooks
use_debug_log(source)— Returns{ hazo_debug_console, log_debug, log_event }.use_debug_query()— Returns{ log_query }for hazo_connect SQL query logging.use_debug_llm()— Returns{ log_llm_call }for hazo_llm_api prompt/response logging.use_debug_files()— Returns{ log_file_op }for hazo_files operation logging.use_debug_enabled()— Returnstrueif debug is active.use_debug_context()— Full access to debug state and dispatch.
Server
create_debug_api_handler()— Next.js route handler for/api/hazo_debug.get_debug_config()— Read INI config with masked secrets.check_debug_permission()— Server-side auth check.
Types
All entry types are exported from the root hazo_debug import:
import type {
QueryLogEntry, // hazo_connect queries
LlmLogEntry, // hazo_llm_api calls
FileLogEntry, // hazo_files operations
DebugLogEntry, // debug log entries
RequestLogEntry, // network requests
DebugEventEntry, // events
DebugErrorEntry, // errors
} from 'hazo_debug';