File size: 3,463 Bytes
c16e487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { create } from "zustand";
import { persist } from "zustand/middleware";

interface LogEntry {
  timestamp: string;
  level: "info" | "warn" | "error";
  message: string;
  data?: any[];
}

interface DebugStore {
  logs: LogEntry[];
  addLog: (level: "info" | "warn" | "error", message: string, ...data: any[]) => void;
  clearLogs: () => void;
  exportLogs: () => string;
}

// Intercept console methods to capture logs
const originalConsoleLog = console.log;
const originalConsoleWarn = console.warn;
const originalConsoleError = console.error;

export const useDebugStore = create<DebugStore>()(
  persist(
    (set, get) => ({
      logs: [],
      addLog: (level, message, ...data) => {
        const serialize = (obj: any, depth = 0): any => {
          if (depth > 3) return "[Max Depth Reached]";
          if (obj === null || obj === undefined) return obj;

          if (obj instanceof Error || (obj && typeof obj.message === 'string' && typeof obj.name === 'string')) {
            return {
              name: obj.name,
              message: obj.message,
              stack: obj.stack,
              cause: obj.cause ? serialize(obj.cause, depth + 1) : undefined,
              // Some APIs return error data in custom properties
              ...(Object.getOwnPropertyNames(obj).reduce((acc, key) => {
                if (!['name', 'message', 'stack', 'cause'].includes(key)) {
                  acc[key] = serialize((obj as any)[key], depth + 1);
                }
                return acc;
              }, {} as any))
            };
          }

          if (typeof obj === "object") {
            try {
              if (Array.isArray(obj)) {
                return obj.map(item => serialize(item, depth + 1));
              }
              const result: any = {};
              for (const key in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, key)) {
                   result[key] = serialize(obj[key], depth + 1);
                }
              }
              return result;
            } catch {
              return "[Unserializable Object]";
            }
          }
          return obj;
        };

        const parsedMessage =
          typeof message === "string" ? message : JSON.stringify(serialize(message));

        const entry: LogEntry = {
          timestamp: new Date().toISOString(),
          level,
          message: parsedMessage,
          data: data.length > 0 ? data.map(serialize) : undefined,
        };
        // Limit logs to last 1000 entries to prevent memory issues
        set((state) => ({
          logs: [...state.logs.slice(-999), entry],
        }));
      },
      clearLogs: () => set({ logs: [] }),
      exportLogs: () => {
        const { logs } = get();
        return JSON.stringify(logs, null, 2);
      },
    }),
    {
      name: "deep-research-debug-store",
    }
  )
);

// Function to initialize log interception
export function initLogInterception() {
  if (typeof window === "undefined") return; // Client-side only

  console.log = (...args) => {
    originalConsoleLog(...args);
    useDebugStore.getState().addLog("info", args[0], ...args.slice(1));
  };

  console.warn = (...args) => {
    originalConsoleWarn(...args);
    useDebugStore.getState().addLog("warn", args[0], ...args.slice(1));
  };

  console.error = (...args) => {
    originalConsoleError(...args);
    useDebugStore.getState().addLog("error", args[0], ...args.slice(1));
  };
}