logbubble 0.1.0 → 0.2.0

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/logbubble.svg)](https://badge.fury.io/js/logbubble)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![GitHub](https://img.shields.io/badge/GitHub-LogBubble-blue)](https://github.com/dktiwary007/LogBubble)
5
+ [![GitHub](https://img.shields.io/badge/GitHub-LogBubble-blue)](https://github.com/dkumar600/LogBubble)
6
6
 
7
7
  A lightweight, floating log viewer for monitoring network requests and console logs in web applications. Perfect for debugging web apps, Capacitor apps, and Progressive Web Apps (PWAs) directly on mobile devices or in production-like environments.
8
8
 
@@ -15,6 +15,10 @@ A lightweight, floating log viewer for monitoring network requests and console l
15
15
  📝 **Console Logging**: Captures `console.log`, `console.warn`, `console.error`, `console.info`, and `console.debug`
16
16
  🔔 **Unread Badge**: Shows count of new logs since last view
17
17
  📋 **Copy to Clipboard**: Export all logs with timestamps
18
+ 🟢 **Draggable Bubble**: Move the log bubble to any corner of the screen
19
+ 🟦 **Toggleable Filters**: Easily filter between console and network logs
20
+ 🗂️ **Log Details Modal**: Click any log to open a modal with full details, timestamp, and a copy button for better readability
21
+ 🛠️ **Built-in Vite Plugin**: Vite plugin included in this project for easy integration during development
18
22
  🔄 **Auto-initialization**: Works out of the box with zero configuration
19
23
  🎨 **Clean UI**: Modern, minimalist design with color-coded log types
20
24
 
@@ -44,6 +48,42 @@ import "logbubble";
44
48
 
45
49
  The floating bubble will appear in the top-right corner of your app.
46
50
 
51
+ ## Vite Plugin Integration
52
+
53
+ LogBubble provides a Vite plugin for easy integration during development. This automatically injects LogBubble into your app for local debugging.
54
+
55
+ ### Setup
56
+
57
+ 1. Install LogBubble (if not already):
58
+
59
+ ```bash
60
+ npm install logbubble
61
+ ```
62
+
63
+ 2. Add the plugin to your `vite.config.js` or `vite.config.ts`:
64
+
65
+ ```javascript
66
+ // vite.config.js
67
+ import { logbubbleVitePlugin } from "logbubble/vite-logbubble-plugin";
68
+
69
+ export default {
70
+ plugins: [
71
+ logbubbleVitePlugin(),
72
+ // ...other plugins
73
+ ],
74
+ };
75
+ ```
76
+
77
+ 3. Start your Vite dev server:
78
+
79
+ ```bash
80
+ npm run dev
81
+ ```
82
+
83
+ The LogBubble UI will appear automatically in your app during development.
84
+
85
+ **Note:** The plugin is intended for development only. For production builds, import LogBubble manually as shown above.
86
+
47
87
  ### Manual initialization
48
88
 
49
89
  If you want more control over when LogBubble initializes:
@@ -128,6 +168,8 @@ LogBubble automatically intercepts and logs:
128
168
 
129
169
  - **Bubble**: Click to toggle the log window
130
170
  - **Badge**: Shows unread log count (disappears when window is opened)
171
+ - **Draggable Button**: Button is draggable in all four corners of thr screen
172
+ - **Toggleable Filter**: Toggleable Buttons to filter between consoles and network logs
131
173
  - **Clear**: Remove all logs
132
174
  - **Copy**: Copy all logs to clipboard with timestamps
133
175
 
@@ -218,7 +260,7 @@ Make sure your app is using native `fetch` or `XMLHttpRequest` APIs. Some custom
218
260
 
219
261
  ## Support
220
262
 
221
- For issues, feature requests, or questions, please open an issue on [GitHub](https://github.com/dktiwary007/LogBubble/issues).
263
+ For issues, feature requests, or questions, please open an issue on [GitHub](https://github.com/dkumar600/LogBubble/issues).
222
264
 
223
265
  ## License
224
266
 
@@ -226,7 +268,7 @@ MIT
226
268
 
227
269
  ## Contributing
228
270
 
229
- Contributions are welcome! Please feel free to submit issues or pull requests on [GitHub](https://github.com/dktiwary007/LogBubble).
271
+ Contributions are welcome! Please feel free to submit issues or pull requests on [GitHub](https://github.com/dkumar600/LogBubble).
230
272
 
231
273
  ### Getting Started with Development
232
274
 
@@ -241,4 +283,4 @@ If you want to contribute to LogBubble:
241
283
 
242
284
  Built with ❤️ for developers who need better mobile debugging tools.
243
285
 
244
- **Made by [Deepak Kumar](https://github.com/dktiwary007)**
286
+ **Made by [Deepak Kumar](https://github.com/dkumar600)**
package/dist/auto.js CHANGED
@@ -1,4 +1,2 @@
1
- // auto.ts
2
- // Side-effect import for auto-initialization
3
1
  import { initNetworkLogger } from "./init";
4
2
  initNetworkLogger();
@@ -2,9 +2,18 @@
2
2
  // Patches console methods to log to the network logger UI
3
3
  import { logStore } from "../ui/logStore";
4
4
  let isPatching = false;
5
+ const CONSOLE_PATCH_FLAG = "__LOGBUBBLE_CONSOLE_PATCHED__";
6
+ const CONSOLE_PATCH_PROP = "__logbubbleConsolePatched";
5
7
  export function patchConsole() {
6
8
  if (typeof window === "undefined" || typeof console === "undefined")
7
9
  return;
10
+ const g = globalThis;
11
+ if (g[CONSOLE_PATCH_FLAG])
12
+ return;
13
+ if (console[CONSOLE_PATCH_PROP])
14
+ return;
15
+ g[CONSOLE_PATCH_FLAG] = true;
16
+ console[CONSOLE_PATCH_PROP] = true;
8
17
  const originalLog = console.log;
9
18
  const originalWarn = console.warn;
10
19
  const originalError = console.error;
@@ -14,8 +23,8 @@ export function patchConsole() {
14
23
  originalLog.apply(console, args);
15
24
  if (!isPatching) {
16
25
  isPatching = true;
17
- const message = args.map((arg) => formatArg(arg)).join(" ");
18
- logStore.addLog(`[LOG] ${message}`, "plugin");
26
+ const payload = args.length === 1 ? args[0] : args;
27
+ logStore.addLog(payload, "console");
19
28
  isPatching = false;
20
29
  }
21
30
  };
@@ -23,8 +32,8 @@ export function patchConsole() {
23
32
  originalWarn.apply(console, args);
24
33
  if (!isPatching) {
25
34
  isPatching = true;
26
- const message = args.map((arg) => formatArg(arg)).join(" ");
27
- logStore.addLog(`[WARN] ${message}`, "plugin");
35
+ const payload = args.length === 1 ? args[0] : args;
36
+ logStore.addLog(payload, "console");
28
37
  isPatching = false;
29
38
  }
30
39
  };
@@ -32,8 +41,8 @@ export function patchConsole() {
32
41
  originalError.apply(console, args);
33
42
  if (!isPatching) {
34
43
  isPatching = true;
35
- const message = args.map((arg) => formatArg(arg)).join(" ");
36
- logStore.addLog(`[ERROR] ${message}`, "plugin");
44
+ const payload = args.length === 1 ? args[0] : args;
45
+ logStore.addLog(payload, "console");
37
46
  isPatching = false;
38
47
  }
39
48
  };
@@ -41,8 +50,8 @@ export function patchConsole() {
41
50
  originalInfo.apply(console, args);
42
51
  if (!isPatching) {
43
52
  isPatching = true;
44
- const message = args.map((arg) => formatArg(arg)).join(" ");
45
- logStore.addLog(`[INFO] ${message}`, "plugin");
53
+ const payload = args.length === 1 ? args[0] : args;
54
+ logStore.addLog(payload, "console");
46
55
  isPatching = false;
47
56
  }
48
57
  };
@@ -50,8 +59,8 @@ export function patchConsole() {
50
59
  originalDebug.apply(console, args);
51
60
  if (!isPatching) {
52
61
  isPatching = true;
53
- const message = args.map((arg) => formatArg(arg)).join(" ");
54
- logStore.addLog(`[DEBUG] ${message}`, "plugin");
62
+ const payload = args.length === 1 ? args[0] : args;
63
+ logStore.addLog(payload, "console");
55
64
  isPatching = false;
56
65
  }
57
66
  };
package/dist/core/dom.js CHANGED
@@ -1,10 +1,17 @@
1
- // dom.ts
2
- // (Optional) Patch dynamic <script> and <link> loading for network logging
3
1
  import { logStore } from "../ui/logStore";
2
+ const DOM_PATCH_FLAG = "__LOGBUBBLE_DOM_PATCHED__";
3
+ const DOM_PATCH_PROP = "__logbubbleDomPatched";
4
4
  export function patchDOM(logFn) {
5
5
  if (typeof window === "undefined")
6
6
  return;
7
+ const g = globalThis;
8
+ if (g[DOM_PATCH_FLAG])
9
+ return;
10
+ if (document.createElement[DOM_PATCH_PROP])
11
+ return;
12
+ g[DOM_PATCH_FLAG] = true;
7
13
  const origCreateElement = document.createElement;
14
+ origCreateElement[DOM_PATCH_PROP] = true;
8
15
  document.createElement = function (tag, options) {
9
16
  const el = origCreateElement.call(this, tag, options);
10
17
  if (tag === "script" || tag === "link") {
@@ -1,10 +1,17 @@
1
- // fetch.ts
2
- // Patches window.fetch to log network requests in dev mode (browser/WebView only)
3
1
  import { logStore } from "../ui/logStore";
2
+ const FETCH_PATCH_FLAG = "__LOGBUBBLE_FETCH_PATCHED__";
3
+ const FETCH_PATCH_PROP = "__logbubbleFetchPatched";
4
4
  export function patchFetch(logFn) {
5
5
  if (typeof window === "undefined" || typeof window.fetch !== "function")
6
6
  return;
7
+ const g = globalThis;
8
+ if (g[FETCH_PATCH_FLAG])
9
+ return;
10
+ if (window.fetch[FETCH_PATCH_PROP])
11
+ return;
12
+ g[FETCH_PATCH_FLAG] = true;
7
13
  const originalFetch = window.fetch;
14
+ originalFetch[FETCH_PATCH_PROP] = true;
8
15
  window.fetch = async function (input, init) {
9
16
  const method = (init && init.method) ||
10
17
  (typeof input === "object" && "method" in input && input.method) ||
@@ -14,6 +21,9 @@ export function patchFetch(logFn) {
14
21
  : input instanceof URL
15
22
  ? input.href
16
23
  : input.url;
24
+ if (typeof url === "string" && url.includes("/__logbubble/")) {
25
+ return originalFetch.call(this, input, init);
26
+ }
17
27
  const start = Date.now();
18
28
  try {
19
29
  const response = await originalFetch.call(this, input, init);
package/dist/core/xhr.js CHANGED
@@ -1,11 +1,18 @@
1
- // xhr.ts
2
- // Patches XMLHttpRequest to log network requests in dev mode (browser/WebView only)
3
1
  import { logStore } from "../ui/logStore";
2
+ const XHR_PATCH_FLAG = "__LOGBUBBLE_XHR_PATCHED__";
3
+ const XHR_PATCH_PROP = "__logbubbleXhrPatched";
4
4
  export function patchXHR(logFn) {
5
5
  if (typeof window === "undefined" ||
6
6
  typeof window.XMLHttpRequest !== "function")
7
7
  return;
8
+ const g = globalThis;
9
+ if (g[XHR_PATCH_FLAG])
10
+ return;
11
+ if (window.XMLHttpRequest[XHR_PATCH_PROP])
12
+ return;
13
+ g[XHR_PATCH_FLAG] = true;
8
14
  const OriginalXHR = window.XMLHttpRequest;
15
+ OriginalXHR[XHR_PATCH_PROP] = true;
9
16
  function PatchedXHR() {
10
17
  const xhr = new OriginalXHR();
11
18
  let url = "";
@@ -22,6 +29,8 @@ export function patchXHR(logFn) {
22
29
  start = Date.now();
23
30
  });
24
31
  xhr.addEventListener("loadend", () => {
32
+ if (url && url.includes("/__logbubble/"))
33
+ return;
25
34
  const ms = Date.now() - start;
26
35
  const message = `[NET] ${method} ${url} ${xhr.status} ${ms}ms`;
27
36
  logFn(message);
package/dist/init.js CHANGED
@@ -1,34 +1,39 @@
1
- // init.ts
2
- // Entry point for web-only dev-network-logger
3
1
  import { patchFetch } from "./core/fetch";
4
2
  import { patchXHR } from "./core/xhr";
5
3
  import { patchDOM } from "./core/dom";
6
4
  import { patchConsole } from "./core/console";
7
5
  import { logUI } from "./ui/logUI";
8
6
  let isInitialized = false;
7
+ const GLOBAL_INIT_FLAG = "__LOGBUBBLE_INITIALIZED__";
9
8
  function isDev() {
10
9
  return (typeof process !== "undefined" &&
11
10
  process.env &&
12
11
  (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev"));
13
12
  }
14
13
  export function initNetworkLogger() {
14
+ {
15
+ const g = globalThis;
16
+ if (g[GLOBAL_INIT_FLAG]) {
17
+ console.warn("[LogBubble] Already initialized, skipping duplicate init");
18
+ return;
19
+ }
20
+ g[GLOBAL_INIT_FLAG] = true;
21
+ if (typeof window !== "undefined") {
22
+ window[GLOBAL_INIT_FLAG] = true;
23
+ }
24
+ }
15
25
  if (isInitialized) {
16
26
  console.warn("[LogBubble] Already initialized, skipping duplicate init");
17
27
  return;
18
28
  }
19
29
  isInitialized = true;
20
30
  logUI.init();
21
- // Patch fetch/XHR/DOM/Console in dev mode
22
31
  patchFetch(() => { });
23
32
  patchXHR(() => { });
24
33
  patchDOM(() => { });
25
34
  patchConsole();
26
35
  }
27
- // Auto-init for browser
28
36
  if (typeof window !== "undefined") {
29
37
  window.initNetworkLogger = initNetworkLogger;
30
- // Optionally auto-init in dev
31
- // if (isDev()) {
32
38
  initNetworkLogger();
33
- // }
34
39
  }
@@ -0,0 +1,22 @@
1
+ export declare class DragHandler {
2
+ private buttonEl;
3
+ private onSnapComplete?;
4
+ private onClick?;
5
+ private isDragging;
6
+ private isPointerDown;
7
+ private dragStartX;
8
+ private dragStartY;
9
+ private mouseDownTime;
10
+ private mouseDownPos;
11
+ private didActuallyDrag;
12
+ buttonX: number;
13
+ buttonY: number;
14
+ constructor(buttonEl: HTMLDivElement, onSnapComplete?: (() => void) | undefined, onClick?: (() => void) | undefined);
15
+ attachListeners(): void;
16
+ isCurrentlyDragging(): boolean;
17
+ private handleDragStart;
18
+ private handleDragMove;
19
+ private handleDragEnd;
20
+ private updateButtonPosition;
21
+ private snapToCorner;
22
+ }
@@ -0,0 +1,127 @@
1
+ export class DragHandler {
2
+ constructor(buttonEl, onSnapComplete, onClick) {
3
+ this.buttonEl = buttonEl;
4
+ this.onSnapComplete = onSnapComplete;
5
+ this.onClick = onClick;
6
+ this.isDragging = false;
7
+ this.isPointerDown = false;
8
+ this.dragStartX = 0;
9
+ this.dragStartY = 0;
10
+ this.mouseDownTime = 0;
11
+ this.mouseDownPos = { x: 0, y: 0 };
12
+ this.didActuallyDrag = false;
13
+ this.buttonX = 0;
14
+ this.buttonY = 0;
15
+ this.buttonX = window.innerWidth - 58;
16
+ this.buttonY = 10;
17
+ this.updateButtonPosition();
18
+ }
19
+ attachListeners() {
20
+ this.buttonEl.addEventListener("mousedown", (e) => this.handleDragStart(e));
21
+ this.buttonEl.addEventListener("touchstart", (e) => this.handleDragStart(e), {
22
+ passive: false,
23
+ });
24
+ document.addEventListener("mousemove", (e) => this.handleDragMove(e));
25
+ document.addEventListener("touchmove", (e) => this.handleDragMove(e), {
26
+ passive: false,
27
+ });
28
+ document.addEventListener("mouseup", () => this.handleDragEnd());
29
+ document.addEventListener("touchend", () => this.handleDragEnd());
30
+ }
31
+ isCurrentlyDragging() {
32
+ return this.isDragging;
33
+ }
34
+ handleDragStart(e) {
35
+ this.isDragging = false;
36
+ this.didActuallyDrag = false;
37
+ this.isPointerDown = true;
38
+ const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
39
+ const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
40
+ this.dragStartX = clientX - this.buttonX;
41
+ this.dragStartY = clientY - this.buttonY;
42
+ this.mouseDownTime = Date.now();
43
+ this.mouseDownPos = { x: clientX, y: clientY };
44
+ if (e instanceof TouchEvent) {
45
+ e.preventDefault();
46
+ }
47
+ }
48
+ handleDragMove(e) {
49
+ if (!this.isPointerDown)
50
+ return;
51
+ if (this.dragStartX === 0 && this.dragStartY === 0)
52
+ return;
53
+ const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
54
+ const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
55
+ const newX = clientX - this.dragStartX;
56
+ const newY = clientY - this.dragStartY;
57
+ const distanceMoved = Math.sqrt(Math.pow(newX - this.buttonX, 2) + Math.pow(newY - this.buttonY, 2));
58
+ if (distanceMoved > 5) {
59
+ this.isDragging = true;
60
+ this.didActuallyDrag = true;
61
+ }
62
+ if (!this.isDragging)
63
+ return;
64
+ const maxX = window.innerWidth - 48;
65
+ const maxY = window.innerHeight - 48;
66
+ this.buttonX = Math.max(0, Math.min(newX, maxX));
67
+ this.buttonY = Math.max(0, Math.min(newY, maxY));
68
+ this.updateButtonPosition();
69
+ if (e instanceof TouchEvent) {
70
+ e.preventDefault();
71
+ }
72
+ }
73
+ handleDragEnd() {
74
+ if (!this.isPointerDown)
75
+ return;
76
+ if (this.isDragging) {
77
+ this.snapToCorner();
78
+ }
79
+ else if (!this.didActuallyDrag && this.onClick) {
80
+ this.onClick();
81
+ }
82
+ this.isDragging = false;
83
+ this.isPointerDown = false;
84
+ this.dragStartX = 0;
85
+ this.dragStartY = 0;
86
+ this.mouseDownTime = 0;
87
+ this.mouseDownPos = { x: 0, y: 0 };
88
+ this.didActuallyDrag = false;
89
+ }
90
+ updateButtonPosition() {
91
+ this.buttonEl.style.left = `${this.buttonX}px`;
92
+ this.buttonEl.style.top = `${this.buttonY}px`;
93
+ this.buttonEl.style.right = "auto";
94
+ }
95
+ snapToCorner() {
96
+ const buttonSize = 48;
97
+ const margin = 10;
98
+ const centerX = this.buttonX + buttonSize / 2;
99
+ const centerY = this.buttonY + buttonSize / 2;
100
+ const isLeftHalf = centerX < window.innerWidth / 2;
101
+ const isTopHalf = centerY < window.innerHeight / 2;
102
+ if (isLeftHalf && isTopHalf) {
103
+ this.buttonX = margin;
104
+ this.buttonY = margin;
105
+ }
106
+ else if (!isLeftHalf && isTopHalf) {
107
+ this.buttonX = window.innerWidth - buttonSize - margin;
108
+ this.buttonY = margin;
109
+ }
110
+ else if (isLeftHalf && !isTopHalf) {
111
+ this.buttonX = margin;
112
+ this.buttonY = window.innerHeight - buttonSize - margin;
113
+ }
114
+ else {
115
+ this.buttonX = window.innerWidth - buttonSize - margin;
116
+ this.buttonY = window.innerHeight - buttonSize - margin;
117
+ }
118
+ this.buttonEl.style.transition = "left 0.3s ease, top 0.3s ease";
119
+ this.updateButtonPosition();
120
+ setTimeout(() => {
121
+ this.buttonEl.style.transition = "none";
122
+ if (this.onSnapComplete) {
123
+ this.onSnapComplete();
124
+ }
125
+ }, 300);
126
+ }
127
+ }
@@ -0,0 +1,15 @@
1
+ import { LogEntry } from "./logStore";
2
+ export type FilterType = "all" | "console" | "network";
3
+ export declare class FilterManager {
4
+ private consoleFilterBtn;
5
+ private networkFilterBtn;
6
+ private onFilterChange;
7
+ private activeFilter;
8
+ private perfMonitor;
9
+ constructor(consoleFilterBtn: HTMLButtonElement, networkFilterBtn: HTMLButtonElement, onFilterChange: (filteredLogs: LogEntry[]) => void);
10
+ toggleFilter(filter: "console" | "network", allLogs: LogEntry[]): void;
11
+ applyFilter(allLogs: LogEntry[]): LogEntry[];
12
+ shouldShowLog(log: LogEntry): boolean;
13
+ getActiveFilter(): FilterType;
14
+ private updateFilterButtons;
15
+ }
@@ -0,0 +1,61 @@
1
+ export class FilterManager {
2
+ constructor(consoleFilterBtn, networkFilterBtn, onFilterChange) {
3
+ this.consoleFilterBtn = consoleFilterBtn;
4
+ this.networkFilterBtn = networkFilterBtn;
5
+ this.onFilterChange = onFilterChange;
6
+ this.activeFilter = "all";
7
+ this.perfMonitor = {
8
+ filterToggleStart: 0,
9
+ };
10
+ }
11
+ toggleFilter(filter, allLogs) {
12
+ this.perfMonitor.filterToggleStart = performance.now();
13
+ if (this.activeFilter === filter) {
14
+ this.activeFilter = "all";
15
+ }
16
+ else {
17
+ this.activeFilter = filter;
18
+ }
19
+ this.updateFilterButtons();
20
+ const filteredLogs = this.applyFilter(allLogs);
21
+ this.onFilterChange(filteredLogs);
22
+ const elapsed = performance.now() - this.perfMonitor.filterToggleStart;
23
+ if (elapsed > 16) {
24
+ console.warn(`[LogBubble] Filter toggle took ${elapsed.toFixed(2)}ms (target: ≤16ms)`);
25
+ }
26
+ }
27
+ applyFilter(allLogs) {
28
+ if (this.activeFilter === "all") {
29
+ return allLogs;
30
+ }
31
+ return allLogs.filter((log) => {
32
+ if (this.activeFilter === "console") {
33
+ return log.category === "console";
34
+ }
35
+ else if (this.activeFilter === "network") {
36
+ return log.category === "network";
37
+ }
38
+ return true;
39
+ });
40
+ }
41
+ shouldShowLog(log) {
42
+ if (this.activeFilter === "all")
43
+ return true;
44
+ if (this.activeFilter === "console")
45
+ return log.category === "console";
46
+ if (this.activeFilter === "network")
47
+ return log.category === "network";
48
+ return true;
49
+ }
50
+ getActiveFilter() {
51
+ return this.activeFilter;
52
+ }
53
+ updateFilterButtons() {
54
+ const consoleActive = this.activeFilter === "console";
55
+ const networkActive = this.activeFilter === "network";
56
+ this.consoleFilterBtn.classList.toggle("filter-active", consoleActive);
57
+ this.consoleFilterBtn.setAttribute("aria-pressed", consoleActive.toString());
58
+ this.networkFilterBtn.classList.toggle("filter-active", networkActive);
59
+ this.networkFilterBtn.setAttribute("aria-pressed", networkActive.toString());
60
+ }
61
+ }
@@ -1,14 +1,27 @@
1
1
  export interface LogEntry {
2
+ key: string;
2
3
  timestamp: number;
3
4
  message: string;
4
- type: "fetch" | "xhr" | "dom" | "plugin";
5
+ type: "fetch" | "xhr" | "dom" | "plugin" | "console";
6
+ category: "console" | "network";
7
+ isCritical?: boolean;
8
+ count?: number;
5
9
  }
6
10
  declare class LogStore {
7
11
  private logs;
8
12
  private listeners;
9
13
  private maxLogs;
10
- addLog(message: string, type?: LogEntry["type"]): void;
11
- getLogs(): LogEntry[];
14
+ private recentNetworkLogs;
15
+ private nextKeyId;
16
+ private networkDedupeKey;
17
+ private shouldCollapseDuplicate;
18
+ /**
19
+ * Estimates payload size without stringifying.
20
+ * Handles nested objects, arrays, circular references, and deep nesting.
21
+ */
22
+ private estimatePayloadSize;
23
+ addLog(payload: any, type?: LogEntry["type"]): void;
24
+ getLogs(filterCategory?: "console" | "network"): LogEntry[];
12
25
  clearLogs(): void;
13
26
  subscribe(listener: (log: LogEntry) => void): () => void;
14
27
  }